@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 +2 -0
- package/dist/catalogLive.d.ts +48 -0
- package/dist/constants/timing.d.ts +6 -0
- package/dist/index.js +193 -45
- package/dist/tui.js +9 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
3304
|
-
|
|
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
|
-
|
|
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
|
|
3604
|
-
import { join as
|
|
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 ??
|
|
3764
|
+
return process.env.HOME ?? homedir3();
|
|
3608
3765
|
}
|
|
3609
3766
|
function opencodeHome() {
|
|
3610
|
-
const base = process.env.XDG_DATA_HOME ??
|
|
3611
|
-
return
|
|
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 ??
|
|
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
|
|
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(
|
|
3682
|
-
keys.push(...await fileKey(
|
|
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
|
|
4128
|
-
import { homedir as
|
|
4129
|
-
import { join as
|
|
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 ??
|
|
4139
|
-
return
|
|
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
|
|
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
|
|
4155
|
-
await
|
|
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
|
|
4972
|
-
import { homedir as
|
|
4973
|
-
import { join as
|
|
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 ??
|
|
4979
|
-
return
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 ?
|
|
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.
|
|
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",
|