






















@@ -2,7 +2,7 @@
22 * Reads and refreshes credentials stored by external CLI runtimes such as
33 * Claude Code, Codex, Gemini, and MiniMax.
44 */
5-import { execFileSync, execSync } from "node:child_process";
5+import { execSync } from "node:child_process";
66import { createHash } from "node:crypto";
77import fs from "node:fs";
88import path from "node:path";
@@ -11,11 +11,10 @@ import {
1111resolveExpiresAtMsFromDurationMs,
1212timestampMsToIsoString,
1313} from "@openclaw/normalization-core/number-coercion";
14-import { formatErrorMessage } from "../infra/errors.js";
15-import { loadJsonFile, saveJsonFile } from "../infra/json-file.js";
14+import { loadJsonFile } from "../infra/json-file.js";
1615import { createSubsystemLogger } from "../logging/subsystem.js";
1716import { resolveUserPath } from "../utils.js";
18-import type { OAuthCredentials, OAuthProvider } from "./auth-profiles/types.js";
17+import type { OAuthProvider } from "./auth-profiles/types.js";
19182019const log = createSubsystemLogger("agents/auth-profiles");
2120@@ -26,8 +25,6 @@ const GEMINI_CLI_CREDENTIALS_RELATIVE_PATH = ".gemini/oauth_creds.json";
2625const CODEX_CLI_FALLBACK_EXPIRY_MS = 60 * 60 * 1000;
27262827const CLAUDE_CLI_KEYCHAIN_SERVICE = "Claude Code-credentials";
29-const CLAUDE_CLI_KEYCHAIN_ACCOUNT = "Claude Code";
30-3128type CachedValue<T> = {
3229value: T | null;
3330readAt: number;
@@ -95,18 +92,7 @@ export type GeminiCliCredential = {
9592email?: string;
9693};
979498-type ClaudeCliFileOptions = {
99-homeDir?: string;
100-};
101-102-type ClaudeCliWriteOptions = ClaudeCliFileOptions & {
103-platform?: NodeJS.Platform;
104-writeKeychain?: (credentials: OAuthCredentials) => boolean;
105-writeFile?: (credentials: OAuthCredentials, options?: ClaudeCliFileOptions) => boolean;
106-};
107-10895type ExecSyncFn = typeof execSync;
109-type ExecFileSyncFn = typeof execFileSync;
1109611197function resolveClaudeCliCredentialsPath(homeDir?: string) {
11298const baseDir = homeDir ?? resolveUserPath("~");
@@ -504,127 +490,6 @@ export function readClaudeCliCredentialsCached(options?: {
504490});
505491}
506492507-/** Writes refreshed Claude OAuth tokens back to the Claude CLI macOS Keychain item. */
508-export function writeClaudeCliKeychainCredentials(
509-newCredentials: OAuthCredentials,
510-options?: { execFileSync?: ExecFileSyncFn },
511-): boolean {
512-const execFileSyncImpl = options?.execFileSync ?? execFileSync;
513-try {
514-const existingResult = execFileSyncImpl(
515-"security",
516-["find-generic-password", "-s", CLAUDE_CLI_KEYCHAIN_SERVICE, "-w"],
517-{ encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] },
518-);
519-520-const existingData = JSON.parse(existingResult.trim());
521-const existingOauth = existingData?.claudeAiOauth;
522-if (!existingOauth || typeof existingOauth !== "object") {
523-return false;
524-}
525-526-existingData.claudeAiOauth = {
527- ...existingOauth,
528-accessToken: newCredentials.access,
529-refreshToken: newCredentials.refresh,
530-expiresAt: newCredentials.expires,
531-};
532-533-const newValue = JSON.stringify(existingData);
534-535-// Use execFileSync to avoid shell interpretation of user-controlled token values.
536-// This prevents command injection via $() or backtick expansion in OAuth tokens.
537-execFileSyncImpl(
538-"security",
539-[
540-"add-generic-password",
541-"-U",
542-"-s",
543-CLAUDE_CLI_KEYCHAIN_SERVICE,
544-"-a",
545-CLAUDE_CLI_KEYCHAIN_ACCOUNT,
546-"-w",
547-newValue,
548-],
549-{ encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "pipe"] },
550-);
551-552-log.info("wrote refreshed credentials to claude cli keychain", {
553-expires: timestampMsToIsoString(newCredentials.expires),
554-});
555-return true;
556-} catch (error) {
557-log.warn("failed to write credentials to claude cli keychain", {
558-error: formatErrorMessage(error),
559-});
560-return false;
561-}
562-}
563-564-/** Writes refreshed Claude OAuth tokens back to the Claude CLI credential file. */
565-export function writeClaudeCliFileCredentials(
566-newCredentials: OAuthCredentials,
567-options?: ClaudeCliFileOptions,
568-): boolean {
569-const credPath = resolveClaudeCliCredentialsPath(options?.homeDir);
570-571-if (!fs.existsSync(credPath)) {
572-return false;
573-}
574-575-try {
576-const raw = loadJsonFile(credPath);
577-if (!raw || typeof raw !== "object") {
578-return false;
579-}
580-581-const data = raw as Record<string, unknown>;
582-const existingOauth = data.claudeAiOauth as Record<string, unknown> | undefined;
583-if (!existingOauth || typeof existingOauth !== "object") {
584-return false;
585-}
586-587-data.claudeAiOauth = {
588- ...existingOauth,
589-accessToken: newCredentials.access,
590-refreshToken: newCredentials.refresh,
591-expiresAt: newCredentials.expires,
592-};
593-594-saveJsonFile(credPath, data);
595-log.info("wrote refreshed credentials to claude cli file", {
596-expires: timestampMsToIsoString(newCredentials.expires),
597-});
598-return true;
599-} catch (error) {
600-log.warn("failed to write credentials to claude cli file", {
601-error: formatErrorMessage(error),
602-});
603-return false;
604-}
605-}
606-607-/** Writes refreshed Claude OAuth tokens to the preferred Claude CLI credential store. */
608-export function writeClaudeCliCredentials(
609-newCredentials: OAuthCredentials,
610-options?: ClaudeCliWriteOptions,
611-): boolean {
612-const platform = options?.platform ?? process.platform;
613-const writeKeychain = options?.writeKeychain ?? writeClaudeCliKeychainCredentials;
614-const writeFile =
615-options?.writeFile ??
616-((credentials, fileOptions) => writeClaudeCliFileCredentials(credentials, fileOptions));
617-618-if (platform === "darwin") {
619-const didWriteKeychain = writeKeychain(newCredentials);
620-if (didWriteKeychain) {
621-return true;
622-}
623-}
624-625-return writeFile(newCredentials, { homeDir: options?.homeDir });
626-}
627-628493/** Reads Codex CLI OAuth credentials from Keychain or CODEX_HOME auth.json. */
629494export function readCodexCliCredentials(options?: {
630495codexHome?: string;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。