mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-31 12:37:59 +00:00
Prefer a validated newer stable same-major Codex CLI on PATH for managed turns, and carry the selected client version into ChatGPT model discovery. Return to the bundled runtime when it catches up or the installed candidate fails qualification/startup. Bound cold selection, retain shared fallback ownership and explicit/desktop precedence, isolate probe storage, and skip selection for unrelated catalog and auth-only requests. Production-authenticated ChatGPT discovery and turns remain unproven; same-major post-initialize protocol compatibility is an accepted residual risk. Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com> Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
105 lines
3.4 KiB
TypeScript
105 lines
3.4 KiB
TypeScript
// Native-loader projects replace execArgv, so their setup also owns SQLite admission.
|
|
import "./vitest/vitest.sqlite-preload.mts";
|
|
import { vi } from "vitest";
|
|
import { installProcessWarningFilter } from "../src/infra/warning-filter.js";
|
|
import { withIsolatedTestHome } from "./test-env.js";
|
|
|
|
const openAiCodexTokenRefreshTestHook = "__OPENCLAW_TEST_REFRESH_OPENAI_CODEX_TOKEN__";
|
|
type GlobalWithOpenAiCodexTokenRefreshTestHook = typeof globalThis & {
|
|
[openAiCodexTokenRefreshTestHook]?: ((...args: unknown[]) => unknown) | undefined;
|
|
};
|
|
|
|
vi.mock("../src/llm/oauth.js", () => ({
|
|
getOAuthApiKey: () => undefined,
|
|
getOAuthProviders: () => [],
|
|
loginOpenAICodex: vi.fn(),
|
|
refreshOpenAICodexToken: vi.fn((...args: unknown[]) =>
|
|
(globalThis as GlobalWithOpenAiCodexTokenRefreshTestHook)[openAiCodexTokenRefreshTestHook]?.(
|
|
...args,
|
|
),
|
|
),
|
|
}));
|
|
|
|
vi.mock("@mariozechner/clipboard", () => ({
|
|
availableFormats: () => [],
|
|
getText: async () => "",
|
|
setText: async () => {},
|
|
hasText: () => false,
|
|
getImageBinary: async () => [],
|
|
getImageBase64: async () => "",
|
|
setImageBinary: async () => {},
|
|
setImageBase64: async () => {},
|
|
hasImage: () => false,
|
|
getHtml: async () => "",
|
|
setHtml: async () => {},
|
|
hasHtml: () => false,
|
|
getRtf: async () => "",
|
|
setRtf: async () => {},
|
|
hasRtf: () => false,
|
|
clear: async () => {},
|
|
watch: () => {},
|
|
callThreadsafeFunction: () => {},
|
|
}));
|
|
|
|
// Ensure Vitest environment is properly set.
|
|
process.env.VITEST = "true";
|
|
// Tests frequently point bundled plugin discovery at temp fixture roots. Production still rejects
|
|
// arbitrary OPENCLAW_BUNDLED_PLUGINS_DIR overrides unless this Vitest-only opt-in is present.
|
|
process.env.OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR ??= "1";
|
|
// The Codex plugin selects a newer PATH-installed Codex once per process. Tests
|
|
// must not probe the developer's own install; selection tests reset this slot.
|
|
(globalThis as Record<PropertyKey, unknown>)[Symbol.for("openclaw.codexInstalledAppServer")] = {
|
|
selection: Promise.resolve(undefined),
|
|
};
|
|
// Vitest fork workers can load transitive lockfile helpers many times per worker.
|
|
// Raise listener budget to avoid noisy MaxListeners warnings and warning-stack overhead.
|
|
const TEST_PROCESS_MAX_LISTENERS = 256;
|
|
if (process.getMaxListeners() > 0 && process.getMaxListeners() < TEST_PROCESS_MAX_LISTENERS) {
|
|
process.setMaxListeners(TEST_PROCESS_MAX_LISTENERS);
|
|
}
|
|
|
|
type SharedTestSetupOptions = {
|
|
loadProfileEnv?: boolean;
|
|
};
|
|
|
|
const SHARED_TEST_SETUP = Symbol.for("openclaw.sharedTestSetup");
|
|
|
|
type SharedTestSetupHandle = {
|
|
cleanup: () => void;
|
|
tempHome: string;
|
|
};
|
|
|
|
export function installSharedTestSetup(options?: SharedTestSetupOptions): {
|
|
cleanup: () => void;
|
|
tempHome: string;
|
|
} {
|
|
const globalState = globalThis as typeof globalThis & {
|
|
[SHARED_TEST_SETUP]?: SharedTestSetupHandle;
|
|
};
|
|
const existing = globalState[SHARED_TEST_SETUP];
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
|
|
const testEnv = withIsolatedTestHome({
|
|
loadProfileEnv: options?.loadProfileEnv,
|
|
});
|
|
installProcessWarningFilter();
|
|
|
|
let cleaned = false;
|
|
const handle: SharedTestSetupHandle = {
|
|
tempHome: testEnv.tempHome,
|
|
cleanup: () => {
|
|
if (cleaned) {
|
|
return;
|
|
}
|
|
cleaned = true;
|
|
process.removeListener("exit", handle.cleanup);
|
|
testEnv.cleanup();
|
|
delete globalState[SHARED_TEST_SETUP];
|
|
},
|
|
};
|
|
process.once("exit", handle.cleanup);
|
|
globalState[SHARED_TEST_SETUP] = handle;
|
|
return handle;
|
|
}
|