runtimeuse 0.2.0 → 0.3.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/.claude/settings.local.json +7 -0
- package/README.md +3 -4
- package/dist/artifact-manager.d.ts.map +1 -1
- package/dist/artifact-manager.js +0 -11
- package/dist/artifact-manager.js.map +1 -1
- package/dist/artifact-manager.test.js +1 -16
- package/dist/artifact-manager.test.js.map +1 -1
- package/dist/cli.js +0 -2
- package/dist/cli.js.map +1 -1
- package/dist/command-handler.d.ts +8 -4
- package/dist/command-handler.d.ts.map +1 -1
- package/dist/command-handler.js +10 -16
- package/dist/command-handler.js.map +1 -1
- package/dist/command-handler.test.js +101 -113
- package/dist/command-handler.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +0 -1
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +0 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +30 -67
- package/dist/session.js.map +1 -1
- package/dist/session.test.js +15 -27
- package/dist/session.test.js.map +1 -1
- package/dist/types.d.ts +3 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/artifact-manager.test.ts +1 -19
- package/src/artifact-manager.ts +0 -14
- package/src/cli.ts +0 -2
- package/src/command-handler.test.ts +111 -182
- package/src/command-handler.ts +18 -25
- package/src/index.ts +1 -5
- package/src/server.ts +0 -2
- package/src/session.test.ts +28 -48
- package/src/session.ts +30 -72
- package/src/types.ts +2 -17
- package/dist/default-handler.d.ts +0 -3
- package/dist/default-handler.d.ts.map +0 -1
- package/dist/default-handler.js +0 -76
- package/dist/default-handler.js.map +0 -1
|
@@ -14,23 +14,23 @@ vi.mock("fs", async () => {
|
|
|
14
14
|
};
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
let
|
|
18
|
-
let
|
|
17
|
+
let execCallback: Function;
|
|
18
|
+
let execChild: any;
|
|
19
19
|
|
|
20
20
|
vi.mock("node:child_process", () => ({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
exec: vi.fn((_cmd: string, _opts: any, cb: Function) => {
|
|
22
|
+
execCallback = cb;
|
|
23
|
+
execChild = {
|
|
24
24
|
on: vi.fn(),
|
|
25
25
|
stdout: { on: vi.fn() },
|
|
26
26
|
stderr: { on: vi.fn() },
|
|
27
27
|
};
|
|
28
|
-
return
|
|
28
|
+
return execChild;
|
|
29
29
|
}),
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
import fs from "fs";
|
|
33
|
-
import {
|
|
33
|
+
import { exec } from "node:child_process";
|
|
34
34
|
import CommandHandler from "./command-handler.js";
|
|
35
35
|
import type { Command } from "./types.js";
|
|
36
36
|
|
|
@@ -40,40 +40,19 @@ const mockLogger: Logger = {
|
|
|
40
40
|
debug: vi.fn(),
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
function createStreams() {
|
|
44
|
-
const stdoutChunks: string[] = [];
|
|
45
|
-
const stderrChunks: string[] = [];
|
|
46
|
-
|
|
47
|
-
const stdoutStream = new Writable({
|
|
48
|
-
write(chunk, _enc, cb) {
|
|
49
|
-
stdoutChunks.push(chunk.toString());
|
|
50
|
-
cb();
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const stderrStream = new Writable({
|
|
55
|
-
write(chunk, _enc, cb) {
|
|
56
|
-
stderrChunks.push(chunk.toString());
|
|
57
|
-
cb();
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return { stdoutStream, stderrStream, stdoutChunks, stderrChunks };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
43
|
function createHandler(
|
|
65
44
|
command: Command,
|
|
66
|
-
stdoutStream: Writable,
|
|
67
|
-
stderrStream: Writable,
|
|
68
45
|
abortController = new AbortController(),
|
|
46
|
+
onStdout?: (stdout: string) => void,
|
|
47
|
+
onStderr?: (stderr: string) => void,
|
|
69
48
|
) {
|
|
70
|
-
return new CommandHandler(
|
|
49
|
+
return new CommandHandler({
|
|
71
50
|
command,
|
|
72
|
-
mockLogger,
|
|
73
|
-
stdoutStream,
|
|
74
|
-
stderrStream,
|
|
51
|
+
logger: mockLogger,
|
|
75
52
|
abortController,
|
|
76
|
-
|
|
53
|
+
onStdout,
|
|
54
|
+
onStderr,
|
|
55
|
+
});
|
|
77
56
|
}
|
|
78
57
|
|
|
79
58
|
describe("CommandHandler", () => {
|
|
@@ -84,17 +63,12 @@ describe("CommandHandler", () => {
|
|
|
84
63
|
describe("directory creation", () => {
|
|
85
64
|
it("creates cwd directory when it does not exist", async () => {
|
|
86
65
|
vi.mocked(fs.existsSync).mockReturnValueOnce(false);
|
|
87
|
-
const {
|
|
88
|
-
const handler = createHandler(
|
|
89
|
-
{ command: "echo", cwd: "/tmp/test-dir" },
|
|
90
|
-
stdoutStream,
|
|
91
|
-
stderrStream,
|
|
92
|
-
);
|
|
66
|
+
const handler = createHandler({ command: "echo", cwd: "/tmp/test-dir" });
|
|
93
67
|
|
|
94
68
|
const promise = handler.execute();
|
|
95
69
|
|
|
96
|
-
|
|
97
|
-
const closeHandler =
|
|
70
|
+
execCallback(null, "ok", "");
|
|
71
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
98
72
|
(c: unknown[]) => c[0] === "close",
|
|
99
73
|
)[1];
|
|
100
74
|
closeHandler(0);
|
|
@@ -108,17 +82,12 @@ describe("CommandHandler", () => {
|
|
|
108
82
|
|
|
109
83
|
it("does not create cwd directory when it already exists", async () => {
|
|
110
84
|
vi.mocked(fs.existsSync).mockReturnValueOnce(true);
|
|
111
|
-
const {
|
|
112
|
-
const handler = createHandler(
|
|
113
|
-
{ command: "echo", cwd: "/tmp/existing" },
|
|
114
|
-
stdoutStream,
|
|
115
|
-
stderrStream,
|
|
116
|
-
);
|
|
85
|
+
const handler = createHandler({ command: "echo", cwd: "/tmp/existing" });
|
|
117
86
|
|
|
118
87
|
const promise = handler.execute();
|
|
119
88
|
|
|
120
|
-
|
|
121
|
-
const closeHandler =
|
|
89
|
+
execCallback(null, "", "");
|
|
90
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
122
91
|
(c: unknown[]) => c[0] === "close",
|
|
123
92
|
)[1];
|
|
124
93
|
closeHandler(0);
|
|
@@ -129,17 +98,12 @@ describe("CommandHandler", () => {
|
|
|
129
98
|
});
|
|
130
99
|
|
|
131
100
|
it("skips directory creation when cwd is not specified", async () => {
|
|
132
|
-
const {
|
|
133
|
-
const handler = createHandler(
|
|
134
|
-
{ command: "echo" },
|
|
135
|
-
stdoutStream,
|
|
136
|
-
stderrStream,
|
|
137
|
-
);
|
|
101
|
+
const handler = createHandler({ command: "echo" });
|
|
138
102
|
|
|
139
103
|
const promise = handler.execute();
|
|
140
104
|
|
|
141
|
-
|
|
142
|
-
const closeHandler =
|
|
105
|
+
execCallback(null, "", "");
|
|
106
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
143
107
|
(c: unknown[]) => c[0] === "close",
|
|
144
108
|
)[1];
|
|
145
109
|
closeHandler(0);
|
|
@@ -152,17 +116,16 @@ describe("CommandHandler", () => {
|
|
|
152
116
|
});
|
|
153
117
|
|
|
154
118
|
describe("command execution", () => {
|
|
155
|
-
it("calls
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
);
|
|
119
|
+
it("calls exec with the right command, cwd, and env", () => {
|
|
120
|
+
const handler = createHandler({
|
|
121
|
+
command: "ls",
|
|
122
|
+
cwd: "/work",
|
|
123
|
+
env: { FOO: "bar" },
|
|
124
|
+
});
|
|
162
125
|
|
|
163
126
|
handler.execute();
|
|
164
127
|
|
|
165
|
-
expect(
|
|
128
|
+
expect(exec).toHaveBeenCalledWith(
|
|
166
129
|
"ls",
|
|
167
130
|
expect.objectContaining({
|
|
168
131
|
cwd: "/work",
|
|
@@ -173,35 +136,24 @@ describe("CommandHandler", () => {
|
|
|
173
136
|
});
|
|
174
137
|
|
|
175
138
|
it("uses process.cwd() when cwd is not specified", () => {
|
|
176
|
-
const {
|
|
177
|
-
const handler = createHandler(
|
|
178
|
-
{ command: "pwd" },
|
|
179
|
-
stdoutStream,
|
|
180
|
-
stderrStream,
|
|
181
|
-
);
|
|
139
|
+
const handler = createHandler({ command: "pwd" });
|
|
182
140
|
|
|
183
141
|
handler.execute();
|
|
184
142
|
|
|
185
|
-
expect(
|
|
143
|
+
expect(exec).toHaveBeenCalledWith(
|
|
186
144
|
"pwd",
|
|
187
145
|
expect.objectContaining({ cwd: process.cwd() }),
|
|
188
146
|
expect.any(Function),
|
|
189
147
|
);
|
|
190
148
|
});
|
|
191
149
|
|
|
192
|
-
it("passes abort signal to
|
|
150
|
+
it("passes abort signal to exec", () => {
|
|
193
151
|
const ac = new AbortController();
|
|
194
|
-
const {
|
|
195
|
-
const handler = createHandler(
|
|
196
|
-
{ command: "sleep" },
|
|
197
|
-
stdoutStream,
|
|
198
|
-
stderrStream,
|
|
199
|
-
ac,
|
|
200
|
-
);
|
|
152
|
+
const handler = createHandler({ command: "sleep" }, ac);
|
|
201
153
|
|
|
202
154
|
handler.execute();
|
|
203
155
|
|
|
204
|
-
expect(
|
|
156
|
+
expect(exec).toHaveBeenCalledWith(
|
|
205
157
|
"sleep",
|
|
206
158
|
expect.objectContaining({ signal: ac.signal }),
|
|
207
159
|
expect.any(Function),
|
|
@@ -211,17 +163,12 @@ describe("CommandHandler", () => {
|
|
|
211
163
|
|
|
212
164
|
describe("exit handling", () => {
|
|
213
165
|
it("resolves with exitCode 0 on successful close", async () => {
|
|
214
|
-
const {
|
|
215
|
-
const handler = createHandler(
|
|
216
|
-
{ command: "true" },
|
|
217
|
-
stdoutStream,
|
|
218
|
-
stderrStream,
|
|
219
|
-
);
|
|
166
|
+
const handler = createHandler({ command: "true" });
|
|
220
167
|
|
|
221
168
|
const promise = handler.execute();
|
|
222
169
|
|
|
223
|
-
|
|
224
|
-
const closeHandler =
|
|
170
|
+
execCallback(null, "output", "");
|
|
171
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
225
172
|
(c: unknown[]) => c[0] === "close",
|
|
226
173
|
)[1];
|
|
227
174
|
closeHandler(0);
|
|
@@ -231,17 +178,12 @@ describe("CommandHandler", () => {
|
|
|
231
178
|
});
|
|
232
179
|
|
|
233
180
|
it("resolves with exitCode 1 on close with code 1", async () => {
|
|
234
|
-
const {
|
|
235
|
-
const handler = createHandler(
|
|
236
|
-
{ command: "false" },
|
|
237
|
-
stdoutStream,
|
|
238
|
-
stderrStream,
|
|
239
|
-
);
|
|
181
|
+
const handler = createHandler({ command: "false" });
|
|
240
182
|
|
|
241
183
|
const promise = handler.execute();
|
|
242
184
|
|
|
243
|
-
|
|
244
|
-
const closeHandler =
|
|
185
|
+
execCallback(null, "", "");
|
|
186
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
245
187
|
(c: unknown[]) => c[0] === "close",
|
|
246
188
|
)[1];
|
|
247
189
|
closeHandler(1);
|
|
@@ -251,17 +193,12 @@ describe("CommandHandler", () => {
|
|
|
251
193
|
});
|
|
252
194
|
|
|
253
195
|
it("resolves with exitCode 1 and error when callback reports code 1", async () => {
|
|
254
|
-
const {
|
|
255
|
-
const handler = createHandler(
|
|
256
|
-
{ command: "failing" },
|
|
257
|
-
stdoutStream,
|
|
258
|
-
stderrStream,
|
|
259
|
-
);
|
|
196
|
+
const handler = createHandler({ command: "failing" });
|
|
260
197
|
|
|
261
198
|
const promise = handler.execute();
|
|
262
199
|
|
|
263
200
|
const err = Object.assign(new Error("exit 1"), { code: 1 });
|
|
264
|
-
|
|
201
|
+
execCallback(err, "", "some error");
|
|
265
202
|
|
|
266
203
|
const result = await promise;
|
|
267
204
|
expect(result.exitCode).toBe(1);
|
|
@@ -269,17 +206,12 @@ describe("CommandHandler", () => {
|
|
|
269
206
|
});
|
|
270
207
|
|
|
271
208
|
it("rejects with non-0/1 exit codes from close event", async () => {
|
|
272
|
-
const {
|
|
273
|
-
const handler = createHandler(
|
|
274
|
-
{ command: "segfault" },
|
|
275
|
-
stdoutStream,
|
|
276
|
-
stderrStream,
|
|
277
|
-
);
|
|
209
|
+
const handler = createHandler({ command: "segfault" });
|
|
278
210
|
|
|
279
211
|
const promise = handler.execute();
|
|
280
212
|
|
|
281
|
-
|
|
282
|
-
const closeHandler =
|
|
213
|
+
execCallback(null, "", "");
|
|
214
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
283
215
|
(c: unknown[]) => c[0] === "close",
|
|
284
216
|
)[1];
|
|
285
217
|
closeHandler(139);
|
|
@@ -288,17 +220,12 @@ describe("CommandHandler", () => {
|
|
|
288
220
|
});
|
|
289
221
|
|
|
290
222
|
it("rejects when callback reports a non-1 error code", async () => {
|
|
291
|
-
const {
|
|
292
|
-
const handler = createHandler(
|
|
293
|
-
{ command: "crash" },
|
|
294
|
-
stdoutStream,
|
|
295
|
-
stderrStream,
|
|
296
|
-
);
|
|
223
|
+
const handler = createHandler({ command: "crash" });
|
|
297
224
|
|
|
298
225
|
const promise = handler.execute();
|
|
299
226
|
|
|
300
227
|
const err = Object.assign(new Error("killed"), { code: 137 });
|
|
301
|
-
|
|
228
|
+
execCallback(err, "", "");
|
|
302
229
|
|
|
303
230
|
await expect(promise).rejects.toMatchObject({ exitCode: 137 });
|
|
304
231
|
});
|
|
@@ -306,151 +233,153 @@ describe("CommandHandler", () => {
|
|
|
306
233
|
|
|
307
234
|
describe("stream output", () => {
|
|
308
235
|
it("writes stdout to the stdout stream", async () => {
|
|
309
|
-
const
|
|
236
|
+
const onStdout = vi.fn();
|
|
310
237
|
const handler = createHandler(
|
|
311
238
|
{ command: "echo" },
|
|
312
|
-
|
|
313
|
-
|
|
239
|
+
new AbortController(),
|
|
240
|
+
onStdout,
|
|
314
241
|
);
|
|
315
242
|
|
|
316
243
|
const promise = handler.execute();
|
|
317
244
|
|
|
318
|
-
const stdoutDataHandler =
|
|
245
|
+
const stdoutDataHandler = execChild.stdout.on.mock.calls.find(
|
|
319
246
|
(c: unknown[]) => c[0] === "data",
|
|
320
247
|
)[1];
|
|
321
248
|
stdoutDataHandler("hello world");
|
|
322
249
|
|
|
323
|
-
|
|
324
|
-
const closeHandler =
|
|
250
|
+
execCallback(null, "", "");
|
|
251
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
325
252
|
(c: unknown[]) => c[0] === "close",
|
|
326
253
|
)[1];
|
|
327
254
|
closeHandler(0);
|
|
328
255
|
|
|
329
256
|
await promise;
|
|
330
|
-
expect(
|
|
257
|
+
expect(onStdout).toHaveBeenCalledWith("hello world");
|
|
331
258
|
});
|
|
332
259
|
|
|
333
260
|
it("writes stderr to the stderr stream", async () => {
|
|
334
|
-
const
|
|
261
|
+
const onStderr = vi.fn();
|
|
335
262
|
const handler = createHandler(
|
|
336
263
|
{ command: "warn" },
|
|
337
|
-
|
|
338
|
-
|
|
264
|
+
new AbortController(),
|
|
265
|
+
undefined,
|
|
266
|
+
onStderr,
|
|
339
267
|
);
|
|
340
268
|
|
|
341
269
|
const promise = handler.execute();
|
|
342
270
|
|
|
343
|
-
const stderrDataHandler =
|
|
271
|
+
const stderrDataHandler = execChild.stderr.on.mock.calls.find(
|
|
344
272
|
(c: unknown[]) => c[0] === "data",
|
|
345
273
|
)[1];
|
|
346
274
|
stderrDataHandler("warning message");
|
|
347
275
|
|
|
348
|
-
|
|
349
|
-
const closeHandler =
|
|
276
|
+
execCallback(null, "", "");
|
|
277
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
350
278
|
(c: unknown[]) => c[0] === "close",
|
|
351
279
|
)[1];
|
|
352
280
|
closeHandler(0);
|
|
353
281
|
|
|
354
282
|
await promise;
|
|
355
|
-
expect(
|
|
283
|
+
expect(onStderr).toHaveBeenCalledWith("warning message");
|
|
356
284
|
});
|
|
357
285
|
|
|
358
286
|
it("redacts secrets from stdout", async () => {
|
|
359
|
-
const
|
|
287
|
+
const onStdout = vi.fn();
|
|
360
288
|
const handler = createHandler(
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
289
|
+
{
|
|
290
|
+
command: "echo",
|
|
291
|
+
env: { API_KEY: "s3cret" },
|
|
292
|
+
},
|
|
293
|
+
new AbortController(),
|
|
294
|
+
onStdout,
|
|
364
295
|
);
|
|
365
296
|
|
|
366
297
|
const promise = handler.execute();
|
|
367
298
|
|
|
368
|
-
const stdoutDataHandler =
|
|
299
|
+
const stdoutDataHandler = execChild.stdout.on.mock.calls.find(
|
|
369
300
|
(c: unknown[]) => c[0] === "data",
|
|
370
301
|
)[1];
|
|
371
302
|
stdoutDataHandler("token is s3cret ok");
|
|
372
303
|
|
|
373
|
-
|
|
374
|
-
const closeHandler =
|
|
304
|
+
execCallback(null, "", "");
|
|
305
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
375
306
|
(c: unknown[]) => c[0] === "close",
|
|
376
307
|
)[1];
|
|
377
308
|
closeHandler(0);
|
|
378
309
|
|
|
379
310
|
await promise;
|
|
380
|
-
expect(
|
|
311
|
+
expect(onStdout).toHaveBeenCalledWith("token is [REDACTED] ok");
|
|
381
312
|
});
|
|
382
313
|
|
|
383
314
|
it("redacts secrets from stderr", async () => {
|
|
384
|
-
const
|
|
315
|
+
const onStderr = vi.fn();
|
|
385
316
|
const handler = createHandler(
|
|
386
317
|
{ command: "warn", env: { PASSWORD: "hunter2" } },
|
|
387
|
-
|
|
388
|
-
|
|
318
|
+
new AbortController(),
|
|
319
|
+
undefined,
|
|
320
|
+
onStderr,
|
|
389
321
|
);
|
|
390
322
|
|
|
391
323
|
const promise = handler.execute();
|
|
392
324
|
|
|
393
|
-
const stderrDataHandler =
|
|
325
|
+
const stderrDataHandler = execChild.stderr.on.mock.calls.find(
|
|
394
326
|
(c: unknown[]) => c[0] === "data",
|
|
395
327
|
)[1];
|
|
396
328
|
stderrDataHandler("error: hunter2 leaked");
|
|
397
329
|
|
|
398
|
-
|
|
399
|
-
const closeHandler =
|
|
330
|
+
execCallback(null, "", "");
|
|
331
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
400
332
|
(c: unknown[]) => c[0] === "close",
|
|
401
333
|
)[1];
|
|
402
334
|
closeHandler(0);
|
|
403
335
|
|
|
404
336
|
await promise;
|
|
405
|
-
expect(
|
|
337
|
+
expect(onStderr).toHaveBeenCalledWith("error: [REDACTED] leaked");
|
|
406
338
|
});
|
|
407
339
|
|
|
408
340
|
it("redacts multiple env secrets from output", async () => {
|
|
409
|
-
const
|
|
341
|
+
const onStdout = vi.fn();
|
|
410
342
|
const handler = createHandler(
|
|
411
343
|
{ command: "echo", env: { KEY: "aaa", TOKEN: "bbb" } },
|
|
412
|
-
|
|
413
|
-
|
|
344
|
+
new AbortController(),
|
|
345
|
+
onStdout,
|
|
414
346
|
);
|
|
415
347
|
|
|
416
348
|
const promise = handler.execute();
|
|
417
349
|
|
|
418
|
-
const stdoutDataHandler =
|
|
350
|
+
const stdoutDataHandler = execChild.stdout.on.mock.calls.find(
|
|
419
351
|
(c: unknown[]) => c[0] === "data",
|
|
420
352
|
)[1];
|
|
421
353
|
stdoutDataHandler("aaa and bbb");
|
|
422
354
|
|
|
423
|
-
|
|
424
|
-
const closeHandler =
|
|
355
|
+
execCallback(null, "", "");
|
|
356
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
425
357
|
(c: unknown[]) => c[0] === "close",
|
|
426
358
|
)[1];
|
|
427
359
|
closeHandler(0);
|
|
428
360
|
|
|
429
361
|
await promise;
|
|
430
|
-
expect(
|
|
362
|
+
expect(onStdout).toHaveBeenCalledWith("[REDACTED] and [REDACTED]");
|
|
431
363
|
});
|
|
432
364
|
|
|
433
365
|
it("passes redacted data to onStdout callback", async () => {
|
|
434
|
-
const { stdoutStream, stderrStream } = createStreams();
|
|
435
366
|
const onStdout = vi.fn();
|
|
436
|
-
const handler = new CommandHandler(
|
|
437
|
-
{ command: "echo", env: { SECRET: "xyz" } },
|
|
438
|
-
mockLogger,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
onStdout,
|
|
443
|
-
);
|
|
367
|
+
const handler = new CommandHandler({
|
|
368
|
+
command: { command: "echo", env: { SECRET: "xyz" } },
|
|
369
|
+
logger: mockLogger,
|
|
370
|
+
abortController: new AbortController(),
|
|
371
|
+
onStdout: onStdout,
|
|
372
|
+
});
|
|
444
373
|
|
|
445
374
|
const promise = handler.execute();
|
|
446
375
|
|
|
447
|
-
const stdoutDataHandler =
|
|
376
|
+
const stdoutDataHandler = execChild.stdout.on.mock.calls.find(
|
|
448
377
|
(c: unknown[]) => c[0] === "data",
|
|
449
378
|
)[1];
|
|
450
379
|
stdoutDataHandler("value=xyz");
|
|
451
380
|
|
|
452
|
-
|
|
453
|
-
const closeHandler =
|
|
381
|
+
execCallback(null, "", "");
|
|
382
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
454
383
|
(c: unknown[]) => c[0] === "close",
|
|
455
384
|
)[1];
|
|
456
385
|
closeHandler(0);
|
|
@@ -460,48 +389,48 @@ describe("CommandHandler", () => {
|
|
|
460
389
|
});
|
|
461
390
|
|
|
462
391
|
it("does not redact when command has no env", async () => {
|
|
463
|
-
const
|
|
392
|
+
const onStdout = vi.fn();
|
|
464
393
|
const handler = createHandler(
|
|
465
394
|
{ command: "echo" },
|
|
466
|
-
|
|
467
|
-
|
|
395
|
+
new AbortController(),
|
|
396
|
+
onStdout,
|
|
468
397
|
);
|
|
469
398
|
|
|
470
399
|
const promise = handler.execute();
|
|
471
400
|
|
|
472
|
-
const stdoutDataHandler =
|
|
401
|
+
const stdoutDataHandler = execChild.stdout.on.mock.calls.find(
|
|
473
402
|
(c: unknown[]) => c[0] === "data",
|
|
474
403
|
)[1];
|
|
475
404
|
stdoutDataHandler("nothing secret here");
|
|
476
405
|
|
|
477
|
-
|
|
478
|
-
const closeHandler =
|
|
406
|
+
execCallback(null, "", "");
|
|
407
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
479
408
|
(c: unknown[]) => c[0] === "close",
|
|
480
409
|
)[1];
|
|
481
410
|
closeHandler(0);
|
|
482
411
|
|
|
483
412
|
await promise;
|
|
484
|
-
expect(
|
|
413
|
+
expect(onStdout).toHaveBeenCalledWith("nothing secret here");
|
|
485
414
|
});
|
|
486
415
|
|
|
487
416
|
it("does not write empty stdout", async () => {
|
|
488
|
-
const
|
|
417
|
+
const onStdout = vi.fn();
|
|
489
418
|
const handler = createHandler(
|
|
490
419
|
{ command: "silent" },
|
|
491
|
-
|
|
492
|
-
|
|
420
|
+
new AbortController(),
|
|
421
|
+
onStdout,
|
|
493
422
|
);
|
|
494
423
|
|
|
495
424
|
const promise = handler.execute();
|
|
496
425
|
|
|
497
|
-
|
|
498
|
-
const closeHandler =
|
|
426
|
+
execCallback(null, "", "");
|
|
427
|
+
const closeHandler = execChild.on.mock.calls.find(
|
|
499
428
|
(c: unknown[]) => c[0] === "close",
|
|
500
429
|
)[1];
|
|
501
430
|
closeHandler(0);
|
|
502
431
|
|
|
503
432
|
await promise;
|
|
504
|
-
expect(
|
|
433
|
+
expect(onStdout).not.toHaveBeenCalled();
|
|
505
434
|
});
|
|
506
435
|
});
|
|
507
436
|
});
|
package/src/command-handler.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { execFile, ExecFileException } from "node:child_process";
|
|
1
|
+
import { exec, ExecFileException } from "node:child_process";
|
|
3
2
|
import fs from "fs";
|
|
4
3
|
import type { Logger } from "./logger.js";
|
|
5
4
|
import type { Command } from "./types.js";
|
|
@@ -9,30 +8,26 @@ export interface CommandResult {
|
|
|
9
8
|
exitCode: number;
|
|
10
9
|
error?: ExecFileException;
|
|
11
10
|
}
|
|
11
|
+
|
|
12
|
+
export interface CommandHandlerOptions {
|
|
13
|
+
command: Command;
|
|
14
|
+
logger: Logger;
|
|
15
|
+
abortController: AbortController;
|
|
16
|
+
onStdout?: (stdout: string) => void;
|
|
17
|
+
onStderr?: (stderr: string) => void;
|
|
18
|
+
}
|
|
12
19
|
class CommandHandler {
|
|
13
20
|
private readonly command: Command;
|
|
14
21
|
private readonly logger: Logger;
|
|
15
|
-
private readonly stdoutStream: Writable;
|
|
16
|
-
private readonly stderrStream: Writable;
|
|
17
22
|
private readonly onStdout?: (stdout: string) => void;
|
|
18
23
|
private readonly onStderr?: (stderr: string) => void;
|
|
19
24
|
private readonly abortController: AbortController;
|
|
20
|
-
constructor(
|
|
21
|
-
command
|
|
22
|
-
logger
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
onStdout?: (stdout: string) => void,
|
|
27
|
-
onStderr?: (stderr: string) => void,
|
|
28
|
-
) {
|
|
29
|
-
this.command = command;
|
|
30
|
-
this.logger = logger;
|
|
31
|
-
this.stdoutStream = stdoutStream;
|
|
32
|
-
this.stderrStream = stderrStream;
|
|
33
|
-
this.abortController = abortController;
|
|
34
|
-
this.onStdout = onStdout;
|
|
35
|
-
this.onStderr = onStderr;
|
|
25
|
+
constructor(options: CommandHandlerOptions) {
|
|
26
|
+
this.command = options.command;
|
|
27
|
+
this.logger = options.logger;
|
|
28
|
+
this.abortController = options.abortController;
|
|
29
|
+
this.onStdout = options.onStdout;
|
|
30
|
+
this.onStderr = options.onStderr;
|
|
36
31
|
}
|
|
37
32
|
|
|
38
33
|
private redactSecrets(data: string): string {
|
|
@@ -46,7 +41,7 @@ class CommandHandler {
|
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
return new Promise((resolve, reject) => {
|
|
49
|
-
const result =
|
|
44
|
+
const result = exec(
|
|
50
45
|
this.command.command,
|
|
51
46
|
{
|
|
52
47
|
cwd: this.command.cwd ?? process.cwd(),
|
|
@@ -56,7 +51,7 @@ class CommandHandler {
|
|
|
56
51
|
(error, stdout, stderr) => {
|
|
57
52
|
if (error) {
|
|
58
53
|
if (error.code === 1) {
|
|
59
|
-
return resolve({ exitCode:
|
|
54
|
+
return resolve({ exitCode: error.code as number, error });
|
|
60
55
|
}
|
|
61
56
|
this.logger.error(
|
|
62
57
|
"Error executing command:",
|
|
@@ -69,12 +64,10 @@ class CommandHandler {
|
|
|
69
64
|
|
|
70
65
|
result.stdout?.on("data", (data) => {
|
|
71
66
|
const redactedData = this.redactSecrets(data);
|
|
72
|
-
this.stdoutStream.write(redactedData);
|
|
73
67
|
this.onStdout?.(redactedData);
|
|
74
68
|
});
|
|
75
69
|
result.stderr?.on("data", (data) => {
|
|
76
70
|
const redactedData = this.redactSecrets(data);
|
|
77
|
-
this.stderrStream.write(redactedData);
|
|
78
71
|
this.onStderr?.(redactedData);
|
|
79
72
|
});
|
|
80
73
|
result.on("exit", (code) => {
|
|
@@ -90,7 +83,7 @@ class CommandHandler {
|
|
|
90
83
|
|
|
91
84
|
result.on("close", (code) => {
|
|
92
85
|
if (code === 0 || code === 1) {
|
|
93
|
-
this.logger.log("Command
|
|
86
|
+
this.logger.log("Command closed with code:", code);
|
|
94
87
|
return resolve({ exitCode: code });
|
|
95
88
|
}
|
|
96
89
|
return reject({ exitCode: code });
|
package/src/index.ts
CHANGED
|
@@ -42,7 +42,6 @@ export type {
|
|
|
42
42
|
ArtifactUploadRequestMessage,
|
|
43
43
|
ArtifactUploadResponseMessage,
|
|
44
44
|
ErrorMessage,
|
|
45
|
-
StreamEndMessage,
|
|
46
45
|
Command,
|
|
47
46
|
RuntimeEnvironmentDownloadable,
|
|
48
47
|
} from "./types.js";
|
|
@@ -53,7 +52,4 @@ export { createLogger, defaultLogger } from "./logger.js";
|
|
|
53
52
|
export type { Logger } from "./logger.js";
|
|
54
53
|
|
|
55
54
|
// Constants
|
|
56
|
-
export {
|
|
57
|
-
DEFAULT_ARTIFACTS_DIR,
|
|
58
|
-
DEFAULT_ARTIFACT_IGNORE,
|
|
59
|
-
} from "./constants.js";
|
|
55
|
+
export { DEFAULT_ARTIFACTS_DIR, DEFAULT_ARTIFACT_IGNORE } from "./constants.js";
|