@bitkyc08/opencodex 2.58.0 → 2.60.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/README.md +28 -10
- package/gui/dist/assets/index-BTuCbqQd.css +1 -0
- package/gui/dist/assets/index-DoBVdPHP.js +134 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/crusoe.svg +1 -0
- package/gui/dist/provider-icons/opper.svg +3 -0
- package/package.json +4 -1
- package/src/adapters/anthropic-image-codec.ts +16 -2
- package/src/adapters/anthropic-image-normalize.ts +49 -2
- package/src/adapters/anthropic.ts +4 -1
- package/src/adapters/base.ts +34 -1
- package/src/adapters/coding-agent/turn.ts +22 -2
- package/src/adapters/command-code.ts +50 -3
- package/src/adapters/cursor/catalog.ts +11 -0
- package/src/adapters/cursor/checkpoint-store.ts +3 -0
- package/src/adapters/cursor/discovery.ts +11 -8
- package/src/adapters/cursor/effort-map.ts +16 -2
- package/src/adapters/cursor/envelope-echo.ts +55 -2
- package/src/adapters/cursor/live-transport.ts +26 -9
- package/src/adapters/cursor/message-mapper.ts +3 -2
- package/src/adapters/cursor/protobuf-request.ts +8 -5
- package/src/adapters/cursor/request-builder.ts +21 -4
- package/src/adapters/cursor/thread-continuity.ts +105 -31
- package/src/adapters/cursor/tool-guidance.ts +5 -4
- package/src/adapters/cursor/transport.ts +19 -0
- package/src/adapters/cursor.ts +45 -2
- package/src/adapters/devin/cloud-direct/chat.ts +14 -3
- package/src/adapters/devin/cloud-direct/index.ts +7 -0
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +140 -0
- package/src/adapters/devin.ts +125 -26
- package/src/adapters/google-antigravity-replay.ts +1 -1
- package/src/adapters/google-antigravity-wire.ts +55 -4
- package/src/adapters/google-http.ts +57 -11
- package/src/adapters/google-tool-schema.ts +595 -31
- package/src/adapters/google-wire-compiler.ts +93 -10
- package/src/adapters/google-wire-shape.ts +461 -0
- package/src/adapters/google.ts +60 -10
- package/src/adapters/openai-chat/response-events.ts +61 -0
- package/src/adapters/openai-chat-images.ts +3 -1
- package/src/adapters/openai-chat.ts +10 -11
- package/src/adapters/openai-responses/image-gen.ts +8 -6
- package/src/adapters/openai-responses/passthrough.ts +25 -4
- package/src/adapters/openai-responses/reasoning.ts +7 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
- package/src/adapters/openai-responses/tool-schema.ts +19 -7
- package/src/adapters/opencode-go-additional-tools.ts +12 -2
- package/src/adapters/responses-tool-schema.ts +76 -46
- package/src/adapters/run-turn-queue.ts +17 -4
- package/src/bridge/response-json.ts +1 -1
- package/src/bridge/sse.ts +28 -35
- package/src/claude/context-windows.ts +22 -0
- package/src/claude/outbound.ts +35 -4
- package/src/cli/account-api.ts +4 -3
- package/src/cli/account-extended.ts +26 -6
- package/src/cli/account-orca-import.ts +63 -0
- package/src/cli/account.ts +32 -4
- package/src/cli/capabilities.ts +40 -0
- package/src/cli/claude.ts +29 -1
- package/src/cli/codex-cli-update.ts +97 -2
- package/src/cli/dispatch.ts +57 -3
- package/src/cli/doctor.ts +218 -4
- package/src/cli/help.ts +4 -1
- package/src/cli/hub.ts +3 -2
- package/src/cli/index.ts +95 -22
- package/src/cli/models-runtime.ts +33 -4
- package/src/cli/opencode.ts +2 -2
- package/src/cli/provider.ts +13 -1
- package/src/cli/registry.ts +11 -1
- package/src/cli/runtime-api.ts +44 -0
- package/src/cli/start-args.ts +94 -0
- package/src/cli/system-command.ts +2 -0
- package/src/client/machine-api.ts +6 -5
- package/src/client/machine-listener.ts +16 -3
- package/src/client/runtime.ts +26 -2
- package/src/clients/config-export/constants.ts +2 -3
- package/src/clients/config-export.ts +5 -5
- package/src/codex/account-store.ts +146 -5
- package/src/codex/auth-api/account-list.ts +19 -11
- package/src/codex/auth-api/pool-quota-probe.ts +44 -10
- package/src/codex/auth-api/routes.ts +17 -2
- package/src/codex/auth-context.ts +16 -12
- package/src/codex/catalog/build-entries.ts +25 -4
- package/src/codex/catalog/derive-entry.ts +8 -1
- package/src/codex/catalog/effort.ts +10 -6
- package/src/codex/catalog/gather-capture.ts +22 -2
- package/src/codex/catalog/model-hints.ts +66 -33
- package/src/codex/catalog/parsing.ts +90 -5
- package/src/codex/catalog/provider-models.ts +19 -2
- package/src/codex/catalog/reserve-warn.ts +96 -0
- package/src/codex/catalog/retained-sync.ts +41 -26
- package/src/codex/catalog/routed-gather.ts +61 -3
- package/src/codex/cli-installation-identity.ts +210 -0
- package/src/codex/cli-installation-targets.ts +158 -0
- package/src/codex/context-compat.ts +5 -2
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desired-state.ts +4 -1
- package/src/codex/history-job.ts +6 -6
- package/src/codex/history-provider.ts +24 -167
- package/src/codex/history-rollout-read.ts +174 -0
- package/src/codex/history-state-open.ts +105 -0
- package/src/codex/inject/config-toml.ts +44 -2
- package/src/codex/inject.ts +3 -2
- package/src/codex/internal/catalog-writer.ts +33 -1
- package/src/codex/lineage.ts +83 -32
- package/src/codex/loopback-target.ts +31 -0
- package/src/codex/main-account-hard-lock.ts +2 -1
- package/src/codex/main-account.ts +10 -3
- package/src/codex/main-device-reauth.ts +17 -9
- package/src/codex/model-cache.ts +47 -0
- package/src/codex/model-entitlements.ts +80 -2
- package/src/codex/observed-model-denials.ts +230 -0
- package/src/codex/orca-auth-source.ts +94 -0
- package/src/codex/orca-import.ts +219 -0
- package/src/codex/prompt-text-probe.ts +289 -16
- package/src/codex/quota-401-recovery.ts +12 -0
- package/src/codex/quota-types.ts +65 -0
- package/src/codex/quota.ts +24 -19
- package/src/codex/routing/cooldown-math.ts +8 -47
- package/src/codex/routing/pin-drain.ts +57 -0
- package/src/codex/routing.ts +20 -16
- package/src/codex/shim.ts +1 -1
- package/src/codex/subagent-model-fallback.ts +114 -2
- package/src/codex/windows-installation-files.ts +224 -0
- package/src/combos/failover.ts +125 -5
- package/src/config/admitted-identity.ts +222 -0
- package/src/config/diagnostics.ts +43 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/load-degrade.ts +33 -0
- package/src/config/pending-teardown.ts +8 -0
- package/src/config/process-state.ts +36 -3
- package/src/config/provider-relative-send-path.ts +16 -0
- package/src/config/proxy-env.ts +31 -7
- package/src/config/schema/compaction-triggers.ts +11 -0
- package/src/config/schema/config-schema.ts +25 -0
- package/src/config/schema/leaf-validators.ts +76 -17
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +410 -258
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/reset-coupons.ts +38 -19
- package/src/images/loop.ts +6 -1
- package/src/integrations/aside-profile-context.ts +37 -3
- package/src/integrations/aside-profile-journal.ts +68 -3
- package/src/integrations/aside-profiles.ts +128 -3
- package/src/integrations/mutation-plan.ts +815 -0
- package/src/integrations/writer.ts +85 -99
- package/src/lab/live/transport.ts +4 -0
- package/src/lab/live/types.ts +5 -0
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/lib/admin-secrets.ts +9 -1
- package/src/lib/bounded-body.ts +4 -2
- package/src/lib/debug-log-buffer.ts +6 -1
- package/src/lib/debug.ts +23 -0
- package/src/lib/destination-policy.ts +48 -6
- package/src/lib/errors.ts +82 -15
- package/src/lib/http-response-semantics.ts +57 -0
- package/src/lib/lab-live-pinned-sender.ts +26 -12
- package/src/lib/local-destinations.ts +32 -5
- package/src/lib/pinned-http.ts +142 -2
- package/src/lib/plain-data.ts +103 -0
- package/src/lib/process-control.ts +13 -5
- package/src/lib/provider-outbound.ts +53 -5
- package/src/lib/proxy-env.ts +70 -3
- package/src/lib/request-execution-budget.ts +11 -3
- package/src/lib/response-body-inactivity.ts +193 -0
- package/src/lib/retry-delay.ts +69 -0
- package/src/lib/socks5-fetch.ts +741 -0
- package/src/lib/spend-ledger-owner.ts +364 -0
- package/src/lib/spend-reservation-ledger.ts +332 -35
- package/src/lib/windows-system-proxy.ts +16 -11
- package/src/lib/workflow-budget.ts +145 -8
- package/src/oauth/account-quota-rank.ts +72 -15
- package/src/oauth/callback-server.ts +4 -3
- package/src/oauth/generic-account-failover.ts +41 -27
- package/src/oauth/health.ts +12 -1
- package/src/oauth/index.ts +3 -107
- package/src/oauth/login-flow-state.ts +127 -0
- package/src/oauth/orcarouter.ts +15 -2
- package/src/oauth/store.ts +8 -0
- package/src/providers/codex-capacity.ts +9 -0
- package/src/providers/derive.ts +34 -17
- package/src/providers/devin-cli-authmode-migration.ts +14 -10
- package/src/providers/devin-provider-merge-migration.ts +33 -12
- package/src/providers/free-directory.ts +20 -2
- package/src/providers/key-failover.ts +327 -25
- package/src/providers/model-rename-migration.ts +56 -1
- package/src/providers/model-rename-startup.ts +7 -5
- package/src/providers/openai-sidecar.ts +4 -0
- package/src/providers/openai-virtual-models.ts +42 -2
- package/src/providers/opencode-go-transport.ts +14 -5
- package/src/providers/quota/antigravity.ts +22 -2
- package/src/providers/quota/report-cache.ts +3 -0
- package/src/providers/quota/vendor-probes-key.ts +1 -1
- package/src/providers/registry/entries-core.ts +39 -17
- package/src/providers/registry/entries-extended.ts +131 -1
- package/src/providers/registry/model-ids.ts +168 -0
- package/src/providers/registry/model-seeds.ts +87 -21
- package/src/providers/registry/types.ts +2 -0
- package/src/providers/resolved-model-policy-merge.ts +167 -0
- package/src/providers/resolved-model-policy.ts +406 -0
- package/src/providers/stale-vision-classification-migration.ts +137 -0
- package/src/responses/apply-patch-envelope.ts +32 -11
- package/src/responses/bridge-search-replay-cache.ts +152 -0
- package/src/responses/code-mode-helper-compat.ts +26 -16
- package/src/responses/custom-tool-compat.ts +1 -1
- package/src/responses/freeform-wrapper-scan.ts +279 -0
- package/src/responses/hosted-tool-policy.ts +85 -2
- package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
- package/src/responses/progressive-freeform-input.ts +130 -0
- package/src/responses/reasoning-envelope.ts +30 -0
- package/src/responses/schema.ts +9 -2
- package/src/responses/state.ts +5 -12
- package/src/responses/tool-name-aliases.ts +15 -1
- package/src/router.ts +91 -115
- package/src/routing/compatibility/behavior.ts +9 -0
- package/src/routing/compatibility/subject.ts +16 -1
- package/src/server/adapter-resolve.ts +9 -0
- package/src/server/auth-cors.ts +29 -0
- package/src/server/chat-completions.ts +13 -5
- package/src/server/chat-native-sse.ts +26 -9
- package/src/server/chat-native.ts +10 -4
- package/src/server/claude-messages.ts +30 -5
- package/src/server/effort-row.ts +11 -3
- package/src/server/grok-responses-control-frame.ts +160 -1
- package/src/server/gui-static.ts +36 -2
- package/src/server/inbound-body-admission.ts +187 -0
- package/src/server/index/serve-options.ts +86 -29
- package/src/server/index/spend-ledger-lifecycle.ts +66 -0
- package/src/server/index/websocket-handler.ts +6 -1
- package/src/server/index.ts +24 -25
- package/src/server/management/api-access.ts +3 -4
- package/src/server/management/aside-profile-routes.ts +266 -7
- package/src/server/management/config-routes.ts +48 -7
- package/src/server/management/context.ts +3 -0
- package/src/server/management/integration-routes.ts +287 -5
- package/src/server/management/metrics-routes.ts +20 -0
- package/src/server/management/model-rows.ts +224 -12
- package/src/server/management/provider-capability-config.ts +35 -7
- package/src/server/management/provider-routes.ts +70 -18
- package/src/server/management/route-registry.ts +14 -0
- package/src/server/management/shared.ts +10 -3
- package/src/server/management/system-restart.ts +7 -2
- package/src/server/management/system-routes.ts +2 -0
- package/src/server/management/usage-aggregate-cache.ts +4 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/management-auth.ts +15 -1
- package/src/server/proxy-liveness.ts +97 -2
- package/src/server/readiness.ts +29 -10
- package/src/server/relay-eager.ts +24 -2
- package/src/server/relay.ts +136 -34
- package/src/server/request-log.ts +80 -3
- package/src/server/request-metrics.ts +236 -0
- package/src/server/responses/adapter-continuation.ts +74 -30
- package/src/server/responses/adapter-delivery.ts +39 -8
- package/src/server/responses/adapter-dispatch.ts +63 -30
- package/src/server/responses/compact.ts +89 -18
- package/src/server/responses/compaction-routing.ts +111 -0
- package/src/server/responses/core-codex-account.ts +90 -24
- package/src/server/responses/core-combo.ts +7 -7
- package/src/server/responses/core-normalize.ts +16 -15
- package/src/server/responses/core-opaque-recovery.ts +1 -0
- package/src/server/responses/core-options.ts +4 -0
- package/src/server/responses/encrypted-payload.ts +20 -2
- package/src/server/responses/fetch-helpers.ts +68 -2
- package/src/server/responses/passthrough-delivery.ts +41 -15
- package/src/server/responses/passthrough-dispatch.ts +145 -53
- package/src/server/responses/passthrough-execution.ts +11 -1
- package/src/server/responses/policy-fallback.ts +5 -13
- package/src/server/responses/request-prepare.ts +96 -17
- package/src/server/responses/request-send-budget.ts +89 -8
- package/src/server/responses/request-sidecar-auth.ts +17 -9
- package/src/server/responses/request-spend.ts +38 -9
- package/src/server/responses/request-transport.ts +15 -12
- package/src/server/responses/run-turn-execution.ts +45 -8
- package/src/server/responses/sidecar-execution.ts +19 -2
- package/src/server/responses/ws-upstream.ts +16 -28
- package/src/server/responses-custom-tool-repair.ts +29 -56
- package/src/server/responses-undeclared-tool-guard.ts +31 -1
- package/src/server/sse-frame-buffer.ts +12 -10
- package/src/server/sse-payload-rewrite.ts +37 -10
- package/src/server/system-env-shell.ts +5 -1
- package/src/server/system-env.ts +7 -1
- package/src/server/workflow-refusal.ts +56 -2
- package/src/service/cli.ts +16 -6
- package/src/service/guards.ts +10 -0
- package/src/service/health.ts +43 -0
- package/src/service/state.ts +7 -2
- package/src/tray/windows-tray.ps1 +155 -3
- package/src/types/accounts.ts +4 -0
- package/src/types/config.ts +111 -6
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +9 -1
- package/src/types/tools.ts +14 -0
- package/src/types/wire.ts +9 -1
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +28 -0
- package/src/usage/log.ts +87 -4
- package/src/vision/eligibility.ts +88 -9
- package/src/vision/plan.ts +34 -10
- package/src/web-search/executor.ts +41 -2
- package/src/web-search/loop.ts +6 -1
- package/src/web-search/passthrough-bridge.ts +39 -5
- package/gui/dist/assets/index-BbrHOIY0.js +0 -128
- package/gui/dist/assets/index-C5-RdDmD.css +0 -1
|
@@ -49,7 +49,7 @@ const DATA: Record<string, readonly Row[]> = {
|
|
|
49
49
|
"moonshot": [["kimi-k2.5",262144,65536,"text,image",1,null,0,0,0,0]],
|
|
50
50
|
"openai": [["codex-mini-latest",200000,100000,"text",1,null,1.5,6,0.375,0],["gpt-4",8192,8192,"text",0,null,30,60,0,0],["gpt-4-turbo",128000,4096,"text,image",0,null,10,30,0,0],["gpt-4.1",1047576,32768,"text,image",0,null,2,8,0.5,0],["gpt-4.1-mini",1047576,32768,"text,image",0,null,0.4,1.6,0.1,0],["gpt-4.1-nano",1047576,32768,"text,image",0,null,0.1,0.4,0.025,0],["gpt-4o",128000,16384,"text,image",0,null,2.5,10,1.25,0],["gpt-4o-2024-05-13",128000,4096,"text,image",0,null,5,15,0,0],["gpt-4o-2024-08-06",128000,16384,"text,image",0,null,2.5,10,1.25,0],["gpt-4o-2024-11-20",128000,16384,"text,image",0,null,2.5,10,1.25,0],["gpt-4o-mini",128000,16384,"text,image",0,null,0.15,0.6,0.075,0],["gpt-5",400000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5-chat-latest",128000,16384,"text,image",0,null,1.25,10,0.125,0],["gpt-5-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5-mini",400000,128000,"text,image",1,null,0.25,2,0.025,0],["gpt-5-nano",400000,128000,"text,image",1,null,0.05,0.4,0.005,0],["gpt-5-pro",400000,272000,"text,image",1,null,15,120,0,0],["gpt-5.1",400000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-chat-latest",128000,16384,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-codex-max",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-codex-mini",272000,128000,"text,image",1,null,0.25,2,0.025,0],["gpt-5.2",400000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.2-chat-latest",128000,16384,"text,image",1,null,1.75,14,0.175,0],["gpt-5.2-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.2-pro",400000,128000,"text,image",1,null,21,168,0,0],["gpt-5.3-chat-latest",128000,16384,"text,image",0,null,1.75,14,0.175,0],["gpt-5.3-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.3-codex-spark",128000,32000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.4",1050000,128000,"text,image",1,null,2.5,15,0.25,0],["gpt-5.4-mini",400000,128000,"text,image",1,null,0.75,4.5,0.075,0],["gpt-5.4-nano",400000,128000,"text,image",1,null,0.2,1.25,0.02,0],["gpt-5.4-pro",1050000,128000,"text,image",1,null,30,180,0,0],["gpt-5.5",1050000,128000,"text,image",1,null,5,30,0.5,0],["gpt-5.5-pro",1050000,128000,"text,image",1,null,30,180,0,0],["gpt-5.6",373000,128000,"text,image",1,null,5,30,0.5,6.25],["gpt-5.6-luna",373000,128000,"text,image",1,null,0.2,1.2,0.02,0.25],["gpt-5.6-sol",373000,128000,"text,image",1,null,5,30,0.5,6.25],["gpt-5.6-terra",373000,128000,"text,image",1,null,2,12,0.2,2.5],["gpt-realtime-2.1",128000,32000,"text,image",1,null,4,24,0.4,0],["o1",200000,100000,"text,image",1,null,15,60,7.5,0],["o1-pro",200000,100000,"text,image",1,null,150,600,0,0],["o3",200000,100000,"text,image",1,null,2,8,0.5,0],["o3-deep-research",200000,100000,"text,image",1,null,10,40,2.5,0],["o3-mini",200000,100000,"text",1,null,1.1,4.4,0.55,0],["o3-pro",200000,100000,"text,image",1,null,20,80,0,0],["o4-mini",200000,100000,"text,image",1,null,1.1,4.4,0.275,0],["o4-mini-deep-research",200000,100000,"text,image",1,null,2,8,0.5,0]],
|
|
51
51
|
"openai-codex": [["codex-auto-review",1000000,128000,"text,image",1,null,0,0,0,0],["gpt-5",400000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5-codex-mini",272000,128000,"text,image",1,null,0,0,0,0],["gpt-5.1",400000,128000,"text,image",1,null,1.25,10,0.13,0],["gpt-5.1-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-codex-max",272000,128000,"text,image",1,null,1.25,10,0.125,0],["gpt-5.1-codex-mini",272000,128000,"text,image",1,null,0.25,2,0.025,0],["gpt-5.2",272000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.2-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.3-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["gpt-5.3-codex-spark",128000,128000,"text",1,null,1.75,14,0.175,0],["gpt-5.4",1000000,128000,"text,image",1,null,2.5,15,0.25,0],["gpt-5.4-mini",272000,128000,"text,image",1,null,0.75,4.5,0.075,0],["gpt-5.4-nano",272000,128000,"text,image",1,null,0.2,1.25,0.02,0],["gpt-5.5",272000,128000,"text,image",1,null,5,30,0.5,0],["gpt-5.6-luna",373000,128000,"text,image",1,null,0.2,1.2,0.02,0.25],["gpt-5.6-sol",373000,128000,"text,image",1,null,5,30,0.5,6.25],["gpt-5.6-terra",373000,128000,"text,image",1,null,2,12,0.2,2.5]],
|
|
52
|
-
"opencode-go": [["deepseek-v4-flash",1000000,384000,"text",1,null,0.14,0.28,0.0028,0],["deepseek-v4-pro",1000000,384000,"text",1,null,1.74,3.48,0.0145,0],["glm-5",204800,131072,"text",1,null,1,3.2,0.2,0],["glm-5.1",200000,131072,"text",1,null,1.4,4.4,0.26,0],["glm-5.2",1000000,131072,"text",1,null,1.4,4.4,0.26,0],["glm-5.3",1000000,131072,"text",1,null,1.4,4.4,0.26,0],["grok-4.5",500000,500000,"text,image",1,null,2,6,0.5,0],["grok-4.6",500000,500000,"text,image",1,null,2,6,0.5,0],["hy3",256000,64000,"text",1,null,0.14,0.58,0.035,0],["kimi-k2.5",262144,262144,"text,image",1,null,0.3,1.9,0,0],["kimi-k2.6",262144,262144,"text,image",1,null,0.95,4,0.2,0],["kimi-k2.7-code",262144,262144,"text,image",1,null,0.95,4,0.19,0],["kimi-k3",1048576,131072,"text,image",1,null,3,15,0.3,0],["mimo-v2-omni",262144,131072,"text,image",1,null,0.4,2,0.08,0],["mimo-v2-pro",1048576,131072,"text",1,null,1,3,0.2,0],["mimo-v2.5",1048576,131072,"text,image",1,null,0.14,0.28,0.0028,0],["mimo-v2.5-pro",1048576,131072,"text",1,null,1.74,3.48,0.0145,0],["minimax-m2.5",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["minimax-m2.7",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["minimax-m3",512000,128000,"text,image",1,null,0.3,1.2,0.06,0],["qwen3.5-plus",1000000,65536,"text,image",1,null,0.4,2.4,0,0],["qwen3.6-plus",1000000,65536,"text,image",1,null,2,6,0.2,2.5],["qwen3.7-max",1000000,65536,"text",1,null,2.5,7.5,0.5,3.125],["qwen3.7-plus",1000000,64000,"text,image",1,null,1.2,4.8,0.12,1.5]],
|
|
52
|
+
"opencode-go": [["deepseek-v4-flash",1000000,384000,"text",1,null,0.14,0.28,0.0028,0],["deepseek-v4-pro",1000000,384000,"text",1,null,1.74,3.48,0.0145,0],["glm-5",204800,131072,"text",1,null,1,3.2,0.2,0],["glm-5.1",200000,131072,"text",1,null,1.4,4.4,0.26,0],["glm-5.2",1000000,131072,"text",1,null,1.4,4.4,0.26,0],["glm-5.3",1000000,131072,"text",1,null,1.4,4.4,0.26,0],["glm-5.3-flash",1000000,131072,"text,image",1],["gpt-5.6-luna",1050000,128000,"text,image",1],["grok-4.5",500000,500000,"text,image",1,null,2,6,0.5,0],["grok-4.6",500000,500000,"text,image",1,null,2,6,0.5,0],["hy3",256000,64000,"text",1,null,0.14,0.58,0.035,0],["hy4-preview",1024000,64000,"text",1],["kimi-k2.5",262144,262144,"text,image",1,null,0.3,1.9,0,0],["kimi-k2.6",262144,262144,"text,image",1,null,0.95,4,0.2,0],["kimi-k2.7-code",262144,262144,"text,image",1,null,0.95,4,0.19,0],["kimi-k3",1048576,131072,"text,image",1,null,3,15,0.3,0],["longcat-2.0",1000000,131072,"text",1],["mimo-v2-omni",262144,131072,"text,image",1,null,0.4,2,0.08,0],["mimo-v2-pro",1048576,131072,"text",1,null,1,3,0.2,0],["mimo-v2.5",1048576,131072,"text,image",1,null,0.14,0.28,0.0028,0],["mimo-v2.5-pro",1048576,131072,"text",1,null,1.74,3.48,0.0145,0],["minimax-m2.5",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["minimax-m2.7",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["minimax-m3",512000,128000,"text,image",1,null,0.3,1.2,0.06,0],["muse-spark-1.2-contributor",1048576,131072,"text,image",1],["muse-spark-1.3-contributor",1048576,131072,"text,image",1],["omen-alpha",500000,128000,"text,image",1],["ox-alpha-free",1000000,131072,"text,image",1],["qwen3.5-plus",1000000,65536,"text,image",1,null,0.4,2.4,0,0],["qwen3.6-plus",1000000,65536,"text,image",1,null,2,6,0.2,2.5],["qwen3.7-max",1000000,65536,"text",1,null,2.5,7.5,0.5,3.125],["qwen3.7-plus",1000000,64000,"text,image",1,null,1.2,4.8,0.12,1.5],["qwen3.8-flash",1000000,131072,"text,image",1],["qwen3.8-max",1000000,131072,"text,image",1],["union-alpha",262144,131072,"text,image",1]],
|
|
53
53
|
"openrouter": [["~anthropic/claude-fable-latest",1000000,128000,"text,image",1,null,10,50,1,12.5],["~anthropic/claude-haiku-latest",200000,64000,"text,image",1,null,1,5,0.09999999999999999,1.25],["~anthropic/claude-opus-latest",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["~anthropic/claude-sonnet-latest",1000000,128000,"text,image",1,null,2,10,0.19999999999999998,2.5],["~google/gemini-flash-latest",1048576,65536,"text,image",1,null,1.5,7.5,0.15,0.08333333333333334],["~google/gemini-pro-latest",1048576,65536,"text,image",1,null,2,12,0.19999999999999998,0.375],["~moonshotai/kimi-latest",1048576,8888,"text,image",1,null,3,15,0.3,0],["~openai/gpt-latest",1050000,128000,"text,image",1,null,5,30,0.5,6.25],["~openai/gpt-mini-latest",400000,128000,"text,image",1,null,0.75,4.5,0.075,0],["~x-ai/grok-latest",500000,8888,"text,image",1,null,2,6,0.3,0],["ai21/jamba-large-1.7",256000,4096,"text",0,null,2,8,0,0],["aion-labs/aion-2.0",131072,32768,"text",1,null,0.7999999999999999,1.5999999999999999,0.19999999999999998,0],["aion-labs/aion-3.0",131072,32768,"text",1,null,3,6,0.75,0],["aion-labs/aion-3.0-mini",131072,32768,"text",1,null,0.7,1.4,0.18,0],["alibaba/tongyi-deepresearch-30b-a3b",131072,131072,"text",1,null,0.09,0.44999999999999996,0.09,0],["allenai/olmo-3.1-32b-instruct",65536,16384,"text",0,null,0.19999999999999998,0.6,0,0],["amazon/nova-2-lite-v1",1000000,65535,"text,image",1,null,0.3,2.5,0,0],["amazon/nova-lite-v1",300000,5120,"text,image",0,null,0.06,0.24,0,0],["amazon/nova-micro-v1",128000,5120,"text",0,null,0.035,0.14,0,0],["amazon/nova-premier-v1",1000000,32000,"text,image",0,null,2.5,12.5,0.625,0],["amazon/nova-pro-v1",300000,5120,"text,image",0,null,0.7999999999999999,3.1999999999999997,0,0],["anthropic/claude-3-haiku",200000,4096,"text,image",0,null,0.25,1.25,0.03,0.3],["anthropic/claude-3.5-haiku",200000,8192,"text,image",0,null,0.7999999999999999,4,0.08,1],["anthropic/claude-3.5-sonnet",200000,8192,"text,image",0,null,6,30,0.6,7.5],["anthropic/claude-3.7-sonnet",200000,128000,"text,image",1,null,3,15,0.3,3.75],["anthropic/claude-3.7-sonnet:thinking",200000,64000,"text,image",1,null,3,15,0.3,3.75],["anthropic/claude-fable-5",1000000,128000,"text,image",1,null,10,50,1,12.5],["anthropic/claude-haiku-4.5",200000,64000,"text,image",0,null,1,5,0.09999999999999999,1.25],["anthropic/claude-opus-4",200000,32000,"text,image",1,null,15,75,1.5,18.75],["anthropic/claude-opus-4.1",200000,32000,"text,image",1,null,15,75,1.5,18.75],["anthropic/claude-opus-4.5",200000,64000,"text,image",1,null,5,25,0.5,6.25],["anthropic/claude-opus-4.6",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["anthropic/claude-opus-4.6-fast",1000000,128000,"text,image",1,null,30,150,3,37.5],["anthropic/claude-opus-4.7",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["anthropic/claude-opus-4.7-fast",1000000,128000,"text,image",1,null,30,150,3,37.5],["anthropic/claude-opus-4.8",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["anthropic/claude-opus-4.8-fast",1000000,128000,"text,image",1,null,10,50,1,12.5],["anthropic/claude-opus-5",1000000,128000,"text,image",1,null,5,25,0.5,6.25],["anthropic/claude-opus-5-fast",1000000,128000,"text,image",1,null,10,50,1,12.5],["anthropic/claude-sonnet-4",1000000,64000,"text,image",1,null,3,15,0.3,3.75],["anthropic/claude-sonnet-4.5",1000000,64000,"text,image",1,null,3,15,0.3,3.75],["anthropic/claude-sonnet-4.6",1000000,128000,"text,image",1,null,3,15,0.3,3.75],["anthropic/claude-sonnet-5",1000000,128000,"text,image",1,null,2,10,0.19999999999999998,2.5],["arcee-ai/trinity-large-preview",131000,8888,"text",0,null,0.15,0.44999999999999996,0,0],["arcee-ai/trinity-large-preview:free",131000,8888,"text",0,null,0,0,0,0],["arcee-ai/trinity-large-thinking",262144,262144,"text",1,null,0.22,0.85,0.06,0],["arcee-ai/trinity-large-thinking:free",262144,80000,"text",1,null,0,0,0,0],["arcee-ai/trinity-mini",131072,131072,"text",1,null,0.045,0.15,0,0],["arcee-ai/trinity-mini:free",131072,8888,"text",1,null,0,0,0,0],["arcee-ai/virtuoso-large",131072,64000,"text",0,null,0.75,1.2,0,0],["auto",2000000,30000,"text,image",1,null,0,0,0,0],["baidu/cobuddy:free",131072,65536,"text",1,null,0,0,0,0],["baidu/ernie-4.5-21b-a3b",131072,8000,"text",0,null,0.07,0.28,0,0],["baidu/ernie-4.5-vl-28b-a3b",131072,8000,"text,image",1,null,0.14,0.56,0,0],["bytedance-seed/seed-1.6",262144,32768,"text,image",1,null,0.25,2,0,0],["bytedance-seed/seed-1.6-flash",262144,32768,"text,image",1,null,0.075,0.3,0,0],["bytedance-seed/seed-2.0-lite",262144,131072,"text,image",1,null,0.25,2,0,0],["bytedance-seed/seed-2.0-mini",262144,131072,"text,image",1,null,0.09999999999999999,0.39999999999999997,0,0],["cohere/command-r-08-2024",128000,4000,"text",0,null,0.15,0.6,0,0],["cohere/command-r-plus-08-2024",128000,4000,"text",0,null,2.5,10,0,0],["cohere/north-mini-code:free",256000,64000,"text",1,null,0,0,0,0],["deepseek/deepseek-chat",163840,16000,"text",0,null,0.20020000000000002,0.8000999999999999,0.15,0],["deepseek/deepseek-chat-v3-0324",163840,65536,"text",1,null,0.27,1.12,0.135,0],["deepseek/deepseek-chat-v3.1",163840,32768,"text",1,null,0.25,0.95,0.13,0],["deepseek/deepseek-r1",163840,16000,"text",1,null,0.7,2.5,0,0],["deepseek/deepseek-r1-0528",163840,32768,"text",1,null,0.5,2.1500000000000004,0.35,0],["deepseek/deepseek-v3.1-terminus",163840,32768,"text",1,null,0.27,1,0.135,0],["deepseek/deepseek-v3.1-terminus:exacto",163840,8888,"text",1,null,0.21,0.7899999999999999,0.16799999999999998,0],["deepseek/deepseek-v3.2",163840,65536,"text",1,null,0.26899999999999996,0.39999999999999997,0.13449999999999998,0],["deepseek/deepseek-v3.2-exp",163840,65536,"text",1,null,0.27,0.41,0,0],["deepseek/deepseek-v4-flash",1048576,384000,"text",1,null,0.09380000000000001,0.18760000000000002,0.01876,0],["deepseek/deepseek-v4-flash:free",1048576,384000,"text",1,null,0,0,0,0],["deepseek/deepseek-v4-pro",1048576,384000,"text",1,null,0.435,0.87,0.003625,0],["essentialai/rnj-1-instruct",32768,8888,"text",0,null,0.15,0.15,0,0],["google/gemini-2.0-flash-001",1048576,8192,"text,image",0,null,0.09999999999999999,0.39999999999999997,0.024999999999999998,0.08333333333333334],["google/gemini-2.0-flash-lite-001",1048576,8192,"text,image",0,null,0.075,0.3,0,0],["google/gemini-2.5-flash",1048576,65535,"text,image",1,null,0.3,2.5,0.03,0.08333333333333334],["google/gemini-2.5-flash-lite",1048576,65535,"text,image",0,null,0.09999999999999999,0.39999999999999997,0.01,0.08333333333333334],["google/gemini-2.5-flash-lite-preview-09-2025",1048576,65535,"text,image",1,null,0.09999999999999999,0.39999999999999997,0.01,0.08333333333333334],["google/gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0.08333333333333334],["google/gemini-2.5-pro",1048576,65536,"text,image",1,null,1.25,10,0.125,0.375],["google/gemini-2.5-pro-preview",1048576,65536,"text,image",1,null,1.25,10,0.125,0.375],["google/gemini-2.5-pro-preview-05-06",1048576,65535,"text,image",1,null,1.25,10,0.125,0.375],["google/gemini-3-flash-preview",1048576,65535,"text,image",1,null,0.5,3,0.049999999999999996,0.08333333333333334],["google/gemini-3-pro-image",131072,32768,"text,image",1,null,2,12,0.19999999999999998,0.375],["google/gemini-3-pro-preview",1048000,64000,"text,image",1,null,2,12,0.19999999999999998,0.375],["google/gemini-3.1-flash-lite",1048576,65536,"text,image",1,null,0.25,1.5,0.024999999999999998,0.08333333333333334],["google/gemini-3.1-flash-lite-preview",1048576,65536,"text,image",0,null,0.25,1.5,0.024999999999999998,0.08333333333333334],["google/gemini-3.1-pro-preview",1048576,65536,"text,image",1,null,2,12,0.19999999999999998,0.375],["google/gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1,null,2,12,0.19999999999999998,0.375],["google/gemini-3.5-flash",1048576,65536,"text,image",1,null,1.5,9,0.15,0.08333333333333334],["google/gemini-3.5-flash-lite",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0.08333333333333334],["google/gemini-3.6-flash",1048576,65536,"text,image",1,null,1.5,7.5,0.15,0.08333333333333334],["google/gemma-3-12b-it",131072,16384,"text,image",0,null,0.049999999999999996,0.15,0,0],["google/gemma-3-27b-it",262144,131072,"text,image",1,null,0.08,0.44999999999999996,0.04,0],["google/gemma-3-27b-it:free",131072,8192,"text,image",0,null,0,0,0,0],["google/gemma-4-26b-a4b-it",262144,262144,"text,image",1,null,0.12,0.35,0.049999999999999996,0],["google/gemma-4-26b-a4b-it:free",262144,32768,"text,image",1,null,0,0,0,0],["google/gemma-4-31b-it",262144,262144,"text,image",1,null,0.14,0.39999999999999997,0.09,0],["google/gemma-4-31b-it:free",262144,32768,"text,image",1,null,0,0,0,0],["ibm-granite/granite-4.1-8b",131072,131072,"text",0,null,0.049999999999999996,0.09999999999999999,0.049999999999999996,0],["inception/mercury",128000,32000,"text",0,null,0.25,0.75,0.024999999999999998,0],["inception/mercury-2",128000,50000,"text",1,null,0.25,0.75,0.024999999999999998,0],["inception/mercury-coder",128000,32000,"text",0,null,0.25,0.75,0.024999999999999998,0],["inclusionai/ling-2.6-1t",262144,32768,"text",0,null,0.075,0.625,0.015,0],["inclusionai/ling-2.6-1t:free",262144,32768,"text",0,null,0,0,0,0],["inclusionai/ling-2.6-flash",262144,32768,"text",0,null,0.01,0.03,0.002,0],["inclusionai/ling-2.6-flash:free",262144,32768,"text",0,null,0,0,0,0],["inclusionai/ling-3.0-flash:free",262144,32768,"text",1,null,0,0,0,0],["inclusionai/ring-2.6-1t",262144,65536,"text",1,null,0.075,0.625,0.015,0],["inclusionai/ring-2.6-1t:free",262144,65536,"text",1,null,0,0,0,0],["kwaipilot/kat-coder-air-v2.5",256000,80000,"text",0,null,0.15,0.6,0.03,0],["kwaipilot/kat-coder-pro",256000,128000,"text",0,null,0.207,0.828,0.0414,0],["kwaipilot/kat-coder-pro-v2",262144,80000,"text",0,null,0.3,1.2,0.06,0],["kwaipilot/kat-coder-pro-v2.5",256000,80000,"text",0,null,0.74,2.96,0.15,0],["liquid/lfm-2.5-1.2b-thinking:free",32768,8888,"text",1,null,0,0,0,0],["meituan/longcat-2.0",1048756,262144,"text",1,null,0.3,1.2,0.006,0],["meituan/longcat-flash-chat",131072,131072,"text",0,null,0.19999999999999998,0.7999999999999999,0.19999999999999998,0],["meta-llama/llama-3-8b-instruct",8192,16384,"text",0,null,0.03,0.04,0,0],["meta-llama/llama-3.1-405b-instruct",131000,8888,"text",0,null,4,4,0,0],["meta-llama/llama-3.1-70b-instruct",131072,16384,"text",0,null,0.39999999999999997,0.39999999999999997,0,0],["meta-llama/llama-3.1-8b-instruct",131072,131072,"text",0,null,0.049999999999999996,0.08,0.024999999999999998,0],["meta-llama/llama-3.3-70b-instruct",131072,128000,"text",0,null,0.13,0.39999999999999997,0,0],["meta-llama/llama-3.3-70b-instruct:free",131072,8888,"text",0,null,0,0,0,0],["meta-llama/llama-4-maverick",1048576,16384,"text,image",0,null,0.19999999999999998,0.7999999999999999,0,0],["meta-llama/llama-4-scout",1310720,16384,"text,image",0,null,0.09999999999999999,0.3,0,0],["meta/muse-spark-1.1",1048576,8888,"text,image",1,null,1.25,4.25,0.15,0],["minimax/minimax-m1",1000000,40000,"text",1,null,0.55,2.2,0,0],["minimax/minimax-m2",204800,131072,"text",1,null,0.255,1.02,0.03,0],["minimax/minimax-m2.1",204800,131072,"text",1,null,0.3,1.2,0.03,0],["minimax/minimax-m2.5",204800,196608,"text",1,null,0.15,0.8999999999999999,0.049999999999999996,0],["minimax/minimax-m2.5:free",262144,8192,"text",1,null,0,0,0,0],["minimax/minimax-m2.7",204800,131072,"text",1,null,0.25,1,0.049999999999999996,0],["minimax/minimax-m3",1048576,512000,"text,image",1,null,0.3,1.2,0.06,0],["mistralai/codestral-2508",256000,8888,"text",0,null,0.3,0.8999999999999999,0.03,0],["mistralai/devstral-2512",262144,8888,"text",0,null,0.39999999999999997,2,0.04,0],["mistralai/devstral-medium",131072,8888,"text",0,null,0.39999999999999997,2,0.04,0],["mistralai/devstral-small",131072,8888,"text",0,null,0.09999999999999999,0.3,0.01,0],["mistralai/ministral-14b-2512",262144,8888,"text,image",0,null,0.19999999999999998,0.19999999999999998,0.02,0],["mistralai/ministral-3b-2512",131072,8888,"text,image",0,null,0.09999999999999999,0.09999999999999999,0.01,0],["mistralai/ministral-8b-2512",262144,8888,"text,image",0,null,0.15,0.15,0.015,0],["mistralai/mistral-large",128000,8888,"text",0,null,2,6,0.19999999999999998,0],["mistralai/mistral-large-2407",131072,8888,"text",0,null,2,6,0.19999999999999998,0],["mistralai/mistral-large-2411",131072,8888,"text",0,null,2,6,0.19999999999999998,0],["mistralai/mistral-large-2512",262144,8888,"text,image",0,null,0.5,1.5,0.049999999999999996,0],["mistralai/mistral-medium-3",131072,8888,"text,image",0,null,0.39999999999999997,2,0.04,0],["mistralai/mistral-medium-3-5",262144,8888,"text,image",1,null,1.5,7.5,0,0],["mistralai/mistral-medium-3.1",131072,8888,"text,image",0,null,0.39999999999999997,2,0.04,0],["mistralai/mistral-nemo",131072,16384,"text",0,null,0.019000000000000003,0.03,0,0],["mistralai/mistral-saba",32768,8888,"text",0,null,0.19999999999999998,0.6,0.02,0],["mistralai/mistral-small-24b-instruct-2501",32768,16384,"text",0,null,0.049999999999999996,0.08,0,0],["mistralai/mistral-small-2603",262144,8888,"text,image",1,null,0.15,0.6,0.015,0],["mistralai/mistral-small-3.1-24b-instruct",131072,131072,"text,image",0,null,0.03,0.11,0.015,0],["mistralai/mistral-small-3.1-24b-instruct:free",128000,8888,"text,image",0,null,0,0,0,0],["mistralai/mistral-small-3.2-24b-instruct",256000,8888,"text,image",0,null,0.09999999999999999,0.3,0.01,0],["mistralai/mistral-small-creative",32768,8888,"text",0,null,0.09999999999999999,0.3,0.01,0],["mistralai/mixtral-8x22b-instruct",65536,13108,"text",0,null,2,6,0.19999999999999998,0],["mistralai/mixtral-8x7b-instruct",32768,16384,"text",0,null,0.54,0.54,0,0],["mistralai/pixtral-large-2411",131072,8888,"text,image",0,null,2,6,0.19999999999999998,0],["mistralai/voxtral-small-24b-2507",32000,8888,"text",0,null,0.09999999999999999,0.3,0.01,0],["moonshotai/kimi-k2",131072,100352,"text",0,null,0.5700000000000001,2.3,0,0],["moonshotai/kimi-k2-0905",262144,100352,"text",0,null,0.6,2.5,0.15,0],["moonshotai/kimi-k2-0905:exacto",262144,8888,"text",0,null,0.6,2.5,0,0],["moonshotai/kimi-k2-thinking",262144,100352,"text",1,null,0.6,2.5,0.15,0],["moonshotai/kimi-k2.5",262144,262144,"text,image",1,null,0.5700000000000001,2.8499999999999996,0.095,0],["moonshotai/kimi-k2.6",262144,262144,"text,image",1,null,0.646,2.7199999999999998,0.1088,0],["moonshotai/kimi-k2.6:free",262144,8888,"text,image",1,null,0,0,0,0],["moonshotai/kimi-k2.7-code",262144,262144,"text,image",1,null,0.78,3.5,0.15,0],["moonshotai/kimi-k3",1048576,131072,"text,image",1,null,3,15,0.3,0],["nex-agi/deepseek-v3.1-nex-n1",131072,163840,"text",0,null,0.135,0.5,0,0],["nex-agi/nex-n2-mini",262144,262144,"text,image",1,null,0.024999999999999998,0.09999999999999999,0.0025,0],["nex-agi/nex-n2-pro",262144,262144,"text,image",1,null,0.25,1,0.024999999999999998,0],["nex-agi/nex-n2-pro:free",262144,262144,"text,image",1,null,0,0,0,0],["nousresearch/deephermes-3-mistral-24b-preview",32768,32768,"text",1,null,0.02,0.09999999999999999,0.01,0],["nousresearch/hermes-4-70b",131072,131072,"text",1,null,0.11,0.38,0.055,0],["nvidia/llama-3.1-nemotron-70b-instruct",131072,16384,"text",0,null,1.2,1.2,0,0],["nvidia/llama-3.3-nemotron-super-49b-v1.5",131072,16384,"text",1,null,0.39999999999999997,0.39999999999999997,0,0],["nvidia/nemotron-3-nano-30b-a3b",262144,228000,"text",1,null,0.049999999999999996,0.19999999999999998,0,0],["nvidia/nemotron-3-nano-30b-a3b:free",256000,8888,"text",1,null,0,0,0,0],["nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",256000,65536,"text,image",1,null,0,0,0,0],["nvidia/nemotron-3-super-120b-a12b",1000000,16384,"text",1,null,0.08499999999999999,0.39999999999999997,0.09999999999999999,0],["nvidia/nemotron-3-super-120b-a12b:free",262144,262144,"text",1,null,0,0,0,0],["nvidia/nemotron-3-ultra-550b-a55b",512288,65536,"text",1,null,0.6,3.5999999999999996,0.19999999999999998,0],["nvidia/nemotron-3-ultra-550b-a55b:free",1000000,65536,"text",1,null,0,0,0,0],["nvidia/nemotron-nano-12b-v2-vl:free",128000,128000,"text,image",1,null,0,0,0,0],["nvidia/nemotron-nano-9b-v2",131072,16384,"text",1,null,0.04,0.16,0,0],["nvidia/nemotron-nano-9b-v2:free",128000,8888,"text",1,null,0,0,0,0],["openai/gpt-3.5-turbo",16385,4096,"text",0,null,0.5,1.5,0,0],["openai/gpt-3.5-turbo-0613",4095,4096,"text",0,null,1,2,0,0],["openai/gpt-3.5-turbo-16k",16385,4096,"text",0,null,3,4,0,0],["openai/gpt-4",8191,8192,"text",0,null,30,60,0,0],["openai/gpt-4-0314",8191,4096,"text",0,null,30,60,0,0],["openai/gpt-4-1106-preview",128000,4096,"text",0,null,10,30,0,0],["openai/gpt-4-turbo",128000,4096,"text,image",0,null,10,30,0,0],["openai/gpt-4-turbo-preview",128000,4096,"text",0,null,10,30,0,0],["openai/gpt-4.1",1047576,32768,"text,image",0,null,2,8,0.5,0],["openai/gpt-4.1-mini",1047576,32768,"text,image",0,null,0.39999999999999997,1.5999999999999999,0.09999999999999999,0],["openai/gpt-4.1-nano",1047576,32768,"text,image",0,null,0.09999999999999999,0.39999999999999997,0.024999999999999998,0],["openai/gpt-4o",128000,16384,"text,image",0,null,2.5,10,1.25,0],["openai/gpt-4o-2024-05-13",128000,4096,"text,image",0,null,5,15,0,0],["openai/gpt-4o-2024-08-06",128000,16384,"text,image",0,null,2.5,10,1.25,0],["openai/gpt-4o-2024-11-20",128000,16384,"text,image",0,null,2.5,10,1.25,0],["openai/gpt-4o-audio-preview",128000,16384,"text",0,null,2.5,10,0,0],["openai/gpt-4o-mini",128000,16384,"text,image",0,null,0.15,0.6,0.075,0],["openai/gpt-4o-mini-2024-07-18",128000,16384,"text,image",0,null,0.15,0.6,0.075,0],["openai/gpt-4o:extended",128000,64000,"text,image",0,null,6,18,0,0],["openai/gpt-5",400000,128000,"text,image",1,null,1.25,10,0.125,0],["openai/gpt-5-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["openai/gpt-5-image",400000,128000,"text,image",1,null,10,10,1.25,0],["openai/gpt-5-image-mini",400000,128000,"text,image",1,null,2.5,2,0.25,0],["openai/gpt-5-mini",400000,128000,"text,image",1,null,0.25,2,0.024999999999999998,0],["openai/gpt-5-nano",400000,128000,"text,image",1,null,0.049999999999999996,0.39999999999999997,0.005,0],["openai/gpt-5-pro",400000,128000,"text,image",1,null,15,120,0,0],["openai/gpt-5.1",400000,128000,"text,image",1,null,1.25,10,0.125,0],["openai/gpt-5.1-chat",128000,16384,"text,image",0,null,1.25,10,0.125,0],["openai/gpt-5.1-codex",272000,128000,"text,image",1,null,1.25,10,0.125,0],["openai/gpt-5.1-codex-max",272000,128000,"text,image",1,null,1.25,10,0.125,0],["openai/gpt-5.1-codex-mini",272000,100000,"text,image",1,null,0.25,2,0.024999999999999998,0],["openai/gpt-5.2",400000,128000,"text,image",1,null,1.75,14,0.175,0],["openai/gpt-5.2-chat",128000,16384,"text,image",0,null,1.75,14,0.175,0],["openai/gpt-5.2-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["openai/gpt-5.2-pro",400000,128000,"text,image",1,null,21,168,0,0],["openai/gpt-5.3-chat",128000,16384,"text",0,null,1.75,14,0.175,0],["openai/gpt-5.3-codex",272000,128000,"text,image",1,null,1.75,14,0.175,0],["openai/gpt-5.4",1050000,128000,"text,image",1,null,2.5,15,0.25,0],["openai/gpt-5.4-mini",400000,128000,"text",0,null,0.75,4.5,0.075,0],["openai/gpt-5.4-nano",400000,128000,"text",0,null,0.19999999999999998,1.25,0.02,0],["openai/gpt-5.4-pro",1050000,128000,"text,image",1,null,30,180,0,0],["openai/gpt-5.5",1050000,128000,"text,image",1,null,5,30,0.5,0],["openai/gpt-5.5-pro",1050000,128000,"text,image",1,null,30,180,0,0],["openai/gpt-5.6-luna",373000,128000,"text,image",1,null,1,6,0.09999999999999999,1.25],["openai/gpt-5.6-luna-pro",373000,128000,"text,image",1,null,1,6,0.09999999999999999,1.25],["openai/gpt-5.6-sol",373000,128000,"text,image",1,null,5,30,0.5,6.25],["openai/gpt-5.6-sol-pro",373000,128000,"text,image",1,null,5,30,0.5,6.25],["openai/gpt-5.6-terra",373000,128000,"text,image",1,null,2.5,15,0.25,3.125],["openai/gpt-5.6-terra-pro",373000,128000,"text,image",1,null,2.5,15,0.25,3.125],["openai/gpt-audio",128000,16384,"text",0,null,2.5,10,0,0],["openai/gpt-audio-mini",128000,16384,"text",0,null,0.6,2.4,0,0],["openai/gpt-chat-latest",400000,128000,"text,image",0,null,5,30,0.5,0],["openai/gpt-oss-120b",131072,131072,"text",1,null,0.037,0.16999999999999998,0,0],["openai/gpt-oss-120b:exacto",131072,8888,"text",1,null,0.039,0.19,0,0],["openai/gpt-oss-120b:free",131072,131072,"text",1,null,0,0,0,0],["openai/gpt-oss-20b",131072,131072,"text",1,null,0.03,0.13,0.03,0],["openai/gpt-oss-20b:free",131072,32768,"text",1,null,0,0,0,0],["openai/gpt-oss-safeguard-20b",131072,65536,"text",1,null,0.075,0.3,0.0375,0],["openai/o1",200000,100000,"text,image",1,null,15,60,7.5,0],["openai/o3",200000,100000,"text,image",1,null,2,8,0.5,0],["openai/o3-deep-research",200000,100000,"text,image",1,null,10,40,2.5,0],["openai/o3-mini",200000,100000,"text",1,null,1.1,4.4,0.55,0],["openai/o3-mini-high",200000,100000,"text",1,null,1.1,4.4,0.55,0],["openai/o3-pro",200000,100000,"text,image",1,null,20,80,0,0],["openai/o4-mini",200000,100000,"text,image",1,null,1.1,4.4,0.275,0],["openai/o4-mini-deep-research",200000,100000,"text,image",1,null,2,8,0.5,0],["openai/o4-mini-high",200000,100000,"text,image",1,null,1.1,4.4,0.275,0],["openrouter/aurora-alpha",128000,50000,"text",1,null,0,0,0,0],["openrouter/auto",2000000,8888,"text,image",1,null,-1000000,-1000000,0,0],["openrouter/auto-beta",2000000,8888,"text,image",1,null,-1000000,-1000000,0,0],["openrouter/elephant-alpha",262144,32768,"text",0,null,0,0,0,0],["openrouter/free",200000,8888,"text,image",1,null,0,0,0,0],["openrouter/healer-alpha",262144,32000,"text,image",1,null,0,0,0,0],["openrouter/hunter-alpha",1048576,32000,"text",1,null,0,0,0,0],["openrouter/owl-alpha",1048756,262144,"text",0,null,0,0,0,0],["poolside/laguna-m.1",262144,32768,"text",1,null,0.19999999999999998,0.39999999999999997,0.09999999999999999,0],["poolside/laguna-m.1:free",262144,32768,"text",1,null,0,0,0,0],["poolside/laguna-s-2.1",1048576,131072,"text",1,null,0.09999999999999999,0.19999999999999998,0.01,0],["poolside/laguna-s-2.1:free",262144,32768,"text",1,null,0,0,0,0],["poolside/laguna-xs-2.1",262144,32768,"text",1,null,0.06,0.12,0.03,0],["poolside/laguna-xs-2.1:free",262144,32768,"text",1,null,0,0,0,0],["poolside/laguna-xs.2",262144,32768,"text",1,null,0.09999999999999999,0.19999999999999998,0.049999999999999996,0],["poolside/laguna-xs.2:free",262144,32768,"text",1,null,0,0,0,0],["prime-intellect/intellect-3",131072,131072,"text",1,null,0.19999999999999998,1.1,0,0],["qwen/qwen-2.5-72b-instruct",32768,16384,"text",0,null,0.36,0.39999999999999997,0,0],["qwen/qwen-2.5-7b-instruct",32768,32768,"text",0,null,0.04,0.09999999999999999,0,0],["qwen/qwen-max",32768,8192,"text",0,null,1.04,4.16,0.20800000000000002,0],["qwen/qwen-plus",1000000,32768,"text",0,null,0.26,0.78,0.052000000000000005,0.325],["qwen/qwen-plus-2025-07-28",1000000,32768,"text",0,null,0.26,0.78,0,0.325],["qwen/qwen-plus-2025-07-28:thinking",1000000,32768,"text",1,null,0.26,0.78,0,0.325],["qwen/qwen-turbo",131072,8192,"text",0,null,0.0325,0.13,0.006500000000000001,0],["qwen/qwen-vl-max",131072,32768,"text,image",0,null,0.52,2.08,0,0],["qwen/qwen3-14b",131072,8192,"text",1,null,0.22749999999999998,0.9099999999999999,0,0],["qwen/qwen3-235b-a22b",131072,8192,"text",1,null,0.45499999999999996,1.8199999999999998,0,0],["qwen/qwen3-235b-a22b-2507",262144,16384,"text",1,null,0.09,0.55,0,0],["qwen/qwen3-235b-a22b-thinking-2507",262144,32768,"text",1,null,0.3,3,0.09999999999999999,0],["qwen/qwen3-30b-a3b",131072,8192,"text",1,null,0.13,0.52,0,0],["qwen/qwen3-30b-a3b-instruct-2507",262144,32000,"text",0,null,0.04815,0.19305,0,0],["qwen/qwen3-30b-a3b-thinking-2507",81920,32768,"text",1,null,0.13,1.56,0.08,0],["qwen/qwen3-32b",131072,16384,"text",1,null,0.08,0.28,0.04,0],["qwen/qwen3-4b",131072,8192,"text",1,null,0.0715,0.273,0,0],["qwen/qwen3-4b:free",40960,8888,"text",1,null,0,0,0,0],["qwen/qwen3-8b",131072,8192,"text",1,null,0.117,0.45499999999999996,0.049999999999999996,0],["qwen/qwen3-coder",262144,65536,"text",0,null,0.3,1,0.09999999999999999,0],["qwen/qwen3-coder-30b-a3b-instruct",262144,32768,"text",0,null,0.07,0.27,0,0],["qwen/qwen3-coder-flash",1000000,65536,"text",0,null,0.195,0.975,0.039,0.24375],["qwen/qwen3-coder-next",262144,262144,"text",0,null,0.11,0.7999999999999999,0.07,0],["qwen/qwen3-coder-plus",1000000,65536,"text",0,null,0.65,3.25,0.13,0.8125],["qwen/qwen3-coder:exacto",262144,65536,"text",0,null,0.22,1.7999999999999998,0.022,0],["qwen/qwen3-coder:free",1048576,262000,"text",0,null,0,0,0,0],["qwen/qwen3-max",262144,32768,"text",1,null,0.78,3.9,0.156,0.975],["qwen/qwen3-max-thinking",262144,32768,"text",1,null,0.78,3.9,0,0],["qwen/qwen3-next-80b-a3b-instruct",262144,262144,"text",0,null,0.09999999999999999,1.1,0.07,0],["qwen/qwen3-next-80b-a3b-instruct:free",262144,8888,"text",0,null,0,0,0,0],["qwen/qwen3-next-80b-a3b-thinking",262144,32768,"text",1,null,0.0975,0.78,0,0],["qwen/qwen3-vl-235b-a22b-instruct",262144,32768,"text,image",0,null,0.21,1.9,0.09999999999999999,0],["qwen/qwen3-vl-235b-a22b-thinking",131072,32768,"text,image",1,null,0.26,2.6,0,0],["qwen/qwen3-vl-30b-a3b-instruct",262144,16384,"text,image",0,null,0.15,0.6,0,0],["qwen/qwen3-vl-30b-a3b-thinking",262144,32768,"text,image",1,null,0.13,1.56,0,0],["qwen/qwen3-vl-32b-instruct",131072,32768,"text,image",0,null,0.10400000000000001,0.41600000000000004,0,0],["qwen/qwen3-vl-8b-instruct",262144,32768,"text,image",0,null,0.117,0.45499999999999996,0,0],["qwen/qwen3-vl-8b-thinking",131072,32768,"text,image",1,null,0.117,1.365,0,0],["qwen/qwen3.5-122b-a10b",262144,65536,"text,image",1,null,0.26,2.08,0,0],["qwen/qwen3.5-27b",262144,65536,"text,image",1,null,0.195,1.56,0,0],["qwen/qwen3.5-35b-a3b",262144,262144,"text,image",1,null,0.14,1,0.049999999999999996,0],["qwen/qwen3.5-397b-a17b",262144,65536,"text,image",1,null,0.39,2.34,0.111,0],["qwen/qwen3.5-9b",262144,262144,"text,image",1,null,0.09999999999999999,0.15,0,0],["qwen/qwen3.5-flash-02-23",1000000,65536,"text,image",1,null,0.065,0.26,0,0.08125],["qwen/qwen3.5-plus-02-15",1000000,65536,"text,image",1,null,0.26,1.56,0,0.325],["qwen/qwen3.5-plus-20260420",1000000,65536,"text,image",1,null,0.3,1.7999999999999998,0,0.375],["qwen/qwen3.6-27b",262144,131072,"text,image",1,null,0.28900000000000003,2.4,0.15,0],["qwen/qwen3.6-35b-a3b",262144,262144,"text,image",1,null,0.14,1,0.049999999999999996,0],["qwen/qwen3.6-flash",1000000,65536,"text,image",1,null,0.1875,1.125,0,0.234375],["qwen/qwen3.6-max-preview",262144,65536,"text",1,null,1.04,6.24,0,1.3],["qwen/qwen3.6-plus",1000000,65536,"text",1,null,0.325,1.95,0,0.40625],["qwen/qwen3.6-plus-preview:free",1000000,32000,"text",1,null,0,0,0,0],["qwen/qwen3.6-plus:free",1000000,65536,"text,image",1,null,0,0,0,0],["qwen/qwen3.7-max",1000000,65536,"text",1,null,1.475,4.425,0.295,1.84375],["qwen/qwen3.7-plus",1000000,65536,"text,image",1,null,0.32,1.28,0.064,0.39999999999999997],["qwen/qwq-32b",131072,131072,"text",1,null,0.15,0.58,0,0],["reka/reka-edge",16384,16384,"text,image",0,null,0.09999999999999999,0.09999999999999999,0,0],["rekaai/reka-edge",16384,16384,"text,image",0,null,0.09999999999999999,0.09999999999999999,0,0],["relace/relace-search",256000,128000,"text",0,null,1,3,0,0],["sakana/fugu-ultra",1000000,128000,"text,image",1,null,5,30,0.5,0],["sao10k/l3-euryale-70b",8192,8192,"text",0,null,1.48,1.48,0,0],["sao10k/l3.1-euryale-70b",131072,16384,"text",0,null,0.85,0.85,0,0],["stepfun/step-3.5-flash",262144,65536,"text",0,null,0.09999999999999999,0.3,0.02,0],["stepfun/step-3.5-flash:free",256000,256000,"text",1,null,0,0,0,0],["stepfun/step-3.7-flash",262144,256000,"text,image",1,null,0.19999999999999998,1.15,0.04,0],["tencent/hy3",262144,128000,"text",1,null,0.13199999999999998,0.5279999999999999,0.032999999999999995,0],["tencent/hy3-preview",262144,64000,"text",1,null,0.063,0.21,0.020999999999999998,0],["tencent/hy3-preview:free",262144,262144,"text",1,null,0,0,0,0],["tencent/hy3:free",262144,262144,"text",1,null,0,0,0,0],["thedrummer/rocinante-12b",32768,32768,"text",0,null,0.16999999999999998,0.43,0,0],["thedrummer/unslopnemo-12b",32768,32768,"text",0,null,0.39999999999999997,0.39999999999999997,0,0],["thinkingmachines/inkling",1048576,8888,"text,image",1,null,1,4.05,0.16999999999999998,0],["tngtech/deepseek-r1t2-chimera",163840,163840,"text",1,null,0.3,1.1,0.15,0],["tngtech/tng-r1t-chimera",163840,65536,"text",1,null,0.25,0.85,0.125,0],["upstage/solar-pro-3",128000,8888,"text",1,null,0.15,0.6,0.015,0],["upstage/solar-pro-3:free",128000,8888,"text",1,null,0,0,0,0],["x-ai/grok-3",131072,8888,"text",0,null,3,15,0.75,0],["x-ai/grok-3-beta",131072,8888,"text",0,null,3,15,0.75,0],["x-ai/grok-3-mini",131072,8888,"text",1,null,0.3,0.5,0.075,0],["x-ai/grok-3-mini-beta",131072,8888,"text",1,null,0.3,0.5,0.075,0],["x-ai/grok-4",256000,64000,"text,image",1,null,3,15,0.75,0],["x-ai/grok-4-fast",2000000,30000,"text,image",1,null,0.19999999999999998,0.5,0.049999999999999996,0],["x-ai/grok-4.1-fast",2000000,30000,"text,image",1,null,0.19999999999999998,0.5,0.049999999999999996,0],["x-ai/grok-4.20",2000000,8888,"text,image",1,null,1.25,2.5,0.19999999999999998,0],["x-ai/grok-4.20-beta",2000000,8888,"text,image",1,null,2,6,0.19999999999999998,0],["x-ai/grok-4.3",1000000,1000000,"text,image",1,null,1.25,2.5,0.19999999999999998,0],["x-ai/grok-4.5",500000,500000,"text,image",1,null,2,6,0.3,0],["x-ai/grok-4.6",500000,500000,"text,image",1,null,2,6,0.3,0],["x-ai/grok-build-0.1",256000,256000,"text,image",1,null,1,2,0.19999999999999998,0],["x-ai/grok-code-fast-1",256000,10000,"text",1,null,0.19999999999999998,1.5,0.02,0],["xiaomi/mimo-v2-flash",262144,65536,"text",1,null,0.09999999999999999,0.3,0.01,0],["xiaomi/mimo-v2-omni",262144,65536,"text,image",1,null,0.39999999999999997,2,0.08,0],["xiaomi/mimo-v2-pro",1048576,131072,"text",1,null,1,3,0.19999999999999998,0],["xiaomi/mimo-v2.5",1050000,131072,"text,image",1,null,0.14,0.28,0.0028,0],["xiaomi/mimo-v2.5-pro",1050000,131072,"text",1,null,0.435,0.87,0.0036,0],["z-ai/glm-4-32b",128000,8888,"text",0,null,0.09999999999999999,0.09999999999999999,0,0],["z-ai/glm-4.5",131072,98304,"text",1,null,0.6,2.2,0.11,0],["z-ai/glm-4.5-air",131072,98304,"text",1,null,0.13,0.85,0.024999999999999998,0],["z-ai/glm-4.5-air:free",131072,96000,"text",1,null,0,0,0,0],["z-ai/glm-4.5v",65536,16384,"text,image",1,null,0.6,1.7999999999999998,0.11,0],["z-ai/glm-4.6",204800,131072,"text",1,null,0.5,2,0.09999999999999999,0],["z-ai/glm-4.6:exacto",204800,131072,"text",1,null,0.44,1.76,0.11,0],["z-ai/glm-4.6v",131072,32768,"text,image",1,null,0.3,0.8999999999999999,0.055,0],["z-ai/glm-4.7",204800,131072,"text",1,null,0.39999999999999997,1.75,0.08,0],["z-ai/glm-4.7-flash",202752,16384,"text",1,null,0.06,0.39999999999999997,0.01,0],["z-ai/glm-5",204800,131072,"text",1,null,0.95,2.5500000000000003,0.19999999999999998,0],["z-ai/glm-5-turbo",202752,131072,"text",1,null,1.2,4,0.24,0],["z-ai/glm-5.1",204800,128000,"text",1,null,0.966,3.036,0.1794,0],["z-ai/glm-5.2",1048576,131072,"text",1,null,0.707,2.222,0.1313,0],["z-ai/glm-5.3",1048576,131072,"text",1,null,0.707,2.222,0.1313,0],["z-ai/glm-5v-turbo",202752,131072,"text,image",1,null,1.2,4,0.24,0]],
|
|
54
54
|
"xai": [["grok-2",131072,8192,"text",0,null,2,10,2,0],["grok-2-1212",131072,8192,"text",0,null,2,10,2,0],["grok-2-latest",131072,8192,"text",0,null,2,10,2,0],["grok-2-vision",8192,4096,"text,image",0,null,2,10,2,0],["grok-2-vision-1212",8192,4096,"text,image",0,null,2,10,2,0],["grok-2-vision-latest",8192,4096,"text,image",0,null,2,10,2,0],["grok-3",131072,8192,"text",0,null,3,15,0.75,0],["grok-3-fast",131072,8192,"text",0,null,5,25,1.25,0],["grok-3-fast-latest",131072,8192,"text",0,null,5,25,1.25,0],["grok-3-latest",131072,8192,"text",0,null,3,15,0.75,0],["grok-3-mini",131072,8192,"text",1,null,0.3,0.5,0.075,0],["grok-3-mini-fast",131072,8192,"text",1,null,0.6,4,0.15,0],["grok-3-mini-fast-latest",131072,8192,"text",1,null,0.6,4,0.15,0],["grok-3-mini-latest",131072,8192,"text",1,null,0.3,0.5,0.075,0],["grok-4",256000,64000,"text",1,null,3,15,0.75,0],["grok-4-1-fast",2000000,30000,"text,image",1,null,0.2,0.5,0.05,0],["grok-4-1-fast-non-reasoning",2000000,30000,"text,image",0,null,0.2,0.5,0.05,0],["grok-4-fast",2000000,30000,"text,image",1,null,0.2,0.5,0.05,0],["grok-4-fast-non-reasoning",2000000,30000,"text,image",0,null,0.2,0.5,0.05,0],["grok-4.20-0309-non-reasoning",1000000,30000,"text,image",0,null,1.25,2.5,0.2,0],["grok-4.20-0309-reasoning",1000000,30000,"text,image",1,null,1.25,2.5,0.2,0],["grok-4.20-beta-latest-non-reasoning",2000000,30000,"text,image",0,null,2,6,0.2,0],["grok-4.20-beta-latest-reasoning",2000000,30000,"text,image",1,null,2,6,0.2,0],["grok-4.20-multi-agent-0309",1000000,30000,"text,image",1,null,1.25,2.5,0.2,0],["grok-4.20-multi-agent-beta-latest",2000000,30000,"text,image",1,null,2,6,0.2,0],["grok-4.3",1000000,30000,"text,image",1,null,1.25,2.5,0.2,0],["grok-4.5",500000,500000,"text,image",1,null,2,6,0.3,0],["grok-4.6",500000,500000,"text,image",1,null,2,6,0.3,0],["grok-beta",131072,4096,"text",0,null,5,15,5,0],["grok-build-0.1",256000,256000,"text,image",1,null,1,2,0.2,0],["grok-code-fast-1",256000,10000,"text",1,null,0.2,1.5,0.02,0],["grok-composer-2.5-fast",200000,64000,"text",1,null,0,0,0,0],["grok-vision-beta",8192,4096,"text,image",0,null,5,15,5,0]],
|
|
55
55
|
"xiaomi": [["mimo-v2-flash",262144,65536,"text",1,null,0.14,0.28,0.0028,0],["mimo-v2-omni",262144,131072,"text,image",1,null,0.14,0.28,0.0028,0],["mimo-v2-pro",1048576,131072,"text",1,null,0.435,0.87,0.0036,0],["mimo-v2.5",1048576,131072,"text,image",1,null,0.14,0.28,0.0028,0],["mimo-v2.5-pro",1048576,131072,"text",1,null,0.435,0.87,0.0036,0],["mimo-v2.5-pro-ultraspeed",1048576,131072,"text",1,null,1.305,2.61,0.0108,0]],
|
|
@@ -52,27 +52,48 @@ export function encodeVarint(value: number | bigint): Uint8Array {
|
|
|
52
52
|
return new Uint8Array(bytes);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
const MAX_PROTOBUF_VARINT_BYTES = 10;
|
|
56
|
+
|
|
55
57
|
/**
|
|
56
58
|
* Decodes a protobuf varint from bytes at offset.
|
|
57
59
|
*/
|
|
58
60
|
export function decodeVarint(bytes: Uint8Array, offset: number): { value: number; bytesRead: number } {
|
|
61
|
+
if (!Number.isInteger(offset) || offset < 0 || offset >= bytes.length) {
|
|
62
|
+
throw new Error("Invalid protobuf varint offset");
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
let result = 0;
|
|
60
|
-
let shift = 0;
|
|
61
66
|
let count = 0;
|
|
62
67
|
|
|
63
68
|
while (offset + count < bytes.length) {
|
|
69
|
+
// Protobuf caps a varint at the ten bytes a 64-bit value needs. The safe-integer guard
|
|
70
|
+
// below cannot stand in for this bound, because a continuation byte carrying no payload
|
|
71
|
+
// bits contributes a part of zero, which IS a safe integer: without an explicit length
|
|
72
|
+
// limit an arbitrarily long run of 0x80 followed by 0x00 decoded as a valid zero, and an
|
|
73
|
+
// overlong zero length can normalize a malformed body into an empty coupon list.
|
|
74
|
+
if (count >= MAX_PROTOBUF_VARINT_BYTES) {
|
|
75
|
+
throw new Error("Overlong protobuf varint");
|
|
76
|
+
}
|
|
64
77
|
const b = bytes[offset + count];
|
|
65
|
-
count
|
|
66
|
-
result
|
|
67
|
-
|
|
68
|
-
shift += 7;
|
|
69
|
-
if (shift > 35) {
|
|
70
|
-
// For timestamps seconds, JS safe integers suffice.
|
|
71
|
-
break;
|
|
78
|
+
const part = (b & 0x7f) * (2 ** (7 * count));
|
|
79
|
+
if (!Number.isSafeInteger(part) || result > Number.MAX_SAFE_INTEGER - part) {
|
|
80
|
+
throw new Error("Protobuf varint exceeds JavaScript safe integer range");
|
|
72
81
|
}
|
|
82
|
+
result += part;
|
|
83
|
+
count++;
|
|
84
|
+
if ((b & 0x80) === 0) return { value: result, bytesRead: count };
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
throw new Error("Truncated protobuf varint");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function decodeLength(bytes: Uint8Array, offset: number): { start: number; end: number } {
|
|
91
|
+
const { value: length, bytesRead } = decodeVarint(bytes, offset);
|
|
92
|
+
const start = offset + bytesRead;
|
|
93
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > bytes.length - start) {
|
|
94
|
+
throw new Error("Invalid protobuf length-delimited field");
|
|
95
|
+
}
|
|
96
|
+
return { start, end: start + length };
|
|
76
97
|
}
|
|
77
98
|
|
|
78
99
|
/**
|
|
@@ -109,8 +130,8 @@ function decodeTimestamp(bytes: Uint8Array): number {
|
|
|
109
130
|
offset += bytesRead;
|
|
110
131
|
if (fieldNum === 1) seconds = value;
|
|
111
132
|
} else if (wireType === 2) {
|
|
112
|
-
const {
|
|
113
|
-
offset
|
|
133
|
+
const { end } = decodeLength(bytes, offset);
|
|
134
|
+
offset = end;
|
|
114
135
|
} else {
|
|
115
136
|
break;
|
|
116
137
|
}
|
|
@@ -135,10 +156,9 @@ function decodeConsumerResetToken(bytes: Uint8Array): GrokResetCoupon | null {
|
|
|
135
156
|
const wireType = tag & 0x7;
|
|
136
157
|
|
|
137
158
|
if (wireType === 2) {
|
|
138
|
-
const {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
offset += len;
|
|
159
|
+
const { start, end } = decodeLength(bytes, offset);
|
|
160
|
+
const sub = bytes.subarray(start, end);
|
|
161
|
+
offset = end;
|
|
142
162
|
|
|
143
163
|
if (fieldNum === 10) {
|
|
144
164
|
tokenId = new TextDecoder("utf-8").decode(sub);
|
|
@@ -178,10 +198,9 @@ export function decodeGetRemainingResetsResponse(payload: Uint8Array): GrokReset
|
|
|
178
198
|
const wireType = tag & 0x7;
|
|
179
199
|
|
|
180
200
|
if (wireType === 2) {
|
|
181
|
-
const {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
offset += len;
|
|
201
|
+
const { start, end } = decodeLength(payload, offset);
|
|
202
|
+
const sub = payload.subarray(start, end);
|
|
203
|
+
offset = end;
|
|
185
204
|
|
|
186
205
|
if (fieldNum === 10) {
|
|
187
206
|
const token = decodeConsumerResetToken(sub);
|
package/src/images/loop.ts
CHANGED
|
@@ -321,10 +321,15 @@ export interface ImageBridgeDeps {
|
|
|
321
321
|
* 429 states the window's reset epoch even when it omits Retry-After -- and a rotation that
|
|
322
322
|
* cannot see it cools the drained account for the short default instead of until the window
|
|
323
323
|
* actually reopens. Optional so existing callers keep compiling.
|
|
324
|
+
*
|
|
325
|
+
* `retryParsed` is the exact iteration-local request the retry will be built from. The loop
|
|
326
|
+
* sends a shallow copy of the outer parsed request, so a rotation that rebinds only the outer
|
|
327
|
+
* object never reaches the wire. Optional so existing callers keep compiling.
|
|
324
328
|
*/
|
|
325
329
|
on429?: (
|
|
326
330
|
retryAfterHeader: string | null,
|
|
327
331
|
responseHeaders?: Headers,
|
|
332
|
+
retryParsed?: OcxParsedRequest,
|
|
328
333
|
) => ProviderAdapter | null | Promise<ProviderAdapter | null>;
|
|
329
334
|
/** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
|
|
330
335
|
retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
|
|
@@ -660,7 +665,7 @@ export async function runWithImageBridge(deps: ImageBridgeDeps): Promise<Respons
|
|
|
660
665
|
}
|
|
661
666
|
// 429 key-failover parity with web-search / normal routed path.
|
|
662
667
|
while (prepared.response.status === 429 && deps.on429) {
|
|
663
|
-
const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers);
|
|
668
|
+
const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers, iterParsed);
|
|
664
669
|
if (!rotated) break;
|
|
665
670
|
try { void prepared.response.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
666
671
|
adapter = rotated;
|
|
@@ -2,6 +2,8 @@ import { lstatSync, readdirSync, realpathSync } from "node:fs";
|
|
|
2
2
|
import { join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { ClientPathError, type ExportModel } from "../clients/config-export";
|
|
4
4
|
import { assertAsideProfileBoundary, guardAsideProfileIO, listAsideProfiles, type AsideProfile } from "../clients/aside-profiles";
|
|
5
|
+
import { detachedConfigSnapshot } from "../config/admitted-identity";
|
|
6
|
+
import { copyPlainData } from "../lib/plain-data";
|
|
5
7
|
import type { OcxConfig } from "../types";
|
|
6
8
|
import { type IntegrationIO } from "./config-io";
|
|
7
9
|
import type { JournalEntry } from "./journal";
|
|
@@ -46,6 +48,17 @@ export interface AsideProfileContext {
|
|
|
46
48
|
defaultEnabled: boolean;
|
|
47
49
|
models: () => Promise<readonly ExportModel[]>;
|
|
48
50
|
scopes: Map<number, AsideProfileScope>;
|
|
51
|
+
/**
|
|
52
|
+
* The configuration every profile write in this action is built from, copied when the context
|
|
53
|
+
* was created.
|
|
54
|
+
*
|
|
55
|
+
* An Aside mutation checks its confirmation, then awaits the preference write, and only then
|
|
56
|
+
* builds each profile's write input. The preference write edits the live configuration itself,
|
|
57
|
+
* so reading it again afterwards guaranteed the document was serialized from a configuration
|
|
58
|
+
* the check never saw. This copy is taken before the check and carried through every write, so
|
|
59
|
+
* the two describe the same thing.
|
|
60
|
+
*/
|
|
61
|
+
boundConfig: OcxConfig;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
function storeUnsafe(): never {
|
|
@@ -142,10 +155,28 @@ export function createAsideProfileContext(input: AsideProfilesInput): AsideProfi
|
|
|
142
155
|
? matched?.id ?? null
|
|
143
156
|
: profiles.find(profile => profile.configPath === newest?.configPath)?.id ?? null;
|
|
144
157
|
let loaded: Promise<readonly ExportModel[]> | undefined;
|
|
158
|
+
const boundConfig = detachedConfigSnapshot(input.config);
|
|
159
|
+
if (boundConfig === null) {
|
|
160
|
+
throw new AsideProfileError("aside_profiles_unavailable", 409, "The proxy configuration could not be captured for this change");
|
|
161
|
+
}
|
|
145
162
|
return {
|
|
146
163
|
input: { ...input, env: { ...(input.env ?? process.env) } }, profiles, rootStore, legacyProfileId, scopes: new Map(),
|
|
147
164
|
defaultEnabled: input.config.asideProfileSync?.allProfiles ?? Boolean(matched),
|
|
148
|
-
|
|
165
|
+
/*
|
|
166
|
+
* Resolved once and copied, so every profile in one action writes the same roster and a
|
|
167
|
+
* caller editing the model objects it passed cannot change what a checked plan described.
|
|
168
|
+
* The copy is taken here rather than at each write because the check reads it too.
|
|
169
|
+
*/
|
|
170
|
+
models: () => loaded ??= Promise.resolve()
|
|
171
|
+
.then(() => typeof input.models === "function" ? input.models() : input.models)
|
|
172
|
+
.then(rows => {
|
|
173
|
+
const copied = copyPlainData([...rows]);
|
|
174
|
+
if (!copied.ok) {
|
|
175
|
+
throw new AsideProfileError("aside_profiles_unavailable", 409, "The model roster could not be captured for this change");
|
|
176
|
+
}
|
|
177
|
+
return copied.value;
|
|
178
|
+
}),
|
|
179
|
+
boundConfig,
|
|
149
180
|
};
|
|
150
181
|
}
|
|
151
182
|
|
|
@@ -160,7 +191,10 @@ export function selectAsideProfiles(ctx: AsideProfileContext, profileId?: number
|
|
|
160
191
|
}
|
|
161
192
|
|
|
162
193
|
export function asideProfileEnabled(ctx: AsideProfileContext, id: number): boolean {
|
|
163
|
-
|
|
194
|
+
// The bound copy, like every other policy read in this context: the preference write inside an
|
|
195
|
+
// action edits the live configuration, and a decision taken afterwards would be answering from
|
|
196
|
+
// a policy the action itself installed.
|
|
197
|
+
return ctx.boundConfig.asideProfileSync?.profiles?.[String(id)] ?? ctx.defaultEnabled;
|
|
164
198
|
}
|
|
165
199
|
|
|
166
200
|
export function asideProfileScope(ctx: AsideProfileContext, profile: AsideProfile): AsideProfileScope {
|
|
@@ -207,7 +241,7 @@ export async function asideWriteInput(ctx: AsideProfileContext, scope: AsideProf
|
|
|
207
241
|
const models = await ctx.models();
|
|
208
242
|
scope.assertBoundary();
|
|
209
243
|
return {
|
|
210
|
-
clientId: "aside", config: ctx.
|
|
244
|
+
clientId: "aside", config: ctx.boundConfig, models, port: ctx.input.port,
|
|
211
245
|
env: ctx.input.env, home: ctx.input.home, store: scope.store, io: scope.io,
|
|
212
246
|
resolvedPaths: { configPath: scope.profile.configPath, detectDir: scope.profile.detectDir },
|
|
213
247
|
};
|
|
@@ -167,10 +167,32 @@ function importOperation(row: AsideOperation, scope: AsideProfileScope): void {
|
|
|
167
167
|
|
|
168
168
|
export function restoreAsideProfile(
|
|
169
169
|
input: AsideProfilesInput,
|
|
170
|
-
request: {
|
|
170
|
+
request: {
|
|
171
|
+
opId: string;
|
|
172
|
+
profileId?: number;
|
|
173
|
+
confirmDrift?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* The row a caller already selected. Aside can hold more than one valid copy, so resolving
|
|
176
|
+
* again here could act on a different one than the plan the operator confirmed described.
|
|
177
|
+
*/
|
|
178
|
+
selectedOperation?: AsideOperation;
|
|
179
|
+
},
|
|
180
|
+
options?: {
|
|
181
|
+
revalidate?: (prepared: IntegrationWriteInput) => Promise<AsideProfileWriteOutcome | null>;
|
|
182
|
+
/**
|
|
183
|
+
* Checked after the preference write, once before the selected history is copied into this
|
|
184
|
+
* profile's store and once more immediately before the restore itself.
|
|
185
|
+
*
|
|
186
|
+
* The preference write and that copy both sit between the first check and the restore they
|
|
187
|
+
* authorize, and Aside takes no writer lock. The snapshot checks below still hold, but they
|
|
188
|
+
* say nothing about the target file, which can be edited in either window; without this, a
|
|
189
|
+
* confirmation about the earlier file still overwrote the later one.
|
|
190
|
+
*/
|
|
191
|
+
revalidateBeforeWrite?: (prepared: IntegrationWriteInput) => Promise<AsideProfileWriteOutcome | null>;
|
|
192
|
+
},
|
|
171
193
|
): Promise<AsideProfileWriteOutcome> {
|
|
172
194
|
return runAsideProfileAction<AsideProfileWriteOutcome>(input, request.profileId, `restore:${request.opId}:${Boolean(request.confirmDrift)}`, async ctx => {
|
|
173
|
-
const row = requiredOperation(ctx, request.opId, request.profileId);
|
|
195
|
+
const row = request.selectedOperation ?? requiredOperation(ctx, request.opId, request.profileId);
|
|
174
196
|
const profile = selectAsideProfiles(ctx, row.profileId)[0]!;
|
|
175
197
|
const scope = asideProfileScope(ctx, profile);
|
|
176
198
|
assertAsideSnapshotEntry(row.entry);
|
|
@@ -188,6 +210,16 @@ export function restoreAsideProfile(
|
|
|
188
210
|
return { clientId: "aside", profileId: profile.id, ok: false, reason: "drift_requires_confirm", state: "conflict", message: "This profile changed after that operation; confirm to replace it" };
|
|
189
211
|
}
|
|
190
212
|
const restoredText = snapshot.kind === "stored" ? snapshot.text : null;
|
|
213
|
+
/*
|
|
214
|
+
* Checked before the preference write and before importOperation, which captures a snapshot
|
|
215
|
+
* and appends a journal row. Both happen ahead of the coordinated restore, so a confirmation
|
|
216
|
+
* checked under the writer lock would already have rewritten the user's preference and their
|
|
217
|
+
* history by the time it was consulted.
|
|
218
|
+
*/
|
|
219
|
+
// The input this restore will actually write from, so the check cannot be answering about a
|
|
220
|
+
// view it rebuilt from the live configuration while the write uses this one.
|
|
221
|
+
const stale = await options?.revalidate?.(bound);
|
|
222
|
+
if (stale) return stale;
|
|
191
223
|
await persistAsidePolicy(ctx, { profileId: profile.id, enabled: snapshotWasOwned(row.entry, restoredText, bound) });
|
|
192
224
|
try {
|
|
193
225
|
scope.assertBoundary();
|
|
@@ -197,8 +229,41 @@ export function restoreAsideProfile(
|
|
|
197
229
|
|| (currentSnapshot.kind === "stored" && currentSnapshot.text !== restoredText)) {
|
|
198
230
|
throw new AsideProfileError("aside_operation_changed", 409, "Aside operation or snapshot changed while saving preferences");
|
|
199
231
|
}
|
|
232
|
+
/*
|
|
233
|
+
* Looked at twice, because the two moments answer different questions and neither covers
|
|
234
|
+
* the other.
|
|
235
|
+
*
|
|
236
|
+
* Here, before the import, so that a confirmation already stale by this point is refused
|
|
237
|
+
* without copying anything: the import writes the selected row and its snapshot into this
|
|
238
|
+
* profile's own store, and a refusal from inside the coordinated restore returns before the
|
|
239
|
+
* restore transaction begins, so nothing would compensate that copy.
|
|
240
|
+
*/
|
|
241
|
+
const beforeImport = await options?.revalidateBeforeWrite?.(bound);
|
|
242
|
+
if (beforeImport) return beforeImport;
|
|
200
243
|
importOperation(row, scope);
|
|
201
|
-
return {
|
|
244
|
+
return {
|
|
245
|
+
...await restoreIntegrationCoordinated(
|
|
246
|
+
{ ...bound, opId: request.opId, confirmDrift: request.confirmDrift },
|
|
247
|
+
{
|
|
248
|
+
lockSeams: input.lockSeams,
|
|
249
|
+
/*
|
|
250
|
+
* And again inside the coordinated restore, immediately before it runs. The import
|
|
251
|
+
* sits between the two, and Aside takes no writer lock, so that path still awaits
|
|
252
|
+
* before the restore begins: a target edited after the copy would otherwise be
|
|
253
|
+
* rewritten by a confirmation that never described it.
|
|
254
|
+
*
|
|
255
|
+
* A refusal here leaves the copied history and the saved preference in place. That is
|
|
256
|
+
* deliberate: the copy is this operation's own history rather than a change to the
|
|
257
|
+
* operator's file, and unwinding it would need a transaction across two stores and a
|
|
258
|
+
* configuration write for no gain the operator can see.
|
|
259
|
+
*/
|
|
260
|
+
...(options?.revalidateBeforeWrite
|
|
261
|
+
? { revalidate: (frozen: IntegrationWriteInput) => options.revalidateBeforeWrite!(frozen) }
|
|
262
|
+
: {}),
|
|
263
|
+
},
|
|
264
|
+
),
|
|
265
|
+
profileId: profile.id,
|
|
266
|
+
};
|
|
202
267
|
} catch (error) { return asideProfileFailure(profile.id, error); }
|
|
203
268
|
});
|
|
204
269
|
}
|
|
@@ -2,20 +2,65 @@ import type { AsideProfile } from "../clients/aside-profiles";
|
|
|
2
2
|
import { asideHomeDir } from "../clients/config-export";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { OwnedIntegrationRefreshOutcome } from "./owned-refresh";
|
|
5
|
+
import type { JournalEntry } from "./journal";
|
|
6
|
+
import type { IntegrationStateStore } from "./store";
|
|
5
7
|
import { readIntegrationState, type IntegrationState, type IntegrationStatus } from "./state";
|
|
6
8
|
import {
|
|
7
9
|
applyIntegrationCoordinated, disableIntegrationCoordinated,
|
|
8
10
|
overwriteIntegrationCoordinated, refreshIntegrationCoordinated,
|
|
11
|
+
type IntegrationWriteInput,
|
|
9
12
|
} from "./writer";
|
|
10
13
|
import {
|
|
11
14
|
asideProfileEnabled, asideProfileFailure, asideProfileScope, asideWriteInput,
|
|
12
15
|
asideRootStore, createAsideProfileContext, persistAsidePolicy, runAsideProfileAction, selectAsideProfiles,
|
|
13
16
|
type AsideProfileContext, type AsideProfilesInput, type AsideProfileWriteOutcome,
|
|
14
17
|
} from "./aside-profile-context";
|
|
18
|
+
import { AsideProfileError as AsideProfileErrorClass } from "./aside-profile-context";
|
|
19
|
+
import {
|
|
20
|
+
previewIntegration,
|
|
21
|
+
type IntegrationMutationPlan,
|
|
22
|
+
type IntegrationPlanOperation,
|
|
23
|
+
} from "./mutation-plan";
|
|
15
24
|
|
|
16
25
|
export { AsideProfileError } from "./aside-profile-context";
|
|
17
26
|
export type { AsideProfilesInput, AsideProfileWriteOutcome } from "./aside-profile-context";
|
|
18
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Plan one profile's change without performing it.
|
|
30
|
+
*
|
|
31
|
+
* A profile is the unit here because a fingerprint can only honestly describe one independently
|
|
32
|
+
* changing file. The scope this builds is the same scope the mutation will use — that profile's
|
|
33
|
+
* store, IO and resolved path pair — so the plan describes the thing that would actually happen
|
|
34
|
+
* rather than an approximation of it.
|
|
35
|
+
*
|
|
36
|
+
* The roster comes from whatever the caller injected, so a preview inherits the caller's no-gather
|
|
37
|
+
* guarantee instead of reaching for a second source of models.
|
|
38
|
+
*/
|
|
39
|
+
export async function previewAsideProfile(
|
|
40
|
+
input: AsideProfilesInput,
|
|
41
|
+
request: {
|
|
42
|
+
profileId: number;
|
|
43
|
+
operation: IntegrationPlanOperation;
|
|
44
|
+
opId?: string;
|
|
45
|
+
confirmDrift?: boolean;
|
|
46
|
+
/** The row and store the route already selected; re-resolving could pick a different copy. */
|
|
47
|
+
resolved?: { entry: JournalEntry; store: IntegrationStateStore };
|
|
48
|
+
},
|
|
49
|
+
): Promise<IntegrationMutationPlan> {
|
|
50
|
+
const ctx = createAsideProfileContext(input);
|
|
51
|
+
const profile = selectAsideProfiles(ctx, request.profileId)[0];
|
|
52
|
+
if (!profile) throw new AsideProfileErrorClass("aside_profile_not_found", 404, "That Aside profile is not available");
|
|
53
|
+
const scope = asideProfileScope(ctx, profile);
|
|
54
|
+
const bound = await asideWriteInput(ctx, scope);
|
|
55
|
+
return previewIntegration(bound, {
|
|
56
|
+
operation: request.operation,
|
|
57
|
+
profileId: request.profileId,
|
|
58
|
+
...(request.opId === undefined ? {} : { opId: request.opId }),
|
|
59
|
+
...(request.confirmDrift === undefined ? {} : { confirmDrift: request.confirmDrift }),
|
|
60
|
+
...(request.resolved === undefined ? {} : { resolved: request.resolved }),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
19
64
|
export interface AsideProfileState extends IntegrationStatus {
|
|
20
65
|
profileId: number;
|
|
21
66
|
name?: string;
|
|
@@ -104,6 +149,18 @@ export async function getAsideProfileState(input: AsideProfilesInput, id: number
|
|
|
104
149
|
export function mutateAsideProfiles(
|
|
105
150
|
input: AsideProfilesInput,
|
|
106
151
|
change: { enabled: boolean; profileId?: number; overwriteConflict?: boolean },
|
|
152
|
+
options?: {
|
|
153
|
+
revalidate?: (prepared: IntegrationWriteInput) => Promise<AsideProfileWriteOutcome | null>;
|
|
154
|
+
/**
|
|
155
|
+
* Checked again immediately before the client document is written.
|
|
156
|
+
*
|
|
157
|
+
* Aside takes no writer lock, and the preference write sits between the first check and the
|
|
158
|
+
* write it authorizes. Nothing stops the target file from being edited in that window, and the
|
|
159
|
+
* writer's own comparison reads the file as it is now, so a confirmation about the earlier
|
|
160
|
+
* file would otherwise still overwrite the later one.
|
|
161
|
+
*/
|
|
162
|
+
revalidateBeforeWrite?: (prepared: IntegrationWriteInput) => Promise<AsideProfileWriteOutcome | null>;
|
|
163
|
+
},
|
|
107
164
|
): Promise<AsideProfileMutationResult> {
|
|
108
165
|
return runAsideProfileAction<AsideProfileMutationResult>(input, change.profileId, `${change.enabled ? "enable" : "disable"}:${Boolean(change.overwriteConflict)}`, async (ctx, profiles) => {
|
|
109
166
|
const refused = new Map<number, AsideProfileWriteOutcome>();
|
|
@@ -111,6 +168,66 @@ export function mutateAsideProfiles(
|
|
|
111
168
|
try { asideProfileScope(ctx, profile); }
|
|
112
169
|
catch (error) { refused.set(profile.id, asideProfileFailure(profile.id, error)); }
|
|
113
170
|
}
|
|
171
|
+
/*
|
|
172
|
+
* The input each profile will be written from, prepared before the check and handed to it.
|
|
173
|
+
*
|
|
174
|
+
* A check that built its own view from the live configuration was answering about a different
|
|
175
|
+
* input than the one that would be written: the context copies the configuration when it is
|
|
176
|
+
* created, so live could be edited to something else and then back again while this action was
|
|
177
|
+
* in flight, and a check reading live would agree with a plan this write never described.
|
|
178
|
+
*
|
|
179
|
+
* Only a checked change prepares early. Preparing resolves the roster, and an unchecked change
|
|
180
|
+
* whose preference write fails must not have done any model work by then, which is a contract
|
|
181
|
+
* of its own. There is nothing to bring forward when there is no check to read it.
|
|
182
|
+
*/
|
|
183
|
+
const prepared = new Map<number, IntegrationWriteInput>();
|
|
184
|
+
if (options?.revalidate) {
|
|
185
|
+
/*
|
|
186
|
+
* Refused before anything is prepared. Preparing resolves the roster, and a supported loader
|
|
187
|
+
* reaches providers and can finalize an initial model selection, so a confirmation this
|
|
188
|
+
* cannot check at all must not cause that work first. A confirmation describes one profile,
|
|
189
|
+
* and HTTP refuses one that names none; guessing which prepared input a bindingless
|
|
190
|
+
* confirmation meant would invent the thing the check exists to verify.
|
|
191
|
+
*/
|
|
192
|
+
if (change.profileId === undefined) {
|
|
193
|
+
throw new AsideProfileErrorClass("invalid_aside_profile", 400, "a confirmed change applies to one profile");
|
|
194
|
+
}
|
|
195
|
+
for (const profile of profiles) {
|
|
196
|
+
if (refused.has(profile.id)) continue;
|
|
197
|
+
try { prepared.set(profile.id, await asideWriteInput(ctx, asideProfileScope(ctx, profile))); }
|
|
198
|
+
catch (error) { refused.set(profile.id, asideProfileFailure(profile.id, error)); }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/*
|
|
202
|
+
* A confirmation is checked HERE, not under the writer lock.
|
|
203
|
+
*
|
|
204
|
+
* The await below persists the user's Aside preference before any writer runs, so a check
|
|
205
|
+
* that waited for the lock would fire after the thing it was meant to prevent. Profile and
|
|
206
|
+
* path selection is frozen by this point, which is everything the check needs.
|
|
207
|
+
*/
|
|
208
|
+
if (options?.revalidate) {
|
|
209
|
+
const guarded = change.profileId === undefined ? undefined : prepared.get(change.profileId);
|
|
210
|
+
// Nothing prepared is nothing to check and nothing to write; refusing keeps a binding from
|
|
211
|
+
// being dropped on the way to a write that would then be unchecked.
|
|
212
|
+
const stale: AsideProfileWriteOutcome | null = guarded === undefined
|
|
213
|
+
? refused.get(change.profileId ?? -1) ?? {
|
|
214
|
+
ok: false, reason: "unsafe", state: "conflict", clientId: "aside",
|
|
215
|
+
message: "that profile cannot be prepared for this change",
|
|
216
|
+
profileId: change.profileId ?? profiles[0]?.id ?? 0,
|
|
217
|
+
}
|
|
218
|
+
: await options.revalidate(guarded);
|
|
219
|
+
if (stale) {
|
|
220
|
+
return {
|
|
221
|
+
ok: false,
|
|
222
|
+
clientId: "aside",
|
|
223
|
+
changed: false,
|
|
224
|
+
state: "conflict",
|
|
225
|
+
message: "that confirmation no longer describes this profile",
|
|
226
|
+
results: [stale],
|
|
227
|
+
result: stale,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
114
231
|
// This await precedes model loading, writer preflight, snapshots and all client writes.
|
|
115
232
|
await persistAsidePolicy(ctx, change);
|
|
116
233
|
const results: AsideProfileWriteOutcome[] = [];
|
|
@@ -118,11 +235,19 @@ export function mutateAsideProfiles(
|
|
|
118
235
|
const refusal = refused.get(profile.id);
|
|
119
236
|
if (refusal) { results.push(refusal); continue; }
|
|
120
237
|
try {
|
|
121
|
-
|
|
122
|
-
const bound = await asideWriteInput(ctx,
|
|
238
|
+
// The prepared input when a check read one, so the write is the thing that was checked.
|
|
239
|
+
const bound = prepared.get(profile.id) ?? await asideWriteInput(ctx, asideProfileScope(ctx, profile));
|
|
123
240
|
const operation = !change.enabled ? disableIntegrationCoordinated
|
|
124
241
|
: change.overwriteConflict ? overwriteIntegrationCoordinated : applyIntegrationCoordinated;
|
|
125
|
-
results.push({
|
|
242
|
+
results.push({
|
|
243
|
+
...await operation(bound, {
|
|
244
|
+
lockSeams: input.lockSeams,
|
|
245
|
+
...(options?.revalidateBeforeWrite
|
|
246
|
+
? { revalidate: (frozen: IntegrationWriteInput) => options.revalidateBeforeWrite!(frozen) }
|
|
247
|
+
: {}),
|
|
248
|
+
}),
|
|
249
|
+
profileId: profile.id,
|
|
250
|
+
});
|
|
126
251
|
} catch (error) { results.push(asideProfileFailure(profile.id, error)); }
|
|
127
252
|
}
|
|
128
253
|
const ok = results.every(result => result.ok);
|