@jeffreyjyz/opencode-command-code 0.4.0 → 0.5.0

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
@@ -2,6 +2,10 @@
2
2
  * the homebrew prefix, so the bare name alone is not enough. `CMDUSE_BIN` wins
3
3
  * when set — the dev-build hook the sibling cmduse consumers (mpc) honour too. */
4
4
  export declare function cmduseCandidates(env?: Record<string, string | undefined>): string[];
5
+ /** Same lookup for mpc, the per-model price comparison: `MPC_BIN`
6
+ * wins when set (the `bun link` dev hook oc-cmd-compare installs),
7
+ * then the bun/homebrew fallbacks. */
8
+ export declare function mpcCandidates(env?: Record<string, string | undefined>): string[];
5
9
  /** Shell-words split: whitespace-separated, with single/double quoting and
6
10
  * backslash escapes ("--tz +05:30", arg='a b'). Unterminated quotes close at
7
11
  * end of input. */
@@ -10,6 +14,10 @@ export declare function splitCliArgs(input: string): string[];
10
14
  * pass-through flags, or a subcommand invocation ("plans",
11
15
  * "--tz +05:30 daily") passed through untouched. */
12
16
  export declare function buildCmduseArgs(arg: string): string[];
17
+ /** argv for an mpc invocation: mpc has no subcommands, so the arg
18
+ * string is split and passed straight through; an empty arg means
19
+ * the fewest-columns view (see MPC_DEFAULT_ARGS). */
20
+ export declare function buildMpcArgs(arg: string): string[];
13
21
  export type RunOptions = {
14
22
  signal?: AbortSignal;
15
23
  /** Extra env for the child (CMD_API_KEY when the plugin resolved one). */
@@ -21,3 +29,9 @@ export type RunOptions = {
21
29
  * homebrew locations; every candidate failing with ENOENT yields a single
22
30
  * install-hint error. */
23
31
  export declare function runCmduse(arg: string, opts?: RunOptions): Promise<string>;
32
+ /** Run mpc and return its plain-text stdout (the per-model price
33
+ * comparison). Same contract as runCmduse: `MPC_BIN` wins, then the
34
+ * fallback paths; every candidate failing with ENOENT yields a
35
+ * single install-hint error, and any other failure (a non-zero exit
36
+ * included) surfaces immediately with mpc's last stderr lines. */
37
+ export declare function runMpc(arg: string, opts?: RunOptions): Promise<string>;
@@ -1,6 +1,11 @@
1
1
  import type { spawn } from "node:child_process";
2
2
  /** Default invocation: one-shot dashboard, no colors / no live redraw. */
3
3
  export declare const DEFAULT_ARGS: readonly string[];
4
+ /** Default mpc invocation: the fewest-columns view. mpc has no
5
+ * subcommands to fall back on, so an empty arg must mean the
6
+ * narrowest table — a bare tool call cannot dump a wide comparison
7
+ * into the model's context. */
8
+ export declare const MPC_DEFAULT_ARGS: readonly string[];
4
9
  /** cmduse subcommands — when the arg starts with one, pass it through
5
10
  * untouched; otherwise it is extra flags for the one-shot dashboard. */
6
11
  export declare const SUBCOMMANDS: ReadonlySet<string>;
@@ -15,6 +15,10 @@ export declare const ZEN_PROVIDER_PREFIX = "opencode-zen";
15
15
  export declare const TUI_ID = "command-code.tui";
16
16
  /** The cmd_usage tool description, shared by the v1 and v2 halves. */
17
17
  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').";
18
+ /** The mpc_prices tool description, shared by the v1 and v2 halves. */
19
+ 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.";
20
+ /** The mpc_prices `arg` description, shared by the v1 and v2 halves. */
21
+ 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";
18
22
  export type Lane = {
19
23
  id: "command-code-anthropic" | "command-code-openai";
20
24
  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.4.0",
2417
+ version: "0.5.0",
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",
@@ -3233,6 +3233,7 @@ var MPC_BIN_CANDIDATES = [
3233
3233
 
3234
3234
  // src/constants/cli.ts
3235
3235
  var DEFAULT_ARGS = ["-1", "--plain"];
3236
+ var MPC_DEFAULT_ARGS = ["--minimal"];
3236
3237
  var SUBCOMMANDS = new Set([
3237
3238
  "plans",
3238
3239
  "models",
@@ -3250,6 +3251,11 @@ function cmduseCandidates(env = process.env) {
3250
3251
  return candidates.filter((bin) => Boolean(bin));
3251
3252
  }
3252
3253
  var CANDIDATES = cmduseCandidates();
3254
+ function mpcCandidates(env = process.env) {
3255
+ const candidates = [env.MPC_BIN, ...MPC_BIN_CANDIDATES];
3256
+ return candidates.filter((bin) => Boolean(bin));
3257
+ }
3258
+ var MPC_CANDIDATES = mpcCandidates();
3253
3259
  function splitCliArgs(input) {
3254
3260
  const out = [];
3255
3261
  let cur = "";
@@ -3296,13 +3302,19 @@ function buildCmduseArgs(arg) {
3296
3302
  return tokens;
3297
3303
  return [...DEFAULT_ARGS, ...tokens];
3298
3304
  }
3305
+ function buildMpcArgs(arg) {
3306
+ const tokens = splitCliArgs(arg);
3307
+ if (!tokens.length)
3308
+ return [...MPC_DEFAULT_ARGS];
3309
+ return tokens;
3310
+ }
3299
3311
  function stderrTail(text) {
3300
3312
  const lines = text.trimEnd().split(`
3301
3313
  `);
3302
3314
  return lines.slice(-3).join(`
3303
3315
  `).slice(-400);
3304
3316
  }
3305
- function runOnce(bin, args, opts) {
3317
+ function runOnce(bin, args, opts, name = "cmduse") {
3306
3318
  return new Promise((resolve, reject) => {
3307
3319
  const child = spawn(bin, args, {
3308
3320
  env: { ...process.env, ...opts.env ?? {} },
@@ -3322,13 +3334,13 @@ function runOnce(bin, args, opts) {
3322
3334
  opts.signal?.removeEventListener("abort", onAbort);
3323
3335
  const stdout = Buffer.concat(out).toString("utf8");
3324
3336
  if (opts.signal?.aborted) {
3325
- reject(new Error("cmduse invocation aborted"));
3337
+ reject(new Error(`${name} invocation aborted`));
3326
3338
  } else if (code === 0) {
3327
3339
  resolve(stdout);
3328
3340
  } else {
3329
3341
  const why = signal ? `killed by ${signal}` : `exit ${code}`;
3330
3342
  const tail = stderrTail(Buffer.concat(err).toString("utf8"));
3331
- reject(new Error(`cmduse ${args.join(" ")}: ${why}${tail ? `
3343
+ reject(new Error(`${name} ${args.join(" ")}: ${why}${tail ? `
3332
3344
  ${tail}` : ""}`));
3333
3345
  }
3334
3346
  });
@@ -3349,6 +3361,21 @@ async function runCmduse(arg, opts = {}) {
3349
3361
  }
3350
3362
  throw new Error(`cmduse binary not found (install: brew install JeffreyJYZ/tap/cmduse)${last instanceof Error && last.message !== last.name ? ` \u2014 ${last.message}` : ""}`, { cause: last });
3351
3363
  }
3364
+ async function runMpc(arg, opts = {}) {
3365
+ const args = buildMpcArgs(arg);
3366
+ const candidates = opts.candidates ?? MPC_CANDIDATES;
3367
+ let last;
3368
+ for (const bin of candidates) {
3369
+ try {
3370
+ return await runOnce(bin, args, opts, "mpc");
3371
+ } catch (e) {
3372
+ last = e;
3373
+ if (e?.code !== "ENOENT")
3374
+ throw e;
3375
+ }
3376
+ }
3377
+ 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 });
3378
+ }
3352
3379
 
3353
3380
  // src/constants/models.ts
3354
3381
  var MODEL_CAPABILITIES = {
@@ -3375,6 +3402,8 @@ var PLUGIN_ID = PROVIDER_ID_PREFIX;
3375
3402
  var INTEGRATION_ID = PROVIDER_ID_PREFIX;
3376
3403
  var INTEGRATION_NAME = "Command Code";
3377
3404
  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').";
3405
+ 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.";
3406
+ 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";
3378
3407
  var LANES = [
3379
3408
  {
3380
3409
  id: "command-code-anthropic",
@@ -4889,6 +4918,29 @@ var commandCodeV2 = {
4889
4918
  };
4890
4919
  }
4891
4920
  });
4921
+ editor.add({
4922
+ name: "mpc_prices",
4923
+ description: MPC_TOOL_DESCRIPTION,
4924
+ input: {
4925
+ type: "object",
4926
+ properties: {
4927
+ arg: {
4928
+ type: "string",
4929
+ description: MPC_TOOL_ARG_DESCRIPTION
4930
+ }
4931
+ },
4932
+ additionalProperties: false
4933
+ },
4934
+ async execute(input) {
4935
+ const arg = typeof input?.arg === "string" ? input.arg : "";
4936
+ const key = await credentialKey(ctx);
4937
+ return {
4938
+ content: await runMpc(arg, {
4939
+ env: key ? { CMD_API_KEY: key } : undefined
4940
+ })
4941
+ };
4942
+ }
4943
+ });
4892
4944
  });
4893
4945
  let hasConnection = false;
4894
4946
  let connectionMs = 0;
@@ -5124,6 +5176,18 @@ var CommandCodePlugin = async (_input) => {
5124
5176
  env: { CMD_API_KEY: key }
5125
5177
  });
5126
5178
  }
5179
+ }),
5180
+ mpc_prices: tool({
5181
+ description: MPC_TOOL_DESCRIPTION,
5182
+ args: {
5183
+ arg: string2().optional().describe(MPC_TOOL_ARG_DESCRIPTION)
5184
+ },
5185
+ async execute(args) {
5186
+ const key = await resolveKey();
5187
+ return runMpc(args.arg ?? "", {
5188
+ env: { CMD_API_KEY: key }
5189
+ });
5190
+ }
5127
5191
  })
5128
5192
  }
5129
5193
  };
@@ -43,7 +43,7 @@ export interface ModelMeta {
43
43
  /** Cheapest plan that serves the model, from the generated catalog. */
44
44
  minPlan?: string | null;
45
45
  allowance?: number;
46
- /** Unbounded on the CommandCode side: `allowance` is 0 by convention. */
46
+ /** Unbounded on the CommandCode side: read `free`, not `allowance`, which may still carry a catalog figure. */
47
47
  free?: boolean;
48
48
  rates?: {
49
49
  input: number;
@@ -118,9 +118,11 @@ export interface Usage {
118
118
  export declare function money(value: number): string;
119
119
  /**
120
120
  * A monthly allowance as it appears on the panel. A free/unbounded model has no
121
- * budget to divide — mpc reports `free: true` and allowance 0 — so it reads `∞`
122
- * rather than the misleading `$0/mo` (0 is not a budget). Both sides call this,
123
- * so a free CommandCode model and a free OpenCode Go one render alike.
121
+ * budget to divide — mpc reports `free: true` — so it reads `∞` rather than the
122
+ * misleading `$0/mo` (0 is not a budget). `free` decides, so this holds whether
123
+ * the allowance is 0 (OpenCode Go's free rows) or a catalog figure
124
+ * (CommandCode's). Both sides call this, so a free CommandCode model and a free
125
+ * OpenCode Go one render alike.
124
126
  */
125
127
  export declare function allowanceLabel(limit: number, free?: boolean): string;
126
128
  /** Rate scale needs more precision than credit scale ($0.0036). */
package/dist/tui.js CHANGED
@@ -26,6 +26,11 @@ function cmduseCandidates(env = process.env) {
26
26
  return candidates.filter((bin) => Boolean(bin));
27
27
  }
28
28
  var CANDIDATES = cmduseCandidates();
29
+ function mpcCandidates(env = process.env) {
30
+ const candidates = [env.MPC_BIN, ...MPC_BIN_CANDIDATES];
31
+ return candidates.filter((bin) => Boolean(bin));
32
+ }
33
+ var MPC_CANDIDATES = mpcCandidates();
29
34
  function splitCliArgs(input) {
30
35
  const out = [];
31
36
  let cur = "";
@@ -78,7 +83,7 @@ function stderrTail(text) {
78
83
  return lines.slice(-3).join(`
79
84
  `).slice(-400);
80
85
  }
81
- function runOnce(bin, args, opts) {
86
+ function runOnce(bin, args, opts, name = "cmduse") {
82
87
  return new Promise((resolve, reject) => {
83
88
  const child = spawn(bin, args, {
84
89
  env: {
@@ -103,13 +108,13 @@ function runOnce(bin, args, opts) {
103
108
  opts.signal?.removeEventListener("abort", onAbort);
104
109
  const stdout = Buffer.concat(out).toString("utf8");
105
110
  if (opts.signal?.aborted) {
106
- reject(new Error("cmduse invocation aborted"));
111
+ reject(new Error(`${name} invocation aborted`));
107
112
  } else if (code === 0) {
108
113
  resolve(stdout);
109
114
  } else {
110
115
  const why = signal ? `killed by ${signal}` : `exit ${code}`;
111
116
  const tail = stderrTail(Buffer.concat(err).toString("utf8"));
112
- reject(new Error(`cmduse ${args.join(" ")}: ${why}${tail ? `
117
+ reject(new Error(`${name} ${args.join(" ")}: ${why}${tail ? `
113
118
  ${tail}` : ""}`));
114
119
  }
115
120
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jeffreyjyz/opencode-command-code",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
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",