@sukeai/pi-logfwd 0.2.0 → 0.2.2
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/extension/extension.ts +50 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -44,6 +44,14 @@ pi -e npm:@sukeai/pi-logfwd
|
|
|
44
44
|
|
|
45
45
|
装完在 pi 里 `/reload`,内置 `bash` 即被替换——所有 shell 命令自动走实时转发(参数 `command` / `timeout` / `cwd` / `logFile` / `noPty` 可用)。想还原内置 bash:`pi remove npm:@sukeai/pi-logfwd` 后 `/reload`。
|
|
46
46
|
|
|
47
|
+
## 工具结果标记
|
|
48
|
+
|
|
49
|
+
每次调用的结果末尾会附加一行状态标记,命令失败**不会静默**:
|
|
50
|
+
|
|
51
|
+
- 退出码非零 → `(exit code: N)`(真退出码取自 Go 端 JSONL `exit` 事件,非 pi-logfwd 进程自身退出码)
|
|
52
|
+
- `timeout` 杀进程 → `(timed out after Ns, exit code 124)`(判据是 exit 事件的 `message: "killed by --timeout"`,命令自己 `exit 124` 不会被误判为超时)
|
|
53
|
+
- 正常退出(0)→ 无标记
|
|
54
|
+
|
|
47
55
|
## 平台支持
|
|
48
56
|
|
|
49
57
|
二进制分发模型:npm **platform companion 包**(esbuild 同款机制)。主包把全部平台包列为 `optionalDependencies`,npm 只安装与当前 `os`/`cpu` 匹配的那一个,其余静默跳过;扩展在运行时按平台定位二进制。
|
package/extension/extension.ts
CHANGED
|
@@ -137,11 +137,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
137
137
|
"Run a bash command in the current working directory through pi-logfwd (PTY, real-time " +
|
|
138
138
|
"log forwarding). Output is streamed back in real time instead of being buffered until " +
|
|
139
139
|
"the process exits; the conversation keeps the last 60000 chars and longer output is " +
|
|
140
|
-
"mirrored to a temp file whose path is reported at the end.
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
"
|
|
140
|
+
"mirrored to a temp file whose path is reported at the end. The real exit status is never " +
|
|
141
|
+
"silent: a non-zero exit appends `(exit code: N)` and a tool-timeout kill appends " +
|
|
142
|
+
"`(timed out after Ns, exit code 124)` at the end of the result. This tool replaces " +
|
|
143
|
+
"pi's built-in buffered bash. NOT supported: interactive secret prompts (sudo/ssh " +
|
|
144
|
+
"password) and GUI authorization dialogs - PTY can render a prompt but nothing can " +
|
|
145
|
+
"answer it, so tell the user to run those manually. Optionally provide a timeout " +
|
|
146
|
+
"(seconds), cwd, logFile to append to, or noPty.",
|
|
145
147
|
promptSnippet: "Execute bash commands (ls, grep, find, etc.) with real-time streamed output (PTY)",
|
|
146
148
|
promptGuidelines: [
|
|
147
149
|
"You can inspect PI_* environment variables for current model and session details.",
|
|
@@ -209,17 +211,32 @@ export default function (pi: ExtensionAPI) {
|
|
|
209
211
|
}
|
|
210
212
|
};
|
|
211
213
|
|
|
214
|
+
// 消费 JSONL:output 事件进流式文本;exit 事件携带「真正」的命令退出码
|
|
215
|
+
// (超时被杀为 124 + message "killed by --timeout")。child(Go 进程) 的 close
|
|
216
|
+
// code 虽经 os.Exit 透传命令退出码,但 Go 自身报错/被杀时即偏离、且丢失
|
|
217
|
+
// timeout 的 message 与 durationMs;故一律以 exit 事件为准,close code 仅在
|
|
218
|
+
// 事件缺失时兜底。
|
|
219
|
+
type ExitInfo = { code: number | null; durationMs: number; message: string };
|
|
220
|
+
let exitEv: ExitInfo | null = null;
|
|
212
221
|
const rl = createInterface({ input: child.stdout });
|
|
213
222
|
rl.on("line", (line) => {
|
|
214
223
|
try {
|
|
215
224
|
const ev = JSON.parse(line);
|
|
216
|
-
if (ev.event === "output")
|
|
225
|
+
if (ev.event === "output") {
|
|
226
|
+
push(ev.data);
|
|
227
|
+
} else if (ev.event === "exit") {
|
|
228
|
+
exitEv = {
|
|
229
|
+
code: typeof ev.code === "number" ? ev.code : null,
|
|
230
|
+
durationMs: typeof ev.durationMs === "number" ? ev.durationMs : 0,
|
|
231
|
+
message: typeof ev.message === "string" ? ev.message : "",
|
|
232
|
+
};
|
|
233
|
+
}
|
|
217
234
|
} catch {
|
|
218
235
|
/* ignore malformed lines */
|
|
219
236
|
}
|
|
220
237
|
});
|
|
221
238
|
|
|
222
|
-
let
|
|
239
|
+
let goExitCode: number | null = null; // child(Go 进程) 自身退出码,仅作 exit 事件缺失时的兜底
|
|
223
240
|
let errText = "";
|
|
224
241
|
child.stderr.on("data", (d: Buffer) => (errText += d.toString()));
|
|
225
242
|
child.on("error", (e: Error) => {
|
|
@@ -229,7 +246,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
229
246
|
errText += `pi-logfwd: ${e.message}\n`;
|
|
230
247
|
}
|
|
231
248
|
});
|
|
232
|
-
child.on("close", (c) => (
|
|
249
|
+
child.on("close", (c) => (goExitCode = c));
|
|
233
250
|
signal?.addEventListener("abort", () => child.kill("SIGTERM"), { once: true });
|
|
234
251
|
|
|
235
252
|
await new Promise<void>((resolve) => {
|
|
@@ -256,12 +273,36 @@ export default function (pi: ExtensionAPI) {
|
|
|
256
273
|
}
|
|
257
274
|
}
|
|
258
275
|
|
|
276
|
+
// 真退出码优先(exit 事件);只有 Go 进程异常退出(未发 exit 事件)时才退回其进程退出码。
|
|
277
|
+
const exitCode = exitEv ? exitEv.code : goExitCode;
|
|
278
|
+
// 超时判据只看 exit 事件 message:Go 端仅在 timeout 杀死时写 "killed by --timeout";
|
|
279
|
+
// 命令自己 exit 124(如 gnu timeout 惯例)时 message 为空,不应误判为超时。
|
|
280
|
+
const timedOut = /timeout/i.test(exitEv?.message ?? "");
|
|
281
|
+
|
|
259
282
|
const text = tail || "(no output)";
|
|
283
|
+
const notes: string[] = [];
|
|
284
|
+
if (timedOut) {
|
|
285
|
+
const t =
|
|
286
|
+
typeof params.timeout === "number"
|
|
287
|
+
? `${params.timeout}s`
|
|
288
|
+
: `${((exitEv?.durationMs ?? 0) / 1000).toFixed(1)}s`;
|
|
289
|
+
notes.push(`(timed out after ${t}, exit code 124)`);
|
|
290
|
+
} else if (exitCode !== null && exitCode !== 0) {
|
|
291
|
+
notes.push(`(exit code: ${exitCode})`);
|
|
292
|
+
}
|
|
293
|
+
|
|
260
294
|
let resultText = errText ? `${text}\n${errText.trimEnd()}` : text;
|
|
295
|
+
if (notes.length) resultText += `\n${notes.join("\n")}`;
|
|
261
296
|
if (logPath) resultText += `\n(输出超长,完整日志已存 ${logPath})`;
|
|
262
297
|
return {
|
|
263
298
|
content: [{ type: "text", text: resultText }],
|
|
264
|
-
details: {
|
|
299
|
+
details: {
|
|
300
|
+
exitCode,
|
|
301
|
+
forwarded: true,
|
|
302
|
+
timedOut,
|
|
303
|
+
durationMs: exitEv?.durationMs ?? null,
|
|
304
|
+
logFile: params.logFile ?? null,
|
|
305
|
+
},
|
|
265
306
|
};
|
|
266
307
|
},
|
|
267
308
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sukeai/pi-logfwd",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "pi package: replaces the built-in bash tool with real-time log forwarding via the pi-logfwd Go binary (PTY, JSONL events, optional log file) / pi 实时命令日志转发扩展(bash 覆盖内置)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"typebox": "*"
|
|
27
27
|
},
|
|
28
28
|
"optionalDependencies": {
|
|
29
|
-
"@sukeai/pi-logfwd-darwin-arm64": "0.2.
|
|
30
|
-
"@sukeai/pi-logfwd-darwin-amd64": "0.2.
|
|
31
|
-
"@sukeai/pi-logfwd-linux-arm64": "0.2.
|
|
32
|
-
"@sukeai/pi-logfwd-linux-amd64": "0.2.
|
|
29
|
+
"@sukeai/pi-logfwd-darwin-arm64": "0.2.2",
|
|
30
|
+
"@sukeai/pi-logfwd-darwin-amd64": "0.2.2",
|
|
31
|
+
"@sukeai/pi-logfwd-linux-arm64": "0.2.2",
|
|
32
|
+
"@sukeai/pi-logfwd-linux-amd64": "0.2.2"
|
|
33
33
|
},
|
|
34
34
|
"pi": {
|
|
35
35
|
"extensions": [
|