@deepseek-ai/dsh-subprocess-local 0.1.5-rc.2 → 0.1.6-alpha.1
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.i18n.yaml +2 -2
- package/README.md +13 -2
- package/README.zh.md +20 -9
- package/lib/index.js +218 -71
- package/lib/output.js +181 -0
- package/lib/{runner-launch-COYGu0Dl.js → runner-launch-BnjugUhn.js} +63 -183
- package/lib/runner.js +21 -10
- package/lib/types/control-spawn.d.ts +19 -0
- package/lib/types/index.d.ts +5 -1
- package/lib/types/linux-execve.d.ts +1 -1
- package/lib/types/linux-scope.d.ts +13 -3
- package/lib/types/managed-owner.d.ts +2 -1
- package/lib/types/output.d.ts +85 -0
- package/lib/types/runner-launch.d.ts +4 -2
- package/lib/types/runner-protocol.d.ts +2 -0
- package/lib/types/spawn-runner.d.ts +1 -1
- package/lib/types/spawn.d.ts +1 -74
- package/lib/types/terminal.d.ts +2 -0
- package/package.json +13 -8
|
@@ -49,8 +49,18 @@ export declare function probeLinuxManager(internals?: LinuxScopeInternals): bool
|
|
|
49
49
|
export declare function probeLinuxNative(internals?: LinuxScopeInternals): boolean;
|
|
50
50
|
interface DirectRange {
|
|
51
51
|
running(): boolean;
|
|
52
|
-
signal
|
|
52
|
+
/** True for group TERM delivery, direct signal submission, or proven direct-PID absence. */
|
|
53
|
+
signal(signal: 'SIGTERM' | 'SIGKILL'): boolean;
|
|
54
|
+
/** Direct exit/error settlement, independent of output drain and managed-range completion. */
|
|
55
|
+
settled: Promise<unknown>;
|
|
53
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Send a direct-process signal, distinguishing an absent PID from failed delivery.
|
|
59
|
+
* @param pid - owned direct-process identity whose exit notification can still be pending.
|
|
60
|
+
* @param send - platform signal operation; true means the signal was submitted.
|
|
61
|
+
* @returns whether the signal was submitted or the owned PID is already absent.
|
|
62
|
+
*/
|
|
63
|
+
export declare function signalLinuxDirectProcess(pid: number, send: () => boolean): boolean;
|
|
54
64
|
/** Linux PTY invocation and owner for the exact one-shot scope/bootstrap. */
|
|
55
65
|
export interface LinuxTerminalScopeLaunch {
|
|
56
66
|
command: string;
|
|
@@ -66,7 +76,7 @@ export interface LinuxTerminalScopeLaunch {
|
|
|
66
76
|
* @param spec - terminal target request.
|
|
67
77
|
* @param targetEnv - validated complete target environment.
|
|
68
78
|
* @param internals - optional runner and systemd seams used by tests.
|
|
69
|
-
* @returns invocation
|
|
79
|
+
* @returns invocation and ownership callbacks; requested termination preserves the observed signal even before bootstrap consumption.
|
|
70
80
|
*/
|
|
71
81
|
export declare function prepareLinuxTerminalScope(spec: SubprocessTerminalSpawnSpec, targetEnv: Record<string, string>, internals?: LinuxScopeInternals): LinuxTerminalScopeLaunch;
|
|
72
82
|
/**
|
|
@@ -74,7 +84,7 @@ export declare function prepareLinuxTerminalScope(spec: SubprocessTerminalSpawnS
|
|
|
74
84
|
* @param spec - ordinary target request.
|
|
75
85
|
* @param targetEnv - validated complete target environment.
|
|
76
86
|
* @param internals - optional runner and systemd seams used by tests.
|
|
77
|
-
* @returns
|
|
87
|
+
* @returns streams, result, and scope owner; requested termination preserves the observed signal even before bootstrap consumption.
|
|
78
88
|
*/
|
|
79
89
|
export declare function launchLinuxScope(spec: SubprocessSpawnSpec, targetEnv: Record<string, string>, internals?: LinuxScopeInternals): ManagedProcessLaunch;
|
|
80
90
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Minimal managed-range ownership bound to one ordinary subprocess handle. */
|
|
2
|
-
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import type { Duplex, Readable, Writable } from 'node:stream';
|
|
3
3
|
import type { SubprocessOutcome } from '@deepseek-ai/dsh-subprocess';
|
|
4
4
|
/** Platform owner used by termination and whole-range settlement. */
|
|
5
5
|
export interface BoundProcessOwner {
|
|
@@ -17,6 +17,7 @@ export interface ManagedProcessLaunch {
|
|
|
17
17
|
stdin: Writable | null;
|
|
18
18
|
stdout: Readable | null;
|
|
19
19
|
stderr: Readable | null;
|
|
20
|
+
control?: Duplex | undefined;
|
|
20
21
|
direct: Promise<SubprocessOutcome>;
|
|
21
22
|
owner: BoundProcessOwner;
|
|
22
23
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { CollectedOutput } from '@deepseek-ai/dsh-subprocess';
|
|
2
|
+
/**
|
|
3
|
+
* Prepare fallible output storage before starting a managed native process.
|
|
4
|
+
* @param internals - optional caller-owned spill directory.
|
|
5
|
+
* @returns binding inputs whose spill directory is ready for use.
|
|
6
|
+
*/
|
|
7
|
+
export declare function prepareManagedProcessBinding(internals?: {
|
|
8
|
+
spillDir?: string;
|
|
9
|
+
}): {
|
|
10
|
+
spillDir: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Collects one stream with a bounded in-memory tail. With a spill cap, on
|
|
14
|
+
* first overflow a spill file is created and every chunk (including those
|
|
15
|
+
* already collected) is appended there while the full stream remains within
|
|
16
|
+
* the cap; without one, only the in-memory tail is ever retained (the
|
|
17
|
+
* diagnostic-tail shape — a language server's stderr).
|
|
18
|
+
*
|
|
19
|
+
* Tail-keep rationale (pi/OpenCode): errors and final results cluster at the
|
|
20
|
+
* end of command output; the spill file covers the head.
|
|
21
|
+
*/
|
|
22
|
+
export declare class OutputCollector {
|
|
23
|
+
private readonly maxBytes;
|
|
24
|
+
private readonly maxSpillBytes;
|
|
25
|
+
private readonly label;
|
|
26
|
+
private readonly spillDir;
|
|
27
|
+
private chunks;
|
|
28
|
+
private bytes;
|
|
29
|
+
private dropped;
|
|
30
|
+
private spillFd;
|
|
31
|
+
private spillFile;
|
|
32
|
+
private spillDisabled;
|
|
33
|
+
/** Total bytes ever pushed (not just retained). */
|
|
34
|
+
private total;
|
|
35
|
+
constructor(maxBytes: number, maxSpillBytes: number | undefined, label: string, spillDir: string);
|
|
36
|
+
/**
|
|
37
|
+
* Ingest one stream chunk, counting it toward the whole-stream total. On
|
|
38
|
+
* first overflow of the in-memory cap a spill file is opened (when spilling
|
|
39
|
+
* is enabled) and every chunk (already-collected ones included) is appended
|
|
40
|
+
* there from then on; the in-memory tail then drops whole chunks from its
|
|
41
|
+
* head (or the head of a single over-cap chunk) until it fits the cap again.
|
|
42
|
+
* @param chunk - the raw bytes from one stream 'data' event.
|
|
43
|
+
*/
|
|
44
|
+
push(chunk: Buffer): void;
|
|
45
|
+
/** Open the spill file lazily and append `chunk` (and any prior chunks once). */
|
|
46
|
+
private spillAll;
|
|
47
|
+
/** Stop spilling and remove the file once it can no longer hold the complete stream. */
|
|
48
|
+
private discardSpill;
|
|
49
|
+
/**
|
|
50
|
+
* Incremental read in whole-stream byte coordinates: returns everything
|
|
51
|
+
* pushed since `fromByte`. When `fromByte` has already slid out of the
|
|
52
|
+
* in-memory tail window, the read is `lossy` — it returns the whole
|
|
53
|
+
* retained tail and the gap is only recoverable from the spill file.
|
|
54
|
+
* @param fromByte - whole-stream offset to resume from (a prior read's `nextOffset`; 0 for the first read).
|
|
55
|
+
* @returns the delta text, the offset for the next read, the `lossy` flag, and the spill path when one was created.
|
|
56
|
+
*/
|
|
57
|
+
readFrom(fromByte: number): {
|
|
58
|
+
text: string;
|
|
59
|
+
nextOffset: number;
|
|
60
|
+
lossy: boolean;
|
|
61
|
+
spillPath?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Copy the retained raw tail with its position in the complete observed stream.
|
|
65
|
+
* @returns independent tail bytes and the total byte count before truncation.
|
|
66
|
+
*/
|
|
67
|
+
snapshot(): {
|
|
68
|
+
bytes: Buffer;
|
|
69
|
+
totalBytes: number;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Close the spill file once the stream has ended. A failed close (delayed
|
|
73
|
+
* writeback fault) stops advertising the spill path — the file may be
|
|
74
|
+
* missing its tail — while every in-memory read keeps working. Idempotent;
|
|
75
|
+
* the spawn path seals both collectors at settlement so reads after exit
|
|
76
|
+
* never point at a still-open file.
|
|
77
|
+
*/
|
|
78
|
+
seal(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Seal the spill file and return the final output.
|
|
81
|
+
* @returns the final collected output: tail text, truncation flag, and the spill path when intact.
|
|
82
|
+
*/
|
|
83
|
+
finalize(): CollectedOutput;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -39,7 +39,7 @@ export declare function consumeRunnerSelection(env?: NodeJS.ProcessEnv): string
|
|
|
39
39
|
export declare function parseRunnerTargetArgv(argv: readonly string[]): string[];
|
|
40
40
|
/**
|
|
41
41
|
* Build direct Linux target stdio, or isolated Windows runner stdio with IPC
|
|
42
|
-
* on fd 3 and target carriers on fd 4 through fd 6.
|
|
42
|
+
* on fd 3 and target carriers on fd 4 through fd 6; optional control uses fd 7.
|
|
43
43
|
* @param spec - ordinary subprocess request whose stdio modes are preserved.
|
|
44
44
|
* @param ipc - whether to isolate the runner and add its private Node IPC descriptor.
|
|
45
45
|
* @param stdinCarrier - runner fd 4 carrier; Windows ignore passes an opened null-device fd.
|
|
@@ -62,5 +62,7 @@ export declare function resolveWindowsExecutable(command: string, cwd: string, e
|
|
|
62
62
|
* @param spec - final target argv, cwd, and environment overrides.
|
|
63
63
|
* @returns complete target environment after Node-equivalent validation.
|
|
64
64
|
*/
|
|
65
|
-
export declare function targetEnvironment(spec: Pick<SubprocessSpawnSpec, 'argv' | 'cwd' | 'env'>
|
|
65
|
+
export declare function targetEnvironment(spec: Pick<SubprocessSpawnSpec, 'argv' | 'cwd' | 'env'> & {
|
|
66
|
+
stdio?: SubprocessSpawnSpec['stdio'];
|
|
67
|
+
}): Record<string, string>;
|
|
66
68
|
//# sourceMappingURL=runner-launch.d.ts.map
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export interface LinuxLaunchRequest {
|
|
4
4
|
cwd: string;
|
|
5
5
|
env: Record<string, string>;
|
|
6
|
+
control?: 'pipe';
|
|
6
7
|
}
|
|
7
8
|
/** Bounded Node-shaped error fields allowed across a private runner boundary. */
|
|
8
9
|
export interface SerializedRunnerError {
|
|
@@ -22,6 +23,7 @@ export interface WindowsStartRequest {
|
|
|
22
23
|
type: 'start';
|
|
23
24
|
cwd: string;
|
|
24
25
|
env: Record<string, string>;
|
|
26
|
+
control?: 'pipe';
|
|
25
27
|
}
|
|
26
28
|
/** The only parent-to-runner control message on Windows. */
|
|
27
29
|
export interface WindowsTerminateRequest {
|
|
@@ -7,7 +7,7 @@ type RunnerHost = Pick<NodeJS.Process, 'env' | 'exitCode' | 'connected' | 'cwd'
|
|
|
7
7
|
};
|
|
8
8
|
/** Injectable operations used by the protocol-owner tests. */
|
|
9
9
|
export interface SpawnRunnerInternals {
|
|
10
|
-
execve(file: string, argv: string[], env: Record<string, string
|
|
10
|
+
execve(file: string, argv: string[], env: Record<string, string>, control?: 'pipe'): never;
|
|
11
11
|
loadWin32ProcessBindings(): CurrentTokenProcessBindings;
|
|
12
12
|
spawnCurrentTokenJobProcess: typeof spawnCurrentTokenJobProcess;
|
|
13
13
|
closeFileDescriptor(fileDescriptor: number): void;
|
package/lib/types/spawn.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module dsh-subprocess-local/spawn
|
|
9
9
|
*/
|
|
10
10
|
import { type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
11
|
-
import type {
|
|
11
|
+
import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess';
|
|
12
12
|
import type { ManagedProcessLaunch } from './managed-owner.ts';
|
|
13
13
|
type SpawnProcess = (program: string, args: readonly string[], options: SpawnOptions) => ChildProcess;
|
|
14
14
|
/**
|
|
@@ -42,79 +42,6 @@ export interface LocalSubprocessHandle extends SubprocessHandle {
|
|
|
42
42
|
/** Force-terminate the current tree synchronously without starting timers or waits. */
|
|
43
43
|
terminateForHostExit(): void;
|
|
44
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
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* Collects one stream with a bounded in-memory tail. With a spill cap, on
|
|
55
|
-
* first overflow a spill file is created and every chunk (including those
|
|
56
|
-
* already collected) is appended there while the full stream remains within
|
|
57
|
-
* the cap; without one, only the in-memory tail is ever retained (the
|
|
58
|
-
* diagnostic-tail shape — a language server's stderr).
|
|
59
|
-
*
|
|
60
|
-
* Tail-keep rationale (pi/OpenCode): errors and final results cluster at the
|
|
61
|
-
* end of command output; the spill file covers the head.
|
|
62
|
-
*/
|
|
63
|
-
export declare class OutputCollector {
|
|
64
|
-
private readonly maxBytes;
|
|
65
|
-
private readonly maxSpillBytes;
|
|
66
|
-
private readonly label;
|
|
67
|
-
private readonly spillDir;
|
|
68
|
-
private chunks;
|
|
69
|
-
private bytes;
|
|
70
|
-
private dropped;
|
|
71
|
-
private spillFd;
|
|
72
|
-
private spillFile;
|
|
73
|
-
private spillDisabled;
|
|
74
|
-
/** Total bytes ever pushed (not just retained). */
|
|
75
|
-
private total;
|
|
76
|
-
constructor(maxBytes: number, maxSpillBytes: number | undefined, label: string, spillDir: string);
|
|
77
|
-
/**
|
|
78
|
-
* Ingest one stream chunk, counting it toward the whole-stream total. On
|
|
79
|
-
* first overflow of the in-memory cap a spill file is opened (when spilling
|
|
80
|
-
* is enabled) and every chunk (already-collected ones included) is appended
|
|
81
|
-
* there from then on; the in-memory tail then drops whole chunks from its
|
|
82
|
-
* head (or the head of a single over-cap chunk) until it fits the cap again.
|
|
83
|
-
* @param chunk - the raw bytes from one stream 'data' event.
|
|
84
|
-
*/
|
|
85
|
-
push(chunk: Buffer): void;
|
|
86
|
-
/** Open the spill file lazily and append `chunk` (and any prior chunks once). */
|
|
87
|
-
private spillAll;
|
|
88
|
-
/** Stop spilling and remove the file once it can no longer hold the complete stream. */
|
|
89
|
-
private discardSpill;
|
|
90
|
-
/**
|
|
91
|
-
* Incremental read in whole-stream byte coordinates: returns everything
|
|
92
|
-
* pushed since `fromByte`. When `fromByte` has already slid out of the
|
|
93
|
-
* in-memory tail window, the read is `lossy` — it returns the whole
|
|
94
|
-
* retained tail and the gap is only recoverable from the spill file.
|
|
95
|
-
* @param fromByte - whole-stream offset to resume from (a prior read's `nextOffset`; 0 for the first read).
|
|
96
|
-
* @returns the delta text, the offset for the next read, the `lossy` flag, and the spill path when one was created.
|
|
97
|
-
*/
|
|
98
|
-
readFrom(fromByte: number): {
|
|
99
|
-
text: string;
|
|
100
|
-
nextOffset: number;
|
|
101
|
-
lossy: boolean;
|
|
102
|
-
spillPath?: string;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Close the spill file once the stream has ended. A failed close (delayed
|
|
106
|
-
* writeback fault) stops advertising the spill path — the file may be
|
|
107
|
-
* missing its tail — while every in-memory read keeps working. Idempotent;
|
|
108
|
-
* the spawn path seals both collectors at settlement so reads after exit
|
|
109
|
-
* never point at a still-open file.
|
|
110
|
-
*/
|
|
111
|
-
seal(): void;
|
|
112
|
-
/**
|
|
113
|
-
* Seal the spill file and return the final output.
|
|
114
|
-
* @returns the final collected output: tail text, truncation flag, and the spill path when intact.
|
|
115
|
-
*/
|
|
116
|
-
finalize(): CollectedOutput;
|
|
117
|
-
}
|
|
118
45
|
/**
|
|
119
46
|
* Send `sig` to a detached POSIX process group. Never throws: delivery races
|
|
120
47
|
* process exit and may run in a timer callback, so failures are contained and
|
package/lib/types/terminal.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
|
|
|
29
29
|
private cleanup;
|
|
30
30
|
private managedOwnerCleaned;
|
|
31
31
|
private exited;
|
|
32
|
+
private outputPaused;
|
|
32
33
|
private trackedDescendants;
|
|
33
34
|
/** The spawned shell's start identity; scans stop adopting members once the root pid no longer carries it. */
|
|
34
35
|
private readonly rootIdentity;
|
|
@@ -42,6 +43,7 @@ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
|
|
|
42
43
|
/** Whether node-pty has not yet published the top-level exit event. */
|
|
43
44
|
get running(): boolean;
|
|
44
45
|
write(data: string): Promise<void>;
|
|
46
|
+
resize(cols: number, rows: number): Promise<void>;
|
|
45
47
|
inspectForeground(): Promise<SubprocessTerminalForeground | undefined>;
|
|
46
48
|
signalForeground(signal: SubprocessTerminalSignal): Promise<number>;
|
|
47
49
|
terminate(): Promise<void>;
|
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.
|
|
4
|
+
"version": "0.1.6-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -23,30 +23,35 @@
|
|
|
23
23
|
"default": "./lib/runner.js"
|
|
24
24
|
},
|
|
25
25
|
"./src/*": "./src/*",
|
|
26
|
-
"./package.json": "./package.json"
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
"./output": {
|
|
28
|
+
"types": "./lib/types/output.d.ts",
|
|
29
|
+
"default": "./lib/output.js"
|
|
30
|
+
}
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
29
33
|
"lib/index.js",
|
|
30
34
|
"lib/runner.js",
|
|
31
35
|
"lib/runner-*.js",
|
|
36
|
+
"lib/output.js",
|
|
32
37
|
"scripts/ensure-spawn-helper.mjs",
|
|
33
38
|
"lib/types/**/*.d.ts"
|
|
34
39
|
],
|
|
35
40
|
"license": "MIT",
|
|
36
41
|
"peerDependencies": {
|
|
37
|
-
"@deepseek-ai/dsh-subprocess": "^0.1.
|
|
38
|
-
"@deepseek-ai/dsh-timeout": "^0.1.
|
|
42
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.1",
|
|
43
|
+
"@deepseek-ai/dsh-timeout": "^0.1.6-alpha.1",
|
|
39
44
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
40
45
|
},
|
|
41
46
|
"dependencies": {
|
|
42
47
|
"koffi": "^3.1.0",
|
|
43
48
|
"node-pty": "1.2.0-beta.15",
|
|
44
|
-
"@deepseek-ai/dsh-win32-process": "^0.1.
|
|
49
|
+
"@deepseek-ai/dsh-win32-process": "^0.1.6-alpha.1"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
|
-
"@deepseek-ai/dsh-loader-smoke": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-subprocess": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-timeout": "^0.1.
|
|
52
|
+
"@deepseek-ai/dsh-loader-smoke": "^0.1.6-alpha.1",
|
|
53
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.6-alpha.1",
|
|
54
|
+
"@deepseek-ai/dsh-timeout": "^0.1.6-alpha.1",
|
|
50
55
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
51
56
|
},
|
|
52
57
|
"scripts": {
|