@bitkyc08/opencodex 2.83.0 → 2.86.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/README.md +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/ollama-native.ts +29 -1
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +3 -0
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +56 -15
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -0
- package/src/bridge.ts +1 -1
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/context-windows.ts +54 -1
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +26 -2
- package/src/cli/account.ts +41 -5
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +14 -2
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +60 -11
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +21 -4
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +15 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +26 -8
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +20 -7
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +59 -16
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +7 -1
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +6 -3
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-models.ts +16 -2
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +15 -6
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +18 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +56 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +8 -2
- package/src/config/paths.ts +13 -3
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +19 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +713 -285
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/reset-coupon-ledger.ts +75 -5
- package/src/grok/reset-coupons.ts +4 -2
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +14 -7
- package/src/integrations/registry.ts +52 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/store.ts +3 -0
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +24 -7
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/shadow-call.ts +44 -5
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +28 -17
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +55 -6
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +2 -0
- package/src/plugins/loader.ts +11 -0
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +21 -20
- package/src/providers/registry/entries-extended.ts +19 -1
- package/src/providers/registry/model-seeds.ts +17 -0
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +37 -20
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +46 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +15 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/lifecycle.ts +2 -1
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +50 -14
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/grok-coupon-routes.ts +78 -27
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +82 -402
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +16 -3
- package/src/server/management/shared.ts +3 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +73 -43
- package/src/server/relay-eager.ts +2 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +63 -6
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +261 -81
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +313 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +126 -48
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +24 -1
- package/src/server/responses/core-combo.ts +28 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +48 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/passthrough-delivery.ts +20 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -7
- package/src/server/responses/request-prepare.ts +74 -9
- package/src/server/responses/request-transport.ts +22 -8
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +89 -5
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +38 -4
- package/src/server/startup-health-cache.ts +5 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +161 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +466 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +71 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +68 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +19 -1
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-Ba8uG696.js +0 -54
- package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
- package/gui/dist/assets/index-BHRZdJuv.js +0 -94
- package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
package/src/update/job.ts
CHANGED
|
@@ -2,18 +2,8 @@ import { spawn, spawnSync, type ChildProcess } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, openSync, readFileSync, writeSync } from "node:fs";
|
|
3
3
|
import { dirname, join, sep } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
getConfigDir,
|
|
8
|
-
loadConfig,
|
|
9
|
-
} from "../config";
|
|
10
|
-
import {
|
|
11
|
-
readPid,
|
|
12
|
-
readRuntimePort,
|
|
13
|
-
removePid,
|
|
14
|
-
removeRuntimePort,
|
|
15
|
-
verifyPidIdentity,
|
|
16
|
-
} from "../config/process-state";
|
|
5
|
+
import { atomicWriteFile, getConfigDir, loadConfig } from "../config";
|
|
6
|
+
import { readPid, readRuntimePort, removePid, removeRuntimePort, verifyPidIdentity } from "../config/process-state";
|
|
17
7
|
import { isProcessAlive, killProxy } from "../lib/process-control";
|
|
18
8
|
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
19
9
|
import { killWindowsSchedulerWrappers } from "../lib/windows-service-wrappers";
|
|
@@ -33,9 +23,10 @@ import {
|
|
|
33
23
|
} from "../server/proxy-liveness";
|
|
34
24
|
import { isServiceInstalled, isServiceViable, readServiceBackend, stopWindows } from "../service";
|
|
35
25
|
import { runUpdateRestartWithOwnershipLease, type ServiceOwnershipResolution } from "./restart-ownership";
|
|
26
|
+
import { restartServiceManagerAfterUpdate, unsettledManagerRestartFailure, unsupervisedRestartFailure, type ManagerRestartFn } from "./job-supervised-restart";
|
|
36
27
|
import {
|
|
37
28
|
type Channel, type Installer,
|
|
38
|
-
PKG,
|
|
29
|
+
PKG, STANDALONE_UPDATE_HINT,
|
|
39
30
|
checkUpdatePackageIntegrity,
|
|
40
31
|
currentVersion,
|
|
41
32
|
defaultUpdateTag,
|
|
@@ -492,17 +483,17 @@ export function checkForUpdate(
|
|
|
492
483
|
const ownership = deps.detectInstallOwnership?.();
|
|
493
484
|
const installer = ownership?.installer ?? deps.detectInstall();
|
|
494
485
|
const channel = requestedChannel ?? normalizeUpdateChannel(null, current);
|
|
495
|
-
const latest = installer === "source" ? null : deps.latestVersion(channel);
|
|
486
|
+
const latest = installer === "source" || installer === "standalone" ? null : deps.latestVersion(channel);
|
|
496
487
|
const updateAvailable = !!latest && isNewer(latest, current, channel);
|
|
497
488
|
let reason: string | undefined;
|
|
498
|
-
let command = installer === "source"
|
|
489
|
+
let command = installer === "standalone" ? STANDALONE_UPDATE_HINT : installer === "source"
|
|
499
490
|
? manualSourceCommand()
|
|
500
491
|
: installer === "mise"
|
|
501
492
|
? (ownership && deps.miseUpdateCommand?.(ownership)) ?? ""
|
|
502
493
|
: updateExecutionCommand(installer, channel).display;
|
|
503
494
|
|
|
504
|
-
if (installer === "source") {
|
|
505
|
-
reason = "source_checkout";
|
|
495
|
+
if (installer === "source" || installer === "standalone") {
|
|
496
|
+
reason = installer === "source" ? "source_checkout" : "standalone_install";
|
|
506
497
|
} else if (installer === "mise") {
|
|
507
498
|
reason = command ? "externally_managed" : "external_ownership_invalid";
|
|
508
499
|
} else if (!latest) {
|
|
@@ -517,7 +508,7 @@ export function checkForUpdate(
|
|
|
517
508
|
channel,
|
|
518
509
|
installer,
|
|
519
510
|
updateAvailable,
|
|
520
|
-
canUpdate: installer !== "source" && installer !== "mise" && updateAvailable,
|
|
511
|
+
canUpdate: installer !== "source" && installer !== "standalone" && installer !== "mise" && updateAvailable,
|
|
521
512
|
command,
|
|
522
513
|
releaseNotesUrl: RELEASE_NOTES_URL,
|
|
523
514
|
...(reason ? { reason } : {}),
|
|
@@ -1005,6 +996,7 @@ export interface RestartIo {
|
|
|
1005
996
|
releaseForServiceManagerFn?: () => void;
|
|
1006
997
|
/** Re-acquire the lease for the direct-start fallthrough, then re-run the veto; a notice stops the restart. */
|
|
1007
998
|
reacquireForDirectStartFn?: () => { readonly notice: string; readonly failed: boolean } | null;
|
|
999
|
+
restartServiceManagerFn?: ManagerRestartFn; // launchd/systemd restart before a managed direct fallback (#7043)
|
|
1008
1000
|
probeProxy?: (port: number, hostname?: string) => Promise<boolean>;
|
|
1009
1001
|
/** Richer /healthz read for update-correlated restart evidence (pid + version). */
|
|
1010
1002
|
probeProxyIdentity?: (port: number, hostname?: string) => Promise<RestartProxyIdentity | null>;
|
|
@@ -1035,7 +1027,7 @@ export interface RestartIo {
|
|
|
1035
1027
|
job: UpdateJobState,
|
|
1036
1028
|
captured?: { port: number; hostname: string; oldPid?: number },
|
|
1037
1029
|
io?: RestartIo,
|
|
1038
|
-
) => Promise<false | void>;
|
|
1030
|
+
) => Promise<false | void | "unsupervised">;
|
|
1039
1031
|
/**
|
|
1040
1032
|
* PIDs currently LISTENing on the captured port. Used to widen the post-update
|
|
1041
1033
|
* kill allowlist beyond the pre-update PID (Windows often leaves a respawned
|
|
@@ -1098,7 +1090,7 @@ async function restartAfterUpdate(
|
|
|
1098
1090
|
job: UpdateJobState,
|
|
1099
1091
|
captured?: { port: number; hostname: string; oldPid?: number },
|
|
1100
1092
|
io: RestartIo = {},
|
|
1101
|
-
): Promise<false | void> {
|
|
1093
|
+
): Promise<false | void | "unsupervised"> {
|
|
1102
1094
|
const serviceInstalled = (io.serviceInstalledFn ?? isServiceInstalled)();
|
|
1103
1095
|
const config = loadConfig();
|
|
1104
1096
|
// Stop-first update already cleared pid/runtime state, so the pre-update capture is
|
|
@@ -1250,6 +1242,12 @@ async function restartAfterUpdate(
|
|
|
1250
1242
|
}
|
|
1251
1243
|
}
|
|
1252
1244
|
|
|
1245
|
+
// #7043: a managed install asks launchd/systemd first; only the manager's own job counts as recovered.
|
|
1246
|
+
const managed = !serviceInstalled ? "failed" : await (io.restartServiceManagerFn ?? restartServiceManagerAfterUpdate)(
|
|
1247
|
+
{ port, hostname, release: io.releaseForServiceManagerFn }, line => updateJob(job, {}, line));
|
|
1248
|
+
if (managed === "supervised") return;
|
|
1249
|
+
if (managed === "unsettled") { updateJob(job, ...unsettledManagerRestartFailure(port)); return false; }
|
|
1250
|
+
const fallback = serviceInstalled ? "unsupervised" as const : undefined; // every exit below is unproven ownership
|
|
1253
1251
|
// The service refresh ran outside the lease; take it back so the veto and the kills/start below stay serialized.
|
|
1254
1252
|
const restartVeto = io.reacquireForDirectStartFn?.();
|
|
1255
1253
|
if (restartVeto) {
|
|
@@ -1282,7 +1280,7 @@ async function restartAfterUpdate(
|
|
|
1282
1280
|
);
|
|
1283
1281
|
if (liveHolders.length > 0) {
|
|
1284
1282
|
updateJob(job, {}, `Live holder(s) remain on port ${port}; not starting on another port. Retry 'ocx start --port ${port}'.`);
|
|
1285
|
-
return;
|
|
1283
|
+
return fallback;
|
|
1286
1284
|
}
|
|
1287
1285
|
// Dead PIDs can still own LISTEN rows. SetTcpEntry needs elevation (rc 317 on a
|
|
1288
1286
|
// normal update worker), so poll until netstat is empty and the start runtime can bind.
|
|
@@ -1306,7 +1304,7 @@ async function restartAfterUpdate(
|
|
|
1306
1304
|
+ `${formatPortHolders(port, listPids, verifyOcx, directAllow)} `
|
|
1307
1305
|
+ `Retry 'ocx start --port ${port}'.`,
|
|
1308
1306
|
);
|
|
1309
|
-
return;
|
|
1307
|
+
return fallback;
|
|
1310
1308
|
}
|
|
1311
1309
|
}
|
|
1312
1310
|
}
|
|
@@ -1314,7 +1312,7 @@ async function restartAfterUpdate(
|
|
|
1314
1312
|
// retries on missing /healthz after prepare + ghost-LISTEN clear.
|
|
1315
1313
|
if (io.spawnStart) {
|
|
1316
1314
|
io.spawnStart(job, job.installer, port, launcher);
|
|
1317
|
-
return;
|
|
1315
|
+
return fallback;
|
|
1318
1316
|
}
|
|
1319
1317
|
const sleep = io.sleepMs ?? ((ms: number) => new Promise<void>(r => setTimeout(r, ms)));
|
|
1320
1318
|
const probe = io.probeProxy ?? (async (p: number, host?: string) => (
|
|
@@ -1334,7 +1332,7 @@ async function restartAfterUpdate(
|
|
|
1334
1332
|
{},
|
|
1335
1333
|
`Proxy already healthy on ${hostname}:${port} at ${expectedVersion}; skipping pinned start.`,
|
|
1336
1334
|
);
|
|
1337
|
-
return;
|
|
1335
|
+
return fallback;
|
|
1338
1336
|
}
|
|
1339
1337
|
}
|
|
1340
1338
|
const attempts = 3;
|
|
@@ -1393,12 +1391,13 @@ async function restartAfterUpdate(
|
|
|
1393
1391
|
child.once("close", retireChild);
|
|
1394
1392
|
const healthDeadline = now() + perAttemptHealthMs;
|
|
1395
1393
|
while (now() < healthDeadline) {
|
|
1396
|
-
if (await probe(port, hostname)) return;
|
|
1394
|
+
if (await probe(port, hostname)) return fallback;
|
|
1397
1395
|
await sleep(500);
|
|
1398
1396
|
}
|
|
1399
1397
|
}
|
|
1400
1398
|
// Exhausted retries: do not leave a hung pinned-start child owning the port.
|
|
1401
1399
|
killSpawnAttempt(lastChild);
|
|
1400
|
+
return fallback;
|
|
1402
1401
|
}
|
|
1403
1402
|
|
|
1404
1403
|
/** Compact listen-holder summary for update-job logs when reclaim fails. */
|
|
@@ -1454,7 +1453,7 @@ export function restartAfterUpdateForTests(
|
|
|
1454
1453
|
job: UpdateJobState,
|
|
1455
1454
|
captured: { port: number; hostname: string; oldPid?: number },
|
|
1456
1455
|
io: RestartIo,
|
|
1457
|
-
): Promise<false | void> {
|
|
1456
|
+
): Promise<false | void | "unsupervised"> {
|
|
1458
1457
|
return restartAfterUpdate(job, captured, io);
|
|
1459
1458
|
}
|
|
1460
1459
|
|
|
@@ -1729,12 +1728,13 @@ export async function finishGuiUpdateRestart(
|
|
|
1729
1728
|
}
|
|
1730
1729
|
}
|
|
1731
1730
|
const restartFn = io.restartAfterUpdateFn ?? restartAfterUpdate;
|
|
1732
|
-
|
|
1733
|
-
if (
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1731
|
+
const restarted = await restartFn(job, captured, io);
|
|
1732
|
+
if (restarted === false) return false;
|
|
1733
|
+
// Bun/source: health alone remains enough unless a richer identity probe is supplied.
|
|
1734
|
+
const confirm = !usesNodeLauncher(installer) && !io.probeProxyIdentity ? confirmRestartedProxy : confirmPackageManagerExplicitRestart;
|
|
1735
|
+
const confirmed = await confirm(job, captured, io);
|
|
1736
|
+
if (confirmed && restarted === "unsupervised") updateJob(job, ...unsupervisedRestartFailure(captured.port)); // #7043
|
|
1737
|
+
return confirmed && restarted !== "unsupervised";
|
|
1738
1738
|
}
|
|
1739
1739
|
|
|
1740
1740
|
/**
|
package/src/update/notify.ts
CHANGED
|
@@ -152,7 +152,7 @@ export function interactiveGuardOk(): boolean {
|
|
|
152
152
|
*/
|
|
153
153
|
export function shouldConsider(): { channel: Channel; current: string } | null {
|
|
154
154
|
const installer = detectInstall();
|
|
155
|
-
if (installer === "source" || installer === "mise") return null;
|
|
155
|
+
if (installer === "source" || installer === "standalone" || installer === "mise") return null;
|
|
156
156
|
const current = currentVersion();
|
|
157
157
|
if (current === "?" || isSourceBuildVersion(current)) return null;
|
|
158
158
|
if (!interactiveGuardOk()) return null;
|
|
@@ -53,7 +53,7 @@ export function createRefreshScheduler(deps: RefreshDeps = defaults) {
|
|
|
53
53
|
const version = deps.current();
|
|
54
54
|
const installer = deps.install();
|
|
55
55
|
return !deps.disabled() && version !== "?" && !isSourceBuildVersion(version)
|
|
56
|
-
&& installer !== "source" && installer !== "mise";
|
|
56
|
+
&& installer !== "source" && installer !== "standalone" && installer !== "mise";
|
|
57
57
|
};
|
|
58
58
|
const channel = () => defaultUpdateTag(deps.current());
|
|
59
59
|
const stale = (tag: Channel) => {
|
|
@@ -123,7 +123,7 @@ export function createRefreshScheduler(deps: RefreshDeps = defaults) {
|
|
|
123
123
|
},
|
|
124
124
|
async check(tag: Channel): Promise<UpdateCheckResult> {
|
|
125
125
|
const installer = deps.install();
|
|
126
|
-
const latest = installer === "source" || installer === "mise" ? null : await lookup(tag, false);
|
|
126
|
+
const latest = installer === "source" || installer === "standalone" || installer === "mise" ? null : await lookup(tag, false);
|
|
127
127
|
return checkForUpdate(tag, {
|
|
128
128
|
currentVersion: deps.current,
|
|
129
129
|
detectInstall: deps.install,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Declarations for the plain-ESM manager recovery shared with `bin/ocx.mjs` (#7043). */
|
|
2
|
+
export type ServiceManagerRun = (file: string, args: string[]) => { ok: boolean; stdout: string; stderr: string; timedOut?: boolean };
|
|
3
|
+
export interface ServiceManagerDeps {
|
|
4
|
+
platform?: NodeJS.Platform;
|
|
5
|
+
uid?: number;
|
|
6
|
+
run?: ServiceManagerRun;
|
|
7
|
+
}
|
|
8
|
+
export type SupervisionVerdict =
|
|
9
|
+
| { kind: "supervised"; managerPid: number; servingPid: number }
|
|
10
|
+
| { kind: "failed"; reason: string };
|
|
11
|
+
export declare const LAUNCHD_SERVICE_LABEL: string;
|
|
12
|
+
export declare const SYSTEMD_SERVICE_UNIT: string;
|
|
13
|
+
export declare function parseLaunchdJobPid(printOutput: string): number | null;
|
|
14
|
+
export declare function parseSystemdMainPid(showOutput: string): number | null;
|
|
15
|
+
export declare function hasRestartableServiceManager(deps?: ServiceManagerDeps): boolean;
|
|
16
|
+
export declare function restartServiceManagerJob(deps?: ServiceManagerDeps):
|
|
17
|
+
{ ok: true } | { ok: false; reason: string; unsettled?: true };
|
|
18
|
+
export declare function freshServingPid(recordPid: number, staleRecordPid: number | null): number | null;
|
|
19
|
+
export declare function unsettledManagerWarning(port: number): string;
|
|
20
|
+
export declare function readServiceManagerPid(deps?: ServiceManagerDeps): number | null;
|
|
21
|
+
export declare function readParentPid(pid: number, deps?: ServiceManagerDeps): number | null;
|
|
22
|
+
export declare function supervisionVerdict(input: {
|
|
23
|
+
servingPid: number | null;
|
|
24
|
+
managerPid: number | null;
|
|
25
|
+
parentPid: (pid: number) => number | null;
|
|
26
|
+
}): SupervisionVerdict;
|
|
27
|
+
export declare function unsupervisedRuntimeWarning(port: number): string;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manager-mediated runtime recovery after an update (#7043).
|
|
5
|
+
*
|
|
6
|
+
* When the service refresh after an update fails or leaves a non-viable manager, both update
|
|
7
|
+
* lanes used to fall straight back to a detached `ocx start`. That proxy answers, but launchd
|
|
8
|
+
* or systemd no longer owns it: a crash is not restarted, and the next `launchctl kickstart`
|
|
9
|
+
* races the detached process for the port. Before that fallback, the lanes now ask the manager
|
|
10
|
+
* to restart the job it already holds, and count it as recovered only when the serving proxy is
|
|
11
|
+
* provably the manager's process: the runtime record's PID is the manager's main PID, or that
|
|
12
|
+
* PID's direct child (the job execs the Node launcher, which runs the Bun proxy as its child).
|
|
13
|
+
*
|
|
14
|
+
* Plain synchronous ESM because `bin/ocx.mjs` runs under Node, cannot import TypeScript, and
|
|
15
|
+
* keeps straight-line control flow; `src/update/index.ts` composes the same pieces with its own
|
|
16
|
+
* asynchronous identity wait.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Must match `LABEL` / `TASK` in src/service/state.ts; a test pins the pairing. */
|
|
20
|
+
export const LAUNCHD_SERVICE_LABEL = "com.opencodex.proxy";
|
|
21
|
+
export const SYSTEMD_SERVICE_UNIT = "opencodex-proxy";
|
|
22
|
+
|
|
23
|
+
function positivePid(value) {
|
|
24
|
+
if (typeof value !== "string" || !/^\d+$/.test(value.trim())) return null;
|
|
25
|
+
const pid = Number(value.trim());
|
|
26
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function defaultRun(file, args) {
|
|
30
|
+
// An armed test process must never reach the developer's real launchd or systemd job;
|
|
31
|
+
// same refusal as `assertLiveServiceManagerAllowed` for the TypeScript service layer.
|
|
32
|
+
if (process.env.OCX_TEST_HOME_GUARD === "1") {
|
|
33
|
+
return { ok: false, stdout: "", stderr: `refusing to run ${file} from an armed test process` };
|
|
34
|
+
}
|
|
35
|
+
// A bare SSH session has no user-bus address; systemd's user manager still lives at the
|
|
36
|
+
// conventional runtime directory (same fallback as the service layer's ensureUserBusEnv).
|
|
37
|
+
const env = file === "systemctl" && !process.env.XDG_RUNTIME_DIR && typeof process.getuid === "function"
|
|
38
|
+
? { ...process.env, XDG_RUNTIME_DIR: `/run/user/${process.getuid()}` }
|
|
39
|
+
: process.env;
|
|
40
|
+
const result = spawnSync(file, args, { encoding: "utf8", timeout: 20_000, windowsHide: true, env });
|
|
41
|
+
if (result.error) {
|
|
42
|
+
// A timeout kills only the waiting client; the manager's queued job keeps running.
|
|
43
|
+
const timedOut = result.error.code === "ETIMEDOUT";
|
|
44
|
+
return { ok: false, stdout: "", stderr: String(result.error.message ?? ""), ...(timedOut ? { timedOut } : {}) };
|
|
45
|
+
}
|
|
46
|
+
return { ok: result.status === 0, stdout: String(result.stdout ?? ""), stderr: String(result.stderr ?? "").trim() };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function launchdTarget(uid) {
|
|
50
|
+
return `gui/${uid}/${LAUNCHD_SERVICE_LABEL}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** `launchctl print` reports a running job as a `pid = <n>` line. */
|
|
54
|
+
export function parseLaunchdJobPid(printOutput) {
|
|
55
|
+
const match = /^\s*pid\s*=\s*(\d+)\s*$/m.exec(String(printOutput ?? ""));
|
|
56
|
+
return positivePid(match?.[1]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** `systemctl show -p MainPID` prints `MainPID=<n>`; 0 means not running. */
|
|
60
|
+
export function parseSystemdMainPid(showOutput) {
|
|
61
|
+
const match = /^MainPID=(\d+)\s*$/m.exec(String(showOutput ?? ""));
|
|
62
|
+
return positivePid(match?.[1]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolve(deps) {
|
|
66
|
+
const platform = deps.platform ?? process.platform;
|
|
67
|
+
const uid = deps.uid ?? (typeof process.getuid === "function" ? process.getuid() : 0);
|
|
68
|
+
return { platform, uid, run: deps.run ?? defaultRun };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Whether this platform has a manager this module can ask (launchd or systemd). */
|
|
72
|
+
export function hasRestartableServiceManager(deps = {}) {
|
|
73
|
+
const { platform } = resolve(deps);
|
|
74
|
+
return platform === "darwin" || platform === "linux";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Ask the manager to restart the job in place. Never throws. `unsettled` means the client gave up
|
|
79
|
+
* waiting while the manager may still complete the restart: callers must not start a competing
|
|
80
|
+
* proxy on that answer.
|
|
81
|
+
*/
|
|
82
|
+
export function restartServiceManagerJob(deps = {}) {
|
|
83
|
+
const { platform, uid, run } = resolve(deps);
|
|
84
|
+
const result = platform === "darwin"
|
|
85
|
+
? run("/bin/launchctl", ["kickstart", "-k", launchdTarget(uid)])
|
|
86
|
+
: platform === "linux"
|
|
87
|
+
? run("systemctl", ["--user", "restart", SYSTEMD_SERVICE_UNIT])
|
|
88
|
+
: null;
|
|
89
|
+
if (result === null) return { ok: false, reason: "no launchd or systemd manager on this platform" };
|
|
90
|
+
if (result.timedOut) return { ok: false, unsettled: true, reason: "the service manager did not finish the restart in time" };
|
|
91
|
+
if (!result.ok) return { ok: false, reason: result.stderr.split("\n")[0] || "the service manager refused the restart" };
|
|
92
|
+
return { ok: true };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The manager's main PID for the job, or null when it reports none. Never throws. */
|
|
96
|
+
export function readServiceManagerPid(deps = {}) {
|
|
97
|
+
const { platform, uid, run } = resolve(deps);
|
|
98
|
+
if (platform === "darwin") {
|
|
99
|
+
const printed = run("/bin/launchctl", ["print", launchdTarget(uid)]);
|
|
100
|
+
return printed.ok ? parseLaunchdJobPid(printed.stdout) : null;
|
|
101
|
+
}
|
|
102
|
+
if (platform === "linux") {
|
|
103
|
+
const shown = run("systemctl", ["--user", "show", "-p", "MainPID", SYSTEMD_SERVICE_UNIT]);
|
|
104
|
+
return shown.ok ? parseSystemdMainPid(shown.stdout) : null;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Parent PID of a process, or null when it cannot be read. Never throws. */
|
|
110
|
+
export function readParentPid(pid, deps = {}) {
|
|
111
|
+
const { run } = resolve(deps);
|
|
112
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return null;
|
|
113
|
+
const result = run("ps", ["-o", "ppid=", "-p", String(pid)]);
|
|
114
|
+
return result.ok ? positivePid(result.stdout) : null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the serving proxy is the manager's job. `parentPid` is consulted only when the
|
|
119
|
+
* serving PID is not the manager's own.
|
|
120
|
+
*/
|
|
121
|
+
export function supervisionVerdict({ servingPid, managerPid, parentPid }) {
|
|
122
|
+
if (!Number.isSafeInteger(servingPid) || servingPid <= 0) {
|
|
123
|
+
return { kind: "failed", reason: "the manager restarted the job but no proxy answered" };
|
|
124
|
+
}
|
|
125
|
+
if (!Number.isSafeInteger(managerPid) || managerPid <= 0) {
|
|
126
|
+
return { kind: "failed", reason: "the manager reports no running process for the job" };
|
|
127
|
+
}
|
|
128
|
+
if (servingPid === managerPid || parentPid(servingPid) === managerPid) {
|
|
129
|
+
return { kind: "supervised", managerPid, servingPid };
|
|
130
|
+
}
|
|
131
|
+
return { kind: "failed", reason: `the serving proxy (pid ${servingPid}) is not the manager's job (pid ${managerPid})` };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The serving PID to judge after a restart: a record still naming the pre-restart process is not
|
|
136
|
+
* evidence about the restarted one.
|
|
137
|
+
*/
|
|
138
|
+
export function freshServingPid(recordPid, staleRecordPid) {
|
|
139
|
+
return Number.isSafeInteger(recordPid) && recordPid > 0 && recordPid !== staleRecordPid ? recordPid : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The warning printed when an update had to leave a proxy the manager does not own. */
|
|
143
|
+
export function unsupervisedRuntimeWarning(port) {
|
|
144
|
+
return [
|
|
145
|
+
`⚠️ The proxy on port ${port} is running UNSUPERVISED: the background service manager did not take it back after the update.`,
|
|
146
|
+
" A crash will not be restarted, and a later service start can race it for the port.",
|
|
147
|
+
" Run 'ocx service repair' to hand it back to the background service.",
|
|
148
|
+
].join("\n");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The warning printed when the manager may still be restarting; no second proxy is started. */
|
|
152
|
+
export function unsettledManagerWarning(port) {
|
|
153
|
+
return [
|
|
154
|
+
`⚠️ The service manager is still restarting the proxy on port ${port}; no second proxy was started.`,
|
|
155
|
+
" Check it with 'ocx service status', and run 'ocx service repair' if it does not come back.",
|
|
156
|
+
].join("\n");
|
|
157
|
+
}
|
|
@@ -294,11 +294,13 @@ export function appendFinalCacheDiagnostic(facts: CacheDiagnosticFinalFacts): vo
|
|
|
294
294
|
|
|
295
295
|
const CACHE_DIAGNOSTIC_HOOK = Symbol.for("opencodex.cache-diagnostic.v1");
|
|
296
296
|
interface CacheDiagnosticHooks {
|
|
297
|
+
enabled(): boolean;
|
|
297
298
|
observeInbound(body: unknown, headers: Headers, source: PromptCacheKeySource): CacheDiagnosticDraft;
|
|
298
299
|
rebind(body: unknown, draft: CacheDiagnosticDraft | undefined): void;
|
|
299
300
|
finalize(facts: CacheDiagnosticFinalFacts): void;
|
|
300
301
|
}
|
|
301
302
|
(globalThis as Record<symbol, CacheDiagnosticHooks | undefined>)[CACHE_DIAGNOSTIC_HOOK] = {
|
|
303
|
+
enabled: isCacheDiagnosticEnabled,
|
|
302
304
|
observeInbound,
|
|
303
305
|
rebind: rebindCacheDiagnosticBodyAlias,
|
|
304
306
|
finalize: appendFinalCacheDiagnostic,
|
|
@@ -88,6 +88,10 @@ const GEMINI_37_FLASH: Cost4 = { input: 0.75, output: 3.75, cacheRead: 0.075, ca
|
|
|
88
88
|
// rising to $1.50 / $7.50 on 2027-01-01. A SEPARATE constant on purpose: equal today, but
|
|
89
89
|
// aliasing them would silently drag 3.8 along if 3.7's row is ever re-verified differently.
|
|
90
90
|
const GEMINI_38_FLASH: Cost4 = { input: 0.75, output: 3.75, cacheRead: 0.075, cacheWrite: 0 };
|
|
91
|
+
// Gemini 4 Argon introductory paid-tier rate: $2 in / $10 out (thinking included) / $0.10 cached
|
|
92
|
+
// per 1M through 2026-11-30, stepping up to $4 / $20 / $0.20 on 2026-12-01. Revisit this row
|
|
93
|
+
// then. No free tier. Implicit caching only, so there is no per-token cache-write price.
|
|
94
|
+
const GEMINI_4_ARGON: Cost4 = { input: 2, output: 10, cacheRead: 0.1, cacheWrite: 0 };
|
|
91
95
|
const MINIMAX_M21_HIGHSPEED: Cost4 = { input: 0.6, output: 2.4, cacheRead: 0.03, cacheWrite: 0.375 };
|
|
92
96
|
const KIMI_K3: Cost4 = { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3 };
|
|
93
97
|
const KIMI_K27_CODE: Cost4 = { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0.95 };
|
|
@@ -200,6 +204,7 @@ export function cursorFastPriceSource(provider: string, modelId: string): string
|
|
|
200
204
|
|
|
201
205
|
const GEMINI_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-07-22); cacheWrite=0: storage is billed per-hour, not per-token";
|
|
202
206
|
const GEMINI_37_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-08-14); promotional rate through 2026-12-31, rises to 1.50/7.50 on 2027-01-01; cacheWrite=0: storage is billed per-hour, not per-token";
|
|
207
|
+
const GEMINI_4_ARGON_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-10-14); introductory rate through 2026-11-30, rises to 4/20 (0.20 cached) on 2026-12-01; cacheWrite=0: implicit caching only";
|
|
203
208
|
const GEMINI_38_PRICING = "https://ai.google.dev/gemini-api/docs/pricing (2026-09-03); promotional rate through 2026-12-31, rises to 1.50/7.50 on 2027-01-01; cacheWrite=0: storage is billed per-hour, not per-token";
|
|
204
209
|
const MINIMAX_PRICING = "https://platform.minimax.io/docs/guides/pricing-paygo";
|
|
205
210
|
const OPENAI_GPT56_PRICING = "https://developers.openai.com/api/docs/pricing";
|
|
@@ -211,6 +216,8 @@ const META_MODEL_PRICING = "https://dev.meta.ai/docs/pricing-rate-limits";
|
|
|
211
216
|
*/
|
|
212
217
|
const META_MUSE_SPARK_13: Cost4 = { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 };
|
|
213
218
|
const META_MUSE_SPARK_13_CONTRIBUTOR: Cost4 = { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 };
|
|
219
|
+
// Muse Wonder (muse-wonder-1.0): Standard mode only, no long-context premium (verified 2026-10-13).
|
|
220
|
+
const META_MUSE_WONDER_10: Cost4 = { input: 4, output: 20, cacheRead: 0.2, cacheWrite: 0 };
|
|
214
221
|
const META_SPARK_SOURCE = `Meta Model API published price ${META_MODEL_PRICING}`;
|
|
215
222
|
const META_SPARK_CONTRIBUTOR_SOURCE = `Meta Model API published Contributor-tier price ${META_MODEL_PRICING}; data-sharing discount tier`;
|
|
216
223
|
const DEEPSEEK_PRICING = "https://api-docs.deepseek.com/quick_start/pricing-details-usd; V4 Flash alias transition scheduled 2026-07-24 — re-verify after";
|
|
@@ -344,6 +351,12 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
|
|
|
344
351
|
// 3.7 Flash rides CCA, whose billing equivalence to the Developer API list price is
|
|
345
352
|
// not published, so this is `verified-derived` rather than `verified`: the number is
|
|
346
353
|
// proven, the claim that Antigravity charges it is inferred.
|
|
354
|
+
// Gemini 4 Argon on Antigravity (AI Ultra / Enterprise only): the picker id plus its three
|
|
355
|
+
// verified CCA wire ids. Billing equivalence to the API price is inferred.
|
|
356
|
+
{ provider: "google-antigravity", modelId: "gemini-4-argon", cost4: GEMINI_4_ARGON, source: `derived: Gemini 4 Argon introductory rate through 2026-11-30 ${GEMINI_4_ARGON_PRICING}`, verifiedAt: "2026-10-14", status: "verified-derived" },
|
|
357
|
+
{ provider: "google-antigravity", modelId: "gemini-4-argon-low", cost4: GEMINI_4_ARGON, source: `derived: gemini-4-argon ${GEMINI_4_ARGON_PRICING}`, verifiedAt: "2026-10-14", status: "verified-derived" },
|
|
358
|
+
{ provider: "google-antigravity", modelId: "gemini-4-argon-medium", cost4: GEMINI_4_ARGON, source: `derived: gemini-4-argon ${GEMINI_4_ARGON_PRICING}`, verifiedAt: "2026-10-14", status: "verified-derived" },
|
|
359
|
+
{ provider: "google-antigravity", modelId: "gemini-4-argon-high", cost4: GEMINI_4_ARGON, source: `derived: gemini-4-argon ${GEMINI_4_ARGON_PRICING}`, verifiedAt: "2026-10-14", status: "verified-derived" },
|
|
347
360
|
{ provider: "google-antigravity", modelId: "gemini-3.8-flash", cost4: GEMINI_38_FLASH, source: `derived: Gemini 3.8 Flash promotional rate through 2026-12-31 ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
|
|
348
361
|
{ provider: "google-antigravity", modelId: "gemini-3.8-flash-low", cost4: GEMINI_38_FLASH, source: `derived: gemini-3.8-flash ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
|
|
349
362
|
{ provider: "google-antigravity", modelId: "gemini-3.8-flash-medium", cost4: GEMINI_38_FLASH, source: `derived: gemini-3.8-flash ${GEMINI_38_PRICING}`, verifiedAt: "2026-09-03", status: "verified-derived" },
|
|
@@ -369,11 +382,13 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
|
|
|
369
382
|
// cacheWrite=0: Meta publishes a cached-input price but no cache-write charge.
|
|
370
383
|
{ provider: "meta-model", modelId: "muse-spark-1.3", cost4: META_MUSE_SPARK_13, source: META_SPARK_SOURCE, verifiedAt: "2026-09-03", status: "verified" },
|
|
371
384
|
{ provider: "meta-model", modelId: "muse-spark-1.3-contributor", cost4: META_MUSE_SPARK_13_CONTRIBUTOR, source: META_SPARK_CONTRIBUTOR_SOURCE, verifiedAt: "2026-09-03", status: "verified" },
|
|
385
|
+
{ provider: "meta-model", modelId: "muse-wonder-1.0", cost4: META_MUSE_WONDER_10, source: META_SPARK_SOURCE, verifiedAt: "2026-10-13", status: "verified" },
|
|
372
386
|
// Same endpoint, same list price, different credential. Meta does not authorize this
|
|
373
387
|
// reuse and settlement is not observable, so these are the public Model API rates as a
|
|
374
388
|
// conservative estimate — not evidence of how the call is actually billed.
|
|
375
389
|
{ provider: "meta-muse", modelId: "muse-spark-1.3", cost4: META_MUSE_SPARK_13, source: META_SPARK_SOURCE, verifiedAt: "2026-09-03", status: "verified-derived" },
|
|
376
390
|
{ provider: "meta-muse", modelId: "muse-spark-1.3-contributor", cost4: META_MUSE_SPARK_13_CONTRIBUTOR, source: META_SPARK_CONTRIBUTOR_SOURCE, verifiedAt: "2026-09-03", status: "verified-derived" },
|
|
391
|
+
{ provider: "meta-muse", modelId: "muse-wonder-1.0", cost4: META_MUSE_WONDER_10, source: META_SPARK_SOURCE, verifiedAt: "2026-10-13", status: "verified-derived" },
|
|
377
392
|
// Daybreak aliases: priced as their current snapshots (red -> gpt-5.6-cyber,
|
|
378
393
|
// blue -> gpt-5.6-sol). The alias ids carry no rows of their own upstream, hence
|
|
379
394
|
// verified-derived. Blue deliberately reuses GPT56_SOL rather than duplicating the tuple.
|
|
@@ -399,6 +414,7 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
|
|
|
399
414
|
// Developer API row: the price IS published for this surface, so `verified`.
|
|
400
415
|
{ provider: "google", modelId: "gemini-3.7-flash", cost4: GEMINI_37_FLASH, source: GEMINI_37_PRICING, verifiedAt: "2026-08-14", status: "verified" },
|
|
401
416
|
{ provider: "google", modelId: "gemini-3.8-flash", cost4: GEMINI_38_FLASH, source: GEMINI_38_PRICING, verifiedAt: "2026-09-03", status: "verified" },
|
|
417
|
+
{ provider: "google", modelId: "gemini-4-argon", cost4: GEMINI_4_ARGON, source: GEMINI_4_ARGON_PRICING, verifiedAt: "2026-10-14", status: "verified" },
|
|
402
418
|
{ provider: "google-antigravity", modelId: "gemini-3.1-pro-preview", cost4: GEMINI_31_PRO, source: GEMINI_PRICING, verifiedAt: "2026-07-20", status: "verified" },
|
|
403
419
|
// Antigravity-bundled third-party models — derived from the underlying vendor's
|
|
404
420
|
// official API price (Antigravity itself bills via subscription quota).
|
package/src/usage/log.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { normalizeRouteDecisionTrace, type RouteDecisionTraceV1 } from "../routi
|
|
|
23
23
|
import { parseProtocolTraceV1, type ProtocolTraceV1 } from "../protocols/dto";
|
|
24
24
|
import { ACCOUNT_LOG_LABEL_RE, CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
25
25
|
import { claudeCompatibilityReason, normalizeClaudeFeatureCodes, type ClaudeFeatureCode } from "../claude/compatibility";
|
|
26
|
-
import type { CodexWsStageRecord } from "../server/responses/codex-ws-wire";
|
|
26
|
+
import type { CodexWsStageRecord, CodexWsTerminalEvent } from "../server/responses/codex-ws-wire";
|
|
27
27
|
import {
|
|
28
28
|
ATTEMPT_RECOVERY_KIND_ROSTER,
|
|
29
29
|
ATTEMPT_RECOVERY_WITHHELD_ROSTER,
|
|
@@ -805,6 +805,15 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null {
|
|
|
805
805
|
* injected free-form field — drops the whole record rather than passing
|
|
806
806
|
* attacker text into the DTO.
|
|
807
807
|
*/
|
|
808
|
+
// Total over the terminal union by `satisfies`; a type-only import keeps the WS transport out of
|
|
809
|
+
// this module's runtime graph.
|
|
810
|
+
const CODEX_WS_TERMINAL_EVENT_NAMES = {
|
|
811
|
+
"response.completed": true,
|
|
812
|
+
"response.failed": true,
|
|
813
|
+
"response.incomplete": true,
|
|
814
|
+
error: true,
|
|
815
|
+
} as const satisfies Record<CodexWsTerminalEvent, true>;
|
|
816
|
+
|
|
808
817
|
function normalizeCodexWsStageRecord(value: unknown): CodexWsStageRecord | undefined {
|
|
809
818
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
810
819
|
const stage = value as Record<string, unknown>;
|
|
@@ -839,6 +848,15 @@ function normalizeCodexWsStageRecord(value: unknown): CodexWsStageRecord | undef
|
|
|
839
848
|
reused: stage.reused,
|
|
840
849
|
ocxVersion: stage.ocxVersion,
|
|
841
850
|
bunVersion: stage.bunVersion,
|
|
851
|
+
// #6740 fields are newer than the record: absent or malformed values read as unobserved
|
|
852
|
+
// rather than costing the row the rest of its stage evidence.
|
|
853
|
+
terminalEvent: typeof stage.terminalEvent === "string" && Object.hasOwn(CODEX_WS_TERMINAL_EVENT_NAMES, stage.terminalEvent)
|
|
854
|
+
? stage.terminalEvent as CodexWsTerminalEvent
|
|
855
|
+
: null,
|
|
856
|
+
terminalErrorStatus: typeof stage.terminalErrorStatus === "number" && Number.isInteger(stage.terminalErrorStatus)
|
|
857
|
+
&& stage.terminalErrorStatus >= 100 && stage.terminalErrorStatus <= 599
|
|
858
|
+
? stage.terminalErrorStatus
|
|
859
|
+
: null,
|
|
842
860
|
};
|
|
843
861
|
}
|
|
844
862
|
|
|
@@ -15,7 +15,7 @@ import { cancelBodyOnAbort, signalWithTimeout } from "../lib/abort";
|
|
|
15
15
|
import { readBoundedResponseBytes } from "../lib/bounded-body";
|
|
16
16
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
17
17
|
import { redactSecretString } from "../lib/redact";
|
|
18
|
-
import {
|
|
18
|
+
import { antigravityUserAgentForWireModel } from "../adapters/google-antigravity-wire";
|
|
19
19
|
import { resolveAntigravityEffortWireModel } from "../providers/antigravity-models";
|
|
20
20
|
import { getProviderRegistryEntry } from "../providers/registry";
|
|
21
21
|
import { MAX_SIDECAR_RESPONSE_BYTES, type WebSearchSource } from "./parse";
|
|
@@ -74,7 +74,7 @@ export async function runGeminiWebSearch(
|
|
|
74
74
|
headers: {
|
|
75
75
|
"Content-Type": "application/json",
|
|
76
76
|
"Authorization": `Bearer ${token}`,
|
|
77
|
-
"User-Agent":
|
|
77
|
+
"User-Agent": antigravityUserAgentForWireModel(wireModelId),
|
|
78
78
|
},
|
|
79
79
|
body: JSON.stringify(envelope),
|
|
80
80
|
signal: linkedSignal.signal,
|
|
Binary file
|