惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
L
LangChain Blog
H
Help Net Security
博客园_首页
T
Tailwind CSS Blog
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
Recent Announcements
Recent Announcements
D
DataBreaches.Net
U
Unit 42
Vercel News
Vercel News
I
InfoQ
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
Apple Machine Learning Research
Apple Machine Learning Research
S
SegmentFault 最新的问题
Jina AI
Jina AI
博客园 - 叶小钗
博客园 - 【当耐特】
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
Last Week in AI
Last Week in AI

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
refactor(agents): remove unused Claude CLI credential wri...
vincentkoc · 2026-06-19 · via Recent Commits to openclaw:main

@@ -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";

66

import { createHash } from "node:crypto";

77

import fs from "node:fs";

88

import path from "node:path";

@@ -11,11 +11,10 @@ import {

1111

resolveExpiresAtMsFromDurationMs,

1212

timestampMsToIsoString,

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";

1615

import { createSubsystemLogger } from "../logging/subsystem.js";

1716

import { resolveUserPath } from "../utils.js";

18-

import type { OAuthCredentials, OAuthProvider } from "./auth-profiles/types.js";

17+

import type { OAuthProvider } from "./auth-profiles/types.js";

19182019

const log = createSubsystemLogger("agents/auth-profiles");

2120

@@ -26,8 +25,6 @@ const GEMINI_CLI_CREDENTIALS_RELATIVE_PATH = ".gemini/oauth_creds.json";

2625

const CODEX_CLI_FALLBACK_EXPIRY_MS = 60 * 60 * 1000;

27262827

const CLAUDE_CLI_KEYCHAIN_SERVICE = "Claude Code-credentials";

29-

const CLAUDE_CLI_KEYCHAIN_ACCOUNT = "Claude Code";

30-3128

type CachedValue<T> = {

3229

value: T | null;

3330

readAt: number;

@@ -95,18 +92,7 @@ export type GeminiCliCredential = {

9592

email?: 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-10895

type ExecSyncFn = typeof execSync;

109-

type ExecFileSyncFn = typeof execFileSync;

1109611197

function resolveClaudeCliCredentialsPath(homeDir?: string) {

11298

const 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. */

629494

export function readCodexCliCredentials(options?: {

630495

codexHome?: string;