@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
package/src/headers/billing.ts
CHANGED
|
@@ -3,42 +3,42 @@ import { CCH_PLACEHOLDER } from "./cch.js";
|
|
|
3
3
|
import { isFalsyEnv } from "../env.js";
|
|
4
4
|
|
|
5
5
|
export function buildAnthropicBillingHeader(claudeCliVersion: string, messages: unknown[]): string {
|
|
6
|
-
|
|
6
|
+
if (isFalsyEnv(process.env.CLAUDE_CODE_ATTRIBUTION_HEADER)) return "";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
// CC derives the 3-char cc_version suffix from the first user message using
|
|
9
|
+
// SHA-256 with salt "59cf53e54c78" and positions [4,7,20]. The cch field is
|
|
10
|
+
// emitted here as the literal placeholder "00000" and replaced later, after
|
|
11
|
+
// full-body serialization, by replaceNativeStyleCch() in src/headers/cch.ts.
|
|
12
|
+
let versionSuffix = "";
|
|
13
|
+
if (Array.isArray(messages)) {
|
|
14
|
+
// Find first user message (CC uses first non-meta user turn)
|
|
15
|
+
const firstUserMsg = messages.find(
|
|
16
|
+
(m) => m !== null && typeof m === "object" && (m as Record<string, unknown>).role === "user",
|
|
17
|
+
) as Record<string, unknown> | undefined;
|
|
18
|
+
if (firstUserMsg) {
|
|
19
|
+
// Extract text from string or content-block array
|
|
20
|
+
let text = "";
|
|
21
|
+
const content = firstUserMsg.content;
|
|
22
|
+
if (typeof content === "string") {
|
|
23
|
+
text = content;
|
|
24
|
+
} else if (Array.isArray(content)) {
|
|
25
|
+
const textBlock = (content as Array<Record<string, unknown>>).find((b) => b.type === "text");
|
|
26
|
+
if (textBlock && typeof textBlock.text === "string") {
|
|
27
|
+
text = textBlock.text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (text) {
|
|
31
|
+
const salt = "59cf53e54c78";
|
|
32
|
+
const picked = [4, 7, 20].map((i) => (i < text.length ? text[i] : "0")).join("");
|
|
33
|
+
const hash = createHash("sha256")
|
|
34
|
+
.update(salt + picked + claudeCliVersion)
|
|
35
|
+
.digest("hex");
|
|
36
|
+
versionSuffix = `.${hash.slice(0, 3)}`;
|
|
37
|
+
}
|
|
28
38
|
}
|
|
29
|
-
}
|
|
30
|
-
if (text) {
|
|
31
|
-
const salt = "59cf53e54c78";
|
|
32
|
-
const picked = [4, 7, 20].map((i) => (i < text.length ? text[i] : "0")).join("");
|
|
33
|
-
const hash = createHash("sha256")
|
|
34
|
-
.update(salt + picked + claudeCliVersion)
|
|
35
|
-
.digest("hex");
|
|
36
|
-
versionSuffix = `.${hash.slice(0, 3)}`;
|
|
37
|
-
}
|
|
38
39
|
}
|
|
39
|
-
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
const entrypoint = process.env.CLAUDE_CODE_ENTRYPOINT ?? "cli";
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
return `x-anthropic-billing-header: cc_version=${claudeCliVersion}${versionSuffix}; cc_entrypoint=${entrypoint}; cch=${CCH_PLACEHOLDER};`;
|
|
44
44
|
}
|
package/src/headers/builder.ts
CHANGED
|
@@ -6,159 +6,159 @@ import { buildStainlessHelperHeader, getStainlessArch, getStainlessOs } from "./
|
|
|
6
6
|
import { buildUserAgent } from "./user-agent.js";
|
|
7
7
|
|
|
8
8
|
function parseAnthropicCustomHeaders(): Record<string, string> {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const raw = process.env.ANTHROPIC_CUSTOM_HEADERS;
|
|
10
|
+
if (!raw) return {};
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
const headers: Record<string, string> = {};
|
|
13
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
if (!trimmed) continue;
|
|
16
|
+
const sep = trimmed.indexOf(":");
|
|
17
|
+
if (sep <= 0) continue;
|
|
18
|
+
const key = trimmed.slice(0, sep).trim();
|
|
19
|
+
const value = trimmed.slice(sep + 1).trim();
|
|
20
|
+
if (!key || !value) continue;
|
|
21
|
+
headers[key] = value;
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
return headers;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function detectProvider(requestUrl: URL | null) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
if (!requestUrl) return "anthropic" as const;
|
|
29
|
+
const host = requestUrl.hostname.toLowerCase();
|
|
30
|
+
if (host.includes("bedrock") || host.includes("amazonaws.com")) return "bedrock" as const;
|
|
31
|
+
if (host.includes("aiplatform") || host.includes("vertex")) return "vertex" as const;
|
|
32
|
+
if (host.includes("foundry") || host.includes("azure")) return "foundry" as const;
|
|
33
|
+
return "anthropic" as const;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function parseRequestBodyMetadata(body: string | undefined): {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
model: string;
|
|
38
|
+
tools: unknown[];
|
|
39
|
+
messages: unknown[];
|
|
40
|
+
hasFileReferences: boolean;
|
|
41
41
|
} {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (!body || typeof body !== "string") {
|
|
43
|
+
return { model: "", tools: [], messages: [], hasFileReferences: false };
|
|
44
|
+
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(body) as Record<string, unknown>;
|
|
48
|
+
const model = typeof parsed?.model === "string" ? parsed.model : "";
|
|
49
|
+
const tools = Array.isArray(parsed?.tools) ? parsed.tools : [];
|
|
50
|
+
const messages = Array.isArray(parsed?.messages) ? parsed.messages : [];
|
|
51
|
+
// hasFileReferences: check if any message content references files
|
|
52
|
+
const hasFileReferences = hasFileIds(parsed);
|
|
53
|
+
return { model, tools, messages, hasFileReferences };
|
|
54
|
+
} catch {
|
|
55
|
+
return { model: "", tools: [], messages: [], hasFileReferences: false };
|
|
56
|
+
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function hasFileIds(parsed: Record<string, unknown>): boolean {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
const str = JSON.stringify(parsed);
|
|
61
|
+
return /file[-_][a-zA-Z0-9]{2,}/.test(str);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export function buildRequestHeaders(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
input: Request | string | URL,
|
|
66
|
+
requestInit: Record<string, unknown>,
|
|
67
|
+
accessToken: string,
|
|
68
|
+
requestBody: string | undefined,
|
|
69
|
+
requestUrl: URL | null,
|
|
70
|
+
signature: SignatureConfig,
|
|
71
71
|
): Headers {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
72
|
+
const requestHeaders = new Headers();
|
|
73
|
+
if (input instanceof Request) {
|
|
74
|
+
input.headers.forEach((value, key) => {
|
|
75
|
+
requestHeaders.set(key, value);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const initHeaders = requestInit.headers;
|
|
79
|
+
if (initHeaders) {
|
|
80
|
+
if (initHeaders instanceof Headers) {
|
|
81
|
+
initHeaders.forEach((value, key) => {
|
|
82
|
+
requestHeaders.set(key, value);
|
|
83
|
+
});
|
|
84
|
+
} else if (Array.isArray(initHeaders)) {
|
|
85
|
+
for (const [key, value] of initHeaders as [string, string | undefined][]) {
|
|
86
|
+
if (typeof value !== "undefined") {
|
|
87
|
+
requestHeaders.set(key, String(value));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
for (const [key, value] of Object.entries(initHeaders as Record<string, unknown>)) {
|
|
92
|
+
if (typeof value !== "undefined") {
|
|
93
|
+
requestHeaders.set(key, String(value));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
94
96
|
}
|
|
95
|
-
}
|
|
96
97
|
}
|
|
97
|
-
}
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
99
|
+
// Preserve all incoming beta headers while ensuring OAuth requirements
|
|
100
|
+
const incomingBeta = requestHeaders.get("anthropic-beta") || "";
|
|
101
|
+
const { model, tools, messages, hasFileReferences } = parseRequestBodyMetadata(requestBody);
|
|
102
|
+
const provider = detectProvider(requestUrl);
|
|
103
|
+
const mergedBetas = buildAnthropicBetaHeader(
|
|
104
|
+
incomingBeta,
|
|
105
|
+
signature.enabled,
|
|
106
|
+
model,
|
|
107
|
+
provider,
|
|
108
|
+
signature.customBetas,
|
|
109
|
+
signature.strategy,
|
|
110
|
+
requestUrl?.pathname,
|
|
111
|
+
hasFileReferences,
|
|
112
|
+
);
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
const authTokenOverride = process.env.ANTHROPIC_AUTH_TOKEN?.trim();
|
|
115
|
+
const bearerToken = authTokenOverride || accessToken;
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
117
|
+
requestHeaders.set("authorization", `Bearer ${bearerToken}`);
|
|
118
|
+
requestHeaders.set("anthropic-beta", mergedBetas);
|
|
119
|
+
requestHeaders.set("user-agent", buildUserAgent(signature.claudeCliVersion));
|
|
120
|
+
if (signature.enabled) {
|
|
121
|
+
requestHeaders.set("anthropic-version", "2023-06-01");
|
|
122
|
+
requestHeaders.set("anthropic-dangerous-direct-browser-access", "true");
|
|
123
|
+
requestHeaders.set("x-app", "cli");
|
|
124
|
+
requestHeaders.set("x-stainless-arch", getStainlessArch(process.arch));
|
|
125
|
+
requestHeaders.set("x-stainless-lang", "js");
|
|
126
|
+
requestHeaders.set("x-stainless-os", getStainlessOs(process.platform));
|
|
127
|
+
// CC's Stainless SDK reports its own package version (0.81.0), not the CLI version
|
|
128
|
+
requestHeaders.set("x-stainless-package-version", "0.81.0");
|
|
129
|
+
requestHeaders.set("x-stainless-runtime", "node");
|
|
130
|
+
requestHeaders.set("x-stainless-runtime-version", process.version);
|
|
131
|
+
// CC's SDK default timeout is 600s (600000ms)
|
|
132
|
+
requestHeaders.set("x-stainless-timeout", "600");
|
|
133
|
+
const incomingRetryCount = requestHeaders.get("x-stainless-retry-count");
|
|
134
|
+
requestHeaders.set(
|
|
135
|
+
"x-stainless-retry-count",
|
|
136
|
+
incomingRetryCount && !isFalsyEnv(incomingRetryCount) ? incomingRetryCount : "0",
|
|
137
|
+
);
|
|
138
|
+
const stainlessHelpers = buildStainlessHelperHeader(tools, messages);
|
|
139
|
+
if (stainlessHelpers) {
|
|
140
|
+
requestHeaders.set("x-stainless-helper", stainlessHelpers);
|
|
141
|
+
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
143
|
+
for (const [key, value] of Object.entries(parseAnthropicCustomHeaders())) {
|
|
144
|
+
requestHeaders.set(key, value);
|
|
145
|
+
}
|
|
146
|
+
if (process.env.CLAUDE_CODE_CONTAINER_ID) {
|
|
147
|
+
requestHeaders.set("x-claude-remote-container-id", process.env.CLAUDE_CODE_CONTAINER_ID);
|
|
148
|
+
}
|
|
149
|
+
if (process.env.CLAUDE_CODE_REMOTE_SESSION_ID) {
|
|
150
|
+
requestHeaders.set("x-claude-remote-session-id", process.env.CLAUDE_CODE_REMOTE_SESSION_ID);
|
|
151
|
+
}
|
|
152
|
+
if (process.env.CLAUDE_AGENT_SDK_CLIENT_APP) {
|
|
153
|
+
requestHeaders.set("x-client-app", process.env.CLAUDE_AGENT_SDK_CLIENT_APP);
|
|
154
|
+
}
|
|
155
|
+
if (isTruthyEnv(process.env.CLAUDE_CODE_ADDITIONAL_PROTECTION)) {
|
|
156
|
+
requestHeaders.set("x-anthropic-additional-protection", "true");
|
|
157
|
+
}
|
|
158
|
+
// CC 2.1.98 sends a per-request UUID
|
|
159
|
+
requestHeaders.set("x-client-request-id", randomUUID());
|
|
157
160
|
}
|
|
158
|
-
|
|
159
|
-
requestHeaders.set("x-client-request-id", randomUUID());
|
|
160
|
-
}
|
|
161
|
-
requestHeaders.delete("x-api-key");
|
|
161
|
+
requestHeaders.delete("x-api-key");
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
return requestHeaders;
|
|
164
164
|
}
|
package/src/headers/cch.ts
CHANGED
|
@@ -13,108 +13,108 @@ export const CCH_SEED = 0x6e52_736a_c806_831en;
|
|
|
13
13
|
const encoder = new TextEncoder();
|
|
14
14
|
|
|
15
15
|
function toUint64(value: bigint): bigint {
|
|
16
|
-
|
|
16
|
+
return value & MASK64;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function rotateLeft64(value: bigint, bits: number): bigint {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const shift = BigInt(bits);
|
|
21
|
+
return toUint64((value << shift) | (value >> (64n - shift)));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function readUint32LE(view: DataView, offset: number): bigint {
|
|
25
|
-
|
|
25
|
+
return BigInt(view.getUint32(offset, true));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function readUint64LE(view: DataView, offset: number): bigint {
|
|
29
|
-
|
|
29
|
+
return view.getBigUint64(offset, true);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function round64(acc: bigint, input: bigint): bigint {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const mixed = toUint64(acc + toUint64(input * PRIME2));
|
|
34
|
+
return toUint64(rotateLeft64(mixed, 31) * PRIME1);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function mergeRound64(acc: bigint, value: bigint): bigint {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const mixed = acc ^ round64(0n, value);
|
|
39
|
+
return toUint64(toUint64(mixed) * PRIME1 + PRIME4);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function avalanche64(hash: bigint): bigint {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
let mixed = hash ^ (hash >> 33n);
|
|
44
|
+
mixed = toUint64(mixed * PRIME2);
|
|
45
|
+
mixed ^= mixed >> 29n;
|
|
46
|
+
mixed = toUint64(mixed * PRIME3);
|
|
47
|
+
mixed ^= mixed >> 32n;
|
|
48
|
+
return toUint64(mixed);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export function xxHash64(input: Uint8Array, seed: bigint = CCH_SEED): bigint {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
53
|
+
const length = input.byteLength;
|
|
54
|
+
let offset = 0;
|
|
55
|
+
let hash: bigint;
|
|
56
|
+
|
|
57
|
+
if (length >= 32) {
|
|
58
|
+
let v1 = toUint64(seed + PRIME1 + PRIME2);
|
|
59
|
+
let v2 = toUint64(seed + PRIME2);
|
|
60
|
+
let v3 = toUint64(seed);
|
|
61
|
+
let v4 = toUint64(seed - PRIME1);
|
|
62
|
+
|
|
63
|
+
while (offset <= length - 32) {
|
|
64
|
+
v1 = round64(v1, readUint64LE(view, offset));
|
|
65
|
+
v2 = round64(v2, readUint64LE(view, offset + 8));
|
|
66
|
+
v3 = round64(v3, readUint64LE(view, offset + 16));
|
|
67
|
+
v4 = round64(v4, readUint64LE(view, offset + 24));
|
|
68
|
+
offset += 32;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
hash = toUint64(rotateLeft64(v1, 1) + rotateLeft64(v2, 7) + rotateLeft64(v3, 12) + rotateLeft64(v4, 18));
|
|
72
|
+
hash = mergeRound64(hash, v1);
|
|
73
|
+
hash = mergeRound64(hash, v2);
|
|
74
|
+
hash = mergeRound64(hash, v3);
|
|
75
|
+
hash = mergeRound64(hash, v4);
|
|
76
|
+
} else {
|
|
77
|
+
hash = toUint64(seed + PRIME5);
|
|
69
78
|
}
|
|
70
79
|
|
|
71
|
-
hash = toUint64(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
offset
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
while (offset < length) {
|
|
96
|
-
hash ^= toUint64(BigInt(view.getUint8(offset)) * PRIME5);
|
|
97
|
-
hash = toUint64(rotateLeft64(hash, 11) * PRIME1);
|
|
98
|
-
offset += 1;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return avalanche64(hash);
|
|
80
|
+
hash = toUint64(hash + BigInt(length));
|
|
81
|
+
|
|
82
|
+
while (offset <= length - 8) {
|
|
83
|
+
const lane = round64(0n, readUint64LE(view, offset));
|
|
84
|
+
hash ^= lane;
|
|
85
|
+
hash = toUint64(rotateLeft64(hash, 27) * PRIME1 + PRIME4);
|
|
86
|
+
offset += 8;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (offset <= length - 4) {
|
|
90
|
+
hash ^= toUint64(readUint32LE(view, offset) * PRIME1);
|
|
91
|
+
hash = toUint64(rotateLeft64(hash, 23) * PRIME2 + PRIME3);
|
|
92
|
+
offset += 4;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
while (offset < length) {
|
|
96
|
+
hash ^= toUint64(BigInt(view.getUint8(offset)) * PRIME5);
|
|
97
|
+
hash = toUint64(rotateLeft64(hash, 11) * PRIME1);
|
|
98
|
+
offset += 1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return avalanche64(hash);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
export function computeNativeStyleCch(serializedBody: string): string {
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const hash = xxHash64(encoder.encode(serializedBody), CCH_SEED);
|
|
106
|
+
return (hash & CCH_MASK).toString(16).padStart(5, "0");
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export function replaceNativeStyleCch(serializedBody: string): string {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
const sentinel = `${CCH_FIELD_PREFIX}${CCH_PLACEHOLDER}`;
|
|
111
|
+
const fieldIndex = serializedBody.indexOf(sentinel);
|
|
112
|
+
if (fieldIndex === -1) {
|
|
113
|
+
return serializedBody;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const valueStart = fieldIndex + CCH_FIELD_PREFIX.length;
|
|
117
|
+
const valueEnd = valueStart + CCH_PLACEHOLDER.length;
|
|
118
|
+
const cch = computeNativeStyleCch(serializedBody);
|
|
119
|
+
return `${serializedBody.slice(0, valueStart)}${cch}${serializedBody.slice(valueEnd)}`;
|
|
120
120
|
}
|
package/src/headers/stainless.ts
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import { STAINLESS_HELPER_KEYS } from "../constants.js";
|
|
2
2
|
|
|
3
3
|
export function getStainlessOs(value: NodeJS.Platform): string {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
if (value === "darwin") return "MacOS";
|
|
5
|
+
if (value === "win32") return "Windows";
|
|
6
|
+
if (value === "linux") return "Linux";
|
|
7
|
+
return value;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function getStainlessArch(value: string): string {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if (value === "x64") return "x64";
|
|
12
|
+
if (value === "arm64") return "arm64";
|
|
13
|
+
return value;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function buildStainlessHelperHeader(tools: unknown[], messages: unknown[]): string {
|
|
17
|
-
|
|
17
|
+
const helpers = new Set<string>();
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const collect = (value: unknown): void => {
|
|
20
|
+
if (!value || typeof value !== "object") return;
|
|
21
|
+
const obj = value as Record<string, unknown>;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
for (const key of STAINLESS_HELPER_KEYS) {
|
|
24
|
+
if (typeof obj[key] === "string" && obj[key]) {
|
|
25
|
+
helpers.add(obj[key] as string);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
if (Array.isArray(obj.content)) {
|
|
30
|
+
for (const contentBlock of obj.content) {
|
|
31
|
+
collect(contentBlock);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
for (const tool of tools) collect(tool);
|
|
37
|
+
for (const message of messages) collect(message);
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
return Array.from(helpers).join(", ");
|
|
40
40
|
}
|