@thelapyae/geniclaw 1.2.0 → 1.2.2

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/bin/geniclaw.js CHANGED
@@ -1,82 +1,139 @@
1
1
  #!/usr/bin/env node
2
- const { spawn, execSync } = require('child_process');
3
- const path = require('path');
2
+ const { program } = require('commander');
4
3
  const fs = require('fs');
4
+ const path = require('path');
5
5
  const os = require('os');
6
- const { program } = require('commander');
6
+ const { spawn, execSync, fork } = require('child_process');
7
7
  const inquirer = require('inquirer');
8
- const https = require('https'); // For simple URL fetch if needed in TUI directly or via script
8
+ const { ConfigManager } = require('../dist/config-manager');
9
9
 
10
- // Config Paths
10
+ // Paths
11
11
  const LOCAL_DIR = path.join(process.cwd(), '.geniclaw');
12
12
  const GENICLAW_WORK_DIR = process.env.GENICLAW_WORK_DIR || (fs.existsSync(LOCAL_DIR) ? LOCAL_DIR : path.join(os.homedir(), '.geniclaw'));
13
13
  const CONFIG_FILE = path.join(GENICLAW_WORK_DIR, 'config.json');
14
14
  const SESSIONS_DIR = path.join(GENICLAW_WORK_DIR, 'sessions');
15
15
  const SKILLS_DIR = path.join(GENICLAW_WORK_DIR, 'skills');
16
+ const { runDoctor } = require('../dist/doctor');
16
17
 
17
18
  const SCRIPT_PATH = path.join(__dirname, '../geniclaw.sh');
18
19
 
19
20
  // Helper to run shell script commands
