@xtruder/opencode-claude-max-plugin 0.1.17 → 0.2.2
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 +56 -18
- package/build/index.d.ts.map +1 -1
- package/build/index.js +256 -336
- package/build/model.d.ts.map +1 -1
- package/build/prompt.d.ts.map +1 -1
- package/build/server.d.ts +14 -0
- package/build/server.d.ts.map +1 -0
- package/build/server.js +1212 -0
- package/build/stream.d.ts.map +1 -1
- package/build/usage.d.ts +72 -5
- package/build/usage.d.ts.map +1 -1
- package/package.json +40 -4
- package/src/credentials.ts +156 -0
- package/src/tui.tsx +396 -0
- package/src/usage.ts +331 -0
- package/build/usage-cache.d.ts +0 -15
- package/build/usage-cache.d.ts.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,10 +1,213 @@
|
|
|
1
|
+
// src/usage.ts
|
|
2
|
+
import {
|
|
3
|
+
closeSync,
|
|
4
|
+
constants as fsConstants,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
openSync,
|
|
7
|
+
readFileSync as readFileSync2,
|
|
8
|
+
renameSync,
|
|
9
|
+
unlinkSync,
|
|
10
|
+
writeFileSync
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { homedir as homedir2 } from "node:os";
|
|
13
|
+
import { join as join2 } from "node:path";
|
|
14
|
+
|
|
15
|
+
// src/credentials.ts
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { execSync } from "node:child_process";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
var CREDENTIAL_CACHE_TTL_MS = 30000;
|
|
21
|
+
var EXPIRY_BUFFER_MS = 60000;
|
|
22
|
+
var cachedCreds = null;
|
|
23
|
+
var cachedAt = 0;
|
|
24
|
+
var cachedPath;
|
|
25
|
+
function getCredentialsPath() {
|
|
26
|
+
return join(homedir(), ".claude", ".credentials.json");
|
|
27
|
+
}
|
|
28
|
+
function readClaudeCredentials(path) {
|
|
29
|
+
const credPath = path ?? getCredentialsPath();
|
|
30
|
+
if (!existsSync(credPath))
|
|
31
|
+
return null;
|
|
32
|
+
try {
|
|
33
|
+
const raw = readFileSync(credPath, "utf-8");
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
if (!parsed.claudeAiOauth?.accessToken)
|
|
36
|
+
return null;
|
|
37
|
+
return parsed.claudeAiOauth;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function isExpired(creds) {
|
|
43
|
+
return Date.now() > creds.expiresAt - EXPIRY_BUFFER_MS;
|
|
44
|
+
}
|
|
45
|
+
function refreshViaCli() {
|
|
46
|
+
const maxAttempts = 2;
|
|
47
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
48
|
+
try {
|
|
49
|
+
execSync("claude -p . --model haiku", {
|
|
50
|
+
timeout: 60000,
|
|
51
|
+
encoding: "utf-8",
|
|
52
|
+
env: { ...process.env, TERM: "dumb" },
|
|
53
|
+
stdio: "ignore"
|
|
54
|
+
});
|
|
55
|
+
return true;
|
|
56
|
+
} catch {}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
function refreshIfNeeded(path) {
|
|
61
|
+
const creds = readClaudeCredentials(path);
|
|
62
|
+
if (!creds)
|
|
63
|
+
return null;
|
|
64
|
+
if (!isExpired(creds))
|
|
65
|
+
return creds;
|
|
66
|
+
console.warn("[anthropic-sdk-provider] OAuth token near expiry, refreshing via Claude CLI...");
|
|
67
|
+
const refreshed = refreshViaCli();
|
|
68
|
+
if (!refreshed) {
|
|
69
|
+
console.warn("[anthropic-sdk-provider] CLI refresh failed. Token may be expired.");
|
|
70
|
+
return creds;
|
|
71
|
+
}
|
|
72
|
+
const fresh = readClaudeCredentials(path);
|
|
73
|
+
if (fresh && !isExpired(fresh)) {
|
|
74
|
+
console.warn("[anthropic-sdk-provider] Token refreshed successfully.");
|
|
75
|
+
return fresh;
|
|
76
|
+
}
|
|
77
|
+
return fresh ?? creds;
|
|
78
|
+
}
|
|
79
|
+
function getCachedCredentials(path) {
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
const resolvedPath = path ?? cachedPath;
|
|
82
|
+
if (cachedCreds && now - cachedAt < CREDENTIAL_CACHE_TTL_MS && !isExpired(cachedCreds)) {
|
|
83
|
+
return cachedCreds;
|
|
84
|
+
}
|
|
85
|
+
const fresh = refreshIfNeeded(resolvedPath);
|
|
86
|
+
if (fresh) {
|
|
87
|
+
cachedCreds = fresh;
|
|
88
|
+
cachedAt = now;
|
|
89
|
+
cachedPath = resolvedPath;
|
|
90
|
+
} else {
|
|
91
|
+
cachedCreds = null;
|
|
92
|
+
}
|
|
93
|
+
return cachedCreds;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/usage.ts
|
|
97
|
+
var cachedUsage = {};
|
|
98
|
+
var XDG_STATE = process.env.XDG_STATE_HOME || join2(homedir2(), ".local", "state");
|
|
99
|
+
var USAGE_CACHE_DIR = join2(XDG_STATE, "opencode");
|
|
100
|
+
var USAGE_CACHE_FILE = join2(USAGE_CACHE_DIR, "usage-cache.json");
|
|
101
|
+
function emptyCache() {
|
|
102
|
+
return { api: null, headers: null, updatedAt: 0, apiFetchedAt: 0, apiRateLimitUntil: 0 };
|
|
103
|
+
}
|
|
104
|
+
var USAGE_LOCK_FILE = USAGE_CACHE_FILE + ".lock";
|
|
105
|
+
var USAGE_TMP_FILE = USAGE_CACHE_FILE + ".tmp";
|
|
106
|
+
var LOCK_STALE_MS = 5000;
|
|
107
|
+
function acquireLock() {
|
|
108
|
+
try {
|
|
109
|
+
mkdirSync(USAGE_CACHE_DIR, { recursive: true });
|
|
110
|
+
const fd = openSync(USAGE_LOCK_FILE, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY);
|
|
111
|
+
writeFileSync(fd, `${process.pid}
|
|
112
|
+
${Date.now()}`);
|
|
113
|
+
closeSync(fd);
|
|
114
|
+
return true;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err?.code !== "EEXIST")
|
|
117
|
+
return false;
|
|
118
|
+
try {
|
|
119
|
+
const text = readFileSync2(USAGE_LOCK_FILE, "utf-8");
|
|
120
|
+
const ts = parseInt(text.split(`
|
|
121
|
+
`)[1] ?? "0");
|
|
122
|
+
if (Date.now() - ts > LOCK_STALE_MS) {
|
|
123
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
124
|
+
return acquireLock();
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
try {
|
|
128
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
129
|
+
} catch {}
|
|
130
|
+
return acquireLock();
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function releaseLock() {
|
|
136
|
+
try {
|
|
137
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
138
|
+
} catch {}
|
|
139
|
+
}
|
|
140
|
+
function readUsageCache() {
|
|
141
|
+
try {
|
|
142
|
+
const text = readFileSync2(USAGE_CACHE_FILE, "utf-8");
|
|
143
|
+
const data = JSON.parse(text);
|
|
144
|
+
if (data && typeof data.updatedAt === "number")
|
|
145
|
+
return data;
|
|
146
|
+
} catch {}
|
|
147
|
+
return emptyCache();
|
|
148
|
+
}
|
|
149
|
+
function writeUsageCache(update) {
|
|
150
|
+
if (!acquireLock())
|
|
151
|
+
return;
|
|
152
|
+
try {
|
|
153
|
+
const existing = readUsageCache();
|
|
154
|
+
const merged = {
|
|
155
|
+
...existing,
|
|
156
|
+
...update,
|
|
157
|
+
updatedAt: Date.now()
|
|
158
|
+
};
|
|
159
|
+
mkdirSync(USAGE_CACHE_DIR, { recursive: true });
|
|
160
|
+
writeFileSync(USAGE_TMP_FILE, JSON.stringify(merged));
|
|
161
|
+
renameSync(USAGE_TMP_FILE, USAGE_CACHE_FILE);
|
|
162
|
+
} catch {
|
|
163
|
+
try {
|
|
164
|
+
unlinkSync(USAGE_TMP_FILE);
|
|
165
|
+
} catch {}
|
|
166
|
+
} finally {
|
|
167
|
+
releaseLock();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function persistCachedUsage() {
|
|
171
|
+
if (cachedUsage.fiveHourUtil == null && cachedUsage.sevenDayUtil == null)
|
|
172
|
+
return;
|
|
173
|
+
writeUsageCache({ headers: { ...cachedUsage } });
|
|
174
|
+
}
|
|
175
|
+
var API_TTL_MS = 5 * 60000;
|
|
176
|
+
|
|
177
|
+
// src/cch.ts
|
|
178
|
+
import xxhashInit from "xxhash-wasm";
|
|
179
|
+
var CCH_SEED = 0x6e52736ac806831en;
|
|
180
|
+
var CCH_PLACEHOLDER = "cch=00000";
|
|
181
|
+
var CCH_MASK = 0xfffffn;
|
|
182
|
+
var hasherPromise = null;
|
|
183
|
+
function getHasher() {
|
|
184
|
+
if (!hasherPromise) {
|
|
185
|
+
hasherPromise = xxhashInit();
|
|
186
|
+
}
|
|
187
|
+
return hasherPromise;
|
|
188
|
+
}
|
|
189
|
+
async function computeCch(body) {
|
|
190
|
+
const hasher = await getHasher();
|
|
191
|
+
const hash = hasher.h64Raw(new TextEncoder().encode(body), CCH_SEED);
|
|
192
|
+
return (hash & CCH_MASK).toString(16).padStart(5, "0");
|
|
193
|
+
}
|
|
194
|
+
function replaceCchPlaceholder(body, cch) {
|
|
195
|
+
return body.replace(CCH_PLACEHOLDER, `cch=${cch}`);
|
|
196
|
+
}
|
|
197
|
+
function hasCchPlaceholder(body) {
|
|
198
|
+
return body.includes(CCH_PLACEHOLDER);
|
|
199
|
+
}
|
|
200
|
+
|
|
1
201
|
// src/index.ts
|
|
2
202
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3
203
|
|
|
204
|
+
// src/model.ts
|
|
205
|
+
import { APIError, RateLimitError } from "@anthropic-ai/sdk";
|
|
206
|
+
|
|
4
207
|
// src/tool-names.ts
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
208
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
209
|
+
import { homedir as homedir3 } from "node:os";
|
|
210
|
+
import { join as join3 } from "node:path";
|
|
8
211
|
var BUILTIN_OPENCODE_TO_CLAUDE = {
|
|
9
212
|
task: "Agent",
|
|
10
213
|
question: "AskUserQuestion",
|
|
@@ -36,15 +239,15 @@ var mcpServerNames = null;
|
|
|
36
239
|
function detectMcpServers() {
|
|
37
240
|
const servers = new Set;
|
|
38
241
|
const paths = [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
242
|
+
join3(process.cwd(), ".opencode", "opencode.json"),
|
|
243
|
+
join3(homedir3(), ".config", "opencode", "opencode.json"),
|
|
244
|
+
join3(homedir3(), ".config", "opencode", "opencode.jsonc")
|
|
42
245
|
];
|
|
43
246
|
for (const p of paths) {
|
|
44
247
|
try {
|
|
45
|
-
if (!
|
|
248
|
+
if (!existsSync2(p))
|
|
46
249
|
continue;
|
|
47
|
-
let raw =
|
|
250
|
+
let raw = readFileSync3(p, "utf-8");
|
|
48
251
|
raw = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
49
252
|
const config = JSON.parse(raw);
|
|
50
253
|
if (config.mcp && typeof config.mcp === "object") {
|
|
@@ -130,6 +333,50 @@ function rewriteToolNamesInText(text) {
|
|
|
130
333
|
return result;
|
|
131
334
|
}
|
|
132
335
|
|
|
336
|
+
// src/tools.ts
|
|
337
|
+
function cleanSchema(schema) {
|
|
338
|
+
const cleaned = {};
|
|
339
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
340
|
+
if (key === "custom")
|
|
341
|
+
continue;
|
|
342
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
343
|
+
cleaned[key] = cleanSchema(value);
|
|
344
|
+
} else {
|
|
345
|
+
cleaned[key] = value;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (cleaned.properties && !cleaned.type) {
|
|
349
|
+
cleaned.type = "object";
|
|
350
|
+
}
|
|
351
|
+
return cleaned;
|
|
352
|
+
}
|
|
353
|
+
function convertTools(tools) {
|
|
354
|
+
if (!tools || tools.length === 0)
|
|
355
|
+
return;
|
|
356
|
+
return tools.filter((t) => t.type === "function").map((tool) => ({
|
|
357
|
+
name: toClaudeToolName(tool.name),
|
|
358
|
+
description: tool.description ?? "",
|
|
359
|
+
input_schema: cleanSchema(tool.inputSchema)
|
|
360
|
+
}));
|
|
361
|
+
}
|
|
362
|
+
function convertToolChoice(toolChoice) {
|
|
363
|
+
if (!toolChoice)
|
|
364
|
+
return;
|
|
365
|
+
switch (toolChoice.type) {
|
|
366
|
+
case "auto":
|
|
367
|
+
return { type: "auto" };
|
|
368
|
+
case "required":
|
|
369
|
+
return { type: "any" };
|
|
370
|
+
case "none":
|
|
371
|
+
return;
|
|
372
|
+
case "tool":
|
|
373
|
+
return { type: "tool", name: toClaudeToolName(toolChoice.toolName) };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/model.ts
|
|
378
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
379
|
+
|
|
133
380
|
// src/prompt.ts
|
|
134
381
|
function convertPrompt(prompt) {
|
|
135
382
|
const systemParts = [];
|
|
@@ -272,235 +519,6 @@ function formatToolResultContent(output) {
|
|
|
272
519
|
}
|
|
273
520
|
}
|
|
274
521
|
|
|
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
|
-
// src/tools.ts
|
|
464
|
-
function cleanSchema(schema) {
|
|
465
|
-
const cleaned = {};
|
|
466
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
467
|
-
if (key === "custom")
|
|
468
|
-
continue;
|
|
469
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
470
|
-
cleaned[key] = cleanSchema(value);
|
|
471
|
-
} else {
|
|
472
|
-
cleaned[key] = value;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (cleaned.properties && !cleaned.type) {
|
|
476
|
-
cleaned.type = "object";
|
|
477
|
-
}
|
|
478
|
-
return cleaned;
|
|
479
|
-
}
|
|
480
|
-
function convertTools(tools) {
|
|
481
|
-
if (!tools || tools.length === 0)
|
|
482
|
-
return;
|
|
483
|
-
return tools.filter((t) => t.type === "function").map((tool) => ({
|
|
484
|
-
name: toClaudeToolName(tool.name),
|
|
485
|
-
description: tool.description ?? "",
|
|
486
|
-
input_schema: cleanSchema(tool.inputSchema)
|
|
487
|
-
}));
|
|
488
|
-
}
|
|
489
|
-
function convertToolChoice(toolChoice) {
|
|
490
|
-
if (!toolChoice)
|
|
491
|
-
return;
|
|
492
|
-
switch (toolChoice.type) {
|
|
493
|
-
case "auto":
|
|
494
|
-
return { type: "auto" };
|
|
495
|
-
case "required":
|
|
496
|
-
return { type: "any" };
|
|
497
|
-
case "none":
|
|
498
|
-
return;
|
|
499
|
-
case "tool":
|
|
500
|
-
return { type: "tool", name: toClaudeToolName(toolChoice.toolName) };
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
522
|
// src/stream.ts
|
|
505
523
|
var idCounter = 0;
|
|
506
524
|
function generateId() {
|
|
@@ -681,8 +699,6 @@ function processEvent(event, blockStates, modelId) {
|
|
|
681
699
|
}
|
|
682
700
|
|
|
683
701
|
// src/model.ts
|
|
684
|
-
import { randomBytes, createHash } from "node:crypto";
|
|
685
|
-
import { APIError, RateLimitError } from "@anthropic-ai/sdk";
|
|
686
702
|
function handleApiError(error) {
|
|
687
703
|
if (error instanceof RateLimitError || error instanceof APIError && error.status === 429) {
|
|
688
704
|
const h = error.headers;
|
|
@@ -732,74 +748,6 @@ function buildMetadata() {
|
|
|
732
748
|
user_id: JSON.stringify({ device_id: DEVICE_ID })
|
|
733
749
|
};
|
|
734
750
|
}
|
|
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
751
|
|
|
804
752
|
class AnthropicSDKModel {
|
|
805
753
|
client;
|
|
@@ -940,8 +888,6 @@ class AnthropicSDKModel {
|
|
|
940
888
|
return { params, warnings };
|
|
941
889
|
}
|
|
942
890
|
async doGenerate(options) {
|
|
943
|
-
if (isUsageCommand(options.prompt))
|
|
944
|
-
await handleUsageCommand();
|
|
945
891
|
const { params, warnings } = this.buildParams(options);
|
|
946
892
|
if (!options.maxOutputTokens && params.max_tokens > 4096) {
|
|
947
893
|
params.max_tokens = 4096;
|
|
@@ -1014,8 +960,6 @@ class AnthropicSDKModel {
|
|
|
1014
960
|
};
|
|
1015
961
|
}
|
|
1016
962
|
async doStream(options) {
|
|
1017
|
-
if (isUsageCommand(options.prompt))
|
|
1018
|
-
await handleUsageCommand();
|
|
1019
963
|
const { params, warnings } = this.buildParams(options);
|
|
1020
964
|
let anthropicStream;
|
|
1021
965
|
try {
|
|
@@ -1078,30 +1022,6 @@ class AnthropicSDKModel {
|
|
|
1078
1022
|
}
|
|
1079
1023
|
}
|
|
1080
1024
|
|
|
1081
|
-
// src/cch.ts
|
|
1082
|
-
import xxhashInit from "xxhash-wasm";
|
|
1083
|
-
var CCH_SEED = 0x6e52736ac806831en;
|
|
1084
|
-
var CCH_PLACEHOLDER = "cch=00000";
|
|
1085
|
-
var CCH_MASK = 0xfffffn;
|
|
1086
|
-
var hasherPromise = null;
|
|
1087
|
-
function getHasher() {
|
|
1088
|
-
if (!hasherPromise) {
|
|
1089
|
-
hasherPromise = xxhashInit();
|
|
1090
|
-
}
|
|
1091
|
-
return hasherPromise;
|
|
1092
|
-
}
|
|
1093
|
-
async function computeCch(body) {
|
|
1094
|
-
const hasher = await getHasher();
|
|
1095
|
-
const hash = hasher.h64Raw(new TextEncoder().encode(body), CCH_SEED);
|
|
1096
|
-
return (hash & CCH_MASK).toString(16).padStart(5, "0");
|
|
1097
|
-
}
|
|
1098
|
-
function replaceCchPlaceholder(body, cch) {
|
|
1099
|
-
return body.replace(CCH_PLACEHOLDER, `cch=${cch}`);
|
|
1100
|
-
}
|
|
1101
|
-
function hasCchPlaceholder(body) {
|
|
1102
|
-
return body.includes(CCH_PLACEHOLDER);
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
1025
|
// src/index.ts
|
|
1106
1026
|
var CLAUDE_CODE_VERSION = "2.1.81";
|
|
1107
1027
|
var OAUTH_BETAS = [
|
|
@@ -1230,7 +1150,7 @@ function createAnthropicSDK(options = {}) {
|
|
|
1230
1150
|
cachedUsage.fiveHourReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-5h-reset") ?? "0");
|
|
1231
1151
|
cachedUsage.sevenDayReset = parseInt(resp.headers.get("anthropic-ratelimit-unified-7d-reset") ?? "0");
|
|
1232
1152
|
cachedUsage.overageStatus = resp.headers.get("anthropic-ratelimit-unified-overage-status") ?? undefined;
|
|
1233
|
-
|
|
1153
|
+
persistCachedUsage();
|
|
1234
1154
|
}
|
|
1235
1155
|
if (resp.status === 429) {
|
|
1236
1156
|
const unifiedStatus = resp.headers.get("anthropic-ratelimit-unified-status") ?? "";
|
package/build/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAG1B,6BAA6B,EAE7B,2BAA2B,EAE5B,MAAM,kBAAkB,CAAA;AAIzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAI9C,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"}
|
package/build/prompt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,gBAAgB,GAAG,SAAS,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzE,KAAK,oBAAoB,GAAG,SAAS,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAA;AAEnE,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,oBAAoB,CAAA;IAC5B,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAiC5E"}
|
|
@@ -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"}
|