@bitkyc08/opencodex 2.65.0 → 2.66.0-preview.20260925
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/README.md +14 -0
- package/gui/dist/assets/App-BVjA0T6O.js +51 -0
- package/gui/dist/assets/App-gkoJIEOV.css +1 -0
- package/gui/dist/assets/{Tray-D4bA1vff.js → Tray-BCFtiWwD.js} +1 -1
- package/gui/dist/assets/index-ComVsdSk.css +1 -0
- package/gui/dist/assets/index-vO4dgfiU.js +86 -0
- package/gui/dist/assets/{tray-data-BZXhB3qz.js → tray-data-C4FiNaxk.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -2
- package/src/adapters/anthropic/beta-allowlist.ts +80 -0
- package/src/adapters/anthropic/passthrough.ts +221 -0
- package/src/adapters/anthropic-image-codec.ts +56 -19
- package/src/adapters/anthropic-image-normalize.ts +20 -10
- package/src/adapters/anthropic.ts +55 -24
- package/src/adapters/coding-agent/protocol.ts +29 -19
- package/src/adapters/cursor/live-transport.ts +18 -3
- package/src/adapters/cursor/native-exec-shell.ts +18 -63
- package/src/adapters/cursor/native-exec.ts +5 -2
- package/src/adapters/cursor/native-foreground-shell.ts +44 -0
- package/src/adapters/devin/cloud-direct/chat.ts +7 -1
- package/src/adapters/exec-tool-result-normalize.ts +4 -1
- package/src/adapters/google.ts +12 -0
- package/src/adapters/ollama-native.ts +32 -1
- package/src/adapters/openai-chat/serialized-tool-call-content.ts +131 -34
- package/src/adapters/openai-chat.ts +6 -4
- package/src/adapters/openai-responses/passthrough.ts +1 -0
- package/src/adapters/openai-responses/reasoning.ts +45 -1
- package/src/bridge/response-json.ts +10 -2
- package/src/chat/outbound.ts +104 -52
- package/src/claude/claude-code-block.ts +16 -0
- package/src/claude/context-windows.ts +52 -4
- package/src/claude/desktop-first-party.ts +25 -8
- package/src/claude/inbound.ts +22 -0
- package/src/claude/intercept/connect-proxy.ts +37 -4
- package/src/claude/intercept/proxy-auth.ts +166 -0
- package/src/claude/intercept/runtime.ts +21 -0
- package/src/claude/intercept/settings.ts +33 -12
- package/src/claude/outbound.ts +45 -27
- package/src/cli/account-extended.ts +47 -15
- package/src/cli/account-target.ts +115 -0
- package/src/cli/account.ts +20 -28
- package/src/cli/agent.ts +10 -1
- package/src/cli/api-protocols.ts +206 -0
- package/src/cli/capabilities.ts +86 -0
- package/src/cli/combo.ts +2 -2
- package/src/cli/connect.ts +127 -3
- package/src/cli/dispatch.ts +8 -0
- package/src/cli/doctor.ts +33 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/link.ts +288 -0
- package/src/cli/registry.ts +23 -0
- package/src/cli/runtime-api.ts +79 -0
- package/src/cli/system-command.ts +21 -2
- package/src/client/connect.ts +125 -23
- package/src/client/hub-relay.ts +12 -8
- package/src/client/link-join.ts +323 -0
- package/src/client/link-relay.ts +241 -0
- package/src/client/link-state.ts +110 -0
- package/src/client/link-teardown.ts +63 -0
- package/src/client/link-tunnel.ts +465 -0
- package/src/client/machine-listener.ts +14 -5
- package/src/client/runtime.ts +91 -42
- package/src/client/state.ts +4 -0
- package/src/codex/catalog/build-entries.ts +4 -0
- package/src/codex/catalog/derive-entry.ts +5 -0
- package/src/codex/catalog/parsing.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +4 -1
- package/src/codex/desktop-switches.ts +88 -8
- package/src/codex/inject/plan.ts +16 -3
- package/src/codex/inject.ts +3 -0
- package/src/codex/internal/catalog-writer.ts +13 -2
- package/src/combos/failover.ts +3 -2
- package/src/combos/index.ts +12 -0
- package/src/combos/jev.ts +646 -0
- package/src/combos/request.ts +5 -0
- package/src/combos/resolve.ts +17 -15
- package/src/combos/types.ts +47 -3
- package/src/config/atomic-write.ts +3 -0
- package/src/config/live-reconcile.ts +20 -4
- package/src/config/persist-unlocked.ts +20 -7
- package/src/config/schema/config-schema.ts +16 -0
- package/src/config/schema/leaf-validators.ts +38 -1
- package/src/generated/compatibility-version.json +416 -128
- package/src/integrations/omp-yaml-source.ts +109 -4
- package/src/lab/automation/orchestrator.ts +60 -4
- package/src/lab/events/limits.ts +1 -1
- package/src/lib/bounded-body.ts +55 -38
- package/src/lib/config-ownership.ts +11 -22
- package/src/lib/local-desktop-snapshot-capability.ts +56 -0
- package/src/lib/local-management-capability.ts +25 -3
- package/src/lib/optional-shutdown-hooks.ts +17 -0
- package/src/lib/state-store-registrations.ts +2 -2
- package/src/lib/windows-elevation.ts +62 -25
- package/src/lib/windows-secret-acl.ts +114 -23
- package/src/link/admission-wait.ts +73 -0
- package/src/link/compensation.ts +100 -0
- package/src/link/fingerprint.ts +21 -0
- package/src/link/paths.ts +19 -0
- package/src/link/ports.ts +12 -0
- package/src/link/routes.ts +32 -0
- package/src/link/ssh-argv.ts +170 -0
- package/src/link/ssh-config.ts +152 -0
- package/src/link/ssh-runner.ts +161 -0
- package/src/link/status-projection.ts +96 -0
- package/src/link/store.ts +139 -0
- package/src/link/supervisor.ts +404 -0
- package/src/link/tunnel-state.ts +91 -0
- package/src/protocols/baseline.ts +88 -0
- package/src/protocols/codecs/chat.ts +17 -0
- package/src/protocols/codecs/messages.ts +18 -0
- package/src/protocols/codecs/responses.ts +17 -0
- package/src/protocols/contract.ts +185 -0
- package/src/protocols/dto.ts +267 -0
- package/src/protocols/encoders/adapter-events.ts +876 -0
- package/src/protocols/encoders/chat.ts +243 -0
- package/src/protocols/encoders/messages.ts +462 -0
- package/src/protocols/envelope.ts +43 -0
- package/src/protocols/features.ts +295 -0
- package/src/protocols/guard.ts +33 -0
- package/src/protocols/opaque-state.ts +132 -0
- package/src/protocols/path.ts +39 -0
- package/src/protocols/plan-snapshot.ts +226 -0
- package/src/protocols/plan.ts +192 -0
- package/src/protocols/provider-summary.ts +101 -0
- package/src/protocols/settings.ts +118 -0
- package/src/protocols/shadow-plan.ts +36 -0
- package/src/protocols/shadow.ts +62 -0
- package/src/protocols/trace.ts +335 -0
- package/src/providers/model-rename-migration.ts +3 -3
- package/src/providers/registry/entries-core.ts +6 -1
- package/src/providers/registry/entries-extended.ts +15 -0
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/types.ts +6 -0
- package/src/providers/registry.ts +1 -1
- package/src/remote-control/workspace-hub.ts +5 -4
- package/src/responses/apply-patch-envelope.ts +6 -1
- package/src/responses/code-mode-shell-input.ts +4 -2
- package/src/responses/freeform-wrapper-scan.ts +51 -24
- package/src/responses/progressive-freeform-input.ts +14 -20
- package/src/responses/spill-store.ts +187 -33
- package/src/responses/state/snapshot-select.ts +49 -0
- package/src/responses/state/spill-inspect.ts +46 -0
- package/src/responses/state/spill-queue.ts +16 -0
- package/src/responses/state.ts +51 -27
- package/src/server/audio-client.ts +8 -3
- package/src/server/audio-upstream.ts +9 -4
- package/src/server/auth-cors.ts +25 -8
- package/src/server/chat-completions.ts +71 -11
- package/src/server/chat-native-eligibility.ts +74 -0
- package/src/server/chat-native.ts +139 -74
- package/src/server/claude-messages.ts +140 -22
- package/src/server/hub-usage.ts +4 -3
- package/src/server/index/link-listener.ts +195 -0
- package/src/server/index/optional-listeners.ts +115 -0
- package/src/server/index/serve-options.ts +44 -10
- package/src/server/index.ts +10 -9
- package/src/server/inference/attempt.ts +51 -0
- package/src/server/inference/client-encoder-delivery.ts +235 -0
- package/src/server/inference/client-wire-log.ts +49 -0
- package/src/server/inference/client-wire.ts +69 -0
- package/src/server/inference/context.ts +15 -0
- package/src/server/inference/final-log.ts +37 -0
- package/src/server/local-desktop-snapshot-auth.ts +57 -0
- package/src/server/management/agent-settings-routes.ts +15 -13
- package/src/server/management/api-access.ts +11 -1
- package/src/server/management/config-routes.ts +3 -6
- package/src/server/management/context.ts +25 -0
- package/src/server/management/link-routes.ts +535 -0
- package/src/server/management/logs-usage-routes.ts +31 -1
- package/src/server/management/native-integration-routes.ts +6 -11
- package/src/server/management/oauth-account-routes.ts +42 -16
- package/src/server/management/protocol-routes.ts +159 -0
- package/src/server/management/protocol-settings-patch.ts +145 -0
- package/src/server/management/provider-patch-transaction.ts +61 -0
- package/src/server/management/provider-routes.ts +39 -18
- package/src/server/management/route-registry.ts +12 -0
- package/src/server/management/sidebar-routes.ts +8 -3
- package/src/server/management/system-routes.ts +18 -1
- package/src/server/management/usage-aggregate-cache.ts +206 -6
- package/src/server/management-api.ts +26 -1
- package/src/server/management-auth.ts +19 -5
- package/src/server/messages-native-eligibility.ts +161 -0
- package/src/server/messages-native-oauth.ts +149 -0
- package/src/server/messages-native.ts +790 -0
- package/src/server/relay.ts +9 -0
- package/src/server/request-log-filter.ts +92 -0
- package/src/server/request-log.ts +31 -70
- package/src/server/responses/adapter-delivery.ts +43 -16
- package/src/server/responses/agent-task-recovery.ts +90 -22
- package/src/server/responses/codex-ws-exchange.ts +4 -0
- package/src/server/responses/codex-ws-wire.ts +10 -1
- package/src/server/responses/core-combo-native.ts +323 -0
- package/src/server/responses/core-combo.ts +224 -13
- package/src/server/responses/core-options.ts +22 -0
- package/src/server/responses/core.ts +14 -26
- package/src/server/responses/request-sidecar-auth.ts +8 -3
- package/src/server/responses/run-turn-execution.ts +257 -63
- package/src/server/responses/sidecar-execution.ts +7 -4
- package/src/server/system-env.ts +7 -0
- package/src/service/diagnostics.ts +7 -1
- package/src/service/launchd.ts +26 -37
- package/src/service/windows-ops.ts +24 -20
- package/src/types/config.ts +58 -1
- package/src/types.ts +2 -0
- package/src/update/transactional-install.d.mts +0 -1
- package/src/update/transactional-install.mjs +17 -17
- package/src/usage/jev-stats.ts +495 -0
- package/src/usage/log.ts +24 -3
- package/src/web-search/run-turn-loop.ts +568 -0
- package/gui/dist/assets/App-8NMiZxT0.css +0 -1
- package/gui/dist/assets/App-DGLte6IR.js +0 -50
- package/gui/dist/assets/index--EWgGQvZ.css +0 -1
- package/gui/dist/assets/index-Bi1K37Wl.js +0 -86
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,n as t}from"./index-Bi1K37Wl.js";var n={anthropic:`claude-color.svg`,"anthropic-apikey":`claude-color.svg`,"claude-cli":`claude-color.svg`,"azure-openai":`openai.svg`,chatgpt:`openai.svg`,"cloudflare-ai-gateway":`cloudflare-ai-gateway-color.svg`,"cloudflare-workers-ai":`cloudflare-ai-gateway-color.svg`,cline:`cline-color.svg`,"cline-pass":`cline-color.svg`,"command-code":`commandcode-color.svg`,commandcode:`commandcode-color.svg`,cursor:`cursor-color.svg`,deepseek:`deepseek-color.svg`,devin:`devin.svg`,firepass:`firepass-color.svg`,fireworks:`fireworks-color.svg`,github:`github-copilot-color.svg`,"github-copilot":`copilot-color.svg`,"gitlab-duo":`gitlab-duo-color.svg`,google:`gemini-color.svg`,"google-antigravity":`antigravity-color.svg`,"google-vertex":`gemini-color.svg`,groq:`groq-color.svg`,huggingface:`huggingface-color.svg`,kimi:`kimi-color.svg`,"kimi-code":`kimi-color.svg`,"kimi-responses":`kimi-color.svg`,kiro:`kiro-color.svg`,"lm-studio":`lm-studio-color.svg`,"meta-model":`meta.svg`,"meta-muse":`meta.svg`,mistral:`mistral-color.svg`,minimax:`minimax.svg`,"minimax-cn":`minimax.svg`,moonshot:`moonshot-color.svg`,nvidia:`nvidia-color.svg`,ollama:`ollama-color.svg`,"ollama-cloud":`ollama-color.svg`,openai:`openai.svg`,"openai-apikey":`openai.svg`,"opencode-free":`opencode.svg`,"opencode-go":`opencode.svg`,"opencode-zen":`opencode.svg`,openrouter:`openrouter-color.svg`,opper:`opper.svg`,qianfan:`qianfan-color.svg`,qoder:`qoder.svg`,"qoder-cn":`qoder.svg`,alibaba:`alibaba-color.svg`,"alibaba-token-plan":`alibaba-color.svg`,"alibaba-token-plan-intl":`alibaba-color.svg`,baseten:`baseten.svg`,bizrouter:`bizrouter.svg`,cerebras:`cerebras.svg`,crusoe:`crusoe.svg`,deepinfra:`deepinfra.svg`,digitalocean:`digitalocean.svg`,featherless:`featherless.svg`,hyperbolic:`hyperbolic.svg`,kilo:`kilo.svg`,nanogpt:`nanogpt.svg`,nebius:`nebius.svg`,neuralwatt:`neuralwatt.svg`,nous:`nous.svg`,novita:`novita.svg`,orcarouter:`orcarouter.svg`,"orcarouter-oauth":`orcarouter.svg`,packycode:`packycode.svg`,parallel:`parallel.svg`,sambanova:`sambanova.svg`,scaleway:`scaleway.svg`,stepfun:`stepfun-color.svg`,siliconflow:`siliconflow.svg`,synthetic:`synthetic.svg`,together:`together.svg`,umans:`umans.svg`,venice:`venice.svg`,vultr:`vultr.svg`,litellm:`litellm.svg`,zenmux:`zenmux.svg`,zai:`zai.svg`,"zhipu-bigmodel":`zai.svg`,"zhipu-bigmodel-coding":`zai.svg`,"qwen-cloud":`qwen-portal-color.svg`,"vercel-ai-gateway":`vercel-ai-gateway-color.svg`,vllm:`vllm-color.svg`,xai:`grok.svg`,"mimo-free":`xiaomi-color.svg`,mimo:`xiaomi-color.svg`,xiaomi:`xiaomi-color.svg`,"xiaomi-mimo":`xiaomi-color.svg`},r={anthropic:`Anthropic Claude`,"anthropic-apikey":`Anthropic Claude`,chatgpt:`ChatGPT`,openai:`OpenAI (Codex login)`,"openai-apikey":`OpenAI API`,"azure-openai":`Azure OpenAI`,"cloudflare-ai-gateway":`Cloudflare AI Gateway`,"cloudflare-workers-ai":`Cloudflare Workers AI`,cline:`Cline`,"cline-pass":`ClinePass`,nvidia:`NVIDIA NIM`,ollama:`Ollama`,"ollama-cloud":`Ollama Cloud`,xai:`xAI Grok`,"mimo-free":`MiMo Free`,xiaomi:`Xiaomi`,cursor:`Cursor`,deepseek:`DeepSeek`,devin:`Devin`,github:`GitHub`,"github-copilot":`GitHub Copilot`,"gitlab-duo":`GitLab Duo`,openrouter:`OpenRouter`,opper:`Opper`,"opencode-go":`OpenCode Go`,"opencode-free":`OpenCode Free`,"opencode-zen":`OpenCode Zen`,orcarouter:`OrcaRouter - API`,"orcarouter-oauth":`OrcaRouter - Auth`,packycode:`PackyCode`,mistral:`Mistral`,groq:`Groq`,"meta-model":`Meta Model API`,"meta-muse":`Muse Code`,alibaba:`Alibaba Coding Plan`,"alibaba-token-plan":`Alibaba Token Plan`,"alibaba-token-plan-intl":`Alibaba Token Plan (Intl)`,kimi:`Kimi`,"kimi-code":`Kimi`,"kimi-responses":`Kimi`,moonshot:`Moonshot`,google:`Google`,"google-vertex":`Google Vertex`,"lm-studio":`LM Studio`,huggingface:`Hugging Face`,"qwen-cloud":`Qwen Cloud`,siliconflow:`SiliconFlow`,stepfun:`StepFun`,"tencent-coding-plan":`Tencent Cloud Coding Plan`,codebuddy:`CodeBuddy`,"codebuddy-cn":`CodeBuddy CN`,qoder:`Qoder`,"qoder-cn":`Qoder CN`,"vercel-ai-gateway":`Vercel AI Gateway`,vllm:`vLLM`,litellm:`LiteLLM`},i={"command-code":`provider.name.commandCodeAuth`,commandcode:`provider.name.commandCodeApi`,orcarouter:`provider.name.orcaRouterApi`,"orcarouter-oauth":`provider.name.orcaRouterAuth`,volcengine:`provider.name.volcengine`,"volcengine-coding-plan":`provider.name.volcengineCodingPlan`,"volcengine-agent-plan":`provider.name.volcengineAgentPlan`},a=new Set([...Object.keys(r),...Object.keys(i)]);function o(e){let t=e.toLowerCase();return Object.hasOwn(n,t)?n[t]:void 0}function s(e,t){let n=o(e);return n?`/provider-icons/${n}`:void 0}var c=new Set([`cerebras.svg`,`deepinfra.svg`,`neuralwatt.svg`,`nous.svg`,`novita.svg`,`packycode.svg`,`opper.svg`,`siliconflow.svg`,`synthetic.svg`,`zenmux.svg`,`grok.svg`,`kimi-color.svg`,`ollama-color.svg`,`opencode.svg`,`vercel-ai-gateway-color.svg`]),l=new Set([`baseten.svg`,`kilo.svg`,`sambanova.svg`,`venice.svg`,`zai.svg`]),u=new Set([`bizrouter.svg`,`featherless.svg`,`hyperbolic.svg`,`nebius.svg`,`parallel.svg`,`umans.svg`]);function d(e){if(!e)return`image`;let t=e.split(`/`).pop()??``;return c.has(t)?`mask`:l.has(t)?`plate`:u.has(t)?`dark-plate`:`image`}function f(e,t){let n=e.toLowerCase(),a=Object.hasOwn(i,n)?i[n]:void 0;return a?t(a):(Object.hasOwn(r,n)?r[n]:void 0)||(e===n&&/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e)?e.split(`-`).map(e=>e&&e[0].toUpperCase()+e.slice(1)).join(` `):e)}function p(e){return a.has(e.toLowerCase())}function m(e){return e===`command-code`?`commandcode-auth`:e===`commandcode`?`commandcode-api`:e}function h(e,t){let n=e.indexOf(`/`);if(n<=0)return e;let r=e.slice(0,n),i=e.slice(n+1);if(r===`command-code`||r===`commandcode`){let e=i.match(/^([a-z0-9]+)-([a-z0-9]+(?:-[a-z0-9]+)+)$/i);return e&&i.startsWith(`${e[1]}-${e[1]}-`)&&(i=i.slice(e[1].length+1)),`${r===`command-code`?`commandcode-auth`:`commandcode-api`}/${i}`}return e}var g={ko:[{v:0x2386f26fc10000,s:`경`},{v:0xe8d4a51000,s:`조`},{v:1e8,s:`억`},{v:1e4,s:`만`}],zh:[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`亿`},{v:1e4,s:`万`}],"zh-TW":[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`億`},{v:1e4,s:`萬`}]};function _(e){return e.replace(/\.0+$/,``).replace(/(\.\d*?)0+$/,`$1`)}function v(e,t){let n=g[t];if(n){for(let t of n)if(e>=t.v)return`${_((e/t.v).toFixed(1))}${t.s}`;return String(e)}return e<1e4?String(e):e<1e6?`${_((e/1e3).toFixed(1))}K`:e<1e9?`${_((e/1e6).toFixed(1))}M`:e<0xe8d4a51000?`${_((e/1e9).toFixed(1))}B`:`${_((e/0xe8d4a51000).toFixed(1))}T`}var y=t(),b=1e10;function x(e){return e<b?e*1e3:e}function S(e,t){let n=e=>typeof e==`number`&&Number.isFinite(e);if(!n(e.shortPercent)||e.shortPercent<100)return!1;let r=e.shortResetAt;if(!n(r)||r<=0){let r=e.shortObservedAt;if(!n(r))return!1;let i=t-r;return i>=0&&i<=3e5}return x(r)>t}function C(e){return{fiveHourAvailable:e?.shortWindowSeconds===18e3&&typeof e.shortResetAt==`number`,weeklyAvailable:typeof e?.weeklyResetAt==`number`}}function w(e){let t=e?.trim().toLowerCase();return t===`go`||t===`free`}function T(e,t){if(!e)return null;let n=e.shortPercent===void 0&&e.shortResetAt===void 0?e:{...e,fiveHourPercent:e.fiveHourPercent??e.shortPercent,fiveHourResetAt:e.fiveHourResetAt??e.shortResetAt};return w(t)?{...n.monthlyPercent===void 0?{}:{monthlyPercent:n.monthlyPercent},...n.monthlyResetAt===void 0?{}:{monthlyResetAt:n.monthlyResetAt},...n.creditsUsd===void 0?{}:{creditsUsd:n.creditsUsd},...n.resetCredits===void 0?{}:{resetCredits:n.resetCredits},updatedAt:n.updatedAt}:n}function E(e,t,n=Date.now()){if(!e)return null;let r=e=>typeof e==`number`&&Number.isFinite(e),i=r(e.fiveHourPercent)?e.fiveHourPercent:r(e.shortPercent)?e.shortPercent:void 0,a=(w(t)?[e.monthlyPercent]:[e.weeklyPercent,e.monthlyPercent]).filter(r);if(a.length===0)return S({...r(i)?{shortPercent:i}:{},...r(e.fiveHourResetAt??e.shortResetAt)?{shortResetAt:e.fiveHourResetAt??e.shortResetAt}:{},...r(e.shortObservedAt)?{shortObservedAt:e.shortObservedAt}:{}},n)?100:null;let o=r(i)?[...a,i]:a;return o.length?Math.max(...o):null}function D(e){let t=new URLSearchParams({hours:String(e.chartHours),bucketMinutes:String(e.bucketMinutes),metric:e.tokenMetric,aggregation:e.aggregation,grouping:e.chartGrouping});e.models?.length&&t.set(`models`,e.models.join(`,`));for(let n of e.hiddenProviders)t.append(`hiddenProvider`,n);return t}function O(e){return Array.isArray(e)&&e.length<=100&&e.every(e=>typeof e==`string`)?JSON.stringify([...new Set(e)].sort()):void 0}function k(e,t){let n=new Set(t.hiddenProviders),r=e.availableModels.filter(e=>!n.has(e.slice(0,e.indexOf(`/`))));if(t.models?.length===0)return{...e,series:[],availableModels:r};let i=t.models===null?null:new Set(t.models),a=i!==null||n.size>0,o=e.appliedFilters,s=!!o&&O(o.hiddenProviders)===O(t.hiddenProviders)&&(t.models===null?o.models===null:O(o.models)===O(t.models)),c=e.series.filter(e=>e.id===`other`&&e.provider===``?!a||s:!n.has(e.provider)&&(i===null||i.has(`${e.provider}/${e.model}`)||i.has(e.model)));return{...e,series:c,availableModels:r,truncated:e.truncated||(a||o!==void 0)&&!s}}var A={6:15,24:60,72:180,168:360};function j(e){return A[e]}function M(e){if(e<1e3)return String(Math.round(e));let t=[[0xe8d4a51000,`T`],[1e9,`B`],[1e6,`M`],[1e3,`K`]];for(let n=0;n<t.length;n+=1){let[r,i]=t[n];if(e>=r){let a=Math.round(e/r);if(a>=1e3&&n>0){let[r,i]=t[n-1];return`${Math.round(e/r)}${i}`}return`${a}${i}`}}return String(Math.round(e))}function N(e,t){let n=new Map;for(let r of e){let e=r.includes(`/`)?r.slice(0,r.indexOf(`/`)):r,i=n.get(e)??{provider:e,models:[],total:0},a=t.get(r)??0;i.models.push({id:r,total:a}),i.total+=a,n.set(e,i)}return Array.from(n.values()).map(e=>({...e,models:e.models.toSorted((e,t)=>t.total-e.total||e.id.localeCompare(t.id))})).toSorted((e,t)=>t.total-e.total||e.provider.localeCompare(t.provider))}function P(e,t,n,r){let i=new Set(t),a=new Set((e??t).filter(e=>i.has(e)));for(let e of n)r?a.add(e):a.delete(e);return t.length>0&&t.every(e=>a.has(e))?null:t.filter(e=>a.has(e))}function F(e,t=[]){let n={...e};if(typeof n.menuBarTemplate==`string`&&n.menuBarTemplate.trim()===``&&(n.menuBarTemplate=null),n.models!==void 0&&t.length>0){let e=n.models??[],r=new Set(e);e.length===t.length&&t.every(e=>r.has(e))&&(n.models=null)}return n}function I(e,t,n,r,i=8){let a=Math.max(0,t-i*2),o=Math.max(0,n-i*2),s=Math.max(r,1),c=Math.max(e.length-1,1);return e.map((e,t)=>`${i+a*t/c},${i+o*(1-Math.max(0,e)/s)}`).join(` `)}function L(e,t,n,r,i=8){let a=e[0]?.points.length??0;if(a===0)return[];let o=Math.max(0,t-i*2),s=Math.max(0,n-i*2),c=Math.max(r,1),l=Math.min(3,o/Math.max(a*8,1)),u=Math.max(0,o/a-l),d=[];for(let t=0;t<a;t+=1){let n=0;for(let r=0;r<e.length;r+=1){let f=s*Math.max(0,e[r]?.points[t]??0)/c;f>0&&d.push({x:i+o/a*t+l/2,y:i+s-n-f,width:u,height:f,seriesIndex:r,bucketIndex:t}),n+=f}}return d}var R=[`#0A84FF`,`#FF9F0A`,`#30D158`,`#BF5AF2`,`#FF453A`,`#64D2FF`],z=640,B=160,V=28;function H(e,t){return Math.max(...t===`stackedBar`?Array.from({length:e.buckets},(t,n)=>e.series.reduce((e,t)=>e+(t.points[n]??0),0)):e.series.flatMap(e=>e.points),0)}function U(e,t){let n=new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}),r=Math.max(1,Math.floor((e.buckets-1)/3));return[0,1,2,3].map(t=>{let i=Math.min(e.buckets-1,t*r);return n.format(new Date((e.start+i*e.bucketSeconds)*1e3))})}function W({timeline:e,chartStyle:t,hours:n,loading:r,error:i,onRetry:a,locale:o,t:s}){if(r)return(0,y.jsx)(`div`,{className:`usage-companion-chart-skeleton`,"aria-busy":`true`,"aria-label":s(`usage.companion.loading`)});if(i)return(0,y.jsxs)(`div`,{className:`usage-companion-chart-state`,role:`alert`,children:[(0,y.jsx)(`span`,{children:s(`usage.companion.timelineUnavailable`)}),(0,y.jsx)(`button`,{type:`button`,className:`btn btn-ghost btn-sm`,onClick:a,children:s(`common.retry`)})]});if(!e||e.series.length===0)return(0,y.jsx)(`div`,{className:`usage-companion-chart-state`,children:s(`usage.companion.empty`,{hours:e?.buckets?Math.round(e.buckets*e.bucketSeconds/3600):n})});let c=H(e,t),l=U(e,o),u=V,d=l.map((e,t)=>(0,y.jsx)(`text`,{x:V+584*t/3,y:156,textAnchor:t===0?`start`:t===3?`end`:`middle`,className:`usage-companion-axis-label`,children:e},`${e}-${t}`)),f=t===`line`?e.series.map((e,t)=>(0,y.jsx)(`polyline`,{points:I(e.points,584,104,c,0),transform:`translate(${V},${V})`,fill:`none`,stroke:R[t%R.length],strokeWidth:`2`,strokeLinejoin:`round`,strokeLinecap:`round`},e.id)):L(e.series,584,104,c,0).map(e=>(0,y.jsx)(`rect`,{x:V+e.x,y:V+e.y,width:e.width,height:e.height,fill:R[e.seriesIndex%R.length],rx:`1`},`${e.bucketIndex}-${e.seriesIndex}`));return(0,y.jsxs)(`div`,{className:`usage-companion-chart`,children:[(0,y.jsxs)(`svg`,{viewBox:`0 0 ${z} ${B}`,role:`img`,"aria-label":s(`usage.companion.chartLabel`),children:[(0,y.jsx)(`line`,{x1:V,y1:u,x2:V,y2:132,className:`usage-companion-axis`}),(0,y.jsx)(`line`,{x1:V,y1:132,x2:612,y2:132,className:`usage-companion-axis`}),(0,y.jsx)(`text`,{x:24,y:32,textAnchor:`end`,className:`usage-companion-axis-label`,children:M(c)}),f,d]}),(0,y.jsx)(`div`,{className:`usage-companion-legend`,children:e.series.map((e,t)=>(0,y.jsxs)(`span`,{className:`usage-companion-legend-item`,children:[(0,y.jsx)(`span`,{className:`usage-companion-swatch`,style:{background:R[t%R.length]},"aria-hidden":`true`}),(0,y.jsx)(`span`,{children:e.id})]},e.id))}),e.truncated&&(0,y.jsx)(`p`,{className:`muted text-caption`,children:s(`usage.companion.olderRecordsSkipped`)})]})}var G=e=>typeof e==`number`&&Number.isFinite(e)&&e>=0,K=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:{};function q(e){return Object.entries(K(K(e).providers)).flatMap(([e,t])=>{let n=K(t);return n.disabled===!0?[]:[{name:e,path:e===`openai`?`/api/codex-auth/accounts`:n.authMode===`oauth`?`/api/oauth/accounts?${new URLSearchParams({provider:e,quota:`1`})}`:n.hasApiKey===!0&&n.authMode!==`forward`?`/api/providers/keys?${new URLSearchParams({name:e,quota:`1`})}`:null}]})}function J(e){let t=K(e),n=t.accounts??t.keys;if(!Array.isArray(n))throw Error(`Invalid account roster`);return n.map(e=>{let n=K(e);if(typeof n.id!=`string`)throw Error(`Invalid account identifier`);let r=typeof n.email==`string`?X(n.email):void 0,i=[n.alias,n.label,r,n.logLabel,n.id].find(e=>typeof e==`string`&&e.length),a=n.quotaUnavailable===!0||n.quotaMode===`unsupported`||!n.quota?null:K(n.quota),o=typeof n.plan==`string`?n.plan:void 0,s=t.activeAccountId??t.activeId??t.activeCodexAccountId;return{id:n.id,label:i,email:r,plan:o,unavailable:n.quotaUnavailable===!0,active:typeof s==`string`?s===n.id:n.active===!0,status:typeof K(n.health).status==`string`?K(n.health).status:void 0,quotaFailure:typeof n.quotaFailure==`string`?n.quotaFailure:void 0,quota:T(a,o)}})}function Y(e){if(!G(e)||e===0)return null;let t=e<0xe8d4a51000?e*1e3:e;return Number.isFinite(new Date(t).getTime())?t:null}function X(e){let[t,n]=e.split(`@`);if(!n)return`•••`;let r=n.includes(`.`)?n.slice(n.lastIndexOf(`.`)):``;return`${t?.slice(0,1)||`•`}•••@${n.slice(0,1)}•••${r}`}function Z(e,t,n=Date.now()){let r=Y(e);if(r===null||r<=n)return{text:`—`};let i=Math.ceil((r-n)/6e4),a=(e,n)=>new Intl.NumberFormat(t,{style:`unit`,unit:n,unitDisplay:`narrow`}).format(e);return{text:i<60?a(i,`minute`):i<1440?`${a(Math.floor(i/60),`hour`)} ${a(i%60,`minute`)}`:i<10080?`${a(Math.floor(i/1440),`day`)} ${a(Math.floor(i%1440/60),`hour`)}`:new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}).format(r),exact:new Date(r).toLocaleString(t)}}function Q(e){if(!e)return[];let t=[{id:`quota.fiveHourLimit`,key:`quota.fiveHourLimit`,percent:e.fiveHourPercent??e.shortPercent,reset:e.fiveHourResetAt??e.shortResetAt},{id:`quota.weeklyLimit`,key:`quota.weeklyLimit`,percent:e.weeklyPercent,reset:e.weeklyResetAt},{id:`quota.monthlyLimit`,key:`quota.monthlyLimit`,percent:e.monthlyPercent,reset:e.monthlyResetAt},...Array.isArray(e.customWindows)?e.customWindows.filter(e=>e&&typeof e.label==`string`).map(e=>({id:e.label,label:e.label,percent:e.percent,reset:e.resetAt})):[]].filter((t,n)=>n===0&&e.monthlyPercent===void 0||G(t.percent)||Y(t.reset)!==null),n=new Map;return t.map(e=>{let t=n.get(e.id)??0;return n.set(e.id,t+1),t===0?e:{...e,id:`${e.id}#${t}`}})}function $(e,t){let n=new Set(t.hiddenProviders),r=t.models===null?null:new Set(t.models),i=e.models.filter(e=>!n.has(e.provider)&&(r===null||r.has(`${e.provider}/${e.model}`)||r.has(e.model)));if(t.models===null&&t.hiddenProviders.length===0)return{...e,models:i};if(t.models?.length!==0&&e.models.some(e=>!e.provider||!e.model||e.provider===`other`&&e.model===`other`))return{...e,models:i.filter(e=>e.provider!==`other`||e.model!==`other`),summary:{},usageIncomplete:!0};let a={};for(let e of[`requests`,`totalTokens`,`inputTokens`,`outputTokens`,`cachedInputTokens`,`cacheReadInputTokens`,`estimatedCostUsd`,`measuredRequests`,`pricedRequests`])i.length&&i.every(t=>G(t[e]))&&(a[e]=i.reduce((t,n)=>t+n[e],0));return{...e,models:i,summary:a}}function ee(e){let t={...e};if((e.requests??0)>0&&(e.measuredRequests===0||e.coverageRatio===0))for(let e of[`totalTokens`,`inputTokens`,`outputTokens`,`cachedInputTokens`,`cacheReadInputTokens`])delete t[e];return(e.requests??0)>0&&e.pricedRequests===0&&delete t.estimatedCostUsd,t}async function te(t,n){let r=e(2e4),i=()=>r.controller.abort();n.aborted&&i(),n.addEventListener(`abort`,i,{once:!0});try{let e=await fetch(t,{signal:r.signal,cache:`no-store`});if(!e.ok)throw Error(String(e.status));let n=await e.json();if(r.signal.aborted)throw Error(`Tray request cancelled`);return n}finally{r.clear(),n.removeEventListener(`abort`,i)}}function ne(e){let t=K(e);if(t.error||!t.summary||typeof t.summary!=`object`||Array.isArray(t.summary)||!Array.isArray(t.models))throw Error(`Invalid usage`);return t}export{h as C,d as D,m as E,s as O,v as S,p as T,N as _,J as a,T as b,Q as c,W as d,j as f,M as g,D as h,ee as i,Z as l,k as m,$ as n,ne as o,F as p,G as r,q as s,te as t,Y as u,P as v,f as w,C as x,E as y};
|
|
1
|
+
import{h as e,n as t}from"./index-vO4dgfiU.js";var n={anthropic:`claude-color.svg`,"anthropic-apikey":`claude-color.svg`,"claude-cli":`claude-color.svg`,"azure-openai":`openai.svg`,chatgpt:`openai.svg`,"cloudflare-ai-gateway":`cloudflare-ai-gateway-color.svg`,"cloudflare-workers-ai":`cloudflare-ai-gateway-color.svg`,cline:`cline-color.svg`,"cline-pass":`cline-color.svg`,"command-code":`commandcode-color.svg`,commandcode:`commandcode-color.svg`,cursor:`cursor-color.svg`,deepseek:`deepseek-color.svg`,devin:`devin.svg`,firepass:`firepass-color.svg`,fireworks:`fireworks-color.svg`,github:`github-copilot-color.svg`,"github-copilot":`copilot-color.svg`,"gitlab-duo":`gitlab-duo-color.svg`,google:`gemini-color.svg`,"google-antigravity":`antigravity-color.svg`,"google-vertex":`gemini-color.svg`,groq:`groq-color.svg`,huggingface:`huggingface-color.svg`,kimi:`kimi-color.svg`,"kimi-code":`kimi-color.svg`,"kimi-responses":`kimi-color.svg`,kiro:`kiro-color.svg`,"lm-studio":`lm-studio-color.svg`,"meta-model":`meta.svg`,"meta-muse":`meta.svg`,mistral:`mistral-color.svg`,minimax:`minimax.svg`,"minimax-cn":`minimax.svg`,moonshot:`moonshot-color.svg`,nvidia:`nvidia-color.svg`,ollama:`ollama-color.svg`,"ollama-cloud":`ollama-color.svg`,openai:`openai.svg`,"openai-apikey":`openai.svg`,"opencode-free":`opencode.svg`,"opencode-go":`opencode.svg`,"opencode-zen":`opencode.svg`,openrouter:`openrouter-color.svg`,opper:`opper.svg`,qianfan:`qianfan-color.svg`,qoder:`qoder.svg`,"qoder-cn":`qoder.svg`,alibaba:`alibaba-color.svg`,"alibaba-token-plan":`alibaba-color.svg`,"alibaba-token-plan-intl":`alibaba-color.svg`,baseten:`baseten.svg`,bizrouter:`bizrouter.svg`,cerebras:`cerebras.svg`,crusoe:`crusoe.svg`,deepinfra:`deepinfra.svg`,digitalocean:`digitalocean.svg`,featherless:`featherless.svg`,hyperbolic:`hyperbolic.svg`,kilo:`kilo.svg`,nanogpt:`nanogpt.svg`,nebius:`nebius.svg`,neuralwatt:`neuralwatt.svg`,nous:`nous.svg`,novita:`novita.svg`,orcarouter:`orcarouter.svg`,"orcarouter-oauth":`orcarouter.svg`,packycode:`packycode.svg`,parallel:`parallel.svg`,sambanova:`sambanova.svg`,scaleway:`scaleway.svg`,stepfun:`stepfun-color.svg`,siliconflow:`siliconflow.svg`,synthetic:`synthetic.svg`,together:`together.svg`,umans:`umans.svg`,venice:`venice.svg`,vultr:`vultr.svg`,litellm:`litellm.svg`,zenmux:`zenmux.svg`,zai:`zai.svg`,"zhipu-bigmodel":`zai.svg`,"zhipu-bigmodel-coding":`zai.svg`,"qwen-cloud":`qwen-portal-color.svg`,"vercel-ai-gateway":`vercel-ai-gateway-color.svg`,vllm:`vllm-color.svg`,xai:`grok.svg`,"mimo-free":`xiaomi-color.svg`,mimo:`xiaomi-color.svg`,xiaomi:`xiaomi-color.svg`,"xiaomi-mimo":`xiaomi-color.svg`},r={anthropic:`Anthropic Claude`,"anthropic-apikey":`Anthropic Claude`,chatgpt:`ChatGPT`,openai:`OpenAI (Codex login)`,"openai-apikey":`OpenAI API`,"azure-openai":`Azure OpenAI`,"cloudflare-ai-gateway":`Cloudflare AI Gateway`,"cloudflare-workers-ai":`Cloudflare Workers AI`,cline:`Cline`,"cline-pass":`ClinePass`,nvidia:`NVIDIA NIM`,ollama:`Ollama`,"ollama-cloud":`Ollama Cloud`,xai:`xAI Grok`,"mimo-free":`MiMo Free`,xiaomi:`Xiaomi`,cursor:`Cursor`,deepseek:`DeepSeek`,devin:`Devin`,github:`GitHub`,"github-copilot":`GitHub Copilot`,"gitlab-duo":`GitLab Duo`,openrouter:`OpenRouter`,opper:`Opper`,"opencode-go":`OpenCode Go`,"opencode-free":`OpenCode Free`,"opencode-zen":`OpenCode Zen`,orcarouter:`OrcaRouter - API`,"orcarouter-oauth":`OrcaRouter - Auth`,packycode:`PackyCode`,mistral:`Mistral`,groq:`Groq`,"meta-model":`Meta Model API`,"meta-muse":`Muse Code`,alibaba:`Alibaba Coding Plan`,"alibaba-token-plan":`Alibaba Token Plan`,"alibaba-token-plan-intl":`Alibaba Token Plan (Intl)`,kimi:`Kimi`,"kimi-code":`Kimi`,"kimi-responses":`Kimi`,moonshot:`Moonshot`,google:`Google`,"google-vertex":`Google Vertex`,"lm-studio":`LM Studio`,huggingface:`Hugging Face`,"qwen-cloud":`Qwen Cloud`,siliconflow:`SiliconFlow`,stepfun:`StepFun`,"tencent-coding-plan":`Tencent Cloud Coding Plan`,codebuddy:`CodeBuddy`,"codebuddy-cn":`CodeBuddy CN`,qoder:`Qoder`,"qoder-cn":`Qoder CN`,"vercel-ai-gateway":`Vercel AI Gateway`,vllm:`vLLM`,litellm:`LiteLLM`},i={"command-code":`provider.name.commandCodeAuth`,commandcode:`provider.name.commandCodeApi`,orcarouter:`provider.name.orcaRouterApi`,"orcarouter-oauth":`provider.name.orcaRouterAuth`,volcengine:`provider.name.volcengine`,"volcengine-coding-plan":`provider.name.volcengineCodingPlan`,"volcengine-agent-plan":`provider.name.volcengineAgentPlan`},a=new Set([...Object.keys(r),...Object.keys(i)]);function o(e){let t=e.toLowerCase();return Object.hasOwn(n,t)?n[t]:void 0}function s(e,t){let n=o(e);return n?`/provider-icons/${n}`:void 0}var c=new Set([`cerebras.svg`,`deepinfra.svg`,`neuralwatt.svg`,`nous.svg`,`novita.svg`,`packycode.svg`,`opper.svg`,`siliconflow.svg`,`synthetic.svg`,`zenmux.svg`,`grok.svg`,`kimi-color.svg`,`ollama-color.svg`,`opencode.svg`,`vercel-ai-gateway-color.svg`]),l=new Set([`baseten.svg`,`kilo.svg`,`sambanova.svg`,`venice.svg`,`zai.svg`]),u=new Set([`bizrouter.svg`,`featherless.svg`,`hyperbolic.svg`,`nebius.svg`,`parallel.svg`,`umans.svg`]);function d(e){if(!e)return`image`;let t=e.split(`/`).pop()??``;return c.has(t)?`mask`:l.has(t)?`plate`:u.has(t)?`dark-plate`:`image`}function f(e,t){let n=e.toLowerCase(),a=Object.hasOwn(i,n)?i[n]:void 0;return a?t(a):(Object.hasOwn(r,n)?r[n]:void 0)||(e===n&&/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(e)?e.split(`-`).map(e=>e&&e[0].toUpperCase()+e.slice(1)).join(` `):e)}function p(e){return a.has(e.toLowerCase())}function m(e){return e===`command-code`?`commandcode-auth`:e===`commandcode`?`commandcode-api`:e}function h(e,t){let n=e.indexOf(`/`);if(n<=0)return e;let r=e.slice(0,n),i=e.slice(n+1);if(r===`command-code`||r===`commandcode`){let e=i.match(/^([a-z0-9]+)-([a-z0-9]+(?:-[a-z0-9]+)+)$/i);return e&&i.startsWith(`${e[1]}-${e[1]}-`)&&(i=i.slice(e[1].length+1)),`${r===`command-code`?`commandcode-auth`:`commandcode-api`}/${i}`}return e}var g={ko:[{v:0x2386f26fc10000,s:`경`},{v:0xe8d4a51000,s:`조`},{v:1e8,s:`억`},{v:1e4,s:`만`}],zh:[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`亿`},{v:1e4,s:`万`}],"zh-TW":[{v:0x2386f26fc10000,s:`京`},{v:0xe8d4a51000,s:`兆`},{v:1e8,s:`億`},{v:1e4,s:`萬`}]};function _(e){return e.replace(/\.0+$/,``).replace(/(\.\d*?)0+$/,`$1`)}function v(e,t){let n=g[t];if(n){for(let t of n)if(e>=t.v)return`${_((e/t.v).toFixed(1))}${t.s}`;return String(e)}return e<1e4?String(e):e<1e6?`${_((e/1e3).toFixed(1))}K`:e<1e9?`${_((e/1e6).toFixed(1))}M`:e<0xe8d4a51000?`${_((e/1e9).toFixed(1))}B`:`${_((e/0xe8d4a51000).toFixed(1))}T`}var y=t(),b=1e10;function x(e){return e<b?e*1e3:e}function S(e,t){let n=e=>typeof e==`number`&&Number.isFinite(e);if(!n(e.shortPercent)||e.shortPercent<100)return!1;let r=e.shortResetAt;if(!n(r)||r<=0){let r=e.shortObservedAt;if(!n(r))return!1;let i=t-r;return i>=0&&i<=3e5}return x(r)>t}function C(e){return{fiveHourAvailable:e?.shortWindowSeconds===18e3&&typeof e.shortResetAt==`number`,weeklyAvailable:typeof e?.weeklyResetAt==`number`}}function w(e){let t=e?.trim().toLowerCase();return t===`go`||t===`free`}function T(e,t){if(!e)return null;let n=e.shortPercent===void 0&&e.shortResetAt===void 0?e:{...e,fiveHourPercent:e.fiveHourPercent??e.shortPercent,fiveHourResetAt:e.fiveHourResetAt??e.shortResetAt};return w(t)?{...n.monthlyPercent===void 0?{}:{monthlyPercent:n.monthlyPercent},...n.monthlyResetAt===void 0?{}:{monthlyResetAt:n.monthlyResetAt},...n.creditsUsd===void 0?{}:{creditsUsd:n.creditsUsd},...n.resetCredits===void 0?{}:{resetCredits:n.resetCredits},updatedAt:n.updatedAt}:n}function E(e,t,n=Date.now()){if(!e)return null;let r=e=>typeof e==`number`&&Number.isFinite(e),i=r(e.fiveHourPercent)?e.fiveHourPercent:r(e.shortPercent)?e.shortPercent:void 0,a=(w(t)?[e.monthlyPercent]:[e.weeklyPercent,e.monthlyPercent]).filter(r);if(a.length===0)return S({...r(i)?{shortPercent:i}:{},...r(e.fiveHourResetAt??e.shortResetAt)?{shortResetAt:e.fiveHourResetAt??e.shortResetAt}:{},...r(e.shortObservedAt)?{shortObservedAt:e.shortObservedAt}:{}},n)?100:null;let o=r(i)?[...a,i]:a;return o.length?Math.max(...o):null}function D(e){let t=new URLSearchParams({hours:String(e.chartHours),bucketMinutes:String(e.bucketMinutes),metric:e.tokenMetric,aggregation:e.aggregation,grouping:e.chartGrouping});e.models?.length&&t.set(`models`,e.models.join(`,`));for(let n of e.hiddenProviders)t.append(`hiddenProvider`,n);return t}function O(e){return Array.isArray(e)&&e.length<=100&&e.every(e=>typeof e==`string`)?JSON.stringify([...new Set(e)].sort()):void 0}function k(e,t){let n=new Set(t.hiddenProviders),r=e.availableModels.filter(e=>!n.has(e.slice(0,e.indexOf(`/`))));if(t.models?.length===0)return{...e,series:[],availableModels:r};let i=t.models===null?null:new Set(t.models),a=i!==null||n.size>0,o=e.appliedFilters,s=!!o&&O(o.hiddenProviders)===O(t.hiddenProviders)&&(t.models===null?o.models===null:O(o.models)===O(t.models)),c=e.series.filter(e=>e.id===`other`&&e.provider===``?!a||s:!n.has(e.provider)&&(i===null||i.has(`${e.provider}/${e.model}`)||i.has(e.model)));return{...e,series:c,availableModels:r,truncated:e.truncated||(a||o!==void 0)&&!s}}var A={6:15,24:60,72:180,168:360};function j(e){return A[e]}function M(e){if(e<1e3)return String(Math.round(e));let t=[[0xe8d4a51000,`T`],[1e9,`B`],[1e6,`M`],[1e3,`K`]];for(let n=0;n<t.length;n+=1){let[r,i]=t[n];if(e>=r){let a=Math.round(e/r);if(a>=1e3&&n>0){let[r,i]=t[n-1];return`${Math.round(e/r)}${i}`}return`${a}${i}`}}return String(Math.round(e))}function N(e,t){let n=new Map;for(let r of e){let e=r.includes(`/`)?r.slice(0,r.indexOf(`/`)):r,i=n.get(e)??{provider:e,models:[],total:0},a=t.get(r)??0;i.models.push({id:r,total:a}),i.total+=a,n.set(e,i)}return Array.from(n.values()).map(e=>({...e,models:e.models.toSorted((e,t)=>t.total-e.total||e.id.localeCompare(t.id))})).toSorted((e,t)=>t.total-e.total||e.provider.localeCompare(t.provider))}function P(e,t,n,r){let i=new Set(t),a=new Set((e??t).filter(e=>i.has(e)));for(let e of n)r?a.add(e):a.delete(e);return t.length>0&&t.every(e=>a.has(e))?null:t.filter(e=>a.has(e))}function F(e,t=[]){let n={...e};if(typeof n.menuBarTemplate==`string`&&n.menuBarTemplate.trim()===``&&(n.menuBarTemplate=null),n.models!==void 0&&t.length>0){let e=n.models??[],r=new Set(e);e.length===t.length&&t.every(e=>r.has(e))&&(n.models=null)}return n}function I(e,t,n,r,i=8){let a=Math.max(0,t-i*2),o=Math.max(0,n-i*2),s=Math.max(r,1),c=Math.max(e.length-1,1);return e.map((e,t)=>`${i+a*t/c},${i+o*(1-Math.max(0,e)/s)}`).join(` `)}function L(e,t,n,r,i=8){let a=e[0]?.points.length??0;if(a===0)return[];let o=Math.max(0,t-i*2),s=Math.max(0,n-i*2),c=Math.max(r,1),l=Math.min(3,o/Math.max(a*8,1)),u=Math.max(0,o/a-l),d=[];for(let t=0;t<a;t+=1){let n=0;for(let r=0;r<e.length;r+=1){let f=s*Math.max(0,e[r]?.points[t]??0)/c;f>0&&d.push({x:i+o/a*t+l/2,y:i+s-n-f,width:u,height:f,seriesIndex:r,bucketIndex:t}),n+=f}}return d}var R=[`#0A84FF`,`#FF9F0A`,`#30D158`,`#BF5AF2`,`#FF453A`,`#64D2FF`],z=640,B=160,V=28;function H(e,t){return Math.max(...t===`stackedBar`?Array.from({length:e.buckets},(t,n)=>e.series.reduce((e,t)=>e+(t.points[n]??0),0)):e.series.flatMap(e=>e.points),0)}function U(e,t){let n=new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}),r=Math.max(1,Math.floor((e.buckets-1)/3));return[0,1,2,3].map(t=>{let i=Math.min(e.buckets-1,t*r);return n.format(new Date((e.start+i*e.bucketSeconds)*1e3))})}function W({timeline:e,chartStyle:t,hours:n,loading:r,error:i,onRetry:a,locale:o,t:s}){if(r)return(0,y.jsx)(`div`,{className:`usage-companion-chart-skeleton`,"aria-busy":`true`,"aria-label":s(`usage.companion.loading`)});if(i)return(0,y.jsxs)(`div`,{className:`usage-companion-chart-state`,role:`alert`,children:[(0,y.jsx)(`span`,{children:s(`usage.companion.timelineUnavailable`)}),(0,y.jsx)(`button`,{type:`button`,className:`btn btn-ghost btn-sm`,onClick:a,children:s(`common.retry`)})]});if(!e||e.series.length===0)return(0,y.jsx)(`div`,{className:`usage-companion-chart-state`,children:s(`usage.companion.empty`,{hours:e?.buckets?Math.round(e.buckets*e.bucketSeconds/3600):n})});let c=H(e,t),l=U(e,o),u=V,d=l.map((e,t)=>(0,y.jsx)(`text`,{x:V+584*t/3,y:156,textAnchor:t===0?`start`:t===3?`end`:`middle`,className:`usage-companion-axis-label`,children:e},`${e}-${t}`)),f=t===`line`?e.series.map((e,t)=>(0,y.jsx)(`polyline`,{points:I(e.points,584,104,c,0),transform:`translate(${V},${V})`,fill:`none`,stroke:R[t%R.length],strokeWidth:`2`,strokeLinejoin:`round`,strokeLinecap:`round`},e.id)):L(e.series,584,104,c,0).map(e=>(0,y.jsx)(`rect`,{x:V+e.x,y:V+e.y,width:e.width,height:e.height,fill:R[e.seriesIndex%R.length],rx:`1`},`${e.bucketIndex}-${e.seriesIndex}`));return(0,y.jsxs)(`div`,{className:`usage-companion-chart`,children:[(0,y.jsxs)(`svg`,{viewBox:`0 0 ${z} ${B}`,role:`img`,"aria-label":s(`usage.companion.chartLabel`),children:[(0,y.jsx)(`line`,{x1:V,y1:u,x2:V,y2:132,className:`usage-companion-axis`}),(0,y.jsx)(`line`,{x1:V,y1:132,x2:612,y2:132,className:`usage-companion-axis`}),(0,y.jsx)(`text`,{x:24,y:32,textAnchor:`end`,className:`usage-companion-axis-label`,children:M(c)}),f,d]}),(0,y.jsx)(`div`,{className:`usage-companion-legend`,children:e.series.map((e,t)=>(0,y.jsxs)(`span`,{className:`usage-companion-legend-item`,children:[(0,y.jsx)(`span`,{className:`usage-companion-swatch`,style:{background:R[t%R.length]},"aria-hidden":`true`}),(0,y.jsx)(`span`,{children:e.id})]},e.id))}),e.truncated&&(0,y.jsx)(`p`,{className:`muted text-caption`,children:s(`usage.companion.olderRecordsSkipped`)})]})}var G=e=>typeof e==`number`&&Number.isFinite(e)&&e>=0,K=e=>typeof e==`object`&&e&&!Array.isArray(e)?e:{};function q(e){return Object.entries(K(K(e).providers)).flatMap(([e,t])=>{let n=K(t);return n.disabled===!0?[]:[{name:e,path:e===`openai`?`/api/codex-auth/accounts`:n.authMode===`oauth`?`/api/oauth/accounts?${new URLSearchParams({provider:e,quota:`1`})}`:n.hasApiKey===!0&&n.authMode!==`forward`?`/api/providers/keys?${new URLSearchParams({name:e,quota:`1`})}`:null}]})}function J(e){let t=K(e),n=t.accounts??t.keys;if(!Array.isArray(n))throw Error(`Invalid account roster`);return n.map(e=>{let n=K(e);if(typeof n.id!=`string`)throw Error(`Invalid account identifier`);let r=typeof n.email==`string`?X(n.email):void 0,i=[n.alias,n.label,r,n.logLabel,n.id].find(e=>typeof e==`string`&&e.length),a=n.quotaUnavailable===!0||n.quotaMode===`unsupported`||!n.quota?null:K(n.quota),o=typeof n.plan==`string`?n.plan:void 0,s=t.activeAccountId??t.activeId??t.activeCodexAccountId;return{id:n.id,label:i,email:r,plan:o,unavailable:n.quotaUnavailable===!0,active:typeof s==`string`?s===n.id:n.active===!0,status:typeof K(n.health).status==`string`?K(n.health).status:void 0,quotaFailure:typeof n.quotaFailure==`string`?n.quotaFailure:void 0,quota:T(a,o)}})}function Y(e){if(!G(e)||e===0)return null;let t=e<0xe8d4a51000?e*1e3:e;return Number.isFinite(new Date(t).getTime())?t:null}function X(e){let[t,n]=e.split(`@`);if(!n)return`•••`;let r=n.includes(`.`)?n.slice(n.lastIndexOf(`.`)):``;return`${t?.slice(0,1)||`•`}•••@${n.slice(0,1)}•••${r}`}function Z(e,t,n=Date.now()){let r=Y(e);if(r===null||r<=n)return{text:`—`};let i=Math.ceil((r-n)/6e4),a=(e,n)=>new Intl.NumberFormat(t,{style:`unit`,unit:n,unitDisplay:`narrow`}).format(e);return{text:i<60?a(i,`minute`):i<1440?`${a(Math.floor(i/60),`hour`)} ${a(i%60,`minute`)}`:i<10080?`${a(Math.floor(i/1440),`day`)} ${a(Math.floor(i%1440/60),`hour`)}`:new Intl.DateTimeFormat(t,{month:`short`,day:`numeric`}).format(r),exact:new Date(r).toLocaleString(t)}}function Q(e){if(!e)return[];let t=[{id:`quota.fiveHourLimit`,key:`quota.fiveHourLimit`,percent:e.fiveHourPercent??e.shortPercent,reset:e.fiveHourResetAt??e.shortResetAt},{id:`quota.weeklyLimit`,key:`quota.weeklyLimit`,percent:e.weeklyPercent,reset:e.weeklyResetAt},{id:`quota.monthlyLimit`,key:`quota.monthlyLimit`,percent:e.monthlyPercent,reset:e.monthlyResetAt},...Array.isArray(e.customWindows)?e.customWindows.filter(e=>e&&typeof e.label==`string`).map(e=>({id:e.label,label:e.label,percent:e.percent,reset:e.resetAt})):[]].filter((t,n)=>n===0&&e.monthlyPercent===void 0||G(t.percent)||Y(t.reset)!==null),n=new Map;return t.map(e=>{let t=n.get(e.id)??0;return n.set(e.id,t+1),t===0?e:{...e,id:`${e.id}#${t}`}})}function $(e,t){let n=new Set(t.hiddenProviders),r=t.models===null?null:new Set(t.models),i=e.models.filter(e=>!n.has(e.provider)&&(r===null||r.has(`${e.provider}/${e.model}`)||r.has(e.model)));if(t.models===null&&t.hiddenProviders.length===0)return{...e,models:i};if(t.models?.length!==0&&e.models.some(e=>!e.provider||!e.model||e.provider===`other`&&e.model===`other`))return{...e,models:i.filter(e=>e.provider!==`other`||e.model!==`other`),summary:{},usageIncomplete:!0};let a={};for(let e of[`requests`,`totalTokens`,`inputTokens`,`outputTokens`,`cachedInputTokens`,`cacheReadInputTokens`,`estimatedCostUsd`,`measuredRequests`,`pricedRequests`])i.length&&i.every(t=>G(t[e]))&&(a[e]=i.reduce((t,n)=>t+n[e],0));return{...e,models:i,summary:a}}function ee(e){let t={...e};if((e.requests??0)>0&&(e.measuredRequests===0||e.coverageRatio===0))for(let e of[`totalTokens`,`inputTokens`,`outputTokens`,`cachedInputTokens`,`cacheReadInputTokens`])delete t[e];return(e.requests??0)>0&&e.pricedRequests===0&&delete t.estimatedCostUsd,t}async function te(t,n){let r=e(2e4),i=()=>r.controller.abort();n.aborted&&i(),n.addEventListener(`abort`,i,{once:!0});try{let e=await fetch(t,{signal:r.signal,cache:`no-store`});if(!e.ok)throw Error(String(e.status));let n=await e.json();if(r.signal.aborted)throw Error(`Tray request cancelled`);return n}finally{r.clear(),n.removeEventListener(`abort`,i)}}function ne(e){let t=K(e);if(t.error||!t.summary||typeof t.summary!=`object`||Array.isArray(t.summary)||!Array.isArray(t.models))throw Error(`Invalid usage`);return t}export{h as C,d as D,m as E,s as O,v as S,p as T,N as _,J as a,T as b,Q as c,W as d,j as f,M as g,D as h,ee as i,Z as l,k as m,$ as n,ne as o,F as p,G as r,q as s,te as t,Y as u,P as v,f as w,C as x,E as y};
|
package/gui/dist/index.html
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-vO4dgfiU.js"></script>
|
|
20
|
+
<link rel="stylesheet" crossorigin href="/assets/index-ComVsdSk.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitkyc08/opencodex",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.66.0-preview.20260925",
|
|
4
4
|
"description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/package-main.mjs",
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
"prepush": "bun run typecheck && bun run lint:gui:if-changed && bun run test && bun run privacy:scan && bun run doctor:gui:if-changed",
|
|
71
71
|
"lint:gui": "cd gui && bun run lint",
|
|
72
72
|
"lint:gui:if-changed": "bun scripts/lint-gui-if-changed.ts",
|
|
73
|
-
"postmerge": "bun scripts/build-gui-if-changed.ts",
|
|
74
73
|
"doctor:gui": "cd gui && bun run doctor",
|
|
75
74
|
"doctor:gui:full": "cd gui && bun run doctor:full",
|
|
76
75
|
"doctor:gui:if-changed": "bun scripts/doctor-gui-if-changed.ts",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `anthropic-beta` values a managed native Messages request may carry from its caller (PF-10).
|
|
3
|
+
*
|
|
4
|
+
* A beta header changes what the provider accepts and bills, so the native lane never forwards
|
|
5
|
+
* the caller's header as written. Each value is compared, case-insensitively, against a fixed
|
|
6
|
+
* list for the destination's provider class; a match is re-emitted in this file's own spelling,
|
|
7
|
+
* so no caller byte ever reaches the wire, and everything else is dropped. The caller learns
|
|
8
|
+
* nothing about which value was dropped, and the trace records only that something was
|
|
9
|
+
* (`anthropic-beta-dropped`), never the value.
|
|
10
|
+
*
|
|
11
|
+
* Proxy-owned betas (the OAuth pair, the fast-mode beta) are not listed here: the lane sets them
|
|
12
|
+
* itself from the provider's own configuration, exactly as the adapter does.
|
|
13
|
+
*
|
|
14
|
+
* LEAF MODULE: no runtime import, so the builder, the lane and tests can read it freely.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Where the native lane sends: Anthropic's own API, or an Anthropic-compatible third party. */
|
|
18
|
+
export type AnthropicProviderClass = "first-party" | "compatible";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Betas a caller may forward to `api.anthropic.com`. Deliberately short: only a value whose
|
|
22
|
+
* effect is limited to how the model uses the body it already sent, with no new body field,
|
|
23
|
+
* no billing tier and no retained server state, belongs here.
|
|
24
|
+
*/
|
|
25
|
+
const FIRST_PARTY_BETAS: readonly string[] = [
|
|
26
|
+
// Lets thinking blocks appear between tool calls in one assistant turn. No request field;
|
|
27
|
+
// the thinking and tool blocks it affects are already on the lane's field allowlist, and the
|
|
28
|
+
// proxy's own OAuth quota probe sends it (`src/providers/quota/vendor-probes-oauth.ts`).
|
|
29
|
+
"interleaved-thinking-2025-05-14",
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Betas a caller may forward to an Anthropic-compatible third party. Empty: a third party's
|
|
34
|
+
* beta semantics are its own, an unknown value can fail the request outright, and none is
|
|
35
|
+
* needed for the fields the lane forwards.
|
|
36
|
+
*/
|
|
37
|
+
const COMPATIBLE_BETAS: readonly string[] = [];
|
|
38
|
+
|
|
39
|
+
const ALLOWLISTS: Readonly<Record<AnthropicProviderClass, ReadonlyMap<string, string>>> = {
|
|
40
|
+
"first-party": new Map(FIRST_PARTY_BETAS.map(beta => [beta.toLowerCase(), beta])),
|
|
41
|
+
compatible: new Map(COMPATIBLE_BETAS.map(beta => [beta.toLowerCase(), beta])),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** A caller header longer than this is not parsed at all and counts as dropped. */
|
|
45
|
+
const MAX_CALLER_BETA_HEADER_CHARS = 2048;
|
|
46
|
+
|
|
47
|
+
export interface AllowlistedAnthropicBetas {
|
|
48
|
+
/** Allowlisted values in this file's spelling, first-seen order, no duplicates. */
|
|
49
|
+
betas: string[];
|
|
50
|
+
/** Whether any non-empty caller value was left out. Never says which. */
|
|
51
|
+
dropped: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Filter the caller's comma-separated `anthropic-beta` header for one provider class. An absent
|
|
56
|
+
* or blank header yields nothing and drops nothing.
|
|
57
|
+
*/
|
|
58
|
+
export function allowlistAnthropicBetas(
|
|
59
|
+
callerHeader: string | null | undefined,
|
|
60
|
+
providerClass: AnthropicProviderClass,
|
|
61
|
+
): AllowlistedAnthropicBetas {
|
|
62
|
+
if (typeof callerHeader !== "string" || callerHeader.trim() === "") return { betas: [], dropped: false };
|
|
63
|
+
if (callerHeader.length > MAX_CALLER_BETA_HEADER_CHARS) return { betas: [], dropped: true };
|
|
64
|
+
const allowed = ALLOWLISTS[providerClass];
|
|
65
|
+
const betas: string[] = [];
|
|
66
|
+
let dropped = false;
|
|
67
|
+
for (const part of callerHeader.split(",")) {
|
|
68
|
+
const token = part.trim().toLowerCase();
|
|
69
|
+
if (!token) continue;
|
|
70
|
+
const canonical = allowed.get(token);
|
|
71
|
+
if (canonical === undefined) dropped = true;
|
|
72
|
+
else if (!betas.includes(canonical)) betas.push(canonical);
|
|
73
|
+
}
|
|
74
|
+
return { betas, dropped };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The allowlist for one class, for documentation and tests. */
|
|
78
|
+
export function anthropicBetaAllowlist(providerClass: AnthropicProviderClass): readonly string[] {
|
|
79
|
+
return [...ALLOWLISTS[providerClass].values()];
|
|
80
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Managed native Messages request builder (PF-08, PF-10).
|
|
3
|
+
*
|
|
4
|
+
* The request a proxy-managed Anthropic credential sends when the client already spoke Messages:
|
|
5
|
+
* the caller's own body, cut to a fixed field allowlist, with the wire model and the provider's
|
|
6
|
+
* credential. URL, `anthropic-version`, client identity and credential placement come from the
|
|
7
|
+
* same helpers the Anthropic adapter uses, so the two lanes cannot drift apart.
|
|
8
|
+
*
|
|
9
|
+
* Authority: only the provider's own credential is ever placed on the request — a configured
|
|
10
|
+
* key, or (PF-10) the access token of the OAuth account the lane resolved, which is sent only to
|
|
11
|
+
* `api.anthropic.com`. The one caller header this builder sees is `anthropic-beta`, handed over
|
|
12
|
+
* explicitly and reduced to `beta-allowlist.ts`; no other caller header is read here at all. The
|
|
13
|
+
* caller-forward passthrough in `src/server/claude-messages.ts` is the only place a caller's
|
|
14
|
+
* Anthropic credential may travel, and it does not come through here.
|
|
15
|
+
*
|
|
16
|
+
* Opaque state: thinking signatures and `redacted_thinking` blocks reach only first-party
|
|
17
|
+
* Anthropic (`src/protocols/opaque-state.ts`). The source body is never mutated, so every build
|
|
18
|
+
* for every destination decides from the full source again.
|
|
19
|
+
*/
|
|
20
|
+
import { mergeAnthropicBetaHeader } from "../../providers/anthropic-fast";
|
|
21
|
+
import { applyClaudeToolPrefix, CLAUDE_CODE_SYSTEM_INSTRUCTION } from "../../oauth/anthropic";
|
|
22
|
+
import { credentialDomainFor, opaqueStateForDestination } from "../../protocols/opaque-state";
|
|
23
|
+
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
24
|
+
import {
|
|
25
|
+
anthropicBaseRequestHeaders,
|
|
26
|
+
applyAnthropicKeyAuth,
|
|
27
|
+
applyAnthropicOAuthAuth,
|
|
28
|
+
resolveAnthropicMessagesUrl,
|
|
29
|
+
} from "../anthropic";
|
|
30
|
+
import { allowlistAnthropicBetas } from "./beta-allowlist";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Top-level Messages fields the native lane forwards. Everything else is dropped: an unknown or
|
|
34
|
+
* beta-gated field would otherwise reach the provider unchecked. None of the dropped fields has
|
|
35
|
+
* a name in the protocol feature vocabulary, so no feature effect is recorded for them.
|
|
36
|
+
*/
|
|
37
|
+
export const ANTHROPIC_MESSAGES_PASSTHROUGH_FIELDS = [
|
|
38
|
+
"model",
|
|
39
|
+
"messages",
|
|
40
|
+
"system",
|
|
41
|
+
"max_tokens",
|
|
42
|
+
"metadata",
|
|
43
|
+
"stop_sequences",
|
|
44
|
+
"stream",
|
|
45
|
+
"temperature",
|
|
46
|
+
"top_p",
|
|
47
|
+
"top_k",
|
|
48
|
+
"tools",
|
|
49
|
+
"tool_choice",
|
|
50
|
+
"thinking",
|
|
51
|
+
"output_config",
|
|
52
|
+
"service_tier",
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
const PASSTHROUGH_FIELD_SET: ReadonlySet<string> = new Set(ANTHROPIC_MESSAGES_PASSTHROUGH_FIELDS);
|
|
56
|
+
|
|
57
|
+
export interface AnthropicMessagesPassthroughRequest {
|
|
58
|
+
url: string;
|
|
59
|
+
headers: Record<string, string>;
|
|
60
|
+
/** The serialized wire body. */
|
|
61
|
+
body: string;
|
|
62
|
+
/** The same body before serialization, for callers that count or inspect what is sent. */
|
|
63
|
+
wireBody: Record<string, unknown>;
|
|
64
|
+
/** Caller `anthropic-beta` values were left out. Which ones is never recorded. */
|
|
65
|
+
droppedBetas: boolean;
|
|
66
|
+
/** Thinking signatures or `redacted_thinking` blocks were removed for this destination. */
|
|
67
|
+
strippedOpaqueState: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* OAuth only: wire tool name to the caller's name, for every tool the OAuth prefix renamed.
|
|
70
|
+
* The lane maps `tool_use` names in the answer back through it.
|
|
71
|
+
*/
|
|
72
|
+
oauthToolNames?: ReadonlyMap<string, string>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface AnthropicMessagesPassthroughOptions {
|
|
76
|
+
/** The caller's `anthropic-beta` header, handed over by the ingress. */
|
|
77
|
+
callerAnthropicBeta?: string | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The allowlisted copy of `body` with `model` set to the wire model. Shallow: nothing is cloned. */
|
|
81
|
+
export function anthropicMessagesPassthroughBody(
|
|
82
|
+
body: Readonly<Record<string, unknown>>,
|
|
83
|
+
modelId: string,
|
|
84
|
+
): Record<string, unknown> {
|
|
85
|
+
const out: Record<string, unknown> = {};
|
|
86
|
+
for (const [key, value] of Object.entries(body)) {
|
|
87
|
+
if (PASSTHROUGH_FIELD_SET.has(key) && value !== undefined) out[key] = value;
|
|
88
|
+
}
|
|
89
|
+
out.model = modelId;
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type Rec = Record<string, unknown>;
|
|
94
|
+
function isRec(value: unknown): value is Rec {
|
|
95
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** A tool the caller executes (no `type`, or `custom`), as opposed to a typed server tool. */
|
|
99
|
+
function isClientTool(tool: unknown): tool is Rec & { name: string } {
|
|
100
|
+
return isRec(tool) && typeof tool.name === "string" && (tool.type === undefined || tool.type === "custom");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The Claude OAuth request shape the adapter produces, applied to a Messages body: the Claude
|
|
105
|
+
* Code identity as the first system block, and declared client tool names under the OAuth
|
|
106
|
+
* prefix — in `tools`, a named `tool_choice` and the history's `tool_use` blocks. Copy-on-write;
|
|
107
|
+
* the input is not mutated. Two caller names that meet under the prefix are refused rather than
|
|
108
|
+
* guessed at.
|
|
109
|
+
*/
|
|
110
|
+
export function anthropicOAuthWireBody(body: Rec): { body: Rec; toolNames: Map<string, string> } {
|
|
111
|
+
const out: Rec = { ...body };
|
|
112
|
+
const toolNames = new Map<string, string>();
|
|
113
|
+
const owners = new Map<string, string>();
|
|
114
|
+
const wireName = (name: string): string => {
|
|
115
|
+
const wire = applyClaudeToolPrefix(name);
|
|
116
|
+
const owner = owners.get(wire);
|
|
117
|
+
if (owner !== undefined && owner !== name) throw new Error("tool names collide under the Claude OAuth tool prefix");
|
|
118
|
+
owners.set(wire, name);
|
|
119
|
+
if (wire !== name) toolNames.set(wire, name);
|
|
120
|
+
return wire;
|
|
121
|
+
};
|
|
122
|
+
const identity = { type: "text", text: CLAUDE_CODE_SYSTEM_INSTRUCTION };
|
|
123
|
+
if (typeof body.system === "string" && body.system.length > 0) {
|
|
124
|
+
out.system = [identity, { type: "text", text: body.system }];
|
|
125
|
+
} else if (Array.isArray(body.system)) {
|
|
126
|
+
const first = body.system[0];
|
|
127
|
+
const present = isRec(first) && first.type === "text" && first.text === CLAUDE_CODE_SYSTEM_INSTRUCTION;
|
|
128
|
+
out.system = present ? body.system : [identity, ...body.system];
|
|
129
|
+
} else {
|
|
130
|
+
out.system = [identity];
|
|
131
|
+
}
|
|
132
|
+
// Only names the caller declared as its own tools are renamed. A typed tool (a server tool,
|
|
133
|
+
// or a client-executed builtin such as `bash_*`) keeps the name its type fixes, and so do
|
|
134
|
+
// its calls in the history.
|
|
135
|
+
const declared = new Set<string>();
|
|
136
|
+
if (Array.isArray(body.tools)) {
|
|
137
|
+
out.tools = body.tools.map(tool => {
|
|
138
|
+
if (!isClientTool(tool)) return tool;
|
|
139
|
+
declared.add(tool.name);
|
|
140
|
+
return { ...tool, name: wireName(tool.name) };
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const renames = (name: unknown): name is string => typeof name === "string" && declared.has(name);
|
|
144
|
+
if (isRec(body.tool_choice) && body.tool_choice.type === "tool" && renames(body.tool_choice.name)) {
|
|
145
|
+
out.tool_choice = { ...body.tool_choice, name: wireName(body.tool_choice.name) };
|
|
146
|
+
}
|
|
147
|
+
const isRenamedUse = (block: unknown): block is Rec & { name: string } => isRec(block) && block.type === "tool_use" && renames(block.name);
|
|
148
|
+
if (Array.isArray(body.messages)) {
|
|
149
|
+
out.messages = body.messages.map(message => {
|
|
150
|
+
if (!isRec(message) || !Array.isArray(message.content) || !message.content.some(isRenamedUse)) return message;
|
|
151
|
+
return {
|
|
152
|
+
...message,
|
|
153
|
+
content: message.content.map(block => isRenamedUse(block) ? { ...block, name: wireName(block.name) } : block),
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return { body: out, toolNames };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* What the native lane sends for this provider, without a credential: the allowlisted body,
|
|
162
|
+
* opaque state kept only for first-party Anthropic, and the OAuth request shape for an OAuth
|
|
163
|
+
* provider. `count_tokens` counts this; the builder below sends it.
|
|
164
|
+
*/
|
|
165
|
+
export function anthropicMessagesNativeWireBody(
|
|
166
|
+
provider: Pick<OcxProviderConfig, "baseUrl" | "authMode">,
|
|
167
|
+
modelId: string,
|
|
168
|
+
body: Readonly<Record<string, unknown>>,
|
|
169
|
+
): { wireBody: Rec; strippedOpaqueState: boolean; oauthToolNames?: Map<string, string> } {
|
|
170
|
+
const allowlisted = anthropicMessagesPassthroughBody(body, modelId);
|
|
171
|
+
const opaque = opaqueStateForDestination(allowlisted, credentialDomainFor(provider));
|
|
172
|
+
if (provider.authMode !== "oauth") return { wireBody: opaque.body, strippedOpaqueState: opaque.stripped };
|
|
173
|
+
const oauth = anthropicOAuthWireBody(opaque.body);
|
|
174
|
+
return { wireBody: oauth.body, strippedOpaqueState: opaque.stripped, oauthToolNames: oauth.toolNames };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build the upstream request. Throws the adapter's own errors for a missing credential or a
|
|
179
|
+
* malformed or unresolved base URL, and refuses an OAuth credential for any destination other
|
|
180
|
+
* than first-party Anthropic. `config` is accepted for parity with the other passthrough
|
|
181
|
+
* builders; no config key changes the wire today.
|
|
182
|
+
*/
|
|
183
|
+
export function buildAnthropicMessagesPassthroughRequest(
|
|
184
|
+
provider: OcxProviderConfig,
|
|
185
|
+
modelId: string,
|
|
186
|
+
body: Readonly<Record<string, unknown>>,
|
|
187
|
+
_config?: OcxConfig,
|
|
188
|
+
options: AnthropicMessagesPassthroughOptions = {},
|
|
189
|
+
): AnthropicMessagesPassthroughRequest {
|
|
190
|
+
const oauth = provider.authMode === "oauth";
|
|
191
|
+
if (provider.authMode !== undefined && provider.authMode !== "key" && !oauth) {
|
|
192
|
+
throw new Error("managed native Messages requires a key-auth or OAuth anthropic provider");
|
|
193
|
+
}
|
|
194
|
+
const domain = credentialDomainFor(provider);
|
|
195
|
+
if (oauth && !domain?.firstPartyAnthropic) {
|
|
196
|
+
throw new Error("managed native Messages sends Anthropic OAuth credentials only to api.anthropic.com");
|
|
197
|
+
}
|
|
198
|
+
if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") {
|
|
199
|
+
throw new Error(oauth
|
|
200
|
+
? "anthropic oauth token missing — run ocx login anthropic"
|
|
201
|
+
: "anthropic provider requires a non-empty apiKey (authMode: key)");
|
|
202
|
+
}
|
|
203
|
+
const url = resolveAnthropicMessagesUrl(provider);
|
|
204
|
+
const { wireBody, strippedOpaqueState, oauthToolNames } = anthropicMessagesNativeWireBody(provider, modelId, body);
|
|
205
|
+
const headers = anthropicBaseRequestHeaders(wireBody.stream === true);
|
|
206
|
+
if (oauth) applyAnthropicOAuthAuth(headers, provider.apiKey);
|
|
207
|
+
else applyAnthropicKeyAuth(headers, provider);
|
|
208
|
+
// Operator-configured provider headers apply exactly as the adapter applies them.
|
|
209
|
+
if (provider.headers) Object.assign(headers, provider.headers);
|
|
210
|
+
const betas = allowlistAnthropicBetas(options.callerAnthropicBeta, domain?.firstPartyAnthropic ? "first-party" : "compatible");
|
|
211
|
+
mergeAnthropicBetaHeader(headers, betas.betas);
|
|
212
|
+
return {
|
|
213
|
+
url,
|
|
214
|
+
headers,
|
|
215
|
+
body: JSON.stringify(wireBody),
|
|
216
|
+
wireBody,
|
|
217
|
+
droppedBetas: betas.dropped,
|
|
218
|
+
strippedOpaqueState,
|
|
219
|
+
...(oauthToolNames ? { oauthToolNames } : {}),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -116,16 +116,40 @@ let encodeCalls = 0;
|
|
|
116
116
|
* rank by one and can push it across a tier boundary — re-encoding it to different
|
|
117
117
|
* bytes and busting Anthropic's prompt prefix cache for the whole history. Pinning the
|
|
118
118
|
* start position to the image's own identity keeps already-emitted bytes stable across
|
|
119
|
-
* appends. Keys are the
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
119
|
+
* appends. Keys are fixed-size digests of the bytes and canonical media type, so
|
|
120
|
+
* caller-controlled metadata cannot make the retained identity arbitrarily large.
|
|
121
|
+
* The bytes count toward the shared retained-memory budget but are pinned: the
|
|
122
|
+
* shared evictor clears normalization cache slots first and never drops position
|
|
123
|
+
* memory, because a request reads this store before it finishes and a mid-request
|
|
124
|
+
* eviction would make a repeated image lose its pinned position and fall back to
|
|
125
|
+
* an age-derived tier. Positions shrink only through this store's own entry-count
|
|
126
|
+
* cap, applied when positions are committed after the request settles.
|
|
123
127
|
*/
|
|
124
128
|
const POSITION_STORE_MAX_ENTRIES = 4_096;
|
|
125
|
-
const
|
|
129
|
+
const MAX_CANONICAL_MEDIA_TYPE_LENGTH = 127;
|
|
130
|
+
const MEDIA_TYPE_PATTERN = /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/;
|
|
131
|
+
interface PositionEntry { position: number; sizeBytes: number; storedAt: number }
|
|
132
|
+
const emittedPositions = new Map<string, PositionEntry>();
|
|
133
|
+
let positionBytes = 0;
|
|
126
134
|
|
|
127
135
|
function positionKey(b64: string, mediaType: string): string {
|
|
128
|
-
|
|
136
|
+
const normalized = mediaType.trim().toLowerCase();
|
|
137
|
+
const canonical = normalized.length <= MAX_CANONICAL_MEDIA_TYPE_LENGTH && MEDIA_TYPE_PATTERN.test(normalized)
|
|
138
|
+
? normalized
|
|
139
|
+
// Keep invalid/overlong types distinct under an "invalid:" namespace: folding
|
|
140
|
+
// them all onto application/octet-stream let a different invalid type reuse a
|
|
141
|
+
// prior emitted position and demote the second image to a smaller tier. The
|
|
142
|
+
// prefix cannot collide with a valid canonical type (':' fails the pattern).
|
|
143
|
+
: `invalid:${normalized}`;
|
|
144
|
+
return new Bun.CryptoHasher("sha256").update(b64).update("\0").update(canonical).digest("hex");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function deletePositionEntry(key: string): number {
|
|
148
|
+
const entry = emittedPositions.get(key);
|
|
149
|
+
if (!entry) return 0;
|
|
150
|
+
emittedPositions.delete(key);
|
|
151
|
+
positionBytes -= entry.sizeBytes;
|
|
152
|
+
return entry.sizeBytes;
|
|
129
153
|
}
|
|
130
154
|
|
|
131
155
|
/**
|
|
@@ -134,12 +158,13 @@ function positionKey(b64: string, mediaType: string): string {
|
|
|
134
158
|
*/
|
|
135
159
|
export function recordedEmittedPosition(b64: string, mediaType: string): number | undefined {
|
|
136
160
|
const key = positionKey(b64, mediaType);
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
161
|
+
const entry = emittedPositions.get(key);
|
|
162
|
+
if (entry !== undefined) {
|
|
139
163
|
emittedPositions.delete(key);
|
|
140
|
-
|
|
164
|
+
entry.storedAt = Date.now();
|
|
165
|
+
emittedPositions.set(key, entry);
|
|
141
166
|
}
|
|
142
|
-
return
|
|
167
|
+
return entry?.position;
|
|
143
168
|
}
|
|
144
169
|
|
|
145
170
|
/**
|
|
@@ -153,15 +178,17 @@ export function recordEmittedPosition(b64: string, mediaType: string, pos: numbe
|
|
|
153
178
|
const key = positionKey(b64, mediaType);
|
|
154
179
|
const existing = emittedPositions.get(key);
|
|
155
180
|
if (existing !== undefined) {
|
|
156
|
-
|
|
157
|
-
pos = Math.max(existing, pos);
|
|
181
|
+
deletePositionEntry(key);
|
|
182
|
+
pos = Math.max(existing.position, pos);
|
|
158
183
|
}
|
|
159
184
|
while (emittedPositions.size + 1 > POSITION_STORE_MAX_ENTRIES) {
|
|
160
185
|
const oldest = emittedPositions.keys().next().value;
|
|
161
186
|
if (oldest === undefined) break;
|
|
162
|
-
|
|
187
|
+
deletePositionEntry(oldest);
|
|
163
188
|
}
|
|
164
|
-
|
|
189
|
+
const sizeBytes = cacheEncoder.encode(key).byteLength + 16;
|
|
190
|
+
emittedPositions.set(key, { position: pos, sizeBytes, storedAt: Date.now() });
|
|
191
|
+
positionBytes += sizeBytes;
|
|
165
192
|
enforceAppOwnedMemoryBudget();
|
|
166
193
|
}
|
|
167
194
|
|
|
@@ -227,6 +254,8 @@ export function getNormalizeStatsForTests(): {
|
|
|
227
254
|
cacheBytes: number;
|
|
228
255
|
sentinelEntries: number;
|
|
229
256
|
metadataBytes: number;
|
|
257
|
+
positionEntries: number;
|
|
258
|
+
positionBytes: number;
|
|
230
259
|
oldestAt: number | null;
|
|
231
260
|
} {
|
|
232
261
|
return {
|
|
@@ -235,12 +264,15 @@ export function getNormalizeStatsForTests(): {
|
|
|
235
264
|
cacheBytes,
|
|
236
265
|
sentinelEntries: cacheSentinelEntries,
|
|
237
266
|
metadataBytes: cacheMetadataBytes,
|
|
267
|
+
positionEntries: emittedPositions.size,
|
|
268
|
+
positionBytes,
|
|
238
269
|
oldestAt: cache.values().next().value?.storedAt ?? null,
|
|
239
270
|
};
|
|
240
271
|
}
|
|
241
272
|
export function resetNormalizeStateForTests(): void {
|
|
242
273
|
cache.clear();
|
|
243
274
|
emittedPositions.clear();
|
|
275
|
+
positionBytes = 0;
|
|
244
276
|
cacheBytes = 0;
|
|
245
277
|
cacheMetadataBytes = 0;
|
|
246
278
|
cacheSentinelEntries = 0;
|
|
@@ -260,17 +292,22 @@ export function anthropicImageNormalizeRetainedStoreSnapshot(): {
|
|
|
260
292
|
oldestAt: number | null;
|
|
261
293
|
} {
|
|
262
294
|
return {
|
|
263
|
-
count: cache.size,
|
|
264
|
-
bytes: cacheBytes,
|
|
295
|
+
count: cache.size + emittedPositions.size,
|
|
296
|
+
bytes: cacheBytes + positionBytes,
|
|
265
297
|
evictableBytes: cacheBytes,
|
|
266
|
-
pinnedBytes:
|
|
298
|
+
pinnedBytes: positionBytes,
|
|
267
299
|
oldestAt: cache.values().next().value?.storedAt ?? null,
|
|
268
300
|
};
|
|
269
301
|
}
|
|
270
302
|
|
|
271
303
|
export function evictOldestAnthropicImageNormalizeForBudget(): number {
|
|
272
|
-
|
|
273
|
-
|
|
304
|
+
// Position memory is pinned for the life of a request (see the store comment):
|
|
305
|
+
// the shared budget reclaims normalization cache slots first, and a position
|
|
306
|
+
// entry is only released by recordEmittedPosition's own entry-count cap after
|
|
307
|
+
// the request settles. A cache miss is cheaper than losing an image's pinned
|
|
308
|
+
// ladder position mid-request.
|
|
309
|
+
const cacheOldest = cache.entries().next().value as [string, CacheEntry] | undefined;
|
|
310
|
+
return cacheOldest ? deleteCacheEntry(cacheOldest[0]) : 0;
|
|
274
311
|
}
|
|
275
312
|
|
|
276
313
|
/** Default encoder: Bun.Image resize-to-fit + JPEG at the given quality. */
|
|
@@ -165,6 +165,15 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
165
165
|
// peak memory, not throughput (native encode parallelism lives below this layer).
|
|
166
166
|
// entries[] stays index-addressed, so completion order never affects output order
|
|
167
167
|
// or the sequential demotion loop below.
|
|
168
|
+
// #4532: snapshot every target's recorded emitted position BEFORE any worker
|
|
169
|
+
// starts. The read used to happen inside the worker loop, so a concurrent request
|
|
170
|
+
// (or this request's own earlier encodes) could fill the position store's
|
|
171
|
+
// entry-count cap and evict an entry between collection and processing — a later
|
|
172
|
+
// occurrence then fell back to an age-derived tier and busted the prompt prefix
|
|
173
|
+
// cache. Reading a fixed pre-pass snapshot keeps every occurrence on the position
|
|
174
|
+
// it had when this request started.
|
|
175
|
+
const recordedPositions = targets.map(t =>
|
|
176
|
+
!t.base64 ? undefined : recordedEmittedPosition(t.base64, t.mediaType.toLowerCase()));
|
|
168
177
|
let nextIndex = 0;
|
|
169
178
|
let firstError: unknown;
|
|
170
179
|
let failed = false;
|
|
@@ -203,16 +212,17 @@ export async function normalizeImageTargets(targets: NormalizeTarget[], options:
|
|
|
203
212
|
// Anthropic's prompt prefix cache. tierBias (413 retry) applies on top of
|
|
204
213
|
// either base and still clamps to TERMINAL_POS.
|
|
205
214
|
//
|
|
206
|
-
// Every read
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
// can appear more than once in one
|
|
210
|
-
// those occurrences onto one entry.
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
// the
|
|
214
|
-
// age tier on a cold store,
|
|
215
|
-
|
|
215
|
+
// Every read sees the pre-request snapshot taken above: nothing is written
|
|
216
|
+
// until the whole request settles (see the record loop at the end), and no
|
|
217
|
+
// mid-pass eviction can change what an occurrence sees. That is
|
|
218
|
+
// load-bearing, not incidental: an image can appear more than once in one
|
|
219
|
+
// history, and identity keying collapses those occurrences onto one entry.
|
|
220
|
+
// Writing during the pass let the OLDEST occurrence's tier win a race
|
|
221
|
+
// against the newest one and drag it down — 30 copies of a screenshot all
|
|
222
|
+
// landed on the oldest copy's tier instead of the age pyramid. Reading a
|
|
223
|
+
// fixed snapshot gives each occurrence its own age tier on a cold store,
|
|
224
|
+
// which is the pre-#4532 behaviour.
|
|
225
|
+
const recorded = recordedPositions[i];
|
|
216
226
|
const pos = Math.min((recorded ?? initialPosition(newestFirstIndex, 0)) + Math.max(0, bias), TERMINAL_POS);
|
|
217
227
|
const result = await process(b64, pos, sourceMedia);
|
|
218
228
|
if (result.kind === "failed") {
|