@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.
Files changed (107) hide show
  1. package/README.md +88 -88
  2. package/dist/opencode-anthropic-auth-cli.mjs +804 -507
  3. package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
  4. package/package.json +67 -59
  5. package/src/__tests__/billing-edge-cases.test.ts +59 -59
  6. package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
  7. package/src/__tests__/cc-comparison.test.ts +87 -87
  8. package/src/__tests__/cc-credentials.test.ts +254 -250
  9. package/src/__tests__/cch-drift-checker.test.ts +51 -51
  10. package/src/__tests__/cch-native-style.test.ts +56 -56
  11. package/src/__tests__/debug-gating.test.ts +42 -42
  12. package/src/__tests__/decomposition-smoke.test.ts +68 -68
  13. package/src/__tests__/fingerprint-regression.test.ts +575 -566
  14. package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
  15. package/src/__tests__/helpers/conversation-history.ts +119 -119
  16. package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
  17. package/src/__tests__/helpers/deferred.ts +69 -69
  18. package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
  19. package/src/__tests__/helpers/in-memory-storage.ts +88 -88
  20. package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
  21. package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
  22. package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
  23. package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
  24. package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
  25. package/src/__tests__/helpers/sse.ts +209 -209
  26. package/src/__tests__/index.parallel.test.ts +605 -595
  27. package/src/__tests__/sanitization-regex.test.ts +112 -112
  28. package/src/__tests__/state-bounds.test.ts +90 -90
  29. package/src/account-identity.test.ts +197 -192
  30. package/src/account-identity.ts +69 -67
  31. package/src/account-state.test.ts +86 -86
  32. package/src/account-state.ts +25 -25
  33. package/src/accounts/matching.test.ts +335 -0
  34. package/src/accounts/matching.ts +167 -0
  35. package/src/accounts/persistence.test.ts +345 -0
  36. package/src/accounts/persistence.ts +432 -0
  37. package/src/accounts/repair.test.ts +276 -0
  38. package/src/accounts/repair.ts +407 -0
  39. package/src/accounts.dedup.test.ts +621 -621
  40. package/src/accounts.test.ts +933 -929
  41. package/src/accounts.ts +633 -989
  42. package/src/backoff.test.ts +345 -345
  43. package/src/backoff.ts +219 -219
  44. package/src/betas.ts +124 -124
  45. package/src/bun-fetch.test.ts +345 -342
  46. package/src/bun-fetch.ts +424 -424
  47. package/src/bun-proxy.test.ts +25 -25
  48. package/src/bun-proxy.ts +209 -209
  49. package/src/cc-credentials.ts +111 -111
  50. package/src/circuit-breaker.test.ts +184 -184
  51. package/src/circuit-breaker.ts +169 -169
  52. package/src/cli/commands/auth.ts +963 -0
  53. package/src/cli/commands/config.ts +547 -0
  54. package/src/cli/formatting.test.ts +406 -0
  55. package/src/cli/formatting.ts +219 -0
  56. package/src/cli.ts +255 -2022
  57. package/src/commands/handlers/betas.ts +100 -0
  58. package/src/commands/handlers/config.ts +99 -0
  59. package/src/commands/handlers/files.ts +375 -0
  60. package/src/commands/oauth-flow.ts +181 -166
  61. package/src/commands/prompts.ts +61 -61
  62. package/src/commands/router.test.ts +421 -0
  63. package/src/commands/router.ts +143 -635
  64. package/src/config.test.ts +482 -482
  65. package/src/config.ts +412 -404
  66. package/src/constants.ts +48 -48
  67. package/src/drift/cch-constants.ts +95 -95
  68. package/src/env.ts +111 -105
  69. package/src/headers/billing.ts +33 -33
  70. package/src/headers/builder.ts +130 -130
  71. package/src/headers/cch.ts +75 -75
  72. package/src/headers/stainless.ts +25 -25
  73. package/src/headers/user-agent.ts +23 -23
  74. package/src/index.ts +436 -828
  75. package/src/models.ts +27 -27
  76. package/src/oauth.test.ts +102 -102
  77. package/src/oauth.ts +178 -178
  78. package/src/parent-pid-watcher.test.ts +148 -148
  79. package/src/parent-pid-watcher.ts +69 -69
  80. package/src/plugin-helpers.ts +82 -82
  81. package/src/refresh-helpers.ts +145 -139
  82. package/src/refresh-lock.test.ts +94 -94
  83. package/src/refresh-lock.ts +93 -93
  84. package/src/request/body.history.test.ts +579 -571
  85. package/src/request/body.ts +255 -255
  86. package/src/request/metadata.ts +65 -65
  87. package/src/request/retry.test.ts +156 -156
  88. package/src/request/retry.ts +67 -67
  89. package/src/request/url.ts +21 -21
  90. package/src/request-orchestration-helpers.ts +648 -0
  91. package/src/response/index.ts +5 -5
  92. package/src/response/mcp.ts +58 -58
  93. package/src/response/streaming.test.ts +313 -311
  94. package/src/response/streaming.ts +412 -410
  95. package/src/rotation.test.ts +304 -301
  96. package/src/rotation.ts +205 -205
  97. package/src/storage.test.ts +547 -547
  98. package/src/storage.ts +315 -291
  99. package/src/system-prompt/builder.ts +38 -38
  100. package/src/system-prompt/index.ts +5 -5
  101. package/src/system-prompt/normalize.ts +60 -60
  102. package/src/system-prompt/sanitize.ts +30 -30
  103. package/src/thinking.ts +21 -20
  104. package/src/token-refresh.test.ts +265 -265
  105. package/src/token-refresh.ts +219 -214
  106. package/src/types.ts +30 -30
  107. package/dist/bun-proxy.mjs +0 -291
