@jeffreyjyz/opencode-command-code 0.3.19 → 0.4.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/constants/binaries.d.ts +6 -0
- package/dist/constants/cli.d.ts +12 -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 +28 -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 +170 -74
- 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 +15 -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 +133 -68
- package/dist/v2.d.ts +2 -16
- package/package.json +2 -2
package/dist/access.d.ts
CHANGED
package/dist/api.d.ts
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Fallback lookup paths for the cmduse CLI (CMDUSE_BIN wins when set). */
|
|
2
|
+
export declare const CMDUSE_BIN_CANDIDATES: readonly string[];
|
|
3
|
+
/** Fallback lookup paths for mpc, the per-model catalog scraper. */
|
|
4
|
+
export declare const MPC_BIN_CANDIDATES: readonly string[];
|
|
5
|
+
/** Fallback lookup paths for ocuse (OCUSE_BIN wins when set). */
|
|
6
|
+
export declare const OCUSE_BIN_CANDIDATES: readonly string[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { spawn } from "node:child_process";
|
|
2
|
+
/** Default invocation: one-shot dashboard, no colors / no live redraw. */
|
|
3
|
+
export declare const DEFAULT_ARGS: readonly string[];
|
|
4
|
+
/** cmduse subcommands — when the arg starts with one, pass it through
|
|
5
|
+
* untouched; otherwise it is extra flags for the one-shot dashboard. */
|
|
6
|
+
export declare const SUBCOMMANDS: ReadonlySet<string>;
|
|
7
|
+
/** `detached` is load-bearing, not tidiness: it starts the child in its own
|
|
8
|
+
* session, so it has no controlling terminal. cmduse's snapshot() paints a
|
|
9
|
+
* "fetching usage…" spinner straight to /dev/tty — piping stdout/stderr does
|
|
10
|
+
* not stop it — which would corrupt the opencode TUI this process inherits its
|
|
11
|
+
* tty from. Detaching makes that open fail; stdout/stderr stay piped. */
|
|
12
|
+
export declare const SPAWN_OPTIONS: Parameters<typeof spawn>[2];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Command Code account/billing API. */
|
|
2
|
+
export declare const API_BASE = "https://api.commandcode.ai";
|
|
3
|
+
/** Provider (inference) API root; the lanes append their wire path. */
|
|
4
|
+
export declare const PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
|
|
5
|
+
/** Default provider base URL used when the host passes none. */
|
|
6
|
+
export declare const DEFAULT_BASE_URL = "https://api.commandcode.ai";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Category } from "../gating";
|
|
2
|
+
/** model id -> category (models absent from this table have unknown category) */
|
|
3
|
+
export declare const MODEL_CATEGORIES: Record<string, Category>;
|
|
4
|
+
/** planId -> access rules; plan ids absent from this table get everything */
|
|
5
|
+
export declare const PLAN_RULES: Record<string, {
|
|
6
|
+
allowedCategories: Category[];
|
|
7
|
+
blockedModels: string[];
|
|
8
|
+
}>;
|
|
9
|
+
/** Models the API hard-blocks (403 MODEL_NOT_IN_PLAN) per plan, beyond the
|
|
10
|
+
* category rules. Empirically probed — the CLI bundle misses these because it
|
|
11
|
+
* tracks category via serving lane, not per-model plan entitlements. */
|
|
12
|
+
export declare const HARD_BLOCKED: Record<string, string[]>;
|
|
13
|
+
/** canonical known model ids (lowercase compare) */
|
|
14
|
+
export declare const KNOWN_MODELS: string[];
|
|
15
|
+
/** deprecated/aliased model id -> canonical id */
|
|
16
|
+
export declare const MODEL_ALIASES: Record<string, string>;
|
|
17
|
+
/** ISO timestamp the snapshot was extracted (absent on pre-metadata files). */
|
|
18
|
+
export declare const GATE_EXTRACTED_AT: string | undefined;
|
|
19
|
+
/** Command Code CLI version the snapshot was scraped from. */
|
|
20
|
+
export declare const GATE_CLI_VERSION: string | undefined;
|
|
21
|
+
/** Warn once a process when gating.json is older than this many days. */
|
|
22
|
+
export declare const GATE_STALE_DAYS = 30;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Every Command Code model shares these capabilities; hoisted so each record
|
|
2
|
+
* does not rebuild them. Vision (`attachment` + `input.image`) is applied per
|
|
3
|
+
* model from the generated catalog in the two model builders. */
|
|
4
|
+
export declare const MODEL_CAPABILITIES: {
|
|
5
|
+
temperature: boolean;
|
|
6
|
+
reasoning: boolean;
|
|
7
|
+
attachment: boolean;
|
|
8
|
+
toolcall: boolean;
|
|
9
|
+
input: {
|
|
10
|
+
text: boolean;
|
|
11
|
+
audio: boolean;
|
|
12
|
+
image: boolean;
|
|
13
|
+
video: boolean;
|
|
14
|
+
pdf: boolean;
|
|
15
|
+
};
|
|
16
|
+
output: {
|
|
17
|
+
text: boolean;
|
|
18
|
+
audio: boolean;
|
|
19
|
+
image: boolean;
|
|
20
|
+
video: boolean;
|
|
21
|
+
pdf: boolean;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
/** Context window advertised when the listing API gives no length. */
|
|
25
|
+
export declare const DEFAULT_CONTEXT_LENGTH = 128000;
|
|
26
|
+
/** Max output tokens advertised per model. */
|
|
27
|
+
export declare const DEFAULT_OUTPUT_TOKENS = 32000;
|
|
28
|
+
/** Provider wire ceiling for a request's max tokens. */
|
|
29
|
+
export declare const DEFAULT_MAX_TOKENS = 64000;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Dir segment under an XDG base that holds our caches/diagnostics. */
|
|
2
|
+
export declare const CACHE_DIR = "command-code";
|
|
3
|
+
/** Live model list disk cache file (under CACHE_DIR). */
|
|
4
|
+
export declare const MODELS_CACHE_FILE = "models.json";
|
|
5
|
+
/** Startup timing log file (under CACHE_DIR). */
|
|
6
|
+
export declare const STARTUP_LOG_FILE = "startup.log";
|
|
7
|
+
/**
|
|
8
|
+
* mpc per-model catalog disk cache file (under CACHE_DIR), one per OpenCode Go
|
|
9
|
+
* plan: mpc bakes the selected plan's per-model allowance into the rows, so a
|
|
10
|
+
* cached catalog is only valid for the plan it was fetched under.
|
|
11
|
+
*/
|
|
12
|
+
export declare function ccCatalogFile(plan: string): string;
|
|
13
|
+
/** opencode's own config/data dir segment. */
|
|
14
|
+
export declare const OPENCODE_DIR = "opencode";
|
|
15
|
+
/** opencode's message store file (under OPENCODE_DIR in the data root). */
|
|
16
|
+
export declare const OPENCODE_DB_FILE = "opencode.db";
|
|
17
|
+
/** Sidebar prefs file (under OPENCODE_DIR in the config root). */
|
|
18
|
+
export declare const PREFS_FILE = "command-code.json";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Provider-id prefix shared by both Command Code lanes and the plugin id. */
|
|
2
|
+
export declare const PROVIDER_ID_PREFIX = "command-code";
|
|
3
|
+
/** Plugin id (stable contract; the v2 `setup` definition and the TUI share it). */
|
|
4
|
+
export declare const PLUGIN_ID = "command-code";
|
|
5
|
+
/** Integration id backing both lanes' credentials (/connect + env). */
|
|
6
|
+
export declare const INTEGRATION_ID = "command-code";
|
|
7
|
+
/** Display name for the integration and the no-key warning. */
|
|
8
|
+
export declare const INTEGRATION_NAME = "Command Code";
|
|
9
|
+
/** OpenCode Go provider id (ocuse reads its store). */
|
|
10
|
+
export declare const GO_PROVIDER_ID = "opencode-go";
|
|
11
|
+
/** OpenCode Zen provider id and its `opencode-zen` alias prefix. */
|
|
12
|
+
export declare const ZEN_PROVIDER_ID = "opencode";
|
|
13
|
+
export declare const ZEN_PROVIDER_PREFIX = "opencode-zen";
|
|
14
|
+
/** TUI plugin id (stable contract; test/tui.test.ts pins it). */
|
|
15
|
+
export declare const TUI_ID = "command-code.tui";
|
|
16
|
+
/** The cmd_usage tool description, shared by the v1 and v2 halves. */
|
|
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
|
+
export type Lane = {
|
|
19
|
+
id: "command-code-anthropic" | "command-code-openai";
|
|
20
|
+
name: string;
|
|
21
|
+
/** v2 runtime package (first-party, distinct from the v1 `npm` value). */
|
|
22
|
+
pkg: string;
|
|
23
|
+
/** Wire field reasoning must round-trip through on the openai-compatible
|
|
24
|
+
* lane; v2 spells the old `interleaved: {field}` as `compatibility`. */
|
|
25
|
+
reasoningField?: string;
|
|
26
|
+
};
|
|
27
|
+
/** The Anthropic and OpenAI-compatible provider lanes this plugin owns. */
|
|
28
|
+
export declare const LANES: [Lane, Lane];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Category } from "../gating";
|
|
2
|
+
import type { Prefs } from "../sidebar/prefs";
|
|
3
|
+
/**
|
|
4
|
+
* The sidebar is 42 columns wide with 2+2 padding, and our panel keeps one more
|
|
5
|
+
* column clear on the right, so a rendered row must stay within this. Rows that
|
|
6
|
+
* cannot fit are split across an indented continuation rather than wrapping in
|
|
7
|
+
* the host's renderer, which is what made the panel look broken.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ROW_WIDTH = 37;
|
|
10
|
+
/** Tier label per gating category. */
|
|
11
|
+
export declare const TIER_DISPLAY: Readonly<Record<Category, string>>;
|
|
12
|
+
/**
|
|
13
|
+
* OpenCode Go plans the panel can render: the same token rates, different
|
|
14
|
+
* per-model monthly limits. Which one a user is on is not discoverable (Go has
|
|
15
|
+
* no usage API and the store records no subscription), so it is a setting.
|
|
16
|
+
*/
|
|
17
|
+
export declare const OC_PLANS: readonly ["go", "go-plus"];
|
|
18
|
+
export type OcPlan = (typeof OC_PLANS)[number];
|
|
19
|
+
/** Plan used when the user has not set one: behaviour is unchanged for them. */
|
|
20
|
+
export declare const OC_PLAN_DEFAULT: OcPlan;
|
|
21
|
+
/** Go's documented per-model window shares of the monthly allowance. */
|
|
22
|
+
export declare const GO_WINDOW_SHARE: {
|
|
23
|
+
readonly fiveHour: 0.2;
|
|
24
|
+
readonly weekly: 0.5;
|
|
25
|
+
readonly month: 1;
|
|
26
|
+
};
|
|
27
|
+
/** 5-hour window length in seconds. Mirrors cmduse_core::FIVE_HOUR_SECS. */
|
|
28
|
+
export declare const FIVE_HOUR_SECS: number;
|
|
29
|
+
/** Weekly window length in seconds. Mirrors cmduse_core::WEEKLY_SECS. */
|
|
30
|
+
export declare const WEEKLY_SECS: number;
|
|
31
|
+
/** Colour is OFF unless asked for; the OpenCode Go plan defaults to `go` (see
|
|
32
|
+
* sidebar/prefs.ts). */
|
|
33
|
+
export declare const PREF_DEFAULTS: Prefs;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Sidebar poll cadence: one cmduse/ocuse spawn plus the local store reads. */
|
|
2
|
+
export declare const POLL_MS = 5000;
|
|
3
|
+
/** How long one sidebar CLI spawn may run before it is killed. An outage can
|
|
4
|
+
* leave a fetch hanging through cmduse's retry ladder, and the poll interval
|
|
5
|
+
* would otherwise stack spawns on top of each other. */
|
|
6
|
+
export declare const CHILD_TIMEOUT_MS = 30000;
|
|
7
|
+
/** Process-lifetime cache for the live model list (src/models.ts). */
|
|
8
|
+
export declare const MODELS_CACHE_TTL_MS: number;
|
|
9
|
+
/** Disk cache for mpc's per-model catalog (src/sidebar/data.ts). An hour, not
|
|
10
|
+
* six: a running panel then picks up new models and promotions. */
|
|
11
|
+
export declare const CATALOG_CACHE_TTL_MS: number;
|
|
12
|
+
/** Re-read window for the sidebar colour prefs file (src/sidebar/prefs.ts). */
|
|
13
|
+
export declare const PREFS_CACHE_MS = 5000;
|
package/dist/gating.d.ts
CHANGED
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
export type Category = "opensource" | "premium";
|
|
2
|
-
/**
|
|
3
|
-
export declare const MODEL_CATEGORIES: Record<string, Category>;
|
|
4
|
-
/** planId -> access rules; plan ids absent from this table get everything */
|
|
5
|
-
export declare const PLAN_RULES: Record<string, {
|
|
6
|
-
allowedCategories: Category[];
|
|
7
|
-
blockedModels: string[];
|
|
8
|
-
}>;
|
|
9
|
-
/** Models the API hard-blocks (403 MODEL_NOT_IN_PLAN) per plan, beyond the
|
|
10
|
-
* category rules. Empirically probed — the CLI bundle misses these because it
|
|
11
|
-
* tracks category via serving lane, not per-model plan entitlements. */
|
|
12
|
-
export declare const HARD_BLOCKED: Record<string, string[]>;
|
|
13
|
-
/** canonical known model ids (lowercase compare) */
|
|
14
|
-
declare const KNOWN_MODELS: string[];
|
|
15
|
-
export { KNOWN_MODELS };
|
|
16
|
-
/** ISO timestamp the snapshot was extracted (absent on pre-metadata files). */
|
|
17
|
-
export declare const GATE_EXTRACTED_AT: string | undefined;
|
|
18
|
-
/** Command Code CLI version the snapshot was scraped from. */
|
|
19
|
-
export declare const GATE_CLI_VERSION: string | undefined;
|
|
20
|
-
/** Warn once per process when gating.json is older than 30 days. */
|
|
2
|
+
/** Warn once per process when gating.json is older than GATE_STALE_DAYS. */
|
|
21
3
|
export declare function warnIfGatingStale(): void;
|
|
22
4
|
export declare function canonicalizeModelId(model: string): string;
|
|
23
5
|
/** Bare model id with any provider qualifier stripped: everything after the
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { Plugin as PluginV1 } from "@opencode-ai/plugin";
|
|
|
2
2
|
export declare const CommandCodePlugin: PluginV1;
|
|
3
3
|
export { createCommandCode } from "./provider";
|
|
4
4
|
declare const _default: {
|
|
5
|
-
/** v1 entrypoint (opencode >=1.18.29 object entrypoints). */
|
|
6
|
-
server: PluginV1;
|
|
7
5
|
id: string;
|
|
8
6
|
setup: (context: import("@opencode/plugin/promise/plugin").Context) => Promise<import("@opencode/plugin/promise/plugin").Cleanup | void> | import("@opencode/plugin/promise/plugin").Cleanup | void;
|
|
7
|
+
/** v1 entrypoint (opencode >=1.18.29 object entrypoints). */
|
|
8
|
+
server: PluginV1;
|
|
9
9
|
};
|
|
10
10
|
export default _default;
|
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.4.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",
|
|
@@ -2438,7 +2438,7 @@ var package_default = {
|
|
|
2438
2438
|
},
|
|
2439
2439
|
devDependencies: {
|
|
2440
2440
|
"@types/bun": "latest",
|
|
2441
|
-
typescript: "^
|
|
2441
|
+
typescript: "^7",
|
|
2442
2442
|
"@opencode-ai/plugin": "*",
|
|
2443
2443
|
"@opencode/plugin": "^2.0.11",
|
|
2444
2444
|
"@ai-sdk/provider": "3.0.8"
|
|
@@ -2461,8 +2461,12 @@ var package_default = {
|
|
|
2461
2461
|
}
|
|
2462
2462
|
};
|
|
2463
2463
|
|
|
2464
|
-
// src/
|
|
2464
|
+
// src/constants/endpoints.ts
|
|
2465
2465
|
var API_BASE = "https://api.commandcode.ai";
|
|
2466
|
+
var PROVIDER_BASE = `${API_BASE}/provider/v1`;
|
|
2467
|
+
var DEFAULT_BASE_URL = API_BASE;
|
|
2468
|
+
|
|
2469
|
+
// src/api.ts
|
|
2466
2470
|
function authHeaders(key) {
|
|
2467
2471
|
return { Authorization: `Bearer ${key}`, Accept: "application/json" };
|
|
2468
2472
|
}
|
|
@@ -2935,7 +2939,12 @@ var MODEL_CATALOG = {
|
|
|
2935
2939
|
name: "Qwen 3.7 Flash",
|
|
2936
2940
|
context: 1e6,
|
|
2937
2941
|
efforts: null,
|
|
2938
|
-
cost: {
|
|
2942
|
+
cost: {
|
|
2943
|
+
input: 0.03,
|
|
2944
|
+
output: 0.13,
|
|
2945
|
+
cacheRead: 0.006,
|
|
2946
|
+
cacheWrite: 0.038
|
|
2947
|
+
},
|
|
2939
2948
|
modalities: ["text", "image"],
|
|
2940
2949
|
minPlan: "Go"
|
|
2941
2950
|
},
|
|
@@ -3207,16 +3216,22 @@ function reasoningVariants(id) {
|
|
|
3207
3216
|
|
|
3208
3217
|
// src/cli.ts
|
|
3209
3218
|
import { spawn } from "child_process";
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
var
|
|
3219
|
+
|
|
3220
|
+
// src/constants/binaries.ts
|
|
3221
|
+
import { homedir } from "os";
|
|
3222
|
+
import { join } from "path";
|
|
3223
|
+
var CMDUSE_BIN_CANDIDATES = [
|
|
3224
|
+
"cmduse",
|
|
3225
|
+
"/opt/homebrew/bin/cmduse",
|
|
3226
|
+
"/usr/local/bin/cmduse"
|
|
3227
|
+
];
|
|
3228
|
+
var MPC_BIN_CANDIDATES = [
|
|
3229
|
+
"mpc",
|
|
3230
|
+
join(homedir(), ".bun/bin/mpc"),
|
|
3231
|
+
"/opt/homebrew/bin/mpc"
|
|
3232
|
+
];
|
|
3233
|
+
|
|
3234
|
+
// src/constants/cli.ts
|
|
3220
3235
|
var DEFAULT_ARGS = ["-1", "--plain"];
|
|
3221
3236
|
var SUBCOMMANDS = new Set([
|
|
3222
3237
|
"plans",
|
|
@@ -3228,6 +3243,13 @@ var SUBCOMMANDS = new Set([
|
|
|
3228
3243
|
"statusline",
|
|
3229
3244
|
"config"
|
|
3230
3245
|
]);
|
|
3246
|
+
|
|
3247
|
+
// src/cli.ts
|
|
3248
|
+
function cmduseCandidates(env = process.env) {
|
|
3249
|
+
const candidates = [env.CMDUSE_BIN, ...CMDUSE_BIN_CANDIDATES];
|
|
3250
|
+
return candidates.filter((bin) => Boolean(bin));
|
|
3251
|
+
}
|
|
3252
|
+
var CANDIDATES = cmduseCandidates();
|
|
3231
3253
|
function splitCliArgs(input) {
|
|
3232
3254
|
const out = [];
|
|
3233
3255
|
let cur = "";
|
|
@@ -3269,7 +3291,7 @@ function splitCliArgs(input) {
|
|
|
3269
3291
|
function buildCmduseArgs(arg) {
|
|
3270
3292
|
const tokens = splitCliArgs(arg);
|
|
3271
3293
|
if (!tokens.length)
|
|
3272
|
-
return DEFAULT_ARGS;
|
|
3294
|
+
return [...DEFAULT_ARGS];
|
|
3273
3295
|
if (tokens.some((t) => SUBCOMMANDS.has(t)))
|
|
3274
3296
|
return tokens;
|
|
3275
3297
|
return [...DEFAULT_ARGS, ...tokens];
|
|
@@ -3328,9 +3350,48 @@ async function runCmduse(arg, opts = {}) {
|
|
|
3328
3350
|
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 });
|
|
3329
3351
|
}
|
|
3330
3352
|
|
|
3353
|
+
// src/constants/models.ts
|
|
3354
|
+
var MODEL_CAPABILITIES = {
|
|
3355
|
+
temperature: true,
|
|
3356
|
+
reasoning: true,
|
|
3357
|
+
attachment: false,
|
|
3358
|
+
toolcall: true,
|
|
3359
|
+
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
3360
|
+
output: {
|
|
3361
|
+
text: true,
|
|
3362
|
+
audio: false,
|
|
3363
|
+
image: false,
|
|
3364
|
+
video: false,
|
|
3365
|
+
pdf: false
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
var DEFAULT_CONTEXT_LENGTH = 128000;
|
|
3369
|
+
var DEFAULT_OUTPUT_TOKENS = 32000;
|
|
3370
|
+
var DEFAULT_MAX_TOKENS = 64000;
|
|
3371
|
+
|
|
3372
|
+
// src/constants/providers.ts
|
|
3373
|
+
var PROVIDER_ID_PREFIX = "command-code";
|
|
3374
|
+
var PLUGIN_ID = PROVIDER_ID_PREFIX;
|
|
3375
|
+
var INTEGRATION_ID = PROVIDER_ID_PREFIX;
|
|
3376
|
+
var INTEGRATION_NAME = "Command Code";
|
|
3377
|
+
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').";
|
|
3378
|
+
var LANES = [
|
|
3379
|
+
{
|
|
3380
|
+
id: "command-code-anthropic",
|
|
3381
|
+
name: "Command Code (Anthropic)",
|
|
3382
|
+
pkg: "@opencode/ai/providers/anthropic"
|
|
3383
|
+
},
|
|
3384
|
+
{
|
|
3385
|
+
id: "command-code-openai",
|
|
3386
|
+
name: "Command Code (OpenAI)",
|
|
3387
|
+
pkg: "@opencode/ai/providers/openai-compatible",
|
|
3388
|
+
reasoningField: "reasoning_content"
|
|
3389
|
+
}
|
|
3390
|
+
];
|
|
3391
|
+
|
|
3331
3392
|
// src/key.ts
|
|
3332
3393
|
import { readFile } from "fs/promises";
|
|
3333
|
-
import { homedir } from "os";
|
|
3394
|
+
import { homedir as homedir2 } from "os";
|
|
3334
3395
|
async function resolveKey(getAuth) {
|
|
3335
3396
|
const env = process.env.CMD_API_KEY;
|
|
3336
3397
|
if (env)
|
|
@@ -3341,7 +3402,7 @@ async function resolveKey(getAuth) {
|
|
|
3341
3402
|
return auth.key;
|
|
3342
3403
|
} catch {}
|
|
3343
3404
|
try {
|
|
3344
|
-
const text = await readFile(`${
|
|
3405
|
+
const text = await readFile(`${homedir2()}/.commandcode/auth.json`, "utf8");
|
|
3345
3406
|
const v = JSON.parse(text);
|
|
3346
3407
|
if (typeof v.apiKey === "string" && v.apiKey)
|
|
3347
3408
|
return v.apiKey;
|
|
@@ -3670,7 +3731,7 @@ var gating_default = {
|
|
|
3670
3731
|
}
|
|
3671
3732
|
};
|
|
3672
3733
|
|
|
3673
|
-
// src/gating.ts
|
|
3734
|
+
// src/constants/gating.ts
|
|
3674
3735
|
var MODEL_CATEGORIES = gating_default.categories;
|
|
3675
3736
|
var PLAN_RULES = gating_default.plans;
|
|
3676
3737
|
var HARD_BLOCKED = gating_default.hardBlocked;
|
|
@@ -3679,6 +3740,8 @@ var MODEL_ALIASES = gating_default.aliases;
|
|
|
3679
3740
|
var GATE_EXTRACTED_AT = gating_default.extractedAt;
|
|
3680
3741
|
var GATE_CLI_VERSION = gating_default.cliVersion;
|
|
3681
3742
|
var GATE_STALE_DAYS = 30;
|
|
3743
|
+
|
|
3744
|
+
// src/gating.ts
|
|
3682
3745
|
var staleWarned = false;
|
|
3683
3746
|
function warnIfGatingStale() {
|
|
3684
3747
|
if (staleWarned || !GATE_EXTRACTED_AT)
|
|
@@ -3745,8 +3808,11 @@ function siblingCategory(model) {
|
|
|
3745
3808
|
return best ? MODEL_CATEGORIES[best] : undefined;
|
|
3746
3809
|
}
|
|
3747
3810
|
|
|
3811
|
+
// src/constants/timing.ts
|
|
3812
|
+
var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
3813
|
+
var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
3814
|
+
|
|
3748
3815
|
// src/models.ts
|
|
3749
|
-
var CACHE_TTL_MS = 5 * 60 * 1000;
|
|
3750
3816
|
var cache = null;
|
|
3751
3817
|
function isClaude(id) {
|
|
3752
3818
|
const bare = id.includes("/") ? id.slice(id.lastIndexOf("/") + 1) : id;
|
|
@@ -3762,8 +3828,8 @@ function splitModels(models) {
|
|
|
3762
3828
|
async function loadModels(key) {
|
|
3763
3829
|
warnIfGatingStale();
|
|
3764
3830
|
let models;
|
|
3765
|
-
const fresh = cache && cache.key === key && Date.now() - cache.at <
|
|
3766
|
-
if (fresh) {
|
|
3831
|
+
const fresh = cache && cache.key === key && Date.now() - cache.at < MODELS_CACHE_TTL_MS;
|
|
3832
|
+
if (fresh && cache) {
|
|
3767
3833
|
models = cache.models;
|
|
3768
3834
|
} else {
|
|
3769
3835
|
try {
|
|
@@ -3801,11 +3867,18 @@ async function loadModels(key) {
|
|
|
3801
3867
|
|
|
3802
3868
|
// src/modelsCache.ts
|
|
3803
3869
|
import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
3804
|
-
import { homedir as
|
|
3805
|
-
import { join } from "path";
|
|
3870
|
+
import { homedir as homedir3 } from "os";
|
|
3871
|
+
import { join as join2 } from "path";
|
|
3872
|
+
|
|
3873
|
+
// src/constants/paths.ts
|
|
3874
|
+
var CACHE_DIR = "command-code";
|
|
3875
|
+
var MODELS_CACHE_FILE = "models.json";
|
|
3876
|
+
var STARTUP_LOG_FILE = "startup.log";
|
|
3877
|
+
|
|
3878
|
+
// src/modelsCache.ts
|
|
3806
3879
|
function cachePath() {
|
|
3807
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
3808
|
-
return
|
|
3880
|
+
const base = process.env.XDG_CACHE_HOME ?? join2(homedir3(), ".cache");
|
|
3881
|
+
return join2(base, CACHE_DIR, MODELS_CACHE_FILE);
|
|
3809
3882
|
}
|
|
3810
3883
|
async function readModelsCache() {
|
|
3811
3884
|
try {
|
|
@@ -3820,7 +3893,7 @@ async function readModelsCache() {
|
|
|
3820
3893
|
async function writeModelsCache(cache) {
|
|
3821
3894
|
try {
|
|
3822
3895
|
const path = cachePath();
|
|
3823
|
-
await mkdir(
|
|
3896
|
+
await mkdir(join2(path, ".."), { recursive: true });
|
|
3824
3897
|
await writeFile(path, JSON.stringify(cache));
|
|
3825
3898
|
} catch {}
|
|
3826
3899
|
}
|
|
@@ -3914,7 +3987,9 @@ function anthropicReducer() {
|
|
|
3914
3987
|
const open = (index, kind) => {
|
|
3915
3988
|
const id = `${kind === "text" ? "text" : "reasoning"}-${index}`;
|
|
3916
3989
|
blocks.set(index, { type: kind, id });
|
|
3917
|
-
return [
|
|
3990
|
+
return [
|
|
3991
|
+
{ type: kind === "text" ? "text-start" : "reasoning-start", id }
|
|
3992
|
+
];
|
|
3918
3993
|
};
|
|
3919
3994
|
return {
|
|
3920
3995
|
feed(event) {
|
|
@@ -3936,7 +4011,13 @@ function anthropicReducer() {
|
|
|
3936
4011
|
if (blockType === "tool_use") {
|
|
3937
4012
|
const id = text(block.id) || `tool-${index}`;
|
|
3938
4013
|
blocks.set(index, { type: "tool_use", id });
|
|
3939
|
-
return [
|
|
4014
|
+
return [
|
|
4015
|
+
{
|
|
4016
|
+
type: "tool-input-start",
|
|
4017
|
+
id,
|
|
4018
|
+
toolName: text(block.name)
|
|
4019
|
+
}
|
|
4020
|
+
];
|
|
3940
4021
|
}
|
|
3941
4022
|
blocks.set(index, { type: "other", id: `block-${index}` });
|
|
3942
4023
|
return [];
|
|
@@ -3949,17 +4030,29 @@ function anthropicReducer() {
|
|
|
3949
4030
|
if (textDelta) {
|
|
3950
4031
|
const parts = block?.type === "text" ? [] : open(index, "text");
|
|
3951
4032
|
const id = blocks.get(index)?.id ?? `text-${index}`;
|
|
3952
|
-
return [
|
|
4033
|
+
return [
|
|
4034
|
+
...parts,
|
|
4035
|
+
{ type: "text-delta", id, delta: textDelta }
|
|
4036
|
+
];
|
|
3953
4037
|
}
|
|
3954
4038
|
const thinking = text(delta.thinking);
|
|
3955
4039
|
if (thinking) {
|
|
3956
4040
|
const parts = block?.type === "thinking" ? [] : open(index, "thinking");
|
|
3957
4041
|
const id = blocks.get(index)?.id ?? `reasoning-${index}`;
|
|
3958
|
-
return [
|
|
4042
|
+
return [
|
|
4043
|
+
...parts,
|
|
4044
|
+
{ type: "reasoning-delta", id, delta: thinking }
|
|
4045
|
+
];
|
|
3959
4046
|
}
|
|
3960
4047
|
const partial = text(delta.partial_json);
|
|
3961
4048
|
if (partial && block?.type === "tool_use") {
|
|
3962
|
-
return [
|
|
4049
|
+
return [
|
|
4050
|
+
{
|
|
4051
|
+
type: "tool-input-delta",
|
|
4052
|
+
id: block.id,
|
|
4053
|
+
delta: partial
|
|
4054
|
+
}
|
|
4055
|
+
];
|
|
3963
4056
|
}
|
|
3964
4057
|
return [];
|
|
3965
4058
|
}
|
|
@@ -4176,7 +4269,11 @@ function openaiReducer() {
|
|
|
4176
4269
|
return parts;
|
|
4177
4270
|
},
|
|
4178
4271
|
close() {
|
|
4179
|
-
const parts = [
|
|
4272
|
+
const parts = [
|
|
4273
|
+
...closeReasoning(),
|
|
4274
|
+
...closeText(),
|
|
4275
|
+
...flushTools()
|
|
4276
|
+
];
|
|
4180
4277
|
if (pending && !finished) {
|
|
4181
4278
|
finished = true;
|
|
4182
4279
|
parts.push({
|
|
@@ -4191,7 +4288,6 @@ function openaiReducer() {
|
|
|
4191
4288
|
}
|
|
4192
4289
|
|
|
4193
4290
|
// src/provider/wire.ts
|
|
4194
|
-
var DEFAULT_MAX_TOKENS = 64000;
|
|
4195
4291
|
function capTokens(value) {
|
|
4196
4292
|
const n = typeof value === "number" && Number.isFinite(value) ? value : DEFAULT_MAX_TOKENS;
|
|
4197
4293
|
return Math.min(n, DEFAULT_MAX_TOKENS);
|
|
@@ -4345,7 +4441,11 @@ function anthropicBody(model, prompt, options) {
|
|
|
4345
4441
|
};
|
|
4346
4442
|
if (system)
|
|
4347
4443
|
body.system = [
|
|
4348
|
-
{
|
|
4444
|
+
{
|
|
4445
|
+
type: "text",
|
|
4446
|
+
text: system,
|
|
4447
|
+
cache_control: { type: "ephemeral" }
|
|
4448
|
+
}
|
|
4349
4449
|
];
|
|
4350
4450
|
if (options.tools?.length) {
|
|
4351
4451
|
body.tools = options.tools.map((t) => ({
|
|
@@ -4443,7 +4543,6 @@ function requestBody(lane, model, prompt, options) {
|
|
|
4443
4543
|
}
|
|
4444
4544
|
|
|
4445
4545
|
// src/provider/index.ts
|
|
4446
|
-
var DEFAULT_BASE_URL = "https://api.commandcode.ai";
|
|
4447
4546
|
function laneOf(modelId) {
|
|
4448
4547
|
const bare = modelId.includes("/") ? modelId.slice(modelId.lastIndexOf("/") + 1) : modelId;
|
|
4449
4548
|
return bare.toLowerCase().startsWith("claude") ? "anthropic" : "openai";
|
|
@@ -4507,10 +4606,11 @@ function createCommandCode(options = {}, images = () => false) {
|
|
|
4507
4606
|
const detail = await response.text().catch(() => "");
|
|
4508
4607
|
throw new Error(`command-code ${lane} ${response.status}: ${detail.slice(0, 300)}`);
|
|
4509
4608
|
}
|
|
4609
|
+
const responseBody = response.body;
|
|
4510
4610
|
const reducer = lane === "anthropic" ? anthropicReducer() : openaiReducer();
|
|
4511
4611
|
const stream = new ReadableStream({
|
|
4512
4612
|
async start(controller) {
|
|
4513
|
-
const reader =
|
|
4613
|
+
const reader = responseBody.getReader();
|
|
4514
4614
|
const decoder = new TextDecoder;
|
|
4515
4615
|
const emit = (parts) => {
|
|
4516
4616
|
for (const part of parts)
|
|
@@ -4546,7 +4646,12 @@ function createCommandCode(options = {}, images = () => false) {
|
|
|
4546
4646
|
raw: "unknown"
|
|
4547
4647
|
};
|
|
4548
4648
|
let usage = {
|
|
4549
|
-
inputTokens: {
|
|
4649
|
+
inputTokens: {
|
|
4650
|
+
total: 0,
|
|
4651
|
+
noCache: 0,
|
|
4652
|
+
cacheRead: 0,
|
|
4653
|
+
cacheWrite: 0
|
|
4654
|
+
},
|
|
4550
4655
|
outputTokens: { total: 0, text: 0, reasoning: 0 }
|
|
4551
4656
|
};
|
|
4552
4657
|
const textOf = {};
|
|
@@ -4559,7 +4664,10 @@ function createCommandCode(options = {}, images = () => false) {
|
|
|
4559
4664
|
if (part.type === "text-delta")
|
|
4560
4665
|
textOf[String(part.id)] = (textOf[String(part.id)] ?? "") + String(part.delta);
|
|
4561
4666
|
else if (part.type === "reasoning-delta") {
|
|
4562
|
-
content.push({
|
|
4667
|
+
content.push({
|
|
4668
|
+
type: "reasoning",
|
|
4669
|
+
text: String(part.delta)
|
|
4670
|
+
});
|
|
4563
4671
|
} else if (part.type === "tool-input-start") {
|
|
4564
4672
|
toolsById.set(String(part.id), {
|
|
4565
4673
|
name: String(part.toolName),
|
|
@@ -4597,43 +4705,25 @@ function createCommandCode(options = {}, images = () => false) {
|
|
|
4597
4705
|
|
|
4598
4706
|
// src/startupLog.ts
|
|
4599
4707
|
import { appendFile, mkdir as mkdir2 } from "fs/promises";
|
|
4600
|
-
import { homedir as
|
|
4601
|
-
import { join as
|
|
4708
|
+
import { homedir as homedir4 } from "os";
|
|
4709
|
+
import { join as join3 } from "path";
|
|
4602
4710
|
function setupTimingLine(t) {
|
|
4603
4711
|
return `setup: key=${t.key}ms register=${t.register}ms connection=${t.connection}ms refresh=${t.refresh}ms models=${t.models}`;
|
|
4604
4712
|
}
|
|
4605
4713
|
function startupLogPath() {
|
|
4606
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
4607
|
-
return
|
|
4714
|
+
const base = process.env.XDG_CACHE_HOME ?? join3(homedir4(), ".cache");
|
|
4715
|
+
return join3(base, CACHE_DIR, STARTUP_LOG_FILE);
|
|
4608
4716
|
}
|
|
4609
4717
|
async function writeStartupLine(line) {
|
|
4610
4718
|
try {
|
|
4611
4719
|
const path = startupLogPath();
|
|
4612
|
-
await mkdir2(
|
|
4720
|
+
await mkdir2(join3(path, ".."), { recursive: true });
|
|
4613
4721
|
await appendFile(path, `[${new Date().toISOString()}] ${line}
|
|
4614
4722
|
`);
|
|
4615
4723
|
} catch {}
|
|
4616
4724
|
}
|
|
4617
4725
|
|
|
4618
4726
|
// src/v2.ts
|
|
4619
|
-
var PLUGIN_ID = "command-code";
|
|
4620
|
-
var PROVIDER_BASE = "https://api.commandcode.ai/provider/v1";
|
|
4621
|
-
var INTEGRATION_ID = "command-code";
|
|
4622
|
-
var INTEGRATION_NAME = "Command Code";
|
|
4623
|
-
var LANES = [
|
|
4624
|
-
{
|
|
4625
|
-
id: "command-code-anthropic",
|
|
4626
|
-
name: "Command Code (Anthropic)",
|
|
4627
|
-
pkg: "@opencode/ai/providers/anthropic"
|
|
4628
|
-
},
|
|
4629
|
-
{
|
|
4630
|
-
id: "command-code-openai",
|
|
4631
|
-
name: "Command Code (OpenAI)",
|
|
4632
|
-
pkg: "@opencode/ai/providers/openai-compatible",
|
|
4633
|
-
reasoningField: "reasoning_content"
|
|
4634
|
-
}
|
|
4635
|
-
];
|
|
4636
|
-
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').";
|
|
4637
4727
|
function costEntry(id) {
|
|
4638
4728
|
const cost = modelCost(id);
|
|
4639
4729
|
return cost ? [
|
|
@@ -4662,7 +4752,10 @@ function toV2Model(m, lane) {
|
|
|
4662
4752
|
cost: costEntry(m.id),
|
|
4663
4753
|
status: "active",
|
|
4664
4754
|
enabled: true,
|
|
4665
|
-
limit: {
|
|
4755
|
+
limit: {
|
|
4756
|
+
context: m.contextLength || DEFAULT_CONTEXT_LENGTH,
|
|
4757
|
+
output: DEFAULT_OUTPUT_TOKENS
|
|
4758
|
+
},
|
|
4666
4759
|
...lane.reasoningField ? { compatibility: { reasoningField: lane.reasoningField } } : {}
|
|
4667
4760
|
};
|
|
4668
4761
|
return info;
|
|
@@ -4759,7 +4852,10 @@ var commandCodeV2 = {
|
|
|
4759
4852
|
if (provider.package === "")
|
|
4760
4853
|
provider.package = lane.pkg;
|
|
4761
4854
|
provider.activation = localKey ? "enabled" : "auto";
|
|
4762
|
-
provider.settings = {
|
|
4855
|
+
provider.settings = {
|
|
4856
|
+
...provider.settings,
|
|
4857
|
+
baseURL: PROVIDER_BASE
|
|
4858
|
+
};
|
|
4763
4859
|
if (localKey && provider.settings.apiKey === undefined) {
|
|
4764
4860
|
provider.settings.apiKey = localKey;
|
|
4765
4861
|
}
|
|
@@ -4880,14 +4976,6 @@ var PROVIDER_NPM = `${package_default.name}@${package_default.version}`;
|
|
|
4880
4976
|
function tool(input) {
|
|
4881
4977
|
return input;
|
|
4882
4978
|
}
|
|
4883
|
-
var MODEL_CAPABILITIES = {
|
|
4884
|
-
temperature: true,
|
|
4885
|
-
reasoning: true,
|
|
4886
|
-
attachment: false,
|
|
4887
|
-
toolcall: true,
|
|
4888
|
-
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
4889
|
-
output: { text: true, audio: false, image: false, video: false, pdf: false }
|
|
4890
|
-
};
|
|
4891
4979
|
function costFor(id) {
|
|
4892
4980
|
const cost = modelCost(id);
|
|
4893
4981
|
return {
|
|
@@ -4909,11 +4997,17 @@ function toModelDefs(models, providerID, npm, interleaved = false) {
|
|
|
4909
4997
|
interleaved,
|
|
4910
4998
|
...supportsImage(m.id) ? {
|
|
4911
4999
|
attachment: true,
|
|
4912
|
-
input: {
|
|
5000
|
+
input: {
|
|
5001
|
+
...MODEL_CAPABILITIES.input,
|
|
5002
|
+
image: true
|
|
5003
|
+
}
|
|
4913
5004
|
} : {}
|
|
4914
5005
|
},
|
|
4915
5006
|
cost: costFor(m.id),
|
|
4916
|
-
limit: {
|
|
5007
|
+
limit: {
|
|
5008
|
+
context: m.contextLength || DEFAULT_CONTEXT_LENGTH,
|
|
5009
|
+
output: DEFAULT_OUTPUT_TOKENS
|
|
5010
|
+
},
|
|
4917
5011
|
status: "active",
|
|
4918
5012
|
options: {},
|
|
4919
5013
|
headers: {},
|
|
@@ -5026,7 +5120,9 @@ var CommandCodePlugin = async (_input) => {
|
|
|
5026
5120
|
},
|
|
5027
5121
|
async execute(args) {
|
|
5028
5122
|
const key = await resolveKey();
|
|
5029
|
-
return runCmduse(args.arg ?? "", {
|
|
5123
|
+
return runCmduse(args.arg ?? "", {
|
|
5124
|
+
env: { CMD_API_KEY: key }
|
|
5125
|
+
});
|
|
5030
5126
|
}
|
|
5031
5127
|
})
|
|
5032
5128
|
}
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
2
|
import { type WireLane } from "./wire";
|
|
3
|
-
export declare const DEFAULT_BASE_URL = "https://api.commandcode.ai";
|
|
4
3
|
export interface CommandCodeProviderOptions {
|
|
5
4
|
/** Bearer key; the v2 host passes the resolved connection credential here. */
|
|
6
5
|
apiKey?: string;
|
package/dist/sidebar/data.d.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type OcPlan } from "../constants/sidebar";
|
|
2
2
|
import { type ModelMeta, type Usage } from "./rows";
|
|
3
3
|
import { type ZenUsage } from "./zen";
|
|
4
|
-
/** `detached` is load-bearing, not tidiness: it starts the child in its own
|
|
5
|
-
* session, so it has no controlling terminal. cmduse's snapshot() paints a
|
|
6
|
-
* "fetching usage…" spinner straight to /dev/tty — piping stdout/stderr does
|
|
7
|
-
* not stop it — which would corrupt the opencode TUI this process inherits its
|
|
8
|
-
* tty from. Detaching makes that open fail; stdout/stderr stay piped. */
|
|
9
|
-
export declare const SPAWN_OPTIONS: Parameters<typeof spawn>[2];
|
|
10
4
|
/** Parse `cmduse -1 --json` into the usage snapshot. */
|
|
11
5
|
export declare function parseUsageJson(text: string): Usage;
|
|
12
6
|
/**
|
|
@@ -23,8 +17,17 @@ export declare function requireSnapshot(usage: Usage): Usage;
|
|
|
23
17
|
* kind of session. Rows that carry only one side are kept.
|
|
24
18
|
*/
|
|
25
19
|
export declare function parseMpcJson(text: string): Map<string, ModelMeta>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
20
|
+
/**
|
|
21
|
+
* mpc's args for the per-model catalog. `--shape off` is the recursion break
|
|
22
|
+
* (mpc's default `--shape auto` spawns reqshape, which spawns mpc back), never
|
|
23
|
+
* dropped; `--oc-plan` selects which Go plan's per-model allowance is baked in.
|
|
24
|
+
*/
|
|
25
|
+
export declare function metaArgs(plan: OcPlan): string[];
|
|
26
|
+
/** ocuse's args: `--plan` selects the Go plan's per-model limits. */
|
|
27
|
+
export declare function zenArgs(plan: OcPlan): string[];
|
|
28
|
+
/** mpc's catalog, cached on disk: it scrapes live docs over the network. The
|
|
29
|
+
* cache is per plan, since mpc bakes that plan's per-model allowance into it. */
|
|
30
|
+
export declare function loadMeta(plan?: OcPlan): Promise<Map<string, ModelMeta>>;
|
|
28
31
|
/** One usage snapshot from cmduse. Throws when cmduse is missing, fails, or
|
|
29
32
|
* returns the errored defaults its JSON carries when the account API is
|
|
30
33
|
* unreachable (see `requireSnapshot`). */
|
|
@@ -34,4 +37,4 @@ export declare function loadUsage(): Promise<Usage>;
|
|
|
34
37
|
* (those providers have no usage API). Throws when ocuse is missing or fails,
|
|
35
38
|
* and the caller then omits the panel.
|
|
36
39
|
*/
|
|
37
|
-
export declare function loadZen(): Promise<ZenUsage>;
|
|
40
|
+
export declare function loadZen(plan?: OcPlan): Promise<ZenUsage>;
|
package/dist/sidebar/prefs.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { type OcPlan } from "../constants/sidebar";
|
|
1
2
|
export interface Prefs {
|
|
2
3
|
colors: boolean;
|
|
4
|
+
/** OpenCode Go plan the sidebar renders per-model limits for. */
|
|
5
|
+
ocPlan: OcPlan;
|
|
3
6
|
}
|
|
4
|
-
export declare const DEFAULTS: Prefs;
|
|
5
7
|
/** Preferences live beside opencode's own config, XDG layout honoured. */
|
|
6
8
|
export declare function prefsPath(env?: NodeJS.ProcessEnv): string;
|
|
7
|
-
/** Only a literal `true` enables colour
|
|
9
|
+
/** Only a literal `true` enables colour; an unknown plan falls back to `go`. */
|
|
8
10
|
export declare function parsePrefs(raw: unknown): Prefs;
|
|
9
|
-
/** Env override, then the file, then the default. Cached for `
|
|
11
|
+
/** Env override, then the file, then the default. Cached for `PREFS_CACHE_MS`. */
|
|
10
12
|
export declare function loadPrefs(env?: NodeJS.ProcessEnv, now?: number): Prefs;
|
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: `allowance` is 0 by convention. */
|
|
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,13 @@ 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` 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.
|
|
124
|
+
*/
|
|
125
|
+
export declare function allowanceLabel(limit: number, free?: boolean): string;
|
|
115
126
|
/** Rate scale needs more precision than credit scale ($0.0036). */
|
|
116
127
|
export declare function rate(value: number): string;
|
|
117
128
|
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,28 @@ 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
|
-
var DEFAULT_ARGS = ["-1", "--plain"];
|
|
13
|
-
var SUBCOMMANDS = new Set(["plans", "models", "daily", "hourly", "model", "session", "statusline", "config"]);
|
|
14
29
|
function splitCliArgs(input) {
|
|
15
30
|
const out = [];
|
|
16
31
|
let cur = "";
|
|
@@ -52,7 +67,7 @@ function splitCliArgs(input) {
|
|
|
52
67
|
function buildCmduseArgs(arg) {
|
|
53
68
|
const tokens = splitCliArgs(arg);
|
|
54
69
|
if (!tokens.length)
|
|
55
|
-
return DEFAULT_ARGS;
|
|
70
|
+
return [...DEFAULT_ARGS];
|
|
56
71
|
if (tokens.some((t) => SUBCOMMANDS.has(t)))
|
|
57
72
|
return tokens;
|
|
58
73
|
return [...DEFAULT_ARGS, ...tokens];
|
|
@@ -118,6 +133,13 @@ async function runCmduse(arg, opts = {}) {
|
|
|
118
133
|
});
|
|
119
134
|
}
|
|
120
135
|
|
|
136
|
+
// src/constants/providers.ts
|
|
137
|
+
var PROVIDER_ID_PREFIX = "command-code";
|
|
138
|
+
var GO_PROVIDER_ID = "opencode-go";
|
|
139
|
+
var ZEN_PROVIDER_ID = "opencode";
|
|
140
|
+
var ZEN_PROVIDER_PREFIX = "opencode-zen";
|
|
141
|
+
var TUI_ID = "command-code.tui";
|
|
142
|
+
|
|
121
143
|
// src/sidebar/panel.tsx
|
|
122
144
|
import { createComponent as _$createComponent } from "@opentui/solid";
|
|
123
145
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
@@ -451,7 +473,7 @@ var gating_default = {
|
|
|
451
473
|
}
|
|
452
474
|
};
|
|
453
475
|
|
|
454
|
-
// src/gating.ts
|
|
476
|
+
// src/constants/gating.ts
|
|
455
477
|
var MODEL_CATEGORIES = gating_default.categories;
|
|
456
478
|
var PLAN_RULES = gating_default.plans;
|
|
457
479
|
var HARD_BLOCKED = gating_default.hardBlocked;
|
|
@@ -459,6 +481,28 @@ var KNOWN_MODELS = gating_default.knownModels;
|
|
|
459
481
|
var MODEL_ALIASES = gating_default.aliases;
|
|
460
482
|
var GATE_EXTRACTED_AT = gating_default.extractedAt;
|
|
461
483
|
var GATE_CLI_VERSION = gating_default.cliVersion;
|
|
484
|
+
|
|
485
|
+
// src/constants/sidebar.ts
|
|
486
|
+
var ROW_WIDTH = 37;
|
|
487
|
+
var TIER_DISPLAY = {
|
|
488
|
+
opensource: "open source",
|
|
489
|
+
premium: "premium"
|
|
490
|
+
};
|
|
491
|
+
var OC_PLANS = ["go", "go-plus"];
|
|
492
|
+
var OC_PLAN_DEFAULT = "go";
|
|
493
|
+
var GO_WINDOW_SHARE = {
|
|
494
|
+
fiveHour: 0.2,
|
|
495
|
+
weekly: 0.5,
|
|
496
|
+
month: 1
|
|
497
|
+
};
|
|
498
|
+
var FIVE_HOUR_SECS = 5 * 3600;
|
|
499
|
+
var WEEKLY_SECS = 7 * 86400;
|
|
500
|
+
var PREF_DEFAULTS = {
|
|
501
|
+
colors: false,
|
|
502
|
+
ocPlan: OC_PLAN_DEFAULT
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// src/gating.ts
|
|
462
506
|
function findKnown(s) {
|
|
463
507
|
const k = s.toLowerCase();
|
|
464
508
|
return KNOWN_MODELS.find((m) => m.toLowerCase() === k);
|
|
@@ -475,8 +519,6 @@ function canonicalizeModelId(model) {
|
|
|
475
519
|
}
|
|
476
520
|
|
|
477
521
|
// src/sidebar/windows.ts
|
|
478
|
-
var FIVE_HOUR_SECS = 5 * 3600;
|
|
479
|
-
var WEEKLY_SECS = 7 * 86400;
|
|
480
522
|
function elapsedPct(resetAtMs, durSecs, nowSecs) {
|
|
481
523
|
if (typeof resetAtMs !== "number" || !Number.isFinite(resetAtMs))
|
|
482
524
|
return;
|
|
@@ -526,7 +568,6 @@ function pctTone(pct) {
|
|
|
526
568
|
return "warn";
|
|
527
569
|
return "ok";
|
|
528
570
|
}
|
|
529
|
-
var ROW_WIDTH = 37;
|
|
530
571
|
function separator() {
|
|
531
572
|
return ["\u2500".repeat(ROW_WIDTH), "", "muted"];
|
|
532
573
|
}
|
|
@@ -541,14 +582,13 @@ function parts(row) {
|
|
|
541
582
|
return [` ${row[1]}`, ""];
|
|
542
583
|
return [row[1] ? `${row[0]}: ` : row[0], row[1]];
|
|
543
584
|
}
|
|
544
|
-
var TIER_DISPLAY = {
|
|
545
|
-
opensource: "open source",
|
|
546
|
-
premium: "premium"
|
|
547
|
-
};
|
|
548
585
|
function money(value) {
|
|
549
586
|
const rounded = Math.round(value * 100) / 100;
|
|
550
587
|
return `$${Number.isInteger(rounded) ? rounded : rounded.toFixed(2)}`;
|
|
551
588
|
}
|
|
589
|
+
function allowanceLabel(limit, free) {
|
|
590
|
+
return free === true || limit <= 0 ? "\u221E" : `${money(limit)}/mo`;
|
|
591
|
+
}
|
|
552
592
|
function rate(value) {
|
|
553
593
|
if (value === 0)
|
|
554
594
|
return "$0";
|
|
@@ -656,7 +696,7 @@ function modelRows(meta, usage, session, fallbackName) {
|
|
|
656
696
|
else if (meta.minPlan)
|
|
657
697
|
rows.push(["Min plan", meta.minPlan, "base"]);
|
|
658
698
|
if (typeof meta.allowance === "number")
|
|
659
|
-
rows.push(["Allowance",
|
|
699
|
+
rows.push(["Allowance", allowanceLabel(meta.allowance, meta.free), "base"]);
|
|
660
700
|
if (meta.deal) {
|
|
661
701
|
const ends = meta.deal.ends?.match(/([A-Z][a-z]{2})[a-z]*\s+(\d{1,2})/);
|
|
662
702
|
rows.push(["Deal", ends ? `${meta.deal.badge} \xB7 ends ${ends[1]} ${ends[2]}` : meta.deal.badge, "ok"]);
|
|
@@ -790,41 +830,71 @@ function Panel(props) {
|
|
|
790
830
|
|
|
791
831
|
// src/sidebar/prefs.ts
|
|
792
832
|
import { readFileSync } from "fs";
|
|
793
|
-
import { homedir } from "os";
|
|
794
|
-
import { join } from "path";
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
833
|
+
import { homedir as homedir2 } from "os";
|
|
834
|
+
import { join as join2 } from "path";
|
|
835
|
+
|
|
836
|
+
// src/constants/paths.ts
|
|
837
|
+
var CACHE_DIR = "command-code";
|
|
838
|
+
function ccCatalogFile(plan) {
|
|
839
|
+
return `cc-catalog-${plan}.json`;
|
|
840
|
+
}
|
|
841
|
+
var OPENCODE_DIR = "opencode";
|
|
842
|
+
var OPENCODE_DB_FILE = "opencode.db";
|
|
843
|
+
var PREFS_FILE = "command-code.json";
|
|
844
|
+
|
|
845
|
+
// src/constants/timing.ts
|
|
846
|
+
var POLL_MS = 5000;
|
|
847
|
+
var CHILD_TIMEOUT_MS = 30000;
|
|
848
|
+
var MODELS_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
849
|
+
var CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
850
|
+
var PREFS_CACHE_MS = 5000;
|
|
851
|
+
|
|
852
|
+
// src/sidebar/prefs.ts
|
|
799
853
|
var cached;
|
|
800
854
|
function prefsPath(env = process.env) {
|
|
801
|
-
const base = env.XDG_CONFIG_HOME ??
|
|
802
|
-
return
|
|
855
|
+
const base = env.XDG_CONFIG_HOME ?? join2(homedir2(), ".config");
|
|
856
|
+
return join2(base, OPENCODE_DIR, PREFS_FILE);
|
|
857
|
+
}
|
|
858
|
+
function planFrom(value, source) {
|
|
859
|
+
if (value === undefined)
|
|
860
|
+
return;
|
|
861
|
+
if (typeof value === "string" && OC_PLANS.includes(value)) {
|
|
862
|
+
return value;
|
|
863
|
+
}
|
|
864
|
+
console.warn(`[command-code] ignoring unknown OpenCode plan ${JSON.stringify(value)} (${source}); using "${OC_PLAN_DEFAULT}"`);
|
|
865
|
+
return;
|
|
803
866
|
}
|
|
804
867
|
function parsePrefs(raw) {
|
|
805
|
-
const
|
|
868
|
+
const obj = raw;
|
|
806
869
|
return {
|
|
807
|
-
colors:
|
|
870
|
+
colors: obj?.colors === true,
|
|
871
|
+
ocPlan: planFrom(obj?.ocPlan, "file") ?? OC_PLAN_DEFAULT
|
|
808
872
|
};
|
|
809
873
|
}
|
|
810
874
|
function loadPrefs(env = process.env, now = Date.now()) {
|
|
811
|
-
if (cached && now - cached.at <
|
|
875
|
+
if (cached && now - cached.at < PREFS_CACHE_MS)
|
|
812
876
|
return cached.prefs;
|
|
877
|
+
let prefs = PREF_DEFAULTS;
|
|
878
|
+
try {
|
|
879
|
+
prefs = parsePrefs(JSON.parse(readFileSync(prefsPath(env), "utf8")));
|
|
880
|
+
} catch {}
|
|
813
881
|
const flag = env.CMD_COLORS;
|
|
814
|
-
let prefs = DEFAULTS;
|
|
815
882
|
if (flag === "1" || flag === "true")
|
|
816
883
|
prefs = {
|
|
884
|
+
...prefs,
|
|
817
885
|
colors: true
|
|
818
886
|
};
|
|
819
887
|
else if (flag === "0" || flag === "false")
|
|
820
888
|
prefs = {
|
|
889
|
+
...prefs,
|
|
821
890
|
colors: false
|
|
822
891
|
};
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
892
|
+
const envPlan = planFrom(env.CMD_OC_PLAN, "CMD_OC_PLAN");
|
|
893
|
+
if (envPlan !== undefined)
|
|
894
|
+
prefs = {
|
|
895
|
+
...prefs,
|
|
896
|
+
ocPlan: envPlan
|
|
897
|
+
};
|
|
828
898
|
cached = {
|
|
829
899
|
at: now,
|
|
830
900
|
prefs
|
|
@@ -1950,15 +2020,10 @@ function minPlan(id) {
|
|
|
1950
2020
|
// src/sidebar/data.ts
|
|
1951
2021
|
import { spawn as spawn2 } from "child_process";
|
|
1952
2022
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
1953
|
-
import { homedir as
|
|
1954
|
-
import { dirname, join as
|
|
2023
|
+
import { homedir as homedir3 } from "os";
|
|
2024
|
+
import { dirname, join as join3 } from "path";
|
|
1955
2025
|
|
|
1956
2026
|
// src/sidebar/zen.ts
|
|
1957
|
-
var GO_WINDOW_SHARE = {
|
|
1958
|
-
fiveHour: 0.2,
|
|
1959
|
-
weekly: 0.5,
|
|
1960
|
-
month: 1
|
|
1961
|
-
};
|
|
1962
2027
|
function totals(value) {
|
|
1963
2028
|
const v = value ?? {};
|
|
1964
2029
|
const num = (key) => typeof v[key] === "number" ? v[key] : 0;
|
|
@@ -2035,7 +2100,7 @@ function zenRows(usage, meta, session, fallbackName) {
|
|
|
2035
2100
|
if (!meta)
|
|
2036
2101
|
return rows;
|
|
2037
2102
|
if (typeof limit === "number") {
|
|
2038
|
-
rows.push(["Allowance",
|
|
2103
|
+
rows.push(["Allowance", allowanceLabel(limit, oc?.free), "base"]);
|
|
2039
2104
|
}
|
|
2040
2105
|
if (oc?.rates) {
|
|
2041
2106
|
const cache = [`cache read ${rate(oc.rates.cacheRead)}`];
|
|
@@ -2055,14 +2120,8 @@ function zenRows(usage, meta, session, fallbackName) {
|
|
|
2055
2120
|
|
|
2056
2121
|
// src/sidebar/data.ts
|
|
2057
2122
|
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;
|
|
2123
|
+
var MPC = MPC_BIN_CANDIDATES;
|
|
2124
|
+
var OCUSE = [process.env.OCUSE_BIN, ...OCUSE_BIN_CANDIDATES].filter((bin) => Boolean(bin));
|
|
2066
2125
|
function run(bin, args) {
|
|
2067
2126
|
return new Promise((resolve, reject) => {
|
|
2068
2127
|
const child = spawn2(bin, args, SPAWN_OPTIONS);
|
|
@@ -2147,6 +2206,7 @@ function parseMpcJson(text) {
|
|
|
2147
2206
|
key: row.key ?? modelKey(row.name),
|
|
2148
2207
|
name: row.name,
|
|
2149
2208
|
allowance: cc?.allowance,
|
|
2209
|
+
free: cc?.free === true ? true : undefined,
|
|
2150
2210
|
rates: cc?.pricing,
|
|
2151
2211
|
intelligence: cc?.ability ?? undefined,
|
|
2152
2212
|
tps: cc?.tps ?? undefined,
|
|
@@ -2158,6 +2218,7 @@ function parseMpcJson(text) {
|
|
|
2158
2218
|
provider: oc.provider,
|
|
2159
2219
|
plan: oc.plan,
|
|
2160
2220
|
allowance: oc.allowance,
|
|
2221
|
+
free: oc.free === true ? true : undefined,
|
|
2161
2222
|
rates: oc.pricing,
|
|
2162
2223
|
ability: oc.ability ?? undefined,
|
|
2163
2224
|
tps: oc.tps ?? undefined
|
|
@@ -2168,20 +2229,26 @@ function parseMpcJson(text) {
|
|
|
2168
2229
|
}
|
|
2169
2230
|
return meta;
|
|
2170
2231
|
}
|
|
2171
|
-
function cachePath() {
|
|
2172
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
2173
|
-
return
|
|
2232
|
+
function cachePath(plan) {
|
|
2233
|
+
const base = process.env.XDG_CACHE_HOME ?? join3(homedir3(), ".cache");
|
|
2234
|
+
return join3(base, CACHE_DIR, ccCatalogFile(plan));
|
|
2174
2235
|
}
|
|
2175
|
-
|
|
2236
|
+
function metaArgs(plan) {
|
|
2237
|
+
return ["--json", "--shape", "off", "--oc-plan", plan];
|
|
2238
|
+
}
|
|
2239
|
+
function zenArgs(plan) {
|
|
2240
|
+
return ["-1", "--json", "--plan", plan];
|
|
2241
|
+
}
|
|
2242
|
+
async function loadMeta(plan = OC_PLAN_DEFAULT) {
|
|
2176
2243
|
try {
|
|
2177
|
-
const cached = JSON.parse(await readFile(cachePath(), "utf8"));
|
|
2178
|
-
if (cached.fetchedAt && Date.now() - cached.fetchedAt <
|
|
2244
|
+
const cached = JSON.parse(await readFile(cachePath(plan), "utf8"));
|
|
2245
|
+
if (cached.fetchedAt && Date.now() - cached.fetchedAt < CATALOG_CACHE_TTL_MS) {
|
|
2179
2246
|
return new Map(cached.rows ?? []);
|
|
2180
2247
|
}
|
|
2181
2248
|
} catch {}
|
|
2182
|
-
const meta = parseMpcJson(await runFirst(MPC,
|
|
2249
|
+
const meta = parseMpcJson(await runFirst(MPC, metaArgs(plan)));
|
|
2183
2250
|
try {
|
|
2184
|
-
const path = cachePath();
|
|
2251
|
+
const path = cachePath(plan);
|
|
2185
2252
|
await mkdir(dirname(path), {
|
|
2186
2253
|
recursive: true
|
|
2187
2254
|
});
|
|
@@ -2195,19 +2262,19 @@ async function loadMeta() {
|
|
|
2195
2262
|
async function loadUsage() {
|
|
2196
2263
|
return requireSnapshot(parseUsageJson(await runFirst(CMDUSE, ["-1", "--json", "--plain"])));
|
|
2197
2264
|
}
|
|
2198
|
-
async function loadZen() {
|
|
2199
|
-
return parseZenJson(await runFirst(OCUSE,
|
|
2265
|
+
async function loadZen(plan = OC_PLAN_DEFAULT) {
|
|
2266
|
+
return parseZenJson(await runFirst(OCUSE, zenArgs(plan)));
|
|
2200
2267
|
}
|
|
2201
2268
|
|
|
2202
2269
|
// src/sidebar/usageDb.ts
|
|
2203
2270
|
import { Database } from "bun:sqlite";
|
|
2204
|
-
import { homedir as
|
|
2205
|
-
import { join as
|
|
2271
|
+
import { homedir as homedir4 } from "os";
|
|
2272
|
+
import { join as join4 } from "path";
|
|
2206
2273
|
function usageDbPath() {
|
|
2207
2274
|
if (process.env.OPENCODE_DB)
|
|
2208
2275
|
return process.env.OPENCODE_DB;
|
|
2209
|
-
const base = process.env.XDG_DATA_HOME ??
|
|
2210
|
-
return
|
|
2276
|
+
const base = process.env.XDG_DATA_HOME ?? join4(homedir4(), ".local", "share");
|
|
2277
|
+
return join4(base, OPENCODE_DIR, OPENCODE_DB_FILE);
|
|
2211
2278
|
}
|
|
2212
2279
|
function usage(value) {
|
|
2213
2280
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
@@ -2333,15 +2400,14 @@ function loadSessionUsage(sessionID, path = usageDbPath()) {
|
|
|
2333
2400
|
}
|
|
2334
2401
|
|
|
2335
2402
|
// src/sidebar/useRows.ts
|
|
2336
|
-
var POLL_MS = 5000;
|
|
2337
2403
|
function sessionKind(providerID) {
|
|
2338
2404
|
if (!providerID)
|
|
2339
2405
|
return "unknown";
|
|
2340
|
-
if (providerID.startsWith(
|
|
2406
|
+
if (providerID.startsWith(PROVIDER_ID_PREFIX))
|
|
2341
2407
|
return "ours";
|
|
2342
|
-
if (providerID.startsWith(
|
|
2408
|
+
if (providerID.startsWith(GO_PROVIDER_ID))
|
|
2343
2409
|
return "go";
|
|
2344
|
-
if (providerID ===
|
|
2410
|
+
if (providerID === ZEN_PROVIDER_ID || providerID.startsWith(ZEN_PROVIDER_PREFIX))
|
|
2345
2411
|
return "zen";
|
|
2346
2412
|
return "other";
|
|
2347
2413
|
}
|
|
@@ -2396,7 +2462,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2396
2462
|
const [sessionUsage, setSessionUsage] = createSignal();
|
|
2397
2463
|
const [wasOurs, setWasOurs] = createSignal(false);
|
|
2398
2464
|
const refreshMeta = () => {
|
|
2399
|
-
loadMeta().then((next) => {
|
|
2465
|
+
loadMeta(loadPrefs().ocPlan).then((next) => {
|
|
2400
2466
|
rememberMeta(next);
|
|
2401
2467
|
if (next.size > 0)
|
|
2402
2468
|
setMeta(next);
|
|
@@ -2431,7 +2497,7 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2431
2497
|
setUsage(usageRows(snapshot));
|
|
2432
2498
|
refreshModelUsage(activeModelId());
|
|
2433
2499
|
} else if (kind === "go" || kind === "zen") {
|
|
2434
|
-
setZen(await loadZen());
|
|
2500
|
+
setZen(await loadZen(loadPrefs().ocPlan));
|
|
2435
2501
|
}
|
|
2436
2502
|
} catch {} finally {
|
|
2437
2503
|
inFlight = false;
|
|
@@ -2481,7 +2547,6 @@ function useRows(activeModelId, providerID, sessionID) {
|
|
|
2481
2547
|
}
|
|
2482
2548
|
|
|
2483
2549
|
// src/tui.tsx
|
|
2484
|
-
var ID = "command-code.tui";
|
|
2485
2550
|
var themed = (colors) => loadPrefs().colors ? colors : mono(colors);
|
|
2486
2551
|
function PanelV1(props) {
|
|
2487
2552
|
const current = () => props.api.state.session.get(props.sessionID)?.model;
|
|
@@ -2520,7 +2585,7 @@ function PanelV2(props) {
|
|
|
2520
2585
|
});
|
|
2521
2586
|
}
|
|
2522
2587
|
var commandCodeTui = {
|
|
2523
|
-
id:
|
|
2588
|
+
id: TUI_ID,
|
|
2524
2589
|
setup(ctx) {
|
|
2525
2590
|
ctx.ui.slot({
|
|
2526
2591
|
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.4.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"
|