mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-30 15:00:26 +00:00
* refactor: share talk event metric extraction * refactor: reuse shared coercion helpers * refactor: reuse shared primitive guards * refactor: reuse shared record guard * refactor: reuse shared primitive helpers * refactor: reuse shared string guards * refactor: reuse shared non-empty string guard * refactor: share plugin primitive coercion helpers * refactor: reuse plugin coercion helpers * refactor: reuse plugin coercion helpers in more plugins * refactor: reuse channel coercion helpers * refactor: reuse monitor coercion helpers * refactor: reuse provider coercion helpers * refactor: reuse core coercion helpers * refactor: reuse runtime coercion helpers * refactor: reuse helper coercion in codex paths * refactor: reuse helper coercion in runtime paths * refactor: reuse codex app-server coercion helpers * refactor: reuse codex record helpers * refactor: reuse migration and qa record helpers * refactor: reuse feishu and core helper guards * refactor: reuse browser and policy coercion helpers * refactor: reuse memory wiki record helper * refactor: share boolean coercion helpers * refactor: reuse finite number coercion * refactor: reuse trimmed string list helpers * refactor: reuse string list normalization * refactor: reuse remaining string list helpers * refactor: reuse string entry normalizer * refactor: share sorted string helpers * refactor: share string list normalization * test: preserve command registry browser imports * refactor: reuse trimmed list helpers * refactor: reuse string dedupe helpers * refactor: reuse local dedupe helpers * refactor: reuse more string dedupe helpers * refactor: reuse command string dedupe helpers * refactor: dedupe memory path lists with helper * refactor: expose string dedupe helpers to plugins * refactor: reuse core string dedupe helpers * refactor: reuse shared unique value helpers * refactor: reuse unique helpers in agent utilities * refactor: reuse unique helpers in config plumbing * refactor: reuse unique helpers in extensions * refactor: reuse unique helpers in core utilities * refactor: reuse unique helpers in qa plugins * refactor: reuse unique helpers in memory plugins * refactor: reuse unique helpers in channel plugins * refactor: reuse unique helpers in core tails * refactor: reuse unique helper in comfy workflow * refactor: reuse unique helpers in test utilities * refactor: expose unique value helper to plugins * refactor: reuse unique helpers for numeric lists * refactor: replace index dedupe filters * refactor: reuse string entry normalization * refactor: reuse string normalization in plugin helpers * refactor: reuse string normalization in extension helpers * refactor: reuse string normalization in channel parsers * refactor: reuse string normalization in memory search * refactor: reuse string normalization in provider parsers * refactor: reuse string normalization in qa helpers * refactor: reuse string normalization in infra parsers * refactor: reuse string normalization in messaging parsers * refactor: reuse string normalization in core parsers * refactor: reuse string normalization in extension parsers * refactor: reuse string normalization in remaining parsers * refactor: reuse string normalization in final parser spots * refactor: reuse string normalization in qa media helpers * refactor: reuse normalization in provider and media lists * refactor: reuse normalization for remaining set filters * refactor: reuse normalization in policy allowlists * refactor: reuse normalization in session and owner lists * refactor: centralize primitive string lists * refactor: reuse lowercase entry helpers * refactor: reuse sorted string helpers * refactor: reuse unique trimmed helpers * refactor: reuse string normalization helpers * refactor: reuse catalog string helpers * refactor: reuse remaining string helpers * refactor: simplify remaining list normalization * refactor: reuse codex auth order normalization * chore: refresh plugin sdk api baseline * fix: make shared string sorting deterministic * chore: refresh plugin sdk api baseline * fix: align host env security ordering
266 lines
8.0 KiB
TypeScript
266 lines
8.0 KiB
TypeScript
import {
|
|
CLAUDE_CLI_PROFILE_ID,
|
|
type OpenClawConfig,
|
|
type ProviderAuthResult,
|
|
} from "openclaw/plugin-sdk/provider-auth";
|
|
import {
|
|
isRecord,
|
|
normalizeLowercaseStringOrEmpty,
|
|
} from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
import { resolveClaudeCliAnthropicModelRefs } from "./claude-model-refs.js";
|
|
import {
|
|
readClaudeCliCredentialsForSetup,
|
|
readClaudeCliCredentialsForSetupNonInteractive,
|
|
} from "./cli-auth-seam.js";
|
|
import { CLAUDE_CLI_BACKEND_ID, CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS } from "./cli-shared.js";
|
|
|
|
type AgentDefaultsModel = NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]>["model"];
|
|
type AgentDefaultsModels = NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]>["models"];
|
|
type AgentDefaultsRuntimePolicy = NonNullable<
|
|
NonNullable<OpenClawConfig["agents"]>["defaults"]
|
|
>["agentRuntime"];
|
|
type ClaudeCliCredential = NonNullable<ReturnType<typeof readClaudeCliCredentialsForSetup>>;
|
|
|
|
function toAnthropicModelRef(raw: string): string | null {
|
|
return resolveClaudeCliAnthropicModelRefs(raw)?.rewriteRef ?? null;
|
|
}
|
|
|
|
function toAnthropicRuntimeRefs(raw: string): string[] {
|
|
return resolveClaudeCliAnthropicModelRefs(raw)?.runtimeRefs ?? [];
|
|
}
|
|
|
|
function toAnthropicSelectedModelRef(raw: string): string | undefined {
|
|
const resolved = resolveClaudeCliAnthropicModelRefs(raw);
|
|
return resolved?.rewriteRef ?? resolved?.selectedRef;
|
|
}
|
|
|
|
function rewriteModelSelection(model: AgentDefaultsModel): {
|
|
value: AgentDefaultsModel;
|
|
primary?: string;
|
|
runtimeRefs: string[];
|
|
changed: boolean;
|
|
} {
|
|
if (typeof model === "string") {
|
|
const runtimeRefs = toAnthropicRuntimeRefs(model);
|
|
const converted = toAnthropicModelRef(model);
|
|
const selectedRef = converted ?? toAnthropicSelectedModelRef(model);
|
|
return converted
|
|
? { value: converted, primary: converted, runtimeRefs, changed: true }
|
|
: {
|
|
value: model,
|
|
...(selectedRef ? { primary: selectedRef } : {}),
|
|
runtimeRefs,
|
|
changed: false,
|
|
};
|
|
}
|
|
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
return { value: model, runtimeRefs: [], changed: false };
|
|
}
|
|
|
|
const current = model as Record<string, unknown>;
|
|
const next: Record<string, unknown> = { ...current };
|
|
const runtimeRefs: string[] = [];
|
|
let changed = false;
|
|
let primary: string | undefined;
|
|
|
|
if (typeof current.primary === "string") {
|
|
runtimeRefs.push(...toAnthropicRuntimeRefs(current.primary));
|
|
const converted = toAnthropicModelRef(current.primary);
|
|
if (converted) {
|
|
next.primary = converted;
|
|
primary = converted;
|
|
changed = true;
|
|
} else {
|
|
primary = toAnthropicSelectedModelRef(current.primary);
|
|
}
|
|
}
|
|
|
|
const currentFallbacks = current.fallbacks;
|
|
if (Array.isArray(currentFallbacks)) {
|
|
const nextFallbacks = currentFallbacks.map((entry) => {
|
|
if (typeof entry !== "string") {
|
|
return entry;
|
|
}
|
|
runtimeRefs.push(...toAnthropicRuntimeRefs(entry));
|
|
const converted = toAnthropicModelRef(entry);
|
|
return converted ?? entry;
|
|
});
|
|
if (nextFallbacks.some((entry, index) => entry !== currentFallbacks[index])) {
|
|
next.fallbacks = nextFallbacks;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
return {
|
|
value: changed ? next : model,
|
|
...(primary ? { primary } : {}),
|
|
runtimeRefs,
|
|
changed,
|
|
};
|
|
}
|
|
|
|
function rewriteModelEntryMap(models: Record<string, unknown> | undefined): {
|
|
value: Record<string, unknown> | undefined;
|
|
migrated: string[];
|
|
runtimeRefs: string[];
|
|
} {
|
|
if (!models) {
|
|
return { value: models, migrated: [], runtimeRefs: [] };
|
|
}
|
|
|
|
const next = { ...models };
|
|
const migrated: string[] = [];
|
|
const runtimeRefs: string[] = [];
|
|
|
|
for (const [rawKey, value] of Object.entries(models)) {
|
|
runtimeRefs.push(...toAnthropicRuntimeRefs(rawKey));
|
|
const converted = toAnthropicModelRef(rawKey);
|
|
if (!converted) {
|
|
continue;
|
|
}
|
|
if (converted === rawKey) {
|
|
continue;
|
|
}
|
|
if (!(converted in next)) {
|
|
next[converted] = value;
|
|
}
|
|
if (normalizeLowercaseStringOrEmpty(rawKey).startsWith(`${CLAUDE_CLI_BACKEND_ID}/`)) {
|
|
delete next[rawKey];
|
|
}
|
|
migrated.push(converted);
|
|
}
|
|
|
|
return {
|
|
value: migrated.length > 0 || runtimeRefs.length > 0 ? next : models,
|
|
migrated,
|
|
runtimeRefs,
|
|
};
|
|
}
|
|
|
|
function seedClaudeCliAllowlist(
|
|
models: NonNullable<AgentDefaultsModels>,
|
|
selectedRefs: readonly string[] = [],
|
|
): NonNullable<AgentDefaultsModels> {
|
|
const next = { ...models };
|
|
const runtimeRefs = new Set<string>();
|
|
for (const ref of CLAUDE_CLI_DEFAULT_ALLOWLIST_REFS) {
|
|
const canonicalRef = toAnthropicModelRef(ref) ?? ref;
|
|
runtimeRefs.add(canonicalRef);
|
|
}
|
|
for (const ref of selectedRefs) {
|
|
runtimeRefs.add(ref);
|
|
}
|
|
for (const ref of runtimeRefs) {
|
|
next[ref] = modelEntryWithClaudeCliRuntime(next[ref]);
|
|
}
|
|
return next;
|
|
}
|
|
|
|
function selectClaudeCliRuntime(agentRuntime: AgentDefaultsRuntimePolicy | undefined) {
|
|
const currentRuntime = agentRuntime?.id?.trim();
|
|
if (currentRuntime && currentRuntime !== "auto") {
|
|
return agentRuntime;
|
|
}
|
|
return {
|
|
...agentRuntime,
|
|
id: CLAUDE_CLI_BACKEND_ID,
|
|
};
|
|
}
|
|
|
|
function modelEntryWithClaudeCliRuntime(entry: unknown): Record<string, unknown> {
|
|
const base = isRecord(entry) ? { ...entry } : {};
|
|
const currentRuntimeId = isRecord(base.agentRuntime) ? base.agentRuntime.id : undefined;
|
|
const currentRuntime =
|
|
typeof currentRuntimeId === "string" ? normalizeLowercaseStringOrEmpty(currentRuntimeId) : "";
|
|
if (currentRuntime && currentRuntime !== "auto") {
|
|
return base;
|
|
}
|
|
base.agentRuntime = {
|
|
...(isRecord(base.agentRuntime) ? base.agentRuntime : {}),
|
|
id: CLAUDE_CLI_BACKEND_ID,
|
|
};
|
|
return base;
|
|
}
|
|
|
|
export function hasClaudeCliAuth(options?: { allowKeychainPrompt?: boolean }): boolean {
|
|
return Boolean(
|
|
options?.allowKeychainPrompt === false
|
|
? readClaudeCliCredentialsForSetupNonInteractive()
|
|
: readClaudeCliCredentialsForSetup(),
|
|
);
|
|
}
|
|
|
|
function buildClaudeCliAuthProfiles(
|
|
credential?: ClaudeCliCredential | null,
|
|
): ProviderAuthResult["profiles"] {
|
|
if (!credential) {
|
|
return [];
|
|
}
|
|
if (credential.type === "oauth") {
|
|
return [
|
|
{
|
|
profileId: CLAUDE_CLI_PROFILE_ID,
|
|
credential: {
|
|
type: "oauth",
|
|
provider: CLAUDE_CLI_BACKEND_ID,
|
|
access: credential.access,
|
|
refresh: credential.refresh,
|
|
expires: credential.expires,
|
|
},
|
|
},
|
|
];
|
|
}
|
|
return [
|
|
{
|
|
profileId: CLAUDE_CLI_PROFILE_ID,
|
|
credential: {
|
|
type: "token",
|
|
provider: CLAUDE_CLI_BACKEND_ID,
|
|
token: credential.token,
|
|
expires: credential.expires,
|
|
},
|
|
},
|
|
];
|
|
}
|
|
|
|
export function buildAnthropicCliMigrationResult(
|
|
config: OpenClawConfig,
|
|
credential?: ClaudeCliCredential | null,
|
|
): ProviderAuthResult {
|
|
const defaults = config.agents?.defaults;
|
|
const rewrittenModel = rewriteModelSelection(defaults?.model);
|
|
const rewrittenModels = rewriteModelEntryMap(defaults?.models);
|
|
const existingModels = (rewrittenModels.value ??
|
|
defaults?.models ??
|
|
{}) as NonNullable<AgentDefaultsModels>;
|
|
const nextModels = seedClaudeCliAllowlist(existingModels, [
|
|
...rewrittenModel.runtimeRefs,
|
|
...rewrittenModels.runtimeRefs,
|
|
...rewrittenModels.migrated,
|
|
]);
|
|
const defaultModel = rewrittenModel.primary ?? "anthropic/claude-opus-4-7";
|
|
|
|
return {
|
|
profiles: buildClaudeCliAuthProfiles(credential),
|
|
configPatch: {
|
|
agents: {
|
|
defaults: {
|
|
...(rewrittenModel.changed ? { model: rewrittenModel.value } : {}),
|
|
agentRuntime: selectClaudeCliRuntime(defaults?.agentRuntime),
|
|
models: nextModels,
|
|
},
|
|
},
|
|
},
|
|
// Rewrites `claude-cli/*` -> `anthropic/*`; merge would keep stale keys.
|
|
replaceDefaultModels: true,
|
|
defaultModel,
|
|
notes: [
|
|
"Claude CLI auth detected; kept Anthropic model refs and selected the local Claude CLI runtime.",
|
|
"Existing Anthropic auth profiles are kept for rollback.",
|
|
...(rewrittenModels.migrated.length > 0
|
|
? [`Migrated allowlist entries: ${rewrittenModels.migrated.join(", ")}.`]
|
|
: []),
|
|
],
|
|
};
|
|
}
|