@@ -4,164 +4,164 @@ import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
5
 
6
6
  export interface CCCredential {
7
- accessToken: string;
8
- refreshToken: string;
9
- expiresAt: number;
10
- subscriptionType?: string;
11
- source: "cc-keychain" | "cc-file";
12
- label: string;
7
+ accessToken: string;
8
+ refreshToken: string;
9
+ expiresAt: number;
10
+ subscriptionType?: string;
11
+ source: "cc-keychain" | "cc-file";
12
+ label: string;
13
13
  }
14
14
 
15
15
  type CCCredentialSource = CCCredential["source"];
16
16
 
17
17
  interface CredentialParseMeta {
18
- source: CCCredentialSource;
19
- label: string;
18
+ source: CCCredentialSource;
19
+ label: string;
20
20
  }
21
21
 
22
22
  interface SecurityCommandError {
23
- status?: number | null;
24
- code?: string;
25
- signal?: string | null;
23
+ status?: number | null;
24
+ code?: string;
25
+ signal?: string | null;
26
26
  }
27
27
 
28
28
  const SECURITY_TIMEOUT_MS = 5000;
29
29
  const SECURITY_HANDLED_EXIT_CODES = new Set([36, 44, 128]);
30
30
  const CLAUDE_CODE_SERVICE_PATTERN = /"svce"<blob>="(Claude Code-credentials[^"]*)"/g;
31
31
  function isRecord(value: unknown): value is Record<string, unknown> {
32
- return typeof value === "object" && value !== null && !Array.isArray(value);
32
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33
33
  }
34
34
 
35
35
  function isValidCredentialShape(value: unknown): value is {
36
- accessToken: string;
37
- refreshToken: string;
38
- expiresAt: number;
39
- subscriptionType?: string;
36
+ accessToken: string;
37
+ refreshToken: string;
38
+ expiresAt: number;
39
+ subscriptionType?: string;
40
40
  } {
41
- if (!isRecord(value)) return false;
42
- if (typeof value.accessToken !== "string" || value.accessToken.length === 0) return false;
43
- if (typeof value.refreshToken !== "string" || value.refreshToken.length === 0) return false;
44
- if (typeof value.expiresAt !== "number" || !Number.isFinite(value.expiresAt)) return false;
45
- if (value.subscriptionType !== undefined && typeof value.subscriptionType !== "string") return false;
46
- return true;
41
+ if (!isRecord(value)) return false;
42
+ if (typeof value.accessToken !== "string" || value.accessToken.length === 0) return false;
43
+ if (typeof value.refreshToken !== "string" || value.refreshToken.length === 0) return false;
44
+ if (typeof value.expiresAt !== "number" || !Number.isFinite(value.expiresAt)) return false;
45
+ if (value.subscriptionType !== undefined && typeof value.subscriptionType !== "string") return false;
46
+ return true;
47
47
  }
