ryugi-artifacts 1.32.0 → 1.34.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/cli.cjs
CHANGED
|
@@ -24,11 +24,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli/main.ts
|
|
27
|
-
var
|
|
27
|
+
var import_node_child_process4 = require("node:child_process");
|
|
28
28
|
var import_node_util = require("node:util");
|
|
29
29
|
|
|
30
30
|
// src/cli/paths.ts
|
|
31
31
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
32
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
32
33
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
33
34
|
var CliError = class extends Error {
|
|
34
35
|
constructor(message, fields = {}, code = 1) {
|
|
@@ -39,6 +40,21 @@ var CliError = class extends Error {
|
|
|
39
40
|
fields;
|
|
40
41
|
code;
|
|
41
42
|
};
|
|
43
|
+
var resolvesRoot = () => !process.env.RYUGI_ARTIFACTS_COMMAND || process.env.RYUGI_ARTIFACTS_RESOLVE_ROOT === "1";
|
|
44
|
+
function projectRoot(dir) {
|
|
45
|
+
let home = "";
|
|
46
|
+
try {
|
|
47
|
+
home = import_node_fs.default.realpathSync(import_node_os.default.homedir());
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
const chain = [];
|
|
51
|
+
for (let current = dir; ; current = import_node_path.default.dirname(current)) {
|
|
52
|
+
chain.push(current);
|
|
53
|
+
if (import_node_fs.default.existsSync(import_node_path.default.join(current, ".git")) && (current !== home || dir === home)) break;
|
|
54
|
+
if (import_node_path.default.dirname(current) === current) return dir;
|
|
55
|
+
}
|
|
56
|
+
return chain.find((candidate) => import_node_fs.default.existsSync(import_node_path.default.join(candidate, ".ryugi", "artifacts"))) ?? chain[chain.length - 1];
|
|
57
|
+
}
|
|
42
58
|
function projectPaths(dir) {
|
|
43
59
|
let project;
|
|
44
60
|
try {
|
|
@@ -47,6 +63,7 @@ function projectPaths(dir) {
|
|
|
47
63
|
} catch {
|
|
48
64
|
throw new CliError(`Project directory not found: ${dir}`);
|
|
49
65
|
}
|
|
66
|
+
if (resolvesRoot()) project = projectRoot(project);
|
|
50
67
|
const root = import_node_path.default.join(project, ".ryugi", "artifacts");
|
|
51
68
|
const state = import_node_path.default.join(root, ".state");
|
|
52
69
|
return {
|
|
@@ -370,7 +387,7 @@ ${groups.map(groupText).join("\n")}` : head;
|
|
|
370
387
|
}
|
|
371
388
|
|
|
372
389
|
// src/cli/config.ts
|
|
373
|
-
var
|
|
390
|
+
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
374
391
|
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
375
392
|
|
|
376
393
|
// src/server/config.ts
|
|
@@ -501,7 +518,7 @@ function writeGlobalAutoPublish(file, value) {
|
|
|
501
518
|
// src/cli/config.ts
|
|
502
519
|
var VALUES = { on: true, off: false, inherit: null };
|
|
503
520
|
function config(options, emit) {
|
|
504
|
-
const globalFile = settingsFileFor(credentialsFile(process.env, process.platform,
|
|
521
|
+
const globalFile = settingsFileFor(credentialsFile(process.env, process.platform, import_node_os2.default.homedir()));
|
|
505
522
|
const projectFile = import_node_path6.default.join(import_node_path6.default.dirname(projectPaths(options.projectDir).root), "config.json");
|
|
506
523
|
if (options.autoPublish !== void 0) {
|
|
507
524
|
const value = VALUES[options.autoPublish];
|
|
@@ -516,17 +533,467 @@ function config(options, emit) {
|
|
|
516
533
|
return 0;
|
|
517
534
|
}
|
|
518
535
|
|
|
519
|
-
// src/cli/
|
|
536
|
+
// src/cli/monitor-view.ts
|
|
537
|
+
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
538
|
+
var REFRESH_SECONDS = 2;
|
|
539
|
+
var BOLD = "\x1B[1m";
|
|
540
|
+
var DIM = "\x1B[2m";
|
|
541
|
+
var INVERSE = "\x1B[7m";
|
|
542
|
+
var RESET = "\x1B[0m";
|
|
543
|
+
var HELP = "\u2191\u2193 select o open c copy URL l logs x stop X stop all safe q quit";
|
|
544
|
+
var COLUMNS = [["PORT", 6], ["PID", 8], ["UP", 7], ["TABS", 5], ["IDLE", 7], ["VERSION", 13]];
|
|
545
|
+
var FIXED = 2 + COLUMNS.reduce((sum, [, width]) => sum + width + 2, 0);
|
|
546
|
+
var TAGS_WIDTH = 26;
|
|
547
|
+
function duration(ms) {
|
|
548
|
+
const minutes = Math.max(0, Math.floor(ms / 6e4));
|
|
549
|
+
if (minutes < 60) return `${minutes}m`;
|
|
550
|
+
const hours = Math.floor(minutes / 60);
|
|
551
|
+
if (hours < 24) return `${hours}h${minutes % 60 ? `${minutes % 60}m` : ""}`;
|
|
552
|
+
return `${Math.floor(hours / 24)}d${hours % 24 ? `${hours % 24}h` : ""}`;
|
|
553
|
+
}
|
|
554
|
+
var shortPath = (project, home = import_node_os3.default.homedir()) => project === home || project.startsWith(`${home}/`) ? `~${project.slice(home.length)}` : project;
|
|
555
|
+
function tags(row) {
|
|
556
|
+
return [row.safeToStop && "safe to stop", row.oldRelease && "old release", row.status === "no-status" && "no status"].filter(Boolean);
|
|
557
|
+
}
|
|
558
|
+
var initialView = () => ({ mode: "list", selected: 0, confirm: null, target: null, message: null });
|
|
559
|
+
var fit = (text2, width) => text2.length > width ? text2.slice(0, width) : text2;
|
|
560
|
+
var pad = (text2, width) => fit(text2, width).padEnd(width);
|
|
561
|
+
var cutLeft = (text2, width) => text2.length > width ? `\u2026${text2.slice(text2.length - width + 1)}` : text2;
|
|
562
|
+
function header(left, right, width) {
|
|
563
|
+
const gap = width - left.length - right.length;
|
|
564
|
+
return `${BOLD}${fit(gap >= 2 ? `${left}${" ".repeat(gap)}${right}` : left, width)}${RESET}`;
|
|
565
|
+
}
|
|
566
|
+
function cells(row, now) {
|
|
567
|
+
return [
|
|
568
|
+
row.port === null ? "-" : String(row.port),
|
|
569
|
+
String(row.pid),
|
|
570
|
+
duration(now - row.startedAt),
|
|
571
|
+
row.clients === null ? "-" : String(row.clients),
|
|
572
|
+
row.lastActivity === null ? "-" : duration(now - row.lastActivity),
|
|
573
|
+
row.version ?? "-"
|
|
574
|
+
];
|
|
575
|
+
}
|
|
576
|
+
function footer(rows, state, home) {
|
|
577
|
+
if (state.confirm === "stop" && state.target) return `Stop ${shortPath(state.target, home)}? y to confirm`;
|
|
578
|
+
if (state.confirm === "stop-safe") {
|
|
579
|
+
const count = rows.filter((row) => row.safeToStop).length;
|
|
580
|
+
return `Stop ${count} server${count === 1 ? "" : "s"} that ${count === 1 ? "is" : "are"} safe to stop? y to confirm`;
|
|
581
|
+
}
|
|
582
|
+
return state.message ?? HELP;
|
|
583
|
+
}
|
|
584
|
+
function renderList(rows, state, screen) {
|
|
585
|
+
const { width, height, now, home } = screen;
|
|
586
|
+
const count = `${rows.length} server${rows.length === 1 ? "" : "s"} \xB7 refreshes every ${REFRESH_SECONDS}s`;
|
|
587
|
+
const lines = [header("ryugi-artifacts monitor", count, width)];
|
|
588
|
+
if (!rows.length) {
|
|
589
|
+
lines.push("", fit("No artifacts servers are running.", width), "");
|
|
590
|
+
} else {
|
|
591
|
+
const projectWidth = Math.max(12, width - FIXED - TAGS_WIDTH);
|
|
592
|
+
const columns = [" ", pad("PROJECT", projectWidth), ...COLUMNS.map(([name, w]) => ` ${pad(name, w)}`)].join("");
|
|
593
|
+
lines.push(`${DIM}${fit(columns.trimEnd(), width)}${RESET}`);
|
|
594
|
+
const visible = Math.max(1, height - 4);
|
|
595
|
+
const first = Math.min(Math.max(0, state.selected - visible + 1), Math.max(0, rows.length - visible));
|
|
596
|
+
rows.slice(first, first + visible).forEach((row, offset) => {
|
|
597
|
+
const selected = first + offset === state.selected;
|
|
598
|
+
const text2 = [
|
|
599
|
+
selected ? "\u203A " : " ",
|
|
600
|
+
pad(cutLeft(shortPath(row.project, home), projectWidth), projectWidth),
|
|
601
|
+
...cells(row, now).map((cell, i) => ` ${pad(cell, COLUMNS[i][1])}`),
|
|
602
|
+
tags(row).length ? ` ${tags(row).join(", ")}` : ""
|
|
603
|
+
].join("");
|
|
604
|
+
const line = fit(text2.trimEnd(), width);
|
|
605
|
+
lines.push(selected ? `${INVERSE}${line}${RESET}` : line);
|
|
606
|
+
});
|
|
607
|
+
lines.push("");
|
|
608
|
+
}
|
|
609
|
+
lines.push(`${DIM}${fit(footer(rows, state, home), width)}${RESET}`);
|
|
610
|
+
return lines;
|
|
611
|
+
}
|
|
612
|
+
function renderLog(row, text2, screen) {
|
|
613
|
+
const { width, height, home } = screen;
|
|
614
|
+
const lines = [header(`Log \xB7 ${shortPath(row.project, home)}`, row.log, width)];
|
|
615
|
+
const body = text2 === null ? ["No log file: this server runs in the foreground, so its output is in the terminal that started it."] : text2.replace(/\n+$/, "").split("\n").slice(-Math.max(1, height - 2));
|
|
616
|
+
lines.push(...body.map((line) => fit(line, width)));
|
|
617
|
+
lines.push(`${DIM}${fit("Esc back Ctrl+C quit", width)}${RESET}`);
|
|
618
|
+
return lines;
|
|
619
|
+
}
|
|
620
|
+
function handleKey(state, key, rows) {
|
|
621
|
+
if (key.ctrl && key.name === "c") return { state, action: { type: "quit" } };
|
|
622
|
+
const next = { ...state, confirm: null, target: null, message: null };
|
|
623
|
+
if (state.mode === "log") return { state: key.name === "escape" || key.name === "q" ? { ...next, mode: "list" } : state };
|
|
624
|
+
if (state.confirm) {
|
|
625
|
+
if (key.name !== "y") return { state: next };
|
|
626
|
+
const targets = state.confirm === "stop" ? rows.filter((row) => row.project === state.target) : rows.filter((row) => row.safeToStop);
|
|
627
|
+
if (targets.length) return { state: next, action: { type: "stop", rows: targets } };
|
|
628
|
+
return { state: { ...next, message: state.target ? `${shortPath(state.target)} is no longer running.` : "Nothing is safe to stop any more." } };
|
|
629
|
+
}
|
|
630
|
+
const selected = rows[state.selected];
|
|
631
|
+
if (key.sequence === "X" || key.name === "x" && key.shift) {
|
|
632
|
+
if (rows.some((row) => row.safeToStop)) return { state: { ...next, confirm: "stop-safe" } };
|
|
633
|
+
return { state: { ...next, message: "Nothing is safe to stop: every server has an open tab or was used in the last 30 minutes." } };
|
|
634
|
+
}
|
|
635
|
+
switch (key.name) {
|
|
636
|
+
case "q":
|
|
637
|
+
return { state, action: { type: "quit" } };
|
|
638
|
+
case "up":
|
|
639
|
+
case "k":
|
|
640
|
+
return { state: { ...next, selected: Math.max(0, state.selected - 1) } };
|
|
641
|
+
case "down":
|
|
642
|
+
case "j":
|
|
643
|
+
return { state: { ...next, selected: Math.min(Math.max(0, rows.length - 1), state.selected + 1) } };
|
|
644
|
+
case "o":
|
|
645
|
+
case "c":
|
|
646
|
+
if (!selected) return { state: next };
|
|
647
|
+
if (!selected.url) return { state: { ...next, message: "This server has no URL yet." } };
|
|
648
|
+
return { state: next, action: { type: key.name === "o" ? "open" : "copy", row: selected } };
|
|
649
|
+
case "l":
|
|
650
|
+
return { state: selected ? { ...next, mode: "log" } : next };
|
|
651
|
+
case "x":
|
|
652
|
+
return { state: selected ? { ...next, confirm: "stop", target: selected.project } : next };
|
|
653
|
+
default:
|
|
654
|
+
return { state: next };
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/cli/monitor-ui.ts
|
|
520
659
|
var import_node_child_process2 = require("node:child_process");
|
|
521
|
-
var
|
|
522
|
-
var
|
|
523
|
-
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
660
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
661
|
+
var import_node_os5 = __toESM(require("node:os"), 1);
|
|
524
662
|
var import_node_readline = __toESM(require("node:readline"), 1);
|
|
525
663
|
|
|
526
|
-
// src/server/
|
|
664
|
+
// src/server/lifecycle.ts
|
|
665
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 4 * 60 * 60 * 1e3;
|
|
666
|
+
var DEFAULT_LIFECYCLE_CHECK_MS = 60 * 1e3;
|
|
667
|
+
function browserLauncher(platform, url) {
|
|
668
|
+
if (platform === "darwin") return { bin: "open", args: [url] };
|
|
669
|
+
if (platform === "win32") return { bin: "cmd", args: ["/c", "start", '""', url] };
|
|
670
|
+
if (platform === "linux" || platform === "freebsd" || platform === "openbsd") return { bin: "xdg-open", args: [url] };
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/cli/servers.ts
|
|
675
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
676
|
+
|
|
677
|
+
// src/server/registry.ts
|
|
527
678
|
var import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
528
679
|
var import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
680
|
+
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
529
681
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
682
|
+
function registryDir(env = process.env, platform = process.platform, uid = process.getuid?.() ?? -1, tmpdir = import_node_os4.default.tmpdir()) {
|
|
683
|
+
if (env.RYUGI_RUNTIME_DIR) return import_node_path7.default.join(env.RYUGI_RUNTIME_DIR, "servers");
|
|
684
|
+
if (platform === "win32") return import_node_path7.default.join(tmpdir, "ryugi", "servers");
|
|
685
|
+
return `/tmp/ryugi-${uid}/servers`;
|
|
686
|
+
}
|
|
687
|
+
var fileFor = (dir, project) => import_node_path7.default.join(dir, `${import_node_crypto.default.createHash("sha256").update(project).digest("hex").slice(0, 16)}.json`);
|
|
688
|
+
function verifyDir(dir) {
|
|
689
|
+
if (process.platform === "win32") return;
|
|
690
|
+
for (const target of [import_node_path7.default.dirname(dir), dir]) {
|
|
691
|
+
const stat = import_node_fs7.default.lstatSync(target);
|
|
692
|
+
if (!stat.isDirectory() || stat.uid !== process.getuid() || stat.mode & 18) throw new Error(`Unsafe registry directory: ${target}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function readEntry(file) {
|
|
696
|
+
try {
|
|
697
|
+
const value = JSON.parse(import_node_fs7.default.readFileSync(file, "utf8"));
|
|
698
|
+
if (typeof value.project !== "string" || typeof value.root !== "string" || typeof value.serverId !== "string") return null;
|
|
699
|
+
if (!Number.isInteger(value.pid) || !Number.isFinite(value.startedAt)) return null;
|
|
700
|
+
if (value.root !== import_node_path7.default.join(value.project, ".ryugi", "artifacts")) return null;
|
|
701
|
+
return value;
|
|
702
|
+
} catch {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function readEntries(dir) {
|
|
707
|
+
let names;
|
|
708
|
+
try {
|
|
709
|
+
verifyDir(dir);
|
|
710
|
+
names = import_node_fs7.default.readdirSync(dir);
|
|
711
|
+
} catch {
|
|
712
|
+
return [];
|
|
713
|
+
}
|
|
714
|
+
return names.filter((name) => name.endsWith(".json")).flatMap((name) => readEntry(import_node_path7.default.join(dir, name)) ?? []);
|
|
715
|
+
}
|
|
716
|
+
function removeEntry(dir, entry) {
|
|
717
|
+
const file = fileFor(dir, entry.project);
|
|
718
|
+
if (readEntry(file)?.serverId === entry.serverId) import_node_fs7.default.rmSync(file, { force: true });
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/cli/servers.ts
|
|
722
|
+
var SAFE_IDLE_MS = 30 * 6e4;
|
|
723
|
+
var STATUS_TIMEOUT_MS = 1e3;
|
|
724
|
+
var safeToStop = (status, now) => !!status && status.clients === 0 && now - status.lastActivity >= SAFE_IDLE_MS;
|
|
725
|
+
async function fetchStatus(url) {
|
|
726
|
+
const base = new URL(url);
|
|
727
|
+
if (base.hostname === "localhost") base.hostname = "127.0.0.1";
|
|
728
|
+
try {
|
|
729
|
+
const res = await fetch(`${base.origin}/api/status?key=${encodeURIComponent(base.searchParams.get("key") ?? "")}`, { signal: AbortSignal.timeout(STATUS_TIMEOUT_MS) });
|
|
730
|
+
if (!res.ok) return null;
|
|
731
|
+
const json = await res.json();
|
|
732
|
+
if (typeof json.pid !== "number" || typeof json.clients !== "number" || typeof json.lastActivity !== "number") return null;
|
|
733
|
+
return { pid: json.pid, clients: json.clients, lastActivity: json.lastActivity };
|
|
734
|
+
} catch {
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function serverUrl(root) {
|
|
739
|
+
try {
|
|
740
|
+
const { url } = JSON.parse(readText(import_node_path8.default.join(root, ".state", "server-info")) ?? "");
|
|
741
|
+
return typeof url === "string" ? url : null;
|
|
742
|
+
} catch {
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
async function listServers(now = Date.now(), platform = process.platform) {
|
|
747
|
+
const dir = registryDir();
|
|
748
|
+
const current = process.env.RYUGI_ARTIFACTS_VERSION;
|
|
749
|
+
const live = readEntries(dir).filter((entry) => {
|
|
750
|
+
if (hasServerId(entry.pid, entry.serverId)) return true;
|
|
751
|
+
try {
|
|
752
|
+
removeEntry(dir, entry);
|
|
753
|
+
} catch {
|
|
754
|
+
}
|
|
755
|
+
return false;
|
|
756
|
+
});
|
|
757
|
+
const rows = await Promise.all(live.map(async (entry) => {
|
|
758
|
+
const url = serverUrl(entry.root);
|
|
759
|
+
const status = url ? await fetchStatus(url) : null;
|
|
760
|
+
if (status && status.pid !== entry.pid) return null;
|
|
761
|
+
if (!status && platform === "win32") return null;
|
|
762
|
+
const version = readText(import_node_path8.default.join(entry.root, ".state", "server-version")) || null;
|
|
763
|
+
return {
|
|
764
|
+
project: entry.project,
|
|
765
|
+
root: entry.root,
|
|
766
|
+
pid: entry.pid,
|
|
767
|
+
url,
|
|
768
|
+
port: url ? Number(new URL(url).port) : null,
|
|
769
|
+
startedAt: entry.startedAt,
|
|
770
|
+
version,
|
|
771
|
+
oldRelease: !!current && !!version && version !== current,
|
|
772
|
+
clients: status?.clients ?? null,
|
|
773
|
+
lastActivity: status?.lastActivity ?? null,
|
|
774
|
+
safeToStop: safeToStop(status, now),
|
|
775
|
+
status: status ? "ok" : "no-status",
|
|
776
|
+
log: import_node_path8.default.join(entry.root, ".state", "server.log")
|
|
777
|
+
};
|
|
778
|
+
}));
|
|
779
|
+
return rows.filter((row) => row !== null).sort((a, b) => a.project.localeCompare(b.project));
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// src/cli/stop.ts
|
|
783
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
784
|
+
async function stop(projectDir, emit) {
|
|
785
|
+
const paths = projectPaths(projectDir);
|
|
786
|
+
const markStopped = (reason) => {
|
|
787
|
+
import_node_fs8.default.rmSync(paths.info, { force: true });
|
|
788
|
+
import_node_fs8.default.mkdirSync(paths.state, { recursive: true });
|
|
789
|
+
import_node_fs8.default.writeFileSync(paths.stopped, `${JSON.stringify({ reason, timestamp: Math.floor(Date.now() / 1e3) })}
|
|
790
|
+
`);
|
|
791
|
+
};
|
|
792
|
+
const pid = Number(readText(paths.pid));
|
|
793
|
+
if (readText(paths.pid) === null) {
|
|
794
|
+
emit({ status: "not_running" });
|
|
795
|
+
return 0;
|
|
796
|
+
}
|
|
797
|
+
const id = readText(paths.serverId) ?? "";
|
|
798
|
+
if (!Number.isInteger(pid) || pid <= 0 || !/^[A-Za-z0-9_-]{32,64}$/.test(id) || !hasServerId(pid, id)) {
|
|
799
|
+
import_node_fs8.default.rmSync(paths.pid, { force: true });
|
|
800
|
+
import_node_fs8.default.rmSync(paths.serverId, { force: true });
|
|
801
|
+
markStopped("stale_pid");
|
|
802
|
+
emit({ status: "stale_pid" });
|
|
803
|
+
return 0;
|
|
804
|
+
}
|
|
805
|
+
try {
|
|
806
|
+
process.kill(pid, "SIGTERM");
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
if (!await waitForExit(pid, 2e3)) {
|
|
810
|
+
try {
|
|
811
|
+
process.kill(pid, "SIGKILL");
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
await waitForExit(pid, 1e3);
|
|
815
|
+
}
|
|
816
|
+
if (alive(pid)) {
|
|
817
|
+
emit({ status: "failed", error: "process still running" });
|
|
818
|
+
return 1;
|
|
819
|
+
}
|
|
820
|
+
for (const file of [paths.pid, paths.serverId, paths.log, paths.owners]) import_node_fs8.default.rmSync(file, { force: true });
|
|
821
|
+
markStopped("stop");
|
|
822
|
+
emit({ status: "stopped" });
|
|
823
|
+
return 0;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/cli/monitor-ui.ts
|
|
827
|
+
var LOG_TAIL_BYTES = 64 * 1024;
|
|
828
|
+
var LOG_REFRESH_MS = 500;
|
|
829
|
+
function readTail(file) {
|
|
830
|
+
let fd;
|
|
831
|
+
try {
|
|
832
|
+
fd = import_node_fs9.default.openSync(file, "r");
|
|
833
|
+
} catch {
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
try {
|
|
837
|
+
const size = import_node_fs9.default.fstatSync(fd).size;
|
|
838
|
+
const length = Math.min(size, LOG_TAIL_BYTES);
|
|
839
|
+
const buffer = Buffer.alloc(length);
|
|
840
|
+
import_node_fs9.default.readSync(fd, buffer, 0, length, size - length);
|
|
841
|
+
return buffer.toString("utf8");
|
|
842
|
+
} finally {
|
|
843
|
+
import_node_fs9.default.closeSync(fd);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function clipboardCommands() {
|
|
847
|
+
if (process.platform === "darwin") return [["pbcopy", []]];
|
|
848
|
+
if (process.platform === "win32") return [["clip", []]];
|
|
849
|
+
return [["wl-copy", []], ["xclip", ["-selection", "clipboard"]], ["xsel", ["--clipboard", "--input"]]];
|
|
850
|
+
}
|
|
851
|
+
function copy(text2, commands = clipboardCommands()) {
|
|
852
|
+
const [first, ...rest] = commands;
|
|
853
|
+
if (!first) return Promise.resolve(false);
|
|
854
|
+
return new Promise((resolve) => {
|
|
855
|
+
const child = (0, import_node_child_process2.spawn)(first[0], first[1], { stdio: ["pipe", "ignore", "ignore"] });
|
|
856
|
+
child.on("error", () => resolve(copy(text2, rest)));
|
|
857
|
+
child.on("close", (code) => code === 0 ? resolve(true) : resolve(copy(text2, rest)));
|
|
858
|
+
child.stdin.end(text2);
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
async function runMonitorUi() {
|
|
862
|
+
const { stdin, stdout } = process;
|
|
863
|
+
let rows = await listServers();
|
|
864
|
+
let state = initialView();
|
|
865
|
+
let busy = false;
|
|
866
|
+
let closed = false;
|
|
867
|
+
const restore = () => {
|
|
868
|
+
if (closed) return;
|
|
869
|
+
closed = true;
|
|
870
|
+
try {
|
|
871
|
+
stdin.setRawMode(false);
|
|
872
|
+
} catch {
|
|
873
|
+
}
|
|
874
|
+
stdout.write("\x1B[?25h\x1B[?1049l");
|
|
875
|
+
};
|
|
876
|
+
process.on("exit", restore);
|
|
877
|
+
const draw = () => {
|
|
878
|
+
if (closed) return;
|
|
879
|
+
const screen = { width: stdout.columns || 80, height: stdout.rows || 24, now: Date.now(), home: import_node_os5.default.homedir() };
|
|
880
|
+
const row = rows[state.selected];
|
|
881
|
+
const lines = state.mode === "log" && row ? renderLog(row, readTail(row.log), screen) : renderList(rows, state, screen);
|
|
882
|
+
stdout.write(`\x1B[H${lines.join("\x1B[K\n")}\x1B[K\x1B[J`);
|
|
883
|
+
};
|
|
884
|
+
const refresh = async () => {
|
|
885
|
+
if (busy || closed) return;
|
|
886
|
+
busy = true;
|
|
887
|
+
const selected = rows[state.selected]?.project;
|
|
888
|
+
try {
|
|
889
|
+
rows = await listServers();
|
|
890
|
+
} finally {
|
|
891
|
+
busy = false;
|
|
892
|
+
}
|
|
893
|
+
const index = rows.findIndex((row) => row.project === selected);
|
|
894
|
+
state = { ...state, selected: index >= 0 ? index : Math.min(state.selected, Math.max(0, rows.length - 1)) };
|
|
895
|
+
if (state.mode === "log" && !rows[state.selected]) state = { ...state, mode: "list" };
|
|
896
|
+
draw();
|
|
897
|
+
};
|
|
898
|
+
const say = (message) => {
|
|
899
|
+
state = { ...state, message };
|
|
900
|
+
draw();
|
|
901
|
+
};
|
|
902
|
+
const run = async (action) => {
|
|
903
|
+
if (action.type === "open") {
|
|
904
|
+
const launcher = browserLauncher(process.platform, action.row.url);
|
|
905
|
+
if (launcher) (0, import_node_child_process2.execFile)(launcher.bin, launcher.args, () => {
|
|
906
|
+
});
|
|
907
|
+
say(launcher ? `Opened ${action.row.url}` : `Open ${action.row.url}`);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (action.type === "copy") {
|
|
911
|
+
say(await copy(action.row.url) ? `Copied ${action.row.url}` : `No clipboard tool found; the URL is ${action.row.url}`);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (action.type === "stop") {
|
|
915
|
+
say(`Stopping ${action.rows.length === 1 ? shortPath(action.rows[0].project) : `${action.rows.length} servers`}\u2026`);
|
|
916
|
+
const failed = [];
|
|
917
|
+
for (const row of action.rows) {
|
|
918
|
+
let status = "failed";
|
|
919
|
+
try {
|
|
920
|
+
await stop(row.project, (value) => {
|
|
921
|
+
status = String(value.status ?? "failed");
|
|
922
|
+
});
|
|
923
|
+
} catch {
|
|
924
|
+
}
|
|
925
|
+
if (status === "failed") failed.push(shortPath(row.project));
|
|
926
|
+
}
|
|
927
|
+
await refresh();
|
|
928
|
+
say(failed.length ? `Could not stop ${failed.join(", ")}` : `Stopped ${action.rows.length === 1 ? shortPath(action.rows[0].project) : `${action.rows.length} servers`}`);
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
return new Promise((resolve) => {
|
|
932
|
+
const timers = [
|
|
933
|
+
setInterval(() => void refresh(), REFRESH_SECONDS * 1e3),
|
|
934
|
+
setInterval(() => {
|
|
935
|
+
if (state.mode === "log") draw();
|
|
936
|
+
}, LOG_REFRESH_MS)
|
|
937
|
+
];
|
|
938
|
+
const quit = (code) => {
|
|
939
|
+
timers.forEach(clearInterval);
|
|
940
|
+
stdin.off("keypress", onKey);
|
|
941
|
+
stdout.off("resize", draw);
|
|
942
|
+
stdin.pause();
|
|
943
|
+
restore();
|
|
944
|
+
resolve(code);
|
|
945
|
+
};
|
|
946
|
+
const onKey = (_, key) => {
|
|
947
|
+
const result = handleKey(state, key ?? {}, rows);
|
|
948
|
+
state = result.state;
|
|
949
|
+
if (result.action?.type === "quit") return quit(0);
|
|
950
|
+
draw();
|
|
951
|
+
if (result.action) void run(result.action);
|
|
952
|
+
};
|
|
953
|
+
for (const [signal, code] of [["SIGINT", 130], ["SIGTERM", 143], ["SIGHUP", 129]]) process.once(signal, () => quit(code));
|
|
954
|
+
stdout.write("\x1B[?1049h\x1B[?25l");
|
|
955
|
+
import_node_readline.default.emitKeypressEvents(stdin);
|
|
956
|
+
stdin.setRawMode(true);
|
|
957
|
+
stdin.resume();
|
|
958
|
+
stdin.on("keypress", onKey);
|
|
959
|
+
stdout.on("resize", draw);
|
|
960
|
+
draw();
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/cli/monitor.ts
|
|
965
|
+
function monitorText(value, now = Date.now()) {
|
|
966
|
+
if (value.type !== "servers") return null;
|
|
967
|
+
const rows = value.servers;
|
|
968
|
+
if (!rows.length) return "No artifacts servers are running.";
|
|
969
|
+
return rows.map((row) => [
|
|
970
|
+
shortPath(row.project),
|
|
971
|
+
row.url ?? "starting",
|
|
972
|
+
`pid ${row.pid}`,
|
|
973
|
+
row.clients === null ? null : `tabs ${row.clients}`,
|
|
974
|
+
row.lastActivity === null ? null : `idle ${duration(now - row.lastActivity)}`,
|
|
975
|
+
...tags(row).map((tag) => `[${tag}]`)
|
|
976
|
+
].filter(Boolean).join(" ")).join("\n");
|
|
977
|
+
}
|
|
978
|
+
async function monitor(options, emit) {
|
|
979
|
+
if (options.json || !process.stdout.isTTY || !process.stdin.isTTY) {
|
|
980
|
+
emit({ type: "servers", servers: await listServers() });
|
|
981
|
+
return 0;
|
|
982
|
+
}
|
|
983
|
+
return runMonitorUi();
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// src/cli/start.ts
|
|
987
|
+
var import_node_child_process3 = require("node:child_process");
|
|
988
|
+
var import_node_crypto3 = __toESM(require("node:crypto"), 1);
|
|
989
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
990
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
991
|
+
var import_node_readline2 = __toESM(require("node:readline"), 1);
|
|
992
|
+
|
|
993
|
+
// src/server/artifact-address.ts
|
|
994
|
+
var import_node_crypto2 = __toESM(require("node:crypto"), 1);
|
|
995
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
996
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
530
997
|
|
|
531
998
|
// src/shared/ids.ts
|
|
532
999
|
var ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -542,15 +1009,15 @@ function artifactKeyOf(relative) {
|
|
|
542
1009
|
|
|
543
1010
|
// src/server/artifact-address.ts
|
|
544
1011
|
function artifactId(dir) {
|
|
545
|
-
const file =
|
|
1012
|
+
const file = import_node_path9.default.join(dir, ".artifact-id");
|
|
546
1013
|
let id;
|
|
547
1014
|
try {
|
|
548
|
-
id =
|
|
1015
|
+
id = import_node_fs10.default.readFileSync(file, "utf8").trim();
|
|
549
1016
|
} catch (err) {
|
|
550
1017
|
if (err.code !== "ENOENT") throw err;
|
|
551
|
-
id =
|
|
1018
|
+
id = import_node_crypto2.default.randomBytes(16).toString("hex");
|
|
552
1019
|
try {
|
|
553
|
-
|
|
1020
|
+
import_node_fs10.default.writeFileSync(file, `${id}
|
|
554
1021
|
`, { flag: "wx" });
|
|
555
1022
|
} catch (writeError) {
|
|
556
1023
|
if (writeError.code !== "EEXIST") throw writeError;
|
|
@@ -562,8 +1029,8 @@ function artifactId(dir) {
|
|
|
562
1029
|
}
|
|
563
1030
|
function artifactPath(root, type, slug) {
|
|
564
1031
|
if (!isArtifactType(type) || !isArtifactId(slug)) throw new Error("Invalid artifact locator");
|
|
565
|
-
const dir =
|
|
566
|
-
|
|
1032
|
+
const dir = import_node_path9.default.join(root, type, slug);
|
|
1033
|
+
import_node_fs10.default.mkdirSync(dir, { recursive: true });
|
|
567
1034
|
return `/${type}/${artifactId(dir)}`;
|
|
568
1035
|
}
|
|
569
1036
|
|
|
@@ -601,13 +1068,13 @@ async function withCloudLink(out, artifact) {
|
|
|
601
1068
|
async function acquireLock(lock) {
|
|
602
1069
|
for (let attempt = 0; attempt < 150; attempt++) {
|
|
603
1070
|
try {
|
|
604
|
-
|
|
1071
|
+
import_node_fs11.default.mkdirSync(lock);
|
|
605
1072
|
return true;
|
|
606
1073
|
} catch {
|
|
607
1074
|
}
|
|
608
1075
|
try {
|
|
609
|
-
if (Date.now() -
|
|
610
|
-
|
|
1076
|
+
if (Date.now() - import_node_fs11.default.statSync(lock).mtimeMs > 6e4) {
|
|
1077
|
+
import_node_fs11.default.rmdirSync(lock);
|
|
611
1078
|
continue;
|
|
612
1079
|
}
|
|
613
1080
|
} catch {
|
|
@@ -637,13 +1104,13 @@ function registerOwner(paths) {
|
|
|
637
1104
|
const owner = ownerPid();
|
|
638
1105
|
if (owner) owners.add(owner);
|
|
639
1106
|
const tmp = `${paths.owners}.${process.pid}`;
|
|
640
|
-
|
|
1107
|
+
import_node_fs11.default.writeFileSync(tmp, [...owners].sort((a, b) => a - b).map((pid) => `${pid}
|
|
641
1108
|
`).join(""));
|
|
642
|
-
|
|
1109
|
+
import_node_fs11.default.renameSync(tmp, paths.owners);
|
|
643
1110
|
}
|
|
644
1111
|
function recordAgent(paths) {
|
|
645
1112
|
const markers = Object.fromEntries(AGENT_MARKERS.flatMap((key) => process.env[key] ? [[key, process.env[key]]] : []));
|
|
646
|
-
if (Object.keys(markers).length)
|
|
1113
|
+
if (Object.keys(markers).length) import_node_fs11.default.writeFileSync(paths.agent, `${JSON.stringify(markers)}
|
|
647
1114
|
`);
|
|
648
1115
|
}
|
|
649
1116
|
async function replace(pid, paths) {
|
|
@@ -675,9 +1142,9 @@ async function start(options, emit) {
|
|
|
675
1142
|
const foreground = options.foreground || !options.background && (!!process.env.CODEX_CI || windowsLike());
|
|
676
1143
|
process.umask(63);
|
|
677
1144
|
const paths = projectPaths(options.projectDir);
|
|
678
|
-
|
|
679
|
-
if (!
|
|
680
|
-
const server =
|
|
1145
|
+
import_node_fs11.default.mkdirSync(paths.state, { recursive: true });
|
|
1146
|
+
if (!import_node_fs11.default.existsSync(import_node_path10.default.join(paths.state, ".gitignore"))) import_node_fs11.default.writeFileSync(import_node_path10.default.join(paths.state, ".gitignore"), "*\n");
|
|
1147
|
+
const server = import_node_path10.default.join(__dirname, "server.cjs");
|
|
681
1148
|
const version = process.env.RYUGI_ARTIFACTS_VERSION ?? "";
|
|
682
1149
|
if (!await acquireLock(paths.lock)) throw new CliError("Another start for this project is still starting; retry in a moment");
|
|
683
1150
|
let locked = true;
|
|
@@ -685,7 +1152,7 @@ async function start(options, emit) {
|
|
|
685
1152
|
if (!locked) return;
|
|
686
1153
|
locked = false;
|
|
687
1154
|
try {
|
|
688
|
-
|
|
1155
|
+
import_node_fs11.default.rmdirSync(paths.lock);
|
|
689
1156
|
} catch {
|
|
690
1157
|
}
|
|
691
1158
|
};
|
|
@@ -703,20 +1170,20 @@ async function start(options, emit) {
|
|
|
703
1170
|
}
|
|
704
1171
|
registerOwner(paths);
|
|
705
1172
|
recordAgent(paths);
|
|
706
|
-
|
|
707
|
-
const id =
|
|
708
|
-
|
|
1173
|
+
import_node_fs11.default.rmSync(paths.info, { force: true });
|
|
1174
|
+
const id = import_node_crypto3.default.randomBytes(24).toString("hex");
|
|
1175
|
+
import_node_fs11.default.writeFileSync(paths.serverId, `${id}
|
|
709
1176
|
`);
|
|
710
|
-
|
|
1177
|
+
import_node_fs11.default.writeFileSync(paths.version, `${version}
|
|
711
1178
|
`);
|
|
712
1179
|
const env = serverEnv(paths, options);
|
|
713
1180
|
const args = [server, `--artifacts-server-id=${id}`];
|
|
714
1181
|
if (foreground) {
|
|
715
|
-
const child2 = (0,
|
|
716
|
-
|
|
1182
|
+
const child2 = (0, import_node_child_process3.spawn)(process.execPath, args, { env, stdio: ["ignore", "pipe", "inherit"] });
|
|
1183
|
+
import_node_fs11.default.writeFileSync(paths.pid, `${child2.pid}
|
|
717
1184
|
`);
|
|
718
1185
|
for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child2.kill("SIGTERM"));
|
|
719
|
-
|
|
1186
|
+
import_node_readline2.default.createInterface({ input: child2.stdout }).on("line", (line) => {
|
|
720
1187
|
let value;
|
|
721
1188
|
try {
|
|
722
1189
|
value = JSON.parse(line);
|
|
@@ -729,15 +1196,15 @@ async function start(options, emit) {
|
|
|
729
1196
|
else void withCloudLink(withLink(value, options.artifact, false), options.artifact).then(emit);
|
|
730
1197
|
});
|
|
731
1198
|
const exited = new Promise((resolve) => child2.on("close", (code) => resolve(code ?? 1)));
|
|
732
|
-
for (let waited = 0; waited < 5e3 && !
|
|
1199
|
+
for (let waited = 0; waited < 5e3 && !import_node_fs11.default.existsSync(paths.info); waited += 100) await sleep(100);
|
|
733
1200
|
unlock();
|
|
734
1201
|
return await exited;
|
|
735
1202
|
}
|
|
736
|
-
const log =
|
|
737
|
-
const child = (0,
|
|
738
|
-
|
|
1203
|
+
const log = import_node_fs11.default.openSync(paths.log, "w");
|
|
1204
|
+
const child = (0, import_node_child_process3.spawn)(process.execPath, args, { env, detached: true, stdio: ["ignore", log, log] });
|
|
1205
|
+
import_node_fs11.default.closeSync(log);
|
|
739
1206
|
child.unref();
|
|
740
|
-
|
|
1207
|
+
import_node_fs11.default.writeFileSync(paths.pid, `${child.pid}
|
|
741
1208
|
`);
|
|
742
1209
|
for (let waited = 0; waited < 5e3; waited += 100) {
|
|
743
1210
|
const lines = (readText(paths.log) ?? "").split("\n");
|
|
@@ -816,52 +1283,8 @@ async function publish(options, emit) {
|
|
|
816
1283
|
return 0;
|
|
817
1284
|
}
|
|
818
1285
|
|
|
819
|
-
// src/cli/stop.ts
|
|
820
|
-
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
821
|
-
async function stop(projectDir, emit) {
|
|
822
|
-
const paths = projectPaths(projectDir);
|
|
823
|
-
const markStopped = (reason) => {
|
|
824
|
-
import_node_fs9.default.rmSync(paths.info, { force: true });
|
|
825
|
-
import_node_fs9.default.mkdirSync(paths.state, { recursive: true });
|
|
826
|
-
import_node_fs9.default.writeFileSync(paths.stopped, `${JSON.stringify({ reason, timestamp: Math.floor(Date.now() / 1e3) })}
|
|
827
|
-
`);
|
|
828
|
-
};
|
|
829
|
-
const pid = Number(readText(paths.pid));
|
|
830
|
-
if (readText(paths.pid) === null) {
|
|
831
|
-
emit({ status: "not_running" });
|
|
832
|
-
return 0;
|
|
833
|
-
}
|
|
834
|
-
const id = readText(paths.serverId) ?? "";
|
|
835
|
-
if (!Number.isInteger(pid) || pid <= 0 || !/^[A-Za-z0-9_-]{32,64}$/.test(id) || !hasServerId(pid, id)) {
|
|
836
|
-
import_node_fs9.default.rmSync(paths.pid, { force: true });
|
|
837
|
-
import_node_fs9.default.rmSync(paths.serverId, { force: true });
|
|
838
|
-
markStopped("stale_pid");
|
|
839
|
-
emit({ status: "stale_pid" });
|
|
840
|
-
return 0;
|
|
841
|
-
}
|
|
842
|
-
try {
|
|
843
|
-
process.kill(pid, "SIGTERM");
|
|
844
|
-
} catch {
|
|
845
|
-
}
|
|
846
|
-
if (!await waitForExit(pid, 2e3)) {
|
|
847
|
-
try {
|
|
848
|
-
process.kill(pid, "SIGKILL");
|
|
849
|
-
} catch {
|
|
850
|
-
}
|
|
851
|
-
await waitForExit(pid, 1e3);
|
|
852
|
-
}
|
|
853
|
-
if (alive(pid)) {
|
|
854
|
-
emit({ status: "failed", error: "process still running" });
|
|
855
|
-
return 1;
|
|
856
|
-
}
|
|
857
|
-
for (const file of [paths.pid, paths.serverId, paths.log, paths.owners]) import_node_fs9.default.rmSync(file, { force: true });
|
|
858
|
-
markStopped("stop");
|
|
859
|
-
emit({ status: "stopped" });
|
|
860
|
-
return 0;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
1286
|
// src/cli/main.ts
|
|
864
|
-
var
|
|
1287
|
+
var HELP2 = `Usage: ryugi-artifacts [project-directory] [--no-open]
|
|
865
1288
|
ryugi-artifacts <command> [options]
|
|
866
1289
|
|
|
867
1290
|
Without a command, opens the project's artifacts in your browser and keeps the server
|
|
@@ -882,6 +1305,8 @@ Commands:
|
|
|
882
1305
|
reply --artifact <document|artboard>/<id> --thread <threadId> [--text <text>]
|
|
883
1306
|
resolve --artifact <document|artboard>/<id> --thread <threadId>
|
|
884
1307
|
Without --text, add and reply read the message from stdin.
|
|
1308
|
+
monitor List every running artifacts server on this machine and manage them. In a terminal it
|
|
1309
|
+
opens a live view (open, copy URL, logs, stop); otherwise, or with --json, prints the list once.
|
|
885
1310
|
config Auto-publish changed artifacts as private cloud copies when the agent's turn ends:
|
|
886
1311
|
--auto-publish on|off for every project
|
|
887
1312
|
--auto-publish on|off|inherit --project override for this project only
|
|
@@ -914,6 +1339,8 @@ ${value.url}`;
|
|
|
914
1339
|
if (value.type === "auto-publish") return `Auto-publish: ${value.global === null ? "not decided" : value.global ? "on" : "off"} for every project${value.project === null ? "" : `, ${value.project ? "on" : "off"} for this project`}.`;
|
|
915
1340
|
const checked = checkText(value);
|
|
916
1341
|
if (checked !== null) return checked;
|
|
1342
|
+
const servers = monitorText(value);
|
|
1343
|
+
if (servers !== null) return servers;
|
|
917
1344
|
const commented = commentText(value);
|
|
918
1345
|
if (commented !== null) return commented;
|
|
919
1346
|
if (typeof value.status === "string") return value.status === "stopped" ? "Stopped." : value.status.replace(/_/g, " ");
|
|
@@ -922,7 +1349,7 @@ ${value.url}`;
|
|
|
922
1349
|
function openBrowser(url) {
|
|
923
1350
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
924
1351
|
const args = process.platform === "win32" ? ["/c", "start", '""', url] : [url];
|
|
925
|
-
(0,
|
|
1352
|
+
(0, import_node_child_process4.execFile)(command, args, () => {
|
|
926
1353
|
});
|
|
927
1354
|
}
|
|
928
1355
|
async function browse(argv) {
|
|
@@ -1045,12 +1472,16 @@ async function main(argv) {
|
|
|
1045
1472
|
project: !!values.project
|
|
1046
1473
|
}, emit);
|
|
1047
1474
|
}
|
|
1475
|
+
if (command === "monitor") {
|
|
1476
|
+
const { values } = parse(rest, {});
|
|
1477
|
+
return await monitor({ json: !!values.json }, emit);
|
|
1478
|
+
}
|
|
1048
1479
|
if (command === "--version" || command === "-v") {
|
|
1049
|
-
console.log("1.
|
|
1480
|
+
console.log("1.34.0");
|
|
1050
1481
|
return 0;
|
|
1051
1482
|
}
|
|
1052
1483
|
if (command === "--help" || command === "-h") {
|
|
1053
|
-
console.log(
|
|
1484
|
+
console.log(HELP2);
|
|
1054
1485
|
return 0;
|
|
1055
1486
|
}
|
|
1056
1487
|
return await browse(argv);
|