@chrishdx/llm-dev-server 1.0.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 +164 -0
- package/backend/bin/llm-dev-server.js +3 -0
- package/backend/dist/cli.d.ts +15 -0
- package/backend/dist/cli.d.ts.map +1 -0
- package/backend/dist/cli.js +326 -0
- package/backend/dist/cli.js.map +1 -0
- package/backend/dist/config/database.d.ts +10 -0
- package/backend/dist/config/database.d.ts.map +1 -0
- package/backend/dist/config/database.js +61 -0
- package/backend/dist/config/database.js.map +1 -0
- package/backend/dist/config/environment.d.ts +20 -0
- package/backend/dist/config/environment.d.ts.map +1 -0
- package/backend/dist/config/environment.js +77 -0
- package/backend/dist/config/environment.js.map +1 -0
- package/backend/dist/controllers/AuthController.d.ts +18 -0
- package/backend/dist/controllers/AuthController.d.ts.map +1 -0
- package/backend/dist/controllers/AuthController.js +282 -0
- package/backend/dist/controllers/AuthController.js.map +1 -0
- package/backend/dist/controllers/ConversationController.d.ts +44 -0
- package/backend/dist/controllers/ConversationController.d.ts.map +1 -0
- package/backend/dist/controllers/ConversationController.js +193 -0
- package/backend/dist/controllers/ConversationController.js.map +1 -0
- package/backend/dist/controllers/JobController.d.ts +49 -0
- package/backend/dist/controllers/JobController.d.ts.map +1 -0
- package/backend/dist/controllers/JobController.js +227 -0
- package/backend/dist/controllers/JobController.js.map +1 -0
- package/backend/dist/index.d.ts +2 -0
- package/backend/dist/index.d.ts.map +1 -0
- package/backend/dist/index.js +160 -0
- package/backend/dist/index.js.map +1 -0
- package/backend/dist/middleware/auth.middleware.d.ts +25 -0
- package/backend/dist/middleware/auth.middleware.d.ts.map +1 -0
- package/backend/dist/middleware/auth.middleware.js +116 -0
- package/backend/dist/middleware/auth.middleware.js.map +1 -0
- package/backend/dist/middleware/authelia.middleware.d.ts +26 -0
- package/backend/dist/middleware/authelia.middleware.d.ts.map +1 -0
- package/backend/dist/middleware/authelia.middleware.js +165 -0
- package/backend/dist/middleware/authelia.middleware.js.map +1 -0
- package/backend/dist/middleware/error.middleware.d.ts +23 -0
- package/backend/dist/middleware/error.middleware.d.ts.map +1 -0
- package/backend/dist/middleware/error.middleware.js +59 -0
- package/backend/dist/middleware/error.middleware.js.map +1 -0
- package/backend/dist/models/AuthToken.d.ts +42 -0
- package/backend/dist/models/AuthToken.d.ts.map +1 -0
- package/backend/dist/models/AuthToken.js +97 -0
- package/backend/dist/models/AuthToken.js.map +1 -0
- package/backend/dist/models/Conversation.d.ts +36 -0
- package/backend/dist/models/Conversation.d.ts.map +1 -0
- package/backend/dist/models/Conversation.js +100 -0
- package/backend/dist/models/Conversation.js.map +1 -0
- package/backend/dist/models/FileOperation.d.ts +36 -0
- package/backend/dist/models/FileOperation.d.ts.map +1 -0
- package/backend/dist/models/FileOperation.js +117 -0
- package/backend/dist/models/FileOperation.js.map +1 -0
- package/backend/dist/models/Job.d.ts +48 -0
- package/backend/dist/models/Job.d.ts.map +1 -0
- package/backend/dist/models/Job.js +87 -0
- package/backend/dist/models/Job.js.map +1 -0
- package/backend/dist/models/Message.d.ts +38 -0
- package/backend/dist/models/Message.d.ts.map +1 -0
- package/backend/dist/models/Message.js +87 -0
- package/backend/dist/models/Message.js.map +1 -0
- package/backend/dist/models/User.d.ts +26 -0
- package/backend/dist/models/User.d.ts.map +1 -0
- package/backend/dist/models/User.js +67 -0
- package/backend/dist/models/User.js.map +1 -0
- package/backend/dist/models/index.d.ts +13 -0
- package/backend/dist/models/index.d.ts.map +1 -0
- package/backend/dist/models/index.js +24 -0
- package/backend/dist/models/index.js.map +1 -0
- package/backend/dist/routes/auth.routes.d.ts +3 -0
- package/backend/dist/routes/auth.routes.d.ts.map +1 -0
- package/backend/dist/routes/auth.routes.js +27 -0
- package/backend/dist/routes/auth.routes.js.map +1 -0
- package/backend/dist/routes/conversation.routes.d.ts +3 -0
- package/backend/dist/routes/conversation.routes.d.ts.map +1 -0
- package/backend/dist/routes/conversation.routes.js +17 -0
- package/backend/dist/routes/conversation.routes.js.map +1 -0
- package/backend/dist/routes/filesystem.routes.d.ts +3 -0
- package/backend/dist/routes/filesystem.routes.d.ts.map +1 -0
- package/backend/dist/routes/filesystem.routes.js +64 -0
- package/backend/dist/routes/filesystem.routes.js.map +1 -0
- package/backend/dist/routes/index.d.ts +3 -0
- package/backend/dist/routes/index.d.ts.map +1 -0
- package/backend/dist/routes/index.js +27 -0
- package/backend/dist/routes/index.js.map +1 -0
- package/backend/dist/routes/job.routes.d.ts +3 -0
- package/backend/dist/routes/job.routes.d.ts.map +1 -0
- package/backend/dist/routes/job.routes.js +18 -0
- package/backend/dist/routes/job.routes.js.map +1 -0
- package/backend/dist/services/auth/BaseAuthService.d.ts +49 -0
- package/backend/dist/services/auth/BaseAuthService.d.ts.map +1 -0
- package/backend/dist/services/auth/BaseAuthService.js +97 -0
- package/backend/dist/services/auth/BaseAuthService.js.map +1 -0
- package/backend/dist/services/auth/ClaudeAuthService.d.ts +69 -0
- package/backend/dist/services/auth/ClaudeAuthService.d.ts.map +1 -0
- package/backend/dist/services/auth/ClaudeAuthService.js +401 -0
- package/backend/dist/services/auth/ClaudeAuthService.js.map +1 -0
- package/backend/dist/services/auth/CodexAuthService.d.ts +37 -0
- package/backend/dist/services/auth/CodexAuthService.d.ts.map +1 -0
- package/backend/dist/services/auth/CodexAuthService.js +186 -0
- package/backend/dist/services/auth/CodexAuthService.js.map +1 -0
- package/backend/dist/services/auth/GeminiAuthService.d.ts +50 -0
- package/backend/dist/services/auth/GeminiAuthService.d.ts.map +1 -0
- package/backend/dist/services/auth/GeminiAuthService.js +284 -0
- package/backend/dist/services/auth/GeminiAuthService.js.map +1 -0
- package/backend/dist/services/auth/JwtService.d.ts +27 -0
- package/backend/dist/services/auth/JwtService.d.ts.map +1 -0
- package/backend/dist/services/auth/JwtService.js +65 -0
- package/backend/dist/services/auth/JwtService.js.map +1 -0
- package/backend/dist/services/auth/TokenRefreshService.d.ts +36 -0
- package/backend/dist/services/auth/TokenRefreshService.d.ts.map +1 -0
- package/backend/dist/services/auth/TokenRefreshService.js +178 -0
- package/backend/dist/services/auth/TokenRefreshService.js.map +1 -0
- package/backend/dist/services/conversation/ConversationService.d.ts +89 -0
- package/backend/dist/services/conversation/ConversationService.d.ts.map +1 -0
- package/backend/dist/services/conversation/ConversationService.js +255 -0
- package/backend/dist/services/conversation/ConversationService.js.map +1 -0
- package/backend/dist/services/job/JobService.d.ts +83 -0
- package/backend/dist/services/job/JobService.d.ts.map +1 -0
- package/backend/dist/services/job/JobService.js +213 -0
- package/backend/dist/services/job/JobService.js.map +1 -0
- package/backend/dist/services/job/WorkingDirectoryService.d.ts +73 -0
- package/backend/dist/services/job/WorkingDirectoryService.d.ts.map +1 -0
- package/backend/dist/services/job/WorkingDirectoryService.js +289 -0
- package/backend/dist/services/job/WorkingDirectoryService.js.map +1 -0
- package/backend/dist/services/llm/ClaudeProvider.d.ts +16 -0
- package/backend/dist/services/llm/ClaudeProvider.d.ts.map +1 -0
- package/backend/dist/services/llm/ClaudeProvider.js +229 -0
- package/backend/dist/services/llm/ClaudeProvider.js.map +1 -0
- package/backend/dist/services/llm/CodexProvider.d.ts +15 -0
- package/backend/dist/services/llm/CodexProvider.d.ts.map +1 -0
- package/backend/dist/services/llm/CodexProvider.js +301 -0
- package/backend/dist/services/llm/CodexProvider.js.map +1 -0
- package/backend/dist/services/llm/GeminiProvider.d.ts +17 -0
- package/backend/dist/services/llm/GeminiProvider.d.ts.map +1 -0
- package/backend/dist/services/llm/GeminiProvider.js +190 -0
- package/backend/dist/services/llm/GeminiProvider.js.map +1 -0
- package/backend/dist/services/llm/LLMProviderBase.d.ts +76 -0
- package/backend/dist/services/llm/LLMProviderBase.d.ts.map +1 -0
- package/backend/dist/services/llm/LLMProviderBase.js +34 -0
- package/backend/dist/services/llm/LLMProviderBase.js.map +1 -0
- package/backend/dist/services/llm/ProviderFactory.d.ts +17 -0
- package/backend/dist/services/llm/ProviderFactory.d.ts.map +1 -0
- package/backend/dist/services/llm/ProviderFactory.js +58 -0
- package/backend/dist/services/llm/ProviderFactory.js.map +1 -0
- package/backend/dist/utils/crypto.d.ts +33 -0
- package/backend/dist/utils/crypto.d.ts.map +1 -0
- package/backend/dist/utils/crypto.js +165 -0
- package/backend/dist/utils/crypto.js.map +1 -0
- package/backend/dist/utils/logger.d.ts +4 -0
- package/backend/dist/utils/logger.d.ts.map +1 -0
- package/backend/dist/utils/logger.js +44 -0
- package/backend/dist/utils/logger.js.map +1 -0
- package/backend/dist/utils/validators.d.ts +22 -0
- package/backend/dist/utils/validators.d.ts.map +1 -0
- package/backend/dist/utils/validators.js +127 -0
- package/backend/dist/utils/validators.js.map +1 -0
- package/backend/package.json +45 -0
- package/backend/public/assets/index-C207-KqP.js +188 -0
- package/backend/public/index.html +12 -0
- package/package.json +96 -0
- package/shared/dist/index.d.ts +5 -0
- package/shared/dist/index.d.ts.map +1 -0
- package/shared/dist/index.js +25 -0
- package/shared/dist/index.js.map +1 -0
- package/shared/dist/types/api.d.ts +27 -0
- package/shared/dist/types/api.d.ts.map +1 -0
- package/shared/dist/types/api.js +3 -0
- package/shared/dist/types/api.js.map +1 -0
- package/shared/dist/types/auth.d.ts +62 -0
- package/shared/dist/types/auth.d.ts.map +1 -0
- package/shared/dist/types/auth.js +3 -0
- package/shared/dist/types/auth.js.map +1 -0
- package/shared/dist/types/conversation.d.ts +98 -0
- package/shared/dist/types/conversation.d.ts.map +1 -0
- package/shared/dist/types/conversation.js +3 -0
- package/shared/dist/types/conversation.js.map +1 -0
- package/shared/dist/types/job.d.ts +93 -0
- package/shared/dist/types/job.d.ts.map +1 -0
- package/shared/dist/types/job.js +3 -0
- package/shared/dist/types/job.js.map +1 -0
- package/shared/package.json +15 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ClaudeAuthService = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const BaseAuthService_1 = require("./BaseAuthService");
|
|
11
|
+
const models_1 = require("../../models");
|
|
12
|
+
const crypto_2 = require("../../utils/crypto");
|
|
13
|
+
const environment_1 = __importDefault(require("../../config/environment"));
|
|
14
|
+
const logger_1 = __importDefault(require("../../utils/logger"));
|
|
15
|
+
const CLAUDE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
16
|
+
const CLAUDE_SCOPES = [
|
|
17
|
+
'org:create_api_key',
|
|
18
|
+
'user:profile',
|
|
19
|
+
'user:inference',
|
|
20
|
+
'user:sessions:claude_code',
|
|
21
|
+
];
|
|
22
|
+
const CLAUDE_TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';
|
|
23
|
+
const CLAUDE_PROFILE_URL = 'https://api.anthropic.com/api/oauth/profile';
|
|
24
|
+
const MANUAL_REDIRECT_URL = 'https://console.anthropic.com/oauth/code/callback';
|
|
25
|
+
const CLAUDE_AI_AUTHORIZE_URL = 'https://claude.ai/oauth/authorize';
|
|
26
|
+
const CONSOLE_AUTHORIZE_URL = 'https://console.anthropic.com/oauth/authorize';
|
|
27
|
+
class ClaudeAuthService extends BaseAuthService_1.BaseAuthService {
|
|
28
|
+
constructor() {
|
|
29
|
+
super('claude');
|
|
30
|
+
this.claudeConfigDir = path_1.default.join(environment_1.default.DATA_DIR, 'claude');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse various callback input formats (URL, query params, or raw code)
|
|
34
|
+
* Based on n8n plugin implementation
|
|
35
|
+
*/
|
|
36
|
+
static parseCallbackInput(input) {
|
|
37
|
+
const trimmed = input.trim();
|
|
38
|
+
if (!trimmed)
|
|
39
|
+
return {};
|
|
40
|
+
let code;
|
|
41
|
+
let state;
|
|
42
|
+
const trySet = (maybeCode, maybeState) => {
|
|
43
|
+
const trimmedCode = maybeCode?.trim() || '';
|
|
44
|
+
const trimmedState = maybeState?.trim() || '';
|
|
45
|
+
if (!code && trimmedCode)
|
|
46
|
+
code = trimmedCode;
|
|
47
|
+
if (!state && trimmedState)
|
|
48
|
+
state = trimmedState;
|
|
49
|
+
};
|
|
50
|
+
// Try parsing as URL
|
|
51
|
+
try {
|
|
52
|
+
const url = new URL(trimmed);
|
|
53
|
+
trySet(url.searchParams.get('code'), url.searchParams.get('state'));
|
|
54
|
+
const hash = url.hash.startsWith('#') ? url.hash.slice(1) : url.hash;
|
|
55
|
+
if (hash) {
|
|
56
|
+
if (hash.includes('=')) {
|
|
57
|
+
const hashParams = new URLSearchParams(hash);
|
|
58
|
+
trySet(hashParams.get('code'), hashParams.get('state'));
|
|
59
|
+
}
|
|
60
|
+
else if (!state) {
|
|
61
|
+
state = hash.trim();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (code || state)
|
|
65
|
+
return { code, state };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Fall through to non-URL parsing
|
|
69
|
+
}
|
|
70
|
+
// Parse as query string or hash fragments
|
|
71
|
+
const [beforeHash, ...hashParts] = trimmed.split('#');
|
|
72
|
+
const hashPart = hashParts.join('#');
|
|
73
|
+
if (beforeHash.includes('?')) {
|
|
74
|
+
const params = new URLSearchParams(beforeHash.slice(beforeHash.indexOf('?') + 1));
|
|
75
|
+
trySet(params.get('code'), params.get('state'));
|
|
76
|
+
}
|
|
77
|
+
if (beforeHash.includes('code=')) {
|
|
78
|
+
const params = new URLSearchParams(beforeHash);
|
|
79
|
+
trySet(params.get('code'), params.get('state'));
|
|
80
|
+
}
|
|
81
|
+
// If still no code, treat entire input as code
|
|
82
|
+
if (!code && beforeHash && !beforeHash.includes('?') && !beforeHash.includes('=')) {
|
|
83
|
+
code = beforeHash.trim();
|
|
84
|
+
}
|
|
85
|
+
if (hashPart) {
|
|
86
|
+
if (hashPart.includes('=')) {
|
|
87
|
+
const hashParams = new URLSearchParams(hashPart);
|
|
88
|
+
trySet(hashParams.get('code'), hashParams.get('state'));
|
|
89
|
+
}
|
|
90
|
+
else if (!state) {
|
|
91
|
+
state = hashPart.trim();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { code, state };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Start Claude OAuth login flow
|
|
98
|
+
*/
|
|
99
|
+
async startLogin(options) {
|
|
100
|
+
const codeVerifier = (0, crypto_2.generateCodeVerifier)();
|
|
101
|
+
const codeChallenge = (0, crypto_2.generateCodeChallenge)(codeVerifier);
|
|
102
|
+
const state = crypto_1.default.randomUUID();
|
|
103
|
+
const baseUrl = options.loginWithClaudeAi
|
|
104
|
+
? 'https://claude.ai/oauth/authorize'
|
|
105
|
+
: 'https://console.anthropic.com/oauth/authorize';
|
|
106
|
+
const redirectUri = 'http://localhost:3000/oauth/callback';
|
|
107
|
+
const url = new URL(baseUrl);
|
|
108
|
+
url.searchParams.set('client_id', CLAUDE_CLIENT_ID);
|
|
109
|
+
url.searchParams.set('response_type', 'code');
|
|
110
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
111
|
+
url.searchParams.set('scope', CLAUDE_SCOPES.join(' '));
|
|
112
|
+
url.searchParams.set('code_challenge', codeChallenge);
|
|
113
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
114
|
+
url.searchParams.set('state', state);
|
|
115
|
+
const sessionId = crypto_1.default.randomUUID();
|
|
116
|
+
await this.storeLoginSession({
|
|
117
|
+
sessionId,
|
|
118
|
+
state,
|
|
119
|
+
codeVerifier: this.encrypt(codeVerifier),
|
|
120
|
+
provider: 'claude',
|
|
121
|
+
redirectUri,
|
|
122
|
+
expiresAt: new Date(Date.now() + 15 * 60 * 1000),
|
|
123
|
+
});
|
|
124
|
+
logger_1.default.info('Claude OAuth login started', { sessionId, baseUrl: baseUrl });
|
|
125
|
+
return {
|
|
126
|
+
loginUrl: url.toString(),
|
|
127
|
+
sessionId,
|
|
128
|
+
state,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Complete Claude OAuth login flow
|
|
133
|
+
*/
|
|
134
|
+
async completeLogin(params) {
|
|
135
|
+
// Validate session
|
|
136
|
+
const session = await this.getLoginSession(params.sessionId);
|
|
137
|
+
if (!session || session.state !== params.state) {
|
|
138
|
+
throw new Error('Invalid OAuth state');
|
|
139
|
+
}
|
|
140
|
+
const codeVerifier = this.decrypt(session.codeVerifier);
|
|
141
|
+
// Exchange code for tokens
|
|
142
|
+
const tokenResponse = await fetch(CLAUDE_TOKEN_URL, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: { 'Content-Type': 'application/json' },
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
grant_type: 'authorization_code',
|
|
147
|
+
code: params.code,
|
|
148
|
+
redirect_uri: session.redirectUri || 'http://localhost:3000/oauth/callback',
|
|
149
|
+
client_id: CLAUDE_CLIENT_ID,
|
|
150
|
+
code_verifier: codeVerifier,
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
if (!tokenResponse.ok) {
|
|
154
|
+
const error = await tokenResponse.text();
|
|
155
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
const tokens = (await tokenResponse.json());
|
|
158
|
+
// Fetch user profile
|
|
159
|
+
const profileResponse = await fetch(CLAUDE_PROFILE_URL, {
|
|
160
|
+
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
|
161
|
+
});
|
|
162
|
+
const profile = profileResponse.ok
|
|
163
|
+
? (await profileResponse.json())
|
|
164
|
+
: {};
|
|
165
|
+
// 1. Create directory structure
|
|
166
|
+
await promises_1.default.mkdir(this.claudeConfigDir, { recursive: true, mode: 0o700 });
|
|
167
|
+
// 2. Write credentials to file (like native Claude CLI)
|
|
168
|
+
const credentialsPath = path_1.default.join(this.claudeConfigDir, '.credentials.json');
|
|
169
|
+
const credentialsData = {
|
|
170
|
+
accessToken: tokens.access_token,
|
|
171
|
+
refreshToken: tokens.refresh_token,
|
|
172
|
+
expiresAt: Date.now() + tokens.expires_in * 1000,
|
|
173
|
+
issuedAt: Date.now(),
|
|
174
|
+
scopes: tokens.scope?.split(' '),
|
|
175
|
+
subscriptionType: profile.organization?.organization_type,
|
|
176
|
+
rateLimitTier: profile.rate_limit_tier,
|
|
177
|
+
};
|
|
178
|
+
await promises_1.default.writeFile(credentialsPath, JSON.stringify(credentialsData, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
179
|
+
logger_1.default.info('Claude credentials written to file');
|
|
180
|
+
// 3. Store in database (for UI/management)
|
|
181
|
+
const authToken = await models_1.AuthToken.create({
|
|
182
|
+
provider: 'claude',
|
|
183
|
+
accessToken: this.encrypt(tokens.access_token),
|
|
184
|
+
refreshToken: tokens.refresh_token ? this.encrypt(tokens.refresh_token) : undefined,
|
|
185
|
+
tokenType: tokens.token_type,
|
|
186
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
|
|
187
|
+
issuedAt: new Date(),
|
|
188
|
+
scopes: tokens.scope?.split(' '),
|
|
189
|
+
email: profile.account?.email,
|
|
190
|
+
organization: profile.organization?.name,
|
|
191
|
+
isActive: true,
|
|
192
|
+
});
|
|
193
|
+
// Cleanup session
|
|
194
|
+
await this.deleteLoginSession(params.sessionId);
|
|
195
|
+
logger_1.default.info('Claude OAuth login completed', { tokenId: authToken.id });
|
|
196
|
+
return authToken;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Load credentials from file on server startup
|
|
200
|
+
*/
|
|
201
|
+
async loadFromFile() {
|
|
202
|
+
const credentialsPath = path_1.default.join(this.claudeConfigDir, '.credentials.json');
|
|
203
|
+
try {
|
|
204
|
+
const content = await promises_1.default.readFile(credentialsPath, 'utf8');
|
|
205
|
+
const creds = JSON.parse(content);
|
|
206
|
+
// Check if already in database
|
|
207
|
+
let authToken = await models_1.AuthToken.findOne({
|
|
208
|
+
where: { provider: 'claude', isActive: true },
|
|
209
|
+
});
|
|
210
|
+
if (!authToken) {
|
|
211
|
+
// Create from file
|
|
212
|
+
authToken = await models_1.AuthToken.create({
|
|
213
|
+
provider: 'claude',
|
|
214
|
+
accessToken: this.encrypt(creds.accessToken),
|
|
215
|
+
refreshToken: creds.refreshToken ? this.encrypt(creds.refreshToken) : undefined,
|
|
216
|
+
tokenType: 'Bearer',
|
|
217
|
+
expiresAt: new Date(creds.expiresAt),
|
|
218
|
+
issuedAt: new Date(creds.issuedAt),
|
|
219
|
+
scopes: creds.scopes,
|
|
220
|
+
isActive: true,
|
|
221
|
+
});
|
|
222
|
+
logger_1.default.info('Claude credentials loaded from file and synced to database');
|
|
223
|
+
}
|
|
224
|
+
return authToken;
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
228
|
+
logger_1.default.warn('Failed to load Claude credentials from file:', error.message);
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Refresh Claude access token
|
|
235
|
+
*/
|
|
236
|
+
async refreshToken(token) {
|
|
237
|
+
if (!token.refreshToken) {
|
|
238
|
+
throw new Error('No refresh token available');
|
|
239
|
+
}
|
|
240
|
+
const refreshTokenDecrypted = this.decrypt(token.refreshToken);
|
|
241
|
+
const tokenResponse = await fetch(CLAUDE_TOKEN_URL, {
|
|
242
|
+
method: 'POST',
|
|
243
|
+
headers: { 'Content-Type': 'application/json' },
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
grant_type: 'refresh_token',
|
|
246
|
+
refresh_token: refreshTokenDecrypted,
|
|
247
|
+
client_id: CLAUDE_CLIENT_ID,
|
|
248
|
+
}),
|
|
249
|
+
});
|
|
250
|
+
if (!tokenResponse.ok) {
|
|
251
|
+
const error = await tokenResponse.text();
|
|
252
|
+
throw new Error(`Token refresh failed: ${error}`);
|
|
253
|
+
}
|
|
254
|
+
const tokens = (await tokenResponse.json());
|
|
255
|
+
logger_1.default.info('Claude token refreshed successfully');
|
|
256
|
+
return {
|
|
257
|
+
accessToken: tokens.access_token,
|
|
258
|
+
refreshToken: tokens.refresh_token,
|
|
259
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get current authentication status
|
|
264
|
+
*/
|
|
265
|
+
async getStatus() {
|
|
266
|
+
const token = await this.getActiveToken();
|
|
267
|
+
if (!token) {
|
|
268
|
+
return { loggedIn: false };
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
loggedIn: true,
|
|
272
|
+
tokenId: token.id,
|
|
273
|
+
email: token.email,
|
|
274
|
+
expiresAt: token.expiresAt,
|
|
275
|
+
hasInferenceScope: token.scopes?.includes('user:inference') || false,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Start Claude OAuth login flow for manual code entry
|
|
280
|
+
* Uses official Anthropic static callback page for displaying authorization code
|
|
281
|
+
*/
|
|
282
|
+
async startLoginWithRedirect(_redirectUri, loginWithClaudeAi = true) {
|
|
283
|
+
const codeVerifier = (0, crypto_2.generateCodeVerifier)();
|
|
284
|
+
const codeChallenge = (0, crypto_2.generateCodeChallenge)(codeVerifier);
|
|
285
|
+
const state = crypto_1.default.randomUUID();
|
|
286
|
+
// Use static manual redirect URI (official Anthropic page)
|
|
287
|
+
const manualRedirectUri = MANUAL_REDIRECT_URL;
|
|
288
|
+
// Choose authorization endpoint
|
|
289
|
+
const baseUrl = loginWithClaudeAi ? CLAUDE_AI_AUTHORIZE_URL : CONSOLE_AUTHORIZE_URL;
|
|
290
|
+
const url = new URL(baseUrl);
|
|
291
|
+
url.searchParams.set('client_id', CLAUDE_CLIENT_ID);
|
|
292
|
+
url.searchParams.set('response_type', 'code');
|
|
293
|
+
url.searchParams.set('redirect_uri', manualRedirectUri);
|
|
294
|
+
url.searchParams.set('scope', CLAUDE_SCOPES.join(' '));
|
|
295
|
+
url.searchParams.set('code_challenge', codeChallenge);
|
|
296
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
297
|
+
url.searchParams.set('state', state);
|
|
298
|
+
// Store session in memory (state is sessionId)
|
|
299
|
+
await this.storeLoginSession({
|
|
300
|
+
sessionId: state,
|
|
301
|
+
state,
|
|
302
|
+
codeVerifier: this.encrypt(codeVerifier),
|
|
303
|
+
provider: 'claude',
|
|
304
|
+
redirectUri: manualRedirectUri,
|
|
305
|
+
expiresAt: new Date(Date.now() + 15 * 60 * 1000),
|
|
306
|
+
});
|
|
307
|
+
logger_1.default.info('Claude OAuth manual login started', { baseUrl, state });
|
|
308
|
+
return {
|
|
309
|
+
loginUrl: url.toString(),
|
|
310
|
+
state,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Complete Claude OAuth login with manual code entry
|
|
315
|
+
* Accepts full callback URL or just the authorization code
|
|
316
|
+
*/
|
|
317
|
+
async completeLoginFromCallback(code, state) {
|
|
318
|
+
// 1. Validate session
|
|
319
|
+
const session = await this.getLoginSession(state);
|
|
320
|
+
if (!session) {
|
|
321
|
+
throw new Error('Login session expired or not found. Please restart the login process.');
|
|
322
|
+
}
|
|
323
|
+
if (session.state !== state) {
|
|
324
|
+
await this.deleteLoginSession(state);
|
|
325
|
+
throw new Error('Invalid authentication state. Please restart the login process.');
|
|
326
|
+
}
|
|
327
|
+
// 2. Validate code
|
|
328
|
+
if (!code || code.trim().length === 0) {
|
|
329
|
+
await this.deleteLoginSession(state);
|
|
330
|
+
throw new Error('Invalid authorization code. Please copy the full callback URL.');
|
|
331
|
+
}
|
|
332
|
+
const codeVerifier = this.decrypt(session.codeVerifier);
|
|
333
|
+
// 3. Exchange code for tokens (with error handling)
|
|
334
|
+
try {
|
|
335
|
+
const tokenResponse = await fetch(CLAUDE_TOKEN_URL, {
|
|
336
|
+
method: 'POST',
|
|
337
|
+
headers: { 'Content-Type': 'application/json' },
|
|
338
|
+
body: JSON.stringify({
|
|
339
|
+
grant_type: 'authorization_code',
|
|
340
|
+
code: code.trim(),
|
|
341
|
+
redirect_uri: session.redirectUri || MANUAL_REDIRECT_URL,
|
|
342
|
+
client_id: CLAUDE_CLIENT_ID,
|
|
343
|
+
code_verifier: codeVerifier,
|
|
344
|
+
state: state, // WICHTIG: State muss auch beim Token-Exchange mitgesendet werden!
|
|
345
|
+
}),
|
|
346
|
+
});
|
|
347
|
+
if (!tokenResponse.ok) {
|
|
348
|
+
const error = await tokenResponse.text();
|
|
349
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
350
|
+
}
|
|
351
|
+
const tokens = (await tokenResponse.json());
|
|
352
|
+
// 4. Fetch user profile
|
|
353
|
+
const profileResponse = await fetch(CLAUDE_PROFILE_URL, {
|
|
354
|
+
headers: { Authorization: `Bearer ${tokens.access_token}` },
|
|
355
|
+
});
|
|
356
|
+
const profile = profileResponse.ok
|
|
357
|
+
? (await profileResponse.json())
|
|
358
|
+
: {};
|
|
359
|
+
// 5. Create directory structure
|
|
360
|
+
await promises_1.default.mkdir(this.claudeConfigDir, { recursive: true, mode: 0o700 });
|
|
361
|
+
// 6. Write credentials to file (like native Claude CLI)
|
|
362
|
+
const credentialsPath = path_1.default.join(this.claudeConfigDir, '.credentials.json');
|
|
363
|
+
const credentialsData = {
|
|
364
|
+
accessToken: tokens.access_token,
|
|
365
|
+
refreshToken: tokens.refresh_token,
|
|
366
|
+
expiresAt: Date.now() + tokens.expires_in * 1000,
|
|
367
|
+
issuedAt: Date.now(),
|
|
368
|
+
scopes: tokens.scope?.split(' '),
|
|
369
|
+
subscriptionType: profile.organization?.organization_type,
|
|
370
|
+
rateLimitTier: profile.rate_limit_tier,
|
|
371
|
+
};
|
|
372
|
+
await promises_1.default.writeFile(credentialsPath, JSON.stringify(credentialsData, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
373
|
+
logger_1.default.info('Claude credentials written to file');
|
|
374
|
+
// 7. Store in database
|
|
375
|
+
const authToken = await models_1.AuthToken.create({
|
|
376
|
+
provider: 'claude',
|
|
377
|
+
accessToken: this.encrypt(tokens.access_token),
|
|
378
|
+
refreshToken: tokens.refresh_token ? this.encrypt(tokens.refresh_token) : undefined,
|
|
379
|
+
tokenType: tokens.token_type,
|
|
380
|
+
expiresAt: new Date(Date.now() + tokens.expires_in * 1000),
|
|
381
|
+
issuedAt: new Date(),
|
|
382
|
+
scopes: tokens.scope?.split(' '),
|
|
383
|
+
email: profile.account?.email,
|
|
384
|
+
organization: profile.organization?.name,
|
|
385
|
+
isActive: true,
|
|
386
|
+
});
|
|
387
|
+
// 8. Cleanup session
|
|
388
|
+
await this.deleteLoginSession(state);
|
|
389
|
+
logger_1.default.info('Claude OAuth manual login completed', { tokenId: authToken.id });
|
|
390
|
+
return authToken;
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
// Cleanup session on failure
|
|
394
|
+
await this.deleteLoginSession(state);
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.ClaudeAuthService = ClaudeAuthService;
|
|
400
|
+
exports.default = ClaudeAuthService;
|
|
401
|
+
//# sourceMappingURL=ClaudeAuthService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeAuthService.js","sourceRoot":"","sources":["../../../src/services/auth/ClaudeAuthService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseAuthService } from './BaseAuthService';
|
|
2
|
+
import { AuthToken } from '../../models';
|
|
3
|
+
export declare class CodexAuthService extends BaseAuthService {
|
|
4
|
+
private codexHome;
|
|
5
|
+
constructor();
|
|
6
|
+
/**
|
|
7
|
+
* Start Codex Device Auth flow
|
|
8
|
+
*/
|
|
9
|
+
startLogin(): Promise<{
|
|
10
|
+
deviceAuthId: string;
|
|
11
|
+
userCode: string;
|
|
12
|
+
verificationUrl: string;
|
|
13
|
+
intervalSeconds: number;
|
|
14
|
+
expiresIn: number;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Poll for device auth token
|
|
18
|
+
*/
|
|
19
|
+
pollForToken(params: {
|
|
20
|
+
deviceAuthId: string;
|
|
21
|
+
userCode: string;
|
|
22
|
+
}): Promise<AuthToken>;
|
|
23
|
+
/**
|
|
24
|
+
* Load credentials from file on server startup
|
|
25
|
+
*/
|
|
26
|
+
loadFromFile(): Promise<AuthToken | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Get current authentication status
|
|
29
|
+
*/
|
|
30
|
+
getStatus(): Promise<{
|
|
31
|
+
loggedIn: boolean;
|
|
32
|
+
tokenId?: string;
|
|
33
|
+
email?: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export default CodexAuthService;
|
|
37
|
+
//# sourceMappingURL=CodexAuthService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodexAuthService.d.ts","sourceRoot":"","sources":["../../../src/services/auth/CodexAuthService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAmCzC,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,OAAO,CAAC,SAAS,CAAS;;IAO1B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAmCF;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE;QACzB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,SAAS,CAAC;IA6FtB;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAmC/C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC;QACzB,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CAaH;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CodexAuthService = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const BaseAuthService_1 = require("./BaseAuthService");
|
|
10
|
+
const models_1 = require("../../models");
|
|
11
|
+
const crypto_1 = require("../../utils/crypto");
|
|
12
|
+
const environment_1 = __importDefault(require("../../config/environment"));
|
|
13
|
+
const logger_1 = __importDefault(require("../../utils/logger"));
|
|
14
|
+
const CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
15
|
+
const CODEX_ISSUER = 'https://auth.openai.com';
|
|
16
|
+
/**
|
|
17
|
+
* Construct the verification URL for device auth
|
|
18
|
+
*/
|
|
19
|
+
function getVerificationUrl(issuer) {
|
|
20
|
+
const normalized = issuer.replace(/\/+$/g, '');
|
|
21
|
+
return `${normalized}/codex/device`;
|
|
22
|
+
}
|
|
23
|
+
class CodexAuthService extends BaseAuthService_1.BaseAuthService {
|
|
24
|
+
constructor() {
|
|
25
|
+
super('codex');
|
|
26
|
+
this.codexHome = path_1.default.join(environment_1.default.DATA_DIR, 'codex');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Start Codex Device Auth flow
|
|
30
|
+
*/
|
|
31
|
+
async startLogin() {
|
|
32
|
+
const response = await fetch(`${CODEX_ISSUER}/api/accounts/deviceauth/usercode`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
client_id: CODEX_CLIENT_ID,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
const error = await response.text();
|
|
41
|
+
throw new Error(`Failed to start device auth: ${error}`);
|
|
42
|
+
}
|
|
43
|
+
const data = (await response.json());
|
|
44
|
+
const verificationUrl = getVerificationUrl(CODEX_ISSUER);
|
|
45
|
+
// OpenAI doesn't return expires_in, so we use a default of 15 minutes
|
|
46
|
+
const expiresIn = data.expires_in || (15 * 60);
|
|
47
|
+
logger_1.default.info('Codex device auth started', {
|
|
48
|
+
userCode: data.user_code,
|
|
49
|
+
expiresIn,
|
|
50
|
+
verificationUrl
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
deviceAuthId: data.device_auth_id,
|
|
54
|
+
userCode: data.user_code,
|
|
55
|
+
verificationUrl,
|
|
56
|
+
intervalSeconds: parseInt(data.interval) || 5,
|
|
57
|
+
expiresIn,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Poll for device auth token
|
|
62
|
+
*/
|
|
63
|
+
async pollForToken(params) {
|
|
64
|
+
// Poll device token endpoint
|
|
65
|
+
const deviceTokenResponse = await fetch(`${CODEX_ISSUER}/api/accounts/deviceauth/token`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
device_auth_id: params.deviceAuthId,
|
|
70
|
+
user_code: params.userCode,
|
|
71
|
+
}),
|
|
72
|
+
});
|
|
73
|
+
logger_1.default.debug('Device token poll response', {
|
|
74
|
+
status: deviceTokenResponse.status,
|
|
75
|
+
ok: deviceTokenResponse.ok
|
|
76
|
+
});
|
|
77
|
+
if (!deviceTokenResponse.ok) {
|
|
78
|
+
// OpenAI returns 403 or 404 when device auth is pending
|
|
79
|
+
if (deviceTokenResponse.status === 400 || deviceTokenResponse.status === 403 || deviceTokenResponse.status === 404) {
|
|
80
|
+
throw new Error('Device auth not yet approved');
|
|
81
|
+
}
|
|
82
|
+
const error = await deviceTokenResponse.text();
|
|
83
|
+
logger_1.default.error('Device token poll failed', { status: deviceTokenResponse.status, error });
|
|
84
|
+
throw new Error(`Failed to poll device token: ${error}`);
|
|
85
|
+
}
|
|
86
|
+
const deviceTokens = (await deviceTokenResponse.json());
|
|
87
|
+
// Exchange for OAuth tokens
|
|
88
|
+
// OAuth token endpoints expect application/x-www-form-urlencoded
|
|
89
|
+
const tokenBody = new URLSearchParams();
|
|
90
|
+
tokenBody.set('grant_type', 'authorization_code');
|
|
91
|
+
tokenBody.set('code', deviceTokens.authorization_code);
|
|
92
|
+
tokenBody.set('redirect_uri', `${CODEX_ISSUER}/deviceauth/callback`);
|
|
93
|
+
tokenBody.set('client_id', CODEX_CLIENT_ID);
|
|
94
|
+
tokenBody.set('code_verifier', deviceTokens.code_verifier);
|
|
95
|
+
const tokenResponse = await fetch(`${CODEX_ISSUER}/oauth/token`, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
98
|
+
body: tokenBody.toString(),
|
|
99
|
+
});
|
|
100
|
+
if (!tokenResponse.ok) {
|
|
101
|
+
const error = await tokenResponse.text();
|
|
102
|
+
logger_1.default.error('Token exchange failed', { status: tokenResponse.status, error });
|
|
103
|
+
throw new Error(`Failed to exchange tokens: ${error}`);
|
|
104
|
+
}
|
|
105
|
+
const tokens = (await tokenResponse.json());
|
|
106
|
+
// Decode JWT for user info
|
|
107
|
+
const claims = (0, crypto_1.decodeJWT)(tokens.id_token);
|
|
108
|
+
// 1. Create directory structure
|
|
109
|
+
await promises_1.default.mkdir(this.codexHome, { recursive: true, mode: 0o700 });
|
|
110
|
+
// 2. Write auth.json (like native Codex CLI)
|
|
111
|
+
const authPath = path_1.default.join(this.codexHome, 'auth.json');
|
|
112
|
+
const authData = {
|
|
113
|
+
tokens: {
|
|
114
|
+
id_token: tokens.id_token,
|
|
115
|
+
access_token: tokens.access_token,
|
|
116
|
+
refresh_token: tokens.refresh_token,
|
|
117
|
+
account_id: claims.account_id || claims.sub,
|
|
118
|
+
},
|
|
119
|
+
last_refresh: new Date().toISOString(),
|
|
120
|
+
};
|
|
121
|
+
await promises_1.default.writeFile(authPath, JSON.stringify(authData, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
122
|
+
logger_1.default.info('Codex credentials written to file');
|
|
123
|
+
// 3. Store in database
|
|
124
|
+
const authToken = await models_1.AuthToken.create({
|
|
125
|
+
provider: 'codex',
|
|
126
|
+
accessToken: this.encrypt(tokens.access_token),
|
|
127
|
+
refreshToken: this.encrypt(tokens.refresh_token),
|
|
128
|
+
tokenType: 'Bearer',
|
|
129
|
+
issuedAt: new Date(),
|
|
130
|
+
email: claims.email,
|
|
131
|
+
isActive: true,
|
|
132
|
+
});
|
|
133
|
+
logger_1.default.info('Codex device auth completed', { tokenId: authToken.id });
|
|
134
|
+
return authToken;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Load credentials from file on server startup
|
|
138
|
+
*/
|
|
139
|
+
async loadFromFile() {
|
|
140
|
+
const authPath = path_1.default.join(this.codexHome, 'auth.json');
|
|
141
|
+
try {
|
|
142
|
+
const content = await promises_1.default.readFile(authPath, 'utf8');
|
|
143
|
+
const auth = JSON.parse(content);
|
|
144
|
+
// Check if already in database
|
|
145
|
+
let authToken = await models_1.AuthToken.findOne({
|
|
146
|
+
where: { provider: 'codex', isActive: true },
|
|
147
|
+
});
|
|
148
|
+
if (!authToken) {
|
|
149
|
+
// Create from file
|
|
150
|
+
authToken = await models_1.AuthToken.create({
|
|
151
|
+
provider: 'codex',
|
|
152
|
+
accessToken: this.encrypt(auth.tokens.access_token),
|
|
153
|
+
refreshToken: this.encrypt(auth.tokens.refresh_token),
|
|
154
|
+
tokenType: 'Bearer',
|
|
155
|
+
issuedAt: new Date(auth.last_refresh),
|
|
156
|
+
isActive: true,
|
|
157
|
+
});
|
|
158
|
+
logger_1.default.info('Codex credentials loaded from file and synced to database');
|
|
159
|
+
}
|
|
160
|
+
return authToken;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
164
|
+
logger_1.default.warn('Failed to load Codex credentials from file:', error.message);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get current authentication status
|
|
171
|
+
*/
|
|
172
|
+
async getStatus() {
|
|
173
|
+
const token = await this.getActiveToken();
|
|
174
|
+
if (!token) {
|
|
175
|
+
return { loggedIn: false };
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
loggedIn: true,
|
|
179
|
+
tokenId: token.id,
|
|
180
|
+
email: token.email,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.CodexAuthService = CodexAuthService;
|
|
185
|
+
exports.default = CodexAuthService;
|
|
186
|
+
//# sourceMappingURL=CodexAuthService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodexAuthService.js","sourceRoot":"","sources":["../../../src/services/auth/CodexAuthService.ts"],"names":[],"mappings":"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"}
|