refactor: share cli runtime error handling
This commit is contained in:
@@ -4,7 +4,7 @@ import { danger, setVerbose } from "../../../globals.js";
|
||||
import { CHANNEL_TARGET_DESCRIPTION } from "../../../infra/outbound/channel-target.js";
|
||||
import { defaultRuntime } from "../../../runtime.js";
|
||||
import { createDefaultDeps } from "../../deps.js";
|
||||
import { ensureConfigReady } from "../config-guard.js";
|
||||
import { runCommandWithRuntime } from "../../cli-utils.js";
|
||||
|
||||
export type MessageCliHelpers = {
|
||||
withMessageBase: (command: Command) => Command;
|
||||
@@ -31,10 +31,11 @@ export function createMessageCliHelpers(
|
||||
command.requiredOption("-t, --target <dest>", CHANNEL_TARGET_DESCRIPTION);
|
||||
|
||||
const runMessageAction = async (action: string, opts: Record<string, unknown>) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: true });
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
const deps = createDefaultDeps();
|
||||
try {
|
||||
await runCommandWithRuntime(
|
||||
defaultRuntime,
|
||||
async () => {
|
||||
await messageCommand(
|
||||
{
|
||||
...(() => {
|
||||
@@ -49,10 +50,12 @@ export function createMessageCliHelpers(
|
||||
deps,
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
defaultRuntime.error(danger(String(err)));
|
||||
defaultRuntime.exit(1);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// `message` is only used for `message.help({ error: true })`, keep the
|
||||
|
||||
@@ -8,8 +8,8 @@ import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { hasExplicitOptions } from "../command-options.js";
|
||||
import { createDefaultDeps } from "../deps.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { collectOption } from "./helpers.js";
|
||||
import { ensureConfigReady } from "./config-guard.js";
|
||||
|
||||
export function registerAgentCommands(program: Command, args: { agentChannelOptions: string }) {
|
||||
program
|
||||
@@ -54,17 +54,13 @@ Examples:
|
||||
${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent")}`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: false });
|
||||
const verboseLevel = typeof opts.verbose === "string" ? opts.verbose.toLowerCase() : "";
|
||||
setVerbose(verboseLevel === "on");
|
||||
// Build default deps (keeps parity with other commands; future-proofing).
|
||||
const deps = createDefaultDeps();
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await agentCliCommand(opts, defaultRuntime, deps);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const agents = program
|
||||
@@ -82,16 +78,12 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent
|
||||
.option("--json", "Output JSON instead of text", false)
|
||||
.option("--bindings", "Include routing bindings", false)
|
||||
.action(async (opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: true });
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await agentsListCommand(
|
||||
{ json: Boolean(opts.json), bindings: Boolean(opts.bindings) },
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
agents
|
||||
@@ -104,8 +96,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent
|
||||
.option("--non-interactive", "Disable prompts; requires --workspace", false)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (name, opts, command) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: true });
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const hasFlags = hasExplicitOptions(command, [
|
||||
"workspace",
|
||||
"model",
|
||||
@@ -126,10 +117,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent
|
||||
defaultRuntime,
|
||||
{ hasFlags },
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
agents
|
||||
@@ -138,8 +126,7 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent
|
||||
.option("--force", "Skip confirmation", false)
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (id, opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: true });
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await agentsDeleteCommand(
|
||||
{
|
||||
id: String(id),
|
||||
@@ -148,19 +135,12 @@ ${theme.muted("Docs:")} ${formatDocsLink("/cli/agent", "docs.clawd.bot/cli/agent
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
agents.action(async () => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: true });
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await agentsListCommand({}, defaultRuntime);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
||||
export function registerConfigureCommand(program: Command) {
|
||||
program
|
||||
@@ -24,7 +25,7 @@ export function registerConfigureCommand(program: Command) {
|
||||
[] as string[],
|
||||
)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const sections: string[] = Array.isArray(opts.section)
|
||||
? opts.section
|
||||
.map((value: unknown) => (typeof value === "string" ? value.trim() : ""))
|
||||
@@ -45,9 +46,6 @@ export function registerConfigureCommand(program: Command) {
|
||||
}
|
||||
|
||||
await configureCommandWithSections(sections as never, defaultRuntime);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { uninstallCommand } from "../../commands/uninstall.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
||||
export function registerMaintenanceCommands(program: Command) {
|
||||
program
|
||||
@@ -24,7 +25,7 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
.option("--generate-gateway-token", "Generate and configure a gateway token", false)
|
||||
.option("--deep", "Scan system services for extra gateway installs", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await doctorCommand(defaultRuntime, {
|
||||
workspaceSuggestions: opts.workspaceSuggestions,
|
||||
yes: Boolean(opts.yes),
|
||||
@@ -34,10 +35,7 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
generateGatewayToken: Boolean(opts.generateGatewayToken),
|
||||
deep: Boolean(opts.deep),
|
||||
});
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
@@ -50,14 +48,11 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
)
|
||||
.option("--no-open", "Print URL but do not launch a browser", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await dashboardCommand(defaultRuntime, {
|
||||
noOpen: Boolean(opts.noOpen),
|
||||
});
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
@@ -73,17 +68,14 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
.option("--non-interactive", "Disable prompts (requires --scope + --yes)", false)
|
||||
.option("--dry-run", "Print actions without removing files", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await resetCommand(defaultRuntime, {
|
||||
scope: opts.scope,
|
||||
yes: Boolean(opts.yes),
|
||||
nonInteractive: Boolean(opts.nonInteractive),
|
||||
dryRun: Boolean(opts.dryRun),
|
||||
});
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
@@ -103,7 +95,7 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
.option("--non-interactive", "Disable prompts (requires --yes)", false)
|
||||
.option("--dry-run", "Print actions without removing files", false)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await uninstallCommand(defaultRuntime, {
|
||||
service: Boolean(opts.service),
|
||||
state: Boolean(opts.state),
|
||||
@@ -114,9 +106,6 @@ export function registerMaintenanceCommands(program: Command) {
|
||||
nonInteractive: Boolean(opts.nonInteractive),
|
||||
dryRun: Boolean(opts.dryRun),
|
||||
});
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
||||
function resolveInstallDaemonFlag(
|
||||
command: unknown,
|
||||
@@ -94,7 +95,7 @@ export function registerOnboardCommand(program: Command) {
|
||||
.option("--node-manager <name>", "Node manager for skills: npm|pnpm|bun")
|
||||
.option("--json", "Output JSON summary", false)
|
||||
.action(async (opts, command) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const installDaemon = resolveInstallDaemonFlag(command, {
|
||||
installDaemon: Boolean(opts.installDaemon),
|
||||
});
|
||||
@@ -147,9 +148,6 @@ export function registerOnboardCommand(program: Command) {
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { hasExplicitOptions } from "../command-options.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
|
||||
export function registerSetupCommand(program: Command) {
|
||||
program
|
||||
@@ -25,7 +26,7 @@ export function registerSetupCommand(program: Command) {
|
||||
.option("--remote-url <url>", "Remote Gateway WebSocket URL")
|
||||
.option("--remote-token <token>", "Remote Gateway token (optional)")
|
||||
.action(async (opts, command) => {
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
const hasWizardFlags = hasExplicitOptions(command, [
|
||||
"wizard",
|
||||
"nonInteractive",
|
||||
@@ -47,9 +48,6 @@ export function registerSetupCommand(program: Command) {
|
||||
return;
|
||||
}
|
||||
await setupCommand({ workspace: opts.workspace as string | undefined }, defaultRuntime);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,8 +6,22 @@ import { setVerbose } from "../../globals.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { formatDocsLink } from "../../terminal/links.js";
|
||||
import { theme } from "../../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "../cli-utils.js";
|
||||
import { parsePositiveIntOrUndefined } from "./helpers.js";
|
||||
import { ensureConfigReady } from "./config-guard.js";
|
||||
|
||||
function resolveVerbose(opts: { verbose?: boolean; debug?: boolean }): boolean {
|
||||
return Boolean(opts.verbose || opts.debug);
|
||||
}
|
||||
|
||||
function parseTimeoutMs(timeout: unknown): number | null | undefined {
|
||||
const parsed = parsePositiveIntOrUndefined(timeout);
|
||||
if (timeout !== undefined && parsed === undefined) {
|
||||
defaultRuntime.error("--timeout must be a positive integer (milliseconds)");
|
||||
defaultRuntime.exit(1);
|
||||
return null;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function registerStatusHealthSessionsCommands(program: Command) {
|
||||
program
|
||||
@@ -38,16 +52,13 @@ Examples:
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/status", "docs.clawd.bot/cli/status")}\n`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: false });
|
||||
const verbose = Boolean(opts.verbose || opts.debug);
|
||||
const verbose = resolveVerbose(opts);
|
||||
setVerbose(verbose);
|
||||
const timeout = parsePositiveIntOrUndefined(opts.timeout);
|
||||
if (opts.timeout !== undefined && timeout === undefined) {
|
||||
defaultRuntime.error("--timeout must be a positive integer (milliseconds)");
|
||||
defaultRuntime.exit(1);
|
||||
const timeout = parseTimeoutMs(opts.timeout);
|
||||
if (timeout === null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await statusCommand(
|
||||
{
|
||||
json: Boolean(opts.json),
|
||||
@@ -59,10 +70,7 @@ Examples:
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
@@ -78,16 +86,13 @@ Examples:
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/health", "docs.clawd.bot/cli/health")}\n`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: false });
|
||||
const verbose = Boolean(opts.verbose || opts.debug);
|
||||
const verbose = resolveVerbose(opts);
|
||||
setVerbose(verbose);
|
||||
const timeout = parsePositiveIntOrUndefined(opts.timeout);
|
||||
if (opts.timeout !== undefined && timeout === undefined) {
|
||||
defaultRuntime.error("--timeout must be a positive integer (milliseconds)");
|
||||
defaultRuntime.exit(1);
|
||||
const timeout = parseTimeoutMs(opts.timeout);
|
||||
if (timeout === null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await runCommandWithRuntime(defaultRuntime, async () => {
|
||||
await healthCommand(
|
||||
{
|
||||
json: Boolean(opts.json),
|
||||
@@ -96,10 +101,7 @@ Examples:
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
program
|
||||
@@ -126,7 +128,6 @@ Shows token usage per session when the agent reports it; set agents.defaults.con
|
||||
`\n${theme.muted("Docs:")} ${formatDocsLink("/cli/sessions", "docs.clawd.bot/cli/sessions")}\n`,
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await ensureConfigReady({ runtime: defaultRuntime, migrateState: false });
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
await sessionsCommand(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user