@jeffreyjyz/opencode-command-code 0.3.13 → 0.3.15

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.
@@ -51,6 +51,14 @@ export interface ModelMeta {
51
51
  };
52
52
  intelligence?: number;
53
53
  tps?: number;
54
+ /**
55
+ * Promotion CommandCode is running on this model (0.3.13): the badge from
56
+ * the docs row, with its expiry line when the page publishes one.
57
+ */
58
+ deal?: {
59
+ badge: string;
60
+ ends?: string;
61
+ };
54
62
  /**
55
63
  * The OpenCode Go / Zen side of the same mpc row (0.4.0): the CommandCode
56
64
  * fields above are the default, this is what a Go/Zen session reads.
@@ -9,9 +9,10 @@ export declare const WEEKLY_SECS: number;
9
9
  */
10
10
  export declare function elapsedPct(resetAtMs: number | undefined, durSecs: number, nowSecs: number): number | undefined;
11
11
  /**
12
- * `elapsedPct` as text: a seven-day window forty minutes in is 0.4%, and "0%"
13
- * reads as "not started". Non-zero fractions below a percent render "<1%".
14
- * Exact zero stays "0%".
12
+ * `elapsedPct` as text, one decimal: a seven-day window forty minutes in is
13
+ * "0.4%". Only a fraction too small to show at one decimal (<0.05%) reads
14
+ * "<0.1%", so a just-started window is not mistaken for an unstarted one.
15
+ * Exact zero stays "0.0%".
15
16
  */
16
17
  export declare function elapsedLabel(resetAtMs: number | undefined, durSecs: number, nowSecs: number): string | undefined;
