fix(onboarding): OpenAI setup installs mismatched Codex plugin (#129195)

* fix(onboarding): align Codex plugin with core release

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b4-f621-7144-bfdf-a68df795dd57

* test(onboarding): reject unpinned Codex candidates

Amp-Thread-ID: https://ampcode.com/threads/T-01a037b4-f621-7144-bfdf-a68df795dd57

* fix(onboarding): match stable correction release plugin cohorts

Co-authored-by: 許元豪 <146086744+edenfunf@users.noreply.github.com>

* test: isolate plugin metadata and register startup retry coverage

* test: adopt upstream startup retry ownership pattern

* test: keep plugin metadata readers process-stable

* test: isolate terminal session recovery cases

* test: drop speculative gateway isolation change

* test(gateway): mock terminal transcript lifecycle decision

* fix(plugins): align correction cohorts across stable tracks

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: 許元豪 <146086744+edenfunf@users.noreply.github.com>
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
This commit is contained in:
Peter Steinberger
2026-08-25 17:03:39 -07:00
committed by GitHub
parent 827433c6fe
commit 85d5df1d87
19 changed files with 238 additions and 23 deletions
+3 -1
View File
@@ -120,7 +120,9 @@ Switching channels with `openclaw update` also syncs plugin sources:
packages.
- `extended-stable` resolves eligible official npm plugins with bare/default
or `latest` intent to the exact installed core version. It does not query
plugin `@extended-stable` tags at runtime.
plugin `@extended-stable` tags at runtime. Version-bound runtime plugins use
the base release cohort for correction versions (for example, `YYYY.M.P-2`
uses plugin `YYYY.M.P`).
- npm-installed plugins are updated after the core update completes.
## Checking current status
+3
View File
@@ -54,6 +54,9 @@ omit `--allow-scripts=openclaw`.
After the core swap, eligible official npm plugins with bare/default or
`latest` intent converge to that exact core version. Exact pins and explicit
non-`latest` tags, third-party plugins, and non-npm sources remain unchanged.
Version-bound runtime plugins converge to the base release cohort when the
core is a correction release (for example, `YYYY.M.P-2` uses plugin
`YYYY.M.P`).
Catalog installs created by current OpenClaw versions retain that default
intent. Older records that contain only an exact version remain pinned because
OpenClaw cannot safely distinguish an old automatic pin from a user pin; run
+98
View File
@@ -11,6 +11,8 @@ IMAGE_NAME="$(docker_e2e_resolve_image "openclaw-codex-on-demand-e2e" OPENCLAW_C
DOCKER_TARGET="${OPENCLAW_CODEX_ON_DEMAND_DOCKER_TARGET:-bare}"
HOST_BUILD="${OPENCLAW_CODEX_ON_DEMAND_HOST_BUILD:-1}"
PACKAGE_TGZ="${OPENCLAW_CURRENT_PACKAGE_TGZ:-}"
PREPUBLISH_PLUGIN_REGISTRY_ARGS=()
AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT=""
run_log=""
# This lane installs the package and then exercises a managed npm install of Codex.
@@ -18,10 +20,30 @@ run_log=""
# the Codex assertions instead of failing as a silent package-install timeout.
export OPENCLAW_E2E_NPM_INSTALL_TIMEOUT="${OPENCLAW_E2E_NPM_INSTALL_TIMEOUT:-1200s}"
configure_prepublish_plugin_registry() {
local registry_dir="$1"
local resolved_registry_dir
resolved_registry_dir="$(cd "$registry_dir" && pwd)"
if [ ! -f "$resolved_registry_dir/prepublish-plugin-registry.json" ]; then
echo "Prepublish plugin registry manifest is missing." >&2
exit 1
fi
PREPUBLISH_PLUGIN_REGISTRY_ARGS=(
-e OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR=/tmp/openclaw-prepublish-plugin-registry
-v "$resolved_registry_dir:/tmp/openclaw-prepublish-plugin-registry:ro"
)
}
if [ -n "${OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR:-}" ]; then
configure_prepublish_plugin_registry "$OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR"
fi
cleanup() {
if [ -n "${PACKAGE_TGZ:-}" ]; then
docker_e2e_cleanup_package_tgz "$PACKAGE_TGZ"
fi
if [ -n "$AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT" ]; then
rm -rf "$AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT"
fi
if [ -n "${run_log:-}" ]; then
rm -f "$run_log"
fi
@@ -44,6 +66,20 @@ prepare_package_tgz() {
prepare_package_tgz
if [ -z "${OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR:-}" ] &&
[ -z "${OPENCLAW_CURRENT_PACKAGE_TGZ:-}" ] &&
[ "$HOST_BUILD" != "0" ]; then
AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT="$(
mktemp -d "${TMPDIR:-/tmp}/openclaw-codex-on-demand-plugin-registry.XXXXXX"
)"
OPENCLAW_DOCKER_ALL_LANES=codex-on-demand \
OPENCLAW_DOCKER_ALL_LOG_DIR="$AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT" \
OPENCLAW_DOCKER_ALL_TIMINGS=0 \
node "$ROOT_DIR/scripts/test-docker-all.mjs" --prepare-plugin-registry >/dev/null
configure_prepublish_plugin_registry \
"$AUTO_PREPUBLISH_PLUGIN_REGISTRY_ROOT/prepublish-plugin-registry"
fi
docker_e2e_package_mount_args "$PACKAGE_TGZ"
run_log="$(docker_e2e_run_log codex-on-demand)"
OPENCLAW_TEST_STATE_SCRIPT_B64="$(docker_e2e_test_state_shell_b64 codex-on-demand empty)"
@@ -52,6 +88,7 @@ echo "Running Codex on-demand Docker E2E..."
if ! docker_e2e_run_with_harness \
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
-e "OPENCLAW_TEST_STATE_SCRIPT_B64=$OPENCLAW_TEST_STATE_SCRIPT_B64" \
${PREPUBLISH_PLUGIN_REGISTRY_ARGS[@]+"${PREPUBLISH_PLUGIN_REGISTRY_ARGS[@]}"} \
"${DOCKER_E2E_PACKAGE_ARGS[@]}" \
-i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF'; then
set -euo pipefail
@@ -71,12 +108,71 @@ dump_debug_logs() {
echo "Codex on-demand scenario failed with exit code $status" >&2
openclaw_e2e_dump_logs \
/tmp/openclaw-install.log \
/tmp/openclaw-codex-registry/server.log \
/tmp/openclaw-onboard.json \
/tmp/openclaw-plugins-list.json \
/tmp/openclaw-codex-inspect.json
}
trap 'status=$?; dump_debug_logs "$status"; exit "$status"' ERR
plugin_registry_pid=""
cleanup_inner() {
openclaw_e2e_stop_process "${plugin_registry_pid:-}"
}
trap cleanup_inner EXIT
configure_plugin_registry() {
[ -n "${OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR:-}" ] || return 0
local registry_root="/tmp/openclaw-codex-registry"
local manifest="$OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR/prepublish-plugin-registry.json"
local package_name package_version package_tarball
IFS=$'\t' read -r package_name package_version package_tarball < <(
PREPUBLISH_PLUGIN_REGISTRY_MANIFEST="$manifest" node <<'NODE'
const fs = require("node:fs");
const path = require("node:path");
const manifestPath = process.env.PREPUBLISH_PLUGIN_REGISTRY_MANIFEST;
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
const matches = Array.isArray(manifest.packages)
? manifest.packages.filter((entry) => entry?.name === "@openclaw/codex")
: [];
if (matches.length !== 1) {
throw new Error("prepublish plugin registry must contain exactly one @openclaw/codex package");
}
const entry = matches[0];
if (
typeof entry.version !== "string" ||
typeof entry.tarball !== "string" ||
path.basename(entry.tarball) !== entry.tarball
) {
throw new Error("invalid @openclaw/codex prepublish plugin registry entry");
}
process.stdout.write(
`${entry.name}\t${entry.version}\t${path.join(path.dirname(manifestPath), entry.tarball)}\n`,
);
NODE
)
mkdir -p "$registry_root"
OPENCLAW_NPM_REGISTRY_DIST_TAGS="latest=0.0.0,beta=$package_version" \
OPENCLAW_NPM_REGISTRY_UPSTREAM=https://registry.npmjs.org \
node scripts/e2e/lib/plugins/npm-registry-server.mjs \
"$registry_root/port" \
"$package_name" "$package_version" "$package_tarball" \
>"$registry_root/server.log" 2>&1 &
plugin_registry_pid="$!"
for _ in $(seq 1 100); do
[ -s "$registry_root/port" ] && break
openclaw_e2e_process_alive "$plugin_registry_pid" || break
sleep 0.1
done
if [ ! -s "$registry_root/port" ]; then
openclaw_e2e_print_log "$registry_root/server.log" >&2
echo "Timed out waiting for Codex npm registry." >&2
return 1
fi
export NPM_CONFIG_REGISTRY="http://127.0.0.1:$(cat "$registry_root/port")"
export npm_config_registry="$NPM_CONFIG_REGISTRY"
}
mkdir -p "$NPM_CONFIG_PREFIX" "$XDG_CACHE_HOME" "$NPM_CONFIG_CACHE"
chmod 700 "$XDG_CACHE_HOME" "$NPM_CONFIG_CACHE" || true
@@ -87,6 +183,8 @@ openclaw_e2e_enable_openclaw_cli_timeout
openclaw_e2e_assert_dep_absent "@openclaw/codex" "$HOME/.openclaw" "$NPM_CONFIG_PREFIX"
openclaw_e2e_assert_dep_absent "@openai/codex" "$HOME/.openclaw" "$NPM_CONFIG_PREFIX"
configure_plugin_registry
echo "Running non-interactive OpenAI onboarding; Codex should install on demand..."
openclaw onboard --non-interactive --accept-risk \
--mode local \
+3
View File
@@ -746,6 +746,9 @@ export function requiredPrepublishPluginPackagesForLanes(poolLanes: DockerE2eLan
const configuredChannelIds = new Set<string>();
const requiredPackages = new Set<string>();
for (const poolLane of poolLanes) {
for (const packageName of poolLane.prepublishPluginPackages ?? []) {
requiredPackages.add(packageName);
}
const scenario = upgradeSurvivorScenarioForLane(poolLane);
if (!scenario) {
continue;
+3
View File
@@ -16,6 +16,7 @@ export type DockerE2eLane = {
name: string;
needsLiveImage?: boolean;
noOutputTimeoutMs?: number;
prepublishPluginPackages?: string[];
resources: string[];
retries: number;
retryPatterns: RegExp[];
@@ -106,6 +107,7 @@ function lane(name: string, command: string, options: LaneOptions = {}): DockerE
noOutputTimeoutMs: options.noOutputTimeoutMs,
name,
needsLiveImage: options.needsLiveImage,
prepublishPluginPackages: options.prepublishPluginPackages,
retryPatterns: options.retryPatterns ?? [],
retries: options.retries ?? 0,
resources: options.resources ?? [],
@@ -442,6 +444,7 @@ export const mainLanes: DockerE2eLane[] = [
weight: 2,
}),
npmLane("codex-on-demand", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:codex-on-demand", {
prepublishPluginPackages: ["@openclaw/codex"],
resources: ["service"],
stateScenario: "empty",
weight: 3,
@@ -175,6 +175,7 @@ describe("Codex runtime plugin install repair", () => {
label: "Codex",
install: { npmSpec: "@openclaw/codex", defaultChoice: "npm" },
trustedSourceLinkedOfficialInstall: true,
versionBoundToOpenClaw: true,
},
}),
);
@@ -10,6 +10,7 @@ const CODEX_RUNTIME_PLUGIN_DESCRIPTOR = {
label: CODEX_RUNTIME_PLUGIN_LABEL,
npmSpec: CODEX_RUNTIME_PLUGIN_NPM_SPEC,
warningLabel: CODEX_RUNTIME_PLUGIN_LABEL,
versionBoundToOpenClaw: true,
};
const codexRuntimePluginInstall = createRuntimePluginModelSelectionHelpers({
@@ -827,6 +827,47 @@ describe("ensureOnboardingPluginInstalled", () => {
);
});
it("keeps version-bound official runtime plugins aligned with the stable core version", async () => {
installPluginFromNpmSpec.mockResolvedValueOnce({
ok: true,
pluginId: "codex",
targetDir: "/tmp/codex",
version: VERSION,
npmResolution: {
name: "@openclaw/codex",
version: VERSION,
resolvedSpec: `@openclaw/codex@${VERSION}`,
},
});
await ensureOnboardingPluginInstalled({
cfg: { update: { channel: "stable" } },
entry: {
pluginId: "codex",
label: "Codex",
install: { npmSpec: "@openclaw/codex" },
trustedSourceLinkedOfficialInstall: true,
versionBoundToOpenClaw: true,
},
prompter: {
select: vi.fn(async () => "npm"),
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
} as never,
runtime: {} as never,
promptInstall: false,
});
const [npmCall] = readFirstMockCall(installPluginFromNpmSpec, "installPluginFromNpmSpec") as [
NpmSpecInstallCall,
];
expect(npmCall.spec).toBe(`@openclaw/codex@${VERSION}`);
const [, recordUpdate] = readFirstMockCall(recordPluginInstall, "recordPluginInstall") as [
OpenClawConfig,
PluginInstallRecord,
];
expect(recordUpdate.spec).toBe("@openclaw/codex");
});
it("logs npm install warnings once while shortening the progress label", async () => {
const warning =
"npm rejected managed npm alias overrides; retrying plugin install without alias overrides for this npm version.";
@@ -73,6 +73,8 @@ export type OnboardingPluginInstallEntry = {
label: string;
install: PluginPackageInstall;
trustedSourceLinkedOfficialInstall?: boolean;
/** Keep this official runtime package on the same release cohort as OpenClaw. */
versionBoundToOpenClaw?: boolean;
preferRemoteInstall?: boolean;
};
@@ -1145,6 +1147,7 @@ export async function ensureOnboardingPluginInstalled(params: {
? parseRegistryNpmSpec(npmSpec)?.name
: undefined,
coreVersion: VERSION,
versionBoundToCore: entry.versionBoundToOpenClaw,
})
: null;
const clawhubInstallSpec = clawhubSpecs?.installSpec ?? clawhubSpec;
+3
View File
@@ -20,6 +20,8 @@ type RuntimePluginInstallDescriptor = {
label: string;
npmSpec: string;
warningLabel: string;
/** Keep this official runtime package on the same release cohort as OpenClaw. */
versionBoundToOpenClaw?: boolean;
};
/** Result returned after ensuring a runtime plugin for a selected model. */
@@ -139,6 +141,7 @@ async function ensureRuntimePluginForModelSelection(params: {
defaultChoice: "npm",
},
trustedSourceLinkedOfficialInstall: true,
...(params.descriptor.versionBoundToOpenClaw ? { versionBoundToOpenClaw: true } : {}),
},
prompter: params.prompter,
runtime: params.runtime,
@@ -1492,13 +1492,7 @@ describe("gateway agent handler", () => {
vi.useFakeTimers({ toFake: ["Date"] });
setDateOnlyFakeClockActive(true);
vi.setSystemTime(now);
mocks.readTranscriptStatsSync.mockReturnValue({
eventCount: 1,
lastMutationAtMs: now - 1_000,
lastObservedMutationAtMs: now - 10_000,
maxSeq: 0,
sizeBytes: 64,
});
mocks.hasTerminalMainSessionTranscriptNewerThanRegistrySync.mockReturnValue(true);
await withTestDir({ prefix: "openclaw-gateway-terminal-main-newer-" }, async (root) => {
const sessionsDir = `${root}/sessions`;
@@ -70,6 +70,7 @@ const mocks = vi.hoisted(() => ({
lastInteractionAt: entry?.lastInteractionAt,
}),
),
hasTerminalMainSessionTranscriptNewerThanRegistrySync: vi.fn(() => false),
lifecycleGeneration: "test-generation",
}));
@@ -99,6 +100,8 @@ vi.mock("../../config/sessions.js", async () => {
return m?.[1] ?? "main";
},
resolveExplicitAgentSessionKey: mocks.resolveExplicitAgentSessionKey,
hasTerminalMainSessionTranscriptNewerThanRegistrySync:
mocks.hasTerminalMainSessionTranscriptNewerThanRegistrySync,
resolveAgentMainSessionKey: ({
cfg,
agentId,
@@ -1089,6 +1092,7 @@ export const describe0AfterEach0 = () => {
lastInteractionAt: entry?.lastInteractionAt,
}),
);
mocks.hasTerminalMainSessionTranscriptNewerThanRegistrySync.mockReset().mockReturnValue(false);
mocks.lifecycleGeneration = "test-generation";
dateOnlyFakeClockActive = false;
vi.useRealTimers();
+11
View File
@@ -8,6 +8,7 @@ import {
isPrereleaseSemverVersion,
isPrereleaseResolutionAllowed,
parseRegistryNpmSpec,
resolveOpenClawReleaseCohortVersion,
resolveNpmJsonEntries,
validateRegistryNpmSpec,
} from "./npm-registry-spec.js";
@@ -147,6 +148,16 @@ describe("npm registry spec parsing helpers", () => {
])("compares OpenClaw release versions for %s and %s", ({ left, right, expected }) => {
expect(compareOpenClawReleaseVersions(left, right)).toBe(expected);
});
it.each([
{ version: "2026.7.1-2", expected: "2026.7.1" },
{ version: " 2026.7.1-1 ", expected: "2026.7.1" },
{ version: "2026.7.1", expected: "2026.7.1" },
{ version: "2026.7.1-beta.3", expected: "2026.7.1-beta.3" },
{ version: "1.2.3-1", expected: "1.2.3-1" },
])("resolves the OpenClaw release cohort for $version", ({ version, expected }) => {
expect(resolveOpenClawReleaseCohortVersion(version)).toBe(expected);
});
});
describe("npm prerelease resolution policy", () => {
+9
View File
@@ -201,6 +201,15 @@ function isOpenClawStableCorrectionVersion(value: string): boolean {
return parsed !== null && isOpenClawCorrectionSemver(parsed);
}
/** Resolves stable correction releases to their shared base release cohort. */
export function resolveOpenClawReleaseCohortVersion(value: string): string {
const trimmed = value.trim();
const parsed = parseOpenClawReleaseVersion(trimmed);
return parsed && isOpenClawCorrectionSemver(parsed)
? `${parsed.major}.${parsed.minor}.${parsed.patch}`
: trimmed;
}
/** Compares OpenClaw monthly patch release versions across alpha, beta, stable, and corrections. */
export function compareOpenClawReleaseVersions(left: string, right: string): number | null {
const parsedLeft = parseOpenClawReleaseVersion(left);
+18
View File
@@ -75,6 +75,24 @@ describe("resolveNpmInstallSpecsForUpdateChannel", () => {
});
});
it.each([
{ channel: "stable" as const, expectedVersion: "2026.7.1" },
{ channel: "extended-stable" as const, expectedVersion: "2026.7.1" },
])("preserves the $channel release-cohort contract", ({ channel, expectedVersion }) => {
expect(
resolveNpmInstallSpecsForUpdateChannel({
spec: "@openclaw/codex",
updateChannel: channel,
officialPackageName: "@openclaw/codex",
coreVersion: "2026.7.1-2",
versionBoundToCore: true,
}),
).toEqual({
installSpec: `@openclaw/codex@${expectedVersion}`,
recordSpec: "@openclaw/codex",
});
});
it("preserves beta behavior for a version-bound plugin", () => {
expect(
resolveNpmInstallSpecsForUpdateChannel({
+9 -2
View File
@@ -1,6 +1,10 @@
// Parses channel-oriented plugin install specs from package inputs.
import { parseClawHubPluginSpec } from "../infra/clawhub-spec.js";
import { isExactSemverVersion, parseRegistryNpmSpec } from "../infra/npm-registry-spec.js";
import {
isExactSemverVersion,
parseRegistryNpmSpec,
resolveOpenClawReleaseCohortVersion,
} from "../infra/npm-registry-spec.js";
import type { UpdateChannel } from "../infra/update-channels.js";
type ChannelInstallSpecs = {
@@ -56,8 +60,11 @@ export function resolveNpmInstallSpecsForUpdateChannel(params: {
`${policy} plugin resolution for ${target.name} requires an exact core version.`,
);
}
const installVersion = params.versionBoundToCore
? resolveOpenClawReleaseCohortVersion(coreVersion)
: coreVersion;
return {
installSpec: `${target.name}@${coreVersion}`,
installSpec: `${target.name}@${installVersion}`,
recordSpec: params.spec,
};
}
+7 -13
View File
@@ -2,7 +2,10 @@
import type { OpenClawConfig } from "../config/types.js";
import type { PluginInstallRecord } from "../config/types.plugins.js";
import { parseClawHubPluginSpec } from "../infra/clawhub-spec.js";
import { parseRegistryNpmSpec } from "../infra/npm-registry-spec.js";
import {
parseRegistryNpmSpec,
resolveOpenClawReleaseCohortVersion,
} from "../infra/npm-registry-spec.js";
import { normalizePluginsConfig, resolveEffectiveEnableState } from "./config-state.js";
import {
resolveTrustedSourceLinkedOfficialClawHubInstall,
@@ -38,7 +41,7 @@ function resolveExactNpmPinPackageName(entry: PluginVersionDriftEntry): string |
export function resolvePluginVersionDriftUpdateCommand(entry: PluginVersionDriftEntry): string {
const exactNpmPackageName = resolveExactNpmPinPackageName(entry);
if (exactNpmPackageName) {
const exactNpmTarget = `${exactNpmPackageName}@${normalizeVersion(entry.gatewayVersion)}`;
const exactNpmTarget = `${exactNpmPackageName}@${resolveOpenClawReleaseCohortVersion(entry.gatewayVersion)}`;
if (parseRegistryNpmSpec(exactNpmTarget)?.selectorKind === "exact-version") {
return `openclaw plugins update ${exactNpmTarget}`;
}
@@ -46,15 +49,6 @@ export function resolvePluginVersionDriftUpdateCommand(entry: PluginVersionDrift
return `openclaw plugins update ${entry.pluginId}`;
}
/**
* Strip a trailing build qualifier (e.g. `2026.5.4-1` -> `2026.5.4`) so that
* a gateway packaged as `2026.5.4-1` is not reported as drifted from a
* plugin packaged as `2026.5.4`. Both ends are normalized identically.
*/
function normalizeVersion(value: string): string {
return value.replace(/-\d+$/, "");
}
function isPluginEnabled(config: OpenClawConfig | undefined, pluginId: string): boolean {
const normalizedPluginConfig = normalizePluginsConfig(config?.plugins);
return resolveEffectiveEnableState({
@@ -104,7 +98,7 @@ export function detectPluginVersionDrift(params: {
config?: OpenClawConfig;
}): PluginVersionDriftReport {
const { gatewayVersion, installRecords, config } = params;
const normalizedGateway = normalizeVersion(gatewayVersion);
const normalizedGateway = resolveOpenClawReleaseCohortVersion(gatewayVersion);
const drifts: PluginVersionDriftEntry[] = [];
for (const [pluginId, record] of Object.entries(installRecords)) {
@@ -129,7 +123,7 @@ export function detectPluginVersionDrift(params: {
// separately if desired.
continue;
}
if (normalizeVersion(installedVersion) === normalizedGateway) {
if (resolveOpenClawReleaseCohortVersion(installedVersion) === normalizedGateway) {
continue;
}
drifts.push({
+18
View File
@@ -3878,6 +3878,24 @@ grep -Fxq preserved "$TMPDIR/caller-fd"
);
});
it("serves the version-matched Codex candidate during package onboarding", () => {
const runner = readFileSync(CODEX_ON_DEMAND_DOCKER_E2E_PATH, "utf8");
expectTextToIncludeAll(runner, [
"OPENCLAW_DOCKER_ALL_LANES=codex-on-demand",
"OPENCLAW_PREPUBLISH_PLUGIN_REGISTRY_DIR=/tmp/openclaw-prepublish-plugin-registry",
"node scripts/e2e/lib/plugins/npm-registry-server.mjs",
'OPENCLAW_NPM_REGISTRY_DIST_TAGS="latest=0.0.0,beta=$package_version"',
"OPENCLAW_NPM_REGISTRY_UPSTREAM=https://registry.npmjs.org",
]);
expect(runner.indexOf("openclaw_e2e_install_package")).toBeLessThan(
runner.indexOf("\nconfigure_plugin_registry\n"),
);
expect(runner.indexOf("\nconfigure_plugin_registry\n")).toBeLessThan(
runner.indexOf("\nopenclaw onboard --non-interactive"),
);
});
it("cleans package-backed onboarding and plugin Docker artifacts on every exit path", () => {
for (const path of [
CODEX_ON_DEMAND_DOCKER_E2E_PATH,
+2
View File
@@ -1463,6 +1463,8 @@ describe("scripts/lib/docker-e2e-plan", () => {
expect(lane.timeoutMs).toBe(1_800_000);
expect(plan.needs.bareImage).toBe(true);
expect(plan.needs.package).toBe(true);
expect(plan.requiredPrepublishPluginPackages).toEqual(["@openclaw/codex"]);
expect(plan.needs.prepublishPluginRegistry).toBe(true);
});
it("plans the plugin binding command escape lane as source Docker proof", () => {