From e5c43c2c0a407f7bb85b3dba2885e7efbd57bef8 Mon Sep 17 00:00:00 2001 From: BearHuddleston Date: Mon, 20 Jul 2026 08:35:16 -0500 Subject: [PATCH 1/2] use codex update for standalone installs --- apps/server/src/provider/Drivers/CodexDriver.ts | 17 ++++++++++++++++- .../Layers/ProviderInstanceRegistryLive.test.ts | 11 ++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index ffcc94ca77d..1f3af7ca592 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -45,6 +45,7 @@ import { mergeProviderInstanceEnvironment } from "../ProviderInstanceEnvironment import { enrichProviderSnapshotWithVersionAdvisory, makePackageManagedProviderMaintenanceResolver, + normalizeCommandPath, resolveProviderMaintenanceCapabilitiesEffect, } from "../providerMaintenance.ts"; import { @@ -61,11 +62,25 @@ const decodeCodexSettings = Schema.decodeSync(CodexSettings); const DRIVER_KIND = ProviderDriverKind.make("codex"); const SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5); + +function isCodexStandaloneCommandPath(commandPath: string): boolean { + const normalized = normalizeCommandPath(commandPath); + return ( + normalized.includes("/.codex/packages/standalone/") && + (normalized.endsWith("/bin/codex") || normalized.endsWith("/bin/codex.exe")) + ); +} + const UPDATE = makePackageManagedProviderMaintenanceResolver({ provider: DRIVER_KIND, npmPackageName: "@openai/codex", homebrewFormula: "codex", - nativeUpdate: null, + nativeUpdate: { + executable: "codex", + args: ["update"], + lockKey: "codex-native", + isCommandPath: isCodexStandaloneCommandPath, + }, }); /** diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts index 384de852f9b..953a8e48e23 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts @@ -136,7 +136,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { displayName: "Codex (work)", enabled: false, config: makeCodexConfig({ - binaryPath: "/opt/codex-work/bin/codex", + binaryPath: "/home/julius/.codex/packages/standalone/current/bin/codex", homePath: "/home/julius/.codex", customModels: ["work-preview"], }), @@ -181,6 +181,15 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { expect(workSnapshot.driver).toBe(codexDriverKind); expect(workSnapshot.enabled).toBe(false); expect(workSnapshot.continuation?.groupKey).toBe("codex:home:/home/julius/.codex"); + expect(work!.snapshot.maintenanceCapabilities).toMatchObject({ + packageName: "@openai/codex", + update: { + command: "codex update", + executable: "codex", + args: ["update"], + lockKey: "codex-native", + }, + }); // Nothing goes to the unavailable bucket — both drivers are registered. const unavailable = yield* registry.listUnavailable; From 14bbd62fcae38f372495ceb754032a1d428b5bd6 Mon Sep 17 00:00:00 2001 From: BearHuddleston Date: Mon, 20 Jul 2026 17:43:05 -0500 Subject: [PATCH 2/2] target codex updates at resolved executable --- apps/server/src/provider/Drivers/CodexDriver.ts | 1 + .../Layers/ProviderInstanceRegistryLive.test.ts | 2 +- .../server/src/provider/providerMaintenance.test.ts | 3 ++- apps/server/src/provider/providerMaintenance.ts | 13 ++++++++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index 1f3af7ca592..5fd4a28d66f 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -80,6 +80,7 @@ const UPDATE = makePackageManagedProviderMaintenanceResolver({ args: ["update"], lockKey: "codex-native", isCommandPath: isCodexStandaloneCommandPath, + useResolvedExecutable: true, }, }); diff --git a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts index 953a8e48e23..0555376864d 100644 --- a/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts +++ b/apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts @@ -185,7 +185,7 @@ describe("ProviderInstanceRegistryLive — multi-instance codex slice", () => { packageName: "@openai/codex", update: { command: "codex update", - executable: "codex", + executable: "/home/julius/.codex/packages/standalone/current/bin/codex", args: ["update"], lockKey: "codex-native", }, diff --git a/apps/server/src/provider/providerMaintenance.test.ts b/apps/server/src/provider/providerMaintenance.test.ts index 8937844f613..4f52bf79496 100644 --- a/apps/server/src/provider/providerMaintenance.test.ts +++ b/apps/server/src/provider/providerMaintenance.test.ts @@ -46,6 +46,7 @@ const nativePackageToolUpdate = makePackageManagedProviderMaintenanceResolver({ args: ["update"], lockKey: "native-package-tool-native", isCommandPath: isNativeTestCommandPath("/.local/bin/native-package-tool"), + useResolvedExecutable: true, }, }); const scopedPackageToolUpdate = makePackageManagedProviderMaintenanceResolver({ @@ -358,7 +359,7 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { update: { command: "native-package-tool update", - executable: "native-package-tool", + executable: nativePackageToolPath, args: ["update"], diff --git a/apps/server/src/provider/providerMaintenance.ts b/apps/server/src/provider/providerMaintenance.ts index 8645f9f943c..e8684f947fd 100644 --- a/apps/server/src/provider/providerMaintenance.ts +++ b/apps/server/src/provider/providerMaintenance.ts @@ -72,6 +72,8 @@ export interface PackageManagedProviderMaintenanceDefinition { readonly args: ReadonlyArray; readonly lockKey: string; readonly isCommandPath: (commandPath: string) => boolean; + /** Execute the provider path resolved during capability detection. */ + readonly useResolvedExecutable?: boolean; } | null; } @@ -97,6 +99,7 @@ function nonEmptyString(value: unknown): string | null { export function makeProviderMaintenanceCapabilities(input: { readonly provider: ProviderDriverKind; readonly packageName: string | null; + readonly updateCommand?: string; readonly updateExecutable: string | null; readonly updateArgs: ReadonlyArray; readonly updateLockKey: string | null; @@ -105,7 +108,7 @@ export function makeProviderMaintenanceCapabilities(input: { input.updateExecutable === null || input.updateLockKey === null ? null : { - command: [input.updateExecutable, ...input.updateArgs].join(" "), + command: input.updateCommand ?? [input.updateExecutable, ...input.updateArgs].join(" "), executable: input.updateExecutable, args: input.updateArgs, lockKey: input.updateLockKey, @@ -199,6 +202,7 @@ function makeHomebrewProviderMaintenanceCapabilities( function makeNativeProviderMaintenanceCapabilities( definition: PackageManagedProviderMaintenanceDefinition, + resolvedCommandPath: string, ): ProviderMaintenanceCapabilities | null { if (!definition.nativeUpdate) { return null; @@ -207,7 +211,10 @@ function makeNativeProviderMaintenanceCapabilities( return makeProviderMaintenanceCapabilities({ provider: definition.provider, packageName: definition.npmPackageName, - updateExecutable: definition.nativeUpdate.executable, + updateCommand: [definition.nativeUpdate.executable, ...definition.nativeUpdate.args].join(" "), + updateExecutable: definition.nativeUpdate.useResolvedExecutable + ? resolvedCommandPath + : definition.nativeUpdate.executable, updateArgs: definition.nativeUpdate.args, updateLockKey: definition.nativeUpdate.lockKey, }); @@ -287,7 +294,7 @@ export function resolvePackageManagedProviderMaintenance( commandPaths.some((commandPath) => nativeUpdate.isCommandPath(commandPath)) ) { return ( - makeNativeProviderMaintenanceCapabilities(definition) ?? + makeNativeProviderMaintenanceCapabilities(definition, resolvedCommandPath) ?? makeNpmGlobalProviderMaintenanceCapabilities(definition) ); }