Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@
"codex-cli-installation-targets.test.ts": "codex-integration",
"codex-cli-update-launcher-policy.test.ts": "codex-integration",
"codex-cli-update-integrity.test.ts": "codex-integration",
"codex-cli-update-plan.test.ts": "codex-integration",
"codex-cli-update-plan.test.ts": "codex-integration", "codex-cli-update-parent-async.test.ts": "codex-integration",
"codex-cli-update-zero-effect.test.ts": "codex-integration",
"codex-cli-windows-installation-files.test.ts": "codex-integration",
"codex-forward-access-programs-writer.test.ts": "codex-integration",
Expand Down
5 changes: 3 additions & 2 deletions src/cli/account-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { redactSecretArgs } from "./secret-args";
import { codexExecInvocation } from "../codex/exec-invocation";
import { validatedCodexExecInvocationAsync, type CodexExecBoundaryAsyncDeps } from "../codex/exec-boundary";
import { resolveAndPersistCodexRuntime, type ResolveCodexRuntimeDeps } from "../codex/runtime";
import { withCodexCliSelectionParentWriteAsync } from "../codex/cli-update-selection-gate";
import type { ResolveDeps, SpawnInvocation } from "../lib/win-exec";
import {
apiError,
Expand Down Expand Up @@ -62,15 +63,15 @@ export async function nativeMainCodexLoginValidatedInvocation(
deps: NativeMainCodexLoginValidatedDeps = {},
): Promise<SpawnInvocation> {
const env = deps.env ?? process.env;
const command = resolveAndPersistCodexRuntime({
const command = (await withCodexCliSelectionParentWriteAsync(() => resolveAndPersistCodexRuntime({
env,
platform,
existsSync: deps.existsSync,
execFileSync: deps.execFileSync,
configDir: deps.configDir,
readFileSync: deps.readFileSync,
discoverAlternatives: false,
}).runtime.command || "codex";
}), { configDir: deps.configDir, signal: deps.signal })).runtime.command || "codex";
return validatedCodexExecInvocationAsync(command, ["login"], {
env,
platform,
Expand Down
18 changes: 17 additions & 1 deletion src/cli/codex-shim-autorestore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { autoRestoreCodexShim } from "../codex/shim";
import { autoRestoreCodexShim, autoRestoreCodexShimAsync } from "../codex/shim";
import { codexShimAutoRestoreEnabled, readConfigDiagnostics } from "../config";

export interface CodexShimAutoRestoreCliDeps {
Expand Down Expand Up @@ -77,3 +77,19 @@ export function runCodexShimAutoRestore(deps: CodexShimAutoRestoreCliDeps = DEFA
);
}
}

export async function maybeAutoRestoreCodexShimAsync(command: string | undefined, args: string[]): Promise<void> {
if (!skipsCodexShimAutoRestore(command, args)) await runCodexShimAutoRestoreAsync();
}

export async function runCodexShimAutoRestoreAsync(): Promise<void> {
try {
const result = await autoRestoreCodexShimAsync({
enabled: () => codexShimAutoRestoreEnabled(DEFAULT_DEPS.readConfig().config, DEFAULT_DEPS.env),
});
if (result.status === "restored") DEFAULT_DEPS.warn(`⚠️ ${result.message} (automatic repair after Codex update)`);
else if ((result.status === "deferred" || result.status === "ineligible") && result.message) DEFAULT_DEPS.warn(`⚠️ ${result.message}`);
} catch (error) {
DEFAULT_DEPS.warn(`⚠️ Codex shim auto-restore failed; continuing without it: ${error instanceof Error ? error.message : String(error)}. Run 'ocx codex-shim install' after the Codex update finishes.`);
}
}
12 changes: 6 additions & 6 deletions src/cli/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,8 @@ const commandRunners: Record<string, CommandRunner> = {
if (expectation) {
// The root preflight skipped the shim repair for a pinned sync; the identity held, so
// run it now, as an unpinned sync would have.
const { runCodexShimAutoRestore } = await import("./codex-shim-autorestore");
runCodexShimAutoRestore();
const { runCodexShimAutoRestoreAsync } = await import("./codex-shim-autorestore");
await runCodexShimAutoRestoreAsync();
}
// The connection the sync ran under, read inside its lock; the read above may be stale.
const link = result.connection.transport === "link";
Expand Down Expand Up @@ -731,10 +731,10 @@ const commandRunners: Record<string, CommandRunner> = {
return Number(process.exitCode ?? 0);
},
"codex-shim": async deps => {
const { codexShimStatus, diagnoseCodexShim, installCodexShim, uninstallCodexShim } = await import("../codex/shim");
const { codexShimStatus, diagnoseCodexShim, installCodexShimAsync, uninstallCodexShimAsync } = await import("../codex/shim");
switch (deps.args[1]) {
case "install": {
const r = installCodexShim();
const r = await installCodexShimAsync();
const { healthy, runnable, active, summary } = diagnoseCodexShim();
const success = !r.refused && (runnable ?? healthy);
const { collectCodexShimReadinessWarnings } = await import("./codex-shim-readiness");
Expand Down Expand Up @@ -764,7 +764,7 @@ const commandRunners: Record<string, CommandRunner> = {
}
case "uninstall":
case "remove": {
const r = uninstallCodexShim();
const r = await uninstallCodexShimAsync();
console.log(r.removed ? `✅ ${r.message}` : `⚠️ ${r.message}`);
break;
}
Expand Down Expand Up @@ -858,7 +858,7 @@ const commandRunners: Record<string, CommandRunner> = {
},
ready: async deps => {
// Fail-closed impossible-state guard: readyArgs is populated by the
// preparse block in src/cli/root.ts before maybeAutoRestoreCodexShim, so
// preparse block in src/cli/root.ts before maybeAutoRestoreCodexShimAsync, so
// reaching here without it means dispatch diverged. Refuse with code 64
// and perform NO I/O (no discovery/probe). process.exit is `never`,
// narrowing below.
Expand Down
10 changes: 5 additions & 5 deletions src/cli/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ import {
effortClampAppliesToRuntime,
liveRemovedEfforts,
loadLastEffortClamp,
persistCodexRuntime,
resolveAndPersistCodexRuntime,
persistCodexRuntimeAsync,
resolveAndPersistCodexRuntimeAsync,
resolveCodexRuntime,
} from "../codex/runtime";
import { CODEX_REAUTH_ACTION, collectOAuthHealthEntriesForCli, MASKED_ACCOUNT_FALLBACK, type OAuthHealthEntry } from "../oauth/health";
Expand Down Expand Up @@ -1266,8 +1266,8 @@ export async function runDoctor(args: string[] = []): Promise<void> {
const resolved = resolveCodexRuntime();
if (!resolved.newerAvailable) {
console.log("No newer Codex runtime found; keeping current selection.");
const current = resolveAndPersistCodexRuntime();
console.log(`Selected: ${displayCodexRuntimePath(current.runtime.command)} (${current.runtime.version ?? "unknown"})`);
const current = await resolveAndPersistCodexRuntimeAsync();
console.log(`Selected: ${displayCodexRuntimePath(current?.runtime.command ?? "codex")} (${current?.runtime.version ?? "unknown"})`);
return;
}
if (resolved.runtime.source === "environment") {
Expand All @@ -1276,7 +1276,7 @@ export async function runDoctor(args: string[] = []): Promise<void> {
console.log("Then run ocx sync.");
return;
}
persistCodexRuntime({
await persistCodexRuntimeAsync({
command: resolved.newerAvailable.command,
version: resolved.newerAvailable.version,
source: "configured",
Expand Down
4 changes: 2 additions & 2 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1674,8 +1674,8 @@ async function handleUninstall() {
});

try {
const { uninstallCodexShim } = await import("../codex/shim");
const r = uninstallCodexShim();
const { uninstallCodexShimAsync } = await import("../codex/shim");
const r = await uninstallCodexShimAsync();
console.log(r.removed ? "✅ Codex autostart shim removed" : "- Codex autostart shim removed: not installed");
} catch (err) {
failures.push("Codex autostart shim removed");
Expand Down
4 changes: 2 additions & 2 deletions src/cli/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ export async function runInit(): Promise<void> {
prompt.throwIfCancelled();
if (shimAnswer.trim().toLowerCase() !== "n") {
try {
const { installCodexShim } = await import("../codex/shim");
const { installCodexShimAsync } = await import("../codex/shim");
prompt.throwIfCancelled();
const result = installCodexShim();
const result = await installCodexShimAsync();
console.log(result.installed ? `✅ ${result.message}` : `⚠️ ${result.message}`);
} catch (err) {
if (err instanceof InitCancelledError) throw err;
Expand Down
4 changes: 2 additions & 2 deletions src/cli/restart-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ export async function handleDesktopAppRestart(
log: Pick<Console, "log" | "error">,
io: DesktopAppRestartIo = {},
): Promise<DesktopAppRestartResult> {
const { restartCodexDesktopApp } = await import("../codex/desktop-app-restart");
const { restartCodexDesktopAppAsync } = await import("../codex/desktop-app-restart");
const { startDesktopRestartHandoff } = await import("../codex/desktop-app/handoff");
const result = restartCodexDesktopApp({
const result = await restartCodexDesktopAppAsync({
...io,
// The CLI is the one caller whose exit is exactly the signal the helper waits for,
// so it is the one caller allowed to hand off. The management service is not (it
Expand Down
12 changes: 6 additions & 6 deletions src/cli/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { parseReadyArgs, type ReadyArgs } from "./ready";
import { parseResolveArgs, type ResolveArgs } from "./resolve";
import { parseStopApproval } from "./stop-approval";
import { parseSyncExpectation, SYNC_EXPECTATION_USAGE_EXIT } from "./sync-expectation";
import { maybeAutoRestoreCodexShim } from "./codex-shim-autorestore";
import { maybeAutoRestoreCodexShimAsync } from "./codex-shim-autorestore";
import { findCommand } from "./registry";
import { printUnknownCommand } from "./help-recovery";
import { noteCredentialArgv, redactSecretArgs } from "./secret-args";
Expand Down Expand Up @@ -64,7 +64,7 @@ export function parseCliHead(argv: string[]): CliHead {
};
}
// P1: pre-parse `ocx ready` and reject invalid arguments with exit 64 BEFORE
// maybeAutoRestoreCodexShim (or any discovery/probe/filesystem-capable global
// maybeAutoRestoreCodexShimAsync (or any discovery/probe/filesystem-capable global
// preflight) runs. `ready --help` / `help ready` already exited above, so this
// only sees ready args without a help flag. Valid args are stashed so the
// switch dispatch can call runReady without a second parse.
Expand All @@ -74,7 +74,7 @@ export function parseCliHead(argv: string[]): CliHead {
return { kind: "ready", command, args, readyArgs: parsed.args };
}
// Same ordering contract as `ready`: `ocx resolve` rejects any argument with exit
// 64 BEFORE maybeAutoRestoreCodexShim (or any other preflight with side effects) runs.
// 64 BEFORE maybeAutoRestoreCodexShimAsync (or any other preflight with side effects) runs.
if (command === "resolve") {
const parsed = parseResolveArgs(args.slice(1));
if (!parsed.ok) return { kind: "resolve", command, args, resolveArgs: undefined };
Expand Down Expand Up @@ -115,7 +115,7 @@ export async function runCli(argv: string[]): Promise<CliHead> {
console.error(" Default wait timeout is 45 seconds.");
process.exit(64);
}
maybeAutoRestoreCodexShim(head.command, head.args);
await maybeAutoRestoreCodexShimAsync(head.command, head.args);
return head;
}
case "resolve": {
Expand All @@ -128,7 +128,7 @@ export async function runCli(argv: string[]): Promise<CliHead> {
console.error(" and the identity-checked liveness verdict.");
process.exit(64);
}
maybeAutoRestoreCodexShim(head.command, head.args);
await maybeAutoRestoreCodexShimAsync(head.command, head.args);
return head;
}
case "command":
Expand All @@ -154,7 +154,7 @@ export async function runCli(argv: string[]): Promise<CliHead> {
}
}
await maybeNoticeVersionSkew(head.command, head.args);
maybeAutoRestoreCodexShim(head.command, head.args);
await maybeAutoRestoreCodexShimAsync(head.command, head.args);
return head;
}
}
Loading
Loading