@deepseek-ai/dsh-subprocess-local 0.1.2-alpha.5 → 0.1.3-alpha.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.
@@ -0,0 +1,109 @@
1
+ /** Closed private transports shared by the native subprocess runner. */
2
+ /** Target state restored by the Linux bootstrap after systemd establishes the scope. */
3
+ export interface LinuxLaunchRequest {
4
+ cwd: string;
5
+ env: Record<string, string>;
6
+ }
7
+ /** Bounded Node-shaped error fields allowed across a private runner boundary. */
8
+ export interface SerializedRunnerError {
9
+ name: string;
10
+ message: string;
11
+ code?: string;
12
+ syscall?: string;
13
+ path?: string;
14
+ }
15
+ /** A Linux pre-exec failure published beside its consumed request. */
16
+ export type LinuxStartupError = {
17
+ type: 'error';
18
+ error: SerializedRunnerError;
19
+ };
20
+ /** The only parent-to-runner start message on Windows. */
21
+ export interface WindowsStartRequest {
22
+ type: 'start';
23
+ cwd: string;
24
+ env: Record<string, string>;
25
+ }
26
+ /** The only parent-to-runner control message on Windows. */
27
+ export interface WindowsTerminateRequest {
28
+ type: 'terminate';
29
+ }
30
+ /** Exactly one direct-result branch is sent by a connected Windows runner. */
31
+ export type WindowsRunnerResult = {
32
+ type: 'target-exit';
33
+ exitCode: number;
34
+ } | {
35
+ type: 'error';
36
+ error: SerializedRunnerError;
37
+ };
38
+ /** Private paths owned by one Linux ordinary or PTY spawn. */
39
+ export interface LinuxLaunchFiles {
40
+ directory: string;
41
+ requestPath: string;
42
+ startupErrorPath: string;
43
+ }
44
+ /**
45
+ * Create a private 0700 directory and one complete 0600 launch request.
46
+ * @param request - target cwd and complete environment for the bootstrap.
47
+ * @returns private paths owned by this launch.
48
+ */
49
+ export declare function createLinuxLaunchFiles(request: LinuxLaunchRequest): LinuxLaunchFiles;
50
+ /**
51
+ * Derive the only permitted startup-error path from an absolute request locator.
52
+ * @param requestPath - absolute path to the private launch-request file.
53
+ * @returns validated sibling paths for this launch.
54
+ */
55
+ export declare function linuxLaunchFilesFromLocator(requestPath: string): LinuxLaunchFiles;
56
+ /**
57
+ * Strictly read and remove a one-shot Linux launch request.
58
+ * @param requestPath - private launch-request path to consume.
59
+ * @returns validated target cwd and environment.
60
+ */
61
+ export declare function consumeLinuxLaunchRequest(requestPath: string): LinuxLaunchRequest;
62
+ /**
63
+ * Publish one strict 0600 Linux pre-exec error.
64
+ * @param files - private paths for this launch.
65
+ * @param error - bounded spawn or runner failure to publish.
66
+ */
67
+ export declare function writeLinuxStartupError(files: LinuxLaunchFiles, error: LinuxStartupError): void;
68
+ /**
69
+ * Read the Linux pre-exec error, if the bootstrap published one.
70
+ * @param path - expected startup-error path.
71
+ * @returns the validated failure, or undefined when none was published.
72
+ */
73
+ export declare function readLinuxStartupError(path: string): LinuxStartupError | undefined;
74
+ /**
75
+ * Strictly parse the single Windows start message.
76
+ * @param value - untrusted IPC payload.
77
+ * @returns validated target start request.
78
+ */
79
+ export declare function parseWindowsStartRequest(value: unknown): WindowsStartRequest;
80
+ /**
81
+ * Return true only for the exact, payload-free Windows terminate control.
82
+ * @param value - untrusted IPC payload.
83
+ * @returns whether the payload is the exact terminate request.
84
+ */
85
+ export declare function isWindowsTerminateRequest(value: unknown): value is WindowsTerminateRequest;
86
+ /**
87
+ * Strictly parse one of the two Windows direct-result branches.
88
+ * @param value - untrusted IPC payload.
89
+ * @returns validated direct-result message.
90
+ */
91
+ export declare function parseWindowsRunnerResult(value: unknown): WindowsRunnerResult;
92
+ /**
93
+ * Convert an unknown failure into the bounded cross-process error record.
94
+ * @param error - failure caught at the process boundary.
95
+ * @returns bounded serializable error fields.
96
+ */
97
+ export declare function serializeRunnerError(error: unknown): SerializedRunnerError;
98
+ /**
99
+ * Rebuild a Node-shaped Error from a strict runner record.
100
+ * @param serialized - validated bounded error fields.
101
+ * @returns reconstructed Error with supported Node fields.
102
+ */
103
+ export declare function deserializeRunnerError(serialized: SerializedRunnerError): Error;
104
+ /**
105
+ * Best-effort removal of only the private paths created for this Linux spawn.
106
+ * @param files - exact private paths owned by this launch.
107
+ */
108
+ export declare function cleanupLinuxLaunchFiles(files: LinuxLaunchFiles): void;
109
+ //# sourceMappingURL=runner-protocol.d.ts.map
@@ -0,0 +1,36 @@
1
+ /** One-shot Linux exec bootstrap and Windows Job-owning subprocess runner. */
2
+ import { closeHandleChecked, isJobEmpty, pollProcessExit, spawnCurrentTokenJobProcess, terminateJob } from '@deepseek-ai/dsh-win32-process';
3
+ import type { CurrentTokenProcessBindings } from '@deepseek-ai/dsh-win32-process';
4
+ import { resolveWindowsExecutable } from './runner-launch.ts';
5
+ type RunnerHost = Pick<NodeJS.Process, 'env' | 'exitCode' | 'connected' | 'cwd' | 'chdir' | 'on' | 'off' | 'once' | 'disconnect'> & {
6
+ send?: NodeJS.Process['send'];
7
+ };
8
+ /** Injectable operations used by the protocol-owner tests. */
9
+ export interface SpawnRunnerInternals {
10
+ execve(file: string, argv: string[], env: Record<string, string>): never;
11
+ loadWin32ProcessBindings(): CurrentTokenProcessBindings;
12
+ spawnCurrentTokenJobProcess: typeof spawnCurrentTokenJobProcess;
13
+ closeFileDescriptor(fileDescriptor: number): void;
14
+ resolveWindowsExecutable: typeof resolveWindowsExecutable;
15
+ pollProcessExit: typeof pollProcessExit;
16
+ isJobEmpty: typeof isJobEmpty;
17
+ terminateJob: typeof terminateJob;
18
+ closeHandleChecked: typeof closeHandleChecked;
19
+ }
20
+ /**
21
+ * Execute the selected Linux bootstrap or Windows Job runner.
22
+ * @param selection - Windows sentinel or Linux launch-request locator.
23
+ * @param argv - private runner arguments beginning with the target delimiter.
24
+ * @param host - process transport and lifecycle host.
25
+ * @param internals - native and filesystem operations used by the runner.
26
+ */
27
+ export declare function runSpawnRunner(selection: string, argv: readonly string[], host?: RunnerHost, internals?: SpawnRunnerInternals): Promise<void>;
28
+ /**
29
+ * Best-effort reporting for failures before the selected runner established its owner.
30
+ * @param selection - Windows sentinel, Linux launch-request locator, or no selection.
31
+ * @param error - failure raised before normal runner settlement.
32
+ * @param host - process transport and lifecycle host.
33
+ */
34
+ export declare function reportSpawnRunnerFailure(selection: string | undefined, error: unknown, host?: RunnerHost): Promise<void>;
35
+ export {};
36
+ //# sourceMappingURL=spawn-runner.d.ts.map
@@ -1,12 +1,16 @@
1
1
  /**
2
- * Process plumbing for the local subprocess service: detached process-tree
3
- * spawn with per-stream stdio dispositions, tail-keep collection with spill
4
- * files, tree-scoped signalling (POSIX groups; Windows taskkill), and the
5
- * SIGTERM→SIGKILL escalation. This layer reacts to an abort signal; callers
6
- * own deadlines, teardown ladders, and cause classification.
2
+ * Process plumbing for the local subprocess service: ordinary process launch
3
+ * with per-stream stdio dispositions, tail-keep collection with spill
4
+ * files, provider-owned range signalling, and common termination scheduling.
5
+ * POSIX owners stage TERM before KILL; Windows owners terminate immediately.
6
+ * This layer reacts to an abort signal; callers own deadlines, teardown
7
+ * ladders, and cause classification.
7
8
  * @module dsh-subprocess-local/spawn
8
9
  */
