mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-13 18:21:27 +00:00
refactor: share cli startup and routing helpers
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import type { Command } from "commander";
|
||||
import { setVerbose } from "../../globals.js";
|
||||
import { isTruthyEnvValue } from "../../infra/env.js";
|
||||
import { routeLogsToStderr } from "../../logging/console.js";
|
||||
import type { LogLevel } from "../../logging/levels.js";
|
||||
import { loggingState } from "../../logging/state.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { getCommandPathWithRootOptions, getVerboseFlag, hasHelpOrVersion } from "../argv.js";
|
||||
import { emitCliBanner } from "../banner.js";
|
||||
import { getVerboseFlag, hasHelpOrVersion } from "../argv.js";
|
||||
import { resolveCliName } from "../cli-name.js";
|
||||
import {
|
||||
applyCliExecutionStartupPresentation,
|
||||
ensureCliExecutionBootstrap,
|
||||
resolveCliExecutionStartupContext,
|
||||
} from "../command-execution-startup.js";
|
||||
import { shouldBypassConfigGuardForCommandPath } from "../command-startup-policy.js";
|
||||
import {
|
||||
resolvePluginInstallInvalidConfigPolicy,
|
||||
resolvePluginInstallPreactionRequest,
|
||||
@@ -27,63 +29,6 @@ function setProcessTitleForCommand(actionCommand: Command) {
|
||||
process.title = `${cliName}-${name}`;
|
||||
}
|
||||
|
||||
// Commands that need plugins loaded before execution.
|
||||
const PLUGIN_REQUIRED_COMMANDS = new Set([
|
||||
"agent",
|
||||
"message",
|
||||
"channels",
|
||||
"directory",
|
||||
"agents",
|
||||
"configure",
|
||||
"status",
|
||||
"health",
|
||||
]);
|
||||
const CONFIG_GUARD_BYPASS_COMMANDS = new Set(["backup", "doctor", "completion", "secrets"]);
|
||||
let configGuardModulePromise: Promise<typeof import("./config-guard.js")> | undefined;
|
||||
let pluginRegistryModulePromise: Promise<typeof import("../plugin-registry.js")> | undefined;
|
||||
|
||||
function shouldBypassConfigGuard(commandPath: string[]): boolean {
|
||||
const [primary, secondary] = commandPath;
|
||||
if (!primary) {
|
||||
return false;
|
||||
}
|
||||
if (CONFIG_GUARD_BYPASS_COMMANDS.has(primary)) {
|
||||
return true;
|
||||
}
|
||||
if (primary === "config" && (secondary === "validate" || secondary === "schema")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadConfigGuardModule() {
|
||||
configGuardModulePromise ??= import("./config-guard.js");
|
||||
return configGuardModulePromise;
|
||||
}
|
||||
|
||||
function loadPluginRegistryModule() {
|
||||
pluginRegistryModulePromise ??= import("../plugin-registry.js");
|
||||
return pluginRegistryModulePromise;
|
||||
}
|
||||
|
||||
function resolvePluginRegistryScope(commandPath: string[]): "channels" | "all" {
|
||||
return commandPath[0] === "status" || commandPath[0] === "health" ? "channels" : "all";
|
||||
}
|
||||
|
||||
function shouldLoadPluginsForCommand(commandPath: string[], jsonOutputMode: boolean): boolean {
|
||||
const [primary, secondary] = commandPath;
|
||||
if (!primary || !PLUGIN_REQUIRED_COMMANDS.has(primary)) {
|
||||
return false;
|
||||
}
|
||||
if ((primary === "status" || primary === "health") && jsonOutputMode) {
|
||||
return false;
|
||||
}
|
||||
// Setup wizard and channels add should stay manifest-first and load selected plugins on demand.
|
||||
if (primary === "onboard" || (primary === "channels" && secondary === "add")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function shouldAllowInvalidConfigForAction(actionCommand: Command, commandPath: string[]): boolean {
|
||||
return (
|
||||
resolvePluginInstallInvalidConfigPolicy(
|
||||
@@ -123,19 +68,16 @@ export function registerPreActionHooks(program: Command, programVersion: string)
|
||||
if (hasHelpOrVersion(argv)) {
|
||||
return;
|
||||
}
|
||||
const commandPath = getCommandPathWithRootOptions(argv, 2);
|
||||
const jsonOutputMode = isCommandJsonOutputMode(actionCommand, argv);
|
||||
if (jsonOutputMode) {
|
||||
routeLogsToStderr();
|
||||
}
|
||||
const hideBanner =
|
||||
isTruthyEnvValue(process.env.OPENCLAW_HIDE_BANNER) ||
|
||||
commandPath[0] === "update" ||
|
||||
commandPath[0] === "completion" ||
|
||||
(commandPath[0] === "plugins" && commandPath[1] === "update");
|
||||
if (!hideBanner) {
|
||||
emitCliBanner(programVersion);
|
||||
}
|
||||
const { commandPath, startupPolicy } = resolveCliExecutionStartupContext({
|
||||
argv,
|
||||
jsonOutputMode,
|
||||
env: process.env,
|
||||
});
|
||||
await applyCliExecutionStartupPresentation({
|
||||
startupPolicy,
|
||||
version: programVersion,
|
||||
});
|
||||
const verbose = getVerboseFlag(argv, { includeDebug: true });
|
||||
setVerbose(verbose);
|
||||
const cliLogLevel = getCliLogLevel(actionCommand);
|
||||
@@ -145,31 +87,14 @@ export function registerPreActionHooks(program: Command, programVersion: string)
|
||||
if (!verbose) {
|
||||
process.env.NODE_NO_WARNINGS ??= "1";
|
||||
}
|
||||
if (shouldBypassConfigGuard(commandPath)) {
|
||||
if (shouldBypassConfigGuardForCommandPath(commandPath)) {
|
||||
return;
|
||||
}
|
||||
const allowInvalid = shouldAllowInvalidConfigForAction(actionCommand, commandPath);
|
||||
const { ensureConfigReady } = await loadConfigGuardModule();
|
||||
await ensureConfigReady({
|
||||
await ensureCliExecutionBootstrap({
|
||||
runtime: defaultRuntime,
|
||||
commandPath,
|
||||
...(allowInvalid ? { allowInvalid: true } : {}),
|
||||
...(jsonOutputMode ? { suppressDoctorStdout: true } : {}),
|
||||
startupPolicy,
|
||||
allowInvalid: shouldAllowInvalidConfigForAction(actionCommand, commandPath),
|
||||
});
|
||||
// Load plugins for commands that need channel access.
|
||||
// When --json output is active, temporarily route logs to stderr so plugin
|
||||
// registration messages don't corrupt the JSON payload on stdout.
|
||||
if (shouldLoadPluginsForCommand(commandPath, jsonOutputMode)) {
|
||||
const { ensurePluginRegistryLoaded } = await loadPluginRegistryModule();
|
||||
const prev = loggingState.forceConsoleToStderr;
|
||||
if (jsonOutputMode) {
|
||||
loggingState.forceConsoleToStderr = true;
|
||||
}
|
||||
try {
|
||||
ensurePluginRegistryLoaded({ scope: resolvePluginRegistryScope(commandPath) });
|
||||
} finally {
|
||||
loggingState.forceConsoleToStderr = prev;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
210
src/cli/program/route-args.test.ts
Normal file
210
src/cli/program/route-args.test.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
parseAgentsListRouteArgs,
|
||||
parseConfigGetRouteArgs,
|
||||
parseConfigUnsetRouteArgs,
|
||||
parseGatewayStatusRouteArgs,
|
||||
parseHealthRouteArgs,
|
||||
parseModelsListRouteArgs,
|
||||
parseModelsStatusRouteArgs,
|
||||
parseSessionsRouteArgs,
|
||||
parseStatusRouteArgs,
|
||||
} from "./route-args.js";
|
||||
|
||||
describe("route-args", () => {
|
||||
it("parses health and status route args", () => {
|
||||
expect(
|
||||
parseHealthRouteArgs(["node", "openclaw", "health", "--json", "--timeout", "5000"]),
|
||||
).toEqual({
|
||||
json: true,
|
||||
verbose: false,
|
||||
timeoutMs: 5000,
|
||||
});
|
||||
expect(
|
||||
parseStatusRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"status",
|
||||
"--json",
|
||||
"--deep",
|
||||
"--all",
|
||||
"--usage",
|
||||
"--timeout",
|
||||
"5000",
|
||||
]),
|
||||
).toEqual({
|
||||
json: true,
|
||||
deep: true,
|
||||
all: true,
|
||||
usage: true,
|
||||
verbose: false,
|
||||
timeoutMs: 5000,
|
||||
});
|
||||
expect(parseStatusRouteArgs(["node", "openclaw", "status", "--timeout"])).toBeNull();
|
||||
});
|
||||
|
||||
it("parses gateway status route args and rejects probe-only ssh flags", () => {
|
||||
expect(
|
||||
parseGatewayStatusRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"gateway",
|
||||
"status",
|
||||
"--url",
|
||||
"ws://127.0.0.1:18789",
|
||||
"--token",
|
||||
"abc",
|
||||
"--password",
|
||||
"def",
|
||||
"--timeout",
|
||||
"5000",
|
||||
"--deep",
|
||||
"--require-rpc",
|
||||
"--json",
|
||||
]),
|
||||
).toEqual({
|
||||
rpc: {
|
||||
url: "ws://127.0.0.1:18789",
|
||||
token: "abc",
|
||||
password: "def",
|
||||
timeout: "5000",
|
||||
},
|
||||
probe: true,
|
||||
requireRpc: true,
|
||||
deep: true,
|
||||
json: true,
|
||||
});
|
||||
expect(
|
||||
parseGatewayStatusRouteArgs(["node", "openclaw", "gateway", "status", "--ssh", "host"]),
|
||||
).toBeNull();
|
||||
expect(
|
||||
parseGatewayStatusRouteArgs(["node", "openclaw", "gateway", "status", "--ssh-auto"]),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("parses sessions and agents list route args", () => {
|
||||
expect(
|
||||
parseSessionsRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"sessions",
|
||||
"--json",
|
||||
"--all-agents",
|
||||
"--agent",
|
||||
"default",
|
||||
"--store",
|
||||
"sqlite",
|
||||
"--active",
|
||||
"true",
|
||||
]),
|
||||
).toEqual({
|
||||
json: true,
|
||||
allAgents: true,
|
||||
agent: "default",
|
||||
store: "sqlite",
|
||||
active: "true",
|
||||
});
|
||||
expect(parseSessionsRouteArgs(["node", "openclaw", "sessions", "--agent"])).toBeNull();
|
||||
expect(
|
||||
parseAgentsListRouteArgs(["node", "openclaw", "agents", "list", "--json", "--bindings"]),
|
||||
).toEqual({
|
||||
json: true,
|
||||
bindings: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("parses config routes", () => {
|
||||
expect(
|
||||
parseConfigGetRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"--log-level",
|
||||
"debug",
|
||||
"config",
|
||||
"get",
|
||||
"update.channel",
|
||||
"--json",
|
||||
]),
|
||||
).toEqual({
|
||||
path: "update.channel",
|
||||
json: true,
|
||||
});
|
||||
expect(
|
||||
parseConfigUnsetRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"config",
|
||||
"unset",
|
||||
"--profile",
|
||||
"work",
|
||||
"update.channel",
|
||||
]),
|
||||
).toEqual({
|
||||
path: "update.channel",
|
||||
});
|
||||
expect(parseConfigGetRouteArgs(["node", "openclaw", "config", "get", "--json"])).toBeNull();
|
||||
});
|
||||
|
||||
it("parses models list and models status route args", () => {
|
||||
expect(
|
||||
parseModelsListRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"models",
|
||||
"list",
|
||||
"--provider",
|
||||
"openai",
|
||||
"--all",
|
||||
"--local",
|
||||
"--json",
|
||||
"--plain",
|
||||
]),
|
||||
).toEqual({
|
||||
provider: "openai",
|
||||
all: true,
|
||||
local: true,
|
||||
json: true,
|
||||
plain: true,
|
||||
});
|
||||
expect(
|
||||
parseModelsStatusRouteArgs([
|
||||
"node",
|
||||
"openclaw",
|
||||
"models",
|
||||
"status",
|
||||
"--probe-provider",
|
||||
"openai",
|
||||
"--probe-timeout",
|
||||
"5000",
|
||||
"--probe-concurrency",
|
||||
"2",
|
||||
"--probe-max-tokens",
|
||||
"64",
|
||||
"--probe-profile",
|
||||
"fast",
|
||||
"--probe-profile",
|
||||
"safe",
|
||||
"--agent",
|
||||
"default",
|
||||
"--json",
|
||||
"--plain",
|
||||
"--check",
|
||||
"--probe",
|
||||
]),
|
||||
).toEqual({
|
||||
probeProvider: "openai",
|
||||
probeTimeout: "5000",
|
||||
probeConcurrency: "2",
|
||||
probeMaxTokens: "64",
|
||||
probeProfile: ["fast", "safe"],
|
||||
agent: "default",
|
||||
json: true,
|
||||
plain: true,
|
||||
check: true,
|
||||
probe: true,
|
||||
});
|
||||
expect(
|
||||
parseModelsStatusRouteArgs(["node", "openclaw", "models", "status", "--probe-profile"]),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
244
src/cli/program/route-args.ts
Normal file
244
src/cli/program/route-args.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { isValueToken } from "../../infra/cli-root-options.js";
|
||||
import {
|
||||
getCommandPositionalsWithRootOptions,
|
||||
getFlagValue,
|
||||
getPositiveIntFlagValue,
|
||||
getVerboseFlag,
|
||||
hasFlag,
|
||||
} from "../argv.js";
|
||||
|
||||
type OptionalFlagParse = {
|
||||
ok: boolean;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
function parseOptionalFlagValue(argv: string[], name: string): OptionalFlagParse {
|
||||
const value = getFlagValue(argv, name);
|
||||
if (value === null) {
|
||||
return { ok: false };
|
||||
}
|
||||
return { ok: true, value };
|
||||
}
|
||||
|
||||
function parseRepeatedFlagValues(argv: string[], name: string): string[] | null {
|
||||
const values: string[] = [];
|
||||
const args = argv.slice(2);
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (!arg || arg === "--") {
|
||||
break;
|
||||
}
|
||||
if (arg === name) {
|
||||
const next = args[i + 1];
|
||||
if (!isValueToken(next)) {
|
||||
return null;
|
||||
}
|
||||
values.push(next);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith(`${name}=`)) {
|
||||
const value = arg.slice(name.length + 1).trim();
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function parseSinglePositional(
|
||||
argv: string[],
|
||||
params: {
|
||||
commandPath: string[];
|
||||
booleanFlags?: string[];
|
||||
},
|
||||
): string | null {
|
||||
const positionals = getCommandPositionalsWithRootOptions(argv, params);
|
||||
if (!positionals || positionals.length !== 1) {
|
||||
return null;
|
||||
}
|
||||
return positionals[0] ?? null;
|
||||
}
|
||||
|
||||
export function parseHealthRouteArgs(argv: string[]) {
|
||||
const timeoutMs = getPositiveIntFlagValue(argv, "--timeout");
|
||||
if (timeoutMs === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: hasFlag(argv, "--json"),
|
||||
verbose: getVerboseFlag(argv, { includeDebug: true }),
|
||||
timeoutMs,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseStatusRouteArgs(argv: string[]) {
|
||||
const timeoutMs = getPositiveIntFlagValue(argv, "--timeout");
|
||||
if (timeoutMs === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: hasFlag(argv, "--json"),
|
||||
deep: hasFlag(argv, "--deep"),
|
||||
all: hasFlag(argv, "--all"),
|
||||
usage: hasFlag(argv, "--usage"),
|
||||
verbose: getVerboseFlag(argv, { includeDebug: true }),
|
||||
timeoutMs,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseGatewayStatusRouteArgs(argv: string[]) {
|
||||
const url = parseOptionalFlagValue(argv, "--url");
|
||||
if (!url.ok) {
|
||||
return null;
|
||||
}
|
||||
const token = parseOptionalFlagValue(argv, "--token");
|
||||
if (!token.ok) {
|
||||
return null;
|
||||
}
|
||||
const password = parseOptionalFlagValue(argv, "--password");
|
||||
if (!password.ok) {
|
||||
return null;
|
||||
}
|
||||
const timeout = parseOptionalFlagValue(argv, "--timeout");
|
||||
if (!timeout.ok) {
|
||||
return null;
|
||||
}
|
||||
const ssh = parseOptionalFlagValue(argv, "--ssh");
|
||||
if (!ssh.ok || ssh.value !== undefined) {
|
||||
return null;
|
||||
}
|
||||
const sshIdentity = parseOptionalFlagValue(argv, "--ssh-identity");
|
||||
if (!sshIdentity.ok || sshIdentity.value !== undefined) {
|
||||
return null;
|
||||
}
|
||||
if (hasFlag(argv, "--ssh-auto")) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
rpc: {
|
||||
url: url.value,
|
||||
token: token.value,
|
||||
password: password.value,
|
||||
timeout: timeout.value,
|
||||
},
|
||||
deep: hasFlag(argv, "--deep"),
|
||||
json: hasFlag(argv, "--json"),
|
||||
requireRpc: hasFlag(argv, "--require-rpc"),
|
||||
probe: !hasFlag(argv, "--no-probe"),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseSessionsRouteArgs(argv: string[]) {
|
||||
const agent = parseOptionalFlagValue(argv, "--agent");
|
||||
if (!agent.ok) {
|
||||
return null;
|
||||
}
|
||||
const store = parseOptionalFlagValue(argv, "--store");
|
||||
if (!store.ok) {
|
||||
return null;
|
||||
}
|
||||
const active = parseOptionalFlagValue(argv, "--active");
|
||||
if (!active.ok) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: hasFlag(argv, "--json"),
|
||||
allAgents: hasFlag(argv, "--all-agents"),
|
||||
agent: agent.value,
|
||||
store: store.value,
|
||||
active: active.value,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseAgentsListRouteArgs(argv: string[]) {
|
||||
return {
|
||||
json: hasFlag(argv, "--json"),
|
||||
bindings: hasFlag(argv, "--bindings"),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseConfigGetRouteArgs(argv: string[]) {
|
||||
const path = parseSinglePositional(argv, {
|
||||
commandPath: ["config", "get"],
|
||||
booleanFlags: ["--json"],
|
||||
});
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
path,
|
||||
json: hasFlag(argv, "--json"),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseConfigUnsetRouteArgs(argv: string[]) {
|
||||
const path = parseSinglePositional(argv, {
|
||||
commandPath: ["config", "unset"],
|
||||
});
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
return { path };
|
||||
}
|
||||
|
||||
export function parseModelsListRouteArgs(argv: string[]) {
|
||||
const provider = parseOptionalFlagValue(argv, "--provider");
|
||||
if (!provider.ok) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: provider.value,
|
||||
all: hasFlag(argv, "--all"),
|
||||
local: hasFlag(argv, "--local"),
|
||||
json: hasFlag(argv, "--json"),
|
||||
plain: hasFlag(argv, "--plain"),
|
||||
};
|
||||
}
|
||||
|
||||
export function parseModelsStatusRouteArgs(argv: string[]) {
|
||||
const probeProvider = parseOptionalFlagValue(argv, "--probe-provider");
|
||||
if (!probeProvider.ok) {
|
||||
return null;
|
||||
}
|
||||
const probeTimeout = parseOptionalFlagValue(argv, "--probe-timeout");
|
||||
if (!probeTimeout.ok) {
|
||||
return null;
|
||||
}
|
||||
const probeConcurrency = parseOptionalFlagValue(argv, "--probe-concurrency");
|
||||
if (!probeConcurrency.ok) {
|
||||
return null;
|
||||
}
|
||||
const probeMaxTokens = parseOptionalFlagValue(argv, "--probe-max-tokens");
|
||||
if (!probeMaxTokens.ok) {
|
||||
return null;
|
||||
}
|
||||
const agent = parseOptionalFlagValue(argv, "--agent");
|
||||
if (!agent.ok) {
|
||||
return null;
|
||||
}
|
||||
const probeProfileValues = parseRepeatedFlagValues(argv, "--probe-profile");
|
||||
if (probeProfileValues === null) {
|
||||
return null;
|
||||
}
|
||||
const probeProfile =
|
||||
probeProfileValues.length === 0
|
||||
? undefined
|
||||
: probeProfileValues.length === 1
|
||||
? probeProfileValues[0]
|
||||
: probeProfileValues;
|
||||
return {
|
||||
probeProvider: probeProvider.value,
|
||||
probeTimeout: probeTimeout.value,
|
||||
probeConcurrency: probeConcurrency.value,
|
||||
probeMaxTokens: probeMaxTokens.value,
|
||||
agent: agent.value,
|
||||
probeProfile,
|
||||
json: hasFlag(argv, "--json"),
|
||||
plain: hasFlag(argv, "--plain"),
|
||||
check: hasFlag(argv, "--check"),
|
||||
probe: hasFlag(argv, "--probe"),
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
import { isValueToken } from "../../infra/cli-root-options.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { hasFlag } from "../argv.js";
|
||||
import { shouldLoadPluginsForCommandPath } from "../command-startup-policy.js";
|
||||
import {
|
||||
getCommandPositionalsWithRootOptions,
|
||||
getFlagValue,
|
||||
getPositiveIntFlagValue,
|
||||
getVerboseFlag,
|
||||
hasFlag,
|
||||
} from "../argv.js";
|
||||
parseAgentsListRouteArgs,
|
||||
parseConfigGetRouteArgs,
|
||||
parseConfigUnsetRouteArgs,
|
||||
parseGatewayStatusRouteArgs,
|
||||
parseHealthRouteArgs,
|
||||
parseModelsListRouteArgs,
|
||||
parseModelsStatusRouteArgs,
|
||||
parseSessionsRouteArgs,
|
||||
parseStatusRouteArgs,
|
||||
} from "./route-args.js";
|
||||
|
||||
export type RouteSpec = {
|
||||
match: (path: string[]) => boolean;
|
||||
@@ -18,16 +23,18 @@ const routeHealth: RouteSpec = {
|
||||
match: (path) => path[0] === "health",
|
||||
// `health --json` only relays gateway RPC output and does not need local plugin metadata.
|
||||
// Keep plugin preload for text output where channel diagnostics/logSelfId are rendered.
|
||||
loadPlugins: (argv) => !hasFlag(argv, "--json"),
|
||||
loadPlugins: (argv) =>
|
||||
shouldLoadPluginsForCommandPath({
|
||||
commandPath: ["health"],
|
||||
jsonOutputMode: hasFlag(argv, "--json"),
|
||||
}),
|
||||
run: async (argv) => {
|
||||
const json = hasFlag(argv, "--json");
|
||||
const verbose = getVerboseFlag(argv, { includeDebug: true });
|
||||
const timeoutMs = getPositiveIntFlagValue(argv, "--timeout");
|
||||
if (timeoutMs === null) {
|
||||
const args = parseHealthRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const { healthCommand } = await import("../../commands/health.js");
|
||||
await healthCommand({ json, timeoutMs, verbose }, defaultRuntime);
|
||||
await healthCommand(args, defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -36,24 +43,31 @@ const routeStatus: RouteSpec = {
|
||||
match: (path) => path[0] === "status",
|
||||
// `status --json` can defer channel plugin loading until config/env inspection
|
||||
// proves it is needed, which keeps the fast-path startup lightweight.
|
||||
loadPlugins: (argv) => !hasFlag(argv, "--json"),
|
||||
loadPlugins: (argv) =>
|
||||
shouldLoadPluginsForCommandPath({
|
||||
commandPath: ["status"],
|
||||
jsonOutputMode: hasFlag(argv, "--json"),
|
||||
}),
|
||||
run: async (argv) => {
|
||||
const json = hasFlag(argv, "--json");
|
||||
const deep = hasFlag(argv, "--deep");
|
||||
const all = hasFlag(argv, "--all");
|
||||
const usage = hasFlag(argv, "--usage");
|
||||
const verbose = getVerboseFlag(argv, { includeDebug: true });
|
||||
const timeoutMs = getPositiveIntFlagValue(argv, "--timeout");
|
||||
if (timeoutMs === null) {
|
||||
const args = parseStatusRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
if (json) {
|
||||
if (args.json) {
|
||||
const { statusJsonCommand } = await import("../../commands/status-json.js");
|
||||
await statusJsonCommand({ deep, all, usage, timeoutMs }, defaultRuntime);
|
||||
await statusJsonCommand(
|
||||
{
|
||||
deep: args.deep,
|
||||
all: args.all,
|
||||
usage: args.usage,
|
||||
timeoutMs: args.timeoutMs,
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
const { statusCommand } = await import("../../commands/status.js");
|
||||
await statusCommand({ json, deep, all, usage, timeoutMs, verbose }, defaultRuntime);
|
||||
await statusCommand(args, defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -61,56 +75,12 @@ const routeStatus: RouteSpec = {
|
||||
const routeGatewayStatus: RouteSpec = {
|
||||
match: (path) => path[0] === "gateway" && path[1] === "status",
|
||||
run: async (argv) => {
|
||||
const url = getFlagValue(argv, "--url");
|
||||
if (url === null) {
|
||||
const args = parseGatewayStatusRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const token = getFlagValue(argv, "--token");
|
||||
if (token === null) {
|
||||
return false;
|
||||
}
|
||||
const password = getFlagValue(argv, "--password");
|
||||
if (password === null) {
|
||||
return false;
|
||||
}
|
||||
const timeout = getFlagValue(argv, "--timeout");
|
||||
if (timeout === null) {
|
||||
return false;
|
||||
}
|
||||
const ssh = getFlagValue(argv, "--ssh");
|
||||
if (ssh === null) {
|
||||
return false;
|
||||
}
|
||||
if (ssh !== undefined) {
|
||||
return false;
|
||||
}
|
||||
const sshIdentity = getFlagValue(argv, "--ssh-identity");
|
||||
if (sshIdentity === null) {
|
||||
return false;
|
||||
}
|
||||
if (sshIdentity !== undefined) {
|
||||
return false;
|
||||
}
|
||||
if (hasFlag(argv, "--ssh-auto")) {
|
||||
return false;
|
||||
}
|
||||
const deep = hasFlag(argv, "--deep");
|
||||
const json = hasFlag(argv, "--json");
|
||||
const requireRpc = hasFlag(argv, "--require-rpc");
|
||||
const probe = !hasFlag(argv, "--no-probe");
|
||||
const { runDaemonStatus } = await import("../daemon-cli/status.js");
|
||||
await runDaemonStatus({
|
||||
rpc: {
|
||||
url: url ?? undefined,
|
||||
token: token ?? undefined,
|
||||
password: password ?? undefined,
|
||||
timeout: timeout ?? undefined,
|
||||
},
|
||||
probe,
|
||||
requireRpc,
|
||||
deep,
|
||||
json,
|
||||
});
|
||||
await runDaemonStatus(args);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -120,22 +90,12 @@ const routeSessions: RouteSpec = {
|
||||
// must fall through to Commander so nested handlers run.
|
||||
match: (path) => path[0] === "sessions" && !path[1],
|
||||
run: async (argv) => {
|
||||
const json = hasFlag(argv, "--json");
|
||||
const allAgents = hasFlag(argv, "--all-agents");
|
||||
const agent = getFlagValue(argv, "--agent");
|
||||
if (agent === null) {
|
||||
return false;
|
||||
}
|
||||
const store = getFlagValue(argv, "--store");
|
||||
if (store === null) {
|
||||
return false;
|
||||
}
|
||||
const active = getFlagValue(argv, "--active");
|
||||
if (active === null) {
|
||||
const args = parseSessionsRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const { sessionsCommand } = await import("../../commands/sessions.js");
|
||||
await sessionsCommand({ json, store, agent, allAgents, active }, defaultRuntime);
|
||||
await sessionsCommand(args, defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -143,59 +103,21 @@ const routeSessions: RouteSpec = {
|
||||
const routeAgentsList: RouteSpec = {
|
||||
match: (path) => path[0] === "agents" && path[1] === "list",
|
||||
run: async (argv) => {
|
||||
const json = hasFlag(argv, "--json");
|
||||
const bindings = hasFlag(argv, "--bindings");
|
||||
const { agentsListCommand } = await import("../../commands/agents.js");
|
||||
await agentsListCommand({ json, bindings }, defaultRuntime);
|
||||
await agentsListCommand(parseAgentsListRouteArgs(argv), defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
function getFlagValues(argv: string[], name: string): string[] | null {
|
||||
const values: string[] = [];
|
||||
const args = argv.slice(2);
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (!arg || arg === "--") {
|
||||
break;
|
||||
}
|
||||
if (arg === name) {
|
||||
const next = args[i + 1];
|
||||
if (!isValueToken(next)) {
|
||||
return null;
|
||||
}
|
||||
values.push(next);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith(`${name}=`)) {
|
||||
const value = arg.slice(name.length + 1).trim();
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
const routeConfigGet: RouteSpec = {
|
||||
match: (path) => path[0] === "config" && path[1] === "get",
|
||||
run: async (argv) => {
|
||||
const positionals = getCommandPositionalsWithRootOptions(argv, {
|
||||
commandPath: ["config", "get"],
|
||||
booleanFlags: ["--json"],
|
||||
});
|
||||
if (!positionals || positionals.length !== 1) {
|
||||
const args = parseConfigGetRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const pathArg = positionals[0];
|
||||
if (!pathArg) {
|
||||
return false;
|
||||
}
|
||||
const json = hasFlag(argv, "--json");
|
||||
const { runConfigGet } = await import("../config-cli.js");
|
||||
await runConfigGet({ path: pathArg, json });
|
||||
await runConfigGet(args);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -203,18 +125,12 @@ const routeConfigGet: RouteSpec = {
|
||||
const routeConfigUnset: RouteSpec = {
|
||||
match: (path) => path[0] === "config" && path[1] === "unset",
|
||||
run: async (argv) => {
|
||||
const positionals = getCommandPositionalsWithRootOptions(argv, {
|
||||
commandPath: ["config", "unset"],
|
||||
});
|
||||
if (!positionals || positionals.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const pathArg = positionals[0];
|
||||
if (!pathArg) {
|
||||
const args = parseConfigUnsetRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const { runConfigUnset } = await import("../config-cli.js");
|
||||
await runConfigUnset({ path: pathArg });
|
||||
await runConfigUnset(args);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -222,16 +138,12 @@ const routeConfigUnset: RouteSpec = {
|
||||
const routeModelsList: RouteSpec = {
|
||||
match: (path) => path[0] === "models" && path[1] === "list",
|
||||
run: async (argv) => {
|
||||
const provider = getFlagValue(argv, "--provider");
|
||||
if (provider === null) {
|
||||
const args = parseModelsListRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const all = hasFlag(argv, "--all");
|
||||
const local = hasFlag(argv, "--local");
|
||||
const json = hasFlag(argv, "--json");
|
||||
const plain = hasFlag(argv, "--plain");
|
||||
const { modelsListCommand } = await import("../../commands/models.js");
|
||||
await modelsListCommand({ all, local, provider, json, plain }, defaultRuntime);
|
||||
await modelsListCommand(args, defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -239,56 +151,12 @@ const routeModelsList: RouteSpec = {
|
||||
const routeModelsStatus: RouteSpec = {
|
||||
match: (path) => path[0] === "models" && path[1] === "status",
|
||||
run: async (argv) => {
|
||||
const probeProvider = getFlagValue(argv, "--probe-provider");
|
||||
if (probeProvider === null) {
|
||||
const args = parseModelsStatusRouteArgs(argv);
|
||||
if (!args) {
|
||||
return false;
|
||||
}
|
||||
const probeTimeout = getFlagValue(argv, "--probe-timeout");
|
||||
if (probeTimeout === null) {
|
||||
return false;
|
||||
}
|
||||
const probeConcurrency = getFlagValue(argv, "--probe-concurrency");
|
||||
if (probeConcurrency === null) {
|
||||
return false;
|
||||
}
|
||||
const probeMaxTokens = getFlagValue(argv, "--probe-max-tokens");
|
||||
if (probeMaxTokens === null) {
|
||||
return false;
|
||||
}
|
||||
const agent = getFlagValue(argv, "--agent");
|
||||
if (agent === null) {
|
||||
return false;
|
||||
}
|
||||
const probeProfileValues = getFlagValues(argv, "--probe-profile");
|
||||
if (probeProfileValues === null) {
|
||||
return false;
|
||||
}
|
||||
const probeProfile =
|
||||
probeProfileValues.length === 0
|
||||
? undefined
|
||||
: probeProfileValues.length === 1
|
||||
? probeProfileValues[0]
|
||||
: probeProfileValues;
|
||||
const json = hasFlag(argv, "--json");
|
||||
const plain = hasFlag(argv, "--plain");
|
||||
const check = hasFlag(argv, "--check");
|
||||
const probe = hasFlag(argv, "--probe");
|
||||
const { modelsStatusCommand } = await import("../../commands/models.js");
|
||||
await modelsStatusCommand(
|
||||
{
|
||||
json,
|
||||
plain,
|
||||
check,
|
||||
probe,
|
||||
probeProvider,
|
||||
probeProfile,
|
||||
probeTimeout,
|
||||
probeConcurrency,
|
||||
probeMaxTokens,
|
||||
agent,
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
await modelsStatusCommand(args, defaultRuntime);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user