noninteractive 0.3.26 → 0.3.30

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.
@@ -172,11 +172,14 @@ function runDaemon(sessionName, executable, args) {
172
172
  let outputBuffer = "";
173
173
  let processExited = false;
174
174
  let exitCode = null;
175
+ let lastReadLength = 0;
175
176
  const detectedUrls = new Set;
176
177
  const reportedUrls = new Set;
177
178
  const waiters = [];
178
179
  let notifyDebounce = null;
179
180
  const NOTIFY_SETTLE_MS = 50;
181
+ let lastStdinWrite = 0;
182
+ const INPUT_SETTLE_MS = 150;
180
183
  function notifyWaiters() {
181
184
  if (waiters.length === 0)
182
185
  return;
@@ -282,14 +285,17 @@ function runDaemon(sessionName, executable, args) {
282
285
  } catch {}
283
286
  });
284
287
  });
285
- function respondWithOutput(socket) {
288
+ function respondWithOutput(socket, updateSnapshot = true) {
286
289
  readInterceptedUrls();
290
+ if (updateSnapshot)
291
+ lastReadLength = outputBuffer.length;
287
292
  const newUrls = Array.from(detectedUrls).filter((u) => !reportedUrls.has(u));
288
293
  for (const u of newUrls)
289
294
  reportedUrls.add(u);
290
295
  socket.end(JSON.stringify({
291
296
  ok: true,
292
297
  output: outputBuffer,
298
+ outputLength: outputBuffer.length,
293
299
  exited: processExited,
294
300
  exitCode,
295
301
  ...newUrls.length > 0 ? { urls: newUrls } : {}
@@ -311,12 +317,26 @@ function runDaemon(sessionName, executable, args) {
311
317
  };
312
318
  waiters.push(waiter);
313
319
  }
320
+ function writeToStdin(data) {
321
+ lastStdinWrite = Date.now();
322
+ stdin?.write(data);
323
+ }
324
+ function withSettleDelay(fn) {
325
+ const now = Date.now();
326
+ const elapsed = now - lastStdinWrite;
327
+ const delay = Math.max(0, INPUT_SETTLE_MS - elapsed);
328
+ if (delay > 0) {
329
+ setTimeout(fn, delay);
330
+ } else {
331
+ fn();
332
+ }
333
+ }
314
334
  function handle(msg, socket) {
315
335
  switch (msg.action) {
316
336
  case "read":
317
337
  if (msg.wait) {
318
338
  const timeout = msg.timeout ?? 30000;
319
- waitForNewOutput(socket, outputBuffer.length, timeout);
339
+ waitForNewOutput(socket, lastReadLength, timeout);
320
340
  } else {
321
341
  respondWithOutput(socket);
322
342
  }
@@ -326,8 +346,11 @@ function runDaemon(sessionName, executable, args) {
326
346
  socket.end(JSON.stringify({ ok: false, error: "process exited" }));
327
347
  break;
328
348
  }
329
- stdin?.write(msg.data);
330
- socket.end(JSON.stringify({ ok: true }));
349
+ withSettleDelay(() => {
350
+ writeToStdin(msg.data);
351
+ lastReadLength = outputBuffer.length;
352
+ socket.end(JSON.stringify({ ok: true }));
353
+ });
331
354
  break;
332
355
  case "sendread": {
333
356
  if (processExited) {
@@ -336,8 +359,10 @@ function runDaemon(sessionName, executable, args) {
336
359
  }
337
360
  const beforeLength = outputBuffer.length;
338
361
  const timeout = msg.timeout ?? 30000;
339
- stdin?.write(msg.data);
340
- waitForNewOutput(socket, beforeLength, timeout);
362
+ withSettleDelay(() => {
363
+ writeToStdin(msg.data);
364
+ waitForNewOutput(socket, beforeLength, timeout);
365
+ });
341
366
  break;
342
367
  }
343
368
  case "stop":
@@ -379,7 +404,7 @@ var init_daemon = __esm(() => {
379
404
  var require_package = __commonJS((exports, module) => {
380
405
  module.exports = {
381
406
  name: "noninteractive",
382
- version: "0.3.26",
407
+ version: "0.3.30",
383
408
  type: "module",
384
409
  bin: {
385
410
  noninteractive: "./bin/noninteractive.js"
@@ -509,6 +534,9 @@ more examples:
509
534
  npx noninteractive supabase init # session "supabase"
510
535
  npx noninteractive start vercel login # explicit start for non-npx commands`;
511
536
  function stripAnsi(s) {
537
+ const lastClear = Math.max(s.lastIndexOf("\x1B[2J"), s.lastIndexOf("\x1B[H\x1B[2J"));
538
+ if (lastClear !== -1)
539
+ s = s.slice(lastClear);
512
540
  s = s.replace(/\x1b\[[012]?K/g, `
513
541
  `);
514
542
  let result = "";
@@ -700,12 +728,13 @@ make sure the command exists. examples:`);
700
728
  console.error(` npx noninteractive start vercel login # run a command directly`);
701
729
  process.exit(1);
702
730
  }
703
- for (let i = 0;i < 50; i++) {
731
+ const stripSpinners = (s) => s.replace(/[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u281B\u2833\u281E\u283D\u283B\u283F\u283E\u2837\u282F\u281F]/g, "").trim();
732
+ for (let i = 0;i < 150; i++) {
704
733
  await new Promise((r) => setTimeout(r, 200));
705
734
  try {
706
735
  const res = await sendMessage(sock, { action: "read" });
707
736
  handleUrls(res, noOpen);
708
- const clean = stripAnsi(res.output ?? "").trim();
737
+ const clean = stripSpinners(stripAnsi(res.output ?? ""));
709
738
  if (clean.length > 10) {
710
739
  process.stdout.write(stripAnsi(res.output));
711
740
  if (res.exited) {
@@ -717,8 +746,8 @@ make sure the command exists. examples:`);
717
746
  } else {
718
747
  console.log(`
719
748
  [session '${name}' started \u2014 read the output above, then use:]`);
720
- console.log(` npx noninteractive send ${name} "<text>" # send and get response`);
721
- console.log(` npx noninteractive read ${name} --wait # wait for new output`);
749
+ console.log(` npx noninteractive send ${name} "<text>" # send and get response (waits by default)`);
750
+ console.log(` npx noninteractive read ${name} --wait # wait for NEW output (no sleep needed)`);
722
751
  console.log(` npx noninteractive stop ${name} # stop the session`);
723
752
  }
724
753
  return;
@@ -735,8 +764,8 @@ make sure the command exists. examples:`);
735
764
  } catch {}
736
765
  }
737
766
  console.log(`[session '${name}' started but no output yet \u2014 use:]`);
738
- console.log(` npx noninteractive send ${name} "<text>" # send and get response`);
739
- console.log(` npx noninteractive read ${name} --wait # wait for new output`);
767
+ console.log(` npx noninteractive send ${name} "<text>" # send and get response (waits by default)`);
768
+ console.log(` npx noninteractive read ${name} --wait # wait for NEW output (no sleep needed)`);
740
769
  console.log(` npx noninteractive stop ${name} # stop the session`);
741
770
  }
742
771
  async function read(name, wait, timeout, noOpen = false) {
@@ -780,9 +809,11 @@ async function send(name, text, wait, timeout, noOpen = false) {
780
809
  if (res.output !== undefined)
781
810
  process.stdout.write(stripAnsi(res.output));
782
811
  handleUrls(res, noOpen);
783
- if (res.exited)
812
+ if (res.exited) {
784
813
  console.log(`
785
814
  [exited ${res.exitCode}]`);
815
+ console.log(`[session complete \u2014 output above is final, no need to read again]`);
816
+ }
786
817
  } else {
787
818
  await sendMessage(sock, { action: "send", data: text });
788
819
  console.log(`[sent to '${name}' \u2014 run "npx noninteractive read ${name}" to see the result]`);
@@ -848,7 +879,23 @@ async function main() {
848
879
  cwd = startArgs[cwdIdx + 1];
849
880
  startArgs.splice(cwdIdx, 2);
850
881
  }
851
- const filtered = startArgs.filter((a) => a !== "--no-open");
882
+ if (startArgs.includes("--")) {
883
+ console.error(`hint: the -- separator is not needed. just put the command after the flags.`);
884
+ }
885
+ const filtered = startArgs.filter((a) => a !== "--no-open" && a !== "--");
886
+ if (filtered.includes("--help") || filtered.includes("-h")) {
887
+ console.log(`usage: noninteractive start [--name <session>] [--cwd <dir>] <cmd> [args...]
888
+
889
+ examples:
890
+ npx noninteractive start npx eslint --init
891
+ npx noninteractive start --name myeslint --cwd /tmp/project npx eslint --init
892
+ npx noninteractive start node server.js
893
+
894
+ flags:
895
+ --name <session> set session name (default: auto-derived from command)
896
+ --cwd <dir> set working directory for the command`);
897
+ process.exit(0);
898
+ }
852
899
  if (filtered.length < 1) {
853
900
  console.error(`usage: noninteractive start [--name <session>] [--cwd <dir>] <cmd> [args...]
854
901
 
@@ -943,8 +990,11 @@ example: npx noninteractive stop vercel`);
943
990
  hint: use --name for session name, --cwd for working directory.`);
944
991
  process.exit(1);
945
992
  }
993
+ if (mutableArgs.includes("--")) {
994
+ console.error(`hint: the -- separator is not needed. just put the command after the flags.`);
995
+ }
946
996
  const noOpen = mutableArgs.includes("--no-open");
947
- const filteredArgs = mutableArgs.filter((a) => a !== "--no-open");
997
+ const filteredArgs = mutableArgs.filter((a) => a !== "--no-open" && a !== "--");
948
998
  console.log(`[installing and running: npx ${filteredArgs.join(" ")}]`);
949
999
  return start(["npx", "--yes", ...filteredArgs], noOpen, sessionName, cwd);
950
1000
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "noninteractive",
3
- "version": "0.3.26",
3
+ "version": "0.3.30",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "noninteractive": "./bin/noninteractive.js"