ai-vault 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -74
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/archive.d.ts +2 -0
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +80 -46
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +29 -27
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/schedule.d.ts.map +1 -1
- package/dist/commands/schedule.js +17 -6
- package/dist/commands/schedule.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +295 -95
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -6
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/verify.d.ts +14 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +293 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/archiver.d.ts +10 -0
- package/dist/core/archiver.d.ts.map +1 -1
- package/dist/core/archiver.js +74 -4
- package/dist/core/archiver.js.map +1 -1
- package/dist/core/archiver.spec.js +2 -0
- package/dist/core/archiver.spec.js.map +1 -1
- package/dist/core/media.d.ts +5 -0
- package/dist/core/media.d.ts.map +1 -1
- package/dist/core/media.js +6 -0
- package/dist/core/media.js.map +1 -1
- package/dist/core/storage.d.ts +4 -0
- package/dist/core/storage.d.ts.map +1 -1
- package/dist/core/storage.js +10 -0
- package/dist/core/storage.js.map +1 -1
- package/dist/providers/chatgpt/api-provider.d.ts.map +1 -1
- package/dist/providers/chatgpt/api-provider.js +8 -82
- package/dist/providers/chatgpt/api-provider.js.map +1 -1
- package/dist/providers/chatgpt/attachments.d.ts +12 -0
- package/dist/providers/chatgpt/attachments.d.ts.map +1 -0
- package/dist/providers/chatgpt/attachments.js +124 -0
- package/dist/providers/chatgpt/attachments.js.map +1 -0
- package/dist/providers/chatgpt/index.d.ts.map +1 -1
- package/dist/providers/chatgpt/index.js +8 -129
- package/dist/providers/chatgpt/index.js.map +1 -1
- package/dist/providers/claude/api-provider.d.ts.map +1 -1
- package/dist/providers/claude/api-provider.js +22 -92
- package/dist/providers/claude/api-provider.js.map +1 -1
- package/dist/providers/claude/errors.d.ts +16 -0
- package/dist/providers/claude/errors.d.ts.map +1 -0
- package/dist/providers/claude/errors.js +38 -0
- package/dist/providers/claude/errors.js.map +1 -0
- package/dist/providers/claude/index.d.ts +4 -7
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +123 -191
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/message-parser.d.ts +3 -0
- package/dist/providers/claude/message-parser.d.ts.map +1 -0
- package/dist/providers/claude/message-parser.js +95 -0
- package/dist/providers/claude/message-parser.js.map +1 -0
- package/dist/providers/gemini/index.d.ts +56 -0
- package/dist/providers/gemini/index.d.ts.map +1 -0
- package/dist/providers/gemini/index.js +420 -0
- package/dist/providers/gemini/index.js.map +1 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/types/provider.d.ts +4 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +8 -0
- package/dist/types/provider.js.map +1 -1
- package/dist/types/schemas.d.ts +6 -6
- package/dist/types/storage.d.ts +4 -0
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/utils/archive-dir.d.ts +5 -0
- package/dist/utils/archive-dir.d.ts.map +1 -0
- package/dist/utils/archive-dir.js +15 -0
- package/dist/utils/archive-dir.js.map +1 -0
- package/dist/utils/cli-ui.d.ts +41 -0
- package/dist/utils/cli-ui.d.ts.map +1 -0
- package/dist/utils/cli-ui.js +145 -0
- package/dist/utils/cli-ui.js.map +1 -0
- package/dist/utils/interactive.d.ts +5 -0
- package/dist/utils/interactive.d.ts.map +1 -0
- package/dist/utils/interactive.js +13 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/scheduler.d.ts.map +1 -1
- package/dist/utils/scheduler.js +1 -6
- package/dist/utils/scheduler.js.map +1 -1
- package/dist/utils/scheduler.spec.js +1 -1
- package/dist/utils/scheduler.spec.js.map +1 -1
- package/dist/utils/scraper.js +1 -1
- package/dist/utils/scraper.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/setup.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Setup Command - Interactive provider configuration
|
|
3
3
|
*/
|
|
4
|
-
import * as clack from '@clack/prompts';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
5
|
import { readFileSync } from 'fs';
|
|
7
6
|
import { resolve } from 'path';
|
|
8
7
|
import { saveProviderConfig, loadConfig, isProviderConfigured } from '../utils/config.js';
|
|
9
8
|
import { getProvider } from '../providers/index.js';
|
|
10
|
-
|
|
9
|
+
import { createCliUI } from '../utils/cli-ui.js';
|
|
10
|
+
import * as clack from '@clack/prompts';
|
|
11
|
+
const VALID_PROVIDERS = ['grok-web', 'grok-x', 'chatgpt', 'claude', 'gemini'];
|
|
11
12
|
export async function setupCommand(options = {}) {
|
|
12
|
-
|
|
13
|
+
const ui = createCliUI();
|
|
14
|
+
ui.intro(chalk.bold.blue('AI Vault Setup'));
|
|
13
15
|
// Check existing configuration
|
|
14
16
|
const config = await loadConfig();
|
|
15
17
|
const configuredProviders = Object.keys(config.providers);
|
|
16
18
|
if (configuredProviders.length > 0) {
|
|
17
|
-
|
|
19
|
+
ui.log.info(`Currently configured: ${configuredProviders.join(', ')}`);
|
|
18
20
|
}
|
|
19
21
|
let providerName;
|
|
20
22
|
// Use provider from argument if provided
|
|
21
23
|
if (options.provider) {
|
|
22
24
|
if (!VALID_PROVIDERS.includes(options.provider)) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
ui.log.error(`Invalid provider: ${options.provider}`);
|
|
26
|
+
ui.log.info(`Valid providers: ${VALID_PROVIDERS.join(', ')}`);
|
|
25
27
|
process.exit(1);
|
|
26
28
|
}
|
|
27
29
|
providerName = options.provider;
|
|
28
|
-
|
|
30
|
+
ui.log.info(`Configuring provider: ${providerName}`);
|
|
29
31
|
}
|
|
30
32
|
else {
|
|
33
|
+
if (!ui.isInteractive) {
|
|
34
|
+
ui.log.error('Provider selection requires a TTY. Pass a provider name explicitly.');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
31
37
|
// Select provider interactively
|
|
32
38
|
const provider = await clack.select({
|
|
33
39
|
message: 'Which provider do you want to configure?',
|
|
@@ -36,22 +42,32 @@ export async function setupCommand(options = {}) {
|
|
|
36
42
|
{ value: 'grok-x', label: 'Grok on X (x.com)', hint: 'x.com/grok' },
|
|
37
43
|
{ value: 'chatgpt', label: 'ChatGPT (OpenAI)', hint: 'chatgpt.com' },
|
|
38
44
|
{ value: 'claude', label: 'Claude (Anthropic)', hint: 'claude.ai' },
|
|
45
|
+
{ value: 'gemini', label: 'Gemini (Google)', hint: 'gemini.google.com' },
|
|
39
46
|
],
|
|
40
47
|
});
|
|
41
48
|
if (clack.isCancel(provider)) {
|
|
42
|
-
|
|
49
|
+
ui.cancel('Setup cancelled');
|
|
43
50
|
process.exit(0);
|
|
44
51
|
}
|
|
45
52
|
providerName = provider;
|
|
46
53
|
}
|
|
47
54
|
// Check if already configured
|
|
48
55
|
if (await isProviderConfigured(providerName)) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
if (!ui.isInteractive) {
|
|
57
|
+
if (!options.cookiesFile) {
|
|
58
|
+
ui.log.error(`${providerName} is already configured. Run setup in a TTY to overwrite or pass --cookies-file.`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
ui.log.warn(`${providerName} is already configured. Overwriting with provided cookies file.`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const overwrite = await clack.confirm({
|
|
65
|
+
message: `${providerName} is already configured. Overwrite?`,
|
|
66
|
+
});
|
|
67
|
+
if (clack.isCancel(overwrite) || !overwrite) {
|
|
68
|
+
ui.cancel('Setup cancelled');
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
55
71
|
}
|
|
56
72
|
}
|
|
57
73
|
// Configure based on provider
|
|
@@ -66,22 +82,30 @@ export async function setupCommand(options = {}) {
|
|
|
66
82
|
case 'claude':
|
|
67
83
|
await setupClaude(options);
|
|
68
84
|
break;
|
|
85
|
+
case 'gemini':
|
|
86
|
+
await setupGemini(options);
|
|
87
|
+
break;
|
|
69
88
|
default:
|
|
70
|
-
|
|
89
|
+
ui.log.error(`${providerName} provider is not yet implemented`);
|
|
71
90
|
process.exit(1);
|
|
72
91
|
}
|
|
73
|
-
|
|
92
|
+
ui.outro(chalk.green('✓ Setup complete! Run `ai-vault archive` to start archiving.'));
|
|
74
93
|
}
|
|
75
94
|
async function setupGrok(providerName, options) {
|
|
95
|
+
const ui = createCliUI();
|
|
76
96
|
const displayName = providerName === 'grok-web' ? 'Grok (grok.com)' : 'Grok on X (x.com/grok)';
|
|
77
|
-
|
|
97
|
+
ui.log.step(`Configuring ${displayName}`);
|
|
78
98
|
// If cookies file provided, skip to cookie auth
|
|
79
99
|
let authMethod;
|
|
80
100
|
if (options.cookiesFile) {
|
|
81
101
|
authMethod = 'cookies';
|
|
82
|
-
|
|
102
|
+
ui.log.info(`Using cookies from file: ${options.cookiesFile}`);
|
|
83
103
|
}
|
|
84
104
|
else {
|
|
105
|
+
if (!ui.isInteractive) {
|
|
106
|
+
ui.log.error('Interactive auth selection requires a TTY. Pass --cookies-file.');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
85
109
|
// Choose auth method
|
|
86
110
|
const selected = await clack.select({
|
|
87
111
|
message: 'Authentication method:',
|
|
@@ -91,7 +115,7 @@ async function setupGrok(providerName, options) {
|
|
|
91
115
|
],
|
|
92
116
|
});
|
|
93
117
|
if (clack.isCancel(selected)) {
|
|
94
|
-
|
|
118
|
+
ui.cancel('Setup cancelled');
|
|
95
119
|
process.exit(0);
|
|
96
120
|
}
|
|
97
121
|
authMethod = selected;
|
|
@@ -143,21 +167,25 @@ async function setupGrok(providerName, options) {
|
|
|
143
167
|
// Already in object format
|
|
144
168
|
cookies = parsed;
|
|
145
169
|
}
|
|
146
|
-
|
|
170
|
+
ui.log.success(`Loaded ${Object.keys(cookies).length} cookies from file`);
|
|
147
171
|
}
|
|
148
172
|
catch (error) {
|
|
149
|
-
|
|
173
|
+
ui.log.error(`Failed to read cookies file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
150
174
|
process.exit(1);
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
177
|
else {
|
|
178
|
+
if (!ui.isInteractive) {
|
|
179
|
+
ui.log.error('Interactive cookie entry requires a TTY. Pass --cookies-file.');
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
154
182
|
// Interactive prompt for each cookie
|
|
155
183
|
const websiteUrl = providerName === 'grok-web' ? 'grok.com' : 'x.com/grok';
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
184
|
+
ui.log.info(`To get cookies from ${displayName}:`);
|
|
185
|
+
ui.log.info(`1. Open ${websiteUrl} in your browser and log in`);
|
|
186
|
+
ui.log.info('2. Open Developer Tools (F12 or Cmd+Option+I)');
|
|
187
|
+
ui.log.info(`3. Go to Application → Cookies → https://${websiteUrl.split('/')[0]}`);
|
|
188
|
+
ui.log.info('4. Find each cookie below and copy its VALUE');
|
|
161
189
|
console.log();
|
|
162
190
|
cookies = {};
|
|
163
191
|
// Prompt for sso cookie first
|
|
@@ -175,7 +203,7 @@ async function setupGrok(providerName, options) {
|
|
|
175
203
|
},
|
|
176
204
|
});
|
|
177
205
|
if (clack.isCancel(ssoValue)) {
|
|
178
|
-
|
|
206
|
+
ui.cancel('Setup cancelled');
|
|
179
207
|
process.exit(0);
|
|
180
208
|
}
|
|
181
209
|
cookies['sso'] = ssoValue.trim();
|
|
@@ -196,14 +224,14 @@ async function setupGrok(providerName, options) {
|
|
|
196
224
|
},
|
|
197
225
|
});
|
|
198
226
|
if (clack.isCancel(ssoRwValue)) {
|
|
199
|
-
|
|
227
|
+
ui.cancel('Setup cancelled');
|
|
200
228
|
process.exit(0);
|
|
201
229
|
}
|
|
202
230
|
// Use sso value if sso-rw is empty
|
|
203
231
|
const ssoRwTrimmed = ssoRwValue ? ssoRwValue.trim() : '';
|
|
204
232
|
cookies['sso-rw'] = ssoRwTrimmed.length > 0 ? ssoRwTrimmed : cookies['sso'];
|
|
205
233
|
if (ssoRwTrimmed.length === 0) {
|
|
206
|
-
|
|
234
|
+
ui.log.info('Using same value as "sso" for "sso-rw"');
|
|
207
235
|
}
|
|
208
236
|
// Prompt for stblid cookie
|
|
209
237
|
const stblidValue = await clack.text({
|
|
@@ -221,11 +249,11 @@ async function setupGrok(providerName, options) {
|
|
|
221
249
|
},
|
|
222
250
|
});
|
|
223
251
|
if (clack.isCancel(stblidValue)) {
|
|
224
|
-
|
|
252
|
+
ui.cancel('Setup cancelled');
|
|
225
253
|
process.exit(0);
|
|
226
254
|
}
|
|
227
255
|
cookies['stblid'] = stblidValue.trim();
|
|
228
|
-
|
|
256
|
+
ui.log.success('All cookies collected successfully!');
|
|
229
257
|
}
|
|
230
258
|
providerConfig = {
|
|
231
259
|
providerName: providerName,
|
|
@@ -234,7 +262,7 @@ async function setupGrok(providerName, options) {
|
|
|
234
262
|
};
|
|
235
263
|
}
|
|
236
264
|
// Test authentication
|
|
237
|
-
const spinner =
|
|
265
|
+
const spinner = ui.spinner();
|
|
238
266
|
spinner.start('Testing authentication...');
|
|
239
267
|
try {
|
|
240
268
|
const provider = getProvider(providerConfig.providerName);
|
|
@@ -243,23 +271,27 @@ async function setupGrok(providerName, options) {
|
|
|
243
271
|
await provider.cleanup?.();
|
|
244
272
|
if (!isAuth) {
|
|
245
273
|
spinner.stop('Authentication failed');
|
|
246
|
-
|
|
274
|
+
ui.log.error('Could not authenticate with provided credentials');
|
|
247
275
|
process.exit(1);
|
|
248
276
|
}
|
|
249
277
|
spinner.stop('✓ Authentication successful!');
|
|
250
278
|
}
|
|
251
279
|
catch (error) {
|
|
252
280
|
spinner.stop('Authentication failed');
|
|
253
|
-
|
|
281
|
+
ui.log.error('Error: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
254
282
|
process.exit(1);
|
|
255
283
|
}
|
|
256
284
|
// Save configuration
|
|
257
285
|
await saveProviderConfig(providerConfig);
|
|
258
|
-
|
|
286
|
+
ui.log.success('Configuration saved to ~/.ai-vault/config.json');
|
|
259
287
|
// Configure archive directory
|
|
260
288
|
const config = await loadConfig();
|
|
261
289
|
if (!config.settings?.archiveDir) {
|
|
262
290
|
console.log();
|
|
291
|
+
if (!ui.isInteractive) {
|
|
292
|
+
ui.log.info('Using default archive directory (~/ai-vault-data)');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
263
295
|
const customDir = await clack.text({
|
|
264
296
|
message: 'Archive directory (press Enter for default):',
|
|
265
297
|
placeholder: '~/ai-vault-data',
|
|
@@ -269,12 +301,13 @@ async function setupGrok(providerName, options) {
|
|
|
269
301
|
config.settings.archiveDir = customDir;
|
|
270
302
|
const { saveConfig } = await import('../utils/config.js');
|
|
271
303
|
await saveConfig(config);
|
|
272
|
-
|
|
304
|
+
ui.log.info(`Archive directory set to: ${customDir}`);
|
|
273
305
|
}
|
|
274
306
|
}
|
|
275
307
|
}
|
|
276
308
|
async function setupChatGPT(options) {
|
|
277
|
-
|
|
309
|
+
const ui = createCliUI();
|
|
310
|
+
ui.log.step('Configuring ChatGPT (OpenAI)');
|
|
278
311
|
let cookies;
|
|
279
312
|
// Read from file if provided
|
|
280
313
|
if (options.cookiesFile) {
|
|
@@ -290,33 +323,28 @@ async function setupChatGPT(options) {
|
|
|
290
323
|
// Already in object format
|
|
291
324
|
cookies = parsed;
|
|
292
325
|
}
|
|
293
|
-
|
|
326
|
+
ui.log.success(`Loaded ${Object.keys(cookies).length} cookies from file`);
|
|
294
327
|
}
|
|
295
328
|
catch (error) {
|
|
296
|
-
|
|
329
|
+
ui.log.error(`Failed to read cookies file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
297
330
|
process.exit(1);
|
|
298
331
|
}
|
|
299
332
|
}
|
|
300
333
|
else {
|
|
334
|
+
if (!ui.isInteractive) {
|
|
335
|
+
ui.log.error('Interactive cookie entry requires a TTY. Pass --cookies-file.');
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
301
338
|
// Interactive prompt for each cookie
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
339
|
+
ui.log.info('To get cookies from ChatGPT:');
|
|
340
|
+
ui.log.info('1. Open chatgpt.com in your browser and log in');
|
|
341
|
+
ui.log.info('2. Open Developer Tools (F12 or Cmd+Option+I)');
|
|
342
|
+
ui.log.info('3. Go to Application → Cookies → https://chatgpt.com');
|
|
343
|
+
ui.log.info('4. Find the cookies below and copy their VALUES');
|
|
344
|
+
ui.log.info('');
|
|
345
|
+
ui.log.warn('⚠️ Important: Some cookies may not be present for all users.');
|
|
346
|
+
ui.log.warn(' Press Enter to skip optional cookies.');
|
|
310
347
|
console.log();
|
|
311
|
-
// Define required cookies for ChatGPT
|
|
312
|
-
const requiredCookies = [
|
|
313
|
-
{
|
|
314
|
-
name: '__Secure-next-auth.session-token',
|
|
315
|
-
description: 'Session token (REQUIRED)',
|
|
316
|
-
hint: 'Long JWT-like token',
|
|
317
|
-
required: true,
|
|
318
|
-
},
|
|
319
|
-
];
|
|
320
348
|
const optionalCookies = [
|
|
321
349
|
{
|
|
322
350
|
name: '__Host-next-auth.csrf-token',
|
|
@@ -332,23 +360,48 @@ async function setupChatGPT(options) {
|
|
|
332
360
|
},
|
|
333
361
|
];
|
|
334
362
|
cookies = {};
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
363
|
+
ui.log.warn(' Some accounts now split the session token into .0 and .1 cookies.');
|
|
364
|
+
ui.log.warn(' Provide either the single session token or both .0 and .1 parts.');
|
|
365
|
+
console.log();
|
|
366
|
+
const baseTokenValue = await clack.text({
|
|
367
|
+
message: 'Enter value for cookie "__Secure-next-auth.session-token" (optional if using .0/.1):',
|
|
368
|
+
placeholder: 'Long JWT-like token - press Enter to use .0/.1 cookies',
|
|
369
|
+
});
|
|
370
|
+
if (clack.isCancel(baseTokenValue)) {
|
|
371
|
+
ui.cancel('Setup cancelled');
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
const tokenPart0Value = await clack.text({
|
|
375
|
+
message: 'Enter value for cookie "__Secure-next-auth.session-token.0" (optional):',
|
|
376
|
+
placeholder: 'First chunk - press Enter to skip if not found',
|
|
377
|
+
});
|
|
378
|
+
if (clack.isCancel(tokenPart0Value)) {
|
|
379
|
+
ui.cancel('Setup cancelled');
|
|
380
|
+
process.exit(0);
|
|
381
|
+
}
|
|
382
|
+
const tokenPart1Value = await clack.text({
|
|
383
|
+
message: 'Enter value for cookie "__Secure-next-auth.session-token.1" (optional):',
|
|
384
|
+
placeholder: 'Second chunk - press Enter to skip if not found',
|
|
385
|
+
});
|
|
386
|
+
if (clack.isCancel(tokenPart1Value)) {
|
|
387
|
+
ui.cancel('Setup cancelled');
|
|
388
|
+
process.exit(0);
|
|
389
|
+
}
|
|
390
|
+
const baseToken = baseTokenValue?.trim();
|
|
391
|
+
const tokenPart0 = tokenPart0Value?.trim();
|
|
392
|
+
const tokenPart1 = tokenPart1Value?.trim();
|
|
393
|
+
if (!baseToken && (!tokenPart0 || !tokenPart1)) {
|
|
394
|
+
ui.log.error('Missing required session token. Provide "__Secure-next-auth.session-token" or both ".0" and ".1" parts.');
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
if (baseToken) {
|
|
398
|
+
cookies['__Secure-next-auth.session-token'] = baseToken;
|
|
399
|
+
}
|
|
400
|
+
if (tokenPart0) {
|
|
401
|
+
cookies['__Secure-next-auth.session-token.0'] = tokenPart0;
|
|
402
|
+
}
|
|
403
|
+
if (tokenPart1) {
|
|
404
|
+
cookies['__Secure-next-auth.session-token.1'] = tokenPart1;
|
|
352
405
|
}
|
|
353
406
|
// Prompt for optional cookies
|
|
354
407
|
for (const cookieInfo of optionalCookies) {
|
|
@@ -357,7 +410,7 @@ async function setupChatGPT(options) {
|
|
|
357
410
|
placeholder: `${cookieInfo.hint} (press Enter to skip)`,
|
|
358
411
|
});
|
|
359
412
|
if (clack.isCancel(value)) {
|
|
360
|
-
|
|
413
|
+
ui.cancel('Setup cancelled');
|
|
361
414
|
process.exit(0);
|
|
362
415
|
}
|
|
363
416
|
const trimmedValue = value?.trim();
|
|
@@ -365,7 +418,7 @@ async function setupChatGPT(options) {
|
|
|
365
418
|
cookies[cookieInfo.name] = trimmedValue;
|
|
366
419
|
}
|
|
367
420
|
}
|
|
368
|
-
|
|
421
|
+
ui.log.success(`Collected ${Object.keys(cookies).length} cookie${Object.keys(cookies).length > 1 ? 's' : ''} successfully!`);
|
|
369
422
|
}
|
|
370
423
|
const providerConfig = {
|
|
371
424
|
providerName: 'chatgpt',
|
|
@@ -373,7 +426,7 @@ async function setupChatGPT(options) {
|
|
|
373
426
|
cookies,
|
|
374
427
|
};
|
|
375
428
|
// Test authentication
|
|
376
|
-
const spinner =
|
|
429
|
+
const spinner = ui.spinner();
|
|
377
430
|
spinner.start('Testing authentication...');
|
|
378
431
|
try {
|
|
379
432
|
const provider = getProvider(providerConfig.providerName);
|
|
@@ -382,23 +435,27 @@ async function setupChatGPT(options) {
|
|
|
382
435
|
await provider.cleanup?.();
|
|
383
436
|
if (!isAuth) {
|
|
384
437
|
spinner.stop('Authentication failed');
|
|
385
|
-
|
|
438
|
+
ui.log.error('Could not authenticate with provided credentials');
|
|
386
439
|
process.exit(1);
|
|
387
440
|
}
|
|
388
441
|
spinner.stop('✓ Authentication successful!');
|
|
389
442
|
}
|
|
390
443
|
catch (error) {
|
|
391
444
|
spinner.stop('Authentication failed');
|
|
392
|
-
|
|
445
|
+
ui.log.error('Error: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
393
446
|
process.exit(1);
|
|
394
447
|
}
|
|
395
448
|
// Save configuration
|
|
396
449
|
await saveProviderConfig(providerConfig);
|
|
397
|
-
|
|
450
|
+
ui.log.success('Configuration saved to ~/.ai-vault/config.json');
|
|
398
451
|
// Configure archive directory
|
|
399
452
|
const config = await loadConfig();
|
|
400
453
|
if (!config.settings?.archiveDir) {
|
|
401
454
|
console.log();
|
|
455
|
+
if (!ui.isInteractive) {
|
|
456
|
+
ui.log.info('Using default archive directory (~/ai-vault-data)');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
402
459
|
const customDir = await clack.text({
|
|
403
460
|
message: 'Archive directory (press Enter for default):',
|
|
404
461
|
placeholder: '~/ai-vault-data',
|
|
@@ -408,12 +465,147 @@ async function setupChatGPT(options) {
|
|
|
408
465
|
config.settings.archiveDir = customDir;
|
|
409
466
|
const { saveConfig } = await import('../utils/config.js');
|
|
410
467
|
await saveConfig(config);
|
|
411
|
-
|
|
468
|
+
ui.log.info(`Archive directory set to: ${customDir}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async function setupGemini(options) {
|
|
473
|
+
const ui = createCliUI();
|
|
474
|
+
ui.log.step('Configuring Gemini (Google)');
|
|
475
|
+
let cookies;
|
|
476
|
+
// Read from file if provided
|
|
477
|
+
if (options.cookiesFile) {
|
|
478
|
+
try {
|
|
479
|
+
const filePath = resolve(options.cookiesFile);
|
|
480
|
+
const fileContent = readFileSync(filePath, 'utf-8');
|
|
481
|
+
const parsed = JSON.parse(fileContent);
|
|
482
|
+
if (Array.isArray(parsed)) {
|
|
483
|
+
cookies = Object.fromEntries(parsed.map((cookie) => [cookie.name, cookie.value]));
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
cookies = parsed;
|
|
487
|
+
}
|
|
488
|
+
ui.log.success(`Loaded ${Object.keys(cookies).length} cookies from file`);
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
ui.log.error(`Failed to read cookies file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
if (!ui.isInteractive) {
|
|
497
|
+
ui.log.error('Interactive cookie entry requires a TTY. Pass --cookies-file.');
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
ui.log.info('Gemini requires the following cookies from gemini.google.com:');
|
|
501
|
+
console.log();
|
|
502
|
+
ui.log.info(' Required:');
|
|
503
|
+
ui.log.info(' SID, HSID, SSID, APISID, SAPISID, SIDCC, NID');
|
|
504
|
+
ui.log.info(' __Secure-1PSID, __Secure-1PSIDTS, __Secure-1PSIDCC, __Secure-1PAPISID');
|
|
505
|
+
ui.log.info(' __Secure-3PSID, __Secure-3PSIDTS, __Secure-3PSIDCC, __Secure-3PAPISID');
|
|
506
|
+
ui.log.info(' Optional (but recommended):');
|
|
507
|
+
ui.log.info(' SOCS (required in EU), AEC, S, NID, __Secure-ENID, __Secure-STRP,');
|
|
508
|
+
ui.log.info(' SEARCH_SAMESITE, __Secure-BUCKET, _ga, _gcl_au');
|
|
509
|
+
console.log();
|
|
510
|
+
ui.log.info('How to export all cookies at once:');
|
|
511
|
+
ui.log.info(' 1. Open gemini.google.com in your browser and log in');
|
|
512
|
+
ui.log.info(' 2. Install "Cookie-Editor" or "EditThisCookie" browser extension');
|
|
513
|
+
ui.log.info(' 3. Click the extension icon → Export as JSON');
|
|
514
|
+
ui.log.info(' 4. Paste the JSON below, OR save to a file and use:');
|
|
515
|
+
ui.log.info(' ai-vault setup gemini --cookies-file <path>');
|
|
516
|
+
console.log();
|
|
517
|
+
const jsonInput = await clack.text({
|
|
518
|
+
message: 'Paste cookies JSON (array or {"name":"value"} object):',
|
|
519
|
+
placeholder: '[{"name":"SID","value":"..."},...] or {"SID":"...","HSID":"..."}',
|
|
520
|
+
validate: (val) => {
|
|
521
|
+
if (!val || val.trim().length === 0)
|
|
522
|
+
return 'Cookies JSON is required';
|
|
523
|
+
try {
|
|
524
|
+
const parsed = JSON.parse(val.trim());
|
|
525
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
526
|
+
return 'Must be a JSON object or array';
|
|
527
|
+
const keys = Array.isArray(parsed)
|
|
528
|
+
? parsed.map((c) => c?.name).filter(Boolean)
|
|
529
|
+
: Object.keys(parsed);
|
|
530
|
+
if (keys.length === 0)
|
|
531
|
+
return 'No cookies found in JSON';
|
|
532
|
+
const required = ['SID', '__Secure-1PSID', 'SAPISID'];
|
|
533
|
+
const missing = required.filter((k) => !keys.includes(k));
|
|
534
|
+
if (missing.length === required.length) {
|
|
535
|
+
return `Missing key cookies: ${required.join(', ')}`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
return 'Invalid JSON — copy the full export from your browser extension';
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
if (clack.isCancel(jsonInput)) {
|
|
544
|
+
ui.cancel('Setup cancelled');
|
|
545
|
+
process.exit(0);
|
|
546
|
+
}
|
|
547
|
+
// JSON validity already confirmed by the validator above
|
|
548
|
+
const parsed = JSON.parse(jsonInput.trim());
|
|
549
|
+
if (Array.isArray(parsed)) {
|
|
550
|
+
cookies = Object.fromEntries(parsed.map((c) => [c.name, c.value]));
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
cookies = parsed;
|
|
554
|
+
}
|
|
555
|
+
ui.log.success(`Loaded ${Object.keys(cookies).length} cookies`);
|
|
556
|
+
}
|
|
557
|
+
const providerConfig = {
|
|
558
|
+
providerName: 'gemini',
|
|
559
|
+
authMethod: 'cookies',
|
|
560
|
+
cookies,
|
|
561
|
+
};
|
|
562
|
+
// Test authentication
|
|
563
|
+
const spinner = ui.spinner();
|
|
564
|
+
spinner.start('Testing authentication...');
|
|
565
|
+
try {
|
|
566
|
+
const provider = getProvider(providerConfig.providerName);
|
|
567
|
+
await provider.authenticate(providerConfig);
|
|
568
|
+
const isAuth = await provider.isAuthenticated();
|
|
569
|
+
await provider.cleanup?.();
|
|
570
|
+
if (!isAuth) {
|
|
571
|
+
spinner.stop('Authentication failed');
|
|
572
|
+
ui.log.error('Could not authenticate with provided credentials');
|
|
573
|
+
process.exit(1);
|
|
574
|
+
}
|
|
575
|
+
spinner.stop('✓ Authentication successful!');
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
spinner.stop('Authentication failed');
|
|
579
|
+
ui.log.error('Error: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
// Save configuration
|
|
583
|
+
await saveProviderConfig(providerConfig);
|
|
584
|
+
ui.log.success('Configuration saved to ~/.ai-vault/config.json');
|
|
585
|
+
// Configure archive directory
|
|
586
|
+
const config = await loadConfig();
|
|
587
|
+
if (!config.settings?.archiveDir) {
|
|
588
|
+
console.log();
|
|
589
|
+
if (!ui.isInteractive) {
|
|
590
|
+
ui.log.info('Using default archive directory (~/ai-vault-data)');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const customDir = await clack.text({
|
|
594
|
+
message: 'Archive directory (press Enter for default):',
|
|
595
|
+
placeholder: '~/ai-vault-data',
|
|
596
|
+
});
|
|
597
|
+
if (!clack.isCancel(customDir) && customDir) {
|
|
598
|
+
config.settings = config.settings || {};
|
|
599
|
+
config.settings.archiveDir = customDir;
|
|
600
|
+
const { saveConfig } = await import('../utils/config.js');
|
|
601
|
+
await saveConfig(config);
|
|
602
|
+
ui.log.info(`Archive directory set to: ${customDir}`);
|
|
412
603
|
}
|
|
413
604
|
}
|
|
414
605
|
}
|
|
415
606
|
async function setupClaude(options) {
|
|
416
|
-
|
|
607
|
+
const ui = createCliUI();
|
|
608
|
+
ui.log.step('Configuring Claude (Anthropic)');
|
|
417
609
|
let cookies;
|
|
418
610
|
// Read from file if provided
|
|
419
611
|
if (options.cookiesFile) {
|
|
@@ -429,20 +621,24 @@ async function setupClaude(options) {
|
|
|
429
621
|
// Already in object format
|
|
430
622
|
cookies = parsed;
|
|
431
623
|
}
|
|
432
|
-
|
|
624
|
+
ui.log.success(`Loaded ${Object.keys(cookies).length} cookies from file`);
|
|
433
625
|
}
|
|
434
626
|
catch (error) {
|
|
435
|
-
|
|
627
|
+
ui.log.error(`Failed to read cookies file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
436
628
|
process.exit(1);
|
|
437
629
|
}
|
|
438
630
|
}
|
|
439
631
|
else {
|
|
632
|
+
if (!ui.isInteractive) {
|
|
633
|
+
ui.log.error('Interactive cookie entry requires a TTY. Pass --cookies-file.');
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
440
636
|
// Interactive prompt for sessionKey cookie
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
637
|
+
ui.log.info('To get your sessionKey cookie from Claude:');
|
|
638
|
+
ui.log.info('1. Open claude.ai in your browser and log in');
|
|
639
|
+
ui.log.info('2. Open Developer Tools (F12 or Cmd+Option+I)');
|
|
640
|
+
ui.log.info('3. Go to Application → Cookies → https://claude.ai');
|
|
641
|
+
ui.log.info('4. Find the "sessionKey" cookie and copy its VALUE');
|
|
446
642
|
console.log();
|
|
447
643
|
const sessionKey = await clack.text({
|
|
448
644
|
message: 'Enter value for cookie "sessionKey":',
|
|
@@ -462,13 +658,13 @@ async function setupClaude(options) {
|
|
|
462
658
|
},
|
|
463
659
|
});
|
|
464
660
|
if (clack.isCancel(sessionKey)) {
|
|
465
|
-
|
|
661
|
+
ui.cancel('Setup cancelled');
|
|
466
662
|
process.exit(0);
|
|
467
663
|
}
|
|
468
664
|
cookies = {
|
|
469
665
|
sessionKey: sessionKey.trim(),
|
|
470
666
|
};
|
|
471
|
-
|
|
667
|
+
ui.log.success('sessionKey collected successfully!');
|
|
472
668
|
}
|
|
473
669
|
const providerConfig = {
|
|
474
670
|
providerName: 'claude',
|
|
@@ -476,7 +672,7 @@ async function setupClaude(options) {
|
|
|
476
672
|
cookies,
|
|
477
673
|
};
|
|
478
674
|
// Test authentication
|
|
479
|
-
const spinner =
|
|
675
|
+
const spinner = ui.spinner();
|
|
480
676
|
spinner.start('Testing authentication...');
|
|
481
677
|
try {
|
|
482
678
|
const provider = getProvider(providerConfig.providerName);
|
|
@@ -485,23 +681,27 @@ async function setupClaude(options) {
|
|
|
485
681
|
await provider.cleanup?.();
|
|
486
682
|
if (!isAuth) {
|
|
487
683
|
spinner.stop('Authentication failed');
|
|
488
|
-
|
|
684
|
+
ui.log.error('Could not authenticate with provided credentials');
|
|
489
685
|
process.exit(1);
|
|
490
686
|
}
|
|
491
687
|
spinner.stop('✓ Authentication successful!');
|
|
492
688
|
}
|
|
493
689
|
catch (error) {
|
|
494
690
|
spinner.stop('Authentication failed');
|
|
495
|
-
|
|
691
|
+
ui.log.error('Error: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
|
496
692
|
process.exit(1);
|
|
497
693
|
}
|
|
498
694
|
// Save configuration
|
|
499
695
|
await saveProviderConfig(providerConfig);
|
|
500
|
-
|
|
696
|
+
ui.log.success('Configuration saved to ~/.ai-vault/config.json');
|
|
501
697
|
// Configure archive directory
|
|
502
698
|
const config = await loadConfig();
|
|
503
699
|
if (!config.settings?.archiveDir) {
|
|
504
700
|
console.log();
|
|
701
|
+
if (!ui.isInteractive) {
|
|
702
|
+
ui.log.info('Using default archive directory (~/ai-vault-data)');
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
505
705
|
const customDir = await clack.text({
|
|
506
706
|
message: 'Archive directory (press Enter for default):',
|
|
507
707
|
placeholder: '~/ai-vault-data',
|
|
@@ -511,7 +711,7 @@ async function setupClaude(options) {
|
|
|
511
711
|
config.settings.archiveDir = customDir;
|
|
512
712
|
const { saveConfig } = await import('../utils/config.js');
|
|
513
713
|
await saveConfig(config);
|
|
514
|
-
|
|
714
|
+
ui.log.info(`Archive directory set to: ${customDir}`);
|
|
515
715
|
}
|
|
516
716
|
}
|
|
517
717
|
}
|