@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,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-independent identity and namespace resolution for Codex writes.
|
|
3
|
+
*
|
|
4
|
+
* Bun 1.3.14 made the obvious implementation unsafe: both `os.homedir()` and
|
|
5
|
+
* `os.userInfo().homedir` follow HOME. A service and CLI for the same account
|
|
6
|
+
* could therefore coordinate through different databases. The namespace is
|
|
7
|
+
* keyed only by the effective uid/SID and the canonical CODEX_HOME.
|
|
8
|
+
*
|
|
9
|
+
* Design record: devlog/_fin/260804_codex_write_substrate/005_contract.md §7.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import {
|
|
13
|
+
lstatSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
realpathSync,
|
|
16
|
+
statSync,
|
|
17
|
+
} from "node:fs";
|
|
18
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
ResolveCodexCoordinatorDatabasePath,
|
|
22
|
+
ResolveCodexCatalogSerializationDatabasePath,
|
|
23
|
+
ResolveCodexHistorySerializationDatabasePath,
|
|
24
|
+
ResolveEffectiveUserIdentity,
|
|
25
|
+
UserIdentity,
|
|
26
|
+
} from "./convergence-types";
|
|
27
|
+
|
|
28
|
+
const POSIX_PRIVATE_MODE = 0o700;
|
|
29
|
+
const POSIX_TMP_REQUIRED_MODE = 0o1003;
|
|
30
|
+
const POSIX_TMP_PATH = "/tmp";
|
|
31
|
+
const SID_PATTERN = /^S-1-(?:\d+-)+\d+$/i;
|
|
32
|
+
|
|
33
|
+
export class CodexUserIdentityRefusal extends Error {
|
|
34
|
+
readonly code = "CODEX_USER_IDENTITY_REFUSED";
|
|
35
|
+
|
|
36
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
37
|
+
super(message, options);
|
|
38
|
+
this.name = "CodexUserIdentityRefusal";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function refuse(message: string, cause?: unknown): never {
|
|
43
|
+
throw new CodexUserIdentityRefusal(message, cause === undefined ? undefined : { cause });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function powershellValue(expression: string): string {
|
|
47
|
+
let result: ReturnType<typeof Bun.spawnSync>;
|
|
48
|
+
try {
|
|
49
|
+
result = Bun.spawnSync([
|
|
50
|
+
"powershell.exe",
|
|
51
|
+
"-NoLogo",
|
|
52
|
+
"-NoProfile",
|
|
53
|
+
"-NonInteractive",
|
|
54
|
+
"-Command",
|
|
55
|
+
expression,
|
|
56
|
+
], {
|
|
57
|
+
stdin: "ignore",
|
|
58
|
+
stdout: "pipe",
|
|
59
|
+
stderr: "pipe",
|
|
60
|
+
});
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
refuse("Windows effective-account lookup could not start.", cause);
|
|
63
|
+
}
|
|
64
|
+
if (result.exitCode !== 0) refuse("Windows effective-account lookup failed.");
|
|
65
|
+
const value = new TextDecoder().decode(result.stdout).trim();
|
|
66
|
+
if (!value) refuse("Windows effective-account lookup returned an empty value.");
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveWindowsSid(): string {
|
|
71
|
+
const sid = powershellValue(
|
|
72
|
+
"[System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value",
|
|
73
|
+
);
|
|
74
|
+
if (!SID_PATTERN.test(sid)) refuse("Windows effective-account lookup returned an invalid SID.");
|
|
75
|
+
return sid.toUpperCase();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const resolveEffectiveUserIdentity: ResolveEffectiveUserIdentity = () => {
|
|
79
|
+
if (process.platform === "win32") {
|
|
80
|
+
return { platform: "win32", sid: resolveWindowsSid() };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const getuid = process.getuid;
|
|
84
|
+
if (typeof getuid !== "function") {
|
|
85
|
+
refuse("The runtime does not expose the effective POSIX uid.");
|
|
86
|
+
}
|
|
87
|
+
let uid: number;
|
|
88
|
+
try {
|
|
89
|
+
uid = getuid.call(process);
|
|
90
|
+
} catch (cause) {
|
|
91
|
+
refuse("The effective POSIX uid lookup failed.", cause);
|
|
92
|
+
}
|
|
93
|
+
if (!Number.isSafeInteger(uid) || uid < 0) {
|
|
94
|
+
refuse("The runtime returned an invalid effective POSIX uid.");
|
|
95
|
+
}
|
|
96
|
+
return { platform: "posix", uid };
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
function assertPrivatePosixDirectory(path: string, uid: number): void {
|
|
100
|
+
let entry;
|
|
101
|
+
try {
|
|
102
|
+
entry = lstatSync(path);
|
|
103
|
+
} catch (cause) {
|
|
104
|
+
refuse("The Codex coordinator namespace cannot be inspected.", cause);
|
|
105
|
+
}
|
|
106
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
107
|
+
refuse("The Codex coordinator namespace is not a real directory.");
|
|
108
|
+
}
|
|
109
|
+
if (entry.uid !== uid || (entry.mode & 0o777) !== POSIX_PRIVATE_MODE) {
|
|
110
|
+
refuse("The Codex coordinator namespace has unsafe ownership or permissions.");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function ensurePrivatePosixDirectory(path: string, uid: number): void {
|
|
115
|
+
try {
|
|
116
|
+
mkdirSync(path, { mode: POSIX_PRIVATE_MODE });
|
|
117
|
+
} catch (cause) {
|
|
118
|
+
const code = cause && typeof cause === "object" && "code" in cause
|
|
119
|
+
? String((cause as { code?: unknown }).code)
|
|
120
|
+
: "";
|
|
121
|
+
if (code !== "EEXIST") refuse("The Codex coordinator namespace cannot be created.", cause);
|
|
122
|
+
}
|
|
123
|
+
assertPrivatePosixDirectory(path, uid);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveTrustedPosixTmp(): string {
|
|
127
|
+
let realTmp: string;
|
|
128
|
+
try {
|
|
129
|
+
realTmp = realpathSync.native(POSIX_TMP_PATH);
|
|
130
|
+
const entry = statSync(realTmp);
|
|
131
|
+
if (!entry.isDirectory() || entry.uid !== 0) {
|
|
132
|
+
refuse("The system temporary directory has unsafe ownership.");
|
|
133
|
+
}
|
|
134
|
+
if ((entry.mode & POSIX_TMP_REQUIRED_MODE) !== POSIX_TMP_REQUIRED_MODE) {
|
|
135
|
+
refuse("The system temporary directory lacks sticky world write/search permissions.");
|
|
136
|
+
}
|
|
137
|
+
} catch (cause) {
|
|
138
|
+
if (cause instanceof CodexUserIdentityRefusal) throw cause;
|
|
139
|
+
refuse("The system temporary directory cannot be trusted.", cause);
|
|
140
|
+
}
|
|
141
|
+
return realTmp;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resolvePosixRuntimeRoot(uid: number): string {
|
|
145
|
+
const realTmp = resolveTrustedPosixTmp();
|
|
146
|
+
const root = join(realTmp, `opencodex-runtime-v1-${uid}`);
|
|
147
|
+
ensurePrivatePosixDirectory(root, uid);
|
|
148
|
+
return root;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type CoordinatorNamespaceProbe =
|
|
152
|
+
| { readonly status: "ok"; readonly root: string }
|
|
153
|
+
| { readonly status: "missing" };
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Read-only namespace probe for diagnostics (`rmx doctor`).
|
|
157
|
+
*
|
|
158
|
+
* Unlike the runtime resolvers, this never creates the root or the lock
|
|
159
|
+
* directories: a doctor run must observe the namespace, not initialize it.
|
|
160
|
+
* A missing namespace is reported as `missing` instead of refused, so a fresh
|
|
161
|
+
* machine does not read as a broken one; an existing but unsafe namespace is
|
|
162
|
+
* refused exactly like the creating path would refuse it.
|
|
163
|
+
*/
|
|
164
|
+
export function probeCodexCoordinatorNamespace(identity: UserIdentity): CoordinatorNamespaceProbe {
|
|
165
|
+
if (identity.platform === "posix") {
|
|
166
|
+
const root = join(resolveTrustedPosixTmp(), `opencodex-runtime-v1-${identity.uid}`);
|
|
167
|
+
let entry;
|
|
168
|
+
try {
|
|
169
|
+
entry = lstatSync(root);
|
|
170
|
+
} catch (cause) {
|
|
171
|
+
const code = cause && typeof cause === "object" && "code" in cause
|
|
172
|
+
? String((cause as { code?: unknown }).code)
|
|
173
|
+
: "";
|
|
174
|
+
if (code === "ENOENT") return { status: "missing" };
|
|
175
|
+
refuse("The Codex coordinator namespace cannot be inspected.", cause);
|
|
176
|
+
}
|
|
177
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
178
|
+
refuse("The Codex coordinator namespace is not a real directory.");
|
|
179
|
+
}
|
|
180
|
+
if (entry.uid !== identity.uid || (entry.mode & 0o777) !== POSIX_PRIVATE_MODE) {
|
|
181
|
+
refuse("The Codex coordinator namespace has unsafe ownership or permissions.");
|
|
182
|
+
}
|
|
183
|
+
return { status: "ok", root };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!SID_PATTERN.test(identity.sid)) refuse("The coordinator identity contains an invalid SID.");
|
|
187
|
+
const localAppData = powershellValue(
|
|
188
|
+
"[Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)",
|
|
189
|
+
);
|
|
190
|
+
if (!isAbsolute(localAppData)) refuse("Windows LocalAppData resolution returned a relative path.");
|
|
191
|
+
const root = resolve(localAppData, "OpenCodex", "Runtime", "v1", identity.sid.toUpperCase());
|
|
192
|
+
let entry;
|
|
193
|
+
try {
|
|
194
|
+
entry = lstatSync(root);
|
|
195
|
+
} catch (cause) {
|
|
196
|
+
const code = cause && typeof cause === "object" && "code" in cause
|
|
197
|
+
? String((cause as { code?: unknown }).code)
|
|
198
|
+
: "";
|
|
199
|
+
if (code === "ENOENT") return { status: "missing" };
|
|
200
|
+
refuse("The Windows coordinator namespace cannot be inspected.", cause);
|
|
201
|
+
}
|
|
202
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
203
|
+
refuse("The Windows coordinator namespace is not a real directory.");
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const real = realpathSync.native(root);
|
|
207
|
+
if (!samePathIdentity(real, root, "win32")) {
|
|
208
|
+
refuse("The Windows coordinator namespace is redirected by a junction or reparse point.");
|
|
209
|
+
}
|
|
210
|
+
return { status: "ok", root: real };
|
|
211
|
+
} catch (cause) {
|
|
212
|
+
if (cause instanceof CodexUserIdentityRefusal) throw cause;
|
|
213
|
+
refuse("The Windows coordinator namespace cannot be resolved.", cause);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function resolveWindowsRuntimeRoot(identity: Extract<UserIdentity, { platform: "win32" }>): string {
|
|
218
|
+
if (!SID_PATTERN.test(identity.sid)) refuse("The coordinator identity contains an invalid SID.");
|
|
219
|
+
const localAppData = powershellValue(
|
|
220
|
+
"[Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)",
|
|
221
|
+
);
|
|
222
|
+
if (!isAbsolute(localAppData)) refuse("Windows LocalAppData resolution returned a relative path.");
|
|
223
|
+
|
|
224
|
+
// The SID and known-folder values come from the effective token/.NET OS APIs,
|
|
225
|
+
// never USERPROFILE or LOCALAPPDATA. WP11 adds descriptor/reparse/ACL checks at
|
|
226
|
+
// the stable-database open boundary where those checks can cover SQLite too.
|
|
227
|
+
const root = resolve(localAppData, "OpenCodex", "Runtime", "v1", identity.sid.toUpperCase());
|
|
228
|
+
try {
|
|
229
|
+
mkdirSync(root, { recursive: true });
|
|
230
|
+
} catch (cause) {
|
|
231
|
+
refuse("The Windows coordinator namespace cannot be created.", cause);
|
|
232
|
+
}
|
|
233
|
+
// Canonicalize before anything is keyed on the path: a junctioned or
|
|
234
|
+
// differently-cased LocalAppData must land on ONE namespace, or two processes
|
|
235
|
+
// that share the real directory would build different lock paths and never
|
|
236
|
+
// contend. The lock modules also compare this path against realpath, so a
|
|
237
|
+
// non-canonical spelling here would read as "unsafe" on every acquisition.
|
|
238
|
+
//
|
|
239
|
+
// Canonicalizing must only ever fold spelling (case, separators). If the
|
|
240
|
+
// realpath lands somewhere else entirely, a component of the namespace is a
|
|
241
|
+
// junction/reparse redirect; accepting the target would convert the old
|
|
242
|
+
// refusal into silently opening the redirected location, so refuse instead.
|
|
243
|
+
try {
|
|
244
|
+
const real = realpathSync.native(root);
|
|
245
|
+
if (!samePathIdentity(real, root, "win32")) {
|
|
246
|
+
refuse("The Windows coordinator namespace is redirected by a junction or reparse point.");
|
|
247
|
+
}
|
|
248
|
+
return real;
|
|
249
|
+
} catch (cause) {
|
|
250
|
+
if (cause instanceof CodexUserIdentityRefusal) throw cause;
|
|
251
|
+
refuse("The Windows coordinator namespace cannot be resolved.", cause);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Windows path identity is case-insensitive; everywhere else it is exact.
|
|
257
|
+
*
|
|
258
|
+
* The lock modules compare a requested lock path against its own realpath, and
|
|
259
|
+
* byte equality refuses legitimate Windows spellings (drive-letter case, mixed
|
|
260
|
+
* component casing) as "unsafe". This is the same semantics
|
|
261
|
+
* `history-provider.ts` already applies to manifest paths; the platform
|
|
262
|
+
* argument exists so both branches are testable on any host.
|
|
263
|
+
*/
|
|
264
|
+
export function samePathIdentity(
|
|
265
|
+
a: string,
|
|
266
|
+
b: string,
|
|
267
|
+
platform: NodeJS.Platform = process.platform,
|
|
268
|
+
): boolean {
|
|
269
|
+
const left = resolve(a);
|
|
270
|
+
const right = resolve(b);
|
|
271
|
+
return platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export const resolveCodexCoordinatorDatabasePath: ResolveCodexCoordinatorDatabasePath = (
|
|
275
|
+
identity,
|
|
276
|
+
canonicalCodexHome,
|
|
277
|
+
) => {
|
|
278
|
+
if (!isAbsolute(canonicalCodexHome)) {
|
|
279
|
+
refuse("The canonical CODEX_HOME must be an absolute path.");
|
|
280
|
+
}
|
|
281
|
+
const root = identity.platform === "posix"
|
|
282
|
+
? resolvePosixRuntimeRoot(identity.uid)
|
|
283
|
+
: resolveWindowsRuntimeRoot(identity);
|
|
284
|
+
const locks = join(root, "native-write-locks");
|
|
285
|
+
if (identity.platform === "posix") ensurePrivatePosixDirectory(locks, identity.uid);
|
|
286
|
+
else {
|
|
287
|
+
try {
|
|
288
|
+
mkdirSync(locks, { recursive: true });
|
|
289
|
+
} catch (cause) {
|
|
290
|
+
refuse("The Windows coordinator lock directory cannot be created.", cause);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const homeDigest = createHash("sha256").update(canonicalCodexHome).digest("hex");
|
|
295
|
+
return join(locks, `${homeDigest}.sqlite`);
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* K's FINAL database path. Never the native coordinator path.
|
|
300
|
+
*
|
|
301
|
+
* Catalog serialization is a different ownership surface from the native
|
|
302
|
+
* coordinator N: `K -> C` is a legal order and `N -> K` nests, so sharing one
|
|
303
|
+
* database would make the required nesting self-contend. The two live in
|
|
304
|
+
* sibling directories under the same per-user runtime root — same identity
|
|
305
|
+
* namespace, same environment-independent parent, distinct exclusion.
|
|
306
|
+
*
|
|
307
|
+
* Consumers use the returned path verbatim and append nothing
|
|
308
|
+
* (`005_contract.md:1256-1330`).
|
|
309
|
+
*/
|
|
310
|
+
export const resolveCodexCatalogSerializationDatabasePath:
|
|
311
|
+
ResolveCodexCatalogSerializationDatabasePath = (identity, canonicalCodexHome) => {
|
|
312
|
+
if (!isAbsolute(canonicalCodexHome)) {
|
|
313
|
+
refuse("The canonical CODEX_HOME must be an absolute path.");
|
|
314
|
+
}
|
|
315
|
+
const root = identity.platform === "posix"
|
|
316
|
+
? resolvePosixRuntimeRoot(identity.uid)
|
|
317
|
+
: resolveWindowsRuntimeRoot(identity);
|
|
318
|
+
const locks = join(root, "catalog-write-locks");
|
|
319
|
+
if (identity.platform === "posix") ensurePrivatePosixDirectory(locks, identity.uid);
|
|
320
|
+
else {
|
|
321
|
+
try {
|
|
322
|
+
mkdirSync(locks, { recursive: true });
|
|
323
|
+
} catch (cause) {
|
|
324
|
+
refuse("The Windows catalog serialization directory cannot be created.", cause);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const homeDigest = createHash("sha256").update(canonicalCodexHome).digest("hex");
|
|
329
|
+
return join(locks, `${homeDigest}.sqlite`);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* H's FINAL database path.
|
|
334
|
+
*
|
|
335
|
+
* Keyed by the canonical state DB in addition to the canonical home, unlike N and
|
|
336
|
+
* K. One `CODEX_HOME` can name a different `state_5.sqlite` — `model_catalog_json`
|
|
337
|
+
* has the same shape of indirection for catalogs — and two operations against
|
|
338
|
+
* different history databases are not the same exclusion. Hashing only the home
|
|
339
|
+
* would serialize them together, and hashing the raw request path would let two
|
|
340
|
+
* spellings of one database take different locks.
|
|
341
|
+
*/
|
|
342
|
+
export const resolveCodexHistorySerializationDatabasePath:
|
|
343
|
+
ResolveCodexHistorySerializationDatabasePath = (
|
|
344
|
+
identity,
|
|
345
|
+
canonicalCodexHome,
|
|
346
|
+
canonicalStateDbPath,
|
|
347
|
+
) => {
|
|
348
|
+
if (!isAbsolute(canonicalCodexHome)) {
|
|
349
|
+
refuse("The canonical CODEX_HOME must be an absolute path.");
|
|
350
|
+
}
|
|
351
|
+
if (!isAbsolute(canonicalStateDbPath)) {
|
|
352
|
+
refuse("The canonical Codex state database must be an absolute path.");
|
|
353
|
+
}
|
|
354
|
+
const root = identity.platform === "posix"
|
|
355
|
+
? resolvePosixRuntimeRoot(identity.uid)
|
|
356
|
+
: resolveWindowsRuntimeRoot(identity);
|
|
357
|
+
const locks = join(root, "history-write-locks");
|
|
358
|
+
if (identity.platform === "posix") ensurePrivatePosixDirectory(locks, identity.uid);
|
|
359
|
+
else {
|
|
360
|
+
try {
|
|
361
|
+
mkdirSync(locks, { recursive: true });
|
|
362
|
+
} catch (cause) {
|
|
363
|
+
refuse("The Windows history serialization directory cannot be created.", cause);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Both components are length-prefixed so no pair of (home, stateDb) values can
|
|
368
|
+
// collide by concatenation.
|
|
369
|
+
const digest = createHash("sha256")
|
|
370
|
+
.update(`${canonicalCodexHome.length}:${canonicalCodexHome}`)
|
|
371
|
+
.update(`${canonicalStateDbPath.length}:${canonicalStateDbPath}`)
|
|
372
|
+
.digest("hex");
|
|
373
|
+
return join(locks, `${digest}.sqlite`);
|
|
374
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
export class CodexWarmupError extends Error {
|
|
2
|
+
code: "http_status" | "missing_body" | "stream_failed" | "stream_incomplete" | "stream_error" | "invalid_sse" | "no_terminal" | "transport";
|
|
3
|
+
status?: number;
|
|
4
|
+
/** Upstream error detail extracted from the response body (truncated to 512 chars). */
|
|
5
|
+
upstreamDetail?: string;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
code: CodexWarmupError["code"],
|
|
9
|
+
message = "Codex warmup failed",
|
|
10
|
+
options: { status?: number; cause?: unknown; upstreamDetail?: string } = {},
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "CodexWarmupError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.status = options.status;
|
|
16
|
+
this.upstreamDetail = options.upstreamDetail;
|
|
17
|
+
if (options.cause !== undefined) this.cause = options.cause;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CodexWarmupOptions {
|
|
22
|
+
accessToken: string;
|
|
23
|
+
chatgptAccountId: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
29
|
+
const DEFAULT_MODEL = "gpt-5.4-mini";
|
|
30
|
+
const FALLBACK_MODELS = ["gpt-5.5"];
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
32
|
+
const MAX_ERROR_BODY_BYTES = 2048;
|
|
33
|
+
|
|
34
|
+
/** Read the first MAX_ERROR_BODY_BYTES of a response body and extract an error message. */
|
|
35
|
+
async function readErrorDetail(res: Response): Promise<string | undefined> {
|
|
36
|
+
try {
|
|
37
|
+
const text = await res.text();
|
|
38
|
+
const trimmed = text.slice(0, MAX_ERROR_BODY_BYTES);
|
|
39
|
+
try {
|
|
40
|
+
const json = JSON.parse(trimmed) as Record<string, unknown>;
|
|
41
|
+
// ChatGPT backend error shape: { error: { message: "..." } } or { detail: "..." }
|
|
42
|
+
const nested = json.error;
|
|
43
|
+
if (nested && typeof nested === "object" && typeof (nested as Record<string, unknown>).message === "string") {
|
|
44
|
+
return ((nested as Record<string, unknown>).message as string).slice(0, 512);
|
|
45
|
+
}
|
|
46
|
+
if (typeof json.detail === "string") return json.detail.slice(0, 512);
|
|
47
|
+
if (typeof json.error === "string") return (json.error as string).slice(0, 512);
|
|
48
|
+
if (typeof json.message === "string") return json.message.slice(0, 512);
|
|
49
|
+
} catch {
|
|
50
|
+
// Non-JSON response body may contain sensitive data (tokens, credentials).
|
|
51
|
+
// Only surface structured error messages, never raw text.
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
} catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function safeWarmupReason(err: unknown): string {
|
|
60
|
+
if (err instanceof CodexWarmupError) {
|
|
61
|
+
const base = err.status ? `${err.code}:${err.status}` : err.code;
|
|
62
|
+
return err.upstreamDetail ? `${base} — ${err.upstreamDetail}` : base;
|
|
63
|
+
}
|
|
64
|
+
return "transport";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function codexWarmupFailureReason(err: unknown): string {
|
|
68
|
+
return safeWarmupReason(err);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function eventTypeFromData(data: unknown): string | undefined {
|
|
72
|
+
if (!data || typeof data !== "object") return undefined;
|
|
73
|
+
const record = data as Record<string, unknown>;
|
|
74
|
+
return typeof record.type === "string" ? record.type : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseSseFrame(frame: string): unknown | null {
|
|
78
|
+
const dataLines = frame
|
|
79
|
+
.split(/\r?\n/)
|
|
80
|
+
.filter(line => line.startsWith("data:"))
|
|
81
|
+
.map(line => line.slice(5).trimStart());
|
|
82
|
+
if (dataLines.length === 0) return null;
|
|
83
|
+
const data = dataLines.join("\n").trim();
|
|
84
|
+
if (!data || data === "[DONE]") return null;
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(data) as unknown;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
throw new CodexWarmupError("invalid_sse", "Codex warmup received invalid SSE", { cause: err });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function drainWarmupSse(body: ReadableStream<Uint8Array>): Promise<void> {
|
|
93
|
+
const reader = body.getReader();
|
|
94
|
+
const decoder = new TextDecoder();
|
|
95
|
+
let buffer = "";
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
for (;;) {
|
|
99
|
+
const { done, value } = await reader.read();
|
|
100
|
+
if (done) break;
|
|
101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
102
|
+
|
|
103
|
+
for (;;) {
|
|
104
|
+
const frameEnd = buffer.search(/\r?\n\r?\n/);
|
|
105
|
+
if (frameEnd < 0) break;
|
|
106
|
+
const frame = buffer.slice(0, frameEnd);
|
|
107
|
+
const delimiterLength = buffer[frameEnd] === "\r" ? 4 : 2;
|
|
108
|
+
buffer = buffer.slice(frameEnd + delimiterLength);
|
|
109
|
+
const parsed = parseSseFrame(frame);
|
|
110
|
+
const type = eventTypeFromData(parsed);
|
|
111
|
+
if (type === "response.completed") return;
|
|
112
|
+
if (type === "response.failed") throw new CodexWarmupError("stream_failed");
|
|
113
|
+
if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
|
|
114
|
+
if (type === "error") throw new CodexWarmupError("stream_error");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (buffer.trim()) {
|
|
119
|
+
const parsed = parseSseFrame(buffer);
|
|
120
|
+
const type = eventTypeFromData(parsed);
|
|
121
|
+
if (type === "response.completed") return;
|
|
122
|
+
if (type === "response.failed") throw new CodexWarmupError("stream_failed");
|
|
123
|
+
if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
|
|
124
|
+
if (type === "error") throw new CodexWarmupError("stream_error");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new CodexWarmupError("no_terminal", "Codex warmup ended before completion");
|
|
128
|
+
} finally {
|
|
129
|
+
reader.releaseLock();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function tryWarmup(options: CodexWarmupOptions, model: string): Promise<void> {
|
|
134
|
+
let res: Response;
|
|
135
|
+
try {
|
|
136
|
+
res = await fetch(CODEX_RESPONSES_URL, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: {
|
|
139
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
140
|
+
"ChatGPT-Account-Id": options.chatgptAccountId,
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
model,
|
|
145
|
+
instructions: "Reply with OK.",
|
|
146
|
+
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
|
|
147
|
+
stream: true,
|
|
148
|
+
store: false,
|
|
149
|
+
}),
|
|
150
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
151
|
+
});
|
|
152
|
+
} catch (err) {
|
|
153
|
+
throw new CodexWarmupError("transport", "Codex warmup request failed", { cause: err });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!res.ok) {
|
|
157
|
+
const upstreamDetail = await readErrorDetail(res);
|
|
158
|
+
throw new CodexWarmupError("http_status", "Codex warmup was rejected", {
|
|
159
|
+
status: res.status,
|
|
160
|
+
upstreamDetail,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (!res.body) throw new CodexWarmupError("missing_body");
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await drainWarmupSse(res.body);
|
|
167
|
+
} finally {
|
|
168
|
+
await res.body?.cancel().catch(() => {});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function warmCodexAccount(options: CodexWarmupOptions): Promise<void> {
|
|
173
|
+
const primaryModel = options.model?.trim() || DEFAULT_MODEL;
|
|
174
|
+
try {
|
|
175
|
+
await tryWarmup(options, primaryModel);
|
|
176
|
+
return;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
// Retry with fallback models on 400 (model may not be available for this account).
|
|
179
|
+
if (!(err instanceof CodexWarmupError) || err.status !== 400) throw err;
|
|
180
|
+
let lastErr = err;
|
|
181
|
+
for (const fallback of FALLBACK_MODELS) {
|
|
182
|
+
if (fallback === primaryModel) continue;
|
|
183
|
+
try {
|
|
184
|
+
await tryWarmup(options, fallback);
|
|
185
|
+
return;
|
|
186
|
+
} catch (retryErr) {
|
|
187
|
+
if (retryErr instanceof CodexWarmupError) lastErr = retryErr;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw lastErr;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ServerWebSocket } from "bun";
|
|
2
|
+
import type { WsData } from "../server/ws-bridge";
|
|
3
|
+
import { createAdmissionGate, type AdmissionMetrics, type AdmissionReservation } from "../lib/admission";
|
|
4
|
+
|
|
5
|
+
export const MAX_TRACKED_CODEX_WEBSOCKETS = 128;
|
|
6
|
+
const websocketGate = createAdmissionGate("codex_websockets", MAX_TRACKED_CODEX_WEBSOCKETS);
|
|
7
|
+
|
|
8
|
+
export function tryReserveCodexWebSocket(): AdmissionReservation<ServerWebSocket<WsData>> | null {
|
|
9
|
+
const gateLease = websocketGate.tryAcquire();
|
|
10
|
+
if (!gateLease) return null;
|
|
11
|
+
let socket: ServerWebSocket<WsData> | undefined;
|
|
12
|
+
let active = true;
|
|
13
|
+
return {
|
|
14
|
+
bind(value) {
|
|
15
|
+
if (!active) return;
|
|
16
|
+
socket = value;
|
|
17
|
+
},
|
|
18
|
+
release() {
|
|
19
|
+
if (!active) return;
|
|
20
|
+
active = false;
|
|
21
|
+
socket = undefined;
|
|
22
|
+
gateLease.release();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function codexWebSocketAdmissionMetrics(): AdmissionMetrics {
|
|
28
|
+
return websocketGate.metrics();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const socketsByAccount = new Map<string, Set<ServerWebSocket<WsData>>>();
|
|
32
|
+
|
|
33
|
+
function trackedAccountId(ws: ServerWebSocket<WsData>): string | null {
|
|
34
|
+
const ctx = ws.data.authContext;
|
|
35
|
+
return ctx?.kind === "pool" || ctx?.kind === "main-pool" ? ctx.accountId : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function addSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
39
|
+
if (!accountId) return;
|
|
40
|
+
let sockets = socketsByAccount.get(accountId);
|
|
41
|
+
if (!sockets) {
|
|
42
|
+
sockets = new Set();
|
|
43
|
+
socketsByAccount.set(accountId, sockets);
|
|
44
|
+
}
|
|
45
|
+
sockets.add(ws);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function removeSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
49
|
+
if (!accountId) return;
|
|
50
|
+
const sockets = socketsByAccount.get(accountId);
|
|
51
|
+
if (!sockets) return;
|
|
52
|
+
sockets.delete(ws);
|
|
53
|
+
if (sockets.size === 0) socketsByAccount.delete(accountId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
57
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function unregisterCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
61
|
+
removeSocketForAccount(trackedAccountId(ws), ws);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function updateCodexWebSocketAuthContext(
|
|
65
|
+
ws: ServerWebSocket<WsData>,
|
|
66
|
+
authContext: WsData["authContext"],
|
|
67
|
+
): void {
|
|
68
|
+
const before = trackedAccountId(ws);
|
|
69
|
+
removeSocketForAccount(before, ws);
|
|
70
|
+
ws.data.authContext = authContext;
|
|
71
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function invalidateCodexWebSocketsForAccount(accountId: string): number {
|
|
75
|
+
const sockets = socketsByAccount.get(accountId);
|
|
76
|
+
if (!sockets) return 0;
|
|
77
|
+
const snapshot = [...sockets];
|
|
78
|
+
socketsByAccount.delete(accountId);
|
|
79
|
+
for (const ws of snapshot) {
|
|
80
|
+
try {
|
|
81
|
+
ws.data.cancel?.();
|
|
82
|
+
} catch {
|
|
83
|
+
/* ignore cancel callbacks during invalidation */
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
ws.close(4001, "Codex account invalidated");
|
|
87
|
+
} catch {
|
|
88
|
+
/* socket may already be closing */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return snapshot.length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getTrackedCodexWebSocketCountForAccount(accountId: string): number {
|
|
95
|
+
return socketsByAccount.get(accountId)?.size ?? 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function clearCodexWebSocketRegistry(): void {
|
|
99
|
+
socketsByAccount.clear();
|
|
100
|
+
}
|