@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,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
chmodSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
realpathSync,
|
|
5
|
+
truncateSync,
|
|
6
|
+
unlinkSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
11
|
+
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
12
|
+
import {
|
|
13
|
+
forgetEphemeralSecretPath,
|
|
14
|
+
hardenSecretPath,
|
|
15
|
+
hardenSecretPathAsync,
|
|
16
|
+
} from "../lib/windows-secret-acl";
|
|
17
|
+
import {
|
|
18
|
+
renameAtomicFile,
|
|
19
|
+
renameAtomicFileAsync,
|
|
20
|
+
} from "../lib/windows-atomic-replace";
|
|
21
|
+
import { getConfigDir } from "./paths";
|
|
22
|
+
|
|
23
|
+
let atomicSequence = 0;
|
|
24
|
+
|
|
25
|
+
/** Shared process-wide suffix source for config-owned atomic sibling files. */
|
|
26
|
+
export function nextAtomicTempSequence(): number {
|
|
27
|
+
return ++atomicSequence;
|
|
28
|
+
}
|
|
29
|
+
/** Internal error classifier shared by config backup and atomic-write paths. */
|
|
30
|
+
export function isMissingPathError(error: unknown): boolean {
|
|
31
|
+
return (error as NodeJS.ErrnoException | undefined)?.code === "ENOENT";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { AtomicRenameIO } from "../lib/windows-atomic-replace";
|
|
35
|
+
export { renameAtomicFile } from "../lib/windows-atomic-replace";
|
|
36
|
+
|
|
37
|
+
export interface AtomicWriteIO {
|
|
38
|
+
write: (path: string, content: string) => void;
|
|
39
|
+
harden: (path: string) => void;
|
|
40
|
+
rename: (source: string, destination: string) => void;
|
|
41
|
+
truncate: (path: string) => void;
|
|
42
|
+
unlink: (path: string) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class AtomicWriteResidualTempError extends Error {
|
|
46
|
+
constructor(readonly tempPath: string, readonly hardened = true, options?: ErrorOptions) {
|
|
47
|
+
super(`Atomic config write left a ${hardened ? "hardened " : ""}zero-byte temporary file`, options);
|
|
48
|
+
this.name = "AtomicWriteResidualTempError";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class AtomicWriteSecretResidualError extends Error {
|
|
53
|
+
constructor(readonly tempPath: string, options?: ErrorOptions) {
|
|
54
|
+
super("Atomic config write could not scrub or remove a secret-bearing temporary file", options);
|
|
55
|
+
this.name = "AtomicWriteSecretResidualError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a write target through any symlink before the temp+rename dance so
|
|
61
|
+
* dotfiles-managed links survive an atomic replacement.
|
|
62
|
+
*/
|
|
63
|
+
export function resolveWriteTarget(path: string): string {
|
|
64
|
+
try {
|
|
65
|
+
return realpathSync(path);
|
|
66
|
+
} catch (cause) {
|
|
67
|
+
let entry;
|
|
68
|
+
try {
|
|
69
|
+
entry = lstatSync(path);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (isMissingPathError(error)) return path;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
if (entry.isSymbolicLink()) {
|
|
75
|
+
throw new Error(`refusing to replace unresolvable symlinked write target: ${path}`, { cause });
|
|
76
|
+
}
|
|
77
|
+
return path;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function assertResolvedTargetAllowed(path: string, target: string): void {
|
|
82
|
+
if (target === path) {
|
|
83
|
+
let realParent: string;
|
|
84
|
+
try {
|
|
85
|
+
realParent = realpathSync(dirname(target));
|
|
86
|
+
} catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (realParent !== dirname(target)) assertNotRealHomeUnderTest(realParent);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
assertNotRealHomeUnderTest(dirname(target));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function atomicWriteFile(path: string, content: string, io: AtomicWriteIO = {
|
|
96
|
+
write: (target, value) => writeFileSync(target, value, { encoding: "utf-8", mode: 0o600 }),
|
|
97
|
+
harden: target => {
|
|
98
|
+
try { chmodSync(target, 0o600); } catch { /* platform may ignore chmod */ }
|
|
99
|
+
if (process.platform === "win32") hardenSecretPath(target, { required: true, timeoutMemoKey: path });
|
|
100
|
+
},
|
|
101
|
+
rename: renameAtomicFile,
|
|
102
|
+
truncate: target => truncateSync(target, 0),
|
|
103
|
+
unlink: unlinkSync,
|
|
104
|
+
}): void {
|
|
105
|
+
recordOwnedConfigPath(getConfigDir(), path);
|
|
106
|
+
const target = resolveWriteTarget(path);
|
|
107
|
+
assertResolvedTargetAllowed(path, target);
|
|
108
|
+
const tmp = `${target}.ocx.${process.pid}.${nextAtomicTempSequence()}.tmp`;
|
|
109
|
+
let hardened = false;
|
|
110
|
+
try {
|
|
111
|
+
io.write(tmp, content);
|
|
112
|
+
io.harden(tmp);
|
|
113
|
+
hardened = true;
|
|
114
|
+
io.rename(tmp, target);
|
|
115
|
+
forgetEphemeralSecretPath(tmp);
|
|
116
|
+
} catch (cause) {
|
|
117
|
+
let scrubbed = false;
|
|
118
|
+
try {
|
|
119
|
+
io.truncate(tmp);
|
|
120
|
+
scrubbed = true;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (isMissingPathError(error)) scrubbed = true;
|
|
123
|
+
else {
|
|
124
|
+
try { io.write(tmp, ""); scrubbed = true; } catch { /* removal may still succeed */ }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
let removed = false;
|
|
128
|
+
try {
|
|
129
|
+
io.unlink(tmp);
|
|
130
|
+
removed = true;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (isMissingPathError(error)) removed = true;
|
|
133
|
+
else {
|
|
134
|
+
try { io.unlink(tmp); removed = true; }
|
|
135
|
+
catch (retryError) { if (isMissingPathError(retryError)) removed = true; }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (!removed && !scrubbed) throw new AtomicWriteSecretResidualError(tmp, { cause });
|
|
139
|
+
if (!removed && !hardened) {
|
|
140
|
+
try { io.harden(tmp); hardened = true; } catch { /* reported below */ }
|
|
141
|
+
}
|
|
142
|
+
if (removed) forgetEphemeralSecretPath(tmp);
|
|
143
|
+
if (!removed) throw new AtomicWriteResidualTempError(tmp, hardened, { cause });
|
|
144
|
+
throw cause;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface AtomicWriteAsyncIO {
|
|
149
|
+
write: (path: string, content: string) => void | Promise<void>;
|
|
150
|
+
harden: (path: string) => void | Promise<void>;
|
|
151
|
+
rename: (source: string, destination: string) => void | Promise<void>;
|
|
152
|
+
truncate: (path: string) => void | Promise<void>;
|
|
153
|
+
unlink: (path: string) => void | Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface AtomicWriteAsyncTestSeam {
|
|
157
|
+
afterTempWrite?: (tempPath: string) => void | Promise<void>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function atomicWriteFileAsync(
|
|
161
|
+
path: string,
|
|
162
|
+
content: string,
|
|
163
|
+
io?: AtomicWriteAsyncIO,
|
|
164
|
+
testSeam?: AtomicWriteAsyncTestSeam,
|
|
165
|
+
): Promise<void> {
|
|
166
|
+
const effective: AtomicWriteAsyncIO = io ?? {
|
|
167
|
+
write: (target, value) => writeFileSync(target, value, { encoding: "utf-8", mode: 0o600 }),
|
|
168
|
+
harden: async target => {
|
|
169
|
+
try { chmodSync(target, 0o600); } catch { /* platform may ignore chmod */ }
|
|
170
|
+
if (process.platform === "win32") {
|
|
171
|
+
await hardenSecretPathAsync(target, { required: true, timeoutMemoKey: path });
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
rename: renameAtomicFileAsync,
|
|
175
|
+
truncate: target => truncateSync(target, 0),
|
|
176
|
+
unlink: unlinkSync,
|
|
177
|
+
};
|
|
178
|
+
const target = resolveWriteTarget(path);
|
|
179
|
+
assertResolvedTargetAllowed(path, target);
|
|
180
|
+
const tmp = `${target}.ocx.${process.pid}.${nextAtomicTempSequence()}.tmp`;
|
|
181
|
+
let hardened = false;
|
|
182
|
+
try {
|
|
183
|
+
await effective.write(tmp, content);
|
|
184
|
+
await testSeam?.afterTempWrite?.(tmp);
|
|
185
|
+
await effective.harden(tmp);
|
|
186
|
+
hardened = true;
|
|
187
|
+
await effective.rename(tmp, target);
|
|
188
|
+
forgetEphemeralSecretPath(tmp);
|
|
189
|
+
} catch (cause) {
|
|
190
|
+
let scrubbed = false;
|
|
191
|
+
try {
|
|
192
|
+
await effective.truncate(tmp);
|
|
193
|
+
scrubbed = true;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (isMissingPathError(error)) scrubbed = true;
|
|
196
|
+
else {
|
|
197
|
+
try { await effective.write(tmp, ""); scrubbed = true; } catch { /* removal may still succeed */ }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let removed = false;
|
|
201
|
+
try {
|
|
202
|
+
await effective.unlink(tmp);
|
|
203
|
+
removed = true;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
if (isMissingPathError(error)) removed = true;
|
|
206
|
+
else {
|
|
207
|
+
try { await effective.unlink(tmp); removed = true; }
|
|
208
|
+
catch (retryError) { if (isMissingPathError(retryError)) removed = true; }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (!removed && !scrubbed) throw new AtomicWriteSecretResidualError(tmp, { cause });
|
|
212
|
+
if (!removed && !hardened) {
|
|
213
|
+
try { await effective.harden(tmp); hardened = true; } catch { /* reported below */ }
|
|
214
|
+
}
|
|
215
|
+
if (removed) forgetEphemeralSecretPath(tmp);
|
|
216
|
+
if (!removed) throw new AtomicWriteResidualTempError(tmp, hardened, { cause });
|
|
217
|
+
throw cause;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { chmodSync, existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { hardenSecretDir } from "../lib/windows-secret-acl";
|
|
5
|
+
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Expand a leading `~` in user-supplied paths without interpreting shell
|
|
9
|
+
* variables or `~user` forms that belong to the caller's shell.
|
|
10
|
+
*/
|
|
11
|
+
export function expandUserPath(raw: string): string {
|
|
12
|
+
if (raw === "~") return homedir();
|
|
13
|
+
if (raw.startsWith("~/") || raw.startsWith("~\\")) return join(homedir(), raw.slice(2));
|
|
14
|
+
return raw;
|
|
15
|
+
}
|
|
16
|
+
let resolvedConfigDirCache: { raw: string | undefined; path: string } | null = null;
|
|
17
|
+
|
|
18
|
+
export function getConfigDir(): string {
|
|
19
|
+
const raw = process.env["OPENCODEX_HOME"]?.trim() || undefined;
|
|
20
|
+
if (resolvedConfigDirCache && resolvedConfigDirCache.raw === raw) return resolvedConfigDirCache.path;
|
|
21
|
+
const path = raw ? resolve(expandUserPath(raw)) : join(homedir(), ".opencodex");
|
|
22
|
+
resolvedConfigDirCache = { raw, path };
|
|
23
|
+
return path;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getConfigPath(): string {
|
|
27
|
+
return join(getConfigDir(), "config.json");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function hardenConfigDir(): void {
|
|
31
|
+
const dir = getConfigDir();
|
|
32
|
+
// The guard runs before any mutation: refusing after chmod/ACL would already
|
|
33
|
+
// have changed the protected directory used by the test-home boundary.
|
|
34
|
+
assertNotRealHomeUnderTest(dir);
|
|
35
|
+
if (!existsSync(dir)) return;
|
|
36
|
+
try { chmodSync(dir, 0o700); } catch { /* best-effort */ }
|
|
37
|
+
if (process.platform === "win32") {
|
|
38
|
+
hardenSecretDir(dir, { required: false });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isLocalAttestationSecret } from "../lib/local-management-attestation";
|
|
5
|
+
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
6
|
+
import {
|
|
7
|
+
resolveTrustedWindowsPowerShellExe,
|
|
8
|
+
resolveTrustedWindowsSystemDirectory,
|
|
9
|
+
} from "../lib/windows-elevation";
|
|
10
|
+
import { atomicWriteFile } from "./atomic-write";
|
|
11
|
+
import { getConfigDir, hardenConfigDir } from "./paths";
|
|
12
|
+
|
|
13
|
+
export function getPidPath(): string {
|
|
14
|
+
return join(getConfigDir(), "ocx.pid");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getRuntimePortPath(): string {
|
|
18
|
+
return join(getConfigDir(), "runtime-port.json");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function ensureProcessStateDir(): void {
|
|
22
|
+
const dir = getConfigDir();
|
|
23
|
+
assertNotRealHomeUnderTest(dir);
|
|
24
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
25
|
+
else hardenConfigDir();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function writePid(pid: number): void {
|
|
29
|
+
ensureProcessStateDir();
|
|
30
|
+
atomicWriteFile(getPidPath(), String(pid));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type RuntimePortState = {
|
|
34
|
+
pid: number;
|
|
35
|
+
port: number;
|
|
36
|
+
hostname?: string;
|
|
37
|
+
/** Per-process proof key; protected by the config directory and never served. */
|
|
38
|
+
attestationSecret?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function isValidRuntimePortState(value: unknown): value is RuntimePortState {
|
|
42
|
+
if (!value || typeof value !== "object") return false;
|
|
43
|
+
const state = value as Record<string, unknown>;
|
|
44
|
+
const hostnameOk = state.hostname === undefined || typeof state.hostname === "string";
|
|
45
|
+
const attestationOk = state.attestationSecret === undefined || isLocalAttestationSecret(state.attestationSecret);
|
|
46
|
+
return Number.isSafeInteger(state.pid)
|
|
47
|
+
&& Number(state.pid) > 0
|
|
48
|
+
&& Number.isInteger(state.port)
|
|
49
|
+
&& Number(state.port) > 0
|
|
50
|
+
&& Number(state.port) <= 65535
|
|
51
|
+
&& hostnameOk
|
|
52
|
+
&& attestationOk;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function writeRuntimePort(state: RuntimePortState): void {
|
|
56
|
+
ensureProcessStateDir();
|
|
57
|
+
atomicWriteFile(getRuntimePortPath(), JSON.stringify(state, null, 2) + "\n");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function parsePidFile(raw: string): number | null {
|
|
61
|
+
const trimmed = raw.trim();
|
|
62
|
+
if (!/^\d+$/.test(trimmed)) return null;
|
|
63
|
+
const pid = Number.parseInt(trimmed, 10);
|
|
64
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function readPidFileValue(): number | null {
|
|
68
|
+
try {
|
|
69
|
+
return parsePidFile(readFileSync(getPidPath(), "utf-8"));
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function readRuntimePort(expectedPid?: number): RuntimePortState | null {
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(readFileSync(getRuntimePortPath(), "utf-8"));
|
|
78
|
+
if (!isValidRuntimePortState(parsed)) return null;
|
|
79
|
+
if (expectedPid !== undefined && parsed.pid !== expectedPid) return null;
|
|
80
|
+
return parsed;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function removePid(expectedPid?: number): void {
|
|
87
|
+
if (expectedPid !== undefined && readPidFileValue() !== expectedPid) return;
|
|
88
|
+
try { unlinkSync(getPidPath()); } catch { /* ignore */ }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function removeRuntimePort(expectedPid?: number): void {
|
|
92
|
+
if (expectedPid !== undefined && readRuntimePort(expectedPid) === null) return;
|
|
93
|
+
try { unlinkSync(getRuntimePortPath()); } catch { /* ignore */ }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Snapshot-guarded stale-state purge. A replacement `ocx start` can publish a
|
|
98
|
+
* fresh record while a liveness probe is in flight, so deletion is authorized
|
|
99
|
+
* only by the exact value observed before that probe.
|
|
100
|
+
*/
|
|
101
|
+
export function removePidIfValueIs(snapshot: number | null): void {
|
|
102
|
+
if (!existsSync(getPidPath())) return;
|
|
103
|
+
if (readPidFileValue() !== snapshot) return;
|
|
104
|
+
try { unlinkSync(getPidPath()); } catch { /* ignore */ }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function removeRuntimePortIfPidIs(snapshotPid: number | null): void {
|
|
108
|
+
const current = readRuntimePort();
|
|
109
|
+
if ((current?.pid ?? null) !== snapshotPid) return;
|
|
110
|
+
try { unlinkSync(getRuntimePortPath()); } catch { /* ignore */ }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function isOcxStartCommandLine(commandLine: string): boolean {
|
|
114
|
+
const normalized = commandLine.toLowerCase().replace(/\\/g, "/");
|
|
115
|
+
// Match both the upstream scope (@bitkyc08) and this fork's publish scope (@iislee).
|
|
116
|
+
const hasOcxEntrypoint = normalized.includes("src/cli.ts")
|
|
117
|
+
|| normalized.includes("src/cli/index.ts")
|
|
118
|
+
|| normalized.includes("@bitkyc08/opencodex")
|
|
119
|
+
|| normalized.includes("@iislee/opencodex")
|
|
120
|
+
|| /@bitkyc08\/\.opencodex-/.test(normalized)
|
|
121
|
+
|| /@iislee\/\.opencodex-/.test(normalized)
|
|
122
|
+
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd)?(?:$|[\s"'])/.test(normalized);
|
|
123
|
+
return hasOcxEntrypoint && /(?:^|[\s"'])start(?:$|[\s"'])/.test(normalized);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Avoid spawning WMIC/PowerShell on every short liveness poll. */
|
|
127
|
+
const ocxStartProcessCache = new Map<number, boolean>();
|
|
128
|
+
let ocxStartProcessSweepCursor = 0;
|
|
129
|
+
let ocxStartProcessProbe: (pid: number) => void = pid => { process.kill(pid, 0); };
|
|
130
|
+
|
|
131
|
+
export function setOcxStartProcessProbeForTests(probe: ((pid: number) => void) | null): void {
|
|
132
|
+
ocxStartProcessProbe = probe ?? (pid => { process.kill(pid, 0); });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function setOcxStartProcessCacheForTests(entries: Iterable<readonly [number, boolean]>): void {
|
|
136
|
+
ocxStartProcessCache.clear();
|
|
137
|
+
for (const [pid, value] of entries) ocxStartProcessCache.set(pid, value);
|
|
138
|
+
ocxStartProcessSweepCursor = 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function sweepDeadOcxStartProcessCache(maxProbes = 64): number {
|
|
142
|
+
const pids: number[] = [];
|
|
143
|
+
let removed = 0;
|
|
144
|
+
for (const pid of ocxStartProcessCache.keys()) {
|
|
145
|
+
if (Number.isSafeInteger(pid) && pid > 0) pids.push(pid);
|
|
146
|
+
else if (ocxStartProcessCache.delete(pid)) removed += 1;
|
|
147
|
+
}
|
|
148
|
+
if (pids.length === 0 || maxProbes <= 0) {
|
|
149
|
+
ocxStartProcessSweepCursor = 0;
|
|
150
|
+
return removed;
|
|
151
|
+
}
|
|
152
|
+
const probeCount = Math.min(Math.floor(maxProbes), pids.length);
|
|
153
|
+
const start = ocxStartProcessSweepCursor % pids.length;
|
|
154
|
+
for (let offset = 0; offset < probeCount; offset += 1) {
|
|
155
|
+
const pid = pids[(start + offset) % pids.length]!;
|
|
156
|
+
try {
|
|
157
|
+
ocxStartProcessProbe(pid);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if ((error as NodeJS.ErrnoException).code !== "ESRCH") continue;
|
|
160
|
+
if (ocxStartProcessCache.delete(pid)) removed += 1;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
ocxStartProcessSweepCursor = (start + probeCount) % pids.length;
|
|
164
|
+
return removed;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function ocxStartProcessCacheSizeForTests(): number {
|
|
168
|
+
return ocxStartProcessCache.size;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isLikelyOcxStartProcess(pid: number): boolean {
|
|
172
|
+
const cached = ocxStartProcessCache.get(pid);
|
|
173
|
+
if (cached !== undefined) return cached;
|
|
174
|
+
const commandLine = readProcessCommandLine(pid);
|
|
175
|
+
if (commandLine === undefined) return false;
|
|
176
|
+
const ok = isOcxStartCommandLine(commandLine);
|
|
177
|
+
ocxStartProcessCache.set(pid, ok);
|
|
178
|
+
return ok;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function readPid(): number | null {
|
|
182
|
+
const pidPath = getPidPath();
|
|
183
|
+
if (!existsSync(pidPath)) return null;
|
|
184
|
+
try {
|
|
185
|
+
const raw = readFileSync(pidPath, "utf-8").trim();
|
|
186
|
+
const pid = parsePidFile(raw);
|
|
187
|
+
if (pid === null) return null;
|
|
188
|
+
try {
|
|
189
|
+
process.kill(pid, 0);
|
|
190
|
+
return isLikelyOcxStartProcess(pid) ? pid : null;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if ((error as NodeJS.ErrnoException).code === "EPERM") {
|
|
193
|
+
return isLikelyOcxStartProcess(pid) ? pid : null;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Cheap non-destructive liveness check. Destructive callers must additionally
|
|
204
|
+
* call `verifyPidIdentity()` before acting on the returned PID.
|
|
205
|
+
*/
|
|
206
|
+
export function readAlivePid(): number | null {
|
|
207
|
+
const pid = readPidFileValue();
|
|
208
|
+
if (pid === null) return null;
|
|
209
|
+
try {
|
|
210
|
+
process.kill(pid, 0);
|
|
211
|
+
return pid;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if ((error as NodeJS.ErrnoException).code === "EPERM") return pid;
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Full identity check for a known candidate. The returned PID must equal the
|
|
220
|
+
* candidate, preventing a pidfile rewrite from swapping in another process.
|
|
221
|
+
*/
|
|
222
|
+
export function verifyPidIdentity(candidatePid: number): number | null {
|
|
223
|
+
try {
|
|
224
|
+
process.kill(candidatePid, 0);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if ((error as NodeJS.ErrnoException).code !== "EPERM") return null;
|
|
227
|
+
}
|
|
228
|
+
return isLikelyOcxStartProcess(candidatePid) ? candidatePid : null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
type ProcessCommandLineExec = (
|
|
232
|
+
executable: string,
|
|
233
|
+
args: string[],
|
|
234
|
+
options: {
|
|
235
|
+
encoding: BufferEncoding;
|
|
236
|
+
stdio: ["ignore", "pipe", "ignore"];
|
|
237
|
+
timeout: number;
|
|
238
|
+
windowsHide: boolean;
|
|
239
|
+
},
|
|
240
|
+
) => string;
|
|
241
|
+
|
|
242
|
+
const defaultProcessCommandLineExec: ProcessCommandLineExec = (executable, args, options) =>
|
|
243
|
+
execFileSync(executable, args, options);
|
|
244
|
+
let processCommandLineExec = defaultProcessCommandLineExec;
|
|
245
|
+
let processCommandLinePlatformForTests: NodeJS.Platform | null = null;
|
|
246
|
+
|
|
247
|
+
export function setProcessCommandLineExecForTests(next: ProcessCommandLineExec | null): void {
|
|
248
|
+
processCommandLineExec = next ?? defaultProcessCommandLineExec;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function setProcessCommandLinePlatformForTests(next: NodeJS.Platform | null): void {
|
|
252
|
+
processCommandLinePlatformForTests = next;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function readProcessCommandLine(pid: number): string | undefined {
|
|
256
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return undefined;
|
|
257
|
+
const platform = processCommandLinePlatformForTests ?? process.platform;
|
|
258
|
+
try {
|
|
259
|
+
if (platform === "linux") {
|
|
260
|
+
try {
|
|
261
|
+
const output = readFileSync(`/proc/${pid}/cmdline`, "utf-8");
|
|
262
|
+
const value = output.replace(/\0/g, " ").trim();
|
|
263
|
+
if (value) return value;
|
|
264
|
+
} catch {
|
|
265
|
+
/* procfs unavailable — use the fixed ps fallback below */
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (platform === "win32") {
|
|
269
|
+
// WMIC is the fast path. Newer Windows images may omit it, so fall back to
|
|
270
|
+
// the trusted fixed PowerShell binary without consulting PATH or env roots.
|
|
271
|
+
const wmic = join(resolveTrustedWindowsSystemDirectory(), "wbem", "WMIC.exe");
|
|
272
|
+
try {
|
|
273
|
+
const output = processCommandLineExec(wmic, [
|
|
274
|
+
"process", "where", `ProcessId=${pid}`, "get", "CommandLine", "/VALUE",
|
|
275
|
+
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 3000, windowsHide: true });
|
|
276
|
+
const match = /^CommandLine=(.*)$/m.exec(output.replace(/\r/g, ""));
|
|
277
|
+
const value = match?.[1]?.trim();
|
|
278
|
+
if (value) return value;
|
|
279
|
+
} catch {
|
|
280
|
+
/* WMIC missing or failed — fall through */
|
|
281
|
+
}
|
|
282
|
+
const output = processCommandLineExec(resolveTrustedWindowsPowerShellExe(), [
|
|
283
|
+
"-NoProfile",
|
|
284
|
+
"-NoLogo",
|
|
285
|
+
"-NonInteractive",
|
|
286
|
+
"-Command",
|
|
287
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`,
|
|
288
|
+
], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 3000, windowsHide: true });
|
|
289
|
+
return output.trim() || undefined;
|
|
290
|
+
}
|
|
291
|
+
for (const ps of ["/bin/ps", "/usr/bin/ps"]) {
|
|
292
|
+
try {
|
|
293
|
+
const output = processCommandLineExec(ps, ["-p", String(pid), "-o", "command="], {
|
|
294
|
+
encoding: "utf-8",
|
|
295
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
296
|
+
timeout: 1000,
|
|
297
|
+
windowsHide: true,
|
|
298
|
+
});
|
|
299
|
+
const value = output.trim();
|
|
300
|
+
if (value) return value;
|
|
301
|
+
} catch {
|
|
302
|
+
/* try the other fixed system path */
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return undefined;
|
|
306
|
+
} catch {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const RESERVED_PROVIDER_NAMES = new Set([
|
|
2
|
+
// JavaScript prototype-pollution guards.
|
|
3
|
+
"__proto__",
|
|
4
|
+
"prototype",
|
|
5
|
+
"constructor",
|
|
6
|
+
// System-reserved routing namespace (resolved before provider/account
|
|
7
|
+
// namespaces in routeModelInternal). "combo" is intentionally NOT reserved:
|
|
8
|
+
// a physical provider named `combo` is a supported pattern (combo aliases
|
|
9
|
+
// hosted on the combo provider), and the combo selector only wins when an
|
|
10
|
+
// actual combo id matches.
|
|
11
|
+
"policy",
|
|
12
|
+
]);
|
|
13
|
+
const PROVIDER_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,62}[A-Za-z0-9])?$/;
|
|
14
|
+
|
|
15
|
+
export function isValidProviderName(name: string): boolean {
|
|
16
|
+
const trimmed = name.trim();
|
|
17
|
+
return trimmed === name
|
|
18
|
+
&& PROVIDER_NAME_PATTERN.test(name)
|
|
19
|
+
&& !RESERVED_PROVIDER_NAMES.has(name.toLowerCase());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasOwnProvider(providers: Record<string, unknown>, name: string): boolean {
|
|
23
|
+
return Object.prototype.hasOwnProperty.call(providers, name);
|
|
24
|
+
}
|