@slkiser/opencode-quota 3.11.1 → 4.0.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 (208) hide show
  1. package/README.md +103 -57
  2. package/dist/bin/opencode-quota.d.ts.map +1 -1
  3. package/dist/bin/opencode-quota.js +24 -7
  4. package/dist/bin/opencode-quota.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/lib/anthropic.d.ts.map +1 -1
  10. package/dist/lib/anthropic.js +106 -3
  11. package/dist/lib/anthropic.js.map +1 -1
  12. package/dist/lib/atomic-json.d.ts +1 -0
  13. package/dist/lib/atomic-json.d.ts.map +1 -1
  14. package/dist/lib/atomic-json.js +5 -2
  15. package/dist/lib/atomic-json.js.map +1 -1
  16. package/dist/lib/cli-show.d.ts.map +1 -1
  17. package/dist/lib/cli-show.js +11 -7
  18. package/dist/lib/cli-show.js.map +1 -1
  19. package/dist/lib/command-surfaces.d.ts +10 -0
  20. package/dist/lib/command-surfaces.d.ts.map +1 -0
  21. package/dist/lib/command-surfaces.js +9 -0
  22. package/dist/lib/command-surfaces.js.map +1 -0
  23. package/dist/lib/config-file-utils.d.ts +5 -0
  24. package/dist/lib/config-file-utils.d.ts.map +1 -1
  25. package/dist/lib/config-file-utils.js +20 -3
  26. package/dist/lib/config-file-utils.js.map +1 -1
  27. package/dist/lib/config.d.ts +5 -3
  28. package/dist/lib/config.d.ts.map +1 -1
  29. package/dist/lib/config.js +124 -32
  30. package/dist/lib/config.js.map +1 -1
  31. package/dist/lib/copilot.d.ts +19 -12
  32. package/dist/lib/copilot.d.ts.map +1 -1
  33. package/dist/lib/copilot.js +462 -566
  34. package/dist/lib/copilot.js.map +1 -1
  35. package/dist/lib/display-sanitize.d.ts.map +1 -1
  36. package/dist/lib/display-sanitize.js +13 -0
  37. package/dist/lib/display-sanitize.js.map +1 -1
  38. package/dist/lib/entries.d.ts +53 -4
  39. package/dist/lib/entries.d.ts.map +1 -1
  40. package/dist/lib/entries.js.map +1 -1
  41. package/dist/lib/init-installer.d.ts +11 -1
  42. package/dist/lib/init-installer.d.ts.map +1 -1
  43. package/dist/lib/init-installer.js +542 -154
  44. package/dist/lib/init-installer.js.map +1 -1
  45. package/dist/lib/openai.d.ts +9 -12
  46. package/dist/lib/openai.d.ts.map +1 -1
  47. package/dist/lib/openai.js +69 -40
  48. package/dist/lib/openai.js.map +1 -1
  49. package/dist/lib/opencode-config-editor.d.ts +53 -0
  50. package/dist/lib/opencode-config-editor.d.ts.map +1 -0
  51. package/dist/lib/opencode-config-editor.js +274 -0
  52. package/dist/lib/opencode-config-editor.js.map +1 -0
  53. package/dist/lib/opencode-config-providers.d.ts +18 -0
  54. package/dist/lib/opencode-config-providers.d.ts.map +1 -1
  55. package/dist/lib/opencode-config-providers.js +75 -4
  56. package/dist/lib/opencode-config-providers.js.map +1 -1
  57. package/dist/lib/opencode-runtime-paths.d.ts.map +1 -1
  58. package/dist/lib/opencode-runtime-paths.js +10 -3
  59. package/dist/lib/opencode-runtime-paths.js.map +1 -1
  60. package/dist/lib/opencode-storage.d.ts +10 -0
  61. package/dist/lib/opencode-storage.d.ts.map +1 -1
  62. package/dist/lib/opencode-storage.js +75 -0
  63. package/dist/lib/opencode-storage.js.map +1 -1
  64. package/dist/lib/provider-add-command.d.ts +35 -0
  65. package/dist/lib/provider-add-command.d.ts.map +1 -0
  66. package/dist/lib/provider-add-command.js +185 -0
  67. package/dist/lib/provider-add-command.js.map +1 -0
  68. package/dist/lib/provider-add.d.ts +21 -0
  69. package/dist/lib/provider-add.d.ts.map +1 -0
  70. package/dist/lib/provider-add.js +210 -0
  71. package/dist/lib/provider-add.js.map +1 -0
  72. package/dist/lib/provider-metadata.d.ts +1 -1
  73. package/dist/lib/provider-metadata.d.ts.map +1 -1
  74. package/dist/lib/provider-metadata.js +11 -5
  75. package/dist/lib/provider-metadata.js.map +1 -1
  76. package/dist/lib/quota-command-format.d.ts +2 -0
  77. package/dist/lib/quota-command-format.d.ts.map +1 -1
  78. package/dist/lib/quota-command-format.js +94 -26
  79. package/dist/lib/quota-command-format.js.map +1 -1
  80. package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
  81. package/dist/lib/quota-dialog-commands.js +8 -4
  82. package/dist/lib/quota-dialog-commands.js.map +1 -1
  83. package/dist/lib/quota-export-types.d.ts +48 -27
  84. package/dist/lib/quota-export-types.d.ts.map +1 -1
  85. package/dist/lib/quota-export-types.js +0 -7
  86. package/dist/lib/quota-export-types.js.map +1 -1
  87. package/dist/lib/quota-export.d.ts.map +1 -1
  88. package/dist/lib/quota-export.js +63 -16
  89. package/dist/lib/quota-export.js.map +1 -1
  90. package/dist/lib/quota-providers-local.d.ts +55 -0
  91. package/dist/lib/quota-providers-local.d.ts.map +1 -0
  92. package/dist/lib/quota-providers-local.js +313 -0
  93. package/dist/lib/quota-providers-local.js.map +1 -0
  94. package/dist/lib/quota-providers-remote.d.ts +23 -0
  95. package/dist/lib/quota-providers-remote.d.ts.map +1 -0
  96. package/dist/lib/quota-providers-remote.js +318 -0
  97. package/dist/lib/quota-providers-remote.js.map +1 -0
  98. package/dist/lib/quota-providers.d.ts +77 -0
  99. package/dist/lib/quota-providers.d.ts.map +1 -0
  100. package/dist/lib/quota-providers.js +630 -0
  101. package/dist/lib/quota-providers.js.map +1 -0
  102. package/dist/lib/quota-render-data.d.ts +1 -0
  103. package/dist/lib/quota-render-data.d.ts.map +1 -1
  104. package/dist/lib/quota-render-data.js +35 -16
  105. package/dist/lib/quota-render-data.js.map +1 -1
  106. package/dist/lib/quota-runtime-context.d.ts.map +1 -1
  107. package/dist/lib/quota-runtime-context.js +3 -4
  108. package/dist/lib/quota-runtime-context.js.map +1 -1
  109. package/dist/lib/quota-state.d.ts +5 -2
  110. package/dist/lib/quota-state.d.ts.map +1 -1
  111. package/dist/lib/quota-state.js +268 -51
  112. package/dist/lib/quota-state.js.map +1 -1
  113. package/dist/lib/quota-status.d.ts +2 -1
  114. package/dist/lib/quota-status.d.ts.map +1 -1
  115. package/dist/lib/quota-status.js +247 -69
  116. package/dist/lib/quota-status.js.map +1 -1
  117. package/dist/lib/qwen-local-quota.d.ts +12 -11
  118. package/dist/lib/qwen-local-quota.d.ts.map +1 -1
  119. package/dist/lib/qwen-local-quota.js +82 -87
  120. package/dist/lib/qwen-local-quota.js.map +1 -1
  121. package/dist/lib/scoped-update.d.ts +63 -0
  122. package/dist/lib/scoped-update.d.ts.map +1 -0
  123. package/dist/lib/scoped-update.js +311 -0
  124. package/dist/lib/scoped-update.js.map +1 -0
  125. package/dist/lib/tui-runtime.d.ts +2 -0
  126. package/dist/lib/tui-runtime.d.ts.map +1 -1
  127. package/dist/lib/tui-runtime.js +33 -9
  128. package/dist/lib/tui-runtime.js.map +1 -1
  129. package/dist/lib/types.d.ts +51 -15
  130. package/dist/lib/types.d.ts.map +1 -1
  131. package/dist/lib/types.js +2 -1
  132. package/dist/lib/types.js.map +1 -1
  133. package/dist/plugin.d.ts.map +1 -1
  134. package/dist/plugin.js +75 -100
  135. package/dist/plugin.js.map +1 -1
  136. package/dist/providers/alibaba-coding-plan.d.ts.map +1 -1
  137. package/dist/providers/alibaba-coding-plan.js +35 -4
  138. package/dist/providers/alibaba-coding-plan.js.map +1 -1
  139. package/dist/providers/anthropic.d.ts.map +1 -1
  140. package/dist/providers/anthropic.js +13 -1
  141. package/dist/providers/anthropic.js.map +1 -1
  142. package/dist/providers/chutes.d.ts.map +1 -1
  143. package/dist/providers/chutes.js +6 -0
  144. package/dist/providers/chutes.js.map +1 -1
  145. package/dist/providers/copilot.d.ts +2 -1
  146. package/dist/providers/copilot.d.ts.map +1 -1
  147. package/dist/providers/copilot.js +143 -60
  148. package/dist/providers/copilot.js.map +1 -1
  149. package/dist/providers/cursor.d.ts.map +1 -1
  150. package/dist/providers/cursor.js +30 -1
  151. package/dist/providers/cursor.js.map +1 -1
  152. package/dist/providers/deepseek.d.ts.map +1 -1
  153. package/dist/providers/deepseek.js +13 -1
  154. package/dist/providers/deepseek.js.map +1 -1
  155. package/dist/providers/google-agy.d.ts.map +1 -1
  156. package/dist/providers/google-agy.js +7 -1
  157. package/dist/providers/google-agy.js.map +1 -1
  158. package/dist/providers/google-antigravity.d.ts.map +1 -1
  159. package/dist/providers/google-antigravity.js +10 -2
  160. package/dist/providers/google-antigravity.js.map +1 -1
  161. package/dist/providers/google-gemini-cli.d.ts.map +1 -1
  162. package/dist/providers/google-gemini-cli.js +8 -2
  163. package/dist/providers/google-gemini-cli.js.map +1 -1
  164. package/dist/providers/kimi-code.d.ts.map +1 -1
  165. package/dist/providers/kimi-code.js +6 -0
  166. package/dist/providers/kimi-code.js.map +1 -1
  167. package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
  168. package/dist/providers/minimax-coding-plan.js +12 -4
  169. package/dist/providers/minimax-coding-plan.js.map +1 -1
  170. package/dist/providers/nanogpt.d.ts.map +1 -1
  171. package/dist/providers/nanogpt.js +19 -1
  172. package/dist/providers/nanogpt.js.map +1 -1
  173. package/dist/providers/ollama-cloud.d.ts.map +1 -1
  174. package/dist/providers/ollama-cloud.js +12 -0
  175. package/dist/providers/ollama-cloud.js.map +1 -1
  176. package/dist/providers/openai.d.ts.map +1 -1
  177. package/dist/providers/openai.js +7 -0
  178. package/dist/providers/openai.js.map +1 -1
  179. package/dist/providers/opencode-go.d.ts.map +1 -1
  180. package/dist/providers/opencode-go.js +9 -1
  181. package/dist/providers/opencode-go.js.map +1 -1
  182. package/dist/providers/quota-providers.d.ts +6 -0
  183. package/dist/providers/quota-providers.d.ts.map +1 -0
  184. package/dist/providers/quota-providers.js +276 -0
  185. package/dist/providers/quota-providers.js.map +1 -0
  186. package/dist/providers/qwen-code.d.ts.map +1 -1
  187. package/dist/providers/qwen-code.js +29 -3
  188. package/dist/providers/qwen-code.js.map +1 -1
  189. package/dist/providers/registry.d.ts.map +1 -1
  190. package/dist/providers/registry.js +2 -0
  191. package/dist/providers/registry.js.map +1 -1
  192. package/dist/providers/result-helpers.d.ts +2 -1
  193. package/dist/providers/result-helpers.d.ts.map +1 -1
  194. package/dist/providers/result-helpers.js +6 -1
  195. package/dist/providers/result-helpers.js.map +1 -1
  196. package/dist/providers/synthetic.d.ts.map +1 -1
  197. package/dist/providers/synthetic.js +7 -1
  198. package/dist/providers/synthetic.js.map +1 -1
  199. package/dist/providers/zai.d.ts.map +1 -1
  200. package/dist/providers/zai.js +7 -1
  201. package/dist/providers/zai.js.map +1 -1
  202. package/dist/providers/zhipu.d.ts.map +1 -1
  203. package/dist/providers/zhipu.js +7 -1
  204. package/dist/providers/zhipu.js.map +1 -1
  205. package/dist/tui.d.ts.map +1 -1
  206. package/dist/tui.js +148 -122
  207. package/dist/tui.tsx +122 -114
  208. package/package.json +29 -21
