@jeffreyjyz/opencode-command-code 0.5.25 → 0.5.26

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 CHANGED
@@ -17,6 +17,8 @@ The model list is fetched live from Command Code's listing API, split by wire, a
17
17
 
18
18
  Each model is registered with Command Code's published $/1M rates, so opencode's own cost display works for a subscription provider instead of showing $0. Vision support and reasoning-effort variants come from a generated catalog, because the listing API publishes no capabilities.
19
19
 
20
+ That catalog is refreshed at runtime: the plugin fetches Command Code's own `models.md` (bundled in their published CLI, served from the npm CDN), caches it for a day under `~/.cache/command-code/catalog-live.json`, and overlays prices, context windows and efforts on the baked snapshot before each model refresh — so a price change reaches a running install without waiting for a plugin release. The baked table stays as the offline and first-run fallback, and vision stays baked because it only exists in the CLI bundle. `catalogSource()` reports which table a given lookup used.
21
+
20
22
  Credentials resolve in one order everywhere: `CMD_API_KEY`, then opencode's credential store (the `commandcode`/`command-code` rows, active first, newest first), then opencode's legacy `auth.json`, then `~/.commandcode/auth.json` from `cmd login`. The store ranks first because a re-connect leaves both files on the key it replaced — and if the API still answers 401, the request is retried with the next candidate rather than failing the turn.
21
23
 
22
24
  ## Install
@@ -0,0 +1,48 @@
1
+ import type { CatalogEntry, ModelCost } from "./catalog";
2
+ import { inputModalities, supportsImage } from "./catalog";
3
+ /** A live row: everything models.md carries except modalities, which only the
4
+ * CLI bundle has and stays baked. */
5
+ export type LiveEntry = Omit<CatalogEntry, "modalities">;
6
+ export type LiveCatalog = {
7
+ fetchedAt: number;
8
+ /** CLI version the document came from, for diagnostics. */
9
+ version: string;
10
+ entries: Record<string, LiveEntry>;
11
+ };
12
+ /** "1M" / "1.05M" / "256K" / "1050000" → tokens. */
13
+ export declare function parseCount(raw: string): number;
14
+ /** "Go and above" → "Go"; "—" → null. */
15
+ export declare function normalizeMinPlan(raw: string | undefined): string | null;
16
+ /** Parse the published models.md table. Rows that fail the rate match are dropped,
17
+ * exactly as the build-time extractor does, so live and baked agree. */
18
+ export declare function parseModelsMd(md: string): Record<string, LiveEntry>;
19
+ /** Install a live table. Exported for tests; production goes through `refreshLiveCatalog`. */
20
+ export declare function applyLiveCatalog(entries: Record<string, LiveEntry> | null, fetchedAt?: number): void;
21
+ /** Provenance for the sidebar/logs: baked snapshot or a live overlay. */
22
+ export declare function catalogSource(): {
23
+ kind: "baked" | "live";
24
+ version: string;
25
+ ageMs: number;
26
+ };
27
+ export declare function liveContext(id: string): number | undefined;
28
+ export declare function liveCost(id: string): ModelCost | undefined;
29
+ export declare function liveMinPlan(id: string): string | null;
30
+ export declare function liveEfforts(id: string): readonly string[] | null | undefined;
31
+ export declare function liveReasoningVariants(id: string): Record<string, {
32
+ reasoningEffort: string;
33
+ }> | undefined;
34
+ export declare function liveIsReasoningModel(id: string): boolean;
35
+ export { inputModalities, supportsImage };
36
+ /**
37
+ * Load the live catalogue: disk cache when fresh, else fetch and rewrite it. Any
38
+ * failure leaves the baked snapshot in place — an outage must never blank prices.
39
+ * Never throws.
40
+ */
41
+ export declare function refreshLiveCatalog(options?: {
42
+ force?: boolean;
43
+ }): Promise<"disk" | "fetch" | "baked">;
44
+ /** Startup path: apply the disk copy synchronously-immediately, then refresh in
45
+ * the background so nothing waits on the network. */
46
+ export declare function primeLiveCatalog(): Promise<"disk" | "fetch" | "baked">;
47
+ /** Path for tests and diagnostics. */
48
+ export declare function liveCatalogPath(): string;
@@ -6,6 +6,12 @@ export declare const POLL_MS = 5000;
6
6
  export declare const CHILD_TIMEOUT_MS = 30000;
