@jeffreyjyz/opencode-command-code 0.5.4 → 0.5.7

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/cli.d.ts CHANGED
@@ -22,6 +22,12 @@ export declare function buildCmduseArgs(arg: string): string[];
22
22
  * string is split and passed straight through; an empty arg means
23
23
  * the fewest-columns view (see MPC_DEFAULT_ARGS). */
24
24
  export declare function buildMpcArgs(arg: string): string[];
25
+ /**
26
+ * The arg the ocuse_usage tool and the /ocuse-usage dialog pass on: the
27
+ * agent's (or slash) args when given, else `-1`. A bare ocuse is the live
28
+ * watch loop and would never return, so empty input never passes through.
29
+ */
30
+ export declare function ocuseArgs(arg: string | undefined): string;
25
31
  export type RunOptions = {
26
32
  signal?: AbortSignal;
27
33
  /** Extra env for the child (CMD_API_KEY when the plugin resolved one). */
@@ -15,12 +15,18 @@ export declare const ZEN_PROVIDER_PREFIX = "opencode-zen";
15
15
  export declare const TUI_ID = "command-code.tui";
16
16
  /** The v2 /cmd-usage slash command id (registered and bound in tui.tsx). */
17
17
  export declare const CMD_USAGE_COMMAND_ID = "command-code.cmd-usage";
18
+ /** The v2 /ocuse-usage slash command id (registered and bound in tui.tsx). */
19
+ export declare const OCUSE_USAGE_COMMAND_ID = "command-code.ocuse-usage";
18
20
  /** The cmd_usage tool description, shared by the v1 and v2 halves. */
19
21
  export declare const TOOL_DESCRIPTION = "Fetch live Command Code plan/usage: plan name, monthly credits, 5-hour & weekly windows, billing-period summary. Pass arg='plans' for the plan comparison table only, or extra cmduse flags (e.g. '--tz +05:30 daily').";
20
22
  /** The mpc_prices tool description, shared by the v1 and v2 halves. */
21
23
  export declare const MPC_TOOL_DESCRIPTION = "Per-model price comparison: what a model costs per request ($/1M input, output and cache rates) and per month (request allowance) on each Command Code (cc) and OpenCode Go (oc) plan side, plus ability, Tok/s, the cheaper-side verdict and running deals. Pass '--model <regex>' to filter rows, '--minimal' | '--medium' | '--detail' for table width, '--json' for the full structured rows, '--cc-plan' / '--oc-plan' to pick a plan, or '--usage' to project real usage.";
22
24
  /** The mpc_prices `arg` description, shared by the v1 and v2 halves. */
23
25
  export declare const MPC_TOOL_ARG_DESCRIPTION = "Optional mpc flags: '--model <regex>' filters rows, '--minimal' | '--medium' | '--detail' set table width, '--json' for full structured rows, '--cc-plan'/'--oc-plan' pick a plan, '--usage' projects real usage";
26
+ /** The ocuse_usage tool description, shared by the v1 and v2 halves. */
27
+ export declare const OCUSE_TOOL_DESCRIPTION = "Fetch live OpenCode Go/Zen usage: the account's window meters, per-model spend against limits, 5-hour & weekly windows, billing-period summary. Defaults to the one-shot dashboard; pass extra ocuse flags for more (e.g. '--plan go-plus', 'daily', '--json').";
28
+ /** The ocuse_usage `arg` description, shared by the v1 and v2 halves. */
29
+ export declare const OCUSE_TOOL_ARG_DESCRIPTION = "Optional: extra ocuse flags (e.g. '--plan go-plus', 'daily', '--json'); empty means the one-shot dashboard";
24
30
  export type Lane = {
25
31
  id: "command-code-anthropic" | "command-code-openai";
26
32
  name: string;
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.4",
2417
+ version: "0.5.7",
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",
@@ -3318,6 +3318,9 @@ function buildMpcArgs(arg) {
3318
3318
  return [...MPC_DEFAULT_ARGS];
3319
3319
  return tokens;
3320
3320
  }
3321
+ function ocuseArgs(arg) {
3322
+ return arg !== undefined && arg.trim() !== "" ? arg : "-1";
3323
+ }
3321
3324
  function stderrTail(text) {
3322
3325
  const lines = text.trimEnd().split(`
3323
3326
  `);
@@ -3403,6 +3406,21 @@ async function runMpc(arg, opts = {}) {
3403
3406
  }
3404
3407
  throw new Error(`mpc binary not found (install: bun add -g @jeffreyjyz/mpc)${last instanceof Error && last.message !== last.name ? ` \u2014 ${last.message}` : ""}`, { cause: last });
3405
3408
  }
3409
+ async function runOcuse(arg, opts = {}) {
3410
+ const args = resolveArgs(arg, splitCliArgs);
3411
+ const candidates = opts.candidates ?? OCUSE_CANDIDATES;
3412
+ let last;
3413
+ for (const bin of candidates) {
3414
+ try {
3415
+ return await runOnce(bin, args, opts, "ocuse");
3416
+ } catch (e) {
3417
+ last = e;
3418
+ if (e?.code !== "ENOENT")
3419
+ throw e;
3420
+ }
3421
+ }
3422
+ throw new Error(`ocuse binary not found (install: brew install JeffreyJYZ/tap/cmduse)${last instanceof Error && last.message !== last.name ? ` \u2014 ${last.message}` : ""}`, { cause: last });
3423
+ }
3406
3424
 
3407
3425
  // src/constants/models.ts
3408
3426
  var MODEL_CAPABILITIES = {
@@ -3431,6 +3449,8 @@ var INTEGRATION_NAME = "Command Code";
3431
3449
  var TOOL_DESCRIPTION = "Fetch live Command Code plan/usage: plan name, monthly credits, 5-hour & weekly windows, billing-period summary. Pass arg='plans' for the plan comparison table only, or extra cmduse flags (e.g. '--tz +05:30 daily').";
3432
3450
  var MPC_TOOL_DESCRIPTION = "Per-model price comparison: what a model costs per request ($/1M input, output and cache rates) and per month (request allowance) on each Command Code (cc) and OpenCode Go (oc) plan side, plus ability, Tok/s, the cheaper-side verdict and running deals. Pass '--model <regex>' to filter rows, '--minimal' | '--medium' | '--detail' for table width, '--json' for the full structured rows, '--cc-plan' / '--oc-plan' to pick a plan, or '--usage' to project real usage.";
3433
3451
  var MPC_TOOL_ARG_DESCRIPTION = "Optional mpc flags: '--model <regex>' filters rows, '--minimal' | '--medium' | '--detail' set table width, '--json' for full structured rows, '--cc-plan'/'--oc-plan' pick a plan, '--usage' projects real usage";
3452
+ var OCUSE_TOOL_DESCRIPTION = "Fetch live OpenCode Go/Zen usage: the account's window meters, per-model spend against limits, 5-hour & weekly windows, billing-period summary. Defaults to the one-shot dashboard; pass extra ocuse flags for more (e.g. '--plan go-plus', 'daily', '--json').";
3453
+ var OCUSE_TOOL_ARG_DESCRIPTION = "Optional: extra ocuse flags (e.g. '--plan go-plus', 'daily', '--json'); empty means the one-shot dashboard";
3434
3454
  var LANES = [
3435
3455
  {
3436
3456
  id: "command-code-anthropic",
@@ -4968,6 +4988,26 @@ var commandCodeV2 = {
4968
4988
  };
4969
4989
  }
4970
4990
  });
4991
+ editor.add({
4992
+ name: "ocuse_usage",
4993
+ description: OCUSE_TOOL_DESCRIPTION,
4994
+ input: {
4995
+ type: "object",
4996
+ properties: {
4997
+ arg: {
4998
+ type: "string",
4999
+ description: OCUSE_TOOL_ARG_DESCRIPTION
5000
+ }
5001
+ },
5002
+ additionalProperties: false
5003
+ },
5004
+ async execute(input) {
5005
+ const arg = typeof input?.arg === "string" ? input.arg : undefined;
5006
+ return {
5007
+ content: await runOcuse(ocuseArgs(arg))
5008
+ };
5009
+ }
5010
+ });
4971
5011
  });
