@remodex/rmx 1.0.2
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 +91 -0
- package/LICENSE +21 -0
- package/README.md +242 -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 +584 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-CZqebSPQ.css +1 -0
- package/gui/dist/assets/index-CkETtt7P.js +71 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
- package/gui/dist/fonts/google-sans-latin.woff2 +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 +118 -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.ts +1205 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +83 -0
- package/src/adapters/client-fingerprint.ts +59 -0
- package/src/adapters/command-code.ts +453 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/cursor-errors.ts +165 -0
- package/src/adapters/cursor/discovery.ts +276 -0
- package/src/adapters/cursor/effort-map.ts +139 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +153 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1235 -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 +59 -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 +548 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +604 -0
- package/src/adapters/cursor/protobuf-events.ts +735 -0
- package/src/adapters/cursor/protobuf-request.ts +719 -0
- package/src/adapters/cursor/request-builder.ts +280 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +621 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +59 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +520 -0
- package/src/adapters/google-antigravity-wire.ts +140 -0
- package/src/adapters/google-errors.ts +85 -0
- package/src/adapters/google-http.ts +100 -0
- package/src/adapters/google-tool-schema.ts +173 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +859 -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 +104 -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 +1924 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1265 -0
- package/src/adapters/openai-responses.ts +1309 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-catalog-nudge.ts +71 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/android-remote/assets.ts +218 -0
- package/src/android-remote/attachments.ts +168 -0
- package/src/android-remote/auth.ts +237 -0
- package/src/android-remote/cloudflare-provisioning.ts +409 -0
- package/src/android-remote/cloudflare-secret.ts +106 -0
- package/src/android-remote/cloudflare-tunnel.ts +488 -0
- package/src/android-remote/cloudflared.ts +286 -0
- package/src/android-remote/codex-app-server.ts +565 -0
- package/src/android-remote/desktop-history-page.ts +936 -0
- package/src/android-remote/desktop-ipc.ts +3643 -0
- package/src/android-remote/desktop-ownership-store.ts +98 -0
- package/src/android-remote/desktop-project-registration.ts +129 -0
- package/src/android-remote/desktop-session-stream.ts +1110 -0
- package/src/android-remote/desktop-workspace-state.ts +443 -0
- package/src/android-remote/file-change-parser.ts +112 -0
- package/src/android-remote/gateway.ts +9108 -0
- package/src/android-remote/mutation-store.ts +299 -0
- package/src/android-remote/projection.ts +1780 -0
- package/src/android-remote/queued-turn-store.ts +248 -0
- package/src/android-remote/session-command-recovery.ts +1384 -0
- package/src/android-remote/store.ts +466 -0
- package/src/android-remote/thread-reconciliation.ts +133 -0
- package/src/android-remote/thread-source-paths.ts +307 -0
- package/src/android-remote/thread-stream.ts +546 -0
- package/src/android-remote/turn-activity.ts +235 -0
- package/src/android-remote/user-message-identity.ts +94 -0
- package/src/bridge.ts +1793 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +267 -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 +189 -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 +601 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +70 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +519 -0
- package/src/claude/model-info.ts +154 -0
- package/src/claude/outbound.ts +898 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +296 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +476 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +297 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +184 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-desktop.ts +211 -0
- package/src/cli/claude.ts +302 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/codex-shim-readiness.ts +69 -0
- package/src/cli/combo.ts +124 -0
- package/src/cli/config-command.ts +183 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/desktop-first-run.ts +25 -0
- package/src/cli/doctor.ts +1022 -0
- package/src/cli/export-command.ts +201 -0
- package/src/cli/help.ts +370 -0
- package/src/cli/index.ts +1565 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +225 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +35 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/models-runtime.ts +224 -0
- package/src/cli/models.ts +340 -0
- package/src/cli/observe.ts +170 -0
- package/src/cli/opencode.ts +587 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +476 -0
- package/src/cli/ready.ts +301 -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 +321 -0
- package/src/cli/system-command.ts +196 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +205 -0
- package/src/cli/v2.ts +200 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1109 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -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 +517 -0
- package/src/codex/account-usability.ts +40 -0
- package/src/codex/admission.ts +263 -0
- package/src/codex/app-server-processes.ts +799 -0
- package/src/codex/auth-api.ts +2098 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +480 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +471 -0
- package/src/codex/catalog/bundled.ts +533 -0
- package/src/codex/catalog/effort.ts +432 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +287 -0
- package/src/codex/catalog/native-models.ts +7 -0
- package/src/codex/catalog/parsing.ts +503 -0
- package/src/codex/catalog/provider-fetch.ts +2267 -0
- package/src/codex/catalog/sync.ts +1606 -0
- package/src/codex/catalog-admission.ts +197 -0
- package/src/codex/catalog-refresh-status.ts +87 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +15 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +593 -0
- package/src/codex/convergence.ts +580 -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/desktop-client-processes.ts +521 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1091 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +347 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +115 -0
- package/src/codex/history-provider.ts +1075 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +204 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +257 -0
- package/src/codex/inject.ts +1857 -0
- package/src/codex/injected-marker.ts +79 -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 +105 -0
- package/src/codex/journal.ts +172 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/management-convergence.ts +114 -0
- package/src/codex/model-cache.ts +267 -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 +167 -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 +348 -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 +691 -0
- package/src/codex/paths.ts +78 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +426 -0
- package/src/codex/prompt-journal.ts +311 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/provider-adoption.ts +242 -0
- package/src/codex/quota-rejection.ts +224 -0
- package/src/codex/quota.ts +494 -0
- package/src/codex/refresh.ts +60 -0
- package/src/codex/routing.ts +1855 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +1215 -0
- package/src/codex/subagent-defaults.ts +557 -0
- package/src/codex/subagent-model-fallback.ts +560 -0
- package/src/codex/sync.ts +238 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +374 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +44 -0
- package/src/combos/request.ts +64 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +392 -0
- package/src/config.ts +3270 -0
- package/src/generated/model-metadata.ts +144 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +540 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +127 -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 +922 -0
- package/src/images/plan.ts +133 -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 +151 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +108 -0
- package/src/integrations/serialize.ts +235 -0
- package/src/integrations/state.ts +290 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer.ts +492 -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 +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +242 -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 +127 -0
- package/src/lib/config-ownership.ts +438 -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 +316 -0
- package/src/lib/errors.ts +364 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +182 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-environment.ts +470 -0
- package/src/lib/provider-outbound.ts +203 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +510 -0
- package/src/lib/remodex-home.ts +616 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +178 -0
- package/src/lib/shadow-call.ts +54 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +109 -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 +98 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +366 -0
- package/src/lib/upstream-reachability.ts +91 -0
- package/src/lib/upstream-retry.ts +508 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-elevation.ts +705 -0
- package/src/lib/windows-secret-acl.ts +817 -0
- package/src/lib/windows-user-principal.ts +283 -0
- package/src/lib/winsw.ts +402 -0
- package/src/model-sources.ts +73 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +177 -0
- package/src/oauth/callback-server.ts +294 -0
- package/src/oauth/chatgpt.ts +150 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +231 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +443 -0
- package/src/oauth/index.ts +1280 -0
- package/src/oauth/key-providers.ts +128 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +121 -0
- package/src/oauth/log.ts +48 -0
- package/src/oauth/login-cli.ts +163 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +655 -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 +317 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/codex-capacity.ts +288 -0
- package/src/providers/command-code-efforts.ts +85 -0
- package/src/providers/context-cap.ts +73 -0
- package/src/providers/derive.ts +451 -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 +361 -0
- package/src/providers/openai-sidecar.ts +235 -0
- package/src/providers/openai-tier-startup.ts +27 -0
- package/src/providers/openai-tiers.ts +301 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +179 -0
- package/src/providers/quota.ts +1942 -0
- package/src/providers/reasoning-capabilities.ts +336 -0
- package/src/providers/registry.ts +2375 -0
- package/src/providers/slug-codec.ts +74 -0
- package/src/providers/xai-transport.ts +149 -0
- package/src/reasoning-effort.ts +243 -0
- package/src/responses/compaction.ts +124 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/parser.ts +714 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +106 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +431 -0
- package/src/responses/state.ts +1039 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +802 -0
- package/src/routing/analytics.ts +377 -0
- package/src/routing/capability.ts +205 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +444 -0
- package/src/routing/health.ts +401 -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 +424 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +686 -0
- package/src/server/adapter-resolve.ts +83 -0
- package/src/server/auth-cors.ts +606 -0
- package/src/server/chat-completions.ts +379 -0
- package/src/server/claude-messages.ts +980 -0
- package/src/server/effort-policy.ts +251 -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 +485 -0
- package/src/server/index.ts +1633 -0
- package/src/server/lifecycle.ts +482 -0
- package/src/server/live.ts +609 -0
- package/src/server/management/agent-settings-routes.ts +1180 -0
- package/src/server/management/android-remote-routes.ts +390 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/combo-routes.ts +244 -0
- package/src/server/management/config-routes.ts +602 -0
- package/src/server/management/context.ts +88 -0
- package/src/server/management/integration-routes.ts +538 -0
- package/src/server/management/logs-usage-routes.ts +516 -0
- package/src/server/management/model-routes.ts +519 -0
- package/src/server/management/model-rows.ts +143 -0
- package/src/server/management/native-integration-routes.ts +781 -0
- package/src/server/management/oauth-account-routes.ts +573 -0
- package/src/server/management/provider-routes.ts +781 -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 +384 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +433 -0
- package/src/server/management/system-routes.ts +141 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +269 -0
- package/src/server/management-auth.ts +353 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +326 -0
- package/src/server/proxy-stop.ts +92 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1179 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1072 -0
- package/src/server/responses/collaboration.ts +409 -0
- package/src/server/responses/compact.ts +710 -0
- package/src/server/responses/core.ts +3561 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +171 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +152 -0
- package/src/server/responses/terminal-guard.ts +230 -0
- package/src/server/responses/upstream-error.ts +48 -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 +52 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses.ts +10 -0
- package/src/server/search.ts +181 -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 +308 -0
- package/src/server/startup-health-cache.ts +119 -0
- package/src/server/system-env.ts +418 -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 +470 -0
- package/src/service-manager-probe.ts +824 -0
- package/src/service.ts +3011 -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 +738 -0
- package/src/types.ts +1531 -0
- package/src/update/badge.ts +72 -0
- package/src/update/desktop-release.ts +1620 -0
- package/src/update/index.ts +402 -0
- package/src/update/job.ts +1906 -0
- package/src/update/notify.ts +261 -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/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +283 -0
- package/src/usage/log.ts +695 -0
- package/src/usage/summary.ts +585 -0
- package/src/usage/totals.ts +14 -0
- package/src/vision/anthropic-describe.ts +185 -0
- package/src/vision/describe.ts +127 -0
- package/src/vision/index.ts +558 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/web-search/anthropic-executor.ts +189 -0
- package/src/web-search/executor.ts +105 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/index.ts +196 -0
- package/src/web-search/loop.ts +791 -0
- package/src/web-search/parse.ts +235 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import type { CatalogModel } from "../../codex/catalog";
|
|
4
|
+
import { catalogModelSlug, invalidateCodexModelsCache, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
7
|
+
codexAutoStartEnabled,
|
|
8
|
+
hasOwnProvider,
|
|
9
|
+
isValidProviderName,
|
|
10
|
+
multiAgentGuidanceEnabled,
|
|
11
|
+
providerBaseUrlConfigError,
|
|
12
|
+
providerHeadersConfigError,
|
|
13
|
+
saveConfigPreservingClaudeCode,
|
|
14
|
+
} from "../../config";
|
|
15
|
+
import {
|
|
16
|
+
clearLoginState,
|
|
17
|
+
getLoginStatus,
|
|
18
|
+
isPublicOAuthProvider,
|
|
19
|
+
listOAuthProviders,
|
|
20
|
+
startLoginFlow,
|
|
21
|
+
submitManualLoginCode,
|
|
22
|
+
upsertOAuthProvider,
|
|
23
|
+
} from "../../oauth";
|
|
24
|
+
import { removeCredential } from "../../oauth/store";
|
|
25
|
+
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
26
|
+
import { isStreamMode } from "../../lib/bun-stream-caps";
|
|
27
|
+
import { shadowSourceModels } from "../../lib/shadow-call";
|
|
28
|
+
import {
|
|
29
|
+
configureAppOwnedMemoryBudget,
|
|
30
|
+
enforceAppOwnedMemoryBudget,
|
|
31
|
+
MAX_APP_OWNED_MEMORY_BUDGET_MB,
|
|
32
|
+
MIN_APP_OWNED_MEMORY_BUDGET_MB,
|
|
33
|
+
resolveAppOwnedMemoryBudgetBytes,
|
|
34
|
+
} from "../../lib/app-owned-memory";
|
|
35
|
+
import { enrichProviderFromCatalog, listKeyLoginProviders } from "../../oauth/key-providers";
|
|
36
|
+
import { deriveProviderPresets } from "../../providers/derive";
|
|
37
|
+
import { providerCodexAccountMode } from "../../providers/registry";
|
|
38
|
+
import { routedSlug, slugEquals } from "../../providers/slug-codec";
|
|
39
|
+
import { clearProviderQuotaCache, fetchProviderQuotaReports } from "../../providers/quota";
|
|
40
|
+
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
41
|
+
import { clearThreadAccountMap } from "../../codex/routing";
|
|
42
|
+
import { primeCodexPoolQuotas } from "../../codex/auth-api";
|
|
43
|
+
import {
|
|
44
|
+
codexAccountPickerEnabled,
|
|
45
|
+
initializeDefaultCodexAccountNamespaces,
|
|
46
|
+
} from "../../codex/account-namespaces";
|
|
47
|
+
import { catalogRefreshIsPending } from "../../codex/catalog-refresh-status";
|
|
48
|
+
import { DEFAULT_PROVIDER_CONTEXT_CAP, globalContextCapValue, providerContextCap, providerContextCaps, setAllProviderContextCaps, setGlobalContextCapValue, setProviderContextCap } from "../../providers/context-cap";
|
|
49
|
+
import { resolveCodexHomeDir } from "../../codex/home";
|
|
50
|
+
import { readUsageEntries } from "../../usage/log";
|
|
51
|
+
import { getUsageDebugLogEntries } from "../../usage/debug";
|
|
52
|
+
import { parseRange, parseUsageSurface, summarizeUsage } from "../../usage/summary";
|
|
53
|
+
import { stripCodexRuntimeProviderFields } from "../../codex/auth-context";
|
|
54
|
+
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
55
|
+
import { VISION_REASONING_EFFORTS, isVisionReasoningEffort } from "../../reasoning-effort";
|
|
56
|
+
import { normalizeVisionReasoningForModel } from "../../vision/reasoning";
|
|
57
|
+
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
58
|
+
import { getInjectionDebugLogEntries } from "../../lib/injection-debug-log";
|
|
59
|
+
import {
|
|
60
|
+
clearDebugSettings,
|
|
61
|
+
clearDebugSetting,
|
|
62
|
+
getDebugSettings,
|
|
63
|
+
setDebugSettings,
|
|
64
|
+
type DebugFlag,
|
|
65
|
+
} from "../../lib/debug-settings";
|
|
66
|
+
import type { OcxClaudeCodeConfig, OcxConfig, OcxCustomModel, OcxProviderConfig } from "../../types";
|
|
67
|
+
import { drainAndShutdown } from "../lifecycle";
|
|
68
|
+
import { filterRequestLogs, getRequestLogEntries, type RequestLogEntry } from "../request-log";
|
|
69
|
+
import { estimateComboCost, estimateRequestCost, normalizeCostTokens, tokensPerSecond } from "../../usage/cost";
|
|
70
|
+
import type { PersistedUsageAttempt } from "../../usage/log";
|
|
71
|
+
import { isAllowedRequestOrigin, jsonResponse, providerManagementConfigError, publicProviderBaseUrl, safeConfigDTO } from "../auth-cors";
|
|
72
|
+
import { applySystemEnvToggle } from "../system-env";
|
|
73
|
+
import { getCachedStartupHealth, invalidateStartupHealthCache } from "../startup-health-cache";
|
|
74
|
+
import { runWindowsTrayAction } from "../windows-tray-control";
|
|
75
|
+
import { runStartupInstallAction, type StartupInstallAction } from "../startup-action-control";
|
|
76
|
+
import { displayCodexRuntimePath, effortClampAppliesToRuntime, loadLastEffortClamp, resolveCodexRuntime } from "../../codex/runtime";
|
|
77
|
+
|
|
78
|
+
import { isPlainRecord, parseDebugLogQuery, tokPerSecondResult, unavailableCostReason, costResult, requestLogDto, stripRegistryOnlyStaticHeaders, fetchAllModels } from "./shared";
|
|
79
|
+
import type { MetricUnavailableReason, TokPerSecondResult, CostEstimateReason, CostResult, MetricSource } from "./shared";
|
|
80
|
+
import type { ManagementContext } from "./context";
|
|
81
|
+
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
82
|
+
|
|
83
|
+
export async function handleConfigRoutes(ctx: ManagementContext): Promise<Response | null> {
|
|
84
|
+
const { req, url, config, deps, convergeCodexCatalog, syncClaudeAgentDefsBestEffort } = ctx;
|
|
85
|
+
if (url.pathname === "/api/config" && req.method === "GET") {
|
|
86
|
+
return jsonResponse(safeConfigDTO(config));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (url.pathname === "/api/config" && req.method === "PUT") {
|
|
90
|
+
return jsonResponse({ error: "Full config PUT is disabled. Use /api/providers POST for provider changes." }, 405);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (url.pathname === "/api/settings" && req.method === "GET") {
|
|
94
|
+
let resolved: ReturnType<typeof resolveCodexRuntime>;
|
|
95
|
+
try {
|
|
96
|
+
// Full alternative discovery (memoized) so newerAvailable warnings work.
|
|
97
|
+
resolved = resolveCodexRuntime();
|
|
98
|
+
} catch {
|
|
99
|
+
resolved = {
|
|
100
|
+
runtime: { command: "codex", version: null, source: "fallback" },
|
|
101
|
+
failures: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const lastClamp = loadLastEffortClamp();
|
|
105
|
+
const clampActive = effortClampAppliesToRuntime(lastClamp, resolved.runtime);
|
|
106
|
+
const warningParts: string[] = [];
|
|
107
|
+
if (resolved.replacedConfigured) {
|
|
108
|
+
warningParts.push(
|
|
109
|
+
`Preferred Codex runtime is unavailable; using ${displayCodexRuntimePath(resolved.runtime.command)} instead.`,
|
|
110
|
+
);
|
|
111
|
+
} else if (
|
|
112
|
+
resolved.runtime.source === "fallback"
|
|
113
|
+
&& resolved.failures.length > 0
|
|
114
|
+
&& !resolved.runtime.version
|
|
115
|
+
) {
|
|
116
|
+
warningParts.push("No validated Codex runtime found; falling back to `codex`.");
|
|
117
|
+
}
|
|
118
|
+
if (clampActive) {
|
|
119
|
+
const clampVersion = lastClamp?.runtimeVersion ?? resolved.runtime.version ?? "an older binary";
|
|
120
|
+
warningParts.push(
|
|
121
|
+
`Some reasoning effort options were hidden because Remodex used Codex ${clampVersion}.${resolved.newerAvailable ? " A newer Codex installation is available." : ""}`,
|
|
122
|
+
);
|
|
123
|
+
} else if (resolved.newerAvailable) {
|
|
124
|
+
warningParts.push(
|
|
125
|
+
`Remodex is using an older Codex binary (${resolved.runtime.version ?? "unknown"}). A newer Codex installation is available.`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return jsonResponse({
|
|
129
|
+
// The dashboard renders request-log timestamps. Without this it formats them in the
|
|
130
|
+
// BROWSER's zone, so a KST proxy viewed from a UTC browser reports every request nine
|
|
131
|
+
// hours off (#725). Carried on settings rather than /api/logs because that route's
|
|
132
|
+
// array response has four consumers that would have to change with it.
|
|
133
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
134
|
+
codexAutoStart: codexAutoStartEnabled(config),
|
|
135
|
+
port: config.port,
|
|
136
|
+
hostname: config.hostname ?? "127.0.0.1",
|
|
137
|
+
streamMode: config.streamMode ?? "auto",
|
|
138
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
139
|
+
codexAccountPickerEnabled: codexAccountPickerEnabled(config),
|
|
140
|
+
startupHealth: await getCachedStartupHealth(config),
|
|
141
|
+
codexRuntime: {
|
|
142
|
+
path: displayCodexRuntimePath(resolved.runtime.command),
|
|
143
|
+
version: resolved.runtime.version,
|
|
144
|
+
source: resolved.runtime.source,
|
|
145
|
+
newerAvailable: resolved.newerAvailable
|
|
146
|
+
? {
|
|
147
|
+
path: displayCodexRuntimePath(resolved.newerAvailable.command),
|
|
148
|
+
version: resolved.newerAvailable.version,
|
|
149
|
+
}
|
|
150
|
+
: null,
|
|
151
|
+
catalogClamp: {
|
|
152
|
+
active: clampActive,
|
|
153
|
+
removedEfforts: clampActive ? (lastClamp?.removedEfforts ?? []) : [],
|
|
154
|
+
runtimeVersion: clampActive ? (lastClamp?.runtimeVersion ?? null) : null,
|
|
155
|
+
},
|
|
156
|
+
warning: warningParts.length > 0 ? warningParts.join(" ") : null,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (url.pathname === "/api/startup-health" && req.method === "GET") {
|
|
162
|
+
return jsonResponse(await getCachedStartupHealth(config));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (url.pathname === "/api/startup-action" && req.method === "POST") {
|
|
166
|
+
let body: { action?: unknown; repair?: unknown };
|
|
167
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
168
|
+
if (!body || !["install-service", "install-shim"].includes(String(body.action))) {
|
|
169
|
+
return jsonResponse({ error: "action must be install-service or install-shim" }, 400);
|
|
170
|
+
}
|
|
171
|
+
if (body.repair !== undefined && typeof body.repair !== "boolean") {
|
|
172
|
+
return jsonResponse({ error: "repair must be a boolean when provided" }, 400);
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const action = body.action as StartupInstallAction;
|
|
176
|
+
const repair = body.repair === true;
|
|
177
|
+
const result = await (deps.runStartupInstallAction ?? runStartupInstallAction)(action, { repair });
|
|
178
|
+
invalidateStartupHealthCache();
|
|
179
|
+
return jsonResponse({ ok: true, action, repair, message: result.message });
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (url.pathname === "/api/windows-tray" && req.method === "GET") {
|
|
186
|
+
if (process.platform !== "win32") return jsonResponse({ supported: false, installed: false, running: false, stale: false, summary: `unsupported on ${process.platform}` });
|
|
187
|
+
try {
|
|
188
|
+
return jsonResponse(await runWindowsTrayAction("status"));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (url.pathname === "/api/windows-tray" && req.method === "POST") {
|
|
195
|
+
let body: { action?: unknown };
|
|
196
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
197
|
+
if (!body || !["install", "start", "stop", "uninstall"].includes(String(body.action))) {
|
|
198
|
+
return jsonResponse({ error: "action must be install, start, stop, or uninstall" }, 400);
|
|
199
|
+
}
|
|
200
|
+
if (process.platform !== "win32") return jsonResponse({ error: "Windows tray is only supported on Windows" }, 400);
|
|
201
|
+
try {
|
|
202
|
+
const status = await runWindowsTrayAction(body.action as "install" | "start" | "stop" | "uninstall");
|
|
203
|
+
return jsonResponse({ ok: true, status });
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return jsonResponse({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (url.pathname === "/api/settings" && req.method === "PUT") {
|
|
210
|
+
// Each field is optional but at least one must be present; fields are
|
|
211
|
+
// validated when present. streamMode-only PUTs must work: Windows/macOS
|
|
212
|
+
// memory troubleshooting can use this persisted stream-shape escape hatch
|
|
213
|
+
// (a Windows service does not inherit shell env). A stream-shape
|
|
214
|
+
// change applies to NEW turns only — the config object is shared by
|
|
215
|
+
// reference with the request handlers, no restart needed.
|
|
216
|
+
let parsedBody: unknown;
|
|
217
|
+
try { parsedBody = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
218
|
+
if (!isPlainRecord(parsedBody)) return jsonResponse({ error: "settings body must be an object" }, 400);
|
|
219
|
+
const body = parsedBody as {
|
|
220
|
+
codexAutoStart?: unknown;
|
|
221
|
+
streamMode?: unknown;
|
|
222
|
+
appOwnedMemoryBudgetMb?: unknown;
|
|
223
|
+
codexAccountPickerEnabled?: unknown;
|
|
224
|
+
};
|
|
225
|
+
if (body.codexAutoStart === undefined
|
|
226
|
+
&& body.streamMode === undefined
|
|
227
|
+
&& body.appOwnedMemoryBudgetMb === undefined
|
|
228
|
+
&& body.codexAccountPickerEnabled === undefined) {
|
|
229
|
+
return jsonResponse({ error: "provide codexAutoStart, streamMode, appOwnedMemoryBudgetMb, or codexAccountPickerEnabled" }, 400);
|
|
230
|
+
}
|
|
231
|
+
if (body.codexAutoStart !== undefined && typeof body.codexAutoStart !== "boolean") {
|
|
232
|
+
return jsonResponse({ error: "codexAutoStart boolean is required" }, 400);
|
|
233
|
+
}
|
|
234
|
+
if (body.streamMode !== undefined && !isStreamMode(body.streamMode)) {
|
|
235
|
+
return jsonResponse({ error: "streamMode must be auto, legacy-tee, or eager-relay" }, 400);
|
|
236
|
+
}
|
|
237
|
+
if (body.codexAccountPickerEnabled !== undefined
|
|
238
|
+
&& typeof body.codexAccountPickerEnabled !== "boolean") {
|
|
239
|
+
return jsonResponse({ error: "codexAccountPickerEnabled boolean is required" }, 400);
|
|
240
|
+
}
|
|
241
|
+
if (body.appOwnedMemoryBudgetMb !== undefined && (
|
|
242
|
+
typeof body.appOwnedMemoryBudgetMb !== "number"
|
|
243
|
+
|| !Number.isInteger(body.appOwnedMemoryBudgetMb)
|
|
244
|
+
|| body.appOwnedMemoryBudgetMb < MIN_APP_OWNED_MEMORY_BUDGET_MB
|
|
245
|
+
|| body.appOwnedMemoryBudgetMb > MAX_APP_OWNED_MEMORY_BUDGET_MB
|
|
246
|
+
)) {
|
|
247
|
+
return jsonResponse({ error: `appOwnedMemoryBudgetMb must be an integer from ${MIN_APP_OWNED_MEMORY_BUDGET_MB} to ${MAX_APP_OWNED_MEMORY_BUDGET_MB}` }, 400);
|
|
248
|
+
}
|
|
249
|
+
const previousSettings = {
|
|
250
|
+
codexAutoStart: config.codexAutoStart,
|
|
251
|
+
hasCodexAutoStart: Object.hasOwn(config, "codexAutoStart"),
|
|
252
|
+
streamMode: config.streamMode,
|
|
253
|
+
hasStreamMode: Object.hasOwn(config, "streamMode"),
|
|
254
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb,
|
|
255
|
+
hasAppOwnedMemoryBudgetMb: Object.hasOwn(config, "appOwnedMemoryBudgetMb"),
|
|
256
|
+
codexAccountNamespaces: config.codexAccountNamespaces,
|
|
257
|
+
hasCodexAccountNamespaces: Object.hasOwn(config, "codexAccountNamespaces"),
|
|
258
|
+
codexAccountPickerEnabled: config.codexAccountPickerEnabled,
|
|
259
|
+
hasCodexAccountPickerEnabled: Object.hasOwn(config, "codexAccountPickerEnabled"),
|
|
260
|
+
};
|
|
261
|
+
const pickerWasEnabled = codexAccountPickerEnabled(config);
|
|
262
|
+
let pickerIsEnabled = pickerWasEnabled;
|
|
263
|
+
try {
|
|
264
|
+
if (typeof body.codexAutoStart === "boolean") {
|
|
265
|
+
config.codexAutoStart = body.codexAutoStart;
|
|
266
|
+
}
|
|
267
|
+
if (body.streamMode !== undefined) {
|
|
268
|
+
if (body.streamMode === "auto") {
|
|
269
|
+
delete config.streamMode;
|
|
270
|
+
} else {
|
|
271
|
+
config.streamMode = body.streamMode as "legacy-tee" | "eager-relay";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
275
|
+
config.appOwnedMemoryBudgetMb = body.appOwnedMemoryBudgetMb;
|
|
276
|
+
}
|
|
277
|
+
if (body.codexAccountPickerEnabled === true) {
|
|
278
|
+
config.codexAccountPickerEnabled = true;
|
|
279
|
+
initializeDefaultCodexAccountNamespaces(config);
|
|
280
|
+
} else if (body.codexAccountPickerEnabled === false) {
|
|
281
|
+
config.codexAccountPickerEnabled = false;
|
|
282
|
+
}
|
|
283
|
+
pickerIsEnabled = codexAccountPickerEnabled(config);
|
|
284
|
+
(deps.saveConfigPreservingClaudeCode ?? saveConfigPreservingClaudeCode)(config);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (previousSettings.hasCodexAutoStart) config.codexAutoStart = previousSettings.codexAutoStart;
|
|
287
|
+
else delete config.codexAutoStart;
|
|
288
|
+
if (previousSettings.hasStreamMode) config.streamMode = previousSettings.streamMode;
|
|
289
|
+
else delete config.streamMode;
|
|
290
|
+
if (previousSettings.hasAppOwnedMemoryBudgetMb) {
|
|
291
|
+
config.appOwnedMemoryBudgetMb = previousSettings.appOwnedMemoryBudgetMb;
|
|
292
|
+
} else delete config.appOwnedMemoryBudgetMb;
|
|
293
|
+
if (previousSettings.hasCodexAccountNamespaces) {
|
|
294
|
+
config.codexAccountNamespaces = previousSettings.codexAccountNamespaces;
|
|
295
|
+
} else delete config.codexAccountNamespaces;
|
|
296
|
+
if (previousSettings.hasCodexAccountPickerEnabled) {
|
|
297
|
+
config.codexAccountPickerEnabled = previousSettings.codexAccountPickerEnabled;
|
|
298
|
+
} else delete config.codexAccountPickerEnabled;
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
if (typeof body.appOwnedMemoryBudgetMb === "number") {
|
|
302
|
+
configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(body.appOwnedMemoryBudgetMb));
|
|
303
|
+
enforceAppOwnedMemoryBudget();
|
|
304
|
+
}
|
|
305
|
+
const catalogRefresh = pickerWasEnabled !== pickerIsEnabled
|
|
306
|
+
? await convergeCodexCatalog()
|
|
307
|
+
: undefined;
|
|
308
|
+
const catalogRefreshPending = catalogRefresh
|
|
309
|
+
? catalogRefreshIsPending(catalogRefresh)
|
|
310
|
+
: false;
|
|
311
|
+
invalidateStartupHealthCache();
|
|
312
|
+
return jsonResponse({
|
|
313
|
+
ok: true,
|
|
314
|
+
codexAutoStart: codexAutoStartEnabled(config),
|
|
315
|
+
streamMode: config.streamMode ?? "auto",
|
|
316
|
+
appOwnedMemoryBudgetMb: config.appOwnedMemoryBudgetMb ?? 256,
|
|
317
|
+
codexAccountPickerEnabled: pickerIsEnabled,
|
|
318
|
+
catalogRefreshPending,
|
|
319
|
+
startupHealth: await getCachedStartupHealth(config),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (url.pathname === "/api/diagnostics/project-config" && req.method === "GET") {
|
|
324
|
+
const { getCachedProjectConfigDiagnostics } = await import("../../codex/project-config-warnings");
|
|
325
|
+
const { warnings, grouped } = getCachedProjectConfigDiagnostics();
|
|
326
|
+
return jsonResponse({ warnings, grouped });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (url.pathname === "/api/sync" && req.method === "POST") {
|
|
330
|
+
const { syncModelsToCodex } = await import("../../codex/sync");
|
|
331
|
+
const { attachStaleAppServerHint } = await import("../../codex/app-server-processes");
|
|
332
|
+
const { readRuntimePort, loadConfig } = await import("../../config");
|
|
333
|
+
// Never use the server-captured startup object for a durable integration
|
|
334
|
+
// decision. A toggle may have persisted while this process was gathering.
|
|
335
|
+
const runtime = readRuntimePort(process.pid);
|
|
336
|
+
const result = await syncModelsToCodex(runtime?.port, loadConfig(), null);
|
|
337
|
+
const status = result.status === "refused" ? 409 : (result.status === "skipped" || result.ok ? 200 : 500);
|
|
338
|
+
return jsonResponse({
|
|
339
|
+
...attachStaleAppServerHint(result),
|
|
340
|
+
...(result.ok ? {} : { error: result.message }),
|
|
341
|
+
}, status);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (url.pathname === "/api/update/check" && req.method === "GET") {
|
|
345
|
+
const { checkForUpdate, normalizeUpdateChannel } = await import("../../update/job");
|
|
346
|
+
const rawTag = url.searchParams.get("tag");
|
|
347
|
+
if (rawTag && rawTag !== "latest" && rawTag !== "preview") {
|
|
348
|
+
return jsonResponse({ error: "tag must be latest or preview" }, 400);
|
|
349
|
+
}
|
|
350
|
+
return jsonResponse(checkForUpdate(normalizeUpdateChannel(rawTag)));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (url.pathname === "/api/update/run" && req.method === "POST") {
|
|
354
|
+
const { normalizeUpdateChannel, startUpdateJob, UpdateJobError } = await import("../../update/job");
|
|
355
|
+
let body: { tag?: unknown; restart?: unknown };
|
|
356
|
+
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
357
|
+
if (body.tag !== undefined && body.tag !== "latest" && body.tag !== "preview") {
|
|
358
|
+
return jsonResponse({ error: "tag must be latest or preview" }, 400);
|
|
359
|
+
}
|
|
360
|
+
if (body.restart !== undefined && typeof body.restart !== "boolean") {
|
|
361
|
+
return jsonResponse({ error: "restart boolean is required" }, 400);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
return jsonResponse({ ok: true, job: startUpdateJob(normalizeUpdateChannel(body.tag as string | undefined), body.restart !== false) });
|
|
365
|
+
} catch (err) {
|
|
366
|
+
if (err instanceof UpdateJobError) {
|
|
367
|
+
return jsonResponse({ error: err.message, code: err.code }, err.status);
|
|
368
|
+
}
|
|
369
|
+
return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (url.pathname === "/api/update/status" && req.method === "GET") {
|
|
374
|
+
const { readUpdateJob } = await import("../../update/job");
|
|
375
|
+
const job = readUpdateJob(url.searchParams.get("jobId"));
|
|
376
|
+
if (!job) return jsonResponse({ error: "update job not found" }, 404);
|
|
377
|
+
return jsonResponse({ ok: true, job });
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Native desktop releases are deliberately separate from the npm runtime
|
|
382
|
+
* updater above. The manifest/check/download state is shared with the
|
|
383
|
+
* Tauri tray through `desktop-update.json`; a normal browser can inspect it
|
|
384
|
+
* and check GitHub. Installer launch never crosses this management route;
|
|
385
|
+
* only the exact-origin Tauri bridge can request it.
|
|
386
|
+
*/
|
|
387
|
+
if (url.pathname === "/api/desktop-update/check" && req.method === "GET") {
|
|
388
|
+
const checkDesktopReleaseUpdate = deps.checkDesktopReleaseUpdate
|
|
389
|
+
?? (await import("../../update/desktop-release")).checkDesktopReleaseUpdate;
|
|
390
|
+
const rawChannel = url.searchParams.get("channel");
|
|
391
|
+
if (rawChannel && rawChannel !== "latest" && rawChannel !== "preview") {
|
|
392
|
+
return jsonResponse({ error: "channel must be latest or preview" }, 400, req, config);
|
|
393
|
+
}
|
|
394
|
+
return jsonResponse(
|
|
395
|
+
await checkDesktopReleaseUpdate(
|
|
396
|
+
rawChannel === "preview" ? "preview" : "latest",
|
|
397
|
+
// A management request is never proof that a verified native shell
|
|
398
|
+
// initiated it. Browser callers may check and download only; the
|
|
399
|
+
// Tauri bridge owns installation.
|
|
400
|
+
{ desktopShell: () => false },
|
|
401
|
+
),
|
|
402
|
+
200,
|
|
403
|
+
req,
|
|
404
|
+
config,
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (url.pathname === "/api/desktop-update/run" && req.method === "POST") {
|
|
409
|
+
const desktopRelease = await import("../../update/desktop-release");
|
|
410
|
+
const startDesktopUpdateJob = deps.startDesktopUpdateJob
|
|
411
|
+
?? desktopRelease.startDesktopUpdateJob;
|
|
412
|
+
let body: unknown;
|
|
413
|
+
try {
|
|
414
|
+
body = await readManagementJsonBody(req);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
rethrowManagementBodyTooLarge(error);
|
|
417
|
+
return jsonResponse({ error: "invalid JSON body" }, 400, req, config);
|
|
418
|
+
}
|
|
419
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
420
|
+
return jsonResponse({ error: "invalid JSON body" }, 400, req, config);
|
|
421
|
+
}
|
|
422
|
+
const update = body as { channel?: unknown; install?: unknown };
|
|
423
|
+
if (update.channel !== undefined && update.channel !== "latest" && update.channel !== "preview") {
|
|
424
|
+
return jsonResponse({ error: "channel must be latest or preview" }, 400, req, config);
|
|
425
|
+
}
|
|
426
|
+
if (update.install !== undefined && typeof update.install !== "boolean") {
|
|
427
|
+
return jsonResponse({ error: "install boolean is required" }, 400, req, config);
|
|
428
|
+
}
|
|
429
|
+
if (update.install === true) {
|
|
430
|
+
return jsonResponse(
|
|
431
|
+
{
|
|
432
|
+
error: "Open the Remodex desktop application to install this update.",
|
|
433
|
+
code: "desktop_required",
|
|
434
|
+
},
|
|
435
|
+
409,
|
|
436
|
+
req,
|
|
437
|
+
config,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
const job = startDesktopUpdateJob(
|
|
442
|
+
update.channel === "preview" ? "preview" : "latest",
|
|
443
|
+
{ install: false },
|
|
444
|
+
);
|
|
445
|
+
return jsonResponse({ ok: true, state: job }, 202, req, config);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
if (error instanceof desktopRelease.DesktopUpdateError) {
|
|
448
|
+
return jsonResponse({ error: error.message, code: error.code }, error.status, req, config);
|
|
449
|
+
}
|
|
450
|
+
return jsonResponse({ error: "desktop update could not start", code: "download_failed" }, 500, req, config);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (url.pathname === "/api/desktop-update/status" && req.method === "GET") {
|
|
455
|
+
const readDesktopUpdateState = deps.readDesktopUpdateState
|
|
456
|
+
?? (await import("../../update/desktop-release")).readDesktopUpdateState;
|
|
457
|
+
return jsonResponse({ ok: true, state: readDesktopUpdateState() }, 200, req, config);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (url.pathname === "/api/sidecar-settings" && req.method === "GET") {
|
|
461
|
+
const ws = config.webSearchSidecar ?? {};
|
|
462
|
+
const vs = config.visionSidecar ?? {};
|
|
463
|
+
const visionModel = vs.model || "gpt-5.4-mini";
|
|
464
|
+
const visionReasoning = normalizeVisionReasoningForModel(visionModel, vs.reasoning) ?? "low";
|
|
465
|
+
return jsonResponse({
|
|
466
|
+
webSearch: { model: ws.model ?? "gpt-5.6-luna", backend: ws.backend },
|
|
467
|
+
vision: {
|
|
468
|
+
model: visionModel,
|
|
469
|
+
backend: vs.backend,
|
|
470
|
+
reasoning: visionReasoning,
|
|
471
|
+
maxDescriptionsPerTurn: vs.maxDescriptionsPerTurn,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (url.pathname === "/api/sidecar-settings" && req.method === "PUT") {
|
|
477
|
+
let raw: unknown;
|
|
478
|
+
try { raw = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
479
|
+
// Strict shape (review F2): reject non-object bodies and non-object sections instead of throwing
|
|
480
|
+
// on `null` or silently accepting arrays/strings as no-op updates.
|
|
481
|
+
if (!isPlainRecord(raw)) return jsonResponse({ error: "body must be a JSON object" }, 400);
|
|
482
|
+
if (raw.webSearch !== undefined && !isPlainRecord(raw.webSearch)) return jsonResponse({ error: "webSearch must be an object" }, 400);
|
|
483
|
+
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
484
|
+
const body = raw as {
|
|
485
|
+
webSearch?: { model?: unknown; backend?: unknown; reasoning?: unknown };
|
|
486
|
+
vision?: { model?: unknown; backend?: unknown; reasoning?: unknown; maxDescriptionsPerTurn?: unknown };
|
|
487
|
+
};
|
|
488
|
+
if (body.webSearch && body.webSearch.backend !== undefined && body.webSearch.backend !== null
|
|
489
|
+
&& body.webSearch.backend !== "openai" && body.webSearch.backend !== "anthropic") {
|
|
490
|
+
return jsonResponse({ error: "webSearch.backend must be openai, anthropic, or null" }, 400);
|
|
491
|
+
}
|
|
492
|
+
if (body.vision && body.vision.backend !== undefined
|
|
493
|
+
&& body.vision.backend !== null && body.vision.backend !== "openai" && body.vision.backend !== "anthropic") {
|
|
494
|
+
return jsonResponse({ error: "vision.backend must be openai, anthropic, or null" }, 400);
|
|
495
|
+
}
|
|
496
|
+
if (body.vision && body.vision.maxDescriptionsPerTurn !== undefined
|
|
497
|
+
&& (typeof body.vision.maxDescriptionsPerTurn !== "number"
|
|
498
|
+
|| !Number.isInteger(body.vision.maxDescriptionsPerTurn)
|
|
499
|
+
|| body.vision.maxDescriptionsPerTurn <= 0)) {
|
|
500
|
+
return jsonResponse({ error: "vision.maxDescriptionsPerTurn must be a positive integer" }, 400);
|
|
501
|
+
}
|
|
502
|
+
if (body.vision?.reasoning !== undefined && !isVisionReasoningEffort(body.vision.reasoning)) {
|
|
503
|
+
return jsonResponse({ error: `vision.reasoning must be ${VISION_REASONING_EFFORTS.join(", ")}` }, 400);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let normalizedVisionReasoning: ReturnType<typeof normalizeVisionReasoningForModel>;
|
|
507
|
+
let visionReasoningTouched = false;
|
|
508
|
+
if (body.vision && (body.vision.model !== undefined || body.vision.reasoning !== undefined)) {
|
|
509
|
+
visionReasoningTouched = true;
|
|
510
|
+
const model = typeof body.vision.model === "string"
|
|
511
|
+
? (body.vision.model === "" ? "gpt-5.4-mini" : body.vision.model)
|
|
512
|
+
: (config.visionSidecar?.model || "gpt-5.4-mini");
|
|
513
|
+
const sourceReasoning = body.vision.reasoning ?? config.visionSidecar?.reasoning;
|
|
514
|
+
normalizedVisionReasoning = sourceReasoning === undefined
|
|
515
|
+
? undefined
|
|
516
|
+
: normalizeVisionReasoningForModel(model, sourceReasoning);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (body.webSearch) {
|
|
520
|
+
config.webSearchSidecar = { ...config.webSearchSidecar };
|
|
521
|
+
if (typeof body.webSearch.model === "string") {
|
|
522
|
+
if (body.webSearch.model === "") delete config.webSearchSidecar.model;
|
|
523
|
+
else config.webSearchSidecar.model = body.webSearch.model;
|
|
524
|
+
}
|
|
525
|
+
if (body.webSearch.backend === null) delete config.webSearchSidecar.backend;
|
|
526
|
+
else if (body.webSearch.backend === "openai" || body.webSearch.backend === "anthropic") {
|
|
527
|
+
config.webSearchSidecar.backend = body.webSearch.backend;
|
|
528
|
+
}
|
|
529
|
+
if (typeof body.webSearch.reasoning === "string") config.webSearchSidecar.reasoning = body.webSearch.reasoning;
|
|
530
|
+
}
|
|
531
|
+
if (body.vision) {
|
|
532
|
+
config.visionSidecar = { ...config.visionSidecar };
|
|
533
|
+
if (typeof body.vision.model === "string") {
|
|
534
|
+
if (body.vision.model === "") delete config.visionSidecar.model;
|
|
535
|
+
else config.visionSidecar.model = body.vision.model;
|
|
536
|
+
}
|
|
537
|
+
if (body.vision.backend === null) delete config.visionSidecar.backend;
|
|
538
|
+
else if (body.vision.backend === "openai" || body.vision.backend === "anthropic") {
|
|
539
|
+
config.visionSidecar.backend = body.vision.backend;
|
|
540
|
+
}
|
|
541
|
+
if (typeof body.vision.maxDescriptionsPerTurn === "number") {
|
|
542
|
+
config.visionSidecar.maxDescriptionsPerTurn = body.vision.maxDescriptionsPerTurn;
|
|
543
|
+
}
|
|
544
|
+
if (visionReasoningTouched) {
|
|
545
|
+
if (normalizedVisionReasoning === undefined) delete config.visionSidecar.reasoning;
|
|
546
|
+
else config.visionSidecar.reasoning = normalizedVisionReasoning;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
saveConfigPreservingClaudeCode(config);
|
|
550
|
+
const ws = config.webSearchSidecar ?? {};
|
|
551
|
+
const vs = config.visionSidecar ?? {};
|
|
552
|
+
const visionModel = vs.model || "gpt-5.4-mini";
|
|
553
|
+
const visionReasoning = normalizeVisionReasoningForModel(visionModel, vs.reasoning) ?? "low";
|
|
554
|
+
return jsonResponse({
|
|
555
|
+
ok: true,
|
|
556
|
+
webSearch: { model: ws.model ?? "gpt-5.6-luna", backend: ws.backend },
|
|
557
|
+
vision: {
|
|
558
|
+
model: visionModel,
|
|
559
|
+
backend: vs.backend,
|
|
560
|
+
reasoning: visionReasoning,
|
|
561
|
+
maxDescriptionsPerTurn: vs.maxDescriptionsPerTurn,
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (url.pathname === "/api/shadow-call-settings" && req.method === "GET") {
|
|
567
|
+
const sci = config.shadowCallIntercept ?? {};
|
|
568
|
+
return jsonResponse({
|
|
569
|
+
enabled: sci.enabled === true,
|
|
570
|
+
model: sci.model ?? "",
|
|
571
|
+
sourceModels: shadowSourceModels(sci.sourceModels),
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (url.pathname === "/api/shadow-call-settings" && req.method === "PUT") {
|
|
576
|
+
let raw: unknown;
|
|
577
|
+
try { raw = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
|
|
578
|
+
if (!isPlainRecord(raw)) return jsonResponse({ error: "body must be a JSON object" }, 400);
|
|
579
|
+
const body = raw as { enabled?: unknown; model?: unknown };
|
|
580
|
+
if (body.enabled !== undefined && typeof body.enabled !== "boolean") {
|
|
581
|
+
return jsonResponse({ error: "enabled must be a boolean" }, 400);
|
|
582
|
+
}
|
|
583
|
+
if (body.model !== undefined && typeof body.model !== "string") {
|
|
584
|
+
return jsonResponse({ error: "model must be a string" }, 400);
|
|
585
|
+
}
|
|
586
|
+
config.shadowCallIntercept = { ...config.shadowCallIntercept };
|
|
587
|
+
if (typeof body.enabled === "boolean") config.shadowCallIntercept.enabled = body.enabled;
|
|
588
|
+
if (typeof body.model === "string") {
|
|
589
|
+
if (body.model === "") delete config.shadowCallIntercept.model;
|
|
590
|
+
else config.shadowCallIntercept.model = body.model;
|
|
591
|
+
}
|
|
592
|
+
saveConfigPreservingClaudeCode(config);
|
|
593
|
+
const sci = config.shadowCallIntercept;
|
|
594
|
+
return jsonResponse({
|
|
595
|
+
ok: true,
|
|
596
|
+
enabled: sci.enabled === true,
|
|
597
|
+
model: sci.model ?? "",
|
|
598
|
+
sourceModels: shadowSourceModels(sci.sourceModels),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|