@vybestack/llxprt-code-core 0.7.0-nightly.251214.7c8736a50 → 0.7.0-nightly.251215.66bf0bc39
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/dist/src/auth/codex-device-flow.d.ts +56 -0
- package/dist/src/auth/codex-device-flow.js +231 -0
- package/dist/src/auth/codex-device-flow.js.map +1 -0
- package/dist/src/auth/token-store.d.ts +38 -8
- package/dist/src/auth/token-store.js +91 -15
- package/dist/src/auth/token-store.js.map +1 -1
- package/dist/src/auth/types.d.ts +82 -5
- package/dist/src/auth/types.js +28 -1
- package/dist/src/auth/types.js.map +1 -1
- package/dist/src/config/config.d.ts +34 -0
- package/dist/src/config/config.js +15 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/confirmation-bus/message-bus.d.ts +18 -0
- package/dist/src/confirmation-bus/message-bus.js +48 -0
- package/dist/src/confirmation-bus/message-bus.js.map +1 -1
- package/dist/src/confirmation-bus/types.d.ts +23 -2
- package/dist/src/confirmation-bus/types.js +2 -0
- package/dist/src/confirmation-bus/types.js.map +1 -1
- package/dist/src/core/bucketFailoverIntegration.d.ts +61 -0
- package/dist/src/core/bucketFailoverIntegration.js +168 -0
- package/dist/src/core/bucketFailoverIntegration.js.map +1 -0
- package/dist/src/core/geminiChat.js +18 -3
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +6 -0
- package/dist/src/mcp/token-storage/keychain-token-storage.js +22 -4
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
- package/dist/src/providers/BaseProvider.d.ts +1 -0
- package/dist/src/providers/BaseProvider.js +2 -1
- package/dist/src/providers/BaseProvider.js.map +1 -1
- package/dist/src/providers/anthropic/AnthropicProvider.js +38 -8
- package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
- package/dist/src/providers/gemini/GeminiProvider.d.ts +11 -0
- package/dist/src/providers/gemini/GeminiProvider.js +75 -10
- package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.js +76 -12
- package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
- package/dist/src/providers/openai-responses/CODEX_MODELS.d.ts +23 -0
- package/dist/src/providers/openai-responses/CODEX_MODELS.js +48 -0
- package/dist/src/providers/openai-responses/CODEX_MODELS.js.map +1 -0
- package/dist/src/providers/openai-responses/CODEX_PROMPT.d.ts +22 -0
- package/dist/src/providers/openai-responses/CODEX_PROMPT.js +332 -0
- package/dist/src/providers/openai-responses/CODEX_PROMPT.js.map +1 -0
- package/dist/src/providers/openai-responses/OpenAIResponsesProvider.d.ts +38 -2
- package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js +191 -32
- package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js.map +1 -1
- package/dist/src/providers/openai-vercel/OpenAIVercelProvider.js +90 -7
- package/dist/src/providers/openai-vercel/OpenAIVercelProvider.js.map +1 -1
- package/dist/src/types/modelParams.d.ts +41 -0
- package/dist/src/types/modelParams.js +30 -0
- package/dist/src/types/modelParams.js.map +1 -1
- package/dist/src/utils/retry.js +33 -0
- package/dist/src/utils/retry.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { type CodexOAuthToken } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Codex OAuth PKCE flow implementation
|
|
9
|
+
* Implements OAuth 2.0 Authorization Code flow with PKCE for Codex authentication
|
|
10
|
+
*/
|
|
11
|
+
export declare class CodexDeviceFlow {
|
|
12
|
+
private logger;
|
|
13
|
+
private codeVerifiers;
|
|
14
|
+
constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Build authorization URL for browser-based OAuth flow
|
|
17
|
+
* @param redirectUri Callback URL for OAuth redirect
|
|
18
|
+
* @param state Random state parameter for CSRF protection
|
|
19
|
+
* @returns Authorization URL to open in browser
|
|
20
|
+
*/
|
|
21
|
+
buildAuthorizationUrl(redirectUri: string, state: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Exchange authorization code for OAuth tokens
|
|
24
|
+
* @param authCode Authorization code from OAuth callback
|
|
25
|
+
* @param redirectUri Callback URL (must match the one used in authorization request)
|
|
26
|
+
* @param state State parameter from OAuth callback
|
|
27
|
+
* @returns Validated CodexOAuthToken with account_id
|
|
28
|
+
* @throws Error if code verifier not found for state or token exchange fails
|
|
29
|
+
*/
|
|
30
|
+
exchangeCodeForToken(authCode: string, redirectUri: string, state: string): Promise<CodexOAuthToken>;
|
|
31
|
+
/**
|
|
32
|
+
* Refresh an expired access token using refresh token
|
|
33
|
+
* @param refreshToken Valid refresh token
|
|
34
|
+
* @returns New CodexOAuthToken with updated expiry
|
|
35
|
+
* @throws Error if refresh fails or id_token missing
|
|
36
|
+
*/
|
|
37
|
+
refreshToken(refreshToken: string): Promise<CodexOAuthToken>;
|
|
38
|
+
/**
|
|
39
|
+
* Extract account_id from id_token JWT without external libraries
|
|
40
|
+
* JWT format: header.payload.signature (base64url encoded)
|
|
41
|
+
* @param idToken JWT id_token from OAuth response
|
|
42
|
+
* @returns account_id extracted from JWT claims
|
|
43
|
+
* @throws Error if JWT format invalid or account_id not found
|
|
44
|
+
*/
|
|
45
|
+
private extractAccountIdFromIdToken;
|
|
46
|
+
/**
|
|
47
|
+
* Helper to throw error when id_token is missing
|
|
48
|
+
* @throws Error indicating id_token required
|
|
49
|
+
*/
|
|
50
|
+
private throwMissingAccountId;
|
|
51
|
+
/**
|
|
52
|
+
* Generates PKCE code verifier and challenge
|
|
53
|
+
* @returns Object containing verifier and challenge strings
|
|
54
|
+
*/
|
|
55
|
+
private generatePKCE;
|
|
56
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes, createHash } from 'crypto';
|
|
7
|
+
import { DebugLogger } from '../debug/index.js';
|
|
8
|
+
import { CodexOAuthTokenSchema, CodexTokenResponseSchema, } from './types.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
/**
|
|
11
|
+
* Codex-specific OAuth configuration
|
|
12
|
+
*/
|
|
13
|
+
const CODEX_CONFIG = {
|
|
14
|
+
clientId: 'app_EMoamEEZ73f0CkXaXp7hrann',
|
|
15
|
+
issuer: 'https://auth.openai.com',
|
|
16
|
+
tokenEndpoint: 'https://auth.openai.com/oauth/token',
|
|
17
|
+
authorizationEndpoint: 'https://auth.openai.com/oauth/authorize',
|
|
18
|
+
scopes: ['openid', 'profile', 'email', 'offline_access'],
|
|
19
|
+
originator: 'codex_cli_rs',
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* JWT payload schema for account_id extraction
|
|
23
|
+
* Handles multiple possible locations of account_id in the JWT
|
|
24
|
+
*/
|
|
25
|
+
const JwtPayloadSchema = z.object({
|
|
26
|
+
'https://api.openai.com/auth': z
|
|
27
|
+
.object({
|
|
28
|
+
chatgpt_account_id: z.string().optional(),
|
|
29
|
+
account_id: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
.optional(),
|
|
32
|
+
account_id: z.string().optional(),
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Codex OAuth PKCE flow implementation
|
|
36
|
+
* Implements OAuth 2.0 Authorization Code flow with PKCE for Codex authentication
|
|
37
|
+
*/
|
|
38
|
+
export class CodexDeviceFlow {
|
|
39
|
+
logger;
|
|
40
|
+
codeVerifiers = new Map();
|
|
41
|
+
constructor() {
|
|
42
|
+
this.logger = new DebugLogger('llxprt:auth:codex-device-flow');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build authorization URL for browser-based OAuth flow
|
|
46
|
+
* @param redirectUri Callback URL for OAuth redirect
|
|
47
|
+
* @param state Random state parameter for CSRF protection
|
|
48
|
+
* @returns Authorization URL to open in browser
|
|
49
|
+
*/
|
|
50
|
+
buildAuthorizationUrl(redirectUri, state) {
|
|
51
|
+
this.logger.debug(() => `[FLOW] buildAuthorizationUrl() called with redirectUri=${redirectUri}, state=${state.substring(0, 8)}...`);
|
|
52
|
+
const { verifier, challenge } = this.generatePKCE();
|
|
53
|
+
this.logger.debug(() => `[FLOW] PKCE generated: verifier length=${verifier.length}, challenge length=${challenge.length}`);
|
|
54
|
+
this.codeVerifiers.set(state, verifier);
|
|
55
|
+
this.logger.debug(() => `[FLOW] Stored PKCE verifier for state, codeVerifiers.size=${this.codeVerifiers.size}`);
|
|
56
|
+
// Manually construct query string to use %20 for spaces (not +)
|
|
57
|
+
// This ensures proper parsing with decodeURIComponent
|
|
58
|
+
// Include all required params per shell-scripts/codex-oauth.sh
|
|
59
|
+
const params = [
|
|
60
|
+
`response_type=code`,
|
|
61
|
+
`client_id=${encodeURIComponent(CODEX_CONFIG.clientId)}`,
|
|
62
|
+
`redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
63
|
+
`scope=${encodeURIComponent(CODEX_CONFIG.scopes.join(' '))}`,
|
|
64
|
+
`code_challenge=${encodeURIComponent(challenge)}`,
|
|
65
|
+
`code_challenge_method=S256`,
|
|
66
|
+
`id_token_add_organizations=true`,
|
|
67
|
+
`codex_cli_simplified_flow=true`,
|
|
68
|
+
`state=${encodeURIComponent(state)}`,
|
|
69
|
+
`originator=${encodeURIComponent(CODEX_CONFIG.originator)}`,
|
|
70
|
+
`theme=dark`,
|
|
71
|
+
].join('&');
|
|
72
|
+
this.logger.debug(() => '[FLOW] Built authorization URL with PKCE S256');
|
|
73
|
+
return `${CODEX_CONFIG.authorizationEndpoint}?${params}`;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Exchange authorization code for OAuth tokens
|
|
77
|
+
* @param authCode Authorization code from OAuth callback
|
|
78
|
+
* @param redirectUri Callback URL (must match the one used in authorization request)
|
|
79
|
+
* @param state State parameter from OAuth callback
|
|
80
|
+
* @returns Validated CodexOAuthToken with account_id
|
|
81
|
+
* @throws Error if code verifier not found for state or token exchange fails
|
|
82
|
+
*/
|
|
83
|
+
async exchangeCodeForToken(authCode, redirectUri, state) {
|
|
84
|
+
this.logger.debug(() => `[FLOW] exchangeCodeForToken() called with code=${authCode.substring(0, 10)}..., redirectUri=${redirectUri}, state=${state.substring(0, 8)}...`);
|
|
85
|
+
const codeVerifier = this.codeVerifiers.get(state);
|
|
86
|
+
if (!codeVerifier) {
|
|
87
|
+
this.logger.debug(() => `[FLOW] PKCE verifier NOT FOUND for state! Available states: ${Array.from(this.codeVerifiers.keys())
|
|
88
|
+
.map((k) => k.substring(0, 8))
|
|
89
|
+
.join(', ')}`);
|
|
90
|
+
throw new Error(`PKCE code verifier not found for state: ${state}`);
|
|
91
|
+
}
|
|
92
|
+
this.logger.debug(() => `[FLOW] Found PKCE verifier for state, length=${codeVerifier.length}`);
|
|
93
|
+
this.logger.debug(() => `[FLOW] Making token exchange request to ${CODEX_CONFIG.tokenEndpoint}`);
|
|
94
|
+
const response = await fetch(CODEX_CONFIG.tokenEndpoint, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: {
|
|
97
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
98
|
+
Accept: 'application/json',
|
|
99
|
+
},
|
|
100
|
+
body: new URLSearchParams({
|
|
101
|
+
grant_type: 'authorization_code',
|
|
102
|
+
code: authCode,
|
|
103
|
+
redirect_uri: redirectUri,
|
|
104
|
+
client_id: CODEX_CONFIG.clientId,
|
|
105
|
+
code_verifier: codeVerifier,
|
|
106
|
+
}).toString(),
|
|
107
|
+
});
|
|
108
|
+
this.logger.debug(() => `[FLOW] Token exchange response status: ${response.status}`);
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
const errorText = await response.text();
|
|
111
|
+
this.logger.debug(() => `[FLOW] Token exchange FAILED: ${errorText}`);
|
|
112
|
+
throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
|
|
113
|
+
}
|
|
114
|
+
const data = await response.json();
|
|
115
|
+
this.logger.debug(() => `[FLOW] Token response received, keys: ${Object.keys(data).join(', ')}`);
|
|
116
|
+
// Validate with Zod schema - NO TYPE ASSERTIONS
|
|
117
|
+
const tokenResponse = CodexTokenResponseSchema.parse(data);
|
|
118
|
+
this.logger.debug(() => `[FLOW] Token response validated: has_id_token=${!!tokenResponse.id_token}, has_refresh_token=${!!tokenResponse.refresh_token}, expires_in=${tokenResponse.expires_in}`);
|
|
119
|
+
// Extract account_id from id_token JWT
|
|
120
|
+
this.logger.debug(() => '[FLOW] Extracting account_id from id_token...');
|
|
121
|
+
const accountId = tokenResponse.id_token
|
|
122
|
+
? this.extractAccountIdFromIdToken(tokenResponse.id_token)
|
|
123
|
+
: this.throwMissingAccountId();
|
|
124
|
+
this.logger.debug(() => `[FLOW] Extracted account_id: ${accountId.substring(0, 8)}...`);
|
|
125
|
+
// Build validated Codex token - use Unix timestamp in SECONDS (not milliseconds)
|
|
126
|
+
const now = Math.floor(Date.now() / 1000);
|
|
127
|
+
const expiresIn = tokenResponse.expires_in || 3600; // Default 1 hour
|
|
128
|
+
const expiry = now + expiresIn;
|
|
129
|
+
this.logger.debug(() => `[FLOW] Building CodexOAuthToken with expiry=${expiry} (in ${expiresIn}s)`);
|
|
130
|
+
const codexToken = CodexOAuthTokenSchema.parse({
|
|
131
|
+
access_token: tokenResponse.access_token,
|
|
132
|
+
token_type: tokenResponse.token_type,
|
|
133
|
+
expiry,
|
|
134
|
+
refresh_token: tokenResponse.refresh_token,
|
|
135
|
+
account_id: accountId,
|
|
136
|
+
id_token: tokenResponse.id_token,
|
|
137
|
+
});
|
|
138
|
+
this.logger.debug(() => `[FLOW] Token exchange successful! account_id=${accountId.substring(0, 8)}..., token_type=${codexToken.token_type}`);
|
|
139
|
+
this.codeVerifiers.delete(state);
|
|
140
|
+
this.logger.debug(() => `[FLOW] Cleaned up PKCE verifier, remaining: ${this.codeVerifiers.size}`);
|
|
141
|
+
return codexToken;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Refresh an expired access token using refresh token
|
|
145
|
+
* @param refreshToken Valid refresh token
|
|
146
|
+
* @returns New CodexOAuthToken with updated expiry
|
|
147
|
+
* @throws Error if refresh fails or id_token missing
|
|
148
|
+
*/
|
|
149
|
+
async refreshToken(refreshToken) {
|
|
150
|
+
this.logger.debug(() => 'Refreshing expired token');
|
|
151
|
+
const response = await fetch(CODEX_CONFIG.tokenEndpoint, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
155
|
+
Accept: 'application/json',
|
|
156
|
+
},
|
|
157
|
+
body: new URLSearchParams({
|
|
158
|
+
grant_type: 'refresh_token',
|
|
159
|
+
refresh_token: refreshToken,
|
|
160
|
+
client_id: CODEX_CONFIG.clientId,
|
|
161
|
+
}).toString(),
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Token refresh failed: ${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
const data = await response.json();
|
|
167
|
+
const tokenResponse = CodexTokenResponseSchema.parse(data);
|
|
168
|
+
// Extract account_id from new id_token or throw
|
|
169
|
+
const accountId = tokenResponse.id_token
|
|
170
|
+
? this.extractAccountIdFromIdToken(tokenResponse.id_token)
|
|
171
|
+
: this.throwMissingAccountId();
|
|
172
|
+
// Use Unix timestamp in SECONDS
|
|
173
|
+
const now = Math.floor(Date.now() / 1000);
|
|
174
|
+
const expiresIn = tokenResponse.expires_in || 3600;
|
|
175
|
+
const expiry = now + expiresIn;
|
|
176
|
+
return CodexOAuthTokenSchema.parse({
|
|
177
|
+
access_token: tokenResponse.access_token,
|
|
178
|
+
token_type: tokenResponse.token_type,
|
|
179
|
+
expiry,
|
|
180
|
+
refresh_token: tokenResponse.refresh_token ?? refreshToken,
|
|
181
|
+
account_id: accountId,
|
|
182
|
+
id_token: tokenResponse.id_token,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Extract account_id from id_token JWT without external libraries
|
|
187
|
+
* JWT format: header.payload.signature (base64url encoded)
|
|
188
|
+
* @param idToken JWT id_token from OAuth response
|
|
189
|
+
* @returns account_id extracted from JWT claims
|
|
190
|
+
* @throws Error if JWT format invalid or account_id not found
|
|
191
|
+
*/
|
|
192
|
+
extractAccountIdFromIdToken(idToken) {
|
|
193
|
+
const parts = idToken.split('.');
|
|
194
|
+
if (parts.length !== 3) {
|
|
195
|
+
throw new Error('Invalid JWT format: expected 3 parts');
|
|
196
|
+
}
|
|
197
|
+
// Decode payload (middle part) from base64url
|
|
198
|
+
const payload = parts[1];
|
|
199
|
+
const decoded = Buffer.from(payload, 'base64url').toString('utf-8');
|
|
200
|
+
// Parse and validate with Zod
|
|
201
|
+
const parsedPayload = JSON.parse(decoded);
|
|
202
|
+
const validated = JwtPayloadSchema.parse(parsedPayload);
|
|
203
|
+
// Extract account_id from OpenAI-specific claim or root
|
|
204
|
+
const accountId = validated['https://api.openai.com/auth']?.chatgpt_account_id ||
|
|
205
|
+
validated['https://api.openai.com/auth']?.account_id ||
|
|
206
|
+
validated.account_id;
|
|
207
|
+
if (!accountId) {
|
|
208
|
+
throw new Error('No account_id found in id_token JWT claims');
|
|
209
|
+
}
|
|
210
|
+
return accountId;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Helper to throw error when id_token is missing
|
|
214
|
+
* @throws Error indicating id_token required
|
|
215
|
+
*/
|
|
216
|
+
throwMissingAccountId() {
|
|
217
|
+
throw new Error('id_token required to extract account_id');
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Generates PKCE code verifier and challenge
|
|
221
|
+
* @returns Object containing verifier and challenge strings
|
|
222
|
+
*/
|
|
223
|
+
generatePKCE() {
|
|
224
|
+
// Generate 64 random bytes for verifier (matches shell script and Rust CLI)
|
|
225
|
+
const verifier = randomBytes(64).toString('base64url');
|
|
226
|
+
// Create SHA-256 hash of verifier for challenge (S256 method)
|
|
227
|
+
const challenge = createHash('sha256').update(verifier).digest('base64url');
|
|
228
|
+
return { verifier, challenge };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=codex-device-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-device-flow.js","sourceRoot":"","sources":["../../../src/auth/codex-device-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Vybestack LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { type OAuthToken } from './types.js';
|
|
6
|
+
import { type OAuthToken, type BucketStats } from './types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Interface for multi-provider OAuth token storage
|
|
9
9
|
*/
|
|
@@ -12,24 +12,40 @@ export interface TokenStore {
|
|
|
12
12
|
* Save an OAuth token for a specific provider
|
|
13
13
|
* @param provider - The provider name (e.g., 'gemini', 'qwen')
|
|
14
14
|
* @param token - The OAuth token to save
|
|
15
|
+
* @param bucket - Optional bucket name for multi-account support
|
|
15
16
|
*/
|
|
16
|
-
saveToken(provider: string, token: OAuthToken): Promise<void>;
|
|
17
|
+
saveToken(provider: string, token: OAuthToken, bucket?: string): Promise<void>;
|
|
17
18
|
/**
|
|
18
19
|
* Retrieve an OAuth token for a specific provider
|
|
19
20
|
* @param provider - The provider name
|
|
21
|
+
* @param bucket - Optional bucket name for multi-account support
|
|
20
22
|
* @returns The token if found, null otherwise
|
|
21
23
|
*/
|
|
22
|
-
getToken(provider: string): Promise<OAuthToken | null>;
|
|
24
|
+
getToken(provider: string, bucket?: string): Promise<OAuthToken | null>;
|
|
23
25
|
/**
|
|
24
26
|
* Remove an OAuth token for a specific provider
|
|
25
27
|
* @param provider - The provider name
|
|
28
|
+
* @param bucket - Optional bucket name for multi-account support
|
|
26
29
|
*/
|
|
27
|
-
removeToken(provider: string): Promise<void>;
|
|
30
|
+
removeToken(provider: string, bucket?: string): Promise<void>;
|
|
28
31
|
/**
|
|
29
32
|
* List all providers that have stored tokens
|
|
30
33
|
* @returns Array of provider names with stored tokens
|
|
31
34
|
*/
|
|
32
35
|
listProviders(): Promise<string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* List all buckets for a specific provider
|
|
38
|
+
* @param provider - The provider name
|
|
39
|
+
* @returns Array of bucket names for the provider
|
|
40
|
+
*/
|
|
41
|
+
listBuckets(provider: string): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Get usage statistics for a specific bucket
|
|
44
|
+
* @param provider - The provider name
|
|
45
|
+
* @param bucket - The bucket name
|
|
46
|
+
* @returns Bucket statistics if available, null otherwise
|
|
47
|
+
*/
|
|
48
|
+
getBucketStats(provider: string, bucket: string): Promise<BucketStats | null>;
|
|
33
49
|
}
|
|
34
50
|
/**
|
|
35
51
|
* Implementation of multi-provider token storage
|
|
@@ -37,23 +53,32 @@ export interface TokenStore {
|
|
|
37
53
|
*/
|
|
38
54
|
export declare class MultiProviderTokenStore implements TokenStore {
|
|
39
55
|
private readonly basePath;
|
|
40
|
-
constructor();
|
|
56
|
+
constructor(basePath?: string);
|
|
41
57
|
/**
|
|
42
58
|
* Save an OAuth token for a specific provider
|
|
43
59
|
*/
|
|
44
|
-
saveToken(provider: string, token: OAuthToken): Promise<void>;
|
|
60
|
+
saveToken(provider: string, token: OAuthToken, bucket?: string): Promise<void>;
|
|
45
61
|
/**
|
|
46
62
|
* Retrieve an OAuth token for a specific provider
|
|
47
63
|
*/
|
|
48
|
-
getToken(provider: string): Promise<OAuthToken | null>;
|
|
64
|
+
getToken(provider: string, bucket?: string): Promise<OAuthToken | null>;
|
|
49
65
|
/**
|
|
50
66
|
* Remove an OAuth token for a specific provider
|
|
51
67
|
*/
|
|
52
|
-
removeToken(provider: string): Promise<void>;
|
|
68
|
+
removeToken(provider: string, bucket?: string): Promise<void>;
|
|
53
69
|
/**
|
|
54
70
|
* List all providers that have stored tokens
|
|
55
71
|
*/
|
|
56
72
|
listProviders(): Promise<string[]>;
|
|
73
|
+
/**
|
|
74
|
+
* List all buckets for a specific provider
|
|
75
|
+
*/
|
|
76
|
+
listBuckets(provider: string): Promise<string[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Get usage statistics for a specific bucket
|
|
79
|
+
* Returns placeholder statistics for now
|
|
80
|
+
*/
|
|
81
|
+
getBucketStats(provider: string, bucket: string): Promise<BucketStats | null>;
|
|
57
82
|
/**
|
|
58
83
|
* Ensure the OAuth directory exists with secure permissions
|
|
59
84
|
*/
|
|
@@ -63,4 +88,9 @@ export declare class MultiProviderTokenStore implements TokenStore {
|
|
|
63
88
|
* Uses path.join to prevent path traversal attacks
|
|
64
89
|
*/
|
|
65
90
|
private getTokenPath;
|
|
91
|
+
/**
|
|
92
|
+
* Validate bucket name for filesystem safety
|
|
93
|
+
* Rejects characters that could cause issues with file paths
|
|
94
|
+
*/
|
|
95
|
+
private validateBucketName;
|
|
66
96
|
}
|
|
@@ -6,30 +6,34 @@
|
|
|
6
6
|
import { promises as fs } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
|
-
import { OAuthTokenSchema } from './types.js';
|
|
9
|
+
import { OAuthTokenSchema, } from './types.js';
|
|
10
10
|
/**
|
|
11
11
|
* Implementation of multi-provider token storage
|
|
12
12
|
* Stores tokens securely in ~/.llxprt/oauth/ directory
|
|
13
13
|
*/
|
|
14
14
|
export class MultiProviderTokenStore {
|
|
15
15
|
basePath;
|
|
16
|
-
constructor() {
|
|
17
|
-
this.basePath = join(homedir(), '.llxprt', 'oauth');
|
|
16
|
+
constructor(basePath) {
|
|
17
|
+
this.basePath = basePath ?? join(homedir(), '.llxprt', 'oauth');
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Save an OAuth token for a specific provider
|
|
21
21
|
*/
|
|
22
|
-
async saveToken(provider, token) {
|
|
22
|
+
async saveToken(provider, token, bucket) {
|
|
23
23
|
// Validate provider name
|
|
24
24
|
if (!provider || provider.trim() === '') {
|
|
25
25
|
throw new Error('Provider name cannot be empty');
|
|
26
26
|
}
|
|
27
|
-
// Validate
|
|
28
|
-
|
|
27
|
+
// Validate bucket name if provided
|
|
28
|
+
if (bucket) {
|
|
29
|
+
this.validateBucketName(bucket);
|
|
30
|
+
}
|
|
31
|
+
// Validate token structure, but allow extra provider-specific fields (e.g., account_id for Codex)
|
|
32
|
+
const validatedToken = OAuthTokenSchema.passthrough().parse(token);
|
|
29
33
|
// Ensure directory exists with secure permissions
|
|
30
34
|
await this.ensureDirectory();
|
|
31
35
|
// Generate file paths
|
|
32
|
-
const tokenPath = this.getTokenPath(provider);
|
|
36
|
+
const tokenPath = this.getTokenPath(provider, bucket);
|
|
33
37
|
const tempPath = `${tokenPath}.tmp-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
34
38
|
try {
|
|
35
39
|
// Write to temporary file first (atomic operation)
|
|
@@ -57,13 +61,17 @@ export class MultiProviderTokenStore {
|
|
|
57
61
|
/**
|
|
58
62
|
* Retrieve an OAuth token for a specific provider
|
|
59
63
|
*/
|
|
60
|
-
async getToken(provider) {
|
|
64
|
+
async getToken(provider, bucket) {
|
|
65
|
+
// Validate bucket name if provided
|
|
66
|
+
if (bucket) {
|
|
67
|
+
this.validateBucketName(bucket);
|
|
68
|
+
}
|
|
61
69
|
try {
|
|
62
|
-
const tokenPath = this.getTokenPath(provider);
|
|
70
|
+
const tokenPath = this.getTokenPath(provider, bucket);
|
|
63
71
|
const content = await fs.readFile(tokenPath, 'utf8');
|
|
64
72
|
const parsed = JSON.parse(content);
|
|
65
|
-
// Validate token structure
|
|
66
|
-
const validatedToken = OAuthTokenSchema.parse(parsed);
|
|
73
|
+
// Validate token structure, but allow extra provider-specific fields (e.g., account_id for Codex)
|
|
74
|
+
const validatedToken = OAuthTokenSchema.passthrough().parse(parsed);
|
|
67
75
|
return validatedToken;
|
|
68
76
|
}
|
|
69
77
|
catch (_error) {
|
|
@@ -74,9 +82,13 @@ export class MultiProviderTokenStore {
|
|
|
74
82
|
/**
|
|
75
83
|
* Remove an OAuth token for a specific provider
|
|
76
84
|
*/
|
|
77
|
-
async removeToken(provider) {
|
|
85
|
+
async removeToken(provider, bucket) {
|
|
86
|
+
// Validate bucket name if provided
|
|
87
|
+
if (bucket) {
|
|
88
|
+
this.validateBucketName(bucket);
|
|
89
|
+
}
|
|
78
90
|
try {
|
|
79
|
-
const tokenPath = this.getTokenPath(provider);
|
|
91
|
+
const tokenPath = this.getTokenPath(provider, bucket);
|
|
80
92
|
await fs.unlink(tokenPath);
|
|
81
93
|
}
|
|
82
94
|
catch (error) {
|
|
@@ -115,6 +127,59 @@ export class MultiProviderTokenStore {
|
|
|
115
127
|
throw error;
|
|
116
128
|
}
|
|
117
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* List all buckets for a specific provider
|
|
132
|
+
*/
|
|
133
|
+
async listBuckets(provider) {
|
|
134
|
+
try {
|
|
135
|
+
const files = await fs.readdir(this.basePath);
|
|
136
|
+
const providerPrefix = `${provider}`;
|
|
137
|
+
const buckets = files
|
|
138
|
+
.filter((file) => file.startsWith(providerPrefix) && file.endsWith('.json'))
|
|
139
|
+
.map((file) => {
|
|
140
|
+
const name = file.slice(0, -5); // Remove .json extension
|
|
141
|
+
if (name === provider) {
|
|
142
|
+
return 'default';
|
|
143
|
+
}
|
|
144
|
+
// Remove "provider-" prefix to get bucket name
|
|
145
|
+
return name.slice(provider.length + 1);
|
|
146
|
+
})
|
|
147
|
+
.sort();
|
|
148
|
+
return buckets;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// If directory doesn't exist, return empty array
|
|
152
|
+
if (error &&
|
|
153
|
+
typeof error === 'object' &&
|
|
154
|
+
'code' in error &&
|
|
155
|
+
error.code === 'ENOENT') {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get usage statistics for a specific bucket
|
|
163
|
+
* Returns placeholder statistics for now
|
|
164
|
+
*/
|
|
165
|
+
async getBucketStats(provider, bucket) {
|
|
166
|
+
// Validate bucket name if provided
|
|
167
|
+
if (bucket) {
|
|
168
|
+
this.validateBucketName(bucket);
|
|
169
|
+
}
|
|
170
|
+
// Check if bucket exists
|
|
171
|
+
const token = await this.getToken(provider, bucket);
|
|
172
|
+
if (!token) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
// Return placeholder stats - actual implementation would track usage
|
|
176
|
+
return {
|
|
177
|
+
bucket,
|
|
178
|
+
requestCount: 0,
|
|
179
|
+
percentage: 0,
|
|
180
|
+
lastUsed: undefined,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
118
183
|
/**
|
|
119
184
|
* Ensure the OAuth directory exists with secure permissions
|
|
120
185
|
*/
|
|
@@ -144,8 +209,19 @@ export class MultiProviderTokenStore {
|
|
|
144
209
|
* Generate secure file path for a provider token
|
|
145
210
|
* Uses path.join to prevent path traversal attacks
|
|
146
211
|
*/
|
|
147
|
-
getTokenPath(provider) {
|
|
148
|
-
|
|
212
|
+
getTokenPath(provider, bucket) {
|
|
213
|
+
const bucketSuffix = bucket && bucket !== 'default' ? `-${bucket}` : '';
|
|
214
|
+
return join(this.basePath, `${provider}${bucketSuffix}.json`);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Validate bucket name for filesystem safety
|
|
218
|
+
* Rejects characters that could cause issues with file paths
|
|
219
|
+
*/
|
|
220
|
+
validateBucketName(bucket) {
|
|
221
|
+
const invalidChars = /[:/\\<>"|?*]/;
|
|
222
|
+
if (invalidChars.test(bucket)) {
|
|
223
|
+
throw new Error(`Invalid bucket name: "${bucket}". Bucket names cannot contain: : / \\ < > " | ? *`);
|
|
224
|
+
}
|
|
149
225
|
}
|
|
150
226
|
}
|
|
151
227
|
//# sourceMappingURL=token-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../../src/auth/token-store.ts"],"names":[],"mappings":"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"}
|