4972
5012
  let hasConnection = false;
4973
5013
  let connectionMs = 0;
@@ -5180,6 +5220,10 @@ var CommandCodePlugin = async (_input) => {
5180
5220
  description: "Show Command Code plan, credits, and usage windows",
5181
5221
  template: "Call the cmd_usage tool and present its markdown output verbatim to the user. If the tool errors, tell the user to run /connect (Command Code (Anthropic)) and retry. $ARGUMENTS"
5182
5222
  };
5223
+ cfg.command["ocuse-usage"] = {
5224
+ description: "Show OpenCode Go/Zen usage: account meters and per-model spend",
5225
+ template: "Call the ocuse_usage tool and present its markdown output verbatim to the user. If the tool errors, the Go key is missing or has no subscription \u2014 usage still shows where the store has it. $ARGUMENTS"
5226
+ };
5183
5227
  },
5184
5228
  provider: {
5185
5229
  id: ANTHROPIC.id,
@@ -5216,6 +5260,15 @@ var CommandCodePlugin = async (_input) => {
5216
5260
  env: { CMD_API_KEY: key }
5217
5261
  });
5218
5262
  }
5263
+ }),
5264
+ ocuse_usage: tool({
5265
+ description: OCUSE_TOOL_DESCRIPTION,
5266
+ args: {
5267
+ arg: string2().optional().describe(OCUSE_TOOL_ARG_DESCRIPTION)
5268
+ },
5269
+ async execute(args) {
5270
+ return runOcuse(ocuseArgs(args.arg));
5271
+ }
5219
5272
  })