7
7
  /** Process-lifetime cache for the live model list (src/models.ts). */
8
8
  export declare const MODELS_CACHE_TTL_MS: number;
9
+ /** Live model-catalogue document (prices, context, efforts) fetched from the
10
+ * published CLI package. A day: rates change on a release cadence, not hourly,
11
+ * and every fetch costs a CDN round trip at startup. */
12
+ export declare const CATALOG_LIVE_TTL_MS: number;
13
+ /** Hard stop for one catalogue download; a hung CDN must not delay startup. */
14
+ export declare const CATALOG_LIVE_TIMEOUT_MS = 10000;
9
15
  /** Disk cache for mpc's per-model catalog (src/sidebar/data.ts). An hour, not
10
16
  * six: a running panel then picks up new models and promotions. */
11
17
  export declare const CATALOG_CACHE_TTL_MS: number;
package/dist/index.js CHANGED
@@ -2414,7 +2414,7 @@ function superRefine(fn) {
2414
2414
  // package.json
2415
2415
  var package_default = {
2416
2416
  name: "@jeffreyjyz/opencode-command-code",
2417
- version: "0.5.25",
2417
+ version: "0.5.26",
2418
2418
  description: "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
2419
2419
  type: "module",
2420
2420
  main: "dist/index.js",
@@ -2516,7 +2516,13 @@ async function providerModels(key) {
2516
2516
  return getJson("/provider/v1/models", key, 15000);
2517
2517
  }
2518
2518
 
2519
+ // src/catalogLive.ts
2520
+ import { mkdir, readFile, writeFile } from "fs/promises";
2521
+ import { homedir } from "os";
2522
+ import { join } from "path";
2523
+
2519
2524
  // src/catalog.ts
2525
+ var CATALOG_VERSION = "1.88.5";
2520
2526
  var MODEL_CATALOG = {
2521
2527
  "claude-fable-5": {
2522
2528
  name: "Claude Fable 5",
@@ -3300,19 +3306,170 @@ function modelCost(id) {
3300
3306
  function modelContext(id) {
3301
3307
  return MODEL_CATALOG[id]?.context;
3302
3308
  }
3303
- function reasoningVariants(id) {
3304
- const efforts = MODEL_CATALOG[id]?.efforts;
3309
+
3310
+ // src/constants/paths.ts
3311
+ var CACHE_DIR = "command-code";
3312
+ var MODELS_CACHE_FILE = "models.json";
3313
+ var STARTUP_LOG_FILE = "startup.log";
3314
+
3315
+ // src/constants/timing.ts
3316
+ var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
3317
+ var CATALOG_LIVE_TTL_MS = 24 * 60 * 60 * 1000;
3318
+ var CATALOG_LIVE_TIMEOUT_MS = 1e4;
3319
+ var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
3320
+
3321
+ // src/catalogLive.ts
3322
+ var CDNS = ["https://cdn.jsdelivr.net/npm", "https://unpkg.com"];
3323
+ var DOC = (cdn) => `${cdn}/command-code@latest/dist/bundled/command-code-knowledge/reference/models.md`;
3324
+ var LIVE_FILE = "catalog-live.json";
3325
+ var RATE_RE = /^\$([\d.]+)\/\$([\d.]+)\s*\u00B7\s*cache\s*\$([\d.]+)(?:\s*\(write\s*\$([\d.]+)\))?$/;
3326
+ var PLAN_NAMES = {
3327
+ free: "Free",
3328
+ go: "Go",
3329
+ goat: "GOAT",
3330
+ pro: "Pro",
3331
+ teampro: "Team Pro"
3332
+ };
3333
+ function parseCount(raw) {
3334
+ const m = raw.trim().match(/^([\d.]+)\s*([MK])?$/i);
3335
+ if (!m)
3336
+ return 0;
3337
+ const value = Number(m[1]);
3338
+ if (!Number.isFinite(value))
3339
+ return 0;
3340
+ const unit = (m[2] ?? "").toUpperCase();
3341
+ return Math.round(value * (unit === "M" ? 1e6 : unit === "K" ? 1000 : 1));
3342
+ }
3343
+ function normalizeMinPlan(raw) {
3344
+ const text = (raw ?? "").trim().replace(/\s+and above$/i, "").toLowerCase();
3345
+ if (!text || text === "\u2014" || text === "-")
3346
+ return null;
3347
+ return PLAN_NAMES[text] ?? null;
3348
+ }
3349
+ function parseModelsMd(md) {
3350
+ const out = {};
3351
+ for (const line of md.split(`
3352
+ `)) {
3353
+ const cells = line.split("|").map((c) => c.trim());
3354
+ if (cells.length < 8 || !cells[1]?.startsWith("`") || !cells[1]?.endsWith("`"))
3355
+ continue;
3356
+ const rate = cells[5]?.match(RATE_RE);
3357
+ if (!rate)
3358
+ continue;
3359
+ const [, input, output, cacheRead, cacheWrite] = rate;
3360
+ const effortsRaw = (cells[4] ?? "").trim();
3361
+ out[cells[1].slice(1, -1)] = {
3362
+ name: (cells[2] ?? "").trim(),
3363
+ context: parseCount(cells[3] ?? ""),
3364
+ efforts: !effortsRaw || effortsRaw === "\u2014" || effortsRaw === "-" ? null : effortsRaw.split(",").map((e) => e.trim()).filter(Boolean),
3365
+ cost: {
3366
+ input: Number(input),
3367
+ output: Number(output),
3368
+ cacheRead: Number(cacheRead),
3369
+ cacheWrite: cacheWrite === undefined ? 0 : Number(cacheWrite)
3370
+ },
3371
+ minPlan: normalizeMinPlan(cells[6])
3372
+ };
3373
+ }
3374
+ return out;
3375
+ }
3376
+ var live = null;
3377
+ var liveFetchedAt = 0;
3378
+ function applyLiveCatalog(entries, fetchedAt = Date.now()) {
3379
+ live = entries && Object.keys(entries).length > 0 ? entries : null;
3380
+ liveFetchedAt = live ? fetchedAt : 0;
3381
+ }
3382
+ function entry(id) {
3383
+ return live?.[id] ?? MODEL_CATALOG[id] ?? {};
3384
+ }
3385
+ function liveContext(id) {
3386
+ const value = entry(id).context;
3387
+ return value && value > 0 ? value : modelContext(id);
3388
+ }
3389
+ function liveCost(id) {
3390
+ return live?.[id]?.cost ?? modelCost(id);
3391
+ }
3392
+ function liveEfforts(id) {
3393
+ return live?.[id]?.efforts ?? MODEL_CATALOG[id]?.efforts;
3394
+ }
3395
+ function liveReasoningVariants(id) {
3396
+ const efforts = liveEfforts(id);
3305
3397
  if (!efforts || efforts.length === 0)
3306
3398
  return;
3307
3399
  return Object.fromEntries(efforts.map((effort) => [effort, { reasoningEffort: effort }]));
3308
3400
  }
3401
+ function livePath() {
3402
+ const base = process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
3403
+ return join(base, CACHE_DIR, LIVE_FILE);
3404
+ }
3405
+ async function readDisk() {
3406
+ try {
3407
+ const parsed = JSON.parse(await readFile(livePath(), "utf8"));
3408
+ if (!parsed.entries || typeof parsed.fetchedAt !== "number")
3409
+ return;
3410
+ return parsed;
3411
+ } catch {
3412
+ return;
3413
+ }
3414
+ }
3415
+ async function writeDisk(catalog) {
3416
+ try {
3417
+ const path = livePath();
3418
+ await mkdir(join(path, ".."), { recursive: true });
3419
+ await writeFile(path, JSON.stringify(catalog));
3420
+ } catch {}
3421
+ }
3422
+ async function fetchDoc() {
3423
+ for (const cdn of CDNS) {
3424
+ try {
3425
+ const response = await fetch(DOC(cdn), {
3426
+ signal: AbortSignal.timeout(CATALOG_LIVE_TIMEOUT_MS)
3427
+ });
3428
+ if (!response.ok)
3429
+ continue;
3430
+ const body = await response.text();
3431
+ if (body.includes("$/1M"))
3432
+ return body;
3433
+ } catch {}
3434
+ }
3435
+ return;
3436
+ }
3437
+ async function refreshLiveCatalog(options = {}) {
3438
+ const cached = await readDisk();
3439
+ if (cached && !options.force && Date.now() - cached.fetchedAt < CATALOG_LIVE_TTL_MS) {
3440
+ applyLiveCatalog(cached.entries, cached.fetchedAt);
3441
+ return "disk";
3442
+ }
3443
+ const doc = await fetchDoc();
3444
+ if (!doc) {
3445
+ if (cached)
3446
+ applyLiveCatalog(cached.entries, cached.fetchedAt);
3447
+ return "baked";
3448
+ }
3449
+ const entries = parseModelsMd(doc);
3450
+ if (Object.keys(entries).length === 0) {
3451
+ if (cached)
3452
+ applyLiveCatalog(cached.entries, cached.fetchedAt);
3453
+ return "baked";
3454
+ }
3455
+ const version = String(CATALOG_VERSION);
3456
+ applyLiveCatalog(entries);
3457
+ await writeDisk({ fetchedAt: Date.now(), version, entries });
3458
+ return "fetch";
3459
+ }
3460
+ async function primeLiveCatalog() {
3461
+ const cached = await readDisk();
3462
+ if (cached)
3463
+ applyLiveCatalog(cached.entries, cached.fetchedAt);
3464
+ return refreshLiveCatalog();
3465
+ }
3309
3466
 
3310
3467
  // src/cli.ts
3311
3468
  import { spawn } from "child_process";
3312
3469
 
3313
3470
  // src/constants/binaries.ts
3314
- import { homedir } from "os";
3315
- import { join } from "path";
3471
+ import { homedir as homedir2 } from "os";
3472
+ import { join as join2 } from "path";
3316
3473
  var CMDUSE_BIN_CANDIDATES = [
3317
3474
  "cmduse",
3318
3475
  "/opt/homebrew/bin/cmduse",
@@ -3320,7 +3477,7 @@ var CMDUSE_BIN_CANDIDATES = [
3320
3477
  ];
3321
3478
  var MPC_BIN_CANDIDATES = [
3322
3479
  "mpc",
3323
- join(homedir(), ".bun/bin/mpc"),
3480
+ join2(homedir2(), ".bun/bin/mpc"),
3324
3481
  "/opt/homebrew/bin/mpc"
3325
3482
  ];
3326
3483
  var OCUSE_BIN_CANDIDATES = [
@@ -3599,19 +3756,19 @@ var LANES = [
3599
3756
  ];
3600
3757
 
3601
3758
  // src/key.ts
3602
- import { readFile } from "fs/promises";
3603
- import { homedir as homedir2 } from "os";
3604
- import { join as join2 } from "path";
3759
+ import { readFile as readFile2 } from "fs/promises";
3760
+ import { homedir as homedir3 } from "os";
3761
+ import { join as join3 } from "path";
3605
3762
  var STORE_PROVIDERS = ["commandcode", "command-code"];
3606
3763
  function home() {
3607
- return process.env.HOME ?? homedir2();
3764
+ return process.env.HOME ?? homedir3();
3608
3765
  }
3609
3766
  function opencodeHome() {
3610
- const base = process.env.XDG_DATA_HOME ?? join2(home(), ".local", "share");
3611
- return join2(base, "opencode");
3767
+ const base = process.env.XDG_DATA_HOME ?? join3(home(), ".local", "share");
3768
+ return join3(base, "opencode");
3612
3769
  }
3613
3770
  async function storeRows() {
3614
- const path = process.env.OPENCODE_DB ?? join2(opencodeHome(), "opencode.db");
3771
+ const path = process.env.OPENCODE_DB ?? join3(opencodeHome(), "opencode.db");
3615
3772
  let db;
3616
3773
  try {
3617
3774
  const sqlite = await import("node:sqlite");
@@ -3657,7 +3814,7 @@ async function storeRows() {
3657
3814
  }
3658
3815
  async function fileKey(path) {
3659
3816
  try {
3660
- const value = JSON.parse(await readFile(path, "utf8"));
3817
+ const value = JSON.parse(await readFile2(path, "utf8"));
3661
3818
  const keys = [];
3662
3819
  for (const provider of STORE_PROVIDERS) {
3663
3820
  const entry = value[provider];
@@ -3678,8 +3835,8 @@ async function candidateKeys(getAuth) {
3678
3835
  keys.push(env);
3679
3836
  for (const row of await storeRows())
3680
3837
  keys.push(row.key);
3681
- keys.push(...await fileKey(join2(opencodeHome(), "auth.json")));
3682
- keys.push(...await fileKey(join2(home(), ".commandcode", "auth.json")));
3838
+ keys.push(...await fileKey(join3(opencodeHome(), "auth.json")));
3839
+ keys.push(...await fileKey(join3(home(), ".commandcode", "auth.json")));
3683
3840
  try {
3684
3841
  const auth = await getAuth?.();
3685
3842
  if (auth?.key)
@@ -4066,10 +4223,6 @@ function siblingCategory(model) {
4066
4223
  return best ? MODEL_CATEGORIES[best] : undefined;
4067
4224
  }
4068
4225
 
4069
- // src/constants/timing.ts
4070
- var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
4071
- var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
4072
-
4073
4226
  // src/models.ts
4074
4227
  var cache = null;
4075
4228
  function isClaude(id) {
@@ -4124,23 +4277,16 @@ async function loadModels(key) {
4124
4277
  }
4125
4278
 
4126
4279
  // src/modelsCache.ts
4127
- import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
4128
- import { homedir as homedir3 } from "os";
4129
- import { join as join3 } from "path";
4130
-
4131
- // src/constants/paths.ts
4132
- var CACHE_DIR = "command-code";
4133
- var MODELS_CACHE_FILE = "models.json";
4134
- var STARTUP_LOG_FILE = "startup.log";
4135
-
4136
- // src/modelsCache.ts
4280
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
4281
+ import { homedir as homedir4 } from "os";
4282
+ import { join as join4 } from "path";
4137
4283
  function cachePath() {
4138
- const base = process.env.XDG_CACHE_HOME ?? join3(homedir3(), ".cache");
4139
- return join3(base, CACHE_DIR, MODELS_CACHE_FILE);
4284
+ const base = process.env.XDG_CACHE_HOME ?? join4(homedir4(), ".cache");
4285
+ return join4(base, CACHE_DIR, MODELS_CACHE_FILE);
4140
4286
  }
4141
4287
  async function readModelsCache() {
4142
4288
  try {
4143
- const parsed = JSON.parse(await readFile2(cachePath(), "utf8"));
4289
+ const parsed = JSON.parse(await readFile3(cachePath(), "utf8"));
4144
4290
  if (!Array.isArray(parsed.claude) || !Array.isArray(parsed.open))
4145
4291
  return;
4146
4292
  return parsed;
@@ -4151,8 +4297,8 @@ async function readModelsCache() {
4151
4297
  async function writeModelsCache(cache) {
4152
4298
  try {
4153
4299
  const path = cachePath();
4154
- await mkdir(join3(path, ".."), { recursive: true });
4155
- await writeFile(path, JSON.stringify(cache));
4300
+ await mkdir2(join4(path, ".."), { recursive: true });
4301
+ await writeFile2(path, JSON.stringify(cache));
4156
4302
  } catch {}
4157
4303
  }
4158
4304
  function idsDiffer(a, b) {
@@ -4968,20 +5114,20 @@ function createCommandCode(options = {}, images = () => false) {
4968
5114
  }
4969
5115
 
4970
5116
  // src/startupLog.ts
4971
- import { appendFile, mkdir as mkdir2 } from "fs/promises";
4972
- import { homedir as homedir4 } from "os";
4973
- import { join as join4 } from "path";
5117
+ import { appendFile, mkdir as mkdir3 } from "fs/promises";
5118
+ import { homedir as homedir5 } from "os";
5119
+ import { join as join5 } from "path";
4974
5120
  function setupTimingLine(t) {
4975
5121
  return `setup: key=${t.key}ms register=${t.register}ms connection=${t.connection}ms refresh=${t.refresh}ms models=${t.models}`;
4976
5122
  }
4977
5123
  function startupLogPath() {
4978
- const base = process.env.XDG_CACHE_HOME ?? join4(homedir4(), ".cache");
4979
- return join4(base, CACHE_DIR, STARTUP_LOG_FILE);
5124
+ const base = process.env.XDG_CACHE_HOME ?? join5(homedir5(), ".cache");
5125
+ return join5(base, CACHE_DIR, STARTUP_LOG_FILE);
4980
5126
  }
4981
5127
  async function writeStartupLine(line) {
4982
5128
  try {
4983
5129
  const path = startupLogPath();
4984
- await mkdir2(join4(path, ".."), { recursive: true });
5130
+ await mkdir3(join5(path, ".."), { recursive: true });
4985
5131
  await appendFile(path, `[${new Date().toISOString()}] ${line}
4986
5132
  `);
4987
5133
  } catch {}
@@ -4989,7 +5135,7 @@ async function writeStartupLine(line) {
4989
5135
 
4990
5136
  // src/v2.ts
4991
5137
  function costEntry(id) {
4992
- const cost = modelCost(id);
5138
+ const cost = liveCost(id);
4993
5139
  return cost ? [
4994
5140
  {
4995
5141
  input: cost.input,
@@ -4999,7 +5145,7 @@ function costEntry(id) {
4999
5145
  ] : [];
5000
5146
  }
5001
5147
  function toV2Model(m, lane) {
5002
- const efforts = reasoningVariants(m.id);
5148
+ const efforts = liveReasoningVariants(m.id);
5003
5149
  const variants = efforts ? Object.entries(efforts).map(([id, settings]) => ({ id, settings })) : [];
5004
5150
  const info = {
5005
5151
  id: m.id,
@@ -5030,7 +5176,7 @@ function staticSeedModels() {
5030
5176
  "command-code-openai": []
5031
5177
  };
5032
5178
  for (const lane of LANES) {
5033
- const models = KNOWN_MODELS.filter((id) => isClaude(id) === (lane.reasoningField === undefined)).map((id) => toV2Model({ id, name: id, contextLength: modelContext(id) ?? 0 }, lane));
5179
+ const models = KNOWN_MODELS.filter((id) => isClaude(id) === (lane.reasoningField === undefined)).map((id) => toV2Model({ id, name: id, contextLength: liveContext(id) ?? 0 }, lane));
5034
5180
  seed[lane.id] = models;
5035
5181
  }
5036
5182
  return seed;
@@ -5265,6 +5411,7 @@ var commandCodeV2 = {
5265
5411
  refreshing = true;
5266
5412
  const refreshStart = Date.now();
5267
5413
  try {
5414
+ await refreshLiveCatalog();
5268
5415
  const key = await credentialKey(ctx);
5269
5416
  if (!key)
5270
5417
  return;
@@ -5300,6 +5447,7 @@ var commandCodeV2 = {
5300
5447
  }
5301
5448
  };
5302
5449
  v2FirstRefresh = setTimeout(() => void refresh(), 3000);
5450
+ primeLiveCatalog();
5303
5451
  v2Timer = setInterval(() => void refresh(), 30 * 60 * 1000);
5304
5452
  return () => {
5305
5453
  controller.abort();
@@ -5322,7 +5470,7 @@ function tool(input) {
5322
5470
  return input;
5323
5471
  }
5324
5472
  function costFor(id) {
5325
- const cost = modelCost(id);
5473
+ const cost = liveCost(id);
5326
5474
  return {
5327
5475
  input: cost?.input ?? 0,
5328
5476
  output: cost?.output ?? 0,
package/dist/tui.js CHANGED
@@ -941,6 +941,7 @@ var PREFS_FILE = "command-code.json";
941
941
  var POLL_MS = 5000;
942
942
  var CHILD_TIMEOUT_MS = 30000;
943
943
  var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
944
+ var CATALOG_LIVE_TTL_MS = 24 * 60 * 60 * 1000;
944
945
  var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
945
946
  var PREFS_CACHE_MS = 5000;
946
947
  var ARK_SNAPSHOT_TTL_MS = 60000;
@@ -2256,6 +2257,13 @@ function minPlan(id) {
2256
2257
  return MODEL_CATALOG[id]?.minPlan ?? null;
2257
2258
  }
2258
2259
 
2260
+ // src/catalogLive.ts
2261
+ var live = null;
2262
+ function liveMinPlan(id) {
2263
+ const plan = live?.[id]?.minPlan;
2264
+ return plan === undefined ? minPlan(id) : plan;
2265
+ }
2266
+
2259
2267
  // src/sidebar/ark.ts
2260
2268
  function totals(value) {
2261
2269
  const v = value ?? {};
@@ -3107,7 +3115,7 @@ function useRows(activeModelId, providerID, sessionID) {
3107
3115
  const model = found ? {
3108
3116
  ...found,
3109
3117
  tier: found.tier ?? (id ? tierFor(id) : undefined),
3110
- minPlan: id ? minPlan(id) : undefined
3118
+ minPlan: id ? liveMinPlan(id) : undefined
3111
3119
  } : undefined;
3112
3120
  return [...usage(), separator(), ...modelRows(model, usageFor(id), sessionUsage(), fallbackName)];
3113
3121
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.5.25",
3
+ "version": "0.5.26",
4
4
  "description": "Command Code provider + usage dashboard for opencode: two providers (Anthropic wire for Claude, OpenAI wire for everything else) against api.commandcode.ai, live model list, plan gating, /cmd-usage. Requires the cmduse CLI for usage rendering.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",