rovecode 0.3.3 → 0.4.0-beta.2
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/CHANGELOG.md +512 -0
- package/README.md +33 -48
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-dybnr56b.js +2 -0
- package/dist/cli/ask-user-p8hq4xgj.js +2 -0
- package/dist/cli/auth-login-ewpgw5sm.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-xv3ypwev.js +9 -0
- package/dist/cli/catalog-737wb2s0.js +2 -0
- package/dist/cli/cli-arhg40m0.js +2 -0
- package/dist/cli/client-cf2pxx8q.js +2 -0
- package/dist/cli/commands-3p7e4xxs.js +2 -0
- package/dist/cli/connect-3q93d7cb.js +2 -0
- package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
- package/dist/cli/context-report-hbw9zfes.js +2 -0
- package/dist/cli/count-remote-mby98cd0.js +2 -0
- package/dist/cli/design-122y0axd.js +2 -0
- package/dist/cli/dispatch-b4egzvvh.js +2 -0
- package/dist/cli/doctor-x4jkv72e.js +3 -0
- package/dist/cli/executor-ftvg6tsy.js +2 -0
- package/dist/cli/export-pdgdhkch.js +2 -0
- package/dist/cli/files-cez9a96p.js +2 -0
- package/dist/cli/gauntlet-r3xxaszc.js +2 -0
- package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
- package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
- package/dist/cli/hashline-ewg5hbe3.js +2 -0
- package/dist/cli/http-n0kehsk8.js +5 -0
- package/dist/cli/index-z5qt1s76.js +2 -0
- package/dist/cli/install-80mp63kx.js +2 -0
- package/dist/cli/loop-12twjcat.js +2 -0
- package/dist/cli/main-01pv9206.js +4 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-1ztz6fkj.js +10 -0
- package/dist/cli/main-23q7cmww.js +9 -0
- package/dist/cli/main-2rzbexn2.js +3 -0
- package/dist/cli/main-2wyax8k9.js +9 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2z3dek0b.js +3 -0
- package/dist/cli/main-2zgsknth.js +3 -0
- package/dist/cli/main-45ejth3a.js +4 -0
- package/dist/cli/main-45rn3trk.js +22 -0
- package/dist/cli/main-4p4e2w7x.js +4 -0
- package/dist/cli/main-4y0tnfpa.js +16 -0
- package/dist/cli/main-5py0rkmc.js +4 -0
- package/dist/cli/main-6dtqmbt6.js +7 -0
- package/dist/cli/main-6h9x282m.js +4 -0
- package/dist/cli/main-6vjeds42.js +3 -0
- package/dist/cli/main-78gq4bt9.js +6 -0
- package/dist/cli/main-7jd5vh3x.js +4 -0
- package/dist/cli/main-7kt6r53y.js +4 -0
- package/dist/cli/main-8c1tbazx.js +58 -0
- package/dist/cli/main-9a9rnh47.js +19 -0
- package/dist/cli/main-9ht36z12.js +3 -0
- package/dist/cli/main-a2yfvcy9.js +7 -0
- package/dist/cli/main-a3f51n0x.js +5 -0
- package/dist/cli/main-b8zq261k.js +3 -0
- package/dist/cli/main-bxtvnf6d.js +13 -0
- package/dist/cli/main-edxc3yzt.js +4 -0
- package/dist/cli/main-evgz4mp5.js +21 -0
- package/dist/cli/main-f33fc5je.js +9 -0
- package/dist/cli/main-fvnpq46y.js +12 -0
- package/dist/cli/main-gbbty4d4.js +3 -0
- package/dist/cli/main-gth53dnt.js +25 -0
- package/dist/cli/main-hqbz10aw.js +9 -0
- package/dist/cli/main-hrrvcfan.js +38 -0
- package/dist/cli/main-hzwtsb2m.js +5 -0
- package/dist/cli/main-j7ttv0sd.js +34 -0
- package/dist/cli/main-jak598k9.js +5 -0
- package/dist/cli/main-kba6zeyd.js +6 -0
- package/dist/cli/main-kwwsz6rq.js +3 -0
- package/dist/cli/main-m8vm17zq.js +3 -0
- package/dist/cli/main-mg4f96e1.js +3 -0
- package/dist/cli/main-mg9b20ac.js +18 -0
- package/dist/cli/main-mgb9ccnx.js +3 -0
- package/dist/cli/main-mjt2p7aj.js +3 -0
- package/dist/cli/main-n6qrdbmy.js +3 -0
- package/dist/cli/main-na7wse0x.js +5 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-ntqef02r.js +10 -0
- package/dist/cli/main-nvc3yjay.js +136 -0
- package/dist/cli/main-p0cfn6nr.js +16 -0
- package/dist/cli/main-qj2djy17.js +19 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-qvarybsp.js +3 -0
- package/dist/cli/main-rebtt91r.js +5 -0
- package/dist/cli/main-rpg7h8mb.js +3 -0
- package/dist/cli/main-rsy72qmw.js +15 -0
- package/dist/cli/main-rvetps99.js +18 -0
- package/dist/cli/main-s4bb0jav.js +3 -0
- package/dist/cli/main-s9v8k74e.js +3 -0
- package/dist/cli/main-tjvwmscs.js +3 -0
- package/dist/cli/main-tkgarpjj.js +4 -0
- package/dist/cli/main-v8y60bb2.js +3 -0
- package/dist/cli/main-vhrrq337.js +3 -0
- package/dist/cli/main-vp2dfb7s.js +4 -0
- package/dist/cli/main-vqbr22sz.js +8 -0
- package/dist/cli/main-vxnwe5xx.js +18 -0
- package/dist/cli/main-wgph00xf.js +5 -0
- package/dist/cli/main-wk2csfnj.js +5 -0
- package/dist/cli/main-wm997zjx.js +3 -0
- package/dist/cli/main-wpkyraxh.js +3 -0
- package/dist/cli/main-wqt32p5x.js +4 -0
- package/dist/cli/main-x9ct6y1a.js +3 -0
- package/dist/cli/main-xfekqh9m.js +7 -0
- package/dist/cli/main-xt9zc3n6.js +7 -0
- package/dist/cli/main-xx2z3zh5.js +4 -0
- package/dist/cli/main-y5c82rxr.js +3 -0
- package/dist/cli/main-yrjt2sqt.js +14 -0
- package/dist/cli/main-ys6zj3yr.js +3 -0
- package/dist/cli/main-ywbxshqc.js +8 -0
- package/dist/cli/main-z13755t8.js +25 -0
- package/dist/cli/main-zc7pyrbj.js +4 -0
- package/dist/cli/main.js +279 -0
- package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
- package/dist/cli/mcp-login-bthtfpt7.js +2 -0
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
- package/dist/cli/notify-54v5z9dz.js +2 -0
- package/dist/cli/oauth-g5gme95c.js +2 -0
- package/dist/cli/output-satndjap.js +16 -0
- package/dist/cli/profiles-sfhpbq3m.js +2 -0
- package/dist/cli/provider-config-hv3xtdt4.js +2 -0
- package/dist/cli/provider-kwzq6g84.js +2 -0
- package/dist/cli/registry-fh0hdnyn.js +2 -0
- package/dist/cli/registry-y1y8e94r.js +2 -0
- package/dist/cli/repl-t4z03mqq.js +11 -0
- package/dist/cli/resume-fqt4chg8.js +2 -0
- package/dist/cli/run-flags-rysbag9t.js +2 -0
- package/dist/cli/runtime-j19fjbsa.js +2 -0
- package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
- package/dist/cli/server-r0b6bksk.js +5 -0
- package/dist/cli/session-arg-txmn5g4x.js +2 -0
- package/dist/cli/session-ed250d9j.js +2 -0
- package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
- package/dist/cli/settings-y9rzcqx8.js +2 -0
- package/dist/cli/setup-jmbr11j0.js +2 -0
- package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
- package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
- package/dist/cli/smoke-1bg937kx.js +8 -0
- package/dist/cli/start-chat-p01cdks3.js +12 -0
- package/dist/cli/stream-4wmyaypz.js +2 -0
- package/dist/cli/task-eg4s093s.js +2 -0
- package/dist/cli/tasks-12v9rr9k.js +2 -0
- package/dist/cli/thinking-a5ngvqyh.js +2 -0
- package/dist/cli/todo-1wxpcecx.js +2 -0
- package/dist/cli/tools-2ftsya7w.js +2 -0
- package/dist/cli/tools-x1tj4fxm.js +2 -0
- package/dist/cli/trust-cmd-hccxehzb.js +2 -0
- package/dist/cli/update-check-ygt3vd7m.js +2 -0
- package/dist/cli/update-cmd-v23qhr8c.js +2 -0
- package/dist/cli/voice-g1gtck92.js +2 -0
- package/dist/cli/webfetch-0nnrjgb5.js +2 -0
- package/dist/cli/websearch-f0vr2p7d.js +2 -0
- package/dist/cli/workspace-9rq1w4ta.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +6 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/rovecode.exe +4 -0
- package/package.json +83 -72
- package/bin/rovecode.ts +0 -21
- package/src/acp/server.ts +0 -374
- package/src/cli/auth-login.ts +0 -122
- package/src/cli/connect.ts +0 -244
- package/src/cli/context-cmd.ts +0 -199
- package/src/cli/dispatch.ts +0 -82
- package/src/cli/doctor.ts +0 -362
- package/src/cli/export.ts +0 -276
- package/src/cli/help.ts +0 -293
- package/src/cli/is-tui-invocation.ts +0 -8
- package/src/cli/main.ts +0 -583
- package/src/cli/market-cmd.ts +0 -658
- package/src/cli/mcp-login.ts +0 -141
- package/src/cli/mcp-market-cmd.ts +0 -302
- package/src/cli/output.ts +0 -382
- package/src/cli/repl.ts +0 -250
- package/src/cli/repomap-root.ts +0 -14
- package/src/cli/resume.ts +0 -57
- package/src/cli/run-flags.ts +0 -43
- package/src/cli/run-limits.ts +0 -78
- package/src/cli/runtime.ts +0 -931
- package/src/cli/session-arg.ts +0 -30
- package/src/cli/sessions-cmd.ts +0 -145
- package/src/cli/setup.ts +0 -153
- package/src/cli/skills-cmd.ts +0 -194
- package/src/cli/start-chat.ts +0 -65
- package/src/cli/trust-cmd.ts +0 -52
- package/src/coding/bash.ts +0 -148
- package/src/coding/checkpoints.ts +0 -327
- package/src/coding/diff.ts +0 -138
- package/src/coding/files.ts +0 -341
- package/src/coding/hashline.ts +0 -274
- package/src/coding/lsp-gate.ts +0 -254
- package/src/coding/lsp-servers.ts +0 -147
- package/src/coding/lsp.ts +0 -283
- package/src/coding/repomap-cache.ts +0 -99
- package/src/coding/repomap-files.ts +0 -192
- package/src/coding/repomap.ts +0 -481
- package/src/core/agents.ts +0 -255
- package/src/core/compaction.ts +0 -259
- package/src/core/config.ts +0 -289
- package/src/core/context-report.ts +0 -228
- package/src/core/context.ts +0 -60
- package/src/core/count-remote.ts +0 -107
- package/src/core/execpolicy-rules.ts +0 -196
- package/src/core/execpolicy.ts +0 -385
- package/src/core/executor.ts +0 -454
- package/src/core/guardrails.ts +0 -400
- package/src/core/hooks.ts +0 -411
- package/src/core/images.ts +0 -230
- package/src/core/intro.ts +0 -266
- package/src/core/loop.ts +0 -567
- package/src/core/modes.ts +0 -372
- package/src/core/orchestrator.ts +0 -245
- package/src/core/proc-group.ts +0 -48
- package/src/core/project-trust.ts +0 -98
- package/src/core/reflection.ts +0 -165
- package/src/core/sandbox-config.ts +0 -186
- package/src/core/session-id.ts +0 -24
- package/src/core/session-images.ts +0 -73
- package/src/core/session-ops.ts +0 -183
- package/src/core/session-text.ts +0 -29
- package/src/core/session.ts +0 -469
- package/src/core/settings.ts +0 -170
- package/src/core/tasks.ts +0 -646
- package/src/core/token-scale.ts +0 -108
- package/src/core/tools.ts +0 -309
- package/src/core/trust.ts +0 -104
- package/src/core/types.ts +0 -330
- package/src/core/update-check.ts +0 -171
- package/src/core/usage.ts +0 -204
- package/src/core/validate.ts +0 -121
- package/src/core/verify-gate.ts +0 -159
- package/src/core/verify.ts +0 -236
- package/src/core/voice.ts +0 -158
- package/src/core/win-job.ts +0 -183
- package/src/core/workspace.ts +0 -184
- package/src/design/audit.ts +0 -797
- package/src/design/direction.ts +0 -190
- package/src/design/rules.ts +0 -157
- package/src/eval/bench.ts +0 -150
- package/src/eval/gauntlet-runner.ts +0 -215
- package/src/eval/gauntlet-support.ts +0 -84
- package/src/eval/gauntlet-wave3.ts +0 -269
- package/src/eval/gauntlet-wave4.ts +0 -217
- package/src/eval/gauntlet.ts +0 -253
- package/src/index.ts +0 -17
- package/src/lanes/agy.ts +0 -95
- package/src/lanes/approval.ts +0 -24
- package/src/lanes/claude.ts +0 -129
- package/src/lanes/codex.ts +0 -127
- package/src/lanes/events.ts +0 -130
- package/src/lanes/job.ts +0 -142
- package/src/lanes/opencode.ts +0 -122
- package/src/lanes/process.ts +0 -184
- package/src/lanes/progress.ts +0 -183
- package/src/lanes/registry.ts +0 -178
- package/src/lanes/runner.ts +0 -124
- package/src/lanes/types.ts +0 -112
- package/src/market/catalogs/mcp-docs.json +0 -111
- package/src/market/catalogs/plugins.json +0 -111
- package/src/market/catalogs/skills.json +0 -478
- package/src/market/clone.ts +0 -72
- package/src/market/context-cost.ts +0 -121
- package/src/market/digest.ts +0 -106
- package/src/market/index.ts +0 -22
- package/src/market/install.ts +0 -578
- package/src/market/manifest.ts +0 -187
- package/src/market/prereq.ts +0 -145
- package/src/market/registry.ts +0 -363
- package/src/market/resolve.ts +0 -111
- package/src/market/types.ts +0 -236
- package/src/market/validate.ts +0 -227
- package/src/mcp/client.ts +0 -449
- package/src/mcp/config.ts +0 -252
- package/src/mcp/local-package.ts +0 -211
- package/src/mcp/market-catalog.ts +0 -84
- package/src/mcp/market-install.ts +0 -289
- package/src/mcp/market.ts +0 -362
- package/src/mcp/oauth.ts +0 -251
- package/src/mcp/prompts-resources.ts +0 -249
- package/src/mcp/shared.ts +0 -149
- package/src/mcp/status.ts +0 -67
- package/src/mcp/tools.ts +0 -275
- package/src/mcp/transport.ts +0 -122
- package/src/mcp/trust.ts +0 -25
- package/src/memory/blocks.ts +0 -278
- package/src/memory/recall.ts +0 -355
- package/src/memory/scope.ts +0 -182
- package/src/memory/store.ts +0 -105
- package/src/memory/tools.ts +0 -99
- package/src/plugins/cli.ts +0 -119
- package/src/plugins/discover.ts +0 -108
- package/src/plugins/index.ts +0 -50
- package/src/plugins/install.ts +0 -184
- package/src/plugins/load.ts +0 -124
- package/src/plugins/manifest.ts +0 -92
- package/src/plugins/state.ts +0 -83
- package/src/providers/auth.ts +0 -408
- package/src/providers/cache.ts +0 -223
- package/src/providers/catalog-local.ts +0 -160
- package/src/providers/catalog.ts +0 -421
- package/src/providers/middleware-context.ts +0 -86
- package/src/providers/middleware.ts +0 -373
- package/src/providers/model-list.ts +0 -23
- package/src/providers/oauth/common.ts +0 -105
- package/src/providers/oauth/device-code.ts +0 -107
- package/src/providers/oauth/github-copilot.ts +0 -146
- package/src/providers/oauth/loopback.ts +0 -158
- package/src/providers/oauth/openai.ts +0 -163
- package/src/providers/oauth/openrouter.ts +0 -89
- package/src/providers/oauth/pkce.ts +0 -45
- package/src/providers/oauth/registry.ts +0 -39
- package/src/providers/oauth/seam.ts +0 -89
- package/src/providers/profile-glm53.ts +0 -111
- package/src/providers/profile-sonnet5-persona.ts +0 -65
- package/src/providers/profile-sonnet5-voice.ts +0 -23
- package/src/providers/profiles.ts +0 -156
- package/src/providers/provider-config.ts +0 -311
- package/src/providers/registry.ts +0 -333
- package/src/providers/responses.ts +0 -209
- package/src/providers/retry.ts +0 -234
- package/src/providers/router.ts +0 -294
- package/src/providers/sse.ts +0 -26
- package/src/providers/stream-errors.ts +0 -117
- package/src/providers/stream.ts +0 -566
- package/src/providers/thinking.ts +0 -189
- package/src/providers/wire-messages.ts +0 -129
- package/src/providers/wire-responses.ts +0 -79
- package/src/providers/wire-select.ts +0 -53
- package/src/server/http.ts +0 -291
- package/src/server/openapi.ts +0 -246
- package/src/sextant/card-hits.ts +0 -102
- package/src/sextant/card-keys.ts +0 -55
- package/src/sextant/context-source.ts +0 -157
- package/src/sextant/crew-cards.ts +0 -350
- package/src/sextant/draw-agents.ts +0 -273
- package/src/sextant/draw-code.ts +0 -388
- package/src/sextant/draw-context.ts +0 -222
- package/src/sextant/draw-frame.ts +0 -164
- package/src/sextant/draw-market.ts +0 -573
- package/src/sextant/draw-messages.ts +0 -386
- package/src/sextant/draw-pet.ts +0 -230
- package/src/sextant/draw-plan.ts +0 -187
- package/src/sextant/draw-tabs.ts +0 -85
- package/src/sextant/draw-util.ts +0 -65
- package/src/sextant/draw-wizard.ts +0 -378
- package/src/sextant/engine.ts +0 -230
- package/src/sextant/frame-hits.ts +0 -25
- package/src/sextant/frame.ts +0 -101
- package/src/sextant/git-status.ts +0 -197
- package/src/sextant/grid.ts +0 -59
- package/src/sextant/input.ts +0 -119
- package/src/sextant/keys.ts +0 -521
- package/src/sextant/layout.ts +0 -86
- package/src/sextant/local-commands.ts +0 -169
- package/src/sextant/market-source.ts +0 -287
- package/src/sextant/mentions.ts +0 -200
- package/src/sextant/message-hits.ts +0 -26
- package/src/sextant/model.ts +0 -387
- package/src/sextant/overlays.ts +0 -456
- package/src/sextant/panel-hits.ts +0 -38
- package/src/sextant/pet.ts +0 -399
- package/src/sextant/screen.ts +0 -324
- package/src/sextant/scroll-hits.ts +0 -66
- package/src/sextant/scrollbar.ts +0 -82
- package/src/sextant/sextant-bridge.ts +0 -174
- package/src/sextant/sextant-cards.ts +0 -142
- package/src/sextant/sextant-diff-base.ts +0 -63
- package/src/sextant/sextant-files.ts +0 -154
- package/src/sextant/sextant-frame-loop.ts +0 -335
- package/src/sextant/sextant-renderer.ts +0 -574
- package/src/sextant/sextant-repo.ts +0 -140
- package/src/sextant/theme.ts +0 -66
- package/src/sextant/tool-rows.ts +0 -189
- package/src/sextant/types.ts +0 -493
- package/src/skills/index.ts +0 -387
- package/src/skills/pack.ts +0 -220
- package/src/skills/spec.ts +0 -162
- package/src/skills/tools.ts +0 -69
- package/src/skills/versioned.ts +0 -227
- package/src/telemetry/otel-export.ts +0 -122
- package/src/telemetry/otel-lanes.ts +0 -89
- package/src/telemetry/otel-logs.ts +0 -131
- package/src/telemetry/otel-metrics.ts +0 -136
- package/src/telemetry/otel.ts +0 -397
- package/src/telemetry/otlp.ts +0 -76
- package/src/tools/ask-user.ts +0 -156
- package/src/tools/bash-bg.ts +0 -94
- package/src/tools/bash-jobs.ts +0 -237
- package/src/tools/design.ts +0 -151
- package/src/tools/evalcell.ts +0 -338
- package/src/tools/html-text.ts +0 -139
- package/src/tools/provider.ts +0 -149
- package/src/tools/task.ts +0 -250
- package/src/tools/todo.ts +0 -320
- package/src/tools/webfetch.ts +0 -332
- package/src/tools/websearch.ts +0 -359
- package/src/tui/agents-cmd.ts +0 -41
- package/src/tui/app.ts +0 -749
- package/src/tui/attach.ts +0 -127
- package/src/tui/boot-notes.ts +0 -41
- package/src/tui/builtin-prompts.ts +0 -59
- package/src/tui/checkpoints-cmd.ts +0 -70
- package/src/tui/clipboard-image.ts +0 -81
- package/src/tui/clipboard.ts +0 -78
- package/src/tui/commands.ts +0 -283
- package/src/tui/config-view.ts +0 -53
- package/src/tui/context-cmds.ts +0 -282
- package/src/tui/cost.ts +0 -108
- package/src/tui/crash-guard.ts +0 -173
- package/src/tui/focus-terminal.ts +0 -34
- package/src/tui/git-cmds.ts +0 -273
- package/src/tui/git-plain.ts +0 -58
- package/src/tui/info-cmd.ts +0 -150
- package/src/tui/input-plain.ts +0 -76
- package/src/tui/mcp-cmd.ts +0 -128
- package/src/tui/memory-note.ts +0 -77
- package/src/tui/modes-cmd.ts +0 -45
- package/src/tui/notify-seq.ts +0 -100
- package/src/tui/notify.ts +0 -318
- package/src/tui/overlays.ts +0 -97
- package/src/tui/pi-renderer.ts +0 -428
- package/src/tui/providers-cmd.ts +0 -377
- package/src/tui/reasoning-view.ts +0 -56
- package/src/tui/renderer.ts +0 -128
- package/src/tui/replay-marker.ts +0 -29
- package/src/tui/session-cmd.ts +0 -148
- package/src/tui/session-manage.ts +0 -95
- package/src/tui/sextant-attach.ts +0 -102
- package/src/tui/sextant-io.ts +0 -202
- package/src/tui/sextant-smoke.ts +0 -110
- package/src/tui/shell-cmd.ts +0 -158
- package/src/tui/smoke.ts +0 -72
- package/src/tui/staged-terminal.ts +0 -50
- package/src/tui/startup.ts +0 -12
- package/src/tui/theme.ts +0 -59
- package/src/tui/todo-label.ts +0 -7
- package/src/tui/trust-card.ts +0 -107
- package/src/tui/tui-commands.ts +0 -87
- package/tsconfig.json +0 -30
- package/vendor/pi-tui/LICENSE +0 -21
- package/vendor/pi-tui/PATCHES.md +0 -12
- package/vendor/pi-tui/PROVENANCE.md +0 -12
- package/vendor/pi-tui/README.upstream.md +0 -854
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
- package/vendor/pi-tui/src/autocomplete.ts +0 -827
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
- package/vendor/pi-tui/src/components/box.ts +0 -138
- package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
- package/vendor/pi-tui/src/components/editor.ts +0 -2364
- package/vendor/pi-tui/src/components/h-stack.ts +0 -45
- package/vendor/pi-tui/src/components/image.ts +0 -128
- package/vendor/pi-tui/src/components/input.ts +0 -448
- package/vendor/pi-tui/src/components/loader.ts +0 -93
- package/vendor/pi-tui/src/components/markdown.ts +0 -1016
- package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
- package/vendor/pi-tui/src/components/select-list.ts +0 -230
- package/vendor/pi-tui/src/components/settings-list.ts +0 -277
- package/vendor/pi-tui/src/components/spacer.ts +0 -29
- package/vendor/pi-tui/src/components/stack.ts +0 -155
- package/vendor/pi-tui/src/components/text.ts +0 -108
- package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
- package/vendor/pi-tui/src/components/v-stack.ts +0 -34
- package/vendor/pi-tui/src/editor-component.ts +0 -75
- package/vendor/pi-tui/src/fuzzy.ts +0 -138
- package/vendor/pi-tui/src/index.ts +0 -149
- package/vendor/pi-tui/src/keybindings.ts +0 -321
- package/vendor/pi-tui/src/keys.ts +0 -1402
- package/vendor/pi-tui/src/kill-ring.ts +0 -47
- package/vendor/pi-tui/src/latex.ts +0 -1381
- package/vendor/pi-tui/src/layout-node.ts +0 -52
- package/vendor/pi-tui/src/layout.ts +0 -411
- package/vendor/pi-tui/src/native-modifiers.ts +0 -60
- package/vendor/pi-tui/src/native-module-path.ts +0 -32
- package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
- package/vendor/pi-tui/src/terminal-colors.ts +0 -74
- package/vendor/pi-tui/src/terminal-image.ts +0 -701
- package/vendor/pi-tui/src/terminal.ts +0 -554
- package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
- package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
- package/vendor/pi-tui/src/tui.ts +0 -1264
- package/vendor/pi-tui/src/undo-stack.ts +0 -29
- package/vendor/pi-tui/src/utils.ts +0 -1327
- package/vendor/pi-tui/src/word-navigation.ts +0 -118
- package/vendor/pi-tui/test/test-themes.ts +0 -39
- package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
- /package/{src/providers → dist/cli}/models-index.json +0 -0
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/** rovecode's own price table for models the models.dev snapshot does not carry — the API ALIASES a
|
|
2
|
-
* vendor sells under (DeepSeek's `deepseek-chat` / `deepseek-reasoner` are aliases of a release; the
|
|
3
|
-
* snapshot lists release ids), and xAI's retired grok-4 slugs, which still resolve but are served and
|
|
4
|
-
* billed as grok-4.3. Without these a real run on those ids is "unpriced" in /cost and `rovecode model show`.
|
|
5
|
-
*
|
|
6
|
-
* Rules: an entry here applies ONLY when neither the live models.dev layer nor the snapshot has the model
|
|
7
|
-
* (catalog.ts findIn) — a later snapshot entry wins by construction. Every entry names the vendor page the
|
|
8
|
-
* numbers were read from and the day; "fetched" means the page was read on that day, "unverified" means the
|
|
9
|
-
* page no longer documents the id and the row is the last known value. `rovecode model show` prints
|
|
10
|
-
* "priced from rovecode's own table" so nobody mistakes them for models.dev's. Prices are USD per million
|
|
11
|
-
* tokens. Output limits are given only where the vendor publishes one (the runtime caps at MAX_OUTPUT_CAP). */
|
|
12
|
-
|
|
13
|
-
export interface LocalModel {
|
|
14
|
-
context: number;
|
|
15
|
-
output?: number;
|
|
16
|
-
/** has a reasoning mode (thinking.ts sends the dial only when this is not false) */
|
|
17
|
-
reasoning: boolean;
|
|
18
|
-
toolCall: boolean;
|
|
19
|
-
image?: boolean;
|
|
20
|
-
cost: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
|
|
21
|
-
/** where the numbers were read — the reader's warrant */
|
|
22
|
-
source: string;
|
|
23
|
-
/** when, and how: "YYYY-MM-DD (fetched)" or "YYYY-MM-DD (unverified: …)" */
|
|
24
|
-
checked: string;
|
|
25
|
-
/** Take precedence over a models.dev entry for this model. Off by default and deliberately rare: the
|
|
26
|
-
* overlay exists to fill gaps, and a snapshot that gains a model should normally win. Set it ONLY when
|
|
27
|
-
* the vendor's own page was read and disagrees with the snapshot, and say so in `source` — a reader has
|
|
28
|
-
* to be able to see which number rovecode chose and why. */
|
|
29
|
-
override?: true;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** A vendor whose price CHANGES WITH PROMPT SIZE. models.dev has no field for this, so it lives beside the
|
|
33
|
-
* overlay and augments a row whatever its source — the snapshot keeps owning the base prices.
|
|
34
|
-
*
|
|
35
|
-
* `mode` is the part that is easy to get wrong and expensive to get wrong:
|
|
36
|
-
* - "per-request": crossing the threshold reprices THE WHOLE REQUEST, every token, at the upper rate
|
|
37
|
-
* (xAI). A 201k-token prompt costs 201k × the high rate, not 200k low + 1k high.
|
|
38
|
-
* - "marginal": only the tokens past the threshold take the upper rate. NO vendor row uses this today —
|
|
39
|
-
* it exists because the two shapes are not interchangeable and a cost function must branch on which one
|
|
40
|
-
* it has, rather than assume. Do not classify a vendor as marginal without wording that says so. */
|
|
41
|
-
export interface PriceTier {
|
|
42
|
-
thresholdTokens: number;
|
|
43
|
-
mode: "per-request" | "marginal";
|
|
44
|
-
above: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
|
|
45
|
-
source: string;
|
|
46
|
-
checked: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** xAI, docs.x.ai/developers/migration/may-15-retirement (fetched 2026-09-04): grok-4-0709, grok-4-fast-reasoning,
|
|
50
|
-
* grok-4-fast-non-reasoning, grok-4-1-fast-* and grok-3 were retired on 2026-05-15; the slugs still resolve and
|
|
51
|
-
* are SERVED BY grok-4.3 — reasoning slugs at low effort, non-reasoning ones at none — and BILLED at grok-4.3
|
|
52
|
-
* rates ($1.25 in / $2.50 out / $0.20 cached per 1M under 200k context; docs.x.ai/docs/models/grok-4.3, fetched
|
|
53
|
-
* the same day: 1M context, configurable reasoning none|low|medium|high). So a request to `grok-4` costs this. */
|
|
54
|
-
const GROK_43_REDIRECT = { context: 1_000_000, toolCall: true, image: true, cost: { input: 1.25, output: 2.5, cacheRead: 0.2 },
|
|
55
|
-
source: "docs.x.ai May-15-2026 retirement: served by grok-4.3, billed at grok-4.3 rates (under 200k context)", checked: "2026-09-04 (fetched)" } as const;
|
|
56
|
-
|
|
57
|
-
/** Tiered pricing, by provider and model id. Verified 2026-09-05 against the vendors' own pricing pages.
|
|
58
|
-
* Nothing here changes a base price — `catalog.ts` attaches the tier to whatever row it resolved, so a
|
|
59
|
-
* models.dev entry keeps its numbers and gains the threshold it has no field for. */
|
|
60
|
-
export const PRICE_TIERS: Readonly<Record<string, Readonly<Record<string, PriceTier>>>> = {
|
|
61
|
-
// xAI: "requests over 200,000 prompt tokens are billed at the higher rate FOR THE ENTIRE REQUEST"
|
|
62
|
-
xai: {
|
|
63
|
-
"grok-4.6": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 12, cacheRead: 1 },
|
|
64
|
-
source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
|
|
65
|
-
"grok-4.3": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 5, cacheRead: 0.4 },
|
|
66
|
-
source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
|
|
67
|
-
},
|
|
68
|
-
// OpenAI, developers.openai.com/api/docs/models/gpt-5.4 and /gpt-5.5: "Prompts with >272K input tokens
|
|
69
|
-
// are priced at 2x input and 1.5x output for the full session." The multipliers are ASYMMETRIC — input
|
|
70
|
-
// doubles, output goes up by half — so these are written out rather than derived from one rate.
|
|
71
|
-
// The -pro tiers are NOT given this: their own pages do not carry the sentence, and inventing a
|
|
72
|
-
// threshold for a $30/1M model is the expensive kind of guess.
|
|
73
|
-
openai: {
|
|
74
|
-
"gpt-5.4": { thresholdTokens: 272_000, mode: "per-request", above: { input: 5, output: 22.5, cacheRead: 0.5 },
|
|
75
|
-
source: "developers.openai.com/api/docs/models/gpt-5.4 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
|
|
76
|
-
"gpt-5.5": { thresholdTokens: 272_000, mode: "per-request", above: { input: 10, output: 45, cacheRead: 1 },
|
|
77
|
-
source: "developers.openai.com/api/docs/models/gpt-5.5 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
|
|
78
|
-
},
|
|
79
|
-
// Google prices by PROMPT SIZE, not by overflow: the page's own columns read "prompts <= 200k tokens"
|
|
80
|
-
// and "prompts > 200k tokens" for BOTH input and output, so a 201k prompt bills every token — and that
|
|
81
|
-
// request's output — at the upper rate. Same shape as xAI, which is why neither is "marginal".
|
|
82
|
-
google: {
|
|
83
|
-
"gemini-3.1-pro-preview": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 18 },
|
|
84
|
-
source: "ai.google.dev/gemini-api/docs/pricing — \"$4.00, prompts > 200k tokens\" / \"$18.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
|
|
85
|
-
"gemini-2.5-pro": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 15 },
|
|
86
|
-
source: "ai.google.dev/gemini-api/docs/pricing — \"$2.50, prompts > 200k tokens\" / \"$15.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
|
|
87
|
-
},
|
|
88
|
-
} as const;
|
|
89
|
-
|
|
90
|
-
/** A price that is scheduled to change, or a condition the number depends on. Not a tier and not an
|
|
91
|
-
* override — just the sentence a reader needs so today's number is not mistaken for a permanent one.
|
|
92
|
-
* Attached to a row whatever its source. */
|
|
93
|
-
export const PRICE_NOTES: Readonly<Record<string, Readonly<Record<string, string>>>> = {
|
|
94
|
-
google: {
|
|
95
|
-
"gemini-3.8-flash": "promotional pricing through 2026-12-31: ai.google.dev/gemini-api/docs/pricing says it doubles to $1.50 in / $7.50 out starting 2027-01-01 (checked 2026-09-05)",
|
|
96
|
-
},
|
|
97
|
-
mistral: {
|
|
98
|
-
// Checked against mistral.ai/pricing/api on 2026-09-05: the table publishes ONLY `-latest` aliases
|
|
99
|
-
// (mistral-medium-latest $1.5/$7.5, mistral-small-latest $0.15/$0.6, mistral-large-latest $0.5/$1.5).
|
|
100
|
-
// No dated id appears — not the snapshot's `mistral-medium-2604`, and not `mistral-medium-3-5` or
|
|
101
|
-
// `ministral-8b-2512` either. The prices match, the ids are unverifiable, so nothing is renamed here.
|
|
102
|
-
"mistral-medium-latest": "mistral.ai/pricing/api publishes only `-latest` aliases and NO maximum output for any model — the catalog's maxOutput equalling the context window is filler, not a vendor figure (checked 2026-09-05)",
|
|
103
|
-
"mistral-medium-2604": "this dated id is not on mistral.ai/pricing/api, which lists only `-latest` aliases; the price matches mistral-medium-latest but the id itself is unverified (checked 2026-09-05)",
|
|
104
|
-
},
|
|
105
|
-
groq: {
|
|
106
|
-
// Not "unverified" — UNPUBLISHED. Groq's docs table says "Contact Sales" for the Llama models and
|
|
107
|
-
// console.groq.com/pricing is a 404, so any number here came from a third party, not from Groq.
|
|
108
|
-
"llama-3.3-70b-versatile": "Groq does not publish a price for this model — its docs table says \"Contact Sales\" and there is no public pricing page; the catalog's figure is models.dev's, not the vendor's (checked 2026-09-05)",
|
|
109
|
-
},
|
|
110
|
-
anthropic: {
|
|
111
|
-
// true of every Anthropic row; recorded on the one most runs use
|
|
112
|
-
"claude-mythos-5-1": "invite-only access (platform.claude.com/docs/en/models/mythos-5-1/overview); outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
|
|
113
|
-
"claude-fable-5-1": "outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
|
|
114
|
-
"claude-opus-5": "thinking tokens bill as OUTPUT and count inside max_tokens; from Opus 4.5 / Sonnet 4.6 / Fable 5.x earlier thinking blocks are preserved and bill as INPUT on the next turn (platform.claude.com/docs, checked 2026-09-05)",
|
|
115
|
-
},
|
|
116
|
-
} as const;
|
|
117
|
-
|
|
118
|
-
export const LOCAL_MODELS: Readonly<Record<string, Readonly<Record<string, LocalModel>>>> = {
|
|
119
|
-
deepseek: {
|
|
120
|
-
// api-docs.deepseek.com/quick_start/pricing, fetched 2026-09-05. These three rows OVERRIDE models.dev,
|
|
121
|
-
// which carries roughly a third of the vendor's own peak numbers for the same ids (flash 0.14/0.28 vs
|
|
122
|
-
// 0.44/1.32). The page is the seller; the snapshot is a mirror that has drifted, so the page wins here
|
|
123
|
-
// and says so. Peak rates are used: the page's own off-peak wording is self-contradictory (it labels
|
|
124
|
-
// "peak" 01:00-04:00 and 06:00-10:00 UTC, which are its cheap hours elsewhere), so rovecode quotes the
|
|
125
|
-
// HIGHER of the two — an estimate that surprises nobody with a bigger bill. Do not build discount logic
|
|
126
|
-
// on that wording until the page is unambiguous.
|
|
127
|
-
"deepseek-v4-flash": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
|
|
128
|
-
cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
|
|
129
|
-
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
130
|
-
"deepseek-v4-pro": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
|
|
131
|
-
cost: { input: 1.32, output: 3.96, cacheRead: 0.044 },
|
|
132
|
-
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
133
|
-
"deepseek-v4-flash-vision-exp": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, image: true, override: true,
|
|
134
|
-
cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
|
|
135
|
-
source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
|
|
136
|
-
// The two API aliases. The pricing page lists only the v4-* ids, so nothing here is verified: the
|
|
137
|
-
// numbers are the last ones DeepSeek published for them and are marked as such rather than being
|
|
138
|
-
// quietly refreshed to a v4 price they may not be billed at.
|
|
139
|
-
"deepseek-chat": { context: 128_000, output: 8_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: the alias is not on the pricing page, which lists deepseek-v4-* only)" },
|
|
140
|
-
"deepseek-reasoner": { context: 128_000, output: 64_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: not on the pricing page; DeepSeek now exposes thinking as a MODE of the v4 models, not a separate id)" },
|
|
141
|
-
},
|
|
142
|
-
openai: {
|
|
143
|
-
// developers.openai.com/api/docs/pricing, fetched 2026-09-05. Not in the snapshot at all, so this is a
|
|
144
|
-
// gap fill, not an override.
|
|
145
|
-
"gpt-6-astra": { context: 1_050_000, output: 128_000, reasoning: true, toolCall: true, image: true,
|
|
146
|
-
cost: { input: 10, output: 50, cacheRead: 1 },
|
|
147
|
-
source: "developers.openai.com/api/docs/pricing", checked: "2026-09-05 (fetched)" },
|
|
148
|
-
},
|
|
149
|
-
xai: {
|
|
150
|
-
"grok-4": { ...GROK_43_REDIRECT, reasoning: true },
|
|
151
|
-
"grok-4-0709": { ...GROK_43_REDIRECT, reasoning: true },
|
|
152
|
-
"grok-4-fast": { ...GROK_43_REDIRECT, reasoning: true },
|
|
153
|
-
"grok-4-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
|
|
154
|
-
"grok-4-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
|
|
155
|
-
"grok-4-1-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
|
|
156
|
-
"grok-4-1-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
|
|
157
|
-
"grok-3": { ...GROK_43_REDIRECT, reasoning: false },
|
|
158
|
-
// grok-3-mini: neither on docs.x.ai/docs/models nor in the retirement list on 2026-09-04 — no row, stays unpriced
|
|
159
|
-
},
|
|
160
|
-
};
|
package/src/providers/catalog.ts
DELETED
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
/** Model catalog: capability/pricing lookups backed by the models.dev database (ADR: port #6).
|
|
2
|
-
*
|
|
3
|
-
* Source of truth is the offline snapshot bundled in @opencode-ai/models (generated from
|
|
4
|
-
* https://models.dev/api.json). Live refresh against models.dev itself is entirely OPT-IN
|
|
5
|
-
* (only when a `fetchFn` is supplied) and layers on top of — never replaces — the offline
|
|
6
|
-
* data, so a fresh `new ModelCatalog()` works fully offline with zero network access. */
|
|
7
|
-
|
|
8
|
-
import { readFileSync, writeFileSync, mkdirSync, statSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
import type { ProviderMap, Model } from "@opencode-ai/models";
|
|
11
|
-
import { LOCAL_MODELS, PRICE_NOTES, PRICE_TIERS, type LocalModel, type PriceTier } from "./catalog-local.ts";
|
|
12
|
-
|
|
13
|
-
// Loaded lazily on the first lookup() so that importing catalog.ts costs nothing — the TUI paints its
|
|
14
|
-
// first frame before buildDef() is called.
|
|
15
|
-
//
|
|
16
|
-
// It reads src/providers/models-index.json, not @opencode-ai/models/snapshot. Same shape, same source,
|
|
17
|
-
// five fields per model instead of the whole models.dev record: the upstream snapshot is 4.26 MB and
|
|
18
|
-
// parsing it costs 55 MB resident, which every session paid on its first status update to answer "what
|
|
19
|
-
// is this model's window and price". The trimmed index is 1.1 MB. scripts/build-model-index.mjs
|
|
20
|
-
// generates it and `--check` fails when it and the installed package disagree, so an upgrade cannot
|
|
21
|
-
// leave the catalog quietly describing the previous release.
|
|
22
|
-
let _snapshot: ProviderMap | null = null;
|
|
23
|
-
function snapshotProviders(): ProviderMap {
|
|
24
|
-
if (_snapshot === null) {
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
-
_snapshot = (require("./models-index.json") as { providers: ProviderMap }).providers;
|
|
27
|
-
}
|
|
28
|
-
return _snapshot;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ModelInfo {
|
|
32
|
-
provider: string;
|
|
33
|
-
model: string;
|
|
34
|
-
contextWindow?: number;
|
|
35
|
-
maxOutput?: number;
|
|
36
|
-
pricing?: { inputPerMTok?: number; outputPerMTok?: number; cacheReadPerMTok?: number; cacheWritePerMTok?: number };
|
|
37
|
-
supportsTools?: boolean;
|
|
38
|
-
supportsReasoning?: boolean;
|
|
39
|
-
/** where the numbers come from: models.dev (snapshot or live) or rovecode's own table (catalog-local.ts) for a
|
|
40
|
-
* model the snapshot lacks — `rovecode model show` says which, so a local price is never mistaken for models.dev's */
|
|
41
|
-
source?: "models.dev" | "local";
|
|
42
|
-
/** local entries only: the vendor page the numbers were read from, and when */
|
|
43
|
-
sourceNote?: string;
|
|
44
|
-
/** this model's price changes past a prompt size (xAI, Gemini). Attached whatever the row's source —
|
|
45
|
-
* models.dev has no field for it. A cost estimator that ignores it under-quotes a long prompt, and for
|
|
46
|
-
* `mode: "per-request"` it under-quotes the WHOLE request, not just the overflow. */
|
|
47
|
-
tier?: PriceTier;
|
|
48
|
-
/** a scheduled change or a condition attached to the price ("doubles on 2027-01-01") */
|
|
49
|
-
priceNote?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface CatalogOptions {
|
|
53
|
-
fetchFn?: typeof fetch | null; // null = offline only; default null (live fetch is OPT-IN)
|
|
54
|
-
cacheDir?: string; // when live: cache api.json to <cacheDir>/models.json, ttl 24h
|
|
55
|
-
/** the local overlay (default catalog-local.ts LOCAL_MODELS); tests inject their own */
|
|
56
|
-
local?: Readonly<Record<string, Readonly<Record<string, LocalModel>>>>;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const CACHE_TTL_MS = 24 * 60 * 60_000;
|
|
60
|
-
const CACHE_FILE = "models.json";
|
|
61
|
-
const LIVE_URL = "https://models.dev/api.json";
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* rovecode provider id -> models.dev provider key. Derived by loading the offline snapshot
|
|
65
|
-
* (node_modules/@opencode-ai/models/dist/snapshot.js) and inspecting Object.keys(providers)
|
|
66
|
-
* directly rather than guessing:
|
|
67
|
-
* - together -> "togetherai" and fireworks -> "fireworks-ai" (not the bare names)
|
|
68
|
-
* - lmstudio -> "lmstudio" DOES exist in the snapshot (3 curated local models), so it maps
|
|
69
|
-
* - everything else here is an exact 1:1 id match confirmed present in the snapshot
|
|
70
|
-
*
|
|
71
|
-
* Deliberately absent (verified NOT a key in the snapshot -> lookup() returns undefined):
|
|
72
|
-
* kaesra (rovecode's own proxy brand, not a models.dev provider — its vendor-prefixed model ids
|
|
73
|
-
* resolve through VENDOR_PREFIX_MAP below instead), ollama (only "ollama-cloud" exists, not
|
|
74
|
-
* bare "ollama"), moondream, vllm.
|
|
75
|
-
*/
|
|
76
|
-
const PROVIDER_MAP: Record<string, string> = {
|
|
77
|
-
openai: "openai",
|
|
78
|
-
anthropic: "anthropic",
|
|
79
|
-
deepseek: "deepseek",
|
|
80
|
-
groq: "groq",
|
|
81
|
-
openrouter: "openrouter",
|
|
82
|
-
lmstudio: "lmstudio",
|
|
83
|
-
together: "togetherai",
|
|
84
|
-
mistral: "mistral",
|
|
85
|
-
cerebras: "cerebras",
|
|
86
|
-
fireworks: "fireworks-ai",
|
|
87
|
-
perplexity: "perplexity",
|
|
88
|
-
xai: "xai",
|
|
89
|
-
// not built-in providers, but the ids people give `rovecode provider add` for these vendors' own endpoints
|
|
90
|
-
// (Google's OpenAI layer, Z.ai, Moonshot, Alibaba DashScope, MiniMax) — mapped so their models price and
|
|
91
|
-
// carry the reasoning flag like the built-ins (2026-09-04: models.dev snapshot 0.0.64 has all five)
|
|
92
|
-
google: "google",
|
|
93
|
-
gemini: "google",
|
|
94
|
-
zai: "zai",
|
|
95
|
-
moonshot: "moonshotai",
|
|
96
|
-
moonshotai: "moonshotai",
|
|
97
|
-
alibaba: "alibaba",
|
|
98
|
-
dashscope: "alibaba",
|
|
99
|
-
minimax: "minimax",
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* HuggingFace-style vendor prefix -> models.dev provider key, for aggregator providers
|
|
104
|
-
* (kaesra, or any custom base URL) that serve models under "vendor/model" ids. Lets
|
|
105
|
-
* lookup("kaesra", "zai-org/glm-5.3-flash") price against the zai snapshot entry.
|
|
106
|
-
* Verified against Object.keys(snapshotProviders): "zai", "deepseek", "moonshotai" all
|
|
107
|
-
* exist; there is NO bare "moonshot" key (only moonshotai/moonshotai-cn), hence the
|
|
108
|
-
* identity mapping for moonshotai.
|
|
109
|
-
*/
|
|
110
|
-
const VENDOR_PREFIX_MAP: Record<string, string> = {
|
|
111
|
-
"zai-org": "zai",
|
|
112
|
-
"deepseek-ai": "deepseek",
|
|
113
|
-
"moonshotai": "moonshotai",
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
/** the tiered-price row for a model, if the vendor has one (case-insensitive, like every other lookup) */
|
|
117
|
-
function tierFor(providerId: string, modelId: string): PriceTier | undefined {
|
|
118
|
-
const table = PRICE_TIERS[providerId.toLowerCase()];
|
|
119
|
-
if (!table) return undefined;
|
|
120
|
-
const key = Object.keys(table).find((k) => k.toLowerCase() === modelId.toLowerCase());
|
|
121
|
-
return key === undefined ? undefined : table[key];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function noteFor(providerId: string, modelId: string): string | undefined {
|
|
125
|
-
const table = PRICE_NOTES[providerId.toLowerCase()];
|
|
126
|
-
if (!table) return undefined;
|
|
127
|
-
const key = Object.keys(table).find((k) => k.toLowerCase() === modelId.toLowerCase());
|
|
128
|
-
return key === undefined ? undefined : table[key];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** The four rates one request is billed at. Same field names as `ModelInfo.pricing`, so a caller can pass
|
|
132
|
-
* either where a rate table is wanted. */
|
|
133
|
-
export interface Rates { inputPerMTok?: number; outputPerMTok?: number; cacheReadPerMTok?: number; cacheWritePerMTok?: number }
|
|
134
|
-
|
|
135
|
-
/** What `promptTokens` actually costs on this model, split the way the vendor bills it. PURE — no I/O, no
|
|
136
|
-
* clock, no rounding: a cost function multiplies these and decides its own precision.
|
|
137
|
-
*
|
|
138
|
-
* Read `promptBase`/`promptAbove` as token counts to multiply by `base`/`above`; `request` is the rate
|
|
139
|
-
* table for everything that is not the prompt (output, cache) on this request, because both vendors we
|
|
140
|
-
* have select the OUTPUT price by prompt size too. For a flat model the split is trivial (everything in
|
|
141
|
-
* `promptBase`) so a caller never needs to branch on whether a tier exists — only on whether it wants the
|
|
142
|
-
* detail. */
|
|
143
|
-
export interface RateBreakdown {
|
|
144
|
-
mode: "flat" | "per-request" | "marginal";
|
|
145
|
-
/** true when the prompt crossed a threshold and the upper rates are in play */
|
|
146
|
-
tierApplied: boolean;
|
|
147
|
-
thresholdTokens?: number;
|
|
148
|
-
promptBase: number;
|
|
149
|
-
promptAbove: number;
|
|
150
|
-
base: Rates;
|
|
151
|
-
above?: Rates;
|
|
152
|
-
request: Rates;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/** The rate breakdown for a prompt of `promptTokens` on this model. A negative or non-finite count is
|
|
156
|
-
* treated as 0 — an estimator must not be able to produce a negative bill. */
|
|
157
|
-
export function ratesFor(info: ModelInfo, promptTokens: number): RateBreakdown {
|
|
158
|
-
const base: Rates = { ...(info.pricing ?? {}) };
|
|
159
|
-
const n = Number.isFinite(promptTokens) && promptTokens > 0 ? Math.floor(promptTokens) : 0;
|
|
160
|
-
const t = info.tier;
|
|
161
|
-
if (!t || n <= t.thresholdTokens) {
|
|
162
|
-
return { mode: t ? t.mode : "flat", tierApplied: false, ...(t ? { thresholdTokens: t.thresholdTokens } : {}),
|
|
163
|
-
promptBase: n, promptAbove: 0, base, ...(t ? { above: ratesOf(t) } : {}), request: base };
|
|
164
|
-
}
|
|
165
|
-
const above = ratesOf(t);
|
|
166
|
-
return t.mode === "per-request"
|
|
167
|
-
// every token of the prompt, and this request's output, at the upper rate
|
|
168
|
-
? { mode: "per-request", tierApplied: true, thresholdTokens: t.thresholdTokens, promptBase: 0, promptAbove: n, base, above, request: above }
|
|
169
|
-
// only the overflow at the upper rate; output follows the upper table, which is the conservative read
|
|
170
|
-
: { mode: "marginal", tierApplied: true, thresholdTokens: t.thresholdTokens, promptBase: t.thresholdTokens, promptAbove: n - t.thresholdTokens, base, above, request: above };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function ratesOf(t: PriceTier): Rates {
|
|
174
|
-
return { inputPerMTok: t.above.input, outputPerMTok: t.above.output,
|
|
175
|
-
...(t.above.cacheRead !== undefined ? { cacheReadPerMTok: t.above.cacheRead } : {}),
|
|
176
|
-
...(t.above.cacheWrite !== undefined ? { cacheWritePerMTok: t.above.cacheWrite } : {}) };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** The price lines a surface shows — one place, so `model show`, /cost and the market cannot drift apart.
|
|
180
|
-
* Empty when the model is unpriced. */
|
|
181
|
-
export function describePricing(info: ModelInfo): string[] {
|
|
182
|
-
const p = info.pricing;
|
|
183
|
-
if (!p || (p.inputPerMTok === undefined && p.outputPerMTok === undefined)) return [];
|
|
184
|
-
const money = (n?: number): string => (n === undefined ? "—" : `$${n}`);
|
|
185
|
-
const lines = [`${money(p.inputPerMTok)} in / ${money(p.outputPerMTok)} out per 1M${p.cacheReadPerMTok !== undefined ? ` · cache read ${money(p.cacheReadPerMTok)}` : ""}${p.cacheWritePerMTok !== undefined ? ` · cache write ${money(p.cacheWritePerMTok)}` : ""}`];
|
|
186
|
-
if (info.tier) {
|
|
187
|
-
const t = info.tier;
|
|
188
|
-
lines.push(t.mode === "per-request"
|
|
189
|
-
? `over ${t.thresholdTokens.toLocaleString("en-US")} prompt tokens the WHOLE request bills at ${money(t.above.input)} in / ${money(t.above.output)} out`
|
|
190
|
-
: `tokens past ${t.thresholdTokens.toLocaleString("en-US")} bill at ${money(t.above.input)} in / ${money(t.above.output)} out`);
|
|
191
|
-
}
|
|
192
|
-
if (info.priceNote) lines.push(info.priceNote);
|
|
193
|
-
return lines;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** a local overlay row in the snapshot's shape, so one toModelInfo serves both */
|
|
197
|
-
function localAsModel(id: string, m: LocalModel): Model {
|
|
198
|
-
return {
|
|
199
|
-
id, name: id, description: `${m.source} — checked ${m.checked}`, attachment: false, reasoning: m.reasoning, tool_call: m.toolCall,
|
|
200
|
-
release_date: m.checked, modalities: { input: m.image ? ["text", "image"] : ["text"], output: ["text"] },
|
|
201
|
-
limit: { context: m.context, ...(m.output !== undefined ? { output: m.output } : {}) },
|
|
202
|
-
cost: { input: m.cost.input, output: m.cost.output, ...(m.cost.cacheRead !== undefined ? { cache_read: m.cost.cacheRead } : {}), ...(m.cost.cacheWrite !== undefined ? { cache_write: m.cost.cacheWrite } : {}) },
|
|
203
|
-
} as unknown as Model;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function isProviderMap(value: unknown): value is ProviderMap {
|
|
207
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
208
|
-
for (const entry of Object.values(value as Record<string, unknown>)) {
|
|
209
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
|
|
210
|
-
const models = (entry as { models?: unknown }).models;
|
|
211
|
-
if (typeof models !== "object" || models === null || Array.isArray(models)) return false;
|
|
212
|
-
}
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** exact match, then case-insensitive, then match after stripping a single leading "vendor/" prefix. */
|
|
217
|
-
function findModelKey(models: Record<string, Model>, modelId: string): string | undefined {
|
|
218
|
-
if (modelId in models) return modelId;
|
|
219
|
-
const lower = modelId.toLowerCase();
|
|
220
|
-
for (const key of Object.keys(models)) {
|
|
221
|
-
if (key.toLowerCase() === lower) return key;
|
|
222
|
-
}
|
|
223
|
-
const slashIndex = modelId.indexOf("/");
|
|
224
|
-
if (slashIndex === -1) return undefined;
|
|
225
|
-
const stripped = modelId.slice(slashIndex + 1);
|
|
226
|
-
if (stripped in models) return stripped;
|
|
227
|
-
const strippedLower = stripped.toLowerCase();
|
|
228
|
-
for (const key of Object.keys(models)) {
|
|
229
|
-
if (key.toLowerCase() === strippedLower) return key;
|
|
230
|
-
}
|
|
231
|
-
return undefined;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function toModelInfo(providerId: string, modelId: string, model: Model): ModelInfo {
|
|
235
|
-
const info: ModelInfo = { provider: providerId, model: modelId };
|
|
236
|
-
if (model.limit?.context !== undefined) info.contextWindow = model.limit.context;
|
|
237
|
-
if (model.limit?.output !== undefined) info.maxOutput = model.limit.output;
|
|
238
|
-
const cost = model.cost;
|
|
239
|
-
if (cost) {
|
|
240
|
-
const pricing: NonNullable<ModelInfo["pricing"]> = {};
|
|
241
|
-
if (cost.input !== undefined) pricing.inputPerMTok = cost.input;
|
|
242
|
-
if (cost.output !== undefined) pricing.outputPerMTok = cost.output;
|
|
243
|
-
if (cost.cache_read !== undefined) pricing.cacheReadPerMTok = cost.cache_read;
|
|
244
|
-
if (cost.cache_write !== undefined) pricing.cacheWritePerMTok = cost.cache_write;
|
|
245
|
-
if (Object.keys(pricing).length > 0) info.pricing = pricing;
|
|
246
|
-
}
|
|
247
|
-
if (model.tool_call !== undefined) info.supportsTools = model.tool_call;
|
|
248
|
-
if (model.reasoning !== undefined) info.supportsReasoning = model.reasoning;
|
|
249
|
-
return info;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export class ModelCatalog {
|
|
253
|
-
private readonly fetchFn: typeof fetch | null;
|
|
254
|
-
private readonly cacheDir: string | undefined;
|
|
255
|
-
private readonly local: Readonly<Record<string, Readonly<Record<string, LocalModel>>>>;
|
|
256
|
-
private liveProviders: ProviderMap | null = null;
|
|
257
|
-
private triedDiskCache = false;
|
|
258
|
-
|
|
259
|
-
constructor(opts: CatalogOptions = {}) {
|
|
260
|
-
this.fetchFn = opts.fetchFn ?? null;
|
|
261
|
-
this.cacheDir = opts.cacheDir;
|
|
262
|
-
this.local = opts.local ?? LOCAL_MODELS;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** offline snapshot first; live cache layered on top when enabled.
|
|
266
|
-
* Resolution order: (1) the rovecode provider's own models.dev entry (PROVIDER_MAP), then
|
|
267
|
-
* (2) the model id's vendor prefix (VENDOR_PREFIX_MAP) with the prefix stripped — the
|
|
268
|
-
* path that makes aggregator providers like kaesra (default model zai-org/glm-5.3-flash)
|
|
269
|
-
* priceable. The vendor hit reports the vendor as `provider`, naming the pricing source. */
|
|
270
|
-
lookup(providerId: string, modelId: string): ModelInfo | undefined {
|
|
271
|
-
const hit = this.resolve(providerId, modelId);
|
|
272
|
-
if (!hit) return undefined;
|
|
273
|
-
const info = toModelInfo(hit.as, hit.key, hit.model);
|
|
274
|
-
info.source = hit.local ? "local" : "models.dev";
|
|
275
|
-
if (hit.local) info.sourceNote = `${hit.local.source}, checked ${hit.local.checked}`;
|
|
276
|
-
const tier = tierFor(providerId, modelId) ?? tierFor(providerId, hit.key);
|
|
277
|
-
if (tier) info.tier = tier;
|
|
278
|
-
const note = noteFor(providerId, modelId) ?? noteFor(providerId, hit.key);
|
|
279
|
-
if (note) info.priceNote = note;
|
|
280
|
-
return info;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/** port #34: does the model accept image input? models.dev `modalities.input` (every entry of
|
|
284
|
-
* the bundled snapshot carries it — 7479/7479 at @opencode-ai/models 0.0.61). undefined = the
|
|
285
|
-
* model is not in the catalog (custom base URLs, local servers, aggregator ids without a
|
|
286
|
-
* vendor prefix): callers treat unknown as "send the image" — a wrong guess then fails loudly
|
|
287
|
-
* at the provider (HTTP 400 → error turn) instead of silently turning the user's image into
|
|
288
|
-
* text. Same resolution order as lookup(). */
|
|
289
|
-
supportsImages(providerId: string, modelId: string): boolean | undefined {
|
|
290
|
-
const hit = this.resolve(providerId, modelId);
|
|
291
|
-
const input: unknown = hit?.model.modalities?.input;
|
|
292
|
-
return Array.isArray(input) ? input.includes("image") : undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** models.dev `reasoning` for a model: true/false, undefined for a model the catalog does not know — the same
|
|
296
|
-
* resolution as supportsImages(). Callers treat unknown as "may reason" (wire-select.ts: an unlisted
|
|
297
|
-
* Responses-only id must not be sent to a wire that 404s it). */
|
|
298
|
-
supportsReasoning(providerId: string, modelId: string): boolean | undefined {
|
|
299
|
-
const hit = this.resolve(providerId, modelId);
|
|
300
|
-
return typeof hit?.model.reasoning === "boolean" ? hit.model.reasoning : undefined;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/** The catalog entry behind lookup()/supportsImages(): provider key candidates in order, live
|
|
304
|
-
* layer over snapshot. `as` = the provider name reported (the vendor for a prefix hit). */
|
|
305
|
-
private resolve(providerId: string, modelId: string): { as: string; key: string; model: Model; local?: LocalModel } | undefined {
|
|
306
|
-
this.loadDiskCacheOnce();
|
|
307
|
-
|
|
308
|
-
const candidates: { key: string; as: string; model: string }[] = [];
|
|
309
|
-
const direct = PROVIDER_MAP[providerId];
|
|
310
|
-
if (direct) candidates.push({ key: direct, as: providerId, model: modelId });
|
|
311
|
-
const slash = modelId.indexOf("/");
|
|
312
|
-
if (slash > 0) {
|
|
313
|
-
const vendorKey = VENDOR_PREFIX_MAP[modelId.slice(0, slash).toLowerCase()];
|
|
314
|
-
if (vendorKey && vendorKey !== direct) candidates.push({ key: vendorKey, as: vendorKey, model: modelId.slice(slash + 1) });
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
for (const c of candidates) {
|
|
318
|
-
const hit = this.findIn(c.key, c.model);
|
|
319
|
-
if (hit) return { as: c.as, ...hit };
|
|
320
|
-
}
|
|
321
|
-
return undefined;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/** one provider key: live layer, then the snapshot, then rovecode's own table (catalog-local.ts). The
|
|
325
|
-
* overlay does not shadow a models.dev entry, so a later snapshot that adds a model wins by construction
|
|
326
|
-
* — EXCEPT for a row marked `override: true`, which is how a vendor page that disagrees with the
|
|
327
|
-
* snapshot wins (today: DeepSeek's v4 family, where models.dev carries about a third of the published
|
|
328
|
-
* price). An override says which page and which day in its `source`, and `model show` prints it. */
|
|
329
|
-
private findIn(key: string, modelId: string): { key: string; model: Model; local?: LocalModel } | undefined {
|
|
330
|
-
const overriding = this.local[key];
|
|
331
|
-
if (overriding) {
|
|
332
|
-
const found = findModelKey(overriding as unknown as Record<string, Model>, modelId);
|
|
333
|
-
const row = found === undefined ? undefined : overriding[found];
|
|
334
|
-
if (found !== undefined && row?.override === true) return { key: found, model: localAsModel(found, row), local: row };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const live = this.liveProviders?.[key];
|
|
338
|
-
if (live) {
|
|
339
|
-
const found = findModelKey(live.models, modelId);
|
|
340
|
-
if (found !== undefined) {
|
|
341
|
-
const model = live.models[found];
|
|
342
|
-
if (model) return { key: found, model };
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const snap = snapshotProviders()[key];
|
|
347
|
-
if (snap) {
|
|
348
|
-
const found = findModelKey(snap.models, modelId);
|
|
349
|
-
if (found !== undefined) {
|
|
350
|
-
const model = snap.models[found];
|
|
351
|
-
if (model) return { key: found, model };
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const table = this.local[key];
|
|
356
|
-
if (!table) return undefined;
|
|
357
|
-
const found = findModelKey(table as unknown as Record<string, Model>, modelId);
|
|
358
|
-
if (found === undefined) return undefined;
|
|
359
|
-
const local = table[found]!;
|
|
360
|
-
return { key: found, model: localAsModel(found, local), local };
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/** live fetch https://models.dev/api.json when fetchFn set; false on failure, never throws. */
|
|
364
|
-
async refresh(): Promise<boolean> {
|
|
365
|
-
if (!this.fetchFn) return false;
|
|
366
|
-
try {
|
|
367
|
-
const res = await this.fetchFn(LIVE_URL);
|
|
368
|
-
if (!res.ok) return false;
|
|
369
|
-
const json: unknown = await res.json();
|
|
370
|
-
if (!isProviderMap(json)) return false;
|
|
371
|
-
this.liveProviders = json;
|
|
372
|
-
this.triedDiskCache = true;
|
|
373
|
-
this.writeDiskCache(json);
|
|
374
|
-
return true;
|
|
375
|
-
} catch {
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/** Load a still-fresh (<24h) on-disk cache at most once per instance. Corrupted/stale/missing
|
|
381
|
-
* cache is silently ignored — offline snapshot remains the fallback. Only consulted when a
|
|
382
|
-
* fetchFn is configured, matching "live fetch is OPT-IN" (a purely-offline catalog never
|
|
383
|
-
* touches disk). */
|
|
384
|
-
private loadDiskCacheOnce(): void {
|
|
385
|
-
if (this.triedDiskCache) return;
|
|
386
|
-
this.triedDiskCache = true;
|
|
387
|
-
if (!this.fetchFn || !this.cacheDir) return;
|
|
388
|
-
try {
|
|
389
|
-
const path = join(this.cacheDir, CACHE_FILE);
|
|
390
|
-
const age = Date.now() - statSync(path).mtimeMs;
|
|
391
|
-
if (age > CACHE_TTL_MS) return;
|
|
392
|
-
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
393
|
-
if (isProviderMap(parsed)) this.liveProviders = parsed;
|
|
394
|
-
} catch {
|
|
395
|
-
// missing file, unreadable, or malformed JSON/shape -> treated as "no cache"
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
private writeDiskCache(data: ProviderMap): void {
|
|
400
|
-
if (!this.cacheDir) return;
|
|
401
|
-
try {
|
|
402
|
-
mkdirSync(this.cacheDir, { recursive: true });
|
|
403
|
-
writeFileSync(join(this.cacheDir, CACHE_FILE), JSON.stringify(data));
|
|
404
|
-
} catch {
|
|
405
|
-
// best-effort cache write; a failed write must not fail refresh()
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
let offlineCatalog: ModelCatalog | null = null;
|
|
411
|
-
|
|
412
|
-
/** Port #34 adapter hook: image capability of a ModelRef from the offline snapshot (no fetchFn →
|
|
413
|
-
* never touches network or disk). undefined = unknown model (see ModelCatalog.supportsImages). */
|
|
414
|
-
export function supportsImages(ref: { provider: string; model: string }, catalog?: ModelCatalog): boolean | undefined {
|
|
415
|
-
return (catalog ?? (offlineCatalog ??= new ModelCatalog())).supportsImages(ref.provider, ref.model);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/** the same hook for the reasoning flag (wire-select.ts): true/false from the offline snapshot, undefined = unknown model */
|
|
419
|
-
export function supportsReasoning(ref: { provider: string; model: string }, catalog?: ModelCatalog): boolean | undefined {
|
|
420
|
-
return (catalog ?? (offlineCatalog ??= new ModelCatalog())).supportsReasoning(ref.provider, ref.model);
|
|
421
|
-
}
|