From b914eaa6fa1be84b0716d022fac15b9a4488bcf4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 3 Jan 2026 05:10:09 +0100 Subject: [PATCH] chore: apply biome lint fixes --- src/agents/bash-tools.ts | 5 +++- src/auto-reply/reply.ts | 29 +++++++++++--------- src/cli/tui-cli.ts | 10 +++++-- src/commands/antigravity-oauth.ts | 26 ++++++++++-------- src/commands/configure.ts | 15 ++++------- src/commands/onboard-interactive.ts | 15 ++++------- src/commands/onboard-non-interactive.ts | 4 ++- src/commands/onboard-types.ts | 7 ++++- src/gateway/server.ts | 11 ++++---- src/hooks/gmail-watcher.ts | 16 ++++++----- src/tui/gateway-chat.ts | 3 +-- src/tui/layout.ts | 10 +++++-- src/tui/message-list.ts | 12 ++++++--- src/tui/theme.ts | 2 +- src/tui/tui.ts | 36 ++++++++++++++++++------- 15 files changed, 123 insertions(+), 78 deletions(-) diff --git a/src/agents/bash-tools.ts b/src/agents/bash-tools.ts index ae76d77d2..e30732785 100644 --- a/src/agents/bash-tools.ts +++ b/src/agents/bash-tools.ts @@ -197,7 +197,10 @@ export function createBashTool( rows: 30, }); } catch (error) { - if (ptyShell !== DEFAULT_SHELL_PATH && existsSync(DEFAULT_SHELL_PATH)) { + if ( + ptyShell !== DEFAULT_SHELL_PATH && + existsSync(DEFAULT_SHELL_PATH) + ) { try { pty = ptyModule.spawn( DEFAULT_SHELL_PATH, diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index 4d3ad67ba..ef6eea4dd 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -30,6 +30,7 @@ import { DEFAULT_AGENT_WORKSPACE_DIR, ensureAgentWorkspace, } from "../agents/workspace.js"; +import { parseDurationMs } from "../cli/parse-duration.js"; import { type ClawdisConfig, loadConfig } from "../config/config.js"; import { buildGroupDisplayName, @@ -54,7 +55,6 @@ import { import { clearCommandLane, getQueueSize } from "../process/command-queue.js"; import { defaultRuntime } from "../runtime.js"; import { normalizeE164 } from "../utils.js"; -import { parseDurationMs } from "../cli/parse-duration.js"; import { resolveHeartbeatSeconds } from "../web/reconnect.js"; import { getWebAuthAgeMs, webAuthExists } from "../web/session.js"; import { @@ -192,10 +192,18 @@ function normalizeQueueMode(raw?: string): QueueMode | undefined { if (!raw) return undefined; const cleaned = raw.trim().toLowerCase(); if (cleaned === "queue" || cleaned === "queued") return "steer"; - if (cleaned === "interrupt" || cleaned === "interrupts" || cleaned === "abort") + if ( + cleaned === "interrupt" || + cleaned === "interrupts" || + cleaned === "abort" + ) return "interrupt"; if (cleaned === "steer" || cleaned === "steering") return "steer"; - if (cleaned === "followup" || cleaned === "follow-ups" || cleaned === "followups") + if ( + cleaned === "followup" || + cleaned === "follow-ups" || + cleaned === "followups" + ) return "followup"; if (cleaned === "collect" || cleaned === "coalesce") return "collect"; if ( @@ -536,10 +544,7 @@ function buildSummaryPrompt(queue: FollowupQueueState): string | undefined { return lines.join("\n"); } -function buildCollectPrompt( - items: FollowupRun[], - summary?: string, -): string { +function buildCollectPrompt(items: FollowupRun[], summary?: string): string { const blocks: string[] = ["[Queued messages while agent was busy]"]; if (summary) { blocks.push(summary); @@ -706,7 +711,8 @@ function resolveQueueSettings(params: { mode: resolvedMode, debounceMs: typeof debounceRaw === "number" ? Math.max(0, debounceRaw) : undefined, - cap: typeof capRaw === "number" ? Math.max(1, Math.floor(capRaw)) : undefined, + cap: + typeof capRaw === "number" ? Math.max(1, Math.floor(capRaw)) : undefined, dropPolicy: dropRaw, }; } @@ -1954,9 +1960,7 @@ export async function getReplyFromConfig( }); } catch (err) { const message = err instanceof Error ? err.message : String(err); - defaultRuntime.error?.( - `Followup agent failed before reply: ${message}`, - ); + defaultRuntime.error?.(`Followup agent failed before reply: ${message}`); return; } @@ -2010,7 +2014,8 @@ export async function getReplyFromConfig( ...entry, inputTokens: input, outputTokens: output, - totalTokens: promptTokens > 0 ? promptTokens : usage.total ?? input, + totalTokens: + promptTokens > 0 ? promptTokens : (usage.total ?? input), model: modelUsed, contextTokens: contextTokensUsed ?? entry.contextTokens, updatedAt: Date.now(), diff --git a/src/cli/tui-cli.ts b/src/cli/tui-cli.ts index 39ce4c481..1ebbfdce6 100644 --- a/src/cli/tui-cli.ts +++ b/src/cli/tui-cli.ts @@ -22,8 +22,14 @@ export function registerTuiCli(program: Command) { .option("--history-limit ", "History entries to load", "200") .action(async (opts) => { try { - const timeoutMs = Number.parseInt(String(opts.timeoutMs ?? "30000"), 10); - const historyLimit = Number.parseInt(String(opts.historyLimit ?? "200"), 10); + const timeoutMs = Number.parseInt( + String(opts.timeoutMs ?? "30000"), + 10, + ); + const historyLimit = Number.parseInt( + String(opts.historyLimit ?? "200"), + 10, + ); await runTui({ url: opts.url as string | undefined, token: opts.token as string | undefined, diff --git a/src/commands/antigravity-oauth.ts b/src/commands/antigravity-oauth.ts index 3e26b5f15..234859f3b 100644 --- a/src/commands/antigravity-oauth.ts +++ b/src/commands/antigravity-oauth.ts @@ -5,16 +5,20 @@ * On VPS/SSH/headless: Shows URL and prompts user to paste the callback URL manually. */ -import { createInterface } from "node:readline/promises"; -import { stdin, stdout } from "node:process"; +import { createHash, randomBytes } from "node:crypto"; import { readFileSync } from "node:fs"; -import { randomBytes, createHash } from "node:crypto"; +import { stdin, stdout } from "node:process"; +import { createInterface } from "node:readline/promises"; import { loginAntigravity, type OAuthCredentials } from "@mariozechner/pi-ai"; // OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync const decode = (s: string) => Buffer.from(s, "base64").toString(); -const CLIENT_ID = decode("MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="); -const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY="); +const CLIENT_ID = decode( + "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==", +); +const CLIENT_SECRET = decode( + "R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=", +); const REDIRECT_URI = "http://localhost:51121/oauth-callback"; const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; const TOKEN_URL = "https://oauth2.googleapis.com/token"; @@ -98,9 +102,7 @@ export function shouldUseManualOAuthFlow(): boolean { */ function generatePKCESync(): { verifier: string; challenge: string } { const verifier = randomBytes(32).toString("hex"); - const challenge = createHash("sha256") - .update(verifier) - .digest("base64url"); + const challenge = createHash("sha256").update(verifier).digest("base64url"); return { verifier, challenge }; } @@ -196,7 +198,7 @@ async function exchangeCodeForTokens( // Fetch user email const email = await getUserEmail(data.access_token); - + // Fetch project ID const projectId = await fetchProjectId(data.access_token); @@ -288,7 +290,7 @@ async function fetchProjectId(accessToken: string): Promise { return data.cloudaicompanionProject.id; } } catch { - continue; + // ignore failed endpoint, try next } } @@ -370,7 +372,9 @@ export async function loginAntigravityManual( console.log("=".repeat(60)); console.log("\n1. Open the URL above in your LOCAL browser"); console.log("2. Complete the Google sign-in"); - console.log("3. Your browser will redirect to a localhost URL that won't load"); + console.log( + "3. Your browser will redirect to a localhost URL that won't load", + ); console.log("4. Copy the ENTIRE URL from your browser's address bar"); console.log("5. Paste it below\n"); console.log("The URL will look like:"); diff --git a/src/commands/configure.ts b/src/commands/configure.ts index 8f37093f5..9ac9853a0 100644 --- a/src/commands/configure.ts +++ b/src/commands/configure.ts @@ -10,16 +10,7 @@ import { spinner, text, } from "@clack/prompts"; -import { - loginAnthropic, - type OAuthCredentials, -} from "@mariozechner/pi-ai"; - -import { - loginAntigravityVpsAware, - isRemoteEnvironment, -} from "./antigravity-oauth.js"; - +import { loginAnthropic, type OAuthCredentials } from "@mariozechner/pi-ai"; import type { ClawdisConfig } from "../config/config.js"; import { CONFIG_PATH_CLAWDIS, @@ -32,6 +23,10 @@ import { resolveGatewayService } from "../daemon/service.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { resolveUserPath, sleep } from "../utils.js"; +import { + isRemoteEnvironment, + loginAntigravityVpsAware, +} from "./antigravity-oauth.js"; import { healthCommand } from "./health.js"; import { applyMinimaxConfig, diff --git a/src/commands/onboard-interactive.ts b/src/commands/onboard-interactive.ts index 2a1f82d68..9a2e2ce8d 100644 --- a/src/commands/onboard-interactive.ts +++ b/src/commands/onboard-interactive.ts @@ -9,16 +9,7 @@ import { spinner, text, } from "@clack/prompts"; -import { - loginAnthropic, - type OAuthCredentials, -} from "@mariozechner/pi-ai"; - -import { - loginAntigravityVpsAware, - isRemoteEnvironment, -} from "./antigravity-oauth.js"; - +import { loginAnthropic, type OAuthCredentials } from "@mariozechner/pi-ai"; import type { ClawdisConfig } from "../config/config.js"; import { CONFIG_PATH_CLAWDIS, @@ -31,6 +22,10 @@ import { resolveGatewayService } from "../daemon/service.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { resolveUserPath, sleep } from "../utils.js"; +import { + isRemoteEnvironment, + loginAntigravityVpsAware, +} from "./antigravity-oauth.js"; import { healthCommand } from "./health.js"; import { applyMinimaxConfig, diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts index 7f977d6f1..a06aa8cc6 100644 --- a/src/commands/onboard-non-interactive.ts +++ b/src/commands/onboard-non-interactive.ts @@ -99,7 +99,9 @@ export async function runNonInteractiveOnboarding( } else if (authChoice === "minimax") { nextConfig = applyMinimaxConfig(nextConfig); } else if (authChoice === "oauth" || authChoice === "antigravity") { - runtime.error(`${authChoice === "oauth" ? "OAuth" : "Antigravity"} requires interactive mode.`); + runtime.error( + `${authChoice === "oauth" ? "OAuth" : "Antigravity"} requires interactive mode.`, + ); runtime.exit(1); return; } diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index 312af24c1..a23caf9c8 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -1,5 +1,10 @@ export type OnboardMode = "local" | "remote"; -export type AuthChoice = "oauth" | "antigravity" | "apiKey" | "minimax" | "skip"; +export type AuthChoice = + | "oauth" + | "antigravity" + | "apiKey" + | "minimax" + | "skip"; export type GatewayAuthChoice = "off" | "token" | "password"; export type ResetScope = "config" | "config+creds+sessions" | "full"; export type GatewayBind = "loopback" | "lan" | "tailnet" | "auto"; diff --git a/src/gateway/server.ts b/src/gateway/server.ts index a9ec6fb54..d95b9cac5 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -77,6 +77,7 @@ import { } from "../discord/index.js"; import { type DiscordProbe, probeDiscord } from "../discord/probe.js"; import { isVerbose } from "../globals.js"; +import { startGmailWatcher, stopGmailWatcher } from "../hooks/gmail-watcher.js"; import { monitorIMessageProvider, sendMessageIMessage, @@ -174,10 +175,6 @@ import { type HookMappingResolved, resolveHookMappings, } from "./hooks-mapping.js"; -import { - startGmailWatcher, - stopGmailWatcher, -} from "../hooks/gmail-watcher.js"; ensureClawdisCliOnPath(); @@ -6869,7 +6866,11 @@ export async function startGatewayServer( const gmailResult = await startGmailWatcher(cfgAtStart); if (gmailResult.started) { logHooks.info("gmail watcher started"); - } else if (gmailResult.reason && gmailResult.reason !== "hooks not enabled" && gmailResult.reason !== "no gmail account configured") { + } else if ( + gmailResult.reason && + gmailResult.reason !== "hooks not enabled" && + gmailResult.reason !== "no gmail account configured" + ) { logHooks.warn(`gmail watcher not started: ${gmailResult.reason}`); } } catch (err) { diff --git a/src/hooks/gmail-watcher.ts b/src/hooks/gmail-watcher.ts index 2c7c268f5..6ec7ea377 100644 --- a/src/hooks/gmail-watcher.ts +++ b/src/hooks/gmail-watcher.ts @@ -5,7 +5,7 @@ * if hooks.gmail is configured with an account. */ -import { spawn, type ChildProcess } from "node:child_process"; +import { type ChildProcess, spawn } from "node:child_process"; import { hasBinary } from "../agents/skills.js"; import type { ClawdisConfig } from "../config/config.js"; import { createSubsystemLogger } from "../logging.js"; @@ -13,8 +13,8 @@ import { runCommandWithTimeout } from "../process/exec.js"; import { buildGogWatchServeArgs, buildGogWatchStartArgs, - resolveGmailHookRuntimeConfig, type GmailHookRuntimeConfig, + resolveGmailHookRuntimeConfig, } from "./gmail.js"; import { ensureTailscaleEndpoint } from "./gmail-setup-utils.js"; @@ -42,7 +42,8 @@ async function startGmailWatch( try { const result = await runCommandWithTimeout(args, { timeoutMs: 120_000 }); if (result.code !== 0) { - const message = result.stderr || result.stdout || "gog watch start failed"; + const message = + result.stderr || result.stdout || "gog watch start failed"; log.error(`watch start failed: ${message}`); return false; } @@ -60,7 +61,7 @@ async function startGmailWatch( function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess { const args = buildGogWatchServeArgs(cfg); log.info(`starting gog ${args.join(" ")}`); - + const child = spawn("gog", args, { stdio: ["ignore", "pipe", "pipe"], detached: false, @@ -142,7 +143,10 @@ export async function startGmailWatcher( ); } catch (err) { log.error(`tailscale setup failed: ${String(err)}`); - return { started: false, reason: `tailscale setup failed: ${String(err)}` }; + return { + started: false, + reason: `tailscale setup failed: ${String(err)}`, + }; } } @@ -184,7 +188,7 @@ export async function stopGmailWatcher(): Promise { if (watcherProcess) { log.info("stopping gmail watcher"); watcherProcess.kill("SIGTERM"); - + // Wait a bit for graceful shutdown await new Promise((resolve) => { const timeout = setTimeout(() => { diff --git a/src/tui/gateway-chat.ts b/src/tui/gateway-chat.ts index 2c4ecfd7c..0ff0902d2 100644 --- a/src/tui/gateway-chat.ts +++ b/src/tui/gateway-chat.ts @@ -81,8 +81,7 @@ export class GatewayChatClient { await this.readyPromise; } - async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> - { + async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> { const runId = randomUUID(); await this.client.request("chat.send", { sessionKey: opts.sessionKey, diff --git a/src/tui/layout.ts b/src/tui/layout.ts index aaccab9b1..b3f61d059 100644 --- a/src/tui/layout.ts +++ b/src/tui/layout.ts @@ -21,7 +21,8 @@ export class ChatLayout implements Component { const statusLines = this.status.render(width); const inputLines = this.input.render(width); - const reserved = headerLines.length + statusLines.length + inputLines.length; + const reserved = + headerLines.length + statusLines.length + inputLines.length; const available = Math.max(rows - reserved, 0); const messageLines = this.messages.render(width); @@ -30,7 +31,12 @@ export class ChatLayout implements Component { ? messageLines.slice(Math.max(0, messageLines.length - available)) : []; - const lines = [...headerLines, ...slicedMessages, ...statusLines, ...inputLines]; + const lines = [ + ...headerLines, + ...slicedMessages, + ...statusLines, + ...inputLines, + ]; if (lines.length < rows) { const padding = Array.from({ length: rows - lines.length }, () => ""); return [...lines, ...padding]; diff --git a/src/tui/message-list.ts b/src/tui/message-list.ts index 57eb77323..2e17e6d3d 100644 --- a/src/tui/message-list.ts +++ b/src/tui/message-list.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; +import type { DefaultTextStyle, MarkdownTheme } from "@mariozechner/pi-tui"; import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui"; -import type { MarkdownTheme, DefaultTextStyle } from "@mariozechner/pi-tui"; import { theme } from "./theme.js"; export class MessageList extends Container { @@ -38,7 +38,13 @@ export class MessageList extends Container { addAssistant(text: string, id?: string): string { const messageId = id ?? crypto.randomUUID(); const label = new Text(theme.assistant("clawd"), 1, 0); - const body = new Markdown(text, 1, 0, this.markdownTheme, this.styles.assistant); + const body = new Markdown( + text, + 1, + 0, + this.markdownTheme, + this.styles.assistant, + ); const group = new Container(); group.addChild(label); group.addChild(body); @@ -60,7 +66,6 @@ export class MessageList extends Container { text: string, style: DefaultTextStyle, ) { - const messageId = crypto.randomUUID(); const label = new Text( role === "user" ? theme.user("you") @@ -76,6 +81,5 @@ export class MessageList extends Container { group.addChild(body); this.addChild(group); this.addChild(new Spacer(1)); - } } diff --git a/src/tui/theme.ts b/src/tui/theme.ts index e40f9eb5c..198ece5c7 100644 --- a/src/tui/theme.ts +++ b/src/tui/theme.ts @@ -1,5 +1,5 @@ -import chalk from "chalk"; import type { MarkdownTheme } from "@mariozechner/pi-tui"; +import chalk from "chalk"; export const markdownTheme: MarkdownTheme = { heading: (text) => chalk.bold.cyan(text), diff --git a/src/tui/tui.ts b/src/tui/tui.ts index 10a072c23..03b396637 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -1,11 +1,11 @@ import { type Component, Input, + isCtrlC, + isEscape, ProcessTerminal, Text, TUI, - isCtrlC, - isEscape, } from "@mariozechner/pi-tui"; import { loadConfig } from "../config/config.js"; import { GatewayChatClient } from "./gateway-chat.js"; @@ -37,8 +37,7 @@ class InputWrapper implements Component { private input: Input, private onAbort: () => void, private onExit: () => void, - ) { - } + ) {} handleInput(data: string): void { if (isCtrlC(data)) { @@ -76,10 +75,13 @@ function extractText(message?: unknown): string { return parts.join("\n").trim(); } -function renderHistoryEntry(entry: unknown): { role: "user" | "assistant"; text: string } | null { +function renderHistoryEntry( + entry: unknown, +): { role: "user" | "assistant"; text: string } | null { if (!entry || typeof entry !== "object") return null; const record = entry as Record; - const role = record.role === "user" || record.role === "assistant" ? record.role : null; + const role = + record.role === "user" || record.role === "assistant" ? record.role : null; if (!role) return null; const text = extractText(record); if (!text) return null; @@ -112,7 +114,10 @@ export async function runTui(opts: TuiOptions) { async () => { if (!activeRunId) return; try { - await client.abortChat({ sessionKey: currentSession, runId: activeRunId }); + await client.abortChat({ + sessionKey: currentSession, + runId: activeRunId, + }); } catch (err) { messages.addSystem(`Abort failed: ${String(err)}`); } @@ -245,14 +250,23 @@ export async function runTui(opts: TuiOptions) { messages.addSystem("no active run"); break; } - await client.abortChat({ sessionKey: currentSession, runId: activeRunId }); + await client.abortChat({ + sessionKey: currentSession, + runId: activeRunId, + }); + break; + } + case "exit": { + client.stop(); + tui.stop(); + process.exit(0); break; } - case "exit": case "quit": { client.stop(); tui.stop(); process.exit(0); + break; } default: messages.addSystem(`unknown command: /${command}`); @@ -319,7 +333,9 @@ export async function runTui(opts: TuiOptions) { }; client.onGap = (info) => { - messages.addSystem(`event gap: expected ${info.expected}, got ${info.received}`); + messages.addSystem( + `event gap: expected ${info.expected}, got ${info.received}`, + ); tui.requestRender(); };