import { execFileSync } from
"node:child_process" ;
import crypto from
"node:crypto" ;
import fs from
"node:fs" ;
import os from
"node:os" ;
import path from
"node:path" ;
import { normalizeOptionalString } from
"../src/shared/string-coerce.ts" ;
type Args = {
agentId: string;
reveal:
boolean ;
sessionKey?: string;
};
const mask = (value: string) => {
const compact = value.trim();
if (!compact) {
return "missing" ;
}
const edge = compact.length >=
12 ?
6 :
4 ;
return `${compact.slice(
0 , edge)}…${compact.slice(-edge)}`;
};
const parseArgs = (): Args => {
const args = process.argv.slice(
2 );
let agentId =
"main" ;
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" ;
continue ;
}
if (arg ===
"--reveal" ) {
reveal =
true ;
continue ;
}
if (arg ===
"--session-key" && args[i +
1 ]) {
sessionKey = normalizeOptionalString(args[++i]);
continue ;
}
}
return { agentId, reveal, sessionKey };
};
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" )
;
if (!fs.existsSync(authPath)) {
throw new Error(`Missing: ${authPath}`);
}
const store = JSON.parse(fs.readFileSync(authPath, "utf8" )) as {
profiles?: Record<string, { provider?: string; type?: string; token?: string; key?: string }>;
};
return { authPath, store };
};
const pickAnthropicTokens = (store: {
profiles?: Record<string, { provider?: string; type?: string; token?: string; key?: string }>;
}): Array<{ profileId: string; token: string }> => {
const profiles = store.profiles ?? {};
const found: Array<{ profileId: string; token: string }> = [];
for (const [id, cred] of Object.entries(profiles)) {
if (cred?.provider !== "anthropic" ) {
continue ;
}
const token = cred.type === "token" ? cred.token?.trim() : undefined;
if (token) {
found.push({ profileId: id, token });
}
}
return found;
};
const fetchAnthropicOAuthUsage = async (token: string) => {
const res = await fetch("https://api.anthropic.com/api/oauth/usage ", {
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json" ,
"anthropic-version" : "2023-06-01" ,
"anthropic-beta" : "oauth-2025-04-20" ,
"User-Agent" : "openclaw-debug" ,
},
});
const text = await res.text();
return { status: res.status, contentType: res.headers.get("content-type" ), text };
};
const readClaudeCliKeychain = (): {
accessToken: string;
expiresAt?: number;
scopes?: string[];
} | null => {
if (process.platform !== "darwin" ) {
return null ;
}
try {
const raw = execFileSync(
"security" ,
["find-generic-password" , "-s" , "Claude Code-credentials" , "-w" ],
{ encoding: "utf8" , stdio: ["ignore" , "pipe" , "ignore" ], timeout: 5000 },
);
const parsed = JSON.parse(raw.trim()) as Record<string, unknown>;
const oauth = parsed?.claudeAiOauth as Record<string, unknown> | undefined;
if (!oauth || typeof oauth !== "object" ) {
return null ;
}
const accessToken = oauth.accessToken;
if (typeof accessToken !== "string" || !accessToken.trim()) {
return null ;
}
const expiresAt = typeof oauth.expiresAt === "number" ? oauth.expiresAt : undefined;
const scopes = Array.isArray(oauth.scopes)
? oauth.scopes.filter((v): v is string => typeof v === "string" )
: undefined;
return { accessToken, expiresAt, scopes };
} catch {
return null ;
}
};
const chromeServiceNameForPath = (cookiePath: string): string => {
if (cookiePath.includes("/Arc/" )) {
return "Arc Safe Storage" ;
}
if (cookiePath.includes("/BraveSoftware/" )) {
return "Brave Safe Storage" ;
}
if (cookiePath.includes("/Microsoft Edge/" )) {
return "Microsoft Edge Safe Storage" ;
}
if (cookiePath.includes("/Chromium/" )) {
return "Chromium Safe Storage" ;
}
return "Chrome Safe Storage" ;
};
const readKeychainPassword = (service: string): string | null => {
try {
const out = execFileSync("security" , ["find-generic-password" , "-w" , "-s" , service], {
encoding: "utf8" ,
stdio: ["ignore" , "pipe" , "ignore" ],
timeout: 5000 ,
});
const pw = out.trim();
return pw ? pw : null ;
} catch {
return null ;
}
};
const decryptChromeCookieValue = (encrypted: Buffer, service: string): string | null => {
if (encrypted.length < 4 ) {
return null ;
}
const prefix = encrypted.subarray(0 , 3 ).toString("utf8" );
if (prefix !== "v10" && prefix !== "v11" ) {
return null ;
}
const password = readKeychainPassword(service);
if (!password) {
return null ;
}
const key = crypto.pbkdf2Sync(password, "saltysalt" , 1003 , 16 , "sha1" );
const iv = Buffer.alloc(16 , 0 x20);
const data = encrypted.subarray(3 );
try {
const decipher = crypto.createDecipheriv("aes-128-cbc" , key, iv);
decipher.setAutoPadding(true );
const decrypted = Buffer.concat([decipher.update(data), decipher.final ()]);
const text = decrypted.toString("utf8" ).trim();
return text ? text : null ;
} catch {
return null ;
}
};
const queryChromeCookieDb = (cookieDb: string): string | null => {
try {
const out = execFileSync(
"sqlite3" ,
[
"-readonly" ,
cookieDb,
`
SELECT
COALESCE(NULLIF(value,'' ), hex(encrypted_value))
FROM cookies
WHERE (host_key LIKE '%claude.ai%' OR host_key = '.claude.ai' )
AND name = 'sessionKey'
LIMIT 1 ;
`,
],
{ encoding: "utf8" , stdio: ["ignore" , "pipe" , "ignore" ], timeout: 5000 },
).trim();
if (!out) {
return null ;
}
if (out.startsWith("sk-ant-" )) {
return out;
}
const hex = out.replace(/[^0 -9 A-Fa-f]/g, "" );
if (!hex) {
return null ;
}
const buf = Buffer.from(hex, "hex" );
const service = chromeServiceNameForPath(cookieDb);
const decrypted = decryptChromeCookieValue(buf, service);
return decrypted && decrypted.startsWith("sk-ant-" ) ? decrypted : null ;
} catch {
return null ;
}
};
const queryFirefoxCookieDb = (cookieDb: string): string | null => {
try {
const out = execFileSync(
"sqlite3" ,
[
"-readonly" ,
cookieDb,
`
SELECT value
FROM moz_cookies
WHERE (host LIKE '%claude.ai%' OR host = '.claude.ai' )
AND name = 'sessionKey'
LIMIT 1 ;
`,
],
{ encoding: "utf8" , stdio: ["ignore" , "pipe" , "ignore" ], timeout: 5000 },
).trim();
return out && out.startsWith("sk-ant-" ) ? out : null ;
} catch {
return null ;
}
};
const findClaudeSessionKey = (): { sessionKey: string; source: string } | null => {
if (process.platform !== "darwin" ) {
return null ;
}
const firefoxRoot = path.join(
os.homedir(),
"Library" ,
"Application Support" ,
"Firefox" ,
"Profiles" ,
);
if (fs.existsSync(firefoxRoot)) {
for (const entry of fs.readdirSync(firefoxRoot)) {
const db = path.join(firefoxRoot, entry, "cookies.sqlite" );
if (!fs.existsSync(db)) {
continue ;
}
const value = queryFirefoxCookieDb(db);
if (value) {
return { sessionKey: value, source: `firefox:${db}` };
}
}
}
const chromeCandidates = [
path.join(os.homedir(), "Library" , "Application Support" , "Google" , "Chrome" ),
path.join(os.homedir(), "Library" , "Application Support" , "Chromium" ),
path.join(os.homedir(), "Library" , "Application Support" , "Arc" ),
path.join(os.homedir(), "Library" , "Application Support" , "BraveSoftware" , "Brave-Browser" ),
path.join(os.homedir(), "Library" , "Application Support" , "Microsoft Edge" ),
];
for (const root of chromeCandidates) {
if (!fs.existsSync(root)) {
continue ;
}
const profiles = fs
.readdirSync(root)
.filter((name) => name === "Default" || name.startsWith("Profile " ));
for (const profile of profiles) {
const db = path.join(root, profile, "Cookies" );
if (!fs.existsSync(db)) {
continue ;
}
const value = queryChromeCookieDb(db);
if (value) {
return { sessionKey: value, source: `chromium:${db}` };
}
}
}
return null ;
};
const fetchClaudeWebUsage = async (sessionKey: string) => {
const headers = {
Cookie: `sessionKey=${sessionKey}`,
Accept: "application/json" ,
"User-Agent" :
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15" ,
};
const orgRes = await fetch("https://claude.ai/api/organizations ", { headers });
const orgText = await orgRes.text();
if (!orgRes.ok) {
return { ok: false as const , step: "organizations" , status: orgRes.status, body: orgText };
}
const orgs = JSON.parse(orgText) as Array<{ uuid?: string }>;
const orgId = orgs?.[0 ]?.uuid;
if (!orgId) {
return { ok: false as const , step: "organizations" , status: 200 , body: orgText };
}
const usageRes = await fetch(`https://claude.ai/api/organizations/${orgId}/usage`, { headers });
const usageText = await usageRes.text();
return usageRes.ok
? { ok: true as const , orgId, body: usageText }
: { ok: false as const , step: "usage" , status: usageRes.status, body: usageText };
};
const main = async () => {
const opts = parseArgs();
const { authPath, store } = loadAuthProfiles(opts.agentId);
console.log(`Auth file: ${authPath}`);
const keychain = readClaudeCliKeychain();
if (keychain) {
console.log(
`Claude Code CLI keychain: accessToken=${opts.reveal ? keychain.accessToken : mask(keychain.accessToken)} scopes=${keychain.scopes?.join("," ) ?? "(unknown)" }`,
);
const oauth = await fetchAnthropicOAuthUsage(keychain.accessToken);
console.log(
`OAuth usage (keychain): HTTP ${oauth.status} (${oauth.contentType ?? "no content-type" })`,
);
console.log(oauth.text.slice(0 , 200 ).replace(/\s+/g, " " ).trim());
} else {
console.log("Claude Code CLI keychain: missing/unreadable" );
}
const anthropic = pickAnthropicTokens(store);
if (anthropic.length === 0 ) {
console.log("Auth profiles: no Anthropic token profiles found" );
} else {
for (const entry of anthropic) {
console.log(
`Auth profiles: ${entry.profileId} token=${opts.reveal ? entry.token : mask(entry.token)}`,
);
const oauth = await fetchAnthropicOAuthUsage(entry.token);
console.log(
`OAuth usage (${entry.profileId}): HTTP ${oauth.status} (${oauth.contentType ?? "no content-type" })`,
);
console.log(oauth.text.slice(0 , 200 ).replace(/\s+/g, " " ).trim());
}
}
const sessionKey =
opts.sessionKey?.trim() ||
process.env.CLAUDE_AI_SESSION_KEY?.trim() ||
process.env.CLAUDE_WEB_SESSION_KEY?.trim() ||
findClaudeSessionKey()?.sessionKey;
const source = opts.sessionKey
? "--session-key"
: process.env.CLAUDE_AI_SESSION_KEY || process.env.CLAUDE_WEB_SESSION_KEY
? "env"
: (findClaudeSessionKey()?.source ?? "auto" );
if (!sessionKey) {
console.log(
"Claude web: no sessionKey found (try --session-key or export CLAUDE_AI_SESSION_KEY)" ,
);
return ;
}
console.log(
`Claude web: sessionKey=${opts.reveal ? sessionKey : mask(sessionKey)} (source: ${source})`,
);
const web = await fetchClaudeWebUsage(sessionKey);
if (!web.ok) {
console.log(`Claude web: ${web.step} HTTP ${web.status}`);
console.log(web.body.slice(0 , 400 ).replace(/\s+/g, " " ).trim());
return ;
}
console.log(`Claude web: org=${web.orgId} OK`);
console.log(web.body.slice(0 , 400 ).replace(/\s+/g, " " ).trim());
};
await main();
Messung V0.5 in Prozent C=99 H=96 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland