@xtruder/opencode-claude-max-plugin 0.1.17 → 0.2.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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  An [OpenCode](https://opencode.ai/) plugin that enables Claude Pro/Max subscription access via the official [`@anthropic-ai/sdk`](https://github.com/anthropics/anthropic-sdk-typescript), using OAuth credentials from Claude Code (`~/.claude/.credentials.json`).
4
4
 
5
+ ![Usage sidebar and /usage command dialog](assets/example-with-usage.png)
6
+
5
7
  ## Why?
6
8
 
7
9
  - **Use your Claude subscription** — Automatically reads OAuth credentials from Claude Code, no separate API key needed
@@ -9,7 +11,7 @@ An [OpenCode](https://opencode.ai/) plugin that enables Claude Pro/Max subscript
9
11
  - **Prompt caching** — 98% of input tokens served from cache (system prompt + tools cached globally)
10
12
  - **All Claude models** — Opus 4.6, Sonnet 4.6, Haiku 4.5
11
13
  - **Extended thinking** — Full reasoning support with thinking variants (high/max)
12
- - **Usage tracking** — Built-in `/usage` command shows subscription utilization
14
+ - **Usage tracking** — Sidebar widget with live progress bars + `/usage` command with full details
13
15
  - **Self-registering** — Models are registered automatically, no manual provider config needed
14
16
 
15
17
  ## Installation
@@ -27,6 +29,57 @@ That's it. The plugin self-registers the `anthropic-sdk` provider and its models
27
29
 
28
30
  Then open OpenCode and run `/connect` → Other → `anthropic-sdk`. If Claude Code is installed and you're logged in, credentials are read automatically from `~/.claude/.credentials.json` — no API key needed.
29
31
 
32
+ ### TUI Plugin (sidebar + /usage command)
33
+
34
+ The plugin includes a TUI component that shows subscription usage in the sidebar and registers a `/usage` slash command. To enable it, add the plugin to your `tui.json` as well:
35
+
36
+ **Project-level** (`.opencode/tui.json`):
37
+
38
+ ```json
39
+ {
40
+ "plugin": ["@xtruder/opencode-claude-max-plugin"]
41
+ }
42
+ ```
43
+
44
+ **Or globally** (`~/.config/opencode/tui.json`):
45
+
46
+ ```json
47
+ {
48
+ "plugin": ["@xtruder/opencode-claude-max-plugin"]
49
+ }
50
+ ```
51
+
52
+ The TUI plugin provides:
53
+
54
+ - **Sidebar widget** — Compact progress bars for 5-hour session and 7-day weekly usage
55
+ - **`/usage` command** — Opens a dialog with full usage breakdown (per-model, extra usage)
56
+ - **Auto-refresh** — Polls the usage API every 60s and after each inference call
57
+
58
+ #### TUI Configuration
59
+
60
+ Options can be set in the `tui.json` plugin entry:
61
+
62
+ ```json
63
+ {
64
+ "plugin": [
65
+ [
66
+ "@xtruder/opencode-claude-max-plugin",
67
+ {
68
+ "enabled": true,
69
+ "sidebar": true,
70
+ "poll_interval": 60
71
+ }
72
+ ]
73
+ ]
74
+ }
75
+ ```
76
+
77
+ | Option | Type | Default | Description |
78
+ | --------------- | ------- | ------- | ----------------------------------------- |
79
+ | `enabled` | boolean | `true` | Enable/disable the TUI plugin entirely |
80
+ | `sidebar` | boolean | `true` | Show/hide sidebar usage widget |
81
+ | `poll_interval` | number | `60` | Seconds between usage API polls (min: 10) |
82
+
30
83
  ### Custom model options
31
84
 
32
85
  If you want to override model settings (e.g. thinking budgets, variants), you can add a `provider` block alongside the plugin:
@@ -71,25 +124,10 @@ For Claude Code credentials, log in via `claude` CLI first (`claude auth login`)
71
124
  - MCP tool name remapping (`server_tool` → `mcp__server__tool`)
72
125
  - Extended thinking with signature passthrough for multi-turn conversations
73
126
  - Prompt caching (98% cache hit rate with full OpenCode tool set)
127
+ - CCH request signing — computes xxHash64 body integrity hash to unlock features like fast mode
74
128
  - Subscription rate limit detection — fails fast with clear message instead of hanging
75
129
  - Long context auto-detection — adds `context-1m` beta header when request body is large
76
- - `/usage` slash command — shows current session and weekly utilization
77
-
78
- ## Usage Command
79
-
80
- After adding the config, run `/usage` inside OpenCode to see your subscription usage:
81
-
82
- ```
83
- Claude Subscription Usage
84
- ────────────────────────────────────────────────────
85
- Current session
86
- ████████████████████████████████░░░░░░░░░░░░░░░░░░ 67% used
87
- Resets 7:00 PM GMT+1
88
-
89
- Current week (all models)
90
- ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8% used
91
- Resets Mar 30, 7:00 AM GMT+2
92
- ```
130
+ - TUI sidebar with live usage bars + `/usage` slash command with full subscription details
93
131
 
94
132
  ## With Vercel AI SDK
95
133
 
package/build/index.js CHANGED
@@ -272,194 +272,6 @@ function formatToolResultContent(output) {
272
272
  }
273
273
  }
274
274
 
275
- // src/usage-cache.ts
276
- var cachedUsage = {};
277
-
278
- // src/credentials.ts
279
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
280
- import { execSync } from "node:child_process";
281
- import { join as join2 } from "node:path";
282
- import { homedir as homedir2 } from "node:os";
283
- var CREDENTIAL_CACHE_TTL_MS = 30000;
284
- var EXPIRY_BUFFER_MS = 60000;
285
- var cachedCreds = null;
286
- var cachedAt = 0;
287
- var cachedPath;
288
- function getCredentialsPath() {
289
- return join2(homedir2(), ".claude", ".credentials.json");
290
- }
291
- function readClaudeCredentials(path) {
292
- const credPath = path ?? getCredentialsPath();
293
- if (!existsSync2(credPath))
294
- return null;
295
- try {
296
- const raw = readFileSync2(credPath, "utf-8");
297
- const parsed = JSON.parse(raw);
298
- if (!parsed.claudeAiOauth?.accessToken)
299
- return null;
300
- return parsed.claudeAiOauth;
301
- } catch {
302
- return null;
303
- }
304
- }
305
- function isExpired(creds) {
306
- return Date.now() > creds.expiresAt - EXPIRY_BUFFER_MS;
307
- }
308
- function refreshViaCli() {
309
- const maxAttempts = 2;
310
- for (let i = 0;i < maxAttempts; i++) {
311
- try {
312
- execSync("claude -p . --model haiku", {
313
- timeout: 60000,
314
- encoding: "utf-8",
315
- env: { ...process.env, TERM: "dumb" },
316
- stdio: "ignore"
317
- });
318
- return true;
319
- } catch {}
320
- }
321
- return false;
322
- }
323
- function refreshIfNeeded(path) {
324
- const creds = readClaudeCredentials(path);
325
- if (!creds)
326
- return null;
327
- if (!isExpired(creds))
328
- return creds;
329
- console.warn("[anthropic-sdk-provider] OAuth token near expiry, refreshing via Claude CLI...");
330
- const refreshed = refreshViaCli();
331
- if (!refreshed) {
332
- console.warn("[anthropic-sdk-provider] CLI refresh failed. Token may be expired.");
333
- return creds;
334
- }
335
- const fresh = readClaudeCredentials(path);
336
- if (fresh && !isExpired(fresh)) {
337
- console.warn("[anthropic-sdk-provider] Token refreshed successfully.");
338
- return fresh;
339
- }
340
- return fresh ?? creds;
341
- }
342
- function getCachedCredentials(path) {
343
- const now = Date.now();
344
- const resolvedPath = path ?? cachedPath;
345
- if (cachedCreds && now - cachedAt < CREDENTIAL_CACHE_TTL_MS && !isExpired(cachedCreds)) {
346
- return cachedCreds;
347
- }
348
- const fresh = refreshIfNeeded(resolvedPath);
349
- if (fresh) {
350
- cachedCreds = fresh;
351
- cachedAt = now;
352
- cachedPath = resolvedPath;
353
- } else {
354
- cachedCreds = null;
355
- }
356
- return cachedCreds;
357
- }
358
-
359
- // src/usage.ts
360
- var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
361
- async function fetchUsage(credentialsPath) {
362
- const creds = readClaudeCredentials(credentialsPath);
363
- if (!creds)
364
- return null;
365
- if (isExpired(creds))
366
- return null;
367
- const resp = await fetch(USAGE_URL, {
368
- headers: {
369
- authorization: `Bearer ${creds.accessToken}`,
370
- "anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
371
- "content-type": "application/json",
372
- "user-agent": "claude-cli/2.1.81 (external, sdk-cli)",
373
- "x-app": "cli"
374
- }
375
- });
376
- if (!resp.ok) {
377
- const body = await resp.text().catch(() => "");
378
- const msg = body.includes("message") ? JSON.parse(body)?.error?.message ?? body : `HTTP ${resp.status}`;
379
- throw new Error(`Usage API error: ${msg}`);
380
- }
381
- return resp.json();
382
- }
383
- function progressBar(utilization, width = 50) {
384
- const filled = Math.round(utilization / 100 * width);
385
- const bar = "█".repeat(filled) + " ".repeat(width - filled);
386
- return bar;
387
- }
388
- function formatReset(resetsAt) {
389
- if (!resetsAt)
390
- return "";
391
- const reset = new Date(resetsAt);
392
- const now = new Date;
393
- const diffMs = reset.getTime() - now.getTime();
394
- if (diffMs <= 0)
395
- return "Reset now";
396
- const diffMin = Math.floor(diffMs / 60000);
397
- const diffHour = Math.floor(diffMin / 60);
398
- const timeStr = reset.toLocaleTimeString("en-US", {
399
- hour: "numeric",
400
- minute: "2-digit",
401
- timeZoneName: "short"
402
- });
403
- const dateStr = reset.toLocaleDateString("en-US", {
404
- month: "short",
405
- day: "numeric"
406
- });
407
- if (diffHour < 24) {
408
- return `Resets ${timeStr}`;
409
- }
410
- return `Resets ${dateStr}, ${timeStr}`;
411
- }
412
- function formatUsage(data) {
413
- const lines = [];
414
- if (data.five_hour) {
415
- const pct = Math.round(data.five_hour.utilization);
416
- lines.push(" Current session");
417
- lines.push(` ${progressBar(pct)} ${pct}% used`);
418
- lines.push(` ${formatReset(data.five_hour.resets_at)}`);
419
- lines.push("");
420
- }
421
- if (data.seven_day) {
422
- const pct = Math.round(data.seven_day.utilization);
423
- lines.push(" Current week (all models)");
424
- lines.push(` ${progressBar(pct)} ${pct}% used`);
425
- lines.push(` ${formatReset(data.seven_day.resets_at)}`);
426
- lines.push("");
427
- }
428
- if (data.seven_day_sonnet) {
429
- const pct = Math.round(data.seven_day_sonnet.utilization);
430
- lines.push(" Current week (Sonnet only)");
431
- lines.push(` ${progressBar(pct)} ${pct}% used`);
432
- if (data.seven_day_sonnet.resets_at) {
433
- lines.push(` ${formatReset(data.seven_day_sonnet.resets_at)}`);
434
- }
435
- lines.push("");
436
- }
437
- if (data.seven_day_opus) {
438
- const pct = Math.round(data.seven_day_opus.utilization);
439
- lines.push(" Current week (Opus only)");
440
- lines.push(` ${progressBar(pct)} ${pct}% used`);
441
- if (data.seven_day_opus.resets_at) {
442
- lines.push(` ${formatReset(data.seven_day_opus.resets_at)}`);
443
- }
444
- lines.push("");
445
- }
446
- if (data.extra_usage) {
447
- if (data.extra_usage.is_enabled) {
448
- const pct = Math.round(data.extra_usage.utilization ?? 0);
449
- lines.push(" Extra usage");
450
- lines.push(` ${progressBar(pct)} ${pct}% used`);
451
- if (data.extra_usage.monthly_limit != null) {
452
- lines.push(` $${data.extra_usage.used_credits?.toFixed(2) ?? "0.00"} / $${data.extra_usage.monthly_limit.toFixed(2)}`);
453
- }
454
- } else {
455
- lines.push(" Extra usage: disabled");
456
- }
457
- lines.push("");
458
- }
459
- return lines.join(`
460
- `);
461
- }
462
-
463
275
  // src/tools.ts
464
276
  function cleanSchema(schema) {
465
277
  const cleaned = {};
@@ -732,74 +544,6 @@ function buildMetadata() {
732
544
  user_id: JSON.stringify({ device_id: DEVICE_ID })
733
545
  };
734
546
  }
735
- function isUsageCommand(prompt) {
736
- for (let i = prompt.length - 1;i >= 0; i--) {
737
- const msg = prompt[i];
738
- if (msg.role !== "user")
739
- continue;
740
- const text = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("").trim();
741
- return /^\/usage(\s|$)/i.test(text);
742
- }
743
- return false;
744
- }
745
- function formatReset2(epoch) {
746
- if (!epoch)
747
- return "";
748
- const reset = new Date(epoch * 1000);
749
- const now = new Date;
750
- const diffMs = reset.getTime() - now.getTime();
751
- if (diffMs <= 0)
752
- return "Reset now";
753
- const timeStr = reset.toLocaleTimeString("en-US", {
754
- hour: "numeric",
755
- minute: "2-digit",
756
- timeZoneName: "short"
757
- });
758
- const dateStr = reset.toLocaleDateString("en-US", { month: "short", day: "numeric" });
759
- return diffMs < 86400000 ? `Resets ${timeStr}` : `Resets ${dateStr}, ${timeStr}`;
760
- }
761
- var bar = (pct, width = 50) => "█".repeat(Math.round(pct / 100 * width)) + " ".repeat(width - Math.round(pct / 100 * width));
762
- function formatCachedUsage(usage) {
763
- const lines = [];
764
- const h5 = Math.round((usage.fiveHourUtil ?? 0) * 100);
765
- lines.push(" Current session");
766
- lines.push(` ${bar(h5)} ${h5}% used`);
767
- if (usage.fiveHourReset)
768
- lines.push(` ${formatReset2(usage.fiveHourReset)}`);
769
- lines.push("");
770
- const d7 = Math.round((usage.sevenDayUtil ?? 0) * 100);
771
- lines.push(" Current week");
772
- lines.push(` ${bar(d7)} ${d7}% used`);
773
- if (usage.sevenDayReset)
774
- lines.push(` ${formatReset2(usage.sevenDayReset)}`);
775
- lines.push("");
776
- if (usage.overageStatus === "rejected") {
777
- lines.push(" Extra usage: disabled");
778
- } else if (usage.overageStatus) {
779
- lines.push(` Extra usage: ${usage.overageStatus}`);
780
- }
781
- return lines.join(`
782
- `);
783
- }
784
- async function handleUsageCommand() {
785
- if (cachedUsage.timestamp) {
786
- throw new Error(`Claude Subscription Usage
787
- ` + "─".repeat(52) + `
788
- ` + formatCachedUsage(cachedUsage));
789
- }
790
- try {
791
- const data = await fetchUsage();
792
- if (data) {
793
- throw new Error(`Claude Subscription Usage
794
- ` + "─".repeat(52) + `
795
- ` + formatUsage(data));
796
- }
797
- } catch (e) {
798
- if (e.message?.startsWith("Claude Subscription Usage"))
799
- throw e;
800
- }
801
- throw new Error("No usage data available. Send a message first, then try /usage again.");
802
- }
803
547
 
804
548
  class AnthropicSDKModel {
805
549
  client;
@@ -940,8 +684,6 @@ class AnthropicSDKModel {
940
684
  return { params, warnings };
941
685
  }
942
686
  async doGenerate(options) {
943
- if (isUsageCommand(options.prompt))
944
- await handleUsageCommand();
945
687
  const { params, warnings } = this.buildParams(options);
946
688
  if (!options.maxOutputTokens && params.max_tokens > 4096) {
947
689
  params.max_tokens = 4096;
@@ -1014,8 +756,6 @@ class AnthropicSDKModel {
1014
756
  };
1015
757
  }
1016
758
  async doStream(options) {
1017
- if (isUsageCommand(options.prompt))
1018
- await handleUsageCommand();
1019
759
  const { params, warnings } = this.buildParams(options);
1020
760
  let anthropicStream;
1021
761
  try {
@@ -1078,6 +818,180 @@ class AnthropicSDKModel {
1078
818
  }
1079
819
  }
1080
820
 
821
+ // src/credentials.ts
822
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
823
+ import { execSync } from "node:child_process";
824
+ import { join as join2 } from "node:path";
825
+ import { homedir as homedir2 } from "node:os";
826
+ var CREDENTIAL_CACHE_TTL_MS = 30000;
827
+ var EXPIRY_BUFFER_MS = 60000;
828
+ var cachedCreds = null;
829
+ var cachedAt = 0;
830
+ var cachedPath;
831
+ function getCredentialsPath() {
832
+ return join2(homedir2(), ".claude", ".credentials.json");
833
+ }
834
+ function readClaudeCredentials(path) {
835
+ const credPath = path ?? getCredentialsPath();
836
+ if (!existsSync2(credPath))
837
+ return null;
838
+ try {
839
+ const raw = readFileSync2(credPath, "utf-8");
840
+ const parsed = JSON.parse(raw);
841
+ if (!parsed.claudeAiOauth?.accessToken)
842
+ return null;
843
+ return parsed.claudeAiOauth;
844
+ } catch {
845
+ return null;
846
+ }
847
+ }
848
+ function isExpired(creds) {
849
+ return Date.now() > creds.expiresAt - EXPIRY_BUFFER_MS;
850
+ }
851
+ function refreshViaCli() {
852
+ const maxAttempts = 2;
853
+ for (let i = 0;i < maxAttempts; i++) {
854
+ try {
855
+ execSync("claude -p . --model haiku", {
856
+ timeout: 60000,
857
+ encoding: "utf-8",
858
+ env: { ...process.env, TERM: "dumb" },
859
+ stdio: "ignore"
860
+ });
861
+ return true;
862
+ } catch {}
863
+ }
864
+ return false;
865
+ }
866
+ function refreshIfNeeded(path) {
867
+ const creds = readClaudeCredentials(path);
868
+ if (!creds)
869
+ return null;
870
+ if (!isExpired(creds))
871
+ return creds;
872
+ console.warn("[anthropic-sdk-provider] OAuth token near expiry, refreshing via Claude CLI...");
873
+ const refreshed = refreshViaCli();
874
+ if (!refreshed) {
875
+ console.warn("[anthropic-sdk-provider] CLI refresh failed. Token may be expired.");
876
+ return creds;
877
+ }
878
+ const fresh = readClaudeCredentials(path);
879
+ if (fresh && !isExpired(fresh)) {
880
+ console.warn("[anthropic-sdk-provider] Token refreshed successfully.");
881
+ return fresh;
882
+ }
883
+ return fresh ?? creds;
884
+ }
885
+ function getCachedCredentials(path) {
886
+ const now = Date.now();
887
+ const resolvedPath = path ?? cachedPath;
888
+ if (cachedCreds && now - cachedAt < CREDENTIAL_CACHE_TTL_MS && !isExpired(cachedCreds)) {
889
+ return cachedCreds;
890
+ }
891
+ const fresh = refreshIfNeeded(resolvedPath);
892
+ if (fresh) {
893
+ cachedCreds = fresh;
894
+ cachedAt = now;
895
+ cachedPath = resolvedPath;
896
+ } else {
897
+ cachedCreds = null;
898
+ }
899
+ return cachedCreds;
900
+ }
901
+
902
+ // src/usage.ts
903
+ import {
904
+ readFileSync as readFileSync3,
905
+ writeFileSync,
906
+ mkdirSync,
907
+ unlinkSync,
908
+ openSync,
909
+ closeSync,
910
+ renameSync,
911
+ constants as fsConstants
912
+ } from "node:fs";
913
+ import { join as join3 } from "node:path";
914
+ import { homedir as homedir3 } from "node:os";
915
+ var cachedUsage = {};
916
+ var XDG_STATE = process.env.XDG_STATE_HOME || join3(homedir3(), ".local", "state");
917
+ var USAGE_CACHE_DIR = join3(XDG_STATE, "opencode");
918
+ var USAGE_CACHE_FILE = join3(USAGE_CACHE_DIR, "usage-cache.json");
919
+ function emptyCache() {
920
+ return { api: null, headers: null, updatedAt: 0, apiFetchedAt: 0, apiRateLimitUntil: 0 };
921
+ }
922
+ var USAGE_LOCK_FILE = USAGE_CACHE_FILE + ".lock";
923
+ var USAGE_TMP_FILE = USAGE_CACHE_FILE + ".tmp";
924
+ var LOCK_STALE_MS = 5000;
925
+ function acquireLock() {
926
+ try {
927
+ mkdirSync(USAGE_CACHE_DIR, { recursive: true });
928
+ const fd = openSync(USAGE_LOCK_FILE, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY);
929
+ writeFileSync(fd, `${process.pid}
930
+ ${Date.now()}`);
931
+ closeSync(fd);
932
+ return true;
933
+ } catch (err) {
934
+ if (err?.code !== "EEXIST")
935
+ return false;
936
+ try {
937
+ const text = readFileSync3(USAGE_LOCK_FILE, "utf-8");
938
+ const ts = parseInt(text.split(`
939
+ `)[1] ?? "0");
940
+ if (Date.now() - ts > LOCK_STALE_MS) {
941
+ unlinkSync(USAGE_LOCK_FILE);
942
+ return acquireLock();
943
+ }
944
+ } catch {
945
+ try {
946
+ unlinkSync(USAGE_LOCK_FILE);
947
+ } catch {}
948
+ return acquireLock();
949
+ }
950
+ return false;
951
+ }
952
+ }
953
+ function releaseLock() {
954
+ try {
955
+ unlinkSync(USAGE_LOCK_FILE);
956
+ } catch {}
957
+ }
958
+ function readUsageCache() {
959
+ try {
960
+ const text = readFileSync3(USAGE_CACHE_FILE, "utf-8");
961
+ const data = JSON.parse(text);
962
+ if (data && typeof data.updatedAt === "number")
963
+ return data;
964
+ } catch {}
965
+ return emptyCache();
966
+ }
967
+ function writeUsageCache(update) {
968
+ if (!acquireLock())
969
+ return;
970
+ try {
971
+ const existing = readUsageCache();
972
+ const merged = {
973
+ ...existing,
974
+ ...update,
975
+ updatedAt: Date.now()
976
+ };
977
+ mkdirSync(USAGE_CACHE_DIR, { recursive: true });
978
+ writeFileSync(USAGE_TMP_FILE, JSON.stringify(merged));
979
+ renameSync(USAGE_TMP_FILE, USAGE_CACHE_FILE);
980
+ } catch {
981
+ try {
982
+ unlinkSync(USAGE_TMP_FILE);
983
+ } catch {}
984
+ } finally {
985
+ releaseLock();
986
+ }
987
+ }
988
+ function persistCachedUsage() {
989
+ if (cachedUsage.fiveHourUtil == null && cachedUsage.sevenDayUtil == null)
990
+ return;
991
+ writeUsageCache({ headers: { ...cachedUsage } });
992
+ }
993
+ var API_TTL_MS = 5 * 60000;
994
+
1081
995
  // src/cch.ts
1082
996
  import xxhashInit from "xxhash-wasm";
1083
997
  var CCH_SEED = 0x6e52736ac806831en;
@@ -1230,7 +1144,7 @@ function createAnthropicSDK(options = {}) {
1230
1144
  cachedUsage.fiveHourReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-5h-reset") ?? "0");
1231
1145
  cachedUsage.sevenDayReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-7d-reset") ?? "0");
1232
1146
  cachedUsage.overageStatus = resp.headers.get("anthropic-ratelimit-unified-overage-status") ?? undefined;
1233
- cachedUsage.timestamp = Date.now();
1147
+ persistCachedUsage();
1234
1148
  }
1235
1149
  if (resp.status === 429) {
1236
1150
  const unifiedStatus = resp.headers.get("anthropic-ratelimit-unified-status") ?? "";
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAI1B,6BAA6B,EAC7B,2BAA2B,EAE5B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAU9C,KAAK,gBAAgB,GAAG,6BAA6B,CAAA;AACrD,KAAK,cAAc,GAAG,2BAA2B,CAAA;AAwLjD,qBAAa,iBAAkB,YAAW,eAAe;IAQrD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IATjB,QAAQ,CAAC,oBAAoB,EAAG,IAAI,CAAS;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAK;gBAGnD,OAAO,EAAE,MAAM,EACP,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAe;IAMlC,OAAO,CAAC,WAAW;IA2Lb,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuF1E,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;CA6E7E"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAI1B,6BAA6B,EAC7B,2BAA2B,EAE5B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAQ9C,KAAK,gBAAgB,GAAG,6BAA6B,CAAA;AACrD,KAAK,cAAc,GAAG,2BAA2B,CAAA;AAkGjD,qBAAa,iBAAkB,YAAW,eAAe;IAQrD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IATjB,QAAQ,CAAC,oBAAoB,EAAG,IAAI,CAAS;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAK;gBAGnD,OAAO,EAAE,MAAM,EACP,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAe;IAMlC,OAAO,CAAC,WAAW;IA2Lb,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsF1E,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;CA4E7E"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Server-side plugin entry point (V1 plugin format).
3
+ *
4
+ * This module re-exports the server plugin in the format expected by
5
+ * OpenCode's V1 plugin loader: `default export { id, server }`.
6
+ *
7
+ * The `./server` export in package.json points here.
8
+ */
9
+ import type { PluginModule } from "@opencode-ai/plugin";
10
+ declare const plugin: PluginModule & {
11
+ id: string;
12
+ };
13
+ export default plugin;
14
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAO/D,QAAA,MAAM,MAAM,EAAE,YAAY,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAGxC,CAAA;AAED,eAAe,MAAM,CAAA"}