@coseung2/opencodex 2.8.0-cs.1
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 +219 -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-notch.mjs +78 -0
- package/bin/ocx.mjs +454 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-CAnnes06.js +67 -0
- package/gui/dist/assets/index-OY43ubAq.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 +107 -0
- package/packages/ocx-notch/README.md +55 -0
- package/packages/ocx-notch/THIRD_PARTY_NOTICES.md +45 -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 +330 -0
- package/src/cli/index.ts +1103 -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/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 +134 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/system-command.ts +114 -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/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 +2355 -0
- package/src/generated/jawcode-model-metadata.ts +104 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +519 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +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 +1173 -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/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 +1045 -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 +305 -0
- package/src/server/live.ts +598 -0
- package/src/server/management/agent-settings-routes.ts +1039 -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 +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 +516 -0
- package/src/server/management/model-routes.ts +525 -0
- package/src/server/management/oauth-account-routes.ts +561 -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 +88 -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 +211 -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 +1060 -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 +2756 -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 +392 -0
- package/src/update/job.ts +1513 -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/vendor/ocx-notch/win32-x64/ocx-notch.exe +0 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { durableBunRuntime } from "../lib/bun-runtime";
|
|
2
|
+
import { codexAutoStartEnabled, getConfigPath, getPidPath, readConfigDiagnostics, readPid, readRuntimePort, type RuntimePortState } from "../config";
|
|
3
|
+
import { diagnoseCodexBundledPlugins, type CodexPluginsDiagnostic } from "../codex/plugins-doctor";
|
|
4
|
+
import { findLiveProxy, isOpencodexHealthz, probeHostname } from "../server/proxy-liveness";
|
|
5
|
+
import type { OcxConfig } from "../types";
|
|
6
|
+
import { diagnoseService, serviceLogPath } from "../service";
|
|
7
|
+
import { collectStartupHealth, type StartupHealth } from "../codex/autostart-health";
|
|
8
|
+
import { getCodexRoutingKind } from "../codex/inject";
|
|
9
|
+
import { diagnoseCodexShim } from "../codex/shim";
|
|
10
|
+
import { displayCodexRuntimePath, effortClampAppliesToRuntime, loadLastEffortClamp, resolveCodexRuntime } from "../codex/runtime";
|
|
11
|
+
import { redactSecretString, redactUserPath } from "../lib/redact";
|
|
12
|
+
import { collectOrcaCodexHomeDiagnostic, type OrcaCodexHomeDiagnostic } from "../codex/home";
|
|
13
|
+
import { grokFenceEndpointDrift, readGrokStatus } from "../grok/status";
|
|
14
|
+
|
|
15
|
+
type HealthCheck = {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
url: string;
|
|
18
|
+
message: string;
|
|
19
|
+
label: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type CliStatusJson = {
|
|
23
|
+
schemaVersion: 1;
|
|
24
|
+
proxy: {
|
|
25
|
+
running: boolean;
|
|
26
|
+
pid: number | null;
|
|
27
|
+
health: {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
url: string;
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
dashboard: { url: string };
|
|
34
|
+
listen: {
|
|
35
|
+
port: number;
|
|
36
|
+
hostname: string | null;
|
|
37
|
+
source: "runtime" | "config";
|
|
38
|
+
};
|
|
39
|
+
paths: {
|
|
40
|
+
config: string;
|
|
41
|
+
pid: string;
|
|
42
|
+
runtime: string;
|
|
43
|
+
};
|
|
44
|
+
runtime: {
|
|
45
|
+
source: string;
|
|
46
|
+
overrideEnv?: string;
|
|
47
|
+
};
|
|
48
|
+
codexAutostart: boolean;
|
|
49
|
+
startup: StartupHealth;
|
|
50
|
+
defaultProvider: string | null;
|
|
51
|
+
config: {
|
|
52
|
+
source: "default" | "file" | "fallback";
|
|
53
|
+
error: string | null;
|
|
54
|
+
};
|
|
55
|
+
service: { summary: string };
|
|
56
|
+
codexShim: { summary: string };
|
|
57
|
+
codexPlugins: CodexPluginsDiagnostic;
|
|
58
|
+
codexRuntime: {
|
|
59
|
+
path: string;
|
|
60
|
+
version: string | null;
|
|
61
|
+
source: string;
|
|
62
|
+
newerAvailable: { path: string; version: string | null } | null;
|
|
63
|
+
warning: string | null;
|
|
64
|
+
catalogClamp: {
|
|
65
|
+
active: boolean;
|
|
66
|
+
removedEfforts: string[];
|
|
67
|
+
runtimeVersion: string | null;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
codexHome: OrcaCodexHomeDiagnostic;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type CliStatusView = {
|
|
74
|
+
json: CliStatusJson;
|
|
75
|
+
proxyLabel: string;
|
|
76
|
+
healthLabel: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
export type ListenTarget = {
|
|
81
|
+
port: number;
|
|
82
|
+
hostname?: string;
|
|
83
|
+
source: "runtime" | "config";
|
|
84
|
+
healthUrl: string;
|
|
85
|
+
dashboardUrl: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export function selectListenTarget(
|
|
89
|
+
config: Pick<OcxConfig, "port" | "hostname">,
|
|
90
|
+
pid: number | null,
|
|
91
|
+
runtimePort: RuntimePortState | null,
|
|
92
|
+
): ListenTarget {
|
|
93
|
+
const currentRuntimePort = pid && runtimePort?.pid === pid ? runtimePort : null;
|
|
94
|
+
const port = currentRuntimePort ? currentRuntimePort.port : config.port ?? 10100;
|
|
95
|
+
const hostname = currentRuntimePort ? currentRuntimePort.hostname : config.hostname;
|
|
96
|
+
return {
|
|
97
|
+
port,
|
|
98
|
+
hostname,
|
|
99
|
+
source: currentRuntimePort ? "runtime" : "config",
|
|
100
|
+
healthUrl: `http://${probeHostname(hostname)}:${port}/healthz`,
|
|
101
|
+
dashboardUrl: `http://localhost:${port}/`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Prefer live result (including authoritative null pid) over the on-disk pid file. */
|
|
106
|
+
export function resolveStatusPid(
|
|
107
|
+
live: { pid: number | null } | null,
|
|
108
|
+
pidFile: number | null,
|
|
109
|
+
): number | null {
|
|
110
|
+
return live ? live.pid : pidFile;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function checkProxyHealth(target: ListenTarget): Promise<HealthCheck> {
|
|
114
|
+
const url = target.healthUrl;
|
|
115
|
+
const controller = new AbortController();
|
|
116
|
+
const timer = setTimeout(() => controller.abort(), 800);
|
|
117
|
+
try {
|
|
118
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const message = `returned HTTP ${response.status}`;
|
|
121
|
+
return { ok: false, url, message, label: `${url} ${message}` };
|
|
122
|
+
}
|
|
123
|
+
const body = await response.json().catch(() => null) as { service?: unknown; status?: unknown; version?: unknown; uptime?: unknown } | null;
|
|
124
|
+
if (!isOpencodexHealthz(body)) {
|
|
125
|
+
const message = "responded, but not an opencodex proxy";
|
|
126
|
+
return { ok: false, url, message, label: `${url} ${message}` };
|
|
127
|
+
}
|
|
128
|
+
const version = typeof body?.version === "string" ? ` v${body.version}` : "";
|
|
129
|
+
const uptime = typeof body?.uptime === "number" ? `, uptime ${Math.round(body.uptime)}s` : "";
|
|
130
|
+
const message = `ok${version}${uptime}`;
|
|
131
|
+
return { ok: true, url, message, label: `${url} ${message}` };
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const reason = error instanceof Error && error.name === "AbortError" ? "timed out" : "unreachable";
|
|
134
|
+
return { ok: false, url, message: reason, label: `${url} ${reason}` };
|
|
135
|
+
} finally {
|
|
136
|
+
clearTimeout(timer);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function collectStatus(): Promise<CliStatusView> {
|
|
141
|
+
const configDiagnostics = readConfigDiagnostics();
|
|
142
|
+
const config = configDiagnostics.config;
|
|
143
|
+
// Prefer identity-verified liveness (runtime-port + /healthz) over ocx.pid alone (#618).
|
|
144
|
+
// Pass the already-resolved diagnostics config so findLiveProxy does not re-load and
|
|
145
|
+
// warn on malformed config.json (status --json must stay stderr-clean).
|
|
146
|
+
const live = await findLiveProxy({
|
|
147
|
+
configFn: () => ({ port: config.port, hostname: config.hostname }),
|
|
148
|
+
});
|
|
149
|
+
const pidFile = readPid();
|
|
150
|
+
// Preserve an authoritative null from orphan/legacy liveness — do not restore pidFile.
|
|
151
|
+
const pid = resolveStatusPid(live, pidFile);
|
|
152
|
+
const listen = live
|
|
153
|
+
? {
|
|
154
|
+
port: live.port,
|
|
155
|
+
hostname: live.hostname,
|
|
156
|
+
source: live.source,
|
|
157
|
+
healthUrl: `http://${probeHostname(live.hostname)}:${live.port}/healthz`,
|
|
158
|
+
dashboardUrl: `http://localhost:${live.port}/`,
|
|
159
|
+
}
|
|
160
|
+
: selectListenTarget(config, pidFile, pidFile ? readRuntimePort(pidFile) : null);
|
|
161
|
+
// findLiveProxy already identity-probed /healthz; avoid a second fetch that can race.
|
|
162
|
+
const health = live
|
|
163
|
+
? {
|
|
164
|
+
ok: true,
|
|
165
|
+
url: listen.healthUrl,
|
|
166
|
+
message: `ok (pid ${live.pid ?? "unknown"})`,
|
|
167
|
+
label: `${listen.healthUrl} ok (live)`,
|
|
168
|
+
}
|
|
169
|
+
: await checkProxyHealth(listen);
|
|
170
|
+
const bunRuntime = durableBunRuntime();
|
|
171
|
+
const service = diagnoseService();
|
|
172
|
+
// A service can be registered and still not serve: the manager reports the job
|
|
173
|
+
// either way. `live` was already identity-probed a few lines above, so cross-check
|
|
174
|
+
// rather than print registration as if it were service.
|
|
175
|
+
const serviceSummary = service.installed && !live
|
|
176
|
+
? `${service.summary} — registered but NOT serving; see ${serviceLogPath()} and re-run 'ocx service install'`
|
|
177
|
+
: service.summary;
|
|
178
|
+
const codexShim = diagnoseCodexShim();
|
|
179
|
+
const codexShimSummary = codexShim.summary;
|
|
180
|
+
const startup = collectStartupHealth(config, {
|
|
181
|
+
service,
|
|
182
|
+
shim: codexShim,
|
|
183
|
+
routingKind: getCodexRoutingKind(),
|
|
184
|
+
});
|
|
185
|
+
const codexPlugins = diagnoseCodexBundledPlugins();
|
|
186
|
+
const resolvedRuntime = (() => {
|
|
187
|
+
try {
|
|
188
|
+
return resolveCodexRuntime();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
const redacted = redactUserPath(redactSecretString(message)).slice(0, 160);
|
|
192
|
+
return {
|
|
193
|
+
runtime: { command: "codex", version: null, source: "fallback" as const },
|
|
194
|
+
failures: [{
|
|
195
|
+
command: "codex",
|
|
196
|
+
source: "fallback" as const,
|
|
197
|
+
reason: `resolve threw: ${redacted}`,
|
|
198
|
+
}],
|
|
199
|
+
replacedConfigured: undefined,
|
|
200
|
+
newerAvailable: undefined,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
})();
|
|
204
|
+
const lastClamp = loadLastEffortClamp();
|
|
205
|
+
const clampActive = effortClampAppliesToRuntime(lastClamp, resolvedRuntime.runtime);
|
|
206
|
+
const codexHome = collectOrcaCodexHomeDiagnostic();
|
|
207
|
+
const warningParts: string[] = [];
|
|
208
|
+
if (
|
|
209
|
+
resolvedRuntime.replacedConfigured
|
|
210
|
+
&& resolvedRuntime.replacedConfigured.from.command !== resolvedRuntime.runtime.command
|
|
211
|
+
) {
|
|
212
|
+
warningParts.push(
|
|
213
|
+
`Preferred Codex runtime is unavailable; using ${displayCodexRuntimePath(resolvedRuntime.runtime.command)} instead. Run ocx doctor for diagnosis and recovery.`,
|
|
214
|
+
);
|
|
215
|
+
} else if (
|
|
216
|
+
resolvedRuntime.runtime.source === "fallback"
|
|
217
|
+
&& resolvedRuntime.failures.length > 0
|
|
218
|
+
&& !resolvedRuntime.runtime.version
|
|
219
|
+
) {
|
|
220
|
+
const detail = resolvedRuntime.failures[0]?.reason;
|
|
221
|
+
warningParts.push(
|
|
222
|
+
detail
|
|
223
|
+
? `No validated Codex runtime found (${detail}); falling back to \`codex\`. Run ocx doctor for diagnosis and recovery.`
|
|
224
|
+
: "No validated Codex runtime found; falling back to `codex`. Run ocx doctor for diagnosis and recovery.",
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (resolvedRuntime.newerAvailable) {
|
|
228
|
+
warningParts.push("OpenCodex is using an older Codex binary. Run ocx doctor for diagnosis and recovery.");
|
|
229
|
+
}
|
|
230
|
+
if (clampActive) {
|
|
231
|
+
warningParts.push(
|
|
232
|
+
`Catalog clamp removed: ${lastClamp!.removedEfforts.join(", ")}. Run ocx doctor for diagnosis and recovery.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
// A Grok fence naming a port we are not listening on is invisible everywhere else:
|
|
236
|
+
// grok retries the refused connection on its own side, so no request — and therefore
|
|
237
|
+
// no log line — ever reaches us. Surface it here, where the live port is already known.
|
|
238
|
+
const grokDrift = (() => {
|
|
239
|
+
try {
|
|
240
|
+
return grokFenceEndpointDrift(readGrokStatus(), health.ok ? listen.port : undefined);
|
|
241
|
+
} catch {
|
|
242
|
+
return null; // reading grok's config must never break `ocx status`
|
|
243
|
+
}
|
|
244
|
+
})();
|
|
245
|
+
if (grokDrift) {
|
|
246
|
+
warningParts.push(
|
|
247
|
+
`Grok Build config points at port ${grokDrift.fencePort}, but the proxy is on `
|
|
248
|
+
+ `${grokDrift.livePort}; grok turns will retry against a closed port. Run 'ocx ensure' to repoint it.`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const codexRuntime = {
|
|
252
|
+
path: displayCodexRuntimePath(resolvedRuntime.runtime.command),
|
|
253
|
+
version: resolvedRuntime.runtime.version,
|
|
254
|
+
source: resolvedRuntime.runtime.source,
|
|
255
|
+
newerAvailable: resolvedRuntime.newerAvailable
|
|
256
|
+
? {
|
|
257
|
+
path: displayCodexRuntimePath(resolvedRuntime.newerAvailable.command),
|
|
258
|
+
version: resolvedRuntime.newerAvailable.version,
|
|
259
|
+
}
|
|
260
|
+
: null,
|
|
261
|
+
warning: warningParts.length > 0 ? warningParts.join(" ") : null,
|
|
262
|
+
catalogClamp: {
|
|
263
|
+
active: clampActive,
|
|
264
|
+
removedEfforts: clampActive ? (lastClamp?.removedEfforts ?? []) : [],
|
|
265
|
+
runtimeVersion: clampActive ? (lastClamp?.runtimeVersion ?? null) : null,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
const proxyLabel = live
|
|
269
|
+
? `running (PID ${live.pid ?? pid ?? "unknown"})`
|
|
270
|
+
: pid && health.ok
|
|
271
|
+
? `running (PID ${pid})`
|
|
272
|
+
: pid
|
|
273
|
+
? `PID file points to PID ${pid}, but health check failed`
|
|
274
|
+
: health.ok
|
|
275
|
+
? "reachable, but PID file is missing or stale"
|
|
276
|
+
: "not running";
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
proxyLabel,
|
|
280
|
+
healthLabel: health.label,
|
|
281
|
+
json: {
|
|
282
|
+
schemaVersion: 1,
|
|
283
|
+
proxy: {
|
|
284
|
+
running: Boolean(live) || Boolean(pid && health.ok),
|
|
285
|
+
pid: live?.pid ?? pid,
|
|
286
|
+
health: {
|
|
287
|
+
ok: health.ok,
|
|
288
|
+
url: health.url,
|
|
289
|
+
message: health.message,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
dashboard: { url: listen.dashboardUrl },
|
|
293
|
+
listen: {
|
|
294
|
+
port: listen.port,
|
|
295
|
+
hostname: listen.hostname ?? null,
|
|
296
|
+
source: listen.source,
|
|
297
|
+
},
|
|
298
|
+
paths: {
|
|
299
|
+
config: getConfigPath(),
|
|
300
|
+
pid: getPidPath(),
|
|
301
|
+
runtime: bunRuntime.path,
|
|
302
|
+
},
|
|
303
|
+
runtime: {
|
|
304
|
+
source: bunRuntime.source,
|
|
305
|
+
...(bunRuntime.source === "override" ? { overrideEnv: bunRuntime.overrideEnv } : {}),
|
|
306
|
+
},
|
|
307
|
+
codexAutostart: codexAutoStartEnabled(config),
|
|
308
|
+
startup,
|
|
309
|
+
defaultProvider: typeof config.defaultProvider === "string" ? config.defaultProvider : null,
|
|
310
|
+
config: {
|
|
311
|
+
source: configDiagnostics.source,
|
|
312
|
+
error: configDiagnostics.error,
|
|
313
|
+
},
|
|
314
|
+
service: { summary: serviceSummary },
|
|
315
|
+
codexShim: { summary: codexShimSummary },
|
|
316
|
+
codexPlugins,
|
|
317
|
+
codexRuntime,
|
|
318
|
+
codexHome,
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliUsageError,
|
|
3
|
+
printData,
|
|
4
|
+
rejectArgs,
|
|
5
|
+
runCliAction,
|
|
6
|
+
runtimeRequest,
|
|
7
|
+
summaryLines,
|
|
8
|
+
takeBooleanOption,
|
|
9
|
+
takeFlag,
|
|
10
|
+
takeOption,
|
|
11
|
+
type RuntimeApiDeps,
|
|
12
|
+
} from "./runtime-api";
|
|
13
|
+
|
|
14
|
+
const USAGE = `Usage:
|
|
15
|
+
ocx system [status] [--json]
|
|
16
|
+
ocx system settings [--auto-start <on|off>] [--stream-mode <auto|legacy-tee|eager-relay>] [--json]
|
|
17
|
+
ocx system startup <health|install-service|install-shim> [--json]
|
|
18
|
+
ocx system diagnostics [--json]
|
|
19
|
+
ocx system sync [--json]
|
|
20
|
+
ocx system update check [--channel <latest|preview|next>] [--json]
|
|
21
|
+
ocx system update run [--channel <latest|preview|next>] [--restart <on|off>] --yes [--json]
|
|
22
|
+
ocx system update status <job-id> [--json]`;
|
|
23
|
+
|
|
24
|
+
async function status(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
25
|
+
const args = [...argv];
|
|
26
|
+
const wantsJson = takeFlag(args, "--json");
|
|
27
|
+
rejectArgs(args, USAGE);
|
|
28
|
+
const [settings, startup, memory] = await Promise.all([
|
|
29
|
+
runtimeRequest("/api/settings", {}, deps),
|
|
30
|
+
runtimeRequest("/api/startup-health", {}, deps),
|
|
31
|
+
runtimeRequest("/api/system/memory", {}, deps),
|
|
32
|
+
]);
|
|
33
|
+
const result = { settings, startup, memory };
|
|
34
|
+
printData(result, wantsJson, summaryLines(result));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function settings(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
38
|
+
const args = [...argv];
|
|
39
|
+
const wantsJson = takeFlag(args, "--json");
|
|
40
|
+
const autoStart = takeBooleanOption(args, "--auto-start");
|
|
41
|
+
const streamMode = takeOption(args, "--stream-mode");
|
|
42
|
+
rejectArgs(args, USAGE);
|
|
43
|
+
if (autoStart === undefined && streamMode === undefined) {
|
|
44
|
+
const result = await runtimeRequest("/api/settings", {}, deps);
|
|
45
|
+
printData(result, wantsJson, summaryLines(result));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const body = { ...(autoStart !== undefined ? { codexAutoStart: autoStart } : {}), ...(streamMode !== undefined ? { streamMode } : {}) };
|
|
49
|
+
const result = await runtimeRequest("/api/settings", { method: "PUT", body: JSON.stringify(body) }, deps);
|
|
50
|
+
printData(result, wantsJson, ["System settings updated."]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function startup(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
54
|
+
const args = [...argv];
|
|
55
|
+
const action = (args.shift() ?? "health").toLowerCase();
|
|
56
|
+
const wantsJson = takeFlag(args, "--json");
|
|
57
|
+
rejectArgs(args, USAGE);
|
|
58
|
+
if (action === "health" || action === "status") {
|
|
59
|
+
const result = await runtimeRequest("/api/startup-health", {}, deps);
|
|
60
|
+
printData(result, wantsJson, summaryLines(result));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (action !== "install-service" && action !== "install-shim") throw new CliUsageError("startup action must be health, install-service, or install-shim", USAGE);
|
|
64
|
+
const result = await runtimeRequest("/api/startup-action", { method: "POST", body: JSON.stringify({ action }) }, deps);
|
|
65
|
+
printData(result, wantsJson, [String((result as Record<string, unknown>).message ?? `${action} complete.`)]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function update(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
69
|
+
const args = [...argv];
|
|
70
|
+
const action = (args.shift() ?? "check").toLowerCase();
|
|
71
|
+
const wantsJson = takeFlag(args, "--json");
|
|
72
|
+
if (action === "status") {
|
|
73
|
+
const jobId = args.shift();
|
|
74
|
+
if (!jobId) throw new CliUsageError("update job id is required", USAGE);
|
|
75
|
+
rejectArgs(args, USAGE);
|
|
76
|
+
printData(await runtimeRequest(`/api/update/status?jobId=${encodeURIComponent(jobId)}`, {}, deps), wantsJson);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const channel = takeOption(args, "--channel") ?? "latest";
|
|
80
|
+
if (channel !== "latest" && channel !== "preview" && channel !== "next") {
|
|
81
|
+
throw new CliUsageError("--channel must be latest, preview, or next", USAGE);
|
|
82
|
+
}
|
|
83
|
+
if (action === "check") {
|
|
84
|
+
rejectArgs(args, USAGE);
|
|
85
|
+
printData(await runtimeRequest(`/api/update/check?tag=${channel}`, {}, deps), wantsJson);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (action !== "run") throw new CliUsageError(`unknown update action ${action}`, USAGE);
|
|
89
|
+
const restart = takeBooleanOption(args, "--restart") ?? true;
|
|
90
|
+
const yes = takeFlag(args, "--yes");
|
|
91
|
+
if (!yes) throw new CliUsageError("update run requires --yes", USAGE);
|
|
92
|
+
rejectArgs(args, USAGE);
|
|
93
|
+
const result = await runtimeRequest("/api/update/run", { method: "POST", body: JSON.stringify({ tag: channel, restart }) }, deps);
|
|
94
|
+
printData(result, wantsJson, [`Update started (${channel}).`]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function handleSystemCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
98
|
+
return runCliAction(async () => {
|
|
99
|
+
const [sub = "status", ...rest] = argv;
|
|
100
|
+
if (sub === "status") await status(rest, deps);
|
|
101
|
+
else if (sub === "settings") await settings(rest, deps);
|
|
102
|
+
else if (sub === "startup") await startup(rest, deps);
|
|
103
|
+
else if (sub === "diagnostics") {
|
|
104
|
+
const args = [...rest]; const wantsJson = takeFlag(args, "--json"); rejectArgs(args, USAGE);
|
|
105
|
+
printData(await runtimeRequest("/api/diagnostics/project-config", {}, deps), wantsJson);
|
|
106
|
+
} else if (sub === "sync") {
|
|
107
|
+
const args = [...rest]; const wantsJson = takeFlag(args, "--json"); rejectArgs(args, USAGE);
|
|
108
|
+
printData(await runtimeRequest("/api/sync", { method: "POST" }, deps), wantsJson);
|
|
109
|
+
} else if (sub === "update") await update(rest, deps);
|
|
110
|
+
else throw new CliUsageError(`unknown system command ${sub}`, USAGE);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const SYSTEM_USAGE = USAGE;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface TrayProxyLive { port: number }
|
|
2
|
+
|
|
3
|
+
export interface TrayProxyServiceState {
|
|
4
|
+
installed: boolean;
|
|
5
|
+
startable: boolean;
|
|
6
|
+
summary: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TrayProxyStartIo {
|
|
10
|
+
findLive: () => Promise<TrayProxyLive | null>;
|
|
11
|
+
diagnoseService: () => TrayProxyServiceState;
|
|
12
|
+
startService: () => void | Promise<void>;
|
|
13
|
+
startDirect: () => void | Promise<void>;
|
|
14
|
+
waitForProxy: () => Promise<TrayProxyLive | null>;
|
|
15
|
+
info: (message: string) => void;
|
|
16
|
+
error: (message: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Side-effect coordinator for the tray's fixed proxy-start action. */
|
|
20
|
+
export async function runTrayProxyStart(io: TrayProxyStartIo): Promise<boolean> {
|
|
21
|
+
const live = await io.findLive();
|
|
22
|
+
if (live) {
|
|
23
|
+
io.info(`Proxy already running on port ${live.port}.`);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const service = io.diagnoseService();
|
|
28
|
+
if (service.installed && !service.startable) {
|
|
29
|
+
io.error(`Cannot start from the tray because the installed service is not viable: ${service.summary}`);
|
|
30
|
+
io.error("Repair or remove the service before starting a direct proxy.");
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (service.startable) await io.startService();
|
|
35
|
+
else await io.startDirect();
|
|
36
|
+
|
|
37
|
+
const started = await io.waitForProxy();
|
|
38
|
+
if (!started) {
|
|
39
|
+
io.error("Proxy did not become healthy after the tray start action.");
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
io.info(`Proxy running on port ${started.port}.`);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function runTrayProxyRestart(io: {
|
|
47
|
+
stop: () => boolean | Promise<boolean>;
|
|
48
|
+
start: () => boolean | Promise<boolean>;
|
|
49
|
+
}): Promise<boolean> {
|
|
50
|
+
if (!await io.stop()) return false;
|
|
51
|
+
return io.start();
|
|
52
|
+
}
|
package/src/cli/v2.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ocx v2 status|on|off` — toggle/report the codex `multi_agent_v2` feature that
|
|
3
|
+
* controls the multi-agent surface (v1 vs v2 collab mode).
|
|
4
|
+
*
|
|
5
|
+
* Contract:
|
|
6
|
+
* - config.toml writes go through the official `codex features enable|disable`
|
|
7
|
+
* CLI only (format-preserving TOML edit stays upstream-owned).
|
|
8
|
+
* - after a successful flip the catalog is RESYNCED so model metadata stays fresh.
|
|
9
|
+
* - flips preserve the active thread limit while moving it between the v1/v2
|
|
10
|
+
* config keys, with byte-for-byte rollback when the feature command fails.
|
|
11
|
+
* - nothing in the catalog build path calls this module; no auto-flip exists.
|
|
12
|
+
*/
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import { getAgentsEnabled, getAgentsMaxDepth, getLogicalMaxThreads, getSubagentDeveloperInstructions, hasAgentsMaxThreads, isMultiAgentV2Enabled, transitionMultiAgentV2 } from "../codex/features";
|
|
15
|
+
|
|
16
|
+
import { commandInvocation, type SpawnInvocation } from "../lib/win-exec";
|
|
17
|
+
import { loadConfig, saveConfig } from "../config";
|
|
18
|
+
import { resolveAndPersistCodexRuntime, type ResolveCodexRuntimeDeps } from "../codex/runtime";
|
|
19
|
+
|
|
20
|
+
export interface V2CliDeps {
|
|
21
|
+
execFile?: (file: string, args: string[], options?: SpawnInvocation["options"]) => void;
|
|
22
|
+
isEnabled?: typeof isMultiAgentV2Enabled;
|
|
23
|
+
hasMaxThreads?: typeof hasAgentsMaxThreads;
|
|
24
|
+
sync?: (port?: number) => Promise<unknown>;
|
|
25
|
+
log?: Pick<Console, "log" | "error">;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type CodexFeaturesInvocationDeps =
|
|
29
|
+
& Parameters<typeof commandInvocation>[3]
|
|
30
|
+
& Pick<ResolveCodexRuntimeDeps, "existsSync" | "execFileSync" | "configDir" | "readFileSync">;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Shared invocation for `codex features enable|disable multi_agent_v2` — the single
|
|
34
|
+
* source of truth for the CLI and the management API fallback. Windows npm installs
|
|
35
|
+
* expose `codex` as a `.cmd` shim, which needs the win-exec launcher
|
|
36
|
+
* (devlog 260715_cross_platform_audit/020).
|
|
37
|
+
*/
|
|
38
|
+
export function codexFeaturesInvocation(
|
|
39
|
+
action: "enable" | "disable",
|
|
40
|
+
platform: NodeJS.Platform = process.platform,
|
|
41
|
+
deps: CodexFeaturesInvocationDeps = {},
|
|
42
|
+
): SpawnInvocation {
|
|
43
|
+
const command = resolveAndPersistCodexRuntime({
|
|
44
|
+
env: deps.env ?? process.env,
|
|
45
|
+
platform,
|
|
46
|
+
existsSync: deps.existsSync,
|
|
47
|
+
execFileSync: deps.execFileSync,
|
|
48
|
+
configDir: deps.configDir,
|
|
49
|
+
readFileSync: deps.readFileSync,
|
|
50
|
+
}).runtime.command || "codex";
|
|
51
|
+
return commandInvocation(command, ["features", action, "multi_agent_v2"], platform, deps);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runCodexFeatures(action: "enable" | "disable", deps: V2CliDeps): void {
|
|
55
|
+
const exec = deps.execFile ?? ((file: string, args: string[], options?: SpawnInvocation["options"]) => {
|
|
56
|
+
execFileSync(file, args, { stdio: ["ignore", "pipe", "pipe"], timeout: 15_000, windowsHide: true, ...options });
|
|
57
|
+
});
|
|
58
|
+
const inv = codexFeaturesInvocation(action);
|
|
59
|
+
exec(inv.file, inv.args, inv.options);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function v2StatusLine(enabled: boolean): string {
|
|
63
|
+
return enabled
|
|
64
|
+
? "multi_agent_v2: ON — v2 multi-agent surface active"
|
|
65
|
+
: "multi_agent_v2: OFF — v1 multi-agent surface (default install)";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function multiAgentModeLine(mode: string): string {
|
|
69
|
+
switch (mode) {
|
|
70
|
+
case "v1": return "multi_agent_mode: v1 — ALL models forced to v1 surface (upstream pins overridden)";
|
|
71
|
+
case "v2": return "multi_agent_mode: v2 — ALL models forced to v2 surface (upstream pins overridden)";
|
|
72
|
+
default: return "multi_agent_mode: default — upstream model pins respected (sol/terra=v2, luna=v1, rest=codex flag)";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function cmdV2(args: string[], deps: V2CliDeps = {}, findPort?: () => Promise<number | undefined>): Promise<number> {
|
|
77
|
+
const log = deps.log ?? console;
|
|
78
|
+
const isEnabled = deps.isEnabled ?? isMultiAgentV2Enabled;
|
|
79
|
+
const hasMaxThreads = deps.hasMaxThreads ?? hasAgentsMaxThreads;
|
|
80
|
+
const verb = (args[0] ?? "status").trim().toLowerCase();
|
|
81
|
+
|
|
82
|
+
if (verb === "status") {
|
|
83
|
+
log.log(v2StatusLine(isEnabled()));
|
|
84
|
+
const cfg = loadConfig();
|
|
85
|
+
log.log(multiAgentModeLine(cfg.multiAgentMode ?? "default"));
|
|
86
|
+
const threads = getLogicalMaxThreads();
|
|
87
|
+
log.log(`max_threads: ${threads ?? "(unset — codex default)"}`);
|
|
88
|
+
const v2Active = isEnabled();
|
|
89
|
+
const agentsEnabled = getAgentsEnabled();
|
|
90
|
+
log.log(`agents.enabled: ${agentsEnabled === null ? "(unset — upstream default true)" : agentsEnabled}`);
|
|
91
|
+
const maxDepth = getAgentsMaxDepth();
|
|
92
|
+
// max_depth is V1-only upstream; say so whenever V2 is active so the number
|
|
93
|
+
// cannot be misread as an effective V2 limit.
|
|
94
|
+
log.log(`agents.max_depth: ${maxDepth ?? "(unset — upstream default 1)"}${v2Active ? " (V1-only — ignored while multi_agent_v2 is enabled)" : ""}`);
|
|
95
|
+
const instructions = getSubagentDeveloperInstructions();
|
|
96
|
+
log.log(`subagent_developer_instructions: ${instructions === null ? "(unset — children inherit)" : instructions === "" ? '"" (clears inherited instructions)' : JSON.stringify(instructions)}`);
|
|
97
|
+
if (isEnabled() && hasMaxThreads()) {
|
|
98
|
+
log.log("WARNING: [agents] max_threads is set — codex refuses to start while multi_agent_v2 is enabled. Remove it from config.toml (concurrency lives in features.multi_agent_v2.max_concurrent_threads_per_session).");
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
if (verb === "threads") {
|
|
103
|
+
const value = Number((args[1] ?? "").trim());
|
|
104
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
105
|
+
log.error("v2 threads: pass an integer >= 1 (features.multi_agent_v2.max_concurrent_threads_per_session)");
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
const enabled = isEnabled();
|
|
109
|
+
const result = transitionMultiAgentV2(enabled, next => runCodexFeatures(next ? "enable" : "disable", deps), { threadLimit: value });
|
|
110
|
+
if (!result.ok) { log.error(`v2 threads: ${result.error}`); return 1; }
|
|
111
|
+
log.log(result.changed
|
|
112
|
+
? `max_threads = ${value} (${enabled ? "v2" : "v1"}) — applies to new sessions.`
|
|
113
|
+
: `max_threads already ${value} — nothing to do.`);
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
if (verb === "mode") {
|
|
117
|
+
const modeArg = (args[1] ?? "").trim().toLowerCase();
|
|
118
|
+
if (modeArg !== "v1" && modeArg !== "default" && modeArg !== "v2") {
|
|
119
|
+
log.error("v2 mode: expected v1|default|v2");
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
const cfg = loadConfig();
|
|
123
|
+
if (modeArg !== "default") {
|
|
124
|
+
const target = modeArg === "v2";
|
|
125
|
+
const transition = transitionMultiAgentV2(target, enabled => runCodexFeatures(enabled ? "enable" : "disable", deps));
|
|
126
|
+
if (!transition.ok) {
|
|
127
|
+
log.error(`multi-agent mode transition failed: ${transition.error}`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (modeArg === "default") delete cfg.multiAgentMode;
|
|
132
|
+
else cfg.multiAgentMode = modeArg as "v1" | "v2";
|
|
133
|
+
saveConfig(cfg);
|
|
134
|
+
try {
|
|
135
|
+
const sync = deps.sync ?? (await import("../codex/sync")).syncModelsToCodex;
|
|
136
|
+
await sync(findPort ? await findPort() : undefined);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
log.error(`catalog resync failed: ${err instanceof Error ? err.message : String(err)} — run 'ocx sync' manually.`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
log.log(multiAgentModeLine(modeArg));
|
|
142
|
+
log.log("Applies to NEW sessions; running sessions keep their pinned multi-agent version.");
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (verb !== "on" && verb !== "off") {
|
|
146
|
+
log.error(`v2: unknown verb '${verb}' (expected status|on|off|mode <v1|default|v2>|threads <n>)`);
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const want = verb === "on";
|
|
151
|
+
const transition = transitionMultiAgentV2(want, enabled => runCodexFeatures(enabled ? "enable" : "disable", deps));
|
|
152
|
+
if (!transition.ok) {
|
|
153
|
+
log.error(`codex features ${want ? "enable" : "disable"} multi_agent_v2 failed: ${transition.error}`);
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
if (!transition.changed) {
|
|
157
|
+
log.log(`multi_agent_v2 already ${want ? "ON" : "OFF"} — nothing to do.`);
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Resync catalog so multi-agent surface metadata stays fresh in both the
|
|
162
|
+
// on-disk catalog and models_cache.json after the toggle flip.
|
|
163
|
+
try {
|
|
164
|
+
const sync = deps.sync ?? (await import("../codex/sync")).syncModelsToCodex;
|
|
165
|
+
await sync(findPort ? await findPort() : undefined);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
log.error(`catalog resync failed (flag IS flipped): ${err instanceof Error ? err.message : String(err)} — run 'ocx sync' manually.`);
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
log.log(v2StatusLine(want));
|
|
171
|
+
log.log("Applies to NEW sessions; running sessions keep their pinned multi-agent version. Restart the Codex app (or wait out its picker cache) to see the ladder change.");
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Compatibility stub for the pre-restructure entrypoint path.
|
|
3
|
+
//
|
|
4
|
+
// Before the src/ restructure the CLI lived at src/cli.ts, and durable launchers
|
|
5
|
+
// (codex shim wrappers, installed service definitions) baked that absolute path
|
|
6
|
+
// into their command lines. Users who upgrade in place with a bare
|
|
7
|
+
// `npm install -g @coseung2/opencodex` (instead of `ocx update`, which repairs the
|
|
8
|
+
// shim/service) would otherwise be stranded on a dead path. Keep this stub for at
|
|
9
|
+
// least one release cycle after the restructure ships.
|
|
10
|
+
import "./cli/index.ts";
|