@jeffreyjyz/opencode-command-code 0.1.2 → 0.1.4
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/gating.d.ts +4 -0
- package/dist/index.js +71 -12
- package/dist/usage.d.ts +13 -1
- package/package.json +1 -1
package/dist/gating.d.ts
CHANGED
|
@@ -11,3 +11,7 @@ export declare const PLAN_RULES: Record<string, {
|
|
|
11
11
|
* tracks category via serving lane, not per-model plan entitlements. */
|
|
12
12
|
export declare const HARD_BLOCKED: Record<string, string[]>;
|
|
13
13
|
export declare function canonicalizeModelId(model: string): string;
|
|
14
|
+
/** Bare model id with any provider qualifier stripped: everything after the
|
|
15
|
+
* FIRST colon ("anthropic:claude-opus-5" → "claude-opus-5"). Matches
|
|
16
|
+
* cmduse-core's `bare_model`; a model id that itself contains ':' keeps it. */
|
|
17
|
+
export declare function bareModel(blocked: string): string;
|
package/dist/index.js
CHANGED
|
@@ -325,6 +325,10 @@ function canonicalizeModelId(model) {
|
|
|
325
325
|
return findKnown(aliased) ?? model;
|
|
326
326
|
return findKnown(stripDate(model)) ?? model;
|
|
327
327
|
}
|
|
328
|
+
function bareModel(blocked) {
|
|
329
|
+
const i = blocked.indexOf(":");
|
|
330
|
+
return i >= 0 ? blocked.slice(i + 1) : blocked;
|
|
331
|
+
}
|
|
328
332
|
|
|
329
333
|
// src/access.ts
|
|
330
334
|
function evaluateModelAccess(model, plan) {
|
|
@@ -342,10 +346,7 @@ function evaluateModelAccess(model, plan) {
|
|
|
342
346
|
const category = MODEL_CATEGORIES[canonical] ?? siblingCategory(canonical);
|
|
343
347
|
if (!category)
|
|
344
348
|
return { allowed: true };
|
|
345
|
-
const blocked = rules.blockedModels.some((b) =>
|
|
346
|
-
const bare = b.includes(":") ? b.slice(b.indexOf(":") + 1) : b;
|
|
347
|
-
return bare.toLowerCase() === canonical.toLowerCase();
|
|
348
|
-
});
|
|
349
|
+
const blocked = rules.blockedModels.some((b) => bareModel(b).toLowerCase() === canonical.toLowerCase());
|
|
349
350
|
if (blocked)
|
|
350
351
|
return { allowed: false };
|
|
351
352
|
if (!rules.allowedCategories.includes(category))
|
|
@@ -393,11 +394,11 @@ async function loadModels(key) {
|
|
|
393
394
|
contextLength: m.context_length ?? 0
|
|
394
395
|
}));
|
|
395
396
|
cache = { at: Date.now(), models };
|
|
396
|
-
} catch {
|
|
397
|
+
} catch (e) {
|
|
397
398
|
if (cache) {
|
|
398
399
|
models = cache.models;
|
|
399
400
|
} else {
|
|
400
|
-
throw new Error("Could not fetch Command Code model list (https://api.commandcode.ai/provider/v1/models). Check network/API key.");
|
|
401
|
+
throw new Error("Could not fetch Command Code model list (https://api.commandcode.ai/provider/v1/models). Check network/API key.", { cause: e });
|
|
401
402
|
}
|
|
402
403
|
}
|
|
403
404
|
}
|
|
@@ -451,6 +452,8 @@ var plans_default = {
|
|
|
451
452
|
|
|
452
453
|
// src/usage.ts
|
|
453
454
|
var BAR_WIDTH = 12;
|
|
455
|
+
var FIVE_HOUR_SECS = 5 * 3600;
|
|
456
|
+
var WEEKLY_SECS = 7 * 86400;
|
|
454
457
|
function money(v) {
|
|
455
458
|
return `$${v.toFixed(2)}`;
|
|
456
459
|
}
|
|
@@ -487,6 +490,50 @@ function relTime(resetAtMs, nowSecs) {
|
|
|
487
490
|
return `${m}m`;
|
|
488
491
|
return "<1m";
|
|
489
492
|
}
|
|
493
|
+
function duration(secs) {
|
|
494
|
+
const d = Math.floor(secs / 86400);
|
|
495
|
+
const h = Math.floor(secs % 86400 / 3600);
|
|
496
|
+
const m = Math.floor(secs % 3600 / 60);
|
|
497
|
+
if (d > 0)
|
|
498
|
+
return `${d}d ${h}h`;
|
|
499
|
+
if (h > 0)
|
|
500
|
+
return `${h}h ${m}m`;
|
|
501
|
+
if (m > 0)
|
|
502
|
+
return `${m}m`;
|
|
503
|
+
return "<1m";
|
|
504
|
+
}
|
|
505
|
+
function elapsedPct(resetAtMs, durSecs, nowSecs) {
|
|
506
|
+
if (resetAtMs === undefined)
|
|
507
|
+
return;
|
|
508
|
+
const resetSecs = Math.floor(resetAtMs / 1000);
|
|
509
|
+
if (resetSecs < durSecs)
|
|
510
|
+
return;
|
|
511
|
+
const start = resetSecs - durSecs;
|
|
512
|
+
if (nowSecs < start)
|
|
513
|
+
return;
|
|
514
|
+
const pct = Math.min(Math.max((nowSecs - start) / durSecs * 100, 0), 100);
|
|
515
|
+
return Math.round(pct);
|
|
516
|
+
}
|
|
517
|
+
function paceEta(resetAtMs, durSecs, used, cap, nowSecs) {
|
|
518
|
+
if (resetAtMs === undefined)
|
|
519
|
+
return;
|
|
520
|
+
const resetSecs = Math.floor(resetAtMs / 1000);
|
|
521
|
+
if (resetSecs < durSecs)
|
|
522
|
+
return;
|
|
523
|
+
const start = resetSecs - durSecs;
|
|
524
|
+
if (nowSecs <= start || nowSecs >= resetSecs)
|
|
525
|
+
return;
|
|
526
|
+
const elapsed = nowSecs - start;
|
|
527
|
+
if (elapsed / durSecs < 0.1)
|
|
528
|
+
return;
|
|
529
|
+
const rate = used / elapsed;
|
|
530
|
+
if (rate <= 0 || used >= cap)
|
|
531
|
+
return;
|
|
532
|
+
const secsToCap = (cap - used) / rate;
|
|
533
|
+
if (secsToCap >= resetSecs - nowSecs)
|
|
534
|
+
return;
|
|
535
|
+
return secsToCap;
|
|
536
|
+
}
|
|
490
537
|
function parseIsoUtc(s) {
|
|
491
538
|
const m = s.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|[+-]\d{2}:?\d{2})?$/);
|
|
492
539
|
if (!m)
|
|
@@ -517,9 +564,13 @@ function planName(planId) {
|
|
|
517
564
|
}
|
|
518
565
|
return DEFAULT_NAME;
|
|
519
566
|
}
|
|
520
|
-
function windowLine(label, w, nowSecs) {
|
|
567
|
+
function windowLine(label, w, nowSecs, durSecs) {
|
|
521
568
|
const status = w.exceeded ? " \xB7 **LIMIT EXCEEDED**" : "";
|
|
522
|
-
|
|
569
|
+
const elapsed = durSecs !== undefined ? elapsedPct(w.resetAt, durSecs, nowSecs) : undefined;
|
|
570
|
+
const thru = elapsed !== undefined ? ` \xB7 window ${elapsed}% elapsed` : "";
|
|
571
|
+
const eta = durSecs !== undefined ? paceEta(w.resetAt, durSecs, w.used, w.cap, nowSecs) : undefined;
|
|
572
|
+
const pace = eta !== undefined ? ` \xB7 **on pace to hit cap in ${duration(eta)}**` : "";
|
|
573
|
+
return `**${label}** \`${bar(w.used, w.cap)}\` ${money(w.used)} of ${money(w.cap)} (${pctStr(w.used, w.cap)}) \xB7 resets in ${relTime(w.resetAt, nowSecs)}${thru}${pace}${status}
|
|
523
574
|
`;
|
|
524
575
|
}
|
|
525
576
|
function plansTable(current) {
|
|
@@ -562,14 +613,16 @@ async function renderUsage(key) {
|
|
|
562
613
|
if (cap) {
|
|
563
614
|
const used = Math.min(Math.max(cap - cr.credits.monthlyCredits, 0), cap);
|
|
564
615
|
const resetAt = sub.currentPeriodEnd ? parseIsoUtc(sub.currentPeriodEnd) : undefined;
|
|
565
|
-
|
|
616
|
+
const start = sub.currentPeriodStart ? parseIsoUtc(sub.currentPeriodStart) : undefined;
|
|
617
|
+
const dur = start !== undefined && resetAt !== undefined ? Math.max(Math.floor((resetAt - start) / 1000), 1) : undefined;
|
|
618
|
+
lines.push(windowLine("Monthly", { used, cap, resetAt }, nowSecs, dur));
|
|
566
619
|
}
|
|
567
620
|
const five = cr.windowLimits?.fiveHour;
|
|
568
621
|
const weekly = cr.windowLimits?.weekly;
|
|
569
622
|
if (five)
|
|
570
|
-
lines.push(windowLine("5-hour", five, nowSecs));
|
|
623
|
+
lines.push(windowLine("5-hour", five, nowSecs, FIVE_HOUR_SECS));
|
|
571
624
|
if (weekly)
|
|
572
|
-
lines.push(windowLine("Weekly", weekly, nowSecs));
|
|
625
|
+
lines.push(windowLine("Weekly", weekly, nowSecs, WEEKLY_SECS));
|
|
573
626
|
if (!five && !weekly)
|
|
574
627
|
lines.push(`No rolling windows on this plan (pay-as-you-go credits only).
|
|
575
628
|
`);
|
|
@@ -662,8 +715,14 @@ var CommandCodePlugin = async (_input) => {
|
|
|
662
715
|
let openKey;
|
|
663
716
|
try {
|
|
664
717
|
openKey = await resolveKey();
|
|
665
|
-
split = await loadModels(openKey);
|
|
666
718
|
} catch {}
|
|
719
|
+
if (openKey) {
|
|
720
|
+
try {
|
|
721
|
+
split = await loadModels(openKey);
|
|
722
|
+
} catch (e) {
|
|
723
|
+
console.warn("[command-code] model list unavailable:", e);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
667
726
|
const claudeDefs = split ? toModelDefs(split.claude, "command-code-anthropic", "@ai-sdk/anthropic") : {};
|
|
668
727
|
const openDefs = split ? toModelDefs(split.open, "command-code-openai", "@ai-sdk/openai-compatible", { field: "reasoning_content" }) : {};
|
|
669
728
|
const userAnthropic = existing("command-code-anthropic");
|
package/dist/usage.d.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { type Window } from "./api";
|
|
2
|
+
export declare const FIVE_HOUR_SECS: number;
|
|
3
|
+
export declare const WEEKLY_SECS: number;
|
|
2
4
|
export declare function money(v: number): string;
|
|
3
5
|
export declare function compact(n: number): string;
|
|
4
6
|
export declare function bar(used: number, cap: number): string;
|
|
5
7
|
export declare function pctStr(used: number, cap: number): string;
|
|
6
8
|
export declare function relTime(resetAtMs: number | undefined, nowSecs: number): string;
|
|
9
|
+
/** Compact "Xh Ym" / "Xd Yh" for a span of seconds. Pure duration — use this
|
|
10
|
+
* (not relTime) for ETAs/countdowns. Mirrors cmduse_core::duration. */
|
|
11
|
+
export declare function duration(secs: number): string;
|
|
12
|
+
/** % of a rolling window elapsed; window length = durSecs, ends at resetAtMs.
|
|
13
|
+
* undefined when the window hasn't started or the reset is unknown. */
|
|
14
|
+
export declare function elapsedPct(resetAtMs: number | undefined, durSecs: number, nowSecs: number): number | undefined;
|
|
15
|
+
/** Seconds until spend hits cap at the current rate, if before the reset.
|
|
16
|
+
* Suppressed before 10% of the window has elapsed (flat-rate projection is
|
|
17
|
+
* unreliable that early). undefined = no warning. */
|
|
18
|
+
export declare function paceEta(resetAtMs: number | undefined, durSecs: number, used: number, cap: number, nowSecs: number): number | undefined;
|
|
7
19
|
/** ISO 8601 → epoch ms. Handles trailing Z or a ±HH:MM / ±HHMM offset. */
|
|
8
20
|
export declare function parseIsoUtc(s: string): number | undefined;
|
|
9
21
|
export declare function planMonthlyCap(planId: string): number | undefined;
|
|
10
22
|
export declare function planName(planId: string): string;
|
|
11
|
-
export declare function windowLine(label: string, w: Window, nowSecs: number): string;
|
|
23
|
+
export declare function windowLine(label: string, w: Window, nowSecs: number, durSecs?: number): string;
|
|
12
24
|
export declare function plansTable(current: string): string;
|
|
13
25
|
/** Fetch billing data in parallel and render the markdown dashboard. */
|
|
14
26
|
export declare function renderUsage(key: string): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jeffreyjyz/opencode-command-code",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|