@niroai/niro 0.9.0 → 0.10.0
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/CHANGELOG.md +69 -0
- package/README.md +55 -1
- package/package.json +1 -1
- package/src/commands/ab.js +198 -0
- package/src/commands/add-project.js +11 -11
- package/src/commands/add-repo.js +23 -23
- package/src/commands/build.js +5 -5
- package/src/commands/delete.js +3 -3
- package/src/commands/edit.js +11 -11
- package/src/commands/info.js +2 -2
- package/src/commands/init.js +108 -87
- package/src/commands/login.js +25 -5
- package/src/commands/projects.js +1 -1
- package/src/commands/release.js +7 -7
- package/src/commands/remove.js +5 -5
- package/src/commands/start.js +567 -0
- package/src/commands/takeover.js +19 -19
- package/src/commands/watch.js +35 -35
- package/src/index.js +57 -3
- package/src/lib/folderSync.js +13 -8
- package/src/lib/moduleExcludeSuggestions.js +5 -5
- package/src/lib/nestedOutput.js +49 -0
- package/src/lib/onboardTarget.js +5 -5
- package/src/lib/prompt.js +3 -0
- package/src/lib/service.js +20 -12
- package/src/lib/spinner.js +28 -7
- package/src/lib/watchDaemon.js +9 -9
- package/src/mcp/setup.js +198 -160
- package/src/mcp/teardown.js +53 -53
package/src/lib/spinner.js
CHANGED
|
@@ -22,6 +22,17 @@ const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "
|
|
|
22
22
|
const FRAME_MS = 120;
|
|
23
23
|
const HEARTBEAT_MS = 30_000;
|
|
24
24
|
const ERASE_LINE = "\r\x1b[2K";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Current nesting depth as spaces.
|
|
28
|
+
*
|
|
29
|
+
* The spinner writes straight to the stream, so withIndent's console wrappers never see it.
|
|
30
|
+
* Left alone, the build status and its "Build completed" line print flush left in the middle
|
|
31
|
+
* of an indented step, which is where they were.
|
|
32
|
+
*/
|
|
33
|
+
function pad() {
|
|
34
|
+
return " ".repeat(require("./nestedOutput").currentIndent());
|
|
35
|
+
}
|
|
25
36
|
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
26
37
|
|
|
27
38
|
/**
|
|
@@ -40,8 +51,18 @@ function fit(line, width) {
|
|
|
40
51
|
* Labels can carry backend text (e.g. build log messages); an embedded
|
|
41
52
|
* newline would defeat the single-line overwrite, so flatten whitespace.
|
|
42
53
|
*/
|
|
54
|
+
/**
|
|
55
|
+
* Flatten a label to one line, but KEEP its leading indent.
|
|
56
|
+
*
|
|
57
|
+
* The old version trimmed both ends, which silently ate the indent callers use to place the
|
|
58
|
+
* label in the output tree: " Waiting for build..." came out flush left, four columns
|
|
59
|
+
* off from every line around it. Interior whitespace still collapses, because a newline would
|
|
60
|
+
* defeat the single-line overwrite.
|
|
61
|
+
*/
|
|
43
62
|
function sanitize(label) {
|
|
44
|
-
|
|
63
|
+
const text = String(label);
|
|
64
|
+
const indent = /^[ \t]*/.exec(text)[0].replace(/\t/g, " ");
|
|
65
|
+
return indent + text.replace(/\s+/g, " ").trim();
|
|
45
66
|
}
|
|
46
67
|
|
|
47
68
|
function start(label, opts = {}) {
|
|
@@ -58,9 +79,9 @@ function start(label, opts = {}) {
|
|
|
58
79
|
let stopped = false;
|
|
59
80
|
|
|
60
81
|
if (!isTty) {
|
|
61
|
-
stream.write(`${label}\n`);
|
|
82
|
+
stream.write(`${pad()}${label}\n`);
|
|
62
83
|
const timer = setInterval(() => {
|
|
63
|
-
stream.write(`${label} ${color.dim(`(still waiting — ${elapsed()}s)`)}\n`);
|
|
84
|
+
stream.write(`${pad()}${label} ${color.dim(`(still waiting — ${elapsed()}s)`)}\n`);
|
|
64
85
|
}, heartbeatMs);
|
|
65
86
|
if (timer.unref) timer.unref();
|
|
66
87
|
return {
|
|
@@ -68,13 +89,13 @@ function start(label, opts = {}) {
|
|
|
68
89
|
newLabel = sanitize(newLabel);
|
|
69
90
|
if (stopped || newLabel === label) return;
|
|
70
91
|
label = newLabel;
|
|
71
|
-
stream.write(`${label}\n`);
|
|
92
|
+
stream.write(`${pad()}${label}\n`);
|
|
72
93
|
},
|
|
73
94
|
stop(finalLine) {
|
|
74
95
|
clearInterval(timer);
|
|
75
96
|
if (stopped) return;
|
|
76
97
|
stopped = true;
|
|
77
|
-
if (finalLine) stream.write(finalLine + "\n");
|
|
98
|
+
if (finalLine) stream.write(pad() + finalLine + "\n");
|
|
78
99
|
},
|
|
79
100
|
};
|
|
80
101
|
}
|
|
@@ -83,7 +104,7 @@ function start(label, opts = {}) {
|
|
|
83
104
|
const render = () => {
|
|
84
105
|
const frame = FRAMES[i = (i + 1) % FRAMES.length];
|
|
85
106
|
const line = `${color.cyan(frame)} ${label} ${color.dim("(" + elapsed() + "s)")}`;
|
|
86
|
-
stream.write(ERASE_LINE + fit(line, (stream.columns || 80) - 1));
|
|
107
|
+
stream.write(ERASE_LINE + pad() + fit(line, (stream.columns || 80) - 1 - pad().length));
|
|
87
108
|
};
|
|
88
109
|
|
|
89
110
|
render();
|
|
@@ -101,7 +122,7 @@ function start(label, opts = {}) {
|
|
|
101
122
|
if (stopped) return;
|
|
102
123
|
stopped = true;
|
|
103
124
|
stream.write(ERASE_LINE);
|
|
104
|
-
if (finalLine) stream.write(finalLine + "\n");
|
|
125
|
+
if (finalLine) stream.write(pad() + finalLine + "\n");
|
|
105
126
|
},
|
|
106
127
|
};
|
|
107
128
|
}
|
package/src/lib/watchDaemon.js
CHANGED
|
@@ -33,7 +33,7 @@ const color = require("./color");
|
|
|
33
33
|
*/
|
|
34
34
|
async function ensureWatchDaemon({ skipPrompts = false } = {}) {
|
|
35
35
|
if (!service.pm2Available()) {
|
|
36
|
-
console.log(`\n
|
|
36
|
+
console.log(`\n ${color.yellow("⚠")} PM2 is not installed. Niro uses PM2 to keep your synced folders`);
|
|
37
37
|
console.log(` up to date in the background, including after a reboot.`);
|
|
38
38
|
|
|
39
39
|
if (skipPrompts || prompt.isNonInteractive()) {
|
|
@@ -49,29 +49,29 @@ async function ensureWatchDaemon({ skipPrompts = false } = {}) {
|
|
|
49
49
|
|
|
50
50
|
console.log("");
|
|
51
51
|
if (!service.installPm2()) {
|
|
52
|
-
console.log(`\n
|
|
52
|
+
console.log(`\n ${color.yellow("⚠")} PM2 install failed (a permission error is the usual cause).`);
|
|
53
53
|
console.log(` Try manually: ${color.cyan("sudo npm install -g pm2")} then ${color.cyan("niro service install")}`);
|
|
54
54
|
return { ok: false, reason: "pm2_install_failed" };
|
|
55
55
|
}
|
|
56
|
-
console.log(`
|
|
56
|
+
console.log(` ${color.green("✓")} PM2 installed.`);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
console.log(`\n
|
|
59
|
+
console.log(`\n ${color.cyan("●")} Installing niro-watch daemon (keeps your synced folders up to date, auto-starts on login)...`);
|
|
60
60
|
try {
|
|
61
61
|
// install() returns false when pm2 turns out not to be runnable after
|
|
62
62
|
// all — e.g. npm installed it into a global bin dir that isn't on PATH
|
|
63
63
|
// (npm exits 0 for that). Treat it as the failure it is; the old code's
|
|
64
64
|
// unchecked call here is how "✓ Done." lied to customers.
|
|
65
65
|
if (!service.install()) {
|
|
66
|
-
console.log(`
|
|
67
|
-
console.log(`
|
|
66
|
+
console.log(` ${color.yellow("⚠")} PM2 was installed but isn't runnable from this shell (its global bin`);
|
|
67
|
+
console.log(` directory may not be on your PATH). Open a NEW terminal and run ${color.cyan("niro service install")}.`);
|
|
68
68
|
return { ok: false, reason: "daemon_install_failed" };
|
|
69
69
|
}
|
|
70
|
-
console.log(`
|
|
70
|
+
console.log(` ${color.green("✓")} niro-watch is running and will start automatically on login.`);
|
|
71
71
|
return { ok: true };
|
|
72
72
|
} catch (err) {
|
|
73
|
-
console.log(`
|
|
74
|
-
console.log(`
|
|
73
|
+
console.log(` ${color.yellow("⚠")} Could not install the watch daemon: ${err.message}`);
|
|
74
|
+
console.log(` Run ${color.cyan("niro service install")} to try again, or ${color.cyan("niro watch")} to sync manually.`);
|
|
75
75
|
return { ok: false, reason: "daemon_install_failed" };
|
|
76
76
|
}
|
|
77
77
|
}
|