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.
Files changed (46) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/README.md +3 -4
  3. package/dist/artifact-manager.d.ts.map +1 -1
  4. package/dist/artifact-manager.js +0 -11
  5. package/dist/artifact-manager.js.map +1 -1
  6. package/dist/artifact-manager.test.js +1 -16
  7. package/dist/artifact-manager.test.js.map +1 -1
  8. package/dist/cli.js +0 -2
  9. package/dist/cli.js.map +1 -1
  10. package/dist/command-handler.d.ts +8 -4
  11. package/dist/command-handler.d.ts.map +1 -1
  12. package/dist/command-handler.js +10 -16
  13. package/dist/command-handler.js.map +1 -1
  14. package/dist/command-handler.test.js +101 -113
  15. package/dist/command-handler.test.js.map +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/server.d.ts +0 -1
  21. package/dist/server.d.ts.map +1 -1
  22. package/dist/server.js +0 -1
  23. package/dist/server.js.map +1 -1
  24. package/dist/session.d.ts +0 -1
  25. package/dist/session.d.ts.map +1 -1
  26. package/dist/session.js +30 -67
  27. package/dist/session.js.map +1 -1
  28. package/dist/session.test.js +15 -27
  29. package/dist/session.test.js.map +1 -1
  30. package/dist/types.d.ts +3 -8
  31. package/dist/types.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/artifact-manager.test.ts +1 -19
  34. package/src/artifact-manager.ts +0 -14
  35. package/src/cli.ts +0 -2
  36. package/src/command-handler.test.ts +111 -182
  37. package/src/command-handler.ts +18 -25
  38. package/src/index.ts +1 -5
  39. package/src/server.ts +0 -2
  40. package/src/session.test.ts +28 -48
  41. package/src/session.ts +30 -72
  42. package/src/types.ts +2 -17
  43. package/dist/default-handler.d.ts +0 -3
  44. package/dist/default-handler.d.ts.map +0 -1
  45. package/dist/default-handler.js +0 -76
  46. package/dist/default-handler.js.map +0 -1
@@ -14,23 +14,23 @@ vi.mock("fs", async () => {
14
14
  };
15
15
  });
16
16
 
17
- let execFileCallback: Function;
18
- let execFileChild: any;
17
+ let execCallback: Function;
18
+ let execChild: any;
19
19
 
