mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-17 01:01:37 +00:00
* feat(codex): add native session supervision * fix(codex): harden supervision integration * fix(codex): preserve locked harness ownership * fix(codex): fence native session archive * fix(codex): revalidate archive binding ownership * feat(codex): integrate supervision runtime * feat(sessions): preserve harness-owned execution * feat(sessions): persist harness ownership invariants * feat(gateway): enforce harness-owned sessions * feat(setup): enable detected Codex supervision * feat(mac): expose supervised Codex sessions * feat(ui): make Codex sessions actionable * docs(codex): document session supervision * test(codex): cover integration ownership * chore(i18n): refresh supervision inventories * fix(setup): finalize Codex activation atomically * test(codex): narrow binding store update * fix(sessions): preserve legacy model locks * test(macos): serialize Codex catalog fixtures * fix(sessions): preserve legacy lock admission * chore(i18n): reconcile supervision metadata * test(sessions): mark legacy lock fixture * fix(macos): drain final Codex catalog frame * docs: leave supervision note to release * style(macos): satisfy Codex catalog type length * chore: record session accessor seam owners * fix(macos): honor configured Codex supervision * fix(codex): preserve harness-owned model locks * fix(codex): satisfy supervision lint gates * chore(i18n): refresh native supervision inventory * fix(codex): align supervision validation contracts * fix(codex): close supervision boundary gaps * fix(codex): preserve supervision activation contracts * fix(codex): dispose standalone supervision runtime * fix(codex): pin supervised source connection * fix(plugins): bind delegated runs to exact session target * fix(codex): scope supervised sessions to configured agents * fix(codex): fingerprint effective supervision home * fix(codex): normalize supervision plugin policy * fix(codex): keep supervised bindings stable across upgrades * fix(codex): guard all supervised binding connections * fix(codex): preserve catalog filters and pending CAS identity * fix(codex): preserve supervision identity for diagnostics * fix(codex): bind uncertain commits to supervision connection * fix(codex): satisfy supervision type boundaries * fix(macos): reconcile current main validation * fix(codex): handle absent runtime config in supervision * fix(doctor): own local audio acceleration check * fix(codex): satisfy integration lint gates * fix(codex): satisfy lifecycle safety guards
158 lines
5.6 KiB
TypeScript
158 lines
5.6 KiB
TypeScript
/**
|
|
* Builds stable Codex plugin/app inventory cache keys from app-server startup,
|
|
* auth, account, and version inputs without storing secret material.
|
|
*/
|
|
import { createHash } from "node:crypto";
|
|
import { createRequire } from "node:module";
|
|
import { OPENCLAW_VERSION } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
import { readPluginPackageVersion } from "openclaw/plugin-sdk/extension-shared";
|
|
import {
|
|
buildCodexAppInventoryCacheKey,
|
|
type CodexAppInventoryCacheKeyInput,
|
|
} from "./app-inventory-cache.js";
|
|
import { resolveCodexAppServerHomeDir } from "./auth-bridge.js";
|
|
import type { CodexAppServerRuntimeIdentity } from "./client.js";
|
|
import {
|
|
resolveCodexAppServerUserHomeDir,
|
|
type CodexAppServerRuntimeOptions,
|
|
type CodexAppServerStartOptions,
|
|
} from "./config.js";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const CODEX_PLUGIN_VERSION = readPluginPackageVersion({ require });
|
|
|
|
/** Inputs that identify the Codex app inventory cache scope for one runtime. */
|
|
export type CodexPluginAppCacheKeyParams = Omit<
|
|
CodexAppInventoryCacheKeyInput,
|
|
"codexHome" | "endpoint"
|
|
> & {
|
|
appServer: Pick<CodexAppServerRuntimeOptions, "start">;
|
|
agentDir?: string;
|
|
runtimeIdentity?: CodexAppServerRuntimeIdentity;
|
|
};
|
|
|
|
/** Builds the full app inventory cache key for Codex plugin/app discovery. */
|
|
export function buildCodexPluginAppCacheKey(params: CodexPluginAppCacheKeyParams): string {
|
|
return buildCodexAppInventoryCacheKey(
|
|
{
|
|
codexHome:
|
|
params.runtimeIdentity?.codexHome ??
|
|
resolveCodexPluginAppCacheCodexHome(params.appServer, params.agentDir),
|
|
endpoint: resolveCodexPluginAppCacheEndpoint(params.appServer),
|
|
authProfileId: params.authProfileId,
|
|
accountId: params.accountId,
|
|
envApiKeyFingerprint: params.envApiKeyFingerprint,
|
|
appServerVersion: params.appServerVersion ?? params.runtimeIdentity?.serverVersion,
|
|
runtimeIdentity: params.runtimeIdentity,
|
|
},
|
|
OPENCLAW_VERSION,
|
|
CODEX_PLUGIN_VERSION,
|
|
);
|
|
}
|
|
|
|
/** Builds a durable thread-binding fingerprint for one initialized app-server runtime. */
|
|
export function buildCodexAppServerRuntimeFingerprint(params: {
|
|
appServer: Pick<
|
|
CodexAppServerRuntimeOptions,
|
|
"start" | "connectionClass" | "remoteWorkspaceRoot"
|
|
>;
|
|
appServerVersion?: string;
|
|
runtimeIdentity?: CodexAppServerRuntimeIdentity;
|
|
}): string {
|
|
return JSON.stringify({
|
|
endpoint: resolveCodexPluginAppCacheEndpoint(params.appServer),
|
|
connectionClass: params.appServer.connectionClass,
|
|
remoteWorkspaceRoot: params.appServer.remoteWorkspaceRoot ?? null,
|
|
appServerVersion: params.appServerVersion ?? params.runtimeIdentity?.serverVersion ?? null,
|
|
runtimeIdentity: params.runtimeIdentity ?? null,
|
|
});
|
|
}
|
|
|
|
/** Fingerprints the configured connection that owns a supervised source thread. */
|
|
export function buildCodexAppServerConnectionFingerprint(
|
|
appServer: Pick<
|
|
CodexAppServerRuntimeOptions,
|
|
"start" | "connectionClass" | "remoteWorkspaceRoot"
|
|
>,
|
|
agentDir?: string,
|
|
): string {
|
|
return JSON.stringify({
|
|
endpoint: resolveCodexPluginAppCacheEndpoint(appServer),
|
|
connectionClass: appServer.connectionClass,
|
|
remoteWorkspaceRoot: appServer.remoteWorkspaceRoot ?? null,
|
|
homeScope: appServer.start.homeScope ?? null,
|
|
codexHome: resolveCodexAppServerConnectionHome(appServer.start, agentDir),
|
|
cwd: appServer.start.cwd ?? null,
|
|
});
|
|
}
|
|
|
|
function resolveCodexAppServerConnectionHome(
|
|
start: CodexAppServerStartOptions,
|
|
agentDir?: string,
|
|
): string | null {
|
|
const configured = start.env?.CODEX_HOME?.trim();
|
|
if (configured) {
|
|
return configured;
|
|
}
|
|
if (start.transport === "unix" && (!start.url || start.url === "unix://")) {
|
|
return resolveCodexAppServerUserHomeDir(start.env ?? process.env);
|
|
}
|
|
if (start.transport !== "stdio") {
|
|
return null;
|
|
}
|
|
if (start.homeScope === "user") {
|
|
return resolveCodexAppServerUserHomeDir(process.env);
|
|
}
|
|
return agentDir ? resolveCodexAppServerHomeDir(agentDir) : null;
|
|
}
|
|
|
|
/** Serializes app-server endpoint identity, including credential fingerprints. */
|
|
export function resolveCodexPluginAppCacheEndpoint(
|
|
appServer: Pick<CodexAppServerRuntimeOptions, "start">,
|
|
): string {
|
|
return JSON.stringify({
|
|
transport: appServer.start.transport,
|
|
command: appServer.start.command,
|
|
args: appServer.start.args,
|
|
url: appServer.start.url ?? null,
|
|
credentialFingerprint: fingerprintCodexPluginAppCacheCredentials(appServer.start),
|
|
});
|
|
}
|
|
|
|
/** Resolves the CODEX_HOME value that scopes local app-server inventory. */
|
|
export function resolveCodexPluginAppCacheCodexHome(
|
|
appServer: Pick<CodexAppServerRuntimeOptions, "start">,
|
|
agentDir?: string,
|
|
): string | undefined {
|
|
const configuredCodexHome = appServer.start.env?.CODEX_HOME?.trim();
|
|
if (configuredCodexHome) {
|
|
return configuredCodexHome;
|
|
}
|
|
return appServer.start.transport === "stdio" && agentDir
|
|
? resolveCodexAppServerHomeDir(agentDir)
|
|
: undefined;
|
|
}
|
|
|
|
function fingerprintCodexPluginAppCacheCredentials(
|
|
startOptions: CodexAppServerStartOptions,
|
|
): string | null {
|
|
const authToken = startOptions.authToken ?? "";
|
|
const headers = Object.entries(startOptions.headers)
|
|
.map(([key, value]) => [key.toLowerCase(), value] as const)
|
|
.toSorted(([left], [right]) => left.localeCompare(right));
|
|
if (!authToken && headers.length === 0) {
|
|
return null;
|
|
}
|
|
const hash = createHash("sha256");
|
|
hash.update("openclaw:codex:plugin-app-cache-credentials:v1");
|
|
hash.update("\0");
|
|
hash.update(authToken);
|
|
for (const [key, value] of headers) {
|
|
hash.update("\0");
|
|
hash.update(key);
|
|
hash.update("\0");
|
|
hash.update(value);
|
|
}
|
|
return `sha256:${hash.digest("hex")}`;
|
|
}
|