openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
99 lines (98 loc) • 3.77 kB
JavaScript
import { a as CLAUDE_CLI_NATIVE_AUTH_MARKER, n as CLAUDE_CLI_CLEAR_ENV, t as CLAUDE_CLI_BACKEND_ID } from "./.setup/cli-constants-BFKhid13.mjs";
import { filterStringEntries, isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { runUtf8CommandWithTimeout } from "openclaw/plugin-sdk/process-runtime";
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
//#region extensions/anthropic/cli-auth-seam.ts
const CLAUDE_CLI_AUTH_METHODS = [
"claude.ai",
"api_key",
"api_key_helper",
"oauth_token",
"third_party",
"none"
];
const CLI_BACKEND_PRESERVE_ENV = "OPENCLAW_LIVE_CLI_BACKEND_PRESERVE_ENV";
function readPreservedCliEnv(env) {
const raw = env[CLI_BACKEND_PRESERVE_ENV]?.trim();
if (!raw) return /* @__PURE__ */ new Set();
if (raw.startsWith("[")) try {
return new Set(filterStringEntries(JSON.parse(raw)));
} catch {
return /* @__PURE__ */ new Set();
}
return new Set(raw.split(/[,\s]+/).filter(Boolean));
}
/** Ask Claude CLI whether its own login is usable without reading token material. */
async function probeClaudeCliAuthStatus(params) {
const env = { ...params?.env ?? process.env };
const preserved = readPreservedCliEnv(env);
for (const name of CLAUDE_CLI_CLEAR_ENV) if (!preserved.has(name)) delete env[name];
try {
const program = resolveWindowsSpawnProgram({
command: params?.command ?? "claude",
env,
packageName: "@anthropic-ai/claude-code"
});
const invocation = materializeWindowsSpawnProgram(program, [
"auth",
"status",
"--json"
]);
const result = await runUtf8CommandWithTimeout([invocation.command, ...invocation.argv], {
baseEnv: env,
maxOutputBytes: 65536,
maxCombinedOutputBytes: 65536,
terminateOnOutputLimit: true,
timeoutMs: 3e3,
signal: params?.signal,
killProcessTree: true
});
params?.signal?.throwIfAborted();
if (result.termination !== "exit" || result.code === null) return { status: "unreadable" };
if (result.code !== 0) return { status: "missing" };
const parsed = JSON.parse(result.stdout);
if (!isRecord(parsed) || parsed.loggedIn !== true) return { status: "missing" };
const authMethod = CLAUDE_CLI_AUTH_METHODS.find((method) => method === parsed.authMethod);
const email = authMethod === "claude.ai" ? normalizeOptionalString(parsed.email) : void 0;
return {
status: "available",
...authMethod ? { authMethod } : {},
...email && email.length <= 320 && !/[\r\n]/u.test(email) ? { email } : {}
};
} catch {
params?.signal?.throwIfAborted();
return { status: "unreadable" };
}
}
//#endregion
//#region extensions/anthropic/provider-discovery.ts
const availability = /* @__PURE__ */ new WeakMap();
const anthropicProviderDiscovery = {
id: CLAUDE_CLI_BACKEND_ID,
label: "Claude CLI",
docsPath: "/providers/models",
auth: [],
async prepareSyntheticAuth({ config, provider, env = process.env, signal }) {
signal?.throwIfAborted();
if (!config || normalizeLowercaseStringOrEmpty(provider) !== "claude-cli") return;
const environments = availability.get(config) ?? /* @__PURE__ */ new WeakMap();
availability.set(config, environments);
const captures = environments.get(env) ?? /* @__PURE__ */ new WeakMap();
environments.set(env, captures);
const owner = signal ?? config;
const pending = captures.get(owner) ?? probeClaudeCliAuthStatus({
env,
signal
});
captures.set(owner, pending);
const result = await pending;
signal?.throwIfAborted();
return result.status === "available" ? {
apiKey: CLAUDE_CLI_NATIVE_AUTH_MARKER,
source: "Claude CLI native auth",
mode: "oauth"
} : void 0;
}
};
//#endregion
export { anthropicProviderDiscovery as default };