@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
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import type { OcxConfig } from "../../types";
|
|
2
|
+
import type { NativeProfileApiDeps } from "../../codex/native-profile-api";
|
|
3
|
+
import type { CodexLogGuardProtectionDeps } from "../../codex/log-guard/protection";
|
|
4
|
+
import type { CodexLogGuardMaintenanceDeps } from "../../codex/log-guard/maintenance";
|
|
5
|
+
import type { StartupHealth } from "../../codex/autostart-health";
|
|
2
6
|
import type { StartupInstallAction } from "../startup-action-control";
|
|
7
|
+
import type { ManagementPrincipal } from "../management-auth";
|
|
8
|
+
import type { CatalogModel } from "../../codex/catalog";
|
|
9
|
+
import type { Paths as CodexPromptPaths } from "../../codex/prompt-layers";
|
|
10
|
+
import type { injectGrokConfig } from "../../grok/inject";
|
|
11
|
+
import type { removeDesktop3pStandardPivot, writeDesktop3pConfig } from "../../claude/desktop-3p";
|
|
12
|
+
import type { RuntimePortState } from "../../config/process-state";
|
|
13
|
+
import type { CatalogDisposition, ConvergeCodex } from "../../codex/convergence-types";
|
|
14
|
+
import type {
|
|
15
|
+
performCodexRestart,
|
|
16
|
+
readCodexAppServerState,
|
|
17
|
+
} from "../../codex/app-server-restart-service";
|
|
3
18
|
|
|
4
19
|
export interface ManagementApiDeps {
|
|
20
|
+
/** Platform seam for capability projections; does not alter host-level startup behavior. */
|
|
21
|
+
platform?: NodeJS.Platform;
|
|
5
22
|
toggleCodexMultiAgentV2?: (enabled: boolean) => void;
|
|
6
|
-
|
|
23
|
+
toggleDefaultModeRequestUserInput?: (enabled: boolean) => void;
|
|
24
|
+
createManagementConvergeCodex?: (config: Readonly<OcxConfig>) => ConvergeCodex;
|
|
25
|
+
/** Startup-health seam keeps route tests from launching platform probes. */
|
|
26
|
+
getCachedStartupHealth?: (config: Pick<OcxConfig, "codexAutoStart">) => Promise<StartupHealth>;
|
|
7
27
|
/**
|
|
8
28
|
* Persistence seam for route-level tests. Production leaves this unset and uses
|
|
9
29
|
* `saveConfigPreservingClaudeCode`; tests that pass an in-memory fixture config
|
|
@@ -11,6 +31,28 @@ export interface ManagementApiDeps {
|
|
|
11
31
|
* OPENCODEX_HOME (incident: devlog 260730.../070).
|
|
12
32
|
*/
|
|
13
33
|
saveConfigPreservingClaudeCode?: (config: OcxConfig) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Catalog seam for the Grok toggle (WP2, devlog 260803_integrations_toggle_all
|
|
36
|
+
* Rev 3 N2). Production leaves this unset and the route dynamic-imports the
|
|
37
|
+
* real one — a static import would close a cycle with management-api.ts.
|
|
38
|
+
* Tests stub it to orphan the fixture file mid-fetch (the r7 recheck test).
|
|
39
|
+
*/
|
|
40
|
+
fetchAllModels?: (config: OcxConfig) => Promise<CatalogModel[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Writer seam for the Grok toggle: lets a test place the file in any state
|
|
43
|
+
* between the pre-write recheck and the write itself (the r8 post-inspection
|
|
44
|
+
* tests). Production leaves this unset and uses the real writer.
|
|
45
|
+
*/
|
|
46
|
+
injectGrokConfig?: typeof injectGrokConfig;
|
|
47
|
+
/** Desktop mutation seams keep route tests inside temporary config libraries. */
|
|
48
|
+
removeDesktop3pStandardPivot?: typeof removeDesktop3pStandardPivot;
|
|
49
|
+
writeDesktop3pConfig?: typeof writeDesktop3pConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Runtime-state seam: the fence must name the host/port the RUNNING process
|
|
52
|
+
* bound (agent-settings-routes.ts:99-103 pattern), and a test must not depend
|
|
53
|
+
* on the developer's real runtime state file.
|
|
54
|
+
*/
|
|
55
|
+
readRuntimePort?: (pid: number) => RuntimePortState | null;
|
|
14
56
|
clearThreadAccountMap?: () => void;
|
|
15
57
|
clearProviderQuotaCache?: () => void;
|
|
16
58
|
primeCodexPoolQuotas?: (config: OcxConfig, reason: string) => Promise<void> | void;
|
|
@@ -18,6 +60,42 @@ export interface ManagementApiDeps {
|
|
|
18
60
|
action: StartupInstallAction,
|
|
19
61
|
options?: { repair?: boolean },
|
|
20
62
|
) => Promise<{ message: string }>;
|
|
63
|
+
/**
|
|
64
|
+
* Native-main profile persistence seam for server-boundary tests. Production
|
|
65
|
+
* leaves this unset, so the route creates its normal NativeProfileManager.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Codex app-server restart seam (devlog/_fin/260815_gui_codex_restart).
|
|
69
|
+
* Grouped rather than three separate fields: the route is an adapter over one
|
|
70
|
+
* service, and a route test that could not stub it would really terminate the
|
|
71
|
+
* developer's own Codex app-servers.
|
|
72
|
+
*/
|
|
73
|
+
codexRestartService?: {
|
|
74
|
+
readState: typeof readCodexAppServerState;
|
|
75
|
+
performRestart: typeof performCodexRestart;
|
|
76
|
+
};
|
|
77
|
+
nativeProfileApi?: NativeProfileApiDeps;
|
|
78
|
+
/**
|
|
79
|
+
* Log Guard mutation seam. Production leaves this unset and therefore uses the
|
|
80
|
+
* owner-verified process enumerator, trusted L namespace and real config store.
|
|
81
|
+
* Route tests inject all three so they cannot depend on local Codex processes
|
|
82
|
+
* or create lock/config state outside the fixture.
|
|
83
|
+
*/
|
|
84
|
+
codexLogGuardProtectionDeps?: CodexLogGuardProtectionDeps;
|
|
85
|
+
/**
|
|
86
|
+
* Log Guard maintenance seam. Production reuses the same fail-closed process
|
|
87
|
+
* enumerator and L namespace as Protect; route tests keep all maintenance
|
|
88
|
+
* state inside their temporary Codex home.
|
|
89
|
+
*/
|
|
90
|
+
codexLogGuardMaintenanceDeps?: CodexLogGuardMaintenanceDeps;
|
|
91
|
+
/**
|
|
92
|
+
* Prompt-layer path seam. Production leaves this unset and
|
|
93
|
+
* `src/codex/prompt-layers.ts` resolves the real CODEX_HOME. A route test
|
|
94
|
+
* that could not inject paths would read AND WRITE the developer's live
|
|
95
|
+
* ~/.codex/config.toml — the same class of incident
|
|
96
|
+
* `saveConfigPreservingClaudeCode` above exists to prevent.
|
|
97
|
+
*/
|
|
98
|
+
codexPromptPaths?: CodexPromptPaths;
|
|
21
99
|
}
|
|
22
100
|
|
|
23
101
|
|
|
@@ -26,6 +104,15 @@ export interface ManagementContext {
|
|
|
26
104
|
url: URL;
|
|
27
105
|
config: OcxConfig;
|
|
28
106
|
deps: ManagementApiDeps;
|
|
29
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Which credential authorized this request, resolved by the auth gate before
|
|
109
|
+
* dispatch. Routes that spend the USER's identity (not just the proxy's) must
|
|
110
|
+
* branch on this instead of on request headers: the admin token is readable by
|
|
111
|
+
* anything running as the user, so a token holder can forge any header a route
|
|
112
|
+
* might otherwise treat as browser evidence. Undefined only in direct-dispatch
|
|
113
|
+
* tests, which are treated as the untrusted `admin-token` case.
|
|
114
|
+
*/
|
|
115
|
+
principal?: ManagementPrincipal;
|
|
116
|
+
convergeCodexCatalog: () => Promise<CatalogDisposition>;
|
|
30
117
|
syncClaudeAgentDefsBestEffort: () => Promise<void>;
|
|
31
118
|
}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Management routes for the client-integration toggle.
|
|
3
|
+
*
|
|
4
|
+
* This module is an HTTP adapter and nothing more: every policy decision —
|
|
5
|
+
* what counts as ownership, when a mutation is refused, what gets journaled —
|
|
6
|
+
* belongs to src/integrations/writer.ts. Duplicating any of it here is how the
|
|
7
|
+
* API and the writer would start disagreeing about what happened to a file.
|
|
8
|
+
*
|
|
9
|
+
* Design of record: devlog/_fin/260802_client_toggle_api/040_wp4_management_api.md.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import type { IntegrationIO } from "../../integrations/config-io";
|
|
13
|
+
import { matchesOperationResult } from "../../integrations/journal";
|
|
14
|
+
import {
|
|
15
|
+
INTEGRATION_CLIENT_IDS,
|
|
16
|
+
isIntegrationClientId,
|
|
17
|
+
type IntegrationClientId,
|
|
18
|
+
} from "../../integrations/registry";
|
|
19
|
+
import { readIntegrationState } from "../../integrations/state";
|
|
20
|
+
import { createIntegrationStateStore, type IntegrationStateStore } from "../../integrations/store";
|
|
21
|
+
import {
|
|
22
|
+
applyIntegrationCoordinated,
|
|
23
|
+
disableIntegrationCoordinated,
|
|
24
|
+
restoreIntegrationCoordinated,
|
|
25
|
+
type IntegrationRestoreInput,
|
|
26
|
+
type IntegrationWriteInput,
|
|
27
|
+
type WriteRefused,
|
|
28
|
+
} from "../../integrations/writer";
|
|
29
|
+
import { IntegrationWriterLockBusyError, type IntegrationWriterLockSeams } from "../../integrations/writer-lock";
|
|
30
|
+
import {
|
|
31
|
+
INTEGRATION_MUTATION_TERMINAL_MS,
|
|
32
|
+
IntegrationMutationBusyError,
|
|
33
|
+
runIntegrationMutationFlight,
|
|
34
|
+
setIntegrationMutationFlightTestHook,
|
|
35
|
+
} from "../../integrations/mutation-flight";
|
|
36
|
+
import { jsonResponse } from "../auth-cors";
|
|
37
|
+
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
38
|
+
import type { ManagementContext } from "./context";
|
|
39
|
+
import { loadExportModels } from "./model-rows";
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const INTEGRATION_ROUTE_PREFIX = "/api/client-integrations/";
|
|
43
|
+
export { INTEGRATION_MUTATION_TERMINAL_MS };
|
|
44
|
+
|
|
45
|
+
type IntegrationStateRecord = Awaited<ReturnType<typeof readIntegrationState>>;
|
|
46
|
+
type ApplyResult = Awaited<ReturnType<typeof applyIntegrationCoordinated>>;
|
|
47
|
+
type DisableResult = Awaited<ReturnType<typeof disableIntegrationCoordinated>>;
|
|
48
|
+
type RestoreResult = Awaited<ReturnType<typeof restoreIntegrationCoordinated>>;
|
|
49
|
+
|
|
50
|
+
export type IntegrationStateEnvelope = {
|
|
51
|
+
clientId: IntegrationClientId;
|
|
52
|
+
} & IntegrationStateRecord;
|
|
53
|
+
|
|
54
|
+
export interface IntegrationStateListEnvelope {
|
|
55
|
+
clients: IntegrationStateEnvelope[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type IntegrationToggleEnvelope =
|
|
59
|
+
| ({ clientId: IntegrationClientId } & ApplyResult)
|
|
60
|
+
| ({ clientId: IntegrationClientId } & DisableResult);
|
|
61
|
+
|
|
62
|
+
export type IntegrationRestoreEnvelope = {
|
|
63
|
+
clientId: IntegrationClientId;
|
|
64
|
+
} & RestoreResult;
|
|
65
|
+
|
|
66
|
+
export interface IntegrationJournalEnvelope {
|
|
67
|
+
operations: IntegrationJournalRow[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface IntegrationJournalRow {
|
|
71
|
+
opId: string;
|
|
72
|
+
clientId: IntegrationClientId;
|
|
73
|
+
kind: "apply" | "disable" | "refresh" | "restore";
|
|
74
|
+
at: string;
|
|
75
|
+
configPath: string;
|
|
76
|
+
snapshot: "none" | "stored" | "expired";
|
|
77
|
+
undoable: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface IntegrationToggleBody {
|
|
81
|
+
enabled: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface IntegrationRestoreBody {
|
|
85
|
+
opId: string;
|
|
86
|
+
confirmDrift?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let integrationMutationTestHooks: {
|
|
90
|
+
io?: IntegrationIO;
|
|
91
|
+
lockSeams?: IntegrationWriterLockSeams;
|
|
92
|
+
/**
|
|
93
|
+
* Bind every read and write in the request to one store. Without this a
|
|
94
|
+
* route test could isolate the writer but not the journal listing or the
|
|
95
|
+
* restore preflight (A-gate round 12).
|
|
96
|
+
*/
|
|
97
|
+
store?: IntegrationStateStore;
|
|
98
|
+
run?: (operation: () => Promise<unknown>) => Promise<unknown>;
|
|
99
|
+
} | null = null;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Home and environment overrides for tests.
|
|
103
|
+
*
|
|
104
|
+
* Bun's `os.homedir()` snapshots the real home at startup and ignores a later
|
|
105
|
+
* `process.env.HOME` assignment, so a test that only rewrites `HOME` still
|
|
106
|
+
* resolves the DEVELOPER'S client configs — which is exactly how a route test
|
|
107
|
+
* wrote a real `~/.hermes/config.yaml` during this work package. The writer
|
|
108
|
+
* and the state reader both already take `env`/`home` explicitly; the route
|
|
109
|
+
* simply had no way to pass them. It does now, and production leaves it unset.
|
|
110
|
+
*/
|
|
111
|
+
let integrationPathTestHooks: { env?: NodeJS.ProcessEnv; home?: string } | null = null;
|
|
112
|
+
|
|
113
|
+
export function setIntegrationPathTestHooks(hooks: { env?: NodeJS.ProcessEnv; home?: string } | null): void {
|
|
114
|
+
integrationPathTestHooks = hooks;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The `env`/`home` overrides, spread into every registry-resolving call. */
|
|
118
|
+
function pathOverrides(): { env?: NodeJS.ProcessEnv; home?: string } {
|
|
119
|
+
return {
|
|
120
|
+
...(integrationPathTestHooks?.env ? { env: integrationPathTestHooks.env } : {}),
|
|
121
|
+
...(integrationPathTestHooks?.home ? { home: integrationPathTestHooks.home } : {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
126
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function decodeClientPath(pathname: string): string | null {
|
|
130
|
+
if (!pathname.startsWith(INTEGRATION_ROUTE_PREFIX)) return null;
|
|
131
|
+
const encoded = pathname.slice(INTEGRATION_ROUTE_PREFIX.length);
|
|
132
|
+
if (!encoded || encoded.includes("/")) return null;
|
|
133
|
+
try {
|
|
134
|
+
return decodeURIComponent(encoded);
|
|
135
|
+
} catch {
|
|
136
|
+
return encoded;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function setIntegrationMutationFlightTestHooks(
|
|
141
|
+
hooks: {
|
|
142
|
+
io?: IntegrationIO;
|
|
143
|
+
lockSeams?: IntegrationWriterLockSeams;
|
|
144
|
+
/** Binds the WHOLE request — reads, writes and journal — to one store. */
|
|
145
|
+
store?: IntegrationStateStore;
|
|
146
|
+
run?: (operation: () => Promise<unknown>) => Promise<unknown>;
|
|
147
|
+
} | null,
|
|
148
|
+
): void {
|
|
149
|
+
integrationMutationTestHooks = hooks;
|
|
150
|
+
setIntegrationMutationFlightTestHook(hooks?.run ?? null);
|
|
151
|
+
// Path overrides are part of the same isolation contract: clearing flights
|
|
152
|
+
// while leaving a temp home bound would let the next suite write real files.
|
|
153
|
+
if (hooks === null) integrationPathTestHooks = null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* ONE store per request, used by every read and every write in that request.
|
|
158
|
+
* The route previously called module-level `listOperations`/`readSnapshot`
|
|
159
|
+
* while handing the writer a separate default store, so a test could not bind
|
|
160
|
+
* the whole operation to a temp root (A-gate round 12).
|
|
161
|
+
*/
|
|
162
|
+
function integrationStore(): IntegrationStateStore {
|
|
163
|
+
return integrationMutationTestHooks?.store ?? createIntegrationStateStore();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function buildIntegrationWriteInput(
|
|
167
|
+
clientId: IntegrationClientId,
|
|
168
|
+
ctx: ManagementContext,
|
|
169
|
+
store: IntegrationStateStore,
|
|
170
|
+
): Promise<IntegrationWriteInput> {
|
|
171
|
+
return {
|
|
172
|
+
clientId,
|
|
173
|
+
models: await loadExportModels(ctx.config),
|
|
174
|
+
config: ctx.config,
|
|
175
|
+
port: Number(ctx.url.port) || ctx.config.port,
|
|
176
|
+
store,
|
|
177
|
+
io: integrationMutationTestHooks?.io,
|
|
178
|
+
...pathOverrides(),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The file's current bytes, or `null` when it is missing.
|
|
184
|
+
*
|
|
185
|
+
* `null` is NOT the same as `""`: an absent file and an empty one are
|
|
186
|
+
* different states, and collapsing them is what made this route disagree with
|
|
187
|
+
* the writer about whether an absence-result operation had drifted.
|
|
188
|
+
* `undefined` means we could not read it at all, which is neither.
|
|
189
|
+
*/
|
|
190
|
+
function currentConfigText(configPath: string): string | null | undefined {
|
|
191
|
+
try {
|
|
192
|
+
return readFileSync(configPath, "utf8");
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (isPlainRecord(error) && error.code === "ENOENT") return null;
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function invalidClientResponse(ctx: ManagementContext): Response {
|
|
200
|
+
return jsonResponse({
|
|
201
|
+
error: "invalid integration client",
|
|
202
|
+
code: "invalid_integration_client",
|
|
203
|
+
validClients: INTEGRATION_CLIENT_IDS,
|
|
204
|
+
}, 400, ctx.req, ctx.config);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function internalErrorResponse(error: unknown, ctx: ManagementContext): Response {
|
|
208
|
+
return jsonResponse({
|
|
209
|
+
error: error instanceof Error ? error.message : String(error),
|
|
210
|
+
code: "integration_internal_error",
|
|
211
|
+
}, 500, ctx.req, ctx.config);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function readJsonBody(ctx: ManagementContext): Promise<unknown | Response> {
|
|
215
|
+
try {
|
|
216
|
+
return await readManagementJsonBody(ctx.req);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
rethrowManagementBodyTooLarge(error);
|
|
219
|
+
return jsonResponse({
|
|
220
|
+
error: "invalid JSON body",
|
|
221
|
+
code: "invalid_json_body",
|
|
222
|
+
}, 400, ctx.req, ctx.config);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function writerFailureResponse(
|
|
227
|
+
clientId: IntegrationClientId,
|
|
228
|
+
/*
|
|
229
|
+
* The writer's own refusal type, not a structural echo of it.
|
|
230
|
+
*
|
|
231
|
+
* A local shape with an optional `message` accepted a refusal that had lost
|
|
232
|
+
* its message on the way here, which is exactly how the drift branch shipped
|
|
233
|
+
* without one. `WriteRefused` requires it, so the compiler now objects.
|
|
234
|
+
*/
|
|
235
|
+
result: WriteRefused,
|
|
236
|
+
ctx: ManagementContext,
|
|
237
|
+
): Response {
|
|
238
|
+
/*
|
|
239
|
+
* Routed by `reason`, never by `state` (006 §5). A `write_failed` that
|
|
240
|
+
* happens to occur while the file is in a `conflict` state is still a write
|
|
241
|
+
* failure, and mapping on state first silently dropped its message,
|
|
242
|
+
* snapshotPath, and residual — the recovery information the flag exists to
|
|
243
|
+
* carry (A-gate round 5, blocker 3).
|
|
244
|
+
*/
|
|
245
|
+
const recovery = {
|
|
246
|
+
message: result.message,
|
|
247
|
+
...(result.snapshotPath ? { snapshotPath: result.snapshotPath } : {}),
|
|
248
|
+
...(result.residual ? { residual: true } : {}),
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
if (result.reason === "unsafe") {
|
|
252
|
+
return jsonResponse({
|
|
253
|
+
error: "integration config is unsafe",
|
|
254
|
+
code: "integration_unsafe",
|
|
255
|
+
clientId, state: result.state, reason: result.reason, ...recovery,
|
|
256
|
+
}, 409, ctx.req, ctx.config);
|
|
257
|
+
}
|
|
258
|
+
if (result.reason === "conflict") {
|
|
259
|
+
return jsonResponse({
|
|
260
|
+
error: "integration config conflicts with ownership record",
|
|
261
|
+
code: "integration_conflict",
|
|
262
|
+
clientId, state: result.state, reason: result.reason, ...recovery,
|
|
263
|
+
}, 409, ctx.req, ctx.config);
|
|
264
|
+
}
|
|
265
|
+
if (result.reason === "drift_requires_confirm") {
|
|
266
|
+
return jsonResponse({
|
|
267
|
+
error: "restore requires drift confirmation",
|
|
268
|
+
code: "integration_drift_confirmation_required",
|
|
269
|
+
clientId, state: result.state, reason: result.reason, ...recovery,
|
|
270
|
+
}, 409, ctx.req, ctx.config);
|
|
271
|
+
}
|
|
272
|
+
if (result.reason === "snapshot_expired") {
|
|
273
|
+
return jsonResponse({
|
|
274
|
+
error: "integration snapshot expired",
|
|
275
|
+
code: "integration_snapshot_expired",
|
|
276
|
+
clientId, state: result.state, reason: result.reason, ...recovery,
|
|
277
|
+
}, 410, ctx.req, ctx.config);
|
|
278
|
+
}
|
|
279
|
+
// not_installed, non_loopback, write_failed — always carry recovery fields.
|
|
280
|
+
return jsonResponse({
|
|
281
|
+
error: "integration mutation failed",
|
|
282
|
+
code: "integration_mutation_failed",
|
|
283
|
+
clientId, state: result.state, reason: result.reason, ...recovery,
|
|
284
|
+
}, 500, ctx.req, ctx.config);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function handleIntegrationRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
288
|
+
const { req, url } = ctx;
|
|
289
|
+
|
|
290
|
+
if (url.pathname === "/api/client-integrations" && req.method === "GET") {
|
|
291
|
+
try {
|
|
292
|
+
const models = await loadExportModels(ctx.config);
|
|
293
|
+
const port = Number(url.port) || ctx.config.port;
|
|
294
|
+
// One store for the whole collection read: without it this route retried
|
|
295
|
+
// maintenance and counted snapshots in the default store even when the
|
|
296
|
+
// caller had bound everything else to a temp root (A-gate round 13).
|
|
297
|
+
const store = integrationStore();
|
|
298
|
+
/*
|
|
299
|
+
* The envelope carries `clientId`, but it is not restated here: WP2 and
|
|
300
|
+
* WP3 both echo `input.clientId` back in their result, and `input`
|
|
301
|
+
* IS this route's client. Writing it twice made the compiler pick the
|
|
302
|
+
* later one silently — a duplicate that could only ever hide a
|
|
303
|
+
* disagreement, never surface it.
|
|
304
|
+
*/
|
|
305
|
+
const clients = INTEGRATION_CLIENT_IDS.map(clientId =>
|
|
306
|
+
readIntegrationState({ clientId, models, config: ctx.config, port, store, ...pathOverrides() }));
|
|
307
|
+
return jsonResponse({ clients } satisfies IntegrationStateListEnvelope, 200, req, ctx.config);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return internalErrorResponse(error, ctx);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (url.pathname === "/api/client-integrations/journal") {
|
|
314
|
+
if (req.method !== "GET") return null;
|
|
315
|
+
const requestedClient = url.searchParams.get("client");
|
|
316
|
+
if (requestedClient !== null && !isIntegrationClientId(requestedClient)) {
|
|
317
|
+
return invalidClientResponse(ctx);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const store = integrationStore();
|
|
321
|
+
const storedOperations = store.listOperations(requestedClient ?? undefined);
|
|
322
|
+
const newestByClient = new Map<IntegrationClientId, string>();
|
|
323
|
+
for (const operation of storedOperations) {
|
|
324
|
+
if (!newestByClient.has(operation.clientId)) {
|
|
325
|
+
newestByClient.set(operation.clientId, operation.opId);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const operations: IntegrationJournalRow[] = storedOperations.map(operation => {
|
|
329
|
+
/*
|
|
330
|
+
* Resolved against the DISK, not read off the row.
|
|
331
|
+
*
|
|
332
|
+
* Retention deletes snapshot files and deliberately leaves the row's
|
|
333
|
+
* persisted tag saying `stored`, so copying that tag advertised undo
|
|
334
|
+
* for bytes that no longer exist — the GUI would offer the button and
|
|
335
|
+
* the restore route would answer 410. `readSnapshot` is the same
|
|
336
|
+
* resolver that preflight uses, which is what keeps the two agreeing.
|
|
337
|
+
*/
|
|
338
|
+
const snapshot = store.readSnapshot(operation).kind;
|
|
339
|
+
return {
|
|
340
|
+
opId: operation.opId,
|
|
341
|
+
clientId: operation.clientId,
|
|
342
|
+
kind: operation.kind,
|
|
343
|
+
at: operation.at,
|
|
344
|
+
configPath: operation.configPath,
|
|
345
|
+
snapshot,
|
|
346
|
+
/*
|
|
347
|
+
* The SAME resolution decides `undoable`. Reporting the tag honestly
|
|
348
|
+
* and then offering undo anyway is the identical defect one field
|
|
349
|
+
* over: restore would answer 410 for a row the GUI drew a button on.
|
|
350
|
+
* `none` stays undoable — restoring an op that created a file means
|
|
351
|
+
* deleting it, and that needs no snapshot bytes.
|
|
352
|
+
*/
|
|
353
|
+
/*
|
|
354
|
+
* Eligibility goes through the SAME matcher restore uses. This route
|
|
355
|
+
* used to represent a missing file as `""` and call that a match,
|
|
356
|
+
* while restore hashed `""` into a real digest — so the row was
|
|
357
|
+
* offered as Undo and then refused as drift.
|
|
358
|
+
*/
|
|
359
|
+
undoable: (() => {
|
|
360
|
+
if (snapshot === "expired") return false;
|
|
361
|
+
if (newestByClient.get(operation.clientId) !== operation.opId) return false;
|
|
362
|
+
const current = currentConfigText(operation.configPath);
|
|
363
|
+
return current === undefined ? false : matchesOperationResult(operation, current);
|
|
364
|
+
})(),
|
|
365
|
+
};
|
|
366
|
+
});
|
|
367
|
+
return jsonResponse({ operations } satisfies IntegrationJournalEnvelope, 200, req, ctx.config);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return internalErrorResponse(error, ctx);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (url.pathname === "/api/client-integrations/restore") {
|
|
374
|
+
if (req.method !== "POST") return null;
|
|
375
|
+
const parsed = await readJsonBody(ctx);
|
|
376
|
+
if (parsed instanceof Response) return parsed;
|
|
377
|
+
if (!isPlainRecord(parsed) || typeof parsed.opId !== "string" || parsed.opId.trim().length === 0) {
|
|
378
|
+
return jsonResponse({
|
|
379
|
+
error: "opId must be a non-empty string",
|
|
380
|
+
code: "invalid_op_id",
|
|
381
|
+
}, 400, req, ctx.config);
|
|
382
|
+
}
|
|
383
|
+
if (parsed.confirmDrift !== undefined && typeof parsed.confirmDrift !== "boolean") {
|
|
384
|
+
return jsonResponse({
|
|
385
|
+
error: "confirmDrift must be a boolean",
|
|
386
|
+
code: "invalid_confirm_drift",
|
|
387
|
+
}, 400, req, ctx.config);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const opId = parsed.opId.trim();
|
|
391
|
+
const confirmDrift = parsed.confirmDrift ?? false;
|
|
392
|
+
let restoreClientId: IntegrationClientId | undefined;
|
|
393
|
+
try {
|
|
394
|
+
const store = integrationStore();
|
|
395
|
+
const operation = store.findOperation(opId);
|
|
396
|
+
if (!operation) {
|
|
397
|
+
return jsonResponse({
|
|
398
|
+
error: "integration operation not found",
|
|
399
|
+
code: "integration_operation_not_found",
|
|
400
|
+
opId,
|
|
401
|
+
}, 404, req, ctx.config);
|
|
402
|
+
}
|
|
403
|
+
restoreClientId = operation.clientId;
|
|
404
|
+
const snapshot = store.readSnapshot(operation);
|
|
405
|
+
if (snapshot.kind === "expired") {
|
|
406
|
+
return jsonResponse({
|
|
407
|
+
error: "integration snapshot expired",
|
|
408
|
+
code: "integration_snapshot_expired",
|
|
409
|
+
opId,
|
|
410
|
+
}, 410, req, ctx.config);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const writeInput = await buildIntegrationWriteInput(operation.clientId, ctx, store);
|
|
414
|
+
const restoreInput: IntegrationRestoreInput = {
|
|
415
|
+
...writeInput,
|
|
416
|
+
opId,
|
|
417
|
+
confirmDrift,
|
|
418
|
+
};
|
|
419
|
+
const result = await runIntegrationMutationFlight(
|
|
420
|
+
operation.clientId,
|
|
421
|
+
`restore:${opId}:${confirmDrift}`,
|
|
422
|
+
writeInput.io?.now ?? Date.now,
|
|
423
|
+
() => restoreIntegrationCoordinated(restoreInput, {
|
|
424
|
+
lockSeams: integrationMutationTestHooks?.lockSeams,
|
|
425
|
+
}),
|
|
426
|
+
);
|
|
427
|
+
if (!result.ok) {
|
|
428
|
+
/*
|
|
429
|
+
* Drift is NOT special-cased here.
|
|
430
|
+
*
|
|
431
|
+
* It used to be, and the hand-written branch dropped the writer's
|
|
432
|
+
* `message` — which is the only thing that tells the user WHICH file
|
|
433
|
+
* drifted and where its backup went. Every refusal leaves through the
|
|
434
|
+
* one serializer, so a refusal cannot lose its recovery fields by
|
|
435
|
+
* being routed through a shorter path.
|
|
436
|
+
*/
|
|
437
|
+
return writerFailureResponse(operation.clientId, result, ctx);
|
|
438
|
+
}
|
|
439
|
+
return jsonResponse(result satisfies IntegrationRestoreEnvelope, 200, req, ctx.config);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
if (error instanceof IntegrationMutationBusyError || error instanceof IntegrationWriterLockBusyError) {
|
|
442
|
+
return jsonResponse({
|
|
443
|
+
error: "integration mutation busy",
|
|
444
|
+
code: "integration_mutation_busy",
|
|
445
|
+
clientId: restoreClientId ?? (error instanceof IntegrationMutationBusyError ? error.clientId : undefined),
|
|
446
|
+
}, 409, req, ctx.config);
|
|
447
|
+
}
|
|
448
|
+
return internalErrorResponse(error, ctx);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (req.method !== "GET" && req.method !== "PUT") return null;
|
|
453
|
+
const requestedClient = decodeClientPath(url.pathname);
|
|
454
|
+
if (requestedClient === null) return null;
|
|
455
|
+
if (!isIntegrationClientId(requestedClient)) return invalidClientResponse(ctx);
|
|
456
|
+
|
|
457
|
+
if (req.method === "GET") {
|
|
458
|
+
try {
|
|
459
|
+
const input = await buildIntegrationWriteInput(requestedClient, ctx, integrationStore());
|
|
460
|
+
const state = readIntegrationState(input);
|
|
461
|
+
return jsonResponse(state satisfies IntegrationStateEnvelope, 200, req, ctx.config);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
return internalErrorResponse(error, ctx);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const parsed = await readJsonBody(ctx);
|
|
468
|
+
if (parsed instanceof Response) return parsed;
|
|
469
|
+
if (!isPlainRecord(parsed) || typeof parsed.enabled !== "boolean") {
|
|
470
|
+
return jsonResponse({
|
|
471
|
+
error: "enabled must be a boolean",
|
|
472
|
+
code: "invalid_enabled",
|
|
473
|
+
}, 400, req, ctx.config);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const input = await buildIntegrationWriteInput(requestedClient, ctx, integrationStore());
|
|
478
|
+
const result = await runIntegrationMutationFlight(
|
|
479
|
+
requestedClient,
|
|
480
|
+
parsed.enabled ? "apply" : "disable",
|
|
481
|
+
input.io?.now ?? Date.now,
|
|
482
|
+
() => parsed.enabled
|
|
483
|
+
? applyIntegrationCoordinated(input, { lockSeams: integrationMutationTestHooks?.lockSeams })
|
|
484
|
+
: disableIntegrationCoordinated(input, { lockSeams: integrationMutationTestHooks?.lockSeams }),
|
|
485
|
+
);
|
|
486
|
+
if (!result.ok) return writerFailureResponse(requestedClient, result, ctx);
|
|
487
|
+
return jsonResponse(result satisfies IntegrationToggleEnvelope, 200, req, ctx.config);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
if (error instanceof IntegrationMutationBusyError || error instanceof IntegrationWriterLockBusyError) {
|
|
490
|
+
return jsonResponse({
|
|
491
|
+
error: "integration mutation busy",
|
|
492
|
+
code: "integration_mutation_busy",
|
|
493
|
+
clientId: requestedClient,
|
|
494
|
+
}, 409, req, ctx.config);
|
|
495
|
+
}
|
|
496
|
+
return internalErrorResponse(error, ctx);
|
|
497
|
+
}
|
|
498
|
+
}
|