@qlzt/dsh-subprocess-local 0.2.1-alpha.3 → 0.2.1-rc.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.
package/README.i18n.yaml CHANGED
@@ -42,8 +42,8 @@
42
42
  en: 531a48766c666ea0
43
43
  zh: b40df533a2fd7244
44
44
  /deepseek-ai-dsh-subprocess-local/understand-the-implementation/design-concept:
45
- en: c1f9a5ee756547b9
46
- zh: d47d9f6175f5cbc9
45
+ en: e4958ac8e71de757
46
+ zh: 713114f106717f9a
47
47
  /deepseek-ai-dsh-subprocess-local/understand-the-implementation/source-map:
48
48
  en: 3d5fd99e4ad1f7af
49
49
  zh: 241490c1052f415c
@@ -63,8 +63,8 @@
63
63
  en: e290ccc9b3c92c08
64
64
  zh: d75d48f485447a74
65
65
  /deepseek-ai-dsh-subprocess-local/known-limitations-and-deferred-work:
66
- en: aca923fe6b461acb
67
- zh: ee19a96907134d85
66
+ en: 4b45e4e4ca67af72
67
+ zh: 5185485974cd3fa0
68
68
  /deepseek-ai-dsh-subprocess-local/known-limitations-and-deferred-work/dev-note:
69
69
  en: e67f633dc6fe7773
70
70
  zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -83,7 +83,7 @@ This section explains the design decisions behind the provider and points at the
83
83
 
84
84
  ### Design concept
85
85
 
86
- Each spawn selects one owner for both signalling and quiescence. Supported Linux ordinary and terminal launches use transient user-systemd scopes, while supported Windows ordinary launches use a helper-owned kill-on-close Job. macOS, older or unavailable user-systemd, and unavailable Windows native support use the existing detached process-group, `taskkill`, or terminal-session observations with one warning. The provider never replays a command through fallback after a native path may have started it.
86
+ Each spawn selects one owner for both signalling and quiescence. Supported Linux ordinary and terminal launches use transient user-systemd scopes, while supported Windows ordinary launches use a helper-owned kill-on-close Job. macOS, unavailable user-systemd, and unavailable Windows native support use the existing detached process-group, `taskkill`, or terminal-session observations with one warning. The provider never replays a command through fallback after a native path may have started it.
87
87
 
88
88
  ### Source map
89
89
 
@@ -146,8 +146,8 @@ No direct invalidation; the named consumers own any request-prefix changes.
146
146
  These limits define when the provider is a poor fit or needs special operational care. They are current package constraints, not a general platform comparison or a task backlog.
147
147
 
148
148
  - **Linux direct exit has no independent deadline** — after direct `SIGKILL` is acknowledged, uninterruptible kernel I/O can keep disposal pending indefinitely; `graceMs` and scope-query polling budgets do not bound this wait.
149
- - **Native ownership has explicit host requirements** — Linux needs a readable user manager and `systemd-run --expand-environment=no`; older systemd versions use the warned PGID fallback. macOS always uses that fallback because no supported public persistent owner exists.
150
- - **Native selection has bounded per-spawn costs** — Linux repeats the bootstrap entry, libc `execve`/`fcntl` bindings, live user manager, and literal-argv scope probe until it first succeeds; later eligible ordinary or terminal spawns recheck only the live user manager. Windows rechecks the runner entry, bindings, and current Job support before every ordinary spawn. Successful Linux deep-probe state and fallback-warning de-duplication persist for the provider lifetime. All probes finish before the user command can run, and child-process probes have a 5-second timeout. Each Linux launch creates a private request directory, checks unresolved scope establishment every 50 milliseconds, then exponentially backs off an established active scope to at most 5 seconds between queries; a Windows ordinary launch keeps one runner and IPC channel until the Job reports zero active processes. Target standard handles are inherited directly, with no named-pipe stdio or result files.
149
+ - **Native ownership has explicit host requirements** — Linux needs a readable user manager whose `systemd-run` supports `--collect` (systemd 236 and later) and keeps scope arguments literal: version 254 and later receive `--expand-environment=no`, and earlier versions, whose scope mode never expands arguments, run without it. Hosts without a usable user manager use the warned PGID fallback. macOS always uses that fallback because no supported public persistent owner exists.
150
+ - **Native selection has bounded per-spawn costs** — Linux checks the bootstrap entry, libc `execve`/`fcntl` bindings, reachable user manager, and literal-argv scope probe on the first eligible ordinary or terminal spawn and keeps that verdict, including a failed or timed-out probe. The user manager is identified by the process listening on `$XDG_RUNTIME_DIR/systemd/private`, the socket `systemd-run --user` connects through, as listed in the kernel's unix socket table (`/proc/net/unix`) for the same network namespace; reading the table costs time proportional to its size. After a positive verdict, each eligible spawn probes again when the `systemd-run` found through `PATH` has a different device, inode, size, or modification time, and otherwise confirms the manager: a listener confirms it without a child process; otherwise one `systemctl --user show` query decides, and that spawn uses the fallback when the query fails. A scope launch whose bootstrap never consumed its launch request, such as one sent to a hung manager that still listens, fails that command, as do scope launches issued before that failure arrives, and the next eligible spawn probes again. After a negative verdict, the provider probes again only when a different listener appears, so a manager that starts later is used without a restart. Windows rechecks the runner entry, bindings, and current Job support before every ordinary spawn. Fallback-warning de-duplication also persists for the provider lifetime. All probes finish before the user command can run; each child-process probe has a 5-second timeout, and a `systemd-run` that rejects `--expand-environment=no` is probed once more without it. Each Linux launch creates a private request directory, checks unresolved scope establishment every 50 milliseconds, then exponentially backs off an established active scope to at most 5 seconds between queries; a Windows ordinary launch keeps one runner and IPC channel until the Job reports zero active processes. Target standard handles are inherited directly, with no named-pipe stdio or result files.
151
151
  - **Windows Job inheritance has defined exclusions** — ordinary descendants inherit the Job by default, but breakaway processes are outside the guarantee. The target starts only after Job assignment; external termination of the runner in the narrow create-to-assignment interval can leave a suspended target behind.
