pi-acp 0.0.20 → 0.0.22
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 +21 -11
- package/dist/index.js +150 -82
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
ACP ([Agent Client Protocol](https://agentclientprotocol.com/overview/introduction)) adapter for [`pi`](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) coding agent (fka shitty coding agent).
|
|
4
4
|
|
|
5
|
-
`pi-acp` communicates **ACP JSON-RPC 2.0 over stdio** to an ACP client (e.g.
|
|
5
|
+
`pi-acp` communicates **ACP JSON-RPC 2.0 over stdio** to an ACP client (e.g. Zed editor) and spawns `pi --mode rpc`, bridging requests/events between the two.
|
|
6
6
|
|
|
7
7
|
## Status
|
|
8
8
|
|
|
9
9
|
This is an MVP-style adapter intended to be useful today and easy to iterate on. Some ACP features may be not implemented or are not supported (see [Limitations](#limitations)). Development is centered around [Zed](https://zed.dev) editor support, other clients may have varying levels of compatibility.
|
|
10
10
|
|
|
11
|
+
Expect some minor breaking changes.
|
|
12
|
+
|
|
11
13
|
## Features
|
|
12
14
|
|
|
13
15
|
- Streams assistant output as ACP `agent_message_chunk`
|
|
@@ -21,7 +23,7 @@ This is an MVP-style adapter intended to be useful today and easy to iterate on.
|
|
|
21
23
|
- Adds a small set of built-in commands for headless/editor usage
|
|
22
24
|
- Supports skill commands (if enabled in pi settings, they appear as `/skill:skill-name` in the ACP client)
|
|
23
25
|
- Skills are loaded by pi directly and are available in ACP sessions
|
|
24
|
-
- (Zed)
|
|
26
|
+
- (Zed) `pi-acp` emits “startup info” block into the session (pi version, context, skills, prompts, extensions - similar to `pi` in the terminal). You can disable it by setting `quietStartup: true` in pi settings (`~/.pi/agent/settings.json` or `<project>/.pi/settings.json`). When `quietStartup` is enabled, `pi-acp` will still emit a 'New version available' message if the installed pi version is outdated.
|
|
25
27
|
- (Zed) Session history is supported in Zed starting with [`v0.225.0`](https://zed.dev/releases/preview/0.225.0). Session loading / history maps to pi's session files. Sessions can be resumed both in `pi` and in the ACP client.
|
|
26
28
|
|
|
27
29
|
## Prerequisites
|
|
@@ -40,19 +42,29 @@ npm install -g @mariozechner/pi-coding-agent
|
|
|
40
42
|
|
|
41
43
|
### Add pi-acp to your ACP client, e.g. [Zed](https://zed.dev/docs/agents/external-agents/)
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
#### Using ACP Registry in Zed or other clients that support it:
|
|
46
|
+
|
|
47
|
+
In Zed launch the registry with `zed: acp registry` command and select `pi ACP` adapter from the list. This will automatically add the agent server configuration to your `settings.json` and keep it up to date:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
"agent_servers": {
|
|
51
|
+
"pi-acp": {
|
|
52
|
+
"type": "registry",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
```
|
|
44
56
|
|
|
45
57
|
#### Using with `npx` (no global install needed, always loads the latest version):
|
|
46
58
|
|
|
59
|
+
Add the following to your Zed `settings.json`:
|
|
60
|
+
|
|
47
61
|
```json
|
|
48
62
|
"agent_servers": {
|
|
49
63
|
"pi": {
|
|
50
64
|
"type": "custom",
|
|
51
65
|
"command": "npx",
|
|
52
66
|
"args": ["-y", "pi-acp"],
|
|
53
|
-
"env": {
|
|
54
|
-
"PI_ACP_STARTUP_INFO": "true" // optional, "true" by default
|
|
55
|
-
}
|
|
67
|
+
"env": {}
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
```
|
|
@@ -98,14 +110,12 @@ Point your ACP client to the built `dist/index.js`:
|
|
|
98
110
|
|
|
99
111
|
`pi-acp` supports slash commands:
|
|
100
112
|
|
|
101
|
-
#### 1) File-based commands (
|
|
113
|
+
#### 1) File-based commands (aka prompts)
|
|
102
114
|
|
|
103
115
|
Loaded from:
|
|
104
116
|
|
|
105
|
-
- User commands: `~/.pi/agent/
|
|
106
|
-
- Project commands: `<cwd>/.pi/
|
|
107
|
-
|
|
108
|
-
These are expanded adapter-side (pi RPC mode doesn’t expand them).
|
|
117
|
+
- User commands: `~/.pi/agent/prompts/**/*.md`
|
|
118
|
+
- Project commands: `<cwd>/.pi/prompts/**/*.md`
|
|
109
119
|
|
|
110
120
|
#### 2) Built-in commands
|
|
111
121
|
|
package/dist/index.js
CHANGED
|
@@ -146,7 +146,7 @@ var PiRpcProcess = class _PiRpcProcess {
|
|
|
146
146
|
}
|
|
147
147
|
static async spawn(params) {
|
|
148
148
|
const cmd = params.piCommand ?? "pi";
|
|
149
|
-
const args = ["--mode", "rpc"];
|
|
149
|
+
const args = ["--mode", "rpc", "--no-themes"];
|
|
150
150
|
if (params.sessionPath) args.push("--session", params.sessionPath);
|
|
151
151
|
const child = spawn(cmd, args, {
|
|
152
152
|
cwd: params.cwd,
|
|
@@ -527,10 +527,34 @@ function expandSlashCommand(text, fileCommands) {
|
|
|
527
527
|
var SessionManager = class {
|
|
528
528
|
sessions = /* @__PURE__ */ new Map();
|
|
529
529
|
store = new SessionStore();
|
|
530
|
+
/** Dispose all sessions and their underlying pi subprocesses. */
|
|
531
|
+
disposeAll() {
|
|
532
|
+
for (const [id] of this.sessions) this.close(id);
|
|
533
|
+
}
|
|
530
534
|
/** Get a registered session if it exists (no throw). */
|
|
531
535
|
maybeGet(sessionId) {
|
|
532
536
|
return this.sessions.get(sessionId);
|
|
533
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* Dispose a session's underlying pi process and remove it from the manager.
|
|
540
|
+
* Used when clients explicitly reload a session and we want a fresh pi subprocess.
|
|
541
|
+
*/
|
|
542
|
+
close(sessionId) {
|
|
543
|
+
const s = this.sessions.get(sessionId);
|
|
544
|
+
if (!s) return;
|
|
545
|
+
try {
|
|
546
|
+
s.proc.dispose?.();
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
this.sessions.delete(sessionId);
|
|
550
|
+
}
|
|
551
|
+
/** Close all sessions except the one with `keepSessionId`. */
|
|
552
|
+
closeAllExcept(keepSessionId) {
|
|
553
|
+
for (const [id] of this.sessions) {
|
|
554
|
+
if (id === keepSessionId) continue;
|
|
555
|
+
this.close(id);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
534
558
|
async create(params) {
|
|
535
559
|
let proc;
|
|
536
560
|
try {
|
|
@@ -645,13 +669,6 @@ var PiAcpSession = class {
|
|
|
645
669
|
});
|
|
646
670
|
}
|
|
647
671
|
async prompt(message, images = []) {
|
|
648
|
-
if (!this.startupInfoSent && this.startupInfo) {
|
|
649
|
-
this.startupInfoSent = true;
|
|
650
|
-
this.emit({
|
|
651
|
-
sessionUpdate: "agent_message_chunk",
|
|
652
|
-
content: { type: "text", text: this.startupInfo }
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
672
|
const expandedMessage = expandSlashCommand(message, this.fileCommands);
|
|
656
673
|
const turnPromise = new Promise((resolve3, reject) => {
|
|
657
674
|
const queued = { message: expandedMessage, images, resolve: resolve3, reject };
|
|
@@ -1254,21 +1271,32 @@ function readJsonFile(path) {
|
|
|
1254
1271
|
return {};
|
|
1255
1272
|
}
|
|
1256
1273
|
}
|
|
1257
|
-
function
|
|
1258
|
-
return process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join4(homedir4(), ".pi", "agent");
|
|
1259
|
-
}
|
|
1260
|
-
function getEnableSkillCommands(cwd) {
|
|
1274
|
+
function getMergedSettings(cwd) {
|
|
1261
1275
|
const globalSettingsPath = join4(getAgentDir(), "settings.json");
|
|
1262
1276
|
const projectSettingsPath = resolve2(cwd, ".pi", "settings.json");
|
|
1263
1277
|
const global = readJsonFile(globalSettingsPath);
|
|
1264
1278
|
const project = readJsonFile(projectSettingsPath);
|
|
1265
|
-
|
|
1279
|
+
return deepMerge(global, project);
|
|
1280
|
+
}
|
|
1281
|
+
function getAgentDir() {
|
|
1282
|
+
return process.env.PI_CODING_AGENT_DIR ? resolve2(process.env.PI_CODING_AGENT_DIR) : join4(homedir4(), ".pi", "agent");
|
|
1283
|
+
}
|
|
1284
|
+
function getEnableSkillCommands(cwd) {
|
|
1285
|
+
const merged = getMergedSettings(cwd);
|
|
1266
1286
|
const direct = merged.enableSkillCommands;
|
|
1267
1287
|
if (typeof direct === "boolean") return direct;
|
|
1268
1288
|
const nested = isObject(merged.skills) ? merged.skills.enableSkillCommands : void 0;
|
|
1269
1289
|
if (typeof nested === "boolean") return nested;
|
|
1270
1290
|
return true;
|
|
1271
1291
|
}
|
|
1292
|
+
function getQuietStartup(cwd) {
|
|
1293
|
+
const merged = getMergedSettings(cwd);
|
|
1294
|
+
const direct = merged.quietStartup;
|
|
1295
|
+
if (typeof direct === "boolean") return direct;
|
|
1296
|
+
const legacy = merged.quietStart;
|
|
1297
|
+
if (typeof legacy === "boolean") return legacy;
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1272
1300
|
|
|
1273
1301
|
// src/acp/pi-commands.ts
|
|
1274
1302
|
function describeFallback(c) {
|
|
@@ -1377,14 +1405,6 @@ function hasAnyPiAuthConfigured() {
|
|
|
1377
1405
|
|
|
1378
1406
|
// src/acp/agent.ts
|
|
1379
1407
|
import { fileURLToPath } from "url";
|
|
1380
|
-
function booleanEnv(name, defaultValue) {
|
|
1381
|
-
const raw = process.env[name];
|
|
1382
|
-
if (raw == null) return defaultValue;
|
|
1383
|
-
const v = String(raw).trim().toLowerCase();
|
|
1384
|
-
if (v === "true") return true;
|
|
1385
|
-
if (v === "false") return false;
|
|
1386
|
-
return defaultValue;
|
|
1387
|
-
}
|
|
1388
1408
|
function builtinAvailableCommands() {
|
|
1389
1409
|
return [
|
|
1390
1410
|
{
|
|
@@ -1441,6 +1461,9 @@ var PiAcpAgent = class {
|
|
|
1441
1461
|
conn;
|
|
1442
1462
|
sessions = new SessionManager();
|
|
1443
1463
|
store = new SessionStore();
|
|
1464
|
+
dispose() {
|
|
1465
|
+
this.sessions.disposeAll();
|
|
1466
|
+
}
|
|
1444
1467
|
// Remember recent session cwd and use it as the default filter.
|
|
1445
1468
|
lastSessionCwd = null;
|
|
1446
1469
|
constructor(conn, _config) {
|
|
@@ -1498,12 +1521,21 @@ var PiAcpAgent = class {
|
|
|
1498
1521
|
fileCommands,
|
|
1499
1522
|
piCommand: process.env.PI_ACP_PI_COMMAND
|
|
1500
1523
|
});
|
|
1501
|
-
let
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1524
|
+
let state = null;
|
|
1525
|
+
let availableModels = null;
|
|
1526
|
+
await Promise.all([
|
|
1527
|
+
session.proc.getState().then((s) => {
|
|
1528
|
+
state = s;
|
|
1529
|
+
}).catch(() => {
|
|
1530
|
+
state = null;
|
|
1531
|
+
}),
|
|
1532
|
+
session.proc.getAvailableModels().then((m) => {
|
|
1533
|
+
availableModels = m;
|
|
1534
|
+
}).catch(() => {
|
|
1535
|
+
availableModels = null;
|
|
1536
|
+
})
|
|
1537
|
+
]);
|
|
1538
|
+
const rawModelsCount = Array.isArray(availableModels?.models) ? availableModels.models.length : 0;
|
|
1507
1539
|
if (rawModelsCount === 0) {
|
|
1508
1540
|
try {
|
|
1509
1541
|
session.proc.dispose?.();
|
|
@@ -1514,11 +1546,18 @@ var PiAcpAgent = class {
|
|
|
1514
1546
|
"Configure an API key or log in with an OAuth provider."
|
|
1515
1547
|
);
|
|
1516
1548
|
}
|
|
1517
|
-
const models = await getModelState(session.proc);
|
|
1518
|
-
const thinking = await getThinkingState(session.proc);
|
|
1519
|
-
const
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1549
|
+
const models = await getModelState(session.proc, { state, availableModels });
|
|
1550
|
+
const thinking = await getThinkingState(session.proc, { state });
|
|
1551
|
+
const quietStartup = getQuietStartup(params.cwd);
|
|
1552
|
+
const updateNotice = buildUpdateNotice();
|
|
1553
|
+
const preludeText = quietStartup ? updateNotice ? updateNotice + "\n" : "" : buildStartupInfo({
|
|
1554
|
+
cwd: params.cwd,
|
|
1555
|
+
fileCommands,
|
|
1556
|
+
updateNotice
|
|
1557
|
+
});
|
|
1558
|
+
if (preludeText)
|
|
1559
|
+
session.setStartupInfo(preludeText);
|
|
1560
|
+
this.sessions.closeAllExcept?.(session.sessionId);
|
|
1522
1561
|
const response = {
|
|
1523
1562
|
sessionId: session.sessionId,
|
|
1524
1563
|
models,
|
|
@@ -1529,7 +1568,7 @@ var PiAcpAgent = class {
|
|
|
1529
1568
|
}
|
|
1530
1569
|
}
|
|
1531
1570
|
};
|
|
1532
|
-
if (
|
|
1571
|
+
if (preludeText) setTimeout(() => session.sendStartupInfoIfPending(), 0);
|
|
1533
1572
|
setTimeout(() => {
|
|
1534
1573
|
void (async () => {
|
|
1535
1574
|
try {
|
|
@@ -1960,6 +1999,7 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
1960
1999
|
if (!isAbsolute2(params.cwd)) {
|
|
1961
2000
|
throw RequestError3.invalidParams(`cwd must be an absolute path: ${params.cwd}`);
|
|
1962
2001
|
}
|
|
2002
|
+
this.sessions.close(params.sessionId);
|
|
1963
2003
|
this.lastSessionCwd = params.cwd;
|
|
1964
2004
|
const stored = this.store.get(params.sessionId);
|
|
1965
2005
|
const sessionFile = stored?.sessionFile ?? findPiSessionFile(params.sessionId);
|
|
@@ -1988,6 +2028,7 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
1988
2028
|
proc,
|
|
1989
2029
|
fileCommands
|
|
1990
2030
|
});
|
|
2031
|
+
this.sessions.closeAllExcept?.(session.sessionId);
|
|
1991
2032
|
this.store.upsert({
|
|
1992
2033
|
sessionId: params.sessionId,
|
|
1993
2034
|
cwd: params.cwd,
|
|
@@ -2135,14 +2176,17 @@ ${JSON.stringify(stats, null, 2)}`;
|
|
|
2135
2176
|
function isThinkingLevel(x) {
|
|
2136
2177
|
return x === "off" || x === "minimal" || x === "low" || x === "medium" || x === "high" || x === "xhigh";
|
|
2137
2178
|
}
|
|
2138
|
-
async function getThinkingState(proc) {
|
|
2179
|
+
async function getThinkingState(proc, pre) {
|
|
2139
2180
|
let current = "medium";
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2181
|
+
const state = pre?.state ?? await (async () => {
|
|
2182
|
+
try {
|
|
2183
|
+
return await proc.getState();
|
|
2184
|
+
} catch {
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
})();
|
|
2188
|
+
const tl = typeof state?.thinkingLevel === "string" ? state.thinkingLevel : null;
|
|
2189
|
+
if (tl && isThinkingLevel(tl)) current = tl;
|
|
2146
2190
|
const available = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
2147
2191
|
return {
|
|
2148
2192
|
currentModeId: current,
|
|
@@ -2153,34 +2197,40 @@ async function getThinkingState(proc) {
|
|
|
2153
2197
|
}))
|
|
2154
2198
|
};
|
|
2155
2199
|
}
|
|
2156
|
-
async function getModelState(proc) {
|
|
2200
|
+
async function getModelState(proc, pre) {
|
|
2157
2201
|
let availableModels = [];
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2202
|
+
const data = pre?.availableModels ?? await (async () => {
|
|
2203
|
+
try {
|
|
2204
|
+
return await proc.getAvailableModels();
|
|
2205
|
+
} catch {
|
|
2206
|
+
return null;
|
|
2207
|
+
}
|
|
2208
|
+
})();
|
|
2209
|
+
const models = Array.isArray(data?.models) ? data.models : [];
|
|
2210
|
+
availableModels = models.map((m) => {
|
|
2211
|
+
const provider = String(m?.provider ?? "").trim();
|
|
2212
|
+
const id = String(m?.id ?? "").trim();
|
|
2213
|
+
if (!provider || !id) return null;
|
|
2214
|
+
const name = String(m?.name ?? id);
|
|
2215
|
+
return {
|
|
2216
|
+
modelId: `${provider}/${id}`,
|
|
2217
|
+
name: `${provider}/${name}`,
|
|
2218
|
+
description: null
|
|
2219
|
+
};
|
|
2220
|
+
}).filter(Boolean);
|
|
2174
2221
|
let currentModelId = null;
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
const id = String(model.id ?? "").trim();
|
|
2181
|
-
if (provider && id) currentModelId = `${provider}/${id}`;
|
|
2222
|
+
const state = pre?.state ?? await (async () => {
|
|
2223
|
+
try {
|
|
2224
|
+
return await proc.getState();
|
|
2225
|
+
} catch {
|
|
2226
|
+
return null;
|
|
2182
2227
|
}
|
|
2183
|
-
}
|
|
2228
|
+
})();
|
|
2229
|
+
const model = state?.model;
|
|
2230
|
+
if (model && typeof model === "object") {
|
|
2231
|
+
const provider = String(model.provider ?? "").trim();
|
|
2232
|
+
const id = String(model.id ?? "").trim();
|
|
2233
|
+
if (provider && id) currentModelId = `${provider}/${id}`;
|
|
2184
2234
|
}
|
|
2185
2235
|
if (!availableModels.length && !currentModelId) return null;
|
|
2186
2236
|
if (!currentModelId) currentModelId = availableModels[0]?.modelId ?? "default";
|
|
@@ -2203,10 +2253,26 @@ function compareSemver(a, b) {
|
|
|
2203
2253
|
}
|
|
2204
2254
|
return 0;
|
|
2205
2255
|
}
|
|
2256
|
+
function buildUpdateNotice() {
|
|
2257
|
+
try {
|
|
2258
|
+
const piVersion = spawnSync("pi", ["--version"], { encoding: "utf-8" });
|
|
2259
|
+
const installed = String(piVersion.stdout ?? "").trim().replace(/^v/i, "");
|
|
2260
|
+
if (!installed || !isSemver(installed)) return null;
|
|
2261
|
+
const latestRes = spawnSync("npm", ["view", "@mariozechner/pi-coding-agent", "version"], {
|
|
2262
|
+
encoding: "utf-8",
|
|
2263
|
+
timeout: 800
|
|
2264
|
+
});
|
|
2265
|
+
const latest = String(latestRes.stdout ?? "").trim().replace(/^v/i, "");
|
|
2266
|
+
if (!latest || !isSemver(latest)) return null;
|
|
2267
|
+
if (compareSemver(latest, installed) <= 0) return null;
|
|
2268
|
+
return `New version available: v${latest} (installed v${installed}). Run: \`npm i -g @mariozechner/pi-coding-agent\``;
|
|
2269
|
+
} catch {
|
|
2270
|
+
return null;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2206
2273
|
function buildStartupInfo(opts) {
|
|
2207
2274
|
void opts.fileCommands;
|
|
2208
2275
|
const md = [];
|
|
2209
|
-
let updateNotice = null;
|
|
2210
2276
|
try {
|
|
2211
2277
|
const piVersion = spawnSync("pi", ["--version"], { encoding: "utf-8" });
|
|
2212
2278
|
const installed = String(piVersion.stdout ?? "").trim().replace(/^v/i, "");
|
|
@@ -2214,17 +2280,6 @@ function buildStartupInfo(opts) {
|
|
|
2214
2280
|
md.push(`pi v${installed}`);
|
|
2215
2281
|
md.push("---");
|
|
2216
2282
|
md.push("");
|
|
2217
|
-
try {
|
|
2218
|
-
const latestRes = spawnSync("npm", ["view", "@mariozechner/pi-coding-agent", "version"], {
|
|
2219
|
-
encoding: "utf-8",
|
|
2220
|
-
timeout: 800
|
|
2221
|
-
});
|
|
2222
|
-
const latest = String(latestRes.stdout ?? "").trim().replace(/^v/i, "");
|
|
2223
|
-
if (latest && isSemver(latest) && isSemver(installed) && compareSemver(latest, installed) > 0) {
|
|
2224
|
-
updateNotice = `New version available: v${latest} (installed v${installed}). Run: \`npm i -g @mariozechner/pi-coding-agent\``;
|
|
2225
|
-
}
|
|
2226
|
-
} catch {
|
|
2227
|
-
}
|
|
2228
2283
|
}
|
|
2229
2284
|
} catch {
|
|
2230
2285
|
}
|
|
@@ -2318,9 +2373,9 @@ function buildStartupInfo(opts) {
|
|
|
2318
2373
|
} catch {
|
|
2319
2374
|
}
|
|
2320
2375
|
addSection("Extensions", extItems);
|
|
2321
|
-
if (updateNotice) {
|
|
2376
|
+
if (opts.updateNotice) {
|
|
2322
2377
|
md.push("---");
|
|
2323
|
-
md.push(updateNotice);
|
|
2378
|
+
md.push(opts.updateNotice);
|
|
2324
2379
|
md.push("");
|
|
2325
2380
|
}
|
|
2326
2381
|
return md.join("\n").trim() + "\n";
|
|
@@ -2378,10 +2433,23 @@ var output = new ReadableStream({
|
|
|
2378
2433
|
}
|
|
2379
2434
|
});
|
|
2380
2435
|
var stream = ndJsonStream(input, output);
|
|
2381
|
-
new AgentSideConnection((conn) => new PiAcpAgent(conn), stream);
|
|
2436
|
+
var agent = new AgentSideConnection((conn) => new PiAcpAgent(conn), stream);
|
|
2437
|
+
function shutdown() {
|
|
2438
|
+
try {
|
|
2439
|
+
;
|
|
2440
|
+
agent?.agent?.dispose?.();
|
|
2441
|
+
} catch {
|
|
2442
|
+
}
|
|
2443
|
+
try {
|
|
2444
|
+
process.exit(0);
|
|
2445
|
+
} catch {
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
process.stdin.on("end", shutdown);
|
|
2449
|
+
process.stdin.on("close", shutdown);
|
|
2382
2450
|
process.stdin.resume();
|
|
2383
|
-
process.on("SIGINT",
|
|
2384
|
-
process.on("SIGTERM",
|
|
2451
|
+
process.on("SIGINT", shutdown);
|
|
2452
|
+
process.on("SIGTERM", shutdown);
|
|
2385
2453
|
process.stdout.on("error", () => {
|
|
2386
2454
|
try {
|
|
2387
2455
|
process.exit(0);
|