20
21
  function runScript(args) {
21
- const child = spawn(SCRIPT_PATH, args, {
22
- stdio: 'inherit',
23
- env: process.env
24
- });
25
-
26
- return new Promise((resolve) => {
27
- child.on('exit', (code) => {
28
- resolve(code);
22
+ return new Promise((resolve, reject) => {
23
+ const child = spawn(SCRIPT_PATH, args, { stdio: 'inherit' });
24
+ child.on('close', (code) => {
25
+ if (code === 0) resolve();
26
+ else reject(new Error(`Script exited with code ${code}`));
29
27
  });
28
+ child.on('error', reject);
30
29
  });
31
30
  }
32
31
 
33
- // Load Config
32
+ // Config Helpers
34
33
  function loadConfig() {
35
- if (fs.existsSync(CONFIG_FILE)) {
36
- try {
37
- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
38
- } catch (e) {
39
- return {};
40
- }
41
- }
42
- return {};
34
+ return ConfigManager.load();
43
35
  }
44
36
 
45
- // Save Config
46
37
  function saveConfig(config) {
47
- if (!fs.existsSync(GENICLAW_WORK_DIR)) {
48
- fs.mkdirSync(GENICLAW_WORK_DIR, { recursive: true });
49
- }
50
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
38
+ ConfigManager.save(config);
39
+ }
40
+
41
+ // Ensure sessions dir exists
42
+ if (!fs.existsSync(SESSIONS_DIR)) {
43
+ fs.mkdirSync(SESSIONS_DIR, { recursive: true });
44
+ }
45
+
46
+ async function promptContinue() {
47
+ await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to continue...' }]);
51
48
  }
52
49
 
53
- // Helper: Get Skills
54
- function getSkills() {
50
+ // Skill Management
51
+ function listSkills() {
55
52
  if (!fs.existsSync(SKILLS_DIR)) return [];
56
53
  return fs.readdirSync(SKILLS_DIR)
57
54
  .filter(f => f.endsWith('.json'))
58
55
  .map(f => {
59
- try { return JSON.parse(fs.readFileSync(path.join(SKILLS_DIR, f), 'utf8')); }
60
- catch(e) { return null; }
56
+ try {
57
+ const data = JSON.parse(fs.readFileSync(path.join(SKILLS_DIR, f), 'utf8'));
58
+ return { name: data.name, filename: f, description: data.description };
59
+ } catch (e) { return null; }
61
60
  })
62
61
  .filter(s => s !== null);
63
62
  }
64
63
 
65
- // Helper: Install Skill
64
+ async function manageSkills() {
65
+ const config = loadConfig();
66
+ const skills = listSkills();
67
+ const activeSkill = config.activeSkill || 'None';
68
+
69
+ console.clear();
70
+ console.log('🧠 Skill Manager');
71
+ console.log(`Active Skill: ${activeSkill}`);
72
+ console.log('----------------');
73
+
74
+ const { action } = await inquirer.prompt([
75
+ {
76
+ type: 'list',
77
+ name: 'action',
78
+ message: 'Choose an action:',
79
+ choices: [
80
+ { name: 'Activate/Deactivate Skill', value: 'activate' },
81
+ { name: 'Install New Skill (URL)', value: 'install' },
82
+ { name: 'List Installed Skills', value: 'list' },
83
+ { name: 'Back', value: 'back' }
84
+ ]
85
+ }
86
+ ]);
87
+
88
+ if (action === 'back') return;
89
+
90
+ if (action === 'activate') {
91
+ const choices = [{ name: 'None (Disable Skills)', value: null }, ...skills.map(s => ({ name: `${s.name} - ${s.description}`, value: s.name }))];
92
+ const { skill } = await inquirer.prompt([
93
+ { type: 'list', name: 'skill', message: 'Select Skill to Activate:', choices }
94
+ ]);
95
+ config.activeSkill = skill; // null if None
96
+ saveConfig(config);
97
+ console.log(`✅ Active Skill set to: ${skill || 'None'}`);
98
+ }
99
+
100
+ if (action === 'install') {
101
+ const { url } = await inquirer.prompt([
102
+ { type: 'input', name: 'url', message: 'Enter Skill JSON URL:' }
103
+ ]);
104
+ try {
105
+ await installSkillFromUrl(url);
106
+ console.log('✅ Skill installed successfully.');
107
+ } catch (e) {
108
+ console.log(`❌ Error installing skill: ${e.message}`);
109
+ }
110
+ }
111
+
112
+ if (action === 'list') {
113
+ skills.forEach(s => console.log(`- ${s.name}: ${s.description}`));
114
+ }
115
+
116
+ await promptContinue();
117
+ return manageSkills();
118
+ }
119
+
66
120
  async function installSkillFromUrl(url) {
121
+ // Simple fetch and save
122
+ // In a real app, use the SkillManager class from src/skill-manager.ts if possible,
123
+ // but here we are in JS land (bin).
124
+ // actually we can require the built JS from dist if needed, but let's keep it simple dependency-free if we can or use the one we have.
125
+ // The previous implementation used a fetch.
67
126
  return new Promise((resolve, reject) => {
127
+ const https = require('https');
68
128
  https.get(url, (res) => {
69
129
  let data = '';
70
130
  res.on('data', chunk => data += chunk);
71
131
  res.on('end', () => {
72
132
  try {
73
133
  const skill = JSON.parse(data);
74
- if (!skill.name || !skill.systemInstruction) {
75
- reject(new Error("Invalid skill JSON"));
76
- return;
77
- }
134
+ if (!skill.name || !skill.prompts) throw new Error('Invalid Skill JSON');
78
135
  if (!fs.existsSync(SKILLS_DIR)) fs.mkdirSync(SKILLS_DIR, { recursive: true });
79
- const safeName = skill.name.replace(/[^a-zA-Z0-9_-]/g, '_');
136
+ const safeName = skill.name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
80
137
  fs.writeFileSync(path.join(SKILLS_DIR, `${safeName}.json`), JSON.stringify(skill, null, 2));
81
138
  resolve(skill);
82
139
  } catch(e) { reject(e); }
@@ -95,16 +152,22 @@ async function showMenu() {
95
152
  const config = loadConfig();
96
153
  const activeSession = config.activeSession || 'default';
97
154
  const activeSkill = config.activeSkill || 'None';
155
+ const activeProvider = config.activeProvider || 'gemini';
98
156
 
99
- if (!config.geminiApiKey) {
100
- console.log('⚠️ Configuration Missing: No Gemini API Key found.');
157
+ // Check if the ACTIVE provider has a key
158
+ const activeKeyField = `${activeProvider}ApiKey`;
159
+ const hasKey = config[activeKeyField] || process.env[activeKeyField.toUpperCase().replace(/([A-Z])/g, '_$1').toUpperCase()] || (activeProvider === 'gemini' && process.env.GEMINI_API_KEY);
160
+
161
+ if (!hasKey) {
162
+ console.log(`⚠️ Configuration Missing: No API Key found for active provider (${activeProvider}).`);
101
163
  const { runSetup } = await inquirer.prompt([
102
- { type: 'confirm', name: 'runSetup', message: 'Would you like to run the setup wizard now?', default: true }
164
+ { type: 'confirm', name: 'runSetup', message: 'Would you like to configure keys now?', default: true }
103
165
  ]);
104
166
 
105
167
  if (runSetup) {
106
- await runScript(['setup']);
107
- await promptContinue();
168
+ // If they say yes, go to keys menu directly? Or setup?
169
+ // Local setup script is gemini-focused. Let's send them to manageApiKey menu.
170
+ await manageApiKeys(activeProvider);
108
171
  return showMenu();
109
172
  }
110
173
  }
@@ -137,11 +200,15 @@ async function showMenu() {
137
200
  { name: '🔀 Task Routing (Chat/Heartbeat/Cron)', value: 'routing' },
138
201
  { name: '🔎 Web Search Config', value: 'search' },
139
202
  { name: '🧠 Manage Skills', value: 'skills' },
140
- { name: '📂 Manage Sessions', value: 'sessions' },
141
- { name: '🔌 Change API Base URL (Gemini/Proxy)', value: 'apiUrl' },
203
+ { name: '📂 Switch Session', value: 'session' },
204
+ { name: ' Create New Session', value: 'new_session' },
205
+ { name: '🗑️ Delete Session', value: 'delete_session' },
206
+ new inquirer.Separator(),
207
+ { name: '🔌 Set Custom API URL', value: 'proxy' },
142
208
  { name: '🔑 Set Gateway/Proxy Key', value: 'gatewayKey' },
143
209
  new inquirer.Separator(),
144
210
  { name: '✨ Update GeniClaw', value: 'update' },
211
+ { name: '🚑 Run Doctor (Fix Issues)', value: 'doctor' },
145
212
  { name: 'Run Setup Wizard', value: 'setup' },
146
213
  { name: 'Exit', value: 'exit' }
147
214
  ]
@@ -149,13 +216,20 @@ async function showMenu() {
149
216
  ]);
150
217
 
151
218
  if (action === 'exit') {
219
+ console.log('Bye! 👋');
152
220
  process.exit(0);
153
221
  }
154
-
222
+
155
223
  if (action === 'update') {
156
224
  await performUpdate();
157
225
  return; // Exit after update to allow restart
158
226
  }
227
+
228
+ if (action === 'doctor') {
229
+ await runDoctor();
230
+ await promptContinue();
231
+ return showMenu();
232
+ }
159
233
 
160
234
  if (action === 'setup') {
161
235
  await runScript(['setup']);
@@ -163,9 +237,40 @@ async function showMenu() {
163
237
  return showMenu();
164
238
  }
165
239
 
240
+ if (action === 'start' || action === 'stop' || action === 'restart') {
241
+ await runScript([action]);
242
+ await promptContinue();
243
+ return showMenu();
244
+ }
245
+
246
+ if (action === 'logs') {
247
+ await runScript(['logs']);
248
+ await promptContinue();
249
+ return showMenu();
250
+ }
251
+
252
+ if (action === 'session') {
253
+ await switchSession();
254
+ return showMenu();
255
+ }
256
+
257
+ if (action === 'new_session') {
258
+ await createSession();
259
+ return showMenu();
260
+ }
261
+
262
+ if (action === 'delete_session') {
263
+ await deleteSession();
264
+ return showMenu();
265
+ }
266
+
267
+ if (action === 'skills') {
268
+ await manageSkills();
269
+ return showMenu();
270
+ }
271
+
166
272
  if (action === 'provider') {
167
273
  await selectProviderAndModel();
168
- await promptContinue();
169
274
  return showMenu();
170
275
  }
171
276
 
@@ -184,155 +289,229 @@ async function showMenu() {
184
289
  return showMenu();
185
290
  }
186
291
 
187
- if (action === 'apiUrl') {
188
- await changeApiUrl();
189
- await promptContinue();
190
- return showMenu();
292
+ if (action === 'proxy') {
293
+ await setCustomUrl();
294
+ return showMenu();
191
295
  }
192
296
 
193
297
  if (action === 'gatewayKey') {
194
- await changeGatewayKey();
195
- await promptContinue();
196
- return showMenu();
298
+ await setGatewayKey();
299
+ return showMenu();
197
300
  }
301
+ }
198
302
 
199
- if (action === 'sessions') {
200
- await manageSessions();
201
- return showMenu();
303
+ async function performUpdate() {
304
+ console.log('Checking for updates...');
305
+ try {
306
+ const updateCmd = spawn('npm', ['install', '-g', '@thelapyae/geniclaw@latest'], { stdio: 'inherit' });
307
+ updateCmd.on('close', (code) => {
308
+ if (code === 0) {
309
+ console.log('✅ Update successful! Please restart GeniClaw.');
310
+ process.exit(0);
311
+ } else {
312
+ console.log('❌ Update failed.');
313
+ }
314
+ });
315
+ } catch (e) {
316
+ console.log('❌ Error updating: ' + e.message);
202
317
  }
318
+ }
203
319
 
204
- if (action === 'skills') {
205
- await manageSkills();
206
- return showMenu();
207
- }
320
+ async function setCustomUrl() {
321
+ const config = loadConfig();
322
+ const currentUrl = config.geminiApiUrl || 'Default (Google)';
323
+
324
+ const { url } = await inquirer.prompt([
325
+ {
326
+ type: 'input',
327
+ name: 'url',
328
+ message: `Enter Custom API Base URL (Current: ${currentUrl}):`,
329
+ suffix: '\n(Leave empty to reset to default)',
330
+ }
331
+ ]);
208
332
 
209
- if (action === 'logs') {
210
- await showLogsMenu();
211
- return showMenu();
333
+ if (url && url.trim() !== '') {
334
+ config.geminiApiUrl = url.trim();
335
+ console.log(`✅ API URL set to: ${url}`);
336
+ } else {
337
+ delete config.geminiApiUrl;
338
+ console.log('✅ API URL reset to default.');
212
339
  }
213
-
214
- // Process other actions via script
215
- await runScript([action]);
340
+ saveConfig(config);
216
341
  await promptContinue();
217
- return showMenu();
218
342
  }
219
343
 
220
- async function promptContinue() {
221
- await inquirer.prompt([
344
+ async function setGatewayKey() {
345
+ const config = loadConfig();
346
+ const currentKey = config.geminiGatewayKey ? '********' : 'Not Set';
347
+
348
+ const { key } = await inquirer.prompt([
222
349
  {
223
- type: 'input',
224
- name: 'continue',
225
- message: 'Press Enter to continue...',
350
+ type: 'password',
351
+ name: 'key',
352
+ message: `Enter Gateway/Proxy Auth Key (Current: ${currentKey}):`,
353
+ suffix: '\n(Leave empty to remove/keep)',
354
+ mask: '*'
226
355
  }
227
356
  ]);
357
+
358
+ if (key && key.trim() !== '') {
359
+ config.geminiGatewayKey = key.trim();
360
+ saveConfig(config);
361
+ console.log('✅ Gateway Key updated.');
362
+ } else if (key === '') {
363
+ // Ask if remove? Na, empty usually means keep or skip.
364
+ // Let's add specific logic: if empty, do nothing.
365
+ // If they want to clear, they might need a clear option.
366
+ // For CLI simplicity, let's keep it as is.
367
+ console.log('No change.');
368
+ }
369
+ await promptContinue();
228
370
  }
229
371
 
230
372
  async function selectProviderAndModel() {
231
373
  const config = loadConfig();
232
- const currentProvider = config.activeProvider || 'gemini';
374
+ let provider = config.activeProvider || 'gemini';
233
375
 
234
- const { provider } = await inquirer.prompt([
376
+ // 1. Select Provider
377
+ const { newProvider } = await inquirer.prompt([
235
378
  {
236
379
  type: 'list',
237
- name: 'provider',
380
+ name: 'newProvider',
238
381
  message: 'Select Active AI Provider:',
239
- default: currentProvider,
382
+ default: provider,
240
383
  choices: [
241
384
  { name: 'Google Gemini', value: 'gemini' },
242
- { name: 'OpenAI (GPT-4)', value: 'openai' },
243
- { name: 'Anthropic (Claude)', value: 'anthropic' },
244
- { name: 'Groq (Llama 3)', value: 'groq' },
385
+ { name: 'OpenAI', value: 'openai' },
386
+ { name: 'Anthropic', value: 'anthropic' },
387
+ { name: 'Groq', value: 'groq' },
245
388
  { name: 'Deepseek', value: 'deepseek' },
246
- { name: 'xAI (Grok)', value: 'grok' }
389
+ { name: 'xAI (Grok)', value: 'grok' },
390
+ { name: 'Back', value: 'back' }
247
391
  ]
248
392
  }
249
393
  ]);
250
394
 
251
- config.activeProvider = provider;
395
+ if (newProvider === 'back') return;
396
+ config.activeProvider = newProvider;
252
397
  saveConfig(config);
253
- console.log(`✅ Active Provider set to: ${provider}`);
254
-
255
- // Now configure model for this provider
256
- let currentModel = 'default';
257
- let choices = [];
258
-
259
- switch (provider) {
260
- case 'gemini':
261
- currentModel = config.geminiModel || 'gemini-2.5-flash';
262
- choices = ['gemini-2.5-flash', 'gemini-flash-lite-latest', 'gemini-2.5-pro', 'gemini-3-flash-preview', 'gemini-3-pro-preview'];
263
- break;
264
- case 'openai':
265
- currentModel = config.openaiModel || 'gpt-4o';
266
- choices = ['gpt-4o', 'gpt-4-turbo', 'gpt-3.5-turbo'];
267
- break;
268
- case 'anthropic':
269
- currentModel = config.anthropicModel || 'claude-3-5-sonnet-20240620';
270
- choices = ['claude-3-5-sonnet-20240620', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'];
271
- break;
272
- case 'groq':
273
- currentModel = config.groqModel || 'llama3-70b-8192';
274
- choices = ['llama3-70b-8192', 'llama3-8b-8192', 'mixtral-8x7b-32768', 'gemma-7b-it'];
275
- break;
276
- case 'deepseek':
277
- currentModel = config.deepseekModel || 'deepseek-chat';
278
- choices = ['deepseek-chat', 'deepseek-coder'];
279
- break;
280
- case 'grok':
281
- currentModel = config.grokModel || 'grok-beta';
282
- choices = ['grok-beta'];
283
- break;
284
- }
285
-
286
- choices.push(new inquirer.Separator());
287
- choices.push({ name: 'Custom (Enter manually)', value: 'custom' });
288
-
289
- const { model } = await inquirer.prompt([
290
- {
291
- type: 'list',
292
- name: 'model',
293
- message: `Select Model for ${provider}:`,
294
- default: currentModel,
295
- choices: choices
296
- }
297
- ]);
298
-
299
- let selectedModel = model;
300
- if (model === 'custom') {
301
- const { customModel } = await inquirer.prompt([
398
+ console.log(`✅ Active Provider set to: ${newProvider}`);
399
+
400
+ // 2. Select Model for that Provider
401
+ if (newProvider === 'gemini') {
402
+ const { model } = await inquirer.prompt([
302
403
  {
303
- type: 'input',
304
- name: 'customModel',
305
- message: 'Enter model name:',
306
- validate: input => input.length > 0
404
+ type: 'list',
405
+ name: 'model',
406
+ message: 'Select Gemini Model:',
407
+ default: config.geminiModel,
408
+ choices: [
409
+ { name: 'Gemini 3 Pro Preview', value: 'gemini-3-pro-preview' },
410
+ { name: 'Gemini 3 Flash Preview', value: 'gemini-3-flash-preview' },
411
+ { name: 'Gemini 2.5 Pro', value: 'gemini-2.5-pro' },
412
+ { name: 'Gemini 2.5 Flash', value: 'gemini-2.5-flash' },
413
+ { name: 'Gemini 2.5 Flash Lite', value: 'gemini-2.5-flash-lite' }
414
+ ]
307
415
  }
308
416
  ]);
309
- selectedModel = customModel;
417
+ config.geminiModel = model;
418
+ } else if (newProvider === 'openai') {
419
+ const { model } = await inquirer.prompt([
420
+ {
421
+ type: 'list',
422
+ name: 'model',
423
+ message: 'Select OpenAI Model:',
424
+ default: config.openaiModel,
425
+ choices: [
426
+ { name: 'GPT-5.3 Codex (Best for Code)', value: 'gpt-5.3-codex' },
427
+ { name: 'GPT-5.2 Pro', value: 'gpt-5.2-pro' },
428
+ { name: 'GPT-5.2', value: 'gpt-5.2' },
429
+ { name: 'GPT-4.1 Nano', value: 'gpt-4.1-nano' },
430
+ { name: 'OpenAI o3-mini', value: 'o3-mini' }
431
+ ]
432
+ }
433
+ ]);
434
+ config.openaiModel = model;
435
+ } else if (newProvider === 'anthropic') {
436
+ const { model } = await inquirer.prompt([
437
+ {
438
+ type: 'list',
439
+ name: 'model',
440
+ message: 'Select Claude Model:',
441
+ default: config.anthropicModel,
442
+ choices: [
443
+ { name: 'Claude Opus 4.6', value: 'claude-opus-4.6' },
444
+ { name: 'Claude Sonnet 4.5', value: 'claude-sonnet-4.5' },
445
+ { name: 'Claude Haiku 4.5', value: 'claude-haiku-4.5' }
446
+ ]
447
+ }
448
+ ]);
449
+ config.anthropicModel = model;
450
+ } else if (newProvider === 'groq') {
451
+ const { model } = await inquirer.prompt([
452
+ {
453
+ type: 'list',
454
+ name: 'model',
455
+ message: 'Select Groq Model:',
456
+ default: config.groqModel,
457
+ choices: [
458
+ { name: 'Llama 3.3 70B Versatile', value: 'llama-3.3-70b-versatile' },
459
+ { name: 'Llama 3.1 8B Instant', value: 'llama-3.1-8b-instant' },
460
+ { name: 'OpenAI GPT-OSS 120B', value: 'openai/gpt-oss-120b' },
461
+ { name: 'Mixtral 8x7B', value: 'mixtral-8x7b-32768' }
462
+ ]
463
+ }
464
+ ]);
465
+ config.groqModel = model;
466
+ } else if (newProvider === 'deepseek') {
467
+ const { model } = await inquirer.prompt([
468
+ {
469
+ type: 'list',
470
+ name: 'model',
471
+ message: 'Select DeepSeek Model:',
472
+ default: config.deepseekModel,
473
+ choices: [
474
+ { name: 'DeepSeek V3 (General)', value: 'deepseek-chat' },
475
+ { name: 'DeepSeek R1 (Reasoning)', value: 'deepseek-reasoner' },
476
+ { name: 'DeepSeek Coder V3', value: 'deepseek-coder' }
477
+ ]
478
+ }
479
+ ]);
480
+ config.deepseekModel = model;
481
+ } else if (newProvider === 'grok') {
482
+ const { model } = await inquirer.prompt([
483
+ {
484
+ type: 'list',
485
+ name: 'model',
486
+ message: 'Select Grok Model:',
487
+ default: config.grokModel,
488
+ choices: [
489
+ { name: 'Grok 4', value: 'grok-4' },
490
+ { name: 'Grok 4.1 Fast', value: 'grok-4-1-fast' },
491
+ { name: 'Grok 3', value: 'grok-3' },
492
+ { name: 'Grok 2', value: 'grok-2-1212' }
493
+ ]
494
+ }
495
+ ]);
496
+ config.grokModel = model;
310
497
  }
311
498
 
312
- // Save model to correct config field
313
- config[`${provider}Model`] = selectedModel;
314
499
  saveConfig(config);
315
- console.log(`✅ Model for ${provider} updated to: ${selectedModel}`);
316
-
317
- // Check if key is set
318
- const keyField = `${provider}ApiKey`;
319
- if (!config[keyField] && !process.env[keyField.toUpperCase().replace(/([A-Z])/g, '_$1').toUpperCase()]) {
320
- console.log(`⚠️ Warning: API Key for ${provider} is not set.`);
321
- const { setKey } = await inquirer.prompt([{ type: 'confirm', name: 'setKey', message: 'Set API Key now?', default: true }]);
322
- if (setKey) await manageApiKeys(provider);
323
- }
500
+ console.log(`✅ Model set to: ${config[newProvider + 'Model'] || config.geminiModel}`);
501
+ await promptContinue();
324
502
  }
325
503
 
504
+
326
505
  async function manageApiKeys(targetProvider = null) {
327
506
  const config = loadConfig();
328
-
329
507
  let provider = targetProvider;
508
+
330
509
  if (!provider) {
331
510
  const result = await inquirer.prompt([
332
511
  {
333
512
  type: 'list',
334
513
  name: 'provider',
335
- message: 'Select Provider to manage key:',
514
+ message: 'Select Provider to set API Key:',
336
515
  choices: [
337
516
  { name: 'Google Gemini', value: 'gemini' },
338
517
  { name: 'OpenAI', value: 'openai' },
@@ -445,356 +624,123 @@ async function configureWebSearch() {
445
624
  config.braveApiKey = key.trim();
446
625
  saveConfig(config);
447
626
  console.log('✅ Brave API Key set.');
448
- } else if (config.braveApiKey && key === '') {
449
- const { remove } = await inquirer.prompt([{ type: 'confirm', name: 'remove', message: 'Remove existing key?', default: false}]);
450
- if (remove) {
451
- delete config.braveApiKey;
452
- saveConfig(config);
453
- console.log('✅ Brave API Key removed.');
454
- }
627
+ } else if (key === '') {
628
+ // do nothing
629
+ console.log('No change.');
630
+ } else {
631
+ // e.g. space to clear? For now TUI doesn't support clear easily.
455
632
  }
456
-
457
633
  await promptContinue();
458
634
  }
459
635
 
460
- async function changeApiUrl() {
461
- const config = loadConfig();
462
- const currentUrl = config.geminiApiUrl || 'Default (Google)';
463
636
 
464
- const { url } = await inquirer.prompt([
465
- {
466
- type: 'input',
467
- name: 'url',
468
- message: `Enter Custom API Base URL (Current: ${currentUrl}):`,
469
- suffix: '\n(Leave empty to reset to default Google API)',
470
- }
471
- ]);
472
-
473
- if (!url || url.trim() === '') {
474
- delete config.geminiApiUrl;
475
- console.log('✅ Reset to default Google API URL.');
476
- } else {
477
- // Basic validation
478
- if (!url.startsWith('http')) {
479
- console.log('⚠️ URL should start with http:// or https://');
480
- }
481
- config.geminiApiUrl = url.trim();
482
- console.log(`✅ API URL updated to: ${config.geminiApiUrl}`);
483
- }
484
- saveConfig(config);
485
- console.log('Note: Restart daemon to apply changes.');
486
- }
637
+ // --- Main Logic ---
487
638
 
488
- async function changeGatewayKey() {
639
+ function switchSession() {
640
+ const sessions = fs.readdirSync(SESSIONS_DIR).filter(f => fs.statSync(path.join(SESSIONS_DIR, f)).isDirectory());
489
641
  const config = loadConfig();
490
- const currentKey = config.geminiGatewayKey ? '********' : 'Not Set';
642
+ const activeSession = config.activeSession || 'default';
491
643
 
492
- const { key } = await inquirer.prompt([
644
+ return inquirer.prompt([
493
645
  {
494
- type: 'password',
495
- name: 'key',
496
- message: `Enter Gateway API Key (Current: ${currentKey}):`,
497
- suffix: '\n(Leave empty to remove key)',
498
- mask: '*'
646
+ type: 'list',
647
+ name: 'session',
648
+ message: 'Select Session:',
649
+ choices: sessions.map(s => ({ name: s === activeSession ? `${s} (Active)` : s, value: s })).concat([{ name: 'Back', value: 'back' }])
499
650
  }
500
- ]);
501
-
502
- if (!key || key.trim() === '') {
503
- delete config.geminiGatewayKey;
504
- console.log('✅ Gateway Key removed.');
505
- } else {
506
- config.geminiGatewayKey = key.trim();
507
- console.log('✅ Gateway Key updated.');
508
- }
509
- saveConfig(config);
510
- console.log('Note: Restart daemon to apply changes.');
651
+ ]).then(answer => {
652
+ if (answer.session === 'back') return;
653
+ config.activeSession = answer.session;
654
+ saveConfig(config);
655
+ console.log(`Switched to session: ${answer.session}`);
656
+ });
511
657
  }
512
658
 
513
- async function manageSessions() {
659
+ async function createSession() {
514
660
  const config = loadConfig();
515
- const activeSession = config.activeSession || 'default';
516
- const sessions = fs.existsSync(SESSIONS_DIR)
517
- ? fs.readdirSync(SESSIONS_DIR).filter(file => fs.statSync(path.join(SESSIONS_DIR, file)).isDirectory())
518
- : ['default'];
519
-
520
- const { action } = await inquirer.prompt([
661
+ const { name } = await inquirer.prompt([
521
662
  {
522
- type: 'list',
523
- name: 'action',
524
- message: `Manage Sessions (Active: ${activeSession})`,
525
- choices: [
526
- { name: 'Switch Session', value: 'switch' },
527
- { name: 'Create New Session', value: 'create' },
528
- { name: 'Delete Session', value: 'delete' },
529
- { name: 'Back', value: 'back' }
530
- ]
663
+ type: 'input',
664
+ name: 'name',
665
+ message: 'Enter new session name (alphanumeric):',
666
+ validate: input => /^[a-zA-Z0-9_-]+$/.test(input) ? true : 'Invalid name.'
531
667
  }
532
668
  ]);
533
669
 
534
- if (action === 'back') return;
535
-
536
- if (action === 'switch') {
537
- const { session } = await inquirer.prompt([
538
- {
539
- type: 'list',
540
- name: 'session',
541
- message: 'Select session to switch to:',
542
- choices: sessions.map(s => s === activeSession ? { name: `${s} (active)`, value: s } : s)
543
- }
670
+ const sessionPath = path.join(SESSIONS_DIR, name);
671
+ if (fs.existsSync(sessionPath)) {
672
+ console.log('❌ Session already exists.');
673
+ } else {
674
+ fs.mkdirSync(sessionPath, { recursive: true });
675
+ console.log(`✅ Session '${name}' created.`);
676
+ const { switchTo } = await inquirer.prompt([
677
+ { type: 'confirm', name: 'switchTo', message: 'Switch to this session now?', default: true }
544
678
  ]);
545
-
546
- if (session !== activeSession) {
547
- config.activeSession = session;
679
+ if (switchTo) {
680
+ config.activeSession = name;
548
681
  saveConfig(config);
549
- console.log(`✅ Switched to session: ${session}`);
550
- } else {
551
- console.log('Already on this session.');
552
- }
553
- }
554
-
555
- if (action === 'create') {
556
- const { name } = await inquirer.prompt([
557
- {
558
- type: 'input',
559
- name: 'name',
560
- message: 'Enter new session name (alphanumeric):',
561
- validate: input => /^[a-zA-Z0-9_-]+$/.test(input) ? true : 'Invalid name.'
562
- }
563
- ]);
564
-
565
- const sessionPath = path.join(SESSIONS_DIR, name);
566
- if (fs.existsSync(sessionPath)) {
567
- console.log('❌ Session already exists.');
568
- } else {
569
- fs.mkdirSync(sessionPath, { recursive: true });
570
- console.log(`✅ Session '${name}' created.`);
571
- const { switchTo } = await inquirer.prompt([
572
- { type: 'confirm', name: 'switchTo', message: 'Switch to this session now?', default: true }
573
- ]);
574
- if (switchTo) {
575
- config.activeSession = name;
576
- saveConfig(config);
577
- }
578
682
  }
579
683
  }
580
-
581
- if (action === 'delete') {
582
- const { session } = await inquirer.prompt([
583
- {
584
- type: 'list',
585
- name: 'session',
586
- message: 'Select session to delete:',
587
- choices: sessions.filter(s => s !== 'default' && s !== activeSession)
588
- }
589
- ]);
590
- if (session) {
591
- const { confirm } = await inquirer.prompt([
592
- { type: 'confirm', name: 'confirm', message: `Delete '${session}' permanently?`, default: false }
593
- ]);
594
- if (confirm) {
595
- fs.rmSync(path.join(SESSIONS_DIR, session), { recursive: true, force: true });
596
- console.log(`🗑️ Session deleted.`);
597
- }
598
- }
599
- }
600
- return manageSessions();
601
684
  }
602
685
 
603
- async function manageSkills() {
604
- const config = loadConfig();
605
- const activeSkill = config.activeSkill || 'None';
606
- const skills = getSkills();
607
-
608
- const { action } = await inquirer.prompt([
686
+ async function deleteSession() {
687
+ const config = loadConfig();
688
+ const activeSession = config.activeSession || 'default';
689
+ const sessions = fs.readdirSync(SESSIONS_DIR).filter(f => fs.statSync(path.join(SESSIONS_DIR, f)).isDirectory());
690
+
691
+ const { session } = await inquirer.prompt([
609
692
  {
610
693
  type: 'list',
611
- name: 'action',
612
- message: `Manage Skills (Active: ${activeSkill})`,
613
- choices: [
614
- { name: 'Acivate/Deactivate Skill', value: 'activate' },
615
- { name: 'Install Skill (from URL)', value: 'install' },
616
- { name: 'Create New Skill', value: 'create' },
617
- { name: 'List Installed Skills', value: 'list' },
618
- { name: 'Delete Skill', value: 'delete' },
619
- { name: 'Back', value: 'back' }
620
- ]
694
+ name: 'session',
695
+ message: 'Select session to delete:',
696
+ choices: sessions.filter(s => s !== 'default' && s !== activeSession).concat([{ name: 'Cancel', value: 'cancel' }])
621
697
  }
622
698
  ]);
623
699
 
624
- if (action === 'back') return;
625
-
626
- if (action === 'activate') {
627
- const choices = [
628
- { name: 'None (Deactivate)', value: null },
629
- new inquirer.Separator(),
630
- ...skills.map(s => ({ name: s.name, value: s.name }))
631
- ];
632
- const { skill } = await inquirer.prompt([
633
- {
634
- type: 'list',
635
- name: 'skill',
636
- message: 'Select skill to activate:',
637
- choices: choices,
638
- default: activeSkill === 'None' ? null : activeSkill
639
- }
640
- ]);
641
-
642
- config.activeSkill = skill;
643
- saveConfig(config);
644
- console.log(skill ? `✅ Activated skill: ${skill}` : `✅ Skill deactivated.`);
645
- }
646
-
647
- if (action === 'install') {
648
- const { url } = await inquirer.prompt([
649
- { type: 'input', name: 'url', message: 'Enter Skill JSON URL:' }
650
- ]);
651
- try {
652
- console.log('Downloading...');
653
- const skill = await installSkillFromUrl(url);
654
- console.log(`✅ Installed skill: ${skill.name}`);
655
- const { activate } = await inquirer.prompt([
656
- { type: 'confirm', name: 'activate', message: 'Activate this skill now?', default: true }
657
- ]);
658
- if (activate) {
659
- config.activeSkill = skill.name;
660
- saveConfig(config);
661
- }
662
- } catch(e) {
663
- console.log(`❌ Failed: ${e.message}`);
664
- }
665
- }
666
-
667
- if (action === 'create') {
668
- // Simple creation wizard
669
- const answers = await inquirer.prompt([
670
- { type: 'input', name: 'name', message: 'Skill Name:', validate: i => /^[a-z0-9_-]+$/.test(i) },
671
- { type: 'input', name: 'description', message: 'Description:' },
672
- { type: 'editor', name: 'instruction', message: 'System Instruction (Persona):' }
673
- ]);
674
-
675
- const skill = {
676
- name: answers.name,
677
- version: "1.0.0",
678
- description: answers.description,
679
- author: "local",
680
- systemInstruction: answers.instruction,
681
- examples: []
682
- };
683
-
684
- if (!fs.existsSync(SKILLS_DIR)) fs.mkdirSync(SKILLS_DIR, { recursive: true });
685
- fs.writeFileSync(path.join(SKILLS_DIR, `${skill.name}.json`), JSON.stringify(skill, null, 2));
686
- console.log(`✅ Skill '${skill.name}' created.`);
687
- }
688
-
689
- if (action === 'list') {
690
- console.log('\nInstalled Skills:');
691
- skills.forEach(s => {
692
- console.log(`- ${s.name}: ${s.description} (${s.active ? 'ACTIVE' : ''})`);
693
- });
694
- await promptContinue();
695
- }
700
+ if (session === 'cancel') return;
696
701
 
697
- if (action === 'delete') {
698
- const { skill } = await inquirer.prompt([
699
- {
700
- type: 'list',
701
- name: 'skill',
702
- message: 'Select skill to delete:',
703
- choices: skills.map(s => ({ name: s.name, value: s.name}))
704
- }
705
- ]);
706
- if (skill) {
707
- const { confirm } = await inquirer.prompt([
708
- { type: 'confirm', name: 'confirm', message: `Delete '${skill}'?`, default: false }
709
- ]);
710
- if (confirm) {
711
- fs.unlinkSync(path.join(SKILLS_DIR, `${skill}.json`));
712
- if (config.activeSkill === skill) {
713
- delete config.activeSkill;
714
- saveConfig(config);
715
- }
716
- console.log(`🗑️ Skill deleted.`);
717
- }
718
- }
719
- }
720
-
721
- return manageSkills();
722
- }
723
-
724
- async function showLogsMenu() {
725
- const { logType } = await inquirer.prompt([
726
- {
727
- type: 'list',
728
- name: 'logType',
729
- message: 'Which logs to view?',
730
- choices: [
731
- { name: 'Telegram', value: 'telegram' },
732
- { name: 'Queue Processor', value: 'queue' },
733
- { name: 'Daemon', value: 'daemon' },
734
- { name: 'Back', value: 'back' }
735
- ]
736
- }
702
+ // confirm
703
+ const { confirm } = await inquirer.prompt([
704
+ { type: 'confirm', name: 'confirm', message: `Are you sure you want to delete '${session}' and all its history?`, default: false }
737
705
  ]);
738
706
 
739
- if (logType === 'back') return;
740
-
741
- console.log('Press Ctrl+C to exit logs.');
742
- await runScript(['logs', logType]);
743
- }
744
-
745
- async function performUpdate() {
746
- console.log('🔄 Checking for updates and installing latest version...');
747
- console.log('📦 Running: npm install -g @thelapyae/geniclaw@latest');
748
-
749
- try {
750
- execSync('npm install -g @thelapyae/geniclaw@latest', { stdio: 'inherit' });
751
- console.log('\n✅ Update completed! Please restart geniclaw.');
752
- process.exit(0);
753
- } catch (error) {
754
- console.error('\n❌ Update failed.');
755
- console.error('Error details:', error.message);
756
- console.log('Try running with sudo: sudo geniclaw update');
757
-
758
- const { trySudo } = await inquirer.prompt([
759
- { type: 'confirm', name: 'trySudo', message: 'Try again with sudo?', default: false }
760
- ]);
761
-
762
- if (trySudo) {
763
- try {
764
- execSync('sudo npm install -g @thelapyae/geniclaw@latest', { stdio: 'inherit' });
765
- console.log('\n✅ Update completed! Please restart geniclaw.');
766
- process.exit(0);
767
- } catch (e) {
768
- console.error('\n❌ Sudo update also failed.');
769
- }
770
- }
707
+ if (confirm) {
708
+ fs.rmSync(path.join(SESSIONS_DIR, session), { recursive: true, force: true });
709
+ console.log(`🗑️ Session '${session}' deleted.`);
771
710
  }
772
711
  }
773
712
 
774
713
 
775
- // CLI Configuration
776
- const packageJson = require('../package.json');
714
+ // CLI Args Handling
777
715
  program
778
- .version(packageJson.version)
779
- .description('GeniClaw Management CLI')
780
- .arguments('[command]')
781
- .action(async (command) => {
716
+ .version(require('../package.json').version)
717
+ .argument('[command]', 'Command to run (start, stop, logs, etc)')
718
+ .argument('[args...]', 'Arguments for the command')
719
+ .action(async (command, args) => {
782
720
  if (!command) {
783
- // No args -> TUI Mode
784
721
  await showMenu();
785
722
  } else {
786
- if (command === 'model') {
787
- await changeModel();
788
- } else if (command === 'sessions') {
789
- await manageSessions();
790
- } else if (command === 'skills') {
791
- await manageSkills();
792
- } else if (command === 'update') {
793
- await performUpdate();
794
- } else {
795
- const args = process.argv.slice(2);
796
- await runScript(args);
797
- }
723
+ if (command === 'provider') {
724
+ await selectProviderAndModel();
725
+ } else if (command === 'keys') {
726
+ await manageApiKeys();
727
+ } else if (command === 'routing') {
728
+ await manageTaskRouting();
729
+ } else if (command === 'search') {
730
+ await configureWebSearch();
731
+ } else if (command === 'services') {
732
+ // Not implemented directly yet, maybe mapping to start/stop?
733
+ // Just fall through to script for now
734
+ await runScript(args);
735
+ } else if (command === 'doctor') {
736
+ await runDoctor();
737
+ } else if (command === 'update') {
738
+ await performUpdate();
739
+ } else {
740
+ // Pass through to shell script
741
+ const allArgs = [command, ...args];
742
+ await runScript(allArgs);
743
+ }
798
744
  }
799
745
  });
800
746
 
@@ -29,7 +29,7 @@ class ConfigManager {
29
29
  // Default config
30
30
  return {
31
31
  onboardingState: 'INIT',
32
- geminiModel: process.env.GEMINI_MODEL || 'gemini-1.5-flash',
32
+ geminiModel: process.env.GEMINI_MODEL || 'gemini-2.5-flash',
33
33
  geminiApiUrl: process.env.GEMINI_API_URL || undefined,
34
34
  geminiGatewayKey: process.env.GEMINI_GATEWAY_KEY || undefined,
35
35
  activeSession: 'default',
@@ -39,12 +39,12 @@ class ConfigManager {
39
39
  heartbeat: 'gemini',
40
40
  cron: 'gemini'
41
41
  },
42
- // Default Models
43
- openaiModel: 'gpt-4o',
44
- anthropicModel: 'claude-3-5-sonnet-20240620',
45
- groqModel: 'llama3-70b-8192',
42
+ // Default Models (2026 Updated)
43
+ openaiModel: 'gpt-5.2',
44
+ anthropicModel: 'claude-opus-4.6',
45
+ groqModel: 'llama-3.3-70b-versatile',
46
46
  deepseekModel: 'deepseek-chat',
47
- grokModel: 'grok-beta'
47
+ grokModel: 'grok-4'
48
48
  };
49
49
  }
50
50
  static save(config) {
@@ -1 +1 @@
1
- {"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,eAAe;AACf,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAC3C,QAAA,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACnI,QAAA,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,yBAAiB,EAAE,aAAa,CAAC,CAAC;AA0CvE,MAAa,aAAa;IACd,MAAM,CAAC,YAAY,GAAqB,IAAI,CAAC;IAErD,MAAM,CAAC,IAAI;QACP,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC;QAEhD,IAAI,YAAE,CAAC,UAAU,CAAC,mBAAW,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,YAAE,CAAC,YAAY,CAAC,mBAAW,EAAE,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrC,OAAO,IAAI,CAAC,YAAa,CAAC;YAC9B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,OAAO;YACH,eAAe,EAAE,MAAM;YACvB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,kBAAkB;YAC3D,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,SAAS;YACrD,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS;YAC7D,aAAa,EAAE,SAAS;YACxB,cAAc,EAAE,QAAQ;YACxB,eAAe,EAAE;gBACb,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,QAAQ;gBACnB,IAAI,EAAE,QAAQ;aACjB;YACD,iBAAiB;YACjB,WAAW,EAAE,QAAQ;YACrB,cAAc,EAAE,4BAA4B;YAC5C,SAAS,EAAE,iBAAiB;YAC5B,aAAa,EAAE,eAAe;YAC9B,SAAS,EAAE,WAAW;SACzB,CAAC;IACN,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,MAAiB;QACzB,IAAI,CAAC;YACD,0BAA0B;YAC1B,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,yBAAiB,CAAC,EAAE,CAAC;gBACpC,YAAE,CAAC,SAAS,CAAC,yBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;YAED,YAAE,CAAC,aAAa,CAAC,mBAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC/B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,OAA2B;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;;AAxDL,sCAyDC"}
1
+ {"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../src/config-manager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AACxB,4CAAoB;AAEpB,eAAe;AACf,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;AAC3C,QAAA,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACnI,QAAA,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,yBAAiB,EAAE,aAAa,CAAC,CAAC;AA0CvE,MAAa,aAAa;IACd,MAAM,CAAC,YAAY,GAAqB,IAAI,CAAC;IAErD,MAAM,CAAC,IAAI;QACP,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC;QAEhD,IAAI,YAAE,CAAC,UAAU,CAAC,mBAAW,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACD,MAAM,IAAI,GAAG,YAAE,CAAC,YAAY,CAAC,mBAAW,EAAE,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrC,OAAO,IAAI,CAAC,YAAa,CAAC;YAC9B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,OAAO;YACH,eAAe,EAAE,MAAM;YACvB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,kBAAkB;YAC3D,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,SAAS;YACrD,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS;YAC7D,aAAa,EAAE,SAAS;YACxB,cAAc,EAAE,QAAQ;YACxB,eAAe,EAAE;gBACb,IAAI,EAAE,QAAQ;gBACd,SAAS,EAAE,QAAQ;gBACnB,IAAI,EAAE,QAAQ;aACjB;YACD,gCAAgC;YAChC,WAAW,EAAE,SAAS;YACtB,cAAc,EAAE,iBAAiB;YACjC,SAAS,EAAE,yBAAyB;YACpC,aAAa,EAAE,eAAe;YAC9B,SAAS,EAAE,QAAQ;SACtB,CAAC;IACN,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,MAAiB;QACzB,IAAI,CAAC;YACD,0BAA0B;YAC1B,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,yBAAiB,CAAC,EAAE,CAAC;gBACpC,YAAE,CAAC,SAAS,CAAC,yBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,CAAC;YAED,YAAE,CAAC,aAAa,CAAC,mBAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC/B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,0BAA2B,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,OAA2B;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;;AAxDL,sCAyDC"}
@@ -0,0 +1,2 @@
1
+ export declare function runDoctor(): Promise<void>;
2
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AA6BA,wBAAsB,SAAS,kBAsG9B"}
package/dist/doctor.js ADDED
@@ -0,0 +1,131 @@
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.runDoctor = runDoctor;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const config_manager_1 = require("./config-manager");
11
+ const openai_1 = require("./providers/openai");
12
+ const gemini_1 = require("./providers/gemini");
13
+ const anthropic_1 = require("./providers/anthropic");
14
+ // Colors for output
15
+ const colors = {
16
+ reset: "\x1b[0m",
17
+ red: "\x1b[31m",
18
+ green: "\x1b[32m",
19
+ yellow: "\x1b[33m",
20
+ blue: "\x1b[34m",
21
+ cyan: "\x1b[36m"
22
+ };
23
+ function log(status, message, detail) {
24
+ let color = colors.green;
25
+ if (status === 'WARN')
26
+ color = colors.yellow;
27
+ if (status === 'FAIL')
28
+ color = colors.red;
29
+ console.log(`${color}[${status}]${colors.reset} ${message}`);
30
+ if (detail) {
31
+ console.log(` ${colors.cyan}↳ ${detail}${colors.reset}`);
32
+ }
33
+ }
34
+ async function runDoctor() {
35
+ console.log(`\n${colors.blue}🦁 GeniClaw Doctor - Diagnostic Tool${colors.reset}\n`);
36
+ const GENICLAW_WORK_DIR = process.env.GENICLAW_WORK_DIR || path_1.default.join(os_1.default.homedir(), '.geniclaw');
37
+ const CONFIG_FILE = path_1.default.join(GENICLAW_WORK_DIR, 'config.json');
38
+ // 1. Environment Check
39
+ const nodeVersion = process.version;
40
+ log('OK', `Node.js Version: ${nodeVersion}`);
41
+ log('OK', `Work Directory: ${GENICLAW_WORK_DIR}`);
42
+ // 2. Directory Permissions
43
+ const dirsToCheck = [
44
+ GENICLAW_WORK_DIR,
45
+ path_1.default.join(GENICLAW_WORK_DIR, 'queue/incoming'),
46
+ path_1.default.join(GENICLAW_WORK_DIR, 'queue/outgoing'),
47
+ path_1.default.join(GENICLAW_WORK_DIR, 'logs'),
48
+ path_1.default.join(GENICLAW_WORK_DIR, 'sessions')
49
+ ];
50
+ let dirsOk = true;
51
+ for (const dir of dirsToCheck) {
52
+ try {
53
+ if (!fs_1.default.existsSync(dir)) {
54
+ log('WARN', `Directory missing: ${dir}`, 'Attempting to create...');
55
+ fs_1.default.mkdirSync(dir, { recursive: true });
56
+ log('OK', `Created directory: ${dir}`);
57
+ }
58
+ fs_1.default.accessSync(dir, fs_1.default.constants.W_OK);
59
+ }
60
+ catch (e) {
61
+ log('FAIL', `Permission Issue: ${dir}`, e.message);
62
+ dirsOk = false;
63
+ }
64
+ }
65
+ if (dirsOk)
66
+ log('OK', 'Directory Permissions');
67
+ // 3. Configuration Check
68
+ if (!fs_1.default.existsSync(CONFIG_FILE)) {
69
+ log('FAIL', 'Config file missing', 'Run `geniclaw setup` or use the TUI to config.');
70
+ return;
71
+ }
72
+ let config;
73
+ try {
74
+ config = config_manager_1.ConfigManager.load();
75
+ log('OK', 'Config file loaded valid JSON');
76
+ }
77
+ catch (e) {
78
+ log('FAIL', 'Config file corrupt', e.message);
79
+ return;
80
+ }
81
+ // 4. Provider Check
82
+ const activeProvider = config.activeProvider || 'gemini';
83
+ log('OK', `Active Provider: ${activeProvider.toUpperCase()}`);
84
+ const keyField = `${activeProvider}ApiKey`;
85
+ // @ts-ignore
86
+ const apiKey = config[keyField] || process.env[keyField.toUpperCase().replace(/([A-Z])/g, '_$1').toUpperCase()] || (activeProvider === 'gemini' && process.env.GEMINI_API_KEY);
87
+ if (!apiKey) {
88
+ log('FAIL', `Missing API Key for ${activeProvider}`, `Please set ${keyField} in TUI.`);
89
+ }
90
+ else {
91
+ log('OK', `API Key present for ${activeProvider}`);
92
+ // 5. Connectivity Test
93
+ console.log(`\n${colors.yellow}Testing Connectivity...${colors.reset}`);
94
+ try {
95
+ let response = "";
96
+ const testMessage = "Hello, are you online? Reply with 'Yes'.";
97
+ const history = []; // Empty history
98
+ if (activeProvider === 'gemini') {
99
+ const provider = new gemini_1.GeminiProvider(config);
100
+ response = await provider.generateResponse(testMessage, history);
101
+ }
102
+ else if (activeProvider === 'anthropic') {
103
+ const provider = new anthropic_1.AnthropicProvider(config);
104
+ response = await provider.generateResponse(testMessage, history);
105
+ }
106
+ else {
107
+ // OpenAI Compatible
108
+ const provider = new openai_1.OpenAIProvider(config, activeProvider);
109
+ response = await provider.generateResponse(testMessage, history);
110
+ }
111
+ if (response && response.length > 0) {
112
+ log('OK', 'API Connection Successful', `Response: ${response.trim()}`);
113
+ }
114
+ else {
115
+ log('WARN', 'API returned empty response');
116
+ }
117
+ }
118
+ catch (e) {
119
+ log('FAIL', 'API Connection Failed', e.message);
120
+ }
121
+ }
122
+ // 6. Web Search Check
123
+ if (config.braveApiKey) {
124
+ log('OK', 'Brave Search Key present');
125
+ }
126
+ else {
127
+ log('WARN', 'Web Search not configured (Brave API Key missing)');
128
+ }
129
+ console.log(`\n${colors.blue}Diagnostic Complete.${colors.reset}\n`);
130
+ }
131
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thelapyae/geniclaw",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Geniclaw - Gemini + Telegram CLI Assistant",
5
5
  "main": "dist/telegram-client.js",
6
6
  "bin": {