@iislee/opencodex 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +451 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-DTpMHS4F.js +67 -0
- package/gui/dist/assets/index-ZNVDE3C7.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/claude.svg +1 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/copilot.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/cursor.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/gemini.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/kiro.svg +14 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +1 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +102 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic.ts +1003 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +72 -0
- package/src/adapters/client-fingerprint.ts +59 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/cursor-errors.ts +165 -0
- package/src/adapters/cursor/discovery.ts +276 -0
- package/src/adapters/cursor/effort-map.ts +127 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +211 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +153 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1214 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +55 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +329 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +548 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +576 -0
- package/src/adapters/cursor/protobuf-events.ts +563 -0
- package/src/adapters/cursor/protobuf-request.ts +714 -0
- package/src/adapters/cursor/request-builder.ts +255 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +505 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +52 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +303 -0
- package/src/adapters/google-antigravity-wire.ts +108 -0
- package/src/adapters/google-errors.ts +85 -0
- package/src/adapters/google-http.ts +100 -0
- package/src/adapters/google-tool-schema.ts +173 -0
- package/src/adapters/google-truncation.ts +13 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +758 -0
- package/src/adapters/identity.ts +44 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +197 -0
- package/src/adapters/kiro-events.ts +179 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +96 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +215 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1898 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1005 -0
- package/src/adapters/openai-responses.ts +1137 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-catalog-nudge.ts +71 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/bridge.ts +1619 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +765 -0
- package/src/claude/agents-inject.ts +243 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +189 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +381 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +70 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +509 -0
- package/src/claude/model-info.ts +151 -0
- package/src/claude/outbound.ts +872 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +268 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account-extended.ts +350 -0
- package/src/cli/account.ts +275 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +184 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-desktop.ts +188 -0
- package/src/cli/claude.ts +286 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/doctor.ts +930 -0
- package/src/cli/export-command.ts +187 -0
- package/src/cli/help.ts +354 -0
- package/src/cli/index.ts +1113 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +142 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +20 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +336 -0
- package/src/cli/observe.ts +117 -0
- package/src/cli/opencode.ts +586 -0
- package/src/cli/pi.ts +188 -0
- package/src/cli/provider-runtime.ts +162 -0
- package/src/cli/provider.ts +463 -0
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +155 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/sync-cloud.ts +283 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/tray-proxy.ts +52 -0
- package/src/cli/v2.ts +173 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +377 -0
- package/src/clients/effective-status.ts +385 -0
- package/src/clients/probes/agy.ts +55 -0
- package/src/clients/probes/cc-switch.ts +110 -0
- package/src/clients/probes/claude.ts +90 -0
- package/src/clients/probes/codex.ts +125 -0
- package/src/clients/probes/grok.ts +29 -0
- package/src/clients/probes/opencode.ts +109 -0
- package/src/clients/probes/paseo.ts +55 -0
- package/src/clients/probes/pi.ts +55 -0
- package/src/cloud/onedrive-auth.ts +666 -0
- package/src/cloud/onedrive-graph.ts +108 -0
- package/src/cloud/settings.ts +75 -0
- package/src/cloud/sync.ts +212 -0
- package/src/cloud/types.ts +56 -0
- package/src/cloud/vault.ts +89 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -0
- package/src/codex/account-lifecycle.ts +55 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +149 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +517 -0
- package/src/codex/account-usability.ts +20 -0
- package/src/codex/app-server-processes.ts +756 -0
- package/src/codex/auth-api.ts +1540 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +352 -0
- package/src/codex/autostart-health.ts +149 -0
- package/src/codex/catalog/aggregation.ts +378 -0
- package/src/codex/catalog/bundled.ts +251 -0
- package/src/codex/catalog/effort.ts +355 -0
- package/src/codex/catalog/metadata.ts +180 -0
- package/src/codex/catalog/parsing.ts +456 -0
- package/src/codex/catalog/provider-fetch.ts +902 -0
- package/src/codex/catalog/sync.ts +620 -0
- package/src/codex/catalog.ts +12 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +969 -0
- package/src/codex/history-migration-guardian.ts +102 -0
- package/src/codex/history-provider.ts +776 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject.ts +799 -0
- package/src/codex/injected-marker.ts +72 -0
- package/src/codex/journal.ts +163 -0
- package/src/codex/main-account-cache.ts +32 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/model-cache.ts +227 -0
- package/src/codex/paths.ts +65 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +225 -0
- package/src/codex/project-config-warnings.ts +411 -0
- package/src/codex/quota.ts +411 -0
- package/src/codex/refresh.ts +53 -0
- package/src/codex/routing.ts +1477 -0
- package/src/codex/runtime.ts +538 -0
- package/src/codex/shim.ts +1189 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +469 -0
- package/src/codex/sync.ts +130 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +41 -0
- package/src/combos/request.ts +62 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +326 -0
- package/src/config.ts +2356 -0
- package/src/generated/jawcode-model-metadata.ts +104 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +545 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +103 -0
- package/src/grok/usage-hook/report.mjs +348 -0
- package/src/grok/usage-hook.ts +278 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +829 -0
- package/src/images/plan.ts +133 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +202 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +71 -0
- package/src/lib/bun-stream-caps.ts +126 -0
- package/src/lib/config-ownership.ts +360 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +316 -0
- package/src/lib/errors.ts +364 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +151 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +165 -0
- package/src/lib/provider-outbound.ts +170 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +105 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +30 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +323 -0
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +356 -0
- package/src/lib/upstream-retry.ts +239 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-elevation.ts +705 -0
- package/src/lib/windows-secret-acl.ts +514 -0
- package/src/lib/winsw.ts +375 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +177 -0
- package/src/oauth/callback-server.ts +294 -0
- package/src/oauth/chatgpt.ts +150 -0
- package/src/oauth/cursor.ts +211 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +399 -0
- package/src/oauth/index.ts +1174 -0
- package/src/oauth/key-providers.ts +108 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +577 -0
- package/src/oauth/local-token-detect.ts +121 -0
- package/src/oauth/log.ts +48 -0
- package/src/oauth/login-cli.ts +163 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +630 -0
- package/src/oauth/token-guardian.ts +303 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/pi/extensions.ts +72 -0
- package/src/pi/home.ts +42 -0
- package/src/pi/index.ts +40 -0
- package/src/pi/models.ts +278 -0
- package/src/pi/packages.ts +219 -0
- package/src/pi/settings.ts +365 -0
- package/src/pi/status.ts +68 -0
- package/src/pi/sync.ts +75 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +205 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/context-cap.ts +65 -0
- package/src/providers/derive.ts +339 -0
- package/src/providers/free-directory.ts +184 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/key-failover.ts +203 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery.ts +356 -0
- package/src/providers/openai-sidecar.ts +175 -0
- package/src/providers/openai-tier-startup.ts +27 -0
- package/src/providers/openai-tiers.ts +301 -0
- package/src/providers/openai-virtual-models.ts +82 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +150 -0
- package/src/providers/quota.ts +1260 -0
- package/src/providers/registry.ts +1600 -0
- package/src/providers/slug-codec.ts +67 -0
- package/src/providers/xai-transport.ts +141 -0
- package/src/reasoning-effort.ts +135 -0
- package/src/responses/compaction.ts +117 -0
- package/src/responses/parser.ts +656 -0
- package/src/responses/reasoning-envelope.ts +52 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +394 -0
- package/src/responses/state.ts +895 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +425 -0
- package/src/server/adapter-resolve.ts +80 -0
- package/src/server/auth-cors.ts +530 -0
- package/src/server/chat-completions.ts +368 -0
- package/src/server/claude-messages.ts +914 -0
- package/src/server/effort-policy.ts +172 -0
- package/src/server/gui-static.ts +123 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +476 -0
- package/src/server/index.ts +1126 -0
- package/src/server/lifecycle.ts +227 -0
- package/src/server/live.ts +598 -0
- package/src/server/management/agent-settings-routes.ts +1169 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/clients-routes.ts +63 -0
- package/src/server/management/cloud-sync-routes.ts +266 -0
- package/src/server/management/combo-routes.ts +220 -0
- package/src/server/management/config-routes.ts +422 -0
- package/src/server/management/context.ts +31 -0
- package/src/server/management/logs-usage-routes.ts +707 -0
- package/src/server/management/model-routes.ts +525 -0
- package/src/server/management/oauth-account-routes.ts +563 -0
- package/src/server/management/provider-routes.ts +556 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +90 -0
- package/src/server/management/system-restart.ts +179 -0
- package/src/server/management/system-routes.ts +117 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +215 -0
- package/src/server/management-auth.ts +267 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +116 -0
- package/src/server/proxy-liveness.ts +201 -0
- package/src/server/relay-eager.ts +313 -0
- package/src/server/relay.ts +1049 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1046 -0
- package/src/server/responses/collaboration.ts +354 -0
- package/src/server/responses/compact.ts +384 -0
- package/src/server/responses/core.ts +2758 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +157 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/terminal-guard.ts +230 -0
- package/src/server/responses/upstream-error.ts +48 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +224 -0
- package/src/server/responses.ts +9 -0
- package/src/server/search.ts +136 -0
- package/src/server/sse-payload-rewrite.ts +175 -0
- package/src/server/startup-action-control.ts +308 -0
- package/src/server/startup-health-cache.ts +113 -0
- package/src/server/system-env.ts +413 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +471 -0
- package/src/service.ts +2554 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +290 -0
- package/src/tray/windows.ts +730 -0
- package/src/types.ts +1237 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +407 -0
- package/src/update/job.ts +1520 -0
- package/src/update/notify.ts +257 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +164 -0
- package/src/usage/log.ts +658 -0
- package/src/usage/summary.ts +585 -0
- package/src/usage/totals.ts +14 -0
- package/src/vision/anthropic-describe.ts +185 -0
- package/src/vision/describe.ts +125 -0
- package/src/vision/index.ts +467 -0
- package/src/web-search/anthropic-executor.ts +189 -0
- package/src/web-search/executor.ts +105 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/index.ts +196 -0
- package/src/web-search/loop.ts +664 -0
- package/src/web-search/parse.ts +220 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
package/src/grok/sync.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Grok Build config sync: gather the visible model catalog and (re)inject the
|
|
3
|
+
* managed block into ~/.grok/config.toml. Used by `ocx start` (server process) and by
|
|
4
|
+
* `ocx ensure` / `ocx restart` (parent process, after live discovery or child readiness)
|
|
5
|
+
* so the fence exists deterministically once the proxy reports healthy.
|
|
6
|
+
*
|
|
7
|
+
* Deps are injectable (mirrors src/codex/sync.ts) so tests can run without a live proxy.
|
|
8
|
+
*/
|
|
9
|
+
import { visibleNativeSlugs, filterCatalogVisibleModels, nativeOpenAiContextWindow, type CatalogModel } from "../codex/catalog";
|
|
10
|
+
import type { OcxConfig } from "../types";
|
|
11
|
+
import { injectGrokConfig, type GrokInjectModel, type GrokInjectResult } from "./inject";
|
|
12
|
+
import { installGrokUsageHooks, type GrokUsageHookResult } from "./usage-hook";
|
|
13
|
+
|
|
14
|
+
export interface GrokSyncDeps {
|
|
15
|
+
fetchAllModels: (config: OcxConfig) => Promise<CatalogModel[]>;
|
|
16
|
+
injectGrokConfig: typeof injectGrokConfig;
|
|
17
|
+
installGrokUsageHooks?: (opts?: { grokHome?: string }) => GrokUsageHookResult;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function defaultFetchAllModels(config: OcxConfig): Promise<CatalogModel[]> {
|
|
21
|
+
const { fetchAllModels } = await import("../server/management-api");
|
|
22
|
+
return fetchAllModels(config);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build the model list and inject the fenced block. `hostname` should be the hostname the
|
|
27
|
+
* RUNNING proxy actually bound (live.hostname from proxy-liveness for ensure's live branch;
|
|
28
|
+
* config.hostname for a freshly spawned start) — a stale config.hostname could otherwise
|
|
29
|
+
* name a host the process never bound.
|
|
30
|
+
*/
|
|
31
|
+
export async function syncGrokConfig(
|
|
32
|
+
port: number,
|
|
33
|
+
config: OcxConfig,
|
|
34
|
+
opts: { hostname?: string; grokHome?: string } = {},
|
|
35
|
+
deps: GrokSyncDeps = { fetchAllModels: defaultFetchAllModels, injectGrokConfig },
|
|
36
|
+
): Promise<GrokInjectResult> {
|
|
37
|
+
let models: GrokInjectModel[];
|
|
38
|
+
try {
|
|
39
|
+
const routed = filterCatalogVisibleModels(await deps.fetchAllModels(config), config);
|
|
40
|
+
models = [
|
|
41
|
+
// Native slugs carry their context window too. Without it Grok falls back to its own
|
|
42
|
+
// default (200k) and understates models like gpt-5.6-sol, which is 372k. This is the same
|
|
43
|
+
// accessor the dashboard's native rows use, so the two cannot disagree.
|
|
44
|
+
...visibleNativeSlugs(config).map(id => {
|
|
45
|
+
const contextWindow = nativeOpenAiContextWindow(id);
|
|
46
|
+
return { id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
47
|
+
}),
|
|
48
|
+
...routed.map(m => ({
|
|
49
|
+
id: m.alias ?? `${m.provider}/${m.id}`,
|
|
50
|
+
...(m.contextWindow !== undefined ? { contextWindow: m.contextWindow } : {}),
|
|
51
|
+
})),
|
|
52
|
+
];
|
|
53
|
+
} catch (err) {
|
|
54
|
+
// Still try to install the usage hook — native-session reporting does not need the catalog.
|
|
55
|
+
const hookResult = runInstallHooks(deps, opts);
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
changed: hookResult.changed,
|
|
59
|
+
message: [
|
|
60
|
+
`Grok config sync skipped: model catalog unavailable (${err instanceof Error ? err.message : String(err)})`,
|
|
61
|
+
hookResult.message,
|
|
62
|
+
].filter(Boolean).join(" "),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Pass the FULL list plus the exclusion set: the writer allocates aliases over
|
|
66
|
+
// everything and emits only what is switched on, so a model's alias never depends on
|
|
67
|
+
// its neighbours' switches. Absent/empty selection keeps today's behaviour exactly.
|
|
68
|
+
const injectResult = deps.injectGrokConfig(port, models, {
|
|
69
|
+
...(opts.hostname !== undefined ? { hostname: opts.hostname } : {}),
|
|
70
|
+
...(opts.grokHome !== undefined ? { grokHome: opts.grokHome } : {}),
|
|
71
|
+
excluded: new Set(config.grokExcludedModels ?? []),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Always (re)install the native-usage hook when a Grok home exists — independent of the
|
|
75
|
+
// model fence (which is skipped on non-loopback binds). Best-effort; never fails the fence.
|
|
76
|
+
const hookResult = runInstallHooks(deps, opts);
|
|
77
|
+
|
|
78
|
+
if (!hookResult.changed && hookResult.ok) return injectResult;
|
|
79
|
+
const parts = [injectResult.message, hookResult.message].filter(Boolean);
|
|
80
|
+
return {
|
|
81
|
+
ok: injectResult.ok && hookResult.ok,
|
|
82
|
+
changed: injectResult.changed || hookResult.changed,
|
|
83
|
+
message: parts.join(" "),
|
|
84
|
+
...(injectResult.skippedReason ? { skippedReason: injectResult.skippedReason } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function runInstallHooks(
|
|
89
|
+
deps: GrokSyncDeps,
|
|
90
|
+
opts: { grokHome?: string },
|
|
91
|
+
): GrokUsageHookResult {
|
|
92
|
+
const installHooks = deps.installGrokUsageHooks ?? installGrokUsageHooks;
|
|
93
|
+
try {
|
|
94
|
+
return installHooks(opts.grokHome !== undefined ? { grokHome: opts.grokHome } : {});
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
changed: false,
|
|
99
|
+
message: `Grok usage hook install failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
100
|
+
homes: [],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Grok Build hook → opencodex usage + live Logs
|
|
4
|
+
*
|
|
5
|
+
* On Stop / SessionEnd, reads Grok's on-disk turn_completed usage (real token
|
|
6
|
+
* counts from the native session) and reports them to opencodex:
|
|
7
|
+
*
|
|
8
|
+
* 1. POST /api/usage/ingest → usage.jsonl + live Logs ring (preferred)
|
|
9
|
+
* 2. Fallback: append usage.jsonl only (if ocx is down or old)
|
|
10
|
+
*
|
|
11
|
+
* Skips ocx-routed / non-native models so proxy traffic is not double-counted.
|
|
12
|
+
* Fail-open: any error exits 0 so Grok is never blocked.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
appendFileSync,
|
|
16
|
+
existsSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
readdirSync,
|
|
20
|
+
writeFileSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
import { homedir } from "node:os";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
|
|
26
|
+
const OPENCODEX_DIR = process.env.OPENCODEX_HOME || join(homedir(), ".opencodex");
|
|
27
|
+
const USAGE_LOG = join(OPENCODEX_DIR, "usage.jsonl");
|
|
28
|
+
const STATE_PATH = join(OPENCODEX_DIR, "grok-native-usage-state.json");
|
|
29
|
+
/** Prefer env, but always also search the canonical ~/.grok (sessions often live there). */
|
|
30
|
+
function grokHomes() {
|
|
31
|
+
const homes = [];
|
|
32
|
+
if (process.env.GROK_HOME) homes.push(process.env.GROK_HOME);
|
|
33
|
+
const canonical = join(homedir(), ".grok");
|
|
34
|
+
if (!homes.includes(canonical)) homes.push(canonical);
|
|
35
|
+
return homes;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function failOpen(err) {
|
|
39
|
+
try {
|
|
40
|
+
const logDir = join(OPENCODEX_DIR, "logs");
|
|
41
|
+
mkdirSync(logDir, { recursive: true });
|
|
42
|
+
appendFileSync(
|
|
43
|
+
join(logDir, "grok-usage-hook.log"),
|
|
44
|
+
`${new Date().toISOString()} ${err?.stack || err?.message || String(err)}\n`,
|
|
45
|
+
);
|
|
46
|
+
} catch {
|
|
47
|
+
/* ignore */
|
|
48
|
+
}
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readStdinJson() {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
55
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
56
|
+
if (!raw) return null;
|
|
57
|
+
return JSON.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function loadState() {
|
|
61
|
+
try {
|
|
62
|
+
if (!existsSync(STATE_PATH)) return { seen: {} };
|
|
63
|
+
const parsed = JSON.parse(readFileSync(STATE_PATH, "utf8"));
|
|
64
|
+
if (!parsed || typeof parsed !== "object") return { seen: {} };
|
|
65
|
+
if (!parsed.seen || typeof parsed.seen !== "object") return { seen: {} };
|
|
66
|
+
return parsed;
|
|
67
|
+
} catch {
|
|
68
|
+
return { seen: {} };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function saveState(state) {
|
|
73
|
+
mkdirSync(OPENCODEX_DIR, { recursive: true, mode: 0o700 });
|
|
74
|
+
writeFileSync(STATE_PATH, `${JSON.stringify(state)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Native xAI Grok models only — anything with a provider slash is routed elsewhere. */
|
|
78
|
+
function isNativeGrokModel(modelId) {
|
|
79
|
+
if (typeof modelId !== "string" || !modelId) return false;
|
|
80
|
+
if (modelId.includes("/")) return false;
|
|
81
|
+
if (modelId.startsWith("ocx-")) return false;
|
|
82
|
+
return /^(grok\b|grok[-_])/i.test(modelId);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function encodeSessionCwd(cwd) {
|
|
86
|
+
// Grok stores sessions under encodeURIComponent(cwd) (slashes → %2F).
|
|
87
|
+
return encodeURIComponent(cwd);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function findSessionDir(sessionId, cwd, workspaceRoot) {
|
|
91
|
+
if (!sessionId) return null;
|
|
92
|
+
const candidates = [];
|
|
93
|
+
for (const home of grokHomes()) {
|
|
94
|
+
const sessionsRoot = join(home, "sessions");
|
|
95
|
+
for (const base of [cwd, workspaceRoot]) {
|
|
96
|
+
if (typeof base === "string" && base) {
|
|
97
|
+
candidates.push(join(sessionsRoot, encodeSessionCwd(base), sessionId));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const dir of candidates) {
|
|
102
|
+
if (existsSync(join(dir, "updates.jsonl"))) return dir;
|
|
103
|
+
}
|
|
104
|
+
// Fallback: scan session groups (encoded cwd dirs) for this session id.
|
|
105
|
+
for (const home of grokHomes()) {
|
|
106
|
+
const sessionsRoot = join(home, "sessions");
|
|
107
|
+
try {
|
|
108
|
+
for (const group of readdirSync(sessionsRoot, { withFileTypes: true })) {
|
|
109
|
+
if (!group.isDirectory()) continue;
|
|
110
|
+
const dir = join(sessionsRoot, group.name, sessionId);
|
|
111
|
+
if (existsSync(join(dir, "updates.jsonl"))) return dir;
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
/* ignore */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function readSummaryModel(sessionDir) {
|
|
121
|
+
try {
|
|
122
|
+
const summary = JSON.parse(readFileSync(join(sessionDir, "summary.json"), "utf8"));
|
|
123
|
+
return summary?.current_model_id || summary?.info?.model || null;
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parseTurnCompleted(line) {
|
|
130
|
+
let obj;
|
|
131
|
+
try {
|
|
132
|
+
obj = JSON.parse(line);
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
const update = obj?.params?.update ?? obj?.update ?? obj;
|
|
137
|
+
if (!update || update.sessionUpdate !== "turn_completed") return null;
|
|
138
|
+
const usage = update.usage;
|
|
139
|
+
if (!usage || typeof usage !== "object") return null;
|
|
140
|
+
const promptId = update.prompt_id || update.promptId || obj?.params?.prompt_id;
|
|
141
|
+
if (!promptId) return null;
|
|
142
|
+
return {
|
|
143
|
+
promptId: String(promptId),
|
|
144
|
+
stopReason: update.stop_reason || update.stopReason || "",
|
|
145
|
+
usage,
|
|
146
|
+
timestampMs: typeof obj.timestamp === "number"
|
|
147
|
+
? (obj.timestamp > 1e12 ? obj.timestamp : obj.timestamp * 1000)
|
|
148
|
+
: Date.now(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function primaryModelFromUsage(usage, fallback) {
|
|
153
|
+
const modelUsage = usage?.modelUsage;
|
|
154
|
+
if (modelUsage && typeof modelUsage === "object") {
|
|
155
|
+
const keys = Object.keys(modelUsage);
|
|
156
|
+
if (keys.length === 1) return keys[0];
|
|
157
|
+
// Prefer a native grok key when multiple are present.
|
|
158
|
+
const native = keys.find(isNativeGrokModel);
|
|
159
|
+
if (native) return native;
|
|
160
|
+
if (keys[0]) return keys[0];
|
|
161
|
+
}
|
|
162
|
+
return fallback || "grok-build";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function toOcxUsage(usage) {
|
|
166
|
+
const inputTokens = Number(usage.inputTokens) || 0;
|
|
167
|
+
const outputTokens = Number(usage.outputTokens) || 0;
|
|
168
|
+
const totalTokens = Number(usage.totalTokens) || inputTokens + outputTokens;
|
|
169
|
+
const cached = Number(usage.cachedReadTokens ?? usage.cachedInputTokens) || 0;
|
|
170
|
+
const reasoning = Number(usage.reasoningTokens ?? usage.reasoningOutputTokens) || 0;
|
|
171
|
+
return {
|
|
172
|
+
inputTokens,
|
|
173
|
+
outputTokens,
|
|
174
|
+
totalTokens,
|
|
175
|
+
cachedInputTokens: cached,
|
|
176
|
+
cacheReadInputTokens: cached,
|
|
177
|
+
cacheCreationInputTokens: 0,
|
|
178
|
+
reasoningOutputTokens: reasoning,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function conversationId(sessionId) {
|
|
183
|
+
// Match opencodex's non-PII correlation style: short hash, not the raw id if sensitive.
|
|
184
|
+
return createHash("sha256").update(String(sessionId)).digest("hex").slice(0, 32);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function appendUsageEntry(entry) {
|
|
188
|
+
mkdirSync(OPENCODEX_DIR, { recursive: true, mode: 0o700 });
|
|
189
|
+
appendFileSync(USAGE_LOG, `${JSON.stringify(entry)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function resolveOpencodexBaseUrl() {
|
|
193
|
+
if (process.env.OPENCODEX_URL?.trim()) {
|
|
194
|
+
return process.env.OPENCODEX_URL.trim().replace(/\/$/, "");
|
|
195
|
+
}
|
|
196
|
+
let port = 10100;
|
|
197
|
+
try {
|
|
198
|
+
const runtime = JSON.parse(readFileSync(join(OPENCODEX_DIR, "runtime-port.json"), "utf8"));
|
|
199
|
+
if (typeof runtime?.port === "number" && runtime.port > 0) port = runtime.port;
|
|
200
|
+
} catch {
|
|
201
|
+
/* default */
|
|
202
|
+
}
|
|
203
|
+
return `http://127.0.0.1:${port}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function readAdminToken() {
|
|
207
|
+
if (process.env.OPENCODEX_ADMIN_TOKEN?.trim()) return process.env.OPENCODEX_ADMIN_TOKEN.trim();
|
|
208
|
+
try {
|
|
209
|
+
return readFileSync(join(OPENCODEX_DIR, "admin-api-token"), "utf8").trim();
|
|
210
|
+
} catch {
|
|
211
|
+
return "";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Push entries into the running opencodex process (usage.jsonl + live Logs).
|
|
217
|
+
* Returns true on success (including all-skipped duplicates), false to fall back.
|
|
218
|
+
*/
|
|
219
|
+
async function ingestViaApi(entries) {
|
|
220
|
+
const token = readAdminToken();
|
|
221
|
+
if (!token) return false;
|
|
222
|
+
const base = resolveOpencodexBaseUrl();
|
|
223
|
+
const url = `${base}/api/usage/ingest`;
|
|
224
|
+
try {
|
|
225
|
+
const res = await fetch(url, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
headers: {
|
|
228
|
+
"content-type": "application/json",
|
|
229
|
+
"x-opencodex-api-key": token,
|
|
230
|
+
},
|
|
231
|
+
body: JSON.stringify({ entries }),
|
|
232
|
+
signal: AbortSignal.timeout(8000),
|
|
233
|
+
});
|
|
234
|
+
// 404 = old ocx without ingest; connection errors fall to catch.
|
|
235
|
+
if (res.status === 404) return false;
|
|
236
|
+
if (!res.ok && res.status !== 207) return false;
|
|
237
|
+
return true;
|
|
238
|
+
} catch {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function toIngestEntry(turn, conv) {
|
|
244
|
+
return {
|
|
245
|
+
requestId: `grok-native-${turn.promptId}`,
|
|
246
|
+
timestamp: turn.timestamp,
|
|
247
|
+
provider: "xai-native",
|
|
248
|
+
model: turn.model,
|
|
249
|
+
surface: "grok",
|
|
250
|
+
conversationId: conv,
|
|
251
|
+
requestedModel: turn.model,
|
|
252
|
+
resolvedModel: turn.model,
|
|
253
|
+
status: 200,
|
|
254
|
+
durationMs: turn.durationMs,
|
|
255
|
+
usageStatus: "reported",
|
|
256
|
+
usage: turn.usage,
|
|
257
|
+
totalTokens: turn.usage.totalTokens,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function collectNewTurns(sessionDir, sessionId, state) {
|
|
262
|
+
const updatesPath = join(sessionDir, "updates.jsonl");
|
|
263
|
+
if (!existsSync(updatesPath)) return [];
|
|
264
|
+
|
|
265
|
+
const seenKey = sessionId;
|
|
266
|
+
const seen = new Set(
|
|
267
|
+
Array.isArray(state.seen[seenKey]) ? state.seen[seenKey] : [],
|
|
268
|
+
);
|
|
269
|
+
const summaryModel = readSummaryModel(sessionDir);
|
|
270
|
+
const fresh = [];
|
|
271
|
+
|
|
272
|
+
const text = readFileSync(updatesPath, "utf8");
|
|
273
|
+
for (const line of text.split("\n")) {
|
|
274
|
+
if (!line.includes("turn_completed")) continue;
|
|
275
|
+
const turn = parseTurnCompleted(line);
|
|
276
|
+
if (!turn) continue;
|
|
277
|
+
if (seen.has(turn.promptId)) continue;
|
|
278
|
+
|
|
279
|
+
const model = primaryModelFromUsage(turn.usage, summaryModel);
|
|
280
|
+
if (!isNativeGrokModel(model)) {
|
|
281
|
+
// Still mark seen so we don't re-scan forever on ocx turns.
|
|
282
|
+
seen.add(turn.promptId);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const ocxUsage = toOcxUsage(turn.usage);
|
|
287
|
+
if (ocxUsage.totalTokens <= 0 && ocxUsage.inputTokens <= 0 && ocxUsage.outputTokens <= 0) {
|
|
288
|
+
seen.add(turn.promptId);
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
fresh.push({
|
|
293
|
+
promptId: turn.promptId,
|
|
294
|
+
model,
|
|
295
|
+
timestamp: turn.timestampMs,
|
|
296
|
+
durationMs: Number(turn.usage.apiDurationMs) || 0,
|
|
297
|
+
usage: ocxUsage,
|
|
298
|
+
});
|
|
299
|
+
seen.add(turn.promptId);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Cap seen set growth per session.
|
|
303
|
+
const seenList = [...seen];
|
|
304
|
+
state.seen[seenKey] = seenList.length > 500 ? seenList.slice(-500) : seenList;
|
|
305
|
+
return fresh;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function main() {
|
|
309
|
+
const input = await readStdinJson();
|
|
310
|
+
if (!input || typeof input !== "object") process.exit(0);
|
|
311
|
+
|
|
312
|
+
const event = String(input.hookEventName || input.hook_event_name || "").toLowerCase();
|
|
313
|
+
// Accept stop / session_end (and camelCase variants already lowercased).
|
|
314
|
+
if (event && event !== "stop" && event !== "sessionend" && event !== "session_end") {
|
|
315
|
+
process.exit(0);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const sessionId = input.sessionId || input.session_id;
|
|
319
|
+
if (!sessionId) process.exit(0);
|
|
320
|
+
|
|
321
|
+
const sessionDir = findSessionDir(
|
|
322
|
+
sessionId,
|
|
323
|
+
input.cwd,
|
|
324
|
+
input.workspaceRoot || input.workspace_root,
|
|
325
|
+
);
|
|
326
|
+
if (!sessionDir) process.exit(0);
|
|
327
|
+
|
|
328
|
+
const state = loadState();
|
|
329
|
+
const turns = collectNewTurns(sessionDir, sessionId, state);
|
|
330
|
+
if (turns.length === 0) {
|
|
331
|
+
saveState(state);
|
|
332
|
+
process.exit(0);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const conv = conversationId(sessionId);
|
|
336
|
+
const entries = turns.map(turn => toIngestEntry(turn, conv));
|
|
337
|
+
|
|
338
|
+
// Prefer live ingest so Dashboard Logs updates immediately; fall back to file-only.
|
|
339
|
+
const viaApi = await ingestViaApi(entries);
|
|
340
|
+
if (!viaApi) {
|
|
341
|
+
for (const entry of entries) appendUsageEntry(entry);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
saveState(state);
|
|
345
|
+
process.exit(0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
main().catch(failOpen);
|