@gobing-ai/ts-runtime 0.4.13 → 0.4.15

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 CHANGED
@@ -601,7 +601,10 @@ maxOutput, forceBuffered, an optional synchronous `onOutput({ stream, chunk,
601
601
  timestamp })` observer, and registry fields (`source`, `teamId`, `agentId`).
602
602
  `onOutput` observes live chunks while the returned `ProcessResult` retains the
603
603
  complete buffered stdout/stderr; observers must enqueue and return, and thrown
604
- observer errors are isolated from the child. Inject the same `ProcessRegistry` into every executor that should
604
+ observer errors are isolated from the child. When `signal` is supplied, one-shot
605
+ commands run in an isolated process group on Unix so abort terminates descendants
606
+ that could otherwise retain the output pipes; Windows falls back to direct-child
607
+ cancellation. Inject the same `ProcessRegistry` into every executor that should
605
608
  appear in one watch list; without a registry, behavior is unchanged.
606
609
 
607
610
  Cloudflare Workers do not expose process execution; check
@@ -10,12 +10,14 @@
10
10
  * variant.
11
11
  */
12
12
  import type { FileSystem } from './file-system';
13
+ import type { RuntimePaths } from './runtime-paths';
13
14
  /**
14
15
  * Create a {@link FileSystem} backed by `node:fs`.
15
16
  *
16
17
  * @param root - Project root directory (default: walks up from `process.cwd()` looking for `bun.lock` or `package.json`).
18
+ * @param paths - Optional {@link RuntimePaths} anchor; `paths.cwd` seeds the project-root walk when `root` is absent. Defaults to the ambient process cwd.
17
19
  */
18
- export declare function createNodeFileSystem(root?: string): FileSystem;
20
+ export declare function createNodeFileSystem(root?: string, paths?: RuntimePaths): FileSystem;
19
21
  /**
20
22
  * Find the project root by walking up from `startDir` looking for a `bun.lock`
21
23
  * or `package.json` marker. Uses `existsSync`, so it works on both Node and Bun.
@@ -1 +1 @@
1
- {"version":3,"file":"file-system-node.d.ts","sourceRoot":"","sources":["../src/file-system-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAmBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,CA+E9D;AAWD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAaxD"}
1
+ {"version":3,"file":"file-system-node.d.ts","sourceRoot":"","sources":["../src/file-system-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAmBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,UAAU,CA+EpF;AAWD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAaxD"}
@@ -15,9 +15,10 @@ import { dirname, resolve as resolvePath } from 'node:path';
15
15
  * Create a {@link FileSystem} backed by `node:fs`.
16
16
  *
17
17
  * @param root - Project root directory (default: walks up from `process.cwd()` looking for `bun.lock` or `package.json`).
18
+ * @param paths - Optional {@link RuntimePaths} anchor; `paths.cwd` seeds the project-root walk when `root` is absent. Defaults to the ambient process cwd.
18
19
  */
