@iislee/opencodex 2.11.0 → 2.31.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 +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -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 +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- 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 +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- 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 +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- 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 +618 -74
- package/src/adapters/identity.ts +39 -6
- 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 +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- 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 +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- 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 +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- 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 +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -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 +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- 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 +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -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/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -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 +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -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 +353 -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 +492 -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 +682 -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 +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -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 +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -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 +152 -29
- package/src/images/plan.ts +23 -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.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -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/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- 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 +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- 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/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- 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/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 +202 -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 +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -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/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- 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/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -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 +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -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 +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -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 +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -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 +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -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 +145 -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 +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- 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 +865 -181
- package/src/server/lifecycle.ts +284 -13
- 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 +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -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 +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -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 +288 -32
- 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 +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- 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/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -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 +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -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 +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -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 +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- 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 +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- 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 +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- 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 +9 -3
- 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 +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- 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
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable generation ownership for OpenCodex config bytes.
|
|
3
|
+
*
|
|
4
|
+
* The WP8b C-phase review found that config ABA and moved configuration could
|
|
5
|
+
* not be detected because cooperating saves had no durable counter. This module
|
|
6
|
+
* owns the singleton schema and conditional increment in the existing config
|
|
7
|
+
* mutation database; callers that already hold its transaction reuse that
|
|
8
|
+
* Database handle so bytes and generation remain one cooperating commit.
|
|
9
|
+
*
|
|
10
|
+
* A hand edit from a non-cooperating writer deliberately does not increment this
|
|
11
|
+
* counter. The convergence contract detects that case with its post-commit file
|
|
12
|
+
* observation instead of pretending SQLite can coordinate an external editor.
|
|
13
|
+
*/
|
|
14
|
+
import { chmodSync, statSync } from "node:fs";
|
|
15
|
+
|
|
16
|
+
import { Database } from "bun:sqlite";
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
ConfigGeneration,
|
|
20
|
+
ConfigGenerationBump,
|
|
21
|
+
ConfigGenerationRead,
|
|
22
|
+
} from "./convergence-types";
|
|
23
|
+
|
|
24
|
+
const CREATE_CONFIG_GENERATION = `
|
|
25
|
+
CREATE TABLE IF NOT EXISTS config_generation (
|
|
26
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
27
|
+
value INTEGER NOT NULL CHECK (value >= 0)
|
|
28
|
+
)`;
|
|
29
|
+
const INITIALIZE_CONFIG_GENERATION = `
|
|
30
|
+
INSERT OR IGNORE INTO config_generation (singleton, value) VALUES (1, 0)`;
|
|
31
|
+
const SELECT_CONFIG_GENERATION = `
|
|
32
|
+
SELECT value FROM config_generation WHERE singleton = 1`;
|
|
33
|
+
const BUMP_CONFIG_GENERATION = `
|
|
34
|
+
UPDATE config_generation
|
|
35
|
+
SET value = value + 1
|
|
36
|
+
WHERE singleton = 1 AND value = ?`;
|
|
37
|
+
|
|
38
|
+
interface ConfigGenerationRow {
|
|
39
|
+
value: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface SchemaVersionRow {
|
|
43
|
+
schema_version: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Observation adds exactly one variant to `ConfigGenerationRead`: `absent`.
|
|
48
|
+
*
|
|
49
|
+
* This used to be deliberately absent itself, on the reasoning that a caller
|
|
50
|
+
* who may only LOOK must not be handed something it could mistake for a
|
|
51
|
+
* known-good baseline of zero. That reasoning still holds, and `absent` does
|
|
52
|
+
* not violate it — because `absent` is not a baseline. It authorizes nothing on
|
|
53
|
+
* its own. A caller may only promote it after taking the config transaction and
|
|
54
|
+
* reading a real zero THERE (`readConfigGenerationInCurrentMutationTransaction`),
|
|
55
|
+
* at which point the zero is observed rather than assumed. A caller with no
|
|
56
|
+
* transaction to open, such as catalog gather, must keep refusing it.
|
|
57
|
+
*
|
|
58
|
+
* What forced the distinction: refusing on absence meant refusing every Codex
|
|
59
|
+
* write on any home whose config predates this database — a permanent refusal
|
|
60
|
+
* for existing users, not a fixture problem.
|
|
61
|
+
*
|
|
62
|
+
* `absent` is returned ONLY for ENOENT on the initial `statSync`. A file that
|
|
63
|
+
* exists but cannot be read, a directory in its place, a bad schema version, or
|
|
64
|
+
* corrupt SQLite all stay `unavailable`, because those are reasons to stop, and
|
|
65
|
+
* collapsing them into absence is how a corrupt coordinator would become a
|
|
66
|
+
* licence to write.
|
|
67
|
+
*/
|
|
68
|
+
export type ConfigGenerationObservation =
|
|
69
|
+
| ConfigGenerationRead
|
|
70
|
+
| { kind: "absent" };
|
|
71
|
+
|
|
72
|
+
function errorCode(error: unknown): string {
|
|
73
|
+
return error && typeof error === "object" && "code" in error
|
|
74
|
+
? String((error as { code?: unknown }).code)
|
|
75
|
+
: "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isBusy(error: unknown): boolean {
|
|
79
|
+
const code = errorCode(error);
|
|
80
|
+
const message = error instanceof Error ? error.message : "";
|
|
81
|
+
return code === "SQLITE_BUSY"
|
|
82
|
+
|| code === "SQLITE_LOCKED"
|
|
83
|
+
|| /database (?:is|table is) locked/i.test(message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function unavailable(error: unknown): Extract<ConfigGenerationRead, { kind: "unavailable" }> {
|
|
87
|
+
return { kind: "unavailable", reason: isBusy(error) ? "busy" : "database" };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function initializeConfigGeneration(database: Database): void {
|
|
91
|
+
database.exec(CREATE_CONFIG_GENERATION);
|
|
92
|
+
database.exec(INITIALIZE_CONFIG_GENERATION);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function readConfigGenerationInTransaction(database: Database): ConfigGeneration {
|
|
96
|
+
const row = database.query<ConfigGenerationRow, []>(SELECT_CONFIG_GENERATION).get();
|
|
97
|
+
if (!row || !Number.isSafeInteger(row.value) || Number(row.value) < 0) {
|
|
98
|
+
throw new Error("The config generation singleton is missing or invalid.");
|
|
99
|
+
}
|
|
100
|
+
return { value: Number(row.value) };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function bumpConfigGenerationInTransaction(
|
|
104
|
+
database: Database,
|
|
105
|
+
expected: ConfigGeneration,
|
|
106
|
+
): ConfigGenerationBump {
|
|
107
|
+
const result = database.query(BUMP_CONFIG_GENERATION).run(expected.value);
|
|
108
|
+
if (result.changes === 1) {
|
|
109
|
+
return { kind: "updated", generation: { value: expected.value + 1 } };
|
|
110
|
+
}
|
|
111
|
+
return { kind: "conflict", current: readConfigGenerationInTransaction(database) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function bumpCurrentConfigGeneration(database: Database): ConfigGeneration {
|
|
115
|
+
const current = readConfigGenerationInTransaction(database);
|
|
116
|
+
const result = bumpConfigGenerationInTransaction(database, current);
|
|
117
|
+
if (result.kind !== "updated") {
|
|
118
|
+
throw new Error("The config generation changed inside its owning transaction.");
|
|
119
|
+
}
|
|
120
|
+
return result.generation;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function runGenerationTransaction<T>(databasePath: string, operation: (database: Database) => T): T {
|
|
124
|
+
let database: Database | undefined;
|
|
125
|
+
let transactionOpen = false;
|
|
126
|
+
try {
|
|
127
|
+
database = new Database(databasePath, { create: true });
|
|
128
|
+
try { chmodSync(databasePath, 0o600); } catch { /* platform may ignore chmod */ }
|
|
129
|
+
database.exec("PRAGMA busy_timeout = 0; BEGIN IMMEDIATE");
|
|
130
|
+
transactionOpen = true;
|
|
131
|
+
initializeConfigGeneration(database);
|
|
132
|
+
const result = operation(database);
|
|
133
|
+
database.exec("COMMIT");
|
|
134
|
+
transactionOpen = false;
|
|
135
|
+
return result;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (transactionOpen) {
|
|
138
|
+
try { database?.exec("ROLLBACK"); } catch { /* close releases the transaction */ }
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
} finally {
|
|
142
|
+
try { database?.close(); } catch { /* operation already completed */ }
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function readConfigGenerationAtPath(databasePath: string): ConfigGenerationRead {
|
|
147
|
+
try {
|
|
148
|
+
return {
|
|
149
|
+
kind: "ready",
|
|
150
|
+
generation: runGenerationTransaction(databasePath, readConfigGenerationInTransaction),
|
|
151
|
+
};
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return unavailable(error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Observe generation state without preparing the mutation database in any way.
|
|
159
|
+
* Missing storage is a first-class state: only cooperating config writes have
|
|
160
|
+
* authority to create and initialize the generation singleton.
|
|
161
|
+
*/
|
|
162
|
+
export function observeConfigGenerationAtPath(
|
|
163
|
+
databasePath: string,
|
|
164
|
+
): ConfigGenerationObservation {
|
|
165
|
+
try {
|
|
166
|
+
statSync(databasePath);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
// ENOENT alone means absent. Everything else — EACCES, ENOTDIR, EIO — is a
|
|
169
|
+
// reason the question could not be answered, which is not the same answer.
|
|
170
|
+
return errorCode(error) === "ENOENT" ? { kind: "absent" } : unavailable(error);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let database: Database | undefined;
|
|
174
|
+
try {
|
|
175
|
+
database = new Database(databasePath, { readonly: true });
|
|
176
|
+
const schema = database.query<SchemaVersionRow, []>("PRAGMA schema_version").get();
|
|
177
|
+
if (!schema || !Number.isSafeInteger(schema.schema_version)) {
|
|
178
|
+
throw new Error("The config generation schema version is invalid.");
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
kind: "ready",
|
|
182
|
+
generation: readConfigGenerationInTransaction(database),
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
return unavailable(error);
|
|
186
|
+
} finally {
|
|
187
|
+
try { database?.close(); } catch { /* observation already completed */ }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function bumpConfigGenerationAtPath(
|
|
192
|
+
databasePath: string,
|
|
193
|
+
expected: ConfigGeneration,
|
|
194
|
+
): ConfigGenerationBump {
|
|
195
|
+
try {
|
|
196
|
+
return runGenerationTransaction(databasePath, database => (
|
|
197
|
+
bumpConfigGenerationInTransaction(database, expected)
|
|
198
|
+
));
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return unavailable(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parent half of the history unit: derive the operation, dispatch the
|
|
3
|
+
* Worker, and never let its failure become the caller's stall.
|
|
4
|
+
*
|
|
5
|
+
* The operation is DERIVED here from what the caller already decided — its
|
|
6
|
+
* config and the direction its native mutation just took — and then handed down
|
|
7
|
+
* as a fixed value. It is not a request field the Worker trusts, because the
|
|
8
|
+
* distinctions are real: `syncResumeHistory: false` means leave history alone,
|
|
9
|
+
* apply targets opencodex only in legacy mode, and legacy recovery must not
|
|
10
|
+
* touch the manifest that generic restore consumes.
|
|
11
|
+
*
|
|
12
|
+
* Every exit is typed. A Worker that errors, dies, or overruns its watchdog
|
|
13
|
+
* produces an outcome the caller can record, because the alternative — an
|
|
14
|
+
* exception crossing back into a route that already persisted its mutation — is
|
|
15
|
+
* how a successful change gets reported as a 500.
|
|
16
|
+
*
|
|
17
|
+
* Design record: devlog/_fin/260804_codex_write_substrate/020_history_isolation.md.
|
|
18
|
+
*/
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
CodexHistoryWorkerOperation,
|
|
24
|
+
HistoryWorkerResult,
|
|
25
|
+
} from "./history-worker";
|
|
26
|
+
import { historyBackupPathFor } from "./history-provider";
|
|
27
|
+
import type { CodexHistoryFailureReason, CodexHistoryVerifiedNoopProof } from "./history-provider";
|
|
28
|
+
import { getCodexHome, resolveCodexStateDbPath } from "./paths";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the paths a history job needs, at CALL time.
|
|
32
|
+
*
|
|
33
|
+
* The SQLite root can differ from CODEX_HOME and both environment/config inputs
|
|
34
|
+
* can change between invocations. The parent resolves one exact target and hands
|
|
35
|
+
* those canonical paths to the Worker rather than asking the Worker to infer a
|
|
36
|
+
* possibly different environment.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveCodexHistoryJobTarget(): {
|
|
39
|
+
readonly canonicalCodexHome: string;
|
|
40
|
+
readonly canonicalStateDbPath: string;
|
|
41
|
+
readonly canonicalBackupPath: string;
|
|
42
|
+
} {
|
|
43
|
+
const home = getCodexHome();
|
|
44
|
+
const stateDb = resolveCodexStateDbPath({ codexHome: home });
|
|
45
|
+
return {
|
|
46
|
+
canonicalCodexHome: home,
|
|
47
|
+
canonicalStateDbPath: stateDb,
|
|
48
|
+
// Derived by the provider's own rule rather than guessed: the manifest lives
|
|
49
|
+
// in the config directory under a hash of the state database, so a
|
|
50
|
+
// hand-built path would address a different file entirely.
|
|
51
|
+
canonicalBackupPath: historyBackupPathFor(stateDb),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** How long a history unit may run before the parent stops waiting on it. */
|
|
56
|
+
const WORKER_TIMEOUT_MS = 30_000;
|
|
57
|
+
/** A terminated Worker must close before its caller can cross the file boundary. */
|
|
58
|
+
const WORKER_CLOSE_TIMEOUT_MS = 5_000;
|
|
59
|
+
|
|
60
|
+
function historyWorkerOsJoinSettleMs(platform = process.platform): number {
|
|
61
|
+
if (platform === "win32") return 1_500;
|
|
62
|
+
if (platform === "darwin" || platform === "linux") return 250;
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function terminateAndJoinHistoryWorker(
|
|
67
|
+
worker: Worker,
|
|
68
|
+
closed: Promise<void>,
|
|
69
|
+
): Promise<boolean> {
|
|
70
|
+
let timedOut = false;
|
|
71
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
72
|
+
const closeTimeout = new Promise<void>(resolve => {
|
|
73
|
+
timeout = setTimeout(() => {
|
|
74
|
+
timedOut = true;
|
|
75
|
+
resolve();
|
|
76
|
+
}, WORKER_CLOSE_TIMEOUT_MS);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
worker.terminate();
|
|
81
|
+
} catch {
|
|
82
|
+
// A Worker that already closed still resolves through the close listener.
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await Promise.race([closed, closeTimeout]);
|
|
87
|
+
} finally {
|
|
88
|
+
if (timeout) clearTimeout(timeout);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const settleMs = historyWorkerOsJoinSettleMs();
|
|
92
|
+
if (settleMs > 0) {
|
|
93
|
+
await Bun.sleep(0);
|
|
94
|
+
await Bun.sleep(settleMs);
|
|
95
|
+
}
|
|
96
|
+
return !timedOut;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CodexHistoryJobRequest {
|
|
100
|
+
readonly canonicalCodexHome: string;
|
|
101
|
+
readonly canonicalStateDbPath: string;
|
|
102
|
+
readonly canonicalBackupPath: string;
|
|
103
|
+
readonly operation: CodexHistoryWorkerOperation;
|
|
104
|
+
readonly expectedDesiredEnabled?: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type CodexHistoryJobOutcome =
|
|
108
|
+
| { readonly kind: "converged"; readonly rows: number; readonly files: number; readonly proof?: CodexHistoryVerifiedNoopProof }
|
|
109
|
+
| { readonly kind: "skipped" }
|
|
110
|
+
| { readonly kind: "blocked"; readonly reason: "busy" | "database" | "unsafe-path" | "desired_disabled" | "desired_enabled" }
|
|
111
|
+
| { readonly kind: "failed"; readonly reason: "worker-error" | "worker-died" | "timeout";
|
|
112
|
+
readonly message: string; readonly historyFailureReason?: CodexHistoryFailureReason };
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Derive the durable history operation from admitted intent.
|
|
116
|
+
*
|
|
117
|
+
* `resumeHistory === false` is the user's explicit opt-out and outranks the
|
|
118
|
+
* direction entirely — an apply that quietly migrated history anyway would be
|
|
119
|
+
* the setting failing silently. `legacyMode` is the only case that routes
|
|
120
|
+
* history TO opencodex; the ordinary apply migrates to native so a later restore
|
|
121
|
+
* has nothing to undo.
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* Test seam: the boundary suite exercises the validator without standing up a
|
|
125
|
+
* Worker whose malformed message it cannot easily emit from inside a test.
|
|
126
|
+
*/
|
|
127
|
+
export function isPlausibleWorkerResultForTests(
|
|
128
|
+
message: Record<string, unknown>,
|
|
129
|
+
requestId: string,
|
|
130
|
+
jobId: string,
|
|
131
|
+
target?: Pick<CodexHistoryJobRequest, "canonicalStateDbPath" | "canonicalBackupPath" | "operation">,
|
|
132
|
+
): boolean {
|
|
133
|
+
return isPlausibleWorkerResult(message, requestId, jobId, target);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Reject a message that names a recognized type but lacks its payload.
|
|
138
|
+
*
|
|
139
|
+
* Without this, `{requestId, type:"done"}` reached an unchecked cast and read
|
|
140
|
+
* as `converged` with undefined fields — a success report for work that may not
|
|
141
|
+
* have happened. Each type is checked for the fields it actually carries, and
|
|
142
|
+
* the ids are matched against the request in flight, not merely present.
|
|
143
|
+
*/
|
|
144
|
+
function isPlausibleWorkerResult(
|
|
145
|
+
message: Record<string, unknown>,
|
|
146
|
+
requestId: string,
|
|
147
|
+
jobId: string,
|
|
148
|
+
target?: Pick<CodexHistoryJobRequest, "canonicalStateDbPath" | "canonicalBackupPath" | "operation">,
|
|
149
|
+
): boolean {
|
|
150
|
+
if (message.requestId !== requestId || message.jobId !== jobId) return false;
|
|
151
|
+
switch (message.type) {
|
|
152
|
+
case "done":
|
|
153
|
+
if (!((message.outcome === "converged" || message.outcome === "skipped")
|
|
154
|
+
&& typeof message.rows === "number"
|
|
155
|
+
&& typeof message.files === "number")) return false;
|
|
156
|
+
if (message.proof === undefined) return true;
|
|
157
|
+
if (!target || !message.proof || typeof message.proof !== "object" || Array.isArray(message.proof)) return false;
|
|
158
|
+
{
|
|
159
|
+
const proof = message.proof as Record<string, unknown>;
|
|
160
|
+
return target.operation === "migrate-openai"
|
|
161
|
+
&& message.outcome === "converged"
|
|
162
|
+
&& message.rows === 0
|
|
163
|
+
&& message.files === 0
|
|
164
|
+
&& proof.kind === "verified-noop"
|
|
165
|
+
&& proof.pendingRows === 0
|
|
166
|
+
&& proof.backupEntries === 0
|
|
167
|
+
&& proof.canonicalStateDbPath === target.canonicalStateDbPath
|
|
168
|
+
&& proof.stateDbPresent === true
|
|
169
|
+
&& proof.canonicalBackupPath === target.canonicalBackupPath
|
|
170
|
+
&& typeof proof.backupPresent === "boolean";
|
|
171
|
+
}
|
|
172
|
+
case "blocked":
|
|
173
|
+
return message.reason === "busy" || message.reason === "database" || message.reason === "unsafe-path"
|
|
174
|
+
|| message.reason === "desired_disabled" || message.reason === "desired_enabled";
|
|
175
|
+
case "error":
|
|
176
|
+
return typeof message.message === "string"
|
|
177
|
+
&& (message.reason === undefined || message.reason === "busy" || message.reason === "permission");
|
|
178
|
+
default:
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function deriveCodexHistoryOperation(intent: {
|
|
184
|
+
readonly direction: "apply" | "restore";
|
|
185
|
+
readonly resumeHistory: boolean;
|
|
186
|
+
readonly legacyMode: boolean;
|
|
187
|
+
}): CodexHistoryWorkerOperation {
|
|
188
|
+
if (!intent.resumeHistory) return "skip";
|
|
189
|
+
if (intent.direction === "restore") return "restore-openai";
|
|
190
|
+
return intent.legacyMode ? "apply-opencodex" : "migrate-openai";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* The honest failure clause for one history job outcome.
|
|
195
|
+
*
|
|
196
|
+
* The caller adds its own framing ("sync SKIPPED", "could NOT be restored").
|
|
197
|
+
* The point of the surface argument is that a genuine lock keeps today's
|
|
198
|
+
* actionable wording, while every other reason stops blaming the Codex app:
|
|
199
|
+
* an unsafe-path refusal, an unavailable coordinator database, a permission
|
|
200
|
+
* denial, or a dead worker is a different problem with a different remedy.
|
|
201
|
+
*/
|
|
202
|
+
export function describeHistoryJobFailure(
|
|
203
|
+
outcome: CodexHistoryJobOutcome,
|
|
204
|
+
surface: "apply" | "restore" | "recover-legacy",
|
|
205
|
+
legacyMode = false,
|
|
206
|
+
): string {
|
|
207
|
+
// Callers only invoke this after observing a failure flag, but that flag is
|
|
208
|
+
// derived from "not converged", which also covers "skipped". Naming those
|
|
209
|
+
// two kinds keeps a widened or miscast call site from printing `undefined`.
|
|
210
|
+
if (outcome.kind === "skipped") {
|
|
211
|
+
return "the history operation was skipped; no failure was recorded.";
|
|
212
|
+
}
|
|
213
|
+
if (outcome.kind === "converged") {
|
|
214
|
+
return "the history job reported no failure; run 'ocx doctor' if this is unexpected.";
|
|
215
|
+
}
|
|
216
|
+
// A busy database reaches here two ways: the lock itself was contended
|
|
217
|
+
// (blocked/busy), or the lock was acquired and the worker then found SQLite
|
|
218
|
+
// busy (failed with historyFailureReason "busy"). Both are the same user
|
|
219
|
+
// situation and deserve the same surface-specific guidance.
|
|
220
|
+
const busyText = surface === "apply"
|
|
221
|
+
? legacyMode
|
|
222
|
+
? "the history DB is locked (Codex app/IDE open?). Close it and rerun 'ocx start'."
|
|
223
|
+
: "the history DB is locked (Codex app/IDE open?). It is retried automatically (while the proxy runs and on every 'ocx start'); to force it now, close the Codex app and run 'ocx sync'."
|
|
224
|
+
: surface === "recover-legacy"
|
|
225
|
+
? "the Codex history DB is locked (Codex app/IDE open?). Close it and rerun this command."
|
|
226
|
+
: "the Codex app appears to be holding the history database. Close Codex and run `ocx restore` again.";
|
|
227
|
+
if (outcome.kind === "blocked") {
|
|
228
|
+
if (outcome.reason === "busy") return busyText;
|
|
229
|
+
switch (outcome.reason) {
|
|
230
|
+
case "unsafe-path":
|
|
231
|
+
return "opencodex refused its history lock path (unsafe coordinator namespace); this is not a Codex app lock. Run 'ocx doctor' and check the opencodex runtime directory.";
|
|
232
|
+
case "database":
|
|
233
|
+
return "the history coordinator database is unavailable; this is not a Codex app lock. Run 'ocx doctor'.";
|
|
234
|
+
case "desired_disabled":
|
|
235
|
+
return "Codex integration is disabled, so the history operation was skipped.";
|
|
236
|
+
case "desired_enabled":
|
|
237
|
+
return "Codex integration is enabled, so the history operation was skipped.";
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (outcome.historyFailureReason === "busy") return busyText;
|
|
241
|
+
if (outcome.historyFailureReason === "permission") {
|
|
242
|
+
return "permission was denied while writing Codex history; this is not a Codex app lock. Run 'ocx doctor'.";
|
|
243
|
+
}
|
|
244
|
+
switch (outcome.reason) {
|
|
245
|
+
case "worker-error":
|
|
246
|
+
return `the history worker failed (${outcome.message}). Run 'ocx doctor'.`;
|
|
247
|
+
case "worker-died":
|
|
248
|
+
return "the history worker exited unexpectedly; this is not a Codex app lock. Run 'ocx doctor'.";
|
|
249
|
+
case "timeout":
|
|
250
|
+
return "the history worker timed out; this is not a Codex app lock. Run 'ocx doctor'.";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Worker exceptions travel into user-facing CLI output, and a raw filesystem
|
|
256
|
+
* error carries absolute paths — on every platform that includes the account
|
|
257
|
+
* name (`/Users/x`, `/home/x`, `C:\Users\x`). Folding the home directory to
|
|
258
|
+
* `~` keeps the diagnostic value and drops the identifier.
|
|
259
|
+
*/
|
|
260
|
+
function redactWorkerMessage(message: string): string {
|
|
261
|
+
const home = homedir();
|
|
262
|
+
if (home.length <= 1) return message;
|
|
263
|
+
// Windows spellings vary in case and separator; an exact match would leave
|
|
264
|
+
// the account name in the message.
|
|
265
|
+
if (process.platform === "win32") {
|
|
266
|
+
const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\\/g, "[\\\\/]");
|
|
267
|
+
return message.replace(new RegExp(escaped, "gi"), "~");
|
|
268
|
+
}
|
|
269
|
+
return message.split(home).join("~");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function classifyWorkerResult(result: HistoryWorkerResult): CodexHistoryJobOutcome {
|
|
273
|
+
if (result.type === "blocked") return { kind: "blocked", reason: result.reason };
|
|
274
|
+
if (result.type === "error") {
|
|
275
|
+
return {
|
|
276
|
+
kind: "failed",
|
|
277
|
+
reason: "worker-error",
|
|
278
|
+
message: redactWorkerMessage(result.message),
|
|
279
|
+
...(result.reason ? { historyFailureReason: result.reason } : {}),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
return result.outcome === "skipped"
|
|
283
|
+
? { kind: "skipped" }
|
|
284
|
+
: { kind: "converged", rows: result.rows, files: result.files, ...(result.proof ? { proof: result.proof } : {}) };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Run one history unit in a Worker and join it before returning.
|
|
289
|
+
*
|
|
290
|
+
* The join is not optional politeness: returning while the thread may still be
|
|
291
|
+
* mutating CODEX_HOME would let a caller's next step observe a half-applied
|
|
292
|
+
* transition, and would let a test suite reach its next file with a worker still
|
|
293
|
+
* exiting behind it.
|
|
294
|
+
*/
|
|
295
|
+
export async function runCodexHistoryJob(
|
|
296
|
+
request: CodexHistoryJobRequest,
|
|
297
|
+
options: { readonly timeoutMs?: number } = {},
|
|
298
|
+
): Promise<CodexHistoryJobOutcome> {
|
|
299
|
+
const requestId = randomUUID();
|
|
300
|
+
const jobId = randomUUID();
|
|
301
|
+
const timeoutMs = options.timeoutMs ?? WORKER_TIMEOUT_MS;
|
|
302
|
+
|
|
303
|
+
// `skip` writes nothing, so spawning a thread to decide that would be pure
|
|
304
|
+
// cost. It still returns a recorded outcome rather than silence.
|
|
305
|
+
if (request.operation === "skip") return { kind: "skipped" };
|
|
306
|
+
|
|
307
|
+
let worker: Worker;
|
|
308
|
+
try {
|
|
309
|
+
worker = new Worker(new URL("./history-worker.ts", import.meta.url).href);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
return {
|
|
312
|
+
kind: "failed",
|
|
313
|
+
reason: "worker-died",
|
|
314
|
+
message: error instanceof Error ? error.message : "history_worker_spawn_failed",
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return new Promise<CodexHistoryJobOutcome>(resolve => {
|
|
319
|
+
let settled = false;
|
|
320
|
+
let resolveWorkerClosed!: () => void;
|
|
321
|
+
const workerClosed = new Promise<void>(closed => {
|
|
322
|
+
resolveWorkerClosed = closed;
|
|
323
|
+
});
|
|
324
|
+
const finish = (outcome: CodexHistoryJobOutcome) => {
|
|
325
|
+
if (settled) return;
|
|
326
|
+
settled = true;
|
|
327
|
+
clearTimeout(timer);
|
|
328
|
+
// Bun 1.3.14 returns void from terminate(); resolving on that return lets
|
|
329
|
+
// the Worker outlive this job and, under `bun test --isolate`, its file.
|
|
330
|
+
void terminateAndJoinHistoryWorker(worker, workerClosed).then(joined => {
|
|
331
|
+
resolve(joined
|
|
332
|
+
? outcome
|
|
333
|
+
: { kind: "failed", reason: "worker-died", message: "history_worker_close_timeout" });
|
|
334
|
+
}, () => {
|
|
335
|
+
resolve({ kind: "failed", reason: "worker-died", message: "history_worker_join_failed" });
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const timer = setTimeout(() => {
|
|
340
|
+
finish({ kind: "failed", reason: "timeout", message: "history_worker_timeout" });
|
|
341
|
+
}, timeoutMs);
|
|
342
|
+
|
|
343
|
+
const died = (detail: string): void => {
|
|
344
|
+
finish({ kind: "failed", reason: "worker-died", message: detail });
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
worker.onmessage = (event: MessageEvent<unknown>) => {
|
|
348
|
+
const data = event.data;
|
|
349
|
+
if (!data || typeof data !== "object") {
|
|
350
|
+
// Not the shape at all: this is a death signal, not silence. Ignoring it
|
|
351
|
+
// would let the watchdog call a dead Worker a timeout.
|
|
352
|
+
died("history_worker_malformed_message");
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const message = data as Record<string, unknown>;
|
|
356
|
+
// A reply for a different request is somebody else's; ignoring it is not
|
|
357
|
+
// the same as accepting it.
|
|
358
|
+
if (message.requestId !== requestId) return;
|
|
359
|
+
if (message.type !== "done" && message.type !== "blocked" && message.type !== "error") {
|
|
360
|
+
died("history_worker_unknown_message_type");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (!isPlausibleWorkerResult(message, requestId, jobId, request)) {
|
|
364
|
+
// A recognized type with a missing payload read as `converged` with
|
|
365
|
+
// undefined fields once — success for work that may not have happened.
|
|
366
|
+
died("history_worker_malformed_payload");
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
finish(classifyWorkerResult(message as unknown as HistoryWorkerResult));
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
/*
|
|
373
|
+
* A Worker that exits early — without erroring — is not an error event, so
|
|
374
|
+
* without these it surfaced as `timeout` after the full wait. Both are death
|
|
375
|
+
* signals. Neither can overturn a settled success: `finish` is idempotent,
|
|
376
|
+
* and the Worker always closes after posting its result.
|
|
377
|
+
*/
|
|
378
|
+
worker.addEventListener("messageerror", () => died("history_worker_unserializable_message"));
|
|
379
|
+
worker.addEventListener("close", () => {
|
|
380
|
+
resolveWorkerClosed();
|
|
381
|
+
died("history_worker_closed_early");
|
|
382
|
+
}, { once: true });
|
|
383
|
+
|
|
384
|
+
worker.onerror = (event: ErrorEvent) => {
|
|
385
|
+
finish({
|
|
386
|
+
kind: "failed",
|
|
387
|
+
reason: "worker-died",
|
|
388
|
+
message: event.message || "history_worker_failed",
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
worker.postMessage({
|
|
393
|
+
type: "run",
|
|
394
|
+
requestId,
|
|
395
|
+
jobId,
|
|
396
|
+
operation: request.operation,
|
|
397
|
+
canonicalCodexHome: request.canonicalCodexHome,
|
|
398
|
+
canonicalStateDbPath: request.canonicalStateDbPath,
|
|
399
|
+
canonicalBackupPath: request.canonicalBackupPath,
|
|
400
|
+
...(request.expectedDesiredEnabled === undefined ? {} : { expectedDesiredEnabled: request.expectedDesiredEnabled }),
|
|
401
|
+
env: {
|
|
402
|
+
...(process.env.CODEX_HOME ? { CODEX_HOME: process.env.CODEX_HOME } : {}),
|
|
403
|
+
...(process.env.OPENCODEX_HOME ? { OPENCODEX_HOME: process.env.OPENCODEX_HOME } : {}),
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
}
|