@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.
Files changed (107) hide show
  1. package/README.md +256 -70
  2. package/dist/bin/opencode-quota.d.ts.map +1 -1
  3. package/dist/bin/opencode-quota.js +4 -1
  4. package/dist/bin/opencode-quota.js.map +1 -1
  5. package/dist/lib/cli-show.d.ts.map +1 -1
  6. package/dist/lib/cli-show.js +94 -3
  7. package/dist/lib/cli-show.js.map +1 -1
  8. package/dist/lib/command-handled.d.ts +2 -2
  9. package/dist/lib/command-handled.d.ts.map +1 -1
  10. package/dist/lib/command-handled.js +19 -4
  11. package/dist/lib/command-handled.js.map +1 -1
  12. package/dist/lib/config.d.ts +1 -1
  13. package/dist/lib/config.d.ts.map +1 -1
  14. package/dist/lib/config.js +60 -5
  15. package/dist/lib/config.js.map +1 -1
  16. package/dist/lib/cursor-pricing.js +1 -1
  17. package/dist/lib/cursor-pricing.js.map +1 -1
  18. package/dist/lib/init-installer.d.ts.map +1 -1
  19. package/dist/lib/init-installer.js +6 -2
  20. package/dist/lib/init-installer.js.map +1 -1
  21. package/dist/lib/ollama-cloud-config.d.ts +37 -0
  22. package/dist/lib/ollama-cloud-config.d.ts.map +1 -0
  23. package/dist/lib/ollama-cloud-config.js +148 -0
  24. package/dist/lib/ollama-cloud-config.js.map +1 -0
  25. package/dist/lib/ollama-cloud.d.ts +17 -0
  26. package/dist/lib/ollama-cloud.d.ts.map +1 -0
  27. package/dist/lib/ollama-cloud.js +150 -0
  28. package/dist/lib/ollama-cloud.js.map +1 -0
  29. package/dist/lib/opencode-go.d.ts +12 -4
  30. package/dist/lib/opencode-go.d.ts.map +1 -1
  31. package/dist/lib/opencode-go.js +90 -7
  32. package/dist/lib/opencode-go.js.map +1 -1
  33. package/dist/lib/opencode-sqlite.d.ts.map +1 -1
  34. package/dist/lib/opencode-sqlite.js +90 -8
  35. package/dist/lib/opencode-sqlite.js.map +1 -1
  36. package/dist/lib/provider-metadata.d.ts +1 -1
  37. package/dist/lib/provider-metadata.d.ts.map +1 -1
  38. package/dist/lib/provider-metadata.js +9 -10
  39. package/dist/lib/provider-metadata.js.map +1 -1
  40. package/dist/lib/quota-command-format.d.ts +2 -0
  41. package/dist/lib/quota-command-format.d.ts.map +1 -1
  42. package/dist/lib/quota-command-format.js +4 -3
  43. package/dist/lib/quota-command-format.js.map +1 -1
  44. package/dist/lib/quota-dialog-commands.d.ts +43 -0
  45. package/dist/lib/quota-dialog-commands.d.ts.map +1 -0
  46. package/dist/lib/quota-dialog-commands.js +677 -0
  47. package/dist/lib/quota-dialog-commands.js.map +1 -0
  48. package/dist/lib/quota-export-types.d.ts +69 -0
  49. package/dist/lib/quota-export-types.d.ts.map +1 -0
  50. package/dist/lib/quota-export-types.js +9 -0
  51. package/dist/lib/quota-export-types.js.map +1 -0
  52. package/dist/lib/quota-export.d.ts +42 -0
  53. package/dist/lib/quota-export.d.ts.map +1 -0
  54. package/dist/lib/quota-export.js +130 -0
  55. package/dist/lib/quota-export.js.map +1 -0
  56. package/dist/lib/quota-render-data.d.ts +3 -0
  57. package/dist/lib/quota-render-data.d.ts.map +1 -1
  58. package/dist/lib/quota-render-data.js +22 -15
  59. package/dist/lib/quota-render-data.js.map +1 -1
  60. package/dist/lib/quota-state.d.ts +12 -0
  61. package/dist/lib/quota-state.d.ts.map +1 -1
  62. package/dist/lib/quota-state.js +38 -2
  63. package/dist/lib/quota-state.js.map +1 -1
  64. package/dist/lib/quota-stats-format.d.ts +5 -0
  65. package/dist/lib/quota-stats-format.d.ts.map +1 -1
  66. package/dist/lib/quota-stats-format.js +48 -13
  67. package/dist/lib/quota-stats-format.js.map +1 -1
  68. package/dist/lib/quota-status.d.ts.map +1 -1
  69. package/dist/lib/quota-status.js +0 -17
  70. package/dist/lib/quota-status.js.map +1 -1
  71. package/dist/lib/tui-runtime.d.ts +27 -0
  72. package/dist/lib/tui-runtime.d.ts.map +1 -1
  73. package/dist/lib/tui-runtime.js +104 -27
  74. package/dist/lib/tui-runtime.js.map +1 -1
  75. package/dist/lib/types.d.ts +35 -7
  76. package/dist/lib/types.d.ts.map +1 -1
  77. package/dist/lib/types.js +4 -0
  78. package/dist/lib/types.js.map +1 -1
  79. package/dist/lib/zai.d.ts.map +1 -1
  80. package/dist/lib/zai.js +8 -1
  81. package/dist/lib/zai.js.map +1 -1
  82. package/dist/plugin.d.ts.map +1 -1
  83. package/dist/plugin.js +6 -476
  84. package/dist/plugin.js.map +1 -1
  85. package/dist/providers/cursor.js +1 -1
  86. package/dist/providers/cursor.js.map +1 -1
  87. package/dist/providers/ollama-cloud.d.ts +9 -0
  88. package/dist/providers/ollama-cloud.d.ts.map +1 -0
  89. package/dist/providers/ollama-cloud.js +77 -0
  90. package/dist/providers/ollama-cloud.js.map +1 -0
  91. package/dist/providers/registry.js +2 -2
  92. package/dist/providers/registry.js.map +1 -1
  93. package/dist/tui.d.ts.map +1 -1
  94. package/dist/tui.tsx +252 -1
  95. package/package.json +5 -2
  96. package/dist/lib/crof-config.d.ts +0 -15
  97. package/dist/lib/crof-config.d.ts.map +0 -1
  98. package/dist/lib/crof-config.js +0 -33
  99. package/dist/lib/crof-config.js.map +0 -1
  100. package/dist/lib/crof.d.ts +0 -13
  101. package/dist/lib/crof.d.ts.map +0 -1
  102. package/dist/lib/crof.js +0 -70
  103. package/dist/lib/crof.js.map +0 -1
  104. package/dist/providers/crof.d.ts +0 -6
  105. package/dist/providers/crof.d.ts.map +0 -1
  106. package/dist/providers/crof.js +0 -56
  107. 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 (collapsed() || !hasDetailLines()) return lines();
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.8.8",
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"}
@@ -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"}
@@ -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
@@ -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
@@ -1 +0,0 @@
1
- {"version":3,"file":"crof.js","sourceRoot":"","sources":["../../src/lib/crof.ts"],"names":[],"mappings":"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"}
@@ -1,6 +0,0 @@
1
- /**
2
- * Crof.ai provider wrapper.
3
- */
4
- import type { QuotaProvider } from "../lib/entries.js";
5
- export declare const crofProvider: QuotaProvider;
6
- //# sourceMappingURL=crof.d.ts.map
@@ -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"}
@@ -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"}