@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
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated read/patch of Pi's global settings.json.
|
|
3
|
+
*
|
|
4
|
+
* Only a allowlisted subset is writable through opencodex. `packages` and path
|
|
5
|
+
* resource arrays are managed by the packages/extensions modules (which shell out
|
|
6
|
+
* to `pi` or list directories), never by free-form JSON merge here — that keeps
|
|
7
|
+
* shell-source injection and arbitrary extension paths out of a single PUT body.
|
|
8
|
+
*
|
|
9
|
+
* Unknown keys already on disk are always preserved.
|
|
10
|
+
*/
|
|
11
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { dirname } from "node:path";
|
|
13
|
+
import { atomicWriteFile } from "../config";
|
|
14
|
+
import { piAgentDirExists, piSettingsPath, resolvePiAgentDir } from "./home";
|
|
15
|
+
|
|
16
|
+
const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
17
|
+
const TRUST_VALUES = new Set(["ask", "always", "never"]);
|
|
18
|
+
|
|
19
|
+
export interface PiCuratedSettings {
|
|
20
|
+
defaultProvider?: string;
|
|
21
|
+
defaultModel?: string;
|
|
22
|
+
defaultThinkingLevel?: string;
|
|
23
|
+
hideThinkingBlock?: boolean;
|
|
24
|
+
theme?: string;
|
|
25
|
+
quietStartup?: boolean;
|
|
26
|
+
defaultProjectTrust?: string;
|
|
27
|
+
enabledModels?: string[];
|
|
28
|
+
compaction?: {
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
reserveTokens?: number;
|
|
31
|
+
keepRecentTokens?: number;
|
|
32
|
+
};
|
|
33
|
+
retry?: {
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
maxRetries?: number;
|
|
36
|
+
baseDelayMs?: number;
|
|
37
|
+
};
|
|
38
|
+
/** Present in the file; packages are managed separately but shown for context. */
|
|
39
|
+
packages?: unknown[];
|
|
40
|
+
extensions?: string[];
|
|
41
|
+
skills?: string[];
|
|
42
|
+
prompts?: string[];
|
|
43
|
+
themes?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PiSettingsStatus {
|
|
47
|
+
settingsPath: string;
|
|
48
|
+
present: boolean;
|
|
49
|
+
settings: PiCuratedSettings;
|
|
50
|
+
/** Raw top-level keys we did not map — count only, never values (may hold secrets). */
|
|
51
|
+
otherKeyCount: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface PiSettingsWriteResult {
|
|
55
|
+
ok: boolean;
|
|
56
|
+
changed: boolean;
|
|
57
|
+
message: string;
|
|
58
|
+
settingsPath: string;
|
|
59
|
+
settings?: PiCuratedSettings;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
63
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readDoc(path: string): { ok: true; doc: Record<string, unknown> } | { ok: false; error: string } {
|
|
67
|
+
if (!existsSync(path)) return { ok: true, doc: {} };
|
|
68
|
+
try {
|
|
69
|
+
const raw = readFileSync(path, "utf8");
|
|
70
|
+
if (raw.trim() === "") return { ok: true, doc: {} };
|
|
71
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
72
|
+
if (!isRecord(parsed)) return { ok: false, error: "settings.json root must be an object" };
|
|
73
|
+
return { ok: true, doc: parsed };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function positiveInt(value: unknown): number | undefined {
|
|
80
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && Number.isInteger(value)
|
|
81
|
+
? value
|
|
82
|
+
: undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function nonNegInt(value: unknown): number | undefined {
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 && Number.isInteger(value)
|
|
87
|
+
? value
|
|
88
|
+
: undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function asStringArray(value: unknown): string[] | undefined {
|
|
92
|
+
if (!Array.isArray(value)) return undefined;
|
|
93
|
+
if (!value.every(entry => typeof entry === "string")) return undefined;
|
|
94
|
+
return value as string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Project a raw settings object onto the curated surface. */
|
|
98
|
+
export function projectPiSettings(doc: Record<string, unknown>): { settings: PiCuratedSettings; otherKeyCount: number } {
|
|
99
|
+
const known = new Set([
|
|
100
|
+
"defaultProvider",
|
|
101
|
+
"defaultModel",
|
|
102
|
+
"defaultThinkingLevel",
|
|
103
|
+
"hideThinkingBlock",
|
|
104
|
+
"theme",
|
|
105
|
+
"quietStartup",
|
|
106
|
+
"defaultProjectTrust",
|
|
107
|
+
"enabledModels",
|
|
108
|
+
"compaction",
|
|
109
|
+
"retry",
|
|
110
|
+
"packages",
|
|
111
|
+
"extensions",
|
|
112
|
+
"skills",
|
|
113
|
+
"prompts",
|
|
114
|
+
"themes",
|
|
115
|
+
]);
|
|
116
|
+
const settings: PiCuratedSettings = {};
|
|
117
|
+
if (typeof doc.defaultProvider === "string") settings.defaultProvider = doc.defaultProvider;
|
|
118
|
+
if (typeof doc.defaultModel === "string") settings.defaultModel = doc.defaultModel;
|
|
119
|
+
if (typeof doc.defaultThinkingLevel === "string") settings.defaultThinkingLevel = doc.defaultThinkingLevel;
|
|
120
|
+
if (typeof doc.hideThinkingBlock === "boolean") settings.hideThinkingBlock = doc.hideThinkingBlock;
|
|
121
|
+
if (typeof doc.theme === "string") settings.theme = doc.theme;
|
|
122
|
+
if (typeof doc.quietStartup === "boolean") settings.quietStartup = doc.quietStartup;
|
|
123
|
+
if (typeof doc.defaultProjectTrust === "string") settings.defaultProjectTrust = doc.defaultProjectTrust;
|
|
124
|
+
const enabledModels = asStringArray(doc.enabledModels);
|
|
125
|
+
if (enabledModels) settings.enabledModels = enabledModels;
|
|
126
|
+
if (isRecord(doc.compaction)) {
|
|
127
|
+
const c: NonNullable<PiCuratedSettings["compaction"]> = {};
|
|
128
|
+
if (typeof doc.compaction.enabled === "boolean") c.enabled = doc.compaction.enabled;
|
|
129
|
+
const reserve = positiveInt(doc.compaction.reserveTokens);
|
|
130
|
+
if (reserve !== undefined) c.reserveTokens = reserve;
|
|
131
|
+
const keep = positiveInt(doc.compaction.keepRecentTokens);
|
|
132
|
+
if (keep !== undefined) c.keepRecentTokens = keep;
|
|
133
|
+
if (Object.keys(c).length > 0) settings.compaction = c;
|
|
134
|
+
}
|
|
135
|
+
if (isRecord(doc.retry)) {
|
|
136
|
+
const r: NonNullable<PiCuratedSettings["retry"]> = {};
|
|
137
|
+
if (typeof doc.retry.enabled === "boolean") r.enabled = doc.retry.enabled;
|
|
138
|
+
const maxRetries = nonNegInt(doc.retry.maxRetries);
|
|
139
|
+
if (maxRetries !== undefined) r.maxRetries = maxRetries;
|
|
140
|
+
const baseDelayMs = nonNegInt(doc.retry.baseDelayMs);
|
|
141
|
+
if (baseDelayMs !== undefined) r.baseDelayMs = baseDelayMs;
|
|
142
|
+
if (Object.keys(r).length > 0) settings.retry = r;
|
|
143
|
+
}
|
|
144
|
+
if (Array.isArray(doc.packages)) settings.packages = doc.packages;
|
|
145
|
+
const extensions = asStringArray(doc.extensions);
|
|
146
|
+
if (extensions) settings.extensions = extensions;
|
|
147
|
+
const skills = asStringArray(doc.skills);
|
|
148
|
+
if (skills) settings.skills = skills;
|
|
149
|
+
const prompts = asStringArray(doc.prompts);
|
|
150
|
+
if (prompts) settings.prompts = prompts;
|
|
151
|
+
const themes = asStringArray(doc.themes);
|
|
152
|
+
if (themes) settings.themes = themes;
|
|
153
|
+
|
|
154
|
+
let otherKeyCount = 0;
|
|
155
|
+
for (const key of Object.keys(doc)) {
|
|
156
|
+
if (!known.has(key)) otherKeyCount += 1;
|
|
157
|
+
}
|
|
158
|
+
return { settings, otherKeyCount };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function readPiSettings(opts: { piHome?: string } = {}): PiSettingsStatus {
|
|
162
|
+
const settingsPath = piSettingsPath(opts.piHome);
|
|
163
|
+
const loaded = readDoc(settingsPath);
|
|
164
|
+
if (!loaded.ok) {
|
|
165
|
+
return { settingsPath, present: false, settings: {}, otherKeyCount: 0 };
|
|
166
|
+
}
|
|
167
|
+
const present = existsSync(settingsPath);
|
|
168
|
+
const { settings, otherKeyCount } = projectPiSettings(loaded.doc);
|
|
169
|
+
return { settingsPath, present, settings, otherKeyCount };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Validate and apply a partial patch. Returns a user-facing error string on bad input.
|
|
174
|
+
* `null` fields clear the key when the property is present with value null.
|
|
175
|
+
*/
|
|
176
|
+
export function validatePiSettingsPatch(patch: unknown): { ok: true; patch: Record<string, unknown> } | { ok: false; error: string } {
|
|
177
|
+
if (!isRecord(patch)) return { ok: false, error: "body must be an object" };
|
|
178
|
+
const out: Record<string, unknown> = {};
|
|
179
|
+
|
|
180
|
+
const stringOrNull = (key: string, max = 200): string | null | undefined | false => {
|
|
181
|
+
if (!(key in patch)) return undefined;
|
|
182
|
+
const value = patch[key];
|
|
183
|
+
if (value === null) return null;
|
|
184
|
+
if (typeof value !== "string" || value.length === 0 || value.length > max) return false;
|
|
185
|
+
return value;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (const key of ["defaultProvider", "defaultModel", "theme"] as const) {
|
|
189
|
+
const value = stringOrNull(key);
|
|
190
|
+
if (value === false) return { ok: false, error: `${key} must be a non-empty string or null` };
|
|
191
|
+
if (value !== undefined) out[key] = value;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if ("defaultThinkingLevel" in patch) {
|
|
195
|
+
const value = patch.defaultThinkingLevel;
|
|
196
|
+
if (value === null) out.defaultThinkingLevel = null;
|
|
197
|
+
else if (typeof value !== "string" || !THINKING_LEVELS.has(value)) {
|
|
198
|
+
return { ok: false, error: `defaultThinkingLevel must be one of ${[...THINKING_LEVELS].join(", ")} or null` };
|
|
199
|
+
} else out.defaultThinkingLevel = value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if ("defaultProjectTrust" in patch) {
|
|
203
|
+
const value = patch.defaultProjectTrust;
|
|
204
|
+
if (value === null) out.defaultProjectTrust = null;
|
|
205
|
+
else if (typeof value !== "string" || !TRUST_VALUES.has(value)) {
|
|
206
|
+
return { ok: false, error: `defaultProjectTrust must be one of ${[...TRUST_VALUES].join(", ")} or null` };
|
|
207
|
+
} else out.defaultProjectTrust = value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const key of ["hideThinkingBlock", "quietStartup"] as const) {
|
|
211
|
+
if (!(key in patch)) continue;
|
|
212
|
+
const value = patch[key];
|
|
213
|
+
if (value === null) out[key] = null;
|
|
214
|
+
else if (typeof value !== "boolean") return { ok: false, error: `${key} must be a boolean or null` };
|
|
215
|
+
else out[key] = value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if ("enabledModels" in patch) {
|
|
219
|
+
const value = patch.enabledModels;
|
|
220
|
+
if (value === null) out.enabledModels = null;
|
|
221
|
+
else {
|
|
222
|
+
const arr = asStringArray(value);
|
|
223
|
+
if (!arr || arr.length > 500) return { ok: false, error: "enabledModels must be a string array (max 500) or null" };
|
|
224
|
+
out.enabledModels = arr;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if ("compaction" in patch) {
|
|
229
|
+
const value = patch.compaction;
|
|
230
|
+
if (value === null) out.compaction = null;
|
|
231
|
+
else if (!isRecord(value)) return { ok: false, error: "compaction must be an object or null" };
|
|
232
|
+
else {
|
|
233
|
+
const c: Record<string, unknown> = {};
|
|
234
|
+
if ("enabled" in value) {
|
|
235
|
+
if (typeof value.enabled !== "boolean") return { ok: false, error: "compaction.enabled must be boolean" };
|
|
236
|
+
c.enabled = value.enabled;
|
|
237
|
+
}
|
|
238
|
+
if ("reserveTokens" in value) {
|
|
239
|
+
const n = positiveInt(value.reserveTokens);
|
|
240
|
+
if (n === undefined) return { ok: false, error: "compaction.reserveTokens must be a positive integer" };
|
|
241
|
+
c.reserveTokens = n;
|
|
242
|
+
}
|
|
243
|
+
if ("keepRecentTokens" in value) {
|
|
244
|
+
const n = positiveInt(value.keepRecentTokens);
|
|
245
|
+
if (n === undefined) return { ok: false, error: "compaction.keepRecentTokens must be a positive integer" };
|
|
246
|
+
c.keepRecentTokens = n;
|
|
247
|
+
}
|
|
248
|
+
out.compaction = c;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if ("retry" in patch) {
|
|
253
|
+
const value = patch.retry;
|
|
254
|
+
if (value === null) out.retry = null;
|
|
255
|
+
else if (!isRecord(value)) return { ok: false, error: "retry must be an object or null" };
|
|
256
|
+
else {
|
|
257
|
+
const r: Record<string, unknown> = {};
|
|
258
|
+
if ("enabled" in value) {
|
|
259
|
+
if (typeof value.enabled !== "boolean") return { ok: false, error: "retry.enabled must be boolean" };
|
|
260
|
+
r.enabled = value.enabled;
|
|
261
|
+
}
|
|
262
|
+
if ("maxRetries" in value) {
|
|
263
|
+
const n = nonNegInt(value.maxRetries);
|
|
264
|
+
if (n === undefined || n > 20) return { ok: false, error: "retry.maxRetries must be an integer 0–20" };
|
|
265
|
+
r.maxRetries = n;
|
|
266
|
+
}
|
|
267
|
+
if ("baseDelayMs" in value) {
|
|
268
|
+
const n = nonNegInt(value.baseDelayMs);
|
|
269
|
+
if (n === undefined || n > 600_000) return { ok: false, error: "retry.baseDelayMs must be an integer 0–600000" };
|
|
270
|
+
r.baseDelayMs = n;
|
|
271
|
+
}
|
|
272
|
+
out.retry = r;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Reject unknown top-level patch keys so clients cannot smuggle packages/extensions.
|
|
277
|
+
const allowed = new Set([
|
|
278
|
+
"defaultProvider",
|
|
279
|
+
"defaultModel",
|
|
280
|
+
"defaultThinkingLevel",
|
|
281
|
+
"hideThinkingBlock",
|
|
282
|
+
"theme",
|
|
283
|
+
"quietStartup",
|
|
284
|
+
"defaultProjectTrust",
|
|
285
|
+
"enabledModels",
|
|
286
|
+
"compaction",
|
|
287
|
+
"retry",
|
|
288
|
+
]);
|
|
289
|
+
for (const key of Object.keys(patch)) {
|
|
290
|
+
if (!allowed.has(key)) return { ok: false, error: `unsupported settings key: ${key}` };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return { ok: true, patch: out };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function applyPatch(doc: Record<string, unknown>, patch: Record<string, unknown>): Record<string, unknown> {
|
|
297
|
+
const next = { ...doc };
|
|
298
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
299
|
+
if (value === null) {
|
|
300
|
+
delete next[key];
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if ((key === "compaction" || key === "retry") && isRecord(value) && isRecord(next[key])) {
|
|
304
|
+
next[key] = { ...next[key], ...value };
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
next[key] = value;
|
|
308
|
+
}
|
|
309
|
+
return next;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function writePiSettings(
|
|
313
|
+
patchInput: unknown,
|
|
314
|
+
opts: { piHome?: string } = {},
|
|
315
|
+
): PiSettingsWriteResult {
|
|
316
|
+
const settingsPath = piSettingsPath(opts.piHome);
|
|
317
|
+
if (!piAgentDirExists(opts.piHome)) {
|
|
318
|
+
return {
|
|
319
|
+
ok: false,
|
|
320
|
+
changed: false,
|
|
321
|
+
message: `Pi settings write skipped: ${resolvePiAgentDir(opts.piHome)} does not exist.`,
|
|
322
|
+
settingsPath,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const validated = validatePiSettingsPatch(patchInput);
|
|
326
|
+
if (!validated.ok) {
|
|
327
|
+
return { ok: false, changed: false, message: validated.error, settingsPath };
|
|
328
|
+
}
|
|
329
|
+
if (Object.keys(validated.patch).length === 0) {
|
|
330
|
+
return { ok: true, changed: false, message: "No settings changes.", settingsPath, settings: readPiSettings(opts).settings };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const loaded = readDoc(settingsPath);
|
|
334
|
+
if (!loaded.ok) {
|
|
335
|
+
return { ok: false, changed: false, message: loaded.error, settingsPath };
|
|
336
|
+
}
|
|
337
|
+
const nextDoc = applyPatch(loaded.doc, validated.patch);
|
|
338
|
+
if (JSON.stringify(loaded.doc) === JSON.stringify(nextDoc)) {
|
|
339
|
+
const projected = projectPiSettings(nextDoc);
|
|
340
|
+
return {
|
|
341
|
+
ok: true,
|
|
342
|
+
changed: false,
|
|
343
|
+
message: "Pi settings unchanged.",
|
|
344
|
+
settingsPath,
|
|
345
|
+
settings: projected.settings,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
349
|
+
if (existsSync(settingsPath)) {
|
|
350
|
+
try {
|
|
351
|
+
copyFileSync(settingsPath, `${settingsPath}.opencodex.bak`);
|
|
352
|
+
} catch {
|
|
353
|
+
// best-effort
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
atomicWriteFile(settingsPath, `${JSON.stringify(nextDoc, null, 2)}\n`);
|
|
357
|
+
const projected = projectPiSettings(nextDoc);
|
|
358
|
+
return {
|
|
359
|
+
ok: true,
|
|
360
|
+
changed: true,
|
|
361
|
+
message: `Pi settings updated → ${settingsPath}`,
|
|
362
|
+
settingsPath,
|
|
363
|
+
settings: projected.settings,
|
|
364
|
+
};
|
|
365
|
+
}
|
package/src/pi/status.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate Pi integration status for CLI + management API + GUI.
|
|
3
|
+
* Never reads auth.json body (credentials).
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import {
|
|
7
|
+
piAgentDirExists,
|
|
8
|
+
piModelsPath,
|
|
9
|
+
piSettingsPath,
|
|
10
|
+
resolvePiAgentDir,
|
|
11
|
+
resolvePiRoot,
|
|
12
|
+
} from "./home";
|
|
13
|
+
import { readPiModelsStatus, type PiModelsStatus } from "./models";
|
|
14
|
+
import { readPiSettings, type PiSettingsStatus } from "./settings";
|
|
15
|
+
import {
|
|
16
|
+
listPiPackagesDetailed,
|
|
17
|
+
readPiPackages,
|
|
18
|
+
resolvePiBinary,
|
|
19
|
+
type PiPackagesStatus,
|
|
20
|
+
} from "./packages";
|
|
21
|
+
import { readPiExtensions, type PiExtensionsStatus } from "./extensions";
|
|
22
|
+
|
|
23
|
+
export interface PiStatus {
|
|
24
|
+
piRoot: string;
|
|
25
|
+
agentDir: string;
|
|
26
|
+
agentDirPresent: boolean;
|
|
27
|
+
piBinary: string | null;
|
|
28
|
+
modelsPath: string;
|
|
29
|
+
settingsPath: string;
|
|
30
|
+
models: PiModelsStatus;
|
|
31
|
+
settings: PiSettingsStatus;
|
|
32
|
+
packages: PiPackagesStatus;
|
|
33
|
+
extensions: PiExtensionsStatus;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function readPiStatus(opts: { piHome?: string; includePackageList?: boolean } = {}): Promise<PiStatus> {
|
|
37
|
+
const packages: PiPackagesStatus = opts.includePackageList === false
|
|
38
|
+
? readPiPackages(opts)
|
|
39
|
+
: await listPiPackagesDetailed(opts);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
piRoot: resolvePiRoot(opts.piHome),
|
|
43
|
+
agentDir: resolvePiAgentDir(opts.piHome),
|
|
44
|
+
agentDirPresent: piAgentDirExists(opts.piHome),
|
|
45
|
+
piBinary: resolvePiBinary(),
|
|
46
|
+
modelsPath: piModelsPath(opts.piHome),
|
|
47
|
+
settingsPath: piSettingsPath(opts.piHome),
|
|
48
|
+
models: readPiModelsStatus(opts),
|
|
49
|
+
settings: readPiSettings(opts),
|
|
50
|
+
packages,
|
|
51
|
+
extensions: readPiExtensions(opts),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Cheap existence probe for status lines that should not shell out. */
|
|
56
|
+
export function piInstallHint(status: Pick<PiStatus, "agentDirPresent" | "piBinary">): string | null {
|
|
57
|
+
if (status.piBinary && status.agentDirPresent) return null;
|
|
58
|
+
if (!status.piBinary && !status.agentDirPresent) {
|
|
59
|
+
return "Pi is not installed (binary missing and ~/.pi/agent absent). Install: npm i -g @earendil-works/pi-coding-agent";
|
|
60
|
+
}
|
|
61
|
+
if (!status.piBinary) return "pi binary not on PATH; package install/remove will be unavailable.";
|
|
62
|
+
if (!status.agentDirPresent) return "Pi agent dir missing; run `pi` once to create ~/.pi/agent.";
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function modelsFileExists(opts: { piHome?: string } = {}): boolean {
|
|
67
|
+
return existsSync(piModelsPath(opts.piHome));
|
|
68
|
+
}
|
package/src/pi/sync.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gather the visible catalog and inject providers.opencodex into Pi models.json.
|
|
3
|
+
* Mirrors grok/sync.ts: used by management apply route and CLI.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
filterCatalogVisibleModels,
|
|
7
|
+
nativeOpenAiContextWindow,
|
|
8
|
+
visibleNativeSlugs,
|
|
9
|
+
type CatalogModel,
|
|
10
|
+
} from "../codex/catalog";
|
|
11
|
+
import type { OcxConfig } from "../types";
|
|
12
|
+
import { injectPiModels, type PiInjectModel, type PiInjectResult } from "./models";
|
|
13
|
+
|
|
14
|
+
export interface PiSyncDeps {
|
|
15
|
+
fetchAllModels: (config: OcxConfig) => Promise<CatalogModel[]>;
|
|
16
|
+
injectPiModels: typeof injectPiModels;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function defaultFetchAllModels(config: OcxConfig): Promise<CatalogModel[]> {
|
|
20
|
+
const { fetchAllModels } = await import("../server/management-api");
|
|
21
|
+
return fetchAllModels(config);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function catalogToPiModels(config: OcxConfig, routed: CatalogModel[]): PiInjectModel[] {
|
|
25
|
+
const native: PiInjectModel[] = visibleNativeSlugs(config).map(id => {
|
|
26
|
+
const contextWindow = nativeOpenAiContextWindow(id);
|
|
27
|
+
return {
|
|
28
|
+
namespaced: id,
|
|
29
|
+
provider: "openai",
|
|
30
|
+
id,
|
|
31
|
+
native: true,
|
|
32
|
+
...(contextWindow !== undefined ? { contextWindow } : {}),
|
|
33
|
+
inputModalities: ["text", "image"],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
const fromRouted: PiInjectModel[] = routed.map(m => {
|
|
37
|
+
const namespaced = m.alias ?? `${m.provider}/${m.id}`;
|
|
38
|
+
return {
|
|
39
|
+
namespaced,
|
|
40
|
+
provider: m.provider,
|
|
41
|
+
id: m.id,
|
|
42
|
+
...(m.displayName !== undefined ? { displayName: m.displayName } : {}),
|
|
43
|
+
...(m.contextWindow !== undefined ? { contextWindow: m.contextWindow } : {}),
|
|
44
|
+
inputModalities: m.inputModalities && m.inputModalities.length > 0
|
|
45
|
+
? [...m.inputModalities]
|
|
46
|
+
: ["text"],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
return [...native, ...fromRouted];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function syncPiConfig(
|
|
53
|
+
port: number,
|
|
54
|
+
config: OcxConfig,
|
|
55
|
+
opts: { hostname?: string; piHome?: string } = {},
|
|
56
|
+
deps: PiSyncDeps = { fetchAllModels: defaultFetchAllModels, injectPiModels },
|
|
57
|
+
): Promise<PiInjectResult> {
|
|
58
|
+
let models: PiInjectModel[];
|
|
59
|
+
try {
|
|
60
|
+
const routed = filterCatalogVisibleModels(await deps.fetchAllModels(config), config);
|
|
61
|
+
models = catalogToPiModels(config, routed);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
changed: false,
|
|
66
|
+
message: `Pi config sync skipped: model catalog unavailable (${err instanceof Error ? err.message : String(err)})`,
|
|
67
|
+
modelsPath: "",
|
|
68
|
+
modelCount: 0,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return deps.injectPiModels(port, models, {
|
|
72
|
+
...(opts.hostname !== undefined ? { hostname: opts.hostname } : {}),
|
|
73
|
+
...(opts.piHome !== undefined ? { piHome: opts.piHome } : {}),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, linkSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
+
import { getConfigPath } from "../config";
|
|
3
|
+
|
|
4
|
+
export interface AlibabaBackupIO {
|
|
5
|
+
exists: (path: string) => boolean;
|
|
6
|
+
read: (path: string) => Buffer;
|
|
7
|
+
copy: (source: string, destination: string) => void;
|
|
8
|
+
/** Publish with no-replace semantics: fails with EEXIST if the destination exists. */
|
|
9
|
+
publishNoReplace: (temp: string, destination: string) => void;
|
|
10
|
+
remove: (path: string) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DEFAULT_IO: AlibabaBackupIO = {
|
|
14
|
+
exists: existsSync,
|
|
15
|
+
read: path => readFileSync(path),
|
|
16
|
+
copy: (source, destination) => copyFileSync(source, destination),
|
|
17
|
+
publishNoReplace: linkSync,
|
|
18
|
+
remove: path => rmSync(path, { force: true }),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class AlibabaBackupIntegrityError extends Error {}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Immutable pre-migration snapshot of `config.json`.
|
|
25
|
+
*
|
|
26
|
+
* Written to a temp file first and published by `link`, which fails with EEXIST
|
|
27
|
+
* rather than replacing. Exclusive *creation* alone is not enough: a crash
|
|
28
|
+
* mid-copy can leave a truncated destination that the next run would accept as a
|
|
29
|
+
* valid rollback point. Copy-verify-link means a published snapshot is always a
|
|
30
|
+
* complete one.
|
|
31
|
+
*
|
|
32
|
+
* An existing snapshot is NOT required to equal the current config. Demanding
|
|
33
|
+
* that has a false positive that would brick a working install: a run that
|
|
34
|
+
* created the snapshot and then aborted for an unrelated reason leaves a
|
|
35
|
+
* perfectly valid snapshot, the user then edits `config.json` legitimately, and
|
|
36
|
+
* every subsequent start would throw out of `startServer` and refuse to boot. A
|
|
37
|
+
* safety net must never become the thing that stops the product.
|
|
38
|
+
*
|
|
39
|
+
* So an existing snapshot is reused as-is — it is the earliest one, which is
|
|
40
|
+
* exactly what a rollback point should be. Integrity is carried by publication.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately not built on `backupConfigBeforeOpenAiTierMigration`: that
|
|
43
|
+
* function's stale/rollback classification exists for a repeatable migration and
|
|
44
|
+
* would be wrong here.
|
|
45
|
+
*/
|
|
46
|
+
export function backupConfigBeforeAlibabaRegionMigration(
|
|
47
|
+
configPath = getConfigPath(),
|
|
48
|
+
io: AlibabaBackupIO = DEFAULT_IO,
|
|
49
|
+
): "absent" | "created" | "reused" {
|
|
50
|
+
if (!io.exists(configPath)) return "absent";
|
|
51
|
+
const backup = `${configPath}.pre-alibaba-region-v1.bak`;
|
|
52
|
+
// The earliest snapshot is the most valuable one: it predates every migration
|
|
53
|
+
// this config has been through.
|
|
54
|
+
if (io.exists(backup)) return "reused";
|
|
55
|
+
|
|
56
|
+
const source = io.read(configPath);
|
|
57
|
+
const temp = `${backup}.${process.pid}.tmp`;
|
|
58
|
+
try {
|
|
59
|
+
io.copy(configPath, temp);
|
|
60
|
+
// Verify before publishing: a short copy must never become the snapshot.
|
|
61
|
+
if (!io.read(temp).equals(source)) {
|
|
62
|
+
throw new AlibabaBackupIntegrityError(`failed to write a complete backup to ${temp}`);
|
|
63
|
+
}
|
|
64
|
+
io.publishNoReplace(temp, backup);
|
|
65
|
+
return "created";
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// Another process published between our `exists` check and the link. It went
|
|
68
|
+
// through the same copy-verify-link sequence, so it is complete by
|
|
69
|
+
// construction — nothing else writes this filename.
|
|
70
|
+
if ((error as NodeJS.ErrnoException).code === "EEXIST") return "reused";
|
|
71
|
+
throw error;
|
|
72
|
+
} finally {
|
|
73
|
+
io.remove(temp);
|
|
74
|
+
}
|
|
75
|
+
}
|