@qlzt/dsh-subprocess-local 0.0.0-stage → 0.2.1-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,80 @@
1
+ /** Local node-pty terminal-process implementation for the subprocess seam. */
2
+ import { PassThrough } from 'node:stream';
3
+ import type { IPty } from 'node-pty';
4
+ import type { SubprocessOutcome, SubprocessTerminalActivity, SubprocessTerminalForeground, SubprocessTerminalHandle, SubprocessTerminalSignal } from '@qlzt/dsh-subprocess';
5
+ import type { BoundProcessOwner } from './managed-owner.ts';
6
+ import type { ProcessInspector } from './process-inspector.ts';
7
+ import type { ShellActivity } from './shell-activity.ts';
8
+ /**
9
+ * A local terminal whose native managed range or fallback process-session
10
+ * ownership stays below the PTY backend.
11
+ * The seam's terminate() promise — no write, inspection, or signal in flight
12
+ * after settlement — holds here without operation tracking only because every
13
+ * handle call completes synchronously under the hood (node-pty write, ps-based
14
+ * inspection). A first genuinely asynchronous step in any handle call must add
15
+ * the tracking a remote provider needs.
16
+ */
17
+ export declare class LocalTerminalHandle implements SubprocessTerminalHandle {
18
+ private readonly terminal;
19
+ private readonly inspector;
20
+ private readonly graceMs;
21
+ private readonly platform;
22
+ private readonly managedOwner?;
23
+ private readonly resolveManagedOutcome?;
24
+ private readonly shellActivity?;
25
+ private readonly onQuiescence?;
26
+ private readonly observeShellExit;
27
+ readonly pid: number;
28
+ readonly output: PassThrough;
29
+ readonly done: Promise<SubprocessOutcome>;
30
+ private readonly outcome;
31
+ private readonly dataDisposable;
32
+ private readonly exitDisposable;
33
+ private cleanup;
34
+ private managedOwnerCleaned;
35
+ private exited;
36
+ private outputPaused;
37
+ private trackedDescendants;
38
+ private activityRevision;
39
+ private activityKey;
40
+ private quiescent;
41
+ private managedRangeEmpty;
42
+ /** The spawned shell's start identity; scans stop adopting members once the root pid no longer carries it. */
43
+ private readonly rootIdentity;
44
+ /**
45
+ * @param terminal - allocated node-pty process.
46
+ * @param inspector - platform process/session operations.
47
+ * @param graceMs - TERM-to-KILL and exit-wait grace.
48
+ * @param platform - host platform; defaults to the running platform, injectable for deterministic tests.
49
+ */
50
+ constructor(terminal: IPty, inspector: ProcessInspector, graceMs: number, platform?: NodeJS.Platform, managedOwner?: BoundProcessOwner | undefined, resolveManagedOutcome?: ((outcome: SubprocessOutcome) => SubprocessOutcome) | undefined, shellActivity?: Pick<ShellActivity, "inspect" | "invalidate" | "dispose"> | undefined, onQuiescence?: (() => void) | undefined, observeShellExit?: boolean);
51
+ /** Whether node-pty has not yet published the top-level exit event. */
52
+ get running(): boolean;
53
+ write(data: string): Promise<void>;
54
+ resize(cols: number, rows: number): Promise<void>;
55
+ inspectForeground(): Promise<SubprocessTerminalForeground | undefined>;
56
+ inspectActivity(): Promise<SubprocessTerminalActivity>;
57
+ signalForeground(signal: SubprocessTerminalSignal): Promise<number>;
58
+ terminate(): Promise<void>;
59
+ /**
60
+ * Force-terminate the observable session synchronously during Node's exit
61
+ * event. This does not claim quiescence and does not replace terminate().
62
+ */
63
+ terminateForHostExit(): void;
64
+ private forceStopShell;
65
+ private survivors;
66
+ private descendants;
67
+ private waitForMembers;
68
+ private signalMembers;
69
+ private forceStopDescendants;
70
+ private unionMembers;
71
+ private stopDescendants;
72
+ private stopShell;
73
+ private stopShellWindows;
74
+ private waitForWindowsShellExit;
75
+ private closeOnce;
76
+ private cleanupManagedOwner;
77
+ private closeManagedRange;
78
+ private settleExitIfGone;
79
+ }
80
+ //# sourceMappingURL=terminal.d.ts.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Windows process-table operations for terminal readiness, signalling, and
3
+ * teardown: Toolhelp32 snapshot enumeration with GetProcessTimes creation-time
4
+ * identity and process-handle wait-state liveness, the shell pid as a pseudo
5
+ * process group (Windows has no POSIX groups), and taskkill tree signalling.
6
+ * The koffi bindings load lazily so
7
+ * non-Windows processes never touch Win32 libraries; all decision logic takes
8
+ * an injectable internals boundary so suites can pin it on any host.
9
+ * @module dsh-subprocess-local/windows-inspector
10
+ */
11
+ import type { SubprocessTerminalSignal } from '@qlzt/dsh-subprocess';
12
+ import type { ProcessIdentity, ProcessInspector, ProcessSnapshot } from './process-inspector.ts';
13
+ /** One Toolhelp32 process-table row. */
14
+ export interface ProcessEntry {
15
+ pid: number;
16
+ parentPid: number;
17
+ }
18
+ /** Creation identity plus the process object's current wait state. */
19
+ export interface WindowsProcessState {
20
+ /** GetProcessTimes creation identity used to fence PID reuse. */
21
+ started: string;
22
+ /** Whether a zero-time process-handle wait reports the process still running. */
23
+ active: boolean;
24
+ }
25
+ /** Injectable Windows process operations used by one local PTY session. */
26
+ export interface WindowsProcessInspectorInternals {
27
+ /** Enumerate the current process table (pid/parent pairs). */
28
+ snapshot(): ProcessEntry[];
29
+ /** Return one process's creation identity and wait state, or undefined when unreadable. */
30
+ processState(pid: number): WindowsProcessState | undefined;
31
+ /** Terminate one process tree; `force` maps to taskkill `/F`. */
32
+ taskkill(pid: number, force: boolean): void;
33
+ }
34
+ /**
35
+ * Walk a process table from one root in children-first order, retaining only
36
+ * members whose start identity is readable (unreadable members are detector
37
+ * misses, exactly like an unreadable `/proc` entry on Linux).
38
+ * @param entries - the process table snapshot.
39
+ * @param rootPid - the tree root to descend from.
40
+ * @param started - creation-time identity resolver for one member.
41
+ * @returns the root and its current transitive descendants, children first.
42
+ */
43
+ export declare function windowsProcessTree(entries: ProcessEntry[], rootPid: number, started: (pid: number) => string | undefined): ProcessIdentity[];
44
+ /**
45
+ * Windows {@link ProcessInspector}. The shell pid stands in for a foreground
46
+ * process group: it is a stable pseudo-group that lets the prompt-marker
47
+ * readiness path compare foreground identities, while every actual signal
48
+ * targets the console-wide tree through taskkill (SIGINT is delivered by the
49
+ * terminal handle as a `\x03` input write and never reaches this layer).
50
+ */
51
+ export declare class WindowsProcessInspector implements ProcessInspector {
52
+ private readonly internals;
53
+ constructor(internals?: WindowsProcessInspectorInternals);
54
+ foregroundPgid(shellPid: number): number;
55
+ isStdinWaiting(_pgid: number, _shellPid: number): boolean;
56
+ isAlive(identity: ProcessIdentity): boolean;
57
+ snapshot(): ProcessSnapshot;
58
+ signalGroup(pgid: number, signal: SubprocessTerminalSignal): void;
59
+ signalProcess(identity: ProcessIdentity, signal: 'SIGTERM' | 'SIGKILL'): void;
60
+ }
61
+ /**
62
+ * Create the Windows process inspector.
63
+ * @param internals - injectable process operations; defaults to the koffi-backed table.
64
+ * @returns the Windows inspector.
65
+ */
66
+ export declare function createWindowsProcessInspector(internals?: WindowsProcessInspectorInternals): WindowsProcessInspector;
67
+ declare const nativePtr: unique symbol;
68
+ /** Koffi 3 native pointer (a BigInt address), branded so it cannot silently enter numeric contexts. */
69
+ export type NativePtr = bigint & {
70
+ readonly [nativePtr]: true;
71
+ };
72
+ /**
73
+ * True for NULL and INVALID_HANDLE_VALUE returns from Win32 handle APIs.
74
+ * @param value - a handle as koffi may hand it back (pointer, null, or 0n).
75
+ * @returns whether the value signals an invalid handle.
76
+ */
77
+ export declare function isInvalidHandle(value: NativePtr | null | undefined): boolean;
78
+ export {};
79
+ //# sourceMappingURL=windows-inspector.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 '@qlzt/dsh-subprocess';
4
+ import { loadWin32ProcessBindings, probeCurrentTokenJobSupport } from '@qlzt/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,6 +1,62 @@
1
1
  {
2
2
  "name": "@qlzt/dsh-subprocess-local",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "Local-subprocess implementation of the DeepSeek Harness subprocess seam",
4
+ "version": "0.2.1-alpha.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitee.com/zsping1989/deepseek-harness.git",
11
+ "directory": "packages/subprocess/subprocess-local"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./runner": {
22
+ "types": "./lib/types/bin.d.ts",
23
+ "default": "./lib/runner.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json",
27
+ "./output": {
28
+ "types": "./lib/types/output.d.ts",
29
+ "default": "./lib/output.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "lib/index.js",
34
+ "lib/runner.js",
35
+ "lib/runner-*.js",
36
+ "lib/output.js",
37
+ "scripts/ensure-spawn-helper.mjs",
38
+ "lib/types/**/*.d.ts"
39
+ ],
40
+ "license": "MIT",
41
+ "peerDependencies": {
42
+ "@qlzt/dsh-subprocess": "0.2.1-alpha.2",
43
+ "@qlzt/dsh-timeout": "0.2.1-alpha.2",
44
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
45
+ },
46
+ "dependencies": {
47
+ "@qlzt/dsh-lazy-require": "0.2.1-alpha.2",
48
+ "@qlzt/dsh-win32-process": "0.2.1-alpha.2",
49
+ "koffi": "3.1.1",
50
+ "node-pty": "1.2.0-beta.15"
51
+ },
52
+ "devDependencies": {
53
+ "@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.2",
54
+ "@deepseek-ai/dsh-subprocess": "0.2.1-alpha.2",
55
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.2",
56
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
57
+ },
58
+ "scripts": {
59
+ "postinstall": "node scripts/ensure-spawn-helper.mjs"
60
+ },
61
+ "dsh": {}
62
+ }
@@ -0,0 +1,16 @@
1
+ /** Restore the executable bit stripped from node-pty's prebuilt helper. */
2
+
3
+ import { chmodSync, existsSync } from 'node:fs'
4
+ import { dirname, join } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const entry = fileURLToPath(import.meta.resolve('node-pty'))
8
+ const packageRoot = dirname(dirname(entry))
9
+ const candidates = [
10
+ join(packageRoot, 'prebuilds', `${process.platform}-${process.arch}`, 'spawn-helper'),
11
+ join(packageRoot, 'build', 'Release', 'spawn-helper'),
12
+ ]
13
+
14
+ for (const helper of candidates) {
15
+ if (existsSync(helper)) chmodSync(helper, 0o755)
16
+ }