Files
Peter Steinberger fe261b0f59 chore(tooling): typecheck root test/** with a dedicated tsgo lane (#104475)
* 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.
2026-07-11 06:15:41 -07:00

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",
});