alive-ai 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/cli/index.js +184 -7
- package/docs/index.html +1 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ alive-ai init my-ai
|
|
|
55
55
|
| `npx alive-ai@latest init my-ai` | Scaffold a clean local Alive-AI project. |
|
|
56
56
|
| `npx . setup` | Guided onboarding for local config, providers, Telegram, voice, images, and memory. |
|
|
57
57
|
| `npx . doctor` | Check OS, Node, Python, uv, ffmpeg, Docker, and OpenMind reachability. |
|
|
58
|
+
| `npx . doctor --fix` | Ask `y/N` for each missing installable tool and run the platform installer if approved. |
|
|
58
59
|
| `npx . chat` | Start the real runtime with split-pane terminal chat and logs. |
|
|
59
60
|
| `npx . chat --plain` | Start raw terminal chat without the TUI. |
|
|
60
61
|
| `npx . demo` | Run a keyless animated dashboard demo. |
|
|
@@ -65,6 +66,8 @@ alive-ai init my-ai
|
|
|
65
66
|
|
|
66
67
|
`start` and `chat` check npm for a newer Alive-AI version. You can update, skip once, or skip that specific version. Stop terminal chat with `/exit` or `Ctrl+C`.
|
|
67
68
|
|
|
69
|
+
`doctor --fix` is conservative: it prints the exact install command before running anything and asks separately for each missing tool. On macOS it uses Homebrew, on Windows it uses winget, and on Linux it supports apt, dnf, and pacman where possible.
|
|
70
|
+
|
|
68
71
|
If you use Docker:
|
|
69
72
|
|
|
70
73
|
```bash
|
|
@@ -286,6 +289,7 @@ Implemented:
|
|
|
286
289
|
- [x] Optional hybrid OpenMind cloud/local semantic memory
|
|
287
290
|
- [x] npm/npx CLI scaffold, setup, doctor, demo, chat, and start commands
|
|
288
291
|
- [x] Update prompt and project uninstall command
|
|
292
|
+
- [x] `doctor --fix` guided system dependency installer
|
|
289
293
|
- [x] Clean public repo with private personas, media, runtime data, and multi-AI orchestration removed
|
|
290
294
|
- [x] GitHub Pages site and full static WebUI export
|
|
291
295
|
|
package/cli/index.js
CHANGED
|
@@ -49,7 +49,7 @@ Usage:
|
|
|
49
49
|
alive-ai start [--skip-install] Install Python deps if needed and start runtime
|
|
50
50
|
alive-ai chat [--skip-install] Start split-pane terminal chat and logs
|
|
51
51
|
alive-ai chat --plain Start raw terminal chat without the TUI
|
|
52
|
-
alive-ai doctor
|
|
52
|
+
alive-ai doctor [--fix] Check local prerequisites and optionally install missing tools
|
|
53
53
|
alive-ai uninstall Remove Alive-AI runtime files from this project
|
|
54
54
|
|
|
55
55
|
Quick start:
|
|
@@ -471,14 +471,26 @@ async function setupProject(args) {
|
|
|
471
471
|
console.log("Run `npx . chat` for terminal chat, `npx . demo` for the dashboard preview, or `npx . start` for Telegram/runtime mode.");
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
function commandResponds(command, versionArgSets = [["--version"], ["-version"]]) {
|
|
475
|
+
for (const args of versionArgSets) {
|
|
476
|
+
const result = spawnSync(command, args, { stdio: "ignore" });
|
|
477
|
+
if (result.status === 0) return true;
|
|
478
|
+
if (result.error && result.error.code === "ENOENT") return false;
|
|
479
|
+
}
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
474
483
|
function findCommand(candidates) {
|
|
475
484
|
for (const command of candidates) {
|
|
476
|
-
|
|
477
|
-
if (result.status === 0) return command;
|
|
485
|
+
if (commandResponds(command)) return command;
|
|
478
486
|
}
|
|
479
487
|
return null;
|
|
480
488
|
}
|
|
481
489
|
|
|
490
|
+
function majorVersion(version) {
|
|
491
|
+
return Number.parseInt(String(version || "0").split(".")[0], 10) || 0;
|
|
492
|
+
}
|
|
493
|
+
|
|
482
494
|
function pythonVersion(command) {
|
|
483
495
|
const result = spawnSync(command, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"], {
|
|
484
496
|
encoding: "utf8",
|
|
@@ -487,6 +499,130 @@ function pythonVersion(command) {
|
|
|
487
499
|
return result.stdout.trim();
|
|
488
500
|
}
|
|
489
501
|
|
|
502
|
+
function hasCommand(command) {
|
|
503
|
+
return commandResponds(command);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function commandLine(command) {
|
|
507
|
+
if (!command) return "";
|
|
508
|
+
return command.map((part) => /[\s"']/.test(part) ? JSON.stringify(part) : part).join(" ");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function runInstallCommand(command) {
|
|
512
|
+
const [bin, ...args] = command;
|
|
513
|
+
return spawnSync(bin, args, { stdio: "inherit", shell: false });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function packageManager() {
|
|
517
|
+
if (process.platform === "darwin" && hasCommand("brew")) return "brew";
|
|
518
|
+
if (process.platform === "win32" && hasCommand("winget")) return "winget";
|
|
519
|
+
if (process.platform === "linux") {
|
|
520
|
+
if (hasCommand("apt-get")) return "apt";
|
|
521
|
+
if (hasCommand("dnf")) return "dnf";
|
|
522
|
+
if (hasCommand("pacman")) return "pacman";
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function installPlan(tool) {
|
|
528
|
+
const manager = packageManager();
|
|
529
|
+
if (process.platform === "darwin") {
|
|
530
|
+
if (manager !== "brew") return null;
|
|
531
|
+
return {
|
|
532
|
+
node: ["brew", "install", "node"],
|
|
533
|
+
python: ["brew", "install", "python@3.12"],
|
|
534
|
+
uv: ["brew", "install", "uv"],
|
|
535
|
+
ffmpeg: ["brew", "install", "ffmpeg"],
|
|
536
|
+
docker: ["brew", "install", "--cask", "docker"],
|
|
537
|
+
ollama: ["brew", "install", "ollama"],
|
|
538
|
+
}[tool] || null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (process.platform === "win32") {
|
|
542
|
+
if (manager !== "winget") return null;
|
|
543
|
+
return {
|
|
544
|
+
node: ["winget", "install", "-e", "--id", "OpenJS.NodeJS.LTS"],
|
|
545
|
+
python: ["winget", "install", "-e", "--id", "Python.Python.3.12"],
|
|
546
|
+
uv: ["winget", "install", "-e", "--id", "astral-sh.uv"],
|
|
547
|
+
ffmpeg: ["winget", "install", "-e", "--id", "Gyan.FFmpeg"],
|
|
548
|
+
docker: ["winget", "install", "-e", "--id", "Docker.DockerDesktop"],
|
|
549
|
+
ollama: ["winget", "install", "-e", "--id", "Ollama.Ollama"],
|
|
550
|
+
}[tool] || null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (process.platform === "linux") {
|
|
554
|
+
const plans = {
|
|
555
|
+
apt: {
|
|
556
|
+
node: ["sudo", "apt-get", "install", "-y", "nodejs", "npm"],
|
|
557
|
+
python: ["sudo", "apt-get", "install", "-y", "python3", "python3-venv"],
|
|
558
|
+
uv: ["sh", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
|
|
559
|
+
ffmpeg: ["sudo", "apt-get", "install", "-y", "ffmpeg"],
|
|
560
|
+
docker: ["sudo", "apt-get", "install", "-y", "docker.io"],
|
|
561
|
+
ollama: ["sh", "-c", "curl -fsSL https://ollama.com/install.sh | sh"],
|
|
562
|
+
},
|
|
563
|
+
dnf: {
|
|
564
|
+
node: ["sudo", "dnf", "install", "-y", "nodejs", "npm"],
|
|
565
|
+
python: ["sudo", "dnf", "install", "-y", "python3"],
|
|
566
|
+
uv: ["sh", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
|
|
567
|
+
ffmpeg: ["sudo", "dnf", "install", "-y", "ffmpeg"],
|
|
568
|
+
docker: ["sudo", "dnf", "install", "-y", "docker"],
|
|
569
|
+
ollama: ["sh", "-c", "curl -fsSL https://ollama.com/install.sh | sh"],
|
|
570
|
+
},
|
|
571
|
+
pacman: {
|
|
572
|
+
node: ["sudo", "pacman", "-S", "--needed", "nodejs", "npm"],
|
|
573
|
+
python: ["sudo", "pacman", "-S", "--needed", "python"],
|
|
574
|
+
uv: ["sudo", "pacman", "-S", "--needed", "uv"],
|
|
575
|
+
ffmpeg: ["sudo", "pacman", "-S", "--needed", "ffmpeg"],
|
|
576
|
+
docker: ["sudo", "pacman", "-S", "--needed", "docker"],
|
|
577
|
+
ollama: ["sudo", "pacman", "-S", "--needed", "ollama"],
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
return plans[manager]?.[tool] || null;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function manualInstallHint(tool) {
|
|
587
|
+
if (process.platform === "darwin" && !hasCommand("brew")) {
|
|
588
|
+
return "Install Homebrew from https://brew.sh, then rerun `npx . doctor --fix`.";
|
|
589
|
+
}
|
|
590
|
+
if (process.platform === "win32" && !hasCommand("winget")) {
|
|
591
|
+
return "Install Windows App Installer/winget, then rerun `npx . doctor --fix`.";
|
|
592
|
+
}
|
|
593
|
+
if (process.platform === "linux" && !packageManager()) {
|
|
594
|
+
return `No supported Linux package manager detected for ${tool}. Install it manually with your distro package manager.`;
|
|
595
|
+
}
|
|
596
|
+
return `No automatic installer is configured for ${tool} on this system.`;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
async function maybeInstallTool(item, assumeYes = false) {
|
|
600
|
+
const command = installPlan(item.id);
|
|
601
|
+
if (!command) {
|
|
602
|
+
console.log(` ${item.name}: ${manualInstallHint(item.id)}`);
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
console.log("");
|
|
607
|
+
console.log(`${item.name} is missing.`);
|
|
608
|
+
console.log(`Command: ${commandLine(command)}`);
|
|
609
|
+
const answer = assumeYes
|
|
610
|
+
? "y"
|
|
611
|
+
: normalizeChoice(await ask(`Install ${item.name}? y/N`, "n", false), "n");
|
|
612
|
+
if (!["y", "yes"].includes(answer)) {
|
|
613
|
+
console.log(`Skipped ${item.name}.`);
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const result = runInstallCommand(command);
|
|
618
|
+
if (result.status === 0) {
|
|
619
|
+
console.log(`${item.name} install command completed.`);
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
622
|
+
console.log(`${item.name} install command failed with exit code ${result.status || 1}.`);
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
|
|
490
626
|
function findPython() {
|
|
491
627
|
const preferred = ["python3.12", "python3.11", "python3.13", "python3", "python"];
|
|
492
628
|
for (const command of preferred) {
|
|
@@ -499,12 +635,24 @@ function findPython() {
|
|
|
499
635
|
return null;
|
|
500
636
|
}
|
|
501
637
|
|
|
502
|
-
|
|
638
|
+
function wantsOllama(settings) {
|
|
639
|
+
const provider = String(settings.LLM_PROVIDER || "").toLowerCase();
|
|
640
|
+
const order = Array.isArray(settings.LLM_FALLBACK?.ORDER)
|
|
641
|
+
? settings.LLM_FALLBACK.ORDER.map((item) => String(item).toLowerCase())
|
|
642
|
+
: [];
|
|
643
|
+
return provider === "ollama" || order.includes("ollama");
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function doctor(args = []) {
|
|
647
|
+
const shouldFix = hasFlag(args, "--fix");
|
|
648
|
+
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y");
|
|
503
649
|
const python = findPython();
|
|
504
650
|
const uv = findCommand(["uv"]);
|
|
505
651
|
const ffmpeg = findCommand(["ffmpeg"]);
|
|
506
652
|
const docker = findCommand(["docker"]);
|
|
653
|
+
const ollama = findCommand(["ollama"]);
|
|
507
654
|
const node = process.version;
|
|
655
|
+
const nodeMajor = majorVersion(process.versions.node);
|
|
508
656
|
const settings = readProjectSettings();
|
|
509
657
|
const venvPython = process.platform === "win32"
|
|
510
658
|
? path.join(process.cwd(), ".alive-ai", "venv", "Scripts", "python.exe")
|
|
@@ -512,7 +660,7 @@ async function doctor() {
|
|
|
512
660
|
|
|
513
661
|
console.log("Alive-AI doctor");
|
|
514
662
|
console.log(` system: ${os.platform()} ${os.arch()}`);
|
|
515
|
-
console.log(` node: ${node}`);
|
|
663
|
+
console.log(` node: ${nodeMajor >= 18 ? node : `${node} (Node 18+ required)`}`);
|
|
516
664
|
console.log(` python: ${python ? `${python.command} ${python.version}` : "missing"}`);
|
|
517
665
|
if (fs.existsSync(venvPython)) {
|
|
518
666
|
console.log(` venv: ${pythonVersion(venvPython) || "unknown"} (${path.relative(process.cwd(), venvPython)})`);
|
|
@@ -520,8 +668,19 @@ async function doctor() {
|
|
|
520
668
|
console.log(` uv: ${uv || "missing, will use venv + pip"}`);
|
|
521
669
|
console.log(` ffmpeg: ${ffmpeg || "missing, voice conversion may be limited"}`);
|
|
522
670
|
console.log(` docker: ${docker || "missing, Redis can still be external"}`);
|
|
671
|
+
if (wantsOllama(settings)) {
|
|
672
|
+
console.log(` ollama: ${ollama || "missing, local LLM unavailable until installed"}`);
|
|
673
|
+
}
|
|
523
674
|
console.log(` input: ${settings.INPUT_CHANNEL || "telegram"}`);
|
|
524
675
|
|
|
676
|
+
const missing = [];
|
|
677
|
+
if (nodeMajor < 18) missing.push({ id: "node", name: "Node.js 18+" });
|
|
678
|
+
if (!python) missing.push({ id: "python", name: "Python 3.11+" });
|
|
679
|
+
if (!uv) missing.push({ id: "uv", name: "uv" });
|
|
680
|
+
if (!ffmpeg) missing.push({ id: "ffmpeg", name: "ffmpeg" });
|
|
681
|
+
if (!docker) missing.push({ id: "docker", name: "Docker" });
|
|
682
|
+
if (wantsOllama(settings) && !ollama) missing.push({ id: "ollama", name: "Ollama" });
|
|
683
|
+
|
|
525
684
|
if (!python) {
|
|
526
685
|
console.log("");
|
|
527
686
|
console.log("Install Python 3.11+ first:");
|
|
@@ -547,7 +706,25 @@ async function doctor() {
|
|
|
547
706
|
console.log(" OpenMind: disabled");
|
|
548
707
|
}
|
|
549
708
|
|
|
550
|
-
if (
|
|
709
|
+
if (shouldFix) {
|
|
710
|
+
if (!missing.length) {
|
|
711
|
+
console.log("");
|
|
712
|
+
console.log("Nothing missing. No fixes needed.");
|
|
713
|
+
} else {
|
|
714
|
+
console.log("");
|
|
715
|
+
console.log("Doctor fix mode: each installer is optional and will ask before running.");
|
|
716
|
+
for (const item of missing) {
|
|
717
|
+
await maybeInstallTool(item, assumeYes);
|
|
718
|
+
}
|
|
719
|
+
console.log("");
|
|
720
|
+
console.log("Run `npx . doctor` again to verify the final state.");
|
|
721
|
+
}
|
|
722
|
+
} else if (missing.length) {
|
|
723
|
+
console.log("");
|
|
724
|
+
console.log("Run `npx . doctor --fix` to install missing tools one by one.");
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (!python || nodeMajor < 18) process.exitCode = 1;
|
|
551
728
|
}
|
|
552
729
|
|
|
553
730
|
function ensurePythonEnv(skipInstall) {
|
|
@@ -735,7 +912,7 @@ async function main() {
|
|
|
735
912
|
if (command === "demo") return startDemo(args);
|
|
736
913
|
if (command === "start") return startRuntime(args);
|
|
737
914
|
if (command === "chat") return startTerminalChat(args);
|
|
738
|
-
if (command === "doctor") return doctor();
|
|
915
|
+
if (command === "doctor") return doctor(args);
|
|
739
916
|
if (command === "uninstall") return uninstallProject(args);
|
|
740
917
|
console.error(`Unknown command: ${command}`);
|
|
741
918
|
usage();
|
package/docs/index.html
CHANGED
|
@@ -292,6 +292,7 @@ npx . chat</code></pre>
|
|
|
292
292
|
<div class="steps">
|
|
293
293
|
<div class="step"><strong>Start local terminal chat</strong><span>`npx . chat` starts the same runtime with chat on the left, logs on the right, and the WebUI at `http://127.0.0.1:8080`.</span></div>
|
|
294
294
|
<div class="step"><strong>Start Telegram/runtime mode</strong><span>`npx . start` starts the configured input channel and validates Telegram before polling. Stop foreground runs with `Ctrl+C`.</span></div>
|
|
295
|
+
<div class="step"><strong>Install missing tools</strong><span>`npx . doctor --fix` asks `y/N` for each missing tool, then uses Homebrew, winget, apt, dnf, or pacman where available.</span></div>
|
|
295
296
|
<div class="step"><strong>Keep it updated</strong><span>`start` and `chat` check npm for newer versions. Use `npx . update` manually or `npx . uninstall` to remove local runtime files.</span></div>
|
|
296
297
|
<div class="step"><strong>Use OpenMind</strong><span>Choose `openmind-cloud` for `https://theopenmind.pro` or `openmind-local` for `http://127.0.0.1:3333`.</span></div>
|
|
297
298
|
<div class="step"><strong>Preview the dashboard</strong><span>`npx . demo` is keyless. The real runtime dashboard streams local state over SSE.</span></div>
|
package/package.json
CHANGED