@nonbot/cli 0.9.6 → 0.9.7
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/dist/commands/daemon.js +16 -47
- package/dist/lib/output.js +81 -37
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/commands/daemon.js
CHANGED
|
@@ -13,7 +13,7 @@ import { applyPaneTitle, applyPaneState } from '../lib/pane-title.js';
|
|
|
13
13
|
import { installService, uninstallService } from '../lib/service.js';
|
|
14
14
|
import { detectTmuxSession, inTmuxSession, nonbotTmuxOptOut, resolveTerminal, } from '../lib/terminal.js';
|
|
15
15
|
import { VERSION } from '../version.js';
|
|
16
|
-
import { errorBlock, statusRow, daemonOpener, daemonCloser,
|
|
16
|
+
import { errorBlock, statusRow, daemonOpener, daemonCloser, activationCard, runSummary, formatElapsed, liveFooter, needsYouBanner, resumedLine, buildPaneBorderFormat, buildTmuxStatusLeft, buildTmuxStatusRight, WORDMARK_WIDTH, c, } from '../lib/output.js';
|
|
17
17
|
export const POLL_FAST_MS = 2000;
|
|
18
18
|
export const POLL_MAX_MS = 30000;
|
|
19
19
|
export const POLL_INTERVAL_MS = POLL_FAST_MS;
|
|
@@ -89,12 +89,12 @@ export function applyNonbotTmuxConfig(deps = {}) {
|
|
|
89
89
|
tmux('set-option', '-g', 'pane-border-lines', 'heavy');
|
|
90
90
|
tmux('set-option', '-g', 'status', 'on');
|
|
91
91
|
tmux('set-option', '-g', 'status-interval', '5');
|
|
92
|
-
tmux('set-option', '-g', 'status-style', 'bg=#
|
|
92
|
+
tmux('set-option', '-g', 'status-style', 'bg=#0a0d17,fg=#8a93a6');
|
|
93
93
|
tmux('set-option', '-g', 'status-left-length', '60');
|
|
94
94
|
tmux('set-option', '-g', 'status-right-length', '120');
|
|
95
95
|
tmux('set-option', '-g', 'status-left', buildTmuxStatusLeft({ ascii }));
|
|
96
96
|
tmux('set-option', '-g', 'status-right', buildTmuxStatusRight({ ascii }));
|
|
97
|
-
tmux('set-option', '-g', 'window-status-current-style', 'fg=#
|
|
97
|
+
tmux('set-option', '-g', 'window-status-current-style', 'fg=#0a0d17,bg=#6c8cff,bold');
|
|
98
98
|
tmux('refresh-client', '-S');
|
|
99
99
|
}
|
|
100
100
|
if (!stdoutIsTTY)
|
|
@@ -289,13 +289,11 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
289
289
|
catch {
|
|
290
290
|
}
|
|
291
291
|
};
|
|
292
|
-
const
|
|
293
|
-
const footerEnabled = stdoutIsTTY && !headless;
|
|
294
|
-
let footerActive = false;
|
|
292
|
+
const log = (s) => rawLog(s);
|
|
295
293
|
let lastPollOk = true;
|
|
296
294
|
const noBell = process.env.NONBOT_NO_BELL === '1' || process.env.NONBOT_NO_BELL === 'true';
|
|
297
|
-
const
|
|
298
|
-
const
|
|
295
|
+
const stdoutIsTTY = deps.stdoutIsTTY ?? (process.stdout?.isTTY === true && !process.env.VITEST);
|
|
296
|
+
const pollHealth = () => {
|
|
299
297
|
if (openPrompts.size > 0)
|
|
300
298
|
return 'waiting';
|
|
301
299
|
if (!lastPollOk)
|
|
@@ -304,7 +302,7 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
304
302
|
return 'backoff';
|
|
305
303
|
return 'live';
|
|
306
304
|
};
|
|
307
|
-
const
|
|
305
|
+
const heartbeatLine = () => liveFooter({
|
|
308
306
|
running: trackedPanes.size,
|
|
309
307
|
waiting: openPrompts.size,
|
|
310
308
|
done: doneCount,
|
|
@@ -312,33 +310,8 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
312
310
|
nextPollMs: fixedInterval ?? currentInterval,
|
|
313
311
|
version: `v${VERSION}`,
|
|
314
312
|
machine: machineName,
|
|
315
|
-
health:
|
|
313
|
+
health: pollHealth(),
|
|
316
314
|
});
|
|
317
|
-
const refreshFooter = () => {
|
|
318
|
-
if (footerEnabled && footerActive)
|
|
319
|
-
rawLog(ERASE + renderFooter());
|
|
320
|
-
};
|
|
321
|
-
const startFooter = () => {
|
|
322
|
-
if (!footerEnabled)
|
|
323
|
-
return;
|
|
324
|
-
footerActive = true;
|
|
325
|
-
rawLog(ERASE + renderFooter());
|
|
326
|
-
};
|
|
327
|
-
const log = (s) => {
|
|
328
|
-
if (!footerEnabled || !footerActive) {
|
|
329
|
-
rawLog(s);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
rawLog(ERASE);
|
|
333
|
-
rawLog(s);
|
|
334
|
-
rawLog(ERASE + renderFooter());
|
|
335
|
-
};
|
|
336
|
-
const dropFooter = () => {
|
|
337
|
-
if (footerActive) {
|
|
338
|
-
footerActive = false;
|
|
339
|
-
rawLog(ERASE);
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
315
|
const emitSummary = () => {
|
|
343
316
|
log(runSummary({ running: trackedPanes.size, done: doneCount, failed: failedCount }) + '\n');
|
|
344
317
|
pushTmuxStatus();
|
|
@@ -372,7 +345,7 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
372
345
|
}) +
|
|
373
346
|
'\n');
|
|
374
347
|
retitlePane('waiting', paneId, meta?.story ?? '');
|
|
375
|
-
if (!noBell &&
|
|
348
|
+
if (!noBell && stdoutIsTTY)
|
|
376
349
|
rawLog('\x07');
|
|
377
350
|
changed = true;
|
|
378
351
|
}
|
|
@@ -391,7 +364,6 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
391
364
|
}
|
|
392
365
|
if (changed) {
|
|
393
366
|
pushTmuxStatus();
|
|
394
|
-
refreshFooter();
|
|
395
367
|
}
|
|
396
368
|
};
|
|
397
369
|
let running = true;
|
|
@@ -401,7 +373,6 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
401
373
|
running = false;
|
|
402
374
|
for (const id of [...runHeartbeats.keys()])
|
|
403
375
|
stopHeartbeat(id);
|
|
404
|
-
dropFooter();
|
|
405
376
|
pushTmuxStatus(0, true);
|
|
406
377
|
rawLog('\n' + statusRow('✓', 'daemon stopped', 'Ctrl-C received') + '\n');
|
|
407
378
|
process.exit(0);
|
|
@@ -428,11 +399,16 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
428
399
|
];
|
|
429
400
|
if (tmuxSessionName)
|
|
430
401
|
badgeSublines.push(`tmux host: ${tmuxSessionName}`);
|
|
402
|
+
const stdoutCols = deps.stdoutColumns ?? process.stdout?.columns ?? 0;
|
|
403
|
+
const stdoutRows = deps.stdoutRows ?? process.stdout?.rows ?? 0;
|
|
404
|
+
const compactMast = stdoutIsTTY &&
|
|
405
|
+
((stdoutCols > 0 && stdoutCols < WORDMARK_WIDTH + 4) || (stdoutRows > 0 && stdoutRows < 16));
|
|
431
406
|
log(daemonOpener(`v${VERSION}`, {
|
|
432
407
|
state: 'IDLE',
|
|
433
408
|
badgeColor: 'green',
|
|
434
409
|
badgeLabel: 'CONNECTED',
|
|
435
410
|
badgeSublines,
|
|
411
|
+
compact: compactMast,
|
|
436
412
|
}) + '\n\n');
|
|
437
413
|
const headerMarker = c.green('[ + ]');
|
|
438
414
|
const headerLabel = c.white('LISTENING · READY FOR ACTIVATIONS');
|
|
@@ -458,7 +434,6 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
458
434
|
}) + '\n');
|
|
459
435
|
log('\n');
|
|
460
436
|
pushTmuxStatus();
|
|
461
|
-
startFooter();
|
|
462
437
|
while (running) {
|
|
463
438
|
let firedThisPoll = false;
|
|
464
439
|
try {
|
|
@@ -719,12 +694,8 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
719
694
|
emitSummary();
|
|
720
695
|
}
|
|
721
696
|
}
|
|
722
|
-
if (!firedThisPoll && pendingKills.length === 0 && !
|
|
723
|
-
log(
|
|
724
|
-
pending: 0,
|
|
725
|
-
sleepMs: fixedInterval ?? currentInterval,
|
|
726
|
-
backingOff: fixedInterval === undefined && currentInterval > POLL_FAST_MS,
|
|
727
|
-
}) + '\n');
|
|
697
|
+
if (!firedThisPoll && pendingKills.length === 0 && !tmuxChromeEnabled) {
|
|
698
|
+
log(heartbeatLine() + '\n');
|
|
728
699
|
}
|
|
729
700
|
evictOldestToCap(seen, 500);
|
|
730
701
|
}
|
|
@@ -736,13 +707,11 @@ export async function runDaemonCommand(args = [], options = {}, deps = {}) {
|
|
|
736
707
|
if (options.oneShot)
|
|
737
708
|
break;
|
|
738
709
|
if (fixedInterval !== undefined) {
|
|
739
|
-
refreshFooter();
|
|
740
710
|
await sleep(fixedInterval);
|
|
741
711
|
}
|
|
742
712
|
else {
|
|
743
713
|
if (firedThisPoll)
|
|
744
714
|
currentInterval = POLL_FAST_MS;
|
|
745
|
-
refreshFooter();
|
|
746
715
|
await sleep(currentInterval);
|
|
747
716
|
if (!firedThisPoll) {
|
|
748
717
|
currentInterval = Math.min(currentInterval * 2, maxIntervalMs);
|
package/dist/lib/output.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { ANSI, renderBannerHeader } from './banner.js';
|
|
2
2
|
export { ANSI };
|
|
3
3
|
export const BRAND = {
|
|
4
|
-
white: '\x1b[38;2;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
white: '\x1b[38;2;240;235;226m',
|
|
5
|
+
cream: '\x1b[38;2;240;235;226m',
|
|
6
|
+
blue: '\x1b[38;2;108;140;255m',
|
|
7
|
+
indigo: '\x1b[38;2;155;108;240m',
|
|
8
|
+
cyan: '\x1b[38;2;88;199;230m',
|
|
9
|
+
green: '\x1b[38;2;69;209;127m',
|
|
10
|
+
amber: '\x1b[38;2;255;180;84m',
|
|
11
|
+
red: '\x1b[38;2;255;92;108m',
|
|
12
|
+
muted: '\x1b[38;2;138;147;166m',
|
|
10
13
|
bold: '\x1b[1m',
|
|
11
14
|
reset: '\x1b[0m',
|
|
12
15
|
underline: '\x1b[4m',
|
|
@@ -31,6 +34,9 @@ function paint(s, code, stream = process.stdout) {
|
|
|
31
34
|
}
|
|
32
35
|
export const c = {
|
|
33
36
|
white: (s, stream) => paintRaw(s, BRAND.white, stream),
|
|
37
|
+
cream: (s, stream) => paintRaw(s, BRAND.cream, stream),
|
|
38
|
+
blue: (s, stream) => paintRaw(s, BRAND.blue, stream),
|
|
39
|
+
indigo: (s, stream) => paintRaw(s, BRAND.indigo, stream),
|
|
34
40
|
cyan: (s, stream) => paintRaw(s, BRAND.cyan, stream),
|
|
35
41
|
green: (s, stream) => paintRaw(s, BRAND.green, stream),
|
|
36
42
|
amber: (s, stream) => paintRaw(s, BRAND.amber, stream),
|
|
@@ -137,17 +143,7 @@ const WORDMARK_LINES = [
|
|
|
137
143
|
];
|
|
138
144
|
export const WORDMARK_WIDTH = 63;
|
|
139
145
|
export const WORDMARK_HEIGHT = 6;
|
|
140
|
-
export const
|
|
141
|
-
'#13b9e2', '#1abadc', '#20bad7', '#27bbd1', '#2ebbcb', '#35bcc5', '#3bbdc0',
|
|
142
|
-
'#42bdba', '#49beb4', '#50bfae', '#50bfae', '#5dc0a3', '#64c09d', '#6bc197',
|
|
143
|
-
'#71c292', '#78c28c', '#7fc386', '#86c380', '#8cc47b', '#93c575', '#93c575',
|
|
144
|
-
'#a1c669', '#a7c664', '#aec75e', '#b5c858', '#bcc852', '#c2c94d', '#c9ca47',
|
|
145
|
-
'#d0ca41', '#d7cb3b', '#ddcb36', '#e4cc30', '#e0c536', '#dcbf3b', '#d8b841',
|
|
146
|
-
'#d4b246', '#d0ab4c', '#cca551', '#c89e57', '#c4975d', '#c09162', '#bc8a68',
|
|
147
|
-
'#bc8a68', '#bc8a68', '#b07679', '#ac707e', '#a86984', '#a36389', '#9f5c8f',
|
|
148
|
-
'#9b5694', '#974f9a', '#9348a0', '#8f42a5', '#8f42a5', '#8735b0', '#832eb6',
|
|
149
|
-
'#7f27bc', '#7b21c1', '#771ac7', '#7314cc', '#6f0dd2', '#6b07d7', '#6700dd',
|
|
150
|
-
];
|
|
146
|
+
export const WORDMARK_DOT_COLS = [31, 32, 33];
|
|
151
147
|
function hexFg(hex) {
|
|
152
148
|
const n = parseInt(hex.slice(1), 16);
|
|
153
149
|
return `\x1b[38;2;${(n >> 16) & 255};${(n >> 8) & 255};${n & 255}m`;
|
|
@@ -163,8 +159,8 @@ function paintWordmarkLine(line, stream = process.stdout) {
|
|
|
163
159
|
out += ' ';
|
|
164
160
|
continue;
|
|
165
161
|
}
|
|
166
|
-
const
|
|
167
|
-
out += BRAND.bold +
|
|
162
|
+
const colour = WORDMARK_DOT_COLS.includes(i) ? BRAND.blue : BRAND.cream;
|
|
163
|
+
out += BRAND.bold + colour + ch + BRAND.reset;
|
|
168
164
|
}
|
|
169
165
|
return out;
|
|
170
166
|
}
|
|
@@ -333,6 +329,43 @@ export function fixBlock(args) {
|
|
|
333
329
|
});
|
|
334
330
|
return lines.join('\n');
|
|
335
331
|
}
|
|
332
|
+
export function promptLine(opts = {}) {
|
|
333
|
+
const stream = opts.stream ?? process.stdout;
|
|
334
|
+
const cursor = asciiOnly() ? '_' : '▌';
|
|
335
|
+
return (`${c.cyan('~', stream)} ${c.green('$', stream)} ` +
|
|
336
|
+
`${c.bold(c.cream('nonbot', stream), stream)} ${c.blue('run', stream)}` +
|
|
337
|
+
c.cream(cursor, stream));
|
|
338
|
+
}
|
|
339
|
+
export function gradientRule(width, opts = {}) {
|
|
340
|
+
const stream = opts.stream ?? process.stdout;
|
|
341
|
+
const n = Math.max(0, Math.floor(width));
|
|
342
|
+
const glyph = asciiOnly() ? '-' : '━';
|
|
343
|
+
if (!isTTY(stream) || n === 0)
|
|
344
|
+
return glyph.repeat(n);
|
|
345
|
+
const from = [0x6c, 0x8c, 0xff];
|
|
346
|
+
const to = [0x9b, 0x6c, 0xf0];
|
|
347
|
+
let out = '';
|
|
348
|
+
for (let i = 0; i < n; i++) {
|
|
349
|
+
const t = n === 1 ? 0 : i / (n - 1);
|
|
350
|
+
const r = Math.round(from[0] + (to[0] - from[0]) * t);
|
|
351
|
+
const g = Math.round(from[1] + (to[1] - from[1]) * t);
|
|
352
|
+
const b = Math.round(from[2] + (to[2] - from[2]) * t);
|
|
353
|
+
out += `\x1b[38;2;${r};${g};${b}m${glyph}`;
|
|
354
|
+
}
|
|
355
|
+
return out + BRAND.reset;
|
|
356
|
+
}
|
|
357
|
+
export function compactMasthead(version, opts = {}) {
|
|
358
|
+
const stream = opts.stream ?? process.stdout;
|
|
359
|
+
const state = opts.state ?? 'IDLE';
|
|
360
|
+
const badgeColor = opts.badgeColor ?? 'green';
|
|
361
|
+
const badgeLabel = opts.badgeLabel ?? 'CONNECTED';
|
|
362
|
+
const dotGlyph = asciiOnly() ? '*' : '●';
|
|
363
|
+
const brand = `${c.bold(c.cream('non.bot', stream), stream)}${c.blue('.', stream)}`;
|
|
364
|
+
const badge = colorize(`${dotGlyph} ${badgeLabel}`, badgeColor, stream);
|
|
365
|
+
const sep = c.muted('·', stream);
|
|
366
|
+
const tail = [c.muted(`daemon ${version}`, stream), sep, c.muted(state, stream)].join(' ');
|
|
367
|
+
return `${brand} ${badge} ${tail}`;
|
|
368
|
+
}
|
|
336
369
|
export const DAEMON_TAGLINES = [
|
|
337
370
|
"click ▶ on the canvas. terminal opens here. that's it.",
|
|
338
371
|
];
|
|
@@ -348,10 +381,19 @@ export function daemonOpener(version, opts = {}) {
|
|
|
348
381
|
const seed = opts.seed ?? Date.now();
|
|
349
382
|
const tagline = pickDaemonTagline(seed, opts.pool);
|
|
350
383
|
const state = opts.state ?? 'IDLE';
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
384
|
+
if (opts.compact) {
|
|
385
|
+
return compactMasthead(version, {
|
|
386
|
+
state,
|
|
387
|
+
badgeColor: opts.badgeColor ?? 'green',
|
|
388
|
+
badgeLabel: opts.badgeLabel ?? 'CONNECTED',
|
|
389
|
+
stream,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const topLine = opts.breadcrumb === ''
|
|
393
|
+
? ''
|
|
394
|
+
: opts.breadcrumb
|
|
395
|
+
? ` ${c.cyan(opts.breadcrumb, stream)}\n\n`
|
|
396
|
+
: ` ${promptLine({ stream })}\n\n`;
|
|
355
397
|
const stamp = renderWordmarkWithBadge({
|
|
356
398
|
badgeColor: opts.badgeColor ?? 'green',
|
|
357
399
|
badgeLabel: opts.badgeLabel ?? 'CONNECTED',
|
|
@@ -369,8 +411,10 @@ export function daemonOpener(version, opts = {}) {
|
|
|
369
411
|
subParts.push(c.muted(trackedCaps(state), stream));
|
|
370
412
|
}
|
|
371
413
|
const subtitleLine = ` ${subParts.join(' ')}`;
|
|
414
|
+
const ruleWidth = Math.min(46, Math.max(10, terminalWidth(opts.width) - 2));
|
|
415
|
+
const ruleLine = ` ${gradientRule(ruleWidth, { stream })}`;
|
|
372
416
|
const taglineLine = tagline ? `\n\n ${c.muted(tagline, stream)}` : '';
|
|
373
|
-
return `${
|
|
417
|
+
return `${topLine}${stamp}\n\n${ruleLine}\n\n${subtitleLine}${taglineLine}`;
|
|
374
418
|
}
|
|
375
419
|
export function daemonCloser(opts = {}) {
|
|
376
420
|
const stream = opts.stream ?? process.stdout;
|
|
@@ -475,27 +519,27 @@ export function resumedLine(activationId, stream = process.stdout) {
|
|
|
475
519
|
}
|
|
476
520
|
export function buildPaneBorderFormat(opts = {}) {
|
|
477
521
|
const warn = opts.ascii ? '!! ' : '⚠ ';
|
|
478
|
-
return (`#{?#{==:#{@nb_state},waiting},#[fg
|
|
522
|
+
return (`#{?#{==:#{@nb_state},waiting},#[fg=#ffb454 bold]${warn},}` +
|
|
479
523
|
'#[fg=' +
|
|
480
|
-
'#{?#{==:#{@nb_state},waiting},
|
|
481
|
-
'#{?#{==:#{@nb_state},failed},
|
|
482
|
-
'#{?#{==:#{@nb_state},completed},
|
|
483
|
-
'#{?#{==:#{@nb_state},stopping}
|
|
524
|
+
'#{?#{==:#{@nb_state},waiting},#ffb454,' +
|
|
525
|
+
'#{?#{==:#{@nb_state},failed},#ff5c6c,' +
|
|
526
|
+
'#{?#{==:#{@nb_state},completed},#45d17f,' +
|
|
527
|
+
'#{?#{==:#{@nb_state},stopping},#ffb454,#58c7e6}}}}' +
|
|
484
528
|
']' +
|
|
485
529
|
' #{pane_title} ');
|
|
486
530
|
}
|
|
487
531
|
export function buildTmuxStatusLeft(opts = {}) {
|
|
488
532
|
const dot = opts.ascii ? '* ' : '● ';
|
|
489
|
-
return (' #[fg=#
|
|
490
|
-
'#{?#{==:#{@nb_halted},1},#[fg=#
|
|
491
|
-
`#[fg=#
|
|
533
|
+
return (' #[fg=#f0ebe2,bold]non#[fg=#6c8cff].#[fg=#f0ebe2]bot#[default] ' +
|
|
534
|
+
'#{?#{==:#{@nb_halted},1},#[fg=#ff5c6c]HALTED,' +
|
|
535
|
+
`#[fg=#45d17f]${dot}CONNECTED}#[default] `);
|
|
492
536
|
}
|
|
493
537
|
export function buildTmuxStatusRight(opts = {}) {
|
|
494
538
|
const warn = opts.ascii ? '!!' : '⚠';
|
|
495
539
|
const dot = '·';
|
|
496
|
-
return ('#[fg=#
|
|
497
|
-
`#[fg=#
|
|
498
|
-
`#{?#{@nb_failed}, #[fg=#
|
|
499
|
-
`#{?#{@nb_needsyou},#[fg=#
|
|
500
|
-
`#[fg=#
|
|
540
|
+
return ('#[fg=#45d17f]#{@nb_running} run#[default] ' +
|
|
541
|
+
`#[fg=#8a93a6]${dot} #{@nb_done} done` +
|
|
542
|
+
`#{?#{@nb_failed}, #[fg=#ff5c6c]${dot} #{@nb_failed} fail,}#[default] ` +
|
|
543
|
+
`#{?#{@nb_needsyou},#[fg=#ffb454,bold]${dot} ${warn} #{@nb_needsyou} NEEDS YOU ,}` +
|
|
544
|
+
`#[fg=#58c7e6]${dot} #{host_short} ${dot} poll #{@nb_poll}s `);
|
|
501
545
|
}
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.9.
|
|
1
|
+
export const VERSION = '0.9.7';
|
package/package.json
CHANGED