@bitkyc08/opencodex 2.81.0 → 2.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/{Tray-EwAOsP0S.js → Tray-BqHF5Trd.js} +1 -1
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/{index-RsoaAxXY.js → index-D3FBCNiP.js} +13 -13
- package/gui/dist/assets/{quota-summary-D2lN8tDm.js → quota-summary-DTBC8n7-.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound.ts +77 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +2 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +5 -0
- package/src/cli/doctor.ts +16 -3
- package/src/cli/index.ts +14 -9
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integrations.ts +16 -3
- package/src/cli/resolve.ts +25 -2
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/routes.ts +10 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/build-entries.ts +18 -0
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/effort.ts +26 -5
- package/src/codex/catalog/gather-capture.ts +5 -0
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/provider-models.ts +43 -35
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -220
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +8 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/config-schema.ts +4 -0
- package/src/generated/compatibility-version.json +303 -171
- package/src/generated/model-metadata.ts +2 -2
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +29 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +18 -2
- package/src/integrations/writer.ts +9 -3
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/request-execution-budget.ts +8 -0
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/oauth/anthropic-account-refusal.ts +39 -1
- package/src/oauth/anthropic-routing.ts +1 -1
- package/src/oauth/anthropic-send-ownership.ts +3 -3
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/index.ts +6 -2
- package/src/oauth/store.ts +1 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +21 -0
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota.ts +1 -1
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +15 -2
- package/src/responses/state/metrics.ts +8 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +12 -5
- package/src/responses/state.ts +18 -10
- package/src/responses/task-input.ts +17 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +1 -0
- package/src/server/chat-native.ts +28 -12
- package/src/server/claude-messages.ts +43 -6
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +2 -1
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +1 -0
- package/src/server/management/agent-settings-routes.ts +93 -32
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +59 -8
- package/src/server/management/context.ts +2 -2
- package/src/server/management/shared.ts +3 -3
- package/src/server/messages-native-oauth.ts +9 -8
- package/src/server/messages-native.ts +49 -13
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +31 -2
- package/src/server/responses/adapter-continuation.ts +2 -2
- package/src/server/responses/adapter-dispatch.ts +34 -13
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +20 -3
- package/src/server/responses/core-combo.ts +22 -14
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-replay.ts +73 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +24 -2
- package/src/server/responses/passthrough-dispatch.ts +19 -2
- package/src/server/responses/request-prepare.ts +4 -0
- package/src/server/responses/request-send-budget.ts +112 -49
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +2 -0
- package/src/server/responses/sidecar-execution.ts +25 -16
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +3 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/config.ts +21 -0
- package/src/types/request.ts +5 -0
- package/src/types/wire.ts +1 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/web-search/loop.ts +5 -2
- package/gui/dist/assets/App-7yhoys8a.js +0 -54
package/bin/ocx.mjs
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* src/cli/index.ts — only the published npm/pnpm `bin` routes through here.)
|
|
10
10
|
*/
|
|
11
11
|
import { spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { createSupervisionLatch, inspectDesktopSupervision } from "../src/service/desktop-supervision.mjs";
|
|
13
|
+
import { planDesktopCliHandoff, runDesktopCliHandoff } from "../src/lib/desktop-cli-handoff.mjs";
|
|
14
|
+
import { desktopCliRecordPath } from "../src/lib/desktop-cli-record.mjs";
|
|
12
15
|
import { STOP_HISTORY_INCOMPLETE_EXIT_CODE } from "../src/update/stop-contract.mjs";
|
|
13
16
|
import { probeProxyLiveness } from "../src/update/proxy-liveness-probe.mjs";
|
|
14
17
|
import { decidePostStopUpdate } from "../src/update/stop-decision.mjs";
|
|
@@ -34,6 +37,7 @@ import { homedir } from "node:os";
|
|
|
34
37
|
import { dirname, join, resolve } from "node:path";
|
|
35
38
|
import { fileURLToPath } from "node:url";
|
|
36
39
|
import { isRealBunBinary } from "../src/lib/bun-binary-validator.mjs";
|
|
40
|
+
import { findDesktopCli, findPathBun } from "../src/lib/bun-path-runtime.mjs";
|
|
37
41
|
import { npmInvocation } from "../src/update/npm-invocation.mjs";
|
|
38
42
|
import { pnpmInvocationForPath, resolvePnpmCommands } from "../src/update/pnpm-invocation.mjs";
|
|
39
43
|
import { detectInstallOwnershipFromPath } from "../src/update/install-detection.mjs";
|
|
@@ -95,6 +99,15 @@ function currentPackageVersion() {
|
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
|
|
102
|
+
function pinnedBunVersion() {
|
|
103
|
+
try {
|
|
104
|
+
const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
|
|
105
|
+
return typeof pkg.dependencies?.bun === "string" ? pkg.dependencies.bun : "";
|
|
106
|
+
} catch {
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
98
111
|
function updateTag(currentVersion) {
|
|
99
112
|
// Allowlist the tag: the value is argv-controlled and (on Windows) flows into a
|
|
100
113
|
// shell-joined spawnSync — never forward arbitrary strings.
|
|
@@ -323,11 +336,13 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
323
336
|
: JSON.stringify(observation.ownership
|
|
324
337
|
? ["owned", observation.ownership.owner, observation.ownership.installId, observation.ownership.consentGeneration]
|
|
325
338
|
: ["none"]);
|
|
339
|
+
const supervisionLatch = createSupervisionLatch();
|
|
340
|
+
const observeSupervision = () => supervisionLatch.observe(inspectDesktopSupervision());
|
|
326
341
|
const initialOwnership = readOwnership();
|
|
327
|
-
let runtimePlan = planUpdateRuntimeHandling({ ...initialOwnership, serviceInstalled: serviceWasInstalled });
|
|
342
|
+
let runtimePlan = planUpdateRuntimeHandling({ ...initialOwnership, serviceInstalled: serviceWasInstalled, supervision: observeSupervision() });
|
|
328
343
|
if (runtimePlan.notice) console.log(runtimePlan.notice);
|
|
329
344
|
if (!runtimePlan.mayReplacePackage) {
|
|
330
|
-
console.error("opencodex: update stopped before tray handoff, runtime stop, or package replacement because runtime
|
|
345
|
+
console.error("opencodex: update stopped before tray handoff, runtime stop, or package replacement because runtime authority does not permit it.");
|
|
331
346
|
process.exit(1);
|
|
332
347
|
}
|
|
333
348
|
const trayBeforeUpdate = planWindowsTrayUpdate(
|
|
@@ -456,6 +471,14 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
456
471
|
: unprivilegedOwnershipMutationEnvironment(process.env);
|
|
457
472
|
|
|
458
473
|
function startProxyDirectly() {
|
|
474
|
+
const supervision = observeSupervision();
|
|
475
|
+
const plan = planUpdateRuntimeHandling({ ...readOwnership(), serviceInstalled: false, supervision });
|
|
476
|
+
if (!plan.mayStopRuntime) {
|
|
477
|
+
console.warn(supervision
|
|
478
|
+
? "OpenCodex Desktop supervises the proxy; no CLI runtime was restored."
|
|
479
|
+
: plan.notice);
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
459
482
|
if (!postUpdateLauncherUsable || !existsSync(postUpdateLauncher)) {
|
|
460
483
|
console.error("opencodex: cannot restart the proxy because the launcher is missing; reinstall opencodex manually.");
|
|
461
484
|
return false;
|
|
@@ -488,6 +511,15 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
488
511
|
}
|
|
489
512
|
|
|
490
513
|
function refreshBackgroundServiceOrStartDirect() {
|
|
514
|
+
const mayRefresh = () => {
|
|
515
|
+
const supervision = observeSupervision();
|
|
516
|
+
const plan = planUpdateRuntimeHandling({ ...readOwnership(), serviceInstalled: true, supervision });
|
|
517
|
+
if (!plan.mayRestoreService) console.warn(supervision
|
|
518
|
+
? "OpenCodex Desktop supervises the proxy; CLI recovery was skipped. Use the app's updater (tray → Check for Updates)."
|
|
519
|
+
: plan.notice);
|
|
520
|
+
return plan.mayRestoreService;
|
|
521
|
+
};
|
|
522
|
+
if (!mayRefresh()) return;
|
|
491
523
|
const prevBake = process.env.OCX_BAKE_PORT;
|
|
492
524
|
process.env.OCX_BAKE_PORT = String(bakePort);
|
|
493
525
|
try {
|
|
@@ -503,6 +535,7 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
503
535
|
// failure would resurrect the elevation prompt this change exists to avoid, and
|
|
504
536
|
// could re-register a service the user just uninstalled.
|
|
505
537
|
if (svc.status !== 0 && readServiceInstalledFromStatus(postUpdateLauncher) === false) {
|
|
538
|
+
if (!mayRefresh()) return;
|
|
506
539
|
console.log("No registered service found — installing it instead.");
|
|
507
540
|
svc = spawnSync(process.execPath, serviceInstallArgs(), {
|
|
508
541
|
stdio: "inherit", windowsHide: true, env: mutationChildEnvironment(),
|
|
@@ -535,9 +568,12 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
535
568
|
// Re-read rather than reuse the plan from before the package install: the app can
|
|
536
569
|
// claim the runtime during an update that takes minutes, and the refusal that repair
|
|
537
570
|
// just returned is indistinguishable from any other failure at this layer.
|
|
538
|
-
const
|
|
571
|
+
const supervision = observeSupervision();
|
|
572
|
+
const nowOwned = planUpdateRuntimeHandling({ ...readOwnership(), serviceInstalled: true, supervision });
|
|
539
573
|
if (!nowOwned.mayStopRuntime) {
|
|
540
|
-
console.warn(
|
|
574
|
+
console.warn(supervision
|
|
575
|
+
? "OpenCodex Desktop supervises the proxy; CLI recovery was skipped. Use the app's updater (tray → Check for Updates)."
|
|
576
|
+
: nowOwned.notice ?? "opencodex: the background runtime is owned elsewhere; not starting a second proxy.");
|
|
541
577
|
return;
|
|
542
578
|
}
|
|
543
579
|
// Repair normally avoids elevation for a healthy registration, but a stale Windows
|
|
@@ -574,7 +610,7 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
574
610
|
// Stop authority is decided under the same lease the child joins. A takeover between the
|
|
575
611
|
// earlier preflight and this boundary therefore blocks stop before it is sent.
|
|
576
612
|
const lockedOwnership = readOwnership();
|
|
577
|
-
const lockedPlan = planUpdateRuntimeHandling({ ...lockedOwnership, serviceInstalled: serviceWasInstalled });
|
|
613
|
+
const lockedPlan = planUpdateRuntimeHandling({ ...lockedOwnership, serviceInstalled: serviceWasInstalled, supervision: observeSupervision() });
|
|
578
614
|
if (lockedOwnership.subjectToken !== initialOwnership.subjectToken || !lockedPlan.mayReplacePackage) {
|
|
579
615
|
releaseUpdateLease();
|
|
580
616
|
console.error(lockedPlan.notice
|
|
@@ -616,6 +652,7 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
616
652
|
liveness,
|
|
617
653
|
plan: planStoppedRuntimeRecovery({
|
|
618
654
|
stopAttempted,
|
|
655
|
+
supervision: observeSupervision(),
|
|
619
656
|
...recoveryOwnership,
|
|
620
657
|
sameOwner: ownershipIdentity(recoveryOwnership) === stoppedOwnershipIdentity,
|
|
621
658
|
liveness,
|
|
@@ -633,7 +670,9 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
633
670
|
releaseUpdateLease();
|
|
634
671
|
({ liveness: recoveryLiveness, plan: recovery } = planRecovery());
|
|
635
672
|
}
|
|
636
|
-
if (recovery.reason === "
|
|
673
|
+
if (recovery.reason === "desktop-supervised") {
|
|
674
|
+
console.log("OpenCodex Desktop supervises the proxy; no CLI runtime was restored.");
|
|
675
|
+
} else if (recovery.reason === "ownership-unknown") {
|
|
637
676
|
console.error(`opencodex: ${reason}; runtime ownership is unknown, so automatic recovery was refused. Run 'ocx status --json' and repair the service-state record before retrying.`);
|
|
638
677
|
} else if (recovery.reason === "ownership-transferred") {
|
|
639
678
|
console.log("opencodex: runtime ownership moved to another installation; the stopped CLI runtime was not revived.");
|
|
@@ -666,6 +705,14 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
666
705
|
process.exit(1);
|
|
667
706
|
}
|
|
668
707
|
if (stopNeeded) {
|
|
708
|
+
const preStopPlan = planUpdateRuntimeHandling({
|
|
709
|
+
...readOwnership(), serviceInstalled: serviceWasInstalled, supervision: observeSupervision(),
|
|
710
|
+
});
|
|
711
|
+
if (!preStopPlan.mayStopRuntime) {
|
|
712
|
+
console.error(preStopPlan.notice);
|
|
713
|
+
releaseUpdateLease();
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
669
716
|
stopAttempted = true;
|
|
670
717
|
console.log("⏹ Stopping the running proxy before updating...");
|
|
671
718
|
const stopRes = spawnSync(process.execPath, [launcher, "stop"], {
|
|
@@ -722,7 +769,7 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
722
769
|
}
|
|
723
770
|
|
|
724
771
|
const replacementOwnership = readOwnership();
|
|
725
|
-
const replacementPlan = planUpdateRuntimeHandling({ ...replacementOwnership, serviceInstalled: serviceWasInstalled });
|
|
772
|
+
const replacementPlan = planUpdateRuntimeHandling({ ...replacementOwnership, serviceInstalled: serviceWasInstalled, supervision: observeSupervision() });
|
|
726
773
|
const replacementLiveness = currentPackageRuntimeLiveness();
|
|
727
774
|
if (replacementOwnership.subjectToken !== initialOwnership.subjectToken
|
|
728
775
|
|| !replacementPlan.mayReplacePackage
|
|
@@ -829,9 +876,10 @@ function runPackageManagerSelfUpdate(manager) {
|
|
|
829
876
|
// path and keeps token restoration coupled to the lease itself.
|
|
830
877
|
releaseUpdateLease();
|
|
831
878
|
}
|
|
832
|
-
const postInstallPlan = planUpdateRuntimeHandling({ ...readOwnership(), serviceInstalled: serviceWasInstalled });
|
|
879
|
+
const postInstallPlan = planUpdateRuntimeHandling({ ...readOwnership(), serviceInstalled: serviceWasInstalled, supervision: observeSupervision() });
|
|
833
880
|
if (res.status === 0) {
|
|
834
881
|
console.log(`\nUpdated${latest ? ` to v${latest}` : ""}.`);
|
|
882
|
+
if (!postInstallPlan.mayStopRuntime) console.warn("Runtime authority does not permit CLI recovery; no service was refreshed or proxy started.");
|
|
835
883
|
repairCodexShimIfNeeded(postUpdateLauncher);
|
|
836
884
|
if (trayBeforeUpdate.refreshAfterReplacement) {
|
|
837
885
|
const tray = spawnSync(process.execPath, [postUpdateLauncher, ...trayBeforeUpdate.installArgs], {
|
|
@@ -884,6 +932,8 @@ const BUN_OVERRIDE_ENV = "OPENCODEX_BUN_PATH";
|
|
|
884
932
|
// imported; tests/cli/ocx-launcher-source.test.ts pins the two together.
|
|
885
933
|
const BUN_RUNTIME_SOURCE_ENV = "OCX_BUN_RUNTIME_SOURCE";
|
|
886
934
|
const BUN_RUNTIME_PATH_ENV = "OCX_BUN_RUNTIME_PATH";
|
|
935
|
+
/** Total budget for validating a PATH Bun fallback (both probes together). */
|
|
936
|
+
const PATH_BUN_PROBE_BUDGET_MS = 5_000;
|
|
887
937
|
|
|
888
938
|
function findBunBinary(bunDir) {
|
|
889
939
|
// The bundled `bun` package ships the binary as bin/bun.exe on every platform;
|
|
@@ -899,6 +949,7 @@ function fail(msg) {
|
|
|
899
949
|
const reinstall = installMethod === "pnpm"
|
|
900
950
|
? "pnpm add -g --allow-build=bun @bitkyc08/opencodex"
|
|
901
951
|
: "npm install -g --allow-scripts=bun @bitkyc08/opencodex";
|
|
952
|
+
const desktopCli = findDesktopCli();
|
|
902
953
|
console.error(
|
|
903
954
|
`opencodex: ${msg}\n` +
|
|
904
955
|
"The bundled Bun runtime could not be prepared. This usually means the\n" +
|
|
@@ -906,7 +957,8 @@ function fail(msg) {
|
|
|
906
957
|
"or pnpm did not approve bun's build) or optional dependencies. Reinstall with:\n" +
|
|
907
958
|
` ${reinstall}\n` +
|
|
908
959
|
"(use sudo if the original install used sudo; without --ignore-scripts\n" +
|
|
909
|
-
"and without --omit=optional / optional=false)"
|
|
960
|
+
"and without --omit=optional / optional=false)" +
|
|
961
|
+
(desktopCli ? `\nAn installed Desktop CLI is available: "${desktopCli}"` : "")
|
|
910
962
|
);
|
|
911
963
|
process.exit(1);
|
|
912
964
|
}
|
|
@@ -923,25 +975,32 @@ function resolveBun({ allowInstall = true } = {}) {
|
|
|
923
975
|
);
|
|
924
976
|
}
|
|
925
977
|
|
|
926
|
-
let bunDir;
|
|
978
|
+
let bunDir = null;
|
|
927
979
|
try {
|
|
928
980
|
bunDir = bunBinDir();
|
|
929
|
-
} catch {
|
|
930
|
-
fail("the `bun` dependency is not installed.");
|
|
931
|
-
}
|
|
981
|
+
} catch { /* Missing dependency can still fall back to a validated PATH Bun. */ }
|
|
932
982
|
|
|
933
|
-
let bin = findBunBinary(bunDir);
|
|
983
|
+
let bin = bunDir ? findBunBinary(bunDir) : null;
|
|
934
984
|
if (bin) return { path: bin, source: "bundled" };
|
|
935
985
|
|
|
936
986
|
// Lazy fallback: --ignore-scripts (or a failed postinstall) leaves the
|
|
937
987
|
// ~450-byte placeholder stub. Run the bun package's own installer once.
|
|
938
|
-
const installJs = join(bunDir, "install.js");
|
|
939
|
-
if (allowInstall && existsSync(installJs)) {
|
|
988
|
+
const installJs = bunDir ? join(bunDir, "install.js") : null;
|
|
989
|
+
if (allowInstall && installJs && existsSync(installJs)) {
|
|
940
990
|
const r = spawnSync(process.execPath, [installJs], { stdio: "inherit" });
|
|
941
991
|
if (r.status === 0) bin = findBunBinary(bunDir);
|
|
942
992
|
}
|
|
943
|
-
if (
|
|
944
|
-
|
|
993
|
+
if (bin) return { path: bin, source: "bundled" };
|
|
994
|
+
|
|
995
|
+
// Reached only when the bundled runtime is unusable. The two probes start the candidate Bun
|
|
996
|
+
// cold, and on Windows a first run of a copied bun.exe is often held by an on-access scan for
|
|
997
|
+
// well over a second, so the total budget is generous rather than interactive-tight.
|
|
998
|
+
const pathBun = findPathBun({ pinnedVersion: pinnedBunVersion(), deadlineMs: PATH_BUN_PROBE_BUDGET_MS });
|
|
999
|
+
if (pathBun) {
|
|
1000
|
+
console.error(`opencodex: using PATH Bun ${pathBun.version}.`);
|
|
1001
|
+
return { path: pathBun.path, source: "process" };
|
|
1002
|
+
}
|
|
1003
|
+
fail(bunDir ? "Bun binary missing after install attempt." : "the `bun` dependency is not installed.");
|
|
945
1004
|
}
|
|
946
1005
|
|
|
947
1006
|
// `ocx update --help` prints usage and exits WITHOUT side effects. The Node launcher
|
|
@@ -961,6 +1020,36 @@ if (codexCliUpdateInspection && typeof process.versions.bun === "string") {
|
|
|
961
1020
|
process.exit(1);
|
|
962
1021
|
}
|
|
963
1022
|
|
|
1023
|
+
const { launchProof, launchContext, inheritedEnv } = createNodeLaunchContext();
|
|
1024
|
+
const desktopPlan = planDesktopCliHandoff({ selfPaths: [fileURLToPath(import.meta.url), process.execPath] });
|
|
1025
|
+
if (desktopPlan.kind === "error") {
|
|
1026
|
+
if (desktopPlan.issue.startsWith("record-")) {
|
|
1027
|
+
console.error(`opencodex: OpenCodex Desktop's terminal-command record at ${desktopCliRecordPath()} could not be used (${desktopPlan.issue}). Open OpenCodex Desktop to repair the terminal command, or run with OCX_NO_DESKTOP_HANDOFF=1.`);
|
|
1028
|
+
} else {
|
|
1029
|
+
console.error(`opencodex: Desktop CLI handoff refused (${desktopPlan.issue}). Repair the terminal command in OpenCodex Desktop, or run with OCX_NO_DESKTOP_HANDOFF=1.`);
|
|
1030
|
+
}
|
|
1031
|
+
process.exit(1);
|
|
1032
|
+
}
|
|
1033
|
+
if (desktopPlan.kind === "handoff") {
|
|
1034
|
+
const result = await runDesktopCliHandoff(desktopPlan, {
|
|
1035
|
+
argv: process.argv.slice(2), env: inheritedEnv, proof: launchProof, context: launchContext,
|
|
1036
|
+
});
|
|
1037
|
+
if (result.kind === "error") {
|
|
1038
|
+
console.error(`opencodex: Desktop CLI handoff failed (${result.issue}). Repair the terminal command in OpenCodex Desktop, or run with OCX_NO_DESKTOP_HANDOFF=1.`);
|
|
1039
|
+
process.exit(127);
|
|
1040
|
+
}
|
|
1041
|
+
if (result.kind === "exit") {
|
|
1042
|
+
if (result.signal) {
|
|
1043
|
+
process.kill(process.pid, result.signal);
|
|
1044
|
+
} else {
|
|
1045
|
+
process.exit(result.code);
|
|
1046
|
+
}
|
|
1047
|
+
// Signal delivery is asynchronous; never resume package repair or Bun lookup.
|
|
1048
|
+
await new Promise(() => {});
|
|
1049
|
+
}
|
|
1050
|
+
// Only confirmed target ENOENT reaches the ordinary package path below.
|
|
1051
|
+
}
|
|
1052
|
+
|
|
964
1053
|
if (process.argv[2] === "update" && installMethod === "mise") {
|
|
965
1054
|
if (installOwnership.owner) {
|
|
966
1055
|
console.error(
|
|
@@ -1015,64 +1104,68 @@ const bun = bunRuntime.path;
|
|
|
1015
1104
|
// billing and prevents it from redirecting the subscriber's OAuth bearer.
|
|
1016
1105
|
// Disabling Bun's dotenv wholesale with --no-env-file is NOT an option: config
|
|
1017
1106
|
// interpolation and provider settings legitimately read the project environment.
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
1023
|
-
//
|
|
1024
|
-
//
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
const
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
)
|
|
1043
|
-
const launchProof = randomBytes(32).toString("base64url");
|
|
1044
|
-
const launchContext = JSON.stringify({
|
|
1045
|
-
version: 1,
|
|
1046
|
-
proof: launchProof,
|
|
1047
|
-
anthropicEnvSlots: preBunAnthropicSlots,
|
|
1048
|
-
codexCliInspectionEnv: codexCliUpdateInspection ? {
|
|
1049
|
-
codexCliPath: preBunCodexCliPath,
|
|
1050
|
-
path: preBunPath,
|
|
1051
|
-
pathExt: preBunPathExt,
|
|
1052
|
-
managerRoots: preBunCodexCliManagerRoots,
|
|
1053
|
-
configDir: configDir(),
|
|
1054
|
-
} : null,
|
|
1055
|
-
});
|
|
1056
|
-
// The inspection snapshot above already carries PATH, PATHEXT, and the manager-root slots as
|
|
1057
|
-
// proof-bound values, and `inspectCodexCliInstall` reads them from that snapshot rather than
|
|
1058
|
-
// from the live environment. Inheriting them again would spend the 32,767-character Windows
|
|
1059
|
-
// environment block twice, so a large-but-valid shell environment could stop the Bun child
|
|
1060
|
-
// from spawning and fail the command before it reports anything. Drop the duplicates for the
|
|
1061
|
-
// one-shot inspection launch only; every other launch inherits the environment unchanged.
|
|
1062
|
-
// Windows environment names are case-insensitive, but this spread produces an ordinary
|
|
1063
|
-
// case-sensitive object, and a real Windows environment commonly spells the variable `Path`.
|
|
1064
|
-
// Deleting only the canonical upper-case spelling would silently leave that copy behind and
|
|
1065
|
-
// reintroduce the duplication this block exists to prevent, so match on the lowercase form.
|
|
1066
|
-
const inheritedEnv = { ...process.env };
|
|
1067
|
-
if (codexCliUpdateInspection) {
|
|
1068
|
-
const snapshotted = new Set(
|
|
1069
|
-
["PATH", "PATHEXT", ...CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS].map(name => name.toLowerCase()),
|
|
1107
|
+
function createNodeLaunchContext() {
|
|
1108
|
+
const preBunAnthropicSlots = ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL"]
|
|
1109
|
+
.filter(name => typeof process.env[name] === "string" && process.env[name] !== "");
|
|
1110
|
+
// A configured CODEX_CLI_PATH may legitimately be cwd-relative (`./tools/codex`), which the
|
|
1111
|
+
// ordinary runtime resolver accepts. Inspection only trusts absolute local paths, so capture
|
|
1112
|
+
// the absolute form here, in the launcher, while the original cwd is still authoritative;
|
|
1113
|
+
// resolving it later would silently reinterpret it against a different working directory.
|
|
1114
|
+
//
|
|
1115
|
+
// A bare command with no separator (`codex`) is NOT a relative path: the runtime resolver
|
|
1116
|
+
// deliberately hands those to executable lookup along PATH. Rewriting it to `<cwd>/codex`
|
|
1117
|
+
// would make the inspector treat it as an explicit path and stop searching PATH entirely.
|
|
1118
|
+
const configuredCodexCliPath = typeof process.env.CODEX_CLI_PATH === "string" && process.env.CODEX_CLI_PATH !== ""
|
|
1119
|
+
? process.env.CODEX_CLI_PATH
|
|
1120
|
+
: null;
|
|
1121
|
+
const preBunCodexCliPath = configuredCodexCliPath !== null
|
|
1122
|
+
&& (configuredCodexCliPath.includes("/") || configuredCodexCliPath.includes("\\") || /^[A-Za-z]:/.test(configuredCodexCliPath))
|
|
1123
|
+
? resolve(configuredCodexCliPath)
|
|
1124
|
+
: configuredCodexCliPath;
|
|
1125
|
+
const preBunPath = typeof process.env.PATH === "string" ? process.env.PATH : null;
|
|
1126
|
+
const preBunPathExt = typeof process.env.PATHEXT === "string" ? process.env.PATHEXT : null;
|
|
1127
|
+
const preBunCodexCliManagerRoots = Object.fromEntries(
|
|
1128
|
+
CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS.flatMap(name => {
|
|
1129
|
+
const value = process.env[name];
|
|
1130
|
+
return typeof value === "string" && value !== "" ? [[name, value]] : [];
|
|
1131
|
+
}),
|
|
1070
1132
|
);
|
|
1071
|
-
|
|
1072
|
-
|
|
1133
|
+
const launchProof = randomBytes(32).toString("base64url");
|
|
1134
|
+
const launchContext = JSON.stringify({
|
|
1135
|
+
version: 1,
|
|
1136
|
+
proof: launchProof,
|
|
1137
|
+
anthropicEnvSlots: preBunAnthropicSlots,
|
|
1138
|
+
codexCliInspectionEnv: codexCliUpdateInspection ? {
|
|
1139
|
+
codexCliPath: preBunCodexCliPath,
|
|
1140
|
+
path: preBunPath,
|
|
1141
|
+
pathExt: preBunPathExt,
|
|
1142
|
+
managerRoots: preBunCodexCliManagerRoots,
|
|
1143
|
+
configDir: configDir(),
|
|
1144
|
+
} : null,
|
|
1145
|
+
});
|
|
1146
|
+
// The inspection snapshot above already carries PATH, PATHEXT, and the manager-root slots as
|
|
1147
|
+
// proof-bound values, and `inspectCodexCliInstall` reads them from that snapshot rather than
|
|
1148
|
+
// from the live environment. Inheriting them again would spend the 32,767-character Windows
|
|
1149
|
+
// environment block twice, so a large-but-valid shell environment could stop the Bun child
|
|
1150
|
+
// from spawning and fail the command before it reports anything. Drop the duplicates for the
|
|
1151
|
+
// one-shot inspection launch only; every other launch inherits the environment unchanged.
|
|
1152
|
+
// Windows environment names are case-insensitive, but this spread produces an ordinary
|
|
1153
|
+
// case-sensitive object, and a real Windows environment commonly spells the variable `Path`.
|
|
1154
|
+
// Deleting only the canonical upper-case spelling would silently leave that copy behind and
|
|
1155
|
+
// reintroduce the duplication this block exists to prevent, so match on the lowercase form.
|
|
1156
|
+
const inheritedEnv = { ...process.env };
|
|
1157
|
+
if (codexCliUpdateInspection) {
|
|
1158
|
+
const snapshotted = new Set(
|
|
1159
|
+
["PATH", "PATHEXT", ...CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS].map(name => name.toLowerCase()),
|
|
1160
|
+
);
|
|
1161
|
+
for (const name of Object.keys(inheritedEnv)) {
|
|
1162
|
+
if (snapshotted.has(name.toLowerCase())) delete inheritedEnv[name];
|
|
1163
|
+
}
|
|
1073
1164
|
}
|
|
1165
|
+
return { launchProof, launchContext, inheritedEnv };
|
|
1074
1166
|
}
|
|
1075
|
-
|
|
1167
|
+
|
|
1168
|
+
const child = spawn(bun, [cliPath, `${NODE_LAUNCH_PROOF_PREFIX}${launchProof}`, ...process.argv.slice(2).filter(arg => !arg.startsWith(NODE_LAUNCH_PROOF_PREFIX))], {
|
|
1076
1169
|
stdio: "inherit",
|
|
1077
1170
|
// A headless Windows parent (Task Scheduler, dashboard restart, shortcut) has no
|
|
1078
1171
|
// console to inherit. Without this flag Windows allocates a visible console for
|