@deepseek-ai/dsh-win32-process 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 CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/subprocess/win32-process/README.md
5
- README.md: 7a4d12f7fe6ef553113e0f202886fd42e8a313a2
6
- README.zh.md: 8ebc614d38ea206cba792b90a2f118c028bf0447
5
+ README.md: e3009bb207e0436b4d4113d7bf1ea677ab191abc
6
+ README.zh.md: 65f834b447a2d9f4db94130fe3f7070456aab351
package/README.md CHANGED
@@ -34,6 +34,8 @@ This low-level Win32 process library is consumed by the Windows ACL sandbox and
34
34
 
35
35
  The Windows ACL sandbox adds SID, DACL, grant, workspace, and public child policy above these primitives.
36
36
 
37
+ - **Inherited control descriptor** — Job creation accepts an optional fd-7 pipe. `STARTUPINFO.cbReserved2/lpReserved2` carries an eight-slot CRT descriptor table with standard handles, closed slots 3–6, and the control pipe at slot 7. The table is allocated until CreateProcess returns; temporary handle inheritance is reset on success and failure. Initializing the slot before Node starts avoids overwriting descriptors Node has already allocated.
38
+
37
39
  <a id="header-verification"></a>
38
40
  ## Header verification
39
41
 
package/README.zh.md CHANGED
@@ -9,33 +9,35 @@ kind: "package-library"
9
9
 
10
10
  ## 概述
11
11
 
12
- 供 Windows ACL 沙箱与普通子进程 Job runner 消费的底层 Win32 进程库。它唯一拥有仓库中可复用 process、stdio 与 Job Object 操作的 Koffi 绑定表;它不是 Cordis 服务,也不决定沙箱策略或公共 child 行为。维护任一原生进程路径或检查 handle 生命周期限制时,请阅读本页。
12
+ 供 Windows ACL 沙箱与普通子进程 Job runner 消费的底层 Win32 进程库。它唯一拥有仓库中可复用 process、stdio 与 Job Object 操作的 Koffi 绑定表;它不是 Cordis 服务,也不决定沙箱策略或公共 child 行为。维护任一原生进程路径或检查句柄生命周期限制时,请阅读本页。
13
13
 
14
14
  ## 目录
15
15
 
