@xtruder/opencode-claude-max-plugin 0.2.2 → 0.2.4
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/index.d.ts.map +1 -1
- package/build/index.js +280 -239
- package/build/model.d.ts +10 -0
- package/build/model.d.ts.map +1 -1
- package/build/server.js +280 -239
- package/build/tui.d.ts +7 -0
- package/build/tui.d.ts.map +1 -0
- package/package.json +2 -1
- package/src/credentials.ts +1 -1
- package/src/tui.tsx +4 -5
package/build/server.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
3
|
+
|
|
4
|
+
// src/cch.ts
|
|
5
|
+
import xxhashInit from "xxhash-wasm";
|
|
6
|
+
var CCH_SEED = 0x6e52736ac806831en;
|
|
7
|
+
var CCH_PLACEHOLDER = "cch=00000";
|
|
8
|
+
var CCH_MASK = 0xfffffn;
|
|
9
|
+
var hasherPromise = null;
|
|
10
|
+
function getHasher() {
|
|
11
|
+
if (!hasherPromise) {
|
|
12
|
+
hasherPromise = xxhashInit();
|
|
13
|
+
}
|
|
14
|
+
return hasherPromise;
|
|
15
|
+
}
|
|
16
|
+
async function computeCch(body) {
|
|
17
|
+
const hasher = await getHasher();
|
|
18
|
+
const hash = hasher.h64Raw(new TextEncoder().encode(body), CCH_SEED);
|
|
19
|
+
return (hash & CCH_MASK).toString(16).padStart(5, "0");
|
|
20
|
+
}
|
|
21
|
+
function replaceCchPlaceholder(body, cch) {
|
|
22
|
+
return body.replace(CCH_PLACEHOLDER, `cch=${cch}`);
|
|
23
|
+
}
|
|
24
|
+
function hasCchPlaceholder(body) {
|
|
25
|
+
return body.includes(CCH_PLACEHOLDER);
|
|
26
|
+
}
|
|
14
27
|
|
|
15
28
|
// src/credentials.ts
|
|
16
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
17
29
|
import { execSync } from "node:child_process";
|
|
30
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
31
|
import { homedir } from "node:os";
|
|
19
32
|
import { join } from "node:path";
|
|
20
33
|
var CREDENTIAL_CACHE_TTL_MS = 30000;
|
|
@@ -93,121 +106,14 @@ function getCachedCredentials(path) {
|
|
|
93
106
|
return cachedCreds;
|
|
94
107
|
}
|
|
95
108
|
|
|
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
|
-
|
|
201
|
-
// src/index.ts
|
|
202
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
203
|
-
|
|
204
109
|
// src/model.ts
|
|
205
110
|
import { APIError, RateLimitError } from "@anthropic-ai/sdk";
|
|
111
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
206
112
|
|
|
207
113
|
// src/tool-names.ts
|
|
208
|
-
import { existsSync as existsSync2, readFileSync as
|
|
209
|
-
import { homedir as
|
|
210
|
-
import { join as
|
|
114
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
115
|
+
import { homedir as homedir2 } from "node:os";
|
|
116
|
+
import { join as join2 } from "node:path";
|
|
211
117
|
var BUILTIN_OPENCODE_TO_CLAUDE = {
|
|
212
118
|
task: "Agent",
|
|
213
119
|
question: "AskUserQuestion",
|
|
@@ -239,15 +145,15 @@ var mcpServerNames = null;
|
|
|
239
145
|
function detectMcpServers() {
|
|
240
146
|
const servers = new Set;
|
|
241
147
|
const paths = [
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
148
|
+
join2(process.cwd(), ".opencode", "opencode.json"),
|
|
149
|
+
join2(homedir2(), ".config", "opencode", "opencode.json"),
|
|
150
|
+
join2(homedir2(), ".config", "opencode", "opencode.jsonc")
|
|
245
151
|
];
|
|
246
152
|
for (const p of paths) {
|
|
247
153
|
try {
|
|
248
154
|
if (!existsSync2(p))
|
|
249
155
|
continue;
|
|
250
|
-
let raw =
|
|
156
|
+
let raw = readFileSync2(p, "utf-8");
|
|
251
157
|
raw = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
252
158
|
const config = JSON.parse(raw);
|
|
253
159
|
if (config.mcp && typeof config.mcp === "object") {
|
|
@@ -333,50 +239,6 @@ function rewriteToolNamesInText(text) {
|
|
|
333
239
|
return result;
|
|
334
240
|
}
|
|
335
241
|
|
|
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
|
-
|
|
380
242
|
// src/prompt.ts
|
|
381
243
|
function convertPrompt(prompt) {
|
|
382
244
|
const systemParts = [];
|
|
@@ -566,8 +428,8 @@ function convertStream(anthropicStream, modelId) {
|
|
|
566
428
|
finishReason,
|
|
567
429
|
usage: {
|
|
568
430
|
inputTokens: {
|
|
569
|
-
total: inputTokens ?? 0,
|
|
570
|
-
noCache:
|
|
431
|
+
total: (inputTokens ?? 0) + (cachedInputTokens ?? 0) + (cacheCreationTokens ?? 0),
|
|
432
|
+
noCache: inputTokens ?? 0,
|
|
571
433
|
cacheRead: cachedInputTokens ?? 0,
|
|
572
434
|
cacheWrite: cacheCreationTokens ?? 0
|
|
573
435
|
},
|
|
@@ -698,6 +560,47 @@ function processEvent(event, blockStates, modelId) {
|
|
|
698
560
|
return parts;
|
|
699
561
|
}
|
|
700
562
|
|
|
563
|
+
// src/tools.ts
|
|
564
|
+
function cleanSchema(schema) {
|
|
565
|
+
const cleaned = {};
|
|
566
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
567
|
+
if (key === "custom")
|
|
568
|
+
continue;
|
|
569
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
570
|
+
cleaned[key] = cleanSchema(value);
|
|
571
|
+
} else {
|
|
572
|
+
cleaned[key] = value;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (cleaned.properties && !cleaned.type) {
|
|
576
|
+
cleaned.type = "object";
|
|
577
|
+
}
|
|
578
|
+
return cleaned;
|
|
579
|
+
}
|
|
580
|
+
function convertTools(tools) {
|
|
581
|
+
if (!tools || tools.length === 0)
|
|
582
|
+
return;
|
|
583
|
+
return tools.filter((t) => t.type === "function").map((tool) => ({
|
|
584
|
+
name: toClaudeToolName(tool.name),
|
|
585
|
+
description: tool.description ?? "",
|
|
586
|
+
input_schema: cleanSchema(tool.inputSchema)
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
function convertToolChoice(toolChoice) {
|
|
590
|
+
if (!toolChoice)
|
|
591
|
+
return;
|
|
592
|
+
switch (toolChoice.type) {
|
|
593
|
+
case "auto":
|
|
594
|
+
return { type: "auto" };
|
|
595
|
+
case "required":
|
|
596
|
+
return { type: "any" };
|
|
597
|
+
case "none":
|
|
598
|
+
return;
|
|
599
|
+
case "tool":
|
|
600
|
+
return { type: "tool", name: toClaudeToolName(toolChoice.toolName) };
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
701
604
|
// src/model.ts
|
|
702
605
|
function handleApiError(error) {
|
|
703
606
|
if (error instanceof RateLimitError || error instanceof APIError && error.status === 429) {
|
|
@@ -748,6 +651,9 @@ function buildMetadata() {
|
|
|
748
651
|
user_id: JSON.stringify({ device_id: DEVICE_ID })
|
|
749
652
|
};
|
|
750
653
|
}
|
|
654
|
+
function supportsContextManagement(apiModelId) {
|
|
655
|
+
return !apiModelId.includes("claude-3-");
|
|
656
|
+
}
|
|
751
657
|
|
|
752
658
|
class AnthropicSDKModel {
|
|
753
659
|
client;
|
|
@@ -757,12 +663,16 @@ class AnthropicSDKModel {
|
|
|
757
663
|
provider;
|
|
758
664
|
modelId;
|
|
759
665
|
supportedUrls = {};
|
|
666
|
+
apiModelId;
|
|
667
|
+
is1MContext;
|
|
760
668
|
constructor(modelId, client, providerName, isOAuth = false) {
|
|
761
669
|
this.client = client;
|
|
762
670
|
this.providerName = providerName;
|
|
763
671
|
this.isOAuth = isOAuth;
|
|
764
672
|
this.modelId = modelId;
|
|
765
673
|
this.provider = providerName;
|
|
674
|
+
this.is1MContext = modelId.endsWith("-1m");
|
|
675
|
+
this.apiModelId = this.is1MContext ? modelId.replace(/-1m$/, "") : modelId;
|
|
766
676
|
}
|
|
767
677
|
buildParams(options) {
|
|
768
678
|
const { system, messages } = convertPrompt(options.prompt);
|
|
@@ -779,7 +689,7 @@ class AnthropicSDKModel {
|
|
|
779
689
|
const tools = options.toolChoice?.type === "none" ? undefined : convertTools(options.tools);
|
|
780
690
|
const toolChoice = options.toolChoice?.type === "none" ? undefined : convertToolChoice(options.toolChoice);
|
|
781
691
|
const params = {
|
|
782
|
-
model: this.
|
|
692
|
+
model: this.apiModelId,
|
|
783
693
|
max_tokens: options.maxOutputTokens ?? 64000,
|
|
784
694
|
messages
|
|
785
695
|
};
|
|
@@ -797,7 +707,7 @@ class AnthropicSDKModel {
|
|
|
797
707
|
params.system = [BILLING_SYSTEM_BLOCK, IDENTITY_SYSTEM_BLOCK];
|
|
798
708
|
}
|
|
799
709
|
params.metadata = buildMetadata();
|
|
800
|
-
const supportsEffort = !this.
|
|
710
|
+
const supportsEffort = !this.apiModelId.includes("haiku") && !this.apiModelId.includes("claude-3-");
|
|
801
711
|
if (supportsEffort) {
|
|
802
712
|
if (options.temperature == null) {
|
|
803
713
|
params.temperature = 1;
|
|
@@ -885,8 +795,27 @@ class AnthropicSDKModel {
|
|
|
885
795
|
if (anthropicOptions.metadata)
|
|
886
796
|
params.metadata = anthropicOptions.metadata;
|
|
887
797
|
}
|
|
798
|
+
if (this.isOAuth && supportsContextManagement(this.apiModelId)) {
|
|
799
|
+
const edits = [];
|
|
800
|
+
const hasThinking = params.thinking?.type === "enabled";
|
|
801
|
+
if (hasThinking) {
|
|
802
|
+
edits.push({
|
|
803
|
+
type: "clear_thinking_20251015",
|
|
804
|
+
keep: "all"
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
if (edits.length > 0) {
|
|
808
|
+
params.context_management = { edits };
|
|
809
|
+
}
|
|
810
|
+
}
|
|
888
811
|
return { params, warnings };
|
|
889
812
|
}
|
|
813
|
+
buildRequestOptions(signal) {
|
|
814
|
+
const opts = {};
|
|
815
|
+
if (signal)
|
|
816
|
+
opts.signal = signal;
|
|
817
|
+
return opts;
|
|
818
|
+
}
|
|
890
819
|
async doGenerate(options) {
|
|
891
820
|
const { params, warnings } = this.buildParams(options);
|
|
892
821
|
if (!options.maxOutputTokens && params.max_tokens > 4096) {
|
|
@@ -897,7 +826,7 @@ class AnthropicSDKModel {
|
|
|
897
826
|
response = await this.client.messages.create({
|
|
898
827
|
...params,
|
|
899
828
|
stream: false
|
|
900
|
-
},
|
|
829
|
+
}, this.buildRequestOptions(options.abortSignal));
|
|
901
830
|
} catch (error) {
|
|
902
831
|
handleApiError(error);
|
|
903
832
|
}
|
|
@@ -935,8 +864,8 @@ class AnthropicSDKModel {
|
|
|
935
864
|
finishReason: mapFinishReason2(response.stop_reason),
|
|
936
865
|
usage: {
|
|
937
866
|
inputTokens: {
|
|
938
|
-
total: response.usage.input_tokens,
|
|
939
|
-
noCache:
|
|
867
|
+
total: response.usage.input_tokens + cacheReadTokens + cacheCreateTokens,
|
|
868
|
+
noCache: response.usage.input_tokens,
|
|
940
869
|
cacheRead: cacheReadTokens,
|
|
941
870
|
cacheWrite: cacheCreateTokens
|
|
942
871
|
},
|
|
@@ -966,7 +895,7 @@ class AnthropicSDKModel {
|
|
|
966
895
|
anthropicStream = await this.client.messages.create({
|
|
967
896
|
...params,
|
|
968
897
|
stream: true
|
|
969
|
-
},
|
|
898
|
+
}, this.buildRequestOptions(options.abortSignal));
|
|
970
899
|
} catch (error) {
|
|
971
900
|
if (error instanceof APIError && error.status === 400 && error.message?.includes("must end with a user message")) {
|
|
972
901
|
return {
|
|
@@ -1022,6 +951,99 @@ class AnthropicSDKModel {
|
|
|
1022
951
|
}
|
|
1023
952
|
}
|
|
1024
953
|
|
|
954
|
+
// src/usage.ts
|
|
955
|
+
import {
|
|
956
|
+
closeSync,
|
|
957
|
+
constants as fsConstants,
|
|
958
|
+
mkdirSync,
|
|
959
|
+
openSync,
|
|
960
|
+
readFileSync as readFileSync3,
|
|
961
|
+
renameSync,
|
|
962
|
+
unlinkSync,
|
|
963
|
+
writeFileSync
|
|
964
|
+
} from "node:fs";
|
|
965
|
+
import { homedir as homedir3 } from "node:os";
|
|
966
|
+
import { join as join3 } from "node:path";
|
|
967
|
+
var cachedUsage = {};
|
|
968
|
+
var XDG_STATE = process.env.XDG_STATE_HOME || join3(homedir3(), ".local", "state");
|
|
969
|
+
var USAGE_CACHE_DIR = join3(XDG_STATE, "opencode");
|
|
970
|
+
var USAGE_CACHE_FILE = join3(USAGE_CACHE_DIR, "usage-cache.json");
|
|
971
|
+
function emptyCache() {
|
|
972
|
+
return { api: null, headers: null, updatedAt: 0, apiFetchedAt: 0, apiRateLimitUntil: 0 };
|
|
973
|
+
}
|
|
974
|
+
var USAGE_LOCK_FILE = USAGE_CACHE_FILE + ".lock";
|
|
975
|
+
var USAGE_TMP_FILE = USAGE_CACHE_FILE + ".tmp";
|
|
976
|
+
var LOCK_STALE_MS = 5000;
|
|
977
|
+
function acquireLock() {
|
|
978
|
+
try {
|
|
979
|
+
mkdirSync(USAGE_CACHE_DIR, { recursive: true });
|
|
980
|
+
const fd = openSync(USAGE_LOCK_FILE, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY);
|
|
981
|
+
writeFileSync(fd, `${process.pid}
|
|
982
|
+
${Date.now()}`);
|
|
983
|
+
closeSync(fd);
|
|
984
|
+
return true;
|
|
985
|
+
} catch (err) {
|
|
986
|
+
if (err?.code !== "EEXIST")
|
|
987
|
+
return false;
|
|
988
|
+
try {
|
|
989
|
+
const text = readFileSync3(USAGE_LOCK_FILE, "utf-8");
|
|
990
|
+
const ts = parseInt(text.split(`
|
|
991
|
+
`)[1] ?? "0");
|
|
992
|
+
if (Date.now() - ts > LOCK_STALE_MS) {
|
|
993
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
994
|
+
return acquireLock();
|
|
995
|
+
}
|
|
996
|
+
} catch {
|
|
997
|
+
try {
|
|
998
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
999
|
+
} catch {}
|
|
1000
|
+
return acquireLock();
|
|
1001
|
+
}
|
|
1002
|
+
return false;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function releaseLock() {
|
|
1006
|
+
try {
|
|
1007
|
+
unlinkSync(USAGE_LOCK_FILE);
|
|
1008
|
+
} catch {}
|
|
1009
|
+
}
|
|
1010
|
+
function readUsageCache() {
|
|
1011
|
+
try {
|
|
1012
|
+
const text = readFileSync3(USAGE_CACHE_FILE, "utf-8");
|
|
1013
|
+
const data = JSON.parse(text);
|
|
1014
|
+
if (data && typeof data.updatedAt === "number")
|
|
1015
|
+
return data;
|
|
1016
|
+
} catch {}
|
|
1017
|
+
return emptyCache();
|
|
1018
|
+
}
|
|
1019
|
+
function writeUsageCache(update) {
|
|
1020
|
+
if (!acquireLock())
|
|
1021
|
+
return;
|
|
1022
|
+
try {
|
|
1023
|
+
const existing = readUsageCache();
|
|
1024
|
+
const merged = {
|
|
1025
|
+
...existing,
|
|
1026
|
+
...update,
|
|
1027
|
+
updatedAt: Date.now()
|
|
1028
|
+
};
|
|
1029
|
+
mkdirSync(USAGE_CACHE_DIR, { recursive: true });
|
|
1030
|
+
writeFileSync(USAGE_TMP_FILE, JSON.stringify(merged));
|
|
1031
|
+
renameSync(USAGE_TMP_FILE, USAGE_CACHE_FILE);
|
|
1032
|
+
} catch {
|
|
1033
|
+
try {
|
|
1034
|
+
unlinkSync(USAGE_TMP_FILE);
|
|
1035
|
+
} catch {}
|
|
1036
|
+
} finally {
|
|
1037
|
+
releaseLock();
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function persistCachedUsage() {
|
|
1041
|
+
if (cachedUsage.fiveHourUtil == null && cachedUsage.sevenDayUtil == null)
|
|
1042
|
+
return;
|
|
1043
|
+
writeUsageCache({ headers: { ...cachedUsage } });
|
|
1044
|
+
}
|
|
1045
|
+
var API_TTL_MS = 5 * 60000;
|
|
1046
|
+
|
|
1025
1047
|
// src/index.ts
|
|
1026
1048
|
var CLAUDE_CODE_VERSION = "2.1.81";
|
|
1027
1049
|
var OAUTH_BETAS = [
|
|
@@ -1032,63 +1054,90 @@ var OAUTH_BETAS = [
|
|
|
1032
1054
|
"prompt-caching-scope-2026-01-05",
|
|
1033
1055
|
"effort-2025-11-24"
|
|
1034
1056
|
];
|
|
1035
|
-
var CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
1036
1057
|
var API_KEY_BETAS = ["interleaved-thinking-2025-05-14", "fine-grained-tool-streaming-2025-05-14"];
|
|
1037
1058
|
var PACKAGE_NAME = "@xtruder/opencode-claude-max-plugin";
|
|
1038
1059
|
var PROVIDER_ID = "anthropic-sdk";
|
|
1039
|
-
var
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
modalities: {
|
|
1049
|
-
input: ["text", "image"],
|
|
1050
|
-
output: ["text"]
|
|
1051
|
-
}
|
|
1052
|
-
},
|
|
1053
|
-
"claude-sonnet-4-6": {
|
|
1054
|
-
name: "Claude Sonnet 4.6",
|
|
1055
|
-
reasoning: true,
|
|
1056
|
-
tool_call: true,
|
|
1057
|
-
attachment: true,
|
|
1058
|
-
temperature: true,
|
|
1059
|
-
limit: { context: 200000, output: 16000 },
|
|
1060
|
-
cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
1061
|
-
modalities: {
|
|
1062
|
-
input: ["text", "image", "pdf"],
|
|
1063
|
-
output: ["text"]
|
|
1064
|
-
},
|
|
1065
|
-
options: { effort: "medium" },
|
|
1060
|
+
var API_KEY_COSTS = {
|
|
1061
|
+
haiku: { input: 1, output: 5, cache_read: 0.1, cache_write: 1.25 },
|
|
1062
|
+
sonnet: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
1063
|
+
opus: { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }
|
|
1064
|
+
};
|
|
1065
|
+
var ZERO_COST = { input: 0, output: 0, cache_read: 0, cache_write: 0 };
|
|
1066
|
+
function buildPluginModels(isOAuth) {
|
|
1067
|
+
const cost = (tier) => isOAuth ? ZERO_COST : API_KEY_COSTS[tier];
|
|
1068
|
+
const opusVariants = {
|
|
1066
1069
|
variants: {
|
|
1067
1070
|
low: { effort: "low" },
|
|
1068
1071
|
medium: { effort: "medium" },
|
|
1069
|
-
high: { effort: "high" }
|
|
1072
|
+
high: { effort: "high" },
|
|
1073
|
+
max: { effort: "max" }
|
|
1070
1074
|
}
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1075
|
+
};
|
|
1076
|
+
return {
|
|
1077
|
+
"claude-haiku-4-5": {
|
|
1078
|
+
name: "Claude Haiku 4.5",
|
|
1079
|
+
reasoning: false,
|
|
1080
|
+
tool_call: true,
|
|
1081
|
+
attachment: true,
|
|
1082
|
+
temperature: true,
|
|
1083
|
+
limit: { context: 200000, output: 64000 },
|
|
1084
|
+
cost: cost("haiku"),
|
|
1085
|
+
modalities: {
|
|
1086
|
+
input: ["text", "image"],
|
|
1087
|
+
output: ["text"]
|
|
1088
|
+
}
|
|
1083
1089
|
},
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1090
|
+
"claude-sonnet-4-6": {
|
|
1091
|
+
name: "Claude Sonnet 4.6",
|
|
1092
|
+
reasoning: true,
|
|
1093
|
+
tool_call: true,
|
|
1094
|
+
attachment: true,
|
|
1095
|
+
temperature: true,
|
|
1096
|
+
limit: { context: 200000, output: 64000 },
|
|
1097
|
+
cost: cost("sonnet"),
|
|
1098
|
+
modalities: {
|
|
1099
|
+
input: ["text", "image", "pdf"],
|
|
1100
|
+
output: ["text"]
|
|
1101
|
+
},
|
|
1102
|
+
options: { effort: "medium" },
|
|
1103
|
+
variants: {
|
|
1104
|
+
low: { effort: "low" },
|
|
1105
|
+
medium: { effort: "medium" },
|
|
1106
|
+
high: { effort: "high" }
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
"claude-opus-4-6": {
|
|
1110
|
+
name: "Claude Opus 4.6",
|
|
1111
|
+
reasoning: true,
|
|
1112
|
+
tool_call: true,
|
|
1113
|
+
attachment: true,
|
|
1114
|
+
temperature: true,
|
|
1115
|
+
limit: { context: 200000, output: 128000 },
|
|
1116
|
+
cost: cost("opus"),
|
|
1117
|
+
modalities: {
|
|
1118
|
+
input: ["text", "image", "pdf"],
|
|
1119
|
+
output: ["text"]
|
|
1120
|
+
},
|
|
1121
|
+
options: { effort: "medium" },
|
|
1122
|
+
...opusVariants
|
|
1123
|
+
},
|
|
1124
|
+
"claude-opus-4-6-1m": {
|
|
1125
|
+
name: "Claude Opus 4.6 (1M)",
|
|
1126
|
+
reasoning: true,
|
|
1127
|
+
tool_call: true,
|
|
1128
|
+
attachment: true,
|
|
1129
|
+
temperature: true,
|
|
1130
|
+
limit: { context: 1e6, output: 128000 },
|
|
1131
|
+
cost: cost("opus"),
|
|
1132
|
+
modalities: {
|
|
1133
|
+
input: ["text", "image", "pdf"],
|
|
1134
|
+
output: ["text"]
|
|
1135
|
+
},
|
|
1136
|
+
options: { effort: "medium" },
|
|
1137
|
+
...opusVariants
|
|
1089
1138
|
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1092
1141
|
function resolveAuth(options) {
|
|
1093
1142
|
if (options.apiKey) {
|
|
1094
1143
|
return { apiKey: options.apiKey, isOAuth: false };
|
|
@@ -1117,7 +1166,6 @@ function createAnthropicSDK(options = {}) {
|
|
|
1117
1166
|
defaultHeaders["anthropic-dangerous-direct-browser-access"] = "true";
|
|
1118
1167
|
defaultHeaders["x-stainless-package-version"] = "0.74.0";
|
|
1119
1168
|
}
|
|
1120
|
-
const CONTEXT_1M_BODY_THRESHOLD = 600000;
|
|
1121
1169
|
const baseFetch = customFetch ?? globalThis.fetch;
|
|
1122
1170
|
const wrappedFetch = async (url, init) => {
|
|
1123
1171
|
if (auth.isOAuth && init) {
|
|
@@ -1128,14 +1176,6 @@ function createAnthropicSDK(options = {}) {
|
|
|
1128
1176
|
init = { ...init, headers: reqHeaders };
|
|
1129
1177
|
}
|
|
1130
1178
|
}
|
|
1131
|
-
if (init?.body && typeof init.body === "string" && init.body.length > CONTEXT_1M_BODY_THRESHOLD) {
|
|
1132
|
-
const betaHeaders = new Headers(init.headers);
|
|
1133
|
-
const existingBetas = betaHeaders.get("anthropic-beta") ?? "";
|
|
1134
|
-
if (!existingBetas.includes(CONTEXT_1M_BETA)) {
|
|
1135
|
-
betaHeaders.set("anthropic-beta", existingBetas + "," + CONTEXT_1M_BETA);
|
|
1136
|
-
init = { ...init, headers: betaHeaders };
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
1179
|
if (auth.isOAuth && init?.body && typeof init.body === "string" && hasCchPlaceholder(init.body)) {
|
|
1140
1180
|
try {
|
|
1141
1181
|
const cch = await computeCch(init.body);
|
|
@@ -1184,6 +1224,7 @@ function createAnthropicSDK(options = {}) {
|
|
|
1184
1224
|
};
|
|
1185
1225
|
}
|
|
1186
1226
|
var anthropicSDKPlugin = async () => {
|
|
1227
|
+
const isOAuth = resolveAuth({}).isOAuth;
|
|
1187
1228
|
return {
|
|
1188
1229
|
config: async (cfg) => {
|
|
1189
1230
|
if (!cfg.provider) {
|
|
@@ -1191,7 +1232,7 @@ var anthropicSDKPlugin = async () => {
|
|
|
1191
1232
|
}
|
|
1192
1233
|
cfg.provider[PROVIDER_ID] = {
|
|
1193
1234
|
npm: PACKAGE_NAME,
|
|
1194
|
-
models:
|
|
1235
|
+
models: buildPluginModels(isOAuth),
|
|
1195
1236
|
...cfg.provider[PROVIDER_ID]
|
|
1196
1237
|
};
|
|
1197
1238
|
}
|
package/build/tui.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,KAAK,EAGV,eAAe,EAEhB,MAAM,yBAAyB,CAAA;AA+XhC,QAAA,MAAM,MAAM,EAAE,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAG3C,CAAA;AAED,eAAe,MAAM,CAAA"}
|