5220
5273
  }
5221
5274
  };
@@ -87,6 +87,15 @@ export interface WindowUsage {
87
87
  resetAt?: number;
88
88
  /** The account API's own over-cap flag (cmduse passes it through). */
89
89
  exceeded?: boolean;
90
+ /**
91
+ * The account's own share of the limit, when its endpoint answered. That
92
+ * figure is the one the server gates on and prints on its console, so it —
93
+ * not `used / cap`, which this machine computes and which sits below the
94
+ * server's meter — sets the row's tone and reads as the share. The money
95
+ * pair stays this store's own, which is why the two need not divide to each
96
+ * other.
97
+ */
98
+ sharePct?: number;
90
99
  }
91
100
  /** One model's usage for the account's current billing period. */
92
101
  export interface ModelUsage {
@@ -142,10 +151,11 @@ export declare function until(epoch: number | undefined, now?: number): string;
142
151
  * implementation. */
143
152
  export declare function windowRows(label: string, w: WindowUsage | undefined, now: number, durSecs: number, extra?: string,
144
153
  /** How the use line reads. The default is money (`$0.55/$14 (3.9%)`); the
145
- * account's own meters publish shares rather than dollars and pass a percent
146
- * formatter, so the tone, `LIMIT EXCEEDED`, `cap in …`, `elapsed` and
147
- * `resets` stay this one implementation for both. */
148
- use?: (used: number, cap: number, pct: number) => string): SidebarRow[];
154
+ * account's own meters publish their own share of the limit, which the
155
+ * formatter receives as `share` — the money pair beside it stays local, so
156
+ * the tone, `LIMIT EXCEEDED`, `cap in …`, `elapsed` and `resets` stay this
157
+ * one implementation for both panels. */
158
+ use?: (used: number, cap: number, share: number) => string): SidebarRow[];
149
159
  /**
150
160
  * The two store-backed rows for the active model — its requests under `Usage
151
161
  * (this model)`, then this conversation's own totals under `Session`. They need
@@ -50,6 +50,13 @@ export interface ZenUsage {
50
50
  };
51
51
  /** The account's own meters, when ocuse could ask for them. */
52
52
  account?: ZenAccount;
53
+ /**
54
+ * Why `account` is absent, when ocuse could name it: `HTTP 401
55
+ * api_key_expired`, `no Go key`, a transport fault (`ocuse` 0.7.14+). The
56
+ * panel shows it, because "estimated locally" alone cannot tell a missing key
57
+ * from an expired one — and only the expired one is something to act on.
58
+ */
59
+ accountError?: string;
53
60
  /** Keyed by canonical model key, the same normalisation the CC side matches on. */
54
61
  models: Map<string, ZenModel>;
55
62
  }
