fix(test): guard claude usage debug args

This commit is contained in:
Vincent Koc 2026-06-20 03:15:30 +02:00
parent 50b5238b38
commit a971641a54
No known key found for this signature in database
2 changed files with 126 additions and 10 deletions

View file

@ -16,6 +16,7 @@ import {
type Args = {
agentId: string;
help: boolean;
reveal: boolean;
sessionKey?: string;
};
@ -36,31 +37,86 @@ const mask = (value: string) => {
);
};
const parseArgs = (): Args => {
const args = process.argv.slice(2);
const parseArgs = (args = process.argv.slice(2)): Args => {
let agentId = "main";
let help = false;
let reveal = false;
let sessionKey: string | undefined;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--agent" && args[i + 1]) {
agentId = args[++i].trim() || "main";
if (arg === "--agent") {
agentId = parseNonBlankArgValue(parseRequiredArgValue(args, i, "--agent"), "--agent");
i += 1;
continue;
}
if (arg.startsWith("--agent=")) {
agentId = parseNonBlankArgValue(parseInlineArgValue(arg, "--agent"), "--agent");
continue;
}
if (arg === "--help" || arg === "-h") {
help = true;
continue;
}
if (arg === "--reveal") {
reveal = true;
continue;
}
if (arg === "--session-key" && args[i + 1]) {
sessionKey = normalizeOptionalString(args[++i]);
if (arg === "--session-key") {
sessionKey = parseNonBlankArgValue(
parseRequiredArgValue(args, i, "--session-key"),
"--session-key",
);
i += 1;
continue;
}
if (arg.startsWith("--session-key=")) {
sessionKey = parseNonBlankArgValue(
parseInlineArgValue(arg, "--session-key"),
"--session-key",
);
continue;
}
throw new Error(`Unknown argument: ${arg}`);
}
return { agentId, reveal, sessionKey };
return { agentId, help, reveal, sessionKey };
};
function parseRequiredArgValue(args: string[], index: number, label: string): string {
const value = args[index + 1];
if (!value || value.startsWith("-")) {
throw new Error(`${label} requires a value`);
}
return value;
}
function parseInlineArgValue(arg: string, label: string): string {
const value = arg.slice(`${label}=`.length);
if (!value) {
throw new Error(`${label} requires a value`);
}
return value;
}
function parseNonBlankArgValue(value: string, label: string): string {
const normalized = normalizeOptionalString(value);
if (!normalized) {
throw new Error(`${label} requires a value`);
}
return normalized;
}
function printUsage(): void {
console.log(`Usage: node --import tsx scripts/debug-claude-usage.ts [options]
Options:
--agent <id> OpenClaw agent id to inspect (default: main)
--session-key <key> Claude web session key override
--reveal Print token/session values instead of masked identifiers
--help, -h Show this help message`);
}
const loadAuthProfiles = (agentId: string) => {
const stateRoot = process.env.OPENCLAW_STATE_DIR?.trim() || path.join(os.homedir(), ".openclaw");
const authPath = path.join(stateRoot, "agents", agentId, "agent", "auth-profiles.json");
@ -413,8 +469,13 @@ const fetchClaudeWebUsage = async (sessionKey: string, options: FetchOptions = {
: { ok: false as const, step: "usage", status: usageRes.status, body: usageText };
};
const main = async () => {
const opts = parseArgs();
const main = async (argv = process.argv.slice(2)) => {
const opts = parseArgs(argv);
if (opts.help) {
printUsage();
return;
}
const { authPath, store } = loadAuthProfiles(opts.agentId);
console.log(`Auth file: ${redactHomePath(authPath)}`);
@ -485,11 +546,12 @@ export const testing = {
browserRootLabel,
fetchAnthropicOAuthUsage,
mask,
parseArgs,
readBoundedResponseText,
resolveFetchTimeoutMs,
};
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
if (import.meta.url === pathToFileURL(path.resolve(process.argv[1] ?? "")).href) {
await main().catch((error: unknown) => {
console.error(
previewForDevToolLog(error instanceof Error ? error.message : String(error), 800),

View file

@ -716,6 +716,60 @@ describe("script-specific dev tooling hardening", () => {
expect(claudeUsageTesting.CLAUDE_COOKIE_HOST_SQL).not.toContain("%claude.ai%");
});
it("rejects malformed Claude usage args before reading auth or browser state", () => {
expect(claudeUsageTesting.parseArgs(["--agent", "work", "--session-key=abc"])).toEqual({
agentId: "work",
help: false,
reveal: false,
sessionKey: "abc",
});
expect(claudeUsageTesting.parseArgs(["--help"])).toEqual({
agentId: "main",
help: true,
reveal: false,
sessionKey: undefined,
});
expect(() => claudeUsageTesting.parseArgs(["--wat"])).toThrow("Unknown argument: --wat");
expect(() => claudeUsageTesting.parseArgs(["--agent"])).toThrow("--agent requires a value");
expect(() => claudeUsageTesting.parseArgs(["--agent="])).toThrow("--agent requires a value");
expect(() => claudeUsageTesting.parseArgs(["--session-key", "--reveal"])).toThrow(
"--session-key requires a value",
);
expect(() => claudeUsageTesting.parseArgs(["--session-key= "])).toThrow(
"--session-key requires a value",
);
});
it("prints Claude usage help without opening auth stores", () => {
const result = spawnSync(
process.execPath,
["--import", "tsx", "scripts/debug-claude-usage.ts", "--help"],
{
cwd: process.cwd(),
encoding: "utf8",
},
);
expect(result.status).toBe(0);
expect(result.stdout).toContain("Usage: node --import tsx scripts/debug-claude-usage.ts");
expect(result.stderr).toBe("");
});
it("fails missing Claude usage option values before defaulting to main auth", () => {
const result = spawnSync(
process.execPath,
["--import", "tsx", "scripts/debug-claude-usage.ts", "--agent"],
{
cwd: process.cwd(),
encoding: "utf8",
},
);
expect(result.status).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr).toContain("--agent requires a value");
});
it("aborts stalled Claude usage fetches at the request timeout", async () => {
let signal: AbortSignal | undefined;
const request = claudeUsageTesting.fetchAnthropicOAuthUsage("test-token", {