@jeffreyjyz/opencode-command-code 0.3.19 → 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/access.d.ts +2 -1
- package/dist/api.d.ts +0 -1
- package/dist/cli.d.ts +14 -0
- package/dist/constants/binaries.d.ts +6 -0
- package/dist/constants/cli.d.ts +17 -0
- package/dist/constants/endpoints.d.ts +6 -0
- package/dist/constants/gating.d.ts +22 -0
- package/dist/constants/models.d.ts +29 -0
- package/dist/constants/paths.d.ts +18 -0
- package/dist/constants/providers.d.ts +32 -0
- package/dist/constants/sidebar.d.ts +33 -0
- package/dist/constants/timing.d.ts +13 -0
- package/dist/gating.d.ts +1 -19
- package/dist/index.d.ts +2 -2
- package/dist/index.js +237 -77
- package/dist/provider/index.d.ts +0 -1
- package/dist/sidebar/data.d.ts +13 -10
- package/dist/sidebar/prefs.d.ts +5 -3
- package/dist/sidebar/rows.d.ts +17 -4
- package/dist/sidebar/windows.d.ts +0 -4
- package/dist/sidebar/zen.d.ts +0 -6
- package/dist/tui.d.ts +1 -1
- package/dist/tui.js +141 -71
- package/dist/v2.d.ts +2 -16
- package/package.json +2 -2
package/dist/sidebar/rows.d.ts
CHANGED
|
@@ -21,11 +21,11 @@ export type SidebarRow = [
|
|
|
21
21
|
export declare function pctTone(pct: number): Tone;
|
|
22
22
|
/**
|
|
23
23
|
* The sidebar is 42 columns wide with 2+2 padding, and our panel keeps one more
|
|
24
|
-
* column clear on the right, so a rendered row must stay within
|
|
25
|
-
* cannot fit are split across an indented continuation (`""` label)
|
|
26
|
-
* wrapping in the host's renderer, which is what made the panel
|
|
24
|
+
* column clear on the right, so a rendered row must stay within ROW_WIDTH.
|
|
25
|
+
* Rows that cannot fit are split across an indented continuation (`""` label)
|
|
26
|
+
* rather than wrapping in the host's renderer, which is what made the panel
|
|
27
|
+
* look broken.
|
|
27
28
|
*/
|
|
28
|
-
export declare const ROW_WIDTH = 37;
|
|
29
29
|
/** A horizontal rule between blocks. */
|
|
30
30
|
export declare function separator(): SidebarRow;
|
|
31
31
|
/** Truncate to `width`, ellipsis included. */
|
|
@@ -43,6 +43,8 @@ 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: read `free`, not `allowance`, which may still carry a catalog figure. */
|
|
47
|
+
free?: boolean;
|
|
46
48
|
rates?: {
|
|
47
49
|
input: number;
|
|
48
50
|
output: number;
|
|
@@ -67,6 +69,8 @@ export interface ModelMeta {
|
|
|
67
69
|
provider?: string;
|
|
68
70
|
plan?: string;
|
|
69
71
|
allowance?: number;
|
|
72
|
+
/** Unbounded on the OpenCode Go side: its allowance is 0 by convention. */
|
|
73
|
+
free?: boolean;
|
|
70
74
|
rates?: {
|
|
71
75
|
input: number;
|
|
72
76
|
output: number;
|
|
@@ -112,6 +116,15 @@ export interface Usage {
|
|
|
112
116
|
}
|
|
113
117
|
/** Money at credit scale: cents, but "$60" not "$60.00". */
|
|
114
118
|
export declare function money(value: number): string;
|
|
119
|
+
/**
|
|
120
|
+
* A monthly allowance as it appears on the panel. A free/unbounded model has no
|
|
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.
|
|
126
|
+
*/
|
|
127
|
+
export declare function allowanceLabel(limit: number, free?: boolean): string;
|
|
115
128
|
/** Rate scale needs more precision than credit scale ($0.0036). */
|
|
116
129
|
export declare function rate(value: number): string;
|
|
117
130
|
export declare function count(value: number): string;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/** 5-hour window length in seconds. Mirrors cmduse_core::FIVE_HOUR_SECS. */
|
|
2
|
-
export declare const FIVE_HOUR_SECS: number;
|
|
3
|
-
/** Weekly window length in seconds. Mirrors cmduse_core::WEEKLY_SECS. */
|
|
4
|
-
export declare const WEEKLY_SECS: number;
|
|
5
1
|
/**
|
|
6
2
|
* Percentage of a rolling window that has elapsed, or undefined when the
|
|
7
3
|
* window has not started (or has no reset time). `nowSecs` is whole seconds —
|
package/dist/sidebar/zen.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { type ModelMeta, type ModelUsage, type SidebarRow } from "./rows";
|
|
2
|
-
/** Go's documented per-model window shares of the monthly allowance. */
|
|
3
|
-
export declare const GO_WINDOW_SHARE: {
|
|
4
|
-
readonly fiveHour: 0.2;
|
|
5
|
-
readonly weekly: 0.5;
|
|
6
|
-
readonly month: 1;
|
|
7
|
-
};
|
|
8
2
|
export interface ZenTotals {
|
|
9
3
|
requests: number;
|
|
10
4
|
cost: number;
|
package/dist/tui.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ interface V1Api {
|
|
|
33
33
|
export declare const tui: (api: V1Api) => Promise<void>;
|
|
34
34
|
export declare const commandCodeTui: TuiPluginNs.Definition;
|
|
35
35
|
declare const _default: {
|
|
36
|
-
tui: (api: V1Api) => Promise<void>;
|
|
37
36
|
id: string;
|
|
38
37
|
setup: (context: TuiPluginNs.Context) => Promise<TuiPluginNs.Cleanup | void> | TuiPluginNs.Cleanup | void;
|
|
38
|
+
tui: typeof tui;
|
|
39
39
|
};
|
|
40
40
|
export default _default;
|
package/dist/tui.js
CHANGED
|
@@ -4,13 +4,33 @@ import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
|
4
4
|
|
|
5
5
|
// src/cli.ts
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
|
+
|
|
8
|
+
// src/constants/binaries.ts
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
var CMDUSE_BIN_CANDIDATES = ["cmduse", "/opt/homebrew/bin/cmduse", "/usr/local/bin/cmduse"];
|
|
12
|
+
var MPC_BIN_CANDIDATES = ["mpc", join(homedir(), ".bun/bin/mpc"), "/opt/homebrew/bin/mpc"];
|
|
13
|
+
var OCUSE_BIN_CANDIDATES = ["ocuse", "/opt/homebrew/bin/ocuse", "/usr/local/bin/ocuse"];
|
|
14
|
+
|
|
15
|
+
// src/constants/cli.ts
|
|
16
|
+
var DEFAULT_ARGS = ["-1", "--plain"];
|
|
17
|
+
var SUBCOMMANDS = new Set(["plans", "models", "daily", "hourly", "model", "session", "statusline", "config"]);
|
|
18
|
+
var SPAWN_OPTIONS = {
|
|
19
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
20
|
+
detached: true
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/cli.ts
|
|
7
24
|
function cmduseCandidates(env = process.env) {
|
|
8
|
-
const candidates = [env.CMDUSE_BIN,
|
|
25
|
+
const candidates = [env.CMDUSE_BIN, ...CMDUSE_BIN_CANDIDATES];
|
|
9
26
|
return candidates.filter((bin) => Boolean(bin));
|
|
10
27
|
}
|
|
11
28
|
var CANDIDATES = cmduseCandidates();
|
|
12
|
-
|
|
13
|
-
|
|
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();
|
|
14
34
|
function splitCliArgs(input) {
|
|
15
35
|
const out = [];
|
|
16
36
|
let cur = "";
|
|
@@ -52,7 +72,7 @@ function splitCliArgs(input) {
|
|
|
52
72
|
function buildCmduseArgs(arg) {
|
|
53
73
|
const tokens = splitCliArgs(arg);
|
|
54
74
|
if (!tokens.length)
|
|
55
|
-
return DEFAULT_ARGS;
|
|
75
|
+
return [...DEFAULT_ARGS];
|
|
56
76
|
if (tokens.some((t) => SUBCOMMANDS.has(t)))
|
|
57
77
|
return tokens;
|
|
58
78
|
return [...DEFAULT_ARGS, ...tokens];
|
|
@@ -63,7 +83,7 @@ function stderrTail(text) {
|
|
|
63
83
|
return lines.slice(-3).join(`
|
|
64
84
|
`).slice(-400);
|
|
65
85
|
}
|
|
66
|
-
function runOnce(bin, args, opts) {
|
|
86
|
+
function runOnce(bin, args, opts, name = "cmduse") {
|
|
67
87
|
return new Promise((resolve, reject) => {
|
|
68
88
|
const child = spawn(bin, args, {
|
|
69
89
|
env: {
|
|
@@ -88,13 +108,13 @@ function runOnce(bin, args, opts) {
|
|
|
88
108
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
89
109
|
const stdout = Buffer.concat(out).toString("utf8");
|
|
90
110
|
if (opts.signal?.aborted) {
|
|
91
|
-
reject(new Error(
|
|
111
|
+
reject(new Error(`${name} invocation aborted`));
|
|
92
112
|
} else if (code === 0) {
|
|
93
113
|
resolve(stdout);
|
|
94
114
|
} else {
|
|
95
115
|
const why = signal ? `killed by ${signal}` : `exit ${code}`;
|
|
96
116
|
const tail = stderrTail(Buffer.concat(err).toString("utf8"));
|
|
97
|
-
reject(new Error(
|
|
117
|
+
reject(new Error(`${name} ${args.join(" ")}: ${why}${tail ? `
|
|
98
118
|
${tail}` : ""}`));
|
|
99
119
|
}
|
|
100
120
|
});
|
|
@@ -118,6 +138,13 @@ async function runCmduse(arg, opts = {}) {
|
|
|
118
138
|
});
|
|
119
139
|
}
|
|
120
140
|
|
|
141
|
+
// src/constants/providers.ts
|
|
142
|
+
var PROVIDER_ID_PREFIX = "command-code";
|
|
143
|
+
var GO_PROVIDER_ID = "opencode-go";
|
|
144
|
+
var ZEN_PROVIDER_ID = "opencode";
|
|
145
|
+
var ZEN_PROVIDER_PREFIX = "opencode-zen";
|
|
146
|
+
var TUI_ID = "command-code.tui";
|
|
147
|
+
|
|
121
148
|
// src/sidebar/panel.tsx
|
|
122
149
|
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
123
150
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
@@ -451,7 +478,7 @@ var gating_default = {
|
|
|
451
478
|
}
|
|
452
479
|
};
|
|
453
480
|
|
|
454
|
-
// src/gating.ts
|
|
481
|
+
// src/constants/gating.ts
|
|
455
482
|
var MODEL_CATEGORIES = gating_default.categories;
|
|
456
483
|
var PLAN_RULES = gating_default.plans;
|
|
457
484
|
var HARD_BLOCKED = gating_default.hardBlocked;
|
|
@@ -459,6 +486,28 @@ var KNOWN_MODELS = gating_default.knownModels;
|
|
|
459
486
|
var MODEL_ALIASES = gating_default.aliases;
|
|
460
487
|
var GATE_EXTRACTED_AT = gating_default.extractedAt;
|
|
461
488
|
var GATE_CLI_VERSION = gating_default.cliVersion;
|
|
489
|
+
|
|
490
|
+
// src/constants/sidebar.ts
|
|
491
|
+
var ROW_WIDTH = 37;
|
|
492
|
+
var TIER_DISPLAY = {
|
|
493
|
+
opensource: "open source",
|
|
494
|
+
premium: "premium"
|
|
495
|
+
};
|
|
496
|
+
var OC_PLANS = ["go", "go-plus"];
|
|
497
|
+
var OC_PLAN_DEFAULT = "go";
|
|
498
|
+
var GO_WINDOW_SHARE = {
|
|
499
|
+
fiveHour: 0.2,
|
|
500
|
+
weekly: 0.5,
|
|
501
|
+
month: 1
|
|
502
|
+
};
|
|
503
|
+
var FIVE_HOUR_SECS = 5 * 3600;
|
|
504
|
+
var WEEKLY_SECS = 7 * 86400;
|
|
505
|
+
var PREF_DEFAULTS = {
|
|
506
|
+
colors: false,
|
|
507
|
+
ocPlan: OC_PLAN_DEFAULT
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/gating.ts
|
|
462
511
|
function findKnown(s) {
|
|
463
512
|
const k = s.toLowerCase();
|
|
464
513
|
return KNOWN_MODELS.find((m) => m.toLowerCase() === k);
|
|
@@ -475,8 +524,6 @@ function canonicalizeModelId(model) {
|
|
|
475
524
|
}
|
|
476
525
|
|
|
477
526
|
// src/sidebar/windows.ts
|
|
478
|
-
var FIVE_HOUR_SECS = 5 * 3600;
|
|
479
|
-
var WEEKLY_SECS = 7 * 86400;
|
|
480
527
|
function elapsedPct(resetAtMs, durSecs, nowSecs) {
|
|
481
528
|
if (typeof resetAtMs !== "number" || !Number.isFinite(resetAtMs))
|
|
482
529
|
return;
|
|
@@ -526,7 +573,6 @@ function pctTone(pct) {
|
|
|
526
573
|
return "warn";
|
|
527
574
|
return "ok";
|
|
528
575
|
}
|
|
529
|
-
var ROW_WIDTH = 37;
|
|
530
576
|
function separator() {
|
|
531
577
|
return ["\u2500".repeat(ROW_WIDTH), "", "muted"];
|
|
532
578
|
}
|
|
@@ -541,14 +587,13 @@ function parts(row) {
|
|
|
541
587
|
return [` ${row[1]}`, ""];
|
|
542
588
|
return [row[1] ? `${row[0]}: ` : row[0], row[1]];
|
|
543
589
|
}
|
|
544
|
-
var TIER_DISPLAY = {
|
|
545
|
-
opensource: "open source",
|
|
546
|
-
premium: "premium"
|
|
547
|
-
};
|
|
548
590
|
function money(value) {
|
|
549
591
|
const rounded = Math.round(value * 100) / 100;
|
|
550
592
|
return `$${Number.isInteger(rounded) ? rounded : rounded.toFixed(2)}`;
|
|
551
593
|
}
|
|
594
|
+
function allowanceLabel(limit, free) {
|
|
595
|
+
return free === true || limit <= 0 ? "\u221E" : `${money(limit)}/mo`;
|
|
596
|
+
}
|
|
552
597
|
function rate(value) {
|
|
553
598
|
if (value === 0)
|
|
554
599
|
return "$0";
|
|
@@ -656,7 +701,7 @@ function modelRows(meta, usage, session, fallbackName) {
|
|
|
656
701
|
else if (meta.minPlan)
|
|
657
702
|
rows.push(["Min plan", meta.minPlan, "base"]);
|
|
658
703
|
if (typeof meta.allowance === "number")
|
|
659
|
-
rows.push(["Allowance",
|
|
704
|
+
rows.push(["Allowance", allowanceLabel(meta.allowance, meta.free), "base"]);
|
|
660
705
|
if (meta.deal) {
|
|
661
706
|
const ends = meta.deal.ends?.match(/([A-Z][a-z]{2})[a-z]*\s+(\d{1,2})/);
|
|
662
707
|
rows.push(["Deal", ends ? `${meta.deal.badge} \xB7 ends ${ends[1]} ${ends[2]}` : meta.deal.badge, "ok"]);
|
|
@@ -790,41 +835,71 @@ function Panel(props) {
|
|
|
790
835
|
|
|
791
836
|
// src/sidebar/prefs.ts
|
|
792
837
|
import { readFileSync } from "fs";
|
|
793
|
-
import { homedir } from "os";
|
|
794
|
-
import { join } from "path";
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
838
|
+
import { homedir as homedir2 } from "os";
|
|
839
|
+
import { join as join2 } from "path";
|
|
840
|
+
|
|
841
|
+
// src/constants/paths.ts
|
|
842
|
+
var CACHE_DIR = "command-code";
|
|
843
|
+
function ccCatalogFile(plan) {
|
|
844
|
+
return `cc-catalog-${plan}.json`;
|
|
845
|
+
}
|
|
846
|
+
var OPENCODE_DIR = "opencode";
|
|
847
|
+
var OPENCODE_DB_FILE = "opencode.db";
|
|
848
|
+
var PREFS_FILE = "command-code.json";
|
|
849
|
+
|
|
850
|
+
// src/constants/timing.ts
|
|
851
|
+
var POLL_MS = 5000;
|
|
852
|
+
var CHILD_TIMEOUT_MS = 30000;
|
|
853
|
+
var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
854
|
+
var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
855
|
+
var PREFS_CACHE_MS = 5000;
|
|
856
|
+
|
|
857
|
+
// src/sidebar/prefs.ts
|
|
799
858
|
var cached;
|
|
800
859
|
function prefsPath(env = process.env) {
|
|
801
|
-
const base = env.XDG_CONFIG_HOME ??
|
|
802
|
-
return
|
|
860
|
+
const base = env.XDG_CONFIG_HOME ?? join2(homedir2(), ".config");
|
|
861
|
+
return join2(base, OPENCODE_DIR, PREFS_FILE);
|
|
862
|
+
}
|
|
863
|
+
function planFrom(value, source) {
|
|
864
|
+
if (value === undefined)
|
|
865
|
+
return;
|
|
866
|
+
if (typeof value === "string" && OC_PLANS.includes(value)) {
|
|
867
|
+
return value;
|
|
868
|
+
}
|
|
869
|
+
console.warn(`[command-code] ignoring unknown OpenCode plan ${JSON.stringify(value)} (${source}); using "${OC_PLAN_DEFAULT}"`);
|
|
870
|
+
return;
|
|
803
871
|
}
|
|
804
872
|
function parsePrefs(raw) {
|
|
805
|
-
const
|
|
873
|
+
const obj = raw;
|
|
806
874
|
return {
|
|
807
|
-
colors:
|
|
875
|
+
colors: obj?.colors === true,
|
|
876
|
+
ocPlan: planFrom(obj?.ocPlan, "file") ?? OC_PLAN_DEFAULT
|
|
808
877
|
};
|
|
809
878
|
}
|
|
810
879
|
function loadPrefs(env = process.env, now = Date.now()) {
|
|
811
|
-
if (cached && now - cached.at <
|
|
880
|
+
if (cached && now - cached.at < PREFS_CACHE_MS)
|
|
812
881
|
return cached.prefs;
|
|
882
|
+
let prefs = PREF_DEFAULTS;
|
|
883
|
+
try {
|
|
884
|
+
prefs = parsePrefs(JSON.parse(readFileSync(prefsPath(env), "utf8")));
|
|
885
|
+
} catch {}
|
|
813
886
|
const flag = env.CMD_COLORS;
|
|
814
|
-
let prefs = DEFAULTS;
|
|
815
887
|
if (flag === "1" || flag === "true")
|
|
816
888
|
prefs = {
|
|
889
|
+
...prefs,
|
|
817
890
|
colors: true
|
|
818
891
|
};
|
|
819
892
|
else if (flag === "0" || flag === "false")
|
|
820
893
|
prefs = {
|
|
894
|
+
...prefs,
|
|
821
895
|
colors: false
|
|
822
896
|
};
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
897
|
+
const envPlan = planFrom(env.CMD_OC_PLAN, "CMD_OC_PLAN");
|
|
898
|
+
if (envPlan !== undefined)
|
|
899
|
+
prefs = {
|
|
900
|
+
...prefs,
|
|
901
|
+
ocPlan: envPlan
|
|
902
|
+
};
|
|
828
903
|
cached = {
|
|
829
904
|
at: now,
|
|
830
905
|
prefs
|
|
@@ -1950,15 +2025,10 @@ function minPlan(id) {
|
|
|
1950
2025
|
// src/sidebar/data.ts
|
|
1951
2026
|
import { spawn as spawn2 } from "child_process";
|
|
1952
2027
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1953
|
-
import { homedir as
|
|
1954
|
-
import { dirname, join as
|
|
2028
|
+
import { homedir as homedir3 } from "os";
|
|
2029
|
+
import { dirname, join as join3 } from "path";
|
|
1955
2030
|
|
|
1956
2031
|
// src/sidebar/zen.ts
|
|
1957
|
-
var GO_WINDOW_SHARE = {
|
|
1958
|
-
fiveHour: 0.2,
|
|
1959
|
-
weekly: 0.5,
|
|
1960
|
-
month: 1
|
|
1961
|
-
};
|
|
1962
2032
|
function totals(value) {
|
|
1963
2033
|
const v = value ?? {};
|
|
1964
2034
|
const num = (key) => typeof v[key] === "number" ? v[key] : 0;
|
|
@@ -2035,7 +2105,7 @@ function zenRows(usage, meta, session, fallbackName) {
|
|
|
2035
2105
|
if (!meta)
|
|
2036
2106
|
return rows;
|
|
2037
2107
|
if (typeof limit === "number") {
|
|
2038
|
-
rows.push(["Allowance",
|
|
2108
|
+
rows.push(["Allowance", allowanceLabel(limit, oc?.free), "base"]);
|
|
2039
2109
|
}
|
|
2040
2110
|
if (oc?.rates) {
|
|
2041
2111
|
const cache = [`cache read ${rate(oc.rates.cacheRead)}`];
|
|
@@ -2055,14 +2125,8 @@ function zenRows(usage, meta, session, fallbackName) {
|
|
|
2055
2125
|
|
|
2056
2126
|
// src/sidebar/data.ts
|
|
2057
2127
|
var CMDUSE = cmduseCandidates();
|
|
2058
|
-
var MPC =
|
|
2059
|
-
var OCUSE = [process.env.OCUSE_BIN,
|
|
2060
|
-
var CACHE_TTL_MS = 60 * 60 * 1000;
|
|
2061
|
-
var SPAWN_OPTIONS = {
|
|
2062
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
2063
|
-
detached: true
|
|
2064
|
-
};
|
|
2065
|
-
var CHILD_TIMEOUT_MS = 30000;
|
|
2128
|
+
var MPC = MPC_BIN_CANDIDATES;
|
|
2129
|
+
var OCUSE = [process.env.OCUSE_BIN, ...OCUSE_BIN_CANDIDATES].filter((bin) => Boolean(bin));
|
|
2066
2130
|
function run(bin, args) {
|
|
2067
2131
|
return new Promise((resolve, reject) => {
|
|
2068
2132
|
const child = spawn2(bin, args, SPAWN_OPTIONS);
|
|
@@ -2147,6 +2211,7 @@ function parseMpcJson(text) {
|
|
|
2147
2211
|
key: row.key ?? modelKey(row.name),
|
|
2148
2212
|
name: row.name,
|
|
2149
2213
|
allowance: cc?.allowance,
|
|
2214
|
+
free: cc?.free === true ? true : undefined,
|
|
2150
2215
|
rates: cc?.pricing,
|
|
2151
2216
|
intelligence: cc?.ability ?? undefined,
|
|
2152
2217
|
tps: cc?.tps ?? undefined,
|
|
@@ -2158,6 +2223,7 @@ function parseMpcJson(text) {
|
|
|
2158
2223
|
provider: oc.provider,
|
|
2159
2224
|
plan: oc.plan,
|
|
2160
2225
|
allowance: oc.allowance,
|
|
2226
|
+
free: oc.free === true ? true : undefined,
|
|
2161
2227
|
rates: oc.pricing,
|
|
2162
2228
|
ability: oc.ability ?? undefined,
|
|
2163
2229
|
tps: oc.tps ?? undefined
|
|
@@ -2168,20 +2234,26 @@ function parseMpcJson(text) {
|
|
|
2168
2234
|
}
|
|
2169
2235
|
return meta;
|
|
2170
2236
|
}
|
|
2171
|
-
function cachePath() {
|
|
2172
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
2173
|
-
return
|
|
2237
|
+
function cachePath(plan) {
|
|
2238
|
+
const base = process.env.XDG_CACHE_HOME ?? join3(homedir3(), ".cache");
|
|
2239
|
+
return join3(base, CACHE_DIR, ccCatalogFile(plan));
|
|
2174
2240
|
}
|
|
2175
|
-
|
|
2241
|
+
function metaArgs(plan) {
|
|
2242
|
+
return ["--json", "--shape", "off", "--oc-plan", plan];
|
|
2243
|
+
}
|
|
2244
|
+
function zenArgs(plan) {
|
|
2245
|
+
return ["-1", "--json", "--plan", plan];
|
|
2246
|
+
}
|
|
2247
|
+
async function loadMeta(plan = OC_PLAN_DEFAULT) {
|
|
2176
2248
|
try {
|
|
2177
|
-
const cached = JSON.parse(await readFile(cachePath(), "utf8"));
|
|
2178
|
-
if (cached.fetchedAt && Date.now() - cached.fetchedAt <
|
|
2249
|
+
const cached = JSON.parse(await readFile(cachePath(plan), "utf8"));
|
|
2250
|
+
if (cached.fetchedAt && Date.now() - cached.fetchedAt < CATALOG_CACHE_TTL_MS) {
|
|
2179
2251
|
return new Map(cached.rows ?? []);
|
|
2180
2252
|
}
|
|
2181
2253
|
} catch {}
|
|
2182
|
-
const meta = parseMpcJson(await runFirst(MPC,
|
|
2254
|
+
const meta = parseMpcJson(await runFirst(MPC, metaArgs(plan)));
|
|
2183
2255
|
try {
|
|
2184
|
-
const path = cachePath();
|
|
2256
|
+
const path = cachePath(plan);
|
|
2185
2257
|
await mkdir(dirname(path), {
|
|
2186
2258
|
recursive: true
|
|
2187
2259
|
});
|
|
@@ -2195,19 +2267,19 @@ async function loadMeta() {
|
|
|
2195
2267
|
async function loadUsage() {
|
|
2196
2268
|
return requireSnapshot(parseUsageJson(await runFirst(CMDUSE, ["-1", "--json", "--plain"])));
|
|
2197
2269
|
}
|
|
2198
|
-
async function loadZen() {
|
|
2199
|
-
return parseZenJson(await runFirst(OCUSE,
|
|
2270
|
+
async function loadZen(plan = OC_PLAN_DEFAULT) {
|
|
2271
|
+
return parseZenJson(await runFirst(OCUSE, zenArgs(plan)));
|
|
2200
2272
|
}
|
|
2201
2273
|
|
|
2202
2274
|
// src/sidebar/usageDb.ts
|
|
2203
2275
|
import { Database } from "bun:sqlite";
|
|
2204
|
-
import { homedir as
|
|
2205
|
-
import { join as
|
|
2276
|
+
import { homedir as homedir4 } from "os";
|
|
2277
|
+
import { join as join4 } from "path";
|
|
2206
2278
|
function usageDbPath() {
|
|
2207
2279
|
if (process.env.OPENCODE_DB)
|
|
2208
2280
|
return process.env.OPENCODE_DB;
|
|
2209
|
-
const base = process.env.XDG_DATA_HOME ??
|
|
2210
|
-
return
|
|
2281
|
+
const base = process.env.XDG_DATA_HOME ?? join4(homedir4(), ".local", "share");
|
|
2282
|
+
return join4(base, OPENCODE_DIR, OPENCODE_DB_FILE);
|
|
2211
2283
|
}
|
|
2212
2284
|
function usage(value) {
|
|
2213
2285
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
@@ -2333,15 +2405,14 @@ function loadSessionUsage(sessionID, path = usageDbPath()) {
|
|
|
2333
2405
|
}
|
|
2334
2406
|
|
|
2335
2407
|
// src/sidebar/useRows.ts
|
|
2336
|
-
var POLL_MS = 5000;
|
|
2337
2408
|
function sessionKind(providerID) {
|
|
2338
2409
|
if (!providerID)
|
|
2339
2410
|
return "unknown";
|
|
2340
|
-
if (providerID.startsWith(
|
|
2411
|
+
if (providerID.startsWith(PROVIDER_ID_PREFIX))
|
|
2341
2412
|
return "ours";
|
|
2342
|
-
if (providerID.startsWith(
|
|
2413
|
+
if (providerID.startsWith(GO_PROVIDER_ID))
|
|
2343
2414
|
return "go";
|
|
2344
|
-
if (providerID ===
|
|
2415
|
+
if (providerID === ZEN_PROVIDER_ID || providerID.startsWith(ZEN_PROVIDER_PREFIX))
|
|
2345
2416
|
return "zen";
|
|
2346
2417
|
return "other";
|
|
2347
2418
|
}
|
|
@@ -2396,7 +2467,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2396
2467
|
const [sessionUsage, setSessionUsage] = createSignal();
|
|
2397
2468
|
const [wasOurs, setWasOurs] = createSignal(false);
|
|
2398
2469
|
const refreshMeta = () => {
|
|
2399
|
-
loadMeta().then((next) => {
|
|
2470
|
+
loadMeta(loadPrefs().ocPlan).then((next) => {
|
|
2400
2471
|
rememberMeta(next);
|
|
2401
2472
|
if (next.size > 0)
|
|
2402
2473
|
setMeta(next);
|
|
@@ -2431,7 +2502,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2431
2502
|
setUsage(usageRows(snapshot));
|
|
2432
2503
|
refreshModelUsage(activeModelId());
|
|
2433
2504
|
} else if (kind === "go" || kind === "zen") {
|
|
2434
|
-
setZen(await loadZen());
|
|
2505
|
+
setZen(await loadZen(loadPrefs().ocPlan));
|
|
2435
2506
|
}
|
|
2436
2507
|
} catch {} finally {
|
|
2437
2508
|
inFlight = false;
|
|
@@ -2481,7 +2552,6 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2481
2552
|
}
|
|
2482
2553
|
|
|
2483
2554
|
// src/tui.tsx
|
|
2484
|
-
var ID = "command-code.tui";
|
|
2485
2555
|
var themed = (colors) => loadPrefs().colors ? colors : mono(colors);
|
|
2486
2556
|
function PanelV1(props) {
|
|
2487
2557
|
const current = () => props.api.state.session.get(props.sessionID)?.model;
|
|
@@ -2520,7 +2590,7 @@ function PanelV2(props) {
|
|
|
2520
2590
|
});
|
|
2521
2591
|
}
|
|
2522
2592
|
var commandCodeTui = {
|
|
2523
|
-
id:
|
|
2593
|
+
id: TUI_ID,
|
|
2524
2594
|
setup(ctx) {
|
|
2525
2595
|
ctx.ui.slot({
|
|
2526
2596
|
append: "sidebar.content",
|
package/dist/v2.d.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import type { Plugin as PluginNs } from "@opencode/plugin";
|
|
2
|
+
import { type Lane } from "./constants/providers";
|
|
2
3
|
import { type CmdModel } from "./models";
|
|
3
|
-
export declare const PLUGIN_ID = "command-code";
|
|
4
|
-
export declare const PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
|
|
5
|
-
/** Shared integration backing both lanes' credentials (/connect + env). */
|
|
6
|
-
export declare const INTEGRATION_ID = "command-code";
|
|
7
|
-
export declare const INTEGRATION_NAME = "Command Code";
|
|
8
|
-
type Lane = {
|
|
9
|
-
id: "command-code-anthropic" | "command-code-openai";
|
|
10
|
-
name: string;
|
|
11
|
-
/** v2 runtime package (first-party, distinct from the v1 `npm` value). */
|
|
12
|
-
pkg: string;
|
|
13
|
-
/** Wire field reasoning must round-trip through on the openai-compatible
|
|
14
|
-
* lane; v2 spells the old `interleaved: {field}` as `compatibility`. */
|
|
15
|
-
reasoningField?: string;
|
|
16
|
-
};
|
|
17
|
-
export type { Lane };
|
|
18
|
-
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').";
|
|
19
4
|
/** API/known model → v2 Model.Info. Capabilities, variants and $/1M cost all
|
|
20
5
|
* come from the generated catalog (the listing API publishes none of them);
|
|
21
6
|
* only the open lane needs the reasoning round-trip field. */
|
|
@@ -51,3 +36,4 @@ export declare function credentialKey(ctx: CredentialContext): Promise<string |
|
|
|
51
36
|
* own rewrite list (@opentui/*, solid-js).
|
|
52
37
|
*/
|
|
53
38
|
export declare const commandCodeV2: PluginNs.Plugin;
|
|
39
|
+
export {};
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/bun": "latest",
|
|
27
|
-
"typescript": "^
|
|
27
|
+
"typescript": "^7",
|
|
28
28
|
"@opencode-ai/plugin": "*",
|
|
29
29
|
"@opencode/plugin": "^2.0.11",
|
|
30
30
|
"@ai-sdk/provider": "3.0.8"
|