16
- - [Behavior](#behavior)
17
- - [头部验证](#header-verification)
18
- - [Model Experience](#model-experience)
19
- - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
16
+ - [行为](#behavior)
17
+ - [头文件验证](#header-verification)
18
+ - [模型体验](#model-experience)
19
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
20
20
  - [开发备注](#dev-note)
21
21
 
22
22
  -----
23
23
 
24
24
  <a id="behavior"></a>
25
- ## Behavior
25
+ ## 行为
26
26
 
27
- - **唯一可复用 ABI owner** — `abi.ts` 拥有两条 process 路径消费的 Win32 常量与 x64 布局值。`ffi.ts` 懒加载 `kernel32.dll` 与 `advapi32.dll`,核验 `STARTUPINFOW` 和 `PROCESS_INFORMATION`,提供带类型的操作与错误格式化,并让 sandbox policy 通过同一组已加载库绑定剩余 API。
28
- - **restricted-token 创建** — `RestrictedProcessSpawnOptions` 要求 sandbox 的 primary token,并使用 `CreateProcessAsUserW`。pipe 与 inherited-stdio 路径共用命令行引用、cwd、restricted-token 空环境策略、返回值检查与句柄清理。
27
+ - **唯一可复用 ABI owner** — `abi.ts` 拥有两条 process 路径消费的 Win32 常量与 x64 布局值。`ffi.ts` 懒加载 `kernel32.dll` 与 `advapi32.dll`,核验 `STARTUPINFOW` 和 `PROCESS_INFORMATION`,提供带类型的操作与错误格式化,并让沙箱策略通过同一组已加载库绑定剩余 API。
28
+ - **restricted-token 创建** — `RestrictedProcessSpawnOptions` 要求沙箱的 primary token,并使用 `CreateProcessAsUserW`。pipe 与 inherited-stdio 路径共用命令行引号处理、cwd、restricted-token null 环境策略、返回值检查与句柄清理。
29
29
  - **管道进程原语** — `spawnPipedProcess()` 创建匿名 stdin/stdout/stderr 管道,立即关闭 stdin,并返回两个读取端;调用方负责等待进程与排空管道。任一局部失败都会关闭该操作已经拥有的句柄,并在各自 Win32 生命周期结束后释放每个 Koffi 输出槽与结构体分配。
30
30
  - **继承 stdio 的 Job 原语** — `spawnInheritedJobProcess()` 创建一个 kill-on-close Job,临时把当前 stdio 句柄设为可继承,以 suspended 状态创建 restricted child,把它分配给 Job,再恢复初始线程。目标代码不会在 Job 分配前运行;受控的分配或恢复失败会终止 suspended child,或在释放全部已拥有句柄前关闭已分配的 Job。
31
- - **ordinary Job runner 原语** — `CurrentTokenProcessSpawnOptions` 要求已解析的 `applicationName`、完整 target 环境,以及三个专用于 target stdin、stdout 与 stderr 的 runner CRT 描述符。`spawnCurrentTokenJobProcess()` 通过 Node 导出的 `uv_get_osfhandle()` 把这些描述符映射为 OS handle,拒绝无效结果,临时把 handle 设为可继承,并通过 `STARTF_USESTDHANDLES` 传入。它使用 `CREATE_UNICODE_ENVIRONMENT` 传入排序后的 UTF-16LE 环境块,再以 suspended 状态通过 `CreateProcessW` 创建 target、把它分配给 unnamed kill-on-close Job,并只在分配后恢复。原始命令行 argv 项保持不变,runner 也可以关闭自己的 carrier 描述符,而不触碰 Node 自身的标准流。
32
- - **ordinary 停稳操作** — `pollProcessExit()` 单独发布 direct exit,`isJobEmpty()` 则读取 `QueryInformationJobObject(JobObjectBasicAccountingInformation)`,直到 `ActiveProcesses` 归零。带检查的 Job 终止与 handle 关闭使 runner 保持唯一 native owner。
33
- - **显式结算归属** — `waitForProcessExit()` 等待并关闭 sandbox process handle;ordinary runner 的 process polling、Job accounting 与 checked Job termination/closure 是独立操作。`drainPipe()` 在排空期间复用一个 native count slot,释放该分配并关闭管道读取句柄。每个调用方拥有自己的 result 组合与返回 handle。
31
+ - **ordinary Job runner 原语** — `CurrentTokenProcessSpawnOptions` 要求已解析的 `applicationName`、完整 target 环境,以及三个专用于 target stdin、stdout 与 stderr 的 runner CRT 描述符。`spawnCurrentTokenJobProcess()` 通过 Node 导出的 `uv_get_osfhandle()` 把这些描述符映射为 OS 句柄,拒绝无效结果,临时把句柄设为可继承,并通过 `STARTF_USESTDHANDLES` 传入。它使用 `CREATE_UNICODE_ENVIRONMENT` 传入排序后的 UTF-16LE 环境块,再以 suspended 状态通过 `CreateProcessW` 创建 target、把它分配给 unnamed kill-on-close Job,并只在分配后恢复。原始命令行 argv 项保持不变,runner 也可以关闭自己的 carrier 描述符,而不触碰 Node 自身的标准流。
32
+ - **ordinary 结算操作** — `pollProcessExit()` 单独发布 direct exit,`isJobEmpty()` 则读取 `QueryInformationJobObject(JobObjectBasicAccountingInformation)`,直到 `ActiveProcesses` 归零。带检查的 Job 终止与句柄关闭使 runner 保持唯一 native owner。
33
+ - **显式结算归属** — `waitForProcessExit()` 等待并关闭沙箱 process 句柄;ordinary runner 的 process polling、Job accounting 与 checked Job termination/closure 是独立操作。`drainPipe()` 在排空期间复用一个 native count slot,释放该分配并关闭管道读取句柄。每个调用方拥有自己的 result 组合与返回句柄。
34
34
 
35
- Windows ACL 沙箱在这些原语上增加 SID、DACL、grant、workspace 与公共 child policy。
35
+ Windows ACL 沙箱在这些原语上增加 SID、DACL、grant、workspace 与公共 child 策略。
36
+
37
+ - **继承控制描述符**——Job 创建接受可选的 fd-7 管道。`STARTUPINFO.cbReserved2/lpReserved2` 携带八槽 CRT 描述符表,其中包含标准句柄、关闭的槽 3–6,以及槽 7 的控制管道。该表保留到 CreateProcess 返回;临时句柄继承在成功和失败时均恢复。在 Node 启动前初始化该槽可避免覆盖 Node 已分配的描述符。
36
38
 
37
39
  <a id="header-verification"></a>
38
- ## 头部验证
40
+ ## 头文件验证
39
41
 
40
42
  process、stdio 与 Job 的常量以及选定结构体的大小和偏移由 [`verify/abi-probe.cpp`](verify/abi-probe.cpp) 对照 MinGW Windows 头文件检查:
41
43
 
@@ -43,16 +45,16 @@ process、stdio 与 Job 的常量以及选定结构体的大小和偏移由 [`ve
43
45
  g++ -std=c++20 -municode -O2 -o abi-probe.exe verify/abi-probe.cpp && ./abi-probe.exe
44
46
  ```
45
47
 
46
- Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载时断言各自的 64 位大小。该探针还固定指针与 handle 宽度、Unicode 环境标志,以及用于判断停稳的基础 Job accounting record 大小与 `ActiveProcesses` 偏移;其余已记录偏移和常量也由该探针提供证据。
48
+ Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载时断言各自的 64 位大小。该探针还固定指针与句柄宽度、Unicode 环境标志,以及用于判断完全停稳的基础 Job accounting record 大小与 `ActiveProcesses` 偏移;其余已记录偏移和常量也由该探针提供证据。
47
49
 
48
50
  <a id="model-experience"></a>
49
- ## Model Experience
51
+ ## 模型体验
50
52
 
51
53
  ### 进程原语
52
54
 
53
55
  #### 模型看到什么
54
56
 
55
- 没有直接内容。本包向 sandbox 与 ordinary runner 提供 `Win32ProcessBindings`、`CurrentTokenProcessBindings` 与进程原语;两者拥有全部模型可见工具、输出与诊断,本包不贡献提示词或工具 schema。
57
+ 没有直接内容。本包向沙箱与 ordinary runner 提供 `Win32ProcessBindings`、`CurrentTokenProcessBindings` 与进程原语;两者拥有全部模型可见工具、输出与诊断,本包不贡献提示词或工具 schema。
56
58
 
57
59
  #### Token 影响
58
60
 
@@ -62,14 +64,14 @@ Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载
62
64
 
63
65
  本包不贡献稳定请求前缀,因此不会使模型 KV Cache 失效。
64
66
 
65
- ## Known Limitations and Deferred Work
67
+ ## 已知限制与延期工作
66
68
 
67
69
  <a id="known-limitations-and-deferred-work"></a>
68
70
 
69
71
  - **仅在 Windows 原生加载** — 导入通用类型可跨平台进行,但解析绑定表会加载 Windows DLL,并在其他宿主失败。跨平台测试注入绑定表,不加载原生 API。
70
- - **没有公共进程服务** — 本包刻意不把原语包装成 Cordis 或 Node streams。消费方必须拥有自己的策略、异步调度、输出上限、取消与最终句柄关闭。
71
- - **restricted-token 空环境** — `CreateProcessAsUserW` sandbox 原语传入空环境块,并先通过 `SetEnvironmentVariableW` 建立改动,因为经 Koffi 传入显式环境块会以 `ERROR_INVALID_PARAMETER` 失败。ordinary `CreateProcessW` runner 则要求完整 target 环境,并传入排序、双 NUL 结尾的 UTF-16LE 块,其中包括 `=X:` 驱动器条目,而不修改自身环境。
72
- - **没有 standalone process API** — 本包只暴露当前 sandbox 与 ordinary-runner consumer 所需的操作,不拥有 Node streams、公共 handle、output policy、cancellation 或 durable state。
72
+ - **没有公共进程服务** — 本包刻意不把原语包装成 Cordis 或 Node 流。消费方必须拥有自己的策略、异步调度、输出上限、取消与最终句柄关闭。
73
+ - **restricted-token null 环境** — `CreateProcessAsUserW` 沙箱原语传入 null 环境块,并先通过 `SetEnvironmentVariableW` 建立改动,因为经 Koffi 传入显式环境块会以 `ERROR_INVALID_PARAMETER` 失败。ordinary `CreateProcessW` runner 则要求完整 target 环境,并传入排序、双 NUL 结尾的 UTF-16LE 块,其中包括 `=X:` 驱动器条目,而不修改自身环境。
74
+ - **没有 standalone process API** — 本包只暴露当前沙箱与 ordinary-runner 消费方所需的操作,不拥有 Node 流、公共句柄、输出策略、取消或 durable state。
73
75
  - **创建到分配之间的中断** — 目标以 suspended 状态启动,不能在 Job 分配前执行,但 runner 若在进程创建到分配之间的极窄区间被外力终止,可能留下 suspended target。本包不声明原子 Job 附加保证。
74
76
  - **header 证据限定架构** — 已提交的 ABI probe 与布局常量覆盖仓库当前 64 位 Windows 目标。支持新的指针宽度或不兼容 Windows ABI 前,必须先更新 probe。
75
77
 
@@ -84,4 +86,4 @@ Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载
84
86
 
85
87
  </details>
86
88
 
87
- **运行时不变式:** 不发布伴生入口。操作只持有调用内的 native handle。
89
+ **运行时不变式:** 不发布伴生入口。操作只持有调用内的原生句柄。
package/lib/index.js CHANGED
@@ -146,6 +146,7 @@ function bindings() {
146
146
  cached = {
147
147
  closeHandle: bind(kernel32, "CloseHandle", "int", [PVOID]),
148
148
  getLastError: bind(kernel32, "GetLastError", "uint32", []),
149
+ getFileType: bind(kernel32, "GetFileType", "uint32", [PVOID]),
149
150
  formatMessageW: bind(kernel32, "FormatMessageW", "uint32", [
150
151
  "uint32",
151
152
  PVOID,
@@ -284,6 +285,44 @@ function throwWin32(api, name, win32Code, detail) {
284
285
  throw new Win32Error(name, win32Code, detail ?? errorText(api, win32Code));
285
286
  }
286
287
  //#endregion
288
+ //#region lib/types/control-stdio.js
289
+ /** Windows CRT startup descriptors for a Node payload with one inherited control pipe. */
290
+ const HANDLE_BYTES = 8;
291
+ const INVALID_HANDLE = 18446744073709551615n;
292
+ const FOPEN = 1;
293
+ const FPIPE = 8;
294
+ const FDEV = 64;
295
+ const FILE_TYPE_CHAR = 2;
296
+ const FILE_TYPE_PIPE = 3;
297
+ /**
298
+ * Encode the CRT's descriptor table before the child runtime allocates descriptors.
299
+ * Supported Windows targets use 64-bit handles. Empty slots remain closed, and the
300
+ * backing Buffer must remain alive until CreateProcess returns.
301
+ * @param api - native file-type inspection for inherited handles.
302
+ * @param stdio - standard handles and the provider-owned fd-7 control pipe.
303
+ * @returns descriptor count, flag bytes, and handle values for STARTUPINFO's reserved CRT fields.
304
+ */
305
+ function inheritedControlStdio(api, stdio) {
306
+ const count = stdio.control.fileDescriptor + 1;
307
+ const handleOffset = 4 + count;
308
+ const bytes = Buffer.alloc(handleOffset + count * HANDLE_BYTES);
309
+ bytes.writeUInt32LE(count, 0);
310
+ for (let index = 0; index < count; index++) bytes.writeBigUInt64LE(INVALID_HANDLE, handleOffset + index * HANDLE_BYTES);
311
+ const entries = [
312
+ [0, stdio.stdin],
313
+ [1, stdio.stdout],
314
+ [2, stdio.stderr],
315
+ [stdio.control.fileDescriptor, stdio.control.handle]
316
+ ];
317
+ for (const [fd, handle] of entries) {
318
+ const kind = api.getFileType(handle);
319
+ if (fd === stdio.control.fileDescriptor && kind !== FILE_TYPE_PIPE) throw new Error("subprocess control descriptor is not a Windows pipe");
320
+ bytes[4 + fd] = FOPEN | (kind === FILE_TYPE_PIPE ? FPIPE : kind === FILE_TYPE_CHAR ? FDEV : 0);
321
+ bytes.writeBigUInt64LE(handle, handleOffset + fd * HANDLE_BYTES);
322
+ }
323
+ return bytes;
324
+ }
325
+ //#endregion
287
326
  //#region lib/types/process.js
288
327
  /** Typed Win32 process operations over the shared binding table. */
289
328
  /**
@@ -488,7 +527,7 @@ function createKillOnCloseJob(api) {
488
527
  }
489
528
  const UV_INVALID_OS_FILE_HANDLE = 18446744073709551615n;
490
529
  const UV_INVALID_FILE_DESCRIPTOR = 18446744073709551614n;
491
- function inheritedStandardHandles(api) {
530
+ function inheritedStandardHandles(api, controlFileDescriptor) {
492
531
  const get = (selector, label) => {
493
532
  const handle = api.getStdHandle(selector);
494
533
  if (!isNullPtr(handle)) return handle;
@@ -497,19 +536,27 @@ function inheritedStandardHandles(api) {
497
536
  return {
498
537
  stdin: get(-10, "stdin"),
499
538
  stdout: get(-11, "stdout"),
500
- stderr: get(-12, "stderr")
539
+ stderr: get(-12, "stderr"),
540
+ ...controlFileDescriptor === void 0 ? {} : { control: {
541
+ fileDescriptor: controlFileDescriptor,
542
+ handle: descriptorHandle(api, controlFileDescriptor, "control")
543
+ } }
501
544
  };
502
545
  }
546
+ function descriptorHandle(api, fileDescriptor, label) {
547
+ const handle = api.uvGetOsfhandle(fileDescriptor);
548
+ if (isNullPtr(handle) || handle === UV_INVALID_OS_FILE_HANDLE || handle === UV_INVALID_FILE_DESCRIPTOR) throw new Error(`uv_get_osfhandle returned an invalid handle for target ${label} fd ${String(fileDescriptor)}`);
549
+ return handle;
550
+ }
503
551
  function targetCarrierHandles(api, descriptors) {
504
- const get = (fileDescriptor, label) => {
505
- const handle = api.uvGetOsfhandle(fileDescriptor);
506
- if (isNullPtr(handle) || handle === UV_INVALID_OS_FILE_HANDLE || handle === UV_INVALID_FILE_DESCRIPTOR) throw new Error(`uv_get_osfhandle returned an invalid handle for target ${label} fd ${String(fileDescriptor)}`);
507
- return handle;
508
- };
509
552
  return {
510
- stdin: get(descriptors.stdin, "stdin"),
511
- stdout: get(descriptors.stdout, "stdout"),
512
- stderr: get(descriptors.stderr, "stderr")
553
+ stdin: descriptorHandle(api, descriptors.stdin, "stdin"),
554
+ stdout: descriptorHandle(api, descriptors.stdout, "stdout"),
555
+ stderr: descriptorHandle(api, descriptors.stderr, "stderr"),
556
+ ...descriptors.control === void 0 ? {} : { control: {
557
+ fileDescriptor: descriptors.control,
558
+ handle: descriptorHandle(api, descriptors.control, "control")
559
+ } }
513
560
  };
514
561
  }
515
562
  /** Shared suspended-create, Job-assignment, and resume lifecycle. */
@@ -518,25 +565,43 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
518
565
  const enabled = [];
519
566
  let startupInfo;
520
567
  let processInfo;
568
+ let controlDescriptorBlock;
521
569
  let created = 0;
522
570
  let createFailureCode = 0;
523
571
  try {
524
572
  const stdio = resolveStdio();
525
- for (const [handle, label] of [
573
+ const inherited = [
526
574
  [stdio.stdin, "stdin"],
527
575
  [stdio.stdout, "stdout"],
528
576
  [stdio.stderr, "stderr"]
529
- ]) {
577
+ ];
578
+ if (stdio.control !== void 0) inherited.push([stdio.control.handle, "control"]);
579
+ for (const [handle, label] of inherited) {
530
580
  if (api.setHandleInformation(handle, 1, 1) === 0) throwLastError(api, "SetHandleInformation", `${label} (enable inherit)`);
531
581
  enabled.push(handle);
532
582
  }
583
+ const controlBytes = stdio.control === void 0 ? void 0 : inheritedControlStdio(api, {
584
+ ...stdio,
585
+ control: stdio.control
586
+ });
587
+ if (controlBytes !== void 0) {
588
+ controlDescriptorBlock = {
589
+ pointer: koffi.alloc("uint8", controlBytes.length),
590
+ length: controlBytes.length
591
+ };
592
+ koffi.encode(controlDescriptorBlock.pointer, "uint8", controlBytes, controlBytes.length);
593
+ }
533
594
  startupInfo = allocStartupInfo();
534
595
  encodeStartupInfo(startupInfo, {
535
596
  cb: 104,
536
597
  dwFlags: 256,
537
598
  hStdInput: stdio.stdin,
538
599
  hStdOutput: stdio.stdout,
539
- hStdError: stdio.stderr
600
+ hStdError: stdio.stderr,
601
+ ...controlDescriptorBlock === void 0 ? {} : {
602
+ cbReserved2: controlDescriptorBlock.length,
603
+ lpReserved2: controlDescriptorBlock.pointer
604
+ }
540
605
  });
541
606
  processInfo = allocProcessInfo();
542
607
  created = create(startupInfo, processInfo);
@@ -547,6 +612,7 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
547
612
  throw error;
548
613
  } finally {
549
614
  freeNative(startupInfo);
615
+ freeNative(controlDescriptorBlock?.pointer);
550
616
  for (const handle of enabled) api.setHandleInformation(handle, 1, 0);
551
617
  }
552
618
  if (created === 0) {
@@ -601,7 +667,7 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
601
667
  */
602
668
  function spawnInheritedJobProcess(api, options) {
603
669
  const commandLine = buildCommandLine(options.command, options.args);
604
- return spawnJobProcess(api, options, () => inheritedStandardHandles(api), "CreateProcessAsUserW", (startupInfo, processInfo) => createRestrictedProcess(api, options, commandLine, 4, startupInfo, processInfo));
670
+ return spawnJobProcess(api, options, () => inheritedStandardHandles(api, options.controlFileDescriptor), "CreateProcessAsUserW", (startupInfo, processInfo) => createRestrictedProcess(api, options, commandLine, 4, startupInfo, processInfo));
605
671
  }
606
672
  /**
607
673
  * Spawn an ordinary process suspended, assign its Job, then resume it.
@@ -0,0 +1,22 @@
1
+ /** Windows CRT startup descriptors for a Node payload with one inherited control pipe. */
2
+ import type { NativePtr, Win32ProcessBindings } from './ffi.ts';
3
+ /** Native stdio handles plus the single additional descriptor selected by the launcher. */
4
+ export interface InheritedControlStdio {
5
+ stdin: NativePtr;
6
+ stdout: NativePtr;
7
+ stderr: NativePtr;
8
+ control: {
9
+ fileDescriptor: 7;
10
+ handle: NativePtr;
11
+ };
12
+ }
13
+ /**
14
+ * Encode the CRT's descriptor table before the child runtime allocates descriptors.
15
+ * Supported Windows targets use 64-bit handles. Empty slots remain closed, and the
16
+ * backing Buffer must remain alive until CreateProcess returns.
17
+ * @param api - native file-type inspection for inherited handles.
18
+ * @param stdio - standard handles and the provider-owned fd-7 control pipe.
19
+ * @returns descriptor count, flag bytes, and handle values for STARTUPINFO's reserved CRT fields.
20
+ */
21
+ export declare function inheritedControlStdio(api: Pick<Win32ProcessBindings, 'getFileType'>, stdio: InheritedControlStdio): Buffer;
22
+ //# sourceMappingURL=control-stdio.d.ts.map
@@ -28,6 +28,8 @@ export interface StartupInfoInput {
28
28
  hStdInput: NativePtr;
29
29
  hStdOutput: NativePtr;
30
30
  hStdError: NativePtr;
31
+ cbReserved2?: number;
32
+ lpReserved2?: NativePtr;
31
33
  }
32
34
  /** Decoded PROCESS_INFORMATION result. */
33
35
  export interface ProcessInfoOutput {
@@ -40,6 +42,8 @@ export interface ProcessInfoOutput {
40
42
  export interface Win32ProcessBindings {
41
43
  closeHandle(handle: NativePtr): number;
42
44
  getLastError(): number;
45
+ getFileType(handle: NativePtr): number;
46
+ uvGetOsfhandle(fileDescriptor: number): NativePtr | null;
43
47
  formatMessageW(flags: number, source: null, messageId: number, languageId: number, buffer: Buffer, size: number, args: null): number;
44
48
  createPipe(readHandle: NativePtr, writeHandle: NativePtr, attributes: null, size: number): number;
45
49
  setHandleInformation(handle: NativePtr, mask: number, flags: number): number;
@@ -35,11 +35,15 @@ export interface CurrentTokenStdioFileDescriptors {
35
35
  stdin: number;
36
36
  stdout: number;
37
37
  stderr: number;
38
+ /** Optional carrier and target descriptor for the inherited control pipe. */
39
+ control?: 7;
38
40
  }
39
41
  /** Restricted-token process creation inputs owned by the Windows ACL sandbox. */
40
42
  export interface RestrictedProcessSpawnOptions extends ProcessSpawnOptions {
41
43
  /** Restricted primary token supplied by sandbox policy. */
42
44
  token: NativePtr;
45
+ /** Optional control pipe inherited at the same descriptor in the payload. */
46
+ controlFileDescriptor?: 7;
43
47
  }
44
48
  /** Piped child resources whose process and read handles remain caller-owned. */
45
49
  export interface SpawnedPipedProcess {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-win32-process",
3
3
  "description": "Shared low-level Win32 process, stdio, and Job Object primitives",
4
- "version": "0.1.5-rc.2",
4
+ "version": "0.1.6-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },