mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-13 23:29:34 +08:00
* chore(types): add declaration files for scripts/lib and scripts/e2e modules
* chore(types): add declaration files for top-level script modules (a-m)
* chore(types): add declaration files for top-level script modules (n-z)
* test: use a non-secret-shaped gateway token fixture
* test: type ci workflow guard helpers for the root test lane
* chore(tooling): typecheck root test/** with a dedicated tsgo lane
- test/tsconfig/tsconfig.test.root.json: root-test program (strict unused checks,
fixtures excluded; two Docker E2E clients that import built dist/** stay out,
same rationale as the scripts/e2e exclusion in tsconfig.scripts.json)
- tsgo:test:root wired into tsgo:test, check:test-types, scripts/check.mjs, and
the ci.yml test-types shard, mirroring the tsgo:scripts lane (#104348)
- changed-lane routing: test/**/*.ts (excluding fixtures) and the lane tsconfig
now trigger 'typecheck test root' in check:changed; previously test/ paths ran
lint only, so harness type errors surfaced first in CI (#104287 envDir case)
- burn down all 1071 latent type errors in the program: precise param/local
types across test/scripts, test/vitest, test/e2e, and transitive scripts/e2e
program members; 205 sibling .d.mts declaration files for imported .mjs
modules (committed separately); zero any, zero ts-expect-error
- resolve the pre-existing testing star-export ambiguity in
scripts/e2e/parallels/common.ts with an explicit re-export
Closes #104388
* chore(types): correct declaration fidelity per structured review
- re-derive 51 .d.mts files from implementation data flow instead of
initializers: fix a wrong never return (runTestProjectsDelegation returns
the child), add encoding-sensitive exec/spawn overloads (plain-gh), restore
the full release profile union, make parsed paths string | null, add missing
parseArgs fields via help/non-help unions, add a missing sibling declaration
(budget-number-args), drop 15 unused lint directives
- precise install-record/tuple typing removes the type-aware oxlint
regressions the first declarations caused in scripts/e2e implementations
- route .mts declaration edits under test/ to the testRoot lane and reference
the test-root project from tsconfig.projects.json so tsgo:all covers it
(closes both review findings against the lane wiring)
* chore(scripts): keep telegram runner dist typing structural for the boundary guard
* chore(types): declare runtime pack and gateway readiness exports added on main
* test: pin the importTargetPlan form of the plugin-contract plan import
The guard expectation still referenced the raw await import( form that
7ae5996bb3 (#103975) replaced with the importTargetPlan fallback helper;
the assertion fails on current main.
128 lines
4.8 KiB
JavaScript
128 lines
4.8 KiB
JavaScript
// Assertions for Codex on-demand plugin E2E scenarios.
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { DatabaseSync } from "node:sqlite";
|
|
import { assertOpenAiEnvAuthProfileStore } from "../auth-profile-store-assertions.mjs";
|
|
import {
|
|
assertPathInside,
|
|
configPath,
|
|
findPackageJson,
|
|
managedNpmRoot,
|
|
npmProjectRootForInstalledPackage,
|
|
readInstallRecords,
|
|
readJson,
|
|
stateDir,
|
|
} from "../codex-install-utils.mjs";
|
|
|
|
const cfg = readJson(configPath());
|
|
const inspect = readJson("/tmp/openclaw-codex-inspect.json");
|
|
const records = readInstallRecords(cfg.plugins?.installs);
|
|
const codexRecord = records.codex || inspect.install;
|
|
if (!codexRecord) {
|
|
throw new Error(`missing codex install record: ${JSON.stringify(records)}`);
|
|
}
|
|
if (codexRecord.source !== "npm") {
|
|
throw new Error(`expected npm codex install record, got ${codexRecord.source}`);
|
|
}
|
|
if (!codexRecord.spec?.includes("@openclaw/codex")) {
|
|
throw new Error(`expected @openclaw/codex install spec, got ${codexRecord.spec}`);
|
|
}
|
|
|
|
const npmRoot = managedNpmRoot();
|
|
const installPath = (codexRecord.installPath || "").replace(/^~(?=$|\/)/u, process.env.HOME);
|
|
if (!installPath) {
|
|
throw new Error(`missing codex installPath: ${JSON.stringify(codexRecord)}`);
|
|
}
|
|
assertPathInside(npmRoot, installPath, "codex install path");
|
|
|
|
const codexPackageJson = path.join(installPath, "package.json");
|
|
if (!fs.existsSync(codexPackageJson)) {
|
|
throw new Error(`missing npm-installed @openclaw/codex package: ${codexPackageJson}`);
|
|
}
|
|
const codexPackage = readJson(codexPackageJson);
|
|
if (codexPackage.name !== "@openclaw/codex") {
|
|
throw new Error(`unexpected codex package name: ${codexPackage.name}`);
|
|
}
|
|
|
|
const npmProjectRoot = npmProjectRootForInstalledPackage(installPath, "@openclaw/codex");
|
|
const openAiCodexPackageJson = findPackageJson("@openai/codex", [
|
|
installPath,
|
|
npmProjectRoot,
|
|
npmRoot,
|
|
]);
|
|
if (!openAiCodexPackageJson) {
|
|
throw new Error("missing @openai/codex dependency under managed npm root");
|
|
}
|
|
assertPathInside(npmRoot, openAiCodexPackageJson, "@openai/codex dependency");
|
|
const openAiCodexPackage = readJson(openAiCodexPackageJson);
|
|
const codexBinPath =
|
|
typeof openAiCodexPackage.bin === "string"
|
|
? openAiCodexPackage.bin
|
|
: openAiCodexPackage.bin && typeof openAiCodexPackage.bin.codex === "string"
|
|
? openAiCodexPackage.bin.codex
|
|
: undefined;
|
|
if (!codexBinPath) {
|
|
throw new Error(`@openai/codex package has no codex bin: ${openAiCodexPackageJson}`);
|
|
}
|
|
const codexBin = path.resolve(path.dirname(openAiCodexPackageJson), codexBinPath);
|
|
if (!fs.existsSync(codexBin)) {
|
|
throw new Error(`missing managed Codex binary: ${codexBin}`);
|
|
}
|
|
assertPathInside(npmRoot, codexBin, "managed Codex binary");
|
|
|
|
const list = readJson("/tmp/openclaw-plugins-list.json");
|
|
const plugin = (list.plugins || []).find((entry) => entry.id === "codex");
|
|
if (!plugin || plugin.enabled !== true || plugin.status !== "loaded") {
|
|
throw new Error(`codex plugin was not enabled+loaded: ${JSON.stringify(plugin)}`);
|
|
}
|
|
|
|
if (inspect.plugin?.id !== "codex" || inspect.plugin?.status !== "loaded") {
|
|
throw new Error(`unexpected codex inspect state: ${JSON.stringify(inspect.plugin)}`);
|
|
}
|
|
const hasHarness =
|
|
(Array.isArray(inspect.plugin?.agentHarnessIds) &&
|
|
inspect.plugin.agentHarnessIds.includes("codex")) ||
|
|
(Array.isArray(inspect.capabilities) &&
|
|
inspect.capabilities.some(
|
|
(entry) => entry?.kind === "agent-harness" && entry.ids?.includes("codex"),
|
|
));
|
|
if (!hasHarness) {
|
|
throw new Error(`codex harness was not registered: ${JSON.stringify(inspect.plugin)}`);
|
|
}
|
|
|
|
const primaryModel = cfg.agents?.defaults?.model?.primary;
|
|
if (primaryModel !== "openai/gpt-5.6") {
|
|
throw new Error(`expected OpenAI onboarding model openai/gpt-5.6, got ${primaryModel}`);
|
|
}
|
|
const providerRuntime = cfg.models?.providers?.openai?.agentRuntime?.id;
|
|
if (providerRuntime && providerRuntime !== "codex") {
|
|
throw new Error(`unexpected OpenAI provider runtime: ${providerRuntime}`);
|
|
}
|
|
|
|
function readAuthProfileStoreText(agentDir) {
|
|
const dbPath = path.join(agentDir, "openclaw-agent.sqlite");
|
|
if (!fs.existsSync(dbPath)) {
|
|
throw new Error("auth profile SQLite store was not persisted");
|
|
}
|
|
let db;
|
|
try {
|
|
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
const row = db
|
|
.prepare("SELECT store_json FROM auth_profile_store WHERE store_key = ?")
|
|
.get("primary");
|
|
return typeof row?.store_json === "string" ? row.store_json : "";
|
|
} finally {
|
|
db?.close();
|
|
}
|
|
}
|
|
|
|
const authRaw = readAuthProfileStoreText(path.join(stateDir(), "agents", "main", "agent"));
|
|
if (!authRaw) {
|
|
throw new Error("auth profile SQLite store row was not persisted");
|
|
}
|
|
assertOpenAiEnvAuthProfileStore(authRaw, {
|
|
envRefMessage: "auth profile did not persist OPENAI_API_KEY env ref",
|
|
rawKeyMessage: "auth profile persisted the raw OpenAI test key",
|
|
rawKeyNeedle: "sk-openclaw-codex-on-demand-e2e",
|
|
});
|