@yansigit/opencodex 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- 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 +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- 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 +14 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +225 -0
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -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/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- 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 +403 -0
- 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-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- 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 +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- 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 +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- 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 +586 -0
- package/src/server/management/model-routes.ts +560 -0
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- 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 +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- 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 +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- 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 +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- 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 +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { atomicWriteFile } from "../config";
|
|
6
|
+
import type { OcxClaudeDesktopProfile } from "../types";
|
|
7
|
+
import { claudeDesktopConfigLibraryDir, resolveConfigLibraryDir } from "./desktop-3p-paths";
|
|
8
|
+
import {
|
|
9
|
+
reconcileDesktopProfile,
|
|
10
|
+
renderDesktopProfile,
|
|
11
|
+
type DesktopProfileModel,
|
|
12
|
+
} from "./desktop-profile";
|
|
13
|
+
import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../codex/catalog";
|
|
14
|
+
import { assertDesktop3pModelsValid } from "./desktop-3p-guard";
|
|
15
|
+
|
|
16
|
+
export interface Desktop3pModelEntry {
|
|
17
|
+
name: string;
|
|
18
|
+
labelOverride: string;
|
|
19
|
+
anthropicFamilyTier: "opus" | "fable" | "sonnet" | "haiku";
|
|
20
|
+
isFamilyDefault?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Desktop's documented 1M-context capability assertion. Set ONLY from an
|
|
23
|
+
* authoritative routed contextWindow >= 1M — never guessed (devlog 136 B5).
|
|
24
|
+
*/
|
|
25
|
+
supports1m?: true;
|
|
26
|
+
/** When true, Desktop selects the 1M variant by default (official schema, Luna research 260722). */
|
|
27
|
+
prefer1m?: true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* static (default, Pro-verified devlog 138): pinned inferenceModels with
|
|
32
|
+
* modelDiscoveryEnabled:false — a static list OVERRIDES discovery (no merge), so
|
|
33
|
+
* this is the deterministic shape. hybrid keeps discovery:true alongside the list
|
|
34
|
+
* (claude-code-router's version-defensive pattern). discovery: /v1/models only.
|
|
35
|
+
*/
|
|
36
|
+
export type Desktop3pConfigMode = "hybrid" | "discovery" | "static";
|
|
37
|
+
|
|
38
|
+
export interface Desktop3pRoutedModel {
|
|
39
|
+
provider: string;
|
|
40
|
+
id: string;
|
|
41
|
+
/** Authoritative context window (CatalogModel.contextWindow); optional. */
|
|
42
|
+
contextWindow?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 1M-context eligibility, shared with the Desktop DTO so the dashboard's 1M chip can
|
|
47
|
+
* never disagree with what the writer emits. The DTO imports this from here — keeping
|
|
48
|
+
* the constant in this module avoids a cycle, since shared.ts already reads
|
|
49
|
+
* claude/desktop-profile.
|
|
50
|
+
*/
|
|
51
|
+
export const DESKTOP_SUPPORTS_1M_THRESHOLD = 1_000_000;
|
|
52
|
+
|
|
53
|
+
export interface Desktop3pConfigLibraryOptions {
|
|
54
|
+
env?: NodeJS.ProcessEnv;
|
|
55
|
+
platform?: NodeJS.Platform;
|
|
56
|
+
homeDir?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve the config library from the same user-data root Claude Desktop uses. Keeping this in one
|
|
61
|
+
* helper prevents the writer and dashboard status probe from agreeing on a path Desktop never reads.
|
|
62
|
+
*
|
|
63
|
+
* The resolution itself lives in `./desktop-3p-paths`, which ports Claude Desktop's own `GE()`
|
|
64
|
+
* branch for branch — including the `-3p` suffix the app appends to its userData root. Dropping
|
|
65
|
+
* that suffix points us at a directory Desktop never reads (GitHub #539).
|
|
66
|
+
*/
|
|
67
|
+
export function resolveDesktop3pConfigLibraryPath(
|
|
68
|
+
options: Desktop3pConfigLibraryOptions = {},
|
|
69
|
+
): string {
|
|
70
|
+
if (options.env === undefined && options.platform === undefined && options.homeDir === undefined) {
|
|
71
|
+
return claudeDesktopConfigLibraryDir();
|
|
72
|
+
}
|
|
73
|
+
return resolveConfigLibraryDir({
|
|
74
|
+
env: options.env ?? process.env,
|
|
75
|
+
platform: options.platform ?? process.platform,
|
|
76
|
+
home: options.homeDir ?? homedir(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** CLI arg parsing for `ocx claude desktop` mode flags (mutually exclusive). */
|
|
81
|
+
export function parseDesktop3pModeArgs(flags: string[]): { mode: Desktop3pConfigMode } | { error: string } {
|
|
82
|
+
const known = new Map<string, Desktop3pConfigMode>([
|
|
83
|
+
["--static", "static"],
|
|
84
|
+
["--hybrid", "hybrid"],
|
|
85
|
+
["--discovery-only", "discovery"],
|
|
86
|
+
]);
|
|
87
|
+
const unknown = flags.filter(a => !known.has(a));
|
|
88
|
+
if (unknown.length > 0) return { error: `알 수 없는 옵션: ${unknown.join(" ")} (지원: --static, --hybrid, --discovery-only)` };
|
|
89
|
+
const picked = [...new Set(flags.map(a => known.get(a)!))];
|
|
90
|
+
if (picked.length > 1) return { error: "모드 옵션은 하나만 쓸 수 있습니다 (--static | --hybrid | --discovery-only)." };
|
|
91
|
+
return { mode: picked[0] ?? "static" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface Desktop3pMetadataEntry {
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
[key: string]: unknown;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface Desktop3pMetadata {
|
|
101
|
+
appliedId?: string;
|
|
102
|
+
entries: Desktop3pMetadataEntry[];
|
|
103
|
+
[key: string]: unknown;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type Desktop3pLibraryKind =
|
|
107
|
+
| "not_installed"
|
|
108
|
+
| "standard"
|
|
109
|
+
| "gateway_ours"
|
|
110
|
+
| "gateway_drifted"
|
|
111
|
+
| "foreign"
|
|
112
|
+
| "no_owned_state"
|
|
113
|
+
| "broken"
|
|
114
|
+
| "unsafe";
|
|
115
|
+
|
|
116
|
+
export interface Desktop3pLibraryInspection {
|
|
117
|
+
kind: Desktop3pLibraryKind;
|
|
118
|
+
libraryPath: string;
|
|
119
|
+
selectedProfilePath: string | null;
|
|
120
|
+
appliedId: string | null;
|
|
121
|
+
/** Paths of opencodex-owned rows that are not selected by Desktop. */
|
|
122
|
+
residualPaths: string[];
|
|
123
|
+
/** Bounded reason code; never includes metadata or profile contents. */
|
|
124
|
+
reason?: "metadata_unreadable" | "unsafe_applied_id" | "invalid_owned_profile";
|
|
125
|
+
fingerprint?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Whether Desktop's applied selection is our owned entry, by ID match alone.
|
|
128
|
+
* `null` = undeterminable (no metadata, unreadable metadata, or no appliedId);
|
|
129
|
+
* a readable appliedId with no owned entry is a KNOWN false, not unknown.
|
|
130
|
+
* Deliberately independent of profile-file health: the status contract
|
|
131
|
+
* predates this inspector and callers render tri-state.
|
|
132
|
+
*/
|
|
133
|
+
ownedProfileActive: boolean | null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface Desktop3pRemovalResult {
|
|
137
|
+
ok: boolean;
|
|
138
|
+
changed: boolean;
|
|
139
|
+
kind: "removed" | "noop" | "cleanup_incomplete" | "unsafe" | "write_failed";
|
|
140
|
+
libraryPath: string;
|
|
141
|
+
residualPaths?: string[];
|
|
142
|
+
reason?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let desktop3pRegistry = new Map<string, string>();
|
|
146
|
+
let desktop3pAliasesByRoute = new Map<string, string>();
|
|
147
|
+
|
|
148
|
+
/** Derive a stable letter-first, three-character base36 code from a route key. */
|
|
149
|
+
export function deriveDesktop3pCode(route: string): string {
|
|
150
|
+
const hash = createHash("sha256").update(route).digest();
|
|
151
|
+
const n = hash.readUInt32BE(0) % 33696;
|
|
152
|
+
const first = String.fromCharCode(97 + Math.floor(n / 1296));
|
|
153
|
+
const rest = (n % 1296).toString(36).padStart(2, "0");
|
|
154
|
+
return first + rest;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Alias for one proxy model. Real Anthropic models pass through unchanged (they must
|
|
159
|
+
* keep hitting the sk-ant native passthrough); everything else gets a Claude-shaped
|
|
160
|
+
* `claude-opus-4-8-{code}` id. Opus 4.8 is chosen deliberately: Desktop's effort
|
|
161
|
+
* selector is an allowlist keyed on exact supported model ids (Opus 4.8/4.7/4.6,
|
|
162
|
+
* Sonnet 4.6 — devlog 131), and 4.6+ canonical ids are dateless, so the letter-first
|
|
163
|
+
* 3-char suffix can never collide with a real id or a legacy date suffix.
|
|
164
|
+
*/
|
|
165
|
+
export function desktop3pAlias(provider: string, modelId: string): string {
|
|
166
|
+
if (provider === "anthropic" && modelId.startsWith("claude-")) return modelId;
|
|
167
|
+
return `claude-opus-4-8-${deriveDesktop3pCode(`${provider}/${modelId}`)}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Pre-rename alias shape (claude-opus-4-{code}) — still decoded for stale Desktop configs. */
|
|
171
|
+
export function legacyDesktop3pAlias(provider: string, modelId: string): string {
|
|
172
|
+
return `claude-opus-4-${deriveDesktop3pCode(`${provider}/${modelId}`)}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function displayModelId(modelId: string): string {
|
|
176
|
+
return modelId
|
|
177
|
+
// Capability markers like [1m] are not name text: strip the brackets so the label
|
|
178
|
+
// reads "K3 1M", never "K3[1m]".
|
|
179
|
+
.replace(/\[([^\]]+)\]/g, "-$1")
|
|
180
|
+
.split(/[-_]+/)
|
|
181
|
+
.filter(Boolean)
|
|
182
|
+
.map(part => {
|
|
183
|
+
const lower = part.toLowerCase();
|
|
184
|
+
if (lower === "gpt" || lower === "glm" || lower === "ai" || lower === "1m") return lower.toUpperCase();
|
|
185
|
+
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
186
|
+
})
|
|
187
|
+
.join(" ");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function collectDesktop3pModels(
|
|
191
|
+
nativeSlugs: string[],
|
|
192
|
+
routedModels: Array<Desktop3pRoutedModel>,
|
|
193
|
+
profile?: OcxClaudeDesktopProfile,
|
|
194
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
195
|
+
): { models: Desktop3pModelEntry[]; registry: Map<string, string> } {
|
|
196
|
+
const registry = new Map<string, string>();
|
|
197
|
+
const models: Desktop3pModelEntry[] = [];
|
|
198
|
+
const candidates: Desktop3pRoutedModel[] = [
|
|
199
|
+
// Native candidates carry their real context window from the same accessor the
|
|
200
|
+
// Desktop DTO uses, so a native 1M/372k model resolves identically in the written
|
|
201
|
+
// config and on the dashboard.
|
|
202
|
+
...nativeSlugs.map(id => {
|
|
203
|
+
const contextWindow = nativeOpenAiContextWindow(id, nativeContextCap);
|
|
204
|
+
return { provider: "native", id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
205
|
+
}),
|
|
206
|
+
...routedModels,
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
if (profile) {
|
|
210
|
+
const profileModels = candidates.map(({ provider, id, contextWindow }) => ({
|
|
211
|
+
route: `${provider}/${id}`,
|
|
212
|
+
label: `${displayModelId(id)} (${provider})`,
|
|
213
|
+
...(typeof contextWindow === "number" ? { contextWindow } : {}),
|
|
214
|
+
} satisfies DesktopProfileModel));
|
|
215
|
+
const reconciled = reconcileDesktopProfile(profile, profileModels);
|
|
216
|
+
const rendered = renderDesktopProfile(reconciled, profileModels);
|
|
217
|
+
const aliasesByRoute = new Map<string, string>();
|
|
218
|
+
for (const model of rendered) {
|
|
219
|
+
aliasesByRoute.set(model.route, model.name);
|
|
220
|
+
if (!model.route.startsWith("anthropic/claude-")) registry.set(model.name, model.route);
|
|
221
|
+
models.push({
|
|
222
|
+
name: model.name,
|
|
223
|
+
labelOverride: model.label,
|
|
224
|
+
anthropicFamilyTier: model.family,
|
|
225
|
+
...(model.isFamilyDefault ? { isFamilyDefault: true } : {}),
|
|
226
|
+
...(model.supports1m ? { supports1m: true, prefer1m: true } : {}),
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Legacy hashes are compatibility-only and can collide. Bind them in stable route order so
|
|
230
|
+
// changing a family default or rendered ordering can never silently rebind an old Desktop id.
|
|
231
|
+
for (const model of [...rendered].sort((a, b) => a.route.localeCompare(b.route))) {
|
|
232
|
+
if (model.route.startsWith("anthropic/claude-")) continue;
|
|
233
|
+
const providerEnd = model.route.indexOf("/");
|
|
234
|
+
const provider = model.route.slice(0, providerEnd);
|
|
235
|
+
const id = model.route.slice(providerEnd + 1);
|
|
236
|
+
const legacy = legacyDesktop3pAlias(provider, id);
|
|
237
|
+
const existing = registry.get(legacy);
|
|
238
|
+
if (existing && existing !== model.route) {
|
|
239
|
+
console.warn(`[opencodex] Claude Desktop legacy alias collision: ${legacy} stays bound to ${existing}; ignoring ${model.route}`);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
registry.set(legacy, model.route);
|
|
243
|
+
}
|
|
244
|
+
desktop3pAliasesByRoute = aliasesByRoute;
|
|
245
|
+
return { models, registry };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (const { provider, id, contextWindow } of candidates) {
|
|
249
|
+
const route = `${provider}/${id}`;
|
|
250
|
+
const alias = desktop3pAlias(provider, id);
|
|
251
|
+
const supports1m = typeof contextWindow === "number" && contextWindow >= DESKTOP_SUPPORTS_1M_THRESHOLD
|
|
252
|
+
? { supports1m: true as const }
|
|
253
|
+
: {};
|
|
254
|
+
if (alias === id) {
|
|
255
|
+
// Real Anthropic model: keep it OUT of the decode registry — registering it would
|
|
256
|
+
// make resolveInboundModel() non-identity and kill the sk-ant native passthrough
|
|
257
|
+
// (audit 133 #1). It still appears in the static Desktop model list below.
|
|
258
|
+
models.push({
|
|
259
|
+
name: alias,
|
|
260
|
+
labelOverride: `${displayModelId(id)} (${provider})`,
|
|
261
|
+
anthropicFamilyTier: "opus",
|
|
262
|
+
...supports1m,
|
|
263
|
+
...(supports1m.supports1m ? { prefer1m: true as const } : {}),
|
|
264
|
+
});
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const existingRoute = registry.get(alias);
|
|
268
|
+
if (existingRoute !== undefined) {
|
|
269
|
+
console.warn(`[opencodex] Claude Desktop 3P alias collision: ${alias} maps to both ${existingRoute} and ${route}; skipping ${route}`);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
registry.set(alias, route);
|
|
274
|
+
// Back-compat decode for Desktop configs written before the opus-4-8 rename.
|
|
275
|
+
const legacy = legacyDesktop3pAlias(provider, id);
|
|
276
|
+
if (!registry.has(legacy)) registry.set(legacy, route);
|
|
277
|
+
models.push({
|
|
278
|
+
name: alias,
|
|
279
|
+
labelOverride: `${displayModelId(id)} (${provider})`,
|
|
280
|
+
anthropicFamilyTier: "opus",
|
|
281
|
+
...supports1m,
|
|
282
|
+
...(supports1m.supports1m ? { prefer1m: true as const } : {}),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (models[0]) models[0].isFamilyDefault = true;
|
|
287
|
+
desktop3pAliasesByRoute = new Map(candidates.map(({ provider, id }) => [`${provider}/${id}`, desktop3pAlias(provider, id)]));
|
|
288
|
+
return { models, registry };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Build and install the registry used to decode Desktop aliases. */
|
|
292
|
+
export function buildDesktop3pRegistry(
|
|
293
|
+
nativeSlugs: string[],
|
|
294
|
+
routedModels: Array<Desktop3pRoutedModel>,
|
|
295
|
+
profile?: OcxClaudeDesktopProfile,
|
|
296
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
297
|
+
): Map<string, string> {
|
|
298
|
+
const { registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
|
|
299
|
+
desktop3pRegistry = registry;
|
|
300
|
+
return registry;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Generate Claude Desktop 3P model entries from the proxy's available models. */
|
|
304
|
+
export function generateDesktop3pModels(
|
|
305
|
+
nativeSlugs: string[],
|
|
306
|
+
routedModels: Array<Desktop3pRoutedModel>,
|
|
307
|
+
profile?: OcxClaudeDesktopProfile,
|
|
308
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
309
|
+
): Desktop3pModelEntry[] {
|
|
310
|
+
const { models, registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
|
|
311
|
+
desktop3pRegistry = registry;
|
|
312
|
+
return models;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Resolve an alias using the most recently generated Desktop model registry. */
|
|
316
|
+
export function resolveDesktop3pAlias(alias: string): string | null {
|
|
317
|
+
return desktop3pRegistry.get(alias) ?? null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Alias selected by the installed profile registry, falling back to the legacy hash shape. */
|
|
321
|
+
export function activeDesktop3pAlias(provider: string, modelId: string): string {
|
|
322
|
+
return desktop3pAliasesByRoute.get(`${provider}/${modelId}`) ?? desktop3pAlias(provider, modelId);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Generate the complete Claude Desktop 3P gateway config.
|
|
327
|
+
*
|
|
328
|
+
* Default mode is "static" (Pro-verified, devlog 138): the static list is the ONLY
|
|
329
|
+
* channel for supports1m/tier pins and it overrides discovery anyway (no merge), so
|
|
330
|
+
* discovery stays off for determinism. supports1m makes Desktop offer a separate 1M
|
|
331
|
+
* row; selecting it sends the bare id + `anthropic-beta: context-1m-2025-08-07`.
|
|
332
|
+
*/
|
|
333
|
+
export function generateDesktop3pConfig(
|
|
334
|
+
port: number,
|
|
335
|
+
nativeSlugs: string[],
|
|
336
|
+
routedModels: Array<Desktop3pRoutedModel>,
|
|
337
|
+
apiKey = "ocx",
|
|
338
|
+
mode: Desktop3pConfigMode = "static",
|
|
339
|
+
profile?: OcxClaudeDesktopProfile,
|
|
340
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
341
|
+
): object {
|
|
342
|
+
const base = {
|
|
343
|
+
inferenceProvider: "gateway",
|
|
344
|
+
inferenceCredentialKind: "static",
|
|
345
|
+
inferenceGatewayBaseUrl: `http://127.0.0.1:${port}`,
|
|
346
|
+
inferenceGatewayApiKey: apiKey,
|
|
347
|
+
};
|
|
348
|
+
if (mode === "discovery") {
|
|
349
|
+
// Build/refresh the decode registry even though no static list is emitted.
|
|
350
|
+
buildDesktop3pRegistry(nativeSlugs, routedModels, profile, nativeContextCap);
|
|
351
|
+
return { ...base, modelDiscoveryEnabled: true };
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
...base,
|
|
355
|
+
modelDiscoveryEnabled: mode === "hybrid",
|
|
356
|
+
inferenceModels: (() => {
|
|
357
|
+
const models = generateDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
|
|
358
|
+
// Fail loud at the write boundary rather than ship a config Desktop rejects:
|
|
359
|
+
// the output counterpart of the request-path guards.
|
|
360
|
+
assertDesktop3pModelsValid(models);
|
|
361
|
+
return models;
|
|
362
|
+
})(),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function parseMetadata(path: string): Desktop3pMetadata {
|
|
367
|
+
if (!existsSync(path)) return { entries: [] };
|
|
368
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as Partial<Desktop3pMetadata>;
|
|
369
|
+
if (!Array.isArray(parsed.entries)) throw new Error("Claude Desktop 3P _meta.json has no entries array");
|
|
370
|
+
return { ...parsed, entries: parsed.entries };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const SAFE_DESKTOP_PROFILE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
374
|
+
|
|
375
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
376
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function isOwnedDesktopEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
|
|
380
|
+
return entry?.name === "opencodex" || entry?.name === "opencodex-standard";
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** A gateway row is removable; the selected standard row must always remain. */
|
|
384
|
+
function isOwnedDesktopGatewayEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
|
|
385
|
+
return entry?.name === "opencodex";
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function profilePath(libraryPath: string, id: string): string {
|
|
389
|
+
return join(libraryPath, `${id}.json`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Read Desktop's selected config without changing its library.
|
|
394
|
+
*
|
|
395
|
+
* This is intentionally separate from the eager writer below: status probes must
|
|
396
|
+
* never manufacture a config-library directory on a machine without Desktop.
|
|
397
|
+
*/
|
|
398
|
+
export function inspectDesktop3pConfigLibrary(
|
|
399
|
+
options: Desktop3pConfigLibraryOptions & { appliedFingerprint?: string | null } = {},
|
|
400
|
+
): Desktop3pLibraryInspection {
|
|
401
|
+
const libraryPath = resolveDesktop3pConfigLibraryPath(options);
|
|
402
|
+
if (!existsSync(libraryPath)) {
|
|
403
|
+
return { kind: "not_installed", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const metadataPath = join(libraryPath, "_meta.json");
|
|
407
|
+
if (!existsSync(metadataPath)) {
|
|
408
|
+
return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let metadata: Desktop3pMetadata;
|
|
412
|
+
try {
|
|
413
|
+
metadata = parseMetadata(metadataPath);
|
|
414
|
+
} catch {
|
|
415
|
+
return {
|
|
416
|
+
kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], reason: "metadata_unreadable", ownedProfileActive: null,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
const appliedId = typeof metadata.appliedId === "string" ? metadata.appliedId : null;
|
|
420
|
+
if (appliedId === null) {
|
|
421
|
+
return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
|
|
422
|
+
}
|
|
423
|
+
const selected = metadata.entries.find(entry => entry?.id === appliedId);
|
|
424
|
+
// A readable appliedId with no owned entry is a KNOWN false, not unknown.
|
|
425
|
+
const ownedProfileActive = isOwnedDesktopEntry(selected);
|
|
426
|
+
if (!SAFE_DESKTOP_PROFILE_ID.test(appliedId)) {
|
|
427
|
+
return {
|
|
428
|
+
kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId, residualPaths: [], reason: "unsafe_applied_id", ownedProfileActive,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const selectedProfilePath = profilePath(libraryPath, appliedId);
|
|
433
|
+
const residualPaths = metadata.entries
|
|
434
|
+
.filter(entry => isOwnedDesktopGatewayEntry(entry) && entry.id !== appliedId && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
|
|
435
|
+
.flatMap(entry => [profilePath(libraryPath, entry.id), `${profilePath(libraryPath, entry.id)}.bak`])
|
|
436
|
+
.filter(existsSync);
|
|
437
|
+
if (!existsSync(selectedProfilePath)) {
|
|
438
|
+
return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
let profile: Record<string, unknown>;
|
|
442
|
+
let fingerprint: string;
|
|
443
|
+
try {
|
|
444
|
+
const source = readFileSync(selectedProfilePath, "utf8");
|
|
445
|
+
const parsed = JSON.parse(source) as unknown;
|
|
446
|
+
if (!isRecord(parsed)) return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
|
|
447
|
+
profile = parsed;
|
|
448
|
+
fingerprint = createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
449
|
+
} catch {
|
|
450
|
+
return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
|
|
451
|
+
}
|
|
452
|
+
if (!isOwnedDesktopEntry(selected)) {
|
|
453
|
+
return { kind: "foreign", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
|
|
454
|
+
}
|
|
455
|
+
if (profile.inferenceProvider === undefined) {
|
|
456
|
+
return { kind: "standard", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
|
|
457
|
+
}
|
|
458
|
+
const validGateway = profile.inferenceProvider === "gateway"
|
|
459
|
+
&& profile.inferenceCredentialKind === "static"
|
|
460
|
+
&& typeof profile.inferenceGatewayBaseUrl === "string"
|
|
461
|
+
&& typeof profile.inferenceGatewayApiKey === "string";
|
|
462
|
+
if (!validGateway) {
|
|
463
|
+
return {
|
|
464
|
+
kind: "unsafe", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, reason: "invalid_owned_profile", ownedProfileActive,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
kind: options.appliedFingerprint && options.appliedFingerprint === fingerprint ? "gateway_ours" : "gateway_drifted",
|
|
469
|
+
libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Select a credential-free standard profile before deleting an owned gateway.
|
|
475
|
+
* The old metadata row remains as a retry locator only until both its profile
|
|
476
|
+
* and backup are absent; successful cleanup removes it in the same operation.
|
|
477
|
+
*
|
|
478
|
+
* `gateway_drifted` is still an owned opencodex gateway (name + valid shape); the
|
|
479
|
+
* fingerprint only says on-disk bytes differ from the last saved marker. Refusing
|
|
480
|
+
* OFF for drift left users unable to disable after a lost `appliedFingerprint`
|
|
481
|
+
* (or any other benign mismatch), while the Integrations card still showed the
|
|
482
|
+
* leftover profile as applied/stale.
|
|
483
|
+
*/
|
|
484
|
+
export function removeDesktop3pStandardPivot(
|
|
485
|
+
options: Desktop3pConfigLibraryOptions & {
|
|
486
|
+
appliedFingerprint?: string | null;
|
|
487
|
+
unlink?: (path: string) => void;
|
|
488
|
+
} = {},
|
|
489
|
+
): Desktop3pRemovalResult {
|
|
490
|
+
const inspected = inspectDesktop3pConfigLibrary(options);
|
|
491
|
+
if (inspected.kind === "not_installed" || inspected.kind === "no_owned_state") {
|
|
492
|
+
return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
|
|
493
|
+
}
|
|
494
|
+
if (inspected.kind === "broken" || inspected.kind === "unsafe") {
|
|
495
|
+
return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: inspected.reason };
|
|
496
|
+
}
|
|
497
|
+
if (!inspected.appliedId || !SAFE_DESKTOP_PROFILE_ID.test(inspected.appliedId)) {
|
|
498
|
+
return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: "unsafe_applied_id" };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const metadataPath = join(inspected.libraryPath, "_meta.json");
|
|
502
|
+
try {
|
|
503
|
+
const metadata = parseMetadata(metadataPath);
|
|
504
|
+
const selectedId = inspected.appliedId;
|
|
505
|
+
// When Desktop is actively using our gateway (current or drifted), pivot only
|
|
506
|
+
// that selected row first. Any second owned row is residue for a later
|
|
507
|
+
// standard-mode retry; this preserves the selected-row preference after an
|
|
508
|
+
// interrupted cleanup.
|
|
509
|
+
const selectedOwnedGatewayActive =
|
|
510
|
+
inspected.kind === "gateway_ours" || inspected.kind === "gateway_drifted";
|
|
511
|
+
const targetIds = selectedOwnedGatewayActive
|
|
512
|
+
? [selectedId]
|
|
513
|
+
: metadata.entries
|
|
514
|
+
.filter(isOwnedDesktopGatewayEntry)
|
|
515
|
+
.map(entry => entry.id)
|
|
516
|
+
.filter(id => SAFE_DESKTOP_PROFILE_ID.test(id));
|
|
517
|
+
if (targetIds.length === 0) return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
|
|
518
|
+
|
|
519
|
+
let metadataAfterPivot = metadata;
|
|
520
|
+
if (selectedOwnedGatewayActive) {
|
|
521
|
+
const standardId = randomUUID();
|
|
522
|
+
const standardPath = profilePath(inspected.libraryPath, standardId);
|
|
523
|
+
atomicWriteFile(standardPath, "{}\n");
|
|
524
|
+
const standardEntry: Desktop3pMetadataEntry = { id: standardId, name: "opencodex-standard" };
|
|
525
|
+
metadataAfterPivot = { ...metadata, appliedId: standardId, entries: [...metadata.entries, standardEntry] };
|
|
526
|
+
atomicWriteFile(metadataPath, JSON.stringify(metadataAfterPivot, null, 2) + "\n");
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const residualPaths: string[] = [];
|
|
530
|
+
for (const id of targetIds) {
|
|
531
|
+
for (const candidate of [profilePath(inspected.libraryPath, id), `${profilePath(inspected.libraryPath, id)}.bak`]) {
|
|
532
|
+
try {
|
|
533
|
+
if (existsSync(candidate)) (options.unlink ?? unlinkSync)(candidate);
|
|
534
|
+
} catch {
|
|
535
|
+
// Only the path is allowed to leave this credential-bearing cleanup boundary.
|
|
536
|
+
}
|
|
537
|
+
if (existsSync(candidate)) residualPaths.push(candidate);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const ownedResiduePaths = metadataAfterPivot.entries
|
|
541
|
+
.filter(entry => isOwnedDesktopGatewayEntry(entry) && !targetIds.includes(entry.id) && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
|
|
542
|
+
.flatMap(entry => [profilePath(inspected.libraryPath, entry.id), `${profilePath(inspected.libraryPath, entry.id)}.bak`])
|
|
543
|
+
.filter(existsSync);
|
|
544
|
+
if (residualPaths.length > 0 || ownedResiduePaths.length > 0) {
|
|
545
|
+
return {
|
|
546
|
+
ok: false, changed: true, kind: "cleanup_incomplete", libraryPath: inspected.libraryPath,
|
|
547
|
+
residualPaths: [...new Set([...residualPaths, ...ownedResiduePaths])],
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
// Do not leave a metadata row pointing at a deleted profile. For a foreign
|
|
551
|
+
// selection this only removes proven opencodex residues; appliedId is kept.
|
|
552
|
+
atomicWriteFile(
|
|
553
|
+
metadataPath,
|
|
554
|
+
JSON.stringify({ ...metadataAfterPivot, entries: metadataAfterPivot.entries.filter(entry => !targetIds.includes(entry.id)) }, null, 2) + "\n",
|
|
555
|
+
);
|
|
556
|
+
return { ok: true, changed: true, kind: "removed", libraryPath: inspected.libraryPath };
|
|
557
|
+
} catch {
|
|
558
|
+
return { ok: false, changed: false, kind: "write_failed", libraryPath: inspected.libraryPath };
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/** Write and apply the opencodex config in Claude Desktop 3P's config library. */
|
|
563
|
+
export function writeDesktop3pConfig(
|
|
564
|
+
port: number,
|
|
565
|
+
nativeSlugs: string[],
|
|
566
|
+
routedModels: Array<Desktop3pRoutedModel>,
|
|
567
|
+
apiKey?: string,
|
|
568
|
+
mode: Desktop3pConfigMode = "static",
|
|
569
|
+
profile?: OcxClaudeDesktopProfile,
|
|
570
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
571
|
+
): { written: boolean; path: string; reason?: string; fingerprint?: string } {
|
|
572
|
+
const libraryPath = resolveDesktop3pConfigLibraryPath();
|
|
573
|
+
const metadataPath = join(libraryPath, "_meta.json");
|
|
574
|
+
let configPath = libraryPath;
|
|
575
|
+
|
|
576
|
+
try {
|
|
577
|
+
mkdirSync(libraryPath, { recursive: true, mode: 0o700 });
|
|
578
|
+
const metadata = parseMetadata(metadataPath);
|
|
579
|
+
const selected = metadata.entries.find(entry => entry?.id === metadata.appliedId && isOwnedDesktopGatewayEntry(entry));
|
|
580
|
+
const existing = selected ?? metadata.entries.find(entry => isOwnedDesktopGatewayEntry(entry) && typeof entry.id === "string");
|
|
581
|
+
const id = existing?.id ?? randomUUID();
|
|
582
|
+
configPath = join(libraryPath, `${id}.json`);
|
|
583
|
+
const entry: Desktop3pMetadataEntry = existing ? { ...existing, id, name: "opencodex" } : { id, name: "opencodex" };
|
|
584
|
+
const entries = existing
|
|
585
|
+
? metadata.entries.map(current => current === existing ? entry : current)
|
|
586
|
+
: [...metadata.entries, entry];
|
|
587
|
+
|
|
588
|
+
const configJson = JSON.stringify(generateDesktop3pConfig(port, nativeSlugs, routedModels, apiKey, mode, profile, nativeContextCap), null, 2) + "\n";
|
|
589
|
+
const fingerprint = createHash("sha256").update(configJson).digest("hex").slice(0, 16);
|
|
590
|
+
const { backupPath } = atomicReplaceDesktopConfig(configPath, configJson);
|
|
591
|
+
try {
|
|
592
|
+
atomicWriteFile(metadataPath, JSON.stringify({ ...metadata, appliedId: id, entries }, null, 2) + "\n");
|
|
593
|
+
} catch (metaError) {
|
|
594
|
+
// Rollback: restore the backed-up config if metadata write fails.
|
|
595
|
+
if (backupPath && existsSync(backupPath)) copyFileSync(backupPath, configPath);
|
|
596
|
+
throw metaError;
|
|
597
|
+
}
|
|
598
|
+
return { written: true, path: configPath, fingerprint };
|
|
599
|
+
} catch (error) {
|
|
600
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
601
|
+
return { written: false, path: configPath, reason };
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** Backup an existing owned config then atomically replace it. Exported for failure-path tests. */
|
|
606
|
+
export function atomicReplaceDesktopConfig(
|
|
607
|
+
path: string,
|
|
608
|
+
content: string,
|
|
609
|
+
writer: (path: string, content: string) => void = atomicWriteFile,
|
|
610
|
+
): { backupPath?: string } {
|
|
611
|
+
const backupPath = `${path}.bak`;
|
|
612
|
+
if (existsSync(path)) copyFileSync(path, backupPath);
|
|
613
|
+
writer(path, content);
|
|
614
|
+
return existsSync(backupPath) ? { backupPath } : {};
|
|
615
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** In-memory Desktop health tracker (resets on server restart). */
|
|
2
|
+
|
|
3
|
+
interface DesktopHealthState {
|
|
4
|
+
lastRequestAt: number | null;
|
|
5
|
+
requestCount: number;
|
|
6
|
+
errorCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const state: DesktopHealthState = { lastRequestAt: null, requestCount: 0, errorCount: 0 };
|
|
10
|
+
|
|
11
|
+
export function recordDesktopRequest(): void {
|
|
12
|
+
state.lastRequestAt = Date.now();
|
|
13
|
+
state.requestCount++;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function recordDesktopError(): void {
|
|
17
|
+
state.errorCount++;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getDesktopHealth(): { lastRequestAt: string | null; requestCount: number; errorCount: number } {
|
|
21
|
+
return {
|
|
22
|
+
lastRequestAt: state.lastRequestAt !== null ? new Date(state.lastRequestAt).toISOString() : null,
|
|
23
|
+
requestCount: state.requestCount,
|
|
24
|
+
errorCount: state.errorCount,
|
|
25
|
+
};
|
|
26
|
+
}
|