mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 10:00:44 +00:00
fix: route tasks json through lean cli path
This commit is contained in:
@@ -261,3 +261,69 @@ export function parseChannelsStatusRouteArgs(argv: string[]) {
|
||||
timeout: timeout.value,
|
||||
};
|
||||
}
|
||||
|
||||
function parseTasksListRouteArgsForCommandPath(argv: string[], commandPath: string[]) {
|
||||
if (!hasFlag(argv, "--json")) {
|
||||
return null;
|
||||
}
|
||||
const positionals = getCommandPositionalsWithRootOptions(argv, {
|
||||
commandPath,
|
||||
booleanFlags: ["--json"],
|
||||
valueFlags: ["--runtime", "--status"],
|
||||
});
|
||||
if (!positionals || positionals.length !== 0) {
|
||||
return null;
|
||||
}
|
||||
const runtime = parseOptionalFlagValue(argv, "--runtime");
|
||||
if (!runtime.ok) {
|
||||
return null;
|
||||
}
|
||||
const status = parseOptionalFlagValue(argv, "--status");
|
||||
if (!status.ok) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: true as const,
|
||||
runtime: runtime.value,
|
||||
status: status.value,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseTasksListRouteArgs(argv: string[]) {
|
||||
return (
|
||||
parseTasksListRouteArgsForCommandPath(argv, ["tasks"]) ??
|
||||
parseTasksListRouteArgsForCommandPath(argv, ["tasks", "list"])
|
||||
);
|
||||
}
|
||||
|
||||
export function parseTasksAuditRouteArgs(argv: string[]) {
|
||||
if (!hasFlag(argv, "--json")) {
|
||||
return null;
|
||||
}
|
||||
const positionals = getCommandPositionalsWithRootOptions(argv, {
|
||||
commandPath: ["tasks", "audit"],
|
||||
booleanFlags: ["--json"],
|
||||
valueFlags: ["--severity", "--code", "--limit"],
|
||||
});
|
||||
if (!positionals || positionals.length !== 0) {
|
||||
return null;
|
||||
}
|
||||
const severity = parseOptionalFlagValue(argv, "--severity");
|
||||
if (!severity.ok) {
|
||||
return null;
|
||||
}
|
||||
const code = parseOptionalFlagValue(argv, "--code");
|
||||
if (!code.ok) {
|
||||
return null;
|
||||
}
|
||||
const limit = getPositiveIntFlagValue(argv, "--limit");
|
||||
if (limit === null) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
json: true as const,
|
||||
severity: severity.value,
|
||||
code: code.value,
|
||||
limit,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
|
||||
export type RouteSpec = {
|
||||
matches: (path: string[]) => boolean;
|
||||
canRun?: (argv: string[]) => boolean;
|
||||
loadPlugins?: boolean | ((argv: string[]) => boolean);
|
||||
run: (argv: string[]) => Promise<boolean>;
|
||||
};
|
||||
@@ -27,6 +28,7 @@ function createParsedRoute(params: {
|
||||
return {
|
||||
matches: (path) =>
|
||||
matchesCommandPath(path, params.entry.commandPath, { exact: params.entry.exact }),
|
||||
canRun: (argv) => Boolean(params.definition.parseArgs(argv)),
|
||||
loadPlugins: params.entry.route?.preloadPlugins
|
||||
? createCommandLoadPlugins(params.entry.commandPath)
|
||||
: undefined,
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
parseModelsStatusRouteArgs,
|
||||
parseSessionsRouteArgs,
|
||||
parseStatusRouteArgs,
|
||||
parseTasksAuditRouteArgs,
|
||||
parseTasksListRouteArgs,
|
||||
} from "./route-args.js";
|
||||
|
||||
type RouteArgParser<TArgs> = (argv: string[]) => TArgs | null;
|
||||
@@ -132,6 +134,20 @@ export const routedCommandDefinitions = {
|
||||
await modelsStatusCommand(args, defaultRuntime);
|
||||
},
|
||||
}),
|
||||
"tasks-list": defineRoutedCommand({
|
||||
parseArgs: parseTasksListRouteArgs,
|
||||
runParsedArgs: async (args) => {
|
||||
const { tasksListJsonCommand } = await import("../../commands/tasks-json.js");
|
||||
await tasksListJsonCommand(args, defaultRuntime);
|
||||
},
|
||||
}),
|
||||
"tasks-audit": defineRoutedCommand({
|
||||
parseArgs: parseTasksAuditRouteArgs,
|
||||
runParsedArgs: async (args) => {
|
||||
const { tasksAuditJsonCommand } = await import("../../commands/tasks-json.js");
|
||||
await tasksAuditJsonCommand(args, defaultRuntime);
|
||||
},
|
||||
}),
|
||||
"channels-list": defineRoutedCommand({
|
||||
parseArgs: parseChannelsListRouteArgs,
|
||||
runParsedArgs: async (args) => {
|
||||
|
||||
@@ -7,6 +7,8 @@ const modelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const modelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const runDaemonStatusMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const statusJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const tasksListJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const tasksAuditJsonCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const channelsListCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const channelsStatusCommandMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
|
||||
@@ -38,6 +40,15 @@ vi.mock("../../commands/status-json.js", () => ({
|
||||
statusJsonCommand: statusJsonCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/tasks-json.js", () => ({
|
||||
tasksListJsonCommand: tasksListJsonCommandMock,
|
||||
tasksAuditJsonCommand: tasksAuditJsonCommandMock,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/tasks.js", () => {
|
||||
throw new Error("routed task JSON commands must not import the full tasks command module");
|
||||
});
|
||||
|
||||
vi.mock("../../commands/channels/list.js", () => ({
|
||||
channelsListCommand: channelsListCommandMock,
|
||||
}));
|
||||
@@ -376,4 +387,117 @@ describe("program routes", () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("routes tasks list JSON through the lean task JSON command", async () => {
|
||||
const rootRoute = expectRoute(["tasks"]);
|
||||
expect(rootRoute?.loadPlugins).toBeUndefined();
|
||||
expect(rootRoute?.canRun?.(["node", "openclaw", "tasks"])).toBe(false);
|
||||
await expect(
|
||||
rootRoute?.run([
|
||||
"node",
|
||||
"openclaw",
|
||||
"tasks",
|
||||
"--json",
|
||||
"--runtime",
|
||||
"cli",
|
||||
"--status=running",
|
||||
]),
|
||||
).resolves.toBe(true);
|
||||
expect(tasksListJsonCommandMock).toHaveBeenCalledWith(
|
||||
{ json: true, runtime: "cli", status: "running" },
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const listRoute = expectRoute(["tasks", "list"]);
|
||||
expect(listRoute?.loadPlugins).toBeUndefined();
|
||||
await expect(
|
||||
listRoute?.run(["node", "openclaw", "tasks", "list", "--json", "--runtime=cron"]),
|
||||
).resolves.toBe(true);
|
||||
expect(tasksListJsonCommandMock).toHaveBeenLastCalledWith(
|
||||
{ json: true, runtime: "cron", status: undefined },
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("routes parent task filter values that command-path discovery sees as positionals", async () => {
|
||||
const separateValueArgv = [
|
||||
"node",
|
||||
"openclaw",
|
||||
"tasks",
|
||||
"--json",
|
||||
"--runtime",
|
||||
"cli",
|
||||
"--status",
|
||||
"running",
|
||||
];
|
||||
const separateValueRoute = findRoutedCommand(["tasks", "cli"], separateValueArgv);
|
||||
expect(separateValueRoute).not.toBeNull();
|
||||
await expect(separateValueRoute?.run(separateValueArgv)).resolves.toBe(true);
|
||||
expect(tasksListJsonCommandMock).toHaveBeenCalledWith(
|
||||
{ json: true, runtime: "cli", status: "running" },
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const parentOptionBeforeSubcommandArgv = [
|
||||
"node",
|
||||
"openclaw",
|
||||
"tasks",
|
||||
"--runtime",
|
||||
"cli",
|
||||
"list",
|
||||
"--json",
|
||||
];
|
||||
const parentOptionBeforeSubcommandRoute = findRoutedCommand(
|
||||
["tasks", "cli"],
|
||||
parentOptionBeforeSubcommandArgv,
|
||||
);
|
||||
expect(parentOptionBeforeSubcommandRoute).not.toBeNull();
|
||||
await expect(
|
||||
parentOptionBeforeSubcommandRoute?.run(parentOptionBeforeSubcommandArgv),
|
||||
).resolves.toBe(true);
|
||||
expect(tasksListJsonCommandMock).toHaveBeenLastCalledWith(
|
||||
{ json: true, runtime: "cli", status: undefined },
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("routes tasks audit JSON through the lean task JSON command", async () => {
|
||||
const route = expectRoute(["tasks", "audit"]);
|
||||
expect(route?.loadPlugins).toBeUndefined();
|
||||
expect(route?.canRun?.(["node", "openclaw", "tasks", "audit"])).toBe(false);
|
||||
await expect(
|
||||
route?.run([
|
||||
"node",
|
||||
"openclaw",
|
||||
"tasks",
|
||||
"audit",
|
||||
"--json",
|
||||
"--severity",
|
||||
"error",
|
||||
"--code=stale_running",
|
||||
"--limit",
|
||||
"5",
|
||||
]),
|
||||
).resolves.toBe(true);
|
||||
expect(tasksAuditJsonCommandMock).toHaveBeenCalledWith(
|
||||
{ json: true, severity: "error", code: "stale_running", limit: 5 },
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for task JSON routes when option values are missing or unknown", async () => {
|
||||
await expectRunFalse(["tasks"], ["node", "openclaw", "tasks", "--json", "--runtime"]);
|
||||
await expectRunFalse(["tasks", "list"], ["node", "openclaw", "tasks", "list"]);
|
||||
await expectRunFalse(
|
||||
["tasks", "audit"],
|
||||
["node", "openclaw", "tasks", "audit", "--json", "--limit"],
|
||||
);
|
||||
await expectRunFalse(
|
||||
["tasks", "audit"],
|
||||
["node", "openclaw", "tasks", "audit", "--json", "--unknown"],
|
||||
);
|
||||
expect(
|
||||
findRoutedCommand(["tasks", "cli"], ["node", "openclaw", "tasks", "--runtime", "cli"]),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,9 +2,12 @@ import { routedCommands, type RouteSpec } from "./route-specs.js";
|
||||
|
||||
export type { RouteSpec } from "./route-specs.js";
|
||||
|
||||
export function findRoutedCommand(path: string[]): RouteSpec | null {
|
||||
export function findRoutedCommand(path: string[], argv?: string[]): RouteSpec | null {
|
||||
for (const route of routedCommands) {
|
||||
if (route.matches(path)) {
|
||||
if (argv && route.canRun && !route.canRun(argv)) {
|
||||
continue;
|
||||
}
|
||||
return route;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user