@jeffreyjyz/opencode-command-code 0.3.7 → 0.3.9
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 +4 -0
- package/dist/index.js +11 -2
- package/dist/sidebar/panel.d.ts +50 -0
- package/dist/sidebar/prefs.d.ts +10 -0
- package/dist/sidebar/rows.d.ts +40 -1
- package/dist/sidebar/usageDb.d.ts +7 -3
- package/dist/sidebar/useRows.d.ts +1 -0
- package/dist/tui.d.ts +1 -1
- package/dist/tui.js +350 -179
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Fallback lookup paths: the opencode background service's PATH often lacks
|
|
2
|
+
* the homebrew prefix, so the bare name alone is not enough. `CMDUSE_BIN` wins
|
|
3
|
+
* when set — the dev-build hook the sibling cmduse consumers (mpc) honour too. */
|
|
4
|
+
export declare function cmduseCandidates(env?: Record<string, string | undefined>): string[];
|
|
1
5
|
/** Shell-words split: whitespace-separated, with single/double quoting and
|
|
2
6
|
* backslash escapes ("--tz +05:30", arg='a b'). Unterminated quotes close at
|
|
3
7
|
* end of input. */
|
package/dist/index.js
CHANGED
|
@@ -2457,7 +2457,16 @@ async function providerModels(key) {
|
|
|
2457
2457
|
|
|
2458
2458
|
// src/cli.ts
|
|
2459
2459
|
import { spawn } from "child_process";
|
|
2460
|
-
|
|
2460
|
+
function cmduseCandidates(env = process.env) {
|
|
2461
|
+
const candidates = [
|
|
2462
|
+
env.CMDUSE_BIN,
|
|
2463
|
+
"cmduse",
|
|
2464
|
+
"/opt/homebrew/bin/cmduse",
|
|
2465
|
+
"/usr/local/bin/cmduse"
|
|
2466
|
+
];
|
|
2467
|
+
return candidates.filter((bin) => Boolean(bin));
|
|
2468
|
+
}
|
|
2469
|
+
var CANDIDATES = cmduseCandidates();
|
|
2461
2470
|
var DEFAULT_ARGS = ["-1", "--plain"];
|
|
2462
2471
|
var SUBCOMMANDS = new Set([
|
|
2463
2472
|
"plans",
|
|
@@ -4054,7 +4063,7 @@ var commandCodeV2 = {
|
|
|
4054
4063
|
// package.json
|
|
4055
4064
|
var package_default = {
|
|
4056
4065
|
name: "@jeffreyjyz/opencode-command-code",
|
|
4057
|
-
version: "0.3.
|
|
4066
|
+
version: "0.3.9",
|
|
4058
4067
|
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.",
|
|
4059
4068
|
type: "module",
|
|
4060
4069
|
main: "dist/index.js",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import { RGBA } from "@opentui/core";
|
|
3
|
+
import { type SidebarRow, type Tone } from "./rows";
|
|
4
|
+
/** One colour per row tone, plus the two label colours (plain rows, headlines). */
|
|
5
|
+
export type PanelColors = Readonly<Record<Tone, RGBA>> & {
|
|
6
|
+
readonly label: RGBA;
|
|
7
|
+
readonly headline: RGBA;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* The slice of the v2 host theme the panel reads. `@opencode/theme` is not a
|
|
11
|
+
* dependency here (the host rewrites its own module instances in), so the shape
|
|
12
|
+
* is declared structurally — and read defensively, since the theme package has
|
|
13
|
+
* renamed tokens before (`text.default` -> `text.base`).
|
|
14
|
+
*/
|
|
15
|
+
export interface HostTheme {
|
|
16
|
+
readonly text: {
|
|
17
|
+
readonly base: RGBA;
|
|
18
|
+
readonly muted: RGBA;
|
|
19
|
+
readonly feedback: Readonly<Record<"success" | "warning" | "error" | "info", {
|
|
20
|
+
readonly base: RGBA;
|
|
21
|
+
}>>;
|
|
22
|
+
};
|
|
23
|
+
readonly hue?: {
|
|
24
|
+
readonly accent?: Readonly<Record<number, RGBA>>;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* v2 host theme -> panel colours. `mode` matters for one thing: the accent hue
|
|
29
|
+
* is a usable purple in dark themes (#9d7cd8) but a peach in light ones, so the
|
|
30
|
+
* headline shade is only taken from it in dark mode. A lighter cyan was tried
|
|
31
|
+
* first and failed: terminals fold one-step-apart cyans onto the same ANSI
|
|
32
|
+
* colour, so the headline rows read identical to the rest.
|
|
33
|
+
*/
|
|
34
|
+
export declare function hostColors(theme: HostTheme, mode?: "dark" | "light"): PanelColors;
|
|
35
|
+
/** v1 hosts expose only a text/muted pair, so the semantic roles are literals. */
|
|
36
|
+
export declare function legacyColors(theme: {
|
|
37
|
+
text: RGBA;
|
|
38
|
+
textMuted: RGBA;
|
|
39
|
+
}): PanelColors;
|
|
40
|
+
/**
|
|
41
|
+
* Colours off: every tone renders as plain text, so only bold and dim carry
|
|
42
|
+
* structure. Headlines lose their accent, status rows their green/amber/red —
|
|
43
|
+
* which is the point of the default.
|
|
44
|
+
*/
|
|
45
|
+
export declare function mono(colors: PanelColors): PanelColors;
|
|
46
|
+
/** Bold title, then one line per row. Renders nothing when there are no rows. */
|
|
47
|
+
export declare function Panel(props: {
|
|
48
|
+
rows: () => SidebarRow[];
|
|
49
|
+
colors: () => PanelColors;
|
|
50
|
+
}): import("solid-js").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Prefs {
|
|
2
|
+
colors: boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare const DEFAULTS: Prefs;
|
|
5
|
+
/** Preferences live beside opencode's own config, XDG layout honoured. */
|
|
6
|
+
export declare function prefsPath(env?: NodeJS.ProcessEnv): string;
|
|
7
|
+
/** Only a literal `true` enables colour, so a typo cannot turn the panel on. */
|
|
8
|
+
export declare function parsePrefs(raw: unknown): Prefs;
|
|
9
|
+
/** Env override, then the file, then the default. Cached for `CACHE_MS`. */
|
|
10
|
+
export declare function loadPrefs(env?: NodeJS.ProcessEnv, now?: number): Prefs;
|
package/dist/sidebar/rows.d.ts
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
import { type Category } from "../gating";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Colour role for a row's *value*, resolved against the host theme by the
|
|
4
|
+
* panel: `ok` / `warn` / `crit` are headroom (green / amber / red), `strong` is
|
|
5
|
+
* plain text in bold (the model name, the way mpc bolds its MODEL column),
|
|
6
|
+
* `muted` is for continuations and rules, `base` is plain text. Labels are
|
|
7
|
+
* coloured separately: the panel draws them in the theme's info hue.
|
|
8
|
+
*/
|
|
9
|
+
export type Tone = "base" | "muted" | "strong" | "ok" | "warn" | "crit";
|
|
10
|
+
/**
|
|
11
|
+
* A row. `headline` marks the two rows whose label names the thing the panel is
|
|
12
|
+
* about (the plan, the active model); the panel colours their labels apart.
|
|
13
|
+
*/
|
|
14
|
+
export type SidebarRow = [
|
|
15
|
+
label: string,
|
|
16
|
+
value: string,
|
|
17
|
+
tone?: Tone,
|
|
18
|
+
headline?: true
|
|
19
|
+
];
|
|
20
|
+
/** Headroom severity: comfortable to 70%, tight to 90%, over after that. */
|
|
21
|
+
export declare function pctTone(pct: number): Tone;
|
|
22
|
+
/**
|
|
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 this. Rows that
|
|
25
|
+
* cannot fit are split across an indented continuation (`""` label) rather than
|
|
26
|
+
* wrapping in the host's renderer, which is what made the panel look broken.
|
|
27
|
+
*/
|
|
28
|
+
export declare const ROW_WIDTH = 37;
|
|
29
|
+
/** A horizontal rule between blocks. */
|
|
30
|
+
export declare function separator(): SidebarRow;
|
|
31
|
+
/** Truncate to `width`, ellipsis included. */
|
|
32
|
+
export declare function clip(text: string, width?: number): string;
|
|
33
|
+
/** Value budget for a labelled row: the panel draws `label: value`. */
|
|
34
|
+
export declare function valueWidth(label: string): number;
|
|
35
|
+
/** How the panel splits a row: a muted label (or banner) and a toned value. */
|
|
36
|
+
export declare function parts(row: SidebarRow): [label: string, value: string];
|
|
37
|
+
/** Rendered width of a row, as the panel draws it. */
|
|
38
|
+
export declare function rowWidth(row: SidebarRow): number;
|
|
3
39
|
export interface ModelMeta {
|
|
4
40
|
key: string;
|
|
5
41
|
name: string;
|
|
@@ -30,6 +66,9 @@ export interface Usage {
|
|
|
30
66
|
plan?: string;
|
|
31
67
|
monthlyCap?: number;
|
|
32
68
|
monthlyCredits?: number;
|
|
69
|
+
/** Billing-period bounds in epoch ms (cmduse 0.7.2+); absent on older builds. */
|
|
70
|
+
periodStartAt?: number;
|
|
71
|
+
periodEndAt?: number;
|
|
33
72
|
fiveHour?: WindowUsage;
|
|
34
73
|
weekly?: WindowUsage;
|
|
35
74
|
periodEnd?: string;
|
|
@@ -3,10 +3,14 @@ import { type ModelUsage } from "./rows";
|
|
|
3
3
|
export declare function usageDbPath(): string;
|
|
4
4
|
/**
|
|
5
5
|
* Start of the billing period a cmduse snapshot reports, as epoch ms. cmduse
|
|
6
|
-
* publishes
|
|
7
|
-
*
|
|
6
|
+
* 0.7.2+ publishes `periodStartAt` directly; older builds publish only
|
|
7
|
+
* `periodEnd` (YYYY-MM-DD), so the start is that date one calendar month back.
|
|
8
|
+
* Falls back to the last 30 days when neither is usable.
|
|
8
9
|
*/
|
|
9
|
-
export declare function periodStart(
|
|
10
|
+
export declare function periodStart(usage: {
|
|
11
|
+
periodStartAt?: number;
|
|
12
|
+
periodEnd?: string;
|
|
13
|
+
}, now?: number): number;
|
|
10
14
|
/**
|
|
11
15
|
* Requests and cost for one model since `sinceMs`. Null when the store is
|
|
12
16
|
* missing or unreadable, so callers can fall back to omitting the row.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useRows(activeModelId: () => string | undefined, active: () => boolean): import("solid-js").Accessor<import("./rows").SidebarRow[]>;
|
package/dist/tui.d.ts
CHANGED
package/dist/tui.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/tui.tsx
|
|
3
|
-
import {
|
|
4
|
-
import { setProp as _$setProp } from "@opentui/solid";
|
|
5
|
-
import { effect as _$effect } from "@opentui/solid";
|
|
6
|
-
import { insert as _$insert } from "@opentui/solid";
|
|
7
|
-
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
8
|
-
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
9
|
-
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
10
|
-
import { createElement as _$createElement } from "@opentui/solid";
|
|
11
|
-
import { For, Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js";
|
|
3
|
+
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
12
4
|
|
|
13
5
|
// src/cli.ts
|
|
14
6
|
import { spawn } from "child_process";
|
|
15
|
-
|
|
7
|
+
function cmduseCandidates(env = process.env) {
|
|
8
|
+
const candidates = [env.CMDUSE_BIN, "cmduse", "/opt/homebrew/bin/cmduse", "/usr/local/bin/cmduse"];
|
|
9
|
+
return candidates.filter((bin) => Boolean(bin));
|
|
10
|
+
}
|
|
11
|
+
var CANDIDATES = cmduseCandidates();
|
|
16
12
|
var DEFAULT_ARGS = ["-1", "--plain"];
|
|
17
13
|
var SUBCOMMANDS = new Set(["plans", "models", "daily", "hourly", "model", "session", "statusline", "config"]);
|
|
18
14
|
function splitCliArgs(input) {
|
|
@@ -122,11 +118,18 @@ async function runCmduse(arg, opts = {}) {
|
|
|
122
118
|
});
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
// src/sidebar/
|
|
126
|
-
import {
|
|
127
|
-
import {
|
|
128
|
-
import {
|
|
129
|
-
import {
|
|
121
|
+
// src/sidebar/panel.tsx
|
|
122
|
+
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
123
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
124
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
125
|
+
import { spread as _$spread } from "@opentui/solid";
|
|
126
|
+
import { mergeProps as _$mergeProps } from "@opentui/solid";
|
|
127
|
+
import { setProp as _$setProp } from "@opentui/solid";
|
|
128
|
+
import { effect as _$effect } from "@opentui/solid";
|
|
129
|
+
import { insert as _$insert } from "@opentui/solid";
|
|
130
|
+
import { createElement as _$createElement } from "@opentui/solid";
|
|
131
|
+
import { RGBA } from "@opentui/core";
|
|
132
|
+
import { For, Show } from "solid-js";
|
|
130
133
|
// ../core/gating.json
|
|
131
134
|
var gating_default = {
|
|
132
135
|
extractedAt: "2026-09-27T06:27:06.878Z",
|
|
@@ -468,6 +471,28 @@ function elapsedLabel(resetAtMs, durSecs, nowSecs) {
|
|
|
468
471
|
}
|
|
469
472
|
|
|
470
473
|
// src/sidebar/rows.ts
|
|
474
|
+
function pctTone(pct) {
|
|
475
|
+
if (pct >= 90)
|
|
476
|
+
return "crit";
|
|
477
|
+
if (pct >= 70)
|
|
478
|
+
return "warn";
|
|
479
|
+
return "ok";
|
|
480
|
+
}
|
|
481
|
+
var ROW_WIDTH = 37;
|
|
482
|
+
function separator() {
|
|
483
|
+
return ["\u2500".repeat(ROW_WIDTH), "", "muted"];
|
|
484
|
+
}
|
|
485
|
+
function clip(text, width = ROW_WIDTH) {
|
|
486
|
+
return text.length <= width ? text : `${text.slice(0, width - 1)}\u2026`;
|
|
487
|
+
}
|
|
488
|
+
function valueWidth(label) {
|
|
489
|
+
return ROW_WIDTH - label.length - 2;
|
|
490
|
+
}
|
|
491
|
+
function parts(row) {
|
|
492
|
+
if (row[0] === "")
|
|
493
|
+
return [` ${row[1]}`, ""];
|
|
494
|
+
return [row[1] ? `${row[0]}: ` : row[0], row[1]];
|
|
495
|
+
}
|
|
471
496
|
var TIER_DISPLAY = {
|
|
472
497
|
opensource: "open source",
|
|
473
498
|
premium: "premium"
|
|
@@ -503,15 +528,30 @@ function until(epoch, now = Date.now()) {
|
|
|
503
528
|
const days = Math.floor(hours / 24);
|
|
504
529
|
return hours % 24 ? `${days}d ${hours % 24}h` : `${days}d`;
|
|
505
530
|
}
|
|
506
|
-
function
|
|
531
|
+
function windowRows(label, w, now, durSecs) {
|
|
507
532
|
if (!w || typeof w.cap !== "number" || w.cap <= 0)
|
|
508
|
-
return;
|
|
533
|
+
return [];
|
|
509
534
|
const used = typeof w.used === "number" ? w.used : 0;
|
|
510
535
|
const pct = Math.round(used / w.cap * 100);
|
|
511
536
|
const elapsed = elapsedLabel(w.resetAt, durSecs, Math.floor(now / 1000));
|
|
512
537
|
const reset = until(w.resetAt, now);
|
|
513
|
-
const
|
|
514
|
-
return [label,
|
|
538
|
+
const detail = [...elapsed === undefined ? [] : [`${elapsed} elapsed`], ...reset ? [`resets ${reset}`] : []].join(" \xB7 ");
|
|
539
|
+
return [[label, `${money(used)}/${money(w.cap)} (${pct}%)`, pctTone(pct)], ...detail ? [["", detail, "muted"]] : []];
|
|
540
|
+
}
|
|
541
|
+
function monthlyDetail(usage, now) {
|
|
542
|
+
const {
|
|
543
|
+
periodStartAt,
|
|
544
|
+
periodEndAt
|
|
545
|
+
} = usage;
|
|
546
|
+
if (typeof periodStartAt !== "number" || typeof periodEndAt !== "number")
|
|
547
|
+
return;
|
|
548
|
+
const durSecs = Math.round((periodEndAt - periodStartAt) / 1000);
|
|
549
|
+
if (durSecs <= 0)
|
|
550
|
+
return;
|
|
551
|
+
const elapsed = elapsedLabel(periodEndAt, durSecs, Math.floor(now / 1000));
|
|
552
|
+
const reset = until(periodEndAt, now);
|
|
553
|
+
const parts = [...elapsed === undefined ? [] : [`${elapsed} elapsed`], ...reset ? [`resets ${reset}`] : []];
|
|
554
|
+
return parts.length ? parts.join(" \xB7 ") : undefined;
|
|
515
555
|
}
|
|
516
556
|
function usageRows(usage, now = Date.now()) {
|
|
517
557
|
if (!usage)
|
|
@@ -519,54 +559,54 @@ function usageRows(usage, now = Date.now()) {
|
|
|
519
559
|
const rows = [];
|
|
520
560
|
if (usage.plan) {
|
|
521
561
|
const credits = typeof usage.monthlyCap === "number" ? ` \xB7 $${usage.monthlyCap}/mo credits` : "";
|
|
522
|
-
rows.push(["Plan", `${usage.plan}${credits}
|
|
562
|
+
rows.push(["Plan", `${usage.plan}${credits}`, "base", true]);
|
|
523
563
|
}
|
|
524
564
|
if (typeof usage.monthlyCap === "number" && typeof usage.monthlyCredits === "number") {
|
|
525
|
-
const used = usage.monthlyCap - usage.monthlyCredits;
|
|
565
|
+
const used = Math.min(usage.monthlyCap, Math.max(0, usage.monthlyCap - usage.monthlyCredits));
|
|
526
566
|
const pct = usage.monthlyCap > 0 ? Math.round(used / usage.monthlyCap * 100) : 0;
|
|
527
|
-
rows.push(["Monthly", `${money(used)} / ${money(usage.monthlyCap)} (${pct}%)
|
|
567
|
+
rows.push(["Monthly", `${money(used)} / ${money(usage.monthlyCap)} (${pct}%)`, pctTone(pct)]);
|
|
568
|
+
const detail = monthlyDetail(usage, now);
|
|
569
|
+
if (detail)
|
|
570
|
+
rows.push(["", detail, "muted"]);
|
|
528
571
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
rows.push(five);
|
|
532
|
-
const week = windowRow("Weekly", usage.weekly, now, WEEKLY_SECS);
|
|
533
|
-
if (week)
|
|
534
|
-
rows.push(week);
|
|
572
|
+
rows.push(...windowRows("5-hour", usage.fiveHour, now, FIVE_HOUR_SECS));
|
|
573
|
+
rows.push(...windowRows("Weekly", usage.weekly, now, WEEKLY_SECS));
|
|
535
574
|
if (typeof usage.requests === "number" || typeof usage.cost === "number") {
|
|
536
575
|
const parts = [];
|
|
537
576
|
if (typeof usage.requests === "number")
|
|
538
577
|
parts.push(`${count(usage.requests)} requests`);
|
|
539
578
|
if (typeof usage.cost === "number")
|
|
540
579
|
parts.push(money(usage.cost));
|
|
541
|
-
rows.push(["Period", parts.join(" \xB7 ")]);
|
|
580
|
+
rows.push(["Period", parts.join(" \xB7 "), "base"]);
|
|
542
581
|
}
|
|
543
582
|
return rows;
|
|
544
583
|
}
|
|
545
584
|
function modelRows(meta, usage) {
|
|
546
585
|
if (!meta)
|
|
547
586
|
return [];
|
|
548
|
-
const rows = [["Model", meta.name]];
|
|
587
|
+
const rows = [["Model", clip(meta.name, valueWidth("Model")), "strong", true]];
|
|
549
588
|
if (usage) {
|
|
550
589
|
const spent = usage.cost > 0 ? ` \xB7 ${money(usage.cost)}` : "";
|
|
551
|
-
rows.push(["Usage (this model)", `${count(usage.requests)} req${spent}
|
|
590
|
+
rows.push(["Usage (this model)", `${count(usage.requests)} req${spent}`, "base"]);
|
|
552
591
|
}
|
|
553
592
|
if (meta.tier)
|
|
554
|
-
rows.push(["Tier", TIER_DISPLAY[meta.tier]]);
|
|
593
|
+
rows.push(["Tier", TIER_DISPLAY[meta.tier], "base"]);
|
|
555
594
|
else if (meta.minPlan)
|
|
556
|
-
rows.push(["Min plan", meta.minPlan]);
|
|
595
|
+
rows.push(["Min plan", meta.minPlan, "base"]);
|
|
557
596
|
if (typeof meta.allowance === "number")
|
|
558
|
-
rows.push(["Allowance", `${money(meta.allowance)}/mo
|
|
597
|
+
rows.push(["Allowance", `${money(meta.allowance)}/mo`, "base"]);
|
|
559
598
|
if (meta.rates) {
|
|
560
|
-
const
|
|
599
|
+
const cache = [`cache read ${rate(meta.rates.cacheRead)}`];
|
|
561
600
|
if (typeof meta.rates.cacheWrite === "number" && meta.rates.cacheWrite > 0) {
|
|
562
|
-
|
|
601
|
+
cache.push(`write ${rate(meta.rates.cacheWrite)}`);
|
|
563
602
|
}
|
|
564
|
-
rows.push(["Rates",
|
|
603
|
+
rows.push(["Rates", `${rate(meta.rates.input)}/${rate(meta.rates.output)} in/out`, "base"]);
|
|
604
|
+
rows.push(["", cache.join(" \xB7 "), "muted"]);
|
|
565
605
|
}
|
|
566
606
|
if (typeof meta.intelligence === "number")
|
|
567
|
-
rows.push(["Intelligence", String(meta.intelligence)]);
|
|
607
|
+
rows.push(["Intelligence", String(meta.intelligence), "base"]);
|
|
568
608
|
if (typeof meta.tps === "number")
|
|
569
|
-
rows.push(["Tok/s", String(meta.tps)]);
|
|
609
|
+
rows.push(["Tok/s", String(meta.tps), "base"]);
|
|
570
610
|
return rows;
|
|
571
611
|
}
|
|
572
612
|
function modelKey(modelId) {
|
|
@@ -578,110 +618,157 @@ function tierFor(modelId) {
|
|
|
578
618
|
return MODEL_CATEGORIES[canonicalizeModelId(modelId)];
|
|
579
619
|
}
|
|
580
620
|
|
|
581
|
-
// src/sidebar/
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const err = [];
|
|
594
|
-
child.stdout?.on("data", (d) => out.push(d));
|
|
595
|
-
child.stderr?.on("data", (d) => err.push(d));
|
|
596
|
-
child.on("error", reject);
|
|
597
|
-
child.on("close", (code) => code === 0 ? resolve(Buffer.concat(out).toString("utf8")) : reject(new Error(`${bin} ${args.join(" ")}: exit ${code} ${Buffer.concat(err).toString("utf8").slice(-200)}`)));
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
async function runFirst(candidates, args) {
|
|
601
|
-
let last;
|
|
602
|
-
for (const bin of candidates) {
|
|
603
|
-
try {
|
|
604
|
-
return await run(bin, args);
|
|
605
|
-
} catch (e) {
|
|
606
|
-
last = e;
|
|
607
|
-
if (e?.code !== "ENOENT")
|
|
608
|
-
throw e;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
throw last instanceof Error ? last : new Error(`${candidates[0]} not found`);
|
|
621
|
+
// src/sidebar/panel.tsx
|
|
622
|
+
function hostColors(theme, mode = "dark") {
|
|
623
|
+
return {
|
|
624
|
+
base: theme.text.base,
|
|
625
|
+
muted: theme.text.muted,
|
|
626
|
+
strong: theme.text.base,
|
|
627
|
+
label: theme.text.feedback.info.base,
|
|
628
|
+
headline: mode === "dark" ? theme.hue?.accent?.[200] ?? theme.text.feedback.info.base : theme.text.feedback.info.base,
|
|
629
|
+
ok: theme.text.feedback.success.base,
|
|
630
|
+
warn: theme.text.feedback.warning.base,
|
|
631
|
+
crit: theme.text.feedback.error.base
|
|
632
|
+
};
|
|
612
633
|
}
|
|
613
|
-
function
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
};
|
|
634
|
+
function legacyColors(theme) {
|
|
635
|
+
return {
|
|
636
|
+
base: theme.text,
|
|
637
|
+
muted: theme.textMuted,
|
|
638
|
+
strong: theme.text,
|
|
639
|
+
label: RGBA.fromValues(0.34, 0.71, 0.76, 1),
|
|
640
|
+
headline: RGBA.fromValues(0.62, 0.49, 0.85, 1),
|
|
641
|
+
ok: RGBA.fromValues(0.5, 0.85, 0.56, 1),
|
|
642
|
+
warn: RGBA.fromValues(0.96, 0.65, 0.26, 1),
|
|
643
|
+
crit: RGBA.fromValues(0.88, 0.42, 0.46, 1)
|
|
624
644
|
};
|
|
625
|
-
|
|
645
|
+
}
|
|
646
|
+
function mono(colors) {
|
|
626
647
|
return {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
648
|
+
base: colors.base,
|
|
649
|
+
muted: colors.muted,
|
|
650
|
+
strong: colors.base,
|
|
651
|
+
label: colors.base,
|
|
652
|
+
headline: colors.base,
|
|
653
|
+
ok: colors.base,
|
|
654
|
+
warn: colors.base,
|
|
655
|
+
crit: colors.base
|
|
635
656
|
};
|
|
636
657
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
for (const row of body.rows ?? []) {
|
|
641
|
-
const cc = row.cc;
|
|
642
|
-
if (!cc || !row.name)
|
|
643
|
-
continue;
|
|
644
|
-
const entry = {
|
|
645
|
-
key: row.key ?? modelKey(row.name),
|
|
646
|
-
name: row.name,
|
|
647
|
-
allowance: cc.allowance,
|
|
648
|
-
rates: cc.pricing,
|
|
649
|
-
intelligence: cc.ability ?? undefined,
|
|
650
|
-
tps: cc.tps ?? undefined
|
|
651
|
-
};
|
|
652
|
-
meta.set(entry.key, entry);
|
|
653
|
-
meta.set(modelKey(row.name), entry);
|
|
658
|
+
var spanStyle = (fg) => ({
|
|
659
|
+
style: {
|
|
660
|
+
fg
|
|
654
661
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
662
|
+
});
|
|
663
|
+
var rowColors = (colors, row) => row[3] ? {
|
|
664
|
+
label: colors.headline,
|
|
665
|
+
value: colors.headline
|
|
666
|
+
} : {
|
|
667
|
+
label: colors.label,
|
|
668
|
+
value: colors[row[2] ?? "base"]
|
|
669
|
+
};
|
|
670
|
+
function Row(props) {
|
|
671
|
+
const [label, value] = parts(props.row);
|
|
672
|
+
const tone = props.row[2] ?? "base";
|
|
673
|
+
const fg = rowColors(props.colors, props.row);
|
|
674
|
+
if (!value) {
|
|
675
|
+
return (() => {
|
|
676
|
+
var _el$ = _$createElement("text");
|
|
677
|
+
_$insert(_el$, label);
|
|
678
|
+
_$effect((_$p) => _$setProp(_el$, "fg", props.colors.muted, _$p));
|
|
679
|
+
return _el$;
|
|
680
|
+
})();
|
|
681
|
+
}
|
|
682
|
+
return (() => {
|
|
683
|
+
var _el$2 = _$createElement("text"), _el$3 = _$createElement("span"), _el$4 = _$createElement("b"), _el$5 = _$createElement("span");
|
|
684
|
+
_$insertNode(_el$2, _el$3);
|
|
685
|
+
_$insertNode(_el$2, _el$5);
|
|
686
|
+
_$insertNode(_el$3, _el$4);
|
|
687
|
+
_$spread(_el$3, _$mergeProps(() => spanStyle(fg.label)), true);
|
|
688
|
+
_$insert(_el$4, label);
|
|
689
|
+
_$spread(_el$5, _$mergeProps(() => spanStyle(fg.value)), true);
|
|
690
|
+
_$insert(_el$5, tone === "strong" ? (() => {
|
|
691
|
+
var _el$6 = _$createElement("b");
|
|
692
|
+
_$insert(_el$6, value);
|
|
693
|
+
return _el$6;
|
|
694
|
+
})() : value);
|
|
695
|
+
return _el$2;
|
|
696
|
+
})();
|
|
660
697
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
698
|
+
function Panel(props) {
|
|
699
|
+
return _$createComponent(Show, {
|
|
700
|
+
get when() {
|
|
701
|
+
return props.rows().length > 0;
|
|
702
|
+
},
|
|
703
|
+
get children() {
|
|
704
|
+
var _el$7 = _$createElement("box"), _el$8 = _$createElement("text"), _el$9 = _$createElement("b");
|
|
705
|
+
_$insertNode(_el$7, _el$8);
|
|
706
|
+
_$insertNode(_el$8, _el$9);
|
|
707
|
+
_$insertNode(_el$9, _$createTextNode(`Command Code`));
|
|
708
|
+
_$insert(_el$7, _$createComponent(For, {
|
|
709
|
+
get each() {
|
|
710
|
+
return props.rows();
|
|
711
|
+
},
|
|
712
|
+
children: (row) => _$createComponent(Row, {
|
|
713
|
+
row,
|
|
714
|
+
get colors() {
|
|
715
|
+
return props.colors();
|
|
716
|
+
}
|
|
717
|
+
})
|
|
718
|
+
}), null);
|
|
719
|
+
_$effect((_$p) => _$setProp(_el$8, "fg", props.colors().base, _$p));
|
|
720
|
+
return _el$7;
|
|
666
721
|
}
|
|
667
|
-
}
|
|
668
|
-
const meta = parseMpcJson(await runFirst(MPC, ["--json"]));
|
|
669
|
-
try {
|
|
670
|
-
const path = cachePath();
|
|
671
|
-
await mkdir(dirname(path), {
|
|
672
|
-
recursive: true
|
|
673
|
-
});
|
|
674
|
-
await writeFile(path, JSON.stringify({
|
|
675
|
-
fetchedAt: Date.now(),
|
|
676
|
-
rows: [...meta]
|
|
677
|
-
}));
|
|
678
|
-
} catch {}
|
|
679
|
-
return meta;
|
|
722
|
+
});
|
|
680
723
|
}
|
|
681
|
-
|
|
682
|
-
|
|
724
|
+
|
|
725
|
+
// src/sidebar/prefs.ts
|
|
726
|
+
import { readFileSync } from "fs";
|
|
727
|
+
import { homedir } from "os";
|
|
728
|
+
import { join } from "path";
|
|
729
|
+
var DEFAULTS = {
|
|
730
|
+
colors: false
|
|
731
|
+
};
|
|
732
|
+
var CACHE_MS = 5000;
|
|
733
|
+
var cached;
|
|
734
|
+
function prefsPath(env = process.env) {
|
|
735
|
+
const base = env.XDG_CONFIG_HOME ?? join(homedir(), ".config");
|
|
736
|
+
return join(base, "opencode", "command-code.json");
|
|
737
|
+
}
|
|
738
|
+
function parsePrefs(raw) {
|
|
739
|
+
const value = raw?.colors;
|
|
740
|
+
return {
|
|
741
|
+
colors: value === true
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function loadPrefs(env = process.env, now = Date.now()) {
|
|
745
|
+
if (cached && now - cached.at < CACHE_MS)
|
|
746
|
+
return cached.prefs;
|
|
747
|
+
const flag = env.CMD_COLORS;
|
|
748
|
+
let prefs = DEFAULTS;
|
|
749
|
+
if (flag === "1" || flag === "true")
|
|
750
|
+
prefs = {
|
|
751
|
+
colors: true
|
|
752
|
+
};
|
|
753
|
+
else if (flag === "0" || flag === "false")
|
|
754
|
+
prefs = {
|
|
755
|
+
colors: false
|
|
756
|
+
};
|
|
757
|
+
else {
|
|
758
|
+
try {
|
|
759
|
+
prefs = parsePrefs(JSON.parse(readFileSync(prefsPath(env), "utf8")));
|
|
760
|
+
} catch {}
|
|
761
|
+
}
|
|
762
|
+
cached = {
|
|
763
|
+
at: now,
|
|
764
|
+
prefs
|
|
765
|
+
};
|
|
766
|
+
return prefs;
|
|
683
767
|
}
|
|
684
768
|
|
|
769
|
+
// src/sidebar/useRows.ts
|
|
770
|
+
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
|
|
771
|
+
|
|
685
772
|
// src/catalog.ts
|
|
686
773
|
var MODEL_CATALOG = {
|
|
687
774
|
"claude-fable-5": {
|
|
@@ -1742,18 +1829,130 @@ function minPlan(id) {
|
|
|
1742
1829
|
return MODEL_CATALOG[id]?.minPlan ?? null;
|
|
1743
1830
|
}
|
|
1744
1831
|
|
|
1832
|
+
// src/sidebar/data.ts
|
|
1833
|
+
import { spawn as spawn2 } from "child_process";
|
|
1834
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1835
|
+
import { homedir as homedir2 } from "os";
|
|
1836
|
+
import { dirname, join as join2 } from "path";
|
|
1837
|
+
var CMDUSE = cmduseCandidates();
|
|
1838
|
+
var MPC = ["mpc", join2(homedir2(), ".bun/bin/mpc"), "/opt/homebrew/bin/mpc"];
|
|
1839
|
+
var CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
1840
|
+
var SPAWN_OPTIONS = {
|
|
1841
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1842
|
+
detached: true
|
|
1843
|
+
};
|
|
1844
|
+
function run(bin, args) {
|
|
1845
|
+
return new Promise((resolve, reject) => {
|
|
1846
|
+
const child = spawn2(bin, args, SPAWN_OPTIONS);
|
|
1847
|
+
const out = [];
|
|
1848
|
+
const err = [];
|
|
1849
|
+
child.stdout?.on("data", (d) => out.push(d));
|
|
1850
|
+
child.stderr?.on("data", (d) => err.push(d));
|
|
1851
|
+
child.on("error", reject);
|
|
1852
|
+
child.on("close", (code) => code === 0 ? resolve(Buffer.concat(out).toString("utf8")) : reject(new Error(`${bin} ${args.join(" ")}: exit ${code} ${Buffer.concat(err).toString("utf8").slice(-200)}`)));
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
async function runFirst(candidates, args) {
|
|
1856
|
+
let last;
|
|
1857
|
+
for (const bin of candidates) {
|
|
1858
|
+
try {
|
|
1859
|
+
return await run(bin, args);
|
|
1860
|
+
} catch (e) {
|
|
1861
|
+
last = e;
|
|
1862
|
+
if (e?.code !== "ENOENT")
|
|
1863
|
+
throw e;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
throw last instanceof Error ? last : new Error(`${candidates[0]} not found`);
|
|
1867
|
+
}
|
|
1868
|
+
function parseUsageJson(text) {
|
|
1869
|
+
const raw = JSON.parse(text);
|
|
1870
|
+
const win = (v) => {
|
|
1871
|
+
if (!v || typeof v !== "object")
|
|
1872
|
+
return;
|
|
1873
|
+
const w = v;
|
|
1874
|
+
return {
|
|
1875
|
+
cap: typeof w.cap === "number" ? w.cap : undefined,
|
|
1876
|
+
used: typeof w.used === "number" ? w.used : undefined,
|
|
1877
|
+
resetAt: typeof w.resetAt === "number" ? w.resetAt : undefined
|
|
1878
|
+
};
|
|
1879
|
+
};
|
|
1880
|
+
const summary = raw.summary ?? {};
|
|
1881
|
+
return {
|
|
1882
|
+
plan: typeof raw.plan === "string" ? raw.plan : undefined,
|
|
1883
|
+
monthlyCap: typeof raw.monthlyCap === "number" ? raw.monthlyCap : undefined,
|
|
1884
|
+
monthlyCredits: typeof raw.monthlyCredits === "number" ? raw.monthlyCredits : undefined,
|
|
1885
|
+
fiveHour: win(raw.fiveHour),
|
|
1886
|
+
weekly: win(raw.weekly),
|
|
1887
|
+
periodEnd: typeof raw.periodEnd === "string" ? raw.periodEnd : undefined,
|
|
1888
|
+
periodStartAt: typeof raw.periodStartAt === "number" ? raw.periodStartAt : undefined,
|
|
1889
|
+
periodEndAt: typeof raw.periodEndAt === "number" ? raw.periodEndAt : undefined,
|
|
1890
|
+
requests: typeof summary.requests === "number" ? summary.requests : undefined,
|
|
1891
|
+
cost: typeof summary.cost === "number" ? summary.cost : undefined
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
function parseMpcJson(text) {
|
|
1895
|
+
const body = JSON.parse(text);
|
|
1896
|
+
const meta = new Map;
|
|
1897
|
+
for (const row of body.rows ?? []) {
|
|
1898
|
+
const cc = row.cc;
|
|
1899
|
+
if (!cc || !row.name)
|
|
1900
|
+
continue;
|
|
1901
|
+
const entry = {
|
|
1902
|
+
key: row.key ?? modelKey(row.name),
|
|
1903
|
+
name: row.name,
|
|
1904
|
+
allowance: cc.allowance,
|
|
1905
|
+
rates: cc.pricing,
|
|
1906
|
+
intelligence: cc.ability ?? undefined,
|
|
1907
|
+
tps: cc.tps ?? undefined
|
|
1908
|
+
};
|
|
1909
|
+
meta.set(entry.key, entry);
|
|
1910
|
+
meta.set(modelKey(row.name), entry);
|
|
1911
|
+
}
|
|
1912
|
+
return meta;
|
|
1913
|
+
}
|
|
1914
|
+
function cachePath() {
|
|
1915
|
+
const base = process.env.XDG_CACHE_HOME ?? join2(homedir2(), ".cache");
|
|
1916
|
+
return join2(base, "command-code", "cc-catalog.json");
|
|
1917
|
+
}
|
|
1918
|
+
async function loadMeta() {
|
|
1919
|
+
try {
|
|
1920
|
+
const cached = JSON.parse(await readFile(cachePath(), "utf8"));
|
|
1921
|
+
if (cached.fetchedAt && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
|
|
1922
|
+
return new Map(cached.rows ?? []);
|
|
1923
|
+
}
|
|
1924
|
+
} catch {}
|
|
1925
|
+
const meta = parseMpcJson(await runFirst(MPC, ["--json"]));
|
|
1926
|
+
try {
|
|
1927
|
+
const path = cachePath();
|
|
1928
|
+
await mkdir(dirname(path), {
|
|
1929
|
+
recursive: true
|
|
1930
|
+
});
|
|
1931
|
+
await writeFile(path, JSON.stringify({
|
|
1932
|
+
fetchedAt: Date.now(),
|
|
1933
|
+
rows: [...meta]
|
|
1934
|
+
}));
|
|
1935
|
+
} catch {}
|
|
1936
|
+
return meta;
|
|
1937
|
+
}
|
|
1938
|
+
async function loadUsage() {
|
|
1939
|
+
return parseUsageJson(await runFirst(CMDUSE, ["-1", "--json", "--plain"]));
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1745
1942
|
// src/sidebar/usageDb.ts
|
|
1746
1943
|
import { Database } from "bun:sqlite";
|
|
1747
|
-
import { homedir as
|
|
1748
|
-
import { join as
|
|
1944
|
+
import { homedir as homedir3 } from "os";
|
|
1945
|
+
import { join as join3 } from "path";
|
|
1749
1946
|
function usageDbPath() {
|
|
1750
1947
|
if (process.env.OPENCODE_DB)
|
|
1751
1948
|
return process.env.OPENCODE_DB;
|
|
1752
|
-
const base = process.env.XDG_DATA_HOME ??
|
|
1753
|
-
return
|
|
1949
|
+
const base = process.env.XDG_DATA_HOME ?? join3(homedir3(), ".local", "share");
|
|
1950
|
+
return join3(base, "opencode", "opencode.db");
|
|
1754
1951
|
}
|
|
1755
|
-
function periodStart(
|
|
1756
|
-
|
|
1952
|
+
function periodStart(usage, now = Date.now()) {
|
|
1953
|
+
if (typeof usage.periodStartAt === "number")
|
|
1954
|
+
return usage.periodStartAt;
|
|
1955
|
+
const end = usage.periodEnd ? Date.parse(`${usage.periodEnd}T23:59:59`) : Number.NaN;
|
|
1757
1956
|
if (!Number.isFinite(end))
|
|
1758
1957
|
return now - 30 * 24 * 60 * 60 * 1000;
|
|
1759
1958
|
const start = new Date(end);
|
|
@@ -1808,38 +2007,8 @@ function loadModelUsage(modelID, sinceMs, path = usageDbPath()) {
|
|
|
1808
2007
|
}
|
|
1809
2008
|
}
|
|
1810
2009
|
|
|
1811
|
-
// src/
|
|
1812
|
-
var ID = "command-code.tui";
|
|
2010
|
+
// src/sidebar/useRows.ts
|
|
1813
2011
|
var POLL_MS = 60000;
|
|
1814
|
-
function Panel(props) {
|
|
1815
|
-
return _$createComponent(Show, {
|
|
1816
|
-
get when() {
|
|
1817
|
-
return props.rows().length > 0;
|
|
1818
|
-
},
|
|
1819
|
-
get children() {
|
|
1820
|
-
var _el$ = _$createElement("box"), _el$2 = _$createElement("text"), _el$3 = _$createElement("b");
|
|
1821
|
-
_$insertNode(_el$, _el$2);
|
|
1822
|
-
_$insertNode(_el$2, _el$3);
|
|
1823
|
-
_$insertNode(_el$3, _$createTextNode(`Command Code`));
|
|
1824
|
-
_$insert(_el$, _$createComponent(For, {
|
|
1825
|
-
get each() {
|
|
1826
|
-
return props.rows();
|
|
1827
|
-
},
|
|
1828
|
-
children: (row) => (() => {
|
|
1829
|
-
var _el$5 = _$createElement("text");
|
|
1830
|
-
_$insert(_el$5, (() => {
|
|
1831
|
-
var _c$ = _$memo(() => !!row[1]);
|
|
1832
|
-
return () => _c$() ? `${row[0]}: ${row[1]}` : row[0];
|
|
1833
|
-
})());
|
|
1834
|
-
_$effect((_$p) => _$setProp(_el$5, "fg", props.muted(), _$p));
|
|
1835
|
-
return _el$5;
|
|
1836
|
-
})()
|
|
1837
|
-
}), null);
|
|
1838
|
-
_$effect((_$p) => _$setProp(_el$2, "fg", props.text(), _$p));
|
|
1839
|
-
return _el$;
|
|
1840
|
-
}
|
|
1841
|
-
});
|
|
1842
|
-
}
|
|
1843
2012
|
function useRows(activeModelId, active) {
|
|
1844
2013
|
const [usage, setUsage] = createSignal([]);
|
|
1845
2014
|
const [meta, setMeta] = createSignal(new Map);
|
|
@@ -1850,7 +2019,7 @@ function useRows(activeModelId, active) {
|
|
|
1850
2019
|
const snapshot = await loadUsage();
|
|
1851
2020
|
setUsage(usageRows(snapshot));
|
|
1852
2021
|
const id = activeModelId();
|
|
1853
|
-
setModelUsage(id ? loadModelUsage(id, periodStart(snapshot
|
|
2022
|
+
setModelUsage(id ? loadModelUsage(id, periodStart(snapshot)) ?? undefined : undefined);
|
|
1854
2023
|
} catch {}
|
|
1855
2024
|
};
|
|
1856
2025
|
createEffect(() => {
|
|
@@ -1870,17 +2039,20 @@ function useRows(activeModelId, active) {
|
|
|
1870
2039
|
tier: found.tier ?? (id ? tierFor(id) : undefined),
|
|
1871
2040
|
minPlan: id ? minPlan(id) : undefined
|
|
1872
2041
|
} : undefined;
|
|
1873
|
-
return [...usage(), ...modelRows(model, modelUsage())];
|
|
2042
|
+
return [...usage(), separator(), ...modelRows(model, modelUsage())];
|
|
1874
2043
|
});
|
|
1875
2044
|
}
|
|
2045
|
+
|
|
2046
|
+
// src/tui.tsx
|
|
2047
|
+
var ID = "command-code.tui";
|
|
1876
2048
|
var isOurs = (providerID) => Boolean(providerID?.startsWith("command-code"));
|
|
2049
|
+
var themed = (colors) => loadPrefs().colors ? colors : mono(colors);
|
|
1877
2050
|
function PanelV1(props) {
|
|
1878
2051
|
const current = () => props.api.state.session.get(props.sessionID)?.model;
|
|
1879
2052
|
const rows = useRows(() => current()?.id, () => isOurs(current()?.providerID));
|
|
1880
|
-
return _$
|
|
2053
|
+
return _$createComponent2(Panel, {
|
|
1881
2054
|
rows,
|
|
1882
|
-
|
|
1883
|
-
muted: () => props.api.theme.current.textMuted
|
|
2055
|
+
colors: () => themed(legacyColors(props.api.theme.current))
|
|
1884
2056
|
});
|
|
1885
2057
|
}
|
|
1886
2058
|
var tui = async (api) => {
|
|
@@ -1888,7 +2060,7 @@ var tui = async (api) => {
|
|
|
1888
2060
|
order: 200,
|
|
1889
2061
|
slots: {
|
|
1890
2062
|
sidebar_content(_ctx, props) {
|
|
1891
|
-
return _$
|
|
2063
|
+
return _$createComponent2(PanelV1, {
|
|
1892
2064
|
api,
|
|
1893
2065
|
get sessionID() {
|
|
1894
2066
|
return props.session_id;
|
|
@@ -1904,10 +2076,9 @@ function PanelV2(props) {
|
|
|
1904
2076
|
} = props;
|
|
1905
2077
|
const current = () => ctx.data.session.get(props.sessionID)?.model;
|
|
1906
2078
|
const rows = useRows(() => current()?.id, () => isOurs(current()?.providerID));
|
|
1907
|
-
return _$
|
|
2079
|
+
return _$createComponent2(Panel, {
|
|
1908
2080
|
rows,
|
|
1909
|
-
|
|
1910
|
-
muted: () => ctx.theme.text.subdued
|
|
2081
|
+
colors: () => themed(hostColors(ctx.theme))
|
|
1911
2082
|
});
|
|
1912
2083
|
}
|
|
1913
2084
|
var commandCodeTui = {
|
|
@@ -1915,7 +2086,7 @@ var commandCodeTui = {
|
|
|
1915
2086
|
setup(ctx) {
|
|
1916
2087
|
ctx.ui.slot({
|
|
1917
2088
|
append: "sidebar.content",
|
|
1918
|
-
render: (input) => _$
|
|
2089
|
+
render: (input) => _$createComponent2(PanelV2, {
|
|
1919
2090
|
ctx,
|
|
1920
2091
|
get sessionID() {
|
|
1921
2092
|
return input.sessionID;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffreyjyz/opencode-command-code",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
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",
|