152
152
  - **Windows terminal signalling is console-wide** — SIGINT is delivered as a `\x03` Ctrl-C input write that the console host turns into a console-wide CTRL_C event (on Windows that host is node-pty's bundled OpenConsole); SIGTSTP and SIGHUP are rejected as unavailable; a `taskkill` without `/F` does not terminate console processes, so the teardown TERM tier is a grace wait before the `/F` escalation. Windows readiness has no exact stdin-wait tier: the prompt-marker fast path compares the shell pid as the pseudo foreground group, and silence/timing tiers cover the rest.
153
153
  - **Windows terminal allocation requires node-pty's bundled console host assets** — every Windows terminal is spawned with `useConptyDll: true`, so allocation resolves `conpty/conpty.dll` and `OpenConsole.exe` relative to node-pty's own addon; an asset that is missing or unpacked wrong makes `spawnTerminal` throw instead of falling back to the operating system console host. node-pty marks the option experimental, so a node-pty upgrade can change this behaviour. See the [bundled console host note](../../../.agents/notes/implemented/bug-fix/2026-09-28-pty-bundled-console-host.md).
package/README.zh.md CHANGED
@@ -83,7 +83,7 @@ Linux 普通进程和终端进程即使在 bootstrap 消费启动请求前被取
83
83
 
84
84
  ### 设计理念
85
85
 
86
- 每次 spawn 都为信号发送与完全停稳选择同一个 owner。受支持的 Linux 普通命令与终端启动使用临时 user-systemd scope,受支持的 Windows 普通命令使用由 helper 持有、关闭时终止成员的 Job。macOS、旧版或不可用的 user-systemd,以及不可用的 Windows 原生支持使用既有 detached 进程组、`taskkill` 或终端会话观察,并只告警一次。native 路径可能已经启动命令后,本提供方绝不会通过 fallback 重放该命令。
86
+ 每次 spawn 都为信号发送与完全停稳选择同一个 owner。受支持的 Linux 普通命令与终端启动使用临时 user-systemd scope,受支持的 Windows 普通命令使用由 helper 持有、关闭时终止成员的 Job。macOS、不可用的 user-systemd,以及不可用的 Windows 原生支持使用既有 detached 进程组、`taskkill` 或终端会话观察,并只告警一次。native 路径可能已经启动命令后,本提供方绝不会通过 fallback 重放该命令。
87
87
 
88
88
  ### 源码地图
89
89
 
@@ -146,8 +146,8 @@ spill 文件以 `0600` 权限、`O_EXCL` 与随机名称在 `0700` 每进程目
146
146
  这些限制说明本提供方何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是通用平台对比或任务积压。
147
147
 
148
148
  - **Linux 直接进程退出没有独立截止时间**——直接 `SIGKILL` 获得确认后,不可中断的内核 I/O 可能让 dispose 无限期等待;`graceMs` 和 scope 查询的轮询预算不限制此等待。
149
- - **native ownership 有明确宿主要求**——Linux 需要可读的 user manager 与 `systemd-run --expand-environment=no`;旧版 systemd 使用带告警的 PGID fallback。macOS 因没有受支持的公开 persistent owner,始终使用该 fallback。
150
- - **native 选择具有有界的每次 spawn 成本**——Linux 会重复检查 bootstrap 入口、libc `execve`/`fcntl` bindings、存活的 user manager 与 literal-argv scope 支持,直到这套完整探测首次成功;后续符合条件的普通命令或终端 spawn 只重新检查存活的 user manager。Windows 会在每次普通 spawn 前重新检查 runner 入口、bindings 与当前 Job 支持。Linux 深度探测的成功状态与 fallback 告警去重会在提供方生命周期内持续保留。所有探测都会在用户命令可能运行前完成,子进程探测的超时为 5 秒。每次 Linux 启动都会创建私有请求目录,以 50 毫秒间隔检查尚未确定的 scope 建立状态;scope 已建立且仍 active 后,查询间隔按指数增长,最多为 5 秒。Windows 普通命令会保留一个 runner 与一条 IPC 通道,直到 Job 报告活动进程数为零。目标会直接继承标准句柄,不使用 named-pipe stdio 或结果文件。
149
+ - **native ownership 有明确宿主要求**——Linux 需要可读的 user manager,且其 `systemd-run` 支持 `--collect`(systemd 236 及以上)并保持 scope 参数按字面传递:254 及以上版本传入 `--expand-environment=no`,更早版本的 scope 模式从不展开参数,因此不传该选项。没有可用 user manager 的宿主使用带告警的 PGID fallback。macOS 因没有受支持的公开 persistent owner,始终使用该 fallback。
150
+ - **native 选择具有有界的每次 spawn 成本**——Linux 在第一次符合条件的普通命令或终端 spawn 时检查 bootstrap 入口、libc `execve`/`fcntl` bindings、可连接的 user manager 与 literal-argv scope 支持,并保留这一结论,探测失败或超时的结论也是如此。user manager 以内核 unix socket 表(`/proc/net/unix`)中同一网络命名空间里在 `$XDG_RUNTIME_DIR/systemd/private`(`systemd-run --user` 连接 manager 所用的 socket)上监听的进程来识别;读取该表的耗时与表的大小成正比。结论为可用时,若经 `PATH` 找到的 `systemd-run` 的设备号、inode、大小或修改时间发生变化,下一次符合条件的 spawn 会重新探测;否则每次都会确认 manager:有监听者时无需子进程即可确认;否则由一次 `systemctl --user show` 查询决定,查询失败时这次 spawn 使用 fallback。bootstrap 从未消费 launch request 的 scope 启动(例如发给卡死但仍在监听的 manager)会让这条命令失败,在该失败到达之前发起的 scope 启动也会失败,下一次符合条件的 spawn 会重新探测。结论为不可用时,只有出现不同的监听者才会重新探测,因此之后才启动的 manager 无需重启即可被使用。Windows 会在每次普通 spawn 前重新检查 runner 入口、bindings 与当前 Job 支持。fallback 告警去重同样在提供方生命周期内持续保留。所有探测都会在用户命令可能运行前完成;每次子进程探测的超时为 5 秒,`systemd-run` 拒绝 `--expand-environment=no` 时会去掉该选项再探测一次。每次 Linux 启动都会创建私有请求目录,以 50 毫秒间隔检查尚未确定的 scope 建立状态;scope 已建立且仍 active 后,查询间隔按指数增长,最多为 5 秒。Windows 普通命令会保留一个 runner 与一条 IPC 通道,直到 Job 报告活动进程数为零。目标会直接继承标准句柄,不使用 named-pipe stdio 或结果文件。
151
151
  - **Windows Job inheritance 有明确排除项**——普通后代默认继承 Job,但 breakaway 进程不在保证范围。目标只在 Job 分配后启动;runner 若在 create-to-assignment 极窄区间遭外力终止,可能留下 suspended target。
152
152
  - **Windows 终端信号是控制台级的**——SIGINT 以 `\x03` Ctrl-C 输入写入投递,由控制台宿主(Windows 上为 node-pty 自带的 OpenConsole)转为控制台级 CTRL_C 事件;SIGTSTP 与 SIGHUP 被拒绝(不可用);不带 `/F` 的 `taskkill` 无法终止控制台进程,因此拆卸的 TERM 档是 `/F` 升级前的宽限等待。Windows 就绪没有精确的 stdin-wait 档:prompt-marker 快路径把 shell pid 作为伪前台进程组比较,其余由静默与计时档覆盖。
153
153
  - **Windows 终端分配依赖 node-pty 自带的控制台宿主资产**——每个 Windows 终端都以 `useConptyDll: true` 启动,分配时按 node-pty 自身 addon 的相对位置解析 `conpty/conpty.dll` 与 `OpenConsole.exe`;资产缺失或解包错误会让 `spawnTerminal` 抛错,而不是回退到操作系统控制台宿主。node-pty 把该选项标为实验性,升级 node-pty 可能改变这一行为。见[自带控制台宿主说明](../../../.agents/notes/implemented/bug-fix/2026-09-28-pty-bundled-console-host.zh.md)。
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { C as bindManagedProcess, D as createProcessInspector, E as validateSubprocessSpec, O as controlPipe, S as loadLinuxExecve, T as spawnSubprocess, _ as parseWindowsRunnerResult, c as runnerStdio, d as cleanupLinuxLaunchFiles, l as spawnRunnerInvocation, m as deserializeRunnerError, n as WINDOWS_RUNNER_SELECTION, o as runnerEnvironment, p as createLinuxLaunchFiles, s as runnerInvocationAvailable, u as targetEnvironment, w as childEnv, y as readLinuxStartupError } from "./runner-launch-B2zsQ1Dz.js";
2
2
  import { logSpillFailure, prepareManagedProcessBinding } from "./output.js";
3
- import { closeSync, constants, existsSync, mkdtempSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { closeSync, constants, existsSync, mkdtempSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { access, stat } from "node:fs/promises";
5
5
  import { constants as constants$1, devNull, tmpdir, userInfo } from "node:os";
6
6
  import { basename, delimiter, extname, isAbsolute, join, resolve } from "node:path";
@@ -17,6 +17,11 @@ import { PassThrough } from "node:stream";
17
17
  const SYSTEMCTL_TIMEOUT_MS = 5e3;
18
18
  const SCOPE_INITIAL_POLL_INTERVAL_MS = 50;
19
19
  const MISSING_UNIT = /\bunit\b[^\r\n]*(?:could not be found|not found|not loaded)/iu;
20
+ const LITERAL_ARGV_OPTION = "--expand-environment=no";
21
+ const LITERAL_ARGV_OPTION_REJECTED = /unrecognized option\b.*expand-environment/u;
22
+ const KILL_ALL_MEMBERS = "--kill-who=all";
23
+ /** Flags bit that /proc/net/unix sets on a listening socket (__SO_ACCEPTCON). */
24
+ const UNIX_SOCKET_ACCEPTING = 65536;
20
25
  function managerEnvironment() {
21
26
  const environment = childEnv({ LC_ALL: "C" });
22
27
  delete environment.SYSTEMD_LOG_TARGET;
@@ -68,35 +73,57 @@ function probeLinuxBootstrap(internals = {}) {
68
73
  /**
69
74
  * Confirm current literal-argv transient-scope support before selecting native launch.
70
75
  * @param internals - optional systemd command seams used by tests.
71
- * @returns whether the current user manager supports the required scope invocation.
76
+ * @returns the options that keep target argv literal, or undefined when the user manager cannot create the scope.
72
77
  */
73
78
  function probeLinuxScope(internals = {}) {
74
- const unitBase = unitStem("dsh-subprocess-probe");
75
- const result = (internals.spawnSync ?? spawnSync)(internals.systemdRun ?? "systemd-run", [
76
- "--user",
77
- "--scope",
78
- "--quiet",
79
- "--collect",
80
- "--expand-environment=no",
81
- `--unit=${unitBase}`,
82
- "--",
83
- internals.systemctl ?? "systemctl",
84
- "--user",
85
- "show",
86
- `${unitBase}.scope`,
87
- "--property=ActiveState",
88
- "--value"
89
- ], {
90
- env: quietSystemdEnvironment(),
91
- stdio: "ignore",
92
- timeout: SYSTEMCTL_TIMEOUT_MS
93
- });
94
- return result.error === void 0 && result.status === 0;
79
+ const attempt = (literalArgv) => {
80
+ const unitBase = unitStem("dsh-subprocess-probe");
81
+ const result = (internals.spawnSync ?? spawnSync)(internals.systemdRun ?? "systemd-run", [
82
+ "--user",
83
+ "--scope",
84
+ "--quiet",
85
+ "--collect",
86
+ ...literalArgv,
87
+ `--unit=${unitBase}`,
88
+ "--",
89
+ internals.systemctl ?? "systemctl",
90
+ "--user",
91
+ "show",
92
+ `${unitBase}.scope`,
93
+ "--property=ActiveState",
94
+ "--value"
95
+ ], {
96
+ encoding: "utf8",
97
+ env: quietSystemdEnvironment(),
98
+ stdio: [
99
+ "ignore",
100
+ "ignore",
101
+ "pipe"
102
+ ],
103
+ timeout: SYSTEMCTL_TIMEOUT_MS
104
+ });
105
+ return {
106
+ created: result.error === void 0 && result.status === 0,
107
+ stderr: result.stderr
108
+ };
109
+ };
110
+ const current = attempt([LITERAL_ARGV_OPTION]);
111
+ if (current.created) return [LITERAL_ARGV_OPTION];
112
+ if (!LITERAL_ARGV_OPTION_REJECTED.test(current.stderr)) return void 0;
113
+ return attempt([]).created ? [] : void 0;
114
+ }
115
+ /**
116
+ * Check every Linux native prerequisite on each call.
117
+ * @param internals - optional native capability seams used by tests.
118
+ * @returns the literal-argv scope options when Linux native containment is available, otherwise undefined.
119
+ */
120
+ function probeLinuxNative(internals = {}) {
121
+ return probeLinuxBootstrap(internals) ? probeLinuxScope(internals) : void 0;
95
122
  }
96
123
  /**
97
- * Confirm that the current user manager remains reachable after a positive deep probe.
124
+ * Query the user manager once through systemctl.
98
125
  * @param internals - optional systemctl seam used by tests.
99
- * @returns whether one lightweight manager query succeeds.
126
+ * @returns whether the query succeeds.
100
127
  */
101
128
  function probeLinuxManager(internals = {}) {
102
129
  const result = (internals.spawnSync ?? spawnSync)(internals.systemctl ?? "systemctl", [
@@ -112,13 +139,69 @@ function probeLinuxManager(internals = {}) {
112
139
  return result.error === void 0 && result.status === 0;
113
140
  }
114
141
  /**
115
- * Re-check every Linux native prerequisite for one eligible spawn.
116
- * @param internals - optional native capability seams used by tests.
117
- * @returns whether the Linux native containment path is currently available.
142
+ * Find the socket a process in this network namespace listens on at path. The kernel lists a
143
+ * bound socket only while a process holds it open, so a socket file that a stopped or crashed
144
+ * listener left behind does not count.
118
145
  */
119
- function probeLinuxNative(internals = {}) {
120
- return probeLinuxBootstrap(internals) && probeLinuxScope(internals);
146
+ function unixSocketListener(path, table) {
147
+ let text;
148
+ try {
149
+ text = readFileSync(table, "utf8");
150
+ } catch (_unreadableSocketTable) {
151
+ return;
152
+ }
153
+ const suffix = ` ${path}`;
154
+ for (const line of text.split("\n")) {
155
+ if (!line.endsWith(suffix)) continue;
156
+ const fields = line.trim().split(/\s+/u);
157
+ if (fields.slice(7).join(" ") === path && (Number.parseInt(String(fields[3]), 16) & UNIX_SOCKET_ACCEPTING) !== 0) return String(fields[6]);
158
+ }
121
159
  }
160
+ /**
161
+ * Identify the user manager's listener on `$XDG_RUNTIME_DIR/systemd/private`, the socket
162
+ * `systemd-run --user` connects through.
163
+ * @param internals - optional socket-table seam used by tests.
164
+ * @returns the listening socket's inode from the kernel socket table, or undefined when
165
+ * XDG_RUNTIME_DIR is unset or nothing in this network namespace listens there.
166
+ */
167
+ function linuxManagerListener(internals = {}) {
168
+ const runtimeDirectory = childEnv().XDG_RUNTIME_DIR;
169
+ if (runtimeDirectory === void 0 || runtimeDirectory === "") return void 0;
170
+ return unixSocketListener(join(runtimeDirectory, "systemd", "private"), internals.unixSocketTable ?? "/proc/net/unix");
171
+ }
172
+ /**
173
+ * Identify the systemd-run binary that scope launches resolve through PATH, so a package upgrade
174
+ * that replaces it triggers another probe of its literal-argv options.
175
+ * @param internals - optional systemd-run seam used by tests.
176
+ * @returns the device, inode, size, and modification time of the first match, or undefined when none resolves.
177
+ */
178
+ function systemdRunIdentity(internals = {}) {
179
+ const command = internals.systemdRun ?? "systemd-run";
180
+ const candidates = command.includes("/") ? [command] : (childEnv().PATH ?? "").split(":").filter((directory) => directory !== "").map((directory) => join(directory, command));
181
+ for (const candidate of candidates) {
182
+ let stats;
183
+ try {
184
+ stats = statSync(candidate, { throwIfNoEntry: false });
185
+ } catch (_unreadableCandidate) {
186
+ continue;
187
+ }
188
+ if (stats?.isFile() === true) return `${String(stats.dev)}:${String(stats.ino)}:${String(stats.size)}:${String(stats.mtimeMs)}`;
189
+ }
190
+ }
191
+ /**
192
+ * Confirm the user manager before a scope launch. A listener on its private socket confirms it
193
+ * without a child process; otherwise one systemctl query decides. A hung manager that still
194
+ * listens passes, and its scope launch fails with LinuxScopeNotStartedError.
195
+ * @param internals - optional socket-table and systemctl seams used by tests.
196
+ * @returns whether a scope launch can reach the user manager.
197
+ */
198
+ function confirmLinuxManager(internals = {}) {
199
+ return linuxManagerListener(internals) !== void 0 || probeLinuxManager(internals);
200
+ }
201
+ /** A scope launch whose bootstrap never consumed its launch request, so its target never ran. */
202
+ var LinuxScopeNotStartedError = class extends Error {
203
+ name = "LinuxScopeNotStartedError";
204
+ };
122
205
  var LinuxScopeStartup = class {
123
206
  files;
124
207
  kind;
@@ -130,7 +213,7 @@ var LinuxScopeStartup = class {
130
213
  resolveOutcome(outcome) {
131
214
  const startup = readLinuxStartupError(this.files.startupErrorPath);
132
215
  if (startup !== void 0) throw deserializeRunnerError(startup.error);
133
- if (existsSync(this.files.requestPath) && !(outcome.signal !== null && this.terminationSignals.has(outcome.signal))) throw new Error(`${this.kind} scope exited before its bootstrap consumed the launch request`);
216
+ if (existsSync(this.files.requestPath) && !(outcome.signal !== null && this.terminationSignals.has(outcome.signal))) throw new LinuxScopeNotStartedError(`${this.kind} scope exited before its bootstrap consumed the launch request; the provider probes native containment again before its next command`);
134
217
  return outcome;
135
218
  }
136
219
  };
@@ -187,7 +270,7 @@ var SystemdScopeOwner = class {
187
270
  const result = this.runSync(this.systemctl, [
188
271
  "--user",
189
272
  "kill",
190
- "--kill-whom=all",
273
+ KILL_ALL_MEMBERS,
191
274
  `--signal=${signal}`,
192
275
  this.unit
193
276
  ], {
@@ -221,7 +304,7 @@ var SystemdScopeOwner = class {
221
304
  this.runSync(this.systemctl, [
222
305
  "--user",
223
306
  "kill",
224
- "--kill-whom=all",
307
+ KILL_ALL_MEMBERS,
225
308
  "--signal=SIGKILL",
226
309
  this.unit
227
310
  ], {
@@ -380,13 +463,13 @@ var SystemdScopeOwner = class {
380
463
  cleanupLinuxLaunchFiles(this.startup.files);
381
464
  }
382
465
  };
383
- function scopeArgs(unitBase, invocation, argv) {
466
+ function scopeArgs(unitBase, literalArgv, invocation, argv) {
384
467
  return [
385
468
  "--user",
386
469
  "--scope",
387
470
  "--quiet",
388
471
  "--collect",
389
- "--expand-environment=no",
472
+ ...literalArgv,
390
473
  `--unit=${unitBase}`,
391
474
  "--",
392
475
  ...invocation,
@@ -445,10 +528,11 @@ function signalChildGroup(child, signal) {
445
528
  * Prepare one Linux PTY scope using the same launch request and bootstrap core.
446
529
  * @param spec - terminal target request.
447
530
  * @param targetEnv - validated complete target environment.
531
+ * @param literalArgv - options from the provider's positive scope probe.
448
532
  * @param internals - optional runner and systemd seams used by tests.
449
533
  * @returns invocation and ownership callbacks; requested termination preserves the observed signal even before bootstrap consumption.
450
534
  */
451
- function prepareLinuxTerminalScope(spec, targetEnv, internals = {}) {
535
+ function prepareLinuxTerminalScope(spec, targetEnv, literalArgv, internals = {}) {
452
536
  const invocation = internals.runnerInvocation ?? spawnRunnerInvocation();
453
537
  const files = createLinuxLaunchFiles({
454
538
  cwd: spec.cwd,
@@ -458,7 +542,7 @@ function prepareLinuxTerminalScope(spec, targetEnv, internals = {}) {
458
542
  const unitBase = unitStem("dsh-terminal");
459
543
  return {
460
544
  command: internals.systemdRun ?? "systemd-run",
461
- args: scopeArgs(unitBase, invocation, spec.argv),
545
+ args: scopeArgs(unitBase, literalArgv, invocation, spec.argv),
462
546
  cwd: process.cwd(),
463
547
  env: runnerEnvironment(files.requestPath, invocation),
464
548
  bindOwner: (direct) => new SystemdScopeOwner(`${unitBase}.scope`, startup, direct, internals.systemctl ?? "systemctl", internals.spawnSync ?? spawnSync, internals.systemctlQuery ?? querySystemctl, internals.sleep ?? sleepWithAbort),
@@ -472,10 +556,11 @@ function prepareLinuxTerminalScope(spec, targetEnv, internals = {}) {
472
556
  * Launch one ordinary target inside a transient user scope.
473
557
  * @param spec - ordinary target request.
474
558
  * @param targetEnv - validated complete target environment.
559
+ * @param literalArgv - options from the provider's positive scope probe.
475
560
  * @param internals - optional runner and systemd seams used by tests.
476
561
  * @returns streams, result, and scope owner; requested termination preserves the observed signal even before bootstrap consumption.
477
562
  */
478
- function launchLinuxScope(spec, targetEnv, internals = {}) {
563
+ function launchLinuxScope(spec, targetEnv, literalArgv, internals = {}) {
479
564
  const invocation = internals.runnerInvocation ?? spawnRunnerInvocation();
480
565
  const files = createLinuxLaunchFiles({
481
566
  cwd: spec.cwd,
@@ -486,7 +571,7 @@ function launchLinuxScope(spec, targetEnv, internals = {}) {
486
571
  const unitBase = unitStem("dsh-subprocess");
487
572
  let child;
488
573
  try {
489
- child = (internals.spawn ?? spawn)(internals.systemdRun ?? "systemd-run", scopeArgs(unitBase, invocation, spec.argv), {
574
+ child = (internals.spawn ?? spawn)(internals.systemdRun ?? "systemd-run", scopeArgs(unitBase, literalArgv, invocation, spec.argv), {
490
575
  cwd: process.cwd(),
491
576
  env: runnerEnvironment(files.requestPath, invocation),
492
577
  stdio: runnerStdio(spec, false),
@@ -512,6 +597,102 @@ function launchLinuxScope(spec, targetEnv, internals = {}) {
512
597
  };
513
598
  }
514
599
  //#endregion
600
+ //#region lib/types/linux-scope-selector.js
601
+ /** Linux native containment decisions over injected host observations. */
602
+ /**
603
+ * Provider-lifetime Linux scope selection. A probe runs on the first selection and again only after
604
+ * a scope launch never started its target, a different user-manager listener appears under a
605
+ * negative verdict, or the systemd-run binary changes under a positive one. A positive verdict
606
+ * confirms the user manager on every selection. Selection is synchronous, so a decision stays
607
+ * valid for the launch that follows it.
608
+ */
609
+ var LinuxScopeSelector = class {
610
+ observe;
611
+ state = {
612
+ kind: "unprobed",
613
+ trigger: "first"
614
+ };
615
+ generation = 0;
616
+ /** @param observe - host checks consulted by each selection. */
617
+ constructor(observe) {
618
+ this.observe = observe;
619
+ }
620
+ /**
621
+ * Choose containment for one eligible launch.
622
+ * @returns the decision and the branch that produced it.
623
+ */
624
+ select() {
625
+ const state = this.state;
626
+ if (state.kind === "unprobed") return this.probe(state.trigger);
627
+ if (state.kind === "available") {
628
+ if (this.observe.launcherIdentity() !== state.launcher) return this.probe("launcher-changed");
629
+ return this.observe.confirmManager() ? {
630
+ kind: "scope",
631
+ reason: "manager-confirmed",
632
+ literalArgv: state.literalArgv,
633
+ generation: this.generation,
634
+ probe: void 0
635
+ } : {
636
+ kind: "fallback",
637
+ reason: "manager-unconfirmed",
638
+ probe: void 0
639
+ };
640
+ }
641
+ const listener = this.observe.managerListener();
642
+ if (listener === void 0) return {
643
+ kind: "fallback",
644
+ reason: "no-listener",
645
+ probe: void 0
646
+ };
647
+ if (listener === state.listener) return {
648
+ kind: "fallback",
649
+ reason: "same-listener",
650
+ probe: void 0
651
+ };
652
+ return this.probe("new-listener");
653
+ }
654
+ /**
655
+ * Record that a scope launched under generation exited before its bootstrap started the target,
656
+ * so the next selection probes again. Launches from a superseded probe are ignored.
657
+ * @param generation - generation of the scope decision that launched it.
658
+ */
659
+ scopeNotStarted(generation) {
660
+ if (generation === this.generation) this.state = {
661
+ kind: "unprobed",
662
+ trigger: "scope-not-started"
663
+ };
664
+ }
665
+ probe(trigger) {
666
+ this.generation += 1;
667
+ const listener = this.observe.managerListener();
668
+ const launcher = this.observe.launcherIdentity();
669
+ const literalArgv = this.observe.probe();
670
+ if (literalArgv === void 0) {
671
+ this.state = {
672
+ kind: "unavailable",
673
+ listener
674
+ };
675
+ return {
676
+ kind: "fallback",
677
+ reason: "probe-unavailable",
678
+ probe: trigger
679
+ };
680
+ }
681
+ this.state = {
682
+ kind: "available",
683
+ literalArgv,
684
+ launcher
685
+ };
686
+ return {
687
+ kind: "scope",
688
+ reason: "probe-available",
689
+ literalArgv,
690
+ generation: this.generation,
691
+ probe: trigger
692
+ };
693
+ }
694
+ };
695
+ //#endregion
515
696
  //#region lib/types/windows-job.js
516
697
  /** Windows parent-side launch and ownership for the private Job runner. */
517
698
  function isWindowsStartCancellationError(error) {
@@ -1277,8 +1458,18 @@ var LocalSubprocessRuntime = class extends SubprocessRuntime {
1277
1458
  internals = {};
1278
1459
  /** Provider-lifetime latch suppressing repeated weaker-containment warnings. */
1279
1460
  fallbackWarningIssued = false;
1280
- /** Positive-only cache for the expensive Linux bootstrap and scope probe. */
1281
- linuxDeepProbePassed = false;
1461
+ /**
1462
+ * Linux native containment decisions. Each probe is a spawnSync that blocks the event loop and forks the
1463
+ * host, so the selector reruns it only after an event invalidates the previous verdict.
1464
+ */
1465
+ linuxScope = new LinuxScopeSelector({
1466
+ probe: () => probeLinuxNative(),
1467
+ managerListener: () => linuxManagerListener(),
1468
+ confirmManager: () => confirmLinuxManager(),
1469
+ launcherIdentity: () => systemdRunIdentity()
1470
+ });
1471
+ /** Scope decision of the launch in progress; selectContainmentMode sets it immediately before the launch reads it. */
1472
+ linuxScopeDecision;
1282
1473
  /** Test hook for platform process inspection; production resolves lazily on terminal spawn. */
1283
1474
  terminalInspector;
1284
1475
  constructor(ctx) {
@@ -1365,7 +1556,9 @@ var LocalSubprocessRuntime = class extends SubprocessRuntime {
1365
1556
  if (containmentMode === "fallback") handle = spawnSubprocess(spec, internals);
1366
1557
  else {
1367
1558
  const binding = prepareManagedProcessBinding(internals);
1368
- handle = bindManagedProcess(spec, containmentMode === "linux-scope" ? launchLinuxScope(spec, env) : launchWindowsJob(spec, env), binding);
1559
+ const linuxScope = containmentMode === "linux-scope" ? this.linuxScopeDecision : void 0;
1560
+ handle = bindManagedProcess(spec, linuxScope === void 0 ? launchWindowsJob(spec, env) : launchLinuxScope(spec, env, linuxScope.literalArgv), binding);
1561
+ if (linuxScope !== void 0) this.watchLinuxScopeStart(handle.done, linuxScope.generation);
1369
1562
  }
1370
1563
  this.live.add(handle);
1371
1564
  const control = handle.control;
@@ -1381,13 +1574,23 @@ var LocalSubprocessRuntime = class extends SubprocessRuntime {
1381
1574
  handle.done.then(release, release).catch(() => {});
1382
1575
  return handle;
1383
1576
  }
1577
+ /** Probe again before the next launch when a scope launched under generation never started its target. */
1578
+ watchLinuxScopeStart(done, generation) {
1579
+ done.catch((error) => {
1580
+ if (error instanceof LinuxScopeNotStartedError) this.linuxScope.scopeNotStarted(generation);
1581
+ });
1582
+ }
1384
1583
  selectContainmentMode(kind) {
1385
1584
  const platform = this.internals.platform ?? process.platform;
1386
1585
  let fallbackReason;
1387
1586
  if (platform === "linux") {
1388
- const available = this.linuxDeepProbePassed ? probeLinuxManager() : probeLinuxNative();
1389
- if (available) this.linuxDeepProbePassed = true;
1390
- if (available) return "linux-scope";
1587
+ const decision = this.linuxScope.select();
1588
+ const probe = decision.probe === void 0 ? "" : `, probed after ${decision.probe}`;
1589
+ this.ctx.logger.debug(`linux native containment: ${decision.kind} (${decision.reason}${probe})`);
1590
+ if (decision.kind === "scope") {
1591
+ this.linuxScopeDecision = decision;
1592
+ return "linux-scope";
1593
+ }
1391
1594
  fallbackReason = "the current user-systemd scope or private bootstrap is unavailable";
1392
1595
  }
1393
1596
  if (kind === "ordinary" && platform === "win32") {
@@ -1437,14 +1640,15 @@ var LocalSubprocessRuntime = class extends SubprocessRuntime {
1437
1640
  },
1438
1641
  ...platform === "win32" ? { useConptyDll: true } : {}
1439
1642
  };
1643
+ const linuxScope = containmentMode === "linux-scope" ? this.linuxScopeDecision : void 0;
1440
1644
  let scope;
1441
1645
  let terminal;
1442
1646
  try {
1443
- scope = containmentMode === "linux-scope" ? prepareLinuxTerminalScope(launch, {
1647
+ scope = linuxScope === void 0 ? void 0 : prepareLinuxTerminalScope(launch, {
1444
1648
  ...activity?.env ?? env,
1445
1649
  PWD: spec.cwd,
1446
1650
  TERM: spec.terminalType
1447
- }) : void 0;
1651
+ }, linuxScope.literalArgv);
1448
1652
  if (scope !== void 0) {
1449
1653
  options.cwd = scope.cwd;
1450
1654
  options.env = scope.env;
@@ -1466,6 +1670,7 @@ var LocalSubprocessRuntime = class extends SubprocessRuntime {
1466
1670
  this.terminals.delete(handle);
1467
1671
  }, spec.shellActivity === true);
1468
1672
  this.terminals.add(handle);
1673
+ if (linuxScope !== void 0) this.watchLinuxScopeStart(handle.done, linuxScope.generation);
1469
1674
  const release = async () => {
1470
1675
  directSettlement.resolve();
1471
1676
  if (spec.shellActivity === true) return;
@@ -30,8 +30,13 @@ export declare class LocalSubprocessRuntime extends SubprocessRuntime {
30
30
  internals: SpawnInternals;
31
31
  /** Provider-lifetime latch suppressing repeated weaker-containment warnings. */
32
32
  private fallbackWarningIssued;
33
- /** Positive-only cache for the expensive Linux bootstrap and scope probe. */
34
- private linuxDeepProbePassed;
33
+ /**
34
+ * Linux native containment decisions. Each probe is a spawnSync that blocks the event loop and forks the
35
+ * host, so the selector reruns it only after an event invalidates the previous verdict.
36
+ */
37
+ private readonly linuxScope;
38
+ /** Scope decision of the launch in progress; selectContainmentMode sets it immediately before the launch reads it. */
39
+ private linuxScopeDecision;
35
40
  /** Test hook for platform process inspection; production resolves lazily on terminal spawn. */
36
41
  terminalInspector: ProcessInspector | undefined;
37
42
  constructor(ctx: Context);
@@ -42,6 +47,8 @@ export declare class LocalSubprocessRuntime extends SubprocessRuntime {
42
47
  resolveExecutable(command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal): Promise<string>;
43
48
  private executableCandidates;
44
49
  spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
50
+ /** Probe again before the next launch when a scope launched under generation never started its target. */
51
+ private watchLinuxScopeStart;
45
52
  private selectContainmentMode;
46
53
  private warnFallback;
47
54
  /** @inheritdoc */
@@ -0,0 +1,60 @@
1
+ /** Linux native containment decisions over injected host observations. */
2
+ import type { LiteralArgvOptions } from './linux-scope.ts';
3
+ /** Host observations behind each decision; the runtime supplies the real checks and tests script them. */
4
+ export interface LinuxScopeObservations {
5
+ /** Run the deep transient-scope probe: literal-argv options when a scope can start, otherwise undefined. */
6
+ probe(): LiteralArgvOptions | undefined;
7
+ /** Identify the process listening on the user manager's private socket, or undefined when none listens. */
8
+ managerListener(): string | undefined;
9
+ /** Confirm the user manager before a launch under a positive verdict. */
10
+ confirmManager(): boolean;
11
+ /** Identify the systemd-run binary scope launches resolve, or undefined when none resolves. */
12
+ launcherIdentity(): string | undefined;
13
+ }
14
+ /** Event that made the selector probe: the first selection or one that invalidated the previous verdict. */
15
+ export type LinuxProbeTrigger = 'first' | 'scope-not-started' | 'new-listener' | 'launcher-changed';
16
+ /** One containment decision and the branch that produced it. */
17
+ export type LinuxScopeDecision = {
18
+ readonly kind: 'scope';
19
+ /** A probe found scope support, or a positive verdict confirmed the user manager. */
20
+ readonly reason: 'probe-available' | 'manager-confirmed';
21
+ readonly literalArgv: LiteralArgvOptions;
22
+ /** Probe count when the verdict was made; pass it to scopeNotStarted for a failed launch. */
23
+ readonly generation: number;
24
+ readonly probe: LinuxProbeTrigger | undefined;
25
+ } | {
26
+ readonly kind: 'fallback';
27
+ /**
28
+ * A probe found no scope support; a positive verdict failed to confirm the manager for this launch;
29
+ * or a negative verdict saw no listener or the listener present at the failed probe.
30
+ */
31
+ readonly reason: 'probe-unavailable' | 'manager-unconfirmed' | 'no-listener' | 'same-listener';
32
+ readonly probe: LinuxProbeTrigger | undefined;
33
+ };
34
+ /**
35
+ * Provider-lifetime Linux scope selection. A probe runs on the first selection and again only after
36
+ * a scope launch never started its target, a different user-manager listener appears under a
37
+ * negative verdict, or the systemd-run binary changes under a positive one. A positive verdict
38
+ * confirms the user manager on every selection. Selection is synchronous, so a decision stays
39
+ * valid for the launch that follows it.
40
+ */
41
+ export declare class LinuxScopeSelector {
42
+ private readonly observe;
43
+ private state;
44
+ private generation;
45
+ /** @param observe - host checks consulted by each selection. */
46
+ constructor(observe: LinuxScopeObservations);
47
+ /**
48
+ * Choose containment for one eligible launch.
49
+ * @returns the decision and the branch that produced it.
50
+ */
51
+ select(): LinuxScopeDecision;
52
+ /**
53
+ * Record that a scope launched under generation exited before its bootstrap started the target,
54
+ * so the next selection probes again. Launches from a superseded probe are ignored.
55
+ * @param generation - generation of the scope decision that launched it.
56
+ */
57
+ scopeNotStarted(generation: number): void;
58
+ private probe;
59
+ }
60
+ //# sourceMappingURL=linux-scope-selector.d.ts.map
@@ -16,6 +16,8 @@ export interface LinuxScopeInternals {
16
16
  runnerAvailable?: (invocation: RunnerInvocation) => boolean;
17
17
  loadLinuxExecve?: typeof loadLinuxExecve;
18
18
  sleep?: (delayMs: number, signal?: AbortSignal) => Promise<void>;
19
+ /** Kernel unix socket table read by the user-manager confirmation; defaults to /proc/net/unix. */
20
+ unixSocketTable?: string;
19
21
  }
20
22
  interface SystemctlResult {
21
23
  status: number | null;
@@ -23,6 +25,8 @@ interface SystemctlResult {
23
25
  stderr: string;
24
26
  error?: Error;
25
27
  }
28
+ /** systemd-run options that keep target argv literal; empty where scope mode never expands argv. */
29
+ export type LiteralArgvOptions = readonly string[];
26
30
  /**
27
31
  * Confirm this exact runner entry and libc execve binding without a probe mode.
28
32
  * @param internals - optional runner and libc-binding seams used by tests.
@@ -32,21 +36,44 @@ export declare function probeLinuxBootstrap(internals?: LinuxScopeInternals): bo
32
36
  /**
33
37
  * Confirm current literal-argv transient-scope support before selecting native launch.
34
38
  * @param internals - optional systemd command seams used by tests.
35
- * @returns whether the current user manager supports the required scope invocation.
39
+ * @returns the options that keep target argv literal, or undefined when the user manager cannot create the scope.
36
40
  */
37
- export declare function probeLinuxScope(internals?: LinuxScopeInternals): boolean;
41
+ export declare function probeLinuxScope(internals?: LinuxScopeInternals): LiteralArgvOptions | undefined;
38
42
  /**
39
- * Confirm that the current user manager remains reachable after a positive deep probe.
43
+ * Check every Linux native prerequisite on each call.
44
+ * @param internals - optional native capability seams used by tests.
45
+ * @returns the literal-argv scope options when Linux native containment is available, otherwise undefined.
46
+ */
47
+ export declare function probeLinuxNative(internals?: LinuxScopeInternals): LiteralArgvOptions | undefined;
48
+ /**
49
+ * Query the user manager once through systemctl.
40
50
  * @param internals - optional systemctl seam used by tests.
41
- * @returns whether one lightweight manager query succeeds.
51
+ * @returns whether the query succeeds.
42
52
  */
43
53
  export declare function probeLinuxManager(internals?: LinuxScopeInternals): boolean;
44
54
  /**
45
- * Re-check every Linux native prerequisite for one eligible spawn.
46
- * @param internals - optional native capability seams used by tests.
47
- * @returns whether the Linux native containment path is currently available.
55
+ * Identify the user manager's listener on `$XDG_RUNTIME_DIR/systemd/private`, the socket
56
+ * `systemd-run --user` connects through.
57
+ * @param internals - optional socket-table seam used by tests.
58
+ * @returns the listening socket's inode from the kernel socket table, or undefined when
59
+ * XDG_RUNTIME_DIR is unset or nothing in this network namespace listens there.
48
60
  */
49
- export declare function probeLinuxNative(internals?: LinuxScopeInternals): boolean;
61
+ export declare function linuxManagerListener(internals?: LinuxScopeInternals): string | undefined;
62
+ /**
63
+ * Identify the systemd-run binary that scope launches resolve through PATH, so a package upgrade
64
+ * that replaces it triggers another probe of its literal-argv options.
65
+ * @param internals - optional systemd-run seam used by tests.
66
+ * @returns the device, inode, size, and modification time of the first match, or undefined when none resolves.
67
+ */
68
+ export declare function systemdRunIdentity(internals?: LinuxScopeInternals): string | undefined;
69
+ /**
70
+ * Confirm the user manager before a scope launch. A listener on its private socket confirms it
71
+ * without a child process; otherwise one systemctl query decides. A hung manager that still
72
+ * listens passes, and its scope launch fails with LinuxScopeNotStartedError.
73
+ * @param internals - optional socket-table and systemctl seams used by tests.
74
+ * @returns whether a scope launch can reach the user manager.
75
+ */
76
+ export declare function confirmLinuxManager(internals?: LinuxScopeInternals): boolean;
50
77
  interface DirectRange {
51
78
  running(): boolean;
52
79
  /** True for group TERM delivery, direct signal submission, or proven direct-PID absence. */
@@ -54,6 +81,10 @@ interface DirectRange {
54
81
  /** Direct exit/error settlement, independent of output drain and managed-range completion. */
55
82
  settled: Promise<unknown>;
56
83
  }
84
+ /** A scope launch whose bootstrap never consumed its launch request, so its target never ran. */
85
+ export declare class LinuxScopeNotStartedError extends Error {
86
+ readonly name = "LinuxScopeNotStartedError";
87
+ }
57
88
  /**
58
89
  * Send a direct-process signal, distinguishing an absent PID from failed delivery.
59
90
  * @param pid - owned direct-process identity whose exit notification can still be pending.
@@ -75,17 +106,19 @@ export interface LinuxTerminalScopeLaunch {
75
106
  * Prepare one Linux PTY scope using the same launch request and bootstrap core.
76
107
  * @param spec - terminal target request.
77
108
  * @param targetEnv - validated complete target environment.
109
+ * @param literalArgv - options from the provider's positive scope probe.
78
110
  * @param internals - optional runner and systemd seams used by tests.
79
111
  * @returns invocation and ownership callbacks; requested termination preserves the observed signal even before bootstrap consumption.
80
112
  */
81
- export declare function prepareLinuxTerminalScope(spec: SubprocessTerminalSpawnSpec, targetEnv: Record<string, string>, internals?: LinuxScopeInternals): LinuxTerminalScopeLaunch;
113
+ export declare function prepareLinuxTerminalScope(spec: SubprocessTerminalSpawnSpec, targetEnv: Record<string, string>, literalArgv: LiteralArgvOptions, internals?: LinuxScopeInternals): LinuxTerminalScopeLaunch;
82
114
  /**
83
115
  * Launch one ordinary target inside a transient user scope.
84
116
  * @param spec - ordinary target request.
85
117
  * @param targetEnv - validated complete target environment.
118
+ * @param literalArgv - options from the provider's positive scope probe.
86
119
  * @param internals - optional runner and systemd seams used by tests.
87
120
  * @returns streams, result, and scope owner; requested termination preserves the observed signal even before bootstrap consumption.
88
121
  */
89
- export declare function launchLinuxScope(spec: SubprocessSpawnSpec, targetEnv: Record<string, string>, internals?: LinuxScopeInternals): ManagedProcessLaunch;
122
+ export declare function launchLinuxScope(spec: SubprocessSpawnSpec, targetEnv: Record<string, string>, literalArgv: LiteralArgvOptions, internals?: LinuxScopeInternals): ManagedProcessLaunch;
90
123
  export {};
91
124
  //# sourceMappingURL=linux-scope.d.ts.map
package/locale/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "meta": {
3
+ "title": "subprocess-local"
4
+ }
5
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "本地子进程",
4
+ "description": "为其他插件提供本地进程的启动和管理能力。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qlzt/dsh-subprocess-local",
3
3
  "description": "Local-subprocess implementation of the DeepSeek Harness subprocess seam",
4
- "version": "0.2.1-alpha.3",
4
+ "version": "0.2.1-rc.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,9 +27,11 @@
27
27
  "./output": {
28
28
  "types": "./lib/types/output.d.ts",
29
29
  "default": "./lib/output.js"
30
- }
30
+ },
31
+ "./locale/*.json": "./locale/*.json"
31
32
  },
32
33
  "files": [
34
+ "locale/*.json",
33
35
  "lib/index.js",
34
36
  "lib/runner.js",
35
37
  "lib/runner-*.js",
@@ -39,21 +41,21 @@
39
41
  ],
40
42
  "license": "MIT",
41
43
  "peerDependencies": {
42
- "@qlzt/dsh-subprocess": "0.2.1-alpha.3",
43
- "@qlzt/dsh-timeout": "0.2.1-alpha.3",
44
- "@deepseek-ai/cordis": "~4.0.5-alpha.1"
44
+ "@qlzt/dsh-subprocess": "0.2.1-rc.2",
45
+ "@qlzt/dsh-timeout": "0.2.1-rc.2",
46
+ "@deepseek-ai/cordis": "~4.0.5"
45
47
  },
46
48
  "dependencies": {
47
- "@qlzt/dsh-lazy-require": "0.2.1-alpha.3",
48
- "@qlzt/dsh-win32-process": "0.2.1-alpha.3",
49
+ "@qlzt/dsh-lazy-require": "0.2.1-rc.2",
50
+ "@qlzt/dsh-win32-process": "0.2.1-rc.2",
49
51
  "koffi": "3.1.1",
50
52
  "node-pty": "1.2.0-beta.15"
51
53
  },
52
54
  "devDependencies": {
53
- "@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.3",
54
- "@deepseek-ai/dsh-subprocess": "0.2.1-alpha.3",
55
- "@deepseek-ai/dsh-timeout": "0.2.1-alpha.3",
56
- "@deepseek-ai/cordis": "~4.0.5-alpha.1"
55
+ "@deepseek-ai/dsh-loader-smoke": "0.2.1-rc.2",
56
+ "@deepseek-ai/dsh-subprocess": "0.2.1-rc.2",
57
+ "@deepseek-ai/dsh-timeout": "0.2.1-rc.2",
58
+ "@deepseek-ai/cordis": "~4.0.5"
57
59
  },
58
60
  "scripts": {
59
61
  "postinstall": "node scripts/ensure-spawn-helper.mjs"