@bitkyc08/opencodex 2.84.0 → 2.87.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-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -2
- package/src/adapters/anthropic/beta-allowlist.ts +91 -11
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +16 -3
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic.ts +108 -21
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +40 -11
- 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/dsh-account.ts +97 -0
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +37 -10
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +44 -3
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +223 -0
- package/src/advisor/context.ts +238 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +353 -0
- package/src/advisor/settings.ts +144 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -1
- package/src/bridge/internal.ts +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/desktop-3p-library.ts +72 -7
- package/src/claude/desktop-3p.ts +75 -19
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +11 -0
- package/src/claude/inbound.ts +154 -50
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +51 -19
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +165 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +23 -2
- package/src/cli/capabilities-base.ts +39 -11
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +46 -6
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +17 -14
- package/src/cli/dispatch.ts +30 -5
- package/src/cli/doctor.ts +15 -1
- package/src/cli/ensure-desktop-cold-start.ts +382 -0
- package/src/cli/export-command.ts +1 -1
- package/src/cli/help.ts +4 -2
- package/src/cli/index.ts +14 -5
- package/src/cli/integration-plan-dto.ts +3 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +41 -9
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +3 -1
- 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 +35 -48
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +81 -7
- package/src/codex/autostart-health.ts +4 -0
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +19 -2
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +4 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +42 -19
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +35 -86
- package/src/codex/catalog/provider-models.ts +40 -34
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +33 -16
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +63 -4
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/picker-freshness.ts +96 -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 +25 -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 +73 -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 +182 -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 +165 -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/features.ts +85 -45
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account-hard-lock.ts +30 -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 +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +2 -2
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +7 -2
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +35 -7
- package/src/config/live-reconcile.ts +51 -2
- package/src/config/load-degrade.ts +23 -1
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +52 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/paths.ts +10 -3
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +39 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +39 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +962 -350
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- 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 +2 -0
- package/src/integrations/registry.ts +15 -0
- package/src/integrations/store.ts +3 -0
- package/src/integrations/writer.ts +10 -1
- package/src/lib/advisor-activation.ts +16 -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/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +72 -0
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +26 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/redact.ts +2 -2
- 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/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +62 -12
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +188 -111
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +7 -1
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- 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/encoders/adapter-events.ts +5 -4
- package/src/protocols/encoders/messages.ts +9 -6
- 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/provider-summary.ts +2 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +5 -0
- package/src/providers/model-rename-migration.ts +7 -5
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +44 -14
- package/src/providers/registry/entries-extended.ts +19 -0
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +78 -22
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +24 -2
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/responses/state.ts +32 -29
- package/src/router.ts +8 -321
- package/src/routing/health.ts +37 -22
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +254 -221
- package/src/routing/history/ingest.ts +242 -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/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +21 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +15 -0
- package/src/server/chat-completions.ts +82 -10
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +164 -433
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +11 -3
- package/src/server/index/startup-warnings.ts +11 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +14 -11
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +202 -0
- package/src/server/management/agent-settings-routes.ts +22 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +77 -6
- package/src/server/management/context.ts +4 -0
- package/src/server/management/cursor-integration-routes.ts +1 -1
- 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/model-routes.ts +13 -8
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +21 -1
- 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-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +106 -22
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +13 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +48 -19
- package/src/server/responses/adapter-delivery.ts +43 -8
- package/src/server/responses/adapter-dispatch.ts +48 -4
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +368 -0
- package/src/server/responses/code-mode-collab-surface.ts +116 -8
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +12 -1
- package/src/server/responses/core-combo.ts +141 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +7 -2
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +51 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +25 -6
- package/src/server/responses/core.ts +3 -116
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +19 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -16
- package/src/server/responses/request-prepare.ts +144 -17
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +120 -27
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +166 -38
- package/src/server/responses/sidecar-execution.ts +98 -5
- package/src/server/responses/terminal-guard.ts +4 -17
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +16 -2
- package/src/server/startup-health-cache.ts +1 -0
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/desktop-startup.ts +14 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +99 -61
- package/src/service/health-sentinel-probe.ts +24 -7
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +135 -0
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +124 -6
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +29 -0
- package/src/types/provider.ts +39 -6
- package/src/types/request.ts +38 -2
- package/src/types/tools.ts +2 -0
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -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/npm-invocation.mjs +12 -2
- 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/cost.ts +48 -12
- package/src/usage/expected-prices.ts +15 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +22 -9
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/gemini-executor.ts +2 -2
- package/src/web-search/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- package/src/web-search/run-turn-loop.ts +9 -1
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/App-CZtqiGcw.js +0 -56
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
- package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
package/src/service/launchd.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { BUN_RUNTIME_PATH_ENV, BUN_RUNTIME_SOURCE_ENV, durableBunRuntime, type D
|
|
|
11
11
|
import { serviceApiTokenFilePath } from "../lib/service-secrets";
|
|
12
12
|
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
13
13
|
import { writeServiceApiTokenFile, assertLiveServiceManagerAllowed } from "./guards";
|
|
14
|
-
import { resolveServiceListenPort, buildServiceShellCommand, buildServiceLauncherShellCommand,
|
|
14
|
+
import { resolveServiceListenPort, buildServiceShellCommand, buildServiceLauncherShellCommand, resolvedProxyEnv } from "./definition";
|
|
15
15
|
import { SERVICE_MANAGED_ENV, LABEL, cliEntry, filterTransientServicePath, logPath, serviceStatePath, currentCodexSqliteHomeAbsolute, type ServiceInstallState, writeServiceInstallState, readServiceInstallState } from "./state";
|
|
16
16
|
import { writeServiceDefinitionFile } from "./windows-ops";
|
|
17
17
|
import { readTextOrNull } from "./windows-taskxml";
|
|
@@ -359,8 +359,11 @@ export function probeLaunchdLoadState(deps: {
|
|
|
359
359
|
for (const domain of [`gui/${uid}`, `user/${uid}`]) {
|
|
360
360
|
const printed = run(["print", `${domain}/${LABEL}`]);
|
|
361
361
|
if (printed.status === 0) {
|
|
362
|
+
// Use this backend's plist, then the configured service port. Foreign artifacts
|
|
363
|
+
// can exist on macOS: systemdListenPort has no platform gate, so the general
|
|
364
|
+
// installed-artifact chain could incorrectly select a copied Linux unit's port.
|
|
362
365
|
const expected = (deps.expectedCommand
|
|
363
|
-
?? (() => expectedLaunchdCommand(
|
|
366
|
+
?? (() => expectedLaunchdCommand(launchdListenPort() ?? resolveServiceListenPort())))();
|
|
364
367
|
const printedText = `${printed.stdout}\n${printed.stderr}`;
|
|
365
368
|
return {
|
|
366
369
|
state: printedText.includes(expected) ? "loaded-current" : "loaded-stale",
|
|
@@ -382,8 +385,16 @@ export function probeLaunchdLoadState(deps: {
|
|
|
382
385
|
return { state: "not-loaded" };
|
|
383
386
|
}
|
|
384
387
|
|
|
385
|
-
/**
|
|
386
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Up to 125 × 200 ms = 25 s: long enough for an evicted job to finish EXITING. Measured on
|
|
390
|
+
* macOS 26: `bootout` returns at once, the label stays registered (`state = SIGTERMed`) until
|
|
391
|
+
* the process exits, and `bootstrap` fails with 5 for that whole window. The proxy drains on
|
|
392
|
+
* SIGTERM (`shutdownTimeoutMs`, 5 s default) behind a launcher that waits for it, so a 1 s
|
|
393
|
+
* settle bootstrapped into that window, the retry and the rollback raced it too, and the old
|
|
394
|
+
* job then exited leaving nothing loaded. launchd SIGKILLs at its default `ExitTimeOut` of
|
|
395
|
+
* 20 s, which bounds this wait.
|
|
396
|
+
*/
|
|
397
|
+
const LAUNCHD_SETTLE_ATTEMPTS = 125;
|
|
387
398
|
|
|
388
399
|
const LAUNCHD_SETTLE_DELAY_MS = 200;
|
|
389
400
|
|
|
@@ -769,6 +780,7 @@ export function restartLaunchdJob(deps: {
|
|
|
769
780
|
export function startLaunchd(deps: {
|
|
770
781
|
launchctl?: typeof runLaunchctl;
|
|
771
782
|
matches?: typeof launchdJobMatchesPlist;
|
|
783
|
+
readPlist?: () => string;
|
|
772
784
|
} = {}): void {
|
|
773
785
|
const run = deps.launchctl ?? runLaunchctl;
|
|
774
786
|
const p = plistPath();
|
|
@@ -778,16 +790,24 @@ export function startLaunchd(deps: {
|
|
|
778
790
|
// already be bootstrapped from THIS plist, which is a no-op rather than an error.
|
|
779
791
|
// `install` can assume a stale job (it just rewrote the plist); `start` cannot, and
|
|
780
792
|
// throwing here would break `ocx service start` on every healthy service.
|
|
793
|
+
// A live job can outlast its deleted or unreadable plist. Only a readable
|
|
794
|
+
// definition can justify the no-op; use the same snapshot for port parsing.
|
|
795
|
+
let plist: string | null;
|
|
796
|
+
try { plist = (deps.readPlist ?? (() => readFileSync(p, "utf8")))(); }
|
|
797
|
+
catch { plist = null; }
|
|
798
|
+
// Match the same launchd-only port choice as probeLaunchdLoadState above.
|
|
781
799
|
const live = (deps.matches ?? launchdJobMatchesPlist)(
|
|
782
|
-
expectedLaunchdCommand(
|
|
800
|
+
expectedLaunchdCommand(launchdListenPort({ readPlist: () => plist ?? "" }) ?? resolveServiceListenPort()),
|
|
783
801
|
);
|
|
784
|
-
if (live.loaded && live.matchesPlist) {
|
|
802
|
+
if (plist !== null && live.loaded && live.matchesPlist) {
|
|
785
803
|
console.log("ℹ️ service was already loaded from the current plist; nothing to do.");
|
|
786
804
|
return;
|
|
787
805
|
}
|
|
788
806
|
throw new Error(
|
|
789
807
|
`launchctl could not load ${p}: ${loaded.stderr || "load reported failure"}\n`
|
|
790
|
-
+ (
|
|
808
|
+
+ (plist === null
|
|
809
|
+
? "The service plist is missing or unreadable. Run 'ocx service repair' to restore it."
|
|
810
|
+
: live.loaded
|
|
791
811
|
? `launchd is running an OLDER plist. Fix:\n launchctl bootout ${launchdGuiDomain()}/${LABEL}\n ocx service repair`
|
|
792
812
|
: "The job is not loaded. Run 'ocx service repair' to reload it."),
|
|
793
813
|
);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { closeSync, constants, fstatSync, fsyncSync, ftruncateSync, lstatSync, openSync, readSync, realpathSync, renameSync, statSync, writeSync, type BigIntStats } from "node:fs";
|
|
2
|
+
import { formatWithOptions } from "node:util";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
5
|
+
|
|
6
|
+
export const SERVICE_LOG_MAX_BYTES = 20 * 1024 * 1024;
|
|
7
|
+
export const SERVICE_LOG_CHECK_INTERVAL_MS = 60_000;
|
|
8
|
+
|
|
9
|
+
function regularLeaf(path: string): BigIntStats | undefined {
|
|
10
|
+
try {
|
|
11
|
+
const info = lstatSync(path, { bigint: true });
|
|
12
|
+
if (!info.isFile() || info.nlink !== 1n || info.ino === 0n) throw new Error("Unsafe service log leaf");
|
|
13
|
+
return info;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sameFile(left: BigIntStats | undefined, right: BigIntStats | undefined): boolean {
|
|
21
|
+
return left !== undefined && right !== undefined && left.dev === right.dev && left.ino === right.ino;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function matchesDescriptor(path: string, fd: number, expected: BigIntStats): boolean {
|
|
25
|
+
const info = fstatSync(fd, { bigint: true });
|
|
26
|
+
return info.isFile() && info.nlink === 1n && sameFile(expected, info)
|
|
27
|
+
&& sameFile(info, regularLeaf(path));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Preserve the manager's open append descriptor; retain at most two bounded archives. */
|
|
31
|
+
export function rotateServiceLog(requestedPath: string, maxBytes = SERVICE_LOG_MAX_BYTES): boolean {
|
|
32
|
+
let source: number | undefined;
|
|
33
|
+
let archive: number | undefined;
|
|
34
|
+
try {
|
|
35
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) return false;
|
|
36
|
+
// Resolve only the parent: exclusive creation through a Windows junction
|
|
37
|
+
// can report EEXIST for a missing leaf. Never resolve away a leaf symlink.
|
|
38
|
+
const parent = dirname(requestedPath);
|
|
39
|
+
const directory = statSync(parent, { bigint: true });
|
|
40
|
+
if (!directory.isDirectory() || directory.ino === 0n) return false;
|
|
41
|
+
const canonicalParent = realpathSync(parent);
|
|
42
|
+
const path = join(canonicalParent, basename(requestedPath));
|
|
43
|
+
const sameDirectory = () => {
|
|
44
|
+
const alias = statSync(parent, { bigint: true });
|
|
45
|
+
const canonical = statSync(canonicalParent, { bigint: true });
|
|
46
|
+
return alias.isDirectory() && canonical.isDirectory()
|
|
47
|
+
&& sameFile(directory, alias) && sameFile(directory, canonical);
|
|
48
|
+
};
|
|
49
|
+
const matches = (suffix: string, fd: number, expected: BigIntStats) => sameDirectory()
|
|
50
|
+
&& matchesDescriptor(`${path}${suffix}`, fd, expected)
|
|
51
|
+
&& matchesDescriptor(`${requestedPath}${suffix}`, fd, expected);
|
|
52
|
+
if (!sameDirectory()) return false;
|
|
53
|
+
const original = regularLeaf(requestedPath);
|
|
54
|
+
if (!original) return false;
|
|
55
|
+
// Windows does not expose O_NOFOLLOW: descriptor/leaf identity checks still
|
|
56
|
+
// apply there. Do not reject legitimate junctions in the home path.
|
|
57
|
+
const guardedFlags = process.platform === "win32" ? 0 : constants.O_NOFOLLOW | constants.O_NONBLOCK;
|
|
58
|
+
source = openSync(path, constants.O_RDWR | guardedFlags);
|
|
59
|
+
if (!matches("", source, original)) return false;
|
|
60
|
+
const size = Number(fstatSync(source, { bigint: true }).size);
|
|
61
|
+
if (!Number.isSafeInteger(size) || size <= maxBytes) return false;
|
|
62
|
+
const previous = regularLeaf(`${path}.1`);
|
|
63
|
+
const oldest = regularLeaf(`${path}.2`);
|
|
64
|
+
if (!matches("", source, original)) return false;
|
|
65
|
+
if (previous) {
|
|
66
|
+
if (!sameFile(previous, regularLeaf(`${path}.1`))) return false;
|
|
67
|
+
const currentOldest = regularLeaf(`${path}.2`);
|
|
68
|
+
if (oldest ? !sameFile(oldest, currentOldest) : currentOldest !== undefined) return false;
|
|
69
|
+
if (!sameDirectory()) return false;
|
|
70
|
+
renameSync(`${path}.1`, `${path}.2`);
|
|
71
|
+
if (!sameFile(previous, regularLeaf(`${path}.2`))) return false;
|
|
72
|
+
}
|
|
73
|
+
// Never truncate an existing archive, even if another rotation or a path
|
|
74
|
+
// replacement wins the gap after rename. Failure leaves the source intact.
|
|
75
|
+
if (!sameDirectory()) return false;
|
|
76
|
+
archive = openSync(`${path}.1`, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | guardedFlags, 0o600);
|
|
77
|
+
const created = fstatSync(archive, { bigint: true });
|
|
78
|
+
if (!matches(".1", archive, created)) return false;
|
|
79
|
+
// A legacy unbounded file may be huge. Copy only its newest cap-sized tail,
|
|
80
|
+
// using fixed memory, before truncating the original inode.
|
|
81
|
+
const buffer = Buffer.alloc(Math.min(64 * 1024, maxBytes));
|
|
82
|
+
let position = size - maxBytes;
|
|
83
|
+
while (position < size) {
|
|
84
|
+
const read = readSync(source, buffer, 0, Math.min(buffer.length, size - position), position);
|
|
85
|
+
if (read === 0) return false;
|
|
86
|
+
let written = 0;
|
|
87
|
+
while (written < read) {
|
|
88
|
+
if (!matches(".1", archive, created)) return false;
|
|
89
|
+
const count = writeSync(archive, buffer, written, read - written);
|
|
90
|
+
if (count === 0) return false;
|
|
91
|
+
written += count;
|
|
92
|
+
}
|
|
93
|
+
position += read;
|
|
94
|
+
}
|
|
95
|
+
fsyncSync(archive);
|
|
96
|
+
// Revalidate both names and open objects at the destructive boundary. These
|
|
97
|
+
// checks detect observed replacement; they are not an atomic namespace lock.
|
|
98
|
+
if (!matches(".1", archive, created)
|
|
99
|
+
|| !matches("", source, original)) return false;
|
|
100
|
+
ftruncateSync(source, 0);
|
|
101
|
+
return true;
|
|
102
|
+
} catch {
|
|
103
|
+
return false; // Missing files, permissions and disk failures must never stop the service.
|
|
104
|
+
} finally {
|
|
105
|
+
for (const fd of [archive, source]) {
|
|
106
|
+
if (fd !== undefined) try { closeSync(fd); } catch { /* best-effort */ }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** One sink wrapper covers startup, provider diagnostics and multiline errors. */
|
|
112
|
+
export function startServiceLogging(
|
|
113
|
+
path: string,
|
|
114
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
115
|
+
intervalMs = SERVICE_LOG_CHECK_INTERVAL_MS,
|
|
116
|
+
): () => void {
|
|
117
|
+
if (!env.OCX_SERVICE) return () => {};
|
|
118
|
+
const restore: (() => void)[] = [];
|
|
119
|
+
for (const method of ["log", "info", "debug", "warn", "error"] as const) {
|
|
120
|
+
const original = console[method];
|
|
121
|
+
console[method] = (...args: unknown[]) => {
|
|
122
|
+
const at = new Date().toISOString();
|
|
123
|
+
const line = formatWithOptions({ colors: false }, ...args);
|
|
124
|
+
original.call(console, line.split("\n").map(part => `${at} ${part}`).join("\n"));
|
|
125
|
+
};
|
|
126
|
+
restore.push(() => { console[method] = original; });
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
for (const suffix of [".1", ".2"]) recordOwnedConfigPath(dirname(path), `${path}${suffix}`);
|
|
130
|
+
} catch { /* ownership bookkeeping must not break logging */ }
|
|
131
|
+
rotateServiceLog(path);
|
|
132
|
+
const timer = setInterval(() => { rotateServiceLog(path); }, intervalMs);
|
|
133
|
+
timer.unref();
|
|
134
|
+
return () => { clearInterval(timer); for (const reset of restore) reset(); };
|
|
135
|
+
}
|
package/src/service/repair.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { randomUUID } from "node:crypto";
|
|
|
6
6
|
import { defaultWinswEntry, installWinswService } from "../lib/winsw";
|
|
7
7
|
import { resolveWindowsTaskDiagnosticUserId, diagnoseService } from "./diagnostics";
|
|
8
8
|
import type { ServiceDiagnostic } from "./diagnostics";
|
|
9
|
-
import {
|
|
9
|
+
import { assertServiceAuthEnvironment } from "./auth-preflight";
|
|
10
|
+
import { assertServiceEnvironmentMatchesInstall } from "./guards";
|
|
10
11
|
import { installLaunchd, restartLaunchdJob } from "./launchd";
|
|
11
12
|
import type { LaunchdInstallOutcome } from "./launchd";
|
|
12
13
|
import { TASK, writeServiceInstallState, serviceSourceDir, resolveServiceOwnership, serviceLauncherPathDiagnostic } from "./state";
|
package/src/service/state.ts
CHANGED
|
@@ -172,20 +172,19 @@ function defaultOpenCodexHome(): string {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
export function serviceStatePathsForOpenCodexHome(opencodexHome: string): string[] {
|
|
175
|
-
|
|
175
|
+
// Bun caches the startup home: parallel workers inherit the wrapper's HOME even
|
|
176
|
+
// after the preload gives each one a sandbox. Tests must not share that mirror,
|
|
177
|
+
// including serial cases that select different OPENCODEX_HOME fixtures.
|
|
178
|
+
return serviceStateFilesFor(opencodexHome, isTestHomeGuardArmed() ? opencodexHome : defaultOpenCodexHome());
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
export function serviceStatePaths(): string[] {
|
|
179
182
|
const paths = serviceStatePathsForOpenCodexHome(currentOpenCodexHome());
|
|
180
183
|
if (!isTestHomeGuardArmed()) return paths;
|
|
181
184
|
/*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* points at a sandbox still writes their live install state — observed while building
|
|
186
|
-
* the launchd repair coverage: one case replaced the real record's codexHome and
|
|
187
|
-
* opencodexHome with temp-directory paths. Drop it rather than deny the write, so the
|
|
188
|
-
* sandbox path keeps working and the real one is simply not in the list.
|
|
185
|
+
* The test resolver omits the legacy mirror; still reject an explicit or fallback
|
|
186
|
+
* OPENCODEX_HOME that names the developer's real home. A missing fixture pin must
|
|
187
|
+
* never allow a test to read or replace their live service installation.
|
|
189
188
|
*
|
|
190
189
|
* The predicate is the guard's own, not a local `resolve()` compare: the guard
|
|
191
190
|
* canonicalizes through `realpath`, and on macOS a sandbox under `/var/folders/...`
|
package/src/service/systemd.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { BUN_RUNTIME_PATH_ENV, BUN_RUNTIME_SOURCE_ENV, durableBunRuntime, type D
|
|
|
8
8
|
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
9
9
|
import { systemdProperty } from "../service-manager-probe";
|
|
10
10
|
import { writeServiceApiTokenFile, sh } from "./guards";
|
|
11
|
-
import { shellQuote, buildServiceShellCommand, buildServiceLauncherShellCommand, resolvedProxyEnv } from "./
|
|
11
|
+
import { shellQuote, buildServiceShellCommand, buildServiceLauncherShellCommand, resolvedProxyEnv } from "./definition";
|
|
12
12
|
import type { ServiceInstallCleanupOps } from "./orchestration";
|
|
13
13
|
import { SERVICE_MANAGED_ENV, TASK, cliEntry, filterTransientServicePath, stableLauncherEntry, logPath, serviceStatePath, currentCodexSqliteHomeAbsolute, writeServiceInstallState } from "./state";
|
|
14
14
|
import { writeServiceDefinitionFile } from "./windows-ops";
|
|
@@ -29,7 +29,8 @@ import { cachedCurrentWindowsIdentity, resolveCurrentWindowsPrincipal, WINDOWS_P
|
|
|
29
29
|
import { statusWinswRaw, winswExePath, WINSW_SERVICE_ID, type WinswStatus } from "../lib/winsw";
|
|
30
30
|
import type { GuardedManagerStopped, GuardedManagerTarget } from "./guarded-manager-target";
|
|
31
31
|
import { TASK, windowsLauncherVbsPath, windowsServiceScriptPath } from "./state";
|
|
32
|
-
import { probeWindowsSchedulerTask, querySchtasks,
|
|
32
|
+
import { probeWindowsSchedulerTask, querySchtasks, type WindowsSchedulerTaskProbe } from "./windows-scheduler";
|
|
33
|
+
import { windowsWscript } from "./windows-wscript";
|
|
33
34
|
import { windowsTaskRegistrationHealthy, windowsTaskRegistrationRefreshableLegacy } from "./windows-taskxml";
|
|
34
35
|
|
|
35
36
|
/** Runtime table of one Win32 process; a null parent means the chain is unreadable. */
|
|
@@ -5,7 +5,7 @@ import { winswXmlPath } from "../lib/winsw";
|
|
|
5
5
|
import { hardenSecretPath } from "../lib/windows-secret-acl";
|
|
6
6
|
import { durableBunRuntime, type DurableBunRuntime } from "../lib/bun-runtime";
|
|
7
7
|
import { assertSelectedRuntimeWritable, type RuntimePreflightDeps } from "../lib/bun-runtime-preflight";
|
|
8
|
-
import { parseBakedListenPort } from "./
|
|
8
|
+
import { parseBakedListenPort } from "./definition";
|
|
9
9
|
import { windowsServiceScriptPath } from "./state";
|
|
10
10
|
import { execFileSync } from "node:child_process";
|
|
11
11
|
import { existsSync, mkdirSync, mkdtempSync, rmdirSync, unlinkSync } from "node:fs";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { findLiveProxy } from "../server/proxy-liveness";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
5
4
|
import { randomUUID } from "node:crypto";
|
|
6
5
|
import { ELEVATION_REQUEST_TIMEOUT_MS, OCX_ELEVATED_PROTOCOL_FAILED, raceWithTimeout, resolveTrustedWindowsSchtasksExe, startElevatedSchtasksCreateAndRun, runWindowsElevated, toWindowsSchtasksError, WindowsElevationError, type ElevatedSchedulerOutcome, type ElevatedSchtasksCreateAndRunExecution, type ElevatedSchtasksCreateAndRunResult } from "../lib/windows-elevation";
|
|
7
6
|
import { statusWinswRaw } from "../lib/winsw";
|
|
@@ -10,9 +9,10 @@ import { isTestHomeGuardArmed } from "../lib/test-home-guard";
|
|
|
10
9
|
import { TASK, windowsServiceScriptPath, windowsLauncherVbsPath, windowsTaskXmlPath, writeServiceInstallState } from "./state";
|
|
11
10
|
import { buildWindowsSchtasksCreateArgs, windowsTaskRegistrationOwnedByAttempt, windowsTaskRegistrationHealthy } from "./windows-taskxml";
|
|
12
11
|
import type { ExpectedWindowsTaskUserId } from "./windows-taskxml";
|
|
13
|
-
import { win32 } from "node:path";
|
|
14
12
|
import { WINSW_SERVICE_ID } from "../lib/winsw";
|
|
15
13
|
|
|
14
|
+
export { windowsWscript } from "./windows-wscript";
|
|
15
|
+
|
|
16
16
|
/**
|
|
17
17
|
* Decode schtasks stdout. `/query /xml` emits UTF-16LE (often with BOM) because the
|
|
18
18
|
* registered task document is UTF-16; reading that as UTF-8 makes every health check
|
|
@@ -69,11 +69,6 @@ function windowsSchtasks(): string {
|
|
|
69
69
|
return resolveTrustedWindowsSchtasksExe();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export function windowsWscript(): string {
|
|
73
|
-
const candidate = join(process.env.SystemRoot ?? "C:\\Windows", "System32", "wscript.exe");
|
|
74
|
-
return existsSync(candidate) ? candidate : "wscript.exe";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
72
|
let querySchtasksForTests: ((args: string[]) => string) | null = null;
|
|
78
73
|
|
|
79
74
|
export function querySchtasks(args: string[]): string {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { WINDOWS_WRAPPER_PROTOCOL_ENV, WINDOWS_WRAPPER_STAY_OUT_EXIT_CODE } from "./windows-wrapper-exit";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { TASK, windowsServiceScriptPath, windowsLauncherVbsPath, windowsTaskXmlPath } from "./state";
|
|
4
|
-
import { windowsWscript } from "./windows-
|
|
4
|
+
import { windowsWscript } from "./windows-wscript";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { BUN_RUNTIME_PATH_ENV, BUN_RUNTIME_SOURCE_ENV } from "../lib/bun-runtime";
|
|
7
7
|
import { REAL_BUN_MIN_BYTES } from "../lib/bun-binary-validator.mjs";
|
|
8
8
|
import { serviceApiTokenFilePath } from "../lib/service-secrets";
|
|
9
9
|
import { windowsEnvIndirectBatchPathList, windowsEnvIndirectBatchValue } from "../lib/win-paths";
|
|
10
10
|
import { cachedCurrentWindowsIdentity, resolveCurrentWindowsPrincipal, WINDOWS_PRINCIPAL_LOOKUP_TIMEOUT_MS } from "../lib/windows-user-principal";
|
|
11
|
-
import { resolveServiceListenPort, resolvedProxyEnv } from "./
|
|
11
|
+
import { resolveServiceListenPort, resolvedProxyEnv } from "./definition";
|
|
12
12
|
import { cliEntry, filterTransientServicePath, serviceLogPath, currentCodexSqliteHomeAbsolute } from "./state";
|
|
13
13
|
|
|
14
14
|
function windowsBatchValue(value: string): string {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `wscript.exe` the Task Scheduler XML must name.
|
|
3
|
+
*
|
|
4
|
+
* Leaf module on purpose: `windows-taskxml.ts` needs it to render the task document,
|
|
5
|
+
* and taking it from `windows-scheduler.ts` made the two one cyclic component.
|
|
6
|
+
* `windows-scheduler` owns the schtasks process surface, not this one path lookup.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
export function windowsWscript(): string {
|
|
12
|
+
const candidate = join(process.env.SystemRoot ?? "C:\\Windows", "System32", "wscript.exe");
|
|
13
|
+
return existsSync(candidate) ? candidate : "wscript.exe";
|
|
14
|
+
}
|
package/src/service.ts
CHANGED
|
@@ -13,8 +13,10 @@ export { acquireOwnershipMutationLease, withOwnershipMutationLease } from "./ser
|
|
|
13
13
|
export type { ManagingCliRole, ManagingCliObservation, RegisteredManagingCliInvocation, ServiceTakeoverCompatibilityInput, ServiceTakeoverCompatibility } from "./service/ownership-compatibility";
|
|
14
14
|
export { registeredManagingCliInvocation, assessServiceTakeoverCompatibility, sameServiceTakeoverCompatibility } from "./service/ownership-compatibility";
|
|
15
15
|
export type { ServiceApiTokenOrigin, ProvisionedServiceApiToken } from "./service/guards";
|
|
16
|
-
export { ServiceOwnershipError, isServiceOwnershipError, serviceEnvironmentOwnedHere, assertServiceEnvironmentMatchesInstall,
|
|
17
|
-
export {
|
|
16
|
+
export { ServiceOwnershipError, isServiceOwnershipError, serviceEnvironmentOwnedHere, assertServiceEnvironmentMatchesInstall, assertNotAdminToken, writeServiceApiTokenFile, assertLiveServiceManagerAllowed } from "./service/guards";
|
|
17
|
+
export { serviceRetryCommand, assertServiceAuthEnvironment } from "./service/auth-preflight";
|
|
18
|
+
export { resolveServiceListenPort, resolvedProxyEnv } from "./service/definition";
|
|
19
|
+
export { installedServiceListenPort, SERVICE_INSTALL_HEALTH_MS, SERVICE_INSTALL_HEALTH_WINDOWS_MS, serviceInstallHealthMs, confirmServiceServing, reportServiceServing } from "./service/health";
|
|
18
20
|
export type { LaunchdLoadState, LaunchdLoadProbe, LaunchdInstallOutcome } from "./service/launchd";
|
|
19
21
|
export { buildPlist, reusePreviousPlistPathVariable, expectedLaunchdCommand, launchdListenPort, runLaunchctl, launchctlLoadFailed, launchdJobMatchesPlist, launchdEvictionTargets, probeLaunchdLoadState, installLaunchd, restartLaunchdJob, startLaunchd } from "./service/launchd";
|
|
20
22
|
export { systemdListenPort, buildUnit, systemdNeedsDaemonReload, uninstallSystemd, systemdServiceInstallCleanupOps } from "./service/systemd";
|
package/src/sidecar/auth.ts
CHANGED
|
@@ -134,6 +134,8 @@ function findAnthropicAuthProvider(
|
|
|
134
134
|
// its token path and the token guardian refuse an unmarked row of that name, so offering such a
|
|
135
135
|
// custom row here would advertise credentials no helper executor will spend.
|
|
136
136
|
if (providerName === "anthropic2") continue;
|
|
137
|
+
// The DSH account token is pinned to its own adapter and DeepSeek host; no helper may spend it.
|
|
138
|
+
if (providerName === "dsh-account") continue;
|
|
137
139
|
if (provider.disabled === true) continue;
|
|
138
140
|
if (provider.adapter !== "anthropic" || provider.authMode !== "oauth") continue;
|
|
139
141
|
const set = getAccountSet(providerName);
|
|
@@ -201,14 +201,10 @@ function Start-StartupHealthProbe {
|
|
|
201
201
|
if ($null -ne $script:startupProbeProcess) {
|
|
202
202
|
# Process.Start succeeded but the async pipe setup failed: Dispose alone
|
|
203
203
|
# would leave the Bun child running, so terminate it before cleanup.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
} catch {
|
|
208
|
-
Write-ActionLog "startup-health probe launch cleanup failed: $($_.Exception.GetType().Name)"
|
|
209
|
-
}
|
|
204
|
+
Stop-StartupHealthProbe
|
|
205
|
+
} else {
|
|
206
|
+
Complete-StartupHealthProbe
|
|
210
207
|
}
|
|
211
|
-
Complete-StartupHealthProbe
|
|
212
208
|
}
|
|
213
209
|
}
|
|
214
210
|
|
|
@@ -217,8 +213,11 @@ function Complete-StartupHealthProbe {
|
|
|
217
213
|
$script:startupProbeProcess = $null
|
|
218
214
|
$script:startupProbeOutputTask = $null
|
|
219
215
|
$script:startupProbeErrorTask = $null
|
|
216
|
+
$script:startupProbeTerminating = $false
|
|
220
217
|
if ($null -ne $process) {
|
|
221
218
|
try {
|
|
219
|
+
$process.StandardOutput.Dispose()
|
|
220
|
+
$process.StandardError.Dispose()
|
|
222
221
|
$process.Dispose()
|
|
223
222
|
} catch {
|
|
224
223
|
Write-ActionLog "startup-health probe dispose failed: $($_.Exception.GetType().Name)"
|
|
@@ -226,6 +225,31 @@ function Complete-StartupHealthProbe {
|
|
|
226
225
|
}
|
|
227
226
|
}
|
|
228
227
|
|
|
228
|
+
function Stop-StartupHealthProbe([switch]$Shutdown) {
|
|
229
|
+
if ($null -eq $script:startupProbeProcess) { return }
|
|
230
|
+
$script:startupProbeTerminating = $true
|
|
231
|
+
try {
|
|
232
|
+
if (-not $script:startupProbeProcess.HasExited) { $script:startupProbeProcess.Kill() }
|
|
233
|
+
} catch { Write-ActionLog "startup-health probe kill failed: $($_.Exception.GetType().Name)" }
|
|
234
|
+
if ($Shutdown) {
|
|
235
|
+
# Shutdown alone may wait. Timeout ticks keep the child tracked until actual exit.
|
|
236
|
+
try { [void]$script:startupProbeProcess.WaitForExit(500) } catch { $null = $_ }
|
|
237
|
+
$exited = $false
|
|
238
|
+
try { $exited = $script:startupProbeProcess.HasExited } catch { $null = $_ }
|
|
239
|
+
if ($exited) {
|
|
240
|
+
# Exit does not mean both pipe readers finished; give them the same bounded wait.
|
|
241
|
+
$readers = @($script:startupProbeOutputTask, $script:startupProbeErrorTask) | Where-Object { $null -ne $_ }
|
|
242
|
+
if ($readers.Count -gt 0) {
|
|
243
|
+
try { [void][System.Threading.Tasks.Task]::WaitAll([System.Threading.Tasks.Task[]]$readers, 500) } catch { $null = $_ }
|
|
244
|
+
}
|
|
245
|
+
Complete-StartupHealthProbe
|
|
246
|
+
} else {
|
|
247
|
+
# Releasing the only reference would orphan a live child; keep it tracked.
|
|
248
|
+
Write-ActionLog "startup-health probe still running at shutdown; leaving it tracked"
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
229
253
|
function Initialize-UpdateBadgeReader {
|
|
230
254
|
if (([System.Management.Automation.PSTypeName]"TrayUpdateBadgeReader").Type) { return }
|
|
231
255
|
Add-Type -TypeDefinition @'
|
|
@@ -433,6 +457,7 @@ $script:startupRefreshMs = 20000L
|
|
|
433
457
|
$script:startupProbeProcess = $null
|
|
434
458
|
$script:startupProbeOutputTask = $null
|
|
435
459
|
$script:startupProbeErrorTask = $null
|
|
460
|
+
$script:startupProbeTerminating = $false
|
|
436
461
|
$script:startupProbeStarted = 0L
|
|
437
462
|
$script:startupProbeTimeoutMs = 30000L
|
|
438
463
|
$script:updateAvailable = $false
|
|
@@ -520,10 +545,16 @@ function Update-TrayState {
|
|
|
520
545
|
try {
|
|
521
546
|
$probeExited = $script:startupProbeProcess.HasExited
|
|
522
547
|
} catch {
|
|
523
|
-
|
|
548
|
+
# Failed inspection is not proof of exit; keep ownership and request termination.
|
|
549
|
+
Stop-StartupHealthProbe
|
|
524
550
|
}
|
|
525
551
|
$probeTimedOut = ($now - $script:startupProbeStarted) -gt $script:startupProbeTimeoutMs
|
|
526
|
-
|
|
552
|
+
$probeDrained = ($null -eq $script:startupProbeOutputTask -or $script:startupProbeOutputTask.IsCompleted) -and
|
|
553
|
+
($null -eq $script:startupProbeErrorTask -or $script:startupProbeErrorTask.IsCompleted)
|
|
554
|
+
if ($script:startupProbeTerminating) {
|
|
555
|
+
if ($probeExited -and $probeDrained) { Complete-StartupHealthProbe }
|
|
556
|
+
elseif (-not $probeExited) { Stop-StartupHealthProbe }
|
|
557
|
+
} elseif ($probeExited -and $probeDrained -and $null -ne $script:startupProbeOutputTask) {
|
|
527
558
|
$probeStartup = $null
|
|
528
559
|
try {
|
|
529
560
|
$probeStartup = Parse-StartupHealthText $script:startupProbeOutputTask.Result
|
|
@@ -537,14 +568,8 @@ function Update-TrayState {
|
|
|
537
568
|
# A hung diagnostic must not survive its refresh slot: terminate it so the
|
|
538
569
|
# next refresh cannot stack another orphaned Bun process behind it.
|
|
539
570
|
Write-ActionLog "startup-health probe timed out; terminating it"
|
|
540
|
-
|
|
541
|
-
$script:startupProbeProcess.Kill()
|
|
542
|
-
[void]$script:startupProbeProcess.WaitForExit(3000)
|
|
543
|
-
} catch {
|
|
544
|
-
Write-ActionLog "startup-health probe kill failed: $($_.Exception.GetType().Name)"
|
|
545
|
-
}
|
|
571
|
+
Stop-StartupHealthProbe
|
|
546
572
|
$script:startupHealth = $null
|
|
547
|
-
Complete-StartupHealthProbe
|
|
548
573
|
$script:startupHealthCheckedAt = $now
|
|
549
574
|
}
|
|
550
575
|
}
|
|
@@ -710,14 +735,8 @@ try {
|
|
|
710
735
|
# A probe still running when the tray shuts down would outlive it: Dispose()
|
|
711
736
|
# releases the handle but does not terminate the child, so kill the active
|
|
712
737
|
# probe, wait briefly for exit, then release the probe state.
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
$script:startupProbeProcess.Kill()
|
|
716
|
-
[void]$script:startupProbeProcess.WaitForExit(3000)
|
|
717
|
-
} catch {
|
|
718
|
-
Write-ActionLog "startup-health probe shutdown kill failed: $($_.Exception.GetType().Name)"
|
|
719
|
-
}
|
|
720
|
-
Complete-StartupHealthProbe
|
|
738
|
+
Write-ActionLog "terminating active startup-health probe on tray shutdown"
|
|
739
|
+
Stop-StartupHealthProbe -Shutdown
|
|
721
740
|
}
|
|
722
741
|
Stop-UpdateBadgeProbe -Shutdown
|
|
723
742
|
$notify.Dispose()
|