@xtruder/opencode-claude-max-plugin 0.1.8 → 0.1.9
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/build/agent-index.js +9 -1
- package/build/agent-model.d.ts.map +1 -1
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +228 -29
- package/build/model.d.ts.map +1 -1
- package/build/stream.d.ts.map +1 -1
- package/build/usage-cli.js +12 -3
- package/build/usage.d.ts.map +1 -1
- package/package.json +2 -7
- package/build/plugin.d.ts +0 -14
- package/build/plugin.d.ts.map +0 -1
- package/build/plugin.js +0 -175
package/build/agent-index.js
CHANGED
|
@@ -92,13 +92,21 @@ function convertAgentStream(stream, modelId) {
|
|
|
92
92
|
}
|
|
93
93
|
if (msg.type === "result") {
|
|
94
94
|
const result = msg;
|
|
95
|
+
const cacheRead = result.usage?.cache_read_input_tokens ?? 0;
|
|
96
|
+
const cacheCreate = result.usage?.cache_creation_input_tokens ?? 0;
|
|
95
97
|
controller.enqueue({
|
|
96
98
|
type: "finish",
|
|
97
99
|
finishReason: mapFinishReason(result.stop_reason),
|
|
98
100
|
usage: {
|
|
99
101
|
inputTokens: result.usage?.input_tokens ?? 0,
|
|
100
102
|
outputTokens: result.usage?.output_tokens ?? 0,
|
|
101
|
-
totalTokens: (result.usage?.input_tokens ?? 0) + (result.usage?.output_tokens ?? 0)
|
|
103
|
+
totalTokens: (result.usage?.input_tokens ?? 0) + (result.usage?.output_tokens ?? 0) + cacheRead + cacheCreate,
|
|
104
|
+
cachedInputTokens: cacheRead
|
|
105
|
+
},
|
|
106
|
+
providerMetadata: {
|
|
107
|
+
anthropic: {
|
|
108
|
+
cacheCreationInputTokens: cacheCreate
|
|
109
|
+
}
|
|
102
110
|
}
|
|
103
111
|
});
|
|
104
112
|
firstAssistantTurnDone = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-model.d.ts","sourceRoot":"","sources":["../src/agent-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAO3B,MAAM,kBAAkB,CAAA;AAMzB,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;AACtE,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"agent-model.d.ts","sourceRoot":"","sources":["../src/agent-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAO3B,MAAM,kBAAkB,CAAA;AAMzB,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;AACtE,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AA6J1E,qBAAa,aAAc,YAAW,eAAe;IAajD,OAAO,CAAC,YAAY;IAZtB,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;IAErD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,qBAAqB,CAAI;IACjC,OAAO,CAAC,gBAAgB,CAAgC;gBAItD,OAAO,EAAE,MAAM,EACP,YAAY,EAAE,MAAM;IAM9B;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;IAoBf,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;IA2EtE,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+DhF;;OAEG;IACH,KAAK;CAMN"}
|
package/build/index.d.ts
CHANGED
|
@@ -51,6 +51,20 @@ export interface AnthropicSDKProvider {
|
|
|
51
51
|
*/
|
|
52
52
|
languageModel(modelId: string): LanguageModelV2;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Cached ratelimit usage from the most recent API response headers.
|
|
56
|
+
* Updated on every successful inference call — same data Claude Code
|
|
57
|
+
* reads for its /usage display. No extra API call needed.
|
|
58
|
+
*/
|
|
59
|
+
export interface CachedUsage {
|
|
60
|
+
fiveHourUtil?: number;
|
|
61
|
+
sevenDayUtil?: number;
|
|
62
|
+
fiveHourReset?: number;
|
|
63
|
+
sevenDayReset?: number;
|
|
64
|
+
overageStatus?: string;
|
|
65
|
+
timestamp?: number;
|
|
66
|
+
}
|
|
67
|
+
export declare const cachedUsage: CachedUsage;
|
|
54
68
|
/**
|
|
55
69
|
* Create an Anthropic SDK provider for use with OpenCode / Vercel AI SDK.
|
|
56
70
|
*
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAQvD;;;GAGG;AACH,eAAO,MAAM,WAAW,UAOvB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,0BAA0B,CAAA;AAEtD;;GAEG;AACH,eAAO,MAAM,aAAa,UAGzB,CAAA;AAED,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEhC;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAE/B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAA;CAChD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAQvD;;;GAGG;AACH,eAAO,MAAM,WAAW,UAOvB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,0BAA0B,CAAA;AAEtD;;GAEG;AACH,eAAO,MAAM,aAAa,UAGzB,CAAA;AAED,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEhC;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAE/B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAA;CAChD;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,WAAW,EAAE,WAAgB,CAAA;AAgC1C;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,2BAAgC,GACxC,oBAAoB,CA0GtB;AAGD,eAAe,kBAAkB,CAAA;AAGjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,kBAAkB,EAClB,KAAK,sBAAsB,GAC5B,MAAM,kBAAkB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -265,6 +265,136 @@ function formatToolResultContent(output) {
|
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
// src/credentials.ts
|
|
269
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
270
|
+
import { join as join2 } from "node:path";
|
|
271
|
+
import { homedir as homedir2 } from "node:os";
|
|
272
|
+
function getCredentialsPath() {
|
|
273
|
+
return join2(homedir2(), ".claude", ".credentials.json");
|
|
274
|
+
}
|
|
275
|
+
function readClaudeCredentials(path) {
|
|
276
|
+
const credPath = path ?? getCredentialsPath();
|
|
277
|
+
if (!existsSync2(credPath))
|
|
278
|
+
return null;
|
|
279
|
+
try {
|
|
280
|
+
const raw = readFileSync2(credPath, "utf-8");
|
|
281
|
+
const parsed = JSON.parse(raw);
|
|
282
|
+
if (!parsed.claudeAiOauth?.accessToken)
|
|
283
|
+
return null;
|
|
284
|
+
return parsed.claudeAiOauth;
|
|
285
|
+
} catch {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function isExpired(creds) {
|
|
290
|
+
const buffer = 5 * 60 * 1000;
|
|
291
|
+
return Date.now() > creds.expiresAt - buffer;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/usage.ts
|
|
295
|
+
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
296
|
+
async function fetchUsage(credentialsPath) {
|
|
297
|
+
const creds = readClaudeCredentials(credentialsPath);
|
|
298
|
+
if (!creds)
|
|
299
|
+
return null;
|
|
300
|
+
if (isExpired(creds))
|
|
301
|
+
return null;
|
|
302
|
+
const resp = await fetch(USAGE_URL, {
|
|
303
|
+
headers: {
|
|
304
|
+
authorization: `Bearer ${creds.accessToken}`,
|
|
305
|
+
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
306
|
+
"content-type": "application/json",
|
|
307
|
+
"user-agent": "claude-cli/2.1.81 (external, sdk-cli)",
|
|
308
|
+
"x-app": "cli"
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
if (!resp.ok) {
|
|
312
|
+
const body = await resp.text().catch(() => "");
|
|
313
|
+
const msg = body.includes("message") ? JSON.parse(body)?.error?.message ?? body : `HTTP ${resp.status}`;
|
|
314
|
+
throw new Error(`Usage API error: ${msg}`);
|
|
315
|
+
}
|
|
316
|
+
return resp.json();
|
|
317
|
+
}
|
|
318
|
+
function progressBar(utilization, width = 50) {
|
|
319
|
+
const filled = Math.round(utilization / 100 * width);
|
|
320
|
+
const bar = "█".repeat(filled) + " ".repeat(width - filled);
|
|
321
|
+
return bar;
|
|
322
|
+
}
|
|
323
|
+
function formatReset(resetsAt) {
|
|
324
|
+
if (!resetsAt)
|
|
325
|
+
return "";
|
|
326
|
+
const reset = new Date(resetsAt);
|
|
327
|
+
const now = new Date;
|
|
328
|
+
const diffMs = reset.getTime() - now.getTime();
|
|
329
|
+
if (diffMs <= 0)
|
|
330
|
+
return "Reset now";
|
|
331
|
+
const diffMin = Math.floor(diffMs / 60000);
|
|
332
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
333
|
+
const timeStr = reset.toLocaleTimeString("en-US", {
|
|
334
|
+
hour: "numeric",
|
|
335
|
+
minute: "2-digit",
|
|
336
|
+
timeZoneName: "short"
|
|
337
|
+
});
|
|
338
|
+
const dateStr = reset.toLocaleDateString("en-US", {
|
|
339
|
+
month: "short",
|
|
340
|
+
day: "numeric"
|
|
341
|
+
});
|
|
342
|
+
if (diffHour < 24) {
|
|
343
|
+
return `Resets ${timeStr}`;
|
|
344
|
+
}
|
|
345
|
+
return `Resets ${dateStr}, ${timeStr}`;
|
|
346
|
+
}
|
|
347
|
+
function formatUsage(data) {
|
|
348
|
+
const lines = [];
|
|
349
|
+
if (data.five_hour) {
|
|
350
|
+
const pct = Math.round(data.five_hour.utilization);
|
|
351
|
+
lines.push(" Current session");
|
|
352
|
+
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
353
|
+
lines.push(` ${formatReset(data.five_hour.resets_at)}`);
|
|
354
|
+
lines.push("");
|
|
355
|
+
}
|
|
356
|
+
if (data.seven_day) {
|
|
357
|
+
const pct = Math.round(data.seven_day.utilization);
|
|
358
|
+
lines.push(" Current week (all models)");
|
|
359
|
+
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
360
|
+
lines.push(` ${formatReset(data.seven_day.resets_at)}`);
|
|
361
|
+
lines.push("");
|
|
362
|
+
}
|
|
363
|
+
if (data.seven_day_sonnet) {
|
|
364
|
+
const pct = Math.round(data.seven_day_sonnet.utilization);
|
|
365
|
+
lines.push(" Current week (Sonnet only)");
|
|
366
|
+
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
367
|
+
if (data.seven_day_sonnet.resets_at) {
|
|
368
|
+
lines.push(` ${formatReset(data.seven_day_sonnet.resets_at)}`);
|
|
369
|
+
}
|
|
370
|
+
lines.push("");
|
|
371
|
+
}
|
|
372
|
+
if (data.seven_day_opus) {
|
|
373
|
+
const pct = Math.round(data.seven_day_opus.utilization);
|
|
374
|
+
lines.push(" Current week (Opus only)");
|
|
375
|
+
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
376
|
+
if (data.seven_day_opus.resets_at) {
|
|
377
|
+
lines.push(` ${formatReset(data.seven_day_opus.resets_at)}`);
|
|
378
|
+
}
|
|
379
|
+
lines.push("");
|
|
380
|
+
}
|
|
381
|
+
if (data.extra_usage) {
|
|
382
|
+
if (data.extra_usage.is_enabled) {
|
|
383
|
+
const pct = Math.round(data.extra_usage.utilization ?? 0);
|
|
384
|
+
lines.push(" Extra usage");
|
|
385
|
+
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
386
|
+
if (data.extra_usage.monthly_limit != null) {
|
|
387
|
+
lines.push(` $${data.extra_usage.used_credits?.toFixed(2) ?? "0.00"} / $${data.extra_usage.monthly_limit.toFixed(2)}`);
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
lines.push(" Extra usage: disabled");
|
|
391
|
+
}
|
|
392
|
+
lines.push("");
|
|
393
|
+
}
|
|
394
|
+
return lines.join(`
|
|
395
|
+
`);
|
|
396
|
+
}
|
|
397
|
+
|
|
268
398
|
// src/tools.ts
|
|
269
399
|
function cleanSchema(schema) {
|
|
270
400
|
const cleaned = {};
|
|
@@ -352,8 +482,13 @@ function convertStream(anthropicStream, modelId) {
|
|
|
352
482
|
usage: {
|
|
353
483
|
inputTokens: inputTokens ?? 0,
|
|
354
484
|
outputTokens: outputTokens ?? 0,
|
|
355
|
-
totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0),
|
|
485
|
+
totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0) + (cachedInputTokens ?? 0) + (cacheCreationTokens ?? 0),
|
|
356
486
|
cachedInputTokens
|
|
487
|
+
},
|
|
488
|
+
providerMetadata: {
|
|
489
|
+
anthropic: {
|
|
490
|
+
cacheCreationInputTokens: cacheCreationTokens ?? 0
|
|
491
|
+
}
|
|
357
492
|
}
|
|
358
493
|
});
|
|
359
494
|
}
|
|
@@ -521,6 +656,74 @@ function buildMetadata() {
|
|
|
521
656
|
user_id: JSON.stringify({ device_id: DEVICE_ID })
|
|
522
657
|
};
|
|
523
658
|
}
|
|
659
|
+
function isUsageCommand(prompt) {
|
|
660
|
+
for (let i = prompt.length - 1;i >= 0; i--) {
|
|
661
|
+
const msg = prompt[i];
|
|
662
|
+
if (msg.role !== "user")
|
|
663
|
+
continue;
|
|
664
|
+
const text = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("").trim();
|
|
665
|
+
return /^\/usage(\s|$)/i.test(text);
|
|
666
|
+
}
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
function formatReset2(epoch) {
|
|
670
|
+
if (!epoch)
|
|
671
|
+
return "";
|
|
672
|
+
const reset = new Date(epoch * 1000);
|
|
673
|
+
const now = new Date;
|
|
674
|
+
const diffMs = reset.getTime() - now.getTime();
|
|
675
|
+
if (diffMs <= 0)
|
|
676
|
+
return "Reset now";
|
|
677
|
+
const timeStr = reset.toLocaleTimeString("en-US", {
|
|
678
|
+
hour: "numeric",
|
|
679
|
+
minute: "2-digit",
|
|
680
|
+
timeZoneName: "short"
|
|
681
|
+
});
|
|
682
|
+
const dateStr = reset.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
683
|
+
return diffMs < 86400000 ? `Resets ${timeStr}` : `Resets ${dateStr}, ${timeStr}`;
|
|
684
|
+
}
|
|
685
|
+
function formatCachedUsage(usage) {
|
|
686
|
+
const bar = (pct, width = 50) => "█".repeat(Math.round(pct / 100 * width)) + " ".repeat(width - Math.round(pct / 100 * width));
|
|
687
|
+
const lines = [];
|
|
688
|
+
const h5 = Math.round((usage.fiveHourUtil ?? 0) * 100);
|
|
689
|
+
lines.push(" Current session");
|
|
690
|
+
lines.push(` ${bar(h5)} ${h5}% used`);
|
|
691
|
+
if (usage.fiveHourReset)
|
|
692
|
+
lines.push(` ${formatReset2(usage.fiveHourReset)}`);
|
|
693
|
+
lines.push("");
|
|
694
|
+
const d7 = Math.round((usage.sevenDayUtil ?? 0) * 100);
|
|
695
|
+
lines.push(" Current week");
|
|
696
|
+
lines.push(` ${bar(d7)} ${d7}% used`);
|
|
697
|
+
if (usage.sevenDayReset)
|
|
698
|
+
lines.push(` ${formatReset2(usage.sevenDayReset)}`);
|
|
699
|
+
lines.push("");
|
|
700
|
+
if (usage.overageStatus === "rejected") {
|
|
701
|
+
lines.push(" Extra usage: disabled");
|
|
702
|
+
} else if (usage.overageStatus) {
|
|
703
|
+
lines.push(` Extra usage: ${usage.overageStatus}`);
|
|
704
|
+
}
|
|
705
|
+
return lines.join(`
|
|
706
|
+
`);
|
|
707
|
+
}
|
|
708
|
+
async function handleUsageCommand() {
|
|
709
|
+
if (cachedUsage.timestamp) {
|
|
710
|
+
throw new Error(`Claude Subscription Usage
|
|
711
|
+
` + "─".repeat(52) + `
|
|
712
|
+
` + formatCachedUsage(cachedUsage));
|
|
713
|
+
}
|
|
714
|
+
try {
|
|
715
|
+
const data = await fetchUsage();
|
|
716
|
+
if (data) {
|
|
717
|
+
throw new Error(`Claude Subscription Usage
|
|
718
|
+
` + "─".repeat(52) + `
|
|
719
|
+
` + formatUsage(data));
|
|
720
|
+
}
|
|
721
|
+
} catch (e) {
|
|
722
|
+
if (e.message?.startsWith("Claude Subscription Usage"))
|
|
723
|
+
throw e;
|
|
724
|
+
}
|
|
725
|
+
throw new Error("No usage data available. Send a message first, then try /usage again.");
|
|
726
|
+
}
|
|
524
727
|
|
|
525
728
|
class AnthropicSDKModel {
|
|
526
729
|
client;
|
|
@@ -652,6 +855,8 @@ class AnthropicSDKModel {
|
|
|
652
855
|
return { params, warnings };
|
|
653
856
|
}
|
|
654
857
|
async doGenerate(options) {
|
|
858
|
+
if (isUsageCommand(options.prompt))
|
|
859
|
+
await handleUsageCommand();
|
|
655
860
|
const { params, warnings } = this.buildParams(options);
|
|
656
861
|
if (!options.maxOutputTokens && params.max_tokens > 4096) {
|
|
657
862
|
params.max_tokens = 4096;
|
|
@@ -692,14 +897,21 @@ class AnthropicSDKModel {
|
|
|
692
897
|
}
|
|
693
898
|
}
|
|
694
899
|
}
|
|
900
|
+
const cacheReadTokens = response.usage.cache_read_input_tokens ?? 0;
|
|
901
|
+
const cacheCreateTokens = response.usage.cache_creation_input_tokens ?? 0;
|
|
695
902
|
return {
|
|
696
903
|
content,
|
|
697
904
|
finishReason: mapFinishReason2(response.stop_reason),
|
|
698
905
|
usage: {
|
|
699
906
|
inputTokens: response.usage.input_tokens,
|
|
700
907
|
outputTokens: response.usage.output_tokens,
|
|
701
|
-
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
702
|
-
cachedInputTokens:
|
|
908
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens + cacheReadTokens + cacheCreateTokens,
|
|
909
|
+
cachedInputTokens: cacheReadTokens
|
|
910
|
+
},
|
|
911
|
+
providerMetadata: {
|
|
912
|
+
anthropic: {
|
|
913
|
+
cacheCreationInputTokens: cacheCreateTokens
|
|
914
|
+
}
|
|
703
915
|
},
|
|
704
916
|
response: {
|
|
705
917
|
id: response.id,
|
|
@@ -710,6 +922,8 @@ class AnthropicSDKModel {
|
|
|
710
922
|
};
|
|
711
923
|
}
|
|
712
924
|
async doStream(options) {
|
|
925
|
+
if (isUsageCommand(options.prompt))
|
|
926
|
+
await handleUsageCommand();
|
|
713
927
|
const { params, warnings } = this.buildParams(options);
|
|
714
928
|
let anthropicStream;
|
|
715
929
|
try {
|
|
@@ -748,32 +962,6 @@ class AnthropicSDKModel {
|
|
|
748
962
|
}
|
|
749
963
|
}
|
|
750
964
|
|
|
751
|
-
// src/credentials.ts
|
|
752
|
-
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
753
|
-
import { join as join2 } from "node:path";
|
|
754
|
-
import { homedir as homedir2 } from "node:os";
|
|
755
|
-
function getCredentialsPath() {
|
|
756
|
-
return join2(homedir2(), ".claude", ".credentials.json");
|
|
757
|
-
}
|
|
758
|
-
function readClaudeCredentials(path) {
|
|
759
|
-
const credPath = path ?? getCredentialsPath();
|
|
760
|
-
if (!existsSync2(credPath))
|
|
761
|
-
return null;
|
|
762
|
-
try {
|
|
763
|
-
const raw = readFileSync2(credPath, "utf-8");
|
|
764
|
-
const parsed = JSON.parse(raw);
|
|
765
|
-
if (!parsed.claudeAiOauth?.accessToken)
|
|
766
|
-
return null;
|
|
767
|
-
return parsed.claudeAiOauth;
|
|
768
|
-
} catch {
|
|
769
|
-
return null;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
function isExpired(creds) {
|
|
773
|
-
const buffer = 5 * 60 * 1000;
|
|
774
|
-
return Date.now() > creds.expiresAt - buffer;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
965
|
// src/index.ts
|
|
778
966
|
var CLAUDE_CODE_VERSION = "2.1.81";
|
|
779
967
|
var OAUTH_BETAS = [
|
|
@@ -789,6 +977,7 @@ var API_KEY_BETAS = [
|
|
|
789
977
|
"interleaved-thinking-2025-05-14",
|
|
790
978
|
"fine-grained-tool-streaming-2025-05-14"
|
|
791
979
|
];
|
|
980
|
+
var cachedUsage = {};
|
|
792
981
|
function resolveAuth(options) {
|
|
793
982
|
if (options.apiKey) {
|
|
794
983
|
return { apiKey: options.apiKey, isOAuth: false };
|
|
@@ -838,6 +1027,15 @@ function createAnthropicSDK(options = {}) {
|
|
|
838
1027
|
}
|
|
839
1028
|
}
|
|
840
1029
|
const resp = await baseFetch(url, init);
|
|
1030
|
+
const h5 = resp.headers.get("anthropic-ratelimit-unified-5h-utilization");
|
|
1031
|
+
if (h5 != null) {
|
|
1032
|
+
cachedUsage.fiveHourUtil = parseFloat(h5);
|
|
1033
|
+
cachedUsage.sevenDayUtil = parseFloat(resp.headers.get("anthropic-ratelimit-unified-7d-utilization") ?? "0");
|
|
1034
|
+
cachedUsage.fiveHourReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-5h-reset") ?? "0");
|
|
1035
|
+
cachedUsage.sevenDayReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-7d-reset") ?? "0");
|
|
1036
|
+
cachedUsage.overageStatus = resp.headers.get("anthropic-ratelimit-unified-overage-status") ?? undefined;
|
|
1037
|
+
cachedUsage.timestamp = Date.now();
|
|
1038
|
+
}
|
|
841
1039
|
if (resp.status === 429) {
|
|
842
1040
|
const unifiedStatus = resp.headers.get("anthropic-ratelimit-unified-status") ?? "";
|
|
843
1041
|
const retryAfter = parseInt(resp.headers.get("retry-after") ?? "0");
|
|
@@ -877,6 +1075,7 @@ export {
|
|
|
877
1075
|
getCredentialsPath,
|
|
878
1076
|
src_default as default,
|
|
879
1077
|
createAnthropicSDK,
|
|
1078
|
+
cachedUsage,
|
|
880
1079
|
OAUTH_BETAS,
|
|
881
1080
|
CONTEXT_1M_BETA,
|
|
882
1081
|
AnthropicSDKModel,
|
package/build/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAK3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAK3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAU9C,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AAC1E,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;AAmMtE,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;IAsLb,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+E1E,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;CA4C7E"}
|
package/build/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,kBAAkB,GAAG,SAAS,CAAC,kBAAkB,CAAA;AAiCtD,wBAAgB,aAAa,CAC3B,eAAe,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAClD,OAAO,EAAE,MAAM,GACd,cAAc,CAAC,yBAAyB,CAAC,
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,kBAAkB,GAAG,SAAS,CAAC,kBAAkB,CAAA;AAiCtD,wBAAgB,aAAa,CAC3B,eAAe,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAClD,OAAO,EAAE,MAAM,GACd,cAAc,CAAC,yBAAyB,CAAC,CAiD3C"}
|
package/build/usage-cli.js
CHANGED
|
@@ -43,8 +43,11 @@ async function fetchUsage(credentialsPath) {
|
|
|
43
43
|
"x-app": "cli"
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
|
-
if (!resp.ok)
|
|
47
|
-
|
|
46
|
+
if (!resp.ok) {
|
|
47
|
+
const body = await resp.text().catch(() => "");
|
|
48
|
+
const msg = body.includes("message") ? JSON.parse(body)?.error?.message ?? body : `HTTP ${resp.status}`;
|
|
49
|
+
throw new Error(`Usage API error: ${msg}`);
|
|
50
|
+
}
|
|
48
51
|
return resp.json();
|
|
49
52
|
}
|
|
50
53
|
function progressBar(utilization, width = 50) {
|
|
@@ -128,7 +131,13 @@ function formatUsage(data) {
|
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
// src/usage-cli.ts
|
|
131
|
-
var data
|
|
134
|
+
var data;
|
|
135
|
+
try {
|
|
136
|
+
data = await fetchUsage();
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error(e.message ?? "Could not fetch usage.");
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
132
141
|
if (!data) {
|
|
133
142
|
console.error("Could not fetch usage. Make sure you're logged into Claude Code (~/.claude/.credentials.json).");
|
|
134
143
|
process.exit(1);
|
package/build/usage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAA;IACxC,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,WAAW,EAAE;QACX,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,GAAG,IAAI,CAAA;CACT;AAID;;;GAGG;AACH,wBAAsB,UAAU,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAA;IACxC,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,WAAW,EAAE;QACX,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,GAAG,IAAI,CAAA;CACT;AAID;;;GAGG;AACH,wBAAsB,UAAU,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAuBpF;AA0CD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAsDnD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xtruder/opencode-claude-max-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "OpenCode plugin for Claude Max/Pro subscription via @anthropic-ai/sdk - uses Claude Code OAuth credentials",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
"./agent": {
|
|
14
14
|
"import": "./build/agent-index.js",
|
|
15
15
|
"types": "./build/agent-index.d.ts"
|
|
16
|
-
},
|
|
17
|
-
"./plugin": {
|
|
18
|
-
"import": "./build/plugin.js",
|
|
19
|
-
"types": "./build/plugin.d.ts"
|
|
20
16
|
}
|
|
21
17
|
},
|
|
22
18
|
"bin": {
|
|
@@ -26,7 +22,7 @@
|
|
|
26
22
|
"build"
|
|
27
23
|
],
|
|
28
24
|
"scripts": {
|
|
29
|
-
"build": "bun build ./src/index.ts ./src/agent-index.ts ./src/usage-cli.ts
|
|
25
|
+
"build": "bun build ./src/index.ts ./src/agent-index.ts ./src/usage-cli.ts --outdir ./build --target node --format esm --packages external && bun x tsc --emitDeclarationOnly --declaration --outDir build",
|
|
30
26
|
"test": "bun run src/test.ts"
|
|
31
27
|
},
|
|
32
28
|
"dependencies": {
|
|
@@ -39,7 +35,6 @@
|
|
|
39
35
|
"devDependencies": {
|
|
40
36
|
"@ai-sdk/provider": "2.0.1",
|
|
41
37
|
"@ai-sdk/provider-utils": "3.0.2",
|
|
42
|
-
"@opencode-ai/plugin": "^1.3.0",
|
|
43
38
|
"ai": "5.0.124",
|
|
44
39
|
"bun-types": "^1.3.11",
|
|
45
40
|
"typescript": "^5.7.0"
|
package/build/plugin.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode Plugin entry point for @xtruder/opencode-claude-max-plugin.
|
|
3
|
-
*
|
|
4
|
-
* Registers a /usage command that fetches Claude subscription usage directly
|
|
5
|
-
* from the Anthropic OAuth API — no bash, no markdown file required.
|
|
6
|
-
*
|
|
7
|
-
* Register in opencode.json:
|
|
8
|
-
* "plugin": ["@xtruder/opencode-claude-max-plugin/plugin"]
|
|
9
|
-
*/
|
|
10
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
11
|
-
declare const UsagePlugin: Plugin;
|
|
12
|
-
export default UsagePlugin;
|
|
13
|
-
export { UsagePlugin };
|
|
14
|
-
//# sourceMappingURL=plugin.d.ts.map
|
package/build/plugin.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA+CjD,QAAA,MAAM,WAAW,EAAE,MAgClB,CAAA;AAED,eAAe,WAAW,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
package/build/plugin.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
// src/credentials.ts
|
|
2
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
function getCredentialsPath() {
|
|
6
|
-
return join(homedir(), ".claude", ".credentials.json");
|
|
7
|
-
}
|
|
8
|
-
function readClaudeCredentials(path) {
|
|
9
|
-
const credPath = path ?? getCredentialsPath();
|
|
10
|
-
if (!existsSync(credPath))
|
|
11
|
-
return null;
|
|
12
|
-
try {
|
|
13
|
-
const raw = readFileSync(credPath, "utf-8");
|
|
14
|
-
const parsed = JSON.parse(raw);
|
|
15
|
-
if (!parsed.claudeAiOauth?.accessToken)
|
|
16
|
-
return null;
|
|
17
|
-
return parsed.claudeAiOauth;
|
|
18
|
-
} catch {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function isExpired(creds) {
|
|
23
|
-
const buffer = 5 * 60 * 1000;
|
|
24
|
-
return Date.now() > creds.expiresAt - buffer;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// src/usage.ts
|
|
28
|
-
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
29
|
-
async function fetchUsage(credentialsPath) {
|
|
30
|
-
const creds = readClaudeCredentials(credentialsPath);
|
|
31
|
-
if (!creds)
|
|
32
|
-
return null;
|
|
33
|
-
if (isExpired(creds))
|
|
34
|
-
return null;
|
|
35
|
-
const resp = await fetch(USAGE_URL, {
|
|
36
|
-
headers: {
|
|
37
|
-
authorization: `Bearer ${creds.accessToken}`,
|
|
38
|
-
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
39
|
-
"content-type": "application/json",
|
|
40
|
-
"user-agent": "claude-cli/2.1.81 (external, sdk-cli)",
|
|
41
|
-
"x-app": "cli"
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
if (!resp.ok)
|
|
45
|
-
return null;
|
|
46
|
-
return resp.json();
|
|
47
|
-
}
|
|
48
|
-
function progressBar(utilization, width = 50) {
|
|
49
|
-
const filled = Math.round(utilization / 100 * width);
|
|
50
|
-
const bar = "█".repeat(filled) + " ".repeat(width - filled);
|
|
51
|
-
return bar;
|
|
52
|
-
}
|
|
53
|
-
function formatReset(resetsAt) {
|
|
54
|
-
if (!resetsAt)
|
|
55
|
-
return "";
|
|
56
|
-
const reset = new Date(resetsAt);
|
|
57
|
-
const now = new Date;
|
|
58
|
-
const diffMs = reset.getTime() - now.getTime();
|
|
59
|
-
if (diffMs <= 0)
|
|
60
|
-
return "Reset now";
|
|
61
|
-
const diffMin = Math.floor(diffMs / 60000);
|
|
62
|
-
const diffHour = Math.floor(diffMin / 60);
|
|
63
|
-
const timeStr = reset.toLocaleTimeString("en-US", {
|
|
64
|
-
hour: "numeric",
|
|
65
|
-
minute: "2-digit",
|
|
66
|
-
timeZoneName: "short"
|
|
67
|
-
});
|
|
68
|
-
const dateStr = reset.toLocaleDateString("en-US", {
|
|
69
|
-
month: "short",
|
|
70
|
-
day: "numeric"
|
|
71
|
-
});
|
|
72
|
-
if (diffHour < 24) {
|
|
73
|
-
return `Resets ${timeStr}`;
|
|
74
|
-
}
|
|
75
|
-
return `Resets ${dateStr}, ${timeStr}`;
|
|
76
|
-
}
|
|
77
|
-
function formatUsage(data) {
|
|
78
|
-
const lines = [];
|
|
79
|
-
if (data.five_hour) {
|
|
80
|
-
const pct = Math.round(data.five_hour.utilization);
|
|
81
|
-
lines.push(" Current session");
|
|
82
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
83
|
-
lines.push(` ${formatReset(data.five_hour.resets_at)}`);
|
|
84
|
-
lines.push("");
|
|
85
|
-
}
|
|
86
|
-
if (data.seven_day) {
|
|
87
|
-
const pct = Math.round(data.seven_day.utilization);
|
|
88
|
-
lines.push(" Current week (all models)");
|
|
89
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
90
|
-
lines.push(` ${formatReset(data.seven_day.resets_at)}`);
|
|
91
|
-
lines.push("");
|
|
92
|
-
}
|
|
93
|
-
if (data.seven_day_sonnet) {
|
|
94
|
-
const pct = Math.round(data.seven_day_sonnet.utilization);
|
|
95
|
-
lines.push(" Current week (Sonnet only)");
|
|
96
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
97
|
-
if (data.seven_day_sonnet.resets_at) {
|
|
98
|
-
lines.push(` ${formatReset(data.seven_day_sonnet.resets_at)}`);
|
|
99
|
-
}
|
|
100
|
-
lines.push("");
|
|
101
|
-
}
|
|
102
|
-
if (data.seven_day_opus) {
|
|
103
|
-
const pct = Math.round(data.seven_day_opus.utilization);
|
|
104
|
-
lines.push(" Current week (Opus only)");
|
|
105
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
106
|
-
if (data.seven_day_opus.resets_at) {
|
|
107
|
-
lines.push(` ${formatReset(data.seven_day_opus.resets_at)}`);
|
|
108
|
-
}
|
|
109
|
-
lines.push("");
|
|
110
|
-
}
|
|
111
|
-
if (data.extra_usage) {
|
|
112
|
-
if (data.extra_usage.is_enabled) {
|
|
113
|
-
const pct = Math.round(data.extra_usage.utilization ?? 0);
|
|
114
|
-
lines.push(" Extra usage");
|
|
115
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
116
|
-
if (data.extra_usage.monthly_limit != null) {
|
|
117
|
-
lines.push(` $${data.extra_usage.used_credits?.toFixed(2) ?? "0.00"} / $${data.extra_usage.monthly_limit.toFixed(2)}`);
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
lines.push(" Extra usage: disabled");
|
|
121
|
-
}
|
|
122
|
-
lines.push("");
|
|
123
|
-
}
|
|
124
|
-
return lines.join(`
|
|
125
|
-
`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// src/plugin.ts
|
|
129
|
-
var COMMAND_NAME = "usage";
|
|
130
|
-
async function buildUsageText(credentialsPath) {
|
|
131
|
-
const data = await fetchUsage(credentialsPath);
|
|
132
|
-
if (!data) {
|
|
133
|
-
return `Display this message verbatim to the user:
|
|
134
|
-
|
|
135
|
-
` + " Could not fetch Claude usage. " + "Make sure you are logged into Claude Code (~/.claude/.credentials.json).";
|
|
136
|
-
}
|
|
137
|
-
return "Display the following Claude subscription usage verbatim to the user. " + `Do not add any commentary, formatting changes, or explanation:
|
|
138
|
-
|
|
139
|
-
` + ` Claude Subscription Usage
|
|
140
|
-
` + " " + "─".repeat(52) + `
|
|
141
|
-
` + formatUsage(data);
|
|
142
|
-
}
|
|
143
|
-
function replaceTextInParts(parts, text) {
|
|
144
|
-
const idx = parts.findIndex((p) => p.type === "text");
|
|
145
|
-
if (idx >= 0) {
|
|
146
|
-
parts[idx] = { ...parts[idx], text };
|
|
147
|
-
} else if (parts.length > 0) {
|
|
148
|
-
parts[0] = { ...parts[0], text };
|
|
149
|
-
} else {
|
|
150
|
-
parts.push({ type: "text", text });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
var UsagePlugin = async (_ctx) => {
|
|
154
|
-
return {
|
|
155
|
-
"command.execute.before": async (input, output) => {
|
|
156
|
-
if (input.command.toLowerCase() !== COMMAND_NAME)
|
|
157
|
-
return;
|
|
158
|
-
const text = await buildUsageText();
|
|
159
|
-
replaceTextInParts(output.parts, text);
|
|
160
|
-
},
|
|
161
|
-
"chat.message": async (_input, output) => {
|
|
162
|
-
const parts = output.parts;
|
|
163
|
-
const idx = parts.findIndex((p) => p.type === "text" && typeof p.text === "string" && p.text.trim().toLowerCase().startsWith(`/${COMMAND_NAME}`));
|
|
164
|
-
if (idx < 0)
|
|
165
|
-
return;
|
|
166
|
-
const text = await buildUsageText();
|
|
167
|
-
parts[idx] = { ...parts[idx], text };
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
var plugin_default = UsagePlugin;
|
|
172
|
-
export {
|
|
173
|
-
plugin_default as default,
|
|
174
|
-
UsagePlugin
|
|
175
|
-
};
|