package/dist/tui.js CHANGED
@@ -89,6 +89,9 @@ function buildMpcArgs(arg) {
89
89
  return [...MPC_DEFAULT_ARGS];
90
90
  return tokens;
91
91
  }
92
+ function ocuseArgs(arg) {
93
+ return arg !== undefined && arg.trim() !== "" ? arg : "-1";
94
+ }
92
95
  function stderrTail(text) {
93
96
  const lines = text.trimEnd().split(`
94
97
  `);
@@ -208,6 +211,7 @@ var ZEN_PROVIDER_ID = "opencode";
208
211
  var ZEN_PROVIDER_PREFIX = "opencode-zen";
209
212
  var TUI_ID = "command-code.tui";
210
213
  var CMD_USAGE_COMMAND_ID = "command-code.cmd-usage";
214
+ var OCUSE_USAGE_COMMAND_ID = "command-code.ocuse-usage";
211
215
 
212
216
  // src/sidebar/panel.tsx
213
217
  import { createComponent as _$createComponent } from "@opentui/solid";
@@ -690,13 +694,14 @@ function windowRows(label, w, now, durSecs, extra, use) {
690
694
  return [];
691
695
  const used = typeof w.used === "number" ? w.used : 0;
692
696
  const pct = used / w.cap * 100;
697
+ const share = typeof w.sharePct === "number" ? w.sharePct : pct;
693
698
  const nowSecs = Math.floor(now / 1000);
694
699
  const elapsed = elapsedLabel(w.resetAt, durSecs, nowSecs);
695
700
  const eta = paceEtaSecs(w.resetAt, durSecs, used, w.cap, nowSecs);
696
701
  const reset = until(w.resetAt, now);
697
702
  const lead = w.exceeded ? "LIMIT EXCEEDED" : eta === undefined ? elapsed === undefined ? "" : `${elapsed} elapsed` : `cap in ${until(now + eta * 1000, now)}`;
698
703
  const detail = [lead, reset ? `resets ${reset}` : ""].filter(Boolean).join(" \xB7 ");
699
- return [[label, use ? use(used, w.cap, pct) : `${money(used)}/${money(w.cap)} (${pct.toFixed(1)}%)`, w.exceeded ? "crit" : pctTone(pct)], ...detail ? [["", detail, "muted"]] : [], ...extra ? [["", extra, "muted"]] : []];
704
+ return [[label, use ? use(used, w.cap, share) : `${money(used)}/${money(w.cap)} (${pct.toFixed(1)}%)`, w.exceeded ? "crit" : pctTone(share)], ...detail ? [["", detail, "muted"]] : [], ...extra ? [["", extra, "muted"]] : []];
700
705
  }
701
706
  function localUsageRows(usage, session) {
702
707
  const rows = [];
@@ -2143,6 +2148,7 @@ function parseZenJson(text) {
2143
2148
  weekly: meter(body.account.weekly),
2144
2149
  monthly: meter(body.account.monthly)
2145
2150
  } : undefined,
2151
+ accountError: typeof body.accountError === "string" && body.accountError ? body.accountError : undefined,
2146
2152
  totals: {
2147
2153
  fiveHour: totals(top.fiveHour),
2148
2154
  weekly: totals(top.weekly),
@@ -2183,13 +2189,14 @@ function zenWindow(label, spent, limit, share, now, durSecs, resetAt) {
2183
2189
  exceeded: spent.cost >= cap
2184
2190
  }, now, durSecs, spent.requests > 0 ? `${count(spent.requests)} req` : undefined);
2185
2191
  }
2186
- function zenMeterRows(label, meter, now, durSecs) {
2192
+ function zenMeterRows(label, meter, now, durSecs, local) {
2187
2193
  return windowRows(label, {
2188
- cap: 100,
2189
- used: meter.percent,
2194
+ cap: local?.cap ?? 100,
2195
+ used: local?.spent ?? meter.percent,
2190
2196
  resetAt: meter.resetsAt,
2197
+ sharePct: meter.percent,
2191
2198
  exceeded: meter.percent >= 100 || meter.status !== "ok"
2192
- }, now, durSecs, undefined, (_used, _cap, pct) => `${pct.toFixed(1)}% of limit`);
2199
+ }, now, durSecs, undefined, (used, cap, share) => local ? `${money(used)}/${money(cap)} (${share.toFixed(1)}% of limit)` : `${share.toFixed(1)}% of limit`);
2193
2200
  }
