@trim21/personal-pi-extensions 0.0.133 → 0.0.135
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/package.json +1 -1
- package/src/gh-readonly.ts +79 -28
package/package.json
CHANGED
package/src/gh-readonly.ts
CHANGED
|
@@ -43,11 +43,13 @@ interface GhResult {
|
|
|
43
43
|
code: number;
|
|
44
44
|
killed: boolean;
|
|
45
45
|
combined: string;
|
|
46
|
+
/** Why the process was killed, when `killed` is true. */
|
|
47
|
+
reason?: "timeout" | "abort";
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
49
51
|
|
|
50
|
-
function runGh(
|
|
52
|
+
export function runGh(
|
|
51
53
|
args: string[],
|
|
52
54
|
ctx: { cwd?: string; signal?: AbortSignal; timeout?: number },
|
|
53
55
|
): Promise<GhResult> {
|
|
@@ -63,11 +65,14 @@ function runGh(
|
|
|
63
65
|
let stderr = "";
|
|
64
66
|
const combined: string[] = [];
|
|
65
67
|
let killed = false;
|
|
68
|
+
let killReason: "timeout" | "abort" | undefined;
|
|
66
69
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
70
|
+
let onAbort: (() => void) | undefined;
|
|
67
71
|
|
|
68
|
-
const killProcess = () => {
|
|
72
|
+
const killProcess = (reason: "timeout" | "abort") => {
|
|
69
73
|
if (!killed) {
|
|
70
74
|
killed = true;
|
|
75
|
+
killReason = reason;
|
|
71
76
|
proc.kill("SIGTERM");
|
|
72
77
|
setTimeout(() => {
|
|
73
78
|
if (!proc.killed) proc.kill("SIGKILL");
|
|
@@ -76,16 +81,21 @@ function runGh(
|
|
|
76
81
|
};
|
|
77
82
|
|
|
78
83
|
if (ctx.signal) {
|
|
84
|
+
onAbort = () => killProcess("abort");
|
|
79
85
|
if (ctx.signal.aborted) {
|
|
80
|
-
killProcess();
|
|
86
|
+
killProcess("abort");
|
|
81
87
|
} else {
|
|
82
|
-
ctx.signal.addEventListener("abort",
|
|
88
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
|
|
92
|
+
// Default timeout: 10 minutes. Long operations like downloading a CI job's
|
|
93
|
+
// full log routinely take well over 30s, so a short default would kill them
|
|
94
|
+
// mid-transfer; combined with `code ?? 0` that would silently cache a
|
|
95
|
+
// truncated log as success. A killed process must never look successful.
|
|
96
|
+
const timeout = ctx.timeout ?? 600_000;
|
|
87
97
|
if (timeout > 0) {
|
|
88
|
-
timeoutId = setTimeout(killProcess, timeout);
|
|
98
|
+
timeoutId = setTimeout(() => killProcess("timeout"), timeout);
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
proc.stdout?.on("data", (data: Buffer) => {
|
|
@@ -101,18 +111,29 @@ function runGh(
|
|
|
101
111
|
|
|
102
112
|
proc.on("close", (code) => {
|
|
103
113
|
if (timeoutId) clearTimeout(timeoutId);
|
|
104
|
-
if (ctx.signal) {
|
|
105
|
-
ctx.signal.removeEventListener("abort",
|
|
114
|
+
if (ctx.signal && onAbort) {
|
|
115
|
+
ctx.signal.removeEventListener("abort", onAbort);
|
|
106
116
|
}
|
|
107
|
-
resolve({
|
|
117
|
+
resolve({
|
|
118
|
+
stdout,
|
|
119
|
+
stderr,
|
|
120
|
+
// When killed by a signal the close event's code is null; report the
|
|
121
|
+
// process as failed instead of pretending it succeeded. -1 is a
|
|
122
|
+
// sentinel for "did not exit normally" — distinct from a real gh
|
|
123
|
+
// failure exit code (1), which is always in 0-255.
|
|
124
|
+
code: code ?? (killed ? -1 : 0),
|
|
125
|
+
killed,
|
|
126
|
+
combined: combined.join(""),
|
|
127
|
+
reason: killReason,
|
|
128
|
+
});
|
|
108
129
|
});
|
|
109
130
|
|
|
110
131
|
proc.on("error", () => {
|
|
111
132
|
if (timeoutId) clearTimeout(timeoutId);
|
|
112
|
-
if (ctx.signal) {
|
|
113
|
-
ctx.signal.removeEventListener("abort",
|
|
133
|
+
if (ctx.signal && onAbort) {
|
|
134
|
+
ctx.signal.removeEventListener("abort", onAbort);
|
|
114
135
|
}
|
|
115
|
-
resolve({ stdout, stderr, code: 1, killed, combined: combined.join("") });
|
|
136
|
+
resolve({ stdout, stderr, code: 1, killed, combined: combined.join(""), reason: killReason });
|
|
116
137
|
});
|
|
117
138
|
});
|
|
118
139
|
}
|
|
@@ -131,7 +152,16 @@ export class GhError extends Error {
|
|
|
131
152
|
|
|
132
153
|
constructor(args: string[], result: GhResult, input?: unknown) {
|
|
133
154
|
const inputText = input === undefined ? "" : `<input>${JSON.stringify(input)}<input>\n`;
|
|
134
|
-
|
|
155
|
+
const killedText = result.killed
|
|
156
|
+
? result.reason === "timeout"
|
|
157
|
+
? " (command timed out)"
|
|
158
|
+
: result.reason === "abort"
|
|
159
|
+
? " (command aborted)"
|
|
160
|
+
: ""
|
|
161
|
+
: "";
|
|
162
|
+
super(
|
|
163
|
+
`${inputText}<output>${result.combined.trim() || `exit code ${result.code}`}${killedText}<output>`,
|
|
164
|
+
);
|
|
135
165
|
this.name = "GhError";
|
|
136
166
|
this.args = args;
|
|
137
167
|
this.code = result.code;
|
|
@@ -196,7 +226,7 @@ export async function pollChecksResult<R extends { code: number; stdout: string
|
|
|
196
226
|
}
|
|
197
227
|
|
|
198
228
|
/** Run `gh` and return stdout. On non-zero exit, throws a `GhError` carrying the toolcall input and raw command. */
|
|
199
|
-
async function ghExec(
|
|
229
|
+
export async function ghExec(
|
|
200
230
|
args: string[],
|
|
201
231
|
ctx: { cwd?: string; signal?: AbortSignal; input?: unknown },
|
|
202
232
|
): Promise<string> {
|
|
@@ -258,12 +288,18 @@ function truncate(
|
|
|
258
288
|
* Format a successful gh invocation's stdout into a tool result.
|
|
259
289
|
* Failures are thrown by `ghExec` as `GhError`, so only the success path lives here.
|
|
260
290
|
*/
|
|
261
|
-
function toToolResult(
|
|
291
|
+
function toToolResult(
|
|
292
|
+
stdout: string,
|
|
293
|
+
input?: unknown,
|
|
294
|
+
): {
|
|
262
295
|
content: Array<{ type: "text"; text: string }>;
|
|
263
296
|
details: Record<string, unknown>;
|
|
264
297
|
} {
|
|
265
298
|
const { text, truncated } = truncate(stdout);
|
|
266
|
-
return {
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: "text", text }],
|
|
301
|
+
details: { ...(input !== undefined ? { input } : {}), truncated },
|
|
302
|
+
};
|
|
267
303
|
}
|
|
268
304
|
|
|
269
305
|
interface ListFilters {
|
|
@@ -883,6 +919,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
883
919
|
],
|
|
884
920
|
{ cwd: ctx.cwd, signal, input: params },
|
|
885
921
|
),
|
|
922
|
+
params,
|
|
886
923
|
);
|
|
887
924
|
},
|
|
888
925
|
});
|
|
@@ -907,6 +944,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
907
944
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
908
945
|
return toToolResult(
|
|
909
946
|
await listGithub("issue", params, { cwd: ctx.cwd, signal, input: params }),
|
|
947
|
+
params,
|
|
910
948
|
);
|
|
911
949
|
},
|
|
912
950
|
});
|
|
@@ -935,6 +973,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
935
973
|
],
|
|
936
974
|
{ cwd: ctx.cwd, signal, input: params },
|
|
937
975
|
),
|
|
976
|
+
params,
|
|
938
977
|
);
|
|
939
978
|
},
|
|
940
979
|
});
|
|
@@ -959,7 +998,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
959
998
|
limit: Type.Optional(Type.Number({ description: "Max results (default 30)" })),
|
|
960
999
|
}),
|
|
961
1000
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
962
|
-
return toToolResult(
|
|
1001
|
+
return toToolResult(
|
|
1002
|
+
await listGithub("pr", params, { cwd: ctx.cwd, signal, input: params }),
|
|
1003
|
+
params,
|
|
1004
|
+
);
|
|
963
1005
|
},
|
|
964
1006
|
});
|
|
965
1007
|
|
|
@@ -976,7 +1018,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
976
1018
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
977
1019
|
const { number, repo } = params;
|
|
978
1020
|
const args = ["pr", "diff", String(number), ...repoArgs(repo)];
|
|
979
|
-
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
|
|
1021
|
+
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }), params);
|
|
980
1022
|
},
|
|
981
1023
|
});
|
|
982
1024
|
|
|
@@ -1003,7 +1045,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1003
1045
|
// Anything else is a real error (cancelled, auth, network, ...)
|
|
1004
1046
|
throw new GhError(args, final, params);
|
|
1005
1047
|
}
|
|
1006
|
-
return toToolResult(final.stdout);
|
|
1048
|
+
return toToolResult(final.stdout, params);
|
|
1007
1049
|
},
|
|
1008
1050
|
});
|
|
1009
1051
|
|
|
@@ -1067,7 +1109,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1067
1109
|
const { text, truncated } = truncate(out);
|
|
1068
1110
|
return {
|
|
1069
1111
|
content: [{ type: "text", text }],
|
|
1070
|
-
details: { truncated },
|
|
1112
|
+
details: { input: params, truncated },
|
|
1071
1113
|
};
|
|
1072
1114
|
},
|
|
1073
1115
|
});
|
|
@@ -1090,6 +1132,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1090
1132
|
signal,
|
|
1091
1133
|
input: params,
|
|
1092
1134
|
}),
|
|
1135
|
+
params,
|
|
1093
1136
|
);
|
|
1094
1137
|
},
|
|
1095
1138
|
});
|
|
@@ -1114,7 +1157,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1114
1157
|
if (limit) args.push("--limit", String(limit));
|
|
1115
1158
|
if (status) args.push("--status", status);
|
|
1116
1159
|
if (workflow) args.push("--workflow", workflow);
|
|
1117
|
-
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
|
|
1160
|
+
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }), params);
|
|
1118
1161
|
},
|
|
1119
1162
|
});
|
|
1120
1163
|
|
|
@@ -1172,12 +1215,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
1172
1215
|
content: [{ type: "text", text: `Fetching job list...` }],
|
|
1173
1216
|
details: {},
|
|
1174
1217
|
});
|
|
1175
|
-
|
|
1218
|
+
const stepResult = await renderStepLog(
|
|
1176
1219
|
{ runId: String(run_id), job, step, offset, limit },
|
|
1177
1220
|
jobs,
|
|
1178
1221
|
fetchJobLog,
|
|
1179
1222
|
onUpdate,
|
|
1180
1223
|
);
|
|
1224
|
+
return { ...stepResult, details: { ...stepResult.details, input: params } };
|
|
1181
1225
|
}
|
|
1182
1226
|
|
|
1183
1227
|
// ── List jobs/steps, with failed step logs expanded ────────────────
|
|
@@ -1185,7 +1229,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
1185
1229
|
content: [{ type: "text", text: `Fetching job list...` }],
|
|
1186
1230
|
details: {},
|
|
1187
1231
|
});
|
|
1188
|
-
|
|
1232
|
+
const jobsResult = await renderJobLogs(
|
|
1233
|
+
{ runId: String(run_id), job, offset, limit },
|
|
1234
|
+
jobs,
|
|
1235
|
+
fetchJobLog,
|
|
1236
|
+
);
|
|
1237
|
+
return { ...jobsResult, details: { ...jobsResult.details, input: params } };
|
|
1189
1238
|
},
|
|
1190
1239
|
});
|
|
1191
1240
|
|
|
@@ -1209,6 +1258,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1209
1258
|
signal,
|
|
1210
1259
|
input: params,
|
|
1211
1260
|
}),
|
|
1261
|
+
params,
|
|
1212
1262
|
);
|
|
1213
1263
|
},
|
|
1214
1264
|
});
|
|
@@ -1226,7 +1276,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1226
1276
|
const { repo } = params;
|
|
1227
1277
|
const args = ["repo", "view"];
|
|
1228
1278
|
if (repo) args.push(repo);
|
|
1229
|
-
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
|
|
1279
|
+
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }), params);
|
|
1230
1280
|
},
|
|
1231
1281
|
});
|
|
1232
1282
|
|
|
@@ -1244,7 +1294,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1244
1294
|
const { repo, limit } = params;
|
|
1245
1295
|
const args = ["release", "list", ...repoArgs(repo)];
|
|
1246
1296
|
if (limit) args.push("--limit", String(limit));
|
|
1247
|
-
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
|
|
1297
|
+
return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }), params);
|
|
1248
1298
|
},
|
|
1249
1299
|
});
|
|
1250
1300
|
|
|
@@ -1266,6 +1316,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1266
1316
|
signal,
|
|
1267
1317
|
input: params,
|
|
1268
1318
|
}),
|
|
1319
|
+
params,
|
|
1269
1320
|
);
|
|
1270
1321
|
},
|
|
1271
1322
|
});
|
|
@@ -1308,7 +1359,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1308
1359
|
content: [
|
|
1309
1360
|
{ type: "text", text: `## PR #${number} CI Checks - FAILED\n\n${stdout}\n${stderr}` },
|
|
1310
1361
|
],
|
|
1311
|
-
details: { status: "failure", exitCode },
|
|
1362
|
+
details: { status: "failure", exitCode, input: params },
|
|
1312
1363
|
};
|
|
1313
1364
|
}
|
|
1314
1365
|
|
|
@@ -1318,7 +1369,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1318
1369
|
|
|
1319
1370
|
return {
|
|
1320
1371
|
content: [{ type: "text", text: `## PR #${number} CI Checks - PASSED\n\n${stdout}` }],
|
|
1321
|
-
details: { status: "success", exitCode: 0 },
|
|
1372
|
+
details: { status: "success", exitCode: 0, input: params },
|
|
1322
1373
|
};
|
|
1323
1374
|
},
|
|
1324
1375
|
});
|
|
@@ -1357,7 +1408,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1357
1408
|
content: [
|
|
1358
1409
|
{ type: "text", text: `## Workflow Run ${run_id} Completed\n\n${result.stdout}` },
|
|
1359
1410
|
],
|
|
1360
|
-
details: { exitCode: 0 },
|
|
1411
|
+
details: { exitCode: 0, input: params },
|
|
1361
1412
|
};
|
|
1362
1413
|
},
|
|
1363
1414
|
});
|