@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,78 @@
|
|
|
1
|
+
import { realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
import { expandUserPath } from "../config";
|
|
4
|
+
import { defaultCodexHome } from "./home";
|
|
5
|
+
|
|
6
|
+
function resolveCodexHome(): string {
|
|
7
|
+
const raw = process.env.CODEX_HOME?.trim();
|
|
8
|
+
if (raw) {
|
|
9
|
+
const path = resolve(expandUserPath(raw));
|
|
10
|
+
let stat;
|
|
11
|
+
try {
|
|
12
|
+
stat = statSync(path);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15
|
+
throw new Error(`CODEX_HOME points to ${raw}, but that path could not be read: ${message}`);
|
|
16
|
+
}
|
|
17
|
+
if (!stat.isDirectory()) {
|
|
18
|
+
throw new Error(`CODEX_HOME points to ${raw}, but that path is not a directory`);
|
|
19
|
+
}
|
|
20
|
+
return realpathSync.native(path);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return defaultCodexHome();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const CODEX_HOME = resolveCodexHome();
|
|
27
|
+
export const CODEX_CONFIG_PATH = join(CODEX_HOME, "config.toml");
|
|
28
|
+
export const CODEX_PROFILE_PATH = join(CODEX_HOME, "opencodex.config.toml");
|
|
29
|
+
export const DEFAULT_CATALOG_PATH = join(CODEX_HOME, "opencodex-catalog.json");
|
|
30
|
+
/**
|
|
31
|
+
* Catalog consumed by Codex instances launched with the Remodex profile.
|
|
32
|
+
*
|
|
33
|
+
* Keep this separate from both config.toml and Codex's private models_cache.json:
|
|
34
|
+
* config.toml remains user-owned, while models_cache.json is an implementation
|
|
35
|
+
* cache whose wrapper and invalidation semantics can change between Codex builds.
|
|
36
|
+
*/
|
|
37
|
+
export const CODEX_EXTERNAL_MODELS_PATH = join(CODEX_HOME, "models.json");
|
|
38
|
+
export const CODEX_MODELS_CACHE_PATH = join(CODEX_HOME, "models_cache.json");
|
|
39
|
+
|
|
40
|
+
/** Runtime CODEX_HOME lookup (honors CODEX_HOME env changes after import). */
|
|
41
|
+
export function getCodexHome(): string {
|
|
42
|
+
return resolveCodexHome();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Runtime path lookup for callers that capture CODEX_HOME after module import. */
|
|
46
|
+
export function externalCodexModelsPath(codexHome = getCodexHome()): string {
|
|
47
|
+
return join(codexHome, "models.json");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function tomlString(value: string): string {
|
|
51
|
+
return JSON.stringify(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseTomlString(raw: string): string {
|
|
55
|
+
if (raw.startsWith("\"")) {
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(raw) as string;
|
|
58
|
+
} catch {
|
|
59
|
+
return raw.slice(1, -1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return raw.slice(1, -1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function readRootTomlString(content: string, key: string): string | null {
|
|
66
|
+
const lines = content.split("\n");
|
|
67
|
+
const firstTable = lines.findIndex(l => /^\s*\[/.test(l));
|
|
68
|
+
const rootLines = firstTable === -1 ? lines : lines.slice(0, firstTable);
|
|
69
|
+
for (const line of rootLines) {
|
|
70
|
+
const m = line.match(new RegExp(`^\\s*${key}\\s*=\\s*(\"(?:\\\\.|[^\"])*\"|'[^']*')`));
|
|
71
|
+
if (m) return parseTomlString(m[1]);
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function resolveCodexConfigPath(path: string): string {
|
|
77
|
+
return isAbsolute(path) ? path : join(CODEX_HOME, path);
|
|
78
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
|
+
import { CODEX_CONFIG_PATH } from "./paths";
|
|
4
|
+
import { redactUserPath } from "../lib/redact";
|
|
5
|
+
|
|
6
|
+
// Mirrors codex-rs core-plugins/src/marketplace.rs MARKETPLACE_MANIFEST_RELATIVE_PATHS.
|
|
7
|
+
// A marketplace root "resolves" only when one of these files exists under it.
|
|
8
|
+
const MARKETPLACE_MANIFEST_RELATIVE_PATHS = [
|
|
9
|
+
".agents/plugins/marketplace.json",
|
|
10
|
+
".claude-plugin/marketplace.json",
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
const OPENAI_BUNDLED_MARKETPLACE_NAME = "openai-bundled";
|
|
14
|
+
|
|
15
|
+
// Where the Codex desktop app's bundled plugins live, relative to an install
|
|
16
|
+
// root. Windows app-package paths embed the app version, so the actual install
|
|
17
|
+
// root is discovered by scanning candidate bases (LOCALAPPDATA, etc.) rather
|
|
18
|
+
// than hardcoded. The bundled marketplace dir is named after the marketplace.
|
|
19
|
+
const BUNDLED_MARKETPLACE_LEAF = join("plugins", "bundled-marketplaces", OPENAI_BUNDLED_MARKETPLACE_NAME);
|
|
20
|
+
const CODEX_APP_DIR_SEGMENTS = [join("Programs", "@openai", "codex"), join("Programs", "codex"), join("@openai", "codex"), "codex"] as const;
|
|
21
|
+
|
|
22
|
+
// Plugins the issue (#43) calls out. Treated as data, not as an authoritative
|
|
23
|
+
// allowlist: codex-rs only allowlists chrome/computer-use, but the diagnostic
|
|
24
|
+
// just reports presence, so listing browser here is informational only.
|
|
25
|
+
const COMMON_BUNDLED_PLUGINS = ["computer-use", "browser", "chrome"] as const;
|
|
26
|
+
|
|
27
|
+
export type CodexPluginsDiagnostic =
|
|
28
|
+
| { applicable: false; reason: string; summary: string }
|
|
29
|
+
| {
|
|
30
|
+
applicable: true;
|
|
31
|
+
stale: boolean;
|
|
32
|
+
marketplace: {
|
|
33
|
+
name: string;
|
|
34
|
+
present: boolean;
|
|
35
|
+
sourceType: string | null;
|
|
36
|
+
source: string | null;
|
|
37
|
+
resolvesToManifest: boolean;
|
|
38
|
+
currentBundledPath: string | null;
|
|
39
|
+
pathMismatch: boolean;
|
|
40
|
+
};
|
|
41
|
+
bundledPlugins: Array<{ id: string; configured: boolean }>;
|
|
42
|
+
suggestedRepair: string | null;
|
|
43
|
+
summary: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** True when the table at `[marketplaces.<name>]` exists in the config text. */
|
|
47
|
+
function readMarketplaceTable(configText: string, name: string): Record<string, string> | null {
|
|
48
|
+
// Split on CRLF or LF: config.toml on Windows (the platform this diagnostic
|
|
49
|
+
// targets) uses CRLF, and a leftover \r would defeat the `$`-anchored regexes.
|
|
50
|
+
const lines = configText.split(/\r?\n/);
|
|
51
|
+
const header = new RegExp(`^\\s*\\[marketplaces\\.(?:"${escapeRegExp(name)}"|${escapeRegExp(name)})\\]\\s*(?:#.*)?$`);
|
|
52
|
+
let start = -1;
|
|
53
|
+
for (let i = 0; i < lines.length; i++) {
|
|
54
|
+
if (header.test(lines[i] ?? "")) { start = i + 1; break; }
|
|
55
|
+
}
|
|
56
|
+
if (start === -1) return null;
|
|
57
|
+
|
|
58
|
+
const table: Record<string, string> = {};
|
|
59
|
+
for (let i = start; i < lines.length; i++) {
|
|
60
|
+
const line = lines[i] ?? "";
|
|
61
|
+
if (/^\s*\[/.test(line)) break; // next table starts; stop
|
|
62
|
+
const m = line.match(/^\s*([A-Za-z0-9_-]+)\s*=\s*("(?:\\.|[^"])*"|'[^']*'|[^#]+?)\s*(?:#.*)?$/);
|
|
63
|
+
if (!m) continue;
|
|
64
|
+
table[m[1]] = unquoteTomlValue(m[2].trim());
|
|
65
|
+
}
|
|
66
|
+
return table;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function unquoteTomlValue(raw: string): string {
|
|
70
|
+
if (raw.startsWith("\"")) {
|
|
71
|
+
try { return JSON.parse(raw) as string; } catch { return raw.slice(1, -1); }
|
|
72
|
+
}
|
|
73
|
+
if (raw.startsWith("'")) return raw.slice(1, -1);
|
|
74
|
+
return raw;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function escapeRegExp(value: string): string {
|
|
78
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A directory is a bundled-marketplace root when it holds a supported manifest. */
|
|
82
|
+
function dirHasManifest(dir: string): boolean {
|
|
83
|
+
return MARKETPLACE_MANIFEST_RELATIVE_PATHS.some(rel => existsSync(join(dir, rel)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Locate the bundled `openai-bundled` marketplace dir under the installed Codex
|
|
88
|
+
* desktop app on Windows. Windows app paths embed the app version, so we scan
|
|
89
|
+
* candidate install bases for a versioned app dir whose
|
|
90
|
+
* `plugins/bundled-marketplaces/openai-bundled` holds a manifest. Returns the
|
|
91
|
+
* newest matching dir (by mtime) or null. Filesystem access is injectable for
|
|
92
|
+
* tests so a Windows layout can be exercised on any OS.
|
|
93
|
+
*/
|
|
94
|
+
export function locateCurrentBundledMarketplace(
|
|
95
|
+
options: {
|
|
96
|
+
env?: NodeJS.ProcessEnv;
|
|
97
|
+
listDir?: (dir: string) => string[];
|
|
98
|
+
isManifestRoot?: (dir: string) => boolean;
|
|
99
|
+
mtimeOf?: (dir: string) => number;
|
|
100
|
+
} = {},
|
|
101
|
+
): string | null {
|
|
102
|
+
const env = options.env ?? process.env;
|
|
103
|
+
const listDir = options.listDir ?? ((dir: string) => {
|
|
104
|
+
try { return readdirSync(dir); } catch { return []; }
|
|
105
|
+
});
|
|
106
|
+
const isManifestRoot = options.isManifestRoot ?? dirHasManifest;
|
|
107
|
+
const mtimeOf = options.mtimeOf ?? ((dir: string) => {
|
|
108
|
+
try { return statSync(dir).mtimeMs; } catch { return 0; }
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const bases = [env.LOCALAPPDATA, env.PROGRAMFILES, env["ProgramFiles(x86)"], env.APPDATA]
|
|
112
|
+
.filter((b): b is string => typeof b === "string" && b.length > 0);
|
|
113
|
+
|
|
114
|
+
const candidates: string[] = [];
|
|
115
|
+
for (const base of bases) {
|
|
116
|
+
for (const seg of CODEX_APP_DIR_SEGMENTS) {
|
|
117
|
+
const appRoot = join(base, seg);
|
|
118
|
+
// Direct (unversioned) layout.
|
|
119
|
+
const direct = join(appRoot, BUNDLED_MARKETPLACE_LEAF);
|
|
120
|
+
if (isManifestRoot(direct)) candidates.push(direct);
|
|
121
|
+
// Versioned layout: appRoot/<version>/plugins/bundled-marketplaces/openai-bundled
|
|
122
|
+
for (const child of listDir(appRoot)) {
|
|
123
|
+
const versioned = join(appRoot, child, BUNDLED_MARKETPLACE_LEAF);
|
|
124
|
+
if (isManifestRoot(versioned)) candidates.push(versioned);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (candidates.length === 0) return null;
|
|
129
|
+
candidates.sort((a, b) => mtimeOf(b) - mtimeOf(a));
|
|
130
|
+
return candidates[0] ?? null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Normalize a path for comparison: lowercase + unify separators (Windows is case-insensitive). */
|
|
134
|
+
function normalizePathForCompare(path: string): string {
|
|
135
|
+
return path.replace(/[\\/]+/g, "\\").replace(/\\+$/, "").toLowerCase();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** A local marketplace `source` resolves when it holds a supported manifest. */
|
|
139
|
+
function sourceResolvesToManifest(source: string): boolean {
|
|
140
|
+
if (!isAbsolute(source)) return false;
|
|
141
|
+
if (!existsSync(source)) return false;
|
|
142
|
+
return MARKETPLACE_MANIFEST_RELATIVE_PATHS.some(rel => existsSync(join(source, rel)));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Read-only diagnostic for the Codex `openai-bundled` plugin marketplace.
|
|
147
|
+
*
|
|
148
|
+
* Only meaningful on Windows, where app-package paths embed the app version and
|
|
149
|
+
* go stale after an update. On other platforms it reports "not applicable".
|
|
150
|
+
* NEVER mutates config.toml, never invokes `codex plugin marketplace add`.
|
|
151
|
+
*/
|
|
152
|
+
export function diagnoseCodexBundledPlugins(
|
|
153
|
+
options: {
|
|
154
|
+
platform?: NodeJS.Platform;
|
|
155
|
+
configPath?: string;
|
|
156
|
+
locateCurrent?: () => string | null;
|
|
157
|
+
} = {},
|
|
158
|
+
): CodexPluginsDiagnostic {
|
|
159
|
+
const platform = options.platform ?? process.platform;
|
|
160
|
+
if (platform !== "win32") {
|
|
161
|
+
return {
|
|
162
|
+
applicable: false,
|
|
163
|
+
reason: "not_windows",
|
|
164
|
+
summary: "not applicable (bundled-marketplace staleness is Windows-specific)",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const configPath = options.configPath ?? CODEX_CONFIG_PATH;
|
|
169
|
+
let configText: string;
|
|
170
|
+
try {
|
|
171
|
+
configText = readFileSync(configPath, "utf8");
|
|
172
|
+
} catch {
|
|
173
|
+
return {
|
|
174
|
+
applicable: false,
|
|
175
|
+
reason: "config_unreadable",
|
|
176
|
+
summary: "not applicable (Codex config.toml not found or unreadable)",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const table = readMarketplaceTable(configText, OPENAI_BUNDLED_MARKETPLACE_NAME);
|
|
181
|
+
const present = table !== null;
|
|
182
|
+
const sourceType = table?.source_type ?? null;
|
|
183
|
+
const source = table?.source ?? null;
|
|
184
|
+
const isLocal = sourceType === "local" && !!source;
|
|
185
|
+
const resolvesToManifest = isLocal ? sourceResolvesToManifest(source as string) : false;
|
|
186
|
+
|
|
187
|
+
// Locate the bundled marketplace under the currently installed Codex app, so
|
|
188
|
+
// we can tell "registered path differs from the live app path" (the Windows
|
|
189
|
+
// app-update staleness signal) apart from a merely missing manifest.
|
|
190
|
+
const locateCurrent = options.locateCurrent ?? (() => locateCurrentBundledMarketplace());
|
|
191
|
+
const currentBundledPath = locateCurrent();
|
|
192
|
+
const pathMismatch = !!(
|
|
193
|
+
currentBundledPath && source && isLocal &&
|
|
194
|
+
normalizePathForCompare(currentBundledPath) !== normalizePathForCompare(source)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// Stale = a registered local bundled marketplace whose source no longer
|
|
198
|
+
// resolves to a manifest, OR whose registered path differs from the live
|
|
199
|
+
// app's bundled path (the Windows app-update signal). A missing marketplace
|
|
200
|
+
// is "not stale" but flagged separately by `present: false`.
|
|
201
|
+
const stale = present && isLocal && (!resolvesToManifest || pathMismatch);
|
|
202
|
+
// Present but not a usable local entry (wrong source_type or empty source):
|
|
203
|
+
// not "stale" in the app-update sense, but it must NOT be reported as healthy.
|
|
204
|
+
const malformed = present && !isLocal;
|
|
205
|
+
|
|
206
|
+
const bundledPlugins = COMMON_BUNDLED_PLUGINS.map(id => ({
|
|
207
|
+
id,
|
|
208
|
+
configured: new RegExp(`\\[plugins\\.(?:"${escapeRegExp(`${id}@${OPENAI_BUNDLED_MARKETPLACE_NAME}`)}")\\]`).test(configText),
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
const repairTarget = currentBundledPath ?? `<current ${OPENAI_BUNDLED_MARKETPLACE_NAME} path under the installed Codex app>`;
|
|
212
|
+
const suggestedRepair = (stale || pathMismatch)
|
|
213
|
+
? `codex plugin marketplace add ${currentBundledPath ? redactUserPath(currentBundledPath) : repairTarget}`
|
|
214
|
+
: null;
|
|
215
|
+
|
|
216
|
+
const summary = !present
|
|
217
|
+
? `no [marketplaces.${OPENAI_BUNDLED_MARKETPLACE_NAME}] entry in Codex config`
|
|
218
|
+
: malformed
|
|
219
|
+
? `[marketplaces.${OPENAI_BUNDLED_MARKETPLACE_NAME}] is present but not a usable local source (source_type/source missing)`
|
|
220
|
+
: !resolvesToManifest
|
|
221
|
+
? `stale: registered ${OPENAI_BUNDLED_MARKETPLACE_NAME} source no longer resolves to a marketplace manifest`
|
|
222
|
+
: pathMismatch
|
|
223
|
+
? `stale: registered ${OPENAI_BUNDLED_MARKETPLACE_NAME} path differs from the installed Codex app's bundled path`
|
|
224
|
+
: `ok: ${OPENAI_BUNDLED_MARKETPLACE_NAME} marketplace resolves`;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
applicable: true,
|
|
228
|
+
stale,
|
|
229
|
+
marketplace: {
|
|
230
|
+
name: OPENAI_BUNDLED_MARKETPLACE_NAME,
|
|
231
|
+
present,
|
|
232
|
+
sourceType,
|
|
233
|
+
source: source ? redactUserPath(source) : null,
|
|
234
|
+
resolvesToManifest,
|
|
235
|
+
currentBundledPath: currentBundledPath ? redactUserPath(currentBundledPath) : null,
|
|
236
|
+
pathMismatch,
|
|
237
|
+
},
|
|
238
|
+
bundledPlugins,
|
|
239
|
+
suggestedRepair,
|
|
240
|
+
summary,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type { OcxAccountPoolRotationStrategy } from "../types";
|
|
2
|
+
import type { GenerationContext } from "../lib/state-store-sweeper";
|
|
3
|
+
|
|
4
|
+
export const POOL_KEY_CODEX = "codex";
|
|
5
|
+
export const POOL_KEY_ANTHROPIC = "anthropic";
|
|
6
|
+
|
|
7
|
+
interface SelectionState {
|
|
8
|
+
activeKey?: string;
|
|
9
|
+
successes: number;
|
|
10
|
+
currentWeights: Map<string, number>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const selectionState = new Map<string, SelectionState>();
|
|
14
|
+
let lastReconciledGeneration = 0;
|
|
15
|
+
|
|
16
|
+
const DEFAULT_STICKY_LIMIT = 1;
|
|
17
|
+
const MIN_STICKY_LIMIT = 1;
|
|
18
|
+
const MAX_STICKY_LIMIT = 100;
|
|
19
|
+
const DEFAULT_STRATEGY: OcxAccountPoolRotationStrategy = "quota";
|
|
20
|
+
const VALID_STRATEGIES = new Set<OcxAccountPoolRotationStrategy>(["quota", "round-robin", "fill-first"]);
|
|
21
|
+
|
|
22
|
+
/** Selection order for an account with no stored preference: one flat tier. */
|
|
23
|
+
export const DEFAULT_ACCOUNT_PRIORITY = 0;
|
|
24
|
+
export const MIN_ACCOUNT_PRIORITY = -100;
|
|
25
|
+
export const MAX_ACCOUNT_PRIORITY = 100;
|
|
26
|
+
|
|
27
|
+
/** Strict parse for management APIs — returns null instead of defaulting. */
|
|
28
|
+
export function parseAccountPoolStrategy(raw: unknown): OcxAccountPoolRotationStrategy | null {
|
|
29
|
+
if (typeof raw === "string" && VALID_STRATEGIES.has(raw as OcxAccountPoolRotationStrategy)) {
|
|
30
|
+
return raw as OcxAccountPoolRotationStrategy;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Strict parse for management APIs — returns null instead of defaulting. */
|
|
36
|
+
export function parseAccountPoolStickyLimit(raw: unknown): number | null {
|
|
37
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= MIN_STICKY_LIMIT && raw <= MAX_STICKY_LIMIT) {
|
|
38
|
+
return raw;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function normalizeAccountPoolStrategy(raw: unknown): OcxAccountPoolRotationStrategy {
|
|
44
|
+
return parseAccountPoolStrategy(raw) ?? DEFAULT_STRATEGY;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function normalizeAccountPoolStickyLimit(raw: unknown): number {
|
|
48
|
+
return parseAccountPoolStickyLimit(raw) ?? DEFAULT_STICKY_LIMIT;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Strict parse for management APIs — returns null instead of defaulting. */
|
|
52
|
+
export function parseAccountPriority(raw: unknown): number | null {
|
|
53
|
+
if (
|
|
54
|
+
typeof raw === "number"
|
|
55
|
+
&& Number.isInteger(raw)
|
|
56
|
+
&& raw >= MIN_ACCOUNT_PRIORITY
|
|
57
|
+
&& raw <= MAX_ACCOUNT_PRIORITY
|
|
58
|
+
) {
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function normalizeAccountPriority(raw: unknown): number {
|
|
65
|
+
return parseAccountPriority(raw) ?? DEFAULT_ACCOUNT_PRIORITY;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Narrow an already-eligible account list to the highest selection-order tier that
|
|
70
|
+
* still has usable quota. Priority is an *ordering* boundary layered on top of
|
|
71
|
+
* eligibility: it never admits an account the caller already filtered out, and it
|
|
72
|
+
* never keeps the pool on a tier whose every member is drained.
|
|
73
|
+
*
|
|
74
|
+
* Contract (each clause is load-bearing for "no behavior change when unconfigured"):
|
|
75
|
+
* - one distinct priority across `ids` (the unconfigured case) returns `ids` unchanged,
|
|
76
|
+
* so today's pick sequence is preserved byte for byte;
|
|
77
|
+
* - input order is preserved inside the returned tier, which keeps the `__main__`
|
|
78
|
+
* head-of-list bias and the first-index tie-break used by SWRR/lowest-usage;
|
|
79
|
+
* - every tier drained returns `ids` unchanged, reproducing today's
|
|
80
|
+
* stay-put-until-429 behavior rather than inventing a pick;
|
|
81
|
+
* - a `pinnedId` that is present *and* has headroom lowers the ceiling to its own
|
|
82
|
+
* tier, which is what makes a manual "use this now" survive round-robin and
|
|
83
|
+
* fill-first without any mutable selection state. A drained or absent pin is
|
|
84
|
+
* ignored, so the pin expires on its own once the account crosses the threshold.
|
|
85
|
+
*/
|
|
86
|
+
export function selectPriorityTier(
|
|
87
|
+
ids: readonly string[],
|
|
88
|
+
priorityOf: (id: string) => number,
|
|
89
|
+
hasHeadroom: (id: string) => boolean,
|
|
90
|
+
pinnedId?: string,
|
|
91
|
+
): readonly string[] {
|
|
92
|
+
// Readonly out as well as in: the no-change cases return the caller's own array, so a
|
|
93
|
+
// mutating caller would corrupt its input in exactly the cases that must not change.
|
|
94
|
+
const list = ids;
|
|
95
|
+
if (list.length <= 1) return list;
|
|
96
|
+
|
|
97
|
+
const priorities = list.map(priorityOf);
|
|
98
|
+
const firstPriority = priorities[0]!;
|
|
99
|
+
if (priorities.every(priority => priority === firstPriority)) return list;
|
|
100
|
+
|
|
101
|
+
let ceiling = Number.POSITIVE_INFINITY;
|
|
102
|
+
if (pinnedId !== undefined) {
|
|
103
|
+
const pinnedIndex = list.indexOf(pinnedId);
|
|
104
|
+
if (pinnedIndex >= 0 && hasHeadroom(pinnedId)) ceiling = priorities[pinnedIndex]!;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const tiers = [...new Set(priorities)].sort((a, b) => b - a);
|
|
108
|
+
for (const tier of tiers) {
|
|
109
|
+
if (tier > ceiling) continue;
|
|
110
|
+
const members = list.filter((_, index) => priorities[index] === tier);
|
|
111
|
+
if (members.some(hasHeadroom)) return members;
|
|
112
|
+
}
|
|
113
|
+
return list;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getOrCreateState(poolKey: string): SelectionState {
|
|
117
|
+
let state = selectionState.get(poolKey);
|
|
118
|
+
if (!state) {
|
|
119
|
+
state = { successes: 0, currentWeights: new Map() };
|
|
120
|
+
selectionState.set(poolKey, state);
|
|
121
|
+
}
|
|
122
|
+
return state;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function cloneSelectionState(state: SelectionState): SelectionState {
|
|
126
|
+
return {
|
|
127
|
+
activeKey: state.activeKey,
|
|
128
|
+
successes: state.successes,
|
|
129
|
+
currentWeights: new Map(state.currentWeights),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function smoothWeightedIndex(ids: readonly string[], state: SelectionState): number {
|
|
134
|
+
let best = -1;
|
|
135
|
+
let bestScore = Number.NEGATIVE_INFINITY;
|
|
136
|
+
let total = 0;
|
|
137
|
+
const weight = 1;
|
|
138
|
+
for (let i = 0; i < ids.length; i++) {
|
|
139
|
+
const id = ids[i]!;
|
|
140
|
+
const score = (state.currentWeights.get(id) ?? 0) + weight;
|
|
141
|
+
state.currentWeights.set(id, score);
|
|
142
|
+
total += weight;
|
|
143
|
+
if (score > bestScore) {
|
|
144
|
+
best = i;
|
|
145
|
+
bestScore = score;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (best >= 0) {
|
|
149
|
+
const key = ids[best]!;
|
|
150
|
+
state.currentWeights.set(key, (state.currentWeights.get(key) ?? 0) - total);
|
|
151
|
+
}
|
|
152
|
+
return best;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Shared pick core. Mutates `state` the same way live resolve does; callers pass
|
|
157
|
+
* either the live map entry or a scratch/clone for dry-run peek.
|
|
158
|
+
*/
|
|
159
|
+
function pickRoundRobinFromState(
|
|
160
|
+
eligibleIds: readonly string[],
|
|
161
|
+
stickyLimit: number,
|
|
162
|
+
state: SelectionState,
|
|
163
|
+
commitSticky: boolean,
|
|
164
|
+
): string | null {
|
|
165
|
+
if (eligibleIds.length === 0) return null;
|
|
166
|
+
|
|
167
|
+
const limit = normalizeAccountPoolStickyLimit(stickyLimit);
|
|
168
|
+
|
|
169
|
+
if (state.activeKey && eligibleIds.includes(state.activeKey)) {
|
|
170
|
+
return state.activeKey;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (state.activeKey) {
|
|
174
|
+
delete state.activeKey;
|
|
175
|
+
state.successes = 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const index = smoothWeightedIndex(eligibleIds, state);
|
|
179
|
+
if (index < 0) return null;
|
|
180
|
+
|
|
181
|
+
const picked = eligibleIds[index]!;
|
|
182
|
+
if (commitSticky && limit > 1) {
|
|
183
|
+
state.activeKey = picked;
|
|
184
|
+
state.successes = 0;
|
|
185
|
+
}
|
|
186
|
+
return picked;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function pickRoundRobinAccount(
|
|
190
|
+
poolKey: string,
|
|
191
|
+
eligibleIds: readonly string[],
|
|
192
|
+
stickyLimit: number,
|
|
193
|
+
): string | null {
|
|
194
|
+
return pickRoundRobinFromState(eligibleIds, stickyLimit, getOrCreateState(poolKey), true);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Dry-run of {@link pickRoundRobinAccount}: returns the same account resolve would
|
|
199
|
+
* pick without advancing ring weights, activeKey, or successes.
|
|
200
|
+
*/
|
|
201
|
+
export function peekRoundRobinAccount(
|
|
202
|
+
poolKey: string,
|
|
203
|
+
eligibleIds: readonly string[],
|
|
204
|
+
stickyLimit: number,
|
|
205
|
+
): string | null {
|
|
206
|
+
const live = selectionState.get(poolKey);
|
|
207
|
+
const scratch = live
|
|
208
|
+
? cloneSelectionState(live)
|
|
209
|
+
: { successes: 0, currentWeights: new Map<string, number>() };
|
|
210
|
+
return pickRoundRobinFromState(eligibleIds, stickyLimit, scratch, false);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function notePoolRotationSuccess(
|
|
214
|
+
poolKey: string,
|
|
215
|
+
accountId: string,
|
|
216
|
+
stickyLimit: number,
|
|
217
|
+
): void {
|
|
218
|
+
const limit = normalizeAccountPoolStickyLimit(stickyLimit);
|
|
219
|
+
const state = selectionState.get(poolKey);
|
|
220
|
+
if (!state) return;
|
|
221
|
+
if (state.activeKey !== accountId) {
|
|
222
|
+
state.activeKey = accountId;
|
|
223
|
+
state.successes = 0;
|
|
224
|
+
}
|
|
225
|
+
state.successes += 1;
|
|
226
|
+
if (state.successes >= limit) {
|
|
227
|
+
delete state.activeKey;
|
|
228
|
+
state.successes = 0;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function notePoolRotationFailure(poolKey: string, accountId: string): void {
|
|
233
|
+
const state = selectionState.get(poolKey);
|
|
234
|
+
if (state?.activeKey === accountId) {
|
|
235
|
+
delete state.activeKey;
|
|
236
|
+
state.successes = 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Force the next sticky/RR pick onto `accountId` (manual dashboard selection).
|
|
242
|
+
* Clears sticky success counters and ring weights so the seeded account is held
|
|
243
|
+
* for the next new-session pick before ordinary rotation resumes.
|
|
244
|
+
*/
|
|
245
|
+
export function seedPoolRotationAccount(poolKey: string, accountId: string): void {
|
|
246
|
+
const state = getOrCreateState(poolKey);
|
|
247
|
+
state.activeKey = accountId;
|
|
248
|
+
state.successes = 0;
|
|
249
|
+
state.currentWeights.clear();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function clearPoolRotationState(poolKey?: string): void {
|
|
253
|
+
if (poolKey === undefined) {
|
|
254
|
+
selectionState.clear();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
selectionState.delete(poolKey);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function reconcilePoolRotationState(context: GenerationContext): number {
|
|
261
|
+
if (context.generation <= lastReconciledGeneration) return 0;
|
|
262
|
+
const anthropicIds = new Set<string>();
|
|
263
|
+
for (const key of context.oauthAccountKeys) {
|
|
264
|
+
const separator = key.indexOf("\0");
|
|
265
|
+
if (separator > 0 && key.slice(0, separator) === "anthropic") {
|
|
266
|
+
anthropicIds.add(key.slice(separator + 1));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
let removed = 0;
|
|
270
|
+
for (const [poolKey, state] of selectionState) {
|
|
271
|
+
const valid = poolKey === POOL_KEY_ANTHROPIC
|
|
272
|
+
? anthropicIds
|
|
273
|
+
: poolKey === POOL_KEY_CODEX || poolKey.startsWith(`${POOL_KEY_CODEX}:`)
|
|
274
|
+
? context.codexAccountIds
|
|
275
|
+
: null;
|
|
276
|
+
if (!valid) continue;
|
|
277
|
+
if (valid.size === 0) {
|
|
278
|
+
selectionState.delete(poolKey);
|
|
279
|
+
removed += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (state.activeKey && !valid.has(state.activeKey)) {
|
|
283
|
+
delete state.activeKey;
|
|
284
|
+
state.successes = 0;
|
|
285
|
+
removed += 1;
|
|
286
|
+
}
|
|
287
|
+
for (const accountId of state.currentWeights.keys()) {
|
|
288
|
+
if (valid.has(accountId)) continue;
|
|
289
|
+
state.currentWeights.delete(accountId);
|
|
290
|
+
removed += 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
lastReconciledGeneration = context.generation;
|
|
294
|
+
return removed;
|
|
295
|
+
}
|