2194
2201
  function zenRows(usage, meta, modelUsage, session, fallbackName, windows) {
2195
2202
  if (!usage)
@@ -2197,14 +2204,21 @@ function zenRows(usage, meta, modelUsage, session, fallbackName, windows) {
2197
2204
  const now = Date.now();
2198
2205
  const oc = meta?.oc;
2199
2206
  const rows = [["Plan", `${oc?.plan ?? "Go"} \xB7 limits per model`, "base"]];
2207
+ if (usage.accountError) {
2208
+ rows.push(["", clip(`usage API: ${usage.accountError}`, valueWidth("")), "warn"]);
2209
+ }
2200
2210
  const mine = usage.models.get(modelKey(fallbackName ?? "")) ?? (meta ? usage.models.get(modelKey(meta.name)) : undefined);
2201
2211
  const limit = oc?.allowance ?? mine?.limit;
2202
2212
  const cap = mine ? limit : undefined;
2213
+ const windowMoney = (spent, share) => mine && typeof limit === "number" && limit > 0 ? {
2214
+ spent: spent.cost,
2215
+ cap: limit * share
2216
+ } : undefined;
2203
2217
  const period = monthPeriod(usage);
2204
2218
  if (usage.account) {
2205
- rows.push(...zenMeterRows("5-hour", usage.account.rolling, now, FIVE_HOUR_SECS));
2206
- rows.push(...zenMeterRows("Weekly", usage.account.weekly, now, WEEKLY_SECS));
2207
- rows.push(...zenMeterRows("Monthly", usage.account.monthly, now, period?.durSecs ?? 0));
2219
+ rows.push(...zenMeterRows("5-hour", usage.account.rolling, now, FIVE_HOUR_SECS, mine ? windowMoney(mine.fiveHour, GO_WINDOW_SHARE.fiveHour) : undefined));
2220
+ rows.push(...zenMeterRows("Weekly", usage.account.weekly, now, WEEKLY_SECS, mine ? windowMoney(mine.weekly, GO_WINDOW_SHARE.weekly) : undefined));
2221
+ rows.push(...zenMeterRows("Monthly", usage.account.monthly, now, period?.durSecs ?? 0, mine ? windowMoney(mine.month, GO_WINDOW_SHARE.month) : undefined));
2208
2222
  } else {
2209
2223
  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)));
2210
2224
  rows.push(...zenWindow("Weekly", mine?.weekly ?? usage.totals.weekly, cap, GO_WINDOW_SHARE.weekly, now, WEEKLY_SECS, rollingReset(windows?.weekly, WEEKLY_SECS)));
@@ -2775,8 +2789,35 @@ var commandCodeTui = {
2775
2789
  message: text.trimEnd()
2776
2790
  });
2777
2791
  }
2792
+ }, {
2793
+ id: OCUSE_USAGE_COMMAND_ID,
2794
+ title: "OpenCode Go/Zen usage",
2795
+ group: "Command Code",
2796
+ slash: {
2797
+ name: "ocuse-usage",
2798
+ arguments: true
2799
+ },
2800
+ enabled: () => true,
2801
+ suggested: true,
2802
+ run: async (input) => {
2803
+ let text;
2804
+ try {
2805
+ text = await runOcuse(ocuseArgs(input));
2806
+ } catch (error) {
2807
+ ctx.ui.toast.show({
2808
+ title: "ocuse-usage failed",
2809
+ message: error instanceof Error ? error.message : String(error),
2810
+ variant: "error"
2811
+ });
2812
+ return;
2813
+ }
2814
+ await ctx.ui.dialog.alert({
2815
+ title: "OpenCode Go/Zen usage",
2816
+ message: text.trimEnd()
2817
+ });
2818
+ }
2778
2819
  }],
2779
- bindings: [CMD_USAGE_COMMAND_ID]
2820
+ bindings: [CMD_USAGE_COMMAND_ID, OCUSE_USAGE_COMMAND_ID]
2780
2821
  }));
2781
2822
  }
2782
2823
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.5.4",
3
+ "version": "0.5.7",
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",