alive-ai 0.1.3 → 0.1.4
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 +174 -5
- 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:
|
|
@@ -479,6 +479,10 @@ function findCommand(candidates) {
|
|
|
479
479
|
return null;
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
+
function majorVersion(version) {
|
|
483
|
+
return Number.parseInt(String(version || "0").split(".")[0], 10) || 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
482
486
|
function pythonVersion(command) {
|
|
483
487
|
const result = spawnSync(command, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"], {
|
|
484
488
|
encoding: "utf8",
|
|
@@ -487,6 +491,130 @@ function pythonVersion(command) {
|
|
|
487
491
|
return result.stdout.trim();
|
|
488
492
|
}
|
|
489
493
|
|
|
494
|
+
function hasCommand(command) {
|
|
495
|
+
return spawnSync(command, ["--version"], { stdio: "ignore" }).status === 0;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function commandLine(command) {
|
|
499
|
+
if (!command) return "";
|
|
500
|
+
return command.map((part) => /[\s"']/.test(part) ? JSON.stringify(part) : part).join(" ");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function runInstallCommand(command) {
|
|
504
|
+
const [bin, ...args] = command;
|
|
505
|
+
return spawnSync(bin, args, { stdio: "inherit", shell: false });
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function packageManager() {
|
|
509
|
+
if (process.platform === "darwin" && hasCommand("brew")) return "brew";
|
|
510
|
+
if (process.platform === "win32" && hasCommand("winget")) return "winget";
|
|
511
|
+
if (process.platform === "linux") {
|
|
512
|
+
if (hasCommand("apt-get")) return "apt";
|
|
513
|
+
if (hasCommand("dnf")) return "dnf";
|
|
514
|
+
if (hasCommand("pacman")) return "pacman";
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function installPlan(tool) {
|
|
520
|
+
const manager = packageManager();
|
|
521
|
+
if (process.platform === "darwin") {
|
|
522
|
+
if (manager !== "brew") return null;
|
|
523
|
+
return {
|
|
524
|
+
node: ["brew", "install", "node"],
|
|
525
|
+
python: ["brew", "install", "python@3.12"],
|
|
526
|
+
uv: ["brew", "install", "uv"],
|
|
527
|
+
ffmpeg: ["brew", "install", "ffmpeg"],
|
|
528
|
+
docker: ["brew", "install", "--cask", "docker"],
|
|
529
|
+
ollama: ["brew", "install", "ollama"],
|
|
530
|
+
}[tool] || null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (process.platform === "win32") {
|
|
534
|
+
if (manager !== "winget") return null;
|
|
535
|
+
return {
|
|
536
|
+
node: ["winget", "install", "-e", "--id", "OpenJS.NodeJS.LTS"],
|
|
537
|
+
python: ["winget", "install", "-e", "--id", "Python.Python.3.12"],
|
|
538
|
+
uv: ["winget", "install", "-e", "--id", "astral-sh.uv"],
|
|
539
|
+
ffmpeg: ["winget", "install", "-e", "--id", "Gyan.FFmpeg"],
|
|
540
|
+
docker: ["winget", "install", "-e", "--id", "Docker.DockerDesktop"],
|
|
541
|
+
ollama: ["winget", "install", "-e", "--id", "Ollama.Ollama"],
|
|
542
|
+
}[tool] || null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (process.platform === "linux") {
|
|
546
|
+
const plans = {
|
|
547
|
+
apt: {
|
|
548
|
+
node: ["sudo", "apt-get", "install", "-y", "nodejs", "npm"],
|
|
549
|
+
python: ["sudo", "apt-get", "install", "-y", "python3", "python3-venv"],
|
|
550
|
+
uv: ["sh", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
|
|
551
|
+
ffmpeg: ["sudo", "apt-get", "install", "-y", "ffmpeg"],
|
|
552
|
+
docker: ["sudo", "apt-get", "install", "-y", "docker.io"],
|
|
553
|
+
ollama: ["sh", "-c", "curl -fsSL https://ollama.com/install.sh | sh"],
|
|
554
|
+
},
|
|
555
|
+
dnf: {
|
|
556
|
+
node: ["sudo", "dnf", "install", "-y", "nodejs", "npm"],
|
|
557
|
+
python: ["sudo", "dnf", "install", "-y", "python3"],
|
|
558
|
+
uv: ["sh", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"],
|
|
559
|
+
ffmpeg: ["sudo", "dnf", "install", "-y", "ffmpeg"],
|
|
560
|
+
docker: ["sudo", "dnf", "install", "-y", "docker"],
|
|
561
|
+
ollama: ["sh", "-c", "curl -fsSL https://ollama.com/install.sh | sh"],
|
|
562
|
+
},
|
|
563
|
+
pacman: {
|
|
564
|
+
node: ["sudo", "pacman", "-S", "--needed", "nodejs", "npm"],
|
|
565
|
+
python: ["sudo", "pacman", "-S", "--needed", "python"],
|
|
566
|
+
uv: ["sudo", "pacman", "-S", "--needed", "uv"],
|
|
567
|
+
ffmpeg: ["sudo", "pacman", "-S", "--needed", "ffmpeg"],
|
|
568
|
+
docker: ["sudo", "pacman", "-S", "--needed", "docker"],
|
|
569
|
+
ollama: ["sudo", "pacman", "-S", "--needed", "ollama"],
|
|
570
|
+
},
|
|
571
|
+
};
|
|
572
|
+
return plans[manager]?.[tool] || null;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function manualInstallHint(tool) {
|
|
579
|
+
if (process.platform === "darwin" && !hasCommand("brew")) {
|
|
580
|
+
return "Install Homebrew from https://brew.sh, then rerun `npx . doctor --fix`.";
|
|
581
|
+
}
|
|
582
|
+
if (process.platform === "win32" && !hasCommand("winget")) {
|
|
583
|
+
return "Install Windows App Installer/winget, then rerun `npx . doctor --fix`.";
|
|
584
|
+
}
|
|
585
|
+
if (process.platform === "linux" && !packageManager()) {
|
|
586
|
+
return `No supported Linux package manager detected for ${tool}. Install it manually with your distro package manager.`;
|
|
587
|
+
}
|
|
588
|
+
return `No automatic installer is configured for ${tool} on this system.`;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async function maybeInstallTool(item, assumeYes = false) {
|
|
592
|
+
const command = installPlan(item.id);
|
|
593
|
+
if (!command) {
|
|
594
|
+
console.log(` ${item.name}: ${manualInstallHint(item.id)}`);
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
console.log("");
|
|
599
|
+
console.log(`${item.name} is missing.`);
|
|
600
|
+
console.log(`Command: ${commandLine(command)}`);
|
|
601
|
+
const answer = assumeYes
|
|
602
|
+
? "y"
|
|
603
|
+
: normalizeChoice(await ask(`Install ${item.name}? y/N`, "n", false), "n");
|
|
604
|
+
if (!["y", "yes"].includes(answer)) {
|
|
605
|
+
console.log(`Skipped ${item.name}.`);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const result = runInstallCommand(command);
|
|
610
|
+
if (result.status === 0) {
|
|
611
|
+
console.log(`${item.name} install command completed.`);
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
console.log(`${item.name} install command failed with exit code ${result.status || 1}.`);
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
|
|
490
618
|
function findPython() {
|
|
491
619
|
const preferred = ["python3.12", "python3.11", "python3.13", "python3", "python"];
|
|
492
620
|
for (const command of preferred) {
|
|
@@ -499,12 +627,24 @@ function findPython() {
|
|
|
499
627
|
return null;
|
|
500
628
|
}
|
|
501
629
|
|
|
502
|
-
|
|
630
|
+
function wantsOllama(settings) {
|
|
631
|
+
const provider = String(settings.LLM_PROVIDER || "").toLowerCase();
|
|
632
|
+
const order = Array.isArray(settings.LLM_FALLBACK?.ORDER)
|
|
633
|
+
? settings.LLM_FALLBACK.ORDER.map((item) => String(item).toLowerCase())
|
|
634
|
+
: [];
|
|
635
|
+
return provider === "ollama" || order.includes("ollama");
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
async function doctor(args = []) {
|
|
639
|
+
const shouldFix = hasFlag(args, "--fix");
|
|
640
|
+
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y");
|
|
503
641
|
const python = findPython();
|
|
504
642
|
const uv = findCommand(["uv"]);
|
|
505
643
|
const ffmpeg = findCommand(["ffmpeg"]);
|
|
506
644
|
const docker = findCommand(["docker"]);
|
|
645
|
+
const ollama = findCommand(["ollama"]);
|
|
507
646
|
const node = process.version;
|
|
647
|
+
const nodeMajor = majorVersion(process.versions.node);
|
|
508
648
|
const settings = readProjectSettings();
|
|
509
649
|
const venvPython = process.platform === "win32"
|
|
510
650
|
? path.join(process.cwd(), ".alive-ai", "venv", "Scripts", "python.exe")
|
|
@@ -512,7 +652,7 @@ async function doctor() {
|
|
|
512
652
|
|
|
513
653
|
console.log("Alive-AI doctor");
|
|
514
654
|
console.log(` system: ${os.platform()} ${os.arch()}`);
|
|
515
|
-
console.log(` node: ${node}`);
|
|
655
|
+
console.log(` node: ${nodeMajor >= 18 ? node : `${node} (Node 18+ required)`}`);
|
|
516
656
|
console.log(` python: ${python ? `${python.command} ${python.version}` : "missing"}`);
|
|
517
657
|
if (fs.existsSync(venvPython)) {
|
|
518
658
|
console.log(` venv: ${pythonVersion(venvPython) || "unknown"} (${path.relative(process.cwd(), venvPython)})`);
|
|
@@ -520,8 +660,19 @@ async function doctor() {
|
|
|
520
660
|
console.log(` uv: ${uv || "missing, will use venv + pip"}`);
|
|
521
661
|
console.log(` ffmpeg: ${ffmpeg || "missing, voice conversion may be limited"}`);
|
|
522
662
|
console.log(` docker: ${docker || "missing, Redis can still be external"}`);
|
|
663
|
+
if (wantsOllama(settings)) {
|
|
664
|
+
console.log(` ollama: ${ollama || "missing, local LLM unavailable until installed"}`);
|
|
665
|
+
}
|
|
523
666
|
console.log(` input: ${settings.INPUT_CHANNEL || "telegram"}`);
|
|
524
667
|
|
|
668
|
+
const missing = [];
|
|
669
|
+
if (nodeMajor < 18) missing.push({ id: "node", name: "Node.js 18+" });
|
|
670
|
+
if (!python) missing.push({ id: "python", name: "Python 3.11+" });
|
|
671
|
+
if (!uv) missing.push({ id: "uv", name: "uv" });
|
|
672
|
+
if (!ffmpeg) missing.push({ id: "ffmpeg", name: "ffmpeg" });
|
|
673
|
+
if (!docker) missing.push({ id: "docker", name: "Docker" });
|
|
674
|
+
if (wantsOllama(settings) && !ollama) missing.push({ id: "ollama", name: "Ollama" });
|
|
675
|
+
|
|
525
676
|
if (!python) {
|
|
526
677
|
console.log("");
|
|
527
678
|
console.log("Install Python 3.11+ first:");
|
|
@@ -547,7 +698,25 @@ async function doctor() {
|
|
|
547
698
|
console.log(" OpenMind: disabled");
|
|
548
699
|
}
|
|
549
700
|
|
|
550
|
-
if (
|
|
701
|
+
if (shouldFix) {
|
|
702
|
+
if (!missing.length) {
|
|
703
|
+
console.log("");
|
|
704
|
+
console.log("Nothing missing. No fixes needed.");
|
|
705
|
+
} else {
|
|
706
|
+
console.log("");
|
|
707
|
+
console.log("Doctor fix mode: each installer is optional and will ask before running.");
|
|
708
|
+
for (const item of missing) {
|
|
709
|
+
await maybeInstallTool(item, assumeYes);
|
|
710
|
+
}
|
|
711
|
+
console.log("");
|
|
712
|
+
console.log("Run `npx . doctor` again to verify the final state.");
|
|
713
|
+
}
|
|
714
|
+
} else if (missing.length) {
|
|
715
|
+
console.log("");
|
|
716
|
+
console.log("Run `npx . doctor --fix` to install missing tools one by one.");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (!python || nodeMajor < 18) process.exitCode = 1;
|
|
551
720
|
}
|
|
552
721
|
|
|
553
722
|
function ensurePythonEnv(skipInstall) {
|
|
@@ -735,7 +904,7 @@ async function main() {
|
|
|
735
904
|
if (command === "demo") return startDemo(args);
|
|
736
905
|
if (command === "start") return startRuntime(args);
|
|
737
906
|
if (command === "chat") return startTerminalChat(args);
|
|
738
|
-
if (command === "doctor") return doctor();
|
|
907
|
+
if (command === "doctor") return doctor(args);
|
|
739
908
|
if (command === "uninstall") return uninstallProject(args);
|
|
740
909
|
console.error(`Unknown command: ${command}`);
|
|
741
910
|
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