@jeffreyjyz/opencode-command-code 0.5.1 → 0.5.3
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/dist/index.js +1 -1
- package/dist/sidebar/usageDb.d.ts +15 -0
- package/dist/sidebar/zen.d.ts +18 -1
- package/dist/tui.js +76 -8
- package/package.json +1 -1
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.3",
|
|
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",
|
|
@@ -24,6 +24,21 @@ export declare function periodStart(usage: {
|
|
|
24
24
|
* while the session said 2.7K).
|
|
25
25
|
*/
|
|
26
26
|
export declare function loadModelUsage(modelID: string, sinceMs: number, path?: string, sessionID?: string): ModelUsage | null;
|
|
27
|
+
/**
|
|
28
|
+
* The oldest in-window assistant turn for one model, as epoch ms — the start of
|
|
29
|
+
* the rolling window the Go/Zen panel needs. A rolling 5-hour/weekly window has
|
|
30
|
+
* no service-reported reset (Go has no usage API), but its clock does have a
|
|
31
|
+
* real start: the oldest request still inside it ages out at `start + window`,
|
|
32
|
+
* which is that window's honest reset. Null when the model has no turn in the
|
|
33
|
+
* window, or the store is missing/unreadable, so callers then omit the
|
|
34
|
+
* continuation exactly as before.
|
|
35
|
+
*
|
|
36
|
+
* Account-wide like ocuse's per-model window totals, so it is *not* scoped to a
|
|
37
|
+
* session: a rolling window counts the model's spend across every conversation.
|
|
38
|
+
* Matching is on the canonical key, the same normalisation `loadModelUsage` uses
|
|
39
|
+
* (the session id and the stored `modelID` differ by vendor prefix/punctuation).
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadModelWindowStart(modelID: string, sinceMs: number, path?: string): number | null;
|
|
27
42
|
/**
|
|
28
43
|
* Requests and cost for one session, so the panel can show what this
|
|
29
44
|
* conversation has spent rather than only the period-to-date figure. Null when
|
package/dist/sidebar/zen.d.ts
CHANGED
|
@@ -34,9 +34,26 @@ export interface ZenUsage {
|
|
|
34
34
|
}
|
|
35
35
|
/** Parse `ocuse -1 --json`. Anything malformed reads as "no usage". */
|
|
36
36
|
export declare function parseZenJson(text: string): ZenUsage;
|
|
37
|
+
/**
|
|
38
|
+
* The starts of the active model's rolling windows, epoch ms: the oldest request
|
|
39
|
+
* still inside each one, from opencode's own store. The Go/Zen panel turns each
|
|
40
|
+
* into a reset via `rollingReset`; absent when no such request exists.
|
|
41
|
+
*/
|
|
42
|
+
export interface ZenWindowStarts {
|
|
43
|
+
fiveHour?: number;
|
|
44
|
+
weekly?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A rolling window's reset, derived from the oldest request still inside it:
|
|
48
|
+
* that request leaves the window at `oldest + durSecs`, which is an honest reset
|
|
49
|
+
* epoch — unlike `now + durSecs`, a constant that would be noise in a usage
|
|
50
|
+
* panel. Undefined when there is no oldest request (an empty window), so the row
|
|
51
|
+
* keeps the same spend-only degrade it had before.
|
|
52
|
+
*/
|
|
53
|
+
export declare function rollingReset(startMs: number | undefined, durSecs: number): number | undefined;
|
|
37
54
|
/** The OpenCode Go / Zen panel: period windows, a rule, then the active model.
|
|
38
55
|
* Like `modelRows`, `fallbackName` (the session's own id) keeps the model block
|
|
39
56
|
* — and the local `Usage (this model)` / `Session` figures, which need no network
|
|
40
57
|
* — on screen when mpc's catalog is unreachable; only the catalog-derived rows
|
|
41
58
|
* then drop out. */
|
|
42
|
-
export declare function zenRows(usage: ZenUsage | undefined, meta: ModelMeta | undefined, modelUsage?: ModelUsage, session?: ModelUsage, fallbackName?: string): SidebarRow[];
|
|
59
|
+
export declare function zenRows(usage: ZenUsage | undefined, meta: ModelMeta | undefined, modelUsage?: ModelUsage, session?: ModelUsage, fallbackName?: string, windows?: ZenWindowStarts): SidebarRow[];
|
package/dist/tui.js
CHANGED
|
@@ -2090,6 +2090,11 @@ function monthPeriod(usage) {
|
|
|
2090
2090
|
durSecs
|
|
2091
2091
|
};
|
|
2092
2092
|
}
|
|
2093
|
+
function rollingReset(startMs, durSecs) {
|
|
2094
|
+
if (typeof startMs !== "number" || !Number.isFinite(startMs))
|
|
2095
|
+
return;
|
|
2096
|
+
return startMs + durSecs * 1000;
|
|
2097
|
+
}
|
|
2093
2098
|
function zenWindow(label, spent, limit, share, now, durSecs, resetAt) {
|
|
2094
2099
|
const cap = typeof limit === "number" && limit > 0 ? limit * share : undefined;
|
|
2095
2100
|
if (cap === undefined) {
|
|
@@ -2102,17 +2107,17 @@ function zenWindow(label, spent, limit, share, now, durSecs, resetAt) {
|
|
|
2102
2107
|
exceeded: spent.cost >= cap
|
|
2103
2108
|
}, now, durSecs, spent.requests > 0 ? `${count(spent.requests)} req` : undefined);
|
|
2104
2109
|
}
|
|
2105
|
-
function zenRows(usage, meta, modelUsage, session, fallbackName) {
|
|
2110
|
+
function zenRows(usage, meta, modelUsage, session, fallbackName, windows) {
|
|
2106
2111
|
if (!usage)
|
|
2107
2112
|
return [];
|
|
2108
2113
|
const now = Date.now();
|
|
2109
2114
|
const oc = meta?.oc;
|
|
2110
2115
|
const rows = [["Plan", `${oc?.plan ?? "Go"} \xB7 limits per model`, "base"]];
|
|
2111
|
-
const mine = meta ? usage.models.get(modelKey(meta.name)) : undefined;
|
|
2116
|
+
const mine = usage.models.get(modelKey(fallbackName ?? "")) ?? (meta ? usage.models.get(modelKey(meta.name)) : undefined);
|
|
2112
2117
|
const limit = oc?.allowance ?? mine?.limit;
|
|
2113
2118
|
const cap = mine ? limit : undefined;
|
|
2114
|
-
rows.push(...zenWindow("5-hour", mine?.fiveHour ?? usage.totals.fiveHour, cap, GO_WINDOW_SHARE.fiveHour, now, FIVE_HOUR_SECS));
|
|
2115
|
-
rows.push(...zenWindow("Weekly", mine?.weekly ?? usage.totals.weekly, cap, GO_WINDOW_SHARE.weekly, now, WEEKLY_SECS));
|
|
2119
|
+
rows.push(...zenWindow("5-hour", mine?.fiveHour ?? usage.totals.fiveHour, cap, GO_WINDOW_SHARE.fiveHour, now, FIVE_HOUR_SECS, rollingReset(windows?.fiveHour, FIVE_HOUR_SECS)));
|
|
2120
|
+
rows.push(...zenWindow("Weekly", mine?.weekly ?? usage.totals.weekly, cap, GO_WINDOW_SHARE.weekly, now, WEEKLY_SECS, rollingReset(windows?.weekly, WEEKLY_SECS)));
|
|
2116
2121
|
const period = monthPeriod(usage);
|
|
2117
2122
|
rows.push(...zenWindow("Monthly", mine?.month ?? usage.totals.month, cap, GO_WINDOW_SHARE.month, now, period?.durSecs ?? 0, period?.resetAt));
|
|
2118
2123
|
rows.push(["Period", `${count(usage.totals.month.requests)} requests \xB7 ${money(usage.totals.month.cost)}`, "base"]);
|
|
@@ -2122,11 +2127,11 @@ function zenRows(usage, meta, modelUsage, session, fallbackName) {
|
|
|
2122
2127
|
return rows;
|
|
2123
2128
|
rows.push(["Model", clip(name, valueWidth("Model")), "strong", true]);
|
|
2124
2129
|
rows.push(...localUsageRows(modelUsage, session));
|
|
2125
|
-
if (!meta)
|
|
2126
|
-
return rows;
|
|
2127
2130
|
if (typeof limit === "number") {
|
|
2128
2131
|
rows.push(["Allowance", allowanceLabel(limit, oc?.free), "base"]);
|
|
2129
2132
|
}
|
|
2133
|
+
if (!meta)
|
|
2134
|
+
return rows;
|
|
2130
2135
|
if (oc?.rates) {
|
|
2131
2136
|
const cache = [`cache read ${rate(oc.rates.cacheRead)}`];
|
|
2132
2137
|
if (typeof oc.rates.cacheWrite === "number" && oc.rates.cacheWrite > 0) {
|
|
@@ -2375,6 +2380,53 @@ function loadModelUsage(modelID, sinceMs, path = usageDbPath(), sessionID) {
|
|
|
2375
2380
|
db.close();
|
|
2376
2381
|
}
|
|
2377
2382
|
}
|
|
2383
|
+
function loadModelWindowStart(modelID, sinceMs, path = usageDbPath()) {
|
|
2384
|
+
let db;
|
|
2385
|
+
try {
|
|
2386
|
+
db = new Database(path, {
|
|
2387
|
+
readonly: true
|
|
2388
|
+
});
|
|
2389
|
+
} catch {
|
|
2390
|
+
return null;
|
|
2391
|
+
}
|
|
2392
|
+
try {
|
|
2393
|
+
const key = modelKey(modelID);
|
|
2394
|
+
if (hasV2(db)) {
|
|
2395
|
+
const rows = db.query(`SELECT json_extract(data, '$.model.id') AS model,
|
|
2396
|
+
min(time_created) AS start
|
|
2397
|
+
FROM session_message
|
|
2398
|
+
WHERE type = 'assistant' AND time_created >= ?
|
|
2399
|
+
GROUP BY model`).all(sinceMs);
|
|
2400
|
+
for (const row of rows) {
|
|
2401
|
+
if (row.model && modelKey(row.model) === key)
|
|
2402
|
+
return row.start;
|
|
2403
|
+
}
|
|
2404
|
+
return null;
|
|
2405
|
+
}
|
|
2406
|
+
const rows = db.query(`SELECT data, time_created FROM message
|
|
2407
|
+
WHERE data LIKE '%"tokens"%' AND time_created >= ?`).all(sinceMs);
|
|
2408
|
+
let oldest = null;
|
|
2409
|
+
for (const row of rows) {
|
|
2410
|
+
let data;
|
|
2411
|
+
try {
|
|
2412
|
+
data = JSON.parse(row.data);
|
|
2413
|
+
} catch {
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
if (data.role !== "assistant" || !data.tokens || !data.modelID)
|
|
2417
|
+
continue;
|
|
2418
|
+
if (modelKey(data.modelID) !== key)
|
|
2419
|
+
continue;
|
|
2420
|
+
if (oldest === null || row.time_created < oldest)
|
|
2421
|
+
oldest = row.time_created;
|
|
2422
|
+
}
|
|
2423
|
+
return oldest;
|
|
2424
|
+
} catch {
|
|
2425
|
+
return null;
|
|
2426
|
+
} finally {
|
|
2427
|
+
db.close();
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2378
2430
|
function loadSessionUsage(sessionID, path = usageDbPath()) {
|
|
2379
2431
|
let db;
|
|
2380
2432
|
try {
|
|
@@ -2485,6 +2537,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2485
2537
|
const [meta, setMeta] = createSignal(seededMeta());
|
|
2486
2538
|
const [modelUsage, setModelUsage] = createSignal(lastModelUsage);
|
|
2487
2539
|
const [sessionUsage, setSessionUsage] = createSignal();
|
|
2540
|
+
const [modelWindows, setModelWindows] = createSignal();
|
|
2488
2541
|
const [wasOurs, setWasOurs] = createSignal(false);
|
|
2489
2542
|
const refreshMeta = () => {
|
|
2490
2543
|
loadMeta(loadPrefs().ocPlan).then((next) => {
|
|
@@ -2505,6 +2558,15 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2505
2558
|
rememberModelUsage(id, fresh, session);
|
|
2506
2559
|
setModelUsage(lastModelUsage);
|
|
2507
2560
|
};
|
|
2561
|
+
const refreshModelWindows = (id) => {
|
|
2562
|
+
if (!id)
|
|
2563
|
+
return;
|
|
2564
|
+
const now = Date.now();
|
|
2565
|
+
setModelWindows({
|
|
2566
|
+
fiveHour: loadModelWindowStart(id, now - FIVE_HOUR_SECS * 1000) ?? undefined,
|
|
2567
|
+
weekly: loadModelWindowStart(id, now - WEEKLY_SECS * 1000) ?? undefined
|
|
2568
|
+
});
|
|
2569
|
+
};
|
|
2508
2570
|
let inFlight = false;
|
|
2509
2571
|
const refresh = async () => {
|
|
2510
2572
|
if (inFlight)
|
|
@@ -2524,6 +2586,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2524
2586
|
} else if (kind === "go" || kind === "zen") {
|
|
2525
2587
|
setZen(await loadZen(loadPrefs().ocPlan));
|
|
2526
2588
|
refreshModelUsage(activeModelId());
|
|
2589
|
+
refreshModelWindows(activeModelId());
|
|
2527
2590
|
}
|
|
2528
2591
|
} catch {} finally {
|
|
2529
2592
|
inFlight = false;
|
|
@@ -2541,7 +2604,12 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2541
2604
|
createEffect(() => {
|
|
2542
2605
|
const id = activeModelId();
|
|
2543
2606
|
const session = sessionID();
|
|
2544
|
-
|
|
2607
|
+
const kind = sessionKind(providerID());
|
|
2608
|
+
if (!shows(kind) || !id)
|
|
2609
|
+
return;
|
|
2610
|
+
if (kind === "go" || kind === "zen")
|
|
2611
|
+
refreshModelWindows(id);
|
|
2612
|
+
if (!session)
|
|
2545
2613
|
return;
|
|
2546
2614
|
refreshModelUsage(id);
|
|
2547
2615
|
});
|
|
@@ -2561,7 +2629,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2561
2629
|
const found = id ? meta().get(modelKey(id)) ?? meta().get(id) : undefined;
|
|
2562
2630
|
const fallbackName = id ? id.split("/").pop() ?? id : undefined;
|
|
2563
2631
|
if (kind === "go" || kind === "zen") {
|
|
2564
|
-
return zenRows(zen(), found, usageFor(id), sessionUsage(), fallbackName);
|
|
2632
|
+
return zenRows(zen(), found, usageFor(id), sessionUsage(), fallbackName, modelWindows());
|
|
2565
2633
|
}
|
|
2566
2634
|
const model = found ? {
|
|
2567
2635
|
...found,
|
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.3",
|
|
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",
|