@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 +14 -0
- package/dist/constants/cli.d.ts +5 -0
- package/dist/constants/providers.d.ts +4 -0
- package/dist/index.js +68 -4
- package/dist/sidebar/rows.d.ts +6 -4
- package/dist/tui.js +8 -3
- package/package.json +1 -1
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>;
|
package/dist/constants/cli.d.ts
CHANGED
|
@@ -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.
|
|
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(
|
|
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(
|
|
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
|
};
|
package/dist/sidebar/rows.d.ts
CHANGED
|
@@ -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
|
|
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`
|
|
122
|
-
*
|
|
123
|
-
*
|
|
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(
|
|
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(
|
|
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.
|
|
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",
|