@claude-auth-sdk/core 0.1.0
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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +707 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Claude Auth SDK contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @claude-auth-sdk/core
|
|
2
|
+
|
|
3
|
+
Core authentication SDK for Claude. Handles OAuth login, credential storage, and browser opening.
|
|
4
|
+
|
|
5
|
+
See the [monorepo README](https://github.com/anthropics/claude-auth-sdk#readme) for full documentation.
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { login } from '@claude-auth-sdk/core';
|
|
11
|
+
|
|
12
|
+
const result = await login('claudeai');
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
|
|
17
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
type LoginErrorCode = 'timeout' | 'cancelled' | 'exchange_failed' | 'storage_failed';
|
|
2
|
+
declare class LoginError extends Error {
|
|
3
|
+
readonly code: LoginErrorCode;
|
|
4
|
+
constructor(message: string, code: LoginErrorCode, cause?: unknown);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface OAuthCredentialBundle {
|
|
8
|
+
accessToken: string;
|
|
9
|
+
refreshToken: string;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
scopes: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
type SubscriptionType = 'max' | 'pro' | 'team' | 'enterprise' | 'unknown';
|
|
14
|
+
interface OfficialProviderTerminalState {
|
|
15
|
+
mode: 'official-provider';
|
|
16
|
+
credentials: OAuthCredentialBundle;
|
|
17
|
+
subscriptionType?: SubscriptionType;
|
|
18
|
+
rateLimitTier?: string;
|
|
19
|
+
}
|
|
20
|
+
interface CompatOAuthTerminalState {
|
|
21
|
+
mode: 'compat-oauth';
|
|
22
|
+
credentials: OAuthCredentialBundle;
|
|
23
|
+
providerKey: string;
|
|
24
|
+
providerLabel?: string;
|
|
25
|
+
}
|
|
26
|
+
interface ApiKeyTerminalState {
|
|
27
|
+
mode: 'api-key';
|
|
28
|
+
apiKey: string;
|
|
29
|
+
source: 'user-supplied' | 'derived-from-console-oauth';
|
|
30
|
+
}
|
|
31
|
+
type CredentialTerminalState = OfficialProviderTerminalState | CompatOAuthTerminalState | ApiKeyTerminalState;
|
|
32
|
+
|
|
33
|
+
interface StoredCredentialEnvelope {
|
|
34
|
+
terminal: CredentialTerminalState;
|
|
35
|
+
updatedAt: number;
|
|
36
|
+
}
|
|
37
|
+
interface StorageAdapter {
|
|
38
|
+
read(): Promise<StoredCredentialEnvelope | null>;
|
|
39
|
+
write(envelope: StoredCredentialEnvelope): Promise<void>;
|
|
40
|
+
clear(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ClaudeAiOauthStorageRecord {
|
|
44
|
+
accessToken: string;
|
|
45
|
+
refreshToken: string;
|
|
46
|
+
expiresAt: number;
|
|
47
|
+
scopes: readonly string[];
|
|
48
|
+
subscriptionType?: string | null;
|
|
49
|
+
rateLimitTier?: string | null;
|
|
50
|
+
}
|
|
51
|
+
interface SecureStorageAdapter {
|
|
52
|
+
readOAuth?(): Promise<ClaudeAiOauthStorageRecord | null>;
|
|
53
|
+
writeOAuth?(value: ClaudeAiOauthStorageRecord): Promise<void>;
|
|
54
|
+
clearOAuth?(): Promise<void>;
|
|
55
|
+
readApiKey?(): Promise<string | null>;
|
|
56
|
+
writeApiKey?(value: string): Promise<void>;
|
|
57
|
+
clearApiKey?(): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
interface NodeStorageAdapterOptions {
|
|
60
|
+
configDir?: string;
|
|
61
|
+
platform?: NodeJS.Platform;
|
|
62
|
+
secureStorage?: SecureStorageAdapter;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare function createNodeDefaultStorageAdapter(options?: NodeStorageAdapterOptions): StorageAdapter;
|
|
66
|
+
|
|
67
|
+
type LoginMode = 'claudeai' | 'console';
|
|
68
|
+
interface LoginResult {
|
|
69
|
+
readonly mode: LoginMode;
|
|
70
|
+
readonly loggedIn: true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface LoginInternalOptions {
|
|
74
|
+
configDir?: string;
|
|
75
|
+
openBrowserFn?: (url: string) => Promise<boolean>;
|
|
76
|
+
fetchImpl?: typeof fetch;
|
|
77
|
+
}
|
|
78
|
+
declare function login(mode: LoginMode, internalOptions?: LoginInternalOptions): Promise<LoginResult>;
|
|
79
|
+
|
|
80
|
+
type ExecFileFn = (cmd: string, args: string[], cb: (err: Error | null) => void) => void;
|
|
81
|
+
interface OpenBrowserOptions {
|
|
82
|
+
platform?: NodeJS.Platform;
|
|
83
|
+
execFileFn?: ExecFileFn;
|
|
84
|
+
}
|
|
85
|
+
declare function openBrowser(url: string, options?: OpenBrowserOptions): Promise<boolean>;
|
|
86
|
+
|
|
87
|
+
export { LoginError, type LoginErrorCode, type LoginInternalOptions, type LoginMode, type LoginResult, type OAuthCredentialBundle, type StoredCredentialEnvelope, createNodeDefaultStorageAdapter, login, openBrowser };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var LoginError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(message, code, cause) {
|
|
5
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
6
|
+
this.name = "LoginError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/internal/storage/node.ts
|
|
12
|
+
import { mkdir, readFile, stat, unlink, writeFile } from "fs/promises";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
import { dirname, join } from "path";
|
|
15
|
+
|
|
16
|
+
// src/internal/core/errors.ts
|
|
17
|
+
var ClaudeAuthError = class extends Error {
|
|
18
|
+
code;
|
|
19
|
+
constructor(message, code, cause) {
|
|
20
|
+
super(message, cause === void 0 ? void 0 : { cause });
|
|
21
|
+
this.name = new.target.name;
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var UnsupportedRuntimeError = class extends ClaudeAuthError {
|
|
26
|
+
runtime;
|
|
27
|
+
constructor(runtime, message) {
|
|
28
|
+
super(message ?? `Unsupported runtime: ${runtime}.`, "UNSUPPORTED_RUNTIME");
|
|
29
|
+
this.runtime = runtime;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var StorageFailureError = class extends ClaudeAuthError {
|
|
33
|
+
constructor(message = "Failed to access credential storage.", cause) {
|
|
34
|
+
super(message, "STORAGE_FAILURE", cause);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/internal/storage/normalize.ts
|
|
39
|
+
function normalizeStringArray(value) {
|
|
40
|
+
if (!Array.isArray(value)) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
return value.filter((entry) => typeof entry === "string");
|
|
44
|
+
}
|
|
45
|
+
function normalizeExpiresAt(value) {
|
|
46
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === "string") {
|
|
50
|
+
const parsed = Number(value);
|
|
51
|
+
if (Number.isFinite(parsed)) {
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
function normalizeOAuthRecord(record) {
|
|
58
|
+
return {
|
|
59
|
+
accessToken: typeof record.accessToken === "string" ? record.accessToken : "",
|
|
60
|
+
refreshToken: typeof record.refreshToken === "string" ? record.refreshToken : "",
|
|
61
|
+
expiresAt: normalizeExpiresAt(record.expiresAt),
|
|
62
|
+
scopes: normalizeStringArray(record.scopes),
|
|
63
|
+
subscriptionType: typeof record.subscriptionType === "string" ? record.subscriptionType : null,
|
|
64
|
+
rateLimitTier: typeof record.rateLimitTier === "string" ? record.rateLimitTier : null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function normalizeStoredOauthEnvelope(record) {
|
|
68
|
+
const normalized = normalizeOAuthRecord(record);
|
|
69
|
+
return {
|
|
70
|
+
mode: "compat-oauth",
|
|
71
|
+
credentials: {
|
|
72
|
+
accessToken: normalized.accessToken,
|
|
73
|
+
refreshToken: normalized.refreshToken,
|
|
74
|
+
expiresAt: normalized.expiresAt,
|
|
75
|
+
scopes: normalized.scopes
|
|
76
|
+
},
|
|
77
|
+
metadata: {
|
|
78
|
+
subscriptionType: normalized.subscriptionType === null ? void 0 : normalized.subscriptionType,
|
|
79
|
+
rateLimitTier: normalized.rateLimitTier === null ? void 0 : normalized.rateLimitTier
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function serializeOAuthTerminalState(terminal) {
|
|
84
|
+
return {
|
|
85
|
+
accessToken: terminal.credentials.accessToken,
|
|
86
|
+
refreshToken: terminal.credentials.refreshToken,
|
|
87
|
+
expiresAt: terminal.credentials.expiresAt,
|
|
88
|
+
scopes: [...terminal.credentials.scopes],
|
|
89
|
+
subscriptionType: terminal.mode === "official-provider" ? terminal.subscriptionType ?? null : null,
|
|
90
|
+
rateLimitTier: terminal.mode === "official-provider" ? terminal.rateLimitTier ?? null : null
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function normalizeApiKeyTerminalState(config) {
|
|
94
|
+
if (typeof config.primaryApiKey !== "string" || config.primaryApiKey.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const state = {
|
|
98
|
+
mode: "api-key",
|
|
99
|
+
apiKey: config.primaryApiKey,
|
|
100
|
+
source: config.customApiKeyResponses?.approved ? "derived-from-console-oauth" : "user-supplied"
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
mode: "api-key",
|
|
104
|
+
state
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function toStoredEnvelopeFromNativeShapes(params) {
|
|
108
|
+
const oauthRecord = params.credentialsFile.claudeAiOauth;
|
|
109
|
+
if (oauthRecord !== void 0) {
|
|
110
|
+
const normalized = normalizeStoredOauthEnvelope(oauthRecord);
|
|
111
|
+
const terminal = {
|
|
112
|
+
mode: normalized.mode,
|
|
113
|
+
credentials: normalized.credentials,
|
|
114
|
+
providerKey: "claudeai",
|
|
115
|
+
providerLabel: "Claude.ai compatibility lane"
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
terminal,
|
|
119
|
+
updatedAt: params.updatedAt
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const normalizedApiKey = normalizeApiKeyTerminalState(params.configFile);
|
|
123
|
+
if (normalizedApiKey === null) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
terminal: normalizedApiKey.state,
|
|
128
|
+
updatedAt: params.updatedAt
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function updateConfigWithApiKey(params) {
|
|
132
|
+
const nextApproved = params.terminal.source === "derived-from-console-oauth";
|
|
133
|
+
return {
|
|
134
|
+
...params.configFile,
|
|
135
|
+
primaryApiKey: params.terminal.apiKey,
|
|
136
|
+
customApiKeyResponses: {
|
|
137
|
+
...params.configFile.customApiKeyResponses,
|
|
138
|
+
approved: nextApproved
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/internal/storage/node.ts
|
|
144
|
+
var CREDENTIALS_FILE_NAME = ".credentials.json";
|
|
145
|
+
var CONFIG_FILE_NAME = "config.json";
|
|
146
|
+
function isErrnoCode(error, code) {
|
|
147
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === code;
|
|
148
|
+
}
|
|
149
|
+
async function readJsonOrDefault(filePath, defaultValue) {
|
|
150
|
+
try {
|
|
151
|
+
const content = await readFile(filePath, "utf8");
|
|
152
|
+
const parsed = JSON.parse(content);
|
|
153
|
+
return parsed ?? defaultValue;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (isErrnoCode(error, "ENOENT")) {
|
|
156
|
+
return defaultValue;
|
|
157
|
+
}
|
|
158
|
+
throw new StorageFailureError(`Failed to read storage file: ${filePath}`, error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function writeJson(filePath, value) {
|
|
162
|
+
try {
|
|
163
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
164
|
+
await writeFile(filePath, JSON.stringify(value, null, 2), { mode: 384, encoding: "utf8" });
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new StorageFailureError(`Failed to write storage file: ${filePath}`, error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function safeUnlink(filePath) {
|
|
170
|
+
try {
|
|
171
|
+
await unlink(filePath);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (isErrnoCode(error, "ENOENT")) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
throw new StorageFailureError(`Failed to remove storage file: ${filePath}`, error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function getUpdatedAt(paths) {
|
|
180
|
+
const mtimes = [];
|
|
181
|
+
for (const filePath of [paths.credentialsFilePath, paths.configFilePath]) {
|
|
182
|
+
try {
|
|
183
|
+
const metadata = await stat(filePath);
|
|
184
|
+
mtimes.push(metadata.mtimeMs);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (!isErrnoCode(error, "ENOENT")) {
|
|
187
|
+
throw new StorageFailureError(`Failed to stat storage file: ${filePath}`, error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (mtimes.length === 0) {
|
|
192
|
+
return Date.now();
|
|
193
|
+
}
|
|
194
|
+
return Math.max(...mtimes);
|
|
195
|
+
}
|
|
196
|
+
async function readNativeShapes(paths) {
|
|
197
|
+
const [credentialsFile, configFile] = await Promise.all([
|
|
198
|
+
readJsonOrDefault(paths.credentialsFilePath, {}),
|
|
199
|
+
readJsonOrDefault(paths.configFilePath, {})
|
|
200
|
+
]);
|
|
201
|
+
return {
|
|
202
|
+
credentialsFile,
|
|
203
|
+
configFile
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function isMacOS(platform) {
|
|
207
|
+
return platform === "darwin";
|
|
208
|
+
}
|
|
209
|
+
function resolveNodeCredentialPaths(configDir) {
|
|
210
|
+
const resolvedConfigDir = configDir ?? process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude");
|
|
211
|
+
return {
|
|
212
|
+
configDir: resolvedConfigDir,
|
|
213
|
+
credentialsFilePath: join(resolvedConfigDir, CREDENTIALS_FILE_NAME),
|
|
214
|
+
configFilePath: join(resolvedConfigDir, CONFIG_FILE_NAME)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function createNodeDefaultStorageAdapter(options = {}) {
|
|
218
|
+
const paths = resolveNodeCredentialPaths(options.configDir);
|
|
219
|
+
const platform = options.platform ?? process.platform;
|
|
220
|
+
const secureStorage = options.secureStorage;
|
|
221
|
+
const secureFirst = isMacOS(platform) && secureStorage !== void 0;
|
|
222
|
+
const readSecureFirst = async () => {
|
|
223
|
+
if (!secureFirst) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const oauthRecord = await secureStorage.readOAuth?.();
|
|
227
|
+
if (oauthRecord !== null && oauthRecord !== void 0) {
|
|
228
|
+
return {
|
|
229
|
+
terminal: {
|
|
230
|
+
mode: "compat-oauth",
|
|
231
|
+
credentials: {
|
|
232
|
+
accessToken: oauthRecord.accessToken,
|
|
233
|
+
refreshToken: oauthRecord.refreshToken,
|
|
234
|
+
expiresAt: oauthRecord.expiresAt,
|
|
235
|
+
scopes: oauthRecord.scopes
|
|
236
|
+
},
|
|
237
|
+
providerKey: "claudeai",
|
|
238
|
+
providerLabel: "Claude.ai compatibility lane"
|
|
239
|
+
},
|
|
240
|
+
updatedAt: Date.now()
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const apiKey = await secureStorage.readApiKey?.();
|
|
244
|
+
if (typeof apiKey === "string" && apiKey.length > 0) {
|
|
245
|
+
return {
|
|
246
|
+
terminal: {
|
|
247
|
+
mode: "api-key",
|
|
248
|
+
apiKey,
|
|
249
|
+
source: "derived-from-console-oauth"
|
|
250
|
+
},
|
|
251
|
+
updatedAt: Date.now()
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
};
|
|
256
|
+
return {
|
|
257
|
+
async read() {
|
|
258
|
+
if (secureFirst) {
|
|
259
|
+
const secureValue = await readSecureFirst();
|
|
260
|
+
if (secureValue !== null) {
|
|
261
|
+
return secureValue;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const nativeShapes = await readNativeShapes(paths);
|
|
265
|
+
const updatedAt = await getUpdatedAt(paths);
|
|
266
|
+
return toStoredEnvelopeFromNativeShapes({
|
|
267
|
+
...nativeShapes,
|
|
268
|
+
updatedAt
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
async write(envelope) {
|
|
272
|
+
if (envelope.terminal.mode === "api-key") {
|
|
273
|
+
if (secureFirst && secureStorage.writeApiKey !== void 0) {
|
|
274
|
+
await secureStorage.writeApiKey(envelope.terminal.apiKey);
|
|
275
|
+
}
|
|
276
|
+
const configFile = await readJsonOrDefault(paths.configFilePath, {});
|
|
277
|
+
const nextConfigFile = updateConfigWithApiKey({
|
|
278
|
+
configFile,
|
|
279
|
+
terminal: envelope.terminal
|
|
280
|
+
});
|
|
281
|
+
await writeJson(paths.configFilePath, nextConfigFile);
|
|
282
|
+
const credentialsFile2 = await readJsonOrDefault(
|
|
283
|
+
paths.credentialsFilePath,
|
|
284
|
+
{}
|
|
285
|
+
);
|
|
286
|
+
if (credentialsFile2.claudeAiOauth !== void 0) {
|
|
287
|
+
const { claudeAiOauth: _omit, ...rest } = credentialsFile2;
|
|
288
|
+
await writeJson(paths.credentialsFilePath, rest);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const oauthRecord = serializeOAuthTerminalState(envelope.terminal);
|
|
293
|
+
if (secureFirst && secureStorage.writeOAuth !== void 0) {
|
|
294
|
+
await secureStorage.writeOAuth(oauthRecord);
|
|
295
|
+
}
|
|
296
|
+
const credentialsFile = await readJsonOrDefault(
|
|
297
|
+
paths.credentialsFilePath,
|
|
298
|
+
{}
|
|
299
|
+
);
|
|
300
|
+
const nextCredentialsFile = {
|
|
301
|
+
...credentialsFile,
|
|
302
|
+
claudeAiOauth: oauthRecord
|
|
303
|
+
};
|
|
304
|
+
await writeJson(paths.credentialsFilePath, nextCredentialsFile);
|
|
305
|
+
},
|
|
306
|
+
async clear() {
|
|
307
|
+
if (secureFirst) {
|
|
308
|
+
await Promise.all([secureStorage.clearOAuth?.(), secureStorage.clearApiKey?.()]);
|
|
309
|
+
}
|
|
310
|
+
await Promise.all([safeUnlink(paths.credentialsFilePath), safeUnlink(paths.configFilePath)]);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/internal/browser/constants.ts
|
|
316
|
+
var DEFAULT_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
317
|
+
var DEFAULT_OAUTH_SCOPE = [
|
|
318
|
+
"org:create_api_key",
|
|
319
|
+
"user:profile",
|
|
320
|
+
"user:inference",
|
|
321
|
+
"user:sessions:claude_code",
|
|
322
|
+
"user:mcp_servers",
|
|
323
|
+
"user:file_upload"
|
|
324
|
+
];
|
|
325
|
+
var COMPAT_AUTHORIZE_ENDPOINTS = {
|
|
326
|
+
claudeai: "https://claude.com/cai/oauth/authorize",
|
|
327
|
+
console: "https://platform.claude.com/oauth/authorize"
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/internal/browser/pkce.ts
|
|
331
|
+
var PKCE_VERIFIER_LENGTH_BYTES = 64;
|
|
332
|
+
var STATE_LENGTH_BYTES = 32;
|
|
333
|
+
function requireWebCrypto() {
|
|
334
|
+
if (typeof globalThis.crypto === "undefined") {
|
|
335
|
+
throw new UnsupportedRuntimeError(
|
|
336
|
+
"browser",
|
|
337
|
+
"Web Crypto API is required for browser auth initiation."
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (typeof globalThis.crypto.getRandomValues !== "function") {
|
|
341
|
+
throw new UnsupportedRuntimeError(
|
|
342
|
+
"browser",
|
|
343
|
+
"crypto.getRandomValues is unavailable in this runtime."
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (typeof globalThis.crypto.subtle === "undefined") {
|
|
347
|
+
throw new UnsupportedRuntimeError("browser", "crypto.subtle is unavailable in this runtime.");
|
|
348
|
+
}
|
|
349
|
+
return globalThis.crypto;
|
|
350
|
+
}
|
|
351
|
+
function bytesToBase64Url(bytes) {
|
|
352
|
+
let binary = "";
|
|
353
|
+
for (const byte of bytes) {
|
|
354
|
+
binary += String.fromCharCode(byte);
|
|
355
|
+
}
|
|
356
|
+
const base64 = typeof globalThis.btoa === "function" ? globalThis.btoa(binary) : Buffer.from(binary, "binary").toString("base64");
|
|
357
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
358
|
+
}
|
|
359
|
+
function createRandomBase64Url(byteLength) {
|
|
360
|
+
const crypto = requireWebCrypto();
|
|
361
|
+
const bytes = new Uint8Array(byteLength);
|
|
362
|
+
crypto.getRandomValues(bytes);
|
|
363
|
+
return bytesToBase64Url(bytes);
|
|
364
|
+
}
|
|
365
|
+
function createState() {
|
|
366
|
+
return createRandomBase64Url(STATE_LENGTH_BYTES);
|
|
367
|
+
}
|
|
368
|
+
function createPkceVerifier() {
|
|
369
|
+
return createRandomBase64Url(PKCE_VERIFIER_LENGTH_BYTES);
|
|
370
|
+
}
|
|
371
|
+
async function createPkceChallenge(verifier) {
|
|
372
|
+
const crypto = requireWebCrypto();
|
|
373
|
+
const source = new TextEncoder().encode(verifier);
|
|
374
|
+
const digest = await crypto.subtle.digest("SHA-256", source);
|
|
375
|
+
return bytesToBase64Url(new Uint8Array(digest));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/internal/server/callback/exchange.ts
|
|
379
|
+
var DEFAULT_COMPAT_OAUTH_ENDPOINTS = {
|
|
380
|
+
claudeai: {
|
|
381
|
+
authorizeEndpoint: "https://claude.com/cai/oauth/authorize",
|
|
382
|
+
tokenEndpoint: "https://platform.claude.com/v1/oauth/token"
|
|
383
|
+
},
|
|
384
|
+
console: {
|
|
385
|
+
authorizeEndpoint: "https://platform.claude.com/oauth/authorize",
|
|
386
|
+
tokenEndpoint: "https://platform.claude.com/v1/oauth/token"
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var COMPAT_PROVIDER_LABELS = {
|
|
390
|
+
claudeai: "Claude.ai",
|
|
391
|
+
console: "Anthropic Console"
|
|
392
|
+
};
|
|
393
|
+
function resolveCompatOAuthEndpointConfig(modeId, override) {
|
|
394
|
+
const defaults = DEFAULT_COMPAT_OAUTH_ENDPOINTS[modeId];
|
|
395
|
+
return {
|
|
396
|
+
authorizeEndpoint: override?.authorizeEndpoint ?? defaults.authorizeEndpoint,
|
|
397
|
+
tokenEndpoint: override?.tokenEndpoint ?? defaults.tokenEndpoint
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async function exchangeCompatOAuthCodeForCredentials(request, options = {}) {
|
|
401
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
402
|
+
const now = options.now ?? Date.now;
|
|
403
|
+
const endpointConfig = resolveCompatOAuthEndpointConfig(request.modeId, request.endpointConfig);
|
|
404
|
+
const response = await fetchImpl(endpointConfig.tokenEndpoint, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: {
|
|
407
|
+
"Content-Type": "application/json"
|
|
408
|
+
},
|
|
409
|
+
body: JSON.stringify({
|
|
410
|
+
grant_type: "authorization_code",
|
|
411
|
+
code: request.code,
|
|
412
|
+
redirect_uri: request.redirectUri,
|
|
413
|
+
client_id: request.clientId,
|
|
414
|
+
code_verifier: request.codeVerifier,
|
|
415
|
+
state: request.state,
|
|
416
|
+
scope: request.scope
|
|
417
|
+
})
|
|
418
|
+
});
|
|
419
|
+
const responseData = await tryReadTokenResponse(response);
|
|
420
|
+
if (!response.ok) {
|
|
421
|
+
const rawMessage = responseData.error_description ?? responseData.error ?? `Token exchange failed with HTTP ${response.status}.`;
|
|
422
|
+
const message = typeof rawMessage === "string" ? rawMessage : JSON.stringify(rawMessage);
|
|
423
|
+
throw new Error(message);
|
|
424
|
+
}
|
|
425
|
+
const accessToken = responseData.access_token;
|
|
426
|
+
if (!accessToken) {
|
|
427
|
+
throw new Error("Token exchange succeeded without access_token.");
|
|
428
|
+
}
|
|
429
|
+
const expiresIn = coerceExpiresInSeconds(responseData.expires_in);
|
|
430
|
+
const scope = responseData.scope ?? request.scope ?? "";
|
|
431
|
+
const scopes = scope.trim() === "" ? [] : scope.trim().split(/\s+/);
|
|
432
|
+
return {
|
|
433
|
+
ok: true,
|
|
434
|
+
terminal: {
|
|
435
|
+
mode: "compat-oauth",
|
|
436
|
+
providerKey: request.modeId,
|
|
437
|
+
providerLabel: COMPAT_PROVIDER_LABELS[request.modeId],
|
|
438
|
+
credentials: {
|
|
439
|
+
accessToken,
|
|
440
|
+
refreshToken: responseData.refresh_token ?? "",
|
|
441
|
+
expiresAt: now() + expiresIn * 1e3,
|
|
442
|
+
scopes
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async function tryReadTokenResponse(response) {
|
|
448
|
+
try {
|
|
449
|
+
const payload = await response.json();
|
|
450
|
+
return payload;
|
|
451
|
+
} catch {
|
|
452
|
+
return {};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function coerceExpiresInSeconds(value) {
|
|
456
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
460
|
+
const parsed = Number(value);
|
|
461
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
462
|
+
return parsed;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return 0;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/runtime/callback-server.ts
|
|
469
|
+
import { createServer } from "http";
|
|
470
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
471
|
+
var SUCCESS_HTML = `<!DOCTYPE html>
|
|
472
|
+
<html><body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0">
|
|
473
|
+
<div style="text-align:center">
|
|
474
|
+
<h2>login successful</h2>
|
|
475
|
+
<p id="msg">This tab will close in <span id="count">3</span> seconds...</p>
|
|
476
|
+
</div>
|
|
477
|
+
<script>
|
|
478
|
+
let n=3;
|
|
479
|
+
const c=document.getElementById("count");
|
|
480
|
+
const m=document.getElementById("msg");
|
|
481
|
+
const t=setInterval(()=>{n--;if(n>0){c.textContent=n}else{clearInterval(t);window.close();m.textContent="You can close this tab."}},1000);
|
|
482
|
+
</script>
|
|
483
|
+
</body></html>`;
|
|
484
|
+
async function startCallbackServer() {
|
|
485
|
+
let pendingCallback;
|
|
486
|
+
let resolveCallback;
|
|
487
|
+
let timeoutHandle;
|
|
488
|
+
function clearPendingWait() {
|
|
489
|
+
if (timeoutHandle !== void 0) {
|
|
490
|
+
clearTimeout(timeoutHandle);
|
|
491
|
+
timeoutHandle = void 0;
|
|
492
|
+
}
|
|
493
|
+
resolveCallback = void 0;
|
|
494
|
+
}
|
|
495
|
+
const server = createServer((req, res) => {
|
|
496
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
497
|
+
if (url.pathname !== "/callback") {
|
|
498
|
+
res.writeHead(404);
|
|
499
|
+
res.end();
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const params = {
|
|
503
|
+
code: url.searchParams.get("code") ?? void 0,
|
|
504
|
+
state: url.searchParams.get("state") ?? void 0,
|
|
505
|
+
error: url.searchParams.get("error") ?? void 0,
|
|
506
|
+
errorDescription: url.searchParams.get("error_description") ?? void 0
|
|
507
|
+
};
|
|
508
|
+
res.shouldKeepAlive = false;
|
|
509
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
510
|
+
res.end(SUCCESS_HTML);
|
|
511
|
+
if (resolveCallback !== void 0) {
|
|
512
|
+
const resolve = resolveCallback;
|
|
513
|
+
clearPendingWait();
|
|
514
|
+
resolve(params);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
pendingCallback = params;
|
|
518
|
+
});
|
|
519
|
+
const port = await new Promise((resolve, reject) => {
|
|
520
|
+
server.listen(0, "localhost", () => {
|
|
521
|
+
const addr = server.address();
|
|
522
|
+
if (addr === null || typeof addr === "string") {
|
|
523
|
+
reject(new Error("Failed to bind callback server"));
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
resolve(addr.port);
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
return {
|
|
530
|
+
port,
|
|
531
|
+
waitForCallback(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
532
|
+
if (pendingCallback !== void 0) {
|
|
533
|
+
const buffered = pendingCallback;
|
|
534
|
+
pendingCallback = void 0;
|
|
535
|
+
return Promise.resolve(buffered);
|
|
536
|
+
}
|
|
537
|
+
return new Promise((resolve, reject) => {
|
|
538
|
+
resolveCallback = resolve;
|
|
539
|
+
timeoutHandle = setTimeout(() => {
|
|
540
|
+
clearPendingWait();
|
|
541
|
+
reject(new Error("Login timeout: no callback received"));
|
|
542
|
+
}, timeoutMs);
|
|
543
|
+
timeoutHandle.unref();
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
close() {
|
|
547
|
+
return new Promise((resolve) => {
|
|
548
|
+
server.closeIdleConnections?.();
|
|
549
|
+
server.close(() => {
|
|
550
|
+
resolve();
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/runtime/open-browser.ts
|
|
558
|
+
import { execFile as defaultExecFile } from "child_process";
|
|
559
|
+
async function openBrowser(url, options = {}) {
|
|
560
|
+
const platform = options.platform ?? process.platform;
|
|
561
|
+
const execFileFn = options.execFileFn ?? defaultExecFile;
|
|
562
|
+
const { cmd, args } = getOpenCommand(platform, url);
|
|
563
|
+
return new Promise((resolve) => {
|
|
564
|
+
execFileFn(cmd, args, (err) => {
|
|
565
|
+
resolve(err === null);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function getOpenCommand(platform, url) {
|
|
570
|
+
switch (platform) {
|
|
571
|
+
case "darwin":
|
|
572
|
+
return { cmd: "open", args: [url] };
|
|
573
|
+
case "win32":
|
|
574
|
+
return { cmd: "cmd", args: ["/c", "start", "", url] };
|
|
575
|
+
default:
|
|
576
|
+
return { cmd: "xdg-open", args: [url] };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/login.ts
|
|
581
|
+
var CREATE_API_KEY_URL = "https://api.anthropic.com/api/oauth/claude_cli/create_api_key";
|
|
582
|
+
async function createApiKeyFromAccessToken(accessToken, fetchImpl) {
|
|
583
|
+
const doFetch = fetchImpl ?? fetch;
|
|
584
|
+
const response = await doFetch(CREATE_API_KEY_URL, {
|
|
585
|
+
method: "POST",
|
|
586
|
+
headers: {
|
|
587
|
+
Authorization: `Bearer ${accessToken}`
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
if (!response.ok) {
|
|
591
|
+
throw new LoginError(`API key creation failed with HTTP ${response.status}`, "exchange_failed");
|
|
592
|
+
}
|
|
593
|
+
const data = await response.json();
|
|
594
|
+
if (!data.raw_key) {
|
|
595
|
+
throw new LoginError("API key creation succeeded but no key returned", "exchange_failed");
|
|
596
|
+
}
|
|
597
|
+
return data.raw_key;
|
|
598
|
+
}
|
|
599
|
+
async function login(mode, internalOptions) {
|
|
600
|
+
const server = await startCallbackServer();
|
|
601
|
+
try {
|
|
602
|
+
const state = createState();
|
|
603
|
+
const verifier = createPkceVerifier();
|
|
604
|
+
const challenge = await createPkceChallenge(verifier);
|
|
605
|
+
const redirectUri = `http://localhost:${server.port}/callback`;
|
|
606
|
+
const scope = DEFAULT_OAUTH_SCOPE.join(" ");
|
|
607
|
+
const params = new URLSearchParams({
|
|
608
|
+
code: "true",
|
|
609
|
+
client_id: DEFAULT_OAUTH_CLIENT_ID,
|
|
610
|
+
response_type: "code",
|
|
611
|
+
scope,
|
|
612
|
+
code_challenge: challenge,
|
|
613
|
+
code_challenge_method: "S256",
|
|
614
|
+
state,
|
|
615
|
+
redirect_uri: redirectUri
|
|
616
|
+
});
|
|
617
|
+
const authorizeUrl = `${COMPAT_AUTHORIZE_ENDPOINTS[mode]}?${params.toString()}`;
|
|
618
|
+
const openFn = internalOptions?.openBrowserFn ?? openBrowser;
|
|
619
|
+
const opened = await openFn(authorizeUrl);
|
|
620
|
+
if (!opened) {
|
|
621
|
+
process.stdout.write(`
|
|
622
|
+
Open this URL in your browser to log in:
|
|
623
|
+
${authorizeUrl}
|
|
624
|
+
|
|
625
|
+
`);
|
|
626
|
+
}
|
|
627
|
+
const callbackParams = await server.waitForCallback();
|
|
628
|
+
if (callbackParams.error) {
|
|
629
|
+
throw new LoginError(callbackParams.errorDescription ?? callbackParams.error, "cancelled");
|
|
630
|
+
}
|
|
631
|
+
if (!callbackParams.code || !callbackParams.state) {
|
|
632
|
+
throw new LoginError("Missing authorization code in callback", "cancelled");
|
|
633
|
+
}
|
|
634
|
+
if (callbackParams.state !== state) {
|
|
635
|
+
throw new LoginError("OAuth state mismatch", "cancelled");
|
|
636
|
+
}
|
|
637
|
+
const credentialResult = await exchangeCompatOAuthCodeForCredentials(
|
|
638
|
+
{
|
|
639
|
+
modeId: mode,
|
|
640
|
+
code: callbackParams.code,
|
|
641
|
+
state: callbackParams.state,
|
|
642
|
+
codeVerifier: verifier,
|
|
643
|
+
redirectUri,
|
|
644
|
+
clientId: DEFAULT_OAUTH_CLIENT_ID,
|
|
645
|
+
scope
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
fetchImpl: internalOptions?.fetchImpl
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
if (!credentialResult.ok) {
|
|
652
|
+
throw new LoginError(credentialResult.message, "exchange_failed");
|
|
653
|
+
}
|
|
654
|
+
const storage = createNodeDefaultStorageAdapter({
|
|
655
|
+
configDir: internalOptions?.configDir
|
|
656
|
+
});
|
|
657
|
+
if (mode === "console") {
|
|
658
|
+
if (credentialResult.terminal.mode !== "compat-oauth") {
|
|
659
|
+
throw new LoginError("Unexpected terminal state for console mode", "exchange_failed");
|
|
660
|
+
}
|
|
661
|
+
const apiKey = await createApiKeyFromAccessToken(
|
|
662
|
+
credentialResult.terminal.credentials.accessToken,
|
|
663
|
+
internalOptions?.fetchImpl
|
|
664
|
+
);
|
|
665
|
+
try {
|
|
666
|
+
await storage.write({
|
|
667
|
+
terminal: {
|
|
668
|
+
mode: "api-key",
|
|
669
|
+
apiKey,
|
|
670
|
+
source: "derived-from-console-oauth"
|
|
671
|
+
},
|
|
672
|
+
updatedAt: Date.now()
|
|
673
|
+
});
|
|
674
|
+
} catch (cause) {
|
|
675
|
+
throw new LoginError("Failed to save credentials", "storage_failed", cause);
|
|
676
|
+
}
|
|
677
|
+
} else {
|
|
678
|
+
try {
|
|
679
|
+
await storage.write({
|
|
680
|
+
terminal: credentialResult.terminal,
|
|
681
|
+
updatedAt: Date.now()
|
|
682
|
+
});
|
|
683
|
+
} catch (cause) {
|
|
684
|
+
throw new LoginError("Failed to save credentials", "storage_failed", cause);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return { mode, loggedIn: true };
|
|
688
|
+
} catch (error) {
|
|
689
|
+
if (error instanceof LoginError) {
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
throw new LoginError(
|
|
693
|
+
error instanceof Error ? error.message : "Login failed",
|
|
694
|
+
"exchange_failed",
|
|
695
|
+
error
|
|
696
|
+
);
|
|
697
|
+
} finally {
|
|
698
|
+
await server.close();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
export {
|
|
702
|
+
LoginError,
|
|
703
|
+
createNodeDefaultStorageAdapter,
|
|
704
|
+
login,
|
|
705
|
+
openBrowser
|
|
706
|
+
};
|
|
707
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/internal/storage/node.ts","../src/internal/core/errors.ts","../src/internal/storage/normalize.ts","../src/internal/browser/constants.ts","../src/internal/browser/pkce.ts","../src/internal/server/callback/exchange.ts","../src/runtime/callback-server.ts","../src/runtime/open-browser.ts","../src/login.ts"],"sourcesContent":["export type LoginErrorCode = 'timeout' | 'cancelled' | 'exchange_failed' | 'storage_failed';\n\nexport class LoginError extends Error {\n public readonly code: LoginErrorCode;\n\n public constructor(message: string, code: LoginErrorCode, cause?: unknown) {\n super(message, cause === undefined ? undefined : { cause });\n this.name = 'LoginError';\n this.code = code;\n }\n}\n","import { mkdir, readFile, stat, unlink, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nimport type { StorageAdapter, StoredCredentialEnvelope } from '../core/contracts.js';\nimport { StorageFailureError } from '../core/errors.js';\nimport {\n serializeOAuthTerminalState,\n toStoredEnvelopeFromNativeShapes,\n updateConfigWithApiKey,\n} from './normalize.js';\nimport type {\n ConfigFileShape,\n CredentialsFileShape,\n NodeCredentialPaths,\n NodeStorageAdapterOptions,\n} from './types.js';\n\nconst CREDENTIALS_FILE_NAME = '.credentials.json';\nconst CONFIG_FILE_NAME = 'config.json';\n\nfunction isErrnoCode(error: unknown, code: string): boolean {\n return (\n error !== null &&\n typeof error === 'object' &&\n 'code' in error &&\n (error as NodeJS.ErrnoException).code === code\n );\n}\n\nasync function readJsonOrDefault<T>(filePath: string, defaultValue: T): Promise<T> {\n try {\n const content = await readFile(filePath, 'utf8');\n const parsed = JSON.parse(content) as unknown;\n return (parsed as T) ?? defaultValue;\n } catch (error) {\n if (isErrnoCode(error, 'ENOENT')) {\n return defaultValue;\n }\n\n throw new StorageFailureError(`Failed to read storage file: ${filePath}`, error);\n }\n}\n\nasync function writeJson(filePath: string, value: unknown): Promise<void> {\n try {\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, JSON.stringify(value, null, 2), { mode: 0o600, encoding: 'utf8' });\n } catch (error) {\n throw new StorageFailureError(`Failed to write storage file: ${filePath}`, error);\n }\n}\n\nasync function safeUnlink(filePath: string): Promise<void> {\n try {\n await unlink(filePath);\n } catch (error) {\n if (isErrnoCode(error, 'ENOENT')) {\n return;\n }\n\n throw new StorageFailureError(`Failed to remove storage file: ${filePath}`, error);\n }\n}\n\nasync function getUpdatedAt(paths: NodeCredentialPaths): Promise<number> {\n const mtimes: number[] = [];\n for (const filePath of [paths.credentialsFilePath, paths.configFilePath]) {\n try {\n const metadata = await stat(filePath);\n mtimes.push(metadata.mtimeMs);\n } catch (error) {\n if (!isErrnoCode(error, 'ENOENT')) {\n throw new StorageFailureError(`Failed to stat storage file: ${filePath}`, error);\n }\n }\n }\n\n if (mtimes.length === 0) {\n return Date.now();\n }\n\n return Math.max(...mtimes);\n}\n\nasync function readNativeShapes(paths: NodeCredentialPaths): Promise<{\n credentialsFile: CredentialsFileShape;\n configFile: ConfigFileShape;\n}> {\n const [credentialsFile, configFile] = await Promise.all([\n readJsonOrDefault<CredentialsFileShape>(paths.credentialsFilePath, {}),\n readJsonOrDefault<ConfigFileShape>(paths.configFilePath, {}),\n ]);\n\n return {\n credentialsFile,\n configFile,\n };\n}\n\nfunction isMacOS(platform: NodeJS.Platform): boolean {\n return platform === 'darwin';\n}\n\nexport function resolveNodeCredentialPaths(configDir?: string): NodeCredentialPaths {\n const resolvedConfigDir =\n configDir ?? process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude');\n\n return {\n configDir: resolvedConfigDir,\n credentialsFilePath: join(resolvedConfigDir, CREDENTIALS_FILE_NAME),\n configFilePath: join(resolvedConfigDir, CONFIG_FILE_NAME),\n };\n}\n\nexport function createNodeDefaultStorageAdapter(\n options: NodeStorageAdapterOptions = {},\n): StorageAdapter {\n const paths = resolveNodeCredentialPaths(options.configDir);\n const platform = options.platform ?? process.platform;\n const secureStorage = options.secureStorage;\n const secureFirst = isMacOS(platform) && secureStorage !== undefined;\n\n const readSecureFirst = async (): Promise<StoredCredentialEnvelope | null> => {\n if (!secureFirst) {\n return null;\n }\n\n const oauthRecord = await secureStorage.readOAuth?.();\n if (oauthRecord !== null && oauthRecord !== undefined) {\n return {\n terminal: {\n mode: 'compat-oauth',\n credentials: {\n accessToken: oauthRecord.accessToken,\n refreshToken: oauthRecord.refreshToken,\n expiresAt: oauthRecord.expiresAt,\n scopes: oauthRecord.scopes,\n },\n providerKey: 'claudeai',\n providerLabel: 'Claude.ai compatibility lane',\n },\n updatedAt: Date.now(),\n };\n }\n\n const apiKey = await secureStorage.readApiKey?.();\n if (typeof apiKey === 'string' && apiKey.length > 0) {\n return {\n terminal: {\n mode: 'api-key',\n apiKey,\n source: 'derived-from-console-oauth',\n },\n updatedAt: Date.now(),\n };\n }\n\n return null;\n };\n\n return {\n async read(): Promise<StoredCredentialEnvelope | null> {\n if (secureFirst) {\n const secureValue = await readSecureFirst();\n if (secureValue !== null) {\n return secureValue;\n }\n }\n\n const nativeShapes = await readNativeShapes(paths);\n const updatedAt = await getUpdatedAt(paths);\n return toStoredEnvelopeFromNativeShapes({\n ...nativeShapes,\n updatedAt,\n });\n },\n\n async write(envelope: StoredCredentialEnvelope): Promise<void> {\n if (envelope.terminal.mode === 'api-key') {\n if (secureFirst && secureStorage.writeApiKey !== undefined) {\n await secureStorage.writeApiKey(envelope.terminal.apiKey);\n }\n\n const configFile = await readJsonOrDefault<ConfigFileShape>(paths.configFilePath, {});\n const nextConfigFile = updateConfigWithApiKey({\n configFile,\n terminal: envelope.terminal,\n });\n\n await writeJson(paths.configFilePath, nextConfigFile);\n\n const credentialsFile = await readJsonOrDefault<CredentialsFileShape>(\n paths.credentialsFilePath,\n {},\n );\n if (credentialsFile.claudeAiOauth !== undefined) {\n const { claudeAiOauth: _omit, ...rest } = credentialsFile;\n await writeJson(paths.credentialsFilePath, rest);\n }\n\n return;\n }\n\n const oauthRecord = serializeOAuthTerminalState(envelope.terminal);\n if (secureFirst && secureStorage.writeOAuth !== undefined) {\n await secureStorage.writeOAuth(oauthRecord);\n }\n\n const credentialsFile = await readJsonOrDefault<CredentialsFileShape>(\n paths.credentialsFilePath,\n {},\n );\n const nextCredentialsFile: CredentialsFileShape = {\n ...credentialsFile,\n claudeAiOauth: oauthRecord,\n };\n\n await writeJson(paths.credentialsFilePath, nextCredentialsFile);\n },\n\n async clear(): Promise<void> {\n if (secureFirst) {\n await Promise.all([secureStorage.clearOAuth?.(), secureStorage.clearApiKey?.()]);\n }\n\n await Promise.all([safeUnlink(paths.credentialsFilePath), safeUnlink(paths.configFilePath)]);\n },\n };\n}\n","import type { AuthMode } from './types.js';\n\nexport type ClaudeAuthErrorCode =\n | 'INVALID_STATE'\n | 'MISSING_CODE'\n | 'UNSUPPORTED_RUNTIME'\n | 'POLICY_GATED_MODE'\n | 'REFRESH_FAILURE'\n | 'STORAGE_FAILURE';\n\nexport class ClaudeAuthError extends Error {\n public readonly code: ClaudeAuthErrorCode;\n\n public constructor(message: string, code: ClaudeAuthErrorCode, cause?: unknown) {\n super(message, cause === undefined ? undefined : { cause });\n this.name = new.target.name;\n this.code = code;\n }\n}\n\nexport class InvalidStateError extends ClaudeAuthError {\n public readonly expectedState: string;\n public readonly receivedState: string | undefined;\n\n public constructor(params: { expectedState: string; receivedState?: string }) {\n const { expectedState, receivedState } = params;\n super(\n `OAuth state validation failed (expected \"${expectedState}\", received \"${receivedState ?? 'undefined'}\").`,\n 'INVALID_STATE',\n );\n this.expectedState = expectedState;\n this.receivedState = receivedState;\n }\n}\n\nexport class MissingAuthorizationCodeError extends ClaudeAuthError {\n public constructor(message = 'Missing authorization code in callback payload.') {\n super(message, 'MISSING_CODE');\n }\n}\n\nexport class UnsupportedRuntimeError extends ClaudeAuthError {\n public readonly runtime: string;\n\n public constructor(runtime: string, message?: string) {\n super(message ?? `Unsupported runtime: ${runtime}.`, 'UNSUPPORTED_RUNTIME');\n this.runtime = runtime;\n }\n}\n\nexport class PolicyGatedModeError extends ClaudeAuthError {\n public readonly mode: AuthMode;\n\n public constructor(mode: AuthMode, reason?: string) {\n super(\n reason === undefined\n ? `Authentication mode \"${mode}\" is disabled by policy.`\n : `Authentication mode \"${mode}\" is disabled by policy: ${reason}`,\n 'POLICY_GATED_MODE',\n );\n this.mode = mode;\n }\n}\n\nexport class RefreshFailureError extends ClaudeAuthError {\n public constructor(message = 'Failed to refresh authentication credentials.', cause?: unknown) {\n super(message, 'REFRESH_FAILURE', cause);\n }\n}\n\nexport class StorageFailureError extends ClaudeAuthError {\n public constructor(message = 'Failed to access credential storage.', cause?: unknown) {\n super(message, 'STORAGE_FAILURE', cause);\n }\n}\n","import type { StoredCredentialEnvelope } from '../core/contracts.js';\nimport type {\n ApiKeyTerminalState,\n CompatOAuthTerminalState,\n CredentialTerminalState,\n OfficialProviderTerminalState,\n} from '../core/types.js';\nimport type {\n ClaudeAiOauthStorageRecord,\n ConfigFileShape,\n CredentialsFileShape,\n NormalizedApiKeyEnvelope,\n NormalizedOauthEnvelope,\n} from './types.js';\n\nfunction normalizeStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) {\n return [];\n }\n\n return value.filter((entry): entry is string => typeof entry === 'string');\n}\n\nfunction normalizeExpiresAt(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n\n if (typeof value === 'string') {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) {\n return parsed;\n }\n }\n\n return 0;\n}\n\nexport function normalizeOAuthRecord(\n record: Partial<ClaudeAiOauthStorageRecord>,\n): ClaudeAiOauthStorageRecord {\n return {\n accessToken: typeof record.accessToken === 'string' ? record.accessToken : '',\n refreshToken: typeof record.refreshToken === 'string' ? record.refreshToken : '',\n expiresAt: normalizeExpiresAt(record.expiresAt),\n scopes: normalizeStringArray(record.scopes),\n subscriptionType: typeof record.subscriptionType === 'string' ? record.subscriptionType : null,\n rateLimitTier: typeof record.rateLimitTier === 'string' ? record.rateLimitTier : null,\n };\n}\n\nexport function normalizeStoredOauthEnvelope(\n record: Partial<ClaudeAiOauthStorageRecord>,\n): NormalizedOauthEnvelope {\n const normalized = normalizeOAuthRecord(record);\n\n return {\n mode: 'compat-oauth',\n credentials: {\n accessToken: normalized.accessToken,\n refreshToken: normalized.refreshToken,\n expiresAt: normalized.expiresAt,\n scopes: normalized.scopes,\n },\n metadata: {\n subscriptionType:\n normalized.subscriptionType === null ? undefined : normalized.subscriptionType,\n rateLimitTier: normalized.rateLimitTier === null ? undefined : normalized.rateLimitTier,\n },\n };\n}\n\nexport function serializeOAuthTerminalState(\n terminal: OfficialProviderTerminalState | CompatOAuthTerminalState,\n): ClaudeAiOauthStorageRecord {\n return {\n accessToken: terminal.credentials.accessToken,\n refreshToken: terminal.credentials.refreshToken,\n expiresAt: terminal.credentials.expiresAt,\n scopes: [...terminal.credentials.scopes],\n subscriptionType:\n terminal.mode === 'official-provider' ? (terminal.subscriptionType ?? null) : null,\n rateLimitTier: terminal.mode === 'official-provider' ? (terminal.rateLimitTier ?? null) : null,\n };\n}\n\nexport function normalizeApiKeyTerminalState(\n config: ConfigFileShape,\n): NormalizedApiKeyEnvelope | null {\n if (typeof config.primaryApiKey !== 'string' || config.primaryApiKey.length === 0) {\n return null;\n }\n\n const state: ApiKeyTerminalState = {\n mode: 'api-key',\n apiKey: config.primaryApiKey,\n source: config.customApiKeyResponses?.approved ? 'derived-from-console-oauth' : 'user-supplied',\n };\n\n return {\n mode: 'api-key',\n state,\n };\n}\n\nexport function toStoredEnvelopeFromNativeShapes(params: {\n credentialsFile: CredentialsFileShape;\n configFile: ConfigFileShape;\n updatedAt: number;\n}): StoredCredentialEnvelope | null {\n const oauthRecord = params.credentialsFile.claudeAiOauth;\n if (oauthRecord !== undefined) {\n const normalized = normalizeStoredOauthEnvelope(oauthRecord);\n const terminal: CredentialTerminalState = {\n mode: normalized.mode,\n credentials: normalized.credentials,\n providerKey: 'claudeai',\n providerLabel: 'Claude.ai compatibility lane',\n };\n\n return {\n terminal,\n updatedAt: params.updatedAt,\n };\n }\n\n const normalizedApiKey = normalizeApiKeyTerminalState(params.configFile);\n if (normalizedApiKey === null) {\n return null;\n }\n\n return {\n terminal: normalizedApiKey.state,\n updatedAt: params.updatedAt,\n };\n}\n\nexport function updateConfigWithApiKey(params: {\n configFile: ConfigFileShape;\n terminal: ApiKeyTerminalState;\n}): ConfigFileShape {\n const nextApproved = params.terminal.source === 'derived-from-console-oauth';\n\n return {\n ...params.configFile,\n primaryApiKey: params.terminal.apiKey,\n customApiKeyResponses: {\n ...params.configFile.customApiKeyResponses,\n approved: nextApproved,\n },\n };\n}\n","import type { CompatModeId, OfficialProviderModeId } from './types.js';\n\nexport const DEFAULT_OAUTH_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';\n\nexport const DEFAULT_OAUTH_SCOPE = [\n 'org:create_api_key',\n 'user:profile',\n 'user:inference',\n 'user:sessions:claude_code',\n 'user:mcp_servers',\n 'user:file_upload',\n] as const;\n\nexport const MANUAL_REDIRECT_URI = 'https://platform.claude.com/oauth/code/callback';\n\nexport const COMPAT_AUTHORIZE_ENDPOINTS: Readonly<Record<CompatModeId, string>> = {\n claudeai: 'https://claude.com/cai/oauth/authorize',\n console: 'https://platform.claude.com/oauth/authorize',\n};\n\nexport const PROVIDER_DOCS_URLS: Readonly<Record<OfficialProviderModeId, string>> = {\n bedrock: 'https://docs.anthropic.com/en/api/claude-on-amazon-bedrock',\n vertex: 'https://docs.anthropic.com/en/api/claude-on-vertex-ai',\n foundry: 'https://learn.microsoft.com/en-us/azure/ai-foundry/',\n};\n\nexport const PROVIDER_CREDENTIAL_HINTS: Readonly<Record<OfficialProviderModeId, string>> = {\n bedrock:\n 'Configure AWS credentials and region, then pass a Bedrock credential adapter on the server.',\n vertex:\n 'Configure Google Cloud credentials and project/location settings, then pass a Vertex credential adapter on the server.',\n foundry:\n 'Configure Azure AI Foundry credentials and endpoint deployment settings, then pass a Foundry credential adapter on the server.',\n};\n","import { UnsupportedRuntimeError } from '../core/errors.js';\n\nconst PKCE_VERIFIER_LENGTH_BYTES = 64;\nconst STATE_LENGTH_BYTES = 32;\n\nfunction requireWebCrypto(): Crypto {\n if (typeof globalThis.crypto === 'undefined') {\n throw new UnsupportedRuntimeError(\n 'browser',\n 'Web Crypto API is required for browser auth initiation.',\n );\n }\n\n if (typeof globalThis.crypto.getRandomValues !== 'function') {\n throw new UnsupportedRuntimeError(\n 'browser',\n 'crypto.getRandomValues is unavailable in this runtime.',\n );\n }\n\n if (typeof globalThis.crypto.subtle === 'undefined') {\n throw new UnsupportedRuntimeError('browser', 'crypto.subtle is unavailable in this runtime.');\n }\n\n return globalThis.crypto;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n const base64 =\n typeof globalThis.btoa === 'function'\n ? globalThis.btoa(binary)\n : Buffer.from(binary, 'binary').toString('base64');\n\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/g, '');\n}\n\nexport function createRandomBase64Url(byteLength: number): string {\n const crypto = requireWebCrypto();\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return bytesToBase64Url(bytes);\n}\n\nexport function createState(): string {\n return createRandomBase64Url(STATE_LENGTH_BYTES);\n}\n\nexport function createPkceVerifier(): string {\n return createRandomBase64Url(PKCE_VERIFIER_LENGTH_BYTES);\n}\n\nexport async function createPkceChallenge(verifier: string): Promise<string> {\n const crypto = requireWebCrypto();\n const source = new TextEncoder().encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', source);\n return bytesToBase64Url(new Uint8Array(digest));\n}\n","import type { CredentialResult } from '../../core/types.js';\nimport type {\n CompatOAuthEndpointConfig,\n CompatOAuthSupportModeId,\n CompatOAuthTokenExchangeOptions,\n CompatOAuthTokenExchangeRequest,\n CompatOAuthTokenResponse,\n} from './types.js';\n\nconst DEFAULT_COMPAT_OAUTH_ENDPOINTS: Readonly<\n Record<CompatOAuthSupportModeId, CompatOAuthEndpointConfig>\n> = {\n claudeai: {\n authorizeEndpoint: 'https://claude.com/cai/oauth/authorize',\n tokenEndpoint: 'https://platform.claude.com/v1/oauth/token',\n },\n console: {\n authorizeEndpoint: 'https://platform.claude.com/oauth/authorize',\n tokenEndpoint: 'https://platform.claude.com/v1/oauth/token',\n },\n};\n\nconst COMPAT_PROVIDER_LABELS: Readonly<Record<CompatOAuthSupportModeId, string>> = {\n claudeai: 'Claude.ai',\n console: 'Anthropic Console',\n};\n\nexport function resolveCompatOAuthEndpointConfig(\n modeId: CompatOAuthSupportModeId,\n override?: Partial<CompatOAuthEndpointConfig>,\n): CompatOAuthEndpointConfig {\n const defaults = DEFAULT_COMPAT_OAUTH_ENDPOINTS[modeId];\n\n return {\n authorizeEndpoint: override?.authorizeEndpoint ?? defaults.authorizeEndpoint,\n tokenEndpoint: override?.tokenEndpoint ?? defaults.tokenEndpoint,\n };\n}\n\nexport async function exchangeCompatOAuthCodeForCredentials(\n request: CompatOAuthTokenExchangeRequest,\n options: CompatOAuthTokenExchangeOptions = {},\n): Promise<CredentialResult> {\n const fetchImpl = options.fetchImpl ?? fetch;\n const now = options.now ?? Date.now;\n const endpointConfig = resolveCompatOAuthEndpointConfig(request.modeId, request.endpointConfig);\n\n const response = await fetchImpl(endpointConfig.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'authorization_code',\n code: request.code,\n redirect_uri: request.redirectUri,\n client_id: request.clientId,\n code_verifier: request.codeVerifier,\n state: request.state,\n scope: request.scope,\n }),\n });\n\n const responseData = await tryReadTokenResponse(response);\n\n if (!response.ok) {\n const rawMessage =\n responseData.error_description ??\n responseData.error ??\n `Token exchange failed with HTTP ${response.status}.`;\n const message = typeof rawMessage === 'string' ? rawMessage : JSON.stringify(rawMessage);\n throw new Error(message);\n }\n\n const accessToken = responseData.access_token;\n if (!accessToken) {\n throw new Error('Token exchange succeeded without access_token.');\n }\n\n const expiresIn = coerceExpiresInSeconds(responseData.expires_in);\n const scope = responseData.scope ?? request.scope ?? '';\n const scopes = scope.trim() === '' ? [] : scope.trim().split(/\\s+/);\n\n return {\n ok: true,\n terminal: {\n mode: 'compat-oauth',\n providerKey: request.modeId,\n providerLabel: COMPAT_PROVIDER_LABELS[request.modeId],\n credentials: {\n accessToken,\n refreshToken: responseData.refresh_token ?? '',\n expiresAt: now() + expiresIn * 1000,\n scopes,\n },\n },\n };\n}\n\nasync function tryReadTokenResponse(response: Response): Promise<CompatOAuthTokenResponse> {\n try {\n const payload = (await response.json()) as CompatOAuthTokenResponse;\n return payload;\n } catch {\n return {};\n }\n}\n\nfunction coerceExpiresInSeconds(value: number | string | undefined): number {\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n return value;\n }\n\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n if (Number.isFinite(parsed) && parsed >= 0) {\n return parsed;\n }\n }\n\n return 0;\n}\n","import { createServer } from 'node:http';\n\nexport interface CallbackParams {\n code?: string;\n state?: string;\n error?: string;\n errorDescription?: string;\n}\n\nexport interface CallbackServerHandle {\n port: number;\n waitForCallback: (timeoutMs?: number) => Promise<CallbackParams>;\n close: () => Promise<void>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html><body style=\"font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0\">\n<div style=\"text-align:center\">\n<h2>login successful</h2>\n<p id=\"msg\">This tab will close in <span id=\"count\">3</span> seconds...</p>\n</div>\n<script>\nlet n=3;\nconst c=document.getElementById(\"count\");\nconst m=document.getElementById(\"msg\");\nconst t=setInterval(()=>{n--;if(n>0){c.textContent=n}else{clearInterval(t);window.close();m.textContent=\"You can close this tab.\"}},1000);\n</script>\n</body></html>`;\n\nexport async function startCallbackServer(): Promise<CallbackServerHandle> {\n let pendingCallback: CallbackParams | undefined;\n let resolveCallback: ((params: CallbackParams) => void) | undefined;\n let timeoutHandle: NodeJS.Timeout | undefined;\n\n function clearPendingWait(): void {\n if (timeoutHandle !== undefined) {\n clearTimeout(timeoutHandle);\n timeoutHandle = undefined;\n }\n resolveCallback = undefined;\n }\n\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n\n if (url.pathname !== '/callback') {\n res.writeHead(404);\n res.end();\n return;\n }\n\n const params: CallbackParams = {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n error: url.searchParams.get('error') ?? undefined,\n errorDescription: url.searchParams.get('error_description') ?? undefined,\n };\n\n res.shouldKeepAlive = false;\n res.writeHead(200, { 'Content-Type': 'text/html', Connection: 'close' });\n res.end(SUCCESS_HTML);\n\n if (resolveCallback !== undefined) {\n const resolve = resolveCallback;\n clearPendingWait();\n resolve(params);\n return;\n }\n\n pendingCallback = params;\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n server.listen(0, 'localhost', () => {\n const addr = server.address();\n if (addr === null || typeof addr === 'string') {\n reject(new Error('Failed to bind callback server'));\n return;\n }\n resolve(addr.port);\n });\n });\n\n return {\n port,\n\n waitForCallback(timeoutMs = DEFAULT_TIMEOUT_MS): Promise<CallbackParams> {\n if (pendingCallback !== undefined) {\n const buffered = pendingCallback;\n pendingCallback = undefined;\n return Promise.resolve(buffered);\n }\n\n return new Promise<CallbackParams>((resolve, reject) => {\n resolveCallback = resolve;\n\n timeoutHandle = setTimeout(() => {\n clearPendingWait();\n reject(new Error('Login timeout: no callback received'));\n }, timeoutMs);\n\n timeoutHandle.unref();\n });\n },\n\n close(): Promise<void> {\n return new Promise<void>((resolve) => {\n server.closeIdleConnections?.();\n server.close(() => {\n resolve();\n });\n });\n },\n };\n}\n","import { execFile as defaultExecFile } from 'node:child_process';\n\ntype ExecFileFn = (cmd: string, args: string[], cb: (err: Error | null) => void) => void;\n\nexport interface OpenBrowserOptions {\n platform?: NodeJS.Platform;\n execFileFn?: ExecFileFn;\n}\n\nexport async function openBrowser(url: string, options: OpenBrowserOptions = {}): Promise<boolean> {\n const platform = options.platform ?? process.platform;\n const execFileFn: ExecFileFn = options.execFileFn ?? defaultExecFile;\n\n const { cmd, args } = getOpenCommand(platform, url);\n\n return new Promise<boolean>((resolve) => {\n execFileFn(cmd, args, (err) => {\n resolve(err === null);\n });\n });\n}\n\nfunction getOpenCommand(platform: NodeJS.Platform, url: string): { cmd: string; args: string[] } {\n switch (platform) {\n case 'darwin':\n return { cmd: 'open', args: [url] };\n case 'win32':\n return { cmd: 'cmd', args: ['/c', 'start', '', url] };\n default:\n return { cmd: 'xdg-open', args: [url] };\n }\n}\n","import { LoginError } from './errors.js';\nimport {\n COMPAT_AUTHORIZE_ENDPOINTS,\n DEFAULT_OAUTH_CLIENT_ID,\n DEFAULT_OAUTH_SCOPE,\n} from './internal/browser/constants.js';\nimport { createPkceChallenge, createPkceVerifier, createState } from './internal/browser/pkce.js';\nimport { exchangeCompatOAuthCodeForCredentials } from './internal/server/callback/exchange.js';\nimport { createNodeDefaultStorageAdapter } from './internal/storage/node.js';\nimport { startCallbackServer } from './runtime/callback-server.js';\nimport { openBrowser } from './runtime/open-browser.js';\nimport type { LoginMode, LoginResult } from './types.js';\n\nconst CREATE_API_KEY_URL = 'https://api.anthropic.com/api/oauth/claude_cli/create_api_key';\n\nasync function createApiKeyFromAccessToken(\n accessToken: string,\n fetchImpl?: typeof fetch,\n): Promise<string> {\n const doFetch = fetchImpl ?? fetch;\n const response = await doFetch(CREATE_API_KEY_URL, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n throw new LoginError(`API key creation failed with HTTP ${response.status}`, 'exchange_failed');\n }\n\n const data = (await response.json()) as { raw_key?: string };\n if (!data.raw_key) {\n throw new LoginError('API key creation succeeded but no key returned', 'exchange_failed');\n }\n\n return data.raw_key;\n}\n\nexport interface LoginInternalOptions {\n configDir?: string;\n openBrowserFn?: (url: string) => Promise<boolean>;\n fetchImpl?: typeof fetch;\n}\n\nexport async function login(\n mode: LoginMode,\n internalOptions?: LoginInternalOptions,\n): Promise<LoginResult> {\n const server = await startCallbackServer();\n\n try {\n const state = createState();\n const verifier = createPkceVerifier();\n const challenge = await createPkceChallenge(verifier);\n\n const redirectUri = `http://localhost:${server.port}/callback`;\n const scope = DEFAULT_OAUTH_SCOPE.join(' ');\n\n const params = new URLSearchParams({\n code: 'true',\n client_id: DEFAULT_OAUTH_CLIENT_ID,\n response_type: 'code',\n scope,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state,\n redirect_uri: redirectUri,\n });\n\n const authorizeUrl = `${COMPAT_AUTHORIZE_ENDPOINTS[mode]}?${params.toString()}`;\n\n const openFn = internalOptions?.openBrowserFn ?? openBrowser;\n const opened = await openFn(authorizeUrl);\n\n if (!opened) {\n process.stdout.write(`\\nOpen this URL in your browser to log in:\\n${authorizeUrl}\\n\\n`);\n }\n\n const callbackParams = await server.waitForCallback();\n\n if (callbackParams.error) {\n throw new LoginError(callbackParams.errorDescription ?? callbackParams.error, 'cancelled');\n }\n\n if (!callbackParams.code || !callbackParams.state) {\n throw new LoginError('Missing authorization code in callback', 'cancelled');\n }\n\n if (callbackParams.state !== state) {\n throw new LoginError('OAuth state mismatch', 'cancelled');\n }\n\n const credentialResult = await exchangeCompatOAuthCodeForCredentials(\n {\n modeId: mode,\n code: callbackParams.code,\n state: callbackParams.state,\n codeVerifier: verifier,\n redirectUri,\n clientId: DEFAULT_OAUTH_CLIENT_ID,\n scope,\n },\n {\n fetchImpl: internalOptions?.fetchImpl,\n },\n );\n\n if (!credentialResult.ok) {\n throw new LoginError(credentialResult.message, 'exchange_failed');\n }\n\n const storage = createNodeDefaultStorageAdapter({\n configDir: internalOptions?.configDir,\n });\n\n if (mode === 'console') {\n // Console mode: use the OAuth access token to create an API key, then discard OAuth tokens\n if (credentialResult.terminal.mode !== 'compat-oauth') {\n throw new LoginError('Unexpected terminal state for console mode', 'exchange_failed');\n }\n\n const apiKey = await createApiKeyFromAccessToken(\n credentialResult.terminal.credentials.accessToken,\n internalOptions?.fetchImpl,\n );\n\n try {\n await storage.write({\n terminal: {\n mode: 'api-key',\n apiKey,\n source: 'derived-from-console-oauth',\n },\n updatedAt: Date.now(),\n });\n } catch (cause) {\n throw new LoginError('Failed to save credentials', 'storage_failed', cause);\n }\n } else {\n // claudeai mode: store OAuth tokens directly\n try {\n await storage.write({\n terminal: credentialResult.terminal,\n updatedAt: Date.now(),\n });\n } catch (cause) {\n throw new LoginError('Failed to save credentials', 'storage_failed', cause);\n }\n }\n\n return { mode, loggedIn: true };\n } catch (error) {\n if (error instanceof LoginError) {\n throw error;\n }\n throw new LoginError(\n error instanceof Error ? error.message : 'Login failed',\n 'exchange_failed',\n error,\n );\n } finally {\n await server.close();\n }\n}\n"],"mappings":"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","names":["credentialsFile"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@claude-auth-sdk/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core authentication SDK for Claude — OAuth login, credential storage, browser opener",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": ["dist", "LICENSE", "README.md"],
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/anthropics/claude-auth-sdk.git",
|
|
20
|
+
"directory": "packages/core"
|
|
21
|
+
},
|
|
22
|
+
"keywords": ["claude", "anthropic", "auth", "oauth", "sdk"],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.14.1",
|
|
34
|
+
"tsup": "^8.4.0",
|
|
35
|
+
"typescript": "^5.8.3",
|
|
36
|
+
"vitest": "^3.0.7"
|
|
37
|
+
}
|
|
38
|
+
}
|