limbo-ai 1.6.0 → 1.6.1
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/cli.js +52 -82
- package/package.json +4 -1
package/cli.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// cli.js — Limbo CLI
|
|
3
3
|
// Orchestrates the Docker-based Limbo runtime.
|
|
4
|
-
// Zero npm dependencies — pure Node.js stdlib.
|
|
5
4
|
'use strict';
|
|
6
5
|
|
|
7
6
|
const { execSync, spawnSync } = require('child_process');
|
|
@@ -14,6 +13,7 @@ const readline = require('readline');
|
|
|
14
13
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
15
14
|
|
|
16
15
|
const LIMBO_DIR = path.join(os.homedir(), '.limbo');
|
|
16
|
+
const VAULT_DIR = path.join(LIMBO_DIR, 'vault');
|
|
17
17
|
const ENV_FILE = path.join(LIMBO_DIR, '.env');
|
|
18
18
|
const COMPOSE_FILE = path.join(LIMBO_DIR, 'docker-compose.yml');
|
|
19
19
|
const GHCR_IMAGE = 'ghcr.io/tomasward1/limbo';
|
|
@@ -68,6 +68,7 @@ const COMPOSE_CONTENT = `services:
|
|
|
68
68
|
- "127.0.0.1:${PORT}:${PORT}"
|
|
69
69
|
volumes:
|
|
70
70
|
- limbo-data:/data
|
|
71
|
+
- ./vault:/data/vault
|
|
71
72
|
- limbo-openclaw-state:/home/limbo/.openclaw
|
|
72
73
|
env_file:
|
|
73
74
|
- .env
|
|
@@ -278,6 +279,22 @@ function sleep(ms) {
|
|
|
278
279
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
279
280
|
}
|
|
280
281
|
|
|
282
|
+
let clackPromise;
|
|
283
|
+
|
|
284
|
+
async function getClack() {
|
|
285
|
+
if (!clackPromise) clackPromise = import('@clack/prompts');
|
|
286
|
+
return clackPromise;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function maybeHandleClackCancel(value) {
|
|
290
|
+
const { cancel, isCancel } = await getClack();
|
|
291
|
+
if (isCancel(value)) {
|
|
292
|
+
cancel('Setup cancelled.');
|
|
293
|
+
process.exit(130);
|
|
294
|
+
}
|
|
295
|
+
return value;
|
|
296
|
+
}
|
|
297
|
+
|
|
281
298
|
function hasDocker() {
|
|
282
299
|
const result = spawnSync('docker', ['compose', 'version'], { stdio: 'pipe' });
|
|
283
300
|
return result.status === 0;
|
|
@@ -311,25 +328,34 @@ function createPromptInterface() {
|
|
|
311
328
|
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
312
329
|
}
|
|
313
330
|
|
|
314
|
-
async function promptValidated(
|
|
331
|
+
async function promptValidated(question, validate, errorMessage) {
|
|
332
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
333
|
+
const { text } = await getClack();
|
|
334
|
+
while (true) {
|
|
335
|
+
const value = await maybeHandleClackCancel(await text({
|
|
336
|
+
message: question.trim(),
|
|
337
|
+
validate: (input) => {
|
|
338
|
+
const validation = validate(String(input ?? ''));
|
|
339
|
+
return validation.ok ? undefined : (validation.message || errorMessage);
|
|
340
|
+
},
|
|
341
|
+
}));
|
|
342
|
+
const validation = validate(String(value));
|
|
343
|
+
if (validation.ok) return validation.value;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const rl = createPromptInterface();
|
|
315
348
|
while (true) {
|
|
316
349
|
const value = (await prompt(rl, question)).trim();
|
|
317
350
|
const validation = validate(value);
|
|
318
|
-
if (validation.ok)
|
|
351
|
+
if (validation.ok) {
|
|
352
|
+
rl.close();
|
|
353
|
+
return validation.value;
|
|
354
|
+
}
|
|
319
355
|
warn(validation.message || errorMessage);
|
|
320
356
|
}
|
|
321
357
|
}
|
|
322
358
|
|
|
323
|
-
function renderMenu(question, options, selectedIndex, lang) {
|
|
324
|
-
const lines = [`${c.bold}${question}${c.reset}`, `${c.dim}${t(lang, 'menuHelp')}${c.reset}`, ''];
|
|
325
|
-
options.forEach((option, index) => {
|
|
326
|
-
const prefix = index === selectedIndex ? `${c.green}>${c.reset}` : ' ';
|
|
327
|
-
lines.push(`${prefix} ${option.label}`);
|
|
328
|
-
if (option.description) lines.push(` ${c.dim}${option.description}${c.reset}`);
|
|
329
|
-
});
|
|
330
|
-
return lines.join('\n');
|
|
331
|
-
}
|
|
332
|
-
|
|
333
359
|
async function selectMenu(question, options, lang) {
|
|
334
360
|
if (!process.stdin.isTTY || !process.stdout.isTTY || typeof process.stdin.setRawMode !== 'function') {
|
|
335
361
|
const rl = createPromptInterface();
|
|
@@ -345,68 +371,16 @@ async function selectMenu(question, options, lang) {
|
|
|
345
371
|
warn('Pick one of the listed options.');
|
|
346
372
|
}
|
|
347
373
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
process.stdin.off('keypress', onKeypress);
|
|
359
|
-
process.stdin.setRawMode(Boolean(previousRawMode));
|
|
360
|
-
rl.close();
|
|
361
|
-
process.stdout.write('\n');
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function draw() {
|
|
365
|
-
const output = renderMenu(question, options, selectedIndex, lang);
|
|
366
|
-
if (lastRenderLineCount > 0) {
|
|
367
|
-
readline.moveCursor(process.stdout, 0, -lastRenderLineCount);
|
|
368
|
-
}
|
|
369
|
-
for (let i = 0; i < lastRenderLineCount; i++) {
|
|
370
|
-
readline.clearLine(process.stdout, 0);
|
|
371
|
-
if (i < lastRenderLineCount - 1) readline.moveCursor(process.stdout, 0, 1);
|
|
372
|
-
}
|
|
373
|
-
if (lastRenderLineCount > 0) {
|
|
374
|
-
readline.moveCursor(process.stdout, 0, -Math.max(lastRenderLineCount - 1, 0));
|
|
375
|
-
}
|
|
376
|
-
readline.cursorTo(process.stdout, 0);
|
|
377
|
-
process.stdout.write(output);
|
|
378
|
-
lastRenderLineCount = output.split('\n').length;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function onKeypress(_, key = {}) {
|
|
382
|
-
if (key.name === 'up' || key.name === 'k') {
|
|
383
|
-
selectedIndex = selectedIndex === 0 ? options.length - 1 : selectedIndex - 1;
|
|
384
|
-
draw();
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (key.name === 'down' || key.name === 'j') {
|
|
389
|
-
selectedIndex = selectedIndex === options.length - 1 ? 0 : selectedIndex + 1;
|
|
390
|
-
draw();
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (key.name === 'return') {
|
|
395
|
-
const value = options[selectedIndex];
|
|
396
|
-
cleanup();
|
|
397
|
-
resolve(value);
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (key.ctrl && key.name === 'c') {
|
|
402
|
-
cleanup();
|
|
403
|
-
process.exit(130);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
process.stdin.on('keypress', onKeypress);
|
|
408
|
-
draw();
|
|
409
|
-
});
|
|
374
|
+
const { select } = await getClack();
|
|
375
|
+
const selectedValue = await maybeHandleClackCancel(await select({
|
|
376
|
+
message: question,
|
|
377
|
+
options: options.map((option) => ({
|
|
378
|
+
value: option.value,
|
|
379
|
+
label: option.label,
|
|
380
|
+
hint: option.description,
|
|
381
|
+
})),
|
|
382
|
+
}));
|
|
383
|
+
return options.find((option) => option.value === selectedValue) || options[0];
|
|
410
384
|
}
|
|
411
385
|
|
|
412
386
|
function parseEnvFile() {
|
|
@@ -516,14 +490,11 @@ async function collectConfig(existingEnv = {}) {
|
|
|
516
490
|
|
|
517
491
|
const modelName = await chooseModel(language, providerFamily, accessMethod);
|
|
518
492
|
const provider = getModelCatalog(providerFamily, accessMethod).provider;
|
|
519
|
-
|
|
520
|
-
const rl = createPromptInterface();
|
|
521
493
|
let apiKey = '';
|
|
522
494
|
|
|
523
495
|
if (accessMethod === 'api-key') {
|
|
524
496
|
if (providerFamily === 'openai') {
|
|
525
497
|
apiKey = await promptValidated(
|
|
526
|
-
rl,
|
|
527
498
|
t(language, 'openAiApiKeyPrompt'),
|
|
528
499
|
(value) => {
|
|
529
500
|
if (!value) return { ok: false, message: t(language, 'requiredField') };
|
|
@@ -533,7 +504,6 @@ async function collectConfig(existingEnv = {}) {
|
|
|
533
504
|
);
|
|
534
505
|
} else {
|
|
535
506
|
apiKey = await promptValidated(
|
|
536
|
-
rl,
|
|
537
507
|
t(language, 'anthropicApiKeyPrompt'),
|
|
538
508
|
(value) => {
|
|
539
509
|
if (!value) return { ok: false, message: t(language, 'requiredField') };
|
|
@@ -552,14 +522,11 @@ async function collectConfig(existingEnv = {}) {
|
|
|
552
522
|
let telegramToken = '';
|
|
553
523
|
if (telegramChoice.value === 'true') {
|
|
554
524
|
telegramToken = await promptValidated(
|
|
555
|
-
rl,
|
|
556
525
|
t(language, 'telegramTokenPrompt'),
|
|
557
526
|
(value) => value ? { ok: true, value } : { ok: false, message: t(language, 'requiredField') },
|
|
558
527
|
);
|
|
559
528
|
}
|
|
560
529
|
|
|
561
|
-
rl.close();
|
|
562
|
-
|
|
563
530
|
return {
|
|
564
531
|
language,
|
|
565
532
|
authMode: accessMethod,
|
|
@@ -575,6 +542,8 @@ async function collectConfig(existingEnv = {}) {
|
|
|
575
542
|
|
|
576
543
|
function ensureComposeFile() {
|
|
577
544
|
fs.mkdirSync(LIMBO_DIR, { recursive: true });
|
|
545
|
+
fs.mkdirSync(path.join(VAULT_DIR, 'notes'), { recursive: true });
|
|
546
|
+
fs.mkdirSync(path.join(VAULT_DIR, 'maps'), { recursive: true });
|
|
578
547
|
fs.writeFileSync(COMPOSE_FILE, COMPOSE_CONTENT);
|
|
579
548
|
}
|
|
580
549
|
|
|
@@ -686,6 +655,7 @@ ${c.green}${c.bold}╚═══════════════════
|
|
|
686
655
|
${c.bold}${t(cfg.language, 'gateway')}:${c.reset} ws://127.0.0.1:${PORT}
|
|
687
656
|
${c.bold}${t(cfg.language, 'gatewayToken')}:${c.reset} ${gatewayToken}
|
|
688
657
|
${c.bold}${t(cfg.language, 'data')}:${c.reset} ${LIMBO_DIR}
|
|
658
|
+
${c.bold}Vault:${c.reset} ${VAULT_DIR}
|
|
689
659
|
${c.bold}${t(cfg.language, 'logs')}:${c.reset} limbo logs
|
|
690
660
|
${c.bold}${t(cfg.language, 'stop')}:${c.reset} limbo stop
|
|
691
661
|
${c.bold}${t(cfg.language, 'update')}:${c.reset} limbo update
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "limbo-ai",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Your personal AI memory agent — install and manage Limbo via npx",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"engines": {
|
|
10
10
|
"node": ">=18"
|
|
11
11
|
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@clack/prompts": "^1.1.0"
|
|
14
|
+
},
|
|
12
15
|
"scripts": {
|
|
13
16
|
"start": "node cli.js start"
|
|
14
17
|
},
|