refactor: centralize cli timeout parsing

This commit is contained in:
Peter Steinberger
2026-01-09 21:28:14 +01:00
parent 1689ef0b97
commit cfeaa34c16
6 changed files with 40 additions and 25 deletions

View File

@@ -114,7 +114,7 @@
- Docs: expand parameter descriptions for agent/wake hooks. (#532) — thanks @mcinteerj
- Docs: add community showcase entries from Discord. (#476) — thanks @gupsammy
- TUI: refresh status bar after think/verbose/reasoning changes. (#519) — thanks @jdrhyne
- TUI: stop overriding agent timeout so config defaults apply. (#549)
- TUI: stop overriding agent timeout so config defaults apply; warn on invalid `--timeout-ms`. (#549)
- Status: show Verbose/Elevated only when enabled.
- Status: filter usage summary to the active model provider.
- Status: map model providers to usage sources so unrelated usage doesnt appear.

View File

@@ -109,7 +109,7 @@ Session lifecycle:
- `--session <key>`: Session key (default: `main`, or `global` when scope is global)
- `--deliver`: Deliver assistant replies to the provider (default off)
- `--thinking <level>`: Override thinking level for sends
- `--timeout-ms <ms>`: Agent timeout (defaults to `agents.defaults.timeoutSeconds`)
- `--timeout-ms <ms>`: Agent timeout in ms (defaults to `agents.defaults.timeoutSeconds`)
- `--history-limit <n>`: History entries to load (default 200)
## Troubleshooting

View File

@@ -1,3 +1,5 @@
import { parseTimeoutMs } from "./parse-timeout.js";
export function parseEnvPairs(
pairs: unknown,
): Record<string, string> | undefined {
@@ -14,17 +16,4 @@ export function parseEnvPairs(
return Object.keys(env).length > 0 ? env : undefined;
}
export function parseTimeoutMs(raw: unknown): number | undefined {
if (raw === undefined || raw === null) return undefined;
let value = Number.NaN;
if (typeof raw === "number") {
value = raw;
} else if (typeof raw === "bigint") {
value = Number(raw);
} else if (typeof raw === "string") {
const trimmed = raw.trim();
if (!trimmed) return undefined;
value = Number.parseInt(trimmed, 10);
}
return Number.isFinite(value) ? value : undefined;
}
export { parseTimeoutMs };

14
src/cli/parse-timeout.ts Normal file
View File

@@ -0,0 +1,14 @@
export function parseTimeoutMs(raw: unknown): number | undefined {
if (raw === undefined || raw === null) return undefined;
let value = Number.NaN;
if (typeof raw === "number") {
value = raw;
} else if (typeof raw === "bigint") {
value = Number(raw);
} else if (typeof raw === "string") {
const trimmed = raw.trim();
if (!trimmed) return undefined;
value = Number.parseInt(trimmed, 10);
}
return Number.isFinite(value) ? value : undefined;
}

View File

@@ -98,6 +98,19 @@ describe("cli program", () => {
);
});
it("warns and ignores invalid tui timeout override", async () => {
const program = buildProgram();
await program.parseAsync(["tui", "--timeout-ms", "nope"], {
from: "user",
});
expect(runtime.error).toHaveBeenCalledWith(
'warning: invalid --timeout-ms "nope"; ignoring',
);
expect(runTui).toHaveBeenCalledWith(
expect.objectContaining({ timeoutMs: undefined }),
);
});
it("runs config alias as configure", async () => {
const program = buildProgram();
await program.parseAsync(["config"], { from: "user" });

View File

@@ -1,6 +1,7 @@
import type { Command } from "commander";
import { defaultRuntime } from "../runtime.js";
import { runTui } from "../tui/tui.js";
import { parseTimeoutMs } from "./parse-timeout.js";
export function registerTuiCli(program: Command) {
program
@@ -26,14 +27,12 @@ export function registerTuiCli(program: Command) {
.option("--history-limit <n>", "History entries to load", "200")
.action(async (opts) => {
try {
const timeoutMs =
typeof opts.timeoutMs === "undefined"
? undefined
: Number.parseInt(String(opts.timeoutMs), 10);
const normalizedTimeoutMs =
typeof timeoutMs === "number" && Number.isFinite(timeoutMs)
? timeoutMs
: undefined;
const timeoutMs = parseTimeoutMs(opts.timeoutMs);
if (opts.timeoutMs !== undefined && timeoutMs === undefined) {
defaultRuntime.error(
`warning: invalid --timeout-ms "${String(opts.timeoutMs)}"; ignoring`,
);
}
const historyLimit = Number.parseInt(
String(opts.historyLimit ?? "200"),
10,
@@ -46,7 +45,7 @@ export function registerTuiCli(program: Command) {
deliver: Boolean(opts.deliver),
thinking: opts.thinking as string | undefined,
message: opts.message as string | undefined,
timeoutMs: normalizedTimeoutMs,
timeoutMs,
historyLimit: Number.isNaN(historyLimit) ? undefined : historyLimit,
});
} catch (err) {