aemeathcli 1.0.4 → 1.0.6

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.
@@ -1,6 +1,6 @@
1
- export { ProviderRegistry, createDefaultRegistry } from './chunk-O3ZF22SW.js';
1
+ export { ProviderRegistry, createDefaultRegistry } from './chunk-LJ6K3FYY.js';
2
2
  import './chunk-HCIHOHLX.js';
3
3
  import './chunk-ZGOHARPV.js';
4
4
  import './chunk-JAXXTYID.js';
5
- //# sourceMappingURL=registry-4KD24ZC3.js.map
6
- //# sourceMappingURL=registry-4KD24ZC3.js.map
5
+ //# sourceMappingURL=registry-H6ZI4OFW.js.map
6
+ //# sourceMappingURL=registry-H6ZI4OFW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"registry-4KD24ZC3.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"registry-H6ZI4OFW.js"}
@@ -1,4 +1,4 @@
1
- export { SessionManager } from './chunk-SUSJPZU2.js';
1
+ export { SessionManager } from './chunk-YVJL75JT.js';
2
2
  import './chunk-I5PZ4JTS.js';
3
3
  import './chunk-4IJD72YB.js';
4
4
  import './chunk-CGEV3ARR.js';
@@ -8,5 +8,5 @@ import './chunk-CS5X3BWX.js';
8
8
  import './chunk-HCIHOHLX.js';
9
9
  import './chunk-ZGOHARPV.js';
10
10
  import './chunk-JAXXTYID.js';