20
20
  vi.mock("node:child_process", () => ({
21
- execFile: vi.fn((_cmd: string, _opts: any, cb: Function) => {
22
- execFileCallback = cb;
23
- execFileChild = {
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 execFileChild;
28
+ return execChild;
29
29
  }),
30
30
  }));
31
31
 
32
32
  import fs from "fs";
33
- import { execFile } from "node:child_process";
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "ok", "");
97
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "", "");
121
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "", "");
142
- const closeHandler = execFileChild.on.mock.calls.find(
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 execFile with the right command, cwd, and env", () => {
156
- const { stdoutStream, stderrStream } = createStreams();
157
- const handler = createHandler(
158
- { command: "ls", cwd: "/work", env: { FOO: "bar" } },
159
- stdoutStream,
160
- stderrStream,
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(execFile).toHaveBeenCalledWith(
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 { stdoutStream, stderrStream } = createStreams();
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(execFile).toHaveBeenCalledWith(
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 execFile", () => {
150
+ it("passes abort signal to exec", () => {
193
151
  const ac = new AbortController();
194
- const { stdoutStream, stderrStream } = createStreams();
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(execFile).toHaveBeenCalledWith(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "output", "");
224
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "", "");
244
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(err, "", "some error");
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(null, "", "");
282
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream } = createStreams();
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
- execFileCallback(err, "", "");
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 { stdoutStream, stderrStream, stdoutChunks } = createStreams();
236
+ const onStdout = vi.fn();
310
237
  const handler = createHandler(
311
238
  { command: "echo" },
312
- stdoutStream,
313
- stderrStream,
239
+ new AbortController(),
240
+ onStdout,
314
241
  );
315
242
 
316
243
  const promise = handler.execute();
317
244
 
318
- const stdoutDataHandler = execFileChild.stdout.on.mock.calls.find(
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
- execFileCallback(null, "", "");
324
- const closeHandler = execFileChild.on.mock.calls.find(
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(stdoutChunks).toContain("hello world");
257
+ expect(onStdout).toHaveBeenCalledWith("hello world");
331
258
  });
332
259
 
333
260
  it("writes stderr to the stderr stream", async () => {
334
- const { stdoutStream, stderrStream, stderrChunks } = createStreams();
261
+ const onStderr = vi.fn();
335
262
  const handler = createHandler(
336
263
  { command: "warn" },
337
- stdoutStream,
338
- stderrStream,
264
+ new AbortController(),
265
+ undefined,
266
+ onStderr,
339
267
  );
340
268
 
341
269
  const promise = handler.execute();
342
270
 
343
- const stderrDataHandler = execFileChild.stderr.on.mock.calls.find(
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
- execFileCallback(null, "", "");
349
- const closeHandler = execFileChild.on.mock.calls.find(
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(stderrChunks).toContain("warning message");
283
+ expect(onStderr).toHaveBeenCalledWith("warning message");
356
284
  });
357
285
 
358
286
  it("redacts secrets from stdout", async () => {
359
- const { stdoutStream, stderrStream, stdoutChunks } = createStreams();
287
+ const onStdout = vi.fn();
360
288
  const handler = createHandler(
361
- { command: "echo", env: { API_KEY: "s3cret" } },
362
- stdoutStream,
363
- stderrStream,
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 = execFileChild.stdout.on.mock.calls.find(
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
- execFileCallback(null, "", "");
374
- const closeHandler = execFileChild.on.mock.calls.find(
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(stdoutChunks).toEqual(["token is [REDACTED] ok"]);
311
+ expect(onStdout).toHaveBeenCalledWith("token is [REDACTED] ok");
381
312
  });
382
313
 
383
314
  it("redacts secrets from stderr", async () => {
384
- const { stdoutStream, stderrStream, stderrChunks } = createStreams();
315
+ const onStderr = vi.fn();
385
316
  const handler = createHandler(
386
317
  { command: "warn", env: { PASSWORD: "hunter2" } },
387
- stdoutStream,
388
- stderrStream,
318
+ new AbortController(),
319
+ undefined,
320
+ onStderr,
389
321
  );
390
322
 
391
323
  const promise = handler.execute();
392
324
 
393
- const stderrDataHandler = execFileChild.stderr.on.mock.calls.find(
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
- execFileCallback(null, "", "");
399
- const closeHandler = execFileChild.on.mock.calls.find(
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(stderrChunks).toEqual(["error: [REDACTED] leaked"]);
337
+ expect(onStderr).toHaveBeenCalledWith("error: [REDACTED] leaked");
406
338
  });
407
339
 
408
340
  it("redacts multiple env secrets from output", async () => {
409
- const { stdoutStream, stderrStream, stdoutChunks } = createStreams();
341
+ const onStdout = vi.fn();
410
342
  const handler = createHandler(
411
343
  { command: "echo", env: { KEY: "aaa", TOKEN: "bbb" } },
412
- stdoutStream,
413
- stderrStream,
344
+ new AbortController(),
345
+ onStdout,
414
346
  );
415
347
 
416
348
  const promise = handler.execute();
417
349
 
418
- const stdoutDataHandler = execFileChild.stdout.on.mock.calls.find(
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
- execFileCallback(null, "", "");
424
- const closeHandler = execFileChild.on.mock.calls.find(
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(stdoutChunks).toEqual(["[REDACTED] and [REDACTED]"]);
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
- stdoutStream,
440
- stderrStream,
441
- new AbortController(),
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 = execFileChild.stdout.on.mock.calls.find(
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
- execFileCallback(null, "", "");
453
- const closeHandler = execFileChild.on.mock.calls.find(
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 { stdoutStream, stderrStream, stdoutChunks } = createStreams();
392
+ const onStdout = vi.fn();
464
393
  const handler = createHandler(
465
394
  { command: "echo" },
466
- stdoutStream,
467
- stderrStream,
395
+ new AbortController(),
396
+ onStdout,
468
397
  );
469
398
 
470
399
  const promise = handler.execute();
471
400
 
472
- const stdoutDataHandler = execFileChild.stdout.on.mock.calls.find(
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
- execFileCallback(null, "", "");
478
- const closeHandler = execFileChild.on.mock.calls.find(
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(stdoutChunks).toEqual(["nothing secret here"]);
413
+ expect(onStdout).toHaveBeenCalledWith("nothing secret here");
485
414
  });
486
415
 
487
416
  it("does not write empty stdout", async () => {
488
- const { stdoutStream, stderrStream, stdoutChunks } = createStreams();
417
+ const onStdout = vi.fn();
489
418
  const handler = createHandler(
490
419
  { command: "silent" },
491
- stdoutStream,
492
- stderrStream,
420
+ new AbortController(),
421
+ onStdout,
493
422
  );
494
423
 
495
424
  const promise = handler.execute();
496
425
 
497
- execFileCallback(null, "", "");
498
- const closeHandler = execFileChild.on.mock.calls.find(
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(stdoutChunks).toHaveLength(0);
433
+ expect(onStdout).not.toHaveBeenCalled();
505
434
  });
506
435
  });
507
436
  });
@@ -1,5 +1,4 @@
1
- import { Writable } from "node:stream";
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: Command,
22
- logger: Logger,
23
- stdoutStream: Writable,
24
- stderrStream: Writable,
25
- abortController: AbortController,
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 = execFile(
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: 1, error });
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 exited with code:", code);
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";