@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 +405 -459
- package/dist/config-manager.js +6 -6
- package/dist/config-manager.js.map +1 -1
- package/dist/doctor.d.ts +2 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +131 -0
- package/dist/doctor.js.map +1 -0
- package/package.json +1 -1
package/bin/geniclaw.js
CHANGED
|
@@ -1,82 +1,139 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const {
|
|
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 {
|
|
6
|
+
const { spawn, execSync, fork } = require('child_process');
|
|
7
7
|
const inquirer = require('inquirer');
|
|
8
|
-
const
|
|
8
|
+
const { ConfigManager } = require('../dist/config-manager');
|
|
9
9
|
|
|
10
|
-
//
|
|
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
|
-
|
|
22
|
-
stdio: 'inherit'
|
|
23
|
-
|
|
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
|
-
//
|
|
32
|
+
// Config Helpers
|
|
34
33
|
function loadConfig() {
|
|
35
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
//
|
|
54
|
-
function
|
|
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 {
|
|
60
|
-
|
|
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
|
-
|
|
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.
|
|
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-
|
|
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
|
|
100
|
-
|
|
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
|
|
164
|
+
{ type: 'confirm', name: 'runSetup', message: 'Would you like to configure keys now?', default: true }
|
|
103
165
|
]);
|
|
104
166
|
|
|
105
167
|
if (runSetup) {
|
|
106
|
-
|
|
107
|
-
|
|
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: '📂
|
|
141
|
-
{ name: '
|
|
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 === '
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return showMenu();
|
|
292
|
+
if (action === 'proxy') {
|
|
293
|
+
await setCustomUrl();
|
|
294
|
+
return showMenu();
|
|
191
295
|
}
|
|
192
296
|
|
|
193
297
|
if (action === 'gatewayKey') {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return showMenu();
|
|
298
|
+
await setGatewayKey();
|
|
299
|
+
return showMenu();
|
|
197
300
|
}
|
|
301
|
+
}
|
|
198
302
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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 (
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
221
|
-
|
|
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: '
|
|
224
|
-
name: '
|
|
225
|
-
message:
|
|
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
|
-
|
|
374
|
+
let provider = config.activeProvider || 'gemini';
|
|
233
375
|
|
|
234
|
-
|
|
376
|
+
// 1. Select Provider
|
|
377
|
+
const { newProvider } = await inquirer.prompt([
|
|
235
378
|
{
|
|
236
379
|
type: 'list',
|
|
237
|
-
name: '
|
|
380
|
+
name: 'newProvider',
|
|
238
381
|
message: 'Select Active AI Provider:',
|
|
239
|
-
default:
|
|
382
|
+
default: provider,
|
|
240
383
|
choices: [
|
|
241
384
|
{ name: 'Google Gemini', value: 'gemini' },
|
|
242
|
-
{ name: 'OpenAI
|
|
243
|
-
{ name: 'Anthropic
|
|
244
|
-
{ name: '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
|
-
|
|
395
|
+
if (newProvider === 'back') return;
|
|
396
|
+
config.activeProvider = newProvider;
|
|
252
397
|
saveConfig(config);
|
|
253
|
-
console.log(`✅ Active Provider set to: ${
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
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: '
|
|
304
|
-
name: '
|
|
305
|
-
message: '
|
|
306
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
642
|
+
const activeSession = config.activeSession || 'default';
|
|
491
643
|
|
|
492
|
-
|
|
644
|
+
return inquirer.prompt([
|
|
493
645
|
{
|
|
494
|
-
type: '
|
|
495
|
-
name: '
|
|
496
|
-
message:
|
|
497
|
-
|
|
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
|
-
|
|
503
|
-
|
|
504
|
-
console.log(
|
|
505
|
-
}
|
|
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
|
|
659
|
+
async function createSession() {
|
|
514
660
|
const config = loadConfig();
|
|
515
|
-
const
|
|
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: '
|
|
523
|
-
name: '
|
|
524
|
-
message:
|
|
525
|
-
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
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
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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: '
|
|
612
|
-
message:
|
|
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 (
|
|
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
|
-
|
|
698
|
-
|
|
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 (
|
|
740
|
-
|
|
741
|
-
|
|
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
|
|
776
|
-
const packageJson = require('../package.json');
|
|
714
|
+
// CLI Args Handling
|
|
777
715
|
program
|
|
778
|
-
.version(
|
|
779
|
-
.
|
|
780
|
-
.
|
|
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
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
|
package/dist/config-manager.js
CHANGED
|
@@ -29,7 +29,7 @@ class ConfigManager {
|
|
|
29
29
|
// Default config
|
|
30
30
|
return {
|
|
31
31
|
onboardingState: 'INIT',
|
|
32
|
-
geminiModel: process.env.GEMINI_MODEL || 'gemini-
|
|
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-
|
|
44
|
-
anthropicModel: 'claude-
|
|
45
|
-
groqModel: '
|
|
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-
|
|
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,
|
|
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"}
|
package/dist/doctor.d.ts
ADDED
|
@@ -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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"}
|