48
48
 
49
49
  function parseCCCredentialWithMeta(raw: string, meta: CredentialParseMeta): CCCredential | null {
50
- try {
51
- const parsed: unknown = JSON.parse(raw);
52
- if (!isRecord(parsed)) return null;
53
-
54
- const wrapped = parsed.claudeAiOauth;
55
- const candidate = isValidCredentialShape(wrapped)
56
- ? wrapped
57
- : isValidCredentialShape(parsed)
58
- ? parsed
59
- : parsed.mcpOAuth !== undefined && parsed.accessToken === undefined
60
- ? null
61
- : null;
62
-
63
- if (!candidate) return null;
64
-
65
- return {
66
- accessToken: candidate.accessToken,
67
- refreshToken: candidate.refreshToken,
68
- expiresAt: candidate.expiresAt,
69
- subscriptionType: candidate.subscriptionType,
70
- source: meta.source,
71
- label: meta.label,
72
- };
73
- } catch {
74
- return null;
75
- }
50
+ try {
51
+ const parsed: unknown = JSON.parse(raw);
52
+ if (!isRecord(parsed)) return null;
53
+
54
+ const wrapped = parsed.claudeAiOauth;
55
+ const candidate = isValidCredentialShape(wrapped)
56
+ ? wrapped
57
+ : isValidCredentialShape(parsed)
58
+ ? parsed
59
+ : parsed.mcpOAuth !== undefined && parsed.accessToken === undefined
60
+ ? null
61
+ : null;
62
+
63
+ if (!candidate) return null;
64
+
65
+ return {
66
+ accessToken: candidate.accessToken,
67
+ refreshToken: candidate.refreshToken,
68
+ expiresAt: candidate.expiresAt,
69
+ subscriptionType: candidate.subscriptionType,
70
+ source: meta.source,
71
+ label: meta.label,
72
+ };
73
+ } catch {
74
+ return null;
75
+ }
76
76
  }
77
77
 
78
78
  function extractClaudeCodeServices(raw: string): string[] {
79
- const services = new Set<string>();
79
+ const services = new Set<string>();
80
80
 
81
- for (const match of raw.matchAll(CLAUDE_CODE_SERVICE_PATTERN)) {
82
- const service = match[1]?.trim();
83
- if (service) services.add(service);
84
- }
81
+ for (const match of raw.matchAll(CLAUDE_CODE_SERVICE_PATTERN)) {
82
+ const service = match[1]?.trim();
83
+ if (service) services.add(service);
84
+ }
85
85
 
86
- return Array.from(services);
86
+ return Array.from(services);
87
87
  }
88
88
 
89
89
  function shellQuote(value: string): string {
90
- return `'${value.replace(/'/g, `'\\''`)}'`;
90
+ return `'${value.replace(/'/g, `'\\''`)}'`;
91
91
  }
92
92
 
