fix(status): use claude-cli token for usage
This commit is contained in:
@@ -227,6 +227,85 @@ describe("provider usage loading", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("prefers claude-cli token for Anthropic usage snapshots", async () => {
|
||||
await withTempHome(
|
||||
async () => {
|
||||
const stateDir = process.env.CLAWDBOT_STATE_DIR;
|
||||
if (!stateDir) throw new Error("Missing CLAWDBOT_STATE_DIR");
|
||||
const agentDir = path.join(stateDir, "agents", "main", "agent");
|
||||
fs.mkdirSync(agentDir, { recursive: true, mode: 0o700 });
|
||||
fs.writeFileSync(
|
||||
path.join(agentDir, "auth-profiles.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
version: 1,
|
||||
profiles: {
|
||||
"anthropic:default": {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: "token-default",
|
||||
expires: Date.UTC(2100, 0, 1, 0, 0, 0),
|
||||
},
|
||||
"anthropic:claude-cli": {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: "token-cli",
|
||||
expires: Date.UTC(2100, 0, 1, 0, 0, 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const makeResponse = (status: number, body: unknown): Response => {
|
||||
const payload =
|
||||
typeof body === "string" ? body : JSON.stringify(body);
|
||||
const headers =
|
||||
typeof body === "string"
|
||||
? undefined
|
||||
: { "Content-Type": "application/json" };
|
||||
return new Response(payload, { status, headers });
|
||||
};
|
||||
|
||||
const mockFetch = vi.fn<
|
||||
Parameters<typeof fetch>,
|
||||
ReturnType<typeof fetch>
|
||||
>(async (input, init) => {
|
||||
const url =
|
||||
typeof input === "string"
|
||||
? input
|
||||
: input instanceof URL
|
||||
? input.toString()
|
||||
: input.url;
|
||||
if (url.includes("api.anthropic.com/api/oauth/usage")) {
|
||||
const headers = (init?.headers ?? {}) as Record<string, string>;
|
||||
expect(headers.Authorization).toBe("Bearer token-cli");
|
||||
return makeResponse(200, {
|
||||
five_hour: { utilization: 20, resets_at: "2026-01-07T01:00:00Z" },
|
||||
});
|
||||
}
|
||||
return makeResponse(404, "not found");
|
||||
});
|
||||
|
||||
const summary = await loadProviderUsageSummary({
|
||||
now: Date.UTC(2026, 0, 7, 0, 0, 0),
|
||||
providers: ["anthropic"],
|
||||
agentDir,
|
||||
fetch: mockFetch,
|
||||
});
|
||||
|
||||
expect(summary.providers).toHaveLength(1);
|
||||
expect(summary.providers[0]?.provider).toBe("anthropic");
|
||||
expect(summary.providers[0]?.windows[0]?.label).toBe("5h");
|
||||
expect(mockFetch).toHaveBeenCalled();
|
||||
},
|
||||
{ prefix: "clawdbot-provider-usage-" },
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to claude.ai web usage when OAuth scope is missing", async () => {
|
||||
const cookieSnapshot = process.env.CLAUDE_AI_SESSION_KEY;
|
||||
process.env.CLAUDE_AI_SESSION_KEY = "sk-ant-web-1";
|
||||
|
||||
@@ -3,6 +3,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
ensureAuthProfileStore,
|
||||
listProfilesForProvider,
|
||||
resolveApiKeyForProfile,
|
||||
@@ -802,7 +803,16 @@ async function resolveOAuthToken(params: {
|
||||
provider: params.provider,
|
||||
});
|
||||
|
||||
for (const profileId of order) {
|
||||
// Claude CLI creds are the only Anthropic tokens that reliably include the
|
||||
// `user:profile` scope required for the OAuth usage endpoint.
|
||||
const candidates =
|
||||
params.provider === "anthropic" ? [CLAUDE_CLI_PROFILE_ID, ...order] : order;
|
||||
const deduped: string[] = [];
|
||||
for (const entry of candidates) {
|
||||
if (!deduped.includes(entry)) deduped.push(entry);
|
||||
}
|
||||
|
||||
for (const profileId of deduped) {
|
||||
const cred = store.profiles[profileId];
|
||||
if (!cred || (cred.type !== "oauth" && cred.type !== "token")) continue;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user