nemoris 0.1.7 → 0.1.9
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/package.json +1 -1
- package/src/cli/runtime-control.js +30 -5
- package/src/cli-main.js +15 -1
- package/src/onboarding/setup-checklist.js +18 -4
- package/src/onboarding/wizard.js +21 -1
- package/src/utils/env-loader.js +28 -0
package/package.json
CHANGED
|
@@ -487,24 +487,49 @@ export async function cleanupOrphanedDaemonProcesses({
|
|
|
487
487
|
|
|
488
488
|
export async function startDirectDaemon({
|
|
489
489
|
projectRoot,
|
|
490
|
-
cliEntryPath
|
|
490
|
+
cliEntryPath,
|
|
491
491
|
fsImpl = fs,
|
|
492
492
|
spawnImpl = spawn,
|
|
493
493
|
processImpl = process,
|
|
494
494
|
} = {}) {
|
|
495
|
+
// Resolve CLI entry: explicit path > source repo > which nemoris > package __dirname
|
|
496
|
+
if (!cliEntryPath) {
|
|
497
|
+
const srcCliPath = path.join(projectRoot, "src", "cli.js");
|
|
498
|
+
if (fsImpl.existsSync(srcCliPath)) {
|
|
499
|
+
cliEntryPath = srcCliPath;
|
|
500
|
+
} else {
|
|
501
|
+
try {
|
|
502
|
+
const { execFileSync } = await import("node:child_process");
|
|
503
|
+
const whichOut = execFileSync("which", ["nemoris"], { encoding: "utf8", timeout: 3000 }).trim();
|
|
504
|
+
if (whichOut) {
|
|
505
|
+
cliEntryPath = whichOut;
|
|
506
|
+
}
|
|
507
|
+
} catch { /* which not available */ }
|
|
508
|
+
}
|
|
509
|
+
if (!cliEntryPath) {
|
|
510
|
+
// Fallback: resolve from this module's own package
|
|
511
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
512
|
+
cliEntryPath = path.join(__dirname, "..", "cli.js");
|
|
513
|
+
}
|
|
514
|
+
}
|
|
495
515
|
const { stateDir, pidFile, stdoutLog, stderrLog } = getDaemonPaths(projectRoot);
|
|
496
516
|
fsImpl.mkdirSync(stateDir, { recursive: true });
|
|
497
517
|
const stdoutFd = fsImpl.openSync(stdoutLog, "a");
|
|
498
518
|
const stderrFd = fsImpl.openSync(stderrLog, "a");
|
|
499
519
|
|
|
500
520
|
try {
|
|
521
|
+
// Only set NEMORIS_INSTALL_DIR for the child when the path is a clean
|
|
522
|
+
// data directory. If it's the source repo (has package.json), the child
|
|
523
|
+
// will detect it via CWD detection instead — avoids the source repo guard.
|
|
524
|
+
const childEnv = { ...processImpl.env };
|
|
525
|
+
const looksLikeSourceRepo = fsImpl.existsSync(path.join(projectRoot, "package.json"));
|
|
526
|
+
if (!looksLikeSourceRepo) {
|
|
527
|
+
childEnv.NEMORIS_INSTALL_DIR = projectRoot;
|
|
528
|
+
}
|
|
501
529
|
const child = spawnImpl(processImpl.execPath, [cliEntryPath, "serve-daemon", "live"], {
|
|
502
530
|
cwd: projectRoot,
|
|
503
531
|
detached: true,
|
|
504
|
-
env:
|
|
505
|
-
...processImpl.env,
|
|
506
|
-
NEMORIS_INSTALL_DIR: projectRoot,
|
|
507
|
-
},
|
|
532
|
+
env: childEnv,
|
|
508
533
|
stdio: ["ignore", stdoutFd, stderrFd],
|
|
509
534
|
});
|
|
510
535
|
child.unref?.();
|
package/src/cli-main.js
CHANGED
|
@@ -2745,13 +2745,27 @@ export async function main(argv = process.argv) {
|
|
|
2745
2745
|
});
|
|
2746
2746
|
} else if (command === "doctor") {
|
|
2747
2747
|
const { runDoctor, formatDoctorReport } = await import("./onboarding/doctor.js");
|
|
2748
|
-
const
|
|
2748
|
+
const doctorInstallDir = resolveRuntimeInstallDir();
|
|
2749
|
+
const results = await runDoctor(doctorInstallDir);
|
|
2749
2750
|
const json = rest.includes("--json");
|
|
2751
|
+
const fix = rest.includes("--fix");
|
|
2750
2752
|
if (json) {
|
|
2751
2753
|
console.log(JSON.stringify(results, null, 2));
|
|
2752
2754
|
} else {
|
|
2753
2755
|
console.log(formatDoctorReport(results));
|
|
2754
2756
|
}
|
|
2757
|
+
// Auto-repair: clean stale PID file
|
|
2758
|
+
if (fix && results.daemon?.stalePidFile) {
|
|
2759
|
+
const { pidFile } = getDaemonPaths(doctorInstallDir);
|
|
2760
|
+
try {
|
|
2761
|
+
fs.unlinkSync(pidFile);
|
|
2762
|
+
console.log("\n \u2705 Cleaned stale PID file. Run: nemoris start");
|
|
2763
|
+
} catch {
|
|
2764
|
+
console.log("\n \u274c Could not remove stale PID file.");
|
|
2765
|
+
}
|
|
2766
|
+
} else if (!fix && results.daemon?.stalePidFile) {
|
|
2767
|
+
console.log("\n Tip: run nemoris doctor --fix to clean the stale PID file.");
|
|
2768
|
+
}
|
|
2755
2769
|
return results.exitCode;
|
|
2756
2770
|
} else if (command === "battle") {
|
|
2757
2771
|
const { runBattle, parseBattleFlags } = await import("./battle.js");
|
|
@@ -41,7 +41,18 @@ function detectProvider(installDir) {
|
|
|
41
41
|
const files = fs.readdirSync(providersDir).filter((f) => f.endsWith(".toml"));
|
|
42
42
|
if (files.length === 0) return { configured: false };
|
|
43
43
|
const name = files[0].replace(/\.toml$/, "");
|
|
44
|
-
|
|
44
|
+
// Read primary model from the first provider config
|
|
45
|
+
let primaryModel = "";
|
|
46
|
+
try {
|
|
47
|
+
const content = fs.readFileSync(path.join(providersDir, files[0]), "utf8");
|
|
48
|
+
const config = parseToml(content);
|
|
49
|
+
const models = config.models || {};
|
|
50
|
+
const firstModelKey = Object.keys(models)[0];
|
|
51
|
+
if (firstModelKey && models[firstModelKey]?.id) {
|
|
52
|
+
primaryModel = models[firstModelKey].id;
|
|
53
|
+
}
|
|
54
|
+
} catch { /* non-fatal */ }
|
|
55
|
+
return { configured: true, name, count: files.length, primaryModel };
|
|
45
56
|
} catch {
|
|
46
57
|
return { configured: false };
|
|
47
58
|
}
|
|
@@ -96,9 +107,12 @@ export function formatSetupChecklist(checklist) {
|
|
|
96
107
|
: "Identity";
|
|
97
108
|
item(checklist.identity, idLabel, "nemoris setup");
|
|
98
109
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
let provLabel = "Provider";
|
|
111
|
+
if (checklist.provider.configured) {
|
|
112
|
+
const model = checklist.provider.primaryModel || "";
|
|
113
|
+
const modelSuffix = model ? ` \u00b7 ${model}` : "";
|
|
114
|
+
provLabel = `Provider ${checklist.provider.name}${modelSuffix}`;
|
|
115
|
+
}
|
|
102
116
|
item(checklist.provider, provLabel, "nemoris setup");
|
|
103
117
|
|
|
104
118
|
item(checklist.telegram, "Telegram", "nemoris setup telegram");
|
package/src/onboarding/wizard.js
CHANGED
|
@@ -336,16 +336,36 @@ async function runFastPathWizard({ installDir }) {
|
|
|
336
336
|
"Setup Complete"
|
|
337
337
|
);
|
|
338
338
|
|
|
339
|
+
// Offer Telegram setup inline
|
|
340
|
+
if (!checklist.telegram.configured) {
|
|
341
|
+
const wantTelegram = await prompter.confirm({
|
|
342
|
+
message: "Set up Telegram now?",
|
|
343
|
+
initialValue: false,
|
|
344
|
+
});
|
|
345
|
+
if (wantTelegram) {
|
|
346
|
+
await runTelegramPhase({
|
|
347
|
+
installDir,
|
|
348
|
+
agentId,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
339
353
|
await prompter.outro("Run: nemoris start");
|
|
340
354
|
|
|
341
355
|
try {
|
|
342
356
|
const pkg = JSON.parse(fs.readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
357
|
+
const caps = { identity: true, provider: true };
|
|
358
|
+
// Re-check telegram after possible inline setup
|
|
359
|
+
const updatedChecklist = buildSetupChecklist(installDir);
|
|
360
|
+
if (updatedChecklist.telegram.configured || updatedChecklist.telegram.pending) {
|
|
361
|
+
caps.telegram = true;
|
|
362
|
+
}
|
|
343
363
|
writeWizardMetadata(installDir, {
|
|
344
364
|
lastRunVersion: pkg.version || "",
|
|
345
365
|
lastRunCommand: "setup",
|
|
346
366
|
lastRunMode: "fast",
|
|
347
367
|
lastRunFlow: "interactive",
|
|
348
|
-
capabilities:
|
|
368
|
+
capabilities: caps,
|
|
349
369
|
});
|
|
350
370
|
} catch {
|
|
351
371
|
// Non-fatal
|
package/src/utils/env-loader.js
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the .env file path. Priority:
|
|
7
|
+
* 1. NEMORIS_INSTALL_DIR/.env (explicit data dir)
|
|
8
|
+
* 2. CWD/.env (dev mode — running from source repo)
|
|
9
|
+
* 3. ~/.nemoris-data/.env (default install dir)
|
|
10
|
+
* 4. package dir/.env (legacy fallback)
|
|
11
|
+
*/
|
|
3
12
|
export function resolveEnvPath(srcDir) {
|
|
13
|
+
// 1. Explicit install dir
|
|
14
|
+
if (process.env.NEMORIS_INSTALL_DIR) {
|
|
15
|
+
return path.join(process.env.NEMORIS_INSTALL_DIR, ".env");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 2. Dev mode — CWD is source repo
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
if (fs.existsSync(path.join(cwd, "package.json")) &&
|
|
21
|
+
fs.existsSync(path.join(cwd, "src", "cli.js"))) {
|
|
22
|
+
return path.join(cwd, ".env");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 3. Default data dir
|
|
26
|
+
const defaultEnv = path.join(os.homedir(), ".nemoris-data", ".env");
|
|
27
|
+
if (fs.existsSync(defaultEnv)) {
|
|
28
|
+
return defaultEnv;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 4. Legacy: package dir
|
|
4
32
|
return path.resolve(srcDir, "..", ".env");
|
|
5
33
|
}
|