17
18
  /**
package/dist/tui.js CHANGED
@@ -458,15 +458,15 @@ function elapsedPct(resetAtMs, durSecs, nowSecs) {
458
458
  if (nowSecs < start)
459
459
  return;
460
460
  const pct = (nowSecs - start) / durSecs * 100;
461
- return Math.round(Math.min(100, Math.max(0, pct)));
461
+ return Math.round(Math.min(100, Math.max(0, pct)) * 10) / 10;
462
462
  }
463
463
  function elapsedLabel(resetAtMs, durSecs, nowSecs) {
464
464
  const pct = elapsedPct(resetAtMs, durSecs, nowSecs);
465
465
  if (pct === undefined)
466
466
  return;
467
467
  if (pct === 0 && nowSecs > Math.floor(resetAtMs / 1000) - durSecs)
468
- return "<1%";
469
- return `${pct}%`;
468
+ return "<0.1%";
469
+ return `${pct.toFixed(1)}%`;
470
470
  }
471
471
  function paceEtaSecs(resetAtMs, durSecs, used, cap, nowSecs) {
472
472
  if (typeof resetAtMs !== "number" || !Number.isFinite(resetAtMs))
@@ -551,14 +551,14 @@ function windowRows(label, w, now, durSecs) {
551
551
  if (!w || typeof w.cap !== "number" || w.cap <= 0)
552
552
  return [];
553
553
  const used = typeof w.used === "number" ? w.used : 0;
554
- const pct = Math.round(used / w.cap * 100);
554
+ const pct = used / w.cap * 100;
555
555
  const nowSecs = Math.floor(now / 1000);
556
556
  const elapsed = elapsedLabel(w.resetAt, durSecs, nowSecs);
557
557
  const eta = paceEtaSecs(w.resetAt, durSecs, used, w.cap, nowSecs);
558
558
  const reset = until(w.resetAt, now);
559
559
  const lead = w.exceeded ? "LIMIT EXCEEDED" : eta === undefined ? elapsed === undefined ? "" : `${elapsed} elapsed` : `cap in ${until(now + eta * 1000, now)}`;
560
560
  const detail = [lead, reset ? `resets ${reset}` : ""].filter(Boolean).join(" \xB7 ");
561
- return [[label, `${money(used)}/${money(w.cap)} (${pct}%)`, w.exceeded ? "crit" : pctTone(pct)], ...detail ? [["", detail, "muted"]] : []];
561
+ return [[label, `${money(used)}/${money(w.cap)} (${pct.toFixed(1)}%)`, w.exceeded ? "crit" : pctTone(pct)], ...detail ? [["", detail, "muted"]] : []];
562
562
  }
563
563
  function monthlyDetail(usage, now) {
564
564
  const {
@@ -585,8 +585,8 @@ function usageRows(usage, now = Date.now()) {
585
585
  }
586
586
  if (typeof usage.monthlyCap === "number" && typeof usage.monthlyCredits === "number") {
587
587
  const used = Math.min(usage.monthlyCap, Math.max(0, usage.monthlyCap - usage.monthlyCredits));
588
- const pct = usage.monthlyCap > 0 ? Math.round(used / usage.monthlyCap * 100) : 0;
589
- rows.push(["Monthly", `${money(used)} / ${money(usage.monthlyCap)} (${pct}%)`, pctTone(pct)]);
588
+ const pct = usage.monthlyCap > 0 ? used / usage.monthlyCap * 100 : 0;
589
+ rows.push(["Monthly", `${money(used)} / ${money(usage.monthlyCap)} (${pct.toFixed(1)}%)`, pctTone(pct)]);
590
590
  const detail = monthlyDetail(usage, now);
591
591
  if (detail)
592
592
  rows.push(["", detail, "muted"]);
@@ -608,8 +608,12 @@ function modelRows(meta, usage, session) {
608
608
  return [];
609
609
  const rows = [["Model", clip(meta.name, valueWidth("Model")), "strong", true]];
610
610
  if (usage) {
611
- const spent = usage.cost > 0 ? ` \xB7 ${money(usage.cost)}` : "";
612
- rows.push(["Usage (this model)", `${count(usage.requests)} req${spent}`, "base"]);
611
+ rows.push(["Usage (this model)", `${count(usage.requests)} req`, "base"]);
612
+ const spend = usage.cost > 0 ? money(usage.cost) : "";
613
+ const share = usage.cost > 0 && session && session.cost > 0 ? `${Math.round(usage.cost / session.cost * 100)}% of session` : "";
614
+ const detail = [spend, share].filter(Boolean).join(" \xB7 ");
615
+ if (detail)
616
+ rows.push(["", detail, "muted"]);
613
617
  }
614
618
  if (session) {
615
619
  const spent = session.cost > 0 ? ` \xB7 ${money(session.cost)}` : "";
@@ -621,6 +625,10 @@ function modelRows(meta, usage, session) {
621
625
  rows.push(["Min plan", meta.minPlan, "base"]);
622
626
  if (typeof meta.allowance === "number")
623
627
  rows.push(["Allowance", `${money(meta.allowance)}/mo`, "base"]);
628
+ if (meta.deal) {
629
+ const ends = meta.deal.ends?.match(/([A-Z][a-z]{2})[a-z]*\s+(\d{1,2})/);
630
+ rows.push(["Deal", ends ? `${meta.deal.badge} \xB7 ends ${ends[1]} ${ends[2]}` : meta.deal.badge, "ok"]);
631
+ }
624
632
  if (meta.rates) {
625
633
  const cache = [`cache read ${rate(meta.rates.cacheRead)}`];
626
634
  if (typeof meta.rates.cacheWrite === "number" && meta.rates.cacheWrite > 0) {
@@ -1913,8 +1921,8 @@ function windowRows2(label, spent, limit, share) {
1913
1921
  return [[label, `${money(spent.cost)} \xB7 ${count(spent.requests)} req`, "base"]];
1914
1922
  }
1915
1923
  const cap = limit * share;
1916
- const pct = cap > 0 ? Math.round(spent.cost / cap * 100) : 0;
1917
- const rows = [[label, `${money(spent.cost)}/${money(cap)} (${pct}%)`, pctTone(pct)]];
1924
+ const pct = cap > 0 ? spent.cost / cap * 100 : 0;
1925
+ const rows = [[label, `${money(spent.cost)}/${money(cap)} (${pct.toFixed(1)}%)`, pctTone(pct)]];
1918
1926
  if (spent.requests > 0)
1919
1927
  rows.push(["", `${count(spent.requests)} req`, "muted"]);
1920
1928
  return rows;
@@ -1961,7 +1969,7 @@ function zenRows(usage, meta, session) {
1961
1969
  var CMDUSE = cmduseCandidates();
1962
1970
  var MPC = ["mpc", join2(homedir2(), ".bun/bin/mpc"), "/opt/homebrew/bin/mpc"];
1963
1971
  var OCUSE = [process.env.OCUSE_BIN, "ocuse", "/opt/homebrew/bin/ocuse", "/usr/local/bin/ocuse"].filter((bin) => Boolean(bin));
1964
- var CACHE_TTL_MS = 6 * 60 * 60 * 1000;
1972
+ var CACHE_TTL_MS = 60 * 60 * 1000;
1965
1973
  var SPAWN_OPTIONS = {
1966
1974
  stdio: ["ignore", "pipe", "pipe"],
1967
1975
  detached: true
@@ -2032,6 +2040,10 @@ function parseMpcJson(text) {
2032
2040
  rates: cc?.pricing,
2033
2041
  intelligence: cc?.ability ?? undefined,
2034
2042
  tps: cc?.tps ?? undefined,
2043
+ deal: cc?.deal?.badge !== undefined ? {
2044
+ badge: cc.deal.badge,
2045
+ ends: cc.deal.ends
2046
+ } : undefined,
2035
2047
  oc: oc ? {
2036
2048
  provider: oc.provider,
2037
2049
  plan: oc.plan,
@@ -2105,20 +2117,25 @@ function loadModelUsage(modelID, sinceMs, path = usageDbPath(), sessionID) {
2105
2117
  }
2106
2118
  try {
2107
2119
  if (hasV2(db)) {
2108
- const rows = db.query(`SELECT json_extract(data, '$.model.id') AS model, count(*) AS c
2120
+ const rows = db.query(`SELECT json_extract(data, '$.model.id') AS model,
2121
+ count(*) AS c,
2122
+ coalesce(sum(json_extract(data, '$.cost')), 0) AS cost
2109
2123
  FROM session_message
2110
2124
  WHERE type = 'assistant'
2111
2125
  ${sessionID ? "AND session_id = ?" : "AND time_created >= ?"}
2112
2126
  GROUP BY model`).all(sessionID ?? sinceMs);
2113
2127
  const key = modelKey(modelID);
2114
2128
  let requests = 0;
2129
+ let cost = 0;
2115
2130
  for (const row of rows) {
2116
- if (row.model && modelKey(row.model) === key)
2131
+ if (row.model && modelKey(row.model) === key) {
2117
2132
  requests += row.c;
2133
+ cost += row.cost;
2134
+ }
2118
2135
  }
2119
2136
  return {
2120
2137
  requests,
2121
- cost: 0
2138
+ cost
2122
2139
  };
2123
2140
  }
2124
2141
  const rows = db.query(`SELECT data, time_created FROM message
@@ -2206,7 +2223,7 @@ function loadSessionUsage(sessionID, path = usageDbPath()) {
2206
2223
  }
2207
2224
 
2208
2225
  // src/sidebar/useRows.ts
2209
- var POLL_MS = 20000;
2226
+ var POLL_MS = 5000;
2210
2227
  function sessionKind(providerID) {
2211
2228
  if (!providerID)
2212
2229
  return "unknown";
@@ -2256,7 +2273,10 @@ function useRows(activeModelId, providerID, sessionID) {
2256
2273
  const [modelUsage, setModelUsage] = createSignal(lastModelUsage);
2257
2274
  const [sessionUsage, setSessionUsage] = createSignal();
2258
2275
  const [wasOurs, setWasOurs] = createSignal(false);
2259
- loadMeta().then(setMeta).catch(() => {});
2276
+ const refreshMeta = () => {
2277
+ loadMeta().then(setMeta).catch(() => {});
2278
+ };
2279
+ refreshMeta();
2260
2280
  const refreshModelUsage = (id) => {
2261
2281
  if (!id)
2262
2282
  return;
@@ -2268,6 +2288,7 @@ function useRows(activeModelId, providerID, sessionID) {
2268
2288
  const refresh = async () => {
2269
2289
  const kind = sessionKind(providerID());
2270
2290
  const session = sessionID();
2291
+ refreshMeta();
2271
2292
  if (session)
2272
2293
  setSessionUsage(loadSessionUsage(session) ?? undefined);
2273
2294
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
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",