package/dist/tui.tsx CHANGED
@@ -9,7 +9,12 @@ import type {
9
9
  import { Show, createEffect, createSignal, onCleanup } from "solid-js";
10
10
 
11
11
  import type { SessionTokenError } from "./lib/quota-status.js";
12
- import type { CompactStatusState, HomeBottomState, SidebarPanelState } from "./lib/tui-panel-state.js";
12
+ import type { TuiCommandDisplay } from "./lib/types.js";
13
+ import type {
14
+ CompactStatusState,
15
+ HomeBottomState,
16
+ SidebarPanelState,
17
+ } from "./lib/tui-panel-state.js";
13
18
 
14
19
  import {
15
20
  getCompactStatusText,
@@ -26,8 +31,8 @@ import {
26
31
  getTuiRuntimeRootHints,
27
32
  getTuiSessionModelMeta,
28
33
  loadTuiHomeBottomStatus,
29
- normalizeTuiSessionID,
30
34
  loadTuiSessionQuotaSurfaces,
35
+ normalizeTuiSessionID,
31
36
  resolveTuiSurfaceRegistration,
32
37
  writeTuiQuotaExportIfEnabled,
33
38
  } from "./lib/tui-runtime.js";
@@ -35,6 +40,7 @@ import {
35
40
  QUOTA_DIALOG_COMMANDS,
36
41
  buildQuotaDialogCommandOutput,
37
42
  type QuotaDialogCommandId,
43
+ type QuotaDialogCommandSpec,
38
44
  } from "./lib/quota-dialog-commands.js";
39
45
 
40
46
  const id = "@slkiser/opencode-quota";
@@ -52,7 +58,6 @@ type DialogSize = "medium" | "large" | "xlarge";
52
58
  type QuotaDialogCommandState = {
53
59
  lastSessionTokenError?: SessionTokenError;
54
60
  };
55
-
56
61
  type SessionQuotaResource = {
57
62
  sessionID: string;
58
63
  sidebar: () => SidebarPanelState;
@@ -209,10 +214,13 @@ function acquireSessionQuotaResource(api: TuiPluginApi, sessionID: string): Sess
209
214
  return next;
210
215
  }
211
216
 
212
- function createHomeBottomResource(api: TuiPluginApi): HomeBottomResource {
217
+ function createHomeBottomResource(
218
+ api: TuiPluginApi,
219
+ compactHomeBottomEnabled: boolean,
220
+ ): HomeBottomResource {
213
221
  const [bottom, setBottom] = createSignal<HomeBottomState>({
214
222
  status: "loading",
215
- compact: { status: "loading" },
223
+ compact: compactHomeBottomEnabled ? { status: "loading" } : { status: "disabled" },
216
224
  });
217
225
 
218
226
  let refCount = 0;
@@ -307,11 +315,14 @@ function createHomeBottomResource(api: TuiPluginApi): HomeBottomResource {
307
315
  return resource;
308
316
  }
309
317
 
310
- function acquireHomeBottomResource(api: TuiPluginApi): HomeBottomResource {
318
+ function acquireHomeBottomResource(
319
+ api: TuiPluginApi,
320
+ compactHomeBottomEnabled: boolean,
321
+ ): HomeBottomResource {
311
322
  const existing = homeResources.get(api);
312
323
  if (existing) return existing.retain();
313
324
 
314
- const next = createHomeBottomResource(api).retain();
325
+ const next = createHomeBottomResource(api, compactHomeBottomEnabled).retain();
315
326
  homeResources.set(api, next);
316
327
  return next;
317
328
  }
@@ -447,37 +458,36 @@ function SessionPromptWithCompactStatus(props: {
447
458
  );
448
459
  }
449
460
 
450
- function HomeBottomView(props: { api: TuiPluginApi }) {
451
- const resource = acquireHomeBottomResource(props.api);
461
+ function HomeBottomView(props: { api: TuiPluginApi; compactHomeBottomEnabled: boolean }) {
462
+ const resource = acquireHomeBottomResource(props.api, props.compactHomeBottomEnabled);
452
463
  onCleanup(() => resource.release());
453
464
 
454
465
  const announcement = () => getHomeBottomAnnouncementText(resource.bottom());
455
466
  const compact = () => resource.bottom().compact;
467
+ const visible = () => shouldRenderHomeBottom(resource.bottom());
456
468
 
457
469
  return (
458
- <Show when={shouldRenderHomeBottom(resource.bottom())}>
459
- <box gap={0}>
470
+ <box gap={0}>
471
+ <Show when={visible()}>
460
472
  <text> </text>
461
- <Show when={announcement()}>
462
- <box flexDirection="row" justifyContent="center">
463
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
464
- {announcement()}
465
- </text>
466
- </box>
467
- </Show>
473
+ </Show>
474
+ <Show when={visible() && announcement()}>
475
+ <box flexDirection="row" justifyContent="center">
476
+ <text fg={props.api.theme.current.textMuted} wrapMode="none">
477
+ {announcement()}
478
+ </text>
479
+ </box>
480
+ </Show>
481
+ <Show when={visible()}>
468
482
  <CompactStatusLine api={props.api} panel={compact} justifyContent="center" />
469
- </box>
470
- </Show>
483
+ </Show>
484
+ </box>
471
485
  );
472
486
  }
473
487
 
474
488
  function getActiveTuiSessionID(api: TuiPluginApi): string | undefined {
475
- const route = (api as any).route?.current;
476
- if (route?.name !== "session" && route?.type !== "session") return undefined;
477
-
478
- return normalizeTuiSessionID(
479
- route.params?.sessionID ?? route.params?.session_id ?? route.params?.id ?? route.sessionID,
480
- );
489
+ if (api.route.current.name !== "session") return undefined;
490
+ return normalizeTuiSessionID(api.route.current.params?.sessionID);
481
491
  }
482
492
 
483
493
  function getTuiCommandArguments(input: unknown): string | undefined {
@@ -492,7 +502,7 @@ function getTuiCommandArguments(input: unknown): string | undefined {
492
502
 
493
503
  function CommandLoadingDialog(props: { api: TuiPluginApi; title: string }) {
494
504
  return (
495
- <box gap={1}>
505
+ <box gap={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
496
506
  <text fg={props.api.theme.current.text}>
497
507
  <b>{props.title}</b>
498
508
  </text>
@@ -526,7 +536,7 @@ function CommandOutputDialog(props: { api: TuiPluginApi; title: string; output:
526
536
  function CommandErrorDialog(props: { api: TuiPluginApi; title: string; error: unknown }) {
527
537
  const message = props.error instanceof Error ? props.error.message : String(props.error);
528
538
  return (
529
- <box gap={1}>
539
+ <box gap={1} paddingLeft={2} paddingRight={2} paddingBottom={1}>
530
540
  <text fg={props.api.theme.current.text}>
531
541
  <b>{props.title}</b>
532
542
  </text>
@@ -539,93 +549,90 @@ function CommandErrorDialog(props: { api: TuiPluginApi; title: string; error: un
539
549
  );
540
550
  }
541
551
 
542
- function TokensBetweenPromptDialog(props: {
543
- api: TuiPluginApi;
544
- onConfirm: (value: string) => void;
545
- onCancel: () => void;
546
- }) {
547
- const DialogPrompt = (props.api as any).ui?.DialogPrompt;
548
- if (DialogPrompt) {
549
- return (
550
- <DialogPrompt
551
- title="OpenCode Quota Token Range"
552
- placeholder="YYYY-MM-DD YYYY-MM-DD"
553
- description={() => (
554
- <text fg={props.api.theme.current.textMuted} wrapMode="none">
555
- Enter start and end dates, for example: 2026-01-01 2026-01-15
556
- </text>
557
- )}
558
- onConfirm={props.onConfirm}
559
- onCancel={props.onCancel}
560
- />
561
- );
552
+ function getCommandPromptCopy(spec: QuotaDialogCommandSpec): {
553
+ title: string;
554
+ placeholder: string;
555
+ description: string;
556
+ } {
557
+ switch (spec.id) {
558
+ case "tokens_between":
559
+ return {
560
+ title: "OpenCode Quota Token Range",
561
+ placeholder: "YYYY-MM-DD YYYY-MM-DD",
562
+ description: "Enter start and end dates, for example: 2026-01-01 2026-01-15",
563
+ };
564
+ case "quota_status":
565
+ return {
566
+ title: "OpenCode Quota Status Options",
567
+ placeholder: 'Optional JSON, e.g. {"refreshGoogleTokens":true}',
568
+ description: "Leave blank for normal diagnostics, or enter one JSON options object.",
569
+ };
570
+ default:
571
+ return {
572
+ title: spec.title,
573
+ placeholder: "Optional arguments",
574
+ description: "Leave blank to run with no arguments.",
575
+ };
562
576
  }
563
-
564
- return (
565
- <CommandOutputDialog
566
- api={props.api}
567
- title="OpenCode Quota Token Range"
568
- output={
569
- "Missing arguments for /tokens_between\n\nExpected: /tokens_between YYYY-MM-DD YYYY-MM-DD\nExample: /tokens_between 2026-01-01 2026-01-15"
570
- }
571
- />
572
- );
573
- }
574
-
575
- function replaceDialog(api: TuiPluginApi, size: DialogSize, render: () => JSX.Element) {
576
- const dialog = (api as any).ui?.dialog;
577
- dialog?.replace?.(render);
578
- // OpenCode's dialog.replace() resets size to medium; xlarge is the widest supported dialog.
579
- dialog?.setSize?.(size);
580
577
  }
581
578
 
582
- function clearDialog(api: TuiPluginApi): void {
583
- (api as any).ui?.dialog?.clear?.();
579
+ function replaceDialog(api: TuiPluginApi, size: DialogSize, render: () => JSX.Element): void {
580
+ api.ui.dialog.replace(render);
581
+ // OpenCode dialog.replace() resets size to medium.
582
+ api.ui.dialog.setSize(size);
584
583
  }
585
584
 
586
585
  async function runQuotaDialogCommandAsync(
587
586
  api: TuiPluginApi,
588
587
  command: QuotaDialogCommandId,
588
+ commandDisplay: TuiCommandDisplay,
589
589
  rawInput?: unknown,
590
590
  state?: QuotaDialogCommandState,
591
591
  ): Promise<void> {
592
592
  const spec = QUOTA_DIALOG_COMMANDS.find((item) => item.id === command)!;
593
- const args = getTuiCommandArguments(rawInput);
593
+ const argumentsText = getTuiCommandArguments(rawInput);
594
594
  const sessionID = getActiveTuiSessionID(api);
595
595
 
596
- if (command === "tokens_between" && !args) {
596
+ if (spec.acceptsArguments && rawInput === undefined) {
597
+ const prompt = getCommandPromptCopy(spec);
597
598
  replaceDialog(api, "medium", () => (
598
- <TokensBetweenPromptDialog
599
- api={api}
600
- onCancel={() => clearDialog(api)}
599
+ <api.ui.DialogPrompt
600
+ title={prompt.title}
601
+ placeholder={prompt.placeholder}
602
+ description={() => (
603
+ <text fg={api.theme.current.textMuted} wrapMode="word">
604
+ {prompt.description}
605
+ </text>
606
+ )}
607
+ onCancel={() => api.ui.dialog.clear()}
601
608
  onConfirm={(value) => {
602
- const nextArgs = value.trim();
603
- if (!nextArgs) {
604
- replaceDialog(api, "large", () => (
605
- <CommandOutputDialog
606
- api={api}
607
- title={spec.title}
608
- output={
609
- "Missing arguments for /tokens_between\n\nExpected: /tokens_between YYYY-MM-DD YYYY-MM-DD\nExample: /tokens_between 2026-01-01 2026-01-15"
610
- }
611
- />
612
- ));
613
- return;
614
- }
615
-
616
- runQuotaDialogCommand(api, command, { arguments: nextArgs }, state);
609
+ void runQuotaDialogCommandAsync(
610
+ api,
611
+ command,
612
+ commandDisplay,
613
+ { arguments: value.trim() },
614
+ state,
615
+ );
617
616
  }}
618
617
  />
619
618
  ));
620
619
  return;
621
620
  }
622
621
 
623
- replaceDialog(api, spec.dialogSize, () => <CommandLoadingDialog api={api} title={spec.title} />);
622
+ const destination =
623
+ commandDisplay === "inline" && sessionID
624
+ ? { type: "inline" as const, sessionID }
625
+ : { type: "dialog" as const };
626
+ if (destination.type === "dialog") {
627
+ replaceDialog(api, spec.dialogSize, () => (
628
+ <CommandLoadingDialog api={api} title={spec.title} />
629
+ ));
630
+ }
624
631
 
625
632
  try {
626
633
  const result = await buildQuotaDialogCommandOutput({
627
634
  command,
628
- arguments: args,
635
+ arguments: argumentsText,
629
636
  client: createTuiQuotaClient(api),
630
637
  roots: getTuiRuntimeRootHints(api),
631
638
  sessionID,
@@ -637,7 +644,7 @@ async function runQuotaDialogCommandAsync(
637
644
  }
638
645
  : undefined,
639
646
  log: async (message, extra) => {
640
- await (api as any).client?.app?.log?.({
647
+ await api.client.app.log({
641
648
  body: {
642
649
  service: "quota-toast",
643
650
  level: "debug",
@@ -649,7 +656,16 @@ async function runQuotaDialogCommandAsync(
649
656
  });
650
657
 
651
658
  if (result.state === "noop") {
652
- clearDialog(api);
659
+ if (destination.type === "dialog") api.ui.dialog.clear();
660
+ return;
661
+ }
662
+
663
+ if (destination.type === "inline") {
664
+ await api.client.session.prompt({
665
+ sessionID: destination.sessionID,
666
+ noReply: true,
667
+ parts: [{ type: "text", text: result.output, ignored: true }],
668
+ });
653
669
  return;
654
670
  }
655
671
 
@@ -657,29 +673,19 @@ async function runQuotaDialogCommandAsync(
657
673
  <CommandOutputDialog api={api} title={result.title} output={result.output} />
658
674
  ));
659
675
  } catch (error) {
660
- replaceDialog(api, "large", () => <CommandErrorDialog api={api} title={spec.title} error={error} />);
661
- (api as any).ui?.toast?.({
676
+ replaceDialog(api, "large", () => (
677
+ <CommandErrorDialog api={api} title={spec.title} error={error} />
678
+ ));
679
+ api.ui.toast({
662
680
  variant: "error",
663
681
  message: "OpenCode Quota command failed",
664
682
  });
665
683
  }
666
684
  }
667
685
 
668
- function runQuotaDialogCommand(
669
- api: TuiPluginApi,
670
- command: QuotaDialogCommandId,
671
- rawInput?: unknown,
672
- state?: QuotaDialogCommandState,
673
- ): void {
674
- void runQuotaDialogCommandAsync(api, command, rawInput, state);
675
- }
676
-
677
- function registerQuotaDialogCommands(api: TuiPluginApi): void {
678
- const keymap = (api as any).keymap;
679
- if (!keymap?.registerLayer) return;
680
-
686
+ function registerQuotaDialogCommands(api: TuiPluginApi, commandDisplay: TuiCommandDisplay): void {
681
687
  const commandState: QuotaDialogCommandState = {};
682
- const dispose = keymap.registerLayer({
688
+ const dispose = api.keymap.registerLayer({
683
689
  commands: QUOTA_DIALOG_COMMANDS.map((spec) => ({
684
690
  namespace: "palette",
685
691
  name: `opencode-quota.${spec.id}`,
@@ -688,14 +694,13 @@ function registerQuotaDialogCommands(api: TuiPluginApi): void {
688
694
  category: "OpenCode Quota",
689
695
  slashName: spec.slashName,
690
696
  run(input?: unknown) {
691
- runQuotaDialogCommand(api, spec.id, input, commandState);
697
+ void runQuotaDialogCommandAsync(api, spec.id, commandDisplay, input, commandState);
692
698
  },
693
699
  })),
700
+ bindings: [],
694
701
  });
695
702
 
696
- if (typeof dispose === "function") {
697
- api.lifecycle.onDispose(dispose);
698
- }
703
+ api.lifecycle.onDispose(dispose);
699
704
  }
700
705
 
701
706
  function registerSidebarSlots(api: TuiPluginApi): void {
@@ -710,16 +715,17 @@ function registerSidebarSlots(api: TuiPluginApi): void {
710
715
  }
711
716
 
712
717
  const tui: TuiPlugin = async (api) => {
713
- registerQuotaDialogCommands(api);
714
-
715
718
  let surfaceRegistration;
716
719
  try {
717
720
  surfaceRegistration = await resolveTuiSurfaceRegistration(api);
718
721
  } catch {
722
+ registerQuotaDialogCommands(api, "inline");
719
723
  registerSidebarSlots(api);
720
724
  return;
721
725
  }
722
726
 
727
+ registerQuotaDialogCommands(api, surfaceRegistration.commandDisplay);
728
+
723
729
  if (surfaceRegistration.sidebar.enabled) {
724
730
  registerSidebarSlots(api);
725
731
  }
@@ -752,7 +758,9 @@ const tui: TuiPlugin = async (api) => {
752
758
  }
753
759
 
754
760
  if (surfaceRegistration.homeBottom) {
755
- compactSlots.home_bottom = () => <HomeBottomView api={api} />;
761
+ compactSlots.home_bottom = () => (
762
+ <HomeBottomView api={api} compactHomeBottomEnabled={surfaceRegistration.compact.homeBottom} />
763
+ );
756
764
  }
757
765
 
758
766
  if (Object.keys(compactSlots).length > 0) {
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@slkiser/opencode-quota",
3
- "version": "3.11.1",
4
- "packageManager": "pnpm@11.0.0",
3
+ "version": "4.0.0",
5
4
  "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
5
  "type": "module",
7
6
  "main": "./dist/index.js",
@@ -32,21 +31,6 @@
32
31
  "README.md",
33
32
  "LICENSE"
34
33
  ],
35
- "scripts": {
36
- "build": "node scripts/clean-dist.mjs && tsc && node scripts/copy-data.mjs && node scripts/prepare-tui-dist.mjs",
37
- "build:check": "pnpm run build && pnpm pack --dry-run",
38
- "pricing:refresh": "node scripts/refresh-modelsdev-pricing.mjs",
39
- "pricing:refresh:if-stale": "node scripts/refresh-modelsdev-pricing-if-stale.mjs",
40
- "upstream:check": "node scripts/check-upstream-plugin-updates.mjs",
41
- "upstream:prepare-review": "node scripts/prepare-upstream-plugin-review.mjs",
42
- "upstream:sync": "node scripts/sync-upstream-plugin-references.mjs",
43
- "verify:release-version": "node scripts/verify-release-version.mjs",
44
- "typecheck": "tsc --noEmit",
45
- "test": "vitest run",
46
- "test:watch": "vitest",
47
- "prepublishOnly": "pnpm run build",
48
- "prepare": "husky"
49
- },
50
34
  "keywords": [
51
35
  "opencode",
52
36
  "plugin",
@@ -72,20 +56,22 @@
72
56
  "devDependencies": {
73
57
  "@babel/core": "^7.28.0",
74
58
  "@babel/preset-typescript": "^7.27.1",
75
- "@opencode-ai/plugin": "^1.4.3",
59
+ "@opencode-ai/plugin": "1.18.1",
76
60
  "@types/node": "^22.0.0",
77
61
  "babel-preset-solid": "^1.9.12",
78
62
  "husky": "^9.1.7",
79
63
  "lint-staged": "^16.2.7",
80
64
  "prettier": "^3.8.0",
81
65
  "typescript": "^5.9.3",
82
- "vitest": "^4.0.17"
66
+ "vitest": "^4.0.17",
67
+ "yaml": "^2.8.3"
83
68
  },
84
69
  "dependencies": {
85
70
  "@clack/prompts": "^1.2.0",
86
71
  "@opentui/core": "^0.4.3",
87
72
  "@opentui/solid": "^0.4.3",
88
73
  "comment-json": "^5.0.0",
74
+ "jsonc-parser": "^3.3.1",
89
75
  "solid-js": "^1.9.12",
90
76
  "xdg-basedir": "^5.1.0"
91
77
  },
@@ -96,6 +82,28 @@
96
82
  "@opencode-ai/plugin": "^1.4.3"
97
83
  },
98
84
  "engines": {
99
- "node": ">=20.0.0"
85
+ "node": ">=22.0.0"
86
+ },
87
+ "scripts": {
88
+ "build": "node scripts/clean-dist.mjs && tsc && node scripts/copy-data.mjs && node scripts/prepare-tui-dist.mjs",
89
+ "build:check": "pnpm run build && pnpm pack --dry-run",
90
+ "pack:release-package": "node scripts/pack-release-package.mjs",
91
+ "format:check": "node scripts/verify-v4-formatting.mjs",
92
+ "pricing:refresh": "node scripts/refresh-modelsdev-pricing.mjs",
93
+ "pricing:refresh:if-stale": "node scripts/refresh-modelsdev-pricing-if-stale.mjs",
94
+ "upstream:check": "node scripts/check-upstream-plugin-updates.mjs",
95
+ "upstream:prepare-review": "node scripts/prepare-upstream-plugin-review.mjs",
96
+ "upstream:sync": "node scripts/sync-upstream-plugin-references.mjs",
97
+ "release:check": "pnpm run format:check && pnpm run verify:typescript-version && pnpm run verify:v4-history && pnpm run typecheck && pnpm run build && pnpm test && pnpm run test:four-surfaces && pnpm run verify:release-package && pnpm run verify:release-version",
98
+ "smoke:package": "node scripts/smoke-packed-package.mjs",
99
+ "test:four-surfaces": "vitest run tests/v4-phase5-cross-surface.test.ts",
100
+ "verify:package-contents": "node scripts/verify-package-contents.mjs",
101
+ "verify:release-package": "node scripts/verify-release-package.mjs",
102
+ "verify:release-version": "node scripts/verify-release-version.mjs",
103
+ "verify:typescript-version": "node scripts/verify-typescript-version.mjs",
104
+ "verify:v4-history": "node scripts/verify-v4-history.mjs",
105
+ "typecheck": "tsc --noEmit",
106
+ "test": "vitest run",
107
+ "test:watch": "vitest"
100
108
  }
101
- }
109
+ }