@iislee/opencodex 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +225 -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 +451 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-DTpMHS4F.js +67 -0
- package/gui/dist/assets/index-ZNVDE3C7.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/claude.svg +1 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/copilot.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/cursor.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/gemini.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-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/kiro.svg +14 -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 +1 -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 +102 -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 +1003 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +72 -0
- package/src/adapters/client-fingerprint.ts +59 -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 +127 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +211 -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 +1214 -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 +55 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +329 -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 +576 -0
- package/src/adapters/cursor/protobuf-events.ts +563 -0
- package/src/adapters/cursor/protobuf-request.ts +714 -0
- package/src/adapters/cursor/request-builder.ts +255 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +505 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +52 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +303 -0
- package/src/adapters/google-antigravity-wire.ts +108 -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 +13 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +758 -0
- package/src/adapters/identity.ts +44 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +197 -0
- package/src/adapters/kiro-events.ts +179 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +96 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +215 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1898 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1005 -0
- package/src/adapters/openai-responses.ts +1137 -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/bridge.ts +1619 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +765 -0
- package/src/claude/agents-inject.ts +243 -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 +381 -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 +509 -0
- package/src/claude/model-info.ts +151 -0
- package/src/claude/outbound.ts +872 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +268 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account-extended.ts +350 -0
- package/src/cli/account.ts +275 -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 +188 -0
- package/src/cli/claude.ts +286 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/doctor.ts +930 -0
- package/src/cli/export-command.ts +187 -0
- package/src/cli/help.ts +354 -0
- package/src/cli/index.ts +1113 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +142 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +20 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +336 -0
- package/src/cli/observe.ts +117 -0
- package/src/cli/opencode.ts +586 -0
- package/src/cli/pi.ts +188 -0
- package/src/cli/provider-runtime.ts +162 -0
- package/src/cli/provider.ts +463 -0
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +155 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/sync-cloud.ts +283 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/tray-proxy.ts +52 -0
- package/src/cli/v2.ts +173 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +377 -0
- package/src/clients/effective-status.ts +385 -0
- package/src/clients/probes/agy.ts +55 -0
- package/src/clients/probes/cc-switch.ts +110 -0
- package/src/clients/probes/claude.ts +90 -0
- package/src/clients/probes/codex.ts +125 -0
- package/src/clients/probes/grok.ts +29 -0
- package/src/clients/probes/opencode.ts +109 -0
- package/src/clients/probes/paseo.ts +55 -0
- package/src/clients/probes/pi.ts +55 -0
- package/src/cloud/onedrive-auth.ts +666 -0
- package/src/cloud/onedrive-graph.ts +108 -0
- package/src/cloud/settings.ts +75 -0
- package/src/cloud/sync.ts +212 -0
- package/src/cloud/types.ts +56 -0
- package/src/cloud/vault.ts +89 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -0
- package/src/codex/account-lifecycle.ts +55 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +149 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +517 -0
- package/src/codex/account-usability.ts +20 -0
- package/src/codex/app-server-processes.ts +756 -0
- package/src/codex/auth-api.ts +1540 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +352 -0
- package/src/codex/autostart-health.ts +149 -0
- package/src/codex/catalog/aggregation.ts +378 -0
- package/src/codex/catalog/bundled.ts +251 -0
- package/src/codex/catalog/effort.ts +355 -0
- package/src/codex/catalog/metadata.ts +180 -0
- package/src/codex/catalog/parsing.ts +456 -0
- package/src/codex/catalog/provider-fetch.ts +902 -0
- package/src/codex/catalog/sync.ts +620 -0
- package/src/codex/catalog.ts +12 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +969 -0
- package/src/codex/history-migration-guardian.ts +102 -0
- package/src/codex/history-provider.ts +776 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject.ts +799 -0
- package/src/codex/injected-marker.ts +72 -0
- package/src/codex/journal.ts +163 -0
- package/src/codex/main-account-cache.ts +32 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/model-cache.ts +227 -0
- package/src/codex/paths.ts +65 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +225 -0
- package/src/codex/project-config-warnings.ts +411 -0
- package/src/codex/quota.ts +411 -0
- package/src/codex/refresh.ts +53 -0
- package/src/codex/routing.ts +1477 -0
- package/src/codex/runtime.ts +538 -0
- package/src/codex/shim.ts +1189 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +469 -0
- package/src/codex/sync.ts +130 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +41 -0
- package/src/combos/request.ts +62 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +326 -0
- package/src/config.ts +2356 -0
- package/src/generated/jawcode-model-metadata.ts +104 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +545 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +103 -0
- package/src/grok/usage-hook/report.mjs +348 -0
- package/src/grok/usage-hook.ts +278 -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 +829 -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/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 +202 -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 +71 -0
- package/src/lib/bun-stream-caps.ts +126 -0
- package/src/lib/config-ownership.ts +360 -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/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +151 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +165 -0
- package/src/lib/provider-outbound.ts +170 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +105 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +30 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +323 -0
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +356 -0
- package/src/lib/upstream-retry.ts +239 -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 +514 -0
- package/src/lib/winsw.ts +375 -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/cursor.ts +211 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +399 -0
- package/src/oauth/index.ts +1174 -0
- package/src/oauth/key-providers.ts +108 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +577 -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 +630 -0
- package/src/oauth/token-guardian.ts +303 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/pi/extensions.ts +72 -0
- package/src/pi/home.ts +42 -0
- package/src/pi/index.ts +40 -0
- package/src/pi/models.ts +278 -0
- package/src/pi/packages.ts +219 -0
- package/src/pi/settings.ts +365 -0
- package/src/pi/status.ts +68 -0
- package/src/pi/sync.ts +75 -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 +205 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/context-cap.ts +65 -0
- package/src/providers/derive.ts +339 -0
- package/src/providers/free-directory.ts +184 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/key-failover.ts +203 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery.ts +356 -0
- package/src/providers/openai-sidecar.ts +175 -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 +82 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +150 -0
- package/src/providers/quota.ts +1260 -0
- package/src/providers/registry.ts +1600 -0
- package/src/providers/slug-codec.ts +67 -0
- package/src/providers/xai-transport.ts +141 -0
- package/src/reasoning-effort.ts +135 -0
- package/src/responses/compaction.ts +117 -0
- package/src/responses/parser.ts +656 -0
- package/src/responses/reasoning-envelope.ts +52 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +394 -0
- package/src/responses/state.ts +895 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +425 -0
- package/src/server/adapter-resolve.ts +80 -0
- package/src/server/auth-cors.ts +530 -0
- package/src/server/chat-completions.ts +368 -0
- package/src/server/claude-messages.ts +914 -0
- package/src/server/effort-policy.ts +172 -0
- package/src/server/gui-static.ts +123 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +476 -0
- package/src/server/index.ts +1126 -0
- package/src/server/lifecycle.ts +227 -0
- package/src/server/live.ts +598 -0
- package/src/server/management/agent-settings-routes.ts +1169 -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/clients-routes.ts +63 -0
- package/src/server/management/cloud-sync-routes.ts +266 -0
- package/src/server/management/combo-routes.ts +220 -0
- package/src/server/management/config-routes.ts +422 -0
- package/src/server/management/context.ts +31 -0
- package/src/server/management/logs-usage-routes.ts +707 -0
- package/src/server/management/model-routes.ts +525 -0
- package/src/server/management/oauth-account-routes.ts +563 -0
- package/src/server/management/provider-routes.ts +556 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +90 -0
- package/src/server/management/system-restart.ts +179 -0
- package/src/server/management/system-routes.ts +117 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +215 -0
- package/src/server/management-auth.ts +267 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +116 -0
- package/src/server/proxy-liveness.ts +201 -0
- package/src/server/relay-eager.ts +313 -0
- package/src/server/relay.ts +1049 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1046 -0
- package/src/server/responses/collaboration.ts +354 -0
- package/src/server/responses/compact.ts +384 -0
- package/src/server/responses/core.ts +2758 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +157 -0
- package/src/server/responses/passthrough-error.ts +78 -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 +224 -0
- package/src/server/responses.ts +9 -0
- package/src/server/search.ts +136 -0
- package/src/server/sse-payload-rewrite.ts +175 -0
- package/src/server/startup-action-control.ts +308 -0
- package/src/server/startup-health-cache.ts +113 -0
- package/src/server/system-env.ts +413 -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 +471 -0
- package/src/service.ts +2554 -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 +290 -0
- package/src/tray/windows.ts +730 -0
- package/src/types.ts +1237 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +407 -0
- package/src/update/job.ts +1520 -0
- package/src/update/notify.ts +257 -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 +164 -0
- package/src/usage/log.ts +658 -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 +125 -0
- package/src/vision/index.ts +467 -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 +664 -0
- package/src/web-search/parse.ts +220 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
package/src/cli/init.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import * as readline from "node:readline";
|
|
2
|
+
import { constants as fsConstants, copyFileSync, existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { injectCodexConfig } from "../codex/inject";
|
|
4
|
+
import { classifyOpenAiTierBackup, getConfigPath, getDefaultConfig, isValidProviderName, saveConfig } from "../config";
|
|
5
|
+
import { enrichProviderFromCatalog } from "../oauth/key-providers";
|
|
6
|
+
import { deriveInitProviders } from "../providers/derive";
|
|
7
|
+
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
8
|
+
|
|
9
|
+
function createPrompt(): { ask(question: string): Promise<string>; close(): void } {
|
|
10
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
+
let closed = false;
|
|
12
|
+
rl.on("close", () => { closed = true; });
|
|
13
|
+
return {
|
|
14
|
+
ask(question: string): Promise<string> {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
if (closed) {
|
|
17
|
+
reject(new Error("stdin closed before the prompt could be answered"));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const onClose = () => {
|
|
21
|
+
reject(new Error("stdin reached EOF while waiting for input"));
|
|
22
|
+
};
|
|
23
|
+
rl.once("close", onClose);
|
|
24
|
+
rl.question(question, answer => {
|
|
25
|
+
rl.off("close", onClose);
|
|
26
|
+
resolve(answer);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
close() {
|
|
31
|
+
if (!closed) rl.close();
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type InitKind = "forward" | "oauth" | "key" | "local";
|
|
37
|
+
export interface InitProvider {
|
|
38
|
+
id: string;
|
|
39
|
+
label: string;
|
|
40
|
+
adapter: string;
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
kind: InitKind;
|
|
43
|
+
dashboardUrl?: string;
|
|
44
|
+
defaultModel?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The full CLI provider menu, derived from the canonical provider registry so `ocx init`,
|
|
49
|
+
* the GUI picker, key-login catalog, OAuth seeds, and metadata aliases cannot drift.
|
|
50
|
+
*/
|
|
51
|
+
export function buildInitProviders(): InitProvider[] {
|
|
52
|
+
return deriveInitProviders();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const KIND_HEADING: Record<InitKind, string> = {
|
|
56
|
+
forward: "ChatGPT login",
|
|
57
|
+
oauth: "Account login (OAuth — then run: ocx login <id>)",
|
|
58
|
+
key: "API key (paste a key from the provider's dashboard)",
|
|
59
|
+
local: "Local servers (usually no key)",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
function printMenu(providers: InitProvider[]): void {
|
|
63
|
+
console.log("Choose your default provider (you can add more later):");
|
|
64
|
+
let lastKind: InitKind | null = null;
|
|
65
|
+
providers.forEach((p, i) => {
|
|
66
|
+
if (p.kind !== lastKind) { console.log(`\n ${KIND_HEADING[p.kind]}:`); lastKind = p.kind; }
|
|
67
|
+
console.log(` ${String(i + 1).padStart(2)}. ${p.label}`);
|
|
68
|
+
});
|
|
69
|
+
console.log(`\n ${providers.length + 1}. custom (enter URL manually)`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const envKeyFor = (id: string) => `${id.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`;
|
|
73
|
+
|
|
74
|
+
/** Post-init cleanup of `.pre-openai-tiers-v2.bak` with rollback preservation (issue #257). */
|
|
75
|
+
export function cleanupOpenAiTierBackupAfterInit(configPath = getConfigPath()): void {
|
|
76
|
+
const backup = `${configPath}.pre-openai-tiers-v2.bak`;
|
|
77
|
+
try {
|
|
78
|
+
if (!existsSync(backup)) return;
|
|
79
|
+
if (classifyOpenAiTierBackup(readFileSync(backup)) === "stale") {
|
|
80
|
+
unlinkSync(backup);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Publish the preserved snapshot with a no-replace copy (COPYFILE_EXCL) so a
|
|
84
|
+
// destination collision (frozen/rolled-back clock, pre-created file) can never
|
|
85
|
+
// silently overwrite another rollback snapshot; retry with a sequence suffix.
|
|
86
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
87
|
+
const preserved = `${configPath}.pre-openai-tiers-v1-rollback.${Date.now()}${attempt ? `-${attempt}` : ""}.bak`;
|
|
88
|
+
try {
|
|
89
|
+
copyFileSync(backup, preserved, fsConstants.COPYFILE_EXCL);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if ((error as NodeJS.ErrnoException).code === "EEXIST") continue;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
unlinkSync(backup);
|
|
95
|
+
console.warn(`⚠️ Kept your pre-migration config rollback snapshot at ${preserved}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
} catch { /* cleanup is best-effort; never block init on backup housekeeping */ }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function runInit(): Promise<void> {
|
|
102
|
+
const prompt = createPrompt();
|
|
103
|
+
try {
|
|
104
|
+
console.log("\n🔧 opencodex (ocx) setup\n");
|
|
105
|
+
|
|
106
|
+
const providers = buildInitProviders();
|
|
107
|
+
printMenu(providers);
|
|
108
|
+
|
|
109
|
+
const choice = await prompt.ask("\nSelect default provider (number): ");
|
|
110
|
+
const idx = parseInt(choice, 10) - 1;
|
|
111
|
+
|
|
112
|
+
let providerName: string;
|
|
113
|
+
let providerConfig: OcxProviderConfig;
|
|
114
|
+
let oauthHint = false;
|
|
115
|
+
|
|
116
|
+
if (idx >= 0 && idx < providers.length) {
|
|
117
|
+
const p = providers[idx];
|
|
118
|
+
providerName = p.id;
|
|
119
|
+
console.log(`\n📡 ${p.label}`);
|
|
120
|
+
console.log(` Base URL: ${p.baseUrl}`);
|
|
121
|
+
|
|
122
|
+
if (p.kind === "forward") {
|
|
123
|
+
providerConfig = { adapter: p.adapter, baseUrl: p.baseUrl, authMode: "forward" };
|
|
124
|
+
console.log(" No API key needed — forwards your existing `codex login`.");
|
|
125
|
+
} else if (p.kind === "oauth") {
|
|
126
|
+
providerConfig = { adapter: p.adapter, baseUrl: p.baseUrl, authMode: "oauth", ...(p.defaultModel ? { defaultModel: p.defaultModel } : {}) };
|
|
127
|
+
oauthHint = true;
|
|
128
|
+
} else {
|
|
129
|
+
// key + local: collect a key (local usually blank).
|
|
130
|
+
if (p.dashboardUrl) console.log(` 🔑 Get your key: ${p.dashboardUrl}`);
|
|
131
|
+
// Template URL with placeholders (e.g. Cloudflare's {account_id}) needs a resolved value.
|
|
132
|
+
let baseUrl = p.baseUrl;
|
|
133
|
+
if (/\{[^}]*\}/.test(baseUrl)) {
|
|
134
|
+
const resolved = (await prompt.ask(` Your endpoint URL (${baseUrl}): `)).trim();
|
|
135
|
+
if (!resolved) {
|
|
136
|
+
console.error(" A resolved URL is required — replace the {placeholder} with your actual value.");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
baseUrl = resolved;
|
|
140
|
+
}
|
|
141
|
+
const env = envKeyFor(p.id);
|
|
142
|
+
const hint = p.kind === "local" ? "API key (usually blank — press Enter): " : `API key (paste, or env var $${env}): `;
|
|
143
|
+
const apiKey = (await prompt.ask(`\n${hint}`)).trim();
|
|
144
|
+
const modelChoice = (await prompt.ask(`Default model${p.defaultModel ? ` [${p.defaultModel}]` : " (optional)"}: `)).trim();
|
|
145
|
+
const defaultModel = modelChoice || p.defaultModel;
|
|
146
|
+
providerConfig = {
|
|
147
|
+
adapter: p.adapter,
|
|
148
|
+
baseUrl,
|
|
149
|
+
...(p.kind === "key" ? { apiKey: apiKey || `\${${env}}` } : apiKey ? { apiKey } : {}),
|
|
150
|
+
...(defaultModel ? { defaultModel } : {}),
|
|
151
|
+
};
|
|
152
|
+
// Apply the catalog's models / vision classification (same enrichment as the GUI).
|
|
153
|
+
enrichProviderFromCatalog(p.id, providerConfig);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
providerName = (await prompt.ask("Provider name: ")).trim();
|
|
157
|
+
if (!isValidProviderName(providerName)) {
|
|
158
|
+
console.error("Provider name must use letters, numbers, dot, underscore, or hyphen and cannot be a reserved object key.");
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
const baseUrl = await prompt.ask("Base URL (e.g. http://localhost:11434/v1): ");
|
|
162
|
+
const adapter = await prompt.ask("Adapter [openai-chat]: ") || "openai-chat";
|
|
163
|
+
const apiKey = await prompt.ask("API key (optional): ");
|
|
164
|
+
const defaultModel = await prompt.ask("Default model: ");
|
|
165
|
+
providerConfig = {
|
|
166
|
+
adapter: adapter.trim(),
|
|
167
|
+
baseUrl: baseUrl.trim(),
|
|
168
|
+
...(apiKey.trim() ? { apiKey: apiKey.trim() } : {}),
|
|
169
|
+
...(defaultModel.trim() ? { defaultModel: defaultModel.trim() } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const portStr = await prompt.ask("\nProxy port [10100]: ");
|
|
174
|
+
const port = parseInt(portStr, 10) || 10100;
|
|
175
|
+
|
|
176
|
+
const config: OcxConfig = {
|
|
177
|
+
...getDefaultConfig(),
|
|
178
|
+
port,
|
|
179
|
+
providers: { [providerName]: providerConfig },
|
|
180
|
+
defaultProvider: providerName,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
saveConfig(config);
|
|
184
|
+
// Init writes a fresh config, so a stale pre-migration backup from a previous
|
|
185
|
+
// installation would make the next `ocx start` crash on a stale-backup
|
|
186
|
+
// collision (issue #257). But only a STALE backup (unparseable, or already a
|
|
187
|
+
// post-migration v2 snapshot) may be deleted; a backup that still parses as a
|
|
188
|
+
// valid pre-migration (v1) config is a user-intentional rollback point and is
|
|
189
|
+
// preserved by renaming it out of the collision path (sol review 260722).
|
|
190
|
+
cleanupOpenAiTierBackupAfterInit();
|
|
191
|
+
console.log(`\n✅ Config saved to ~/.opencodex/config.json`);
|
|
192
|
+
if (oauthHint) console.log(`🔐 Authenticate this provider with: ocx login ${providerName}`);
|
|
193
|
+
|
|
194
|
+
const injectAnswer = await prompt.ask("Inject into Codex config.toml? [Y/n]: ");
|
|
195
|
+
if (injectAnswer.trim().toLowerCase() !== "n") {
|
|
196
|
+
console.log("Fetching available models from provider...");
|
|
197
|
+
const result = await injectCodexConfig(port, config);
|
|
198
|
+
console.log(result.success ? `✅ ${result.message}` : `⚠️ ${result.message}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const shimAnswer = await prompt.ask("Install Codex autostart shim? [Y/n]: ");
|
|
202
|
+
if (shimAnswer.trim().toLowerCase() !== "n") {
|
|
203
|
+
try {
|
|
204
|
+
const { installCodexShim } = await import("../codex/shim");
|
|
205
|
+
const result = installCodexShim();
|
|
206
|
+
console.log(result.installed ? `✅ ${result.message}` : `⚠️ ${result.message}`);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.log(`⚠️ Codex autostart shim skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
console.log(`\n🚀 Setup complete! Run 'ocx start' to start the proxy.`);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
215
|
+
if (/stdin (closed|reached EOF)/i.test(message)) {
|
|
216
|
+
console.error(`\n❌ ${message}. Re-run \`ocx init\` in an interactive terminal.`);
|
|
217
|
+
process.exitCode = 1;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
throw error;
|
|
221
|
+
} finally {
|
|
222
|
+
prompt.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliUsageError,
|
|
3
|
+
csv,
|
|
4
|
+
printData,
|
|
5
|
+
rejectArgs,
|
|
6
|
+
runCliAction,
|
|
7
|
+
runtimeRequest,
|
|
8
|
+
summaryLines,
|
|
9
|
+
takeBooleanOption,
|
|
10
|
+
takeFlag,
|
|
11
|
+
takeOption,
|
|
12
|
+
type RuntimeApiDeps,
|
|
13
|
+
} from "./runtime-api";
|
|
14
|
+
|
|
15
|
+
const CLAUDE_USAGE = `Usage:
|
|
16
|
+
ocx claude config [status] [--json]
|
|
17
|
+
ocx claude config set [--enabled <on|off>] [--auth-mode <auto|proxy|subscription>]
|
|
18
|
+
[--system-env <on|off>] [--fast-mode <on|off>] [--auto-context <on|off>]
|
|
19
|
+
[--compact-window <tokens|default>] [--inject-agents <on|off>]
|
|
20
|
+
[--small-fast-model <id|->] [--model-map <from=to,from=to|->]
|
|
21
|
+
[--blocked-skills <name,name|->] [--web-model <id|->] [--web-backend <openai|anthropic|->]
|
|
22
|
+
[--vision-model <id|->] [--vision-backend <openai|anthropic|->] [--json]`;
|
|
23
|
+
|
|
24
|
+
const GROK_USAGE = `Usage:
|
|
25
|
+
ocx grok [status] [--json]
|
|
26
|
+
ocx grok <exclude|include|set> <model,model...> [--json]
|
|
27
|
+
ocx grok clear [--json]
|
|
28
|
+
ocx grok apply [--json]`;
|
|
29
|
+
|
|
30
|
+
function parseMap(raw: string): Record<string, string> {
|
|
31
|
+
if (raw === "-") return {};
|
|
32
|
+
const map: Record<string, string> = {};
|
|
33
|
+
for (const pair of raw.split(",")) {
|
|
34
|
+
const index = pair.indexOf("=");
|
|
35
|
+
if (index <= 0 || index === pair.length - 1) throw new CliUsageError(`invalid model map entry "${pair}"; use from=to`, CLAUDE_USAGE);
|
|
36
|
+
map[pair.slice(0, index).trim()] = pair.slice(index + 1).trim();
|
|
37
|
+
}
|
|
38
|
+
return map;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function handleClaudeConfigCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
42
|
+
return runCliAction(async () => {
|
|
43
|
+
const args = [...argv];
|
|
44
|
+
const action = (args.shift() ?? "status").toLowerCase();
|
|
45
|
+
const wantsJson = takeFlag(args, "--json");
|
|
46
|
+
if (action === "status" || action === "show") {
|
|
47
|
+
rejectArgs(args, CLAUDE_USAGE);
|
|
48
|
+
const result = await runtimeRequest("/api/claude-code", {}, deps);
|
|
49
|
+
printData(result, wantsJson, summaryLines(result));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (action !== "set") throw new CliUsageError(`unknown Claude config command ${action}`, CLAUDE_USAGE);
|
|
53
|
+
const body: Record<string, unknown> = {};
|
|
54
|
+
const enabled = takeBooleanOption(args, "--enabled");
|
|
55
|
+
const authMode = takeOption(args, "--auth-mode");
|
|
56
|
+
const systemEnv = takeBooleanOption(args, "--system-env");
|
|
57
|
+
const fastMode = takeBooleanOption(args, "--fast-mode");
|
|
58
|
+
const autoContext = takeBooleanOption(args, "--auto-context");
|
|
59
|
+
const compact = takeOption(args, "--compact-window");
|
|
60
|
+
const injectAgents = takeBooleanOption(args, "--inject-agents");
|
|
61
|
+
const smallFastModel = takeOption(args, "--small-fast-model");
|
|
62
|
+
const modelMap = takeOption(args, "--model-map");
|
|
63
|
+
const blockedSkills = takeOption(args, "--blocked-skills");
|
|
64
|
+
const webModel = takeOption(args, "--web-model");
|
|
65
|
+
const webBackend = takeOption(args, "--web-backend");
|
|
66
|
+
const visionModel = takeOption(args, "--vision-model");
|
|
67
|
+
const visionBackend = takeOption(args, "--vision-backend");
|
|
68
|
+
rejectArgs(args, CLAUDE_USAGE);
|
|
69
|
+
if (enabled !== undefined) body.enabled = enabled;
|
|
70
|
+
if (authMode !== undefined) body.authMode = authMode;
|
|
71
|
+
if (systemEnv !== undefined) body.systemEnv = systemEnv;
|
|
72
|
+
if (fastMode !== undefined) body.fastMode = fastMode;
|
|
73
|
+
if (autoContext !== undefined) body.autoContext = autoContext;
|
|
74
|
+
if (compact !== undefined) {
|
|
75
|
+
if (compact === "default" || compact === "-") body.autoCompactWindow = null;
|
|
76
|
+
else {
|
|
77
|
+
const value = Number(compact.replace(/[_,]/g, ""));
|
|
78
|
+
if (!Number.isInteger(value) || value <= 0) throw new CliUsageError("--compact-window must be a positive integer or default", CLAUDE_USAGE);
|
|
79
|
+
body.autoCompactWindow = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (injectAgents !== undefined) body.injectAgents = injectAgents;
|
|
83
|
+
if (smallFastModel !== undefined) body.smallFastModel = smallFastModel === "-" ? "" : smallFastModel;
|
|
84
|
+
if (modelMap !== undefined) body.modelMap = parseMap(modelMap);
|
|
85
|
+
if (blockedSkills !== undefined) body.blockedSkills = blockedSkills === "-" ? null : csv(blockedSkills);
|
|
86
|
+
const sidecar = (model: string | undefined, backend: string | undefined): Record<string, unknown> | undefined => {
|
|
87
|
+
if (model === undefined && backend === undefined) return undefined;
|
|
88
|
+
const result: Record<string, unknown> = {};
|
|
89
|
+
if (model !== undefined) result.model = model === "-" ? "" : model;
|
|
90
|
+
if (backend !== undefined) result.backend = backend === "-" ? null : backend;
|
|
91
|
+
return result;
|
|
92
|
+
};
|
|
93
|
+
const web = sidecar(webModel, webBackend);
|
|
94
|
+
const vision = sidecar(visionModel, visionBackend);
|
|
95
|
+
if (web) body.webSearchSidecar = web;
|
|
96
|
+
if (vision) body.visionSidecar = vision;
|
|
97
|
+
if (Object.keys(body).length === 0) throw new CliUsageError("at least one Claude setting is required", CLAUDE_USAGE);
|
|
98
|
+
const result = await runtimeRequest("/api/claude-code", { method: "PUT", body: JSON.stringify(body) }, deps);
|
|
99
|
+
printData(result, wantsJson, ["Claude Code settings updated."]);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type GrokState = Record<string, unknown> & { excluded?: string[] };
|
|
104
|
+
|
|
105
|
+
export async function handleGrokCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
106
|
+
return runCliAction(async () => {
|
|
107
|
+
const args = [...argv];
|
|
108
|
+
const action = (args.shift() ?? "status").toLowerCase();
|
|
109
|
+
const wantsJson = takeFlag(args, "--json");
|
|
110
|
+
if (action === "status" || action === "show") {
|
|
111
|
+
rejectArgs(args, GROK_USAGE);
|
|
112
|
+
const result = await runtimeRequest("/api/grok", {}, deps);
|
|
113
|
+
printData(result, wantsJson, summaryLines(result));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (action === "apply") {
|
|
117
|
+
rejectArgs(args, GROK_USAGE);
|
|
118
|
+
const result = await runtimeRequest("/api/grok/apply", { method: "POST" }, deps);
|
|
119
|
+
printData(result, wantsJson, [String((result as Record<string, unknown>).message ?? "Grok configuration applied.")]);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let excluded: string[];
|
|
123
|
+
if (action === "clear") excluded = [];
|
|
124
|
+
else if (["exclude", "include", "set"].includes(action)) {
|
|
125
|
+
const raw = args.shift();
|
|
126
|
+
if (!raw) throw new CliUsageError("comma-separated models are required", GROK_USAGE);
|
|
127
|
+
const requested = csv(raw) ?? [];
|
|
128
|
+
if (action === "set") excluded = requested;
|
|
129
|
+
else {
|
|
130
|
+
const state = await runtimeRequest<GrokState>("/api/grok", {}, deps);
|
|
131
|
+
const current = new Set(state.excluded ?? []);
|
|
132
|
+
for (const model of requested) action === "exclude" ? current.add(model) : current.delete(model);
|
|
133
|
+
excluded = [...current].sort();
|
|
134
|
+
}
|
|
135
|
+
} else throw new CliUsageError(`unknown Grok command ${action}`, GROK_USAGE);
|
|
136
|
+
rejectArgs(args, GROK_USAGE);
|
|
137
|
+
const result = await runtimeRequest("/api/grok/selection", { method: "PUT", body: JSON.stringify({ excluded }) }, deps);
|
|
138
|
+
printData(result, wantsJson, [`Grok exclusions: ${excluded.join(", ") || "none"}`]);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const INTEGRATION_USAGE = { claude: CLAUDE_USAGE, grok: GROK_USAGE };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Inline yes/no selector for interactive CLI prompts.
|
|
5
|
+
*
|
|
6
|
+
* Both choices are drawn on one line: the user moves between them with the
|
|
7
|
+
* arrow keys (or Tab), confirms with Enter, or answers straight away with
|
|
8
|
+
* `y`/`n`. Escape and Ctrl-C resolve to "no", so backing out never counts as
|
|
9
|
+
* consent.
|
|
10
|
+
*
|
|
11
|
+
* The highlighted choice is caller-supplied and is what a bare Enter returns.
|
|
12
|
+
* Whatever the default, the selector always shows which side is highlighted, so
|
|
13
|
+
* Enter never does something the screen did not already say it would.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface InteractiveConfirmOptions {
|
|
17
|
+
/** Question text rendered before the choices. May contain ANSI styling. */
|
|
18
|
+
question: string;
|
|
19
|
+
/** Which choice starts highlighted, and therefore what a bare Enter returns. */
|
|
20
|
+
defaultYes: boolean;
|
|
21
|
+
/** Key hint shown after the choices. */
|
|
22
|
+
hint?: string;
|
|
23
|
+
/** Overridable for tests; defaults to the process streams. */
|
|
24
|
+
input?: NodeJS.ReadStream;
|
|
25
|
+
output?: NodeJS.WriteStream;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The highlight sets an explicit black-on-white pair rather than bare reverse
|
|
29
|
+
// video (\x1b[7m). Reverse alone inherits whatever foreground colour is in
|
|
30
|
+
// effect, so on some themes the selected label rendered as black text on a black
|
|
31
|
+
// block and the choice became invisible.
|
|
32
|
+
const REVERSE = "\x1b[30;47m";
|
|
33
|
+
const DIM = "\x1b[2m";
|
|
34
|
+
const RESET = "\x1b[0m";
|
|
35
|
+
const CLEAR_LINE = "\r\x1b[K";
|
|
36
|
+
|
|
37
|
+
const KEY_ENTER = new Set(["\r", "\n"]);
|
|
38
|
+
const KEY_YES_SIDE = new Set(["\x1b[D", "\x1b[A", "\x1bOD", "\x1bOA"]); // left / up
|
|
39
|
+
const KEY_NO_SIDE = new Set(["\x1b[C", "\x1b[B", "\x1bOC", "\x1bOB"]); // right / down
|
|
40
|
+
const KEY_ESCAPE = "\x1b";
|
|
41
|
+
const KEY_INTERRUPT = "\x03";
|
|
42
|
+
const KEY_TAB = "\t";
|
|
43
|
+
|
|
44
|
+
function renderChoices(question: string, yes: boolean, hint: string): string {
|
|
45
|
+
const yesLabel = yes ? `${REVERSE} Yes ${RESET}` : `${DIM} Yes ${RESET}`;
|
|
46
|
+
const noLabel = yes ? `${DIM} No ${RESET}` : `${REVERSE} No ${RESET}`;
|
|
47
|
+
return `${CLEAR_LINE}${question} ${yesLabel} ${noLabel} ${DIM}${hint}${RESET}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function renderAnswer(question: string, yes: boolean): string {
|
|
51
|
+
return `${CLEAR_LINE}${question} ${yes ? "Yes" : "No"}\n`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Fallback for terminals without raw mode: a plain typed answer. */
|
|
55
|
+
async function readlineConfirm(
|
|
56
|
+
options: InteractiveConfirmOptions,
|
|
57
|
+
input: NodeJS.ReadStream,
|
|
58
|
+
output: NodeJS.WriteStream,
|
|
59
|
+
): Promise<boolean> {
|
|
60
|
+
const suffix = options.defaultYes ? "[Y/n]" : "[y/N]";
|
|
61
|
+
const rl = createInterface({ input, output });
|
|
62
|
+
try {
|
|
63
|
+
const answer = (await rl.question(`${options.question} ${suffix} `)).trim().toLowerCase();
|
|
64
|
+
if (answer === "") return options.defaultYes;
|
|
65
|
+
return answer === "y" || answer === "yes";
|
|
66
|
+
} finally {
|
|
67
|
+
rl.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Ask a yes/no question with an inline arrow-key selector. Resolves to the
|
|
73
|
+
* user's choice; never throws for input handling and always restores the
|
|
74
|
+
* terminal mode it changed.
|
|
75
|
+
*/
|
|
76
|
+
export async function interactiveConfirm(options: InteractiveConfirmOptions): Promise<boolean> {
|
|
77
|
+
const input = options.input ?? process.stdin;
|
|
78
|
+
const output = options.output ?? process.stdout;
|
|
79
|
+
const hint = options.hint ?? "←/→ move · y/n · enter";
|
|
80
|
+
|
|
81
|
+
// Raw mode is what makes single-keypress navigation possible. Without it
|
|
82
|
+
// (pipes, some CI shells, Windows consoles without a TTY) fall back to a
|
|
83
|
+
// typed answer rather than silently swallowing the question.
|
|
84
|
+
if (typeof input.setRawMode !== "function") {
|
|
85
|
+
return await readlineConfirm(options, input, output);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return await new Promise<boolean>(resolve => {
|
|
89
|
+
let yes = options.defaultYes;
|
|
90
|
+
const wasRaw = input.isRaw === true;
|
|
91
|
+
const hadOtherReaders = input.listenerCount("data") > 0;
|
|
92
|
+
|
|
93
|
+
const paint = () => {
|
|
94
|
+
output.write(renderChoices(options.question, yes, hint));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const finish = (answer: boolean, interrupted: boolean) => {
|
|
98
|
+
input.off("data", onData);
|
|
99
|
+
if (!wasRaw) input.setRawMode(false);
|
|
100
|
+
if (!hadOtherReaders) input.pause();
|
|
101
|
+
output.write(renderAnswer(options.question, answer));
|
|
102
|
+
resolve(answer);
|
|
103
|
+
// A Ctrl-C during the prompt is still a Ctrl-C: hand it back to the
|
|
104
|
+
// process so the normal shutdown path runs instead of being eaten here.
|
|
105
|
+
if (interrupted) process.kill(process.pid, "SIGINT");
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const onData = (chunk: Buffer | string) => {
|
|
109
|
+
const key = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
110
|
+
if (key === KEY_INTERRUPT) return finish(false, true);
|
|
111
|
+
if (key === KEY_ESCAPE) return finish(false, false);
|
|
112
|
+
if (KEY_ENTER.has(key)) return finish(yes, false);
|
|
113
|
+
const lower = key.toLowerCase();
|
|
114
|
+
if (lower === "y") return finish(true, false);
|
|
115
|
+
if (lower === "n") return finish(false, false);
|
|
116
|
+
if (KEY_YES_SIDE.has(key)) {
|
|
117
|
+
yes = true;
|
|
118
|
+
paint();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (KEY_NO_SIDE.has(key) || key === KEY_TAB) {
|
|
122
|
+
yes = key === KEY_TAB ? !yes : false;
|
|
123
|
+
paint();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
input.setRawMode(true);
|
|
129
|
+
input.resume();
|
|
130
|
+
input.on("data", onData);
|
|
131
|
+
paint();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type InternalCliCommand = "__tray-start" | "__tray-restart" | "__startup-health";
|
|
2
|
+
|
|
3
|
+
export interface InternalCliHandlers {
|
|
4
|
+
trayStart: () => void | Promise<void>;
|
|
5
|
+
trayRestart: () => void | Promise<void>;
|
|
6
|
+
startupHealth: () => void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Dispatch fixed internal commands without accepting caller-selected process arguments. */
|
|
10
|
+
export async function dispatchInternalCliCommand(
|
|
11
|
+
command: InternalCliCommand,
|
|
12
|
+
handlers: InternalCliHandlers,
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
switch (command) {
|
|
15
|
+
case "__tray-start": return void await handlers.trayStart();
|
|
16
|
+
case "__tray-restart": return void await handlers.trayRestart();
|
|
17
|
+
case "__startup-health": return void await handlers.startupHealth();
|
|
18
|
+
default: throw new Error(`Unsupported internal CLI command: ${String(command)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|