@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
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
3
|
+
import type { OcxConfig } from "../types";
|
|
4
|
+
import { isSelectableCodexPoolAccount } from "./account-id";
|
|
5
|
+
import { getValidCodexToken, readCodexAccountRecord } from "./account-store";
|
|
6
|
+
import { getMainAccountToken, MAIN_CODEX_ACCOUNT_ID } from "./main-account";
|
|
7
|
+
import { ACCOUNT_GATED_NATIVE_OPENAI_MODELS } from "./catalog/native-models";
|
|
8
|
+
|
|
9
|
+
const CODEX_MODELS_URL = "https://chatgpt.com/backend-api/codex/models?client_version=0.0.0";
|
|
10
|
+
const MODEL_ROSTER_TTL_MS = 5 * 60_000;
|
|
11
|
+
const MODEL_ROSTER_FAILURE_TTL_MS = 15_000;
|
|
12
|
+
const MODEL_ROSTER_TIMEOUT_MS = 8_000;
|
|
13
|
+
const MODEL_ROSTER_MAX_BYTES = 2 * 1024 * 1024;
|
|
14
|
+
const MODEL_ROSTER_CACHE_MAX = 64;
|
|
15
|
+
const DIRECT_CALLER_ACCOUNT_PREFIX = "__direct_codex__:";
|
|
16
|
+
|
|
17
|
+
export interface CodexModelEntitlementCredentialSnapshot {
|
|
18
|
+
readonly accountId: string;
|
|
19
|
+
readonly accessToken: string;
|
|
20
|
+
readonly chatgptAccountId: string;
|
|
21
|
+
/** Stable local identity for rejecting a catalog commit after credential replacement. */
|
|
22
|
+
readonly credentialIdentity: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface CachedAccountModels {
|
|
26
|
+
readonly credentialIdentity: string;
|
|
27
|
+
readonly expiresAt: number;
|
|
28
|
+
readonly models: ReadonlySet<string>;
|
|
29
|
+
readonly confirmed: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CodexModelEntitlementSnapshot {
|
|
33
|
+
readonly modelsByAccount: ReadonlyMap<string, ReadonlySet<string>>;
|
|
34
|
+
readonly confirmedAccountIds: ReadonlySet<string>;
|
|
35
|
+
readonly credentialIdentities: ReadonlyMap<string, string>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CodexModelEntitlementResolveOptions {
|
|
39
|
+
readonly fetcher?: typeof fetch;
|
|
40
|
+
readonly now?: number;
|
|
41
|
+
/** Test-only credential seam; production callers enumerate local main + Pool credentials. */
|
|
42
|
+
readonly credentials?: readonly CodexModelEntitlementCredentialSnapshot[];
|
|
43
|
+
/** Test-only seam for proving lifecycle exclusions happen before credential reads. */
|
|
44
|
+
readonly credentialSnapshot?: typeof accountCredentialSnapshot;
|
|
45
|
+
/** Accounts whose credentials must not be read while another lifecycle owns them. */
|
|
46
|
+
readonly excludeAccountIds?: ReadonlySet<string>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const accountModelsCache = new Map<string, CachedAccountModels>();
|
|
50
|
+
const accountModelsFlights = new Map<string, Promise<CachedAccountModels>>();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Direct-caller entries are evicted separately from main/Pool entries.
|
|
54
|
+
*
|
|
55
|
+
* Direct keys are per-credential (`__direct_codex__:<hash>`) and unbounded in practice, while
|
|
56
|
+
* main/Pool keys are the evidence the CATALOG projects from. Sharing one LRU let 64 distinct
|
|
57
|
+
* Direct callers evict `__main__` and the Pool accounts, which makes the gated row vanish from
|
|
58
|
+
* the catalog until rediscovery — fail-closed flapping rather than a leak, but still a visible
|
|
59
|
+
* model disappearing for a reason the operator cannot see. Two budgets keep one class of caller
|
|
60
|
+
* from erasing the other's evidence.
|
|
61
|
+
*/
|
|
62
|
+
function boundedCacheSet(accountId: string, value: CachedAccountModels): void {
|
|
63
|
+
accountModelsCache.delete(accountId);
|
|
64
|
+
accountModelsCache.set(accountId, value);
|
|
65
|
+
const isDirect = (key: string): boolean => key.startsWith(DIRECT_CALLER_ACCOUNT_PREFIX);
|
|
66
|
+
const evictClass = (direct: boolean): void => {
|
|
67
|
+
let count = 0;
|
|
68
|
+
for (const key of accountModelsCache.keys()) if (isDirect(key) === direct) count += 1;
|
|
69
|
+
while (count > MODEL_ROSTER_CACHE_MAX) {
|
|
70
|
+
let oldest: string | undefined;
|
|
71
|
+
for (const key of accountModelsCache.keys()) {
|
|
72
|
+
if (isDirect(key) === direct) { oldest = key; break; }
|
|
73
|
+
}
|
|
74
|
+
if (oldest === undefined) break;
|
|
75
|
+
accountModelsCache.delete(oldest);
|
|
76
|
+
count -= 1;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
evictClass(isDirect(accountId));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function currentCredentialIdentity(accountId: string): string | undefined {
|
|
83
|
+
if (accountId.startsWith(DIRECT_CALLER_ACCOUNT_PREFIX)) {
|
|
84
|
+
return `direct:${accountId.slice(DIRECT_CALLER_ACCOUNT_PREFIX.length)}`;
|
|
85
|
+
}
|
|
86
|
+
if (accountId === MAIN_CODEX_ACCOUNT_ID) {
|
|
87
|
+
const token = getMainAccountToken();
|
|
88
|
+
return token ? `main:${token.chatgptAccountId}` : undefined;
|
|
89
|
+
}
|
|
90
|
+
const record = readCodexAccountRecord(accountId);
|
|
91
|
+
if (!record?.credential || record.deletedAt != null) return undefined;
|
|
92
|
+
return `pool:${record.generation}:${record.credential.chatgptAccountId}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function accountCredentialSnapshot(accountId: string): Promise<CodexModelEntitlementCredentialSnapshot | null> {
|
|
96
|
+
if (accountId === MAIN_CODEX_ACCOUNT_ID) {
|
|
97
|
+
const token = getMainAccountToken();
|
|
98
|
+
return token
|
|
99
|
+
? {
|
|
100
|
+
accountId,
|
|
101
|
+
accessToken: token.accessToken,
|
|
102
|
+
chatgptAccountId: token.chatgptAccountId,
|
|
103
|
+
credentialIdentity: `main:${token.chatgptAccountId}`,
|
|
104
|
+
}
|
|
105
|
+
: null;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const token = await getValidCodexToken(accountId);
|
|
109
|
+
return {
|
|
110
|
+
accountId,
|
|
111
|
+
accessToken: token.accessToken,
|
|
112
|
+
chatgptAccountId: token.chatgptAccountId,
|
|
113
|
+
credentialIdentity: `pool:${token.generation}:${token.chatgptAccountId}`,
|
|
114
|
+
};
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseAccountModels(text: string): ReadonlySet<string> | null {
|
|
121
|
+
try {
|
|
122
|
+
const payload = JSON.parse(text) as { models?: unknown };
|
|
123
|
+
if (!Array.isArray(payload.models)) return null;
|
|
124
|
+
const models = payload.models.flatMap(entry => {
|
|
125
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
126
|
+
const row = entry as { slug?: unknown; supported_in_api?: unknown; visibility?: unknown };
|
|
127
|
+
if (typeof row.slug !== "string" || row.supported_in_api !== true || row.visibility === "hide") return [];
|
|
128
|
+
return [row.slug];
|
|
129
|
+
});
|
|
130
|
+
return new Set(models);
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function fetchAccountModels(
|
|
137
|
+
credential: CodexModelEntitlementCredentialSnapshot,
|
|
138
|
+
fetcher: typeof fetch,
|
|
139
|
+
now: number,
|
|
140
|
+
): Promise<CachedAccountModels> {
|
|
141
|
+
const controller = new AbortController();
|
|
142
|
+
const timer = setTimeout(() => controller.abort(new DOMException("Codex model discovery timed out", "TimeoutError")), MODEL_ROSTER_TIMEOUT_MS);
|
|
143
|
+
try {
|
|
144
|
+
const headers = new Headers({
|
|
145
|
+
Authorization: `Bearer ${credential.accessToken}`,
|
|
146
|
+
Accept: "application/json",
|
|
147
|
+
});
|
|
148
|
+
if (credential.chatgptAccountId) headers.set("ChatGPT-Account-Id", credential.chatgptAccountId);
|
|
149
|
+
const response = await fetcher(CODEX_MODELS_URL, {
|
|
150
|
+
headers,
|
|
151
|
+
redirect: "error",
|
|
152
|
+
signal: controller.signal,
|
|
153
|
+
});
|
|
154
|
+
const body = await readBoundedResponseBody(response, {
|
|
155
|
+
signal: controller.signal,
|
|
156
|
+
maxBytes: MODEL_ROSTER_MAX_BYTES,
|
|
157
|
+
fatalUtf8: true,
|
|
158
|
+
});
|
|
159
|
+
const models = response.ok && body.displaySafe && !body.truncated
|
|
160
|
+
? parseAccountModels(body.text)
|
|
161
|
+
: null;
|
|
162
|
+
return {
|
|
163
|
+
credentialIdentity: credential.credentialIdentity,
|
|
164
|
+
expiresAt: now + (models ? MODEL_ROSTER_TTL_MS : MODEL_ROSTER_FAILURE_TTL_MS),
|
|
165
|
+
models: models ?? new Set(),
|
|
166
|
+
confirmed: models !== null,
|
|
167
|
+
};
|
|
168
|
+
} catch {
|
|
169
|
+
return {
|
|
170
|
+
credentialIdentity: credential.credentialIdentity,
|
|
171
|
+
expiresAt: now + MODEL_ROSTER_FAILURE_TTL_MS,
|
|
172
|
+
models: new Set(),
|
|
173
|
+
confirmed: false,
|
|
174
|
+
};
|
|
175
|
+
} finally {
|
|
176
|
+
clearTimeout(timer);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function directCallerCredential(headers: Headers): CodexModelEntitlementCredentialSnapshot | null {
|
|
181
|
+
const match = /^Bearer\s+(\S+)$/i.exec(headers.get("authorization")?.trim() ?? "");
|
|
182
|
+
if (!match) return null;
|
|
183
|
+
const accessToken = match[1]!;
|
|
184
|
+
const chatgptAccountId = headers.get("chatgpt-account-id")?.trim() ?? "";
|
|
185
|
+
const fingerprint = createHash("sha256")
|
|
186
|
+
.update(accessToken)
|
|
187
|
+
.update("\0")
|
|
188
|
+
.update(chatgptAccountId)
|
|
189
|
+
.digest("hex");
|
|
190
|
+
return {
|
|
191
|
+
accountId: `${DIRECT_CALLER_ACCOUNT_PREFIX}${fingerprint}`,
|
|
192
|
+
accessToken,
|
|
193
|
+
chatgptAccountId,
|
|
194
|
+
credentialIdentity: `direct:${fingerprint}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function modelsForCredential(
|
|
199
|
+
credential: CodexModelEntitlementCredentialSnapshot,
|
|
200
|
+
fetcher: typeof fetch,
|
|
201
|
+
now: number,
|
|
202
|
+
): Promise<CachedAccountModels> {
|
|
203
|
+
const cached = accountModelsCache.get(credential.accountId);
|
|
204
|
+
if (
|
|
205
|
+
cached
|
|
206
|
+
&& cached.credentialIdentity === credential.credentialIdentity
|
|
207
|
+
&& cached.expiresAt > now
|
|
208
|
+
) return cached;
|
|
209
|
+
|
|
210
|
+
const flightKey = `${credential.accountId}\u0000${credential.credentialIdentity}`;
|
|
211
|
+
const existing = accountModelsFlights.get(flightKey);
|
|
212
|
+
if (existing) return existing;
|
|
213
|
+
const flight = fetchAccountModels(credential, fetcher, now)
|
|
214
|
+
.then(result => {
|
|
215
|
+
if (currentCredentialIdentity(credential.accountId) === credential.credentialIdentity) {
|
|
216
|
+
boundedCacheSet(credential.accountId, result);
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
})
|
|
220
|
+
.finally(() => {
|
|
221
|
+
if (accountModelsFlights.get(flightKey) === flight) accountModelsFlights.delete(flightKey);
|
|
222
|
+
});
|
|
223
|
+
accountModelsFlights.set(flightKey, flight);
|
|
224
|
+
return flight;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function candidateAccountIds(config: Pick<OcxConfig, "codexAccounts">): string[] {
|
|
228
|
+
return [
|
|
229
|
+
MAIN_CODEX_ACCOUNT_ID,
|
|
230
|
+
...(config.codexAccounts ?? [])
|
|
231
|
+
.filter(isSelectableCodexPoolAccount)
|
|
232
|
+
.map(account => account.id),
|
|
233
|
+
];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Fetch the authenticated model roster for every locally usable Codex account.
|
|
238
|
+
*
|
|
239
|
+
* [Decision Log]
|
|
240
|
+
* - 목적과 의도: Account-gated native models must be advertised and selected only for accounts
|
|
241
|
+
* whose own authenticated upstream catalog confirms the model.
|
|
242
|
+
* - 기존 구현 및 제약 조건: The injected Codex catalog is static, while Pool may contain
|
|
243
|
+
* accounts with different entitlements. A global allowlist therefore exposed unusable rows.
|
|
244
|
+
* - 검토한 주요 대안: Infer access from plan labels, learn only after a failed prompt, or rewrite
|
|
245
|
+
* Daybreak to its current physical model.
|
|
246
|
+
* - 선택한 방식: Cache bounded authenticated `/models` rosters per credential generation and
|
|
247
|
+
* fail closed for unconfirmed accounts.
|
|
248
|
+
* - 다른 대안 대신 이 방식을 선택한 이유: Plan names do not prove grants, post-failure
|
|
249
|
+
* learning spends a real turn, and model rewriting changes the requested product identity.
|
|
250
|
+
* - 장점, 단점 및 영향: Catalog and routing share exact account evidence. Cold gated requests
|
|
251
|
+
* pay one bounded discovery call per account; discovery failure temporarily hides the gated row.
|
|
252
|
+
*/
|
|
253
|
+
export async function resolveCodexModelEntitlements(
|
|
254
|
+
config: Pick<OcxConfig, "codexAccounts">,
|
|
255
|
+
options: CodexModelEntitlementResolveOptions = {},
|
|
256
|
+
): Promise<CodexModelEntitlementSnapshot> {
|
|
257
|
+
const now = options.now ?? Date.now();
|
|
258
|
+
const fetcher = options.fetcher ?? fetch;
|
|
259
|
+
const allowedAccountIds = candidateAccountIds(config)
|
|
260
|
+
.filter(accountId => !options.excludeAccountIds?.has(accountId));
|
|
261
|
+
const credentialSnapshot = options.credentialSnapshot ?? accountCredentialSnapshot;
|
|
262
|
+
const credentials = options.credentials
|
|
263
|
+
? [...options.credentials].filter(credential => !options.excludeAccountIds?.has(credential.accountId))
|
|
264
|
+
: (await Promise.all(allowedAccountIds.map(credentialSnapshot)))
|
|
265
|
+
.filter((value): value is CodexModelEntitlementCredentialSnapshot => value !== null);
|
|
266
|
+
const results = await Promise.all(credentials.map(async credential => ({
|
|
267
|
+
credential,
|
|
268
|
+
result: await modelsForCredential(credential, fetcher, now),
|
|
269
|
+
})));
|
|
270
|
+
return {
|
|
271
|
+
modelsByAccount: new Map(results.map(({ credential, result }) => [credential.accountId, result.models])),
|
|
272
|
+
confirmedAccountIds: new Set(results.flatMap(({ credential, result }) => result.confirmed ? [credential.accountId] : [])),
|
|
273
|
+
credentialIdentities: new Map(results.map(({ credential }) => [credential.accountId, credential.credentialIdentity])),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Fail-closed entitlement check for a Direct request's own forwarded ChatGPT credential. */
|
|
278
|
+
export async function isDirectCallerEntitledToCodexModel(
|
|
279
|
+
headers: Headers,
|
|
280
|
+
modelId: string,
|
|
281
|
+
options: Pick<CodexModelEntitlementResolveOptions, "fetcher" | "now"> = {},
|
|
282
|
+
): Promise<boolean> {
|
|
283
|
+
if (!ACCOUNT_GATED_NATIVE_OPENAI_MODELS.has(modelId)) return true;
|
|
284
|
+
const credential = directCallerCredential(headers);
|
|
285
|
+
if (!credential) return false;
|
|
286
|
+
const result = await modelsForCredential(
|
|
287
|
+
credential,
|
|
288
|
+
options.fetcher ?? fetch,
|
|
289
|
+
options.now ?? Date.now(),
|
|
290
|
+
);
|
|
291
|
+
return result.confirmed && result.models.has(modelId);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function entitledCodexAccountIdsForModel(
|
|
295
|
+
snapshot: CodexModelEntitlementSnapshot,
|
|
296
|
+
modelId: string | undefined,
|
|
297
|
+
): ReadonlySet<string> | undefined {
|
|
298
|
+
if (!modelId || !ACCOUNT_GATED_NATIVE_OPENAI_MODELS.has(modelId)) return undefined;
|
|
299
|
+
return new Set([...snapshot.modelsByAccount].flatMap(([accountId, models]) => (
|
|
300
|
+
snapshot.confirmedAccountIds.has(accountId) && models.has(modelId) ? [accountId] : []
|
|
301
|
+
)));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function availableAccountGatedNativeModels(
|
|
305
|
+
snapshot: CodexModelEntitlementSnapshot,
|
|
306
|
+
eligibleAccountIds?: ReadonlySet<string>,
|
|
307
|
+
): ReadonlySet<string> {
|
|
308
|
+
return new Set([...ACCOUNT_GATED_NATIVE_OPENAI_MODELS].filter(modelId => (
|
|
309
|
+
[...snapshot.modelsByAccount].some(([accountId, models]) => (
|
|
310
|
+
(!eligibleAccountIds || eligibleAccountIds.has(accountId))
|
|
311
|
+
&& snapshot.confirmedAccountIds.has(accountId)
|
|
312
|
+
&& models.has(modelId)
|
|
313
|
+
))
|
|
314
|
+
)));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** Synchronous projection for management/catalog readers after a discovery pass. */
|
|
318
|
+
export function cachedAvailableAccountGatedNativeModels(
|
|
319
|
+
now = Date.now(),
|
|
320
|
+
eligibleAccountIds?: ReadonlySet<string>,
|
|
321
|
+
): ReadonlySet<string> {
|
|
322
|
+
return new Set([...ACCOUNT_GATED_NATIVE_OPENAI_MODELS].filter(modelId => (
|
|
323
|
+
[...accountModelsCache].some(([accountId, entry]) => (
|
|
324
|
+
(!eligibleAccountIds || eligibleAccountIds.has(accountId))
|
|
325
|
+
&& !accountId.startsWith(DIRECT_CALLER_ACCOUNT_PREFIX)
|
|
326
|
+
&& entry.confirmed
|
|
327
|
+
&& entry.expiresAt > now
|
|
328
|
+
&& entry.models.has(modelId)
|
|
329
|
+
))
|
|
330
|
+
)));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function isCodexModelEntitlementSnapshotCurrent(snapshot: CodexModelEntitlementSnapshot): boolean {
|
|
334
|
+
for (const [accountId, identity] of snapshot.credentialIdentities) {
|
|
335
|
+
if (currentCredentialIdentity(accountId) !== identity) return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function invalidateCodexModelEntitlementsForAccount(accountId: string | null | undefined): void {
|
|
341
|
+
if (accountId) accountModelsCache.delete(accountId);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function resetCodexModelEntitlementCacheForTests(): void {
|
|
345
|
+
accountModelsCache.clear();
|
|
346
|
+
accountModelsFlights.clear();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function seedCodexModelEntitlementsForTests(
|
|
350
|
+
accountId: string,
|
|
351
|
+
models: readonly string[],
|
|
352
|
+
now = Date.now(),
|
|
353
|
+
): void {
|
|
354
|
+
boundedCacheSet(accountId, {
|
|
355
|
+
credentialIdentity: `test:${accountId}`,
|
|
356
|
+
expiresAt: now + MODEL_ROSTER_TTL_MS,
|
|
357
|
+
models: new Set(models),
|
|
358
|
+
confirmed: true,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AdmissionLease } from "../lib/admission";
|
|
2
|
+
import {
|
|
3
|
+
tryAcquireNativeMainProfileClaim as tryAcquireLifecycleNativeMainProfileClaim,
|
|
4
|
+
tryClaimNativeMainProfileForTurn as tryClaimLifecycleNativeMainProfileForTurn,
|
|
5
|
+
} from "../server/lifecycle";
|
|
6
|
+
import { isNativeMainTrafficBlocked } from "./native-profile-startup";
|
|
7
|
+
|
|
8
|
+
export interface NativeMainTurnClaimDeps {
|
|
9
|
+
/** Test seams for the synchronous precheck/claim/postcheck transition. */
|
|
10
|
+
isTrafficBlocked?: typeof isNativeMainTrafficBlocked;
|
|
11
|
+
claimTurn?: typeof tryClaimLifecycleNativeMainProfileForTurn;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Admit a physical native-main credential read on an existing turn.
|
|
16
|
+
*
|
|
17
|
+
* Lifecycle admission owns the scoped switch/shutdown fence while the startup
|
|
18
|
+
* gate owns retained journal/manual recovery. Keep their composition here so
|
|
19
|
+
* low-level server lifecycle code does not import NativeProfileManager through
|
|
20
|
+
* native-profile-startup and create a fragile dependency cycle.
|
|
21
|
+
*/
|
|
22
|
+
export function tryClaimNativeMainProfileForTurn(
|
|
23
|
+
lease?: AdmissionLease,
|
|
24
|
+
deps: NativeMainTurnClaimDeps = {},
|
|
25
|
+
): boolean {
|
|
26
|
+
const isBlocked = deps.isTrafficBlocked ?? isNativeMainTrafficBlocked;
|
|
27
|
+
const claimTurn = deps.claimTurn ?? tryClaimLifecycleNativeMainProfileForTurn;
|
|
28
|
+
if (isBlocked()) return false;
|
|
29
|
+
if (!claimTurn(lease)) return false;
|
|
30
|
+
if (!isBlocked()) return true;
|
|
31
|
+
|
|
32
|
+
// Recovery became visible between the precheck and lifecycle claim. This
|
|
33
|
+
// request must not read auth.json. Keep the caller-owned turn claimed until
|
|
34
|
+
// normal request cleanup: optional routed work may continue, and releasing
|
|
35
|
+
// the whole turn here would let shutdown/profile switching overlap it.
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Acquire standalone ownership only when both native-main gates admit work. */
|
|
40
|
+
export function tryAcquireNativeMainProfileClaim(): AdmissionLease | null {
|
|
41
|
+
if (isNativeMainTrafficBlocked()) return null;
|
|
42
|
+
const claim = tryAcquireLifecycleNativeMainProfileClaim();
|
|
43
|
+
if (!claim) return null;
|
|
44
|
+
if (!isNativeMainTrafficBlocked()) return claim;
|
|
45
|
+
claim.release();
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeSync,
|
|
3
|
+
constants as fsConstants,
|
|
4
|
+
fstatSync,
|
|
5
|
+
fsyncSync,
|
|
6
|
+
ftruncateSync,
|
|
7
|
+
lstatSync,
|
|
8
|
+
openSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
realpathSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
|
|
15
|
+
import type { NativeProfileContext } from "./native-profile-store";
|
|
16
|
+
import { NativeProfileError } from "./native-profile-types";
|
|
17
|
+
|
|
18
|
+
const AUTH_TEMP_NAME = /^auth\.json\.ocx\.[1-9]\d*\.[1-9]\d*\.tmp$/;
|
|
19
|
+
const MAX_AUTH_TEMP_RESIDUES = 128;
|
|
20
|
+
|
|
21
|
+
interface PathIdentity {
|
|
22
|
+
readonly dev: bigint;
|
|
23
|
+
readonly ino: bigint;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface NativeMainAuthTempScrubTestSeam {
|
|
27
|
+
/** Test-only race seam. Production never supplies this callback. */
|
|
28
|
+
beforeOpen?: (path: string) => void;
|
|
29
|
+
/** Test-only removal-failure seam. Production never supplies this callback. */
|
|
30
|
+
beforeUnlink?: (path: string) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface NativeMainAuthTempScrubResult {
|
|
34
|
+
scrubbed: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function cleanupRequired(): NativeProfileError {
|
|
38
|
+
return new NativeProfileError(
|
|
39
|
+
"AUTH_TEMP_CLEANUP_REQUIRED",
|
|
40
|
+
"A native-main credential write residue could not be safely removed; manual cleanup is required.",
|
|
41
|
+
500,
|
|
42
|
+
false,
|
|
43
|
+
true,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function errorCode(error: unknown): string | undefined {
|
|
48
|
+
return error && typeof error === "object" && "code" in error
|
|
49
|
+
? String((error as { code?: unknown }).code)
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sameIdentity(stats: { dev: bigint; ino: bigint }, expected: PathIdentity): boolean {
|
|
54
|
+
return stats.dev === expected.dev && stats.ino === expected.ino;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function samePath(left: string, right: string): boolean {
|
|
58
|
+
return resolve(left) === resolve(right);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function inspectCanonicalParent(path: string, expected?: PathIdentity): PathIdentity {
|
|
62
|
+
const absolute = resolve(path);
|
|
63
|
+
const entry = lstatSync(absolute, { bigint: true });
|
|
64
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) throw cleanupRequired();
|
|
65
|
+
const canonical = realpathSync.native(absolute);
|
|
66
|
+
if (!samePath(canonical, absolute)) throw cleanupRequired();
|
|
67
|
+
if (expected && !sameIdentity(entry, expected)) throw cleanupRequired();
|
|
68
|
+
return { dev: entry.dev, ino: entry.ino };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function inspectExactPath(path: string, parent: string, expected?: PathIdentity): PathIdentity {
|
|
72
|
+
const absolute = resolve(path);
|
|
73
|
+
if (!samePath(dirname(absolute), parent)) throw cleanupRequired();
|
|
74
|
+
const entry = lstatSync(absolute, { bigint: true });
|
|
75
|
+
if (!entry.isFile() || entry.isSymbolicLink() || entry.nlink !== 1n) throw cleanupRequired();
|
|
76
|
+
const canonical = realpathSync.native(absolute);
|
|
77
|
+
if (!samePath(canonical, absolute) || !samePath(dirname(canonical), parent)) throw cleanupRequired();
|
|
78
|
+
if (expected && !sameIdentity(entry, expected)) throw cleanupRequired();
|
|
79
|
+
return { dev: entry.dev, ino: entry.ino };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function exactResidues(parent: string, parentIdentity: PathIdentity): string[] {
|
|
83
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
84
|
+
const names = readdirSync(parent).filter(name => AUTH_TEMP_NAME.test(name)).sort();
|
|
85
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
86
|
+
if (names.length > MAX_AUTH_TEMP_RESIDUES) throw cleanupRequired();
|
|
87
|
+
return names;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function assertOpenIdentity(
|
|
91
|
+
fd: number,
|
|
92
|
+
path: string,
|
|
93
|
+
parent: string,
|
|
94
|
+
parentIdentity: PathIdentity,
|
|
95
|
+
expected: PathIdentity,
|
|
96
|
+
expectedLinks: bigint,
|
|
97
|
+
): void {
|
|
98
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
99
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
100
|
+
if (
|
|
101
|
+
!opened.isFile()
|
|
102
|
+
|| !sameIdentity(opened, expected)
|
|
103
|
+
|| opened.nlink !== expectedLinks
|
|
104
|
+
) throw cleanupRequired();
|
|
105
|
+
inspectExactPath(path, parent, expected);
|
|
106
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function scrubOne(
|
|
110
|
+
path: string,
|
|
111
|
+
parent: string,
|
|
112
|
+
parentIdentity: PathIdentity,
|
|
113
|
+
seam: NativeMainAuthTempScrubTestSeam,
|
|
114
|
+
): void {
|
|
115
|
+
const observed = inspectExactPath(path, parent);
|
|
116
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
117
|
+
seam.beforeOpen?.(path);
|
|
118
|
+
|
|
119
|
+
const flags = process.platform === "win32"
|
|
120
|
+
? fsConstants.O_RDWR
|
|
121
|
+
: fsConstants.O_RDWR | fsConstants.O_NOFOLLOW;
|
|
122
|
+
let fd: number | undefined;
|
|
123
|
+
let failed = false;
|
|
124
|
+
try {
|
|
125
|
+
fd = openSync(path, flags);
|
|
126
|
+
assertOpenIdentity(fd, path, parent, parentIdentity, observed, 1n);
|
|
127
|
+
|
|
128
|
+
ftruncateSync(fd, 0);
|
|
129
|
+
fsyncSync(fd);
|
|
130
|
+
const truncated = fstatSync(fd, { bigint: true });
|
|
131
|
+
if (!sameIdentity(truncated, observed) || truncated.nlink !== 1n || truncated.size !== 0n) {
|
|
132
|
+
throw cleanupRequired();
|
|
133
|
+
}
|
|
134
|
+
assertOpenIdentity(fd, path, parent, parentIdentity, observed, 1n);
|
|
135
|
+
seam.beforeUnlink?.(path);
|
|
136
|
+
|
|
137
|
+
// The lifetime owner and exclusive claim exclude cooperative OpenCodex writers.
|
|
138
|
+
// A malicious process running as the same OS user can still race Node's final
|
|
139
|
+
// identity-check/unlink gap and is intentionally outside this trusted-owner boundary.
|
|
140
|
+
unlinkSync(path);
|
|
141
|
+
const unlinked = fstatSync(fd, { bigint: true });
|
|
142
|
+
if (!sameIdentity(unlinked, observed) || unlinked.nlink !== 0n || unlinked.size !== 0n) {
|
|
143
|
+
throw cleanupRequired();
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
lstatSync(path);
|
|
147
|
+
throw cleanupRequired();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (error instanceof NativeProfileError) throw error;
|
|
150
|
+
if (errorCode(error) !== "ENOENT") throw error;
|
|
151
|
+
}
|
|
152
|
+
inspectCanonicalParent(parent, parentIdentity);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
failed = true;
|
|
155
|
+
throw error;
|
|
156
|
+
} finally {
|
|
157
|
+
if (fd !== undefined) {
|
|
158
|
+
try { closeSync(fd); }
|
|
159
|
+
catch (error) { if (!failed) throw error; }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Remove only crash residues created by atomicWriteFileAsync for canonical auth.json.
|
|
166
|
+
* The caller must hold the lifetime native-main owner and exclusive credential claim.
|
|
167
|
+
*/
|
|
168
|
+
export function scrubNativeMainAuthTempResidues(
|
|
169
|
+
context: NativeProfileContext,
|
|
170
|
+
seam: NativeMainAuthTempScrubTestSeam = {},
|
|
171
|
+
): NativeMainAuthTempScrubResult {
|
|
172
|
+
try {
|
|
173
|
+
const parent = resolve(context.codexHome);
|
|
174
|
+
const parentIdentity = inspectCanonicalParent(parent);
|
|
175
|
+
const names = exactResidues(parent, parentIdentity);
|
|
176
|
+
for (const name of names) {
|
|
177
|
+
scrubOne(join(parent, name), parent, parentIdentity, seam);
|
|
178
|
+
}
|
|
179
|
+
// A cooperative writer cannot create a new residue while the owner and
|
|
180
|
+
// exclusive claim are held. Seeing one now is ambiguous, so admission stays closed.
|
|
181
|
+
if (exactResidues(parent, parentIdentity).length !== 0) throw cleanupRequired();
|
|
182
|
+
return { scrubbed: names.length };
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error instanceof NativeProfileError) throw error;
|
|
185
|
+
throw cleanupRequired();
|
|
186
|
+
}
|
|
187
|
+
}
|