@slkiser/opencode-quota 3.8.8 → 3.10.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/README.md +256 -70
- package/dist/bin/opencode-quota.d.ts.map +1 -1
- package/dist/bin/opencode-quota.js +4 -1
- package/dist/bin/opencode-quota.js.map +1 -1
- package/dist/lib/cli-show.d.ts.map +1 -1
- package/dist/lib/cli-show.js +94 -3
- package/dist/lib/cli-show.js.map +1 -1
- package/dist/lib/command-handled.d.ts +2 -2
- package/dist/lib/command-handled.d.ts.map +1 -1
- package/dist/lib/command-handled.js +19 -4
- package/dist/lib/command-handled.js.map +1 -1
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +60 -5
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/cursor-pricing.js +1 -1
- package/dist/lib/cursor-pricing.js.map +1 -1
- package/dist/lib/init-installer.d.ts.map +1 -1
- package/dist/lib/init-installer.js +6 -2
- package/dist/lib/init-installer.js.map +1 -1
- package/dist/lib/ollama-cloud-config.d.ts +37 -0
- package/dist/lib/ollama-cloud-config.d.ts.map +1 -0
- package/dist/lib/ollama-cloud-config.js +148 -0
- package/dist/lib/ollama-cloud-config.js.map +1 -0
- package/dist/lib/ollama-cloud.d.ts +17 -0
- package/dist/lib/ollama-cloud.d.ts.map +1 -0
- package/dist/lib/ollama-cloud.js +150 -0
- package/dist/lib/ollama-cloud.js.map +1 -0
- package/dist/lib/opencode-go.d.ts +12 -4
- package/dist/lib/opencode-go.d.ts.map +1 -1
- package/dist/lib/opencode-go.js +90 -7
- package/dist/lib/opencode-go.js.map +1 -1
- package/dist/lib/opencode-sqlite.d.ts.map +1 -1
- package/dist/lib/opencode-sqlite.js +90 -8
- package/dist/lib/opencode-sqlite.js.map +1 -1
- package/dist/lib/provider-metadata.d.ts +1 -1
- package/dist/lib/provider-metadata.d.ts.map +1 -1
- package/dist/lib/provider-metadata.js +9 -10
- package/dist/lib/provider-metadata.js.map +1 -1
- package/dist/lib/quota-command-format.d.ts +2 -0
- package/dist/lib/quota-command-format.d.ts.map +1 -1
- package/dist/lib/quota-command-format.js +4 -3
- package/dist/lib/quota-command-format.js.map +1 -1
- package/dist/lib/quota-dialog-commands.d.ts +43 -0
- package/dist/lib/quota-dialog-commands.d.ts.map +1 -0
- package/dist/lib/quota-dialog-commands.js +677 -0
- package/dist/lib/quota-dialog-commands.js.map +1 -0
- package/dist/lib/quota-export-types.d.ts +69 -0
- package/dist/lib/quota-export-types.d.ts.map +1 -0
- package/dist/lib/quota-export-types.js +9 -0
- package/dist/lib/quota-export-types.js.map +1 -0
- package/dist/lib/quota-export.d.ts +42 -0
- package/dist/lib/quota-export.d.ts.map +1 -0
- package/dist/lib/quota-export.js +130 -0
- package/dist/lib/quota-export.js.map +1 -0
- package/dist/lib/quota-render-data.d.ts +3 -0
- package/dist/lib/quota-render-data.d.ts.map +1 -1
- package/dist/lib/quota-render-data.js +22 -15
- package/dist/lib/quota-render-data.js.map +1 -1
- package/dist/lib/quota-state.d.ts +12 -0
- package/dist/lib/quota-state.d.ts.map +1 -1
- package/dist/lib/quota-state.js +38 -2
- package/dist/lib/quota-state.js.map +1 -1
- package/dist/lib/quota-stats-format.d.ts +5 -0
- package/dist/lib/quota-stats-format.d.ts.map +1 -1
- package/dist/lib/quota-stats-format.js +48 -13
- package/dist/lib/quota-stats-format.js.map +1 -1
- package/dist/lib/quota-status.d.ts.map +1 -1
- package/dist/lib/quota-status.js +0 -17
- package/dist/lib/quota-status.js.map +1 -1
- package/dist/lib/tui-runtime.d.ts +27 -0
- package/dist/lib/tui-runtime.d.ts.map +1 -1
- package/dist/lib/tui-runtime.js +104 -27
- package/dist/lib/tui-runtime.js.map +1 -1
- package/dist/lib/types.d.ts +35 -7
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +4 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/zai.d.ts.map +1 -1
- package/dist/lib/zai.js +8 -1
- package/dist/lib/zai.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +6 -476
- package/dist/plugin.js.map +1 -1
- package/dist/providers/cursor.js +1 -1
- package/dist/providers/cursor.js.map +1 -1
- package/dist/providers/ollama-cloud.d.ts +9 -0
- package/dist/providers/ollama-cloud.d.ts.map +1 -0
- package/dist/providers/ollama-cloud.js +77 -0
- package/dist/providers/ollama-cloud.js.map +1 -0
- package/dist/providers/registry.js +2 -2
- package/dist/providers/registry.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.tsx +252 -1
- package/package.json +5 -2
- package/dist/lib/crof-config.d.ts +0 -15
- package/dist/lib/crof-config.d.ts.map +0 -1
- package/dist/lib/crof-config.js +0 -33
- package/dist/lib/crof-config.js.map +0 -1
- package/dist/lib/crof.d.ts +0 -13
- package/dist/lib/crof.d.ts.map +0 -1
- package/dist/lib/crof.js +0 -70
- package/dist/lib/crof.js.map +0 -1
- package/dist/providers/crof.d.ts +0 -6
- package/dist/providers/crof.d.ts.map +0 -1
- package/dist/providers/crof.js +0 -56
- package/dist/providers/crof.js.map +0 -1
package/dist/tui.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
} from "@opencode-ai/plugin/tui";
|
|
9
9
|
import { Show, createEffect, createSignal, onCleanup } from "solid-js";
|
|
10
10
|
|
|
11
|
+
import type { SessionTokenError } from "./lib/quota-status.js";
|
|
11
12
|
import type { CompactStatusState, HomeBottomState, SidebarPanelState } from "./lib/tui-panel-state.js";
|
|
12
13
|
|
|
13
14
|
import {
|
|
@@ -21,10 +22,20 @@ import {
|
|
|
21
22
|
} from "./lib/tui-panel-state.js";
|
|
22
23
|
import { getSidebarBodyLineColor } from "./lib/tui-line-style.js";
|
|
23
24
|
import {
|
|
25
|
+
createTuiQuotaClient,
|
|
26
|
+
getTuiRuntimeRootHints,
|
|
27
|
+
getTuiSessionModelMeta,
|
|
24
28
|
loadTuiHomeBottomStatus,
|
|
29
|
+
normalizeTuiSessionID,
|
|
25
30
|
loadTuiSessionQuotaSurfaces,
|
|
26
31
|
resolveTuiSurfaceRegistration,
|
|
32
|
+
writeTuiQuotaExportIfEnabled,
|
|
27
33
|
} from "./lib/tui-runtime.js";
|
|
34
|
+
import {
|
|
35
|
+
QUOTA_DIALOG_COMMANDS,
|
|
36
|
+
buildQuotaDialogCommandOutput,
|
|
37
|
+
type QuotaDialogCommandId,
|
|
38
|
+
} from "./lib/quota-dialog-commands.js";
|
|
28
39
|
|
|
29
40
|
const id = "@slkiser/opencode-quota";
|
|
30
41
|
// Place Quota near the top so variable-height built-in sections
|
|
@@ -36,6 +47,11 @@ const EVENT_REFRESH_DELAYS_MS = [150, 600] as const;
|
|
|
36
47
|
const MOUNT_RECOVERY_DELAYS_MS = [500, 1_500, 4_000] as const;
|
|
37
48
|
|
|
38
49
|
type TuiPromptRefCallback = (ref: TuiPromptRef | undefined) => void;
|
|
50
|
+
type DialogSize = "medium" | "large" | "xlarge";
|
|
51
|
+
|
|
52
|
+
type QuotaDialogCommandState = {
|
|
53
|
+
lastSessionTokenError?: SessionTokenError;
|
|
54
|
+
};
|
|
39
55
|
|
|
40
56
|
type SessionQuotaResource = {
|
|
41
57
|
sessionID: string;
|
|
@@ -222,6 +238,11 @@ function createHomeBottomResource(api: TuiPluginApi): HomeBottomResource {
|
|
|
222
238
|
.then((next) => {
|
|
223
239
|
if (disposed || currentVersion !== loadVersion) return;
|
|
224
240
|
setBottom(next);
|
|
241
|
+
// Fire-and-forget: write export file if enabled. A failed write must
|
|
242
|
+
// never affect TUI rendering, so log a warning and continue.
|
|
243
|
+
void writeTuiQuotaExportIfEnabled({ api }).catch((err) => {
|
|
244
|
+
console.warn(`[opencode-quota] quota export write failed: ${String(err)}`);
|
|
245
|
+
});
|
|
225
246
|
})
|
|
226
247
|
.catch(() => {
|
|
227
248
|
if (disposed || currentVersion !== loadVersion) return;
|
|
@@ -339,7 +360,8 @@ function SidebarContentView(props: { api: TuiPluginApi; sessionID: string }) {
|
|
|
339
360
|
};
|
|
340
361
|
|
|
341
362
|
const displayLines = () => {
|
|
342
|
-
if (
|
|
363
|
+
if (!hasDetailLines()) return lines();
|
|
364
|
+
if (collapsed()) return [];
|
|
343
365
|
return getSidebarPanelLinesExpanded(panel());
|
|
344
366
|
};
|
|
345
367
|
|
|
@@ -450,6 +472,233 @@ function HomeBottomView(props: { api: TuiPluginApi }) {
|
|
|
450
472
|
);
|
|
451
473
|
}
|
|
452
474
|
|
|
475
|
+
function getActiveTuiSessionID(api: TuiPluginApi): string | undefined {
|
|
476
|
+
const route = (api as any).route?.current;
|
|
477
|
+
if (route?.name !== "session" && route?.type !== "session") return undefined;
|
|
478
|
+
|
|
479
|
+
return normalizeTuiSessionID(
|
|
480
|
+
route.params?.sessionID ?? route.params?.session_id ?? route.params?.id ?? route.sessionID,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function getTuiCommandArguments(input: unknown): string | undefined {
|
|
485
|
+
if (!input || typeof input !== "object") return undefined;
|
|
486
|
+
const record = input as Record<string, unknown>;
|
|
487
|
+
for (const key of ["arguments", "args", "query"] as const) {
|
|
488
|
+
const value = record[key];
|
|
489
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
490
|
+
}
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function CommandLoadingDialog(props: { api: TuiPluginApi; title: string }) {
|
|
495
|
+
return (
|
|
496
|
+
<box gap={1}>
|
|
497
|
+
<text fg={props.api.theme.current.text}>
|
|
498
|
+
<b>{props.title}</b>
|
|
499
|
+
</text>
|
|
500
|
+
<text fg={props.api.theme.current.textMuted}>Loading deterministic local output…</text>
|
|
501
|
+
</box>
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function CommandOutputDialog(props: { api: TuiPluginApi; title: string; output: string }) {
|
|
506
|
+
const lines = () => props.output.split("\n");
|
|
507
|
+
const bodyHeight = () => Math.min(28, Math.max(6, lines().length));
|
|
508
|
+
return (
|
|
509
|
+
<box gap={1} width="100%" flexGrow={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
|
|
510
|
+
<text fg={props.api.theme.current.text}>
|
|
511
|
+
<b>{props.title}</b>
|
|
512
|
+
</text>
|
|
513
|
+
<scrollbox width="100%" flexGrow={1} minHeight={bodyHeight()} maxHeight={28}>
|
|
514
|
+
<box gap={0} width="100%" minWidth={0}>
|
|
515
|
+
{lines().map((line) => (
|
|
516
|
+
<text fg={props.api.theme.current.text} wrapMode="word" width="100%">
|
|
517
|
+
{line || " "}
|
|
518
|
+
</text>
|
|
519
|
+
))}
|
|
520
|
+
</box>
|
|
521
|
+
</scrollbox>
|
|
522
|
+
<text fg={props.api.theme.current.textMuted}>esc closes</text>
|
|
523
|
+
</box>
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function CommandErrorDialog(props: { api: TuiPluginApi; title: string; error: unknown }) {
|
|
528
|
+
const message = props.error instanceof Error ? props.error.message : String(props.error);
|
|
529
|
+
return (
|
|
530
|
+
<box gap={1}>
|
|
531
|
+
<text fg={props.api.theme.current.text}>
|
|
532
|
+
<b>{props.title}</b>
|
|
533
|
+
</text>
|
|
534
|
+
<text fg={props.api.theme.current.text}>OpenCode Quota command failed.</text>
|
|
535
|
+
<text fg={props.api.theme.current.textMuted} wrapMode="none">
|
|
536
|
+
{message || "Unknown error"}
|
|
537
|
+
</text>
|
|
538
|
+
<text fg={props.api.theme.current.textMuted}>esc closes</text>
|
|
539
|
+
</box>
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function TokensBetweenPromptDialog(props: {
|
|
544
|
+
api: TuiPluginApi;
|
|
545
|
+
onConfirm: (value: string) => void;
|
|
546
|
+
onCancel: () => void;
|
|
547
|
+
}) {
|
|
548
|
+
const DialogPrompt = (props.api as any).ui?.DialogPrompt;
|
|
549
|
+
if (DialogPrompt) {
|
|
550
|
+
return (
|
|
551
|
+
<DialogPrompt
|
|
552
|
+
title="OpenCode Quota Token Range"
|
|
553
|
+
placeholder="YYYY-MM-DD YYYY-MM-DD"
|
|
554
|
+
description={() => (
|
|
555
|
+
<text fg={props.api.theme.current.textMuted} wrapMode="none">
|
|
556
|
+
Enter start and end dates, for example: 2026-01-01 2026-01-15
|
|
557
|
+
</text>
|
|
558
|
+
)}
|
|
559
|
+
onConfirm={props.onConfirm}
|
|
560
|
+
onCancel={props.onCancel}
|
|
561
|
+
/>
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return (
|
|
566
|
+
<CommandOutputDialog
|
|
567
|
+
api={props.api}
|
|
568
|
+
title="OpenCode Quota Token Range"
|
|
569
|
+
output={
|
|
570
|
+
"Missing arguments for /tokens_between\n\nExpected: /tokens_between YYYY-MM-DD YYYY-MM-DD\nExample: /tokens_between 2026-01-01 2026-01-15"
|
|
571
|
+
}
|
|
572
|
+
/>
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function replaceDialog(api: TuiPluginApi, size: DialogSize, render: () => JSX.Element) {
|
|
577
|
+
const dialog = (api as any).ui?.dialog;
|
|
578
|
+
dialog?.replace?.(render);
|
|
579
|
+
// OpenCode's dialog.replace() resets size to medium; xlarge is the widest supported dialog.
|
|
580
|
+
dialog?.setSize?.(size);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function clearDialog(api: TuiPluginApi): void {
|
|
584
|
+
(api as any).ui?.dialog?.clear?.();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
async function runQuotaDialogCommandAsync(
|
|
588
|
+
api: TuiPluginApi,
|
|
589
|
+
command: QuotaDialogCommandId,
|
|
590
|
+
rawInput?: unknown,
|
|
591
|
+
state?: QuotaDialogCommandState,
|
|
592
|
+
): Promise<void> {
|
|
593
|
+
const spec = QUOTA_DIALOG_COMMANDS.find((item) => item.id === command)!;
|
|
594
|
+
const args = getTuiCommandArguments(rawInput);
|
|
595
|
+
const sessionID = getActiveTuiSessionID(api);
|
|
596
|
+
|
|
597
|
+
if (command === "tokens_between" && !args) {
|
|
598
|
+
replaceDialog(api, "medium", () => (
|
|
599
|
+
<TokensBetweenPromptDialog
|
|
600
|
+
api={api}
|
|
601
|
+
onCancel={() => clearDialog(api)}
|
|
602
|
+
onConfirm={(value) => {
|
|
603
|
+
const nextArgs = value.trim();
|
|
604
|
+
if (!nextArgs) {
|
|
605
|
+
replaceDialog(api, "large", () => (
|
|
606
|
+
<CommandOutputDialog
|
|
607
|
+
api={api}
|
|
608
|
+
title={spec.title}
|
|
609
|
+
output={
|
|
610
|
+
"Missing arguments for /tokens_between\n\nExpected: /tokens_between YYYY-MM-DD YYYY-MM-DD\nExample: /tokens_between 2026-01-01 2026-01-15"
|
|
611
|
+
}
|
|
612
|
+
/>
|
|
613
|
+
));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
runQuotaDialogCommand(api, command, { arguments: nextArgs }, state);
|
|
618
|
+
}}
|
|
619
|
+
/>
|
|
620
|
+
));
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
replaceDialog(api, spec.dialogSize, () => <CommandLoadingDialog api={api} title={spec.title} />);
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
const result = await buildQuotaDialogCommandOutput({
|
|
628
|
+
command,
|
|
629
|
+
arguments: args,
|
|
630
|
+
client: createTuiQuotaClient(api),
|
|
631
|
+
roots: getTuiRuntimeRootHints(api),
|
|
632
|
+
sessionID,
|
|
633
|
+
resolveSessionMeta: (id) => getTuiSessionModelMeta(api, id),
|
|
634
|
+
lastSessionTokenError: state?.lastSessionTokenError,
|
|
635
|
+
setLastSessionTokenError: state
|
|
636
|
+
? (error) => {
|
|
637
|
+
state.lastSessionTokenError = error;
|
|
638
|
+
}
|
|
639
|
+
: undefined,
|
|
640
|
+
log: async (message, extra) => {
|
|
641
|
+
await (api as any).client?.app?.log?.({
|
|
642
|
+
body: {
|
|
643
|
+
service: "quota-toast",
|
|
644
|
+
level: "debug",
|
|
645
|
+
message,
|
|
646
|
+
extra,
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
if (result.state === "noop") {
|
|
653
|
+
clearDialog(api);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
replaceDialog(api, result.dialogSize, () => (
|
|
658
|
+
<CommandOutputDialog api={api} title={result.title} output={result.output} />
|
|
659
|
+
));
|
|
660
|
+
} catch (error) {
|
|
661
|
+
replaceDialog(api, "large", () => <CommandErrorDialog api={api} title={spec.title} error={error} />);
|
|
662
|
+
(api as any).ui?.toast?.({
|
|
663
|
+
variant: "error",
|
|
664
|
+
message: "OpenCode Quota command failed",
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function runQuotaDialogCommand(
|
|
670
|
+
api: TuiPluginApi,
|
|
671
|
+
command: QuotaDialogCommandId,
|
|
672
|
+
rawInput?: unknown,
|
|
673
|
+
state?: QuotaDialogCommandState,
|
|
674
|
+
): void {
|
|
675
|
+
void runQuotaDialogCommandAsync(api, command, rawInput, state);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function registerQuotaDialogCommands(api: TuiPluginApi): void {
|
|
679
|
+
const keymap = (api as any).keymap;
|
|
680
|
+
if (!keymap?.registerLayer) return;
|
|
681
|
+
|
|
682
|
+
const commandState: QuotaDialogCommandState = {};
|
|
683
|
+
const dispose = keymap.registerLayer({
|
|
684
|
+
commands: QUOTA_DIALOG_COMMANDS.map((spec) => ({
|
|
685
|
+
namespace: "palette",
|
|
686
|
+
name: `opencode-quota.${spec.id}`,
|
|
687
|
+
title: spec.title,
|
|
688
|
+
desc: spec.description,
|
|
689
|
+
category: "OpenCode Quota",
|
|
690
|
+
slashName: spec.slashName,
|
|
691
|
+
run(input?: unknown) {
|
|
692
|
+
runQuotaDialogCommand(api, spec.id, input, commandState);
|
|
693
|
+
},
|
|
694
|
+
})),
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
if (typeof dispose === "function") {
|
|
698
|
+
api.lifecycle.onDispose(dispose);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
453
702
|
function registerSidebarSlots(api: TuiPluginApi): void {
|
|
454
703
|
api.slots.register({
|
|
455
704
|
order: SIDEBAR_ORDER,
|
|
@@ -462,6 +711,8 @@ function registerSidebarSlots(api: TuiPluginApi): void {
|
|
|
462
711
|
}
|
|
463
712
|
|
|
464
713
|
const tui: TuiPlugin = async (api) => {
|
|
714
|
+
registerQuotaDialogCommands(api);
|
|
715
|
+
|
|
465
716
|
let surfaceRegistration;
|
|
466
717
|
try {
|
|
467
718
|
surfaceRegistration = await resolveTuiSurfaceRegistration(api);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slkiser/opencode-quota",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"packageManager": "pnpm@11.0.0",
|
|
5
5
|
"description": "OpenCode quota & tokens usage with zero context window pollution. Supports GitHub Copilot, OpenAI (Plus/Pro), Qwen Code, Chutes AI, Synthetic, Google Antigravity, Z.ai coding plan and more.",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"LICENSE"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
-
"build": "tsc && node scripts/copy-data.mjs && node scripts/prepare-tui-dist.mjs",
|
|
36
|
+
"build": "node scripts/clean-dist.mjs && tsc && node scripts/copy-data.mjs && node scripts/prepare-tui-dist.mjs",
|
|
37
37
|
"build:check": "pnpm run build && pnpm pack --dry-run",
|
|
38
38
|
"pricing:refresh": "node scripts/refresh-modelsdev-pricing.mjs",
|
|
39
39
|
"pricing:refresh:if-stale": "node scripts/refresh-modelsdev-pricing-if-stale.mjs",
|
|
@@ -86,6 +86,9 @@
|
|
|
86
86
|
"solid-js": "^1.9.12",
|
|
87
87
|
"xdg-basedir": "^5.1.0"
|
|
88
88
|
},
|
|
89
|
+
"optionalDependencies": {
|
|
90
|
+
"better-sqlite3": "^12.10.0"
|
|
91
|
+
},
|
|
89
92
|
"peerDependencies": {
|
|
90
93
|
"@opencode-ai/plugin": "^1.4.3"
|
|
91
94
|
},
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface CrofApiKeyResult {
|
|
2
|
-
key: string;
|
|
3
|
-
source: CrofKeySource;
|
|
4
|
-
}
|
|
5
|
-
export type CrofKeySource = "env:CROF_API_KEY" | "env:CROFAI_API_KEY" | "opencode.json" | "opencode.jsonc" | "auth.json";
|
|
6
|
-
export { getGlobalOpencodeConfigCandidatePaths as getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
|
|
7
|
-
export declare function resolveCrofApiKey(): Promise<CrofApiKeyResult | null>;
|
|
8
|
-
export declare function hasCrofApiKey(): Promise<boolean>;
|
|
9
|
-
export declare function getCrofKeyDiagnostics(): Promise<{
|
|
10
|
-
configured: boolean;
|
|
11
|
-
source: CrofKeySource | null;
|
|
12
|
-
checkedPaths: string[];
|
|
13
|
-
authPaths: string[];
|
|
14
|
-
}>;
|
|
15
|
-
//# sourceMappingURL=crof-config.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof-config.d.ts","sourceRoot":"","sources":["../../src/lib/crof-config.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;CACvB;AAKD,MAAM,MAAM,aAAa,GACrB,kBAAkB,GAClB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,WAAW,CAAC;AAEhB,OAAO,EAAE,qCAAqC,IAAI,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAkBjH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAE1E;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAEtD;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IACrD,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC,CAKD"}
|
package/dist/lib/crof-config.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { createProviderApiKeyResolver, getGlobalOpencodeConfigCandidatePaths, } from "./api-key-resolver.js";
|
|
2
|
-
import { getAuthPaths, readAuthFile } from "./opencode-auth.js";
|
|
3
|
-
const ALLOWED_CROF_ENV_VARS = ["CROF_API_KEY", "CROFAI_API_KEY"];
|
|
4
|
-
const CROF_PROVIDER_KEYS = ["crof"];
|
|
5
|
-
export { getGlobalOpencodeConfigCandidatePaths as getOpencodeConfigCandidatePaths } from "./api-key-resolver.js";
|
|
6
|
-
const crofApiKeyResolver = createProviderApiKeyResolver({
|
|
7
|
-
envVars: [
|
|
8
|
-
{ name: "CROF_API_KEY", source: "env:CROF_API_KEY" },
|
|
9
|
-
{ name: "CROFAI_API_KEY", source: "env:CROFAI_API_KEY" },
|
|
10
|
-
],
|
|
11
|
-
providerKeys: CROF_PROVIDER_KEYS,
|
|
12
|
-
allowedEnvVars: ALLOWED_CROF_ENV_VARS,
|
|
13
|
-
configJsonSource: "opencode.json",
|
|
14
|
-
configJsoncSource: "opencode.jsonc",
|
|
15
|
-
getConfigCandidates: getGlobalOpencodeConfigCandidatePaths,
|
|
16
|
-
auth: {
|
|
17
|
-
readAuth: readAuthFile,
|
|
18
|
-
authSource: "auth.json",
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
export async function resolveCrofApiKey() {
|
|
22
|
-
return crofApiKeyResolver.resolve();
|
|
23
|
-
}
|
|
24
|
-
export async function hasCrofApiKey() {
|
|
25
|
-
return crofApiKeyResolver.has();
|
|
26
|
-
}
|
|
27
|
-
export async function getCrofKeyDiagnostics() {
|
|
28
|
-
return {
|
|
29
|
-
...(await crofApiKeyResolver.diagnostics()),
|
|
30
|
-
authPaths: getAuthPaths(),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=crof-config.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof-config.js","sourceRoot":"","sources":["../../src/lib/crof-config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,qCAAqC,GACtC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOhE,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,gBAAgB,CAAU,CAAC;AAC1E,MAAM,kBAAkB,GAAG,CAAC,MAAM,CAAU,CAAC;AAS7C,OAAO,EAAE,qCAAqC,IAAI,+BAA+B,EAAE,MAAM,uBAAuB,CAAC;AAEjH,MAAM,kBAAkB,GAAG,4BAA4B,CAAgB;IACrE,OAAO,EAAE;QACP,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,kBAAkB,EAAE;QACpD,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,oBAAoB,EAAE;KACzD;IACD,YAAY,EAAE,kBAAkB;IAChC,cAAc,EAAE,qBAAqB;IACrC,gBAAgB,EAAE,eAAe;IACjC,iBAAiB,EAAE,gBAAgB;IACnC,mBAAmB,EAAE,qCAAqC;IAC1D,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY;QACtB,UAAU,EAAE,WAAW;KACxB;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,OAAO,kBAAkB,CAAC,OAAO,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,kBAAkB,CAAC,GAAG,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB;IAMzC,OAAO;QACL,GAAG,CAAC,MAAM,kBAAkB,CAAC,WAAW,EAAE,CAAC;QAC3C,SAAS,EAAE,YAAY,EAAE;KAC1B,CAAC;AACJ,CAAC"}
|
package/dist/lib/crof.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Crof.ai quota fetcher.
|
|
3
|
-
*
|
|
4
|
-
* Resolves API key from multiple sources and queries:
|
|
5
|
-
* https://crof.ai/usage_api/
|
|
6
|
-
*/
|
|
7
|
-
import type { CrofResult } from "./types.js";
|
|
8
|
-
export { getCrofKeyDiagnostics, hasCrofApiKey as hasCrofApiKeyConfigured, type CrofKeySource } from "./crof-config.js";
|
|
9
|
-
export declare function formatCrofCreditsValue(credits: number): string;
|
|
10
|
-
export declare function queryCrofQuota(options?: {
|
|
11
|
-
requestTimeoutMs?: number;
|
|
12
|
-
}): Promise<CrofResult>;
|
|
13
|
-
//# sourceMappingURL=crof.d.ts.map
|
package/dist/lib/crof.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof.d.ts","sourceRoot":"","sources":["../../src/lib/crof.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAoB7C,OAAO,EAAE,qBAAqB,EAAE,aAAa,IAAI,uBAAuB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA6BvH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAgCrG"}
|
package/dist/lib/crof.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Crof.ai quota fetcher.
|
|
3
|
-
*
|
|
4
|
-
* Resolves API key from multiple sources and queries:
|
|
5
|
-
* https://crof.ai/usage_api/
|
|
6
|
-
*/
|
|
7
|
-
import { sanitizeDisplaySnippet, sanitizeDisplayText } from "./display-sanitize.js";
|
|
8
|
-
import { clampPercent } from "./format-utils.js";
|
|
9
|
-
import { fetchWithTimeout } from "./http.js";
|
|
10
|
-
import { resolveCrofApiKey, } from "./crof-config.js";
|
|
11
|
-
const CROF_USAGE_URL = "https://crof.ai/usage_api/";
|
|
12
|
-
const USER_AGENT = "OpenCode-Quota-Toast/1.0";
|
|
13
|
-
export { getCrofKeyDiagnostics, hasCrofApiKey as hasCrofApiKeyConfigured } from "./crof-config.js";
|
|
14
|
-
function getFiniteNumber(value) {
|
|
15
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
16
|
-
}
|
|
17
|
-
function parseCrofUsage(data) {
|
|
18
|
-
if (!data || typeof data !== "object") {
|
|
19
|
-
throw new Error("Crof API returned an unexpected response shape");
|
|
20
|
-
}
|
|
21
|
-
const response = data;
|
|
22
|
-
const credits = getFiniteNumber(response.credits);
|
|
23
|
-
const requestsPlan = getFiniteNumber(response.requests_plan);
|
|
24
|
-
const usableRequests = getFiniteNumber(response.usable_requests);
|
|
25
|
-
if (credits === undefined || requestsPlan === undefined || usableRequests === undefined) {
|
|
26
|
-
throw new Error("Crof API returned an unexpected response shape");
|
|
27
|
-
}
|
|
28
|
-
return {
|
|
29
|
-
success: true,
|
|
30
|
-
credits,
|
|
31
|
-
requestsPlan,
|
|
32
|
-
usableRequests,
|
|
33
|
-
percentRemaining: requestsPlan > 0 ? clampPercent((usableRequests / requestsPlan) * 100) : 0,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
export function formatCrofCreditsValue(credits) {
|
|
37
|
-
if (!Number.isFinite(credits))
|
|
38
|
-
return "0 credits";
|
|
39
|
-
const value = Number.isInteger(credits) ? String(Math.trunc(credits)) : credits.toFixed(4).replace(/\.?0+$/u, "");
|
|
40
|
-
return `${value} credits`;
|
|
41
|
-
}
|
|
42
|
-
export async function queryCrofQuota(options = {}) {
|
|
43
|
-
const resolved = await resolveCrofApiKey();
|
|
44
|
-
if (!resolved)
|
|
45
|
-
return null;
|
|
46
|
-
try {
|
|
47
|
-
const resp = await fetchWithTimeout(CROF_USAGE_URL, {
|
|
48
|
-
method: "GET",
|
|
49
|
-
headers: {
|
|
50
|
-
Authorization: `Bearer ${resolved.key}`,
|
|
51
|
-
"User-Agent": USER_AGENT,
|
|
52
|
-
},
|
|
53
|
-
}, options.requestTimeoutMs);
|
|
54
|
-
if (!resp.ok) {
|
|
55
|
-
const text = await resp.text();
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
error: `Crof API error ${resp.status}: ${sanitizeDisplaySnippet(text, 120)}`,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
return parseCrofUsage(await resp.json());
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
error: sanitizeDisplayText(err instanceof Error ? err.message : String(err)),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
//# sourceMappingURL=crof.js.map
|
package/dist/lib/crof.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof.js","sourceRoot":"","sources":["../../src/lib/crof.ts"],"names":[],"mappings":"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"}
|
package/dist/providers/crof.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof.d.ts","sourceRoot":"","sources":["../../src/providers/crof.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,aAAa,EAId,MAAM,mBAAmB,CAAC;AAY3B,eAAO,MAAM,YAAY,EAAE,aA6C1B,CAAC"}
|
package/dist/providers/crof.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Crof.ai provider wrapper.
|
|
3
|
-
*/
|
|
4
|
-
import { formatCrofCreditsValue, hasCrofApiKeyConfigured, queryCrofQuota } from "../lib/crof.js";
|
|
5
|
-
import { isCanonicalProviderAvailable } from "../lib/provider-availability.js";
|
|
6
|
-
import { modelProviderMatchesRuntimeId } from "../lib/provider-model-matching.js";
|
|
7
|
-
import { attemptedResult, mapNullableProviderResult } from "./result-helpers.js";
|
|
8
|
-
function formatRequestAmount(value) {
|
|
9
|
-
if (!Number.isFinite(value))
|
|
10
|
-
return "0";
|
|
11
|
-
if (Number.isInteger(value))
|
|
12
|
-
return String(Math.trunc(value));
|
|
13
|
-
return value.toFixed(2).replace(/\.?0+$/u, "");
|
|
14
|
-
}
|
|
15
|
-
export const crofProvider = {
|
|
16
|
-
id: "crof",
|
|
17
|
-
async isAvailable(ctx) {
|
|
18
|
-
const providerAvailable = await isCanonicalProviderAvailable({
|
|
19
|
-
ctx,
|
|
20
|
-
providerId: "crof",
|
|
21
|
-
fallbackOnError: false,
|
|
22
|
-
});
|
|
23
|
-
if (providerAvailable)
|
|
24
|
-
return true;
|
|
25
|
-
return await hasCrofApiKeyConfigured();
|
|
26
|
-
},
|
|
27
|
-
matchesCurrentModel(model) {
|
|
28
|
-
return modelProviderMatchesRuntimeId(model, "crof");
|
|
29
|
-
},
|
|
30
|
-
async fetch(ctx) {
|
|
31
|
-
const result = await queryCrofQuota({ requestTimeoutMs: ctx.config?.requestTimeoutMs });
|
|
32
|
-
return mapNullableProviderResult(result, {
|
|
33
|
-
errorLabel: "Crof",
|
|
34
|
-
onSuccess: (result) => {
|
|
35
|
-
const entries = [
|
|
36
|
-
{
|
|
37
|
-
name: "Crof Requests",
|
|
38
|
-
group: "Crof",
|
|
39
|
-
label: "Requests:",
|
|
40
|
-
right: `${formatRequestAmount(result.usableRequests)}/${formatRequestAmount(result.requestsPlan)}`,
|
|
41
|
-
percentRemaining: result.percentRemaining,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
kind: "value",
|
|
45
|
-
name: "Crof Credits",
|
|
46
|
-
group: "Crof",
|
|
47
|
-
label: "Credits:",
|
|
48
|
-
value: formatCrofCreditsValue(result.credits),
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
return attemptedResult(entries);
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
//# sourceMappingURL=crof.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crof.js","sourceRoot":"","sources":["../../src/providers/crof.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,mCAAmC,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,SAAS,mBAAmB,CAAC,KAAa;IACxC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,EAAE,EAAE,MAAM;IAEV,KAAK,CAAC,WAAW,CAAC,GAAyB;QACzC,MAAM,iBAAiB,GAAG,MAAM,4BAA4B,CAAC;YAC3D,GAAG;YACH,UAAU,EAAE,MAAM;YAClB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;QACH,IAAI,iBAAiB;YAAE,OAAO,IAAI,CAAC;QAEnC,OAAO,MAAM,uBAAuB,EAAE,CAAC;IACzC,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,OAAO,6BAA6B,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAyB;QACnC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAExF,OAAO,yBAAyB,CAAC,MAAM,EAAE;YACvC,UAAU,EAAE,MAAM;YAClB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE;gBACpB,MAAM,OAAO,GAAsB;oBACjC;wBACE,IAAI,EAAE,eAAe;wBACrB,KAAK,EAAE,MAAM;wBACb,KAAK,EAAE,WAAW;wBAClB,KAAK,EAAE,GAAG,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,mBAAmB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;wBAClG,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;qBAC1C;oBACD;wBACE,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE,cAAc;wBACpB,KAAK,EAAE,MAAM;wBACb,KAAK,EAAE,UAAU;wBACjB,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC;qBAC9C;iBACF,CAAC;gBAEF,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|