pinomad 0.12.0 → 0.14.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/dist/chunk-0dh2k3er.js +43 -0
- package/dist/{chunk-47ff1d1t.js → chunk-1mjbt2z2.js} +191 -11
- package/dist/{chunk-7wj0jh3t.js → chunk-1rdbnvy6.js} +1 -1
- package/dist/{chunk-ksfxqd8s.js → chunk-35szq73k.js} +2682 -459
- package/dist/{chunk-mnmqyq7d.js → chunk-5j7ybhjj.js} +1984 -333
- package/dist/{chunk-agq4tn9r.js → chunk-5pv0g1nj.js} +12 -10
- package/dist/{chunk-7mn38n4f.js → chunk-63ymn5c9.js} +2 -2
- package/dist/{chunk-nppgxh6c.js → chunk-6j6hkfbj.js} +8 -4
- package/dist/{chunk-pqe7q1s7.js → chunk-6vbp1vva.js} +2 -1
- package/dist/{chunk-g3ahp7b9.js → chunk-9trbbz4r.js} +1 -1
- package/dist/{chunk-gg2dcqbg.js → chunk-b0nwq1dt.js} +29 -12
- package/dist/chunk-dsr19q5h.js +23 -0
- package/dist/{chunk-41q0saz0.js → chunk-e53c0s8z.js} +9 -7
- package/dist/{chunk-np4jd070.js → chunk-e59k3bmz.js} +307 -56
- package/dist/chunk-eerpgvy9.js +315 -0
- package/dist/chunk-eewxy4cp.js +240 -0
- package/dist/{chunk-vqkb70d7.js → chunk-evbv1fkn.js} +3 -3
- package/dist/{chunk-vx7dy07y.js → chunk-ex5yr09h.js} +11 -6
- package/dist/{chunk-t91zt5f0.js → chunk-hg9gs396.js} +1 -1
- package/dist/{chunk-aqj9wfmb.js → chunk-mtkvft88.js} +1 -1
- package/dist/{chunk-t5abqsva.js → chunk-mxwcnnkg.js} +15 -305
- package/dist/{chunk-5qf1grkk.js → chunk-w927q9m1.js} +52 -16
- package/dist/pinomad.js +12 -12
- package/package.json +16 -11
- package/web/assets/{core-DnpzcQzs.js → core-BWHGsFpV.js} +1 -1
- package/web/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/web/assets/{highlighted-diff-Dn5ifsvK.js → highlighted-diff-DJ7KZCpS.js} +2 -2
- package/web/assets/{highlights-DnHv6NBM.js → highlights-CyEmo6OI.js} +1 -1
- package/web/assets/{highlights-themes-CUdrlZk3.js → highlights-themes-CSNkBojy.js} +1 -1
- package/web/assets/index-C_qkbyL7.css +1 -0
- package/web/assets/index-DefQtCI2.js +396 -0
- package/web/assets/{shiki-BuPP5HCg.js → shiki-BzCmRG-v.js} +2 -2
- package/web/assets/{shiki-langs-FJk0v0Ga.js → shiki-langs-DOkjyV4d.js} +1 -1
- package/web/assets/{stream-tokenizer-Bl6bisIa.js → stream-tokenizer-CBPqFe4Z.js} +1 -1
- package/web/assets/{theming-themes-BL_CC-Mm.js → theming-themes-wKVca0aB.js} +1 -1
- package/web/assets/{xterm-view-ZsuzJb2C.js → xterm-view-CKa1-QCM.js} +12 -12
- package/web/index.html +3 -2
- package/web/theme-boot.js +11 -0
- package/dist/chunk-42nqtx5r.js +0 -22
- package/dist/chunk-jeqsdcj1.js +0 -96
- package/web/assets/index-BV0ecolo.css +0 -1
- package/web/assets/index-DNsTTgPE.js +0 -396
|
@@ -1,29 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ProtocolMismatchError,
|
|
3
|
-
connectRemoteDurable
|
|
4
|
-
} from "./chunk-np4jd070.js";
|
|
5
|
-
import {
|
|
6
|
-
isParseArgsError
|
|
7
|
-
} from "./chunk-kzbzacz9.js";
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_DATA_DIR,
|
|
10
|
-
hostAddress
|
|
11
|
-
} from "./chunk-ngf1zatm.js";
|
|
12
|
-
import {
|
|
13
|
-
commandHint,
|
|
14
|
-
hostEntry,
|
|
15
|
-
packagedVersion
|
|
16
|
-
} from "./chunk-7wj0jh3t.js";
|
|
17
|
-
|
|
18
|
-
// apps/host/src/cli/service.ts
|
|
19
|
-
import { execFile as execFile2 } from "node:child_process";
|
|
20
|
-
import { homedir as homedir2 } from "node:os";
|
|
21
|
-
import { resolve as resolve2 } from "node:path";
|
|
22
|
-
import { fileURLToPath } from "node:url";
|
|
23
|
-
import { parseArgs, promisify as promisify2 } from "node:util";
|
|
24
|
-
|
|
25
1
|
// apps/host/src/service/definition.ts
|
|
26
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
var LAUNCHER_EXECUTABLE = "pinomad-host";
|
|
4
|
+
var isLauncher = (path) => path.endsWith(`.app/Contents/MacOS/${LAUNCHER_EXECUTABLE}`);
|
|
27
5
|
function sanitizePath(path) {
|
|
28
6
|
const seen = new Set;
|
|
29
7
|
const kept = [];
|
|
@@ -149,11 +127,21 @@ function specFromLaunchdPlist(text) {
|
|
|
149
127
|
const workdir = /<key>WorkingDirectory<\/key>\s*<string>([\s\S]*?)<\/string>/.exec(text)?.[1];
|
|
150
128
|
if (workdir === undefined)
|
|
151
129
|
throw new Error("plist has no WorkingDirectory");
|
|
152
|
-
|
|
130
|
+
if (program[0] === undefined || !isLauncher(program[0]))
|
|
131
|
+
return specFromProgram(program, env, xmlUnescape(workdir));
|
|
132
|
+
return { ...specFromProgram(program.slice(1), env, xmlUnescape(workdir)), launcher: program[0] };
|
|
153
133
|
}
|
|
154
134
|
var xmlEscape = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
155
135
|
function launchdPlist(spec) {
|
|
156
|
-
const program = [
|
|
136
|
+
const program = [
|
|
137
|
+
...spec.launcher === undefined ? [] : [spec.launcher],
|
|
138
|
+
spec.nodePath,
|
|
139
|
+
spec.mainPath,
|
|
140
|
+
...spec.entryArgs,
|
|
141
|
+
"--data-dir",
|
|
142
|
+
spec.dataDir,
|
|
143
|
+
...spec.hostArgs
|
|
144
|
+
];
|
|
157
145
|
const log = join(spec.dataDir, "logs", "host.log");
|
|
158
146
|
const env = Object.entries(spec.env).map(([key, value]) => ` <key>${xmlEscape(key)}</key>
|
|
159
147
|
<string>${xmlEscape(value)}</string>`);
|
|
@@ -207,37 +195,6 @@ WantedBy=default.target
|
|
|
207
195
|
`;
|
|
208
196
|
}
|
|
209
197
|
|
|
210
|
-
// apps/host/src/service/idle.ts
|
|
211
|
-
async function waitForIdle(remote, onBusy) {
|
|
212
|
-
if (remote.view.current().tasks === undefined)
|
|
213
|
-
await remote.controller.toggleTasks();
|
|
214
|
-
let announced = false;
|
|
215
|
-
await new Promise((resolve2) => {
|
|
216
|
-
const check = () => {
|
|
217
|
-
const view = remote.view.current();
|
|
218
|
-
if (view.connection !== "connected") {
|
|
219
|
-
unsubscribe();
|
|
220
|
-
resolve2();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
const tasks = view.tasks;
|
|
224
|
-
if (tasks === undefined)
|
|
225
|
-
return;
|
|
226
|
-
if (Object.keys(tasks.tasks).length === 0) {
|
|
227
|
-
unsubscribe();
|
|
228
|
-
resolve2();
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (!announced) {
|
|
232
|
-
announced = true;
|
|
233
|
-
onBusy(tasks);
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
const unsubscribe = remote.view.subscribe(check);
|
|
237
|
-
check();
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
198
|
// apps/host/src/service/manager.ts
|
|
242
199
|
import { execFile, spawn } from "node:child_process";
|
|
243
200
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -416,251 +373,4 @@ function serviceManager(platform = process.platform) {
|
|
|
416
373
|
throw new Error(`service commands support macOS and Linux, not ${platform}`);
|
|
417
374
|
}
|
|
418
375
|
|
|
419
|
-
|
|
420
|
-
var run2 = promisify2(execFile2);
|
|
421
|
-
var repoRoot = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
422
|
-
var message = (error) => error instanceof Error ? error.message : String(error);
|
|
423
|
-
function connectToHost(url, token, timeoutMs = 3000) {
|
|
424
|
-
const connecting = connectRemoteDurable({ url, token, reconnectDelayMs: { min: 200, max: 2000 } });
|
|
425
|
-
const timedOut = Symbol("timedOut");
|
|
426
|
-
return Promise.race([
|
|
427
|
-
connecting,
|
|
428
|
-
new Promise((resolve3) => {
|
|
429
|
-
const timer = setTimeout(() => resolve3(timedOut), timeoutMs);
|
|
430
|
-
timer.unref();
|
|
431
|
-
})
|
|
432
|
-
]).then((winner) => {
|
|
433
|
-
if (winner !== timedOut)
|
|
434
|
-
return winner;
|
|
435
|
-
connecting.then((remote) => remote.close(), () => {});
|
|
436
|
-
throw new Error("connect timed out");
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
async function install(argv) {
|
|
440
|
-
const spec = installSpec(argv, {
|
|
441
|
-
execPath: process.execPath,
|
|
442
|
-
env: process.env,
|
|
443
|
-
home: homedir2(),
|
|
444
|
-
cwd: process.cwd(),
|
|
445
|
-
mainPath: hostEntry.script,
|
|
446
|
-
entryArgs: hostEntry.args
|
|
447
|
-
});
|
|
448
|
-
const path = await serviceManager().install(spec);
|
|
449
|
-
console.log(`installed ${path}`);
|
|
450
|
-
console.log(`data dir: ${spec.dataDir}`);
|
|
451
|
-
console.log(`browser link: ${commandHint("link")} pairing QR: ${commandHint("pair")}`);
|
|
452
|
-
}
|
|
453
|
-
async function waitUntilIdle(address, connect, log) {
|
|
454
|
-
try {
|
|
455
|
-
const remote = await connect(address.url, address.token);
|
|
456
|
-
await waitForIdle(remote, (tasks) => {
|
|
457
|
-
log("waiting for running tasks to finish (Ctrl-C to cancel)");
|
|
458
|
-
for (const task of Object.values(tasks.tasks)) {
|
|
459
|
-
log(` ${task.kind} in conversation ${task.conversationId}`);
|
|
460
|
-
}
|
|
461
|
-
});
|
|
462
|
-
remote.close();
|
|
463
|
-
} catch {}
|
|
464
|
-
}
|
|
465
|
-
async function stopService({
|
|
466
|
-
force,
|
|
467
|
-
argv,
|
|
468
|
-
manager = serviceManager(),
|
|
469
|
-
connect = connectToHost,
|
|
470
|
-
log = (line) => console.log(line),
|
|
471
|
-
logError = (line) => console.error(line)
|
|
472
|
-
}) {
|
|
473
|
-
const state = await manager.state();
|
|
474
|
-
if (!state.installed) {
|
|
475
|
-
log(`service not installed (${commandHint("service")} install)`);
|
|
476
|
-
return 1;
|
|
477
|
-
}
|
|
478
|
-
if (state.stopped) {
|
|
479
|
-
log("already stopped");
|
|
480
|
-
return 0;
|
|
481
|
-
}
|
|
482
|
-
if (!force) {
|
|
483
|
-
const spec = argv.length === 0 ? await manager.read() : undefined;
|
|
484
|
-
const port = spec === undefined ? undefined : argValue(spec.hostArgs, "--port");
|
|
485
|
-
const flags = spec === undefined ? argv : ["--data-dir", spec.dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]];
|
|
486
|
-
const address = await hostAddress(flags).catch((error) => {
|
|
487
|
-
if (isParseArgsError(error))
|
|
488
|
-
throw error;
|
|
489
|
-
return;
|
|
490
|
-
});
|
|
491
|
-
if (address !== undefined)
|
|
492
|
-
await waitUntilIdle(address, connect, log);
|
|
493
|
-
}
|
|
494
|
-
const holdInterrupt = () => {};
|
|
495
|
-
process.on("SIGINT", holdInterrupt);
|
|
496
|
-
try {
|
|
497
|
-
await manager.stop();
|
|
498
|
-
} finally {
|
|
499
|
-
process.off("SIGINT", holdInterrupt);
|
|
500
|
-
}
|
|
501
|
-
const after = await manager.state();
|
|
502
|
-
if (!after.stopped) {
|
|
503
|
-
logError(`the host is still running (${after.detail}); run \`${commandHint("service")} stop\` again`);
|
|
504
|
-
return 1;
|
|
505
|
-
}
|
|
506
|
-
log(`stopped; it stays off, also after a restart, until \`${commandHint("service")} start\``);
|
|
507
|
-
return 0;
|
|
508
|
-
}
|
|
509
|
-
async function startService({
|
|
510
|
-
manager = serviceManager(),
|
|
511
|
-
log = (line) => console.log(line)
|
|
512
|
-
} = {}) {
|
|
513
|
-
if (!(await manager.state()).installed) {
|
|
514
|
-
log(`service not installed (${commandHint("service")} install)`);
|
|
515
|
-
return 1;
|
|
516
|
-
}
|
|
517
|
-
await manager.start();
|
|
518
|
-
log("started");
|
|
519
|
-
return 0;
|
|
520
|
-
}
|
|
521
|
-
async function versionLine() {
|
|
522
|
-
if (packagedVersion !== undefined)
|
|
523
|
-
return `version: ${packagedVersion}`;
|
|
524
|
-
const head = await run2("git", ["rev-parse", "--short", "HEAD"], { cwd: repoRoot }).then(({ stdout }) => stdout.trim(), () => "unknown");
|
|
525
|
-
return `checkout: ${head}`;
|
|
526
|
-
}
|
|
527
|
-
async function reachabilityLine(argv) {
|
|
528
|
-
try {
|
|
529
|
-
const { url, token } = await hostAddress(argv);
|
|
530
|
-
const remote = await connectToHost(url, token);
|
|
531
|
-
if (remote.view.current().tasks === undefined)
|
|
532
|
-
await remote.controller.toggleTasks();
|
|
533
|
-
const tasks = remote.view.current().tasks;
|
|
534
|
-
const count = tasks === undefined ? 0 : Object.keys(tasks.tasks).length;
|
|
535
|
-
remote.close();
|
|
536
|
-
return `host: reachable at ${url}, ${count} live task${count === 1 ? "" : "s"}`;
|
|
537
|
-
} catch (error) {
|
|
538
|
-
if (isParseArgsError(error))
|
|
539
|
-
throw error;
|
|
540
|
-
return `host: unreachable (${message(error)})`;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
var serviceStatusJson = (state) => JSON.stringify({
|
|
544
|
-
installed: state.installed,
|
|
545
|
-
stopped: state.stopped,
|
|
546
|
-
running: state.running,
|
|
547
|
-
...state.pid === undefined ? {} : { pid: state.pid },
|
|
548
|
-
detail: state.detail
|
|
549
|
-
});
|
|
550
|
-
async function status(argv) {
|
|
551
|
-
const state = await serviceManager().state();
|
|
552
|
-
if (argv.includes("--json")) {
|
|
553
|
-
console.log(serviceStatusJson(state));
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
console.log(`service: ${state.installed ? state.detail : "not installed"}`);
|
|
557
|
-
console.log(await versionLine());
|
|
558
|
-
console.log(await reachabilityLine(argv));
|
|
559
|
-
}
|
|
560
|
-
async function restartService({
|
|
561
|
-
force,
|
|
562
|
-
argv,
|
|
563
|
-
manager = serviceManager(),
|
|
564
|
-
restart,
|
|
565
|
-
connect = connectToHost,
|
|
566
|
-
pollTimeoutMs = 30000,
|
|
567
|
-
pollDelayMs = 500
|
|
568
|
-
}) {
|
|
569
|
-
const state = await manager.state();
|
|
570
|
-
if (!state.installed) {
|
|
571
|
-
console.log(`service not installed (${commandHint("service")} install)`);
|
|
572
|
-
return 1;
|
|
573
|
-
}
|
|
574
|
-
if (state.stopped && restart === undefined) {
|
|
575
|
-
console.log(`service stopped (${commandHint("service")} start)`);
|
|
576
|
-
return 1;
|
|
577
|
-
}
|
|
578
|
-
const address = await hostAddress(argv).catch((error) => {
|
|
579
|
-
if (isParseArgsError(error))
|
|
580
|
-
throw error;
|
|
581
|
-
return;
|
|
582
|
-
});
|
|
583
|
-
if (!force && address !== undefined)
|
|
584
|
-
await waitUntilIdle(address, connect, (line) => console.log(line));
|
|
585
|
-
await (restart?.() ?? manager.restart());
|
|
586
|
-
if (address === undefined) {
|
|
587
|
-
console.log("restart requested (host address unknown; not polling)");
|
|
588
|
-
return 0;
|
|
589
|
-
}
|
|
590
|
-
const deadline = Date.now() + pollTimeoutMs;
|
|
591
|
-
for (;; ) {
|
|
592
|
-
try {
|
|
593
|
-
const remote = await connect(address.url, address.token, 2000);
|
|
594
|
-
remote.close();
|
|
595
|
-
console.log(`host restarted, reachable at ${address.url}`);
|
|
596
|
-
return 0;
|
|
597
|
-
} catch (error) {
|
|
598
|
-
if (error instanceof ProtocolMismatchError && error.direction === "client-older") {
|
|
599
|
-
const newer = error.hostVersion ?? `protocol major ${error.hostMajor}`;
|
|
600
|
-
console.log(`host restarted (${newer}, newer than this upgrader), reachable at ${address.url}`);
|
|
601
|
-
return 0;
|
|
602
|
-
}
|
|
603
|
-
if (Date.now() > deadline) {
|
|
604
|
-
console.error(`host did not answer within ${pollTimeoutMs / 1000}s after restart: ${message(error)}`);
|
|
605
|
-
return 1;
|
|
606
|
-
}
|
|
607
|
-
await new Promise((resolve3) => setTimeout(resolve3, pollDelayMs));
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
async function logs(argv) {
|
|
612
|
-
const { values } = parseArgs({
|
|
613
|
-
args: [...argv],
|
|
614
|
-
options: {
|
|
615
|
-
follow: { type: "boolean", default: false },
|
|
616
|
-
lines: { type: "string", short: "n", default: "100" },
|
|
617
|
-
"data-dir": { type: "string" }
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
const dataDir = resolve2(values["data-dir"] ?? DEFAULT_DATA_DIR);
|
|
621
|
-
await serviceManager().logs(dataDir, Number(values.lines), values.follow);
|
|
622
|
-
}
|
|
623
|
-
async function main(argv) {
|
|
624
|
-
const [command, ...args] = argv;
|
|
625
|
-
try {
|
|
626
|
-
switch (command) {
|
|
627
|
-
case "install":
|
|
628
|
-
await install(args);
|
|
629
|
-
break;
|
|
630
|
-
case "uninstall":
|
|
631
|
-
await serviceManager().uninstall();
|
|
632
|
-
console.log("uninstalled");
|
|
633
|
-
break;
|
|
634
|
-
case "status":
|
|
635
|
-
await status(args);
|
|
636
|
-
break;
|
|
637
|
-
case "restart": {
|
|
638
|
-
const force = args.includes("--force");
|
|
639
|
-
process.exitCode = await restartService({ force, argv: args.filter((arg) => arg !== "--force") });
|
|
640
|
-
break;
|
|
641
|
-
}
|
|
642
|
-
case "stop": {
|
|
643
|
-
const force = args.includes("--force");
|
|
644
|
-
process.exitCode = await stopService({ force, argv: args.filter((arg) => arg !== "--force") });
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
case "start":
|
|
648
|
-
process.exitCode = await startService();
|
|
649
|
-
break;
|
|
650
|
-
case "logs":
|
|
651
|
-
await logs(args);
|
|
652
|
-
break;
|
|
653
|
-
default:
|
|
654
|
-
console.error(`usage: ${commandHint("service")} install|uninstall|status|restart|stop|start|logs`);
|
|
655
|
-
process.exitCode = 1;
|
|
656
|
-
}
|
|
657
|
-
} catch (error) {
|
|
658
|
-
if (isParseArgsError(error))
|
|
659
|
-
throw error;
|
|
660
|
-
console.error(message(error));
|
|
661
|
-
process.exitCode = 1;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
if (false) {}
|
|
665
|
-
|
|
666
|
-
export { installSpec, argValue, withoutArg, withoutRelayArgs, serviceManager, stopService, startService, versionLine, reachabilityLine, serviceStatusJson, restartService, main };
|
|
376
|
+
export { LAUNCHER_EXECUTABLE, isLauncher, installSpec, argValue, withoutArg, withoutRelayArgs, serviceManager };
|
|
@@ -3,36 +3,40 @@ import {
|
|
|
3
3
|
joinServer,
|
|
4
4
|
openServiceSlot,
|
|
5
5
|
writeService
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import"./chunk-
|
|
8
|
-
import"./chunk-
|
|
9
|
-
import"./chunk-
|
|
6
|
+
} from "./chunk-b0nwq1dt.js";
|
|
7
|
+
import"./chunk-63ymn5c9.js";
|
|
8
|
+
import"./chunk-6j6hkfbj.js";
|
|
9
|
+
import"./chunk-hg9gs396.js";
|
|
10
10
|
import {
|
|
11
11
|
normalizeOrigin,
|
|
12
12
|
readRemovalNotice,
|
|
13
13
|
readServerConfig
|
|
14
14
|
} from "./chunk-nq57fdnq.js";
|
|
15
|
-
import"./chunk-
|
|
15
|
+
import"./chunk-9trbbz4r.js";
|
|
16
|
+
import"./chunk-eerpgvy9.js";
|
|
17
|
+
import"./chunk-e59k3bmz.js";
|
|
18
|
+
import"./chunk-kzbzacz9.js";
|
|
16
19
|
import {
|
|
20
|
+
LAUNCHER_EXECUTABLE,
|
|
17
21
|
argValue,
|
|
22
|
+
isLauncher,
|
|
18
23
|
serviceManager,
|
|
19
24
|
withoutArg,
|
|
20
25
|
withoutRelayArgs
|
|
21
|
-
} from "./chunk-
|
|
22
|
-
import"./chunk-
|
|
23
|
-
import"./chunk-
|
|
24
|
-
import"./chunk-47ff1d1t.js";
|
|
25
|
-
import"./chunk-pqe7q1s7.js";
|
|
26
|
+
} from "./chunk-mxwcnnkg.js";
|
|
27
|
+
import"./chunk-1mjbt2z2.js";
|
|
28
|
+
import"./chunk-6vbp1vva.js";
|
|
26
29
|
import {
|
|
27
30
|
DEFAULT_DATA_DIR
|
|
28
31
|
} from "./chunk-ngf1zatm.js";
|
|
29
32
|
import {
|
|
30
33
|
commandHint
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-1rdbnvy6.js";
|
|
32
35
|
import"./chunk-df32avw6.js";
|
|
33
36
|
|
|
34
37
|
// apps/host/src/cli/up.ts
|
|
35
|
-
import {
|
|
38
|
+
import { existsSync } from "node:fs";
|
|
39
|
+
import { isAbsolute, resolve } from "node:path";
|
|
36
40
|
import { createInterface } from "node:readline/promises";
|
|
37
41
|
import { parseArgs } from "node:util";
|
|
38
42
|
var OFFICIAL_SERVER_ORIGIN = "https://app.pinomad.si";
|
|
@@ -85,7 +89,14 @@ async function upHost(args, deps) {
|
|
|
85
89
|
const lan = (hostArgs) => withLan(hostArgs, lanPort);
|
|
86
90
|
const notes = [];
|
|
87
91
|
if (reach.server && config === undefined) {
|
|
88
|
-
const joined = await joinServer({
|
|
92
|
+
const joined = await joinServer({
|
|
93
|
+
origin,
|
|
94
|
+
dataDir,
|
|
95
|
+
code: args.code,
|
|
96
|
+
...args.token === undefined ? {} : { token: args.token },
|
|
97
|
+
...args.launcher === undefined ? {} : { launcher: args.launcher },
|
|
98
|
+
editHostArgs: lan
|
|
99
|
+
}, { ...deps, manager });
|
|
89
100
|
return {
|
|
90
101
|
joined: { origin: joined.origin, username: joined.username, fresh: true },
|
|
91
102
|
...lanPort === undefined ? {} : { lanPort },
|
|
@@ -94,7 +105,7 @@ async function upHost(args, deps) {
|
|
|
94
105
|
};
|
|
95
106
|
}
|
|
96
107
|
const edit = reach.server ? (hostArgs) => lan(withoutRelayArgs(hostArgs)) : lan;
|
|
97
|
-
const service = await writeService(slot, edit, { changed: reach.server });
|
|
108
|
+
const service = await writeService(slot, edit, { changed: reach.server, launcher: args.launcher });
|
|
98
109
|
if (!reach.server && config !== undefined) {
|
|
99
110
|
notes.push(`still joined to ${config.origin} — run \`${commandHint("leave")}\` to leave it`);
|
|
100
111
|
}
|
|
@@ -105,6 +116,12 @@ async function upHost(args, deps) {
|
|
|
105
116
|
notes
|
|
106
117
|
};
|
|
107
118
|
}
|
|
119
|
+
async function readAllStdin() {
|
|
120
|
+
let text = "";
|
|
121
|
+
for await (const chunk of process.stdin)
|
|
122
|
+
text += String(chunk);
|
|
123
|
+
return text;
|
|
124
|
+
}
|
|
108
125
|
async function askOnTerminal(question) {
|
|
109
126
|
const terminal = createInterface({ input: process.stdin, output: process.stdout });
|
|
110
127
|
try {
|
|
@@ -139,7 +156,8 @@ var FLAGS = `choose with flags:
|
|
|
139
156
|
--server [--origin URL] through a PiNomad server (default ${OFFICIAL_SERVER_ORIGIN})
|
|
140
157
|
--lan [--remote-port N] only on the local network and tailnet (default port ${DEFAULT_REMOTE_PORT})
|
|
141
158
|
--server --lan both
|
|
142
|
-
--yes keep what this host has, or the server on a new host
|
|
159
|
+
--yes keep what this host has, or the server on a new host
|
|
160
|
+
--token-stdin join with a one-time token read from stdin, without a code`;
|
|
143
161
|
async function main(argv, deps) {
|
|
144
162
|
const { values } = parseArgs({
|
|
145
163
|
args: [...argv],
|
|
@@ -150,6 +168,8 @@ async function main(argv, deps) {
|
|
|
150
168
|
yes: { type: "boolean", short: "y" },
|
|
151
169
|
origin: { type: "string" },
|
|
152
170
|
code: { type: "boolean" },
|
|
171
|
+
"token-stdin": { type: "boolean" },
|
|
172
|
+
launcher: { type: "string" },
|
|
153
173
|
"remote-port": { type: "string" },
|
|
154
174
|
"data-dir": { type: "string" }
|
|
155
175
|
}
|
|
@@ -157,6 +177,20 @@ async function main(argv, deps) {
|
|
|
157
177
|
const log = deps?.log ?? ((line) => console.log(line));
|
|
158
178
|
try {
|
|
159
179
|
const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
|
|
180
|
+
const launcher = values.launcher;
|
|
181
|
+
if (launcher !== undefined && process.platform !== "darwin")
|
|
182
|
+
throw new Error("--launcher is for the Mac App's service only");
|
|
183
|
+
if (launcher !== undefined && !(isAbsolute(launcher) && isLauncher(launcher))) {
|
|
184
|
+
throw new Error(`--launcher ${launcher}: not a path to the launcher inside an app (….app/Contents/MacOS/${LAUNCHER_EXECUTABLE})`);
|
|
185
|
+
}
|
|
186
|
+
if (launcher !== undefined && !existsSync(launcher))
|
|
187
|
+
throw new Error(`--launcher ${launcher}: no such file`);
|
|
188
|
+
let token;
|
|
189
|
+
if (values["token-stdin"] === true) {
|
|
190
|
+
token = (await (deps?.readStdin ?? readAllStdin)()).trim();
|
|
191
|
+
if (token === "")
|
|
192
|
+
throw new Error("--token-stdin: no join token on stdin");
|
|
193
|
+
}
|
|
160
194
|
const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
|
|
161
195
|
const current = await currentReach(dataDir, deps);
|
|
162
196
|
let reach = resolveReach({ server: values.server, lan: values.lan }, current);
|
|
@@ -182,7 +216,9 @@ async function main(argv, deps) {
|
|
|
182
216
|
origin,
|
|
183
217
|
reach,
|
|
184
218
|
...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] },
|
|
185
|
-
...values.code === true ? { code: true } : {}
|
|
219
|
+
...values.code === true ? { code: true } : {},
|
|
220
|
+
...token === undefined ? {} : { token },
|
|
221
|
+
...launcher === undefined ? {} : { launcher }
|
|
186
222
|
}, deps);
|
|
187
223
|
if (outcome.joined !== undefined) {
|
|
188
224
|
const who = outcome.joined.username ?? "an account";
|
package/dist/pinomad.js
CHANGED
|
@@ -5,24 +5,24 @@ import {
|
|
|
5
5
|
} from "./chunk-kzbzacz9.js";
|
|
6
6
|
import {
|
|
7
7
|
packagedVersion
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-1rdbnvy6.js";
|
|
9
9
|
import {
|
|
10
10
|
__require
|
|
11
11
|
} from "./chunk-df32avw6.js";
|
|
12
12
|
|
|
13
13
|
// apps/cli/src/pinomad.ts
|
|
14
14
|
var COMMANDS = {
|
|
15
|
-
up: () => import("./chunk-
|
|
16
|
-
status: () => import("./chunk-
|
|
17
|
-
host: () => import("./chunk-
|
|
18
|
-
server: () => import("./chunk-
|
|
19
|
-
relay: () => import("./chunk-
|
|
20
|
-
service: () => import("./chunk-
|
|
21
|
-
upgrade: () => import("./chunk-
|
|
22
|
-
link: () => import("./chunk-
|
|
23
|
-
pair: () => import("./chunk-
|
|
24
|
-
join: () => import("./chunk-
|
|
25
|
-
leave: () => import("./chunk-
|
|
15
|
+
up: () => import("./chunk-w927q9m1.js"),
|
|
16
|
+
status: () => import("./chunk-e53c0s8z.js"),
|
|
17
|
+
host: () => import("./chunk-35szq73k.js"),
|
|
18
|
+
server: () => import("./chunk-5j7ybhjj.js"),
|
|
19
|
+
relay: () => import("./chunk-5j7ybhjj.js"),
|
|
20
|
+
service: () => import("./chunk-ex5yr09h.js"),
|
|
21
|
+
upgrade: () => import("./chunk-eewxy4cp.js"),
|
|
22
|
+
link: () => import("./chunk-mtkvft88.js"),
|
|
23
|
+
pair: () => import("./chunk-evbv1fkn.js"),
|
|
24
|
+
join: () => import("./chunk-dsr19q5h.js"),
|
|
25
|
+
leave: () => import("./chunk-5pv0g1nj.js")
|
|
26
26
|
};
|
|
27
27
|
var USAGE = `pinomad — PiNomad host, server, and utilities
|
|
28
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinomad",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "PiNomad — self-hosted coding agent: host, relay, and web client in one package",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -20,25 +20,30 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/bubbleptr/pinomad",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@earendil-works/chord": "1.
|
|
24
|
-
"@earendil-works/pi-ai": "1.
|
|
25
|
-
"@earendil-works/pi-codemode": "1.
|
|
26
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
27
|
-
"@earendil-works/pi-durable": "1.
|
|
28
|
-
"@earendil-works/pi-mcp": "1.
|
|
23
|
+
"@earendil-works/chord": "1.5.0",
|
|
24
|
+
"@earendil-works/pi-ai": "1.5.0",
|
|
25
|
+
"@earendil-works/pi-codemode": "1.5.0",
|
|
26
|
+
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
27
|
+
"@earendil-works/pi-durable": "1.5.0",
|
|
28
|
+
"@earendil-works/pi-mcp": "1.5.0",
|
|
29
29
|
"@lydell/node-pty": "1.2.0-beta.18",
|
|
30
30
|
"@noble/curves": "2.4.0",
|
|
31
31
|
"diff": "8.0.4",
|
|
32
32
|
"node-datachannel": "0.33.4",
|
|
33
33
|
"proper-lockfile": "4.1.2",
|
|
34
34
|
"typebox": "1.3.27",
|
|
35
|
-
"undici": "8.
|
|
35
|
+
"undici": "8.12.3",
|
|
36
36
|
"uqr": "0.1.3",
|
|
37
37
|
"ws": "8.22.0",
|
|
38
|
+
"nodemailer": "10.3.1",
|
|
38
39
|
"@noble/ciphers": "2.4.0",
|
|
39
40
|
"@noble/hashes": "2.4.0",
|
|
40
|
-
"@earendil-works/pi-agent-core": "1.
|
|
41
|
-
"@earendil-works/pi-
|
|
42
|
-
"@earendil-works/pi-
|
|
41
|
+
"@earendil-works/pi-agent-core": "1.5.0",
|
|
42
|
+
"@earendil-works/pi-schema": "1.5.0",
|
|
43
|
+
"@earendil-works/pi-telemetry": "1.5.0",
|
|
44
|
+
"@earendil-works/pi-tui": "1.5.0"
|
|
45
|
+
},
|
|
46
|
+
"pinomad": {
|
|
47
|
+
"protocolMajor": 5
|
|
43
48
|
}
|
|
44
49
|
}
|