From 07650765d2bf7d0f1dd1b30c6bed8fa0ce5e8bc8 Mon Sep 17 00:00:00 2001 From: Cyrus Goh Date: Sun, 25 Jan 2026 14:09:30 -0800 Subject: [PATCH] lint fix --- src/line/auto-reply-delivery.ts | 2 ++ src/line/bot-handlers.ts | 1 + src/line/bot-message-context.ts | 2 ++ src/line/rich-menu.ts | 1 + src/line/send.ts | 1 + src/line/template-messages.ts | 1 + src/line/types.ts | 2 ++ src/line/webhook.ts | 1 + ui/src/ui/app-tool-stream.ts | 6 +++++- ui/src/ui/app-view-state.ts | 4 ++-- ui/src/ui/app.ts | 4 ++-- ui/src/ui/chat-markdown.browser.test.ts | 1 + ui/src/ui/chat/message-normalizer.ts | 3 ++- ui/src/ui/chat/tool-cards.ts | 6 ++++-- ui/src/ui/controllers/config.ts | 2 +- ui/src/ui/controllers/debug.ts | 2 +- ui/src/ui/focus-mode.browser.test.ts | 1 + ui/src/ui/navigation.browser.test.ts | 1 + ui/src/ui/presenter.ts | 3 ++- ui/src/ui/views/channels.config.ts | 2 +- ui/src/ui/views/channels.types.ts | 2 +- ui/src/ui/views/config-form.node.ts | 22 +++++++++++++++------- ui/src/ui/views/config.ts | 2 +- 23 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/line/auto-reply-delivery.ts b/src/line/auto-reply-delivery.ts index ad4573ca1..0b1444b07 100644 --- a/src/line/auto-reply-delivery.ts +++ b/src/line/auto-reply-delivery.ts @@ -8,6 +8,7 @@ import type { LineReplyMessage, SendLineReplyChunksParams } from "./reply-chunks export type LineAutoReplyDeps = { buildTemplateMessageFromPayload: ( payload: LineTemplateMessagePayload, + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types ) => messagingApi.TemplateMessage | null; processLineMessage: (text: string) => ProcessedLineMessage; chunkMarkdownText: (text: string, limit: number) => string[]; @@ -168,6 +169,7 @@ export async function deliverLineAutoReply(params: { const quickReply = deps.createQuickReplyItems(lineData.quickReplies!); const targetIndex = replyToken && !replyTokenUsed ? Math.min(4, combined.length - 1) : combined.length - 1; + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types const target = combined[targetIndex] as messagingApi.Message & { quickReply?: messagingApi.QuickReply; }; diff --git a/src/line/bot-handlers.ts b/src/line/bot-handlers.ts index 28e91b984..55f3ea500 100644 --- a/src/line/bot-handlers.ts +++ b/src/line/bot-handlers.ts @@ -123,6 +123,7 @@ async function sendLinePairingReply(params: { } async function shouldProcessLineEvent( + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types event: MessageEvent | PostbackEvent, context: LineHandlerContext, ): Promise { diff --git a/src/line/bot-message-context.ts b/src/line/bot-message-context.ts index 2fa508982..1588ac78a 100644 --- a/src/line/bot-message-context.ts +++ b/src/line/bot-message-context.ts @@ -86,12 +86,14 @@ const STICKER_PACKAGES: Record = { function describeStickerKeywords(sticker: StickerEventMessage): string { // Use sticker keywords if available (LINE provides these for some stickers) + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types const keywords = (sticker as StickerEventMessage & { keywords?: string[] }).keywords; if (keywords && keywords.length > 0) { return keywords.slice(0, 3).join(", "); } // Use sticker text if available + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types const stickerText = (sticker as StickerEventMessage & { text?: string }).text; if (stickerText) { return stickerText; diff --git a/src/line/rich-menu.ts b/src/line/rich-menu.ts index 6149405a9..2fb05dc26 100644 --- a/src/line/rich-menu.ts +++ b/src/line/rich-menu.ts @@ -291,6 +291,7 @@ export async function getRichMenuList(opts: RichMenuOpts = {}): Promise { const client = getClient(opts); diff --git a/src/line/send.ts b/src/line/send.ts index 68be26a29..b7035e687 100644 --- a/src/line/send.ts +++ b/src/line/send.ts @@ -531,6 +531,7 @@ export function createQuickReplyItems(labels: string[]): QuickReply { export function createTextMessageWithQuickReplies( text: string, quickReplyLabels: string[], + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types ): TextMessage & { quickReply: QuickReply } { return { type: "text", diff --git a/src/line/template-messages.ts b/src/line/template-messages.ts index 686dc8337..a24786b65 100644 --- a/src/line/template-messages.ts +++ b/src/line/template-messages.ts @@ -324,6 +324,7 @@ import type { LineTemplateMessagePayload } from "./types.js"; */ export function buildTemplateMessageFromPayload( payload: LineTemplateMessagePayload, + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types ): TemplateMessage | null { switch (payload.type) { case "confirm": { diff --git a/src/line/types.ts b/src/line/types.ts index 252fcb949..06c9f1df9 100644 --- a/src/line/types.ts +++ b/src/line/types.ts @@ -61,6 +61,7 @@ export interface ResolvedLineAccount { config: LineConfig & LineAccountConfig; } +/* eslint-disable @typescript-eslint/no-redundant-type-constituents -- SDK types */ export type LineMessageType = | TextMessage | ImageMessage @@ -68,6 +69,7 @@ export type LineMessageType = | AudioMessage | StickerMessage | LocationMessage; +/* eslint-enable @typescript-eslint/no-redundant-type-constituents */ export interface LineWebhookContext { event: WebhookEvent; diff --git a/src/line/webhook.ts b/src/line/webhook.ts index 5f5e12441..4a6eb3217 100644 --- a/src/line/webhook.ts +++ b/src/line/webhook.ts @@ -23,6 +23,7 @@ function readRawBody(req: Request): string | null { return Buffer.isBuffer(rawBody) ? rawBody.toString("utf-8") : rawBody; } +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- SDK types function parseWebhookBody(req: Request, rawBody: string): WebhookRequestBody | null { if (req.body && typeof req.body === "object" && !Buffer.isBuffer(req.body)) { return req.body as WebhookRequestBody; diff --git a/ui/src/ui/app-tool-stream.ts b/ui/src/ui/app-tool-stream.ts index f43814998..80a0aac49 100644 --- a/ui/src/ui/app-tool-stream.ts +++ b/ui/src/ui/app-tool-stream.ts @@ -67,7 +67,11 @@ function formatToolOutput(value: unknown): string | null { try { text = JSON.stringify(value, null, 2); } catch { - text = String(value); + if (typeof value === "object" && value !== null) { + text = "[object]"; + } else { + text = String(value as string | number | boolean | symbol | bigint); + } } } const truncated = truncateText(text, TOOL_OUTPUT_CHAR_LIMIT); diff --git a/ui/src/ui/app-view-state.ts b/ui/src/ui/app-view-state.ts index f7e76052c..1ed7a1c34 100644 --- a/ui/src/ui/app-view-state.ts +++ b/ui/src/ui/app-view-state.ts @@ -79,7 +79,7 @@ export type AppViewState = { configApplying: boolean; updateRunning: boolean; configSnapshot: ConfigSnapshot | null; - configSchema: unknown | null; + configSchema: unknown; configSchemaLoading: boolean; configUiHints: Record; configForm: Record | null; @@ -129,7 +129,7 @@ export type AppViewState = { debugStatus: StatusSummary | null; debugHealth: HealthSnapshot | null; debugModels: unknown[]; - debugHeartbeat: unknown | null; + debugHeartbeat: unknown; debugCallMethod: string; debugCallParams: string; debugCallResult: string | null; diff --git a/ui/src/ui/app.ts b/ui/src/ui/app.ts index 34d997aab..7debb5cf5 100644 --- a/ui/src/ui/app.ts +++ b/ui/src/ui/app.ts @@ -160,7 +160,7 @@ export class ClawdbotApp extends LitElement { @state() updateRunning = false; @state() applySessionKey = this.settings.lastActiveSessionKey; @state() configSnapshot: ConfigSnapshot | null = null; - @state() configSchema: unknown | null = null; + @state() configSchema: unknown = null; @state() configSchemaVersion: string | null = null; @state() configSchemaLoading = false; @state() configUiHints: ConfigUiHints = {}; @@ -221,7 +221,7 @@ export class ClawdbotApp extends LitElement { @state() debugStatus: StatusSummary | null = null; @state() debugHealth: HealthSnapshot | null = null; @state() debugModels: unknown[] = []; - @state() debugHeartbeat: unknown | null = null; + @state() debugHeartbeat: unknown = null; @state() debugCallMethod = ""; @state() debugCallParams = "{}"; @state() debugCallResult: string | null = null; diff --git a/ui/src/ui/chat-markdown.browser.test.ts b/ui/src/ui/chat-markdown.browser.test.ts index 3548dc35c..8d56a44da 100644 --- a/ui/src/ui/chat-markdown.browser.test.ts +++ b/ui/src/ui/chat-markdown.browser.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { ClawdbotApp } from "./app"; +// eslint-disable-next-line @typescript-eslint/unbound-method const originalConnect = ClawdbotApp.prototype.connect; function mountApp(pathname: string) { diff --git a/ui/src/ui/chat/message-normalizer.ts b/ui/src/ui/chat/message-normalizer.ts index 388939b9f..991b0312c 100644 --- a/ui/src/ui/chat/message-normalizer.ts +++ b/ui/src/ui/chat/message-normalizer.ts @@ -21,7 +21,8 @@ export function normalizeMessage(message: unknown): NormalizedMessage { Array.isArray(contentItems) && contentItems.some((item) => { const x = item as Record; - const t = String(x.type ?? "").toLowerCase(); + const rawType = x.type; + const t = (typeof rawType === "string" ? rawType : "").toLowerCase(); return t === "toolresult" || t === "tool_result"; }); diff --git a/ui/src/ui/chat/tool-cards.ts b/ui/src/ui/chat/tool-cards.ts index 8db5e5a8d..0b1f0b1a8 100644 --- a/ui/src/ui/chat/tool-cards.ts +++ b/ui/src/ui/chat/tool-cards.ts @@ -13,7 +13,8 @@ export function extractToolCards(message: unknown): ToolCard[] { const cards: ToolCard[] = []; for (const item of content) { - const kind = String(item.type ?? "").toLowerCase(); + const rawType = item.type; + const kind = (typeof rawType === "string" ? rawType : "").toLowerCase(); const isToolCall = ["toolcall", "tool_call", "tooluse", "tool_use"].includes(kind) || (typeof item.name === "string" && item.arguments != null); @@ -27,7 +28,8 @@ export function extractToolCards(message: unknown): ToolCard[] { } for (const item of content) { - const kind = String(item.type ?? "").toLowerCase(); + const rawKind = item.type; + const kind = (typeof rawKind === "string" ? rawKind : "").toLowerCase(); if (kind !== "toolresult" && kind !== "tool_result") continue; const text = extractToolText(item); const name = typeof item.name === "string" ? item.name : "tool"; diff --git a/ui/src/ui/controllers/config.ts b/ui/src/ui/controllers/config.ts index 84b9ae515..44356c54e 100644 --- a/ui/src/ui/controllers/config.ts +++ b/ui/src/ui/controllers/config.ts @@ -20,7 +20,7 @@ export type ConfigState = { configApplying: boolean; updateRunning: boolean; configSnapshot: ConfigSnapshot | null; - configSchema: unknown | null; + configSchema: unknown; configSchemaVersion: string | null; configSchemaLoading: boolean; configUiHints: ConfigUiHints; diff --git a/ui/src/ui/controllers/debug.ts b/ui/src/ui/controllers/debug.ts index 2f189af88..5237f0469 100644 --- a/ui/src/ui/controllers/debug.ts +++ b/ui/src/ui/controllers/debug.ts @@ -8,7 +8,7 @@ export type DebugState = { debugStatus: StatusSummary | null; debugHealth: HealthSnapshot | null; debugModels: unknown[]; - debugHeartbeat: unknown | null; + debugHeartbeat: unknown; debugCallMethod: string; debugCallParams: string; debugCallResult: string | null; diff --git a/ui/src/ui/focus-mode.browser.test.ts b/ui/src/ui/focus-mode.browser.test.ts index 6f736dbf8..8c346df92 100644 --- a/ui/src/ui/focus-mode.browser.test.ts +++ b/ui/src/ui/focus-mode.browser.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { ClawdbotApp } from "./app"; +// eslint-disable-next-line @typescript-eslint/unbound-method const originalConnect = ClawdbotApp.prototype.connect; function mountApp(pathname: string) { diff --git a/ui/src/ui/navigation.browser.test.ts b/ui/src/ui/navigation.browser.test.ts index c07d2bb79..cf1f32e11 100644 --- a/ui/src/ui/navigation.browser.test.ts +++ b/ui/src/ui/navigation.browser.test.ts @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { ClawdbotApp } from "./app"; import "../styles.css"; +// eslint-disable-next-line @typescript-eslint/unbound-method const originalConnect = ClawdbotApp.prototype.connect; function mountApp(pathname: string) { diff --git a/ui/src/ui/presenter.ts b/ui/src/ui/presenter.ts index ddb99d9c5..4558b9fa6 100644 --- a/ui/src/ui/presenter.ts +++ b/ui/src/ui/presenter.ts @@ -31,7 +31,8 @@ export function formatEventPayload(payload: unknown): string { try { return JSON.stringify(payload, null, 2); } catch { - return String(payload); + if (typeof payload === "object" && payload !== null) return "[object]"; + return String(payload as string | number | boolean | symbol | bigint); } } diff --git a/ui/src/ui/views/channels.config.ts b/ui/src/ui/views/channels.config.ts index 73801b62d..272eb2b5e 100644 --- a/ui/src/ui/views/channels.config.ts +++ b/ui/src/ui/views/channels.config.ts @@ -7,7 +7,7 @@ import { analyzeConfigSchema, renderNode, schemaType, type JsonSchema } from "./ type ChannelConfigFormProps = { channelId: string; configValue: Record | null; - schema: unknown | null; + schema: unknown; uiHints: ConfigUiHints; disabled: boolean; onPatch: (path: Array, value: unknown) => void; diff --git a/ui/src/ui/views/channels.types.ts b/ui/src/ui/views/channels.types.ts index fa1a9094b..844445390 100644 --- a/ui/src/ui/views/channels.types.ts +++ b/ui/src/ui/views/channels.types.ts @@ -26,7 +26,7 @@ export type ChannelsProps = { whatsappQrDataUrl: string | null; whatsappConnected: boolean | null; whatsappBusy: boolean; - configSchema: unknown | null; + configSchema: unknown; configSchemaLoading: boolean; configForm: Record | null; configUiHints: ConfigUiHints; diff --git a/ui/src/ui/views/config-form.node.ts b/ui/src/ui/views/config-form.node.ts index 5812a6f30..e5b9cf09a 100644 --- a/ui/src/ui/views/config-form.node.ts +++ b/ui/src/ui/views/config-form.node.ts @@ -130,7 +130,7 @@ export function renderNode(params: { } // Check if it's a set of literal values (enum-like) - const extractLiteral = (v: JsonSchema): unknown | undefined => { + const extractLiteral = (v: JsonSchema): unknown => { if (v.const !== undefined) return v.const; if (v.enum && v.enum.length === 1) return v.enum[0]; return undefined; @@ -141,6 +141,8 @@ export function renderNode(params: { if (allLiterals && literals.length > 0 && literals.length <= 5) { // Use segmented control for small sets const resolvedValue = value ?? schema.default; + const stringify = (v: unknown) => + typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? String(v) : ""; return html`
${showLabel ? html`` : nothing} @@ -150,11 +152,11 @@ export function renderNode(params: { (lit) => html` `, )} @@ -296,9 +298,15 @@ function renderTextInput(params: { const label = hint?.label ?? schema.title ?? humanize(String(path.at(-1))); const help = hint?.help ?? schema.description; const isSensitive = hint?.sensitive ?? isSensitivePath(path); + const defaultStr = + schema.default !== undefined && + (typeof schema.default === "string" || + typeof schema.default === "number" || + typeof schema.default === "boolean") + ? String(schema.default) + : undefined; const placeholder = - hint?.placeholder ?? - (isSensitive ? "••••" : schema.default !== undefined ? `Default: ${schema.default}` : ""); + hint?.placeholder ?? (isSensitive ? "••••" : defaultStr ? `Default: ${defaultStr}` : ""); const displayValue = value ?? ""; return html` @@ -310,7 +318,7 @@ function renderTextInput(params: { type=${isSensitive ? "password" : inputType} class="cfg-input" placeholder=${placeholder} - .value=${displayValue == null ? "" : String(displayValue)} + .value=${displayValue == null ? "" : typeof displayValue === "string" || typeof displayValue === "number" ? String(displayValue) : ""} ?disabled=${disabled} @input=${(e: Event) => { const raw = (e.target as HTMLInputElement).value; @@ -375,7 +383,7 @@ function renderNumberInput(params: { { const raw = (e.target as HTMLInputElement).value; diff --git a/ui/src/ui/views/config.ts b/ui/src/ui/views/config.ts index e3056e8b5..12426335a 100644 --- a/ui/src/ui/views/config.ts +++ b/ui/src/ui/views/config.ts @@ -13,7 +13,7 @@ export type ConfigProps = { applying: boolean; updating: boolean; connected: boolean; - schema: unknown | null; + schema: unknown; schemaLoading: boolean; uiHints: ConfigUiHints; formMode: "form" | "raw";