11
- //# sourceMappingURL=session-manager-ECEEACGY.js.map
12
- //# sourceMappingURL=session-manager-ECEEACGY.js.map
11
+ //# sourceMappingURL=session-manager-ZMRJN33X.js.map
12
+ //# sourceMappingURL=session-manager-ZMRJN33X.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"session-manager-ECEEACGY.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"session-manager-ZMRJN33X.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aemeathcli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Next-generation multi-model CLI coding tool with agent teams and split-panel coordination",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/providers/codex-login.ts"],"names":[],"mappings":"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","file":"codex-login-7HHLJHBF.js","sourcesContent":["/**\n * Codex (OpenAI) delegated authentication\n * Spawns `codex login` which opens the browser automatically for ChatGPT login.\n * After login, reads cached tokens from ~/.codex/auth.json.\n */\n\nimport { execFile, spawn } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { ICredential } from \"../../types/index.js\";\nimport { AuthenticationError } from \"../../types/index.js\";\nimport { CredentialStore } from \"../credential-store.js\";\nimport { logger } from \"../../utils/index.js\";\n\nconst execFileAsync = promisify(execFile);\n\n// ── Codex CLI Token Paths ───────────────────────────────────────────────\n\nconst CLI_COMMAND = \"codex\";\n\nfunction getCodexHome(): string {\n return process.env[\"CODEX_HOME\"] ?? join(homedir(), \".codex\");\n}\n\nfunction getAuthJsonPath(): string {\n return join(getCodexHome(), \"auth.json\");\n}\n\n// ── auth.json Schema ────────────────────────────────────────────────────\n\ninterface ICodexAuthJson {\n readonly OPENAI_API_KEY?: string | null;\n readonly tokens?: {\n readonly id_token?: string;\n readonly access_token?: string;\n readonly refresh_token?: string;\n readonly account_id?: string;\n };\n readonly last_refresh?: string;\n}\n\nfunction readCodexAuthJson(): ICodexAuthJson | undefined {\n const authPath = getAuthJsonPath();\n if (!existsSync(authPath)) {\n return undefined;\n }\n\n try {\n const raw = readFileSync(authPath, \"utf-8\");\n return JSON.parse(raw) as ICodexAuthJson;\n } catch {\n return undefined;\n }\n}\n\nfunction extractEmailFromIdToken(idToken: string): string | undefined {\n try {\n const payload = idToken.split(\".\")[1];\n if (!payload) return undefined;\n const decoded = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\")) as {\n email?: string;\n };\n return decoded.email;\n } catch {\n return undefined;\n }\n}\n\n// ── CodexLogin Class ────────────────────────────────────────────────────\n\nexport class CodexLogin {\n private readonly credentialStore: CredentialStore;\n\n constructor(store?: CredentialStore) {\n this.credentialStore = store ?? new CredentialStore();\n }\n\n /**\n * Spawn `codex login` which opens the browser automatically for ChatGPT login,\n * then read the cached tokens from ~/.codex/auth.json.\n */\n async login(): Promise<ICredential> {\n // Check if already logged in via cached tokens\n const existing = this.readCachedCredentials();\n if (existing) {\n logger.info(\"Found existing Codex credentials in ~/.codex/auth.json\");\n await this.credentialStore.set(\"openai\", existing);\n return existing;\n }\n\n // Check if the CLI is available\n const cliAvailable = await this.isCliAvailable();\n if (!cliAvailable) {\n throw new AuthenticationError(\n \"openai\",\n \"Codex CLI not found. Install it first: npm install -g @openai/codex\\n\" +\n \"Or set an API key: aemeathcli auth set-key codex <key>\",\n );\n }\n\n // Spawn `codex login` — browser opens automatically\n logger.info(\"Spawning codex login (browser will open automatically)\");\n try {\n await this.spawnInteractive(CLI_COMMAND, [\"login\"]);\n } catch (error: unknown) {\n // Try device auth as fallback for headless environments\n try {\n await this.spawnInteractive(CLI_COMMAND, [\"login\", \"--device-auth\"]);\n } catch {\n const msg = error instanceof Error ? error.message : String(error);\n throw new AuthenticationError(\"openai\", `Codex login failed: ${msg}`);\n }\n }\n\n // Read the freshly cached credentials\n const credential = this.readCachedCredentials();\n if (!credential) {\n throw new AuthenticationError(\n \"openai\",\n \"No credentials found after Codex login. Please try again or set an API key: aemeathcli auth set-key codex <key>\",\n );\n }\n\n await this.credentialStore.set(\"openai\", credential);\n logger.info(\"Codex credentials imported successfully\");\n return credential;\n }\n\n async logout(): Promise<void> {\n await this.credentialStore.delete(\"openai\");\n logger.info(\"OpenAI session revoked from AemeathCLI\");\n }\n\n async isLoggedIn(): Promise<boolean> {\n const credential = this.readCachedCredentials();\n if (!credential) {\n return false;\n }\n\n await this.credentialStore.set(\"openai\", credential);\n return true;\n }\n\n async getStatus(): Promise<{ loggedIn: boolean; email?: string | undefined; plan?: string | undefined }> {\n const loggedIn = await this.isLoggedIn();\n if (!loggedIn) return { loggedIn: false };\n\n const credential = await this.credentialStore.get(\"openai\");\n if (!credential) return { loggedIn: false };\n\n return {\n loggedIn: true,\n ...(credential.email !== undefined ? { email: credential.email } : {}),\n };\n }\n\n async getCachedCredential(): Promise<ICredential | undefined> {\n const credential = this.readCachedCredentials();\n if (credential) {\n await this.credentialStore.set(\"openai\", credential);\n }\n return credential;\n }\n\n // ── Internal ──────────────────────────────────────────────────────────\n\n private readCachedCredentials(): ICredential | undefined {\n const authData = readCodexAuthJson();\n if (!authData) return undefined;\n\n if (authData.tokens?.access_token) {\n const email = authData.tokens.id_token\n ? extractEmailFromIdToken(authData.tokens.id_token)\n : undefined;\n\n return {\n provider: \"openai\",\n method: \"native_login\",\n token: authData.tokens.access_token,\n ...(authData.tokens.refresh_token !== undefined ? { refreshToken: authData.tokens.refresh_token } : {}),\n ...(email !== undefined ? { email } : {}),\n };\n }\n\n if (authData.OPENAI_API_KEY && typeof authData.OPENAI_API_KEY === \"string\") {\n return {\n provider: \"openai\",\n method: \"api_key\",\n token: authData.OPENAI_API_KEY,\n };\n }\n\n return undefined;\n }\n\n private spawnInteractive(command: string, args: readonly string[]): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, [...args], { stdio: \"inherit\", timeout: 300_000 });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Process exited with code ${String(code)}`));\n });\n child.on(\"error\", reject);\n });\n }\n\n private async isCliAvailable(): Promise<boolean> {\n try {\n await execFileAsync(\"which\", [CLI_COMMAND], { timeout: 3000 });\n return true;\n } catch {\n return false;\n }\n }\n}\n"]}
@@ -1,346 +0,0 @@
1
- import { CredentialStore } from './chunk-4IJD72YB.js';
2
- import './chunk-CGEV3ARR.js';
3
- import './chunk-CYQNBB25.js';
4
- import './chunk-NBR3GHMT.js';
5
- import './chunk-CS5X3BWX.js';
6
- import './chunk-HCIHOHLX.js';
7
- import { AuthenticationError } from './chunk-ZGOHARPV.js';
8
- import { logger } from './chunk-JAXXTYID.js';
9
- import { createServer } from 'http';
10
- import { randomBytes, createHash } from 'crypto';
11
- import { URL } from 'url';
12
- import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
13
- import { join } from 'path';
14
- import { homedir } from 'os';
15
-
16
- var CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
17
- var CLIENT_SECRET = "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";
18
- var AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
19
- var TOKEN_URL = "https://oauth2.googleapis.com/token";
20
- var SCOPE = [
21
- "openid",
22
- "https://www.googleapis.com/auth/userinfo.email",
23
- "https://www.googleapis.com/auth/cloud-platform",
24
- "https://www.googleapis.com/auth/generative-language"
25
- ].join(" ");
26
- var CALLBACK_TIMEOUT_MS = 3e5;
27
- var LOCALHOST = "127.0.0.1";
28
- function getGeminiHome() {
29
- return process.env["GEMINI_HOME"] ?? join(homedir(), ".gemini");
30
- }
31
- function getOAuthCredsPath() {
32
- return join(getGeminiHome(), "oauth_creds.json");
33
- }
34
- function getGoogleAccountsPath() {
35
- return join(getGeminiHome(), "google_accounts.json");
36
- }
37
- function readOAuthCreds() {
38
- const credsPath = getOAuthCredsPath();
39
- if (!existsSync(credsPath)) return void 0;
40
- try {
41
- return JSON.parse(readFileSync(credsPath, "utf-8"));
42
- } catch {
43
- return void 0;
44
- }
45
- }
46
- function readGoogleAccounts() {
47
- const accountsPath = getGoogleAccountsPath();
48
- if (!existsSync(accountsPath)) return void 0;
49
- try {
50
- return JSON.parse(readFileSync(accountsPath, "utf-8"));
51
- } catch {
52
- return void 0;
53
- }
54
- }
55
- function writeOAuthCreds(creds) {
56
- const geminiHome = getGeminiHome();
57
- try {
58
- if (!existsSync(geminiHome)) {
59
- mkdirSync(geminiHome, { recursive: true, mode: 448 });
60
- }
61
- writeFileSync(getOAuthCredsPath(), JSON.stringify(creds, null, 2), {
62
- encoding: "utf-8",
63
- mode: 384
64
- });
65
- } catch (error) {
66
- logger.warn({ err: error }, "Failed to write Gemini oauth_creds.json");
67
- }
68
- }
69
- function writeGoogleAccounts(email) {
70
- const geminiHome = getGeminiHome();
71
- try {
72
- if (!existsSync(geminiHome)) {
73
- mkdirSync(geminiHome, { recursive: true, mode: 448 });
74
- }
75
- const existing = readGoogleAccounts();
76
- const data = { active: email, old: existing?.active && existing.active !== email ? [existing.active] : [] };
77
- writeFileSync(getGoogleAccountsPath(), JSON.stringify(data, null, 2), { encoding: "utf-8" });
78
- } catch {
79
- }
80
- }
81
- function generateCodeVerifier() {
82
- return randomBytes(32).toString("base64url");
83
- }
84
- function generateCodeChallenge(verifier) {
85
- return createHash("sha256").update(verifier).digest("base64url");
86
- }
87
- function generateState() {
88
- return randomBytes(16).toString("hex");
89
- }
90
- function escapeHtml(unsafe) {
91
- return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
92
- }
93
- function successHtml() {
94
- return `<!DOCTYPE html>
95
- <html><head><title>AemeathCLI \u2014 Google Login Successful</title></head>
96
- <body style="font-family:system-ui;text-align:center;padding:40px">
97
- <h1>Google Login Successful</h1>
98
- <p>You can close this window and return to your terminal.</p>
99
- </body></html>`;
100
- }
101
- function errorHtml(message) {
102
- return `<!DOCTYPE html>
103
- <html><head><title>AemeathCLI \u2014 Google Login Failed</title></head>
104
- <body style="font-family:system-ui;text-align:center;padding:40px">
105
- <h1>Login Failed</h1>
106
- <p>${escapeHtml(message)}</p>
107
- </body></html>`;
108
- }
109
- function extractEmailFromIdToken(idToken) {
110
- try {
111
- const payload = idToken.split(".")[1];
112
- if (!payload) return void 0;
113
- const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
114
- return decoded.email;
115
- } catch {
116
- return void 0;
117
- }
118
- }
119
- var GeminiLogin = class {
120
- credentialStore;
121
- callbackServer;
122
- constructor(store) {
123
- this.credentialStore = store ?? new CredentialStore();
124
- }
125
- /**
126
- * Run browser-based Google OAuth login using the same client ID
127
- * as the official Gemini CLI. Browser opens automatically.
128
- */
129
- async login() {
130
- const existing = this.readCachedCredential();
131
- if (existing) {
132
- const isExpired = existing.expiresAt ? /* @__PURE__ */ new Date() > existing.expiresAt : false;
133
- if (!isExpired) {
134
- logger.info("Imported existing Gemini CLI credentials");
135
- await this.credentialStore.set("google", existing);
136
- return existing;
137
- }
138
- }
139
- const codeVerifier = generateCodeVerifier();
140
- const codeChallenge = generateCodeChallenge(codeVerifier);
141
- const state = generateState();
142
- const { port, server } = await this.startCallbackServer();
143
- this.callbackServer = server;
144
- const redirectUri = `http://${LOCALHOST}:${port}/callback`;
145
- const authUrl = new URL(AUTHORIZE_URL);
146
- authUrl.searchParams.set("client_id", CLIENT_ID);
147
- authUrl.searchParams.set("redirect_uri", redirectUri);
148
- authUrl.searchParams.set("response_type", "code");
149
- authUrl.searchParams.set("scope", SCOPE);
150
- authUrl.searchParams.set("state", state);
151
- authUrl.searchParams.set("code_challenge", codeChallenge);
152
- authUrl.searchParams.set("code_challenge_method", "S256");
153
- authUrl.searchParams.set("access_type", "offline");
154
- authUrl.searchParams.set("prompt", "consent");
155
- logger.info("Opening browser for Google OAuth login");
156
- try {
157
- const openModule = await import('open');
158
- await openModule.default(authUrl.toString());
159
- } catch {
160
- this.stopServer();
161
- throw new AuthenticationError("google", "Failed to open browser for login");
162
- }
163
- try {
164
- const code = await this.waitForCallback(state);
165
- const credential = await this.exchangeCodeForToken(code, codeVerifier, redirectUri);
166
- await this.credentialStore.set("google", credential);
167
- logger.info("Google OAuth login successful");
168
- return credential;
169
- } finally {
170
- this.stopServer();
171
- }
172
- }
173
- async logout() {
174
- await this.credentialStore.delete("google");
175
- logger.info("Google session revoked");
176
- }
177
- async isLoggedIn() {
178
- const credential = this.readCachedCredential();
179
- if (!credential) return false;
180
- if (credential.expiresAt && /* @__PURE__ */ new Date() > credential.expiresAt && credential.refreshToken === void 0) {
181
- return false;
182
- }
183
- await this.credentialStore.set("google", credential);
184
- return true;
185
- }
186
- async getStatus() {
187
- const loggedIn = await this.isLoggedIn();
188
- if (!loggedIn) return { loggedIn: false };
189
- const credential = await this.credentialStore.get("google");
190
- if (!credential) return { loggedIn: false };
191
- return {
192
- loggedIn: true,
193
- ...credential.email !== void 0 ? { email: credential.email } : {},
194
- plan: "Google AI"
195
- };
196
- }
197
- async getCachedCredential() {
198
- const credential = this.readCachedCredential();
199
- if (credential) {
200
- await this.credentialStore.set("google", credential);
201
- }
202
- return credential;
203
- }
204
- // ── Read from Gemini CLI cache ────────────────────────────────────────
205
- readCachedCredential() {
206
- const oauthCreds = readOAuthCreds();
207
- if (!oauthCreds?.access_token) return void 0;
208
- let email;
209
- const accounts = readGoogleAccounts();
210
- if (accounts?.active) {
211
- email = accounts.active;
212
- } else if (oauthCreds.id_token) {
213
- email = extractEmailFromIdToken(oauthCreds.id_token);
214
- }
215
- const expiresAt = oauthCreds.expiry_date ? new Date(oauthCreds.expiry_date) : void 0;
216
- return {
217
- provider: "google",
218
- method: "native_login",
219
- token: oauthCreds.access_token,
220
- ...oauthCreds.refresh_token !== void 0 ? { refreshToken: oauthCreds.refresh_token } : {},
221
- ...expiresAt !== void 0 ? { expiresAt } : {},
222
- ...email !== void 0 ? { email } : {},
223
- plan: "Google AI"
224
- };
225
- }
226
- // ── Internal ──────────────────────────────────────────────────────────
227
- startCallbackServer() {
228
- return new Promise((resolve, reject) => {
229
- const server = createServer();
230
- server.listen(0, LOCALHOST, () => {
231
- const address = server.address();
232
- if (address === null || typeof address === "string") {
233
- server.close();
234
- reject(new Error("Failed to bind callback server"));
235
- return;
236
- }
237
- resolve({ port: address.port, server });
238
- });
239
- server.on("error", reject);
240
- });
241
- }
242
- waitForCallback(expectedState) {
243
- return new Promise((resolve, reject) => {
244
- const server = this.callbackServer;
245
- if (server === void 0) {
246
- reject(new AuthenticationError("google", "Callback server not started"));
247
- return;
248
- }
249
- const timeout = setTimeout(() => {
250
- reject(new AuthenticationError("google", "Login timed out"));
251
- }, CALLBACK_TIMEOUT_MS);
252
- server.on("request", (req, res) => {
253
- const requestUrl = new URL(req.url ?? "/", `http://${LOCALHOST}`);
254
- if (requestUrl.pathname !== "/callback") {
255
- res.writeHead(404);
256
- res.end("Not found");
257
- return;
258
- }
259
- clearTimeout(timeout);
260
- const code = requestUrl.searchParams.get("code");
261
- const state = requestUrl.searchParams.get("state");
262
- const error = requestUrl.searchParams.get("error");
263
- if (error) {
264
- res.writeHead(400, { "Content-Type": "text/html" });
265
- res.end(errorHtml(`Google OAuth error: ${error}`));
266
- reject(new AuthenticationError("google", `OAuth error: ${error}`));
267
- return;
268
- }
269
- if (state !== expectedState) {
270
- res.writeHead(400, { "Content-Type": "text/html" });
271
- res.end(errorHtml("State mismatch"));
272
- reject(new AuthenticationError("google", "OAuth state mismatch"));
273
- return;
274
- }
275
- if (!code) {
276
- res.writeHead(400, { "Content-Type": "text/html" });
277
- res.end(errorHtml("Missing authorization code"));
278
- reject(new AuthenticationError("google", "No authorization code received"));
279
- return;
280
- }
281
- res.writeHead(200, { "Content-Type": "text/html" });
282
- res.end(successHtml());
283
- resolve(code);
284
- });
285
- });
286
- }
287
- async exchangeCodeForToken(code, codeVerifier, redirectUri) {
288
- const body = new URLSearchParams({
289
- grant_type: "authorization_code",
290
- code,
291
- redirect_uri: redirectUri,
292
- client_id: CLIENT_ID,
293
- client_secret: CLIENT_SECRET,
294
- code_verifier: codeVerifier
295
- });
296
- const response = await fetch(TOKEN_URL, {
297
- method: "POST",
298
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
299
- body: body.toString()
300
- });
301
- if (!response.ok) {
302
- const text = await response.text();
303
- throw new AuthenticationError("google", `Token exchange failed: ${response.status} ${text}`);
304
- }
305
- const data = await response.json();
306
- if (!data.access_token) {
307
- throw new AuthenticationError("google", "Token exchange returned no access_token");
308
- }
309
- const expiresAt = data.expires_in ? new Date(Date.now() + data.expires_in * 1e3) : void 0;
310
- let email;
311
- if (data.id_token) {
312
- email = extractEmailFromIdToken(data.id_token);
313
- }
314
- const geminiCreds = {
315
- access_token: data.access_token,
316
- ...data.scope !== void 0 ? { scope: data.scope } : {},
317
- ...data.token_type !== void 0 ? { token_type: data.token_type } : {},
318
- ...data.id_token !== void 0 ? { id_token: data.id_token } : {},
319
- ...expiresAt !== void 0 ? { expiry_date: expiresAt.getTime() } : {},
320
- ...data.refresh_token !== void 0 ? { refresh_token: data.refresh_token } : {}
321
- };
322
- writeOAuthCreds(geminiCreds);
323
- if (email) {
324
- writeGoogleAccounts(email);
325
- }
326
- return {
327
- provider: "google",
328
- method: "native_login",
329
- token: data.access_token,
330
- ...data.refresh_token !== void 0 ? { refreshToken: data.refresh_token } : {},
331
- ...expiresAt !== void 0 ? { expiresAt } : {},
332
- ...email !== void 0 ? { email } : {},
333
- plan: "Google AI"
334
- };
335
- }
336
- stopServer() {
337
- if (this.callbackServer !== void 0) {
338
- this.callbackServer.close();
339
- this.callbackServer = void 0;
340
- }
341
- }
342
- };
343
-
344
- export { GeminiLogin };
345
- //# sourceMappingURL=gemini-login-ZZLYC3J6.js.map
346
- //# sourceMappingURL=gemini-login-ZZLYC3J6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/providers/gemini-login.ts"],"names":[],"mappings":"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","file":"gemini-login-ZZLYC3J6.js","sourcesContent":["/**\n * Gemini (Google) OAuth login\n * Uses the same client ID as the official Gemini CLI.\n * After login, stores tokens at ~/.gemini/oauth_creds.json\n * so credentials are shared with the official Gemini CLI.\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { randomBytes, createHash } from \"node:crypto\";\nimport { URL } from \"node:url\";\nimport { writeFileSync, readFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { ICredential } from \"../../types/index.js\";\nimport { AuthenticationError } from \"../../types/index.js\";\nimport { CredentialStore } from \"../credential-store.js\";\nimport { logger } from \"../../utils/index.js\";\n\n// ── Gemini CLI OAuth Config (same as official Gemini CLI) ───────────────\n\nconst CLIENT_ID = \"681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com\";\nconst CLIENT_SECRET = \"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\";\nconst AUTHORIZE_URL = \"https://accounts.google.com/o/oauth2/v2/auth\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst SCOPE = [\n \"openid\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/generative-language\",\n].join(\" \");\nconst CALLBACK_TIMEOUT_MS = 300_000;\nconst LOCALHOST = \"127.0.0.1\";\n\n// ── Gemini CLI Token Paths ──────────────────────────────────────────────\n\nfunction getGeminiHome(): string {\n return process.env[\"GEMINI_HOME\"] ?? join(homedir(), \".gemini\");\n}\n\nfunction getOAuthCredsPath(): string {\n return join(getGeminiHome(), \"oauth_creds.json\");\n}\n\nfunction getGoogleAccountsPath(): string {\n return join(getGeminiHome(), \"google_accounts.json\");\n}\n\n// ── Read existing tokens from Gemini CLI cache ──────────────────────────\n\ninterface IGeminiOAuthCreds {\n readonly access_token?: string;\n readonly scope?: string;\n readonly token_type?: string;\n readonly id_token?: string;\n readonly expiry_date?: number;\n readonly refresh_token?: string;\n}\n\ninterface IGoogleAccounts {\n readonly active?: string;\n}\n\nfunction readOAuthCreds(): IGeminiOAuthCreds | undefined {\n const credsPath = getOAuthCredsPath();\n if (!existsSync(credsPath)) return undefined;\n try {\n return JSON.parse(readFileSync(credsPath, \"utf-8\")) as IGeminiOAuthCreds;\n } catch {\n return undefined;\n }\n}\n\nfunction readGoogleAccounts(): IGoogleAccounts | undefined {\n const accountsPath = getGoogleAccountsPath();\n if (!existsSync(accountsPath)) return undefined;\n try {\n return JSON.parse(readFileSync(accountsPath, \"utf-8\")) as IGoogleAccounts;\n } catch {\n return undefined;\n }\n}\n\n// ── Write tokens in Gemini CLI format ───────────────────────────────────\n\nfunction writeOAuthCreds(creds: IGeminiOAuthCreds): void {\n const geminiHome = getGeminiHome();\n try {\n if (!existsSync(geminiHome)) {\n mkdirSync(geminiHome, { recursive: true, mode: 0o700 });\n }\n writeFileSync(getOAuthCredsPath(), JSON.stringify(creds, null, 2), {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n } catch (error: unknown) {\n logger.warn({ err: error }, \"Failed to write Gemini oauth_creds.json\");\n }\n}\n\nfunction writeGoogleAccounts(email: string): void {\n const geminiHome = getGeminiHome();\n try {\n if (!existsSync(geminiHome)) {\n mkdirSync(geminiHome, { recursive: true, mode: 0o700 });\n }\n const existing = readGoogleAccounts();\n const data = { active: email, old: existing?.active && existing.active !== email ? [existing.active] : [] };\n writeFileSync(getGoogleAccountsPath(), JSON.stringify(data, null, 2), { encoding: \"utf-8\" });\n } catch {\n // Non-critical\n }\n}\n\n// ── PKCE Helpers ────────────────────────────────────────────────────────\n\nfunction generateCodeVerifier(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return createHash(\"sha256\").update(verifier).digest(\"base64url\");\n}\n\nfunction generateState(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\n// ── HTML Responses ──────────────────────────────────────────────────────\n\nfunction escapeHtml(unsafe: string): string {\n return unsafe.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\").replace(/'/g, \"&#039;\");\n}\n\nfunction successHtml(): string {\n return `<!DOCTYPE html>\n<html><head><title>AemeathCLI — Google Login Successful</title></head>\n<body style=\"font-family:system-ui;text-align:center;padding:40px\">\n<h1>Google Login Successful</h1>\n<p>You can close this window and return to your terminal.</p>\n</body></html>`;\n}\n\nfunction errorHtml(message: string): string {\n return `<!DOCTYPE html>\n<html><head><title>AemeathCLI — Google Login Failed</title></head>\n<body style=\"font-family:system-ui;text-align:center;padding:40px\">\n<h1>Login Failed</h1>\n<p>${escapeHtml(message)}</p>\n</body></html>`;\n}\n\nfunction extractEmailFromIdToken(idToken: string): string | undefined {\n try {\n const payload = idToken.split(\".\")[1];\n if (!payload) return undefined;\n const decoded = JSON.parse(Buffer.from(payload, \"base64url\").toString(\"utf8\")) as { email?: string };\n return decoded.email;\n } catch {\n return undefined;\n }\n}\n\n// ── GeminiLogin Class ───────────────────────────────────────────────────\n\nexport class GeminiLogin {\n private readonly credentialStore: CredentialStore;\n private callbackServer: Server | undefined;\n\n constructor(store?: CredentialStore) {\n this.credentialStore = store ?? new CredentialStore();\n }\n\n /**\n * Run browser-based Google OAuth login using the same client ID\n * as the official Gemini CLI. Browser opens automatically.\n */\n async login(): Promise<ICredential> {\n // First try importing existing credentials from Gemini CLI's cache\n const existing = this.readCachedCredential();\n if (existing) {\n const isExpired = existing.expiresAt ? new Date() > existing.expiresAt : false;\n if (!isExpired) {\n logger.info(\"Imported existing Gemini CLI credentials\");\n await this.credentialStore.set(\"google\", existing);\n return existing;\n }\n }\n\n // Run the OAuth flow — browser opens automatically\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n const state = generateState();\n\n const { port, server } = await this.startCallbackServer();\n this.callbackServer = server;\n\n const redirectUri = `http://${LOCALHOST}:${port}/callback`;\n\n const authUrl = new URL(AUTHORIZE_URL);\n authUrl.searchParams.set(\"client_id\", CLIENT_ID);\n authUrl.searchParams.set(\"redirect_uri\", redirectUri);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"scope\", SCOPE);\n authUrl.searchParams.set(\"state\", state);\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"access_type\", \"offline\");\n authUrl.searchParams.set(\"prompt\", \"consent\");\n\n logger.info(\"Opening browser for Google OAuth login\");\n\n try {\n const openModule = await import(\"open\");\n await openModule.default(authUrl.toString());\n } catch {\n this.stopServer();\n throw new AuthenticationError(\"google\", \"Failed to open browser for login\");\n }\n\n try {\n const code = await this.waitForCallback(state);\n const credential = await this.exchangeCodeForToken(code, codeVerifier, redirectUri);\n\n await this.credentialStore.set(\"google\", credential);\n logger.info(\"Google OAuth login successful\");\n\n return credential;\n } finally {\n this.stopServer();\n }\n }\n\n async logout(): Promise<void> {\n await this.credentialStore.delete(\"google\");\n logger.info(\"Google session revoked\");\n }\n\n async isLoggedIn(): Promise<boolean> {\n const credential = this.readCachedCredential();\n if (!credential) return false;\n if (credential.expiresAt && new Date() > credential.expiresAt && credential.refreshToken === undefined) {\n return false;\n }\n\n await this.credentialStore.set(\"google\", credential);\n return true;\n }\n\n async getStatus(): Promise<{ loggedIn: boolean; email?: string | undefined; plan?: string | undefined }> {\n const loggedIn = await this.isLoggedIn();\n if (!loggedIn) return { loggedIn: false };\n\n const credential = await this.credentialStore.get(\"google\");\n if (!credential) return { loggedIn: false };\n\n return {\n loggedIn: true,\n ...(credential.email !== undefined ? { email: credential.email } : {}),\n plan: \"Google AI\",\n };\n }\n\n async getCachedCredential(): Promise<ICredential | undefined> {\n const credential = this.readCachedCredential();\n if (credential) {\n await this.credentialStore.set(\"google\", credential);\n }\n return credential;\n }\n\n // ── Read from Gemini CLI cache ────────────────────────────────────────\n\n private readCachedCredential(): ICredential | undefined {\n const oauthCreds = readOAuthCreds();\n if (!oauthCreds?.access_token) return undefined;\n\n let email: string | undefined;\n const accounts = readGoogleAccounts();\n if (accounts?.active) {\n email = accounts.active;\n } else if (oauthCreds.id_token) {\n email = extractEmailFromIdToken(oauthCreds.id_token);\n }\n\n const expiresAt = oauthCreds.expiry_date ? new Date(oauthCreds.expiry_date) : undefined;\n\n return {\n provider: \"google\",\n method: \"native_login\",\n token: oauthCreds.access_token,\n ...(oauthCreds.refresh_token !== undefined ? { refreshToken: oauthCreds.refresh_token } : {}),\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n ...(email !== undefined ? { email } : {}),\n plan: \"Google AI\",\n };\n }\n\n // ── Internal ──────────────────────────────────────────────────────────\n\n private startCallbackServer(): Promise<{ port: number; server: Server }> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.listen(0, LOCALHOST, () => {\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n server.close();\n reject(new Error(\"Failed to bind callback server\"));\n return;\n }\n resolve({ port: address.port, server });\n });\n server.on(\"error\", reject);\n });\n }\n\n private waitForCallback(expectedState: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const server = this.callbackServer;\n if (server === undefined) {\n reject(new AuthenticationError(\"google\", \"Callback server not started\"));\n return;\n }\n\n const timeout = setTimeout(() => {\n reject(new AuthenticationError(\"google\", \"Login timed out\"));\n }, CALLBACK_TIMEOUT_MS);\n\n server.on(\"request\", (req: IncomingMessage, res: ServerResponse) => {\n const requestUrl = new URL(req.url ?? \"/\", `http://${LOCALHOST}`);\n if (requestUrl.pathname !== \"/callback\") {\n res.writeHead(404);\n res.end(\"Not found\");\n return;\n }\n\n clearTimeout(timeout);\n\n const code = requestUrl.searchParams.get(\"code\");\n const state = requestUrl.searchParams.get(\"state\");\n const error = requestUrl.searchParams.get(\"error\");\n\n if (error) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(errorHtml(`Google OAuth error: ${error}`));\n reject(new AuthenticationError(\"google\", `OAuth error: ${error}`));\n return;\n }\n if (state !== expectedState) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(errorHtml(\"State mismatch\"));\n reject(new AuthenticationError(\"google\", \"OAuth state mismatch\"));\n return;\n }\n if (!code) {\n res.writeHead(400, { \"Content-Type\": \"text/html\" });\n res.end(errorHtml(\"Missing authorization code\"));\n reject(new AuthenticationError(\"google\", \"No authorization code received\"));\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(successHtml());\n resolve(code);\n });\n });\n }\n\n private async exchangeCodeForToken(\n code: string,\n codeVerifier: string,\n redirectUri: string,\n ): Promise<ICredential> {\n const body = new URLSearchParams({\n grant_type: \"authorization_code\",\n code,\n redirect_uri: redirectUri,\n client_id: CLIENT_ID,\n client_secret: CLIENT_SECRET,\n code_verifier: codeVerifier,\n });\n\n const response = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new AuthenticationError(\"google\", `Token exchange failed: ${response.status} ${text}`);\n }\n\n const data = (await response.json()) as {\n access_token?: string;\n refresh_token?: string;\n expires_in?: number;\n id_token?: string;\n scope?: string;\n token_type?: string;\n };\n\n if (!data.access_token) {\n throw new AuthenticationError(\"google\", \"Token exchange returned no access_token\");\n }\n\n const expiresAt = data.expires_in\n ? new Date(Date.now() + data.expires_in * 1000)\n : undefined;\n\n let email: string | undefined;\n if (data.id_token) {\n email = extractEmailFromIdToken(data.id_token);\n }\n\n // Write tokens in Gemini CLI format so the Gemini CLI can also use them\n const geminiCreds: IGeminiOAuthCreds = {\n access_token: data.access_token,\n ...(data.scope !== undefined ? { scope: data.scope } : {}),\n ...(data.token_type !== undefined ? { token_type: data.token_type } : {}),\n ...(data.id_token !== undefined ? { id_token: data.id_token } : {}),\n ...(expiresAt !== undefined ? { expiry_date: expiresAt.getTime() } : {}),\n ...(data.refresh_token !== undefined ? { refresh_token: data.refresh_token } : {}),\n };\n writeOAuthCreds(geminiCreds);\n\n if (email) {\n writeGoogleAccounts(email);\n }\n\n return {\n provider: \"google\",\n method: \"native_login\",\n token: data.access_token,\n ...(data.refresh_token !== undefined ? { refreshToken: data.refresh_token } : {}),\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n ...(email !== undefined ? { email } : {}),\n plan: \"Google AI\",\n };\n }\n\n private stopServer(): void {\n if (this.callbackServer !== undefined) {\n this.callbackServer.close();\n this.callbackServer = undefined;\n }\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/auth/providers/kimi-login.ts"],"names":["expiresAt"],"mappings":"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","file":"kimi-login-CZPS63NK.js","sourcesContent":["/**\n * Kimi (Moonshot) delegated authentication\n * Reads cached credentials from Kimi CLI's ~/.kimi/credentials/kimi-code.json.\n * If not found, spawns `kimi` CLI for interactive login.\n * Kimi's login opens a browser automatically from its interactive session.\n */\n\nimport { execFile, spawn } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { ICredential } from \"../../types/index.js\";\nimport { AuthenticationError } from \"../../types/index.js\";\nimport { CredentialStore } from \"../credential-store.js\";\nimport { logger } from \"../../utils/index.js\";\n\nconst execFileAsync = promisify(execFile);\n\n// ── Kimi CLI Token Paths ────────────────────────────────────────────────\n\nconst CLI_COMMAND = \"kimi\";\n\nfunction getKimiHome(): string {\n return process.env[\"KIMI_HOME\"] ?? join(homedir(), \".kimi\");\n}\n\nfunction getCredentialsPath(): string {\n return join(getKimiHome(), \"credentials\", \"kimi-code.json\");\n}\n\n// ── kimi-code.json Schema ───────────────────────────────────────────────\n\ninterface IKimiCredentials {\n readonly access_token?: string;\n readonly refresh_token?: string;\n readonly expires_at?: number;\n readonly scope?: string;\n readonly token_type?: string;\n}\n\nfunction readKimiCredentials(): IKimiCredentials | undefined {\n const credsPath = getCredentialsPath();\n if (!existsSync(credsPath)) return undefined;\n try {\n return JSON.parse(readFileSync(credsPath, \"utf-8\")) as IKimiCredentials;\n } catch {\n return undefined;\n }\n}\n\n// ── KimiLogin Class ─────────────────────────────────────────────────────\n\nexport class KimiLogin {\n private readonly credentialStore: CredentialStore;\n\n constructor(store?: CredentialStore) {\n this.credentialStore = store ?? new CredentialStore();\n }\n\n /**\n * Login via the Kimi CLI.\n * First checks for cached credentials. If not found, spawns the Kimi CLI\n * interactive session which handles browser-based login automatically.\n */\n async login(): Promise<ICredential> {\n // Check if already logged in via cached tokens\n const existing = this.readCachedCredentials();\n if (existing) {\n logger.info(\"Found existing Kimi CLI credentials\");\n await this.credentialStore.set(\"kimi\", existing);\n return existing;\n }\n\n // Check if the CLI is available\n const cliAvailable = await this.isCliAvailable();\n if (!cliAvailable) {\n throw new AuthenticationError(\n \"kimi\",\n \"Kimi CLI not found. Install it first: npm install -g @anthropic-ai/kimi-cli\\n\" +\n \"Or set an API key: aemeathcli auth set-key kimi <key>\",\n );\n }\n\n // Spawn kimi CLI — it handles login with browser automatically\n logger.info(\"Spawning Kimi CLI for login (browser will open automatically)\");\n try {\n await this.spawnInteractive(CLI_COMMAND, [\"login\"]);\n } catch {\n // Some versions may not have `kimi login` — try spawning interactive session\n try {\n await this.spawnInteractive(CLI_COMMAND, []);\n } catch (error2: unknown) {\n const msg = error2 instanceof Error ? error2.message : String(error2);\n throw new AuthenticationError(\"kimi\", `Kimi login failed: ${msg}`);\n }\n }\n\n // Read the freshly cached credentials\n const credential = this.readCachedCredentials();\n if (!credential) {\n throw new AuthenticationError(\n \"kimi\",\n \"No Kimi credentials found after login. Please try again or set an API key: aemeathcli auth set-key kimi <key>\",\n );\n }\n\n await this.credentialStore.set(\"kimi\", credential);\n logger.info(\"Kimi credentials imported successfully\");\n return credential;\n }\n\n async logout(): Promise<void> {\n await this.credentialStore.delete(\"kimi\");\n logger.info(\"Kimi session revoked from AemeathCLI\");\n }\n\n async isLoggedIn(): Promise<boolean> {\n const credential = this.readCachedCredentials();\n if (!credential) {\n return false;\n }\n\n await this.credentialStore.set(\"kimi\", credential);\n return true;\n }\n\n async getStatus(): Promise<{ loggedIn: boolean; email?: string | undefined; plan?: string | undefined }> {\n const loggedIn = await this.isLoggedIn();\n if (!loggedIn) return { loggedIn: false };\n\n const credential = await this.credentialStore.get(\"kimi\");\n if (!credential) return { loggedIn: false };\n\n return {\n loggedIn: true,\n ...(credential.email !== undefined ? { email: credential.email } : {}),\n };\n }\n\n async getCachedCredential(): Promise<ICredential | undefined> {\n const credential = this.readCachedCredentials();\n if (credential) {\n await this.credentialStore.set(\"kimi\", credential);\n }\n return credential;\n }\n\n // ── Internal ──────────────────────────────────────────────────────────\n\n private readCachedCredentials(): ICredential | undefined {\n const kimiCreds = readKimiCredentials();\n if (!kimiCreds?.access_token) return undefined;\n\n // Check expiry (expires_at is seconds since epoch)\n if (kimiCreds.expires_at) {\n const expiresAt = new Date(kimiCreds.expires_at * 1000);\n if (new Date() > expiresAt && !kimiCreds.refresh_token) {\n logger.debug(\"Kimi CLI token expired with no refresh token\");\n return undefined;\n }\n }\n\n const expiresAt = kimiCreds.expires_at\n ? new Date(kimiCreds.expires_at * 1000)\n : undefined;\n\n return {\n provider: \"kimi\",\n method: \"native_login\",\n token: kimiCreds.access_token,\n ...(kimiCreds.refresh_token !== undefined ? { refreshToken: kimiCreds.refresh_token } : {}),\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n };\n }\n\n private spawnInteractive(command: string, args: readonly string[]): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, [...args], { stdio: \"inherit\", timeout: 300_000 });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`Process exited with code ${String(code)}`));\n });\n child.on(\"error\", reject);\n });\n }\n\n private async isCliAvailable(): Promise<boolean> {\n try {\n await execFileAsync(\"which\", [CLI_COMMAND], { timeout: 3000 });\n return true;\n } catch {\n return false;\n }\n }\n}\n"]}