@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/models.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import type { Provider } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
export function isHaikuModel(model: string): boolean {
|
|
8
|
-
|
|
8
|
+
return /haiku/i.test(model);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function supportsThinking(model: string): boolean {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (!model) return true;
|
|
13
|
+
return /claude|sonnet|opus|haiku/i.test(model);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -19,11 +19,11 @@ export function supportsThinking(model: string): boolean {
|
|
|
19
19
|
* manual budgetTokens.
|
|
20
20
|
*/
|
|
21
21
|
export function isOpus46Model(model: string): boolean {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
if (!model) return false;
|
|
23
|
+
// Match standard IDs (claude-opus-4-6, claude-opus-4.6) and Bedrock ARNs
|
|
24
|
+
// (arn:aws:bedrock:...anthropic.claude-opus-4-6-...).
|
|
25
|
+
// Also match bare "opus-4-6" / "opus-4.6" fragments for non-standard strings.
|
|
26
|
+
return /claude-opus-4[._-]6|opus[._-]4[._-]6/i.test(model);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -31,8 +31,8 @@ export function isOpus46Model(model: string): boolean {
|
|
|
31
31
|
* Like Opus 4.6, these models use adaptive thinking (effort parameter).
|
|
32
32
|
*/
|
|
33
33
|
export function isSonnet46Model(model: string): boolean {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
if (!model) return false;
|
|
35
|
+
return /claude-sonnet-4[._-]6|sonnet[._-]4[._-]6/i.test(model);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -40,39 +40,39 @@ export function isSonnet46Model(model: string): boolean {
|
|
|
40
40
|
* This includes both Opus 4.6 and Sonnet 4.6.
|
|
41
41
|
*/
|
|
42
42
|
export function isAdaptiveThinkingModel(model: string): boolean {
|
|
43
|
-
|
|
43
|
+
return isOpus46Model(model) || isSonnet46Model(model);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export function hasOneMillionContext(model: string): boolean {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Models with explicit 1m suffix, or Opus 4.6 (1M by default since v2.1.75).
|
|
48
|
+
return /(^|[-_ ])1m($|[-_ ])|context[-_]?1m/i.test(model) || isOpus46Model(model);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export function supportsStructuredOutputs(model: string): boolean {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (!/claude|sonnet|opus|haiku/i.test(model)) return false;
|
|
53
|
+
return !isHaikuModel(model);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Context management is supported on Claude 4+ models (matches upstream CC).
|
|
58
58
|
*/
|
|
59
59
|
export function supportsContextManagement(model: string): boolean {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
if (!model) return false;
|
|
61
|
+
// Claude 3.x does not support context management
|
|
62
|
+
if (/claude-3-/i.test(model)) return false;
|
|
63
|
+
// Any other Claude model (4+) supports it
|
|
64
|
+
return /claude|sonnet|opus|haiku/i.test(model);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export function supportsWebSearch(model: string): boolean {
|
|
68
|
-
|
|
68
|
+
return /claude|sonnet|opus|haiku|gpt|gemini/i.test(model);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export function detectProvider(requestUrl: URL | null): Provider {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
if (!requestUrl) return "anthropic";
|
|
73
|
+
const host = requestUrl.hostname.toLowerCase();
|
|
74
|
+
if (host.includes("bedrock") || host.includes("amazonaws.com")) return "bedrock";
|
|
75
|
+
if (host.includes("aiplatform") || host.includes("vertex")) return "vertex";
|
|
76
|
+
if (host.includes("foundry") || host.includes("azure")) return "foundry";
|
|
77
|
+
return "anthropic";
|
|
78
78
|
}
|
package/src/oauth.test.ts
CHANGED
|
@@ -5,129 +5,129 @@ const mockFetch = vi.fn();
|
|
|
5
5
|
const originalFetch = globalThis.fetch;
|
|
6
6
|
|
|
7
7
|
describe("oauth headers", () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
globalThis.fetch = originalFetch;
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("sends Claude Code user-agent on token exchange", async () => {
|
|
18
|
-
mockFetch.mockResolvedValueOnce({
|
|
19
|
-
ok: false,
|
|
20
|
-
status: 400,
|
|
21
|
-
text: async () => "",
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
vi.resetAllMocks();
|
|
10
|
+
globalThis.fetch = mockFetch as typeof fetch;
|
|
22
11
|
});
|
|
23
12
|
|
|
24
|
-
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
globalThis.fetch = originalFetch;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("sends Claude Code user-agent on token exchange", async () => {
|
|
18
|
+
mockFetch.mockResolvedValueOnce({
|
|
19
|
+
ok: false,
|
|
20
|
+
status: 400,
|
|
21
|
+
text: async () => "",
|
|
22
|
+
});
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
expect(init.headers["User-Agent"]).toBe("axios/1.13.6");
|
|
28
|
-
});
|
|
24
|
+
await exchange("code#state", "verifier");
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ok: true,
|
|
33
|
-
json: async () => ({
|
|
34
|
-
access_token: "a",
|
|
35
|
-
refresh_token: "r",
|
|
36
|
-
expires_in: 3600,
|
|
37
|
-
}),
|
|
26
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
27
|
+
expect(init.headers["User-Agent"]).toBe("axios/1.13.6");
|
|
38
28
|
});
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
it("sends Claude Code user-agent on token refresh", async () => {
|
|
31
|
+
mockFetch.mockResolvedValueOnce({
|
|
32
|
+
ok: true,
|
|
33
|
+
json: async () => ({
|
|
34
|
+
access_token: "a",
|
|
35
|
+
refresh_token: "r",
|
|
36
|
+
expires_in: 3600,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
expect(init.headers["User-Agent"]).toBe("axios/1.13.6");
|
|
44
|
-
});
|
|
40
|
+
await refreshToken("refresh-token");
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
43
|
+
expect(init.headers["User-Agent"]).toBe("axios/1.13.6");
|
|
44
|
+
});
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
it("sends Claude Code user-agent on token revoke", async () => {
|
|
47
|
+
mockFetch.mockResolvedValueOnce({ ok: true });
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
await revoke("refresh-token");
|
|
50
|
+
|
|
51
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
52
|
+
expect(init.headers["User-Agent"]).toBe("axios/1.13.6");
|
|
53
|
+
});
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
describe("oauth scopes", () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
57
|
+
it("requests all required scopes in authorization URL", async () => {
|
|
58
|
+
const { url } = await authorize("console");
|
|
59
|
+
const parsed = new URL(url);
|
|
60
|
+
const scope = parsed.searchParams.get("scope");
|
|
61
|
+
|
|
62
|
+
expect(scope).toContain("org:create_api_key");
|
|
63
|
+
expect(scope).toContain("user:profile");
|
|
64
|
+
expect(scope).toContain("user:inference");
|
|
65
|
+
expect(scope).toContain("user:sessions:claude_code");
|
|
66
|
+
expect(scope).toContain("user:mcp_servers");
|
|
67
|
+
expect(scope).toContain("user:file_upload");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("uses console host for console mode", async () => {
|
|
71
|
+
const { url } = await authorize("console");
|
|
72
|
+
expect(url).toContain("platform.claude.com/oauth/authorize");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("uses max host for max mode", async () => {
|
|
76
|
+
const { url } = await authorize("max");
|
|
77
|
+
expect(url).toContain("claude.ai/oauth/authorize");
|
|
78
|
+
});
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
describe("oauth authorize options", () => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
it("includes orgUUID when provided", async () => {
|
|
83
|
+
const { url } = await authorize("console", { orgUUID: "org-123" });
|
|
84
|
+
const parsed = new URL(url);
|
|
85
|
+
expect(parsed.searchParams.get("orgUUID")).toBe("org-123");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("includes login_hint when provided", async () => {
|
|
89
|
+
const { url } = await authorize("console", {
|
|
90
|
+
loginHint: "user@example.com",
|
|
91
|
+
});
|
|
92
|
+
const parsed = new URL(url);
|
|
93
|
+
expect(parsed.searchParams.get("login_hint")).toBe("user@example.com");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("includes login_method when provided", async () => {
|
|
97
|
+
const { url } = await authorize("console", { loginMethod: "google" });
|
|
98
|
+
const parsed = new URL(url);
|
|
99
|
+
expect(parsed.searchParams.get("login_method")).toBe("google");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("omits optional params when not provided", async () => {
|
|
103
|
+
const { url } = await authorize("console");
|
|
104
|
+
const parsed = new URL(url);
|
|
105
|
+
expect(parsed.searchParams.has("orgUUID")).toBe(false);
|
|
106
|
+
expect(parsed.searchParams.has("login_hint")).toBe(false);
|
|
107
|
+
expect(parsed.searchParams.has("login_method")).toBe(false);
|
|
91
108
|
});
|
|
92
|
-
const parsed = new URL(url);
|
|
93
|
-
expect(parsed.searchParams.get("login_hint")).toBe("user@example.com");
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("includes login_method when provided", async () => {
|
|
97
|
-
const { url } = await authorize("console", { loginMethod: "google" });
|
|
98
|
-
const parsed = new URL(url);
|
|
99
|
-
expect(parsed.searchParams.get("login_method")).toBe("google");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("omits optional params when not provided", async () => {
|
|
103
|
-
const { url } = await authorize("console");
|
|
104
|
-
const parsed = new URL(url);
|
|
105
|
-
expect(parsed.searchParams.has("orgUUID")).toBe(false);
|
|
106
|
-
expect(parsed.searchParams.has("login_hint")).toBe(false);
|
|
107
|
-
expect(parsed.searchParams.has("login_method")).toBe(false);
|
|
108
|
-
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
describe("oauth exchange timeout", () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("sets 15s timeout on token exchange", async () => {
|
|
122
|
-
mockFetch.mockResolvedValueOnce({
|
|
123
|
-
ok: false,
|
|
124
|
-
status: 400,
|
|
125
|
-
text: async () => "",
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
vi.resetAllMocks();
|
|
114
|
+
globalThis.fetch = mockFetch as typeof fetch;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
afterEach(() => {
|
|
118
|
+
globalThis.fetch = originalFetch;
|
|
126
119
|
});
|
|
127
120
|
|
|
128
|
-
|
|
121
|
+
it("sets 15s timeout on token exchange", async () => {
|
|
122
|
+
mockFetch.mockResolvedValueOnce({
|
|
123
|
+
ok: false,
|
|
124
|
+
status: 400,
|
|
125
|
+
text: async () => "",
|
|
126
|
+
});
|
|
129
127
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
await exchange("code#state", "verifier");
|
|
129
|
+
|
|
130
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
131
|
+
expect(init.signal).toBeDefined();
|
|
132
|
+
});
|
|
133
133
|
});
|