93
93
  function runSecurityCommand(command: string): string | null {
94
- try {
95
- return execSync(command, {
96
- encoding: "utf-8",
97
- timeout: SECURITY_TIMEOUT_MS,
98
- });
99
- } catch (error) {
100
- const securityError = error as SecurityCommandError;
101
- if (typeof securityError.status === "number" && SECURITY_HANDLED_EXIT_CODES.has(securityError.status)) {
102
- return null;
103
- }
104
- if (securityError.code === "ETIMEDOUT" || securityError.signal === "SIGTERM") {
105
- return null;
94
+ try {
95
+ return execSync(command, {
96
+ encoding: "utf-8",
97
+ timeout: SECURITY_TIMEOUT_MS,
98
+ });
99
+ } catch (error) {
100
+ const securityError = error as SecurityCommandError;
101
+ if (typeof securityError.status === "number" && SECURITY_HANDLED_EXIT_CODES.has(securityError.status)) {
102
+ return null;
103
+ }
104
+ if (securityError.code === "ETIMEDOUT" || securityError.signal === "SIGTERM") {
105
+ return null;
106
+ }
107
+ return null;
106
108
  }
107
- return null;
108
- }
109
109
  }
110
110
 
111
111
  export function parseCCCredentialData(raw: string): CCCredential | null {
112
- return parseCCCredentialWithMeta(raw, {
113
- source: "cc-file",
114
- label: join(homedir(), ".claude", ".credentials.json"),
115
- });
112
+ return parseCCCredentialWithMeta(raw, {
113
+ source: "cc-file",
114
+ label: join(homedir(), ".claude", ".credentials.json"),
115
+ });
116
116
  }
117
117
 
118
118
  export function readCCCredentialsFromKeychain(): CCCredential[] | null {
119
- if (process.platform !== "darwin") return null;
120
-
121
- const dumpOutput = runSecurityCommand("security dump-keychain");
122
- if (!dumpOutput) return null;
123
-
124
- const services = extractClaudeCodeServices(dumpOutput);
125
- if (services.length === 0) return null;
126
-
127
- const credentials: CCCredential[] = [];
128
- for (const service of services) {
129
- const rawCredential = runSecurityCommand(`security find-generic-password -s ${shellQuote(service)} -w`);
130
- if (!rawCredential) return null;
131
- const credential = parseCCCredentialWithMeta(rawCredential, {
132
- source: "cc-keychain",
133
- label: service,
134
- });
135
- if (credential) credentials.push(credential);
136
- }
119
+ if (process.platform !== "darwin") return null;
120
+
121
+ const dumpOutput = runSecurityCommand("security dump-keychain");
122
+ if (!dumpOutput) return null;
123
+
124
+ const services = extractClaudeCodeServices(dumpOutput);
125
+ if (services.length === 0) return null;
126
+
127
+ const credentials: CCCredential[] = [];
128
+ for (const service of services) {
129
+ const rawCredential = runSecurityCommand(`security find-generic-password -s ${shellQuote(service)} -w`);
130
+ if (!rawCredential) return null;
131
+ const credential = parseCCCredentialWithMeta(rawCredential, {
132
+ source: "cc-keychain",
133
+ label: service,
134
+ });
135
+ if (credential) credentials.push(credential);
136
+ }
137
137
 
138
- return credentials.length > 0 ? credentials : null;
138
+ return credentials.length > 0 ? credentials : null;
139
139
  }
140
140
 
141
141
  export function readCCCredentialsFromFile(): CCCredential | null {
142
- const credentialsPath = join(homedir(), ".claude", ".credentials.json");
143
-
144
- try {
145
- const raw = readFileSync(credentialsPath, "utf-8");
146
- return parseCCCredentialWithMeta(raw, {
147
- source: "cc-file",
148
- label: credentialsPath,
149
- });
150
- } catch {
151
- return null;
152
- }
142
+ const credentialsPath = join(homedir(), ".claude", ".credentials.json");
143
+
144
+ try {
145
+ const raw = readFileSync(credentialsPath, "utf-8");
146
+ return parseCCCredentialWithMeta(raw, {
147
+ source: "cc-file",
148
+ label: credentialsPath,
149
+ });
150
+ } catch {
151
+ return null;
152
+ }
153
153
  }
154
154
 
155
155
  export function readCCCredentials(): CCCredential[] {
156
- const credentials: CCCredential[] = [];
156
+ const credentials: CCCredential[] = [];
157
157
 
158
- if (process.platform === "darwin") {
159
- const keychainCredentials = readCCCredentialsFromKeychain();
160
- if (keychainCredentials) credentials.push(...keychainCredentials);
161
- }
158
+ if (process.platform === "darwin") {
159
+ const keychainCredentials = readCCCredentialsFromKeychain();
160
+ if (keychainCredentials) credentials.push(...keychainCredentials);
161
+ }
162
162
 
163
- const fileCredential = readCCCredentialsFromFile();
164
- if (fileCredential) credentials.push(fileCredential);
163
+ const fileCredential = readCCCredentialsFromFile();
164
+ if (fileCredential) credentials.push(fileCredential);
165
165
 
166
- return credentials;
166
+ return credentials;
167
167
  }