opencode-anthropic-multi-account 0.2.15 → 0.2.16
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/dist/chunk-IETVH43F.js +114 -0
- package/dist/chunk-IETVH43F.js.map +1 -0
- package/dist/chunk-RVXWLAVK.js +1130 -0
- package/dist/chunk-RVXWLAVK.js.map +1 -0
- package/dist/fingerprint-capture.d.ts +67 -0
- package/dist/fingerprint-capture.js +28 -0
- package/dist/fingerprint-capture.js.map +1 -0
- package/dist/index.js +1308 -848
- package/dist/index.js.map +1 -1
- package/dist/scrub-template.d.ts +12 -0
- package/dist/scrub-template.js +15 -0
- package/dist/scrub-template.js.map +1 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ACCOUNTS_FILENAME,
|
|
3
|
+
ANTHROPIC_OAUTH_ADAPTER,
|
|
4
|
+
ANTHROPIC_PROFILE_ENDPOINT,
|
|
5
|
+
ANTHROPIC_USAGE_ENDPOINT,
|
|
6
|
+
CLAIMS_FILENAME,
|
|
7
|
+
PLAN_LABELS,
|
|
8
|
+
TOKEN_EXPIRY_BUFFER_MS,
|
|
9
|
+
TOKEN_REFRESH_TIMEOUT_MS,
|
|
10
|
+
cc_derived_defaults_default,
|
|
11
|
+
checkCCCompat,
|
|
12
|
+
createMinimalClient,
|
|
13
|
+
debugLog,
|
|
14
|
+
detectCliVersion,
|
|
15
|
+
detectDrift,
|
|
16
|
+
detectOAuthConfig,
|
|
17
|
+
fingerprint_data_default,
|
|
18
|
+
formatWaitTime,
|
|
19
|
+
getAccountLabel,
|
|
20
|
+
getConfig,
|
|
21
|
+
getConfigDir,
|
|
22
|
+
loadConfig,
|
|
23
|
+
loadTemplate,
|
|
24
|
+
refreshLiveFingerprintAsync,
|
|
25
|
+
showToast,
|
|
26
|
+
sleep,
|
|
27
|
+
updateConfigField
|
|
28
|
+
} from "./chunk-RVXWLAVK.js";
|
|
29
|
+
import "./chunk-IETVH43F.js";
|
|
30
|
+
|
|
1
31
|
// src/index.ts
|
|
2
32
|
import { tool } from "@opencode-ai/plugin";
|
|
3
33
|
import {
|
|
@@ -10,33 +40,8 @@ import {
|
|
|
10
40
|
// src/account-manager.ts
|
|
11
41
|
import { createAccountManagerForProvider } from "opencode-multi-account-core";
|
|
12
42
|
|
|
13
|
-
// src/config.ts
|
|
14
|
-
import {
|
|
15
|
-
createConfigLoader
|
|
16
|
-
} from "opencode-multi-account-core";
|
|
17
|
-
var configLoader = createConfigLoader("claude-multiauth.json");
|
|
18
|
-
var { getConfig, loadConfig, resetConfigCache, updateConfigField } = configLoader;
|
|
19
|
-
|
|
20
43
|
// src/claims.ts
|
|
21
44
|
import { createClaimsManager } from "opencode-multi-account-core";
|
|
22
|
-
|
|
23
|
-
// src/constants.ts
|
|
24
|
-
import { anthropicOAuthAdapter } from "opencode-multi-account-core";
|
|
25
|
-
var ANTHROPIC_OAUTH_ADAPTER = anthropicOAuthAdapter;
|
|
26
|
-
var ANTHROPIC_CLIENT_ID = ANTHROPIC_OAUTH_ADAPTER.oauthClientId;
|
|
27
|
-
var ANTHROPIC_TOKEN_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.tokenEndpoint;
|
|
28
|
-
var ANTHROPIC_USAGE_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.usageEndpoint;
|
|
29
|
-
var ANTHROPIC_PROFILE_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.profileEndpoint;
|
|
30
|
-
var ANTHROPIC_BETA_HEADER = ANTHROPIC_OAUTH_ADAPTER.requestBetaHeader;
|
|
31
|
-
var CLAUDE_CLI_USER_AGENT = ANTHROPIC_OAUTH_ADAPTER.cliUserAgent;
|
|
32
|
-
var TOOL_PREFIX = ANTHROPIC_OAUTH_ADAPTER.toolPrefix;
|
|
33
|
-
var ACCOUNTS_FILENAME = ANTHROPIC_OAUTH_ADAPTER.accountStorageFilename;
|
|
34
|
-
var CLAIMS_FILENAME = "anthropic-multi-account-claims.json";
|
|
35
|
-
var PLAN_LABELS = ANTHROPIC_OAUTH_ADAPTER.planLabels;
|
|
36
|
-
var TOKEN_EXPIRY_BUFFER_MS = 6e4;
|
|
37
|
-
var TOKEN_REFRESH_TIMEOUT_MS = 3e4;
|
|
38
|
-
|
|
39
|
-
// src/claims.ts
|
|
40
45
|
var claimsManager = createClaimsManager(CLAIMS_FILENAME);
|
|
41
46
|
var {
|
|
42
47
|
isClaimedByOther,
|
|
@@ -45,9 +50,155 @@ var {
|
|
|
45
50
|
writeClaim
|
|
46
51
|
} = claimsManager;
|
|
47
52
|
|
|
48
|
-
// src/
|
|
49
|
-
import {
|
|
50
|
-
import * as
|
|
53
|
+
// src/anthropic-oauth.ts
|
|
54
|
+
import { exec } from "child_process";
|
|
55
|
+
import * as v3 from "valibot";
|
|
56
|
+
|
|
57
|
+
// src/cc-derived-profile.ts
|
|
58
|
+
var bundledTemplate = fingerprint_data_default;
|
|
59
|
+
var derivedDefaults = cc_derived_defaults_default;
|
|
60
|
+
var DEFAULT_BASE_API_URL = derivedDefaults.request?.baseApiUrl || "https://api.anthropic.com";
|
|
61
|
+
var DEFAULT_ANTHROPIC_VERSION = bundledTemplate.header_values?.["anthropic-version"] || derivedDefaults.request?.anthropicVersion || "2023-06-01";
|
|
62
|
+
var DEFAULT_X_APP = bundledTemplate.header_values?.["x-app"] || derivedDefaults.request?.xApp || "cli";
|
|
63
|
+
var DEFAULT_BETA_HEADER = bundledTemplate.anthropic_beta || bundledTemplate.header_values?.["anthropic-beta"] || derivedDefaults.request?.betaHeader || "oauth-2025-04-20,interleaved-thinking-2025-05-14";
|
|
64
|
+
function loadCCDerivedRequestProfile() {
|
|
65
|
+
const template = loadTemplate();
|
|
66
|
+
const cliVersion = detectCliVersion();
|
|
67
|
+
const anthropicVersion = template.header_values?.["anthropic-version"] || DEFAULT_ANTHROPIC_VERSION;
|
|
68
|
+
const betaHeader = template.anthropic_beta || template.header_values?.["anthropic-beta"] || DEFAULT_BETA_HEADER;
|
|
69
|
+
const xApp = template.header_values?.["x-app"] || DEFAULT_X_APP;
|
|
70
|
+
return {
|
|
71
|
+
template,
|
|
72
|
+
cliVersion,
|
|
73
|
+
userAgent: `claude-cli/${cliVersion} (external, cli)`,
|
|
74
|
+
anthropicVersion,
|
|
75
|
+
betaHeader,
|
|
76
|
+
xApp,
|
|
77
|
+
baseApiUrl: DEFAULT_BASE_API_URL,
|
|
78
|
+
apiV1BaseUrl: `${DEFAULT_BASE_API_URL}/v1`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function loadCCDerivedAuthProfile() {
|
|
82
|
+
const requestProfile = loadCCDerivedRequestProfile();
|
|
83
|
+
const oauthConfig = await detectOAuthConfig();
|
|
84
|
+
const baseApiUrl = oauthConfig.baseApiUrl || requestProfile.baseApiUrl;
|
|
85
|
+
return {
|
|
86
|
+
...requestProfile,
|
|
87
|
+
oauthConfig,
|
|
88
|
+
baseApiUrl,
|
|
89
|
+
apiV1BaseUrl: `${baseApiUrl}/v1`
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/oauth-callback-server.ts
|
|
94
|
+
import { createServer } from "http";
|
|
95
|
+
var DEFAULT_TIMEOUT_MS = 3e5;
|
|
96
|
+
var SUCCESS_REDIRECT_URL = "https://platform.claude.com/oauth/code/success?app=claude-code";
|
|
97
|
+
function startCallbackServer(options) {
|
|
98
|
+
const { expectedState, timeoutMs = DEFAULT_TIMEOUT_MS } = options;
|
|
99
|
+
return new Promise((resolveServer, rejectServer) => {
|
|
100
|
+
let settled = false;
|
|
101
|
+
let resolveCode = null;
|
|
102
|
+
let rejectCode = null;
|
|
103
|
+
let timeoutHandle = null;
|
|
104
|
+
const waitForCode = new Promise((resolve, reject) => {
|
|
105
|
+
resolveCode = resolve;
|
|
106
|
+
rejectCode = reject;
|
|
107
|
+
});
|
|
108
|
+
function settle(error, result) {
|
|
109
|
+
if (settled) return;
|
|
110
|
+
settled = true;
|
|
111
|
+
if (timeoutHandle !== null) {
|
|
112
|
+
clearTimeout(timeoutHandle);
|
|
113
|
+
timeoutHandle = null;
|
|
114
|
+
}
|
|
115
|
+
if (error) {
|
|
116
|
+
rejectCode?.(error);
|
|
117
|
+
} else if (result) {
|
|
118
|
+
resolveCode?.(result);
|
|
119
|
+
}
|
|
120
|
+
resolveCode = null;
|
|
121
|
+
rejectCode = null;
|
|
122
|
+
server.close();
|
|
123
|
+
}
|
|
124
|
+
function stop() {
|
|
125
|
+
settle(new Error("OAuth callback server stopped"));
|
|
126
|
+
}
|
|
127
|
+
function sendResponse(res, status, headers, body, onFinish) {
|
|
128
|
+
res.writeHead(status, { ...headers, Connection: "close" });
|
|
129
|
+
res.end(body ?? "", onFinish);
|
|
130
|
+
}
|
|
131
|
+
const server = createServer(
|
|
132
|
+
(req, res) => {
|
|
133
|
+
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
134
|
+
if (url.pathname !== "/callback") {
|
|
135
|
+
res.writeHead(404, {
|
|
136
|
+
"Content-Type": "text/plain",
|
|
137
|
+
Connection: "close"
|
|
138
|
+
});
|
|
139
|
+
res.end("Not Found");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const code = url.searchParams.get("code");
|
|
143
|
+
const state = url.searchParams.get("state");
|
|
144
|
+
if (state !== expectedState) {
|
|
145
|
+
sendResponse(
|
|
146
|
+
res,
|
|
147
|
+
400,
|
|
148
|
+
{ "Content-Type": "text/plain" },
|
|
149
|
+
"State mismatch",
|
|
150
|
+
() => settle(new Error("OAuth callback state mismatch"))
|
|
151
|
+
);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!code) {
|
|
155
|
+
sendResponse(
|
|
156
|
+
res,
|
|
157
|
+
400,
|
|
158
|
+
{ "Content-Type": "text/plain" },
|
|
159
|
+
"Missing code",
|
|
160
|
+
() => settle(new Error("OAuth callback missing code"))
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
sendResponse(
|
|
165
|
+
res,
|
|
166
|
+
302,
|
|
167
|
+
{ Location: SUCCESS_REDIRECT_URL },
|
|
168
|
+
void 0,
|
|
169
|
+
() => settle(null, { code, state })
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
server.listen(0, "localhost", () => {
|
|
174
|
+
const addr = server.address();
|
|
175
|
+
const port = addr.port;
|
|
176
|
+
timeoutHandle = setTimeout(() => {
|
|
177
|
+
settle(new Error("OAuth callback timed out"));
|
|
178
|
+
}, timeoutMs);
|
|
179
|
+
resolveServer({ port, waitForCode, stop });
|
|
180
|
+
});
|
|
181
|
+
server.on("error", (err) => {
|
|
182
|
+
if (!settled) {
|
|
183
|
+
rejectServer(err);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/oauth-pkce.ts
|
|
190
|
+
import { createHash, randomBytes } from "crypto";
|
|
191
|
+
function base64url(buffer) {
|
|
192
|
+
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
193
|
+
}
|
|
194
|
+
function generatePKCE() {
|
|
195
|
+
const verifier = base64url(randomBytes(32));
|
|
196
|
+
const challenge = base64url(createHash("sha256").update(verifier).digest());
|
|
197
|
+
return { verifier, challenge };
|
|
198
|
+
}
|
|
199
|
+
function generateState() {
|
|
200
|
+
return base64url(randomBytes(32));
|
|
201
|
+
}
|
|
51
202
|
|
|
52
203
|
// src/token-node-request.ts
|
|
53
204
|
import * as childProcess from "child_process";
|
|
@@ -55,8 +206,10 @@ function buildNodeTokenRequestScript() {
|
|
|
55
206
|
return `
|
|
56
207
|
const https = require("node:https");
|
|
57
208
|
const endpoint = process.env.ANTHROPIC_REFRESH_ENDPOINT;
|
|
209
|
+
const contentType = process.env.ANTHROPIC_REFRESH_CONTENT_TYPE || "application/json";
|
|
58
210
|
const timeoutMs = Number(process.env.ANTHROPIC_REFRESH_TIMEOUT_MS || "30000");
|
|
59
211
|
const payload = process.env.ANTHROPIC_REFRESH_REQUEST_BODY || "";
|
|
212
|
+
const userAgent = process.env.ANTHROPIC_REFRESH_USER_AGENT;
|
|
60
213
|
|
|
61
214
|
function printSuccess(body) {
|
|
62
215
|
console.log(JSON.stringify({ ok: true, body }));
|
|
@@ -69,9 +222,10 @@ function printFailure(error) {
|
|
|
69
222
|
const request = https.request(endpoint, {
|
|
70
223
|
method: "POST",
|
|
71
224
|
headers: {
|
|
72
|
-
"Content-Type":
|
|
225
|
+
"Content-Type": contentType,
|
|
73
226
|
Accept: "application/json",
|
|
74
227
|
"Content-Length": Buffer.byteLength(payload).toString(),
|
|
228
|
+
...(userAgent ? { "User-Agent": userAgent } : {}),
|
|
75
229
|
},
|
|
76
230
|
}, (response) => {
|
|
77
231
|
let body = "";
|
|
@@ -104,6 +258,7 @@ request.end();
|
|
|
104
258
|
}
|
|
105
259
|
async function defaultRunNodeTokenRequest(options) {
|
|
106
260
|
const script = buildNodeTokenRequestScript();
|
|
261
|
+
const contentType = options.contentType ?? "application/json";
|
|
107
262
|
return await new Promise((resolve, reject) => {
|
|
108
263
|
childProcess.execFile(
|
|
109
264
|
options.executable,
|
|
@@ -113,9 +268,11 @@ async function defaultRunNodeTokenRequest(options) {
|
|
|
113
268
|
maxBuffer: 1024 * 1024,
|
|
114
269
|
env: {
|
|
115
270
|
...process.env,
|
|
271
|
+
ANTHROPIC_REFRESH_CONTENT_TYPE: contentType,
|
|
116
272
|
ANTHROPIC_REFRESH_ENDPOINT: options.endpoint,
|
|
117
273
|
ANTHROPIC_REFRESH_REQUEST_BODY: options.body,
|
|
118
|
-
ANTHROPIC_REFRESH_TIMEOUT_MS: String(options.timeoutMs)
|
|
274
|
+
ANTHROPIC_REFRESH_TIMEOUT_MS: String(options.timeoutMs),
|
|
275
|
+
ANTHROPIC_REFRESH_USER_AGENT: options.userAgent ?? ""
|
|
119
276
|
}
|
|
120
277
|
},
|
|
121
278
|
(error, stdout, stderr) => {
|
|
@@ -138,30 +295,6 @@ async function runNodeTokenRequest(options) {
|
|
|
138
295
|
return await nodeTokenRequestRunner(options);
|
|
139
296
|
}
|
|
140
297
|
|
|
141
|
-
// src/utils.ts
|
|
142
|
-
import {
|
|
143
|
-
createMinimalClient,
|
|
144
|
-
formatWaitTime,
|
|
145
|
-
getAccountLabel,
|
|
146
|
-
getConfigDir,
|
|
147
|
-
getErrorCode,
|
|
148
|
-
sleep
|
|
149
|
-
} from "opencode-multi-account-core";
|
|
150
|
-
async function showToast(client, message, variant) {
|
|
151
|
-
if (getConfig().quiet_mode) return;
|
|
152
|
-
try {
|
|
153
|
-
await client.tui.showToast({ body: { message, variant } });
|
|
154
|
-
} catch {
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function debugLog(client, message, extra) {
|
|
158
|
-
if (!getConfig().debug) return;
|
|
159
|
-
client.app.log({
|
|
160
|
-
body: { service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName, level: "debug", message, extra }
|
|
161
|
-
}).catch(() => {
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
298
|
// src/usage.ts
|
|
166
299
|
import * as v2 from "valibot";
|
|
167
300
|
|
|
@@ -328,186 +461,180 @@ function getPlanLabel(account) {
|
|
|
328
461
|
return PLAN_LABELS[account.planTier] ?? account.planTier.charAt(0).toUpperCase() + account.planTier.slice(1);
|
|
329
462
|
}
|
|
330
463
|
|
|
331
|
-
// src/
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
464
|
+
// src/anthropic-oauth.ts
|
|
465
|
+
var TOKEN_REQUEST_EXECUTABLE = process.env.OPENCODE_REFRESH_NODE_EXECUTABLE || "node";
|
|
466
|
+
var browserExec = (command, callback) => {
|
|
467
|
+
exec(command, callback);
|
|
468
|
+
};
|
|
469
|
+
var callbackServerStarter = startCallbackServer;
|
|
470
|
+
var profileFetcher = fetchProfile;
|
|
471
|
+
var usageFetcher = fetchUsage;
|
|
472
|
+
function buildTokenRequestError(url, details) {
|
|
473
|
+
return new Error(`Anthropic token request failed. url=${url}; details=${details}`);
|
|
338
474
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
var tokenProxyInstalled = false;
|
|
344
|
-
var tokenProxyOriginalFetch = null;
|
|
345
|
-
var refreshEndpointUrl = new URL(ANTHROPIC_REFRESH_ENDPOINT);
|
|
346
|
-
function buildRefreshRequestError(details) {
|
|
347
|
-
return new Error(`Anthropic token refresh request failed. url=${ANTHROPIC_REFRESH_ENDPOINT}; details=${details}`);
|
|
348
|
-
}
|
|
349
|
-
function buildRefreshInvalidJsonError(body, details) {
|
|
350
|
-
return new Error(`Anthropic token refresh returned invalid JSON. url=${ANTHROPIC_REFRESH_ENDPOINT}; body=${body}; details=${details}`);
|
|
351
|
-
}
|
|
352
|
-
function getRequestUrlString(input) {
|
|
353
|
-
if (typeof input === "string") return input;
|
|
354
|
-
if (input instanceof URL) return input.toString();
|
|
355
|
-
return input.url;
|
|
356
|
-
}
|
|
357
|
-
function isAnthropicTokenEndpoint(input) {
|
|
358
|
-
const rawUrl = getRequestUrlString(input);
|
|
475
|
+
function buildTokenInvalidJsonError(url, body, details) {
|
|
476
|
+
return new Error(`Anthropic token request returned invalid JSON. url=${url}; body=${body}; details=${details}`);
|
|
477
|
+
}
|
|
478
|
+
function parseNodeTokenEnvelope(output, endpoint) {
|
|
359
479
|
try {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
480
|
+
return JSON.parse(output);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
const details = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
483
|
+
throw buildTokenInvalidJsonError(endpoint, output, details);
|
|
364
484
|
}
|
|
365
485
|
}
|
|
366
|
-
function
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
486
|
+
function parseTokenResponseBody(body, endpoint) {
|
|
487
|
+
let parsed;
|
|
488
|
+
try {
|
|
489
|
+
parsed = JSON.parse(body);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
const details = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
492
|
+
throw buildTokenInvalidJsonError(endpoint, body, details);
|
|
372
493
|
}
|
|
373
|
-
return
|
|
494
|
+
return v3.parse(TokenResponseSchema, parsed);
|
|
374
495
|
}
|
|
375
|
-
function
|
|
376
|
-
if (
|
|
377
|
-
return
|
|
496
|
+
function getOpenBrowserCommand(url, platform = process.platform) {
|
|
497
|
+
if (platform === "win32") {
|
|
498
|
+
return `start "" ${JSON.stringify(url)}`;
|
|
378
499
|
}
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
async function getRequestBody(input, init) {
|
|
382
|
-
const body = getRequestBodySource(input, init);
|
|
383
|
-
if (typeof body === "string") return body;
|
|
384
|
-
if (body instanceof URLSearchParams) return body.toString();
|
|
385
|
-
if (body instanceof Uint8Array || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
|
|
386
|
-
return stringifyBinaryBody(body);
|
|
500
|
+
if (platform === "darwin") {
|
|
501
|
+
return `open ${JSON.stringify(url)}`;
|
|
387
502
|
}
|
|
388
|
-
|
|
389
|
-
if (body instanceof ReadableStream) return await new Response(body).text();
|
|
390
|
-
if (input instanceof Request && init?.body === void 0) return await input.clone().text();
|
|
391
|
-
if (body == null) return "";
|
|
392
|
-
throw buildRefreshRequestError(`Unsupported token request body type: ${Object.prototype.toString.call(body)}`);
|
|
393
|
-
}
|
|
394
|
-
function getRequestMethod(input, init) {
|
|
395
|
-
return init?.method ?? (input instanceof Request ? input.method : "GET");
|
|
503
|
+
return `xdg-open ${JSON.stringify(url)}`;
|
|
396
504
|
}
|
|
397
|
-
function
|
|
398
|
-
return tokenProxyContext.getStore()?.proxyTokenRequests === true && isAnthropicTokenEndpoint(input);
|
|
399
|
-
}
|
|
400
|
-
function shouldInjectAnthropicUserAgent(input) {
|
|
401
|
-
const userAgent = tokenProxyContext.getStore()?.userAgent;
|
|
402
|
-
if (!userAgent) return false;
|
|
403
|
-
const rawUrl = getRequestUrlString(input);
|
|
505
|
+
function openBrowser(url) {
|
|
404
506
|
try {
|
|
405
|
-
|
|
406
|
-
|
|
507
|
+
browserExec(getOpenBrowserCommand(url), () => {
|
|
508
|
+
});
|
|
407
509
|
} catch {
|
|
408
|
-
return rawUrl.includes(ANTHROPIC_TOKEN_HOST);
|
|
409
510
|
}
|
|
410
511
|
}
|
|
411
|
-
async function
|
|
512
|
+
async function postTokenEndpoint(contentType, body, timeoutMs = TOKEN_REFRESH_TIMEOUT_MS, userAgent) {
|
|
513
|
+
const derivedProfile = await loadCCDerivedAuthProfile();
|
|
514
|
+
const oauthConfig = derivedProfile.oauthConfig;
|
|
515
|
+
const endpoint = oauthConfig.tokenUrl;
|
|
516
|
+
const resolvedUserAgent = userAgent ?? derivedProfile.userAgent;
|
|
412
517
|
let output;
|
|
413
518
|
try {
|
|
414
519
|
output = await runNodeTokenRequest({
|
|
415
520
|
body,
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
521
|
+
contentType,
|
|
522
|
+
endpoint,
|
|
523
|
+
executable: TOKEN_REQUEST_EXECUTABLE,
|
|
524
|
+
timeoutMs,
|
|
525
|
+
userAgent: resolvedUserAgent
|
|
419
526
|
});
|
|
420
527
|
} catch (error) {
|
|
421
528
|
const details = error instanceof Error ? error.message : String(error);
|
|
422
|
-
throw
|
|
529
|
+
throw buildTokenRequestError(endpoint, details);
|
|
423
530
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
} catch (error) {
|
|
428
|
-
const details = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
429
|
-
throw buildRefreshInvalidJsonError(output, details);
|
|
531
|
+
const result = parseNodeTokenEnvelope(output, endpoint);
|
|
532
|
+
if (result.ok) {
|
|
533
|
+
return parseTokenResponseBody(result.body ?? "", endpoint);
|
|
430
534
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (result.error) {
|
|
434
|
-
throw buildRefreshRequestError(result.error);
|
|
435
|
-
}
|
|
436
|
-
throw buildRefreshRequestError(`Error: HTTP request failed. status=${result.status ?? 0}; url=${ANTHROPIC_REFRESH_ENDPOINT}; body=${result.body ?? ""}`);
|
|
535
|
+
if (result.error) {
|
|
536
|
+
throw buildTokenRequestError(endpoint, result.error);
|
|
437
537
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
});
|
|
538
|
+
throw buildTokenRequestError(
|
|
539
|
+
endpoint,
|
|
540
|
+
`Error: HTTP request failed. status=${result.status ?? 0}; url=${endpoint}; body=${result.body ?? ""}`
|
|
541
|
+
);
|
|
444
542
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
headers.set("user-agent", tokenProxyContext.getStore().userAgent);
|
|
457
|
-
return originalFetch(input, { ...init, headers });
|
|
458
|
-
}
|
|
459
|
-
return originalFetch(input, init);
|
|
543
|
+
var CODE_EXCHANGE_TIMEOUT_MS = 3e4;
|
|
544
|
+
async function exchangeCodeForTokens(params) {
|
|
545
|
+
const derivedProfile = await loadCCDerivedAuthProfile();
|
|
546
|
+
const oauthConfig = derivedProfile.oauthConfig;
|
|
547
|
+
const body = JSON.stringify({
|
|
548
|
+
grant_type: "authorization_code",
|
|
549
|
+
client_id: oauthConfig.clientId,
|
|
550
|
+
code: params.code,
|
|
551
|
+
redirect_uri: params.redirectUri,
|
|
552
|
+
code_verifier: params.codeVerifier,
|
|
553
|
+
state: params.state
|
|
460
554
|
});
|
|
555
|
+
return postTokenEndpoint("application/json", body, CODE_EXCHANGE_TIMEOUT_MS);
|
|
556
|
+
}
|
|
557
|
+
async function loginWithOAuth(callbacks) {
|
|
558
|
+
const { oauthConfig: cfg } = await loadCCDerivedAuthProfile();
|
|
559
|
+
const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();
|
|
560
|
+
const state = generateState();
|
|
561
|
+
const { port, waitForCode, stop } = await callbackServerStarter({ expectedState: state });
|
|
562
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
563
|
+
try {
|
|
564
|
+
const authorizeUrl = `${cfg.authorizeUrl}?${new URLSearchParams({
|
|
565
|
+
code: "true",
|
|
566
|
+
client_id: cfg.clientId,
|
|
567
|
+
response_type: "code",
|
|
568
|
+
redirect_uri: redirectUri,
|
|
569
|
+
scope: cfg.scopes,
|
|
570
|
+
code_challenge: codeChallenge,
|
|
571
|
+
code_challenge_method: "S256",
|
|
572
|
+
state
|
|
573
|
+
}).toString()}`;
|
|
574
|
+
callbacks.onAuth({
|
|
575
|
+
url: authorizeUrl,
|
|
576
|
+
instructions: "Complete authorization in your browser."
|
|
577
|
+
});
|
|
578
|
+
openBrowser(authorizeUrl);
|
|
579
|
+
callbacks.onProgress?.("Waiting for browser authorization...");
|
|
580
|
+
const { code } = await waitForCode;
|
|
581
|
+
callbacks.onProgress?.("Exchanging authorization code...");
|
|
582
|
+
const tokens = await exchangeCodeForTokens({
|
|
583
|
+
code,
|
|
584
|
+
codeVerifier,
|
|
585
|
+
state,
|
|
586
|
+
redirectUri
|
|
587
|
+
});
|
|
588
|
+
callbacks.onProgress?.("Fetching profile...");
|
|
589
|
+
const profileResult = await profileFetcher(tokens.access_token);
|
|
590
|
+
try {
|
|
591
|
+
await usageFetcher(tokens.access_token);
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
const profileData = profileResult.ok ? profileResult.data : void 0;
|
|
595
|
+
const now2 = Date.now();
|
|
596
|
+
return {
|
|
597
|
+
accessToken: tokens.access_token,
|
|
598
|
+
refreshToken: tokens.refresh_token,
|
|
599
|
+
expiresAt: now2 + tokens.expires_in * 1e3,
|
|
600
|
+
email: profileData?.email,
|
|
601
|
+
planTier: profileData?.planTier ?? "",
|
|
602
|
+
addedAt: now2,
|
|
603
|
+
lastUsed: now2
|
|
604
|
+
};
|
|
605
|
+
} catch (error) {
|
|
606
|
+
stop();
|
|
607
|
+
throw error;
|
|
608
|
+
}
|
|
461
609
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
if (profileResult.ok) {
|
|
475
|
-
return profileResult;
|
|
476
|
-
}
|
|
477
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
478
|
-
profileResult = await fetchProfile(accessToken);
|
|
479
|
-
return profileResult;
|
|
480
|
-
}
|
|
481
|
-
async function loginWithPiAi(callbacks) {
|
|
482
|
-
const piCreds = await withAnthropicTokenProxyFetch(
|
|
483
|
-
() => piAiOauth.loginAnthropic({
|
|
484
|
-
onAuth: callbacks.onAuth,
|
|
485
|
-
onPrompt: callbacks.onPrompt,
|
|
486
|
-
onProgress: callbacks.onProgress,
|
|
487
|
-
onManualCodeInput: callbacks.onManualCodeInput
|
|
488
|
-
}),
|
|
489
|
-
{ userAgent: callbacks.userAgent }
|
|
610
|
+
var REFRESH_TIMEOUT_MS = 15e3;
|
|
611
|
+
async function refreshWithOAuth(currentRefreshToken) {
|
|
612
|
+
const { oauthConfig } = await loadCCDerivedAuthProfile();
|
|
613
|
+
const body = new URLSearchParams({
|
|
614
|
+
grant_type: "refresh_token",
|
|
615
|
+
refresh_token: currentRefreshToken,
|
|
616
|
+
client_id: oauthConfig.clientId
|
|
617
|
+
}).toString();
|
|
618
|
+
const response = await postTokenEndpoint(
|
|
619
|
+
"application/x-www-form-urlencoded",
|
|
620
|
+
body,
|
|
621
|
+
REFRESH_TIMEOUT_MS
|
|
490
622
|
);
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return {
|
|
495
|
-
...base,
|
|
496
|
-
email: profileData?.email,
|
|
497
|
-
planTier: profileData?.planTier ?? "",
|
|
498
|
-
addedAt: Date.now(),
|
|
499
|
-
lastUsed: Date.now()
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
async function refreshWithPiAi(currentRefreshToken) {
|
|
503
|
-
const piCreds = await withAnthropicTokenProxyFetch(() => piAiOauth.refreshAnthropicToken(currentRefreshToken));
|
|
504
|
-
return {
|
|
505
|
-
accessToken: piCreds.access,
|
|
506
|
-
refreshToken: piCreds.refresh,
|
|
507
|
-
expiresAt: piCreds.expires
|
|
623
|
+
const patch = {
|
|
624
|
+
accessToken: response.access_token,
|
|
625
|
+
expiresAt: Date.now() + response.expires_in * 1e3
|
|
508
626
|
};
|
|
627
|
+
if (response.refresh_token) {
|
|
628
|
+
patch.refreshToken = response.refresh_token;
|
|
629
|
+
}
|
|
630
|
+
if (response.account?.uuid) {
|
|
631
|
+
patch.uuid = response.account.uuid;
|
|
632
|
+
}
|
|
633
|
+
if (response.account?.email_address) {
|
|
634
|
+
patch.email = response.account.email_address;
|
|
635
|
+
}
|
|
636
|
+
return patch;
|
|
509
637
|
}
|
|
510
|
-
var PI_AI_ADAPTER_SERVICE = ANTHROPIC_OAUTH_ADAPTER.serviceLogName;
|
|
511
638
|
|
|
512
639
|
// src/token.ts
|
|
513
640
|
var PERMANENT_FAILURE_HTTP_STATUSES = /* @__PURE__ */ new Set([400, 401, 403]);
|
|
@@ -529,7 +656,7 @@ async function refreshToken(currentRefreshToken, accountId, client) {
|
|
|
529
656
|
if (inFlightRefresh) return inFlightRefresh;
|
|
530
657
|
const refreshPromise = (async () => {
|
|
531
658
|
try {
|
|
532
|
-
const patch = await
|
|
659
|
+
const patch = await refreshWithOAuth(currentRefreshToken);
|
|
533
660
|
return { ok: true, patch };
|
|
534
661
|
} catch (error) {
|
|
535
662
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -754,11 +881,11 @@ function getAccountStatus(account) {
|
|
|
754
881
|
if (account.isAuthDisabled) return "auth-disabled";
|
|
755
882
|
if (!account.enabled) return "disabled";
|
|
756
883
|
if (account.cachedUsage) {
|
|
757
|
-
const
|
|
884
|
+
const now2 = Date.now();
|
|
758
885
|
const usage = account.cachedUsage;
|
|
759
886
|
const hasEvaluableUsageTier = [usage.five_hour, usage.seven_day].some((tier) => tier != null);
|
|
760
887
|
const exhaustedTiers = [usage.five_hour, usage.seven_day].filter(
|
|
761
|
-
(tier) => tier && tier.utilization >= 100 && tier.resets_at != null && Date.parse(tier.resets_at) >
|
|
888
|
+
(tier) => tier && tier.utilization >= 100 && tier.resets_at != null && Date.parse(tier.resets_at) > now2
|
|
762
889
|
);
|
|
763
890
|
if (exhaustedTiers.length > 0) {
|
|
764
891
|
return "rate-limited";
|
|
@@ -870,10 +997,10 @@ function createProgressBar(utilization, width = 20) {
|
|
|
870
997
|
function formatResetTime(resetAt) {
|
|
871
998
|
if (!resetAt) return "";
|
|
872
999
|
const date = new Date(resetAt);
|
|
873
|
-
const
|
|
1000
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
874
1001
|
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
875
1002
|
const timeStr = date.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit", hour12: true });
|
|
876
|
-
const isSameDay = date.getFullYear() ===
|
|
1003
|
+
const isSameDay = date.getFullYear() === now2.getFullYear() && date.getMonth() === now2.getMonth() && date.getDate() === now2.getDate();
|
|
877
1004
|
if (isSameDay) {
|
|
878
1005
|
return ` (resets ${timeStr}, ${tz})`;
|
|
879
1006
|
}
|
|
@@ -952,8 +1079,7 @@ var AccountStore = class extends CoreAccountStore {
|
|
|
952
1079
|
|
|
953
1080
|
// src/auth-handler.ts
|
|
954
1081
|
import { randomUUID } from "crypto";
|
|
955
|
-
import {
|
|
956
|
-
import { exec } from "child_process";
|
|
1082
|
+
import { exec as exec2 } from "child_process";
|
|
957
1083
|
function makeFailedFlowResult(message) {
|
|
958
1084
|
return {
|
|
959
1085
|
url: "",
|
|
@@ -976,38 +1102,17 @@ function asOAuthCallbackResponse(account) {
|
|
|
976
1102
|
expires: account.expiresAt
|
|
977
1103
|
};
|
|
978
1104
|
}
|
|
979
|
-
function
|
|
980
|
-
if (!isTTY()) return Promise.resolve("");
|
|
981
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
982
|
-
return new Promise((resolve) => {
|
|
983
|
-
rl.question(message, (answer) => {
|
|
984
|
-
rl.close();
|
|
985
|
-
resolve(answer.trim());
|
|
986
|
-
});
|
|
987
|
-
});
|
|
988
|
-
}
|
|
989
|
-
function normalizePromptMessage(prompt) {
|
|
990
|
-
return prompt.message ?? prompt.text ?? prompt.label ?? prompt.title ?? prompt.placeholder ?? "Continue authentication: ";
|
|
991
|
-
}
|
|
992
|
-
async function startPiAiFlow() {
|
|
1105
|
+
async function startOAuthFlow() {
|
|
993
1106
|
try {
|
|
994
|
-
const completedAccount = await
|
|
1107
|
+
const completedAccount = await loginWithOAuth({
|
|
995
1108
|
onAuth: (info) => {
|
|
996
|
-
if (info.url) {
|
|
997
|
-
openBrowser(info.url);
|
|
998
|
-
}
|
|
999
1109
|
const instruction = info.instructions ?? "Complete authorization in your browser.";
|
|
1000
1110
|
const urlLine = info.url ? `
|
|
1001
1111
|
Auth URL (manual fallback): ${info.url}` : "";
|
|
1002
1112
|
console.log(`
|
|
1003
1113
|
${instruction}${urlLine}
|
|
1004
1114
|
`);
|
|
1005
|
-
}
|
|
1006
|
-
onPrompt: async (prompt) => {
|
|
1007
|
-
const text = normalizePromptMessage(prompt);
|
|
1008
|
-
return promptLine(text.endsWith(":") || text.endsWith("?") ? `${text} ` : `${text}: `);
|
|
1009
|
-
},
|
|
1010
|
-
userAgent: CLAUDE_CLI_USER_AGENT
|
|
1115
|
+
}
|
|
1011
1116
|
});
|
|
1012
1117
|
const completedResult = asOAuthCallbackResponse(completedAccount);
|
|
1013
1118
|
const accountEmail = completedAccount.email;
|
|
@@ -1028,7 +1133,7 @@ function wrapCallbackWithAccountReplace(result, manager, targetAccount) {
|
|
|
1028
1133
|
...result,
|
|
1029
1134
|
callback: async function(code) {
|
|
1030
1135
|
const callbackResult = await originalCallback(code);
|
|
1031
|
-
if (callbackResult.type === "success"
|
|
1136
|
+
if (callbackResult.type === "success") {
|
|
1032
1137
|
if (targetAccount.uuid) {
|
|
1033
1138
|
await manager.replaceAccountCredentials(targetAccount.uuid, toOAuthCredentials(callbackResult));
|
|
1034
1139
|
}
|
|
@@ -1047,7 +1152,7 @@ function wrapCallbackWithManagerSync(result, manager) {
|
|
|
1047
1152
|
...result,
|
|
1048
1153
|
callback: async function(code) {
|
|
1049
1154
|
const callbackResult = await originalCallback(code);
|
|
1050
|
-
if (callbackResult.type === "success"
|
|
1155
|
+
if (callbackResult.type === "success") {
|
|
1051
1156
|
const auth = toOAuthCredentials(callbackResult);
|
|
1052
1157
|
if (manager) {
|
|
1053
1158
|
const countBefore = manager.getAccounts().length;
|
|
@@ -1068,25 +1173,17 @@ function wrapCallbackWithManagerSync(result, manager) {
|
|
|
1068
1173
|
}
|
|
1069
1174
|
};
|
|
1070
1175
|
}
|
|
1071
|
-
function openBrowser(url) {
|
|
1072
|
-
const commands = {
|
|
1073
|
-
darwin: "open",
|
|
1074
|
-
win32: "start"
|
|
1075
|
-
};
|
|
1076
|
-
const cmd = commands[process.platform] ?? "xdg-open";
|
|
1077
|
-
exec(`${cmd} ${JSON.stringify(url)}`);
|
|
1078
|
-
}
|
|
1079
1176
|
async function persistFallback(auth) {
|
|
1080
1177
|
try {
|
|
1081
1178
|
const store = new AccountStore();
|
|
1082
|
-
const
|
|
1179
|
+
const now2 = Date.now();
|
|
1083
1180
|
const account = {
|
|
1084
1181
|
uuid: randomUUID(),
|
|
1085
1182
|
refreshToken: auth.refresh,
|
|
1086
1183
|
accessToken: auth.access,
|
|
1087
1184
|
expiresAt: auth.expires,
|
|
1088
|
-
addedAt:
|
|
1089
|
-
lastUsed:
|
|
1185
|
+
addedAt: now2,
|
|
1186
|
+
lastUsed: now2,
|
|
1090
1187
|
enabled: true,
|
|
1091
1188
|
planTier: "",
|
|
1092
1189
|
consecutiveAuthFailures: 0,
|
|
@@ -1099,11 +1196,11 @@ async function persistFallback(auth) {
|
|
|
1099
1196
|
}
|
|
1100
1197
|
async function handleAuthorize(manager, inputs, client) {
|
|
1101
1198
|
if (!inputs || !isTTY()) {
|
|
1102
|
-
return wrapCallbackWithManagerSync(await
|
|
1199
|
+
return wrapCallbackWithManagerSync(await startOAuthFlow(), manager);
|
|
1103
1200
|
}
|
|
1104
1201
|
const effectiveManager = manager ?? await loadManagerFromDisk(client);
|
|
1105
1202
|
if (!effectiveManager || effectiveManager.getAccounts().length === 0) {
|
|
1106
|
-
return wrapCallbackWithManagerSync(await
|
|
1203
|
+
return wrapCallbackWithManagerSync(await startOAuthFlow(), manager);
|
|
1107
1204
|
}
|
|
1108
1205
|
return runAccountManagementMenu(effectiveManager, client);
|
|
1109
1206
|
}
|
|
@@ -1120,7 +1217,7 @@ async function runAccountManagementMenu(manager, client) {
|
|
|
1120
1217
|
const menuAction = await showAuthMenu(allAccounts);
|
|
1121
1218
|
switch (menuAction.type) {
|
|
1122
1219
|
case "add":
|
|
1123
|
-
return wrapCallbackWithManagerSync(await
|
|
1220
|
+
return wrapCallbackWithManagerSync(await startOAuthFlow(), manager);
|
|
1124
1221
|
case "check-quotas":
|
|
1125
1222
|
await handleCheckQuotas(manager, client);
|
|
1126
1223
|
continue;
|
|
@@ -1129,7 +1226,7 @@ async function runAccountManagementMenu(manager, client) {
|
|
|
1129
1226
|
if (result.action === "back" || result.action === "cancel") continue;
|
|
1130
1227
|
const manageResult = await handleManageAction(manager, result.action, result.account, client);
|
|
1131
1228
|
if (manageResult.triggerOAuth) {
|
|
1132
|
-
return wrapCallbackWithAccountReplace(await
|
|
1229
|
+
return wrapCallbackWithAccountReplace(await startOAuthFlow(), manager, manageResult.account);
|
|
1133
1230
|
}
|
|
1134
1231
|
continue;
|
|
1135
1232
|
}
|
|
@@ -1139,7 +1236,7 @@ async function runAccountManagementMenu(manager, client) {
|
|
|
1139
1236
|
case "delete-all":
|
|
1140
1237
|
await manager.clearAllAccounts();
|
|
1141
1238
|
console.log("\nAll accounts deleted.\n");
|
|
1142
|
-
return wrapCallbackWithManagerSync(await
|
|
1239
|
+
return wrapCallbackWithManagerSync(await startOAuthFlow(), manager);
|
|
1143
1240
|
case "cancel":
|
|
1144
1241
|
return makeFailedFlowResult("Authentication cancelled");
|
|
1145
1242
|
}
|
|
@@ -1256,15 +1353,27 @@ Retrying authentication for ${label}...
|
|
|
1256
1353
|
return { triggerOAuth: false };
|
|
1257
1354
|
}
|
|
1258
1355
|
|
|
1356
|
+
// src/proactive-refresh.ts
|
|
1357
|
+
import { createProactiveRefreshQueueForProvider } from "opencode-multi-account-core";
|
|
1358
|
+
var ProactiveRefreshQueue = createProactiveRefreshQueueForProvider({
|
|
1359
|
+
providerAuthId: "anthropic",
|
|
1360
|
+
getConfig,
|
|
1361
|
+
isTokenExpired,
|
|
1362
|
+
refreshToken,
|
|
1363
|
+
debugLog
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
// src/runtime-factory.ts
|
|
1367
|
+
import { TokenRefreshError } from "opencode-multi-account-core";
|
|
1368
|
+
|
|
1259
1369
|
// src/request-transform.ts
|
|
1260
|
-
import {
|
|
1370
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
1261
1371
|
|
|
1262
1372
|
// src/model-config.ts
|
|
1263
1373
|
function splitBetaFlags(value) {
|
|
1264
1374
|
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
1265
1375
|
}
|
|
1266
1376
|
var config = {
|
|
1267
|
-
ccVersion: ANTHROPIC_OAUTH_ADAPTER.cliVersion,
|
|
1268
1377
|
baseBetas: splitBetaFlags(ANTHROPIC_OAUTH_ADAPTER.requestBetaHeader),
|
|
1269
1378
|
longContextBetas: ["context-1m-2025-08-07", "interleaved-thinking-2025-05-14"],
|
|
1270
1379
|
modelOverrides: {
|
|
@@ -1273,18 +1382,6 @@ var config = {
|
|
|
1273
1382
|
}
|
|
1274
1383
|
}
|
|
1275
1384
|
};
|
|
1276
|
-
function getCliVersion() {
|
|
1277
|
-
return process.env.ANTHROPIC_CLI_VERSION ?? config.ccVersion;
|
|
1278
|
-
}
|
|
1279
|
-
function getUserAgent() {
|
|
1280
|
-
if (process.env.ANTHROPIC_USER_AGENT) {
|
|
1281
|
-
return process.env.ANTHROPIC_USER_AGENT;
|
|
1282
|
-
}
|
|
1283
|
-
if (process.env.ANTHROPIC_CLI_VERSION) {
|
|
1284
|
-
return `claude-cli/${getCliVersion()} (external, cli)`;
|
|
1285
|
-
}
|
|
1286
|
-
return ANTHROPIC_OAUTH_ADAPTER.cliUserAgent;
|
|
1287
|
-
}
|
|
1288
1385
|
function getRequiredBetas() {
|
|
1289
1386
|
return splitBetaFlags(process.env.ANTHROPIC_BETA_FLAGS ?? config.baseBetas.join(","));
|
|
1290
1387
|
}
|
|
@@ -1374,590 +1471,449 @@ function getModelBetas(modelId, excluded) {
|
|
|
1374
1471
|
return betas.filter((beta) => !excluded.has(beta));
|
|
1375
1472
|
}
|
|
1376
1473
|
|
|
1377
|
-
// src/
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
</example>
|
|
1403
|
-
|
|
1404
|
-
<example>
|
|
1405
|
-
user: what is 2+2?
|
|
1406
|
-
assistant: 4
|
|
1407
|
-
</example>
|
|
1408
|
-
|
|
1409
|
-
<example>
|
|
1410
|
-
user: is 11 a prime number?
|
|
1411
|
-
assistant: Yes
|
|
1412
|
-
</example>
|
|
1413
|
-
|
|
1414
|
-
<example>
|
|
1415
|
-
user: what command should I run to list files in the current directory?
|
|
1416
|
-
assistant: ls
|
|
1417
|
-
</example>
|
|
1418
|
-
|
|
1419
|
-
<example>
|
|
1420
|
-
user: what command should I run to watch files in the current directory?
|
|
1421
|
-
assistant: [runs ls to list the files in the current directory, then read docs/commands in the relevant file to find out how to watch files]
|
|
1422
|
-
npm run dev
|
|
1423
|
-
</example>
|
|
1424
|
-
|
|
1425
|
-
<example>
|
|
1426
|
-
user: How many golf balls fit inside a jetta?
|
|
1427
|
-
assistant: 150000
|
|
1428
|
-
</example>
|
|
1429
|
-
|
|
1430
|
-
<example>
|
|
1431
|
-
user: what files are in the directory src/?
|
|
1432
|
-
assistant: [runs ls and sees foo.c, bar.c, baz.c]
|
|
1433
|
-
user: which file contains the implementation of foo?
|
|
1434
|
-
assistant: src/foo.c
|
|
1435
|
-
</example>
|
|
1436
|
-
When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).
|
|
1437
|
-
Remember that your output will be displayed on a command line interface. Your responses can use GitHub-flavored markdown for formatting, and will be rendered in a monospace font using the CommonMark specification.
|
|
1438
|
-
Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
|
|
1439
|
-
If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.
|
|
1440
|
-
Only use emojis if the user explicitly requests it. Avoid using emojis in all communication unless asked.
|
|
1441
|
-
IMPORTANT: Keep your responses short, since they will be displayed on a command line interface.
|
|
1442
|
-
|
|
1443
|
-
# Proactiveness
|
|
1444
|
-
You are allowed to be proactive, but only when the user asks you to do something. You should strive to strike a balance between:
|
|
1445
|
-
- Doing the right thing when asked, including taking actions and follow-up actions
|
|
1446
|
-
- Not surprising the user with actions you take without asking
|
|
1447
|
-
For example, if the user asks you how to approach something, you should do your best to answer their question first, and not immediately jump into taking actions.
|
|
1448
|
-
|
|
1449
|
-
# Professional objectivity
|
|
1450
|
-
Prioritize technical accuracy and truthfulness over validating the user's beliefs. Focus on facts and problem-solving, providing direct, objective technical info without any unnecessary superlatives, praise, or emotional validation. It is best for the user if Claude honestly applies the same rigorous standards to all ideas and disagrees when necessary, even if it may not be what the user wants to hear. Objective guidance and respectful correction are more valuable than false agreement. Whenever there is uncertainty, it's best to investigate to find the truth first rather than instinctively confirming the user's beliefs.
|
|
1451
|
-
|
|
1452
|
-
# Task Management
|
|
1453
|
-
You have access to the TodoWrite tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
|
|
1454
|
-
These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
|
|
1455
|
-
|
|
1456
|
-
It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.
|
|
1457
|
-
|
|
1458
|
-
Examples:
|
|
1459
|
-
|
|
1460
|
-
<example>
|
|
1461
|
-
user: Run the build and fix any type errors
|
|
1462
|
-
assistant: I'm going to use the TodoWrite tool to write the following items to the todo list:
|
|
1463
|
-
- Run the build
|
|
1464
|
-
- Fix any type errors
|
|
1465
|
-
|
|
1466
|
-
I'm now going to run the build using Bash.
|
|
1467
|
-
|
|
1468
|
-
Looks like I found 10 type errors. I'm going to use the TodoWrite tool to write 10 items to the todo list.
|
|
1469
|
-
|
|
1470
|
-
marking the first todo as in_progress
|
|
1471
|
-
|
|
1472
|
-
Let me start working on the first item...
|
|
1473
|
-
|
|
1474
|
-
The first item has been fixed, let me mark the first todo as completed, and move on to the second item...
|
|
1475
|
-
..
|
|
1476
|
-
..
|
|
1477
|
-
</example>
|
|
1478
|
-
In the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors.
|
|
1479
|
-
|
|
1480
|
-
<example>
|
|
1481
|
-
user: Help me write a new feature that allows users to track their usage metrics and export them to various formats
|
|
1482
|
-
|
|
1483
|
-
assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the TodoWrite tool to plan this task.
|
|
1484
|
-
Adding the following todos to the todo list:
|
|
1485
|
-
1. Research existing metrics tracking in the codebase
|
|
1486
|
-
2. Design the metrics collection system
|
|
1487
|
-
3. Implement core metrics tracking functionality
|
|
1488
|
-
4. Create export functionality for different formats
|
|
1489
|
-
|
|
1490
|
-
Let me start by researching the existing codebase to understand what metrics we might already be tracking and how we can build on that.
|
|
1491
|
-
|
|
1492
|
-
I'm going to search for any existing metrics or telemetry code in the project.
|
|
1493
|
-
|
|
1494
|
-
I've found some existing telemetry code. Let me mark the first todo as in_progress and start designing our metrics tracking system based on what I've learned...
|
|
1495
|
-
|
|
1496
|
-
[Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go]
|
|
1497
|
-
</example>
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
Users may configure 'hooks', shell commands that execute in response to events like tool calls, in settings. Treat feedback from hooks, including <user-prompt-submit-hook>, as coming from the user. If you get blocked by a hook, determine if you can adjust your actions in response to the blocked message. If not, ask the user to check their hooks configuration.
|
|
1501
|
-
|
|
1502
|
-
# Doing tasks
|
|
1503
|
-
The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
|
|
1504
|
-
- Use the TodoWrite tool to plan the task if required
|
|
1505
|
-
|
|
1506
|
-
- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are automatically added by the system, and bear no direct relation to the specific tool results or user messages in which they appear.
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
# Tool usage policy
|
|
1510
|
-
- When doing file search, prefer to use the Task tool in order to reduce context usage.
|
|
1511
|
-
- You should proactively use the Task tool with specialized agents when the task at hand matches the agent's description.
|
|
1512
|
-
|
|
1513
|
-
- When WebFetch returns a message about a redirect to a different host, you should immediately make a new WebFetch request with the redirect URL provided in the response.
|
|
1514
|
-
- You have the capability to call multiple tools in a single response. When multiple independent pieces of information are requested, batch your tool calls together for optimal performance. When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls to run the calls in parallel.
|
|
1515
|
-
- If the user specifies that they want you to run tools "in parallel", you MUST send a single message with multiple tool use content blocks. For example, if you need to launch multiple agents in parallel, send a single message with multiple Task tool calls.
|
|
1516
|
-
- Use specialized tools instead of bash commands when possible, as this provides a better user experience. For file operations, use dedicated tools: Read for reading files instead of cat/head/tail, Edit for editing instead of sed/awk, and Write for creating files instead of cat with heredoc or echo redirection. Reserve bash tools exclusively for actual system commands and terminal operations that require shell execution. NEVER use bash echo or other command-line tools to communicate thoughts, explanations, or instructions to the user. Output all communication directly in your response text instead.
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
Here is useful information about the environment you are running in:
|
|
1520
|
-
<env>
|
|
1521
|
-
Working directory: /home/thdxr/dev/projects/anomalyco/opencode/packages/opencode
|
|
1522
|
-
Is directory a git repo: Yes
|
|
1523
|
-
Platform: linux
|
|
1524
|
-
OS Version: Linux 6.12.4-arch1-1
|
|
1525
|
-
Today's date: 2025-09-30
|
|
1526
|
-
</env>
|
|
1527
|
-
You are powered by the model named Sonnet 4.5. The exact model ID is claude-sonnet-4-5-20250929.
|
|
1528
|
-
|
|
1529
|
-
Assistant knowledge cutoff is January 2025.
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code that may be used maliciously. Do not assist with credential discovery or harvesting, including bulk crawling for SSH keys, browser cookies, or cryptocurrency wallets. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
IMPORTANT: Always use the TodoWrite tool to plan and track tasks throughout the conversation.
|
|
1536
|
-
|
|
1537
|
-
# Code References
|
|
1538
|
-
|
|
1539
|
-
When referencing specific functions or pieces of code include the pattern \`file_path:line_number\` to allow the user to easily navigate to the source code location.
|
|
1540
|
-
|
|
1541
|
-
<example>
|
|
1542
|
-
user: Where are errors from the client handled?
|
|
1543
|
-
assistant: Clients are marked as failed in the \`connectToServer\` function in src/services/process.ts:712.
|
|
1544
|
-
</example>`;
|
|
1545
|
-
|
|
1546
|
-
// src/request-transform.ts
|
|
1547
|
-
function getInjectedSystemPrompt() {
|
|
1548
|
-
return INJECTED_SYSTEM_PROMPT;
|
|
1549
|
-
}
|
|
1550
|
-
function sampleCodeUnits(text, indices) {
|
|
1551
|
-
return indices.map((i) => i < text.length ? text.charCodeAt(i).toString(16) : "30").join("");
|
|
1552
|
-
}
|
|
1553
|
-
function buildBillingHeader(firstUserMessage) {
|
|
1554
|
-
const version = ANTHROPIC_OAUTH_ADAPTER.cliVersion;
|
|
1555
|
-
const salt = ANTHROPIC_OAUTH_ADAPTER.billingSalt;
|
|
1556
|
-
if (!version || !salt) return "";
|
|
1557
|
-
const sampled = sampleCodeUnits(firstUserMessage, [4, 7, 20]);
|
|
1558
|
-
const hash = createHash("sha256").update(`${salt}${sampled}${version}`).digest("hex").slice(0, 3);
|
|
1559
|
-
return `x-anthropic-billing-header: cc_version=${version}.${hash}; cc_entrypoint=cli; cch=00000;`;
|
|
1560
|
-
}
|
|
1561
|
-
var OPENCODE_CAMEL_RE = /OpenCode/g;
|
|
1562
|
-
var OPENCODE_LOWER_RE = /(?<!\/)opencode/gi;
|
|
1563
|
-
var TOOL_MASK_PREFIX = "tool_";
|
|
1564
|
-
var PARAGRAPH_REMOVAL_ANCHORS = [
|
|
1565
|
-
"github.com/anomalyco/opencode",
|
|
1566
|
-
"opencode.ai/docs"
|
|
1567
|
-
];
|
|
1568
|
-
var BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
1569
|
-
var DOCUMENTED_BUILTIN_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1570
|
-
"Agent",
|
|
1571
|
-
"AskUserQuestion",
|
|
1572
|
-
"Bash",
|
|
1573
|
-
"CronCreate",
|
|
1574
|
-
"CronDelete",
|
|
1575
|
-
"CronList",
|
|
1576
|
-
"Edit",
|
|
1577
|
-
"EnterPlanMode",
|
|
1578
|
-
"EnterWorktree",
|
|
1579
|
-
"ExitPlanMode",
|
|
1580
|
-
"ExitWorktree",
|
|
1581
|
-
"Glob",
|
|
1582
|
-
"Grep",
|
|
1583
|
-
"ListMcpResourcesTool",
|
|
1584
|
-
"LSP",
|
|
1585
|
-
"Monitor",
|
|
1586
|
-
"NotebookEdit",
|
|
1587
|
-
"PowerShell",
|
|
1588
|
-
"Read",
|
|
1589
|
-
"ReadMcpResourceTool",
|
|
1590
|
-
"SendMessage",
|
|
1591
|
-
"Skill",
|
|
1592
|
-
"TaskCreate",
|
|
1593
|
-
"TaskGet",
|
|
1594
|
-
"TaskList",
|
|
1595
|
-
"TaskOutput",
|
|
1596
|
-
"TaskStop",
|
|
1597
|
-
"TaskUpdate",
|
|
1598
|
-
"TeamCreate",
|
|
1599
|
-
"TeamDelete",
|
|
1600
|
-
"TodoWrite",
|
|
1601
|
-
"ToolSearch",
|
|
1602
|
-
"WebFetch",
|
|
1603
|
-
"WebSearch",
|
|
1604
|
-
"Write"
|
|
1605
|
-
]);
|
|
1606
|
-
function isTypedTool(tool2) {
|
|
1607
|
-
return typeof tool2.type === "string" && tool2.type.trim().length > 0;
|
|
1474
|
+
// src/claude-identity.ts
|
|
1475
|
+
import { readFileSync, readdirSync } from "fs";
|
|
1476
|
+
import { homedir } from "os";
|
|
1477
|
+
import { join } from "path";
|
|
1478
|
+
var EMPTY_IDENTITY = {
|
|
1479
|
+
deviceId: "",
|
|
1480
|
+
accountUuid: ""
|
|
1481
|
+
};
|
|
1482
|
+
var testOverrideIdentity = null;
|
|
1483
|
+
function getCandidatePaths() {
|
|
1484
|
+
const home = homedir();
|
|
1485
|
+
const paths = [
|
|
1486
|
+
join(home, ".claude.json"),
|
|
1487
|
+
join(home, ".claude", ".claude.json"),
|
|
1488
|
+
join(home, ".claude", "claude.json")
|
|
1489
|
+
];
|
|
1490
|
+
try {
|
|
1491
|
+
const backupDir = join(home, ".claude", "backups");
|
|
1492
|
+
const backups = readdirSync(backupDir).filter((file) => file.startsWith(".claude.json.backup.")).sort().reverse();
|
|
1493
|
+
for (const backup of backups) {
|
|
1494
|
+
paths.push(join(backupDir, backup));
|
|
1495
|
+
}
|
|
1496
|
+
} catch {
|
|
1497
|
+
}
|
|
1498
|
+
return paths;
|
|
1608
1499
|
}
|
|
1609
|
-
function
|
|
1610
|
-
|
|
1611
|
-
|
|
1500
|
+
function parseIdentityFile(path) {
|
|
1501
|
+
try {
|
|
1502
|
+
const data = JSON.parse(readFileSync(path, "utf-8"));
|
|
1503
|
+
if (!data.userID) {
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
return {
|
|
1507
|
+
deviceId: data.userID,
|
|
1508
|
+
accountUuid: data.oauthAccount?.accountUuid ?? data.accountUuid ?? ""
|
|
1509
|
+
};
|
|
1510
|
+
} catch {
|
|
1511
|
+
return null;
|
|
1612
1512
|
}
|
|
1613
|
-
return !DOCUMENTED_BUILTIN_TOOL_NAMES.has(name) && !name.startsWith(TOOL_MASK_PREFIX);
|
|
1614
1513
|
}
|
|
1615
|
-
function
|
|
1616
|
-
if (
|
|
1617
|
-
return
|
|
1514
|
+
function loadClaudeIdentity() {
|
|
1515
|
+
if (testOverrideIdentity) {
|
|
1516
|
+
return testOverrideIdentity;
|
|
1618
1517
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1518
|
+
try {
|
|
1519
|
+
for (const path of getCandidatePaths()) {
|
|
1520
|
+
const identity = parseIdentityFile(path);
|
|
1521
|
+
if (identity) {
|
|
1522
|
+
return identity;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
} catch {
|
|
1621
1526
|
}
|
|
1622
|
-
return
|
|
1527
|
+
return EMPTY_IDENTITY;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// src/upstream-request.ts
|
|
1531
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
1532
|
+
var BILLING_SEED = "59cf53e54c78";
|
|
1533
|
+
var DEFAULT_CC_VERSION = "2.1.100";
|
|
1534
|
+
var SESSION_IDLE_ROTATE_MS = 15 * 60 * 1e3;
|
|
1535
|
+
var MAX_TOOL_RESULT_TEXT_LENGTH = 30 * 1024;
|
|
1536
|
+
var TRUNCATION_SUFFIX = "[...truncated]";
|
|
1537
|
+
var DEFAULT_CONTEXT_MANAGEMENT = {};
|
|
1538
|
+
var DEFAULT_OUTPUT_CONFIG = {};
|
|
1539
|
+
var ORCHESTRATION_TAG_NAMES = [
|
|
1540
|
+
"system-reminder",
|
|
1541
|
+
"env",
|
|
1542
|
+
"system_information",
|
|
1543
|
+
"current_working_directory",
|
|
1544
|
+
"operating_system",
|
|
1545
|
+
"default_shell",
|
|
1546
|
+
"home_directory",
|
|
1547
|
+
"task_metadata",
|
|
1548
|
+
"directories",
|
|
1549
|
+
"thinking",
|
|
1550
|
+
"agent_persona",
|
|
1551
|
+
"agent_context",
|
|
1552
|
+
"tool_context",
|
|
1553
|
+
"persona",
|
|
1554
|
+
"tool_call"
|
|
1555
|
+
];
|
|
1556
|
+
var ORCHESTRATION_PATTERNS = ORCHESTRATION_TAG_NAMES.flatMap((tag) => [
|
|
1557
|
+
new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, "gi"),
|
|
1558
|
+
new RegExp(`<${tag}\\b[^>]*/>`, "gi")
|
|
1559
|
+
]);
|
|
1560
|
+
var FRAMEWORK_PATTERNS = [
|
|
1561
|
+
/\b(roo[- ]?cline|roo[- ]?code|big[- ]?agi|claude[- ]?bridge|amazon\s+q)\b/gi,
|
|
1562
|
+
/\b(openclaw|hermes|aider|cursor|windsurf|cline|continue|copilot|cody)\b/gi,
|
|
1563
|
+
/\b(zed|plandex|tabby|opencode|daytona)\b/gi,
|
|
1564
|
+
/\b(librechat|typingmind)\b/gi,
|
|
1565
|
+
/\b(openai|gpt-4|gpt-3\.5)\b/gi,
|
|
1566
|
+
/powered by [a-z]+/gi,
|
|
1567
|
+
/\bgateway\b/gi,
|
|
1568
|
+
/\bsessions_[a-z_]+\b/gi
|
|
1569
|
+
];
|
|
1570
|
+
var upstreamRequestTestOverrides = {};
|
|
1571
|
+
var sessionId = randomUUID2();
|
|
1572
|
+
var sessionLastUsed = 0;
|
|
1573
|
+
function now() {
|
|
1574
|
+
return upstreamRequestTestOverrides.now?.() ?? Date.now();
|
|
1623
1575
|
}
|
|
1624
|
-
function
|
|
1625
|
-
|
|
1626
|
-
|
|
1576
|
+
function createSessionId() {
|
|
1577
|
+
return upstreamRequestTestOverrides.createSessionId?.() ?? randomUUID2();
|
|
1578
|
+
}
|
|
1579
|
+
function getActiveSessionId() {
|
|
1580
|
+
const currentTime = now();
|
|
1581
|
+
if (sessionLastUsed === 0 || currentTime - sessionLastUsed > SESSION_IDLE_ROTATE_MS) {
|
|
1582
|
+
sessionId = createSessionId();
|
|
1627
1583
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1584
|
+
sessionLastUsed = currentTime;
|
|
1585
|
+
return sessionId;
|
|
1586
|
+
}
|
|
1587
|
+
function getUpstreamSessionId() {
|
|
1588
|
+
return getActiveSessionId();
|
|
1589
|
+
}
|
|
1590
|
+
function isRecord(value) {
|
|
1591
|
+
return typeof value === "object" && value !== null;
|
|
1592
|
+
}
|
|
1593
|
+
function cloneBody(value) {
|
|
1594
|
+
return structuredClone(value);
|
|
1595
|
+
}
|
|
1596
|
+
function sanitizeContent(text) {
|
|
1597
|
+
let result = text;
|
|
1598
|
+
for (const pattern of ORCHESTRATION_PATTERNS) {
|
|
1599
|
+
pattern.lastIndex = 0;
|
|
1600
|
+
result = result.replace(pattern, "");
|
|
1631
1601
|
}
|
|
1632
|
-
return
|
|
1602
|
+
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
1633
1603
|
}
|
|
1634
|
-
function
|
|
1635
|
-
|
|
1636
|
-
return `${TOOL_MASK_PREFIX}${digest}`;
|
|
1604
|
+
function sanitizeAndScrubText(text) {
|
|
1605
|
+
return scrubFrameworkIdentifiers(sanitizeContent(text)).replace(/\n{3,}/g, "\n\n").trim();
|
|
1637
1606
|
}
|
|
1638
|
-
function
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
if (!isRecord(tool2) || isTypedTool(tool2) || !shouldMaskToolName(tool2.name)) {
|
|
1643
|
-
continue;
|
|
1644
|
-
}
|
|
1645
|
-
candidates.add(tool2.name);
|
|
1607
|
+
function stripCacheControl(value) {
|
|
1608
|
+
if (Array.isArray(value)) {
|
|
1609
|
+
for (const item of value) {
|
|
1610
|
+
stripCacheControl(item);
|
|
1646
1611
|
}
|
|
1612
|
+
return;
|
|
1647
1613
|
}
|
|
1648
|
-
if (
|
|
1649
|
-
|
|
1650
|
-
if (!Array.isArray(message.content)) {
|
|
1651
|
-
continue;
|
|
1652
|
-
}
|
|
1653
|
-
for (const contentBlock of message.content) {
|
|
1654
|
-
if (contentBlock.type !== "tool_use" || !shouldMaskToolName(contentBlock.name)) {
|
|
1655
|
-
continue;
|
|
1656
|
-
}
|
|
1657
|
-
candidates.add(contentBlock.name);
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1614
|
+
if (!isRecord(value)) {
|
|
1615
|
+
return;
|
|
1660
1616
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1617
|
+
delete value.cache_control;
|
|
1618
|
+
for (const nested of Object.values(value)) {
|
|
1619
|
+
stripCacheControl(nested);
|
|
1663
1620
|
}
|
|
1664
|
-
return [...candidates];
|
|
1665
1621
|
}
|
|
1666
|
-
function
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
const firstUserText = extractFirstUserText(parsed);
|
|
1670
|
-
const usedMaskedNames = /* @__PURE__ */ new Set();
|
|
1671
|
-
for (const candidate of candidates) {
|
|
1672
|
-
let hashLength = 8;
|
|
1673
|
-
let maskedName = buildMaskedToolName(firstUserText, candidate, hashLength);
|
|
1674
|
-
while (usedMaskedNames.has(maskedName)) {
|
|
1675
|
-
hashLength += 2;
|
|
1676
|
-
maskedName = buildMaskedToolName(firstUserText, candidate, hashLength);
|
|
1677
|
-
}
|
|
1678
|
-
maskMap.set(candidate, maskedName);
|
|
1679
|
-
usedMaskedNames.add(maskedName);
|
|
1622
|
+
function sanitizeMessageBlock(block) {
|
|
1623
|
+
if (typeof block.text === "string") {
|
|
1624
|
+
block.text = sanitizeAndScrubText(block.text);
|
|
1680
1625
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
function extractRequestToolMaskMap(body) {
|
|
1684
|
-
if (!body) {
|
|
1685
|
-
return /* @__PURE__ */ new Map();
|
|
1626
|
+
if (block.type !== "tool_result") {
|
|
1627
|
+
return;
|
|
1686
1628
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
return
|
|
1690
|
-
}
|
|
1691
|
-
|
|
1629
|
+
if (typeof block.content === "string") {
|
|
1630
|
+
block.content = truncateToolResultText(sanitizeAndScrubText(block.content));
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
if (!Array.isArray(block.content)) {
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
for (const item of block.content) {
|
|
1637
|
+
if (isRecord(item) && typeof item.text === "string") {
|
|
1638
|
+
item.text = truncateToolResultText(sanitizeAndScrubText(item.text));
|
|
1639
|
+
}
|
|
1692
1640
|
}
|
|
1693
1641
|
}
|
|
1694
|
-
function
|
|
1695
|
-
|
|
1696
|
-
|
|
1642
|
+
function stripAssistantThinkingBlocks(messages) {
|
|
1643
|
+
for (const message of messages) {
|
|
1644
|
+
if (message.role !== "assistant" || !Array.isArray(message.content)) {
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
message.content = message.content.filter((block) => block.type !== "thinking");
|
|
1697
1648
|
}
|
|
1698
|
-
return maskMap.get(name) ?? name;
|
|
1699
1649
|
}
|
|
1700
|
-
function
|
|
1701
|
-
if (
|
|
1702
|
-
return
|
|
1650
|
+
function hasMeaningfulContent(content) {
|
|
1651
|
+
if (typeof content === "string") {
|
|
1652
|
+
return content.trim().length > 0;
|
|
1703
1653
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1654
|
+
if (!Array.isArray(content)) {
|
|
1655
|
+
return false;
|
|
1656
|
+
}
|
|
1657
|
+
return content.some((block) => {
|
|
1658
|
+
if (!isRecord(block)) {
|
|
1659
|
+
return false;
|
|
1707
1660
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
return nextName === contentBlock.name ? contentBlock : { ...contentBlock, name: nextName };
|
|
1716
|
-
})
|
|
1717
|
-
};
|
|
1661
|
+
if (typeof block.text === "string" && block.text.trim().length > 0) {
|
|
1662
|
+
return true;
|
|
1663
|
+
}
|
|
1664
|
+
if (typeof block.content === "string" && block.content.trim().length > 0) {
|
|
1665
|
+
return true;
|
|
1666
|
+
}
|
|
1667
|
+
return false;
|
|
1718
1668
|
});
|
|
1719
1669
|
}
|
|
1720
|
-
function
|
|
1721
|
-
|
|
1722
|
-
|
|
1670
|
+
function trimTrailingEmptyTurns(messages) {
|
|
1671
|
+
while (messages.length > 0) {
|
|
1672
|
+
const lastMessage = messages[messages.length - 1];
|
|
1673
|
+
if (!lastMessage || hasMeaningfulContent(lastMessage.content)) {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
messages.pop();
|
|
1723
1677
|
}
|
|
1724
|
-
const nextName = renameMaskedToolName(toolChoice.name, maskMap);
|
|
1725
|
-
return nextName === toolChoice.name ? toolChoice : { ...toolChoice, name: nextName };
|
|
1726
1678
|
}
|
|
1727
|
-
function
|
|
1728
|
-
return typeof value === "object" && value !== null;
|
|
1729
|
-
}
|
|
1730
|
-
function isProtectedSystemText(text) {
|
|
1731
|
-
return text === SYSTEM_PROMPT || text === INJECTED_SYSTEM_PROMPT || text.startsWith(BILLING_HEADER_PREFIX);
|
|
1732
|
-
}
|
|
1733
|
-
function sanitizeSystemText(text) {
|
|
1734
|
-
const paragraphs = text.split(/\n\n+/).map((paragraph) => paragraph.trim()).filter(Boolean).filter((paragraph) => !PARAGRAPH_REMOVAL_ANCHORS.some((anchor) => paragraph.includes(anchor)));
|
|
1735
|
-
return paragraphs.join("\n\n").replace(OPENCODE_CAMEL_RE, "Claude Code").replace(OPENCODE_LOWER_RE, "Claude").replace(/\n{3,}/g, "\n\n").trim();
|
|
1736
|
-
}
|
|
1737
|
-
function normalizeSystemEntries(system) {
|
|
1679
|
+
function normalizeSystemTexts(system) {
|
|
1738
1680
|
if (typeof system === "string") {
|
|
1739
|
-
const
|
|
1740
|
-
return
|
|
1681
|
+
const next = sanitizeAndScrubText(system);
|
|
1682
|
+
return next ? [next] : [];
|
|
1741
1683
|
}
|
|
1742
1684
|
if (!Array.isArray(system)) {
|
|
1743
1685
|
return [];
|
|
1744
1686
|
}
|
|
1745
|
-
const
|
|
1687
|
+
const texts = [];
|
|
1746
1688
|
for (const entry of system) {
|
|
1747
1689
|
if (typeof entry === "string") {
|
|
1748
|
-
const
|
|
1749
|
-
if (
|
|
1750
|
-
|
|
1690
|
+
const next = sanitizeAndScrubText(entry);
|
|
1691
|
+
if (next) {
|
|
1692
|
+
texts.push(next);
|
|
1751
1693
|
}
|
|
1752
1694
|
continue;
|
|
1753
1695
|
}
|
|
1754
|
-
if (
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
continue;
|
|
1696
|
+
if (isRecord(entry) && typeof entry.text === "string") {
|
|
1697
|
+
const next = sanitizeAndScrubText(entry.text);
|
|
1698
|
+
if (next) {
|
|
1699
|
+
texts.push(next);
|
|
1700
|
+
}
|
|
1760
1701
|
}
|
|
1761
|
-
normalized.push({
|
|
1762
|
-
...entry,
|
|
1763
|
-
type: "text",
|
|
1764
|
-
text: rawText
|
|
1765
|
-
});
|
|
1766
1702
|
}
|
|
1767
|
-
return
|
|
1703
|
+
return texts;
|
|
1768
1704
|
}
|
|
1769
|
-
function
|
|
1770
|
-
|
|
1771
|
-
return content;
|
|
1772
|
-
}
|
|
1773
|
-
if (typeof content === "string") {
|
|
1774
|
-
return content ? `${prefix}
|
|
1775
|
-
|
|
1776
|
-
${content}` : prefix;
|
|
1777
|
-
}
|
|
1778
|
-
if (!Array.isArray(content)) {
|
|
1779
|
-
return [{ type: "text", text: prefix }];
|
|
1780
|
-
}
|
|
1781
|
-
const firstTextIndex = content.findIndex(
|
|
1782
|
-
(block) => isRecord(block) && block.type === "text" && typeof block.text === "string"
|
|
1783
|
-
);
|
|
1784
|
-
if (firstTextIndex === -1) {
|
|
1785
|
-
return [{ type: "text", text: prefix }, ...content];
|
|
1786
|
-
}
|
|
1787
|
-
return content.map((block, index) => {
|
|
1788
|
-
if (index !== firstTextIndex || !isRecord(block) || typeof block.text !== "string") {
|
|
1789
|
-
return block;
|
|
1790
|
-
}
|
|
1791
|
-
return {
|
|
1792
|
-
...block,
|
|
1793
|
-
text: block.text ? `${prefix}
|
|
1794
|
-
|
|
1795
|
-
${block.text}` : prefix
|
|
1796
|
-
};
|
|
1797
|
-
});
|
|
1705
|
+
function filterInjectedSystemTexts(systemTexts, template, billingHeader) {
|
|
1706
|
+
return systemTexts.filter((entry) => entry !== billingHeader && entry !== template.agent_identity && entry !== template.system_prompt && !entry.startsWith("x-anthropic-billing-header:"));
|
|
1798
1707
|
}
|
|
1799
|
-
function
|
|
1800
|
-
if (!Array.isArray(
|
|
1801
|
-
return
|
|
1708
|
+
function extractFirstUserMessage(messages) {
|
|
1709
|
+
if (!Array.isArray(messages)) {
|
|
1710
|
+
return "";
|
|
1802
1711
|
}
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
for (const entry of systemEntries) {
|
|
1806
|
-
if (isProtectedSystemText(entry.text)) {
|
|
1807
|
-
preservedEntries.push(entry);
|
|
1712
|
+
for (const message of messages) {
|
|
1713
|
+
if (message.role !== "user") {
|
|
1808
1714
|
continue;
|
|
1809
1715
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1716
|
+
if (typeof message.content === "string") {
|
|
1717
|
+
return message.content;
|
|
1718
|
+
}
|
|
1719
|
+
if (!Array.isArray(message.content)) {
|
|
1720
|
+
return "";
|
|
1813
1721
|
}
|
|
1814
|
-
|
|
1722
|
+
const text = message.content.filter((block) => typeof block.text === "string").map((block) => block.text).join("\n\n").trim();
|
|
1723
|
+
return text;
|
|
1815
1724
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1725
|
+
return "";
|
|
1726
|
+
}
|
|
1727
|
+
function hasCompleteToolSchemas(tools) {
|
|
1728
|
+
return tools.length > 0 && tools.every((tool2) => typeof tool2 === "object" && tool2 !== null && "input_schema" in tool2);
|
|
1729
|
+
}
|
|
1730
|
+
function getCcVersion(template) {
|
|
1731
|
+
return template.cc_version ?? DEFAULT_CC_VERSION;
|
|
1732
|
+
}
|
|
1733
|
+
function buildBillingHeader(firstUserMessage, template) {
|
|
1734
|
+
const version = getCcVersion(template);
|
|
1735
|
+
const buildTag = computeBuildTag(firstUserMessage, version);
|
|
1736
|
+
return `x-anthropic-billing-header: cc_version=${version}.${buildTag}; cc_entrypoint=cli; cch=00000;`;
|
|
1737
|
+
}
|
|
1738
|
+
function truncateToolResultText(text) {
|
|
1739
|
+
if (text.length <= MAX_TOOL_RESULT_TEXT_LENGTH) {
|
|
1740
|
+
return text;
|
|
1818
1741
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
if (
|
|
1823
|
-
return
|
|
1742
|
+
return `${text.slice(0, MAX_TOOL_RESULT_TEXT_LENGTH)}${TRUNCATION_SUFFIX}`;
|
|
1743
|
+
}
|
|
1744
|
+
function getReverseName(name, reverseLookup) {
|
|
1745
|
+
if (!reverseLookup) {
|
|
1746
|
+
return name;
|
|
1824
1747
|
}
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
return systemEntries.map((entry) => isProtectedSystemText(entry.text) ? entry : { ...entry, text: sanitizeSystemText(entry.text) }).filter((entry) => entry.text);
|
|
1748
|
+
if (reverseLookup instanceof Map) {
|
|
1749
|
+
return reverseLookup.get(name) ?? name;
|
|
1828
1750
|
}
|
|
1829
|
-
|
|
1830
|
-
...userMessage,
|
|
1831
|
-
content: prependToMessageContent(userMessage.content, prefix)
|
|
1832
|
-
};
|
|
1833
|
-
parsed.messages = nextMessages;
|
|
1834
|
-
return preservedEntries;
|
|
1751
|
+
return typeof reverseLookup[name] === "string" ? String(reverseLookup[name]) : name;
|
|
1835
1752
|
}
|
|
1836
|
-
function
|
|
1837
|
-
if (
|
|
1838
|
-
return
|
|
1753
|
+
function reverseMapToolUseNames(value, reverseLookup) {
|
|
1754
|
+
if (Array.isArray(value)) {
|
|
1755
|
+
return value.map((item) => reverseMapToolUseNames(item, reverseLookup));
|
|
1839
1756
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
} catch {
|
|
1847
|
-
return [];
|
|
1757
|
+
if (!isRecord(value)) {
|
|
1758
|
+
return value;
|
|
1759
|
+
}
|
|
1760
|
+
const cloned = {};
|
|
1761
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
1762
|
+
cloned[key] = reverseMapToolUseNames(nested, reverseLookup);
|
|
1848
1763
|
}
|
|
1764
|
+
if (cloned.type === "tool_use" && typeof cloned.name === "string") {
|
|
1765
|
+
cloned.name = getReverseName(cloned.name, reverseLookup);
|
|
1766
|
+
}
|
|
1767
|
+
return cloned;
|
|
1849
1768
|
}
|
|
1850
|
-
function
|
|
1851
|
-
if (!
|
|
1769
|
+
function remapSseLine(line, reverseLookup) {
|
|
1770
|
+
if (!line.startsWith("data:")) {
|
|
1852
1771
|
return line;
|
|
1853
1772
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
new RegExp(`"name"\\s*:\\s*"${maskedName}"`, "g"),
|
|
1858
|
-
`"name": "${originalName}"`
|
|
1859
|
-
);
|
|
1773
|
+
const payload = line.slice(5).trimStart();
|
|
1774
|
+
if (payload.length === 0 || payload === "[DONE]") {
|
|
1775
|
+
return line;
|
|
1860
1776
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
if (lines.length === 0) {
|
|
1867
|
-
return { output: "", remaining };
|
|
1777
|
+
try {
|
|
1778
|
+
const parsed = JSON.parse(payload);
|
|
1779
|
+
return `data: ${JSON.stringify(reverseMapResponse(parsed, reverseLookup))}`;
|
|
1780
|
+
} catch {
|
|
1781
|
+
return line;
|
|
1868
1782
|
}
|
|
1869
|
-
const output = `${lines.map((line) => stripToolPrefixFromLine(line, maskMap)).join("\n")}
|
|
1870
|
-
`;
|
|
1871
|
-
return { output, remaining };
|
|
1872
1783
|
}
|
|
1873
|
-
function
|
|
1874
|
-
const
|
|
1875
|
-
if (
|
|
1876
|
-
|
|
1877
|
-
headers.set(key, value);
|
|
1878
|
-
});
|
|
1784
|
+
function sanitizeMessages(body) {
|
|
1785
|
+
const messages = body.messages;
|
|
1786
|
+
if (!Array.isArray(messages)) {
|
|
1787
|
+
return;
|
|
1879
1788
|
}
|
|
1880
|
-
|
|
1881
|
-
if (
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1789
|
+
for (const message of messages) {
|
|
1790
|
+
if (!isRecord(message)) {
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
if (typeof message.content === "string") {
|
|
1794
|
+
message.content = sanitizeContent(message.content);
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
if (!Array.isArray(message.content)) {
|
|
1798
|
+
continue;
|
|
1799
|
+
}
|
|
1800
|
+
for (const block of message.content) {
|
|
1801
|
+
if (isRecord(block) && typeof block.text === "string") {
|
|
1802
|
+
block.text = sanitizeContent(block.text);
|
|
1892
1803
|
}
|
|
1893
1804
|
}
|
|
1894
1805
|
}
|
|
1895
|
-
const incomingBetas = (headers.get("anthropic-beta") || "").split(",").map((b) => b.trim()).filter(Boolean);
|
|
1896
|
-
const modelBetas = getModelBetas(modelId, excludedBetas2);
|
|
1897
|
-
const mergedBetas = [.../* @__PURE__ */ new Set([
|
|
1898
|
-
...modelBetas,
|
|
1899
|
-
...incomingBetas
|
|
1900
|
-
])].join(",");
|
|
1901
|
-
headers.set("authorization", `Bearer ${accessToken}`);
|
|
1902
|
-
headers.set("anthropic-beta", mergedBetas);
|
|
1903
|
-
headers.set("user-agent", getUserAgent());
|
|
1904
|
-
headers.set("anthropic-dangerous-direct-browser-access", "true");
|
|
1905
|
-
headers.set("x-app", "cli");
|
|
1906
|
-
headers.delete("x-api-key");
|
|
1907
|
-
return headers;
|
|
1908
|
-
}
|
|
1909
|
-
function transformRequestBody(body) {
|
|
1910
|
-
if (!body) return body;
|
|
1911
|
-
try {
|
|
1912
|
-
const parsed = JSON.parse(body);
|
|
1913
|
-
const toolMaskMap = buildToolMaskMap(parsed);
|
|
1914
|
-
if (ANTHROPIC_OAUTH_ADAPTER.transform.rewriteOpenCodeBranding) {
|
|
1915
|
-
const normalizedSystemEntries = normalizeSystemEntries(parsed.system);
|
|
1916
|
-
parsed.system = relocateSystemTextToFirstUser(parsed, normalizedSystemEntries);
|
|
1917
|
-
}
|
|
1918
|
-
if (parsed.tools && Array.isArray(parsed.tools)) {
|
|
1919
|
-
parsed.tools = parsed.tools.map((tool2) => ({
|
|
1920
|
-
...tool2,
|
|
1921
|
-
name: renameMaskedToolName(tool2.name, toolMaskMap)
|
|
1922
|
-
}));
|
|
1923
|
-
}
|
|
1924
|
-
parsed.messages = remapToolUseNames(parsed.messages, toolMaskMap);
|
|
1925
|
-
parsed.tool_choice = remapToolChoice(parsed.tool_choice, toolMaskMap);
|
|
1926
|
-
return JSON.stringify(parsed);
|
|
1927
|
-
} catch {
|
|
1928
|
-
return body;
|
|
1929
|
-
}
|
|
1930
1806
|
}
|
|
1931
|
-
function
|
|
1932
|
-
|
|
1933
|
-
|
|
1807
|
+
function scrubFrameworkIdentifiers(text) {
|
|
1808
|
+
let result = text;
|
|
1809
|
+
for (const pattern of FRAMEWORK_PATTERNS) {
|
|
1810
|
+
pattern.lastIndex = 0;
|
|
1811
|
+
result = result.replace(pattern, (match, ...args) => {
|
|
1812
|
+
const offset = args.at(-2);
|
|
1813
|
+
const source = args.at(-1);
|
|
1814
|
+
if (typeof offset !== "number" || typeof source !== "string") {
|
|
1815
|
+
return match;
|
|
1816
|
+
}
|
|
1817
|
+
const before = offset > 0 ? source[offset - 1] ?? "" : "";
|
|
1818
|
+
const after = offset + match.length < source.length ? source[offset + match.length] ?? "" : "";
|
|
1819
|
+
if (before === "." || before === "/" || before === "\\" || before === "-" || before === "_") {
|
|
1820
|
+
return match;
|
|
1821
|
+
}
|
|
1822
|
+
if (after === "/" || after === "\\") {
|
|
1823
|
+
return match;
|
|
1824
|
+
}
|
|
1825
|
+
return "";
|
|
1826
|
+
});
|
|
1934
1827
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1828
|
+
return result;
|
|
1829
|
+
}
|
|
1830
|
+
function computeBuildTag(userMessage, version) {
|
|
1831
|
+
const chars = [4, 7, 20].map((index) => userMessage[index] ?? "0").join("");
|
|
1832
|
+
return createHash2("sha256").update(`${BILLING_SEED}${chars}${version}`).digest("hex").slice(0, 3);
|
|
1833
|
+
}
|
|
1834
|
+
function buildUpstreamRequest(inputBody, identity, template, options) {
|
|
1835
|
+
const body = cloneBody(inputBody);
|
|
1836
|
+
const messages = Array.isArray(body.messages) ? body.messages : [];
|
|
1837
|
+
const systemTexts = normalizeSystemTexts(body.system);
|
|
1838
|
+
stripCacheControl(body);
|
|
1839
|
+
sanitizeMessages(body);
|
|
1840
|
+
stripAssistantThinkingBlocks(messages);
|
|
1841
|
+
for (const message of messages) {
|
|
1842
|
+
if (typeof message.content === "string") {
|
|
1843
|
+
message.content = sanitizeAndScrubText(message.content);
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (!Array.isArray(message.content)) {
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1849
|
+
for (const block of message.content) {
|
|
1850
|
+
sanitizeMessageBlock(block);
|
|
1851
|
+
}
|
|
1940
1852
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1853
|
+
trimTrailingEmptyTurns(messages);
|
|
1854
|
+
const firstUserMessage = extractFirstUserMessage(messages);
|
|
1855
|
+
const billingHeader = buildBillingHeader(firstUserMessage, template);
|
|
1856
|
+
const mergedSystemPrompt = [
|
|
1857
|
+
template.system_prompt,
|
|
1858
|
+
...filterInjectedSystemTexts(systemTexts, template, billingHeader)
|
|
1859
|
+
].map((entry) => sanitizeAndScrubText(entry)).filter(Boolean).join("\n\n");
|
|
1860
|
+
const activeSessionId = options?.sessionId ?? getActiveSessionId();
|
|
1861
|
+
body.messages = messages;
|
|
1862
|
+
const incomingTools = Array.isArray(body.tools) ? body.tools : [];
|
|
1863
|
+
body.tools = hasCompleteToolSchemas(template.tools) ? template.tools.map((tool2) => ({ ...tool2 })) : incomingTools;
|
|
1864
|
+
body.system = [
|
|
1865
|
+
{
|
|
1866
|
+
type: "text",
|
|
1867
|
+
text: billingHeader
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
type: "text",
|
|
1871
|
+
text: template.agent_identity,
|
|
1872
|
+
cache_control: { type: "ephemeral" }
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
type: "text",
|
|
1876
|
+
text: mergedSystemPrompt,
|
|
1877
|
+
cache_control: { type: "ephemeral" }
|
|
1878
|
+
}
|
|
1879
|
+
];
|
|
1880
|
+
body.metadata = {
|
|
1881
|
+
...isRecord(body.metadata) ? body.metadata : {},
|
|
1882
|
+
user_id: JSON.stringify({
|
|
1883
|
+
device_id: identity.deviceId,
|
|
1884
|
+
account_uuid: identity.accountUuid,
|
|
1885
|
+
session_id: activeSessionId
|
|
1886
|
+
})
|
|
1887
|
+
};
|
|
1888
|
+
body.thinking = { type: "adaptive" };
|
|
1889
|
+
body.context_management = DEFAULT_CONTEXT_MANAGEMENT;
|
|
1890
|
+
body.output_config = DEFAULT_OUTPUT_CONFIG;
|
|
1891
|
+
body.max_tokens = 64e3;
|
|
1892
|
+
return orderBodyForOutbound(body, template.body_field_order);
|
|
1893
|
+
}
|
|
1894
|
+
function orderBodyForOutbound(body, overrideOrder) {
|
|
1895
|
+
if (!Array.isArray(overrideOrder) || overrideOrder.length === 0) return body;
|
|
1896
|
+
const ordered = {};
|
|
1897
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1898
|
+
for (const name of overrideOrder) {
|
|
1899
|
+
if (seen.has(name)) continue;
|
|
1900
|
+
if (Object.prototype.hasOwnProperty.call(body, name)) {
|
|
1901
|
+
ordered[name] = body[name];
|
|
1902
|
+
seen.add(name);
|
|
1949
1903
|
}
|
|
1950
|
-
} catch {
|
|
1951
|
-
return input;
|
|
1952
1904
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
return input instanceof Request ? new Request(url.toString(), input) : url;
|
|
1905
|
+
for (const k of Object.keys(body)) {
|
|
1906
|
+
if (!seen.has(k)) ordered[k] = body[k];
|
|
1956
1907
|
}
|
|
1957
|
-
return
|
|
1908
|
+
return ordered;
|
|
1909
|
+
}
|
|
1910
|
+
function reverseMapResponse(response, reverseLookup) {
|
|
1911
|
+
return reverseMapToolUseNames(response, reverseLookup);
|
|
1958
1912
|
}
|
|
1959
|
-
function
|
|
1960
|
-
if (!response.body)
|
|
1913
|
+
function createStreamingReverseMapper(response, reverseLookup) {
|
|
1914
|
+
if (!response.body) {
|
|
1915
|
+
return response;
|
|
1916
|
+
}
|
|
1961
1917
|
const reader = response.body.getReader();
|
|
1962
1918
|
const decoder = new TextDecoder();
|
|
1963
1919
|
const encoder = new TextEncoder();
|
|
@@ -1970,24 +1926,26 @@ function createResponseStreamTransform(response, maskMap = /* @__PURE__ */ new M
|
|
|
1970
1926
|
if (done) {
|
|
1971
1927
|
buffer += decoder.decode();
|
|
1972
1928
|
if (buffer) {
|
|
1973
|
-
|
|
1929
|
+
const lines2 = buffer.split("\n").map((line) => remapSseLine(line, reverseLookup));
|
|
1930
|
+
controller.enqueue(encoder.encode(lines2.join("\n")));
|
|
1974
1931
|
buffer = "";
|
|
1975
1932
|
}
|
|
1976
1933
|
controller.close();
|
|
1977
1934
|
return;
|
|
1978
1935
|
}
|
|
1979
1936
|
buffer += decoder.decode(value, { stream: true });
|
|
1980
|
-
const
|
|
1981
|
-
buffer =
|
|
1982
|
-
if (
|
|
1983
|
-
|
|
1937
|
+
const lines = buffer.split("\n");
|
|
1938
|
+
buffer = lines.pop() ?? "";
|
|
1939
|
+
if (lines.length > 0) {
|
|
1940
|
+
const remapped = `${lines.map((line) => remapSseLine(line, reverseLookup)).join("\n")}
|
|
1941
|
+
`;
|
|
1942
|
+
controller.enqueue(encoder.encode(remapped));
|
|
1984
1943
|
return;
|
|
1985
1944
|
}
|
|
1986
1945
|
}
|
|
1987
1946
|
} catch (error) {
|
|
1988
1947
|
try {
|
|
1989
|
-
reader.cancel()
|
|
1990
|
-
});
|
|
1948
|
+
await reader.cancel();
|
|
1991
1949
|
} catch {
|
|
1992
1950
|
}
|
|
1993
1951
|
controller.error(error);
|
|
@@ -2004,26 +1962,142 @@ function createResponseStreamTransform(response, maskMap = /* @__PURE__ */ new M
|
|
|
2004
1962
|
});
|
|
2005
1963
|
}
|
|
2006
1964
|
|
|
2007
|
-
// src/
|
|
2008
|
-
import {
|
|
2009
|
-
var
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
1965
|
+
// src/upstream-headers.ts
|
|
1966
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
1967
|
+
var UPSTREAM_TIMEOUT_MS = 3e5;
|
|
1968
|
+
var STAINLESS_PACKAGE_VERSION = "0.81.0";
|
|
1969
|
+
var BILLABLE_BETA_PREFIXES = ["extended-cache-ttl-"];
|
|
1970
|
+
function getOsName() {
|
|
1971
|
+
const platform = process.platform;
|
|
1972
|
+
if (platform === "win32") return "Windows";
|
|
1973
|
+
if (platform === "darwin") return "MacOS";
|
|
1974
|
+
return "Linux";
|
|
1975
|
+
}
|
|
1976
|
+
function getStaticHeaders() {
|
|
1977
|
+
const profile = loadCCDerivedRequestProfile();
|
|
1978
|
+
const headers = {
|
|
1979
|
+
"accept": "application/json",
|
|
1980
|
+
"content-type": "application/json",
|
|
1981
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
1982
|
+
"user-agent": profile.userAgent,
|
|
1983
|
+
"x-app": profile.xApp,
|
|
1984
|
+
"x-stainless-arch": process.arch,
|
|
1985
|
+
"x-stainless-lang": "js",
|
|
1986
|
+
"x-stainless-os": getOsName(),
|
|
1987
|
+
"x-stainless-package-version": STAINLESS_PACKAGE_VERSION,
|
|
1988
|
+
"x-stainless-retry-count": "0",
|
|
1989
|
+
"x-stainless-runtime": "node",
|
|
1990
|
+
"x-stainless-runtime-version": process.version
|
|
1991
|
+
};
|
|
1992
|
+
const { template } = profile;
|
|
1993
|
+
if (template.header_values) {
|
|
1994
|
+
for (const [key, value] of Object.entries(template.header_values)) {
|
|
1995
|
+
headers[key] = value;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return headers;
|
|
1999
|
+
}
|
|
2000
|
+
function getPerRequestHeaders(sessionId2) {
|
|
2001
|
+
return {
|
|
2002
|
+
"x-claude-code-session-id": sessionId2,
|
|
2003
|
+
"x-client-request-id": randomUUID3(),
|
|
2004
|
+
"anthropic-version": getAnthropicVersion(),
|
|
2005
|
+
"x-stainless-timeout": String(UPSTREAM_TIMEOUT_MS / 1e3)
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
function getAnthropicVersion() {
|
|
2009
|
+
return loadCCDerivedRequestProfile().anthropicVersion;
|
|
2010
|
+
}
|
|
2011
|
+
function getBetaHeader() {
|
|
2012
|
+
return loadCCDerivedRequestProfile().betaHeader;
|
|
2013
|
+
}
|
|
2014
|
+
function orderHeadersForOutbound(headers, overrideHeaderOrder) {
|
|
2015
|
+
const { template } = loadCCDerivedRequestProfile();
|
|
2016
|
+
const order = overrideHeaderOrder ?? template.header_order;
|
|
2017
|
+
if (!Array.isArray(order) || order.length === 0) {
|
|
2018
|
+
return headers;
|
|
2019
|
+
}
|
|
2020
|
+
const lowerToValue = /* @__PURE__ */ new Map();
|
|
2021
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
2022
|
+
lowerToValue.set(key.toLowerCase(), value);
|
|
2023
|
+
}
|
|
2024
|
+
const ordered = [];
|
|
2025
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2026
|
+
for (const name of order) {
|
|
2027
|
+
const key = name.toLowerCase();
|
|
2028
|
+
if (seen.has(key)) {
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
const value = lowerToValue.get(key);
|
|
2032
|
+
if (value !== void 0) {
|
|
2033
|
+
ordered.push([name, value]);
|
|
2034
|
+
seen.add(key);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
2038
|
+
if (!seen.has(key.toLowerCase())) {
|
|
2039
|
+
ordered.push([key, value]);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return ordered;
|
|
2043
|
+
}
|
|
2044
|
+
function filterBillableBetas(betas) {
|
|
2045
|
+
return betas.split(",").map((beta) => beta.trim()).filter(
|
|
2046
|
+
(beta) => beta.length > 0 && !BILLABLE_BETA_PREFIXES.some((prefix) => beta.startsWith(prefix))
|
|
2047
|
+
).join(",");
|
|
2048
|
+
}
|
|
2016
2049
|
|
|
2017
|
-
// src/
|
|
2018
|
-
|
|
2050
|
+
// src/request-transform.ts
|
|
2051
|
+
function extractModelIdFromBody(body) {
|
|
2052
|
+
if (typeof body !== "string") {
|
|
2053
|
+
return "unknown";
|
|
2054
|
+
}
|
|
2055
|
+
try {
|
|
2056
|
+
const parsed = JSON.parse(body);
|
|
2057
|
+
return typeof parsed.model === "string" ? parsed.model : "unknown";
|
|
2058
|
+
} catch {
|
|
2059
|
+
return "unknown";
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function transformRequestUrl(input) {
|
|
2063
|
+
let url = null;
|
|
2064
|
+
try {
|
|
2065
|
+
if (typeof input === "string" || input instanceof URL) {
|
|
2066
|
+
url = new URL(input.toString());
|
|
2067
|
+
} else if (input instanceof Request) {
|
|
2068
|
+
url = new URL(input.url);
|
|
2069
|
+
}
|
|
2070
|
+
} catch {
|
|
2071
|
+
return input;
|
|
2072
|
+
}
|
|
2073
|
+
if (ANTHROPIC_OAUTH_ADAPTER.transform.enableMessagesBetaQuery && url && url.pathname === "/v1/messages" && !url.searchParams.has("beta")) {
|
|
2074
|
+
url.searchParams.set("beta", "true");
|
|
2075
|
+
return input instanceof Request ? new Request(url.toString(), input) : url;
|
|
2076
|
+
}
|
|
2077
|
+
return input;
|
|
2078
|
+
}
|
|
2079
|
+
function extractToolNamesFromRequestBody(body) {
|
|
2080
|
+
if (!body) {
|
|
2081
|
+
return [];
|
|
2082
|
+
}
|
|
2083
|
+
try {
|
|
2084
|
+
const parsed = JSON.parse(body);
|
|
2085
|
+
if (!Array.isArray(parsed.tools)) {
|
|
2086
|
+
return [];
|
|
2087
|
+
}
|
|
2088
|
+
return parsed.tools.map((tool2) => typeof tool2.name === "string" ? tool2.name : null).filter((toolName) => Boolean(toolName));
|
|
2089
|
+
} catch {
|
|
2090
|
+
return [];
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2019
2093
|
|
|
2020
2094
|
// src/tool-observation.ts
|
|
2021
2095
|
import { promises as fs } from "fs";
|
|
2022
|
-
import { dirname, join } from "path";
|
|
2096
|
+
import { dirname, join as join2 } from "path";
|
|
2023
2097
|
var OBSERVED_TOOL_FILE = "anthropic-observed-tools.json";
|
|
2024
2098
|
var FILE_MODE = 384;
|
|
2025
2099
|
function getObservedToolPath() {
|
|
2026
|
-
return
|
|
2100
|
+
return join2(getConfigDir(), OBSERVED_TOOL_FILE);
|
|
2027
2101
|
}
|
|
2028
2102
|
async function loadObservedToolInventory() {
|
|
2029
2103
|
try {
|
|
@@ -2047,34 +2121,194 @@ async function recordObservedToolNames(toolNames) {
|
|
|
2047
2121
|
return;
|
|
2048
2122
|
}
|
|
2049
2123
|
const inventory = await loadObservedToolInventory();
|
|
2050
|
-
const
|
|
2124
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
2051
2125
|
for (const toolName of toolNames) {
|
|
2052
2126
|
const entry = inventory.observedTools[toolName];
|
|
2053
2127
|
if (!entry) {
|
|
2054
2128
|
inventory.observedTools[toolName] = {
|
|
2055
2129
|
count: 1,
|
|
2056
|
-
firstSeenAt:
|
|
2057
|
-
lastSeenAt:
|
|
2130
|
+
firstSeenAt: now2,
|
|
2131
|
+
lastSeenAt: now2
|
|
2058
2132
|
};
|
|
2059
2133
|
continue;
|
|
2060
2134
|
}
|
|
2061
2135
|
entry.count += 1;
|
|
2062
|
-
entry.lastSeenAt =
|
|
2136
|
+
entry.lastSeenAt = now2;
|
|
2063
2137
|
}
|
|
2064
2138
|
await saveObservedToolInventory(inventory);
|
|
2065
2139
|
}
|
|
2066
2140
|
|
|
2141
|
+
// src/error-utils.ts
|
|
2142
|
+
function sanitizeError(err) {
|
|
2143
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2144
|
+
return msg.replace(/sk-ant-[a-zA-Z0-9_-]+/g, "[REDACTED]").replace(/eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g, "[REDACTED_JWT]").replace(/Bearer\s+[^\s,;]+/gi, "Bearer [REDACTED]");
|
|
2145
|
+
}
|
|
2146
|
+
function enrich429(body, headers) {
|
|
2147
|
+
try {
|
|
2148
|
+
const parsed = JSON.parse(body);
|
|
2149
|
+
const error = parsed.error;
|
|
2150
|
+
if (error && (error.message === "Error" || !error.message)) {
|
|
2151
|
+
const claim = headers.get("anthropic-ratelimit-unified-representative-claim") || "unknown";
|
|
2152
|
+
const status = headers.get("anthropic-ratelimit-unified-status") || "rejected";
|
|
2153
|
+
const util5h = headers.get("anthropic-ratelimit-unified-5h-utilization");
|
|
2154
|
+
const util7d = headers.get("anthropic-ratelimit-unified-7d-utilization");
|
|
2155
|
+
const reset = headers.get("anthropic-ratelimit-unified-reset");
|
|
2156
|
+
const parts = [`Rate limited (${status}). Limiting window: ${claim}`];
|
|
2157
|
+
if (util5h) {
|
|
2158
|
+
const parsedUtil5h = Number.parseFloat(util5h);
|
|
2159
|
+
if (Number.isFinite(parsedUtil5h)) {
|
|
2160
|
+
parts.push(`5h utilization: ${Math.round(parsedUtil5h * 100)}%`);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
if (util7d) {
|
|
2164
|
+
const parsedUtil7d = Number.parseFloat(util7d);
|
|
2165
|
+
if (Number.isFinite(parsedUtil7d)) {
|
|
2166
|
+
parts.push(`7d utilization: ${Math.round(parsedUtil7d * 100)}%`);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
if (reset) {
|
|
2170
|
+
const parsedReset = Number.parseInt(reset, 10);
|
|
2171
|
+
if (Number.isFinite(parsedReset)) {
|
|
2172
|
+
const resetDate = new Date(parsedReset * 1e3);
|
|
2173
|
+
const minutesUntilReset = Math.max(0, Math.round((resetDate.getTime() - Date.now()) / 6e4));
|
|
2174
|
+
parts.push(`resets in ${minutesUntilReset}m`);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
error.message = parts.join(". ");
|
|
2178
|
+
}
|
|
2179
|
+
return JSON.stringify(parsed);
|
|
2180
|
+
} catch {
|
|
2181
|
+
return body;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// src/pacing.ts
|
|
2186
|
+
function pickNonNegativeInt(...values) {
|
|
2187
|
+
for (const v4 of values) {
|
|
2188
|
+
if (v4 === void 0 || v4 === null) continue;
|
|
2189
|
+
const n = typeof v4 === "number" ? v4 : Number.parseInt(v4, 10);
|
|
2190
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
2191
|
+
}
|
|
2192
|
+
return void 0;
|
|
2193
|
+
}
|
|
2194
|
+
function computePacingDelay(now2, lastRequestTime, cfg, rng = Math.random) {
|
|
2195
|
+
if (lastRequestTime <= 0) return 0;
|
|
2196
|
+
const minGap = Math.max(0, cfg.minGapMs);
|
|
2197
|
+
const jitter = Math.max(0, cfg.jitterMs);
|
|
2198
|
+
const jitterAdd = jitter > 0 ? Math.floor(rng() * jitter) : 0;
|
|
2199
|
+
const effectiveGap = minGap + jitterAdd;
|
|
2200
|
+
const elapsed = now2 - lastRequestTime;
|
|
2201
|
+
if (elapsed >= effectiveGap) return 0;
|
|
2202
|
+
return effectiveGap - elapsed;
|
|
2203
|
+
}
|
|
2204
|
+
function resolvePacingConfig(explicit = {}, env = process.env) {
|
|
2205
|
+
const minGap = pickNonNegativeInt(explicit.minGapMs, env.ANTHROPIC_PACE_MIN_MS, env.MIN_REQUEST_INTERVAL_MS) ?? 500;
|
|
2206
|
+
const jitter = pickNonNegativeInt(explicit.jitterMs, env.ANTHROPIC_PACE_JITTER_MS) ?? 0;
|
|
2207
|
+
return { minGapMs: minGap, jitterMs: jitter };
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2067
2210
|
// src/runtime-factory.ts
|
|
2068
2211
|
var TOKEN_REFRESH_PERMANENT_FAILURE_STATUS = 401;
|
|
2212
|
+
function mergeHeaders(target, headers) {
|
|
2213
|
+
if (!headers) {
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
if (headers instanceof Headers) {
|
|
2217
|
+
headers.forEach((value, key) => {
|
|
2218
|
+
target[key.toLowerCase()] = value;
|
|
2219
|
+
});
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
if (Array.isArray(headers)) {
|
|
2223
|
+
for (const [key, value] of headers) {
|
|
2224
|
+
target[String(key).toLowerCase()] = String(value);
|
|
2225
|
+
}
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
2229
|
+
if (value !== void 0) {
|
|
2230
|
+
target[key.toLowerCase()] = String(value);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function extractIncomingHeaders(input, init) {
|
|
2235
|
+
const headers = {};
|
|
2236
|
+
if (input instanceof Request) {
|
|
2237
|
+
mergeHeaders(headers, input.headers);
|
|
2238
|
+
}
|
|
2239
|
+
mergeHeaders(headers, init?.headers);
|
|
2240
|
+
return headers;
|
|
2241
|
+
}
|
|
2242
|
+
function splitBetaValues(value) {
|
|
2243
|
+
if (!value) {
|
|
2244
|
+
return [];
|
|
2245
|
+
}
|
|
2246
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
2247
|
+
}
|
|
2248
|
+
function deduplicateBetas(values) {
|
|
2249
|
+
return [...new Set(values.filter(Boolean))];
|
|
2250
|
+
}
|
|
2251
|
+
function excludeBetas(values, excludedBetas2) {
|
|
2252
|
+
if (excludedBetas2.size === 0) {
|
|
2253
|
+
return values;
|
|
2254
|
+
}
|
|
2255
|
+
return values.filter((beta) => !excludedBetas2.has(beta));
|
|
2256
|
+
}
|
|
2257
|
+
function transformBodyToUpstream(body, identity, sessionId2) {
|
|
2258
|
+
try {
|
|
2259
|
+
const parsed = JSON.parse(body);
|
|
2260
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2261
|
+
return body;
|
|
2262
|
+
}
|
|
2263
|
+
return JSON.stringify(
|
|
2264
|
+
buildUpstreamRequest(
|
|
2265
|
+
parsed,
|
|
2266
|
+
identity,
|
|
2267
|
+
loadTemplate(),
|
|
2268
|
+
{ sessionId: sessionId2 }
|
|
2269
|
+
)
|
|
2270
|
+
);
|
|
2271
|
+
} catch {
|
|
2272
|
+
return body;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
async function enrichRateLimitResponse(response) {
|
|
2276
|
+
if (response.status !== 429) {
|
|
2277
|
+
return response;
|
|
2278
|
+
}
|
|
2279
|
+
const body = await response.clone().text();
|
|
2280
|
+
const enrichedBody = enrich429(body, response.headers);
|
|
2281
|
+
if (enrichedBody === body) {
|
|
2282
|
+
return response;
|
|
2283
|
+
}
|
|
2284
|
+
return new Response(enrichedBody, {
|
|
2285
|
+
status: response.status,
|
|
2286
|
+
statusText: response.statusText,
|
|
2287
|
+
headers: new Headers(response.headers)
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2069
2290
|
var AccountRuntimeFactory = class {
|
|
2070
|
-
constructor(store, client) {
|
|
2291
|
+
constructor(store, client, identity = loadClaudeIdentity()) {
|
|
2071
2292
|
this.store = store;
|
|
2072
2293
|
this.client = client;
|
|
2294
|
+
this.identity = identity;
|
|
2073
2295
|
}
|
|
2074
2296
|
store;
|
|
2075
2297
|
client;
|
|
2298
|
+
identity;
|
|
2076
2299
|
runtimes = /* @__PURE__ */ new Map();
|
|
2077
2300
|
initLocks = /* @__PURE__ */ new Map();
|
|
2301
|
+
lastRequestTime = 0;
|
|
2302
|
+
pacingGate = Promise.resolve();
|
|
2303
|
+
pacingTestOverrides = {};
|
|
2304
|
+
setPacingTestOverrides(overrides) {
|
|
2305
|
+
this.pacingTestOverrides = overrides;
|
|
2306
|
+
}
|
|
2307
|
+
resetPacingForTest() {
|
|
2308
|
+
this.lastRequestTime = 0;
|
|
2309
|
+
this.pacingGate = Promise.resolve();
|
|
2310
|
+
this.pacingTestOverrides = {};
|
|
2311
|
+
}
|
|
2078
2312
|
async getRuntime(uuid) {
|
|
2079
2313
|
const cached = this.runtimes.get(uuid);
|
|
2080
2314
|
if (cached) return cached;
|
|
@@ -2126,22 +2360,77 @@ var AccountRuntimeFactory = class {
|
|
|
2126
2360
|
});
|
|
2127
2361
|
return { accessToken: refreshed.patch.accessToken, expiresAt: refreshed.patch.expiresAt };
|
|
2128
2362
|
}
|
|
2363
|
+
buildOutboundHeaders(incomingHeaders, sessionId2, accessToken, modelId, excludedBetas2) {
|
|
2364
|
+
const mergedBetas = deduplicateBetas([
|
|
2365
|
+
...excludeBetas(splitBetaValues(getBetaHeader()), excludedBetas2),
|
|
2366
|
+
...getModelBetas(modelId, excludedBetas2),
|
|
2367
|
+
...excludeBetas(splitBetaValues(incomingHeaders["anthropic-beta"]), excludedBetas2)
|
|
2368
|
+
]).join(",");
|
|
2369
|
+
const outbound = {
|
|
2370
|
+
...incomingHeaders,
|
|
2371
|
+
...getStaticHeaders(),
|
|
2372
|
+
...getPerRequestHeaders(sessionId2),
|
|
2373
|
+
"authorization": `Bearer ${accessToken}`,
|
|
2374
|
+
"anthropic-beta": filterBillableBetas(mergedBetas)
|
|
2375
|
+
};
|
|
2376
|
+
delete outbound["x-api-key"];
|
|
2377
|
+
return orderHeadersForOutbound(outbound);
|
|
2378
|
+
}
|
|
2129
2379
|
async executeTransformedFetch(input, init, accessToken) {
|
|
2130
2380
|
const transformedInput = transformRequestUrl(input);
|
|
2131
2381
|
const modelId = extractModelIdFromBody(init?.body);
|
|
2132
2382
|
const excludedBetas2 = getExcludedBetas(modelId);
|
|
2133
|
-
const
|
|
2383
|
+
const incomingHeaders = extractIncomingHeaders(transformedInput, init);
|
|
2384
|
+
const sessionId2 = incomingHeaders["x-claude-code-session-id"] ?? getUpstreamSessionId();
|
|
2385
|
+
const headers = this.buildOutboundHeaders(
|
|
2386
|
+
incomingHeaders,
|
|
2387
|
+
sessionId2,
|
|
2388
|
+
accessToken,
|
|
2389
|
+
modelId,
|
|
2390
|
+
excludedBetas2
|
|
2391
|
+
);
|
|
2134
2392
|
if (typeof init?.body === "string") {
|
|
2135
2393
|
void recordObservedToolNames(extractToolNamesFromRequestBody(init.body)).catch(() => {
|
|
2136
2394
|
});
|
|
2137
2395
|
}
|
|
2138
|
-
const
|
|
2139
|
-
const
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2396
|
+
const transformedBody = typeof init?.body === "string" ? transformBodyToUpstream(init.body, this.identity, sessionId2) : init?.body;
|
|
2397
|
+
const pacingCfg = resolvePacingConfig();
|
|
2398
|
+
const getNow = this.pacingTestOverrides.now ?? Date.now;
|
|
2399
|
+
const sleepFn = this.pacingTestOverrides.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
2400
|
+
const reservePacingSlot = async () => {
|
|
2401
|
+
let releaseGate;
|
|
2402
|
+
const previousGate = this.pacingGate;
|
|
2403
|
+
this.pacingGate = new Promise((resolve) => {
|
|
2404
|
+
releaseGate = resolve;
|
|
2405
|
+
});
|
|
2406
|
+
await previousGate;
|
|
2407
|
+
try {
|
|
2408
|
+
const delay = computePacingDelay(getNow(), this.lastRequestTime, pacingCfg);
|
|
2409
|
+
if (delay > 0) {
|
|
2410
|
+
await sleepFn(delay);
|
|
2411
|
+
}
|
|
2412
|
+
this.lastRequestTime = getNow();
|
|
2413
|
+
} finally {
|
|
2414
|
+
releaseGate?.();
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
const performFetch = async (requestHeaders) => {
|
|
2418
|
+
await reservePacingSlot();
|
|
2419
|
+
try {
|
|
2420
|
+
const response2 = await fetch(transformedInput, {
|
|
2421
|
+
...init,
|
|
2422
|
+
headers: requestHeaders,
|
|
2423
|
+
body: transformedBody
|
|
2424
|
+
});
|
|
2425
|
+
return await enrichRateLimitResponse(response2);
|
|
2426
|
+
} catch (error) {
|
|
2427
|
+
debugLog(this.client, "Anthropic upstream fetch failed", {
|
|
2428
|
+
error: sanitizeError(error)
|
|
2429
|
+
});
|
|
2430
|
+
throw error;
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
let response = await performFetch(headers);
|
|
2145
2434
|
for (let attempt = 0; attempt < LONG_CONTEXT_BETAS.length; attempt += 1) {
|
|
2146
2435
|
if (response.status !== 400 && response.status !== 429) {
|
|
2147
2436
|
break;
|
|
@@ -2155,20 +2444,16 @@ var AccountRuntimeFactory = class {
|
|
|
2155
2444
|
break;
|
|
2156
2445
|
}
|
|
2157
2446
|
addExcludedBeta(modelId, betaToExclude);
|
|
2158
|
-
const retryHeaders =
|
|
2159
|
-
|
|
2160
|
-
|
|
2447
|
+
const retryHeaders = this.buildOutboundHeaders(
|
|
2448
|
+
incomingHeaders,
|
|
2449
|
+
sessionId2,
|
|
2161
2450
|
accessToken,
|
|
2162
2451
|
modelId,
|
|
2163
2452
|
getExcludedBetas(modelId)
|
|
2164
2453
|
);
|
|
2165
|
-
response = await
|
|
2166
|
-
...init,
|
|
2167
|
-
headers: retryHeaders,
|
|
2168
|
-
body: transformedBody
|
|
2169
|
-
});
|
|
2454
|
+
response = await performFetch(retryHeaders);
|
|
2170
2455
|
}
|
|
2171
|
-
return
|
|
2456
|
+
return createStreamingReverseMapper(response);
|
|
2172
2457
|
}
|
|
2173
2458
|
async createRuntime(uuid) {
|
|
2174
2459
|
const fetchWithAccount = async (input, init) => {
|
|
@@ -2194,7 +2479,7 @@ var AccountRuntimeFactory = class {
|
|
|
2194
2479
|
|
|
2195
2480
|
// src/bootstrap-auth.ts
|
|
2196
2481
|
import { promises as fs2 } from "fs";
|
|
2197
|
-
import { join as
|
|
2482
|
+
import { join as join3 } from "path";
|
|
2198
2483
|
import { getConfigDir as getConfigDir2 } from "opencode-multi-account-core";
|
|
2199
2484
|
var AUTH_JSON_FILENAME = "auth.json";
|
|
2200
2485
|
function hasCompleteOAuthCredential(account) {
|
|
@@ -2215,7 +2500,7 @@ function selectBootstrapAccount(accounts, activeAccountUuid) {
|
|
|
2215
2500
|
return firstUsableAccount ?? completeAccounts[0] ?? null;
|
|
2216
2501
|
}
|
|
2217
2502
|
async function readCurrentAuth(providerId) {
|
|
2218
|
-
const authPath =
|
|
2503
|
+
const authPath = join3(getConfigDir2(), AUTH_JSON_FILENAME);
|
|
2219
2504
|
let raw;
|
|
2220
2505
|
try {
|
|
2221
2506
|
raw = await fs2.readFile(authPath, "utf-8");
|
|
@@ -2270,6 +2555,70 @@ async function syncBootstrapAuth(client, store) {
|
|
|
2270
2555
|
return true;
|
|
2271
2556
|
}
|
|
2272
2557
|
|
|
2558
|
+
// src/session-heartbeat.ts
|
|
2559
|
+
var DEFAULT_HEARTBEAT_INTERVAL_MS = 3e4;
|
|
2560
|
+
var CLIENT_PLATFORM = "cli";
|
|
2561
|
+
var testOverrides = {};
|
|
2562
|
+
function presenceUrl(sessionId2) {
|
|
2563
|
+
return `${loadCCDerivedRequestProfile().baseApiUrl}/v1/code/sessions/${sessionId2}/client/presence`;
|
|
2564
|
+
}
|
|
2565
|
+
function fetchFn() {
|
|
2566
|
+
return testOverrides.fetch ?? globalThis.fetch;
|
|
2567
|
+
}
|
|
2568
|
+
function startHeartbeat(options) {
|
|
2569
|
+
testOverrides.onStart?.(options);
|
|
2570
|
+
const {
|
|
2571
|
+
sessionId: sessionId2,
|
|
2572
|
+
deviceId,
|
|
2573
|
+
accessToken,
|
|
2574
|
+
intervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS
|
|
2575
|
+
} = options;
|
|
2576
|
+
let activeController = null;
|
|
2577
|
+
let stopped = false;
|
|
2578
|
+
const sendPresence = async () => {
|
|
2579
|
+
if (stopped) return;
|
|
2580
|
+
const controller = new AbortController();
|
|
2581
|
+
activeController = controller;
|
|
2582
|
+
try {
|
|
2583
|
+
await fetchFn()(presenceUrl(sessionId2), {
|
|
2584
|
+
method: "POST",
|
|
2585
|
+
headers: {
|
|
2586
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2587
|
+
"anthropic-version": getAnthropicVersion(),
|
|
2588
|
+
"anthropic-client-platform": CLIENT_PLATFORM,
|
|
2589
|
+
"Content-Type": "application/json"
|
|
2590
|
+
},
|
|
2591
|
+
body: JSON.stringify({
|
|
2592
|
+
client_id: deviceId,
|
|
2593
|
+
connected_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2594
|
+
}),
|
|
2595
|
+
signal: controller.signal
|
|
2596
|
+
});
|
|
2597
|
+
} catch {
|
|
2598
|
+
} finally {
|
|
2599
|
+
if (activeController === controller) {
|
|
2600
|
+
activeController = null;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
};
|
|
2604
|
+
const timer = setInterval(sendPresence, intervalMs);
|
|
2605
|
+
if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") {
|
|
2606
|
+
timer.unref();
|
|
2607
|
+
}
|
|
2608
|
+
return {
|
|
2609
|
+
stop() {
|
|
2610
|
+
if (stopped) return;
|
|
2611
|
+
stopped = true;
|
|
2612
|
+
clearInterval(timer);
|
|
2613
|
+
activeController?.abort();
|
|
2614
|
+
activeController = null;
|
|
2615
|
+
}
|
|
2616
|
+
};
|
|
2617
|
+
}
|
|
2618
|
+
function getSessionId() {
|
|
2619
|
+
return getUpstreamSessionId();
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2273
2622
|
// src/index.ts
|
|
2274
2623
|
var EMPTY_OAUTH_CREDENTIALS = {
|
|
2275
2624
|
type: "oauth",
|
|
@@ -2277,7 +2626,7 @@ var EMPTY_OAUTH_CREDENTIALS = {
|
|
|
2277
2626
|
access: "",
|
|
2278
2627
|
expires: 0
|
|
2279
2628
|
};
|
|
2280
|
-
function
|
|
2629
|
+
function extractFirstUserText(input) {
|
|
2281
2630
|
try {
|
|
2282
2631
|
const raw = input;
|
|
2283
2632
|
const messages = raw.messages ?? raw.request?.messages;
|
|
@@ -2295,19 +2644,118 @@ function extractFirstUserText2(input) {
|
|
|
2295
2644
|
}
|
|
2296
2645
|
return "";
|
|
2297
2646
|
}
|
|
2298
|
-
function
|
|
2299
|
-
const
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
output.system.
|
|
2647
|
+
function composeBillingSystemEntry(firstUserMessage, version) {
|
|
2648
|
+
const buildTag = computeBuildTag(firstUserMessage, version);
|
|
2649
|
+
return `x-anthropic-billing-header: cc_version=${version}.${buildTag}; cc_entrypoint=cli; cch=00000;`;
|
|
2650
|
+
}
|
|
2651
|
+
function prependMissingSystemEntries(output, entries) {
|
|
2652
|
+
output.system ??= [];
|
|
2653
|
+
for (const entry of entries.toReversed()) {
|
|
2654
|
+
if (entry && !output.system.includes(entry)) {
|
|
2655
|
+
output.system.unshift(entry);
|
|
2656
|
+
}
|
|
2306
2657
|
}
|
|
2307
2658
|
}
|
|
2659
|
+
function applyOrderedHeaders(output, headers) {
|
|
2660
|
+
const orderedHeaders = orderHeadersForOutbound(headers);
|
|
2661
|
+
output.headers = Array.isArray(orderedHeaders) ? Object.fromEntries(orderedHeaders) : orderedHeaders;
|
|
2662
|
+
}
|
|
2308
2663
|
var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
2309
2664
|
const { client } = ctx;
|
|
2310
2665
|
await loadConfig();
|
|
2666
|
+
const requestProfile = loadCCDerivedRequestProfile();
|
|
2667
|
+
const template = requestProfile.template;
|
|
2668
|
+
const claudeIdentity = loadClaudeIdentity();
|
|
2669
|
+
const claudeCodeVersion = template.cc_version ?? requestProfile.cliVersion;
|
|
2670
|
+
const upstreamAgentIdentity = template.agent_identity;
|
|
2671
|
+
const upstreamSystemPrompt = template.system_prompt;
|
|
2672
|
+
let heartbeatHandle = null;
|
|
2673
|
+
let heartbeatToken = null;
|
|
2674
|
+
let heartbeatSessionId = null;
|
|
2675
|
+
const stopHeartbeat = () => {
|
|
2676
|
+
heartbeatHandle?.stop();
|
|
2677
|
+
heartbeatHandle = null;
|
|
2678
|
+
heartbeatToken = null;
|
|
2679
|
+
heartbeatSessionId = null;
|
|
2680
|
+
};
|
|
2681
|
+
const ensureHeartbeat = (accessToken) => {
|
|
2682
|
+
if (!accessToken || !claudeIdentity.deviceId) {
|
|
2683
|
+
stopHeartbeat();
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2686
|
+
const sessionId2 = getSessionId();
|
|
2687
|
+
if (heartbeatHandle && heartbeatToken === accessToken && heartbeatSessionId === sessionId2) {
|
|
2688
|
+
return;
|
|
2689
|
+
}
|
|
2690
|
+
stopHeartbeat();
|
|
2691
|
+
heartbeatToken = accessToken;
|
|
2692
|
+
heartbeatSessionId = sessionId2;
|
|
2693
|
+
heartbeatHandle = startHeartbeat({
|
|
2694
|
+
sessionId: sessionId2,
|
|
2695
|
+
deviceId: claudeIdentity.deviceId,
|
|
2696
|
+
accessToken
|
|
2697
|
+
});
|
|
2698
|
+
};
|
|
2699
|
+
const startupDrift = detectDrift(template);
|
|
2700
|
+
if (startupDrift.drifted) {
|
|
2701
|
+
client.app.log({
|
|
2702
|
+
body: {
|
|
2703
|
+
service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
|
|
2704
|
+
level: "warn",
|
|
2705
|
+
message: startupDrift.message,
|
|
2706
|
+
extra: {
|
|
2707
|
+
cachedVersion: startupDrift.cachedVersion,
|
|
2708
|
+
installedVersion: startupDrift.installedVersion
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
}).catch(() => {
|
|
2712
|
+
});
|
|
2713
|
+
}
|
|
2714
|
+
const compat = checkCCCompat();
|
|
2715
|
+
if (compat.status !== "ok" && compat.status !== "unknown") {
|
|
2716
|
+
client.app.log({
|
|
2717
|
+
body: {
|
|
2718
|
+
service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
|
|
2719
|
+
level: "warn",
|
|
2720
|
+
message: compat.message,
|
|
2721
|
+
extra: {
|
|
2722
|
+
installedVersion: compat.installedVersion,
|
|
2723
|
+
range: compat.range
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}).catch(() => {
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
void refreshLiveFingerprintAsync({ silent: true }).then((refreshedTemplate) => {
|
|
2730
|
+
const refreshedDrift = detectDrift(refreshedTemplate ?? template);
|
|
2731
|
+
if (!refreshedDrift.drifted) {
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
return client.app.log({
|
|
2735
|
+
body: {
|
|
2736
|
+
service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
|
|
2737
|
+
level: "warn",
|
|
2738
|
+
message: refreshedDrift.message,
|
|
2739
|
+
extra: {
|
|
2740
|
+
cachedVersion: refreshedDrift.cachedVersion,
|
|
2741
|
+
installedVersion: refreshedDrift.installedVersion
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
}).catch(() => {
|
|
2745
|
+
});
|
|
2746
|
+
}).catch((error) => {
|
|
2747
|
+
client.app.log({
|
|
2748
|
+
body: {
|
|
2749
|
+
service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
|
|
2750
|
+
level: "debug",
|
|
2751
|
+
message: "live fingerprint refresh failed",
|
|
2752
|
+
extra: {
|
|
2753
|
+
error: sanitizeError(error)
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}).catch(() => {
|
|
2757
|
+
});
|
|
2758
|
+
});
|
|
2311
2759
|
const store = new AccountStore();
|
|
2312
2760
|
await syncBootstrapAuth(client, store).catch(() => {
|
|
2313
2761
|
});
|
|
@@ -2318,7 +2766,7 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2318
2766
|
let cascadeStateManager = null;
|
|
2319
2767
|
let poolChainConfig = { pools: [], chains: [] };
|
|
2320
2768
|
async function ensureExecutionInfrastructure() {
|
|
2321
|
-
runtimeFactory ??= new AccountRuntimeFactory(store, client);
|
|
2769
|
+
runtimeFactory ??= new AccountRuntimeFactory(store, client, claudeIdentity);
|
|
2322
2770
|
poolChainConfig = await loadPoolChainConfig();
|
|
2323
2771
|
poolManager ??= new PoolManager();
|
|
2324
2772
|
poolManager.loadPools(poolChainConfig.pools);
|
|
@@ -2357,6 +2805,7 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2357
2805
|
manager = await AccountManager.create(store, EMPTY_OAUTH_CREDENTIALS, client);
|
|
2358
2806
|
await ensureExecutionInfrastructure();
|
|
2359
2807
|
await startRefreshQueueIfNeeded();
|
|
2808
|
+
ensureHeartbeat(manager.getActiveAccount()?.accessToken);
|
|
2360
2809
|
return manager.getAccountCount() > 0;
|
|
2361
2810
|
}
|
|
2362
2811
|
async function initializeManagerFromAuth(credentials) {
|
|
@@ -2370,11 +2819,12 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2370
2819
|
});
|
|
2371
2820
|
return {
|
|
2372
2821
|
"experimental.chat.system.transform": (input, output) => {
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2822
|
+
const billingHeader = composeBillingSystemEntry(extractFirstUserText(input), claudeCodeVersion);
|
|
2823
|
+
prependMissingSystemEntries(output, [
|
|
2824
|
+
billingHeader,
|
|
2825
|
+
upstreamAgentIdentity,
|
|
2826
|
+
upstreamSystemPrompt
|
|
2827
|
+
]);
|
|
2378
2828
|
},
|
|
2379
2829
|
tool: {
|
|
2380
2830
|
[ANTHROPIC_OAUTH_ADAPTER.statusToolName]: tool({
|
|
@@ -2412,10 +2862,10 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2412
2862
|
}
|
|
2413
2863
|
}
|
|
2414
2864
|
if (account.cachedUsage) {
|
|
2415
|
-
const
|
|
2865
|
+
const now2 = Date.now();
|
|
2416
2866
|
const usage2 = account.cachedUsage;
|
|
2417
2867
|
const exhaustedTiers = [usage2.five_hour, usage2.seven_day].filter(
|
|
2418
|
-
(tier) => tier && tier.utilization >= 100 && tier.resets_at != null && Date.parse(tier.resets_at) >
|
|
2868
|
+
(tier) => tier && tier.utilization >= 100 && tier.resets_at != null && Date.parse(tier.resets_at) > now2
|
|
2419
2869
|
);
|
|
2420
2870
|
if (exhaustedTiers.length > 0) {
|
|
2421
2871
|
statusParts.push("USAGE EXHAUSTED");
|
|
@@ -2446,6 +2896,7 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2446
2896
|
async loader(getAuth, provider) {
|
|
2447
2897
|
const auth = await getAuth();
|
|
2448
2898
|
if (auth.type !== "oauth") {
|
|
2899
|
+
stopHeartbeat();
|
|
2449
2900
|
return { apiKey: "", fetch };
|
|
2450
2901
|
}
|
|
2451
2902
|
for (const model of Object.values(provider.models ?? {})) {
|
|
@@ -2456,6 +2907,7 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2456
2907
|
const credentials = auth;
|
|
2457
2908
|
await migrateFromAuthJson("anthropic", store);
|
|
2458
2909
|
await initializeManagerFromAuth(credentials);
|
|
2910
|
+
ensureHeartbeat(credentials.access);
|
|
2459
2911
|
const initializedManager = manager;
|
|
2460
2912
|
if (!initializedManager) {
|
|
2461
2913
|
return { apiKey: "", fetch };
|
|
@@ -2478,24 +2930,32 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
|
|
|
2478
2930
|
);
|
|
2479
2931
|
}
|
|
2480
2932
|
}
|
|
2933
|
+
const authProfile = await loadCCDerivedAuthProfile();
|
|
2481
2934
|
return {
|
|
2482
2935
|
apiKey: "",
|
|
2483
|
-
baseURL:
|
|
2936
|
+
baseURL: authProfile.apiV1BaseUrl,
|
|
2484
2937
|
"chat.headers": async (input, output) => {
|
|
2485
2938
|
if (input.provider?.info?.id !== ANTHROPIC_OAUTH_ADAPTER.authProviderId) return;
|
|
2486
|
-
|
|
2487
|
-
output
|
|
2488
|
-
|
|
2939
|
+
const sessionId2 = getUpstreamSessionId();
|
|
2940
|
+
applyOrderedHeaders(output, {
|
|
2941
|
+
...output.headers,
|
|
2942
|
+
...getStaticHeaders(),
|
|
2943
|
+
...getPerRequestHeaders(sessionId2),
|
|
2944
|
+
"anthropic-beta": getBetaHeader()
|
|
2945
|
+
});
|
|
2489
2946
|
},
|
|
2490
2947
|
async fetch(input, init) {
|
|
2491
2948
|
if (!initializedManager || !runtimeFactory) {
|
|
2949
|
+
stopHeartbeat();
|
|
2492
2950
|
return fetch(input, init);
|
|
2493
2951
|
}
|
|
2494
2952
|
if (initializedManager.getAccountCount() === 0) {
|
|
2953
|
+
stopHeartbeat();
|
|
2495
2954
|
throw new Error(
|
|
2496
2955
|
"No Anthropic accounts configured. Run `opencode auth login` to add an account."
|
|
2497
2956
|
);
|
|
2498
2957
|
}
|
|
2958
|
+
ensureHeartbeat(initializedManager.getActiveAccount()?.accessToken);
|
|
2499
2959
|
if (!poolManager || !cascadeStateManager) {
|
|
2500
2960
|
poolManager = new PoolManager();
|
|
2501
2961
|
poolManager.loadPools(poolChainConfig.pools);
|