alive-ai 0.1.2 → 0.1.3
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 +27 -4
- package/cli/index.js +284 -15
- package/cli/tui.js +248 -0
- package/config/settings.example.json +1 -0
- package/core/hot_reload.py +2 -1
- package/core/manifest.md +2 -0
- package/core/paths.py +48 -0
- package/core/self.py +42 -1
- package/core/user_manager.py +2 -4
- package/docs/index.html +4 -3
- package/heart/emotional_state.py +2 -2
- package/heart/hormonal.py +2 -2
- package/heart/integrity.py +2 -1
- package/heart/interoception.py +2 -5
- package/heart/love.py +2 -2
- package/heart/scars.py +2 -3
- package/heart/telemetry.py +3 -9
- package/input/manifest.md +2 -2
- package/input/telegram/listener.py +47 -10
- package/input/terminal/listener.py +51 -46
- package/main.py +50 -15
- package/manifest.md +1 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/webui/app.py +15 -18
- package/webui/bridge.py +22 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ npx . doctor
|
|
|
29
29
|
npx . chat
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The terminal chat starts the real runtime
|
|
32
|
+
The terminal chat starts the real runtime in a split-pane TUI: chat on the left, live logs on the right. The dashboard runs locally at:
|
|
33
33
|
|
|
34
34
|
```text
|
|
35
35
|
http://127.0.0.1:8080
|
|
@@ -55,12 +55,15 @@ 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 . chat` | Start the real runtime with terminal chat
|
|
58
|
+
| `npx . chat` | Start the real runtime with split-pane terminal chat and logs. |
|
|
59
|
+
| `npx . chat --plain` | Start raw terminal chat without the TUI. |
|
|
59
60
|
| `npx . demo` | Run a keyless animated dashboard demo. |
|
|
60
61
|
| `npx . start` | Start the runtime using the configured input channel, usually Telegram. |
|
|
61
62
|
| `npx . start --skip-install` | Start again without reinstalling Python dependencies. |
|
|
63
|
+
| `npx . update` | Refresh runtime files from the latest npm package while preserving config/data/media. |
|
|
64
|
+
| `npx . uninstall` | Remove Alive-AI runtime files, config, venv, cache, data, and media from the project. |
|
|
62
65
|
|
|
63
|
-
|
|
66
|
+
`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`.
|
|
64
67
|
|
|
65
68
|
If you use Docker:
|
|
66
69
|
|
|
@@ -90,6 +93,16 @@ myvids/
|
|
|
90
93
|
|
|
91
94
|
The setup accepts `skip` for optional keys and `local` for Ollama.
|
|
92
95
|
|
|
96
|
+
Startup config is loaded from multiple places in this order:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
.env
|
|
100
|
+
config/secrets.env
|
|
101
|
+
config/settings.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Shell environment variables win over `.env`/`config/secrets.env`. Runtime settings come from `config/settings.json`. Telegram uses `TELEGRAM_TOKEN` when present, otherwise `telegram_token` from `config/settings.json`.
|
|
105
|
+
|
|
93
106
|
| Setup item | Options |
|
|
94
107
|
| --- | --- |
|
|
95
108
|
| LLM | `local`/Ollama, OpenRouter, ZAI, or `skip` for demo/fallback-only mode. |
|
|
@@ -126,7 +139,13 @@ myvids/example.txt
|
|
|
126
139
|
|
|
127
140
|
## Terminal Chat
|
|
128
141
|
|
|
129
|
-
`npx . chat` uses the same core runtime as Telegram. It emits the same `message_received` events, saves memory the same way, and updates the local WebUI.
|
|
142
|
+
`npx . chat` uses the same core runtime as Telegram. It emits the same `message_received` events, saves memory the same way, and updates the local WebUI. The default terminal interface is split-pane: chat/input on the left, startup/runtime logs on the right.
|
|
143
|
+
|
|
144
|
+
Use raw mode when you want the old plain shell behavior:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx . chat --plain
|
|
148
|
+
```
|
|
130
149
|
|
|
131
150
|
Terminal commands:
|
|
132
151
|
|
|
@@ -204,6 +223,8 @@ Comfortable local setup:
|
|
|
204
223
|
|
|
205
224
|
`npx . start` creates `.alive-ai/venv` and installs Python dependencies. System-level packages such as Node, Python, Ollama, Docker, and ffmpeg must already exist on the machine.
|
|
206
225
|
|
|
226
|
+
The CLI prefers Python 3.12, 3.11, then 3.13 before falling back to the system `python3`. When `uv` is installed, Alive-AI now passes the selected Python explicitly so `uv` does not silently choose a newer interpreter.
|
|
227
|
+
|
|
207
228
|
## Platform Support
|
|
208
229
|
|
|
209
230
|
Alive-AI is designed for macOS, Windows, and Linux.
|
|
@@ -260,9 +281,11 @@ Implemented:
|
|
|
260
281
|
- [x] Per-user memory/state isolation
|
|
261
282
|
- [x] Telegram input/output runtime
|
|
262
283
|
- [x] Terminal chat runtime with owner-style slash commands
|
|
284
|
+
- [x] Split-pane terminal chat with logs
|
|
263
285
|
- [x] Local WebUI dashboard with live state streaming
|
|
264
286
|
- [x] Optional hybrid OpenMind cloud/local semantic memory
|
|
265
287
|
- [x] npm/npx CLI scaffold, setup, doctor, demo, chat, and start commands
|
|
288
|
+
- [x] Update prompt and project uninstall command
|
|
266
289
|
- [x] Clean public repo with private personas, media, runtime data, and multi-AI orchestration removed
|
|
267
290
|
- [x] GitHub Pages site and full static WebUI export
|
|
268
291
|
|
package/cli/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const os = require("os");
|
|
|
7
7
|
const path = require("path");
|
|
8
8
|
const readline = require("readline");
|
|
9
9
|
const { spawn, spawnSync } = require("child_process");
|
|
10
|
+
const { runRuntimeTui } = require("./tui");
|
|
10
11
|
|
|
11
12
|
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
12
13
|
const DEFAULT_PORT = 8080;
|
|
@@ -44,9 +45,12 @@ Usage:
|
|
|
44
45
|
alive-ai init <directory> Create a new Alive-AI project
|
|
45
46
|
alive-ai setup [--yes] Create local config from templates
|
|
46
47
|
alive-ai demo [--port 8080] Run the animated dashboard demo
|
|
48
|
+
alive-ai update [--yes] Update this project from the latest package
|
|
47
49
|
alive-ai start [--skip-install] Install Python deps if needed and start runtime
|
|
48
|
-
alive-ai chat [--skip-install] Start
|
|
50
|
+
alive-ai chat [--skip-install] Start split-pane terminal chat and logs
|
|
51
|
+
alive-ai chat --plain Start raw terminal chat without the TUI
|
|
49
52
|
alive-ai doctor Check local prerequisites
|
|
53
|
+
alive-ai uninstall Remove Alive-AI runtime files from this project
|
|
50
54
|
|
|
51
55
|
Quick start:
|
|
52
56
|
npx alive-ai@latest init my-ai
|
|
@@ -55,7 +59,8 @@ Quick start:
|
|
|
55
59
|
npx . doctor
|
|
56
60
|
npx . chat
|
|
57
61
|
npx . demo
|
|
58
|
-
npx . start
|
|
62
|
+
npx . start
|
|
63
|
+
npx . uninstall`);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
function argValue(args, name, fallback) {
|
|
@@ -165,6 +170,198 @@ function readProjectSettings() {
|
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
|
|
173
|
+
function readSimpleEnv(file) {
|
|
174
|
+
if (!fs.existsSync(file)) return {};
|
|
175
|
+
const data = {};
|
|
176
|
+
for (const rawLine of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
177
|
+
const line = rawLine.trim();
|
|
178
|
+
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
179
|
+
const [key, ...rest] = line.split("=");
|
|
180
|
+
data[key.trim()] = rest.join("=").trim();
|
|
181
|
+
}
|
|
182
|
+
return data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function packageVersion() {
|
|
186
|
+
try {
|
|
187
|
+
return readJson(path.join(PACKAGE_ROOT, "package.json")).version || "0.0.0";
|
|
188
|
+
} catch {
|
|
189
|
+
return "0.0.0";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function compareVersions(a, b) {
|
|
194
|
+
const left = String(a || "0").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
195
|
+
const right = String(b || "0").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
196
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
197
|
+
const delta = (left[i] || 0) - (right[i] || 0);
|
|
198
|
+
if (delta) return delta;
|
|
199
|
+
}
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function npmLatestVersion() {
|
|
204
|
+
const result = spawnSync("npm", ["view", "alive-ai", "version", "--silent"], {
|
|
205
|
+
encoding: "utf8",
|
|
206
|
+
timeout: 5000,
|
|
207
|
+
});
|
|
208
|
+
if (result.status !== 0) return null;
|
|
209
|
+
return result.stdout.trim() || null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function updatePrefsPath() {
|
|
213
|
+
return path.join(os.homedir(), ".alive-ai", "update-prefs.json");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function readUpdatePrefs() {
|
|
217
|
+
try {
|
|
218
|
+
return readJson(updatePrefsPath());
|
|
219
|
+
} catch {
|
|
220
|
+
return {};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function writeUpdatePrefs(data) {
|
|
225
|
+
writeJson(updatePrefsPath(), data);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function maybeCheckForUpdate(args) {
|
|
229
|
+
if (hasFlag(args, "--no-update-check") || process.env.ALIVE_AI_SKIP_UPDATE_CHECK === "1") return;
|
|
230
|
+
if (!process.stdin.isTTY) return;
|
|
231
|
+
const current = packageVersion();
|
|
232
|
+
const latest = npmLatestVersion();
|
|
233
|
+
if (!latest || compareVersions(latest, current) <= 0) return;
|
|
234
|
+
|
|
235
|
+
const prefs = readUpdatePrefs();
|
|
236
|
+
if (prefs.skipVersion === latest) return;
|
|
237
|
+
|
|
238
|
+
console.log("");
|
|
239
|
+
console.log(`Alive-AI ${latest} is available. Current project runtime is ${current}.`);
|
|
240
|
+
const answer = normalizeChoice(await ask("Update before starting? yes, skip, or never", "yes", false), "yes");
|
|
241
|
+
if (answer === "never") {
|
|
242
|
+
prefs.skipVersion = latest;
|
|
243
|
+
writeUpdatePrefs(prefs);
|
|
244
|
+
console.log(`Skipping Alive-AI ${latest}. Run \`npx . update\` whenever you want it.`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (answer === "skip" || answer === "no" || answer === "n") return;
|
|
248
|
+
|
|
249
|
+
const update = spawnSync("npx", ["-y", "alive-ai@latest", "update", "--yes"], {
|
|
250
|
+
stdio: "inherit",
|
|
251
|
+
cwd: process.cwd(),
|
|
252
|
+
});
|
|
253
|
+
if (update.status !== 0) {
|
|
254
|
+
console.log("Update failed, continuing with the current runtime.");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
console.log("Update complete. Starting with the refreshed project files.");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const UPDATE_PRESERVE = new Set([
|
|
261
|
+
"config/settings.json",
|
|
262
|
+
"config/self.json",
|
|
263
|
+
"config/directives.json",
|
|
264
|
+
"config/instructions.md",
|
|
265
|
+
".env",
|
|
266
|
+
"config/secrets.env",
|
|
267
|
+
"data",
|
|
268
|
+
"mypics",
|
|
269
|
+
"myvids",
|
|
270
|
+
".alive-ai",
|
|
271
|
+
".cache",
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
function shouldPreserve(relPath) {
|
|
275
|
+
const normalized = relPath.split(path.sep).join("/");
|
|
276
|
+
if (UPDATE_PRESERVE.has(normalized)) return true;
|
|
277
|
+
return [...UPDATE_PRESERVE].some((prefix) => normalized.startsWith(`${prefix}/`));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function copyUpdateRecursive(src, dest, baseDest = dest) {
|
|
281
|
+
const relPath = path.relative(baseDest, dest);
|
|
282
|
+
if (relPath && shouldPreserve(relPath)) return;
|
|
283
|
+
const stat = fs.statSync(src);
|
|
284
|
+
if (stat.isDirectory()) {
|
|
285
|
+
ensureDir(dest);
|
|
286
|
+
for (const entry of fs.readdirSync(src)) {
|
|
287
|
+
if (entry === ".git" || entry === "node_modules" || entry === "__pycache__") continue;
|
|
288
|
+
copyUpdateRecursive(path.join(src, entry), path.join(dest, entry), baseDest);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (src.endsWith(".pyc")) return;
|
|
293
|
+
ensureDir(path.dirname(dest));
|
|
294
|
+
fs.copyFileSync(src, dest);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function updateProject(args) {
|
|
298
|
+
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y") || !process.stdin.isTTY;
|
|
299
|
+
if (!fs.existsSync(path.join(process.cwd(), "config")) || !fs.existsSync(path.join(process.cwd(), "main.py"))) {
|
|
300
|
+
console.error("Run `alive-ai update` from an Alive-AI project directory.");
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
if (!assumeYes) {
|
|
304
|
+
const answer = normalizeChoice(await ask("Update runtime files while preserving config/data? yes or no", "yes", false), "yes");
|
|
305
|
+
if (!["yes", "y"].includes(answer)) return;
|
|
306
|
+
}
|
|
307
|
+
for (const entry of COPY_ENTRIES) {
|
|
308
|
+
const src = path.join(PACKAGE_ROOT, entry);
|
|
309
|
+
if (!fs.existsSync(src)) continue;
|
|
310
|
+
copyUpdateRecursive(src, path.join(process.cwd(), entry), process.cwd());
|
|
311
|
+
}
|
|
312
|
+
console.log(`Alive-AI project updated to ${packageVersion()}.`);
|
|
313
|
+
console.log("Preserved config/, data/, mypics/, myvids/, .alive-ai/, and .cache/.");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function uninstallProject(args) {
|
|
317
|
+
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y");
|
|
318
|
+
const deleteProject = hasFlag(args, "--delete-project");
|
|
319
|
+
const target = process.cwd();
|
|
320
|
+
if (!fs.existsSync(path.join(target, "main.py")) || !fs.existsSync(path.join(target, "package.json"))) {
|
|
321
|
+
console.error("Run `alive-ai uninstall` from an Alive-AI project directory.");
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
if (!assumeYes) {
|
|
325
|
+
const answer = normalizeChoice(await ask("Remove Alive-AI runtime files, config, venv, cache, data, and media from this project? yes or no", "no", false), "no");
|
|
326
|
+
if (!["yes", "y"].includes(answer)) {
|
|
327
|
+
console.log("Uninstall cancelled.");
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const entries = new Set([
|
|
333
|
+
...COPY_ENTRIES,
|
|
334
|
+
"config",
|
|
335
|
+
"data",
|
|
336
|
+
"mypics",
|
|
337
|
+
"myvids",
|
|
338
|
+
".alive-ai",
|
|
339
|
+
".cache",
|
|
340
|
+
".env",
|
|
341
|
+
]);
|
|
342
|
+
for (const entry of entries) {
|
|
343
|
+
const dest = path.join(target, entry);
|
|
344
|
+
if (!fs.existsSync(dest)) continue;
|
|
345
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
346
|
+
console.log(`removed ${entry}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
fs.rmSync(updatePrefsPath(), { force: true });
|
|
351
|
+
} catch {}
|
|
352
|
+
|
|
353
|
+
if (deleteProject) {
|
|
354
|
+
const parent = path.dirname(target);
|
|
355
|
+
process.chdir(parent);
|
|
356
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
357
|
+
console.log(`Removed project directory: ${target}`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
console.log("Alive-AI local files removed. The project folder itself was kept.");
|
|
362
|
+
console.log("If you installed globally, remove the global CLI with: npm uninstall -g alive-ai");
|
|
363
|
+
}
|
|
364
|
+
|
|
168
365
|
async function setupProject(args) {
|
|
169
366
|
const dryRun = hasFlag(args, "--dry-run");
|
|
170
367
|
const assumeYes = hasFlag(args, "--yes") || hasFlag(args, "-y") || dryRun;
|
|
@@ -282,18 +479,44 @@ function findCommand(candidates) {
|
|
|
282
479
|
return null;
|
|
283
480
|
}
|
|
284
481
|
|
|
482
|
+
function pythonVersion(command) {
|
|
483
|
+
const result = spawnSync(command, ["-c", "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')"], {
|
|
484
|
+
encoding: "utf8",
|
|
485
|
+
});
|
|
486
|
+
if (result.status !== 0) return "";
|
|
487
|
+
return result.stdout.trim();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function findPython() {
|
|
491
|
+
const preferred = ["python3.12", "python3.11", "python3.13", "python3", "python"];
|
|
492
|
+
for (const command of preferred) {
|
|
493
|
+
const result = spawnSync(command, ["--version"], { stdio: "ignore" });
|
|
494
|
+
if (result.status !== 0) continue;
|
|
495
|
+
const version = pythonVersion(command);
|
|
496
|
+
const [major, minor] = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
497
|
+
if (major === 3 && minor >= 11) return { command, version };
|
|
498
|
+
}
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
|
|
285
502
|
async function doctor() {
|
|
286
|
-
const python =
|
|
503
|
+
const python = findPython();
|
|
287
504
|
const uv = findCommand(["uv"]);
|
|
288
505
|
const ffmpeg = findCommand(["ffmpeg"]);
|
|
289
506
|
const docker = findCommand(["docker"]);
|
|
290
507
|
const node = process.version;
|
|
291
508
|
const settings = readProjectSettings();
|
|
509
|
+
const venvPython = process.platform === "win32"
|
|
510
|
+
? path.join(process.cwd(), ".alive-ai", "venv", "Scripts", "python.exe")
|
|
511
|
+
: path.join(process.cwd(), ".alive-ai", "venv", "bin", "python");
|
|
292
512
|
|
|
293
513
|
console.log("Alive-AI doctor");
|
|
294
514
|
console.log(` system: ${os.platform()} ${os.arch()}`);
|
|
295
515
|
console.log(` node: ${node}`);
|
|
296
|
-
console.log(` python: ${python
|
|
516
|
+
console.log(` python: ${python ? `${python.command} ${python.version}` : "missing"}`);
|
|
517
|
+
if (fs.existsSync(venvPython)) {
|
|
518
|
+
console.log(` venv: ${pythonVersion(venvPython) || "unknown"} (${path.relative(process.cwd(), venvPython)})`);
|
|
519
|
+
}
|
|
297
520
|
console.log(` uv: ${uv || "missing, will use venv + pip"}`);
|
|
298
521
|
console.log(` ffmpeg: ${ffmpeg || "missing, voice conversion may be limited"}`);
|
|
299
522
|
console.log(` docker: ${docker || "missing, Redis can still be external"}`);
|
|
@@ -328,7 +551,7 @@ async function doctor() {
|
|
|
328
551
|
}
|
|
329
552
|
|
|
330
553
|
function ensurePythonEnv(skipInstall) {
|
|
331
|
-
const python =
|
|
554
|
+
const python = findPython();
|
|
332
555
|
if (!python) {
|
|
333
556
|
console.error("Python 3.11+ is required.");
|
|
334
557
|
process.exit(1);
|
|
@@ -347,8 +570,8 @@ function ensurePythonEnv(skipInstall) {
|
|
|
347
570
|
if (!fs.existsSync(pythonBin)) {
|
|
348
571
|
console.log("Creating Python environment...");
|
|
349
572
|
const create = uv
|
|
350
|
-
? spawnSync("uv", ["venv", venvDir], { stdio: "inherit" })
|
|
351
|
-
: spawnSync(python, ["-m", "venv", venvDir], { stdio: "inherit" });
|
|
573
|
+
? spawnSync("uv", ["venv", "--python", python.command, venvDir], { stdio: "inherit" })
|
|
574
|
+
: spawnSync(python.command, ["-m", "venv", venvDir], { stdio: "inherit" });
|
|
352
575
|
if (create.status !== 0) process.exit(create.status || 1);
|
|
353
576
|
}
|
|
354
577
|
|
|
@@ -363,23 +586,67 @@ function ensurePythonEnv(skipInstall) {
|
|
|
363
586
|
return pythonBin;
|
|
364
587
|
}
|
|
365
588
|
|
|
366
|
-
function startRuntime(args) {
|
|
589
|
+
async function startRuntime(args, options = {}) {
|
|
367
590
|
if (!fs.existsSync(path.join(process.cwd(), "config", "settings.json"))) {
|
|
368
591
|
console.log("Missing config/settings.json. Starting onboarding first.");
|
|
369
592
|
const setupArgs = process.stdin.isTTY ? [] : ["--yes"];
|
|
370
|
-
setupProject(setupArgs)
|
|
371
|
-
|
|
593
|
+
await setupProject(setupArgs);
|
|
594
|
+
}
|
|
595
|
+
await maybeCheckForUpdate(args);
|
|
596
|
+
const settings = readProjectSettings();
|
|
597
|
+
const secrets = readSimpleEnv(path.join(process.cwd(), "config", "secrets.env"));
|
|
598
|
+
const requestedInputChannel = argValue(args, "--input", null);
|
|
599
|
+
const effectiveInputChannel = (requestedInputChannel || settings.INPUT_CHANNEL || "telegram").toLowerCase();
|
|
600
|
+
const telegramToken = process.env.TELEGRAM_TOKEN || secrets.TELEGRAM_TOKEN || settings.telegram_token;
|
|
601
|
+
if (effectiveInputChannel === "telegram" && !telegramToken) {
|
|
602
|
+
console.error("Telegram is selected, but no Telegram bot token is configured.");
|
|
603
|
+
console.error("Run `npx . setup` to add a token, or use `npx . chat` for terminal mode.");
|
|
604
|
+
process.exit(1);
|
|
372
605
|
}
|
|
606
|
+
|
|
373
607
|
const pythonBin = ensurePythonEnv(hasFlag(args, "--skip-install"));
|
|
374
608
|
const extraArgs = [];
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
609
|
+
if (requestedInputChannel) extraArgs.push("--input", requestedInputChannel);
|
|
610
|
+
const dataPath = path.join(process.cwd(), "data");
|
|
611
|
+
ensureDir(dataPath);
|
|
612
|
+
const env = {
|
|
613
|
+
...process.env,
|
|
614
|
+
ALIVE_AI_ROOT: process.cwd(),
|
|
615
|
+
ALIVE_AI_DATA_PATH: dataPath,
|
|
616
|
+
DATA_PATH: dataPath,
|
|
617
|
+
HF_HOME: path.join(process.cwd(), ".cache", "huggingface"),
|
|
618
|
+
SENTENCE_TRANSFORMERS_HOME: path.join(process.cwd(), ".cache", "sentence-transformers"),
|
|
619
|
+
TRANSFORMERS_CACHE: path.join(process.cwd(), ".cache", "huggingface"),
|
|
620
|
+
TOKENIZERS_PARALLELISM: process.env.TOKENIZERS_PARALLELISM || "false",
|
|
621
|
+
};
|
|
622
|
+
if (options.tui) env.ALIVE_AI_TUI = "1";
|
|
623
|
+
|
|
624
|
+
const child = spawn(pythonBin, ["main.py", ...extraArgs], {
|
|
625
|
+
stdio: options.tui ? ["pipe", "pipe", "pipe"] : "inherit",
|
|
626
|
+
cwd: process.cwd(),
|
|
627
|
+
env,
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
if (options.tui) {
|
|
631
|
+
const code = await runRuntimeTui(child, {
|
|
632
|
+
dashboard: `http://127.0.0.1:${readProjectSettings().WEBUI_PORT || DEFAULT_PORT}`,
|
|
633
|
+
});
|
|
634
|
+
process.exitCode = code;
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
await new Promise((resolve) => {
|
|
639
|
+
child.on("exit", (code) => {
|
|
640
|
+
process.exitCode = code || 0;
|
|
641
|
+
resolve();
|
|
642
|
+
});
|
|
643
|
+
});
|
|
379
644
|
}
|
|
380
645
|
|
|
381
646
|
function startTerminalChat(args) {
|
|
382
|
-
|
|
647
|
+
const plain = hasFlag(args, "--plain");
|
|
648
|
+
const filteredArgs = args.filter((arg) => arg !== "--plain");
|
|
649
|
+
return startRuntime(["--input", "terminal", ...filteredArgs], { tui: !plain });
|
|
383
650
|
}
|
|
384
651
|
|
|
385
652
|
function demoHtml() {
|
|
@@ -464,10 +731,12 @@ async function main() {
|
|
|
464
731
|
if (!command || command === "--help" || command === "-h") return usage();
|
|
465
732
|
if (command === "init") return initProject(args);
|
|
466
733
|
if (command === "setup") return setupProject(args);
|
|
734
|
+
if (command === "update") return updateProject(args);
|
|
467
735
|
if (command === "demo") return startDemo(args);
|
|
468
736
|
if (command === "start") return startRuntime(args);
|
|
469
737
|
if (command === "chat") return startTerminalChat(args);
|
|
470
738
|
if (command === "doctor") return doctor();
|
|
739
|
+
if (command === "uninstall") return uninstallProject(args);
|
|
471
740
|
console.error(`Unknown command: ${command}`);
|
|
472
741
|
usage();
|
|
473
742
|
process.exit(1);
|