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,
});
} 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,

View File

@@ -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(),

View File

@@ -22,8 +22,14 @@ export function registerTuiCli(program: Command) {
.option("--history-limit <n>", "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,

View File

@@ -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<string> {
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:");

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -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) {

View File

@@ -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<void> {
if (watcherProcess) {
log.info("stopping gmail watcher");
watcherProcess.kill("SIGTERM");
// Wait a bit for graceful shutdown
await new Promise<void>((resolve) => {
const timeout = setTimeout(() => {

View File

@@ -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,

View File

@@ -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];

View File

@@ -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));
}
}

View File

@@ -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),

View File

@@ -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<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;
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();
};