10
+ import { type ChildProcess, type SpawnOptions } from 'node:child_process';
9
11
  import type { CollectedOutput, SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
12
+ import type { ManagedProcessLaunch } from './managed-owner.ts';
13
+ type SpawnProcess = (program: string, args: readonly string[], options: SpawnOptions) => ChildProcess;
10
14
  /**
11
15
  * Build a child environment: explicit caller entries override the scrubbed
12
16
  * parent base using the target platform's environment-key semantics. A string
@@ -16,8 +20,10 @@ import type { CollectedOutput, SubprocessHandle, SubprocessSpawnSpec } from '@de
16
20
  * @returns the environment to hand to `spawn` for the child process.
17
21
  */
18
22
  export declare function childEnv(extra?: Readonly<NodeJS.ProcessEnv>): NodeJS.ProcessEnv;
19
- /** Injectable knobs so tests can exercise spill and platform behavior deterministically. */
23
+ /** Injectable process, spill, and platform operations. */
20
24
  export interface SpawnInternals {
25
+ /** Process spawner (defaults to `node:child_process` `spawn`). */
26
+ spawn?: SpawnProcess;
21
27
  /** Directory for spill files (defaults to the OS temp dir). */
22
28
  spillDir?: string;
23
29
  /** Windows tree-termination runner (defaults to `taskkill /PID <pid> /T /F`). */
@@ -36,6 +42,14 @@ export interface LocalSubprocessHandle extends SubprocessHandle {
36
42
  /** Force-terminate the current tree synchronously without starting timers or waits. */
37
43
  terminateForHostExit(): void;
38
44
  }
45
+ /**
46
+ * Prepare fallible output storage before starting a managed native process.
47
+ * @param internals - optional caller-owned spill directory.
48
+ * @returns binding inputs whose spill directory is ready for use.
49
+ */
50
+ export declare function prepareManagedProcessBinding(internals?: Pick<SpawnInternals, 'spillDir'>): {
51
+ spillDir: string;
52
+ };
39
53
  /**
40
54
  * Collects one stream with a bounded in-memory tail. With a spill cap, on
41
55
  * first overflow a spill file is created and every chunk (including those
@@ -104,27 +118,39 @@ export declare class OutputCollector {
104
118
  /**
105
119
  * Send `sig` to a detached POSIX process group. Never throws: delivery races
106
120
  * process exit and may run in a timer callback, so failures are contained and
107
- * a non-positive pid is a no-op.
108
- * @param pid - the group leader's pid; non-positive means the spawn failed and the call is a no-op.
121
+ * a missing pid is a no-op.
122
+ * @param pid - the group leader's pid, when the spawn published one.
109
123
  * @param sig - the signal to deliver to the whole group.
110
124
  */
111
- export declare function killGroup(pid: number, sig: NodeJS.Signals): void;
125
+ export declare function killGroup(pid: number | undefined, sig: NodeJS.Signals): void;
112
126
  /**
113
127
  * Terminate one Windows process tree with `taskkill /T /F`. Contained like
114
128
  * POSIX group signalling — delivery races tree exit, so an absent tree, a
115
129
  * nonzero status, or a missing taskkill binary must not break idempotent
116
130
  * teardown.
117
- * @param pid - root process id; non-positive is a no-op.
131
+ * @param pid - root process id, when the spawn published one.
132
+ */
133
+ export declare function taskkillProcessTree(pid: number | undefined): void;
134
+ /**
135
+ * Validate the synchronous portion of one ordinary spawn request.
136
+ * @param spec - exact target request.
137
+ * @throws when grace, cancellation, or argv is invalid before launch.
138
+ */
139
+ export declare function validateSubprocessSpec(spec: SubprocessSpawnSpec): void;
140
+ /**
141
+ * Bind platform launch facts to the existing stdio, outcome, abort, and termination lifecycle.
142
+ * @param spec - fully resolved argv, cwd, stdio, grace, cancellation, environment.
143
+ * @param launch - platform streams, direct outcome, and managed-range owner.
144
+ * @param internals - test-only spill-directory override.
145
+ * @returns live subprocess handle.
118
146
  */
119
- export declare function taskkillProcessTree(pid: number): void;
147
+ export declare function bindManagedProcess(spec: SubprocessSpawnSpec, launch: ManagedProcessLaunch, internals?: Pick<SpawnInternals, 'spillDir'>): LocalSubprocessHandle;
120
148
  /**
121
- * Spawn one isolated detached process tree with the spec's per-stream stdio
122
- * dispositions. Runtime exits resolve `done` as {@link SubprocessOutcome};
123
- * only spawn failures reject.
149
+ * Spawn one detached PGID/taskkill fallback and bind the common lifecycle.
124
150
  * @param spec - fully resolved argv, cwd, stdio, grace, cancellation, environment.
125
151
  * @param internals - test-only spill-directory, platform, and taskkill overrides.
126
152
  * @returns live subprocess handle.
127
- * @throws when `graceMs` cannot be represented by one Node timer.
128
153
  */
129
154
  export declare function spawnSubprocess(spec: SubprocessSpawnSpec, internals?: SpawnInternals): LocalSubprocessHandle;
155
+ export {};
130
156
  //# sourceMappingURL=spawn.d.ts.map
@@ -2,9 +2,11 @@
2
2
  import { PassThrough } from 'node:stream';
3
3
  import type { IPty } from 'node-pty';
4
4
  import type { SubprocessOutcome, SubprocessTerminalForeground, SubprocessTerminalHandle, SubprocessTerminalSignal } from '@deepseek-ai/dsh-subprocess';
5
+ import type { BoundProcessOwner } from './managed-owner.ts';
5
6
  import type { ProcessInspector } from './process-inspector.ts';
6
7
  /**
7
- * A local terminal whose process-session ownership stays below the PTY backend.
8
+ * A local terminal whose native managed range or fallback process-session
9
+ * ownership stays below the PTY backend.
8
10
  * The seam's terminate() promise — no write, inspection, or signal in flight
9
11
  * after settlement — holds here without operation tracking only because every
10
12
  * handle call completes synchronously under the hood (node-pty write, ps-based
@@ -16,6 +18,8 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
16
18
  private readonly inspector;
17
19
  private readonly graceMs;
18
20
  private readonly platform;
21
+ private readonly managedOwner?;
22
+ private readonly resolveManagedOutcome?;
19
23
  readonly pid: number;
20
24
  readonly output: PassThrough;
21
25
  readonly done: Promise<SubprocessOutcome>;
@@ -23,6 +27,7 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
23
27
  private readonly dataDisposable;
24
28
  private readonly exitDisposable;
25
29
  private cleanup;
30
+ private managedOwnerCleaned;
26
31
  private exited;
27
32
  private trackedDescendants;
28
33
  /** The spawned shell's start identity; scans stop adopting members once the root pid no longer carries it. */
@@ -33,7 +38,9 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
33
38
  * @param graceMs - TERM-to-KILL and exit-wait grace.
34
39
  * @param platform - host platform; defaults to the running platform, injectable for deterministic tests.
35
40
  */
36
- constructor(terminal: IPty, inspector: ProcessInspector, graceMs: number, platform?: NodeJS.Platform);
41
+ constructor(terminal: IPty, inspector: ProcessInspector, graceMs: number, platform?: NodeJS.Platform, managedOwner?: BoundProcessOwner | undefined, resolveManagedOutcome?: ((outcome: SubprocessOutcome) => SubprocessOutcome) | undefined);
42
+ /** Whether node-pty has not yet published the top-level exit event. */
43
+ get running(): boolean;
37
44
  write(data: string): Promise<void>;
38
45
  inspectForeground(): Promise<SubprocessTerminalForeground | undefined>;
39
46
  signalForeground(signal: SubprocessTerminalSignal): Promise<number>;
@@ -55,6 +62,8 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
55
62
  private stopShellWindows;
56
63
  private waitForWindowsShellExit;
57
64
  private closeOnce;
65
+ private cleanupManagedOwner;
66
+ private closeManagedRange;
58
67
  private settleExitIfGone;
59
68
  }
60
69
  //# sourceMappingURL=terminal.d.ts.map
@@ -0,0 +1,30 @@
1
+ /** Windows parent-side launch and ownership for the private Job runner. */
2
+ import { spawn } from 'node:child_process';
3
+ import type { SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
4
+ import { loadWin32ProcessBindings, probeCurrentTokenJobSupport } from '@deepseek-ai/dsh-win32-process';
5
+ import type { ManagedProcessLaunch } from './managed-owner.ts';
6
+ import type { RunnerInvocation } from './runner-launch.ts';
7
+ /** Test seams for runner launch and dynamic capability checks. */
8
+ export interface WindowsJobInternals {
9
+ spawn?: typeof spawn;
10
+ runnerInvocation?: RunnerInvocation;
11
+ resolveRunnerInvocation?: () => RunnerInvocation;
12
+ runnerAvailable?: (invocation: RunnerInvocation) => boolean;
13
+ loadWin32ProcessBindings?: typeof loadWin32ProcessBindings;
14
+ probeCurrentTokenJobSupport?: typeof probeCurrentTokenJobSupport;
15
+ }
16
+ /**
17
+ * Re-check the runner entry, bindings, and current Job capability for every spawn.
18
+ * @param internals - optional runner and Win32 capability seams used by tests.
19
+ * @returns whether the Windows native containment path is currently available.
20
+ */
21
+ export declare function probeWindowsJob(internals?: WindowsJobInternals): boolean;
22
+ /**
23
+ * Launch one target through a runner that uniquely owns its Job handle.
24
+ * @param spec - ordinary target request.
25
+ * @param targetEnv - validated complete target environment.
26
+ * @param internals - optional runner launch seams used by tests.
27
+ * @returns direct streams, result, and runner-owned managed range.
28
+ */
29
+ export declare function launchWindowsJob(spec: SubprocessSpawnSpec, targetEnv: Record<string, string>, internals?: WindowsJobInternals): ManagedProcessLaunch;
30
+ //# sourceMappingURL=windows-job.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-subprocess-local",
3
3
  "description": "Local-subprocess implementation of the DeepSeek Harness subprocess seam",
4
- "version": "0.1.2-alpha.5",
4
+ "version": "0.1.3-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,28 +18,35 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
+ "./runner": {
22
+ "types": "./lib/types/bin.d.ts",
23
+ "default": "./lib/runner.js"
24
+ },
21
25
  "./src/*": "./src/*",
22
26
  "./package.json": "./package.json"
23
27
  },
24
28
  "files": [
25
29
  "lib/index.js",
30
+ "lib/runner.js",
31
+ "lib/runner-*.js",
26
32
  "scripts/ensure-spawn-helper.mjs",
27
33
  "lib/types/**/*.d.ts"
28
34
  ],
29
35
  "license": "MIT",
30
36
  "peerDependencies": {
31
- "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.5",
32
- "@deepseek-ai/cordis": "^4.0.2",
33
- "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.5"
37
+ "@deepseek-ai/dsh-subprocess": "^0.1.3-alpha.2",
38
+ "@deepseek-ai/dsh-timeout": "^0.1.3-alpha.2",
39
+ "@deepseek-ai/cordis": "^4.0.2"
34
40
  },
35
41
  "dependencies": {
36
42
  "koffi": "^3.1.0",
37
- "node-pty": "1.2.0-beta.15"
43
+ "node-pty": "1.2.0-beta.15",
44
+ "@deepseek-ai/dsh-win32-process": "^0.1.3-alpha.2"
38
45
  },
39
46
  "devDependencies": {
40
- "@deepseek-ai/dsh-loader-smoke": "^0.1.2-alpha.5",
41
- "@deepseek-ai/dsh-timeout": "^0.1.2-alpha.5",
42
- "@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.5",
47
+ "@deepseek-ai/dsh-loader-smoke": "^0.1.3-alpha.2",
48
+ "@deepseek-ai/dsh-subprocess": "^0.1.3-alpha.2",
49
+ "@deepseek-ai/dsh-timeout": "^0.1.3-alpha.2",
43
50
  "@deepseek-ai/cordis": "^4.0.2"
44
51
  },
45
52
  "scripts": {