19
- export function createNodeFileSystem(root) {
20
- const projectRoot = root ?? findProjectRoot(process.cwd());
20
+ export function createNodeFileSystem(root, paths) {
21
+ const projectRoot = root ?? findProjectRoot(paths?.cwd ?? process.cwd());
21
22
  return {
22
23
  getProjectRoot: () => projectRoot,
23
24
  resolve: (...segments) => resolvePath(projectRoot, ...segments),
package/dist/index.d.ts CHANGED
@@ -15,6 +15,8 @@ export { createInMemoryProcessRegistry, InMemoryProcessRegistry } from './proces
15
15
  export { cloudflareWorkersFactory } from './runtime-cf';
16
16
  export type { RuntimeFactory } from './runtime-factory';
17
17
  export { _resetNodeFileSystem, nodeBunFactory } from './runtime-node-bun';
18
+ export type { RuntimePaths } from './runtime-paths';
19
+ export { ambientRuntimePaths } from './runtime-paths';
18
20
  export * from './schema-validation';
19
21
  export * from './types';
20
22
  export { BunPipeProcessSpawner, BunSyncProcessExecutor } from './process-executor';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,+BAA+B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAC9E,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACH,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,aAAa,GAChB,MAAM,MAAM,CAAC;AACd,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EACR,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,UAAU,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EACR,8BAA8B,EAC9B,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,oBAAoB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AAIxB,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEnF;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,sBAAsB,CAAC,CAAC;AAE3G;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,+BAA+B,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAC9E,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EACH,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,aAAa,GAChB,MAAM,MAAM,CAAC;AACd,cAAc,QAAQ,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACjG,YAAY,EACR,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,UAAU,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EACR,8BAA8B,EAC9B,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,eAAe,EACf,oBAAoB,GACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AAIxB,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEnF;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,sBAAsB,CAAC,CAAC;AAE3G;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAAC,cAAc,oBAAoB,EAAE,qBAAqB,CAAC,CAAC"}
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export { NodeProcessExecutor, ProcessExecutor } from './process-executor.js';
11
11
  export { createInMemoryProcessRegistry, InMemoryProcessRegistry } from './process-registry.js';
12
12
  export { cloudflareWorkersFactory } from './runtime-cf.js';
13
13
  export { _resetNodeFileSystem, nodeBunFactory } from './runtime-node-bun.js';
14
+ export { ambientRuntimePaths } from './runtime-paths.js';
14
15
  export * from './schema-validation.js';
15
16
  export * from './types.js';
16
17
  // ── Deprecated re-exports (backward compatibility) ──────────────────────
@@ -1,4 +1,5 @@
1
1
  import type { ProcessExecutionSource, ProcessRegistry } from './process-registry';
2
+ import type { RuntimePaths } from './runtime-paths';
2
3
  /** Controls how stdout/stderr is captured: buffered in memory or streamed to the caller's terminal. */
3
4
  export type OutputPolicy = {
4
5
  mode: 'buffered';
@@ -19,6 +20,12 @@ export interface ProcessExecutorConfig {
19
20
  * Share one registry across all executors that should appear in the same watch list.
20
21
  */
21
22
  registry?: ProcessRegistry;
23
+ /**
24
+ * Optional cwd/home anchor (ADR-023 A1 / task 0042). When set, `paths.cwd` is applied to
25
+ * any `run` that carries no explicit per-call `cwd`. Precedence is total:
26
+ * explicit per-call `cwd` > injected `paths.cwd` > ambient process cwd.
27
+ */
28
+ paths?: RuntimePaths;
22
29
  }
23
30
  /** Options for spawning a child process. */
24
31
  export interface ProcessOptions {
@@ -31,7 +38,10 @@ export interface ProcessOptions {
31
38
  label?: string;
32
39
  rejectOnError?: boolean;
33
40
  forceBuffered?: boolean;
34
- /** AbortSignal forwarded to execa as `cancelSignal` — aborts the child process when fired. */
41
+ /**
42
+ * AbortSignal forwarded to execa and, on Unix, to the child's isolated
43
+ * process group so descendants cannot outlive a cancelled one-shot run.
44
+ */
35
45
  signal?: AbortSignal;
36
46
  /**
37
47
  * Optional non-blocking observer for incremental stdout/stderr.
@@ -1 +1 @@
1
- {"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../src/process-executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../src/process-executor.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,7 @@ export class NodeProcessExecutor {
24
24
  async runUntraced(options) {
25
25
  const args = options.args ?? [];
26
26
  const execaOptions = buildExecaOptions({
27
- cwd: options.cwd,
27
+ cwd: options.cwd ?? this.config.paths?.cwd,
28
28
  env: options.env,
29
29
  timeout: options.timeout ?? this.config.defaultTimeout,
30
30
  maxOutput: options.maxOutput ?? this.config.defaultMaxOutput,
@@ -53,9 +53,10 @@ export class NodeProcessExecutor {
53
53
  });
54
54
  try {
55
55
  const subprocess = execa(options.command, args, execaOptions);
56
+ const stopGroupCancellation = observeProcessGroupCancellation(subprocess, options.signal);
56
57
  observeOutput(subprocess.stdout, 'stdout', options.onOutput);
57
58
  observeOutput(subprocess.stderr, 'stderr', options.onOutput);
58
- const result = await subprocess;
59
+ const result = await subprocess.finally(stopGroupCancellation);
59
60
  // execa@9 Result does not expose pid — buffered runs leave pid unset.
60
61
  const processResult = {
61
62
  command: options.command,
@@ -355,8 +356,35 @@ function buildExecaOptions(opts) {
355
356
  ...(opts.env !== undefined ? { env: opts.env } : {}),
356
357
  ...(opts.timeout !== undefined ? { timeout: opts.timeout } : {}),
357
358
  ...(opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {}),
358
- ...(opts.signal !== undefined ? { cancelSignal: opts.signal } : {}),
359
+ ...(opts.signal !== undefined
360
+ ? {
361
+ cancelSignal: opts.signal,
362
+ // Node's negative-pid group signal requires the child to lead
363
+ // a distinct process group. Windows has no equivalent.
364
+ ...(process.platform !== 'win32' ? { detached: true } : {}),
365
+ }
366
+ : {}),
367
+ };
368
+ }
369
+ function observeProcessGroupCancellation(subprocess, signal) {
370
+ const pid = subprocess.pid;
371
+ if (signal === undefined || process.platform === 'win32' || pid === undefined)
372
+ return () => { };
373
+ const abort = () => {
374
+ try {
375
+ process.kill(-pid, 'SIGTERM');
376
+ }
377
+ catch {
378
+ // The leader may already have exited through execa's cancelSignal.
379
+ // Fall back to the direct child without changing cancellation semantics.
380
+ subprocess.kill('SIGTERM');
381
+ }
359
382
  };
383
+ if (signal.aborted)
384
+ abort();
385
+ else
386
+ signal.addEventListener('abort', abort, { once: true });
387
+ return () => signal.removeEventListener('abort', abort);
360
388
  }
361
389
  function observeOutput(stream, name, observer) {
362
390
  if (!stream || !observer)
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Injectable cwd/home anchor consumed by runtime seams ({@link createNodeFileSystem},
3
+ * {@link NodeProcessExecutor}, and the JSONL importer's root resolution).
4
+ *
5
+ * Defaults to the ambient process environment via {@link ambientRuntimePaths}; inject a fake
6
+ * in tests or whenever a consumer must not reach for ambient cwd/home state.
7
+ */
8
+ export interface RuntimePaths {
9
+ /** Absolute working-directory anchor. */
10
+ readonly cwd: string;
11
+ /** Absolute home-directory anchor. */
12
+ readonly home: string;
13
+ }
14
+ /**
15
+ * Capture the current ambient {@link RuntimePaths}: `process.cwd()` for `cwd`, and `HOME` /
16
+ * `USERPROFILE` for `home`. When `home` is unset (e.g. on Cloudflare Workers), falls back to
17
+ * `cwd` so the field stays a non-optional absolute anchor — matching the `/`-fallback philosophy
18
+ * of {@link getProcessCwd}: a degraded but always-absolute anchor.
19
+ */
20
+ export declare function ambientRuntimePaths(): RuntimePaths;
21
+ //# sourceMappingURL=runtime-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-paths.d.ts","sourceRoot":"","sources":["../src/runtime-paths.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IACzB,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,CAGlD"}
@@ -0,0 +1,15 @@
1
+ // Injectable cwd/home anchor for runtime seams. Composes {@link getProcessCwd} (path.ts) and
2
+ // {@link getHomeDir} (config.ts) so the two owning modules stay focused; this third module is the
3
+ // single home for the injectable shape and its ambient factory (ADR-023 A1 / task 0042).
4
+ import { getHomeDir } from './config.js';
5
+ import { getProcessCwd } from './path.js';
6
+ /**
7
+ * Capture the current ambient {@link RuntimePaths}: `process.cwd()` for `cwd`, and `HOME` /
8
+ * `USERPROFILE` for `home`. When `home` is unset (e.g. on Cloudflare Workers), falls back to
9
+ * `cwd` so the field stays a non-optional absolute anchor — matching the `/`-fallback philosophy
10
+ * of {@link getProcessCwd}: a degraded but always-absolute anchor.
11
+ */
12
+ export function ambientRuntimePaths() {
13
+ const cwd = getProcessCwd();
14
+ return { cwd, home: getHomeDir() ?? cwd };
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-runtime",
3
- "version": "0.4.13",
3
+ "version": "0.4.15",
4
4
  "description": "@gobing-ai/ts-runtime — Runtime abstractions for Bun, Node, and Cloudflare Workers.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -58,17 +58,17 @@
58
58
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-runtime-v<version> && git push --tags' && exit 1"
59
59
  },
60
60
  "dependencies": {
61
- "@gobing-ai/ts-utils": "^0.4.13",
61
+ "@gobing-ai/ts-utils": "^0.4.15",
62
62
  "execa": "^9.5.0",
63
63
  "yaml": "^2.7.0",
64
64
  "zod": "^4.1.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@gobing-ai/ts-db": "^0.4.13",
67
+ "@gobing-ai/ts-db": "^0.4.15",
68
68
  "@types/bun": "1.3.14"
69
69
  },
70
70
  "peerDependencies": {
71
- "@gobing-ai/ts-db": "^0.4.13"
71
+ "@gobing-ai/ts-db": "^0.4.15"
72
72
  },
73
73
  "peerDependenciesMeta": {
74
74
  "@gobing-ai/ts-db": {
@@ -28,14 +28,16 @@ import {
28
28
  import { dirname, resolve as resolvePath } from 'node:path';
29
29
 
30
30
  import type { FileSystem } from './file-system';
31
+ import type { RuntimePaths } from './runtime-paths';
31
32
 
32
33
  /**
33
34
  * Create a {@link FileSystem} backed by `node:fs`.
34
35
  *
35
36
  * @param root - Project root directory (default: walks up from `process.cwd()` looking for `bun.lock` or `package.json`).
37
+ * @param paths - Optional {@link RuntimePaths} anchor; `paths.cwd` seeds the project-root walk when `root` is absent. Defaults to the ambient process cwd.
36
38
  */
37
- export function createNodeFileSystem(root?: string): FileSystem {
38
- const projectRoot = root ?? findProjectRoot(process.cwd());
39
+ export function createNodeFileSystem(root?: string, paths?: RuntimePaths): FileSystem {
40
+ const projectRoot = root ?? findProjectRoot(paths?.cwd ?? process.cwd());
39
41
 
40
42
  return {
41
43
  getProjectRoot: () => projectRoot,
package/src/index.ts CHANGED
@@ -47,6 +47,8 @@ export { createInMemoryProcessRegistry, InMemoryProcessRegistry } from './proces
47
47
  export { cloudflareWorkersFactory } from './runtime-cf';
48
48
  export type { RuntimeFactory } from './runtime-factory';
49
49
  export { _resetNodeFileSystem, nodeBunFactory } from './runtime-node-bun';
50
+ export type { RuntimePaths } from './runtime-paths';
51
+ export { ambientRuntimePaths } from './runtime-paths';
50
52
  export * from './schema-validation';
51
53
  export * from './types';
52
54
 
@@ -1,6 +1,7 @@
1
1
  import { isatty } from 'node:tty';
2
2
  import { type Options as ExecaOptions, execa } from 'execa';
3
3
  import type { ProcessExecutionSource, ProcessRegistry } from './process-registry';
4
+ import type { RuntimePaths } from './runtime-paths';
4
5
 
5
6
  // ── Types ────────────────────────────────────────────────────────────────
6
7
 
@@ -20,6 +21,12 @@ export interface ProcessExecutorConfig {
20
21
  * Share one registry across all executors that should appear in the same watch list.
21
22
  */
22
23
  registry?: ProcessRegistry;
24
+ /**
25
+ * Optional cwd/home anchor (ADR-023 A1 / task 0042). When set, `paths.cwd` is applied to
26
+ * any `run` that carries no explicit per-call `cwd`. Precedence is total:
27
+ * explicit per-call `cwd` > injected `paths.cwd` > ambient process cwd.
28
+ */
29
+ paths?: RuntimePaths;
23
30
  }
24
31
 
25
32
  /** Options for spawning a child process. */
@@ -33,7 +40,10 @@ export interface ProcessOptions {
33
40
  label?: string;
34
41
  rejectOnError?: boolean;
35
42
  forceBuffered?: boolean;
36
- /** AbortSignal forwarded to execa as `cancelSignal` — aborts the child process when fired. */
43
+ /**
44
+ * AbortSignal forwarded to execa and, on Unix, to the child's isolated
45
+ * process group so descendants cannot outlive a cancelled one-shot run.
46
+ */
37
47
  signal?: AbortSignal;
38
48
  /**
39
49
  * Optional non-blocking observer for incremental stdout/stderr.
@@ -191,7 +201,7 @@ export class NodeProcessExecutor implements ProcessExecutor {
191
201
  private async runUntraced(options: ProcessOptions): Promise<ProcessResult> {
192
202
  const args = options.args ?? [];
193
203
  const execaOptions = buildExecaOptions({
194
- cwd: options.cwd,
204
+ cwd: options.cwd ?? this.config.paths?.cwd,
195
205
  env: options.env,
196
206
  timeout: options.timeout ?? this.config.defaultTimeout,
197
207
  maxOutput: options.maxOutput ?? this.config.defaultMaxOutput,
@@ -221,9 +231,10 @@ export class NodeProcessExecutor implements ProcessExecutor {
221
231
 
222
232
  try {
223
233
  const subprocess = execa(options.command, args, execaOptions);
234
+ const stopGroupCancellation = observeProcessGroupCancellation(subprocess, options.signal);
224
235
  observeOutput(subprocess.stdout, 'stdout', options.onOutput);
225
236
  observeOutput(subprocess.stderr, 'stderr', options.onOutput);
226
- const result = await subprocess;
237
+ const result = await subprocess.finally(stopGroupCancellation);
227
238
  // execa@9 Result does not expose pid — buffered runs leave pid unset.
228
239
  const processResult = {
229
240
  command: options.command,
@@ -602,8 +613,35 @@ function buildExecaOptions(opts: {
602
613
  ...(opts.env !== undefined ? { env: opts.env } : {}),
603
614
  ...(opts.timeout !== undefined ? { timeout: opts.timeout } : {}),
604
615
  ...(opts.maxOutput !== undefined ? { maxBuffer: opts.maxOutput } : {}),
605
- ...(opts.signal !== undefined ? { cancelSignal: opts.signal } : {}),
616
+ ...(opts.signal !== undefined
617
+ ? {
618
+ cancelSignal: opts.signal,
619
+ // Node's negative-pid group signal requires the child to lead
620
+ // a distinct process group. Windows has no equivalent.
621
+ ...(process.platform !== 'win32' ? { detached: true } : {}),
622
+ }
623
+ : {}),
624
+ };
625
+ }
626
+
627
+ function observeProcessGroupCancellation(
628
+ subprocess: { pid?: number; kill(signal?: NodeJS.Signals | number): boolean },
629
+ signal: AbortSignal | undefined,
630
+ ): () => void {
631
+ const pid = subprocess.pid;
632
+ if (signal === undefined || process.platform === 'win32' || pid === undefined) return () => {};
633
+ const abort = (): void => {
634
+ try {
635
+ process.kill(-pid, 'SIGTERM');
636
+ } catch {
637
+ // The leader may already have exited through execa's cancelSignal.
638
+ // Fall back to the direct child without changing cancellation semantics.
639
+ subprocess.kill('SIGTERM');
640
+ }
606
641
  };
642
+ if (signal.aborted) abort();
643
+ else signal.addEventListener('abort', abort, { once: true });
644
+ return () => signal.removeEventListener('abort', abort);
607
645
  }
608
646
 
609
647
  function observeOutput(
@@ -0,0 +1,31 @@
1
+ // Injectable cwd/home anchor for runtime seams. Composes {@link getProcessCwd} (path.ts) and
2
+ // {@link getHomeDir} (config.ts) so the two owning modules stay focused; this third module is the
3
+ // single home for the injectable shape and its ambient factory (ADR-023 A1 / task 0042).
4
+
5
+ import { getHomeDir } from './config';
6
+ import { getProcessCwd } from './path';
7
+
8
+ /**
9
+ * Injectable cwd/home anchor consumed by runtime seams ({@link createNodeFileSystem},
10
+ * {@link NodeProcessExecutor}, and the JSONL importer's root resolution).
11
+ *
12
+ * Defaults to the ambient process environment via {@link ambientRuntimePaths}; inject a fake
13
+ * in tests or whenever a consumer must not reach for ambient cwd/home state.
14
+ */
15
+ export interface RuntimePaths {
16
+ /** Absolute working-directory anchor. */
17
+ readonly cwd: string;
18
+ /** Absolute home-directory anchor. */
19
+ readonly home: string;
20
+ }
21
+
22
+ /**
23
+ * Capture the current ambient {@link RuntimePaths}: `process.cwd()` for `cwd`, and `HOME` /
24
+ * `USERPROFILE` for `home`. When `home` is unset (e.g. on Cloudflare Workers), falls back to
25
+ * `cwd` so the field stays a non-optional absolute anchor — matching the `/`-fallback philosophy
26
+ * of {@link getProcessCwd}: a degraded but always-absolute anchor.
27
+ */
28
+ export function ambientRuntimePaths(): RuntimePaths {
29
+ const cwd = getProcessCwd();
30
+ return { cwd, home: getHomeDir() ?? cwd };
31
+ }