noninteractive 0.3.12 → 0.3.13
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/bin/noninteractive.js +157 -20
- package/package.json +1 -1
package/bin/noninteractive.js
CHANGED
|
@@ -34,6 +34,9 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
34
34
|
var exports_paths = {};
|
|
35
35
|
__export(exports_paths, {
|
|
36
36
|
socketPath: () => socketPath,
|
|
37
|
+
sessionUrlsFile: () => sessionUrlsFile,
|
|
38
|
+
sessionDir: () => sessionDir,
|
|
39
|
+
sessionBinDir: () => sessionBinDir,
|
|
37
40
|
ensureSessionsDir: () => ensureSessionsDir,
|
|
38
41
|
SESSIONS_DIR: () => SESSIONS_DIR
|
|
39
42
|
});
|
|
@@ -46,6 +49,15 @@ function ensureSessionsDir() {
|
|
|
46
49
|
function socketPath(name) {
|
|
47
50
|
return resolve(SESSIONS_DIR, `${name}.sock`);
|
|
48
51
|
}
|
|
52
|
+
function sessionDir(name) {
|
|
53
|
+
return resolve(SESSIONS_DIR, name);
|
|
54
|
+
}
|
|
55
|
+
function sessionBinDir(name) {
|
|
56
|
+
return resolve(SESSIONS_DIR, name, "bin");
|
|
57
|
+
}
|
|
58
|
+
function sessionUrlsFile(name) {
|
|
59
|
+
return resolve(SESSIONS_DIR, name, "urls");
|
|
60
|
+
}
|
|
49
61
|
var SESSIONS_DIR;
|
|
50
62
|
var init_paths = __esm(() => {
|
|
51
63
|
SESSIONS_DIR = resolve(homedir(), ".noninteractive", "sessions");
|
|
@@ -61,6 +73,15 @@ function ensureSessionsDir2() {
|
|
|
61
73
|
function socketPath2(name) {
|
|
62
74
|
return resolve2(SESSIONS_DIR2, `${name}.sock`);
|
|
63
75
|
}
|
|
76
|
+
function sessionDir2(name) {
|
|
77
|
+
return resolve2(SESSIONS_DIR2, name);
|
|
78
|
+
}
|
|
79
|
+
function sessionBinDir2(name) {
|
|
80
|
+
return resolve2(SESSIONS_DIR2, name, "bin");
|
|
81
|
+
}
|
|
82
|
+
function sessionUrlsFile2(name) {
|
|
83
|
+
return resolve2(SESSIONS_DIR2, name, "urls");
|
|
84
|
+
}
|
|
64
85
|
var SESSIONS_DIR2;
|
|
65
86
|
var init_paths2 = __esm(() => {
|
|
66
87
|
SESSIONS_DIR2 = resolve2(homedir2(), ".noninteractive", "sessions");
|
|
@@ -72,7 +93,15 @@ __export(exports_daemon, {
|
|
|
72
93
|
runDaemon: () => runDaemon
|
|
73
94
|
});
|
|
74
95
|
import { spawn } from "node:child_process";
|
|
75
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
chmodSync,
|
|
98
|
+
existsSync,
|
|
99
|
+
mkdirSync as mkdirSync3,
|
|
100
|
+
readFileSync,
|
|
101
|
+
rmSync,
|
|
102
|
+
unlinkSync,
|
|
103
|
+
writeFileSync
|
|
104
|
+
} from "node:fs";
|
|
76
105
|
import { createServer } from "node:net";
|
|
77
106
|
import { dirname, resolve as resolve3 } from "node:path";
|
|
78
107
|
function getPtyBridge() {
|
|
@@ -95,15 +124,48 @@ function getPtyBridge() {
|
|
|
95
124
|
}
|
|
96
125
|
return candidates[0];
|
|
97
126
|
}
|
|
127
|
+
function createInterceptorScripts(name) {
|
|
128
|
+
const binDir = sessionBinDir2(name);
|
|
129
|
+
const urlsFile = sessionUrlsFile2(name);
|
|
130
|
+
mkdirSync3(binDir, { recursive: true });
|
|
131
|
+
const openScript = `#!/bin/sh
|
|
132
|
+
case "$1" in
|
|
133
|
+
http://*|https://*) echo "$1" >> "${urlsFile}" ;;
|
|
134
|
+
*) /usr/bin/open "$@" ;;
|
|
135
|
+
esac
|
|
136
|
+
`;
|
|
137
|
+
const xdgOpenScript = `#!/bin/sh
|
|
138
|
+
echo "$1" >> "${urlsFile}"
|
|
139
|
+
`;
|
|
140
|
+
const browserOpenScript = `#!/bin/sh
|
|
141
|
+
echo "$1" >> "${urlsFile}"
|
|
142
|
+
`;
|
|
143
|
+
for (const [file, content] of [
|
|
144
|
+
["open", openScript],
|
|
145
|
+
["xdg-open", xdgOpenScript],
|
|
146
|
+
["browser-open", browserOpenScript]
|
|
147
|
+
]) {
|
|
148
|
+
const path = resolve3(binDir, file);
|
|
149
|
+
writeFileSync(path, content);
|
|
150
|
+
chmodSync(path, 493);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function cleanupSession(name) {
|
|
154
|
+
try {
|
|
155
|
+
rmSync(sessionDir2(name), { recursive: true, force: true });
|
|
156
|
+
} catch {}
|
|
157
|
+
}
|
|
98
158
|
function runDaemon(sessionName, executable, args) {
|
|
99
159
|
ensureSessionsDir2();
|
|
100
160
|
const sock = socketPath2(sessionName);
|
|
101
161
|
try {
|
|
102
162
|
unlinkSync(sock);
|
|
103
163
|
} catch {}
|
|
164
|
+
createInterceptorScripts(sessionName);
|
|
104
165
|
let outputBuffer = "";
|
|
105
166
|
let processExited = false;
|
|
106
167
|
let exitCode = null;
|
|
168
|
+
const detectedUrls = new Set;
|
|
107
169
|
const waiters = [];
|
|
108
170
|
function notifyWaiters() {
|
|
109
171
|
let w = waiters.shift();
|
|
@@ -113,18 +175,50 @@ function runDaemon(sessionName, executable, args) {
|
|
|
113
175
|
w = waiters.shift();
|
|
114
176
|
}
|
|
115
177
|
}
|
|
178
|
+
const binDir = sessionBinDir2(sessionName);
|
|
116
179
|
const ptyBridge = getPtyBridge();
|
|
117
180
|
const proc = spawn(ptyBridge, [executable, ...args], {
|
|
118
181
|
stdio: ["pipe", "pipe", "pipe"],
|
|
119
|
-
env: {
|
|
182
|
+
env: {
|
|
183
|
+
...process.env,
|
|
184
|
+
TERM: "xterm-256color",
|
|
185
|
+
BROWSER: resolve3(binDir, "browser-open"),
|
|
186
|
+
PATH: `${binDir}:${process.env.PATH}`
|
|
187
|
+
}
|
|
120
188
|
});
|
|
121
189
|
const { stdout, stderr, stdin } = proc;
|
|
190
|
+
function scanForUrls(text) {
|
|
191
|
+
const matches = text.match(URL_RE);
|
|
192
|
+
if (matches) {
|
|
193
|
+
for (const url of matches)
|
|
194
|
+
detectedUrls.add(url);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function readInterceptedUrls() {
|
|
198
|
+
const urlsFile = sessionUrlsFile2(sessionName);
|
|
199
|
+
try {
|
|
200
|
+
if (!existsSync(urlsFile))
|
|
201
|
+
return;
|
|
202
|
+
const content = readFileSync(urlsFile, "utf-8");
|
|
203
|
+
const lines = content.split(`
|
|
204
|
+
`);
|
|
205
|
+
for (const line of lines) {
|
|
206
|
+
const trimmed = line.trim();
|
|
207
|
+
if (trimmed)
|
|
208
|
+
detectedUrls.add(trimmed);
|
|
209
|
+
}
|
|
210
|
+
} catch {}
|
|
211
|
+
}
|
|
122
212
|
stdout?.on("data", (chunk) => {
|
|
123
|
-
|
|
213
|
+
const text = chunk.toString();
|
|
214
|
+
outputBuffer += text;
|
|
215
|
+
scanForUrls(text);
|
|
124
216
|
notifyWaiters();
|
|
125
217
|
});
|
|
126
218
|
stderr?.on("data", (chunk) => {
|
|
127
|
-
|
|
219
|
+
const text = chunk.toString();
|
|
220
|
+
outputBuffer += text;
|
|
221
|
+
scanForUrls(text);
|
|
128
222
|
notifyWaiters();
|
|
129
223
|
});
|
|
130
224
|
proc.on("exit", (code) => {
|
|
@@ -138,6 +232,7 @@ function runDaemon(sessionName, executable, args) {
|
|
|
138
232
|
try {
|
|
139
233
|
unlinkSync(sock);
|
|
140
234
|
} catch {}
|
|
235
|
+
cleanupSession(sessionName);
|
|
141
236
|
process.exit(0);
|
|
142
237
|
}, 60000);
|
|
143
238
|
});
|
|
@@ -158,11 +253,14 @@ function runDaemon(sessionName, executable, args) {
|
|
|
158
253
|
});
|
|
159
254
|
});
|
|
160
255
|
function respondWithOutput(socket) {
|
|
256
|
+
readInterceptedUrls();
|
|
257
|
+
const urls = Array.from(detectedUrls);
|
|
161
258
|
socket.end(JSON.stringify({
|
|
162
259
|
ok: true,
|
|
163
260
|
output: outputBuffer,
|
|
164
261
|
exited: processExited,
|
|
165
|
-
exitCode
|
|
262
|
+
exitCode,
|
|
263
|
+
...urls.length > 0 ? { urls } : {}
|
|
166
264
|
}));
|
|
167
265
|
}
|
|
168
266
|
function waitForNewOutput(socket, sinceLength, timeout) {
|
|
@@ -218,6 +316,7 @@ function runDaemon(sessionName, executable, args) {
|
|
|
218
316
|
try {
|
|
219
317
|
unlinkSync(sock);
|
|
220
318
|
} catch {}
|
|
319
|
+
cleanupSession(sessionName);
|
|
221
320
|
process.exit(0);
|
|
222
321
|
}, 500);
|
|
223
322
|
break;
|
|
@@ -235,15 +334,17 @@ function runDaemon(sessionName, executable, args) {
|
|
|
235
334
|
}
|
|
236
335
|
server.listen(sock);
|
|
237
336
|
}
|
|
337
|
+
var URL_RE;
|
|
238
338
|
var init_daemon = __esm(() => {
|
|
239
339
|
init_paths2();
|
|
340
|
+
URL_RE = /https?:\/\/[^\s<>"')\]]+/g;
|
|
240
341
|
});
|
|
241
342
|
|
|
242
343
|
// package.json
|
|
243
344
|
var require_package = __commonJS((exports, module) => {
|
|
244
345
|
module.exports = {
|
|
245
346
|
name: "noninteractive",
|
|
246
|
-
version: "0.3.
|
|
347
|
+
version: "0.3.13",
|
|
247
348
|
type: "module",
|
|
248
349
|
bin: {
|
|
249
350
|
noninteractive: "./bin/noninteractive.js"
|
|
@@ -272,8 +373,8 @@ var require_package = __commonJS((exports, module) => {
|
|
|
272
373
|
});
|
|
273
374
|
|
|
274
375
|
// src/index.ts
|
|
275
|
-
import { spawn as spawn2 } from "child_process";
|
|
276
|
-
import { existsSync } from "fs";
|
|
376
|
+
import { execSync, spawn as spawn2 } from "child_process";
|
|
377
|
+
import { existsSync as existsSync2 } from "fs";
|
|
277
378
|
|
|
278
379
|
// src/client.ts
|
|
279
380
|
import { createConnection } from "node:net";
|
|
@@ -348,6 +449,7 @@ commands:
|
|
|
348
449
|
flags:
|
|
349
450
|
--wait, -w block until new output appears (for send and read)
|
|
350
451
|
--timeout <ms> max wait time in ms (default: 30000, used with --wait)
|
|
452
|
+
--no-open don't auto-open URLs in browser (still shown in output)
|
|
351
453
|
|
|
352
454
|
the session name is auto-derived from the tool (e.g. "workos" \u2192 session "workos").
|
|
353
455
|
|
|
@@ -364,6 +466,30 @@ more examples:
|
|
|
364
466
|
npx noninteractive start vercel login # explicit start for non-npx commands`;
|
|
365
467
|
var stripAnsi = (s) => s.replace(/\x1b\[[\x20-\x3f]*[\x40-\x7e]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[()][A-Z0-9]|\x1b[\x20-\x2f]*[\x30-\x7e]|\x07/g, "").replace(/\r\n?/g, `
|
|
366
468
|
`);
|
|
469
|
+
var seenUrls = new Set;
|
|
470
|
+
function openUrl(url) {
|
|
471
|
+
try {
|
|
472
|
+
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
473
|
+
execSync(`${cmd} ${JSON.stringify(url)}`, { stdio: "ignore" });
|
|
474
|
+
} catch {}
|
|
475
|
+
}
|
|
476
|
+
function handleUrls(res, noOpen) {
|
|
477
|
+
if (!res.urls || res.urls.length === 0)
|
|
478
|
+
return;
|
|
479
|
+
for (const url of res.urls) {
|
|
480
|
+
if (seenUrls.has(url))
|
|
481
|
+
continue;
|
|
482
|
+
seenUrls.add(url);
|
|
483
|
+
if (!noOpen) {
|
|
484
|
+
openUrl(url);
|
|
485
|
+
process.stderr.write(`[opened: ${url}]
|
|
486
|
+
`);
|
|
487
|
+
} else {
|
|
488
|
+
process.stderr.write(`[url: ${url}]
|
|
489
|
+
`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
367
493
|
function getSelfCommand() {
|
|
368
494
|
const script = process.argv[1];
|
|
369
495
|
if (!script)
|
|
@@ -391,7 +517,7 @@ function deriveSessionName(cmd, args) {
|
|
|
391
517
|
const stripped = name.replace(/(?<=.)@[^/].*$/, "");
|
|
392
518
|
return (stripped || name).replace(/^@[^/]+\//, "").replace(/[^a-zA-Z0-9_-]/g, "");
|
|
393
519
|
}
|
|
394
|
-
async function start(cmdArgs) {
|
|
520
|
+
async function start(cmdArgs, noOpen = false) {
|
|
395
521
|
const executable = cmdArgs[0];
|
|
396
522
|
const args = cmdArgs.slice(1);
|
|
397
523
|
const name = deriveSessionName(executable, args);
|
|
@@ -400,6 +526,7 @@ async function start(cmdArgs) {
|
|
|
400
526
|
const res = await sendMessage(sock, { action: "read" });
|
|
401
527
|
if (res.ok) {
|
|
402
528
|
process.stdout.write(stripAnsi(res.output ?? ""));
|
|
529
|
+
handleUrls(res, noOpen);
|
|
403
530
|
if (res.exited) {
|
|
404
531
|
console.log(`
|
|
405
532
|
[session '${name}' already exists but exited ${res.exitCode} \u2014 stopping it]`);
|
|
@@ -428,11 +555,11 @@ async function start(cmdArgs) {
|
|
|
428
555
|
});
|
|
429
556
|
child.unref();
|
|
430
557
|
for (let i = 0;i < 50; i++) {
|
|
431
|
-
if (
|
|
558
|
+
if (existsSync2(sock))
|
|
432
559
|
break;
|
|
433
560
|
await new Promise((r) => setTimeout(r, 100));
|
|
434
561
|
}
|
|
435
|
-
if (!
|
|
562
|
+
if (!existsSync2(sock)) {
|
|
436
563
|
console.error(`error: failed to start session '${name}'.`);
|
|
437
564
|
console.error(`the command was: ${executable} ${args.join(" ")}`);
|
|
438
565
|
console.error(`
|
|
@@ -445,6 +572,7 @@ make sure the command exists. examples:`);
|
|
|
445
572
|
await new Promise((r) => setTimeout(r, 200));
|
|
446
573
|
try {
|
|
447
574
|
const res = await sendMessage(sock, { action: "read" });
|
|
575
|
+
handleUrls(res, noOpen);
|
|
448
576
|
const clean = stripAnsi(res.output ?? "").trim();
|
|
449
577
|
if (clean.length > 10) {
|
|
450
578
|
process.stdout.write(stripAnsi(res.output));
|
|
@@ -479,7 +607,7 @@ make sure the command exists. examples:`);
|
|
|
479
607
|
console.log(` npx noninteractive read ${name} --wait # wait for new output`);
|
|
480
608
|
console.log(` npx noninteractive stop ${name} # stop the session`);
|
|
481
609
|
}
|
|
482
|
-
async function read(name, wait, timeout) {
|
|
610
|
+
async function read(name, wait, timeout, noOpen = false) {
|
|
483
611
|
const sock = socketPath(name);
|
|
484
612
|
const msg = { action: "read" };
|
|
485
613
|
if (wait) {
|
|
@@ -490,16 +618,18 @@ async function read(name, wait, timeout) {
|
|
|
490
618
|
const res = await sendMessage(sock, msg, clientTimeout);
|
|
491
619
|
if (res.output !== undefined)
|
|
492
620
|
process.stdout.write(stripAnsi(res.output));
|
|
621
|
+
handleUrls(res, noOpen);
|
|
493
622
|
if (res.exited)
|
|
494
623
|
console.log(`
|
|
495
624
|
[exited ${res.exitCode}]`);
|
|
496
625
|
}
|
|
497
|
-
async function send(name, text, wait, timeout) {
|
|
626
|
+
async function send(name, text, wait, timeout, noOpen = false) {
|
|
498
627
|
const sock = socketPath(name);
|
|
499
628
|
if (wait) {
|
|
500
629
|
const res = await sendMessage(sock, { action: "sendread", data: text, timeout }, timeout + 5000);
|
|
501
630
|
if (res.output !== undefined)
|
|
502
631
|
process.stdout.write(stripAnsi(res.output));
|
|
632
|
+
handleUrls(res, noOpen);
|
|
503
633
|
if (res.exited)
|
|
504
634
|
console.log(`
|
|
505
635
|
[exited ${res.exitCode}]`);
|
|
@@ -542,16 +672,19 @@ async function main() {
|
|
|
542
672
|
const cmd = args[0];
|
|
543
673
|
switch (cmd) {
|
|
544
674
|
case "start": {
|
|
545
|
-
|
|
675
|
+
const startArgs = args.slice(1).filter((a) => a !== "--no-open");
|
|
676
|
+
const noOpen = args.includes("--no-open");
|
|
677
|
+
if (startArgs.length < 1) {
|
|
546
678
|
console.error(`usage: noninteractive start <cmd> [args...]
|
|
547
679
|
|
|
548
680
|
example: npx noninteractive start npx vercel`);
|
|
549
681
|
process.exit(1);
|
|
550
682
|
}
|
|
551
|
-
return start(
|
|
683
|
+
return start(startArgs, noOpen);
|
|
552
684
|
}
|
|
553
685
|
case "read": {
|
|
554
686
|
const readArgs = args.slice(1);
|
|
687
|
+
const noOpen = readArgs.includes("--no-open");
|
|
555
688
|
const name = readArgs.find((a) => !a.startsWith("-"));
|
|
556
689
|
if (!name) {
|
|
557
690
|
console.error(`usage: noninteractive read <session> [-w|--wait] [--timeout <ms>]
|
|
@@ -562,11 +695,12 @@ example: npx noninteractive read vercel --wait`);
|
|
|
562
695
|
const wait = readArgs.includes("-w") || readArgs.includes("--wait");
|
|
563
696
|
const timeoutIdx = readArgs.indexOf("--timeout");
|
|
564
697
|
const timeout = timeoutIdx !== -1 ? Number(readArgs[timeoutIdx + 1]) : 30000;
|
|
565
|
-
return read(name, wait, timeout);
|
|
698
|
+
return read(name, wait, timeout, noOpen);
|
|
566
699
|
}
|
|
567
700
|
case "sendread":
|
|
568
701
|
case "send": {
|
|
569
702
|
const sendArgs = args.slice(1);
|
|
703
|
+
const noOpen = sendArgs.includes("--no-open");
|
|
570
704
|
const positional = sendArgs.filter((a) => !a.startsWith("-"));
|
|
571
705
|
const name = positional[0];
|
|
572
706
|
const text = positional[1];
|
|
@@ -579,7 +713,7 @@ example: npx noninteractive send workos "" --wait`);
|
|
|
579
713
|
const wait = cmd === "sendread" || sendArgs.includes("-w") || sendArgs.includes("--wait");
|
|
580
714
|
const timeoutIdx = sendArgs.indexOf("--timeout");
|
|
581
715
|
const timeout = timeoutIdx !== -1 ? Number(sendArgs[timeoutIdx + 1]) : 30000;
|
|
582
|
-
return send(name, text, wait, timeout);
|
|
716
|
+
return send(name, text, wait, timeout, noOpen);
|
|
583
717
|
}
|
|
584
718
|
case "stop": {
|
|
585
719
|
const name = args[1];
|
|
@@ -607,9 +741,12 @@ example: npx noninteractive stop vercel`);
|
|
|
607
741
|
case "-h":
|
|
608
742
|
console.log(HELP);
|
|
609
743
|
break;
|
|
610
|
-
default:
|
|
611
|
-
|
|
612
|
-
|
|
744
|
+
default: {
|
|
745
|
+
const noOpen = args.includes("--no-open");
|
|
746
|
+
const filteredArgs = args.filter((a) => a !== "--no-open");
|
|
747
|
+
console.log(`[installing and running: npx ${filteredArgs.join(" ")}]`);
|
|
748
|
+
return start(["npx", "--yes", ...filteredArgs], noOpen);
|
|
749
|
+
}
|
|
613
750
|
}
|
|
614
751
|
}
|
|
615
752
|
main().catch((err) => {
|