@yusukeshib/pi-babysit 0.3.9 → 0.3.11
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/README.md +8 -0
- package/format-stream.mjs +39 -12
- package/index.ts +50 -17
- package/package.json +2 -1
- package/rpc-stream-proxy.mjs +134 -0
package/README.md
CHANGED
|
@@ -95,6 +95,14 @@ Tail and search results are capped at 200 lines and clipped to 8 KB. Pattern
|
|
|
95
95
|
search returns the latest matching lines with line numbers. Do not read a
|
|
96
96
|
potentially large log file in full.
|
|
97
97
|
|
|
98
|
+
Subagent logs also compact Pi's streaming `message_update` events before they
|
|
99
|
+
are recorded. Pi repeats the complete growing assistant message and partial
|
|
100
|
+
snapshot on every token; pi-babysit retains only the incremental delta while
|
|
101
|
+
leaving authoritative `message_end`, tool, lifecycle, response, and error
|
|
102
|
+
events untouched. This keeps long RPC sessions approximately linear in emitted
|
|
103
|
+
content without changing final answers, completion detection, or follow-up
|
|
104
|
+
behavior. Live `/babysit` and attach views render the retained deltas.
|
|
105
|
+
|
|
98
106
|
All shell commands, including `pwd` and Git, are redirected to `babysit_run`.
|
|
99
107
|
Set `PI_BABYSIT_ALLOW_BASH=1` only as an explicit emergency escape hatch.
|
|
100
108
|
|
package/format-stream.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* format-stream.mjs — human-readable view for a `pi --mode
|
|
3
|
+
* format-stream.mjs — human-readable view for a `pi --mode rpc` event stream.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* pane sees turns, thinking, tool calls and answers instead of a JSON firehose.
|
|
5
|
+
* This filter sits on the interactive attach path via `babysit run --view-cmd`.
|
|
6
|
+
* It accepts both Pi's raw cumulative updates and pi-babysit's compact deltas,
|
|
7
|
+
* then prints a colored, incrementally streamed transcript so a human sees
|
|
8
|
+
* turns, thinking, tool calls and answers instead of a JSON firehose.
|
|
10
9
|
*/
|
|
11
10
|
import * as readline from "node:readline";
|
|
12
11
|
|
|
@@ -52,8 +51,8 @@ function summarizeTool(name, a = {}) {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
// Join the text of a given block kind ("text" or "thinking") in a message's
|
|
55
|
-
// content array.
|
|
56
|
-
//
|
|
54
|
+
// content array. Unfiltered RPC streams repeat the whole growing message;
|
|
55
|
+
// pi-babysit's compact stream instead retains only assistantMessageEvent.delta.
|
|
57
56
|
function blockText(content, kind) {
|
|
58
57
|
if (!Array.isArray(content)) return "";
|
|
59
58
|
let s = "";
|
|
@@ -75,15 +74,20 @@ const openBlock = () => {
|
|
|
75
74
|
out("\n");
|
|
76
75
|
};
|
|
77
76
|
|
|
78
|
-
function
|
|
79
|
-
if (!msg || msg.role !== "assistant") return;
|
|
80
|
-
const id = msg.id ?? `t${turn}`;
|
|
77
|
+
function messageState(id = `t${turn}`, fresh = false) {
|
|
81
78
|
if (fresh) emitted.delete(id);
|
|
82
79
|
let st = emitted.get(id);
|
|
83
80
|
if (!st) {
|
|
84
81
|
st = { think: 0, text: 0, hThink: false, hText: false };
|
|
85
82
|
emitted.set(id, st);
|
|
86
83
|
}
|
|
84
|
+
return st;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function streamMessage(msg, fresh = false) {
|
|
88
|
+
if (!msg || msg.role !== "assistant") return;
|
|
89
|
+
const id = msg.id ?? `t${turn}`;
|
|
90
|
+
const st = messageState(id, fresh);
|
|
87
91
|
const think = blockText(msg.content, "thinking");
|
|
88
92
|
if (think.length > st.think) {
|
|
89
93
|
if (!st.hThink) {
|
|
@@ -106,6 +110,28 @@ function streamMessage(msg, fresh = false) {
|
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
function streamDelta(event) {
|
|
114
|
+
if (!event || typeof event.delta !== "string" || !event.delta) return;
|
|
115
|
+
const st = messageState();
|
|
116
|
+
if (event.type === "thinking_delta") {
|
|
117
|
+
if (!st.hThink) {
|
|
118
|
+
openBlock();
|
|
119
|
+
out(`${C.gray}[think] `);
|
|
120
|
+
st.hThink = true;
|
|
121
|
+
}
|
|
122
|
+
out(C.gray + event.delta + C.reset);
|
|
123
|
+
st.think += event.delta.length;
|
|
124
|
+
} else if (event.type === "text_delta") {
|
|
125
|
+
if (!st.hText) {
|
|
126
|
+
openBlock();
|
|
127
|
+
out(`${C.green}[text]${C.reset} `);
|
|
128
|
+
st.hText = true;
|
|
129
|
+
}
|
|
130
|
+
out(event.delta);
|
|
131
|
+
st.text += event.delta.length;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
109
135
|
function usageLine(msg) {
|
|
110
136
|
const u = msg?.usage;
|
|
111
137
|
if (!u) return;
|
|
@@ -138,7 +164,8 @@ rl.on("line", (line) => {
|
|
|
138
164
|
streamMessage(ev.message, true);
|
|
139
165
|
break;
|
|
140
166
|
case "message_update":
|
|
141
|
-
streamMessage(ev.message);
|
|
167
|
+
if (ev.message) streamMessage(ev.message);
|
|
168
|
+
else streamDelta(ev.assistantMessageEvent);
|
|
142
169
|
break;
|
|
143
170
|
case "message_end":
|
|
144
171
|
streamMessage(ev.message);
|
package/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ import * as path from "node:path";
|
|
|
38
38
|
import { fileURLToPath } from "node:url";
|
|
39
39
|
import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
40
40
|
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
41
|
-
import { Box,
|
|
41
|
+
import { Box, Markdown, Text } from "@earendil-works/pi-tui";
|
|
42
42
|
import { Type } from "typebox";
|
|
43
43
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
44
44
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents";
|
|
@@ -130,10 +130,10 @@ export function planSubagentSpawn(
|
|
|
130
130
|
// apart from any other turn end (see isParkedToolResult / self-reap.ts).
|
|
131
131
|
export const NOTIFY_MARKER = "[notify-on-exit]";
|
|
132
132
|
|
|
133
|
-
// Human-readable view for the subagent JSONL stream when a human
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// disable
|
|
133
|
+
// Human-readable view for the compact subagent JSONL stream when a human
|
|
134
|
+
// attaches. The RPC proxy removes cumulative streaming snapshots before both
|
|
135
|
+
// recording and display; authoritative events remain intact for parsers. Set
|
|
136
|
+
// PI_BABYSIT_VIEW_CMD="" to disable formatting, or provide a custom command.
|
|
137
137
|
const EXT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
138
138
|
const shq = (s: string) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
139
139
|
const VIEW_CMD =
|
|
@@ -634,7 +634,10 @@ export function summarizeNotificationCommand(command: string | undefined): strin
|
|
|
634
634
|
.trim()
|
|
635
635
|
.replace(/\r/g, "\\r")
|
|
636
636
|
.replace(/\n/g, "\\n")
|
|
637
|
-
.replace(/\t/g, "\\t")
|
|
637
|
+
.replace(/\t/g, "\\t")
|
|
638
|
+
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, (char) =>
|
|
639
|
+
`\\x${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`,
|
|
640
|
+
) || "?";
|
|
638
641
|
const bytes = Buffer.from(preview, "utf8");
|
|
639
642
|
if (bytes.length <= NOTIFY_COMMAND_MAX_BYTES) return preview;
|
|
640
643
|
if (NOTIFY_COMMAND_MAX_BYTES === 0) return "";
|
|
@@ -674,6 +677,7 @@ interface ProcessCompletionMessage {
|
|
|
674
677
|
status: CompletionStatus;
|
|
675
678
|
runtime?: string;
|
|
676
679
|
logPath?: string;
|
|
680
|
+
command?: string;
|
|
677
681
|
count: number;
|
|
678
682
|
totalCount: number;
|
|
679
683
|
remainingCount: number;
|
|
@@ -683,6 +687,7 @@ interface ProcessCompletionMessage {
|
|
|
683
687
|
success: boolean;
|
|
684
688
|
status: CompletionStatus;
|
|
685
689
|
runtime: string;
|
|
690
|
+
command: string | undefined;
|
|
686
691
|
logPath: string;
|
|
687
692
|
}>;
|
|
688
693
|
};
|
|
@@ -791,6 +796,7 @@ export function buildProcessCompletionMessage(
|
|
|
791
796
|
success: notice.success,
|
|
792
797
|
status: notice.status,
|
|
793
798
|
runtime: notice.runtime,
|
|
799
|
+
command: notice.command,
|
|
794
800
|
logPath: notice.logPath,
|
|
795
801
|
}));
|
|
796
802
|
const single = totalCount === 1 ? batch[0] : undefined;
|
|
@@ -805,6 +811,7 @@ export function buildProcessCompletionMessage(
|
|
|
805
811
|
status,
|
|
806
812
|
runtime: single?.runtime,
|
|
807
813
|
logPath: single?.logPath,
|
|
814
|
+
command: single?.command,
|
|
808
815
|
count: batch.length,
|
|
809
816
|
totalCount,
|
|
810
817
|
remainingCount: totalCount - batch.length,
|
|
@@ -871,7 +878,7 @@ interface ToolCall {
|
|
|
871
878
|
name: string;
|
|
872
879
|
summary: string;
|
|
873
880
|
}
|
|
874
|
-
interface Progress {
|
|
881
|
+
export interface Progress {
|
|
875
882
|
turns: number;
|
|
876
883
|
toolCalls: ToolCall[];
|
|
877
884
|
finalText: string;
|
|
@@ -914,7 +921,7 @@ function summarizeToolCall(name: string, args: Record<string, unknown>): string
|
|
|
914
921
|
}
|
|
915
922
|
}
|
|
916
923
|
|
|
917
|
-
function parseEvents(logText: string): Progress {
|
|
924
|
+
export function parseEvents(logText: string): Progress {
|
|
918
925
|
const p: Progress = {
|
|
919
926
|
turns: 0,
|
|
920
927
|
toolCalls: [],
|
|
@@ -1122,13 +1129,21 @@ async function spawnSubagent(
|
|
|
1122
1129
|
"--timeout",
|
|
1123
1130
|
opts.timeout,
|
|
1124
1131
|
];
|
|
1125
|
-
// Pretty-print the JSONL stream for humans who `attach
|
|
1126
|
-
//
|
|
1132
|
+
// Pretty-print the compact JSONL stream for humans who `attach`. The RPC
|
|
1133
|
+
// proxy removes only cumulative streaming snapshots; authoritative final,
|
|
1134
|
+
// tool, lifecycle, response, and error events remain intact for parseEvents.
|
|
1127
1135
|
if (VIEW_CMD.trim()) bsArgs.push("--view-cmd", VIEW_CMD);
|
|
1128
1136
|
if (opts.idleTimeout && opts.idleTimeout !== "none") {
|
|
1129
1137
|
bsArgs.push("--idle-timeout", opts.idleTimeout);
|
|
1130
1138
|
}
|
|
1131
|
-
bsArgs.push(
|
|
1139
|
+
bsArgs.push(
|
|
1140
|
+
"--",
|
|
1141
|
+
process.execPath,
|
|
1142
|
+
path.join(EXT_DIR, "rpc-stream-proxy.mjs"),
|
|
1143
|
+
"--",
|
|
1144
|
+
PI_BIN,
|
|
1145
|
+
...piArgs,
|
|
1146
|
+
);
|
|
1132
1147
|
|
|
1133
1148
|
const r = await bs(bsArgs, {
|
|
1134
1149
|
cwd: opts.cwd,
|
|
@@ -1738,11 +1753,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
1738
1753
|
status?: DisplayStatus;
|
|
1739
1754
|
success?: boolean;
|
|
1740
1755
|
exitCode?: number | null;
|
|
1756
|
+
command?: string;
|
|
1757
|
+
count?: number;
|
|
1741
1758
|
};
|
|
1742
1759
|
const status =
|
|
1743
1760
|
d.status ?? (d.success ? "success" : d.exitCode == null ? "terminated" : "failed");
|
|
1761
|
+
const payload = d.command
|
|
1762
|
+
? ` ${summarizeNotificationCommand(d.command)}`
|
|
1763
|
+
: d.count && d.count > 1
|
|
1764
|
+
? ` ×${d.count}`
|
|
1765
|
+
: "";
|
|
1766
|
+
const header = theme.fg("warning", theme.bold(`babysit_run COMMAND${payload}`));
|
|
1744
1767
|
const box = new Box(1, 1, (t) => theme.bg("toolSuccessBg", t));
|
|
1745
|
-
box.addChild(new Text(
|
|
1768
|
+
box.addChild(new Text(header, 0, 0));
|
|
1769
|
+
box.addChild(new Text(renderStatus(status, theme), 0, 0));
|
|
1746
1770
|
box.addChild(new Text(theme.fg("toolOutput", content), 0, 0));
|
|
1747
1771
|
return box;
|
|
1748
1772
|
});
|
|
@@ -2129,10 +2153,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
2129
2153
|
},
|
|
2130
2154
|
};
|
|
2131
2155
|
},
|
|
2132
|
-
//
|
|
2133
|
-
//
|
|
2134
|
-
renderCall() {
|
|
2135
|
-
|
|
2156
|
+
// Make the execution kind and payload identifiable before the result arrives.
|
|
2157
|
+
// Use semantic theme colors rather than the fallback gray tool-call text.
|
|
2158
|
+
renderCall(args, theme) {
|
|
2159
|
+
const isSubagent = args.profile === "subagent";
|
|
2160
|
+
const kind = isSubagent ? "AGENT" : "COMMAND";
|
|
2161
|
+
const payload = isSubagent ? args.task : args.command;
|
|
2162
|
+
const agent = isSubagent && args.agent ? ` [${args.agent}]` : "";
|
|
2163
|
+
const title = theme.bold(`babysit_run ${kind}`);
|
|
2164
|
+
const detail =
|
|
2165
|
+
typeof payload === "string" && payload.length > 0
|
|
2166
|
+
? ` ${summarizeNotificationCommand(payload)}`
|
|
2167
|
+
: "";
|
|
2168
|
+
return new Text(theme.fg("warning", `${title}${agent}${detail}`), 0, 0);
|
|
2136
2169
|
},
|
|
2137
2170
|
renderResult(result, { isPartial }, theme, context) {
|
|
2138
2171
|
const details = (result.details ?? {}) as {
|
|
@@ -2160,7 +2193,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
2160
2193
|
: details.kind === "subagent" || content.includes(NOTIFY_MARKER)
|
|
2161
2194
|
? "started"
|
|
2162
2195
|
: "success");
|
|
2163
|
-
const label = renderStatus(status, theme
|
|
2196
|
+
const label = renderStatus(status, theme);
|
|
2164
2197
|
return new Text(content ? `${label}\n${theme.fg("toolOutput", content)}` : label, 0, 0);
|
|
2165
2198
|
},
|
|
2166
2199
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yusukeshib/pi-babysit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Run any shell command and pi subagents under babysit, with context-safe captured output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"agents.ts",
|
|
24
24
|
"self-reap.ts",
|
|
25
25
|
"format-stream.mjs",
|
|
26
|
+
"rpc-stream-proxy.mjs",
|
|
26
27
|
"search-log.mjs",
|
|
27
28
|
"README.md",
|
|
28
29
|
"LICENSE"
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Transparent stdin/stdout proxy for `pi --mode rpc`.
|
|
4
|
+
*
|
|
5
|
+
* Pi's `message_update` events repeat the entire growing assistant message and
|
|
6
|
+
* `assistantMessageEvent.partial` on every token. Those cumulative snapshots
|
|
7
|
+
* are useful only while streaming; `message_end` is the authoritative final
|
|
8
|
+
* message. Persisting every snapshot makes babysit logs grow quadratically.
|
|
9
|
+
*
|
|
10
|
+
* This proxy forwards stdin byte-for-byte and compacts only `message_update`
|
|
11
|
+
* lines on stdout. All final, tool, lifecycle, response, and error events pass
|
|
12
|
+
* through byte-for-byte.
|
|
13
|
+
*/
|
|
14
|
+
import { spawn } from "node:child_process";
|
|
15
|
+
import { StringDecoder } from "node:string_decoder";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
17
|
+
|
|
18
|
+
export function compactRpcLine(line) {
|
|
19
|
+
if (!line.trimStart().startsWith("{")) return line;
|
|
20
|
+
|
|
21
|
+
let event;
|
|
22
|
+
try {
|
|
23
|
+
event = JSON.parse(line);
|
|
24
|
+
} catch {
|
|
25
|
+
return line;
|
|
26
|
+
}
|
|
27
|
+
if (event?.type !== "message_update") return line;
|
|
28
|
+
|
|
29
|
+
const { message: _cumulativeMessage, assistantMessageEvent, ...rest } = event;
|
|
30
|
+
if (!assistantMessageEvent || typeof assistantMessageEvent !== "object") {
|
|
31
|
+
return JSON.stringify(rest);
|
|
32
|
+
}
|
|
33
|
+
const { partial: _cumulativePartial, ...deltaEvent } = assistantMessageEvent;
|
|
34
|
+
return JSON.stringify({ ...rest, assistantMessageEvent: deltaEvent });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function transformRpcOutput(input, output) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const decoder = new StringDecoder("utf8");
|
|
40
|
+
let buffered = "";
|
|
41
|
+
|
|
42
|
+
const write = (value) => {
|
|
43
|
+
if (!output.write(value)) input.pause();
|
|
44
|
+
};
|
|
45
|
+
output.on("drain", () => input.resume());
|
|
46
|
+
input.on("error", reject);
|
|
47
|
+
input.on("data", (chunk) => {
|
|
48
|
+
buffered += decoder.write(chunk);
|
|
49
|
+
for (;;) {
|
|
50
|
+
const newline = buffered.indexOf("\n");
|
|
51
|
+
if (newline < 0) break;
|
|
52
|
+
const line = buffered.slice(0, newline);
|
|
53
|
+
buffered = buffered.slice(newline + 1);
|
|
54
|
+
write(`${compactRpcLine(line)}\n`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
input.on("end", () => {
|
|
58
|
+
buffered += decoder.end();
|
|
59
|
+
if (buffered) write(compactRpcLine(buffered));
|
|
60
|
+
if (output.writableNeedDrain) output.once("drain", resolve);
|
|
61
|
+
else resolve();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function waitForDrain(stream) {
|
|
67
|
+
return stream.writableNeedDrain
|
|
68
|
+
? new Promise((resolve) => stream.once("drain", resolve))
|
|
69
|
+
: Promise.resolve();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function runRpcProxy(argv = process.argv.slice(2)) {
|
|
73
|
+
const separator = argv.indexOf("--");
|
|
74
|
+
const command = separator >= 0 ? argv[separator + 1] : argv[0];
|
|
75
|
+
const args = separator >= 0 ? argv.slice(separator + 2) : argv.slice(1);
|
|
76
|
+
if (!command) {
|
|
77
|
+
console.error("Usage: rpc-stream-proxy.mjs -- <command> [args...]");
|
|
78
|
+
process.exitCode = 2;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const child = spawn(command, args, {
|
|
83
|
+
cwd: process.cwd(),
|
|
84
|
+
env: process.env,
|
|
85
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let transportFailed = false;
|
|
89
|
+
process.stdin.pipe(child.stdin);
|
|
90
|
+
// The RPC worker can exit or self-reap while a command is still arriving.
|
|
91
|
+
// Ignore the resulting pipe-close race so the proxy preserves the worker's
|
|
92
|
+
// actual exit code/signal instead of crashing with an unhandled EPIPE.
|
|
93
|
+
child.stdin.on("error", (error) => {
|
|
94
|
+
if (error?.code === "EPIPE" || error?.code === "ERR_STREAM_DESTROYED") return;
|
|
95
|
+
transportFailed = true;
|
|
96
|
+
console.error(`rpc stream proxy stdin failed: ${error.message}`);
|
|
97
|
+
});
|
|
98
|
+
const outputFlushed = transformRpcOutput(child.stdout, process.stdout);
|
|
99
|
+
child.stderr.pipe(process.stderr);
|
|
100
|
+
|
|
101
|
+
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
102
|
+
const signalHandlers = new Map(
|
|
103
|
+
forwardedSignals.map((signal) => [signal, () => child.kill(signal)]),
|
|
104
|
+
);
|
|
105
|
+
for (const [signal, handler] of signalHandlers) process.on(signal, handler);
|
|
106
|
+
let spawnFailed = false;
|
|
107
|
+
child.on("error", (error) => {
|
|
108
|
+
spawnFailed = true;
|
|
109
|
+
console.error(`rpc stream proxy failed to start ${command}: ${error.message}`);
|
|
110
|
+
});
|
|
111
|
+
child.on("close", async (code, signal) => {
|
|
112
|
+
try {
|
|
113
|
+
await outputFlushed;
|
|
114
|
+
await Promise.all([waitForDrain(process.stdout), waitForDrain(process.stderr)]);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`rpc stream proxy output failed: ${error.message}`);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (transportFailed) {
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (signal) {
|
|
125
|
+
for (const [forwarded, handler] of signalHandlers) process.off(forwarded, handler);
|
|
126
|
+
process.kill(process.pid, signal);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
process.exitCode = spawnFailed ? 1 : (code ?? 1);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const entry = process.argv[1] ? pathToFileURL(process.argv[1]).href : undefined;
|
|
134
|
+
if (entry === import.meta.url) runRpcProxy();
|