mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-02 09:14:54 +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
319 lines
12 KiB
TypeScript
319 lines
12 KiB
TypeScript
import { isDeepStrictEqual } from "node:util";
|
|
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope-config.js";
|
|
import {
|
|
clearRuntimeAuthProfileStoreSnapshots,
|
|
loadAuthProfileStoreForSecretsRuntime,
|
|
loadAuthProfileStoreWithoutExternalProfiles,
|
|
} from "../agents/auth-profiles.js";
|
|
import type { AuthProfileStore } from "../agents/auth-profiles/types.js";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import type { PluginOrigin } from "../plugins/plugin-origin.types.js";
|
|
import { uniqueStrings } from "../shared/string-normalization.js";
|
|
import { resolveUserPath } from "../utils.js";
|
|
import {
|
|
canUseSecretsRuntimeFastPath,
|
|
collectCandidateAgentDirs,
|
|
createEmptyRuntimeWebToolsMetadata,
|
|
mergeSecretsRuntimeEnv,
|
|
resolveRefreshAgentDirs,
|
|
} from "./runtime-fast-path.js";
|
|
import {
|
|
activateSecretsRuntimeSnapshotState,
|
|
clearSecretsRuntimeSnapshot as clearSecretsRuntimeSnapshotState,
|
|
getActiveSecretsRuntimeEnv as getActiveSecretsRuntimeEnvState,
|
|
getActiveSecretsRuntimeRefreshContext,
|
|
getActiveSecretsRuntimeSnapshot as getActiveSecretsRuntimeSnapshotState,
|
|
getLiveSecretsRuntimeAuthStores,
|
|
getPreparedSecretsRuntimeSnapshotRefreshContext,
|
|
registerSecretsRuntimeStateClearHook,
|
|
setPreparedSecretsRuntimeSnapshotRefreshContext,
|
|
type PreparedSecretsRuntimeSnapshot,
|
|
type SecretsRuntimeRefreshContext,
|
|
} from "./runtime-state.js";
|
|
import { getActiveRuntimeWebToolsMetadata as getActiveRuntimeWebToolsMetadataFromState } from "./runtime-web-tools-state.js";
|
|
import type { RuntimeWebToolsMetadata } from "./runtime-web-tools.types.js";
|
|
|
|
export type { SecretResolverWarning } from "./runtime-shared.js";
|
|
export type { PreparedSecretsRuntimeSnapshot } from "./runtime-state.js";
|
|
|
|
registerSecretsRuntimeStateClearHook(clearRuntimeAuthProfileStoreSnapshots);
|
|
|
|
let runtimeManifestPromise: Promise<typeof import("./runtime-manifest.runtime.js")> | null = null;
|
|
let runtimePreparePromise: Promise<typeof import("./runtime-prepare.runtime.js")> | null = null;
|
|
|
|
function loadRuntimeManifestHelpers() {
|
|
runtimeManifestPromise ??= import("./runtime-manifest.runtime.js");
|
|
return runtimeManifestPromise;
|
|
}
|
|
|
|
function loadRuntimePrepareHelpers() {
|
|
runtimePreparePromise ??= import("./runtime-prepare.runtime.js");
|
|
return runtimePreparePromise;
|
|
}
|
|
|
|
async function resolveLoadablePluginOrigins(params: {
|
|
config: OpenClawConfig;
|
|
env: NodeJS.ProcessEnv;
|
|
}): Promise<ReadonlyMap<string, PluginOrigin>> {
|
|
const workspaceDir = resolveAgentWorkspaceDir(
|
|
params.config,
|
|
resolveDefaultAgentId(params.config),
|
|
);
|
|
const { listPluginOriginsFromMetadataSnapshot, loadPluginMetadataSnapshot } =
|
|
await loadRuntimeManifestHelpers();
|
|
const snapshot = loadPluginMetadataSnapshot({
|
|
config: params.config,
|
|
workspaceDir,
|
|
env: params.env,
|
|
});
|
|
return listPluginOriginsFromMetadataSnapshot(snapshot);
|
|
}
|
|
|
|
function hasConfiguredPluginEntries(config: OpenClawConfig): boolean {
|
|
const entries = config.plugins?.entries;
|
|
return (
|
|
!!entries &&
|
|
typeof entries === "object" &&
|
|
!Array.isArray(entries) &&
|
|
Object.keys(entries).length > 0
|
|
);
|
|
}
|
|
|
|
function hasConfiguredChannelEntries(config: OpenClawConfig): boolean {
|
|
const channels = config.channels;
|
|
return (
|
|
!!channels &&
|
|
typeof channels === "object" &&
|
|
!Array.isArray(channels) &&
|
|
Object.keys(channels).some((channelId) => channelId !== "defaults")
|
|
);
|
|
}
|
|
|
|
export async function prepareSecretsRuntimeSnapshot(params: {
|
|
config: OpenClawConfig;
|
|
env?: NodeJS.ProcessEnv;
|
|
agentDirs?: string[];
|
|
includeAuthStoreRefs?: boolean;
|
|
loadAuthStore?: (agentDir?: string) => AuthProfileStore;
|
|
/** Test override for discovered loadable plugins and their origins. */
|
|
loadablePluginOrigins?: ReadonlyMap<string, PluginOrigin>;
|
|
}): Promise<PreparedSecretsRuntimeSnapshot> {
|
|
const runtimeEnv = mergeSecretsRuntimeEnv(params.env);
|
|
const sourceConfig = structuredClone(params.config);
|
|
const resolvedConfig = structuredClone(params.config);
|
|
const includeAuthStoreRefs = params.includeAuthStoreRefs ?? true;
|
|
let authStores: Array<{ agentDir: string; store: AuthProfileStore }> = [];
|
|
const fastPathLoadAuthStore = params.loadAuthStore ?? loadAuthProfileStoreWithoutExternalProfiles;
|
|
const candidateDirs = params.agentDirs?.length
|
|
? uniqueStrings(params.agentDirs.map((entry) => resolveUserPath(entry, runtimeEnv)))
|
|
: collectCandidateAgentDirs(resolvedConfig, runtimeEnv);
|
|
if (includeAuthStoreRefs) {
|
|
for (const agentDir of candidateDirs) {
|
|
authStores.push({
|
|
agentDir,
|
|
store: structuredClone(fastPathLoadAuthStore(agentDir)),
|
|
});
|
|
}
|
|
}
|
|
if (canUseSecretsRuntimeFastPath({ sourceConfig, authStores })) {
|
|
const snapshot = {
|
|
sourceConfig,
|
|
config: resolvedConfig,
|
|
authStores,
|
|
warnings: [],
|
|
webTools: createEmptyRuntimeWebToolsMetadata(),
|
|
};
|
|
setPreparedSecretsRuntimeSnapshotRefreshContext(snapshot, {
|
|
env: runtimeEnv,
|
|
explicitAgentDirs: params.agentDirs?.length ? [...candidateDirs] : null,
|
|
includeAuthStoreRefs,
|
|
loadAuthStore: fastPathLoadAuthStore,
|
|
loadablePluginOrigins: params.loadablePluginOrigins ?? new Map<string, PluginOrigin>(),
|
|
});
|
|
return snapshot;
|
|
}
|
|
|
|
const {
|
|
applyResolvedAssignments,
|
|
collectAuthStoreAssignments,
|
|
collectConfigAssignments,
|
|
createResolverContext,
|
|
resolveRuntimeWebTools,
|
|
resolveSecretRefValues,
|
|
} = await loadRuntimePrepareHelpers();
|
|
const loadablePluginOrigins =
|
|
params.loadablePluginOrigins ??
|
|
(hasConfiguredPluginEntries(sourceConfig) || hasConfiguredChannelEntries(sourceConfig)
|
|
? await resolveLoadablePluginOrigins({ config: sourceConfig, env: runtimeEnv })
|
|
: new Map<string, PluginOrigin>());
|
|
const context = createResolverContext({
|
|
sourceConfig,
|
|
env: runtimeEnv,
|
|
});
|
|
|
|
collectConfigAssignments({
|
|
config: resolvedConfig,
|
|
context,
|
|
loadablePluginOrigins,
|
|
});
|
|
|
|
if (includeAuthStoreRefs) {
|
|
const loadAuthStore = params.loadAuthStore ?? loadAuthProfileStoreForSecretsRuntime;
|
|
if (!params.loadAuthStore) {
|
|
authStores = candidateDirs.map((agentDir) => ({
|
|
agentDir,
|
|
store: structuredClone(loadAuthStore(agentDir)),
|
|
}));
|
|
}
|
|
for (const entry of authStores) {
|
|
collectAuthStoreAssignments({
|
|
store: entry.store,
|
|
context,
|
|
agentDir: entry.agentDir,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (context.assignments.length > 0) {
|
|
const refs = context.assignments.map((assignment) => assignment.ref);
|
|
const resolved = await resolveSecretRefValues(refs, {
|
|
config: sourceConfig,
|
|
env: context.env,
|
|
cache: context.cache,
|
|
});
|
|
applyResolvedAssignments({
|
|
assignments: context.assignments,
|
|
resolved,
|
|
});
|
|
}
|
|
|
|
const snapshot = {
|
|
sourceConfig,
|
|
config: resolvedConfig,
|
|
authStores,
|
|
warnings: context.warnings,
|
|
webTools: await resolveRuntimeWebTools({
|
|
sourceConfig,
|
|
resolvedConfig,
|
|
context,
|
|
}),
|
|
};
|
|
setPreparedSecretsRuntimeSnapshotRefreshContext(snapshot, {
|
|
env: runtimeEnv,
|
|
explicitAgentDirs: params.agentDirs?.length ? [...candidateDirs] : null,
|
|
includeAuthStoreRefs,
|
|
loadAuthStore: params.loadAuthStore ?? loadAuthProfileStoreForSecretsRuntime,
|
|
loadablePluginOrigins,
|
|
});
|
|
return snapshot;
|
|
}
|
|
|
|
export function activateSecretsRuntimeSnapshot(snapshot: PreparedSecretsRuntimeSnapshot): void {
|
|
const refreshContext =
|
|
getPreparedSecretsRuntimeSnapshotRefreshContext(snapshot) ??
|
|
getActiveSecretsRuntimeRefreshContext() ??
|
|
({
|
|
env: { ...process.env } as Record<string, string | undefined>,
|
|
explicitAgentDirs: null,
|
|
includeAuthStoreRefs: snapshot.authStores.length > 0,
|
|
loadAuthStore: loadAuthProfileStoreForSecretsRuntime,
|
|
loadablePluginOrigins: new Map<string, PluginOrigin>(),
|
|
} satisfies SecretsRuntimeRefreshContext);
|
|
const coercePreflightSnapshot = (
|
|
value: unknown,
|
|
sourceConfig: OpenClawConfig,
|
|
): PreparedSecretsRuntimeSnapshot | null => {
|
|
if (!value || typeof value !== "object") {
|
|
return null;
|
|
}
|
|
const candidate = value as PreparedSecretsRuntimeSnapshot;
|
|
return isDeepStrictEqual(candidate.sourceConfig, sourceConfig) ? candidate : null;
|
|
};
|
|
activateSecretsRuntimeSnapshotState({
|
|
snapshot,
|
|
refreshContext,
|
|
refreshHandler: {
|
|
preflight: async ({ sourceConfig, includeAuthStoreRefs }) => {
|
|
const activeRefreshContext = getActiveSecretsRuntimeRefreshContext();
|
|
const activeSnapshot = getActiveSecretsRuntimeSnapshotState();
|
|
if (!activeSnapshot || !activeRefreshContext) {
|
|
return false;
|
|
}
|
|
return await prepareSecretsRuntimeSnapshot({
|
|
config: sourceConfig,
|
|
env: activeRefreshContext.env,
|
|
agentDirs: resolveRefreshAgentDirs(sourceConfig, activeRefreshContext),
|
|
includeAuthStoreRefs: includeAuthStoreRefs ?? activeRefreshContext.includeAuthStoreRefs,
|
|
loadablePluginOrigins: activeRefreshContext.loadablePluginOrigins,
|
|
...(activeRefreshContext.loadAuthStore
|
|
? { loadAuthStore: activeRefreshContext.loadAuthStore }
|
|
: {}),
|
|
});
|
|
},
|
|
refresh: async ({ sourceConfig, includeAuthStoreRefs, preflightResult }) => {
|
|
const activeRefreshContext = getActiveSecretsRuntimeRefreshContext();
|
|
const activeSnapshot = getActiveSecretsRuntimeSnapshotState();
|
|
if (!activeSnapshot || !activeRefreshContext) {
|
|
return false;
|
|
}
|
|
const oneShotSkipAuthStoreRefs =
|
|
includeAuthStoreRefs === false && activeRefreshContext.includeAuthStoreRefs;
|
|
const refreshed =
|
|
coercePreflightSnapshot(preflightResult, sourceConfig) ??
|
|
(await prepareSecretsRuntimeSnapshot({
|
|
config: sourceConfig,
|
|
env: activeRefreshContext.env,
|
|
agentDirs: resolveRefreshAgentDirs(sourceConfig, activeRefreshContext),
|
|
includeAuthStoreRefs: includeAuthStoreRefs ?? activeRefreshContext.includeAuthStoreRefs,
|
|
loadablePluginOrigins: activeRefreshContext.loadablePluginOrigins,
|
|
...(activeRefreshContext.loadAuthStore
|
|
? { loadAuthStore: activeRefreshContext.loadAuthStore }
|
|
: {}),
|
|
}));
|
|
if (oneShotSkipAuthStoreRefs) {
|
|
refreshed.authStores = getLiveSecretsRuntimeAuthStores();
|
|
setPreparedSecretsRuntimeSnapshotRefreshContext(refreshed, activeRefreshContext);
|
|
}
|
|
activateSecretsRuntimeSnapshot(refreshed);
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function refreshActiveSecretsRuntimeSnapshot(): Promise<boolean> {
|
|
const activeSnapshot = getActiveSecretsRuntimeSnapshotState();
|
|
const activeRefreshContext = getActiveSecretsRuntimeRefreshContext();
|
|
if (!activeSnapshot || !activeRefreshContext) {
|
|
return false;
|
|
}
|
|
const refreshed = await prepareSecretsRuntimeSnapshot({
|
|
config: activeSnapshot.sourceConfig,
|
|
env: activeRefreshContext.env,
|
|
agentDirs: resolveRefreshAgentDirs(activeSnapshot.sourceConfig, activeRefreshContext),
|
|
includeAuthStoreRefs: activeRefreshContext.includeAuthStoreRefs,
|
|
loadablePluginOrigins: activeRefreshContext.loadablePluginOrigins,
|
|
...(activeRefreshContext.loadAuthStore
|
|
? { loadAuthStore: activeRefreshContext.loadAuthStore }
|
|
: {}),
|
|
});
|
|
activateSecretsRuntimeSnapshot(refreshed);
|
|
return true;
|
|
}
|
|
|
|
export function getActiveSecretsRuntimeSnapshot(): PreparedSecretsRuntimeSnapshot | null {
|
|
return getActiveSecretsRuntimeSnapshotState();
|
|
}
|
|
|
|
export function getActiveSecretsRuntimeEnv(): NodeJS.ProcessEnv {
|
|
return getActiveSecretsRuntimeEnvState();
|
|
}
|
|
|
|
export function getActiveRuntimeWebToolsMetadata(): RuntimeWebToolsMetadata | null {
|
|
return getActiveRuntimeWebToolsMetadataFromState();
|
|
}
|
|
|
|
export function clearSecretsRuntimeSnapshot(): void {
|
|
clearSecretsRuntimeSnapshotState();
|
|
}
|