@provos/ironcurtain 0.9.0 → 0.10.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/README.md +20 -12
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +12 -3
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/model-provider.d.ts +8 -2
- package/dist/config/model-provider.js +52 -9
- package/dist/config/model-provider.js.map +1 -1
- package/dist/config/paths.d.ts +6 -0
- package/dist/config/paths.js +18 -9
- package/dist/config/paths.js.map +1 -1
- package/dist/config/user-config.d.ts +9 -0
- package/dist/config/user-config.js +26 -5
- package/dist/config/user-config.js.map +1 -1
- package/dist/cron/compile-task-policy.js +1 -0
- package/dist/cron/compile-task-policy.js.map +1 -1
- package/dist/docker/adapters/claude-code.d.ts +2 -1
- package/dist/docker/adapters/claude-code.js +135 -120
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/agent-registry.js +3 -2
- package/dist/docker/agent-registry.js.map +1 -1
- package/dist/docker/container-lifecycle.d.ts +15 -0
- package/dist/docker/container-lifecycle.js +32 -0
- package/dist/docker/container-lifecycle.js.map +1 -0
- package/dist/docker/docker-agent-session.js +16 -23
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +10 -0
- package/dist/docker/docker-infrastructure.js +71 -4
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/docker-manager.js +37 -0
- package/dist/docker/docker-manager.js.map +1 -1
- package/dist/docker/mitm-proxy.d.ts +6 -0
- package/dist/docker/mitm-proxy.js +343 -25
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +45 -6
- package/dist/docker/oauth-credentials.js +151 -24
- package/dist/docker/oauth-credentials.js.map +1 -1
- package/dist/docker/oauth-token-manager.d.ts +15 -16
- package/dist/docker/oauth-token-manager.js +23 -17
- package/dist/docker/oauth-token-manager.js.map +1 -1
- package/dist/docker/provider-config.d.ts +32 -0
- package/dist/docker/provider-config.js +20 -0
- package/dist/docker/provider-config.js.map +1 -1
- package/dist/docker/pty-session.js +14 -22
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/docker/types.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/mux/mux-app.d.ts +2 -0
- package/dist/mux/mux-app.js +48 -37
- package/dist/mux/mux-app.js.map +1 -1
- package/dist/mux/mux-command.js +7 -1
- package/dist/mux/mux-command.js.map +1 -1
- package/dist/mux/mux-input-handler.d.ts +2 -0
- package/dist/mux/mux-input-handler.js +6 -4
- package/dist/mux/mux-input-handler.js.map +1 -1
- package/dist/mux/mux-renderer.js +20 -5
- package/dist/mux/mux-renderer.js.map +1 -1
- package/dist/mux/pty-bridge.d.ts +2 -0
- package/dist/mux/pty-bridge.js +3 -0
- package/dist/mux/pty-bridge.js.map +1 -1
- package/dist/mux/session-scanner.d.ts +7 -0
- package/dist/mux/session-scanner.js +21 -1
- package/dist/mux/session-scanner.js.map +1 -1
- package/dist/mux/types.d.ts +0 -4
- package/dist/mux/types.js.map +1 -1
- package/dist/persona/compile-persona-policy.js +1 -0
- package/dist/persona/compile-persona-policy.js.map +1 -1
- package/dist/pipeline/annotate.d.ts +7 -1
- package/dist/pipeline/annotate.js +92 -2
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/compile.js +2 -0
- package/dist/pipeline/compile.js.map +1 -1
- package/dist/pipeline/llm-logger.d.ts +1 -0
- package/dist/pipeline/llm-logger.js +8 -1
- package/dist/pipeline/llm-logger.js.map +1 -1
- package/dist/pipeline/parallel-progress.d.ts +59 -0
- package/dist/pipeline/parallel-progress.js +263 -0
- package/dist/pipeline/parallel-progress.js.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +49 -8
- package/dist/pipeline/pipeline-runner.js +375 -226
- package/dist/pipeline/pipeline-runner.js.map +1 -1
- package/dist/pipeline/pipeline-shared.d.ts +58 -0
- package/dist/pipeline/pipeline-shared.js +124 -1
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/server-prefilter.d.ts +31 -0
- package/dist/pipeline/server-prefilter.js +115 -0
- package/dist/pipeline/server-prefilter.js.map +1 -0
- package/dist/pipeline/types.d.ts +5 -0
- package/dist/session/preflight.js +15 -16
- package/dist/session/preflight.js.map +1 -1
- package/dist/trusted-process/sandbox-integration.js +8 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -1
- package/docker/Dockerfile.base +3 -1
- package/docker/Dockerfile.base.arm64 +8 -1
- package/package.json +4 -1
|
@@ -20,11 +20,21 @@ export interface OAuthCredentials {
|
|
|
20
20
|
}
|
|
21
21
|
/** Where OAuth credentials were loaded from. */
|
|
22
22
|
export type OAuthCredentialSource = 'file' | 'keychain';
|
|
23
|
+
/** Keychain lookup result that preserves the service name for write-back. */
|
|
24
|
+
export interface KeychainResult {
|
|
25
|
+
readonly credentials: OAuthCredentials;
|
|
26
|
+
readonly serviceName: string;
|
|
27
|
+
}
|
|
23
28
|
/** Authentication method detected on the host. */
|
|
24
29
|
export type AuthMethod = {
|
|
25
30
|
readonly kind: 'oauth';
|
|
26
31
|
readonly credentials: OAuthCredentials;
|
|
27
|
-
readonly source:
|
|
32
|
+
readonly source: 'file';
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'oauth';
|
|
35
|
+
readonly credentials: OAuthCredentials;
|
|
36
|
+
readonly source: 'keychain';
|
|
37
|
+
readonly keychainServiceName: string;
|
|
28
38
|
} | {
|
|
29
39
|
readonly kind: 'apikey';
|
|
30
40
|
readonly key: string;
|
|
@@ -56,14 +66,30 @@ export declare function loadCredentialsFromFile(filePath: string): OAuthCredenti
|
|
|
56
66
|
*/
|
|
57
67
|
export declare function parseCredentialsJson(json: string): OAuthCredentials | null;
|
|
58
68
|
/**
|
|
59
|
-
* Attempts to extract OAuth credentials from the macOS Keychain
|
|
69
|
+
* Attempts to extract OAuth credentials from the macOS Keychain,
|
|
70
|
+
* also returning which service name succeeded (needed for write-back).
|
|
60
71
|
* Returns null on non-macOS platforms, Keychain access failure, or
|
|
61
72
|
* when no valid credentials are found.
|
|
62
73
|
*
|
|
63
74
|
* Tries both service names because Claude Code has a known bug where
|
|
64
75
|
* the write and read service names differ.
|
|
65
76
|
*/
|
|
77
|
+
export declare function extractFromKeychainWithService(): KeychainResult | null;
|
|
78
|
+
/**
|
|
79
|
+
* Attempts to extract OAuth credentials from the macOS Keychain.
|
|
80
|
+
* Returns null on non-macOS platforms, Keychain access failure, or
|
|
81
|
+
* when no valid credentials are found.
|
|
82
|
+
*
|
|
83
|
+
* Delegates to extractFromKeychainWithService() and strips the service name.
|
|
84
|
+
*/
|
|
66
85
|
export declare function extractFromKeychain(): OAuthCredentials | null;
|
|
86
|
+
/**
|
|
87
|
+
* Writes updated OAuth credentials back to the macOS Keychain.
|
|
88
|
+
* Preserves existing fields in the Keychain entry (e.g., scopes, subscriptionType).
|
|
89
|
+
*
|
|
90
|
+
* No-op on non-macOS platforms.
|
|
91
|
+
*/
|
|
92
|
+
export declare function writeToKeychain(credentials: OAuthCredentials, serviceName: string): void;
|
|
67
93
|
/**
|
|
68
94
|
* Refreshes an OAuth access token using a refresh token grant.
|
|
69
95
|
*
|
|
@@ -76,19 +102,32 @@ export declare function refreshOAuthToken(refreshToken: string): Promise<OAuthCr
|
|
|
76
102
|
* preserving other fields in the file (e.g., scopes, subscriptionType).
|
|
77
103
|
*/
|
|
78
104
|
export declare function saveOAuthCredentials(credentials: OAuthCredentials, filePath?: string): void;
|
|
79
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Injectable credential sources for testability.
|
|
107
|
+
*
|
|
108
|
+
* The optional refresh/save/keychain-write functions enable token refresh
|
|
109
|
+
* and persistence in detectAuthMethod(). When these functions are omitted,
|
|
110
|
+
* expired tokens are not refreshed or written back; the caller only receives
|
|
111
|
+
* the detection result (and any existing, possibly stale, credentials).
|
|
112
|
+
*/
|
|
80
113
|
export interface CredentialSources {
|
|
81
114
|
loadFromFile: () => OAuthCredentials | null;
|
|
82
115
|
loadFromKeychain: () => OAuthCredentials | null;
|
|
116
|
+
refreshToken?: (refreshToken: string) => Promise<OAuthCredentials | null>;
|
|
117
|
+
saveToFile?: (credentials: OAuthCredentials) => void;
|
|
118
|
+
loadFromKeychainWithService?: () => KeychainResult | null;
|
|
119
|
+
writeToKeychain?: (credentials: OAuthCredentials, serviceName: string) => void;
|
|
83
120
|
}
|
|
84
121
|
/**
|
|
85
122
|
* Detects the best authentication method available on the host.
|
|
123
|
+
* When refresh functions are provided in sources, attempts to refresh
|
|
124
|
+
* expired OAuth tokens before falling back to API key auth.
|
|
86
125
|
*
|
|
87
126
|
* Priority:
|
|
88
127
|
* 1. IRONCURTAIN_DOCKER_AUTH=apikey env var forces API key mode
|
|
89
|
-
* 2. OAuth credentials from ~/.claude/.credentials.json (
|
|
90
|
-
* 3. OAuth credentials from macOS Keychain (
|
|
128
|
+
* 2. OAuth credentials from ~/.claude/.credentials.json (valid or refreshable)
|
|
129
|
+
* 3. OAuth credentials from macOS Keychain (valid or refreshable)
|
|
91
130
|
* 4. API key from config
|
|
92
131
|
* 5. None
|
|
93
132
|
*/
|
|
94
|
-
export declare function detectAuthMethod(config: IronCurtainConfig, sources?: CredentialSources): AuthMethod
|
|
133
|
+
export declare function detectAuthMethod(config: IronCurtainConfig, sources?: CredentialSources): Promise<AuthMethod>;
|
|
@@ -94,14 +94,15 @@ export function parseCredentialsJson(json) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
|
-
* Attempts to extract OAuth credentials from the macOS Keychain
|
|
97
|
+
* Attempts to extract OAuth credentials from the macOS Keychain,
|
|
98
|
+
* also returning which service name succeeded (needed for write-back).
|
|
98
99
|
* Returns null on non-macOS platforms, Keychain access failure, or
|
|
99
100
|
* when no valid credentials are found.
|
|
100
101
|
*
|
|
101
102
|
* Tries both service names because Claude Code has a known bug where
|
|
102
103
|
* the write and read service names differ.
|
|
103
104
|
*/
|
|
104
|
-
export function
|
|
105
|
+
export function extractFromKeychainWithService() {
|
|
105
106
|
if (platform() !== 'darwin')
|
|
106
107
|
return null;
|
|
107
108
|
const account = userInfo().username;
|
|
@@ -115,7 +116,7 @@ export function extractFromKeychain() {
|
|
|
115
116
|
const credentials = parseCredentialsJson(result.trim());
|
|
116
117
|
if (credentials) {
|
|
117
118
|
logger.info(`Found OAuth credentials in macOS Keychain (service: "${service}")`);
|
|
118
|
-
return credentials;
|
|
119
|
+
return { credentials, serviceName: service };
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
catch {
|
|
@@ -125,6 +126,58 @@ export function extractFromKeychain() {
|
|
|
125
126
|
}
|
|
126
127
|
return null;
|
|
127
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Attempts to extract OAuth credentials from the macOS Keychain.
|
|
131
|
+
* Returns null on non-macOS platforms, Keychain access failure, or
|
|
132
|
+
* when no valid credentials are found.
|
|
133
|
+
*
|
|
134
|
+
* Delegates to extractFromKeychainWithService() and strips the service name.
|
|
135
|
+
*/
|
|
136
|
+
export function extractFromKeychain() {
|
|
137
|
+
return extractFromKeychainWithService()?.credentials ?? null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Writes updated OAuth credentials back to the macOS Keychain.
|
|
141
|
+
* Preserves existing fields in the Keychain entry (e.g., scopes, subscriptionType).
|
|
142
|
+
*
|
|
143
|
+
* No-op on non-macOS platforms.
|
|
144
|
+
*/
|
|
145
|
+
export function writeToKeychain(credentials, serviceName) {
|
|
146
|
+
if (platform() !== 'darwin')
|
|
147
|
+
return;
|
|
148
|
+
const account = userInfo().username;
|
|
149
|
+
// Read existing Keychain value to preserve extra fields
|
|
150
|
+
let existing = {};
|
|
151
|
+
try {
|
|
152
|
+
const raw = execFileSync('security', ['find-generic-password', '-s', serviceName, '-a', account, '-w'], {
|
|
153
|
+
encoding: 'utf8',
|
|
154
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
155
|
+
timeout: 10_000,
|
|
156
|
+
});
|
|
157
|
+
const parsed = JSON.parse(raw.trim());
|
|
158
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
159
|
+
existing = parsed;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Entry doesn't exist or is unreadable -- start fresh
|
|
164
|
+
}
|
|
165
|
+
const existingOauth = typeof existing.claudeAiOauth === 'object' && existing.claudeAiOauth !== null
|
|
166
|
+
? existing.claudeAiOauth
|
|
167
|
+
: {};
|
|
168
|
+
existing.claudeAiOauth = {
|
|
169
|
+
...existingOauth,
|
|
170
|
+
accessToken: credentials.accessToken,
|
|
171
|
+
refreshToken: credentials.refreshToken,
|
|
172
|
+
expiresAt: credentials.expiresAt,
|
|
173
|
+
};
|
|
174
|
+
const json = JSON.stringify(existing);
|
|
175
|
+
// -U flag updates an existing entry or creates a new one
|
|
176
|
+
execFileSync('security', ['add-generic-password', '-U', '-s', serviceName, '-a', account, '-w', json], {
|
|
177
|
+
timeout: 10_000,
|
|
178
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
179
|
+
});
|
|
180
|
+
}
|
|
128
181
|
/** Claude Code's public OAuth client ID. */
|
|
129
182
|
const OAUTH_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
130
183
|
/** Anthropic's OAuth token endpoint (platform.claude.com since mid-2025). */
|
|
@@ -136,22 +189,25 @@ const OAUTH_TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
|
|
|
136
189
|
* token, network error, etc.).
|
|
137
190
|
*/
|
|
138
191
|
export async function refreshOAuthToken(refreshToken) {
|
|
192
|
+
const REFRESH_TIMEOUT_MS = 30_000;
|
|
139
193
|
try {
|
|
194
|
+
const body = new URLSearchParams({
|
|
195
|
+
grant_type: 'refresh_token',
|
|
196
|
+
refresh_token: refreshToken,
|
|
197
|
+
client_id: OAUTH_CLIENT_ID,
|
|
198
|
+
});
|
|
140
199
|
const response = await fetch(OAUTH_TOKEN_URL, {
|
|
141
200
|
method: 'POST',
|
|
142
|
-
headers: { 'Content-Type': 'application/
|
|
143
|
-
body:
|
|
144
|
-
|
|
145
|
-
refresh_token: refreshToken,
|
|
146
|
-
client_id: OAUTH_CLIENT_ID,
|
|
147
|
-
}),
|
|
201
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
202
|
+
body: body.toString(),
|
|
203
|
+
signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
|
|
148
204
|
});
|
|
149
205
|
if (!response.ok) {
|
|
150
206
|
logger.warn(`OAuth token refresh failed: HTTP ${response.status}`);
|
|
151
207
|
return null;
|
|
152
208
|
}
|
|
153
209
|
const data = (await response.json());
|
|
154
|
-
return parseTokenResponse(data);
|
|
210
|
+
return parseTokenResponse(data, refreshToken);
|
|
155
211
|
}
|
|
156
212
|
catch (err) {
|
|
157
213
|
logger.warn(`OAuth token refresh error: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -160,19 +216,19 @@ export async function refreshOAuthToken(refreshToken) {
|
|
|
160
216
|
}
|
|
161
217
|
/**
|
|
162
218
|
* Validates and extracts credentials from an OAuth token endpoint response.
|
|
163
|
-
* Returns null if
|
|
219
|
+
* Returns null if access_token or expires_in are missing/invalid.
|
|
220
|
+
* Preserves the original refresh token when the response omits refresh_token
|
|
221
|
+
* (not all providers rotate refresh tokens on every grant).
|
|
164
222
|
*/
|
|
165
|
-
function parseTokenResponse(data) {
|
|
223
|
+
function parseTokenResponse(data, fallbackRefreshToken) {
|
|
166
224
|
const { access_token: accessToken, refresh_token: refreshToken, expires_in: expiresIn } = data;
|
|
167
225
|
if (!isNonEmptyString(accessToken))
|
|
168
226
|
return null;
|
|
169
|
-
if (!isNonEmptyString(refreshToken))
|
|
170
|
-
return null;
|
|
171
227
|
if (!isPositiveFiniteNumber(expiresIn))
|
|
172
228
|
return null;
|
|
173
229
|
return {
|
|
174
230
|
accessToken,
|
|
175
|
-
refreshToken,
|
|
231
|
+
refreshToken: isNonEmptyString(refreshToken) ? refreshToken : fallbackRefreshToken,
|
|
176
232
|
expiresAt: Date.now() + expiresIn * 1000,
|
|
177
233
|
};
|
|
178
234
|
}
|
|
@@ -208,39 +264,62 @@ export function saveOAuthCredentials(credentials, filePath) {
|
|
|
208
264
|
const defaultSources = {
|
|
209
265
|
loadFromFile: loadOAuthCredentials,
|
|
210
266
|
loadFromKeychain: extractFromKeychain,
|
|
267
|
+
refreshToken: refreshOAuthToken,
|
|
268
|
+
saveToFile: saveOAuthCredentials,
|
|
269
|
+
loadFromKeychainWithService: extractFromKeychainWithService,
|
|
270
|
+
writeToKeychain,
|
|
211
271
|
};
|
|
212
272
|
/**
|
|
213
273
|
* Detects the best authentication method available on the host.
|
|
274
|
+
* When refresh functions are provided in sources, attempts to refresh
|
|
275
|
+
* expired OAuth tokens before falling back to API key auth.
|
|
214
276
|
*
|
|
215
277
|
* Priority:
|
|
216
278
|
* 1. IRONCURTAIN_DOCKER_AUTH=apikey env var forces API key mode
|
|
217
|
-
* 2. OAuth credentials from ~/.claude/.credentials.json (
|
|
218
|
-
* 3. OAuth credentials from macOS Keychain (
|
|
279
|
+
* 2. OAuth credentials from ~/.claude/.credentials.json (valid or refreshable)
|
|
280
|
+
* 3. OAuth credentials from macOS Keychain (valid or refreshable)
|
|
219
281
|
* 4. API key from config
|
|
220
282
|
* 5. None
|
|
221
283
|
*/
|
|
222
|
-
export function detectAuthMethod(config, sources) {
|
|
223
|
-
const
|
|
284
|
+
export async function detectAuthMethod(config, sources) {
|
|
285
|
+
const s = sources ?? defaultSources;
|
|
224
286
|
// Allow explicit override to force API key mode
|
|
225
287
|
if (process.env.IRONCURTAIN_DOCKER_AUTH === 'apikey') {
|
|
226
288
|
logger.info('Docker auth override: forced API key mode via IRONCURTAIN_DOCKER_AUTH');
|
|
227
289
|
return resolveApiKeyAuth(config);
|
|
228
290
|
}
|
|
229
291
|
// Try OAuth from credentials file
|
|
230
|
-
const fileCreds = loadFromFile();
|
|
292
|
+
const fileCreds = s.loadFromFile();
|
|
231
293
|
if (fileCreds) {
|
|
232
294
|
if (!isTokenExpired(fileCreds)) {
|
|
233
295
|
logger.info('Detected OAuth credentials from ~/.claude/.credentials.json');
|
|
234
296
|
return { kind: 'oauth', credentials: fileCreds, source: 'file' };
|
|
235
297
|
}
|
|
298
|
+
// Attempt refresh if refresh function is available
|
|
299
|
+
if (s.refreshToken) {
|
|
300
|
+
const refreshed = await tryRefreshFileCreds(fileCreds, s.refreshToken, s.saveToFile);
|
|
301
|
+
if (refreshed)
|
|
302
|
+
return refreshed;
|
|
303
|
+
}
|
|
236
304
|
logger.warn('OAuth token from credentials file is expired');
|
|
237
305
|
}
|
|
238
306
|
// Try macOS Keychain if credentials file was not found
|
|
239
307
|
if (!fileCreds) {
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
if (!isTokenExpired(
|
|
243
|
-
return {
|
|
308
|
+
const keychainResult = s.loadFromKeychainWithService?.() ?? toKeychainResult(s.loadFromKeychain());
|
|
309
|
+
if (keychainResult) {
|
|
310
|
+
if (!isTokenExpired(keychainResult.credentials)) {
|
|
311
|
+
return {
|
|
312
|
+
kind: 'oauth',
|
|
313
|
+
credentials: keychainResult.credentials,
|
|
314
|
+
source: 'keychain',
|
|
315
|
+
keychainServiceName: keychainResult.serviceName,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// Attempt refresh if refresh function is available
|
|
319
|
+
if (s.refreshToken) {
|
|
320
|
+
const refreshed = await tryRefreshKeychainCreds(keychainResult, s.refreshToken, s.writeToKeychain);
|
|
321
|
+
if (refreshed)
|
|
322
|
+
return refreshed;
|
|
244
323
|
}
|
|
245
324
|
logger.warn('OAuth token from macOS Keychain is expired');
|
|
246
325
|
}
|
|
@@ -248,6 +327,54 @@ export function detectAuthMethod(config, sources) {
|
|
|
248
327
|
// Fall back to API key
|
|
249
328
|
return resolveApiKeyAuth(config);
|
|
250
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Wraps a plain OAuthCredentials from loadFromKeychain() into a KeychainResult.
|
|
332
|
+
* Uses the first service name as a fallback since we don't know which one matched.
|
|
333
|
+
*/
|
|
334
|
+
function toKeychainResult(creds) {
|
|
335
|
+
if (!creds)
|
|
336
|
+
return null;
|
|
337
|
+
return { credentials: creds, serviceName: KEYCHAIN_SERVICE_NAMES[0] };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Attempts to refresh expired file-sourced credentials.
|
|
341
|
+
* On success, saves to file and returns the oauth AuthMethod.
|
|
342
|
+
*/
|
|
343
|
+
async function tryRefreshFileCreds(fileCreds, doRefresh, saveToFile) {
|
|
344
|
+
const refreshed = await doRefresh(fileCreds.refreshToken);
|
|
345
|
+
if (!refreshed)
|
|
346
|
+
return null;
|
|
347
|
+
try {
|
|
348
|
+
saveToFile?.(refreshed);
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
logger.warn(`Failed to save refreshed credentials to file: ${err instanceof Error ? err.message : String(err)}`);
|
|
352
|
+
}
|
|
353
|
+
logger.info('Refreshed expired OAuth token from credentials file');
|
|
354
|
+
return { kind: 'oauth', credentials: refreshed, source: 'file' };
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Attempts to refresh expired Keychain-sourced credentials.
|
|
358
|
+
* On success, writes back to Keychain and returns the oauth AuthMethod.
|
|
359
|
+
*/
|
|
360
|
+
async function tryRefreshKeychainCreds(keychainResult, doRefresh, doWriteToKeychain) {
|
|
361
|
+
const refreshed = await doRefresh(keychainResult.credentials.refreshToken);
|
|
362
|
+
if (!refreshed)
|
|
363
|
+
return null;
|
|
364
|
+
try {
|
|
365
|
+
doWriteToKeychain?.(refreshed, keychainResult.serviceName);
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
logger.warn(`Failed to write refreshed credentials to Keychain: ${err instanceof Error ? err.message : String(err)}`);
|
|
369
|
+
}
|
|
370
|
+
logger.info('Refreshed expired OAuth token from macOS Keychain');
|
|
371
|
+
return {
|
|
372
|
+
kind: 'oauth',
|
|
373
|
+
credentials: refreshed,
|
|
374
|
+
source: 'keychain',
|
|
375
|
+
keychainServiceName: keychainResult.serviceName,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
251
378
|
/**
|
|
252
379
|
* Resolves API key authentication from config.
|
|
253
380
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-credentials.js","sourceRoot":"","sources":["../../src/docker/oauth-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth-credentials.js","sourceRoot":"","sources":["../../src/docker/oauth-credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* may also refresh tokens concurrently — re-reads the credentials file
|
|
7
7
|
* before attempting a refresh to avoid using a stale refresh token.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* host Claude Code's refresh token, which lives in the Keychain.
|
|
9
|
+
* For file-sourced credentials, refreshed tokens are saved back to the
|
|
10
|
+
* credentials file. For Keychain-sourced credentials (macOS), refreshed
|
|
11
|
+
* tokens are written back to the Keychain via the writeToKeychain dep.
|
|
13
12
|
*
|
|
14
13
|
* A single in-flight refresh promise is shared across concurrent callers
|
|
15
14
|
* to prevent duplicate refresh requests.
|
|
@@ -22,6 +21,10 @@ export interface TokenManagerDeps {
|
|
|
22
21
|
refreshToken: (refreshToken: string) => Promise<OAuthCredentials | null>;
|
|
23
22
|
saveCredentials: (credentials: OAuthCredentials, filePath?: string) => void;
|
|
24
23
|
credentialsFilePath: string;
|
|
24
|
+
/** When set, refreshed credentials are written to the Keychain instead of the file. */
|
|
25
|
+
writeToKeychain?: (credentials: OAuthCredentials, serviceName: string) => void;
|
|
26
|
+
/** The Keychain service name to use with writeToKeychain. */
|
|
27
|
+
keychainServiceName?: string;
|
|
25
28
|
/** @deprecated No longer used internally. Kept for backward compatibility with tests. */
|
|
26
29
|
now?: () => number;
|
|
27
30
|
}
|
|
@@ -29,12 +32,12 @@ export interface OAuthTokenManagerOptions {
|
|
|
29
32
|
/**
|
|
30
33
|
* Whether the manager may perform its own refresh_token grant.
|
|
31
34
|
*
|
|
32
|
-
* When false
|
|
33
|
-
*
|
|
34
|
-
* the token endpoint itself, because doing so would rotate the refresh
|
|
35
|
-
* token and break host Claude Code's copy in the Keychain.
|
|
35
|
+
* When false, the manager only re-reads credentials from the file and
|
|
36
|
+
* Keychain — it never POSTs to the token endpoint itself.
|
|
36
37
|
*
|
|
37
|
-
* Defaults to true
|
|
38
|
+
* Defaults to true. For Keychain-sourced credentials, set writeToKeychain
|
|
39
|
+
* and keychainServiceName in deps so refreshed tokens are persisted to
|
|
40
|
+
* the Keychain rather than the credentials file.
|
|
38
41
|
*/
|
|
39
42
|
canRefresh?: boolean;
|
|
40
43
|
}
|
|
@@ -67,13 +70,9 @@ export declare class OAuthTokenManager {
|
|
|
67
70
|
* Core refresh logic with deduplication. Concurrent callers share a single
|
|
68
71
|
* in-flight refresh promise.
|
|
69
72
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* Keychain-sourced credentials (canRefresh=false):
|
|
75
|
-
* Re-read file and Keychain only -- never POST a refresh grant, because
|
|
76
|
-
* doing so would rotate the refresh token and break host Claude Code.
|
|
73
|
+
* Re-read file -> for Keychain-sourced creds, also re-read Keychain ->
|
|
74
|
+
* if still expired and canRefresh is true, POST refresh grant ->
|
|
75
|
+
* on failure, re-read file once more (host process may have won the race).
|
|
77
76
|
*/
|
|
78
77
|
private doRefresh;
|
|
79
78
|
private executeRefresh;
|
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* may also refresh tokens concurrently — re-reads the credentials file
|
|
7
7
|
* before attempting a refresh to avoid using a stale refresh token.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* host Claude Code's refresh token, which lives in the Keychain.
|
|
9
|
+
* For file-sourced credentials, refreshed tokens are saved back to the
|
|
10
|
+
* credentials file. For Keychain-sourced credentials (macOS), refreshed
|
|
11
|
+
* tokens are written back to the Keychain via the writeToKeychain dep.
|
|
13
12
|
*
|
|
14
13
|
* A single in-flight refresh promise is shared across concurrent callers
|
|
15
14
|
* to prevent duplicate refresh requests.
|
|
@@ -68,13 +67,9 @@ export class OAuthTokenManager {
|
|
|
68
67
|
* Core refresh logic with deduplication. Concurrent callers share a single
|
|
69
68
|
* in-flight refresh promise.
|
|
70
69
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Keychain-sourced credentials (canRefresh=false):
|
|
76
|
-
* Re-read file and Keychain only -- never POST a refresh grant, because
|
|
77
|
-
* doing so would rotate the refresh token and break host Claude Code.
|
|
70
|
+
* Re-read file -> for Keychain-sourced creds, also re-read Keychain ->
|
|
71
|
+
* if still expired and canRefresh is true, POST refresh grant ->
|
|
72
|
+
* on failure, re-read file once more (host process may have won the race).
|
|
78
73
|
*/
|
|
79
74
|
doRefresh() {
|
|
80
75
|
if (this.inflightRefresh) {
|
|
@@ -93,24 +88,35 @@ export class OAuthTokenManager {
|
|
|
93
88
|
this.credentials = fileCreds;
|
|
94
89
|
return this.credentials.accessToken;
|
|
95
90
|
}
|
|
96
|
-
//
|
|
97
|
-
|
|
91
|
+
// For Keychain-sourced creds, check if the host process already refreshed.
|
|
92
|
+
// Capture the Keychain's refresh token in case it's newer than our startup copy.
|
|
93
|
+
let keychainRefreshToken;
|
|
94
|
+
if (this.deps.keychainServiceName) {
|
|
98
95
|
const keychainCreds = this.deps.loadFromKeychain();
|
|
99
96
|
if (keychainCreds && !isTokenExpired(keychainCreds)) {
|
|
100
97
|
logger.info('[oauth-token-manager] Found valid token in Keychain (refreshed by host process)');
|
|
101
98
|
this.credentials = keychainCreds;
|
|
102
99
|
return this.credentials.accessToken;
|
|
103
100
|
}
|
|
104
|
-
|
|
101
|
+
keychainRefreshToken = keychainCreds?.refreshToken;
|
|
102
|
+
}
|
|
103
|
+
// Hard guard: when canRefresh is false, never POST a refresh grant
|
|
104
|
+
if (!this.canRefresh) {
|
|
105
|
+
logger.warn('[oauth-token-manager] Token expired and self-refresh is disabled');
|
|
105
106
|
return null;
|
|
106
107
|
}
|
|
107
|
-
//
|
|
108
|
-
const refreshToken = fileCreds?.refreshToken ?? this.credentials.refreshToken;
|
|
108
|
+
// Pick the best available refresh token: file > keychain > initial credentials
|
|
109
|
+
const refreshToken = fileCreds?.refreshToken ?? keychainRefreshToken ?? this.credentials.refreshToken;
|
|
109
110
|
const newCreds = await this.deps.refreshToken(refreshToken);
|
|
110
111
|
if (newCreds) {
|
|
111
112
|
logger.info('[oauth-token-manager] Token refresh successful');
|
|
112
113
|
try {
|
|
113
|
-
this.deps.
|
|
114
|
+
if (this.deps.keychainServiceName && this.deps.writeToKeychain) {
|
|
115
|
+
this.deps.writeToKeychain(newCreds, this.deps.keychainServiceName);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.deps.saveCredentials(newCreds, this.deps.credentialsFilePath);
|
|
119
|
+
}
|
|
114
120
|
}
|
|
115
121
|
catch (err) {
|
|
116
122
|
logger.warn(`[oauth-token-manager] Failed to save refreshed credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-token-manager.js","sourceRoot":"","sources":["../../src/docker/oauth-token-manager.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"oauth-token-manager.js","sourceRoot":"","sources":["../../src/docker/oauth-token-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,32 @@ export type RequestBodyRewriter = (body: Record<string, unknown>, context: {
|
|
|
22
22
|
method: string;
|
|
23
23
|
path: string;
|
|
24
24
|
}) => RewriteResult | null;
|
|
25
|
+
/**
|
|
26
|
+
* Upstream target for provider requests when the default API host
|
|
27
|
+
* is overridden via environment variables (e.g., ANTHROPIC_BASE_URL).
|
|
28
|
+
* The MITM proxy uses this to route upstream connections to a custom
|
|
29
|
+
* API gateway (e.g., LiteLLM) instead of the provider's canonical host.
|
|
30
|
+
*/
|
|
31
|
+
export interface UpstreamTarget {
|
|
32
|
+
/** Hostname of the upstream server. */
|
|
33
|
+
readonly hostname: string;
|
|
34
|
+
/** Port number of the upstream server. */
|
|
35
|
+
readonly port: number;
|
|
36
|
+
/** Path prefix to prepend to all request paths ('' for none). */
|
|
37
|
+
readonly pathPrefix: string;
|
|
38
|
+
/** Whether to use TLS for the upstream connection. */
|
|
39
|
+
readonly useTls: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parses a base URL string into an UpstreamTarget.
|
|
43
|
+
*
|
|
44
|
+
* Supports http:// and https:// URLs. The path component becomes the
|
|
45
|
+
* pathPrefix (trailing slashes stripped). Defaults to port 443 for
|
|
46
|
+
* https and port 80 for http when no port is specified.
|
|
47
|
+
*
|
|
48
|
+
* @throws {Error} If the URL uses an unsupported protocol.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseUpstreamBaseUrl(baseUrl: string): UpstreamTarget;
|
|
25
51
|
export interface ProviderConfig {
|
|
26
52
|
/** Hostname of the API endpoint (e.g., 'api.anthropic.com'). */
|
|
27
53
|
readonly host: string;
|
|
@@ -52,6 +78,12 @@ export interface ProviderConfig {
|
|
|
52
78
|
* Only applies to POST requests. Requires requestRewriter to be set.
|
|
53
79
|
*/
|
|
54
80
|
readonly rewriteEndpoints?: readonly string[];
|
|
81
|
+
/**
|
|
82
|
+
* Optional upstream target override. When set, the MITM proxy routes
|
|
83
|
+
* requests to this target instead of the provider's canonical host.
|
|
84
|
+
* Populated from environment variables (e.g., ANTHROPIC_BASE_URL).
|
|
85
|
+
*/
|
|
86
|
+
readonly upstreamTarget?: UpstreamTarget;
|
|
55
87
|
}
|
|
56
88
|
export interface EndpointPattern {
|
|
57
89
|
readonly method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -5,6 +5,26 @@
|
|
|
5
5
|
* and fake key prefix. The proxy uses these to filter requests and swap
|
|
6
6
|
* sentinel keys for real ones.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parses a base URL string into an UpstreamTarget.
|
|
10
|
+
*
|
|
11
|
+
* Supports http:// and https:// URLs. The path component becomes the
|
|
12
|
+
* pathPrefix (trailing slashes stripped). Defaults to port 443 for
|
|
13
|
+
* https and port 80 for http when no port is specified.
|
|
14
|
+
*
|
|
15
|
+
* @throws {Error} If the URL uses an unsupported protocol.
|
|
16
|
+
*/
|
|
17
|
+
export function parseUpstreamBaseUrl(baseUrl) {
|
|
18
|
+
const parsed = new URL(baseUrl);
|
|
19
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
20
|
+
throw new Error(`Unsupported protocol in upstream base URL: ${parsed.protocol}`);
|
|
21
|
+
}
|
|
22
|
+
const useTls = parsed.protocol === 'https:';
|
|
23
|
+
const defaultPort = useTls ? 443 : 80;
|
|
24
|
+
const port = parsed.port ? parseInt(parsed.port, 10) : defaultPort;
|
|
25
|
+
const pathPrefix = parsed.pathname.replace(/\/+$/, '');
|
|
26
|
+
return { hostname: parsed.hostname, port, pathPrefix, useTls };
|
|
27
|
+
}
|
|
8
28
|
/**
|
|
9
29
|
* Checks whether a request method+path is in the provider's allowlist.
|
|
10
30
|
*/
|