@iislee/opencodex 2.11.0 → 2.35.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/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
package/src/lib/bounded-body.ts
CHANGED
|
@@ -7,10 +7,24 @@ export const BOUNDED_BODY_TIMEOUT_MS = 5_000;
|
|
|
7
7
|
export interface BoundedBodyOptions {
|
|
8
8
|
/** Abort the read with this signal. Its reason is rethrown by identity. */
|
|
9
9
|
signal?: AbortSignal;
|
|
10
|
+
/**
|
|
11
|
+
* Reject the returned promise with TypeError on malformed or truncated UTF-8
|
|
12
|
+
* instead of replacing invalid bytes, including during timeout-path flushes.
|
|
13
|
+
* Reader cancellation and lock release still run. Defaults to false.
|
|
14
|
+
*/
|
|
15
|
+
fatalUtf8?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Byte ceiling for retained body data. Defaults to BOUNDED_BODY_MAX_BYTES (64 KiB),
|
|
18
|
+
* which suits error bodies; callers materializing whole success payloads (e.g. a
|
|
19
|
+
* non-streaming upstream JSON completion) pass a larger explicit budget.
|
|
20
|
+
*/
|
|
21
|
+
maxBytes?: number;
|
|
10
22
|
/** Total wall-clock deadline. Exposed for focused tests. */
|
|
11
23
|
totalTimeoutMs?: number;
|
|
12
24
|
/** Deadline between non-empty raw chunks. Exposed for focused tests. */
|
|
13
25
|
inactivityTimeoutMs?: number;
|
|
26
|
+
/** Deadline for the first non-empty raw chunk. Defaults to inactivityTimeoutMs. */
|
|
27
|
+
firstByteTimeoutMs?: number;
|
|
14
28
|
}
|
|
15
29
|
|
|
16
30
|
export interface BoundedBodyResult {
|
|
@@ -30,9 +44,42 @@ export interface BoundedBodyResult {
|
|
|
30
44
|
displaySafe: boolean;
|
|
31
45
|
}
|
|
32
46
|
|
|
47
|
+
export interface BoundedBytesOptions {
|
|
48
|
+
/** Abort the read with this signal. Its reason is rethrown by identity. */
|
|
49
|
+
signal?: AbortSignal;
|
|
50
|
+
/** Maximum number of raw bytes retained from the response body. */
|
|
51
|
+
maxBytes: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface BoundedBytesResult {
|
|
55
|
+
/**
|
|
56
|
+
* Exact raw bytes retained from the response. Empty when the cap was exceeded.
|
|
57
|
+
*
|
|
58
|
+
* This is a view over internal storage, so `bytes.buffer.byteLength` can exceed
|
|
59
|
+
* `bytes.byteLength`. Consumers must honor the view's byteOffset and byteLength
|
|
60
|
+
* instead of reading or transferring the backing buffer directly.
|
|
61
|
+
*/
|
|
62
|
+
bytes: Uint8Array<ArrayBuffer>;
|
|
63
|
+
/** True when the body was observed to exceed the byte cap. */
|
|
64
|
+
oversized: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
const TOTAL_TIMEOUT = Symbol("bounded body total timeout");
|
|
34
68
|
const INACTIVITY_TIMEOUT = Symbol("bounded body inactivity timeout");
|
|
35
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Test-only instrumentation: how many times the retained buffer was reallocated
|
|
72
|
+
* during the most recent read. The accumulator grows geometrically, so this is
|
|
73
|
+
* logarithmic in the body size and independent of how many chunks the peer sends.
|
|
74
|
+
* The per-chunk array it replaced retained one object per chunk instead, which a
|
|
75
|
+
* fragmenting peer can inflate far past the payload ceiling — a property no
|
|
76
|
+
* correctness assertion can see, which is why it is observable here.
|
|
77
|
+
*/
|
|
78
|
+
let bufferGrowthsForTests = 0;
|
|
79
|
+
export function boundedBodyBufferGrowthsForTests(): number {
|
|
80
|
+
return bufferGrowthsForTests;
|
|
81
|
+
}
|
|
82
|
+
|
|
36
83
|
function timeoutPromise(ms: number, value: symbol): { promise: Promise<symbol>; clear: () => void } {
|
|
37
84
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
38
85
|
const promise = new Promise<symbol>((resolve) => {
|
|
@@ -56,8 +103,102 @@ function cancelWithoutWaiting(reader: ReadableStreamDefaultReader<Uint8Array>, r
|
|
|
56
103
|
}
|
|
57
104
|
}
|
|
58
105
|
|
|
59
|
-
function
|
|
60
|
-
|
|
106
|
+
function cancelBodyWithoutWaiting(body: ReadableStream<Uint8Array>, reason?: unknown): void {
|
|
107
|
+
// A signal can already be aborted before a reader is attached. Still settle the
|
|
108
|
+
// original body so fetch-backed streams cannot retain a rejected read in that gap.
|
|
109
|
+
try {
|
|
110
|
+
void body.cancel(reason).catch(() => undefined);
|
|
111
|
+
} catch {
|
|
112
|
+
// A locked or non-conforming stream may throw synchronously from cancel().
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Consume the original response body as raw bytes under a strict memory ceiling.
|
|
118
|
+
*
|
|
119
|
+
* The caller owns any wall-clock deadline through `signal`, which lets one budget
|
|
120
|
+
* cover both response headers and body consumption. No decoding, cloning, or teeing
|
|
121
|
+
* occurs, so arbitrary upstream bytes remain unchanged.
|
|
122
|
+
*/
|
|
123
|
+
export async function readBoundedResponseBytes(
|
|
124
|
+
response: Response,
|
|
125
|
+
options: BoundedBytesOptions,
|
|
126
|
+
): Promise<BoundedBytesResult> {
|
|
127
|
+
const signal = options.signal;
|
|
128
|
+
if (signal?.aborted) throw signal.reason;
|
|
129
|
+
|
|
130
|
+
const body = response.body;
|
|
131
|
+
if (!body) return { bytes: new Uint8Array(0), oversized: false };
|
|
132
|
+
|
|
133
|
+
const reader = body.getReader();
|
|
134
|
+
const maxBytes = options.maxBytes;
|
|
135
|
+
let retained = new Uint8Array(Math.min(maxBytes, 64 * 1024));
|
|
136
|
+
let retainedBytes = 0;
|
|
137
|
+
let mustCancel = false;
|
|
138
|
+
let cancelReason: unknown;
|
|
139
|
+
|
|
140
|
+
let rejectForAbort: ((reason: unknown) => void) | undefined;
|
|
141
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
142
|
+
rejectForAbort = reject;
|
|
143
|
+
});
|
|
144
|
+
const onAbort = () => rejectForAbort?.(signal?.reason);
|
|
145
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
146
|
+
// Close the narrow race between the preflight check and listener install.
|
|
147
|
+
if (signal?.aborted) onAbort();
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
while (true) {
|
|
151
|
+
const read = reader.read();
|
|
152
|
+
// Observe a late read rejection when abort/cancellation wins the race.
|
|
153
|
+
void read.catch(() => undefined);
|
|
154
|
+
const outcome = await Promise.race([read, aborted]);
|
|
155
|
+
if (signal?.aborted) {
|
|
156
|
+
mustCancel = true;
|
|
157
|
+
cancelReason = signal.reason;
|
|
158
|
+
throw signal.reason;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const { value, done } = outcome;
|
|
162
|
+
if (done) {
|
|
163
|
+
return { bytes: retained.subarray(0, retainedBytes), oversized: false };
|
|
164
|
+
}
|
|
165
|
+
if (!value || value.byteLength === 0) continue;
|
|
166
|
+
|
|
167
|
+
if (value.byteLength > maxBytes - retainedBytes) {
|
|
168
|
+
mustCancel = true;
|
|
169
|
+
cancelReason = new DOMException("Response body size limit reached", "QuotaExceededError");
|
|
170
|
+
retained = new Uint8Array(0);
|
|
171
|
+
retainedBytes = 0;
|
|
172
|
+
return { bytes: retained, oversized: true };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (retainedBytes + value.byteLength > retained.length) {
|
|
176
|
+
const grown = new Uint8Array(
|
|
177
|
+
Math.min(maxBytes, Math.max(retained.length * 2, retainedBytes + value.byteLength)),
|
|
178
|
+
);
|
|
179
|
+
grown.set(retained.subarray(0, retainedBytes));
|
|
180
|
+
retained = grown;
|
|
181
|
+
}
|
|
182
|
+
retained.set(value, retainedBytes);
|
|
183
|
+
retainedBytes += value.byteLength;
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
mustCancel = true;
|
|
187
|
+
cancelReason = error;
|
|
188
|
+
throw error;
|
|
189
|
+
} finally {
|
|
190
|
+
signal?.removeEventListener("abort", onAbort);
|
|
191
|
+
if (mustCancel) cancelWithoutWaiting(reader, cancelReason);
|
|
192
|
+
try {
|
|
193
|
+
reader.releaseLock();
|
|
194
|
+
} catch {
|
|
195
|
+
// A pending read can keep the lock briefly while cancel settles.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function decodeUtf8(chunks: readonly Uint8Array[], fatal: boolean): string {
|
|
201
|
+
const decoder = new TextDecoder("utf-8", { fatal });
|
|
61
202
|
let text = "";
|
|
62
203
|
for (const chunk of chunks) text += decoder.decode(chunk, { stream: true });
|
|
63
204
|
// Flush an incomplete trailing UTF-8 sequence deterministically.
|
|
@@ -77,9 +218,11 @@ export async function readBoundedResponseBody(
|
|
|
77
218
|
options: BoundedBodyOptions = {},
|
|
78
219
|
): Promise<BoundedBodyResult> {
|
|
79
220
|
const signal = options.signal;
|
|
80
|
-
if (signal?.aborted) throw signal.reason;
|
|
81
|
-
|
|
82
221
|
const body = response.body;
|
|
222
|
+
if (signal?.aborted) {
|
|
223
|
+
if (body) cancelBodyWithoutWaiting(body, signal.reason);
|
|
224
|
+
throw signal.reason;
|
|
225
|
+
}
|
|
83
226
|
if (!body) {
|
|
84
227
|
return {
|
|
85
228
|
text: "",
|
|
@@ -93,13 +236,18 @@ export async function readBoundedResponseBody(
|
|
|
93
236
|
}
|
|
94
237
|
|
|
95
238
|
const reader = body.getReader();
|
|
96
|
-
const
|
|
239
|
+
const maxBytes = options.maxBytes ?? BOUNDED_BODY_MAX_BYTES;
|
|
240
|
+
// Geometrically growing single buffer: per-chunk arrays would retain one object per
|
|
241
|
+
// transport chunk, which a hostile peer could inflate into metadata amplification far
|
|
242
|
+
// beyond the payload ceiling on large budgets.
|
|
243
|
+
let retained = new Uint8Array(Math.min(maxBytes, 64 * 1024));
|
|
97
244
|
let retainedBytes = 0;
|
|
245
|
+
bufferGrowthsForTests = 0;
|
|
98
246
|
let mustCancel = false;
|
|
99
247
|
let cancelReason: unknown;
|
|
100
248
|
const total = timeoutPromise(options.totalTimeoutMs ?? BOUNDED_BODY_TIMEOUT_MS, TOTAL_TIMEOUT);
|
|
101
249
|
let inactivity = timeoutPromise(
|
|
102
|
-
options.inactivityTimeoutMs ?? BOUNDED_BODY_TIMEOUT_MS,
|
|
250
|
+
options.firstByteTimeoutMs ?? options.inactivityTimeoutMs ?? BOUNDED_BODY_TIMEOUT_MS,
|
|
103
251
|
INACTIVITY_TIMEOUT,
|
|
104
252
|
);
|
|
105
253
|
|
|
@@ -134,7 +282,7 @@ export async function readBoundedResponseBody(
|
|
|
134
282
|
"TimeoutError",
|
|
135
283
|
);
|
|
136
284
|
return {
|
|
137
|
-
text: decodeUtf8(
|
|
285
|
+
text: decodeUtf8([retained.subarray(0, retainedBytes)], options.fatalUtf8 === true),
|
|
138
286
|
truncated: true,
|
|
139
287
|
timedOut: true,
|
|
140
288
|
totalTimedOut: outcome === TOTAL_TIMEOUT,
|
|
@@ -147,7 +295,7 @@ export async function readBoundedResponseBody(
|
|
|
147
295
|
const { value, done } = outcome as ReadableStreamReadResult<Uint8Array>;
|
|
148
296
|
if (done) {
|
|
149
297
|
return {
|
|
150
|
-
text: decodeUtf8(
|
|
298
|
+
text: decodeUtf8([retained.subarray(0, retainedBytes)], options.fatalUtf8 === true),
|
|
151
299
|
truncated: false,
|
|
152
300
|
timedOut: false,
|
|
153
301
|
totalTimedOut: false,
|
|
@@ -165,10 +313,10 @@ export async function readBoundedResponseBody(
|
|
|
165
313
|
INACTIVITY_TIMEOUT,
|
|
166
314
|
);
|
|
167
315
|
|
|
168
|
-
if (value.byteLength >
|
|
316
|
+
if (value.byteLength > maxBytes - retainedBytes) {
|
|
169
317
|
mustCancel = true;
|
|
170
318
|
cancelReason = new DOMException("Error body size limit reached", "QuotaExceededError");
|
|
171
|
-
|
|
319
|
+
retained = new Uint8Array(0);
|
|
172
320
|
retainedBytes = 0;
|
|
173
321
|
return {
|
|
174
322
|
text: "",
|
|
@@ -181,7 +329,15 @@ export async function readBoundedResponseBody(
|
|
|
181
329
|
};
|
|
182
330
|
}
|
|
183
331
|
|
|
184
|
-
|
|
332
|
+
if (retainedBytes + value.byteLength > retained.length) {
|
|
333
|
+
const grown = new Uint8Array(
|
|
334
|
+
Math.min(maxBytes, Math.max(retained.length * 2, retainedBytes + value.byteLength)),
|
|
335
|
+
);
|
|
336
|
+
grown.set(retained.subarray(0, retainedBytes));
|
|
337
|
+
retained = grown;
|
|
338
|
+
bufferGrowthsForTests += 1;
|
|
339
|
+
}
|
|
340
|
+
retained.set(value, retainedBytes);
|
|
185
341
|
retainedBytes += value.byteLength;
|
|
186
342
|
}
|
|
187
343
|
} catch (error) {
|
package/src/lib/bun-runtime.ts
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* back to `process.execPath` (which is itself Bun when run via `bun src/cli/index.ts`).
|
|
12
12
|
*/
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
|
-
import {
|
|
14
|
+
import { realpathSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
15
16
|
import { isRealBunBinary } from "./bun-binary-validator.mjs";
|
|
16
17
|
|
|
17
18
|
export { isRealBunBinary };
|
|
@@ -20,12 +21,124 @@ const require = createRequire(import.meta.url);
|
|
|
20
21
|
|
|
21
22
|
const BUN_OVERRIDE_ENV = "OPENCODEX_BUN_PATH";
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Env marker stamped by whichever launcher selected the Bun binary, then read back
|
|
26
|
+
* inside the launched process.
|
|
27
|
+
*
|
|
28
|
+
* Provenance has to travel with the launch because it cannot be recovered afterwards:
|
|
29
|
+
* resolving it at report time answers "what would this shell pick now", not "what was
|
|
30
|
+
* this service started with", and those differ exactly when the answer matters.
|
|
31
|
+
*/
|
|
32
|
+
export const BUN_RUNTIME_SOURCE_ENV = "OCX_BUN_RUNTIME_SOURCE";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The binary the marker was minted for. Stamped beside the source so a reader can tell
|
|
36
|
+
* whether a marker still describes the process holding it, without re-deriving the
|
|
37
|
+
* selection from an environment that may no longer contain it.
|
|
38
|
+
*/
|
|
39
|
+
export const BUN_RUNTIME_PATH_ENV = "OCX_BUN_RUNTIME_PATH";
|
|
40
|
+
|
|
41
|
+
export type BunRuntimeSource = "override" | "bundled" | "process";
|
|
42
|
+
|
|
43
|
+
/** The only provenance values any surface may accept off the wire or out of the env. */
|
|
44
|
+
export const BUN_RUNTIME_SOURCES: readonly BunRuntimeSource[] = ["override", "bundled", "process"];
|
|
45
|
+
|
|
23
46
|
export type DurableBunRuntime = {
|
|
24
47
|
path: string;
|
|
25
|
-
source:
|
|
48
|
+
source: BunRuntimeSource;
|
|
26
49
|
overrideEnv: typeof BUN_OVERRIDE_ENV;
|
|
27
50
|
};
|
|
28
51
|
|
|
52
|
+
/**
|
|
53
|
+
* The provenance this process was launched with, or `undefined` when nothing
|
|
54
|
+
* trustworthy is recorded.
|
|
55
|
+
*
|
|
56
|
+
* Deliberately never falls back to `durableBunRuntime()`. A service installed before
|
|
57
|
+
* the marker existed has no provenance, and guessing one from the current environment
|
|
58
|
+
* would report a confident wrong answer — "unknown" is the honest result and callers
|
|
59
|
+
* are expected to say so. Values outside the allowlist are treated as absent rather
|
|
60
|
+
* than passed through.
|
|
61
|
+
*/
|
|
62
|
+
export function reportedBunRuntimeSource(
|
|
63
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
64
|
+
): BunRuntimeSource | undefined {
|
|
65
|
+
const raw = env[BUN_RUNTIME_SOURCE_ENV]?.trim();
|
|
66
|
+
const source = BUN_RUNTIME_SOURCES.find(candidate => candidate === raw);
|
|
67
|
+
if (!source) return undefined;
|
|
68
|
+
// Source and binary are a PAIR: without a recorded path that names THIS
|
|
69
|
+
// executable, the marker describes some other launch and must not be
|
|
70
|
+
// reported (a bare OCX_BUN_RUNTIME_SOURCE inherited from an unrelated
|
|
71
|
+
// parent would otherwise claim a confident wrong origin).
|
|
72
|
+
const recordedPath = env[BUN_RUNTIME_PATH_ENV]?.trim();
|
|
73
|
+
if (!recordedPath || !samePath(recordedPath, process.execPath)) return undefined;
|
|
74
|
+
return source;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Env pair a launcher stamps for the binary it just selected. */
|
|
78
|
+
export function bunRuntimeProvenanceEnv(runtime: DurableBunRuntime): Record<string, string> {
|
|
79
|
+
return { [BUN_RUNTIME_SOURCE_ENV]: runtime.source, [BUN_RUNTIME_PATH_ENV]: runtime.path };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Child environment for a proxy started with `process.execPath` — the runtime this
|
|
84
|
+
* process is already using.
|
|
85
|
+
*
|
|
86
|
+
* These launchers re-exec the current runtime rather than resolving a binary, so the
|
|
87
|
+
* provenance to report is whatever launched THIS process. Without this the marker would
|
|
88
|
+
* be silently dropped on `ocx ensure`, GUI start, restart, and update-relaunch, and the
|
|
89
|
+
* service would report an unknown origin it actually knows.
|
|
90
|
+
*
|
|
91
|
+
* An inherited marker is carried forward only when the binary it was minted for is the
|
|
92
|
+
* one about to be re-executed. Inheritance travels down a process tree, so a marker can
|
|
93
|
+
* outlive its binary — something started under a marked process but running a different
|
|
94
|
+
* Bun would otherwise relaunch the daemon with a provenance contradicting the binary
|
|
95
|
+
* actually serving it. The check compares the recorded path rather than re-deriving the
|
|
96
|
+
* selection, because a service installed with a shell-local override keeps neither that
|
|
97
|
+
* shell nor its `OPENCODEX_BUN_PATH`, and re-deriving would demote a correct `override`
|
|
98
|
+
* to `process` on its first relaunch.
|
|
99
|
+
*/
|
|
100
|
+
export function withProcessRuntimeProvenance(
|
|
101
|
+
env: NodeJS.ProcessEnv,
|
|
102
|
+
): NodeJS.ProcessEnv {
|
|
103
|
+
return { ...env, ...bunRuntimeProvenanceEnv(currentRuntimeProvenance(env)) };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Provenance for `process.execPath`: the inherited claim when it was minted for this
|
|
108
|
+
* exact executable, otherwise what this executable actually is.
|
|
109
|
+
*/
|
|
110
|
+
function currentRuntimeProvenance(env: NodeJS.ProcessEnv): DurableBunRuntime {
|
|
111
|
+
const recorded = recordedCurrentRuntime(env);
|
|
112
|
+
if (recorded) return recorded;
|
|
113
|
+
// No marker that describes this binary: report what is running. One resolution
|
|
114
|
+
// supplies both halves so the pair can never disagree.
|
|
115
|
+
const runtime = unmarkedDurableBunRuntime();
|
|
116
|
+
return samePath(runtime.path, process.execPath)
|
|
117
|
+
? runtime
|
|
118
|
+
: { path: process.execPath, source: "process", overrideEnv: BUN_OVERRIDE_ENV };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function recordedCurrentRuntime(env: NodeJS.ProcessEnv): DurableBunRuntime | null {
|
|
122
|
+
const source = reportedBunRuntimeSource(env);
|
|
123
|
+
const path = env[BUN_RUNTIME_PATH_ENV]?.trim();
|
|
124
|
+
if (!source || !path || !samePath(path, process.execPath)) return null;
|
|
125
|
+
return { path, source, overrideEnv: BUN_OVERRIDE_ENV };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Same file, allowing for the aliases a path can pick up between launch and relaunch:
|
|
130
|
+
* symlinks/junctions, mapped drives, and Windows case differences. Falls back to a
|
|
131
|
+
* lexical comparison when a path cannot be resolved (it may be gone).
|
|
132
|
+
*/
|
|
133
|
+
function samePath(left: string, right: string): boolean {
|
|
134
|
+
const canonical = (value: string): string => {
|
|
135
|
+
let resolved = value;
|
|
136
|
+
try { resolved = realpathSync(value); } catch { /* keep the literal path */ }
|
|
137
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
138
|
+
};
|
|
139
|
+
return canonical(left) === canonical(right);
|
|
140
|
+
}
|
|
141
|
+
|
|
29
142
|
/**
|
|
30
143
|
* Absolute path to the bundled Bun binary, or null if the `bun` dependency is
|
|
31
144
|
* not installed/resolvable (or only the un-downloaded placeholder is present).
|
|
@@ -45,21 +158,21 @@ export function bundledBunPath(): string | null {
|
|
|
45
158
|
}
|
|
46
159
|
}
|
|
47
160
|
|
|
48
|
-
|
|
49
|
-
const value = process.env[BUN_OVERRIDE_ENV]?.trim();
|
|
50
|
-
if (!value) return null;
|
|
51
|
-
const resolved = resolve(value);
|
|
52
|
-
return isRealBunBinary(resolved) ? resolved : null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function durableBunRuntime(): DurableBunRuntime {
|
|
56
|
-
const override = overrideBunPath();
|
|
57
|
-
if (override) return { path: override, source: "override", overrideEnv: BUN_OVERRIDE_ENV };
|
|
161
|
+
function unmarkedDurableBunRuntime(): DurableBunRuntime {
|
|
58
162
|
const bundled = bundledBunPath();
|
|
59
163
|
if (bundled) return { path: bundled, source: "bundled", overrideEnv: BUN_OVERRIDE_ENV };
|
|
60
164
|
return { path: process.execPath, source: "process", overrideEnv: BUN_OVERRIDE_ENV };
|
|
61
165
|
}
|
|
62
166
|
|
|
167
|
+
export function durableBunRuntime(): DurableBunRuntime {
|
|
168
|
+
// A durable artifact must use the runtime selected BEFORE Bun auto-loaded a
|
|
169
|
+
// project dotenv. The Node launcher and owned service/shim launchers stamp the
|
|
170
|
+
// selected source/path pair; it is accepted only when it names this exact
|
|
171
|
+
// running executable. Re-reading OPENCODEX_BUN_PATH here would let a project
|
|
172
|
+
// `.env` persist an arbitrary executable into a shim or service.
|
|
173
|
+
return recordedCurrentRuntime(process.env) ?? unmarkedDurableBunRuntime();
|
|
174
|
+
}
|
|
175
|
+
|
|
63
176
|
/**
|
|
64
177
|
* Bun path to bake into durable artifacts (launchd/systemd/Task Scheduler and
|
|
65
178
|
* the Codex auto-start shim). Prefer the bundled binary — it lives under the
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The eager bounded relay (src/server/relay-eager.ts) uses a JS async producer
|
|
5
5
|
* loop — the exact shape of the Bun#32111 use-after-free (fixed upstream by Bun
|
|
6
|
-
* PR #32120, merged 2026-06-21).
|
|
7
|
-
* that fix
|
|
8
|
-
* "known-bad"
|
|
9
|
-
* follows this runtime/config decision
|
|
6
|
+
* PR #32120, merged 2026-06-21). Bun 1.4.0 is the first RELEASED version proven
|
|
7
|
+
* to carry that fix, so `MIN_FIXED_BUN_VERSION` is "1.4.0": older runtimes stay
|
|
8
|
+
* "known-bad". Windows no-rewrite traffic
|
|
9
|
+
* follows this runtime/config decision, preserving the explicit legacy-tee
|
|
10
|
+
* safety pin. Darwin no-rewrite traffic stays on tee
|
|
10
11
|
* for `auto` regardless of runtime capability and reaches eager relay only via
|
|
11
12
|
* explicit `streamMode: "eager-relay"` opt-in (see
|
|
12
|
-
* devlog/
|
|
13
|
+
* devlog/_fin/260731_macos_rss_retention/100_darwin_eager_optin.md).
|
|
13
14
|
*
|
|
14
15
|
* Prerelease conservatism: a version carrying a prerelease suffix (e.g.
|
|
15
16
|
* `1.4.0-canary.3`) is NEVER treated as fixed even when its numeric triple
|
|
@@ -20,8 +21,11 @@
|
|
|
20
21
|
/**
|
|
21
22
|
* Bump in the SAME commit that bumps package.json's bundled Bun to a version
|
|
22
23
|
* verified to include Bun PR #32120. null = no released version is known-fixed.
|
|
24
|
+
* Bun 1.4.0 (npm stable, bundled by this package.json) carries the fix: PR
|
|
25
|
+
* #32120 merged 2026-06-21, well before the 1.4.0 cut, and the full suite ran
|
|
26
|
+
* green under the 1.4 line on every supported OS (devlog/260814_bun14-preview-dev).
|
|
23
27
|
*/
|
|
24
|
-
export const MIN_FIXED_BUN_VERSION: string | null =
|
|
28
|
+
export const MIN_FIXED_BUN_VERSION: string | null = "1.4.0";
|
|
25
29
|
|
|
26
30
|
export type StreamMode = "auto" | "legacy-tee" | "eager-relay";
|
|
27
31
|
|
|
@@ -93,7 +97,7 @@ export function decideEagerRelay(
|
|
|
93
97
|
* Windows preserves the decision for no-rewrite traffic. Darwin permits only
|
|
94
98
|
* explicit config opt-in; `auto` remains tee even on a future fixed runtime.
|
|
95
99
|
* Returns the normalized effective decision, or null when platform policy,
|
|
96
|
-
* rewrite needs, or a Darwin non-config-eager mode selects tee.
|
|
100
|
+
* Windows rewrite needs, or a Darwin non-config-eager mode selects tee.
|
|
97
101
|
*/
|
|
98
102
|
export function selectEagerPath(
|
|
99
103
|
platform: NodeJS.Platform,
|
|
@@ -102,12 +106,12 @@ export function selectEagerPath(
|
|
|
102
106
|
version: string = Bun.version,
|
|
103
107
|
minFixed: string | null = MIN_FIXED_BUN_VERSION,
|
|
104
108
|
): EagerRelayDecision | null {
|
|
105
|
-
if (
|
|
109
|
+
if (platform !== "win32" && platform !== "darwin") {
|
|
106
110
|
return null;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
const decision = decideEagerRelay(mode, version, minFixed);
|
|
110
|
-
if (platform === "win32") return decision;
|
|
114
|
+
if (platform === "win32") return needsClientRewrite ? null : decision;
|
|
111
115
|
return decision.reason === "config-eager" ? decision : null;
|
|
112
116
|
}
|
|
113
117
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for the dashboard-driven Codex app-server restart (#1046 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* Distinct from `system-restart-contract.ts`: that one restarts THIS proxy process
|
|
5
|
+
* and needs a pid-bound capability because it kills its own listener. This one asks
|
|
6
|
+
* matching Codex app-server children to exit so Codex rereads the catalog on next
|
|
7
|
+
* launch. It never touches the proxy and never spawns a replacement — whoever owns
|
|
8
|
+
* the app-server (the Codex app, an SSH bootstrap) relaunches it on next use.
|
|
9
|
+
*
|
|
10
|
+
* Scalar-only payload. A command line can contain a home directory and a username,
|
|
11
|
+
* and an OS error message often embeds a path, so neither crosses this boundary.
|
|
12
|
+
*
|
|
13
|
+
* Design and audit history: `devlog/_fin/260815_gui_codex_restart/`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const CODEX_RESTART_METHOD = "POST";
|
|
17
|
+
export const CODEX_RESTART_PATH = "/api/system/codex-restart";
|
|
18
|
+
export const CODEX_APP_SERVER_STATE_PATH = "/api/system/codex-app-server";
|
|
19
|
+
|
|
20
|
+
/** Mirrors CodexAppServerCatalogState so the GUI never imports runtime code. */
|
|
21
|
+
export type CodexAppServerState = "fresh" | "stale" | "not_running" | "unknown";
|
|
22
|
+
|
|
23
|
+
export type CodexRestartCode =
|
|
24
|
+
| "stopped"
|
|
25
|
+
| "nothing_running"
|
|
26
|
+
| "enumeration_unavailable"
|
|
27
|
+
| "partially_stopped";
|
|
28
|
+
|
|
29
|
+
/** GET response: a cheap reading with no side effects. It never signals. */
|
|
30
|
+
export interface CodexAppServerStateResponse {
|
|
31
|
+
state: CodexAppServerState;
|
|
32
|
+
runningCount: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** POST response. All four arrays are pid lists — never command lines. */
|
|
36
|
+
export interface CodexRestartResponse {
|
|
37
|
+
success: boolean;
|
|
38
|
+
/** Classifier reading taken BEFORE any signal, so the UI can explain why it acted. */
|
|
39
|
+
stateBefore: CodexAppServerState;
|
|
40
|
+
/** Whether a catalog or cache write happened during this request. */
|
|
41
|
+
synced: boolean;
|
|
42
|
+
requested: number[];
|
|
43
|
+
stopped: number[];
|
|
44
|
+
surviving: number[];
|
|
45
|
+
failed: number[];
|
|
46
|
+
code: CodexRestartCode;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const APP_SERVER_STATES: readonly string[] = ["fresh", "stale", "not_running", "unknown"];
|
|
50
|
+
const RESTART_CODES: readonly string[] = [
|
|
51
|
+
"stopped",
|
|
52
|
+
"nothing_running",
|
|
53
|
+
"enumeration_unavailable",
|
|
54
|
+
"partially_stopped",
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A pid is a positive safe integer. Accepting a float or a negative number would
|
|
59
|
+
* let a malformed body reach UI code that renders counts and indexes lengths.
|
|
60
|
+
*/
|
|
61
|
+
function isPidList(value: unknown): value is number[] {
|
|
62
|
+
return Array.isArray(value)
|
|
63
|
+
&& value.every(entry => typeof entry === "number" && Number.isSafeInteger(entry) && entry > 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Runtime guard for GUI consumers: a 2xx body is not automatically this shape.
|
|
68
|
+
*
|
|
69
|
+
* Structure is not enough. A body can be structurally valid and still contradict
|
|
70
|
+
* itself — `code: "stopped"` alongside surviving pids, or `success: true` with a
|
|
71
|
+
* failure code — and a caller that trusts it reports a success that did not happen.
|
|
72
|
+
* A version-skewed or regressed proxy is exactly how that arrives, so the
|
|
73
|
+
* cross-field invariants are checked here rather than assumed.
|
|
74
|
+
*/
|
|
75
|
+
export function isCodexRestartResponse(value: unknown): value is CodexRestartResponse {
|
|
76
|
+
if (typeof value !== "object" || value === null) return false;
|
|
77
|
+
const view = value as Record<string, unknown>;
|
|
78
|
+
const structural = typeof view.success === "boolean"
|
|
79
|
+
&& typeof view.synced === "boolean"
|
|
80
|
+
&& typeof view.stateBefore === "string"
|
|
81
|
+
&& APP_SERVER_STATES.includes(view.stateBefore)
|
|
82
|
+
&& typeof view.code === "string"
|
|
83
|
+
&& RESTART_CODES.includes(view.code)
|
|
84
|
+
&& isPidList(view.requested)
|
|
85
|
+
&& isPidList(view.stopped)
|
|
86
|
+
&& isPidList(view.surviving)
|
|
87
|
+
&& isPidList(view.failed);
|
|
88
|
+
if (!structural) return false;
|
|
89
|
+
|
|
90
|
+
const success = view.success as boolean;
|
|
91
|
+
const code = view.code as CodexRestartCode;
|
|
92
|
+
const surviving = view.surviving as number[];
|
|
93
|
+
const failed = view.failed as number[];
|
|
94
|
+
const stopped = view.stopped as number[];
|
|
95
|
+
|
|
96
|
+
// `success` and `code` must agree: only partially_stopped is an unsuccessful code.
|
|
97
|
+
if (success !== (code !== "partially_stopped")) return false;
|
|
98
|
+
// A clean outcome cannot leave anything behind.
|
|
99
|
+
if (success && (surviving.length > 0 || failed.length > 0)) return false;
|
|
100
|
+
// An unsuccessful outcome must name what survived.
|
|
101
|
+
if (!success && surviving.length === 0 && failed.length === 0) return false;
|
|
102
|
+
// Nothing can be reported stopped when the service says nothing was running.
|
|
103
|
+
if ((code === "nothing_running" || code === "enumeration_unavailable") && stopped.length > 0) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function isCodexAppServerStateResponse(
|
|
110
|
+
value: unknown,
|
|
111
|
+
): value is CodexAppServerStateResponse {
|
|
112
|
+
if (typeof value !== "object" || value === null) return false;
|
|
113
|
+
const view = value as Record<string, unknown>;
|
|
114
|
+
return typeof view.state === "string"
|
|
115
|
+
&& APP_SERVER_STATES.includes(view.state)
|
|
116
|
+
&& typeof view.runningCount === "number"
|
|
117
|
+
&& Number.isSafeInteger(view.runningCount)
|
|
118
|
+
&& view.runningCount >= 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
readFileSync,
|
|
6
6
|
readdirSync,
|
|
7
7
|
realpathSync,
|
|
8
|
-
renameSync,
|
|
9
8
|
rmdirSync,
|
|
10
9
|
unlinkSync,
|
|
11
10
|
writeFileSync,
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
import { randomUUID } from "node:crypto";
|
|
14
13
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
15
14
|
import type { GenerationContext } from "./state-store-sweeper";
|
|
15
|
+
import { renameAtomicFile } from "./windows-atomic-replace";
|
|
16
16
|
|
|
17
17
|
export const CONFIG_OWNER_FILE = ".opencodex-owner.json";
|
|
18
18
|
export const CONFIG_UNINSTALL_MANIFEST = ".opencodex-uninstall.json";
|
|
@@ -40,6 +40,7 @@ const INITIAL_OWNED_PATHS = [
|
|
|
40
40
|
"artifacts",
|
|
41
41
|
"auth.json",
|
|
42
42
|
"auth.store.lock",
|
|
43
|
+
"admin-api-token",
|
|
43
44
|
"catalog-backup.json",
|
|
44
45
|
"claude-env.sh",
|
|
45
46
|
"codex-accounts.json",
|
|
@@ -69,6 +70,7 @@ const INITIAL_OWNED_PATHS = [
|
|
|
69
70
|
"service-state.json",
|
|
70
71
|
"service.log",
|
|
71
72
|
"system-env-port",
|
|
73
|
+
"thought-signature-replay.json",
|
|
72
74
|
"tray-heartbeat.json",
|
|
73
75
|
"tray-state.json",
|
|
74
76
|
"update-job.json",
|
|
@@ -231,7 +233,10 @@ function writeManifest(configDir: string, manifest: ConfigUninstallManifest): vo
|
|
|
231
233
|
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
232
234
|
writeFileSync(temp, `${JSON.stringify(manifest, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
233
235
|
try {
|
|
234
|
-
|
|
236
|
+
// Same Windows sharing-violation tolerance the config writer has: a
|
|
237
|
+
// scanner holding the manifest must not turn uninstall bookkeeping into a
|
|
238
|
+
// hard failure.
|
|
239
|
+
renameAtomicFile(temp, path, undefined, "config-ownership");
|
|
235
240
|
} catch (error) {
|
|
236
241
|
try { unlinkSync(temp); } catch { /* best effort */ }
|
|
237
242
|
throw error;
|
|
@@ -329,6 +334,25 @@ export function removeOwnedConfigState(configDir: string): ConfigRemovalResult {
|
|
|
329
334
|
}
|
|
330
335
|
}
|
|
331
336
|
|
|
337
|
+
// Per-catalog backups are named `catalog-backup-<16 hex>.json` (catalogBackupPathFor), one per
|
|
338
|
+
// CODEX_HOME, so they cannot be enumerated as literal manifest entries the way every other
|
|
339
|
+
// owned file can. Without this, `ocx uninstall` always reported "unowned files remain" and
|
|
340
|
+
// refused to remove a home OpenCodex created itself — the file is unambiguously ours, produced
|
|
341
|
+
// by our own writer, and the strict hex shape keeps the match from widening.
|
|
342
|
+
for (const name of readdirSync(configDir)) {
|
|
343
|
+
if (!/^catalog-backup-[0-9a-f]{16}\.json$/.test(name)) continue;
|
|
344
|
+
const path = join(configDir, name);
|
|
345
|
+
try {
|
|
346
|
+
removeOwnedEntry(rootPath, path);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
return {
|
|
349
|
+
status: "partial",
|
|
350
|
+
reason: `could not remove owned path ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
351
|
+
residualPaths: [path],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
332
356
|
try {
|
|
333
357
|
unlinkSync(join(configDir, CONFIG_UNINSTALL_MANIFEST));
|
|
334
358
|
unlinkSync(join(configDir, CONFIG_OWNER_FILE));
|