chore: apply biome lint fixes

This commit is contained in:
Peter Steinberger
2026-01-03 05:10:09 +01:00
parent 988b67aa30
commit b914eaa6fa
15 changed files with 123 additions and 78 deletions

View File

@@ -197,7 +197,10 @@ export function createBashTool(
rows: 30, rows: 30,
}); });
} catch (error) { } catch (error) {
if (ptyShell !== DEFAULT_SHELL_PATH && existsSync(DEFAULT_SHELL_PATH)) { if (
ptyShell !== DEFAULT_SHELL_PATH &&
existsSync(DEFAULT_SHELL_PATH)
) {
try { try {
pty = ptyModule.spawn( pty = ptyModule.spawn(
DEFAULT_SHELL_PATH, DEFAULT_SHELL_PATH,

View File

@@ -30,6 +30,7 @@ import {
DEFAULT_AGENT_WORKSPACE_DIR, DEFAULT_AGENT_WORKSPACE_DIR,
ensureAgentWorkspace, ensureAgentWorkspace,
} from "../agents/workspace.js"; } from "../agents/workspace.js";
import { parseDurationMs } from "../cli/parse-duration.js";
import { type ClawdisConfig, loadConfig } from "../config/config.js"; import { type ClawdisConfig, loadConfig } from "../config/config.js";
import { import {
buildGroupDisplayName, buildGroupDisplayName,
@@ -54,7 +55,6 @@ import {
import { clearCommandLane, getQueueSize } from "../process/command-queue.js"; import { clearCommandLane, getQueueSize } from "../process/command-queue.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { normalizeE164 } from "../utils.js"; import { normalizeE164 } from "../utils.js";
import { parseDurationMs } from "../cli/parse-duration.js";
import { resolveHeartbeatSeconds } from "../web/reconnect.js"; import { resolveHeartbeatSeconds } from "../web/reconnect.js";
import { getWebAuthAgeMs, webAuthExists } from "../web/session.js"; import { getWebAuthAgeMs, webAuthExists } from "../web/session.js";
import { import {
@@ -192,10 +192,18 @@ function normalizeQueueMode(raw?: string): QueueMode | undefined {
if (!raw) return undefined; if (!raw) return undefined;
const cleaned = raw.trim().toLowerCase(); const cleaned = raw.trim().toLowerCase();
if (cleaned === "queue" || cleaned === "queued") return "steer"; if (cleaned === "queue" || cleaned === "queued") return "steer";
if (cleaned === "interrupt" || cleaned === "interrupts" || cleaned === "abort") if (
cleaned === "interrupt" ||
cleaned === "interrupts" ||
cleaned === "abort"
)
return "interrupt"; return "interrupt";
if (cleaned === "steer" || cleaned === "steering") return "steer"; 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"; return "followup";
if (cleaned === "collect" || cleaned === "coalesce") return "collect"; if (cleaned === "collect" || cleaned === "coalesce") return "collect";
if ( if (
@@ -536,10 +544,7 @@ function buildSummaryPrompt(queue: FollowupQueueState): string | undefined {
return lines.join("\n"); return lines.join("\n");
} }
function buildCollectPrompt( function buildCollectPrompt(items: FollowupRun[], summary?: string): string {
items: FollowupRun[],
summary?: string,
): string {
const blocks: string[] = ["[Queued messages while agent was busy]"]; const blocks: string[] = ["[Queued messages while agent was busy]"];
if (summary) { if (summary) {
blocks.push(summary); blocks.push(summary);
@@ -706,7 +711,8 @@ function resolveQueueSettings(params: {
mode: resolvedMode, mode: resolvedMode,
debounceMs: debounceMs:
typeof debounceRaw === "number" ? Math.max(0, debounceRaw) : undefined, 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, dropPolicy: dropRaw,
}; };
} }
@@ -1954,9 +1960,7 @@ export async function getReplyFromConfig(
}); });
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : String(err); const message = err instanceof Error ? err.message : String(err);
defaultRuntime.error?.( defaultRuntime.error?.(`Followup agent failed before reply: ${message}`);
`Followup agent failed before reply: ${message}`,
);
return; return;
} }
@@ -2010,7 +2014,8 @@ export async function getReplyFromConfig(
...entry, ...entry,
inputTokens: input, inputTokens: input,
outputTokens: output, outputTokens: output,
totalTokens: promptTokens > 0 ? promptTokens : usage.total ?? input, totalTokens:
promptTokens > 0 ? promptTokens : (usage.total ?? input),
model: modelUsed, model: modelUsed,
contextTokens: contextTokensUsed ?? entry.contextTokens, contextTokens: contextTokensUsed ?? entry.contextTokens,
updatedAt: Date.now(), updatedAt: Date.now(),

View File

@@ -22,8 +22,14 @@ export function registerTuiCli(program: Command) {
.option("--history-limit <n>", "History entries to load", "200") .option("--history-limit <n>", "History entries to load", "200")
.action(async (opts) => { .action(async (opts) => {
try { try {
const timeoutMs = Number.parseInt(String(opts.timeoutMs ?? "30000"), 10); const timeoutMs = Number.parseInt(
const historyLimit = Number.parseInt(String(opts.historyLimit ?? "200"), 10); String(opts.timeoutMs ?? "30000"),
10,
);
const historyLimit = Number.parseInt(
String(opts.historyLimit ?? "200"),
10,
);
await runTui({ await runTui({
url: opts.url as string | undefined, url: opts.url as string | undefined,
token: opts.token as string | undefined, token: opts.token as string | undefined,

View File

@@ -5,16 +5,20 @@
* On VPS/SSH/headless: Shows URL and prompts user to paste the callback URL manually. * On VPS/SSH/headless: Shows URL and prompts user to paste the callback URL manually.
*/ */
import { createInterface } from "node:readline/promises"; import { createHash, randomBytes } from "node:crypto";
import { stdin, stdout } from "node:process";
import { readFileSync } from "node:fs"; 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"; import { loginAntigravity, type OAuthCredentials } from "@mariozechner/pi-ai";
// OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync // OAuth constants - decoded from pi-ai's base64 encoded values to stay in sync
const decode = (s: string) => Buffer.from(s, "base64").toString(); const decode = (s: string) => Buffer.from(s, "base64").toString();
const CLIENT_ID = decode("MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="); const CLIENT_ID = decode(
const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY="); "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==",
);
const CLIENT_SECRET = decode(
"R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=",
);
const REDIRECT_URI = "http://localhost:51121/oauth-callback"; const REDIRECT_URI = "http://localhost:51121/oauth-callback";
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
const TOKEN_URL = "https://oauth2.googleapis.com/token"; const TOKEN_URL = "https://oauth2.googleapis.com/token";
@@ -98,9 +102,7 @@ export function shouldUseManualOAuthFlow(): boolean {
*/ */
function generatePKCESync(): { verifier: string; challenge: string } { function generatePKCESync(): { verifier: string; challenge: string } {
const verifier = randomBytes(32).toString("hex"); const verifier = randomBytes(32).toString("hex");
const challenge = createHash("sha256") const challenge = createHash("sha256").update(verifier).digest("base64url");
.update(verifier)
.digest("base64url");
return { verifier, challenge }; return { verifier, challenge };
} }
@@ -196,7 +198,7 @@ async function exchangeCodeForTokens(
// Fetch user email // Fetch user email
const email = await getUserEmail(data.access_token); const email = await getUserEmail(data.access_token);
// Fetch project ID // Fetch project ID
const projectId = await fetchProjectId(data.access_token); const projectId = await fetchProjectId(data.access_token);
@@ -288,7 +290,7 @@ async function fetchProjectId(accessToken: string): Promise<string> {
return data.cloudaicompanionProject.id; return data.cloudaicompanionProject.id;
} }
} catch { } catch {
continue; // ignore failed endpoint, try next
} }
} }
@@ -370,7 +372,9 @@ export async function loginAntigravityManual(
console.log("=".repeat(60)); console.log("=".repeat(60));
console.log("\n1. Open the URL above in your LOCAL browser"); console.log("\n1. Open the URL above in your LOCAL browser");
console.log("2. Complete the Google sign-in"); 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("4. Copy the ENTIRE URL from your browser's address bar");
console.log("5. Paste it below\n"); console.log("5. Paste it below\n");
console.log("The URL will look like:"); console.log("The URL will look like:");

View File

@@ -10,16 +10,7 @@ import {
spinner, spinner,
text, text,
} from "@clack/prompts"; } from "@clack/prompts";
import { import { loginAnthropic, type OAuthCredentials } from "@mariozechner/pi-ai";
loginAnthropic,
type OAuthCredentials,
} from "@mariozechner/pi-ai";
import {
loginAntigravityVpsAware,
isRemoteEnvironment,
} from "./antigravity-oauth.js";
import type { ClawdisConfig } from "../config/config.js"; import type { ClawdisConfig } from "../config/config.js";
import { import {
CONFIG_PATH_CLAWDIS, CONFIG_PATH_CLAWDIS,
@@ -32,6 +23,10 @@ import { resolveGatewayService } from "../daemon/service.js";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { resolveUserPath, sleep } from "../utils.js"; import { resolveUserPath, sleep } from "../utils.js";
import {
isRemoteEnvironment,
loginAntigravityVpsAware,
} from "./antigravity-oauth.js";
import { healthCommand } from "./health.js"; import { healthCommand } from "./health.js";
import { import {
applyMinimaxConfig, applyMinimaxConfig,

View File

@@ -9,16 +9,7 @@ import {
spinner, spinner,
text, text,
} from "@clack/prompts"; } from "@clack/prompts";
import { import { loginAnthropic, type OAuthCredentials } from "@mariozechner/pi-ai";
loginAnthropic,
type OAuthCredentials,
} from "@mariozechner/pi-ai";
import {
loginAntigravityVpsAware,
isRemoteEnvironment,
} from "./antigravity-oauth.js";
import type { ClawdisConfig } from "../config/config.js"; import type { ClawdisConfig } from "../config/config.js";
import { import {
CONFIG_PATH_CLAWDIS, CONFIG_PATH_CLAWDIS,
@@ -31,6 +22,10 @@ import { resolveGatewayService } from "../daemon/service.js";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { resolveUserPath, sleep } from "../utils.js"; import { resolveUserPath, sleep } from "../utils.js";
import {
isRemoteEnvironment,
loginAntigravityVpsAware,
} from "./antigravity-oauth.js";
import { healthCommand } from "./health.js"; import { healthCommand } from "./health.js";
import { import {
applyMinimaxConfig, applyMinimaxConfig,

View File

@@ -99,7 +99,9 @@ export async function runNonInteractiveOnboarding(
} else if (authChoice === "minimax") { } else if (authChoice === "minimax") {
nextConfig = applyMinimaxConfig(nextConfig); nextConfig = applyMinimaxConfig(nextConfig);
} else if (authChoice === "oauth" || authChoice === "antigravity") { } 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); runtime.exit(1);
return; return;
} }

View File

@@ -1,5 +1,10 @@
export type OnboardMode = "local" | "remote"; 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 GatewayAuthChoice = "off" | "token" | "password";
export type ResetScope = "config" | "config+creds+sessions" | "full"; export type ResetScope = "config" | "config+creds+sessions" | "full";
export type GatewayBind = "loopback" | "lan" | "tailnet" | "auto"; export type GatewayBind = "loopback" | "lan" | "tailnet" | "auto";

View File

@@ -77,6 +77,7 @@ import {
} from "../discord/index.js"; } from "../discord/index.js";
import { type DiscordProbe, probeDiscord } from "../discord/probe.js"; import { type DiscordProbe, probeDiscord } from "../discord/probe.js";
import { isVerbose } from "../globals.js"; import { isVerbose } from "../globals.js";
import { startGmailWatcher, stopGmailWatcher } from "../hooks/gmail-watcher.js";
import { import {
monitorIMessageProvider, monitorIMessageProvider,
sendMessageIMessage, sendMessageIMessage,
@@ -174,10 +175,6 @@ import {
type HookMappingResolved, type HookMappingResolved,
resolveHookMappings, resolveHookMappings,
} from "./hooks-mapping.js"; } from "./hooks-mapping.js";
import {
startGmailWatcher,
stopGmailWatcher,
} from "../hooks/gmail-watcher.js";
ensureClawdisCliOnPath(); ensureClawdisCliOnPath();
@@ -6869,7 +6866,11 @@ export async function startGatewayServer(
const gmailResult = await startGmailWatcher(cfgAtStart); const gmailResult = await startGmailWatcher(cfgAtStart);
if (gmailResult.started) { if (gmailResult.started) {
logHooks.info("gmail watcher 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}`); logHooks.warn(`gmail watcher not started: ${gmailResult.reason}`);
} }
} catch (err) { } catch (err) {

View File

@@ -5,7 +5,7 @@
* if hooks.gmail is configured with an account. * 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 { hasBinary } from "../agents/skills.js";
import type { ClawdisConfig } from "../config/config.js"; import type { ClawdisConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging.js"; import { createSubsystemLogger } from "../logging.js";
@@ -13,8 +13,8 @@ import { runCommandWithTimeout } from "../process/exec.js";
import { import {
buildGogWatchServeArgs, buildGogWatchServeArgs,
buildGogWatchStartArgs, buildGogWatchStartArgs,
resolveGmailHookRuntimeConfig,
type GmailHookRuntimeConfig, type GmailHookRuntimeConfig,
resolveGmailHookRuntimeConfig,
} from "./gmail.js"; } from "./gmail.js";
import { ensureTailscaleEndpoint } from "./gmail-setup-utils.js"; import { ensureTailscaleEndpoint } from "./gmail-setup-utils.js";
@@ -42,7 +42,8 @@ async function startGmailWatch(
try { try {
const result = await runCommandWithTimeout(args, { timeoutMs: 120_000 }); const result = await runCommandWithTimeout(args, { timeoutMs: 120_000 });
if (result.code !== 0) { 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}`); log.error(`watch start failed: ${message}`);
return false; return false;
} }
@@ -60,7 +61,7 @@ async function startGmailWatch(
function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess { function spawnGogServe(cfg: GmailHookRuntimeConfig): ChildProcess {
const args = buildGogWatchServeArgs(cfg); const args = buildGogWatchServeArgs(cfg);
log.info(`starting gog ${args.join(" ")}`); log.info(`starting gog ${args.join(" ")}`);
const child = spawn("gog", args, { const child = spawn("gog", args, {
stdio: ["ignore", "pipe", "pipe"], stdio: ["ignore", "pipe", "pipe"],
detached: false, detached: false,
@@ -142,7 +143,10 @@ export async function startGmailWatcher(
); );
} catch (err) { } catch (err) {
log.error(`tailscale setup failed: ${String(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<void> {
if (watcherProcess) { if (watcherProcess) {
log.info("stopping gmail watcher"); log.info("stopping gmail watcher");
watcherProcess.kill("SIGTERM"); watcherProcess.kill("SIGTERM");
// Wait a bit for graceful shutdown // Wait a bit for graceful shutdown
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {

View File

@@ -81,8 +81,7 @@ export class GatewayChatClient {
await this.readyPromise; await this.readyPromise;
} }
async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {
{
const runId = randomUUID(); const runId = randomUUID();
await this.client.request("chat.send", { await this.client.request("chat.send", {
sessionKey: opts.sessionKey, sessionKey: opts.sessionKey,

View File

@@ -21,7 +21,8 @@ export class ChatLayout implements Component {
const statusLines = this.status.render(width); const statusLines = this.status.render(width);
const inputLines = this.input.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 available = Math.max(rows - reserved, 0);
const messageLines = this.messages.render(width); const messageLines = this.messages.render(width);
@@ -30,7 +31,12 @@ export class ChatLayout implements Component {
? messageLines.slice(Math.max(0, messageLines.length - available)) ? messageLines.slice(Math.max(0, messageLines.length - available))
: []; : [];
const lines = [...headerLines, ...slicedMessages, ...statusLines, ...inputLines]; const lines = [
...headerLines,
...slicedMessages,
...statusLines,
...inputLines,
];
if (lines.length < rows) { if (lines.length < rows) {
const padding = Array.from({ length: rows - lines.length }, () => ""); const padding = Array.from({ length: rows - lines.length }, () => "");
return [...lines, ...padding]; return [...lines, ...padding];

View File

@@ -1,6 +1,6 @@
import crypto from "node:crypto"; import crypto from "node:crypto";
import type { DefaultTextStyle, MarkdownTheme } from "@mariozechner/pi-tui";
import { Container, Markdown, Spacer, Text } 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"; import { theme } from "./theme.js";
export class MessageList extends Container { export class MessageList extends Container {
@@ -38,7 +38,13 @@ export class MessageList extends Container {
addAssistant(text: string, id?: string): string { addAssistant(text: string, id?: string): string {
const messageId = id ?? crypto.randomUUID(); const messageId = id ?? crypto.randomUUID();
const label = new Text(theme.assistant("clawd"), 1, 0); 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(); const group = new Container();
group.addChild(label); group.addChild(label);
group.addChild(body); group.addChild(body);
@@ -60,7 +66,6 @@ export class MessageList extends Container {
text: string, text: string,
style: DefaultTextStyle, style: DefaultTextStyle,
) { ) {
const messageId = crypto.randomUUID();
const label = new Text( const label = new Text(
role === "user" role === "user"
? theme.user("you") ? theme.user("you")
@@ -76,6 +81,5 @@ export class MessageList extends Container {
group.addChild(body); group.addChild(body);
this.addChild(group); this.addChild(group);
this.addChild(new Spacer(1)); this.addChild(new Spacer(1));
} }
} }

View File

@@ -1,5 +1,5 @@
import chalk from "chalk";
import type { MarkdownTheme } from "@mariozechner/pi-tui"; import type { MarkdownTheme } from "@mariozechner/pi-tui";
import chalk from "chalk";
export const markdownTheme: MarkdownTheme = { export const markdownTheme: MarkdownTheme = {
heading: (text) => chalk.bold.cyan(text), heading: (text) => chalk.bold.cyan(text),

View File

@@ -1,11 +1,11 @@
import { import {
type Component, type Component,
Input, Input,
isCtrlC,
isEscape,
ProcessTerminal, ProcessTerminal,
Text, Text,
TUI, TUI,
isCtrlC,
isEscape,
} from "@mariozechner/pi-tui"; } from "@mariozechner/pi-tui";
import { loadConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js";
import { GatewayChatClient } from "./gateway-chat.js"; import { GatewayChatClient } from "./gateway-chat.js";
@@ -37,8 +37,7 @@ class InputWrapper implements Component {
private input: Input, private input: Input,
private onAbort: () => void, private onAbort: () => void,
private onExit: () => void, private onExit: () => void,
) { ) {}
}
handleInput(data: string): void { handleInput(data: string): void {
if (isCtrlC(data)) { if (isCtrlC(data)) {
@@ -76,10 +75,13 @@ function extractText(message?: unknown): string {
return parts.join("\n").trim(); 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; if (!entry || typeof entry !== "object") return null;
const record = entry as Record<string, unknown>; const record = entry as Record<string, unknown>;
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; if (!role) return null;
const text = extractText(record); const text = extractText(record);
if (!text) return null; if (!text) return null;
@@ -112,7 +114,10 @@ export async function runTui(opts: TuiOptions) {
async () => { async () => {
if (!activeRunId) return; if (!activeRunId) return;
try { try {
await client.abortChat({ sessionKey: currentSession, runId: activeRunId }); await client.abortChat({
sessionKey: currentSession,
runId: activeRunId,
});
} catch (err) { } catch (err) {
messages.addSystem(`Abort failed: ${String(err)}`); messages.addSystem(`Abort failed: ${String(err)}`);
} }
@@ -245,14 +250,23 @@ export async function runTui(opts: TuiOptions) {
messages.addSystem("no active run"); messages.addSystem("no active run");
break; 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; break;
} }
case "exit":
case "quit": { case "quit": {
client.stop(); client.stop();
tui.stop(); tui.stop();
process.exit(0); process.exit(0);
break;
} }
default: default:
messages.addSystem(`unknown command: /${command}`); messages.addSystem(`unknown command: /${command}`);
@@ -319,7 +333,9 @@ export async function runTui(opts: TuiOptions) {
}; };
client.onGap = (info) => { 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(); tui.requestRender();
}; };