mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-09 08:11:09 +00:00
CLI: fix root-option parsing for routed config paths
This commit is contained in:
@@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Gateway/Control UI basePath webhook passthrough: let non-read methods under configured `controlUiBasePath` fall through to plugin routes (instead of returning Control UI 405), restoring webhook handlers behind basePath mounts. (#32311) Thanks @ademczuk.
|
||||
- CLI/route-first config path parsing: ignore split root-option values when extracting routed `config get/unset` positional args so invocations like `openclaw --log-level debug config get <path>` and `--profile <name> config unset <path>` resolve the intended config key. (#32050) thanks @gumadeiras.
|
||||
- Voice-call/Twilio signature verification: retry signature validation across deterministic URL port variants (with/without port) to handle mixed Twilio signing behavior behind reverse proxies and non-standard ports. (#25140) Thanks @drvoss.
|
||||
- Hooks/webhook ACK compatibility: return `200` (instead of `202`) for successful `/hooks/agent` requests so providers that require `200` (for example Forward Email) accept dispatched agent hook deliveries. (#28204) Thanks @Glucksberg.
|
||||
- Voice-call/Twilio external outbound: auto-register webhook-first `outbound-api` calls (initiated outside OpenClaw) so media streams are accepted and call direction metadata stays accurate. (#31181) Thanks @scoootscooob.
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { findRoutedCommand } from "./routes.js";
|
||||
|
||||
const runConfigGetMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const runConfigUnsetMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
|
||||
vi.mock("../config-cli.js", () => ({
|
||||
runConfigGet: runConfigGetMock,
|
||||
runConfigUnset: runConfigUnsetMock,
|
||||
}));
|
||||
|
||||
describe("program routes", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
function expectRoute(path: string[]) {
|
||||
const route = findRoutedCommand(path);
|
||||
expect(route).not.toBeNull();
|
||||
@@ -58,6 +70,31 @@ describe("program routes", () => {
|
||||
await expectRunFalse(["config", "unset"], ["node", "openclaw", "config", "unset"]);
|
||||
});
|
||||
|
||||
it("passes config get path correctly when root option values precede command", async () => {
|
||||
const route = expectRoute(["config", "get"]);
|
||||
await expect(
|
||||
route?.run([
|
||||
"node",
|
||||
"openclaw",
|
||||
"--log-level",
|
||||
"debug",
|
||||
"config",
|
||||
"get",
|
||||
"update.channel",
|
||||
"--json",
|
||||
]),
|
||||
).resolves.toBe(true);
|
||||
expect(runConfigGetMock).toHaveBeenCalledWith({ path: "update.channel", json: true });
|
||||
});
|
||||
|
||||
it("passes config unset path correctly when root option values precede command", async () => {
|
||||
const route = expectRoute(["config", "unset"]);
|
||||
await expect(
|
||||
route?.run(["node", "openclaw", "--profile", "work", "config", "unset", "update.channel"]),
|
||||
).resolves.toBe(true);
|
||||
expect(runConfigUnsetMock).toHaveBeenCalledWith({ path: "update.channel" });
|
||||
});
|
||||
|
||||
it("returns false for memory status route when --agent value is missing", async () => {
|
||||
await expectRunFalse(["memory", "status"], ["node", "openclaw", "memory", "status", "--agent"]);
|
||||
});
|
||||
|
||||
@@ -99,16 +99,44 @@ const routeMemoryStatus: RouteSpec = {
|
||||
},
|
||||
};
|
||||
|
||||
function isValueToken(arg: string | undefined): boolean {
|
||||
if (!arg || arg === "--") {
|
||||
return false;
|
||||
}
|
||||
if (!arg.startsWith("-")) {
|
||||
return true;
|
||||
}
|
||||
return /^-\d+(?:\.\d+)?$/.test(arg);
|
||||
}
|
||||
|
||||
function getCommandPositionals(argv: string[]): string[] {
|
||||
const out: string[] = [];
|
||||
const args = argv.slice(2);
|
||||
for (const arg of args) {
|
||||
let commandStarted = false;
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
const arg = args[i];
|
||||
if (!arg || arg === "--") {
|
||||
break;
|
||||
}
|
||||
if (!commandStarted) {
|
||||
if (arg.startsWith("--profile=") || arg.startsWith("--log-level=")) {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--dev" || arg === "--no-color") {
|
||||
continue;
|
||||
}
|
||||
if (arg === "--profile" || arg === "--log-level") {
|
||||
const next = args[i + 1];
|
||||
if (isValueToken(next)) {
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (arg.startsWith("-")) {
|
||||
continue;
|
||||
}
|
||||
commandStarted = true;
|
||||
out.push(arg);
|
||||
}
|
||||
return out;
|
||||
|
||||
Reference in New Issue
Block a user