@deepseek-ai/dsh-win32-process 0.1.5-rc.2 → 0.1.6-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.
- package/README.i18n.yaml +2 -2
- package/README.md +4 -0
- package/README.zh.md +25 -21
- package/lib/index.js +153 -64
- package/lib/types/abi.d.ts +4 -0
- package/lib/types/control-stdio.d.ts +22 -0
- package/lib/types/ffi.d.ts +20 -9
- package/lib/types/koffi.d.ts +49 -0
- package/lib/types/process.d.ts +7 -2
- package/package.json +3 -2
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:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 155ca3807791a83fef437af67312b3480ce7d2a2
|
|
6
|
+
README.zh.md: f0c4439af27ef45c88d73bcba54851d789da063d
|
package/README.md
CHANGED
|
@@ -32,8 +32,12 @@ This low-level Win32 process library is consumed by the Windows ACL sandbox and
|
|
|
32
32
|
- **Ordinary settlement operations** — `pollProcessExit()` publishes direct exit separately, while `isJobEmpty()` reads `QueryInformationJobObject(JobObjectBasicAccountingInformation)` until `ActiveProcesses` reaches zero. Checked Job termination and handle closure keep the runner as the only native owner.
|
|
33
33
|
- **Explicit settlement ownership** — `waitForProcessExit()` waits and closes a sandbox process handle; ordinary runner process polling, Job accounting, and checked Job termination/closure remain separate operations. `drainPipe()` reuses one native count slot while draining, frees it, and closes the pipe read handle. Each caller owns its result composition and returned handles.
|
|
34
34
|
|
|
35
|
+
Process creation sets `STARTF_USESHOWWINDOW` with `SW_HIDE` before target code runs. It preserves console inheritance and does not add `CREATE_NO_WINDOW` or `CREATE_NEW_CONSOLE`, which can fail DLL initialization under the restricted token. Existing parent console windows are not hidden.
|
|
36
|
+
|
|
35
37
|
The Windows ACL sandbox adds SID, DACL, grant, workspace, and public child policy above these primitives.
|
|
36
38
|
|
|
39
|
+
- **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.
|
|
40
|
+
|
|
37
41
|
<a id="header-verification"></a>
|
|
38
42
|
## Header verification
|
|
39
43
|
|
package/README.zh.md
CHANGED
|
@@ -9,33 +9,37 @@ kind: "package-library"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
供 Windows ACL 沙箱与普通子进程 Job runner 消费的底层 Win32 进程库。它唯一拥有仓库中可复用 process、stdio 与 Job Object 操作的 Koffi 绑定表;它不是 Cordis 服务,也不决定沙箱策略或公共 child
|
|
12
|
+
供 Windows ACL 沙箱与普通子进程 Job runner 消费的底层 Win32 进程库。它唯一拥有仓库中可复用 process、stdio 与 Job Object 操作的 Koffi 绑定表;它不是 Cordis 服务,也不决定沙箱策略或公共 child 行为。维护任一原生进程路径或检查句柄生命周期限制时,请阅读本页。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
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
|
-
##
|
|
25
|
+
## 行为
|
|
26
26
|
|
|
27
|
-
- **唯一可复用 ABI owner** — `abi.ts` 拥有两条 process 路径消费的 Win32 常量与 x64 布局值。`ffi.ts` 懒加载 `kernel32.dll` 与 `advapi32.dll`,核验 `STARTUPINFOW` 和 `PROCESS_INFORMATION
|
|
28
|
-
- **restricted-token 创建** — `RestrictedProcessSpawnOptions`
|
|
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
|
|
32
|
-
- **ordinary
|
|
33
|
-
- **显式结算归属** — `waitForProcessExit()`
|
|
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
|
-
|
|
35
|
+
进程创建在目标代码运行前设置 `STARTF_USESHOWWINDOW` 和 `SW_HIDE`。它保留控制台继承,不添加可能导致受限令牌下 DLL 初始化失败的 `CREATE_NO_WINDOW` 或 `CREATE_NEW_CONSOLE`。已有的父进程控制台窗口不会被隐藏。
|
|
36
|
+
|
|
37
|
+
Windows ACL 沙箱在这些原语上增加 SID、DACL、grant、workspace 与公共 child 策略。
|
|
38
|
+
|
|
39
|
+
- **继承控制描述符**——Job 创建接受可选的 fd-7 管道。`STARTUPINFO.cbReserved2/lpReserved2` 携带八槽 CRT 描述符表,其中包含标准句柄、关闭的槽 3–6,以及槽 7 的控制管道。该表保留到 CreateProcess 返回;临时句柄继承在成功和失败时均恢复。在 Node 启动前初始化该槽可避免覆盖 Node 已分配的描述符。
|
|
36
40
|
|
|
37
41
|
<a id="header-verification"></a>
|
|
38
|
-
##
|
|
42
|
+
## 头文件验证
|
|
39
43
|
|
|
40
44
|
process、stdio 与 Job 的常量以及选定结构体的大小和偏移由 [`verify/abi-probe.cpp`](verify/abi-probe.cpp) 对照 MinGW Windows 头文件检查:
|
|
41
45
|
|
|
@@ -43,16 +47,16 @@ process、stdio 与 Job 的常量以及选定结构体的大小和偏移由 [`ve
|
|
|
43
47
|
g++ -std=c++20 -municode -O2 -o abi-probe.exe verify/abi-probe.cpp && ./abi-probe.exe
|
|
44
48
|
```
|
|
45
49
|
|
|
46
|
-
Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载时断言各自的 64
|
|
50
|
+
Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载时断言各自的 64 位大小。该探针还固定指针与句柄宽度、Unicode 环境标志,以及用于判断完全停稳的基础 Job accounting record 大小与 `ActiveProcesses` 偏移;其余已记录偏移和常量也由该探针提供证据。
|
|
47
51
|
|
|
48
52
|
<a id="model-experience"></a>
|
|
49
|
-
##
|
|
53
|
+
## 模型体验
|
|
50
54
|
|
|
51
55
|
### 进程原语
|
|
52
56
|
|
|
53
57
|
#### 模型看到什么
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
没有直接内容。本包向沙箱与 ordinary runner 提供 `Win32ProcessBindings`、`CurrentTokenProcessBindings` 与进程原语;两者拥有全部模型可见工具、输出与诊断,本包不贡献提示词或工具 schema。
|
|
56
60
|
|
|
57
61
|
#### Token 影响
|
|
58
62
|
|
|
@@ -62,14 +66,14 @@ Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载
|
|
|
62
66
|
|
|
63
67
|
本包不贡献稳定请求前缀,因此不会使模型 KV Cache 失效。
|
|
64
68
|
|
|
65
|
-
##
|
|
69
|
+
## 已知限制与延期工作
|
|
66
70
|
|
|
67
71
|
<a id="known-limitations-and-deferred-work"></a>
|
|
68
72
|
|
|
69
73
|
- **仅在 Windows 原生加载** — 导入通用类型可跨平台进行,但解析绑定表会加载 Windows DLL,并在其他宿主失败。跨平台测试注入绑定表,不加载原生 API。
|
|
70
|
-
- **没有公共进程服务** — 本包刻意不把原语包装成 Cordis 或 Node
|
|
71
|
-
- **restricted-token
|
|
72
|
-
- **没有 standalone process API** —
|
|
74
|
+
- **没有公共进程服务** — 本包刻意不把原语包装成 Cordis 或 Node 流。消费方必须拥有自己的策略、异步调度、输出上限、取消与最终句柄关闭。
|
|
75
|
+
- **restricted-token null 环境** — `CreateProcessAsUserW` 沙箱原语传入 null 环境块,并先通过 `SetEnvironmentVariableW` 建立改动,因为经 Koffi 传入显式环境块会以 `ERROR_INVALID_PARAMETER` 失败。ordinary `CreateProcessW` runner 则要求完整 target 环境,并传入排序、双 NUL 结尾的 UTF-16LE 块,其中包括 `=X:` 驱动器条目,而不修改自身环境。
|
|
76
|
+
- **没有 standalone process API** — 本包只暴露当前沙箱与 ordinary-runner 消费方所需的操作,不拥有 Node 流、公共句柄、输出策略、取消或 durable state。
|
|
73
77
|
- **创建到分配之间的中断** — 目标以 suspended 状态启动,不能在 Job 分配前执行,但 runner 若在进程创建到分配之间的极窄区间被外力终止,可能留下 suspended target。本包不声明原子 Job 附加保证。
|
|
74
78
|
- **header 证据限定架构** — 已提交的 ABI probe 与布局常量覆盖仓库当前 64 位 Windows 目标。支持新的指针宽度或不兼容 Windows ABI 前,必须先更新 probe。
|
|
75
79
|
|
|
@@ -84,4 +88,4 @@ Koffi 的 `STARTUPINFOW` 与 `PROCESS_INFORMATION` 定义还会在模块加载
|
|
|
84
88
|
|
|
85
89
|
</details>
|
|
86
90
|
|
|
87
|
-
**运行时不变式:**
|
|
91
|
+
**运行时不变式:** 不发布伴生入口。操作只持有调用内的原生句柄。
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createLazyRequire } from "@deepseek-ai/dsh-lazy-require";
|
|
2
2
|
/** Win32 code reporting a caller-provided buffer is too small. */
|
|
3
3
|
const ERROR_INSUFFICIENT_BUFFER = 122;
|
|
4
4
|
/** Job limit that terminates every member when the final Job handle closes. */
|
|
@@ -19,10 +19,13 @@ var Win32Error = class extends Error {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
//#endregion
|
|
22
|
+
//#region lib/types/koffi.js
|
|
23
|
+
/** Process-realm lazy access to Koffi's CommonJS entry. */
|
|
24
|
+
/** Load Koffi on the first Win32 native operation. */
|
|
25
|
+
const requireKoffi = createLazyRequire("koffi", import.meta.url);
|
|
26
|
+
//#endregion
|
|
22
27
|
//#region lib/types/ffi.js
|
|
23
28
|
/** Lazy Koffi bindings for generic Win32 process, stdio, and Job operations. */
|
|
24
|
-
const PVOID = koffi.pointer("void");
|
|
25
|
-
const PPVOID = koffi.pointer(PVOID);
|
|
26
29
|
/**
|
|
27
30
|
* Return whether a Koffi pointer represents NULL.
|
|
28
31
|
* @param value - pointer value returned by Koffi or a Win32 call.
|
|
@@ -31,51 +34,64 @@ const PPVOID = koffi.pointer(PVOID);
|
|
|
31
34
|
function isNullPtr(value) {
|
|
32
35
|
return value === null || value === void 0 || value === 0n;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
let cachedTypes;
|
|
38
|
+
/** Resolve Koffi pointer and process layouts on the first native operation. */
|
|
39
|
+
function win32Types() {
|
|
40
|
+
if (cachedTypes !== void 0) return cachedTypes;
|
|
41
|
+
const koffi = requireKoffi();
|
|
42
|
+
const PVOID = koffi.pointer("void");
|
|
43
|
+
const PPVOID = koffi.pointer(PVOID);
|
|
44
|
+
const STARTUPINFOW = koffi.struct("DSH_STARTUPINFOW", {
|
|
45
|
+
cb: "uint32",
|
|
46
|
+
lpReserved: "str16",
|
|
47
|
+
lpDesktop: "str16",
|
|
48
|
+
lpTitle: "str16",
|
|
49
|
+
dwX: "uint32",
|
|
50
|
+
dwY: "uint32",
|
|
51
|
+
dwXSize: "uint32",
|
|
52
|
+
dwYSize: "uint32",
|
|
53
|
+
dwXCountChars: "uint32",
|
|
54
|
+
dwYCountChars: "uint32",
|
|
55
|
+
dwFillAttribute: "uint32",
|
|
56
|
+
dwFlags: "uint32",
|
|
57
|
+
wShowWindow: "uint16",
|
|
58
|
+
cbReserved2: "uint16",
|
|
59
|
+
lpReserved2: koffi.pointer("uint8"),
|
|
60
|
+
hStdInput: PVOID,
|
|
61
|
+
hStdOutput: PVOID,
|
|
62
|
+
hStdError: PVOID
|
|
63
|
+
});
|
|
64
|
+
const PROCESS_INFORMATION = koffi.struct("DSH_PROCESS_INFORMATION", {
|
|
65
|
+
hProcess: PVOID,
|
|
66
|
+
hThread: PVOID,
|
|
67
|
+
dwProcessId: "uint32",
|
|
68
|
+
dwThreadId: "uint32"
|
|
69
|
+
});
|
|
70
|
+
/* v8 ignore start -- ABI guards are pinned by native header probes. */
|
|
71
|
+
if (STARTUPINFOW.size !== 104) throw new Error(`STARTUPINFOW layout mismatch: koffi computed ${STARTUPINFOW.size}, expected 104`);
|
|
72
|
+
if (PROCESS_INFORMATION.size !== 24) throw new Error(`PROCESS_INFORMATION layout mismatch: koffi computed ${PROCESS_INFORMATION.size}, expected 24`);
|
|
73
|
+
/* v8 ignore stop */
|
|
74
|
+
return cachedTypes = {
|
|
75
|
+
PVOID,
|
|
76
|
+
PPVOID,
|
|
77
|
+
STARTUPINFOW,
|
|
78
|
+
PROCESS_INFORMATION
|
|
79
|
+
};
|
|
80
|
+
}
|
|
66
81
|
/**
|
|
67
82
|
* Allocate a pointer-sized out-parameter slot.
|
|
68
83
|
* @returns allocated native slot.
|
|
69
84
|
*/
|
|
70
85
|
function allocPtrSlot() {
|
|
71
|
-
|
|
86
|
+
const { PVOID } = win32Types();
|
|
87
|
+
return requireKoffi().alloc(PVOID, 1);
|
|
72
88
|
}
|
|
73
89
|
/**
|
|
74
90
|
* Allocate a uint32 out-parameter slot.
|
|
75
91
|
* @returns allocated native slot.
|
|
76
92
|
*/
|
|
77
93
|
function allocUint32() {
|
|
78
|
-
return
|
|
94
|
+
return requireKoffi().alloc("uint32", 1);
|
|
79
95
|
}
|
|
80
96
|
/**
|
|
81
97
|
* Decode a pointer out-parameter.
|
|
@@ -83,7 +99,7 @@ function allocUint32() {
|
|
|
83
99
|
* @returns decoded pointer, or null for address zero.
|
|
84
100
|
*/
|
|
85
101
|
function decodePtr(slot) {
|
|
86
|
-
const value =
|
|
102
|
+
const value = requireKoffi().decode(slot, win32Types().PVOID);
|
|
87
103
|
return isNullPtr(value) ? null : value;
|
|
88
104
|
}
|
|
89
105
|
/**
|
|
@@ -92,14 +108,14 @@ function decodePtr(slot) {
|
|
|
92
108
|
* @returns decoded unsigned value.
|
|
93
109
|
*/
|
|
94
110
|
function decodeUint32(slot) {
|
|
95
|
-
return
|
|
111
|
+
return requireKoffi().decode(slot, "uint32");
|
|
96
112
|
}
|
|
97
113
|
/**
|
|
98
114
|
* Allocate a zeroed STARTUPINFOW.
|
|
99
115
|
* @returns allocated struct pointer.
|
|
100
116
|
*/
|
|
101
117
|
function allocStartupInfo() {
|
|
102
|
-
return
|
|
118
|
+
return requireKoffi().alloc(win32Types().STARTUPINFOW, 1);
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
105
121
|
* Encode the stdio-bearing STARTUPINFOW fields.
|
|
@@ -107,14 +123,14 @@ function allocStartupInfo() {
|
|
|
107
123
|
* @param fields - fields required for inherited stdio.
|
|
108
124
|
*/
|
|
109
125
|
function encodeStartupInfo(startupInfo, fields) {
|
|
110
|
-
|
|
126
|
+
requireKoffi().encode(startupInfo, win32Types().STARTUPINFOW, fields);
|
|
111
127
|
}
|
|
112
128
|
/**
|
|
113
129
|
* Allocate a zeroed PROCESS_INFORMATION.
|
|
114
130
|
* @returns allocated struct pointer.
|
|
115
131
|
*/
|
|
116
132
|
function allocProcessInfo() {
|
|
117
|
-
return
|
|
133
|
+
return requireKoffi().alloc(win32Types().PROCESS_INFORMATION, 1);
|
|
118
134
|
}
|
|
119
135
|
/**
|
|
120
136
|
* Decode PROCESS_INFORMATION.
|
|
@@ -122,13 +138,14 @@ function allocProcessInfo() {
|
|
|
122
138
|
* @returns process/thread handles and ids.
|
|
123
139
|
*/
|
|
124
140
|
function decodeProcessInfo(processInfo) {
|
|
125
|
-
return
|
|
141
|
+
return requireKoffi().decode(processInfo, win32Types().PROCESS_INFORMATION);
|
|
126
142
|
}
|
|
127
143
|
let cachedContext;
|
|
128
144
|
let cached;
|
|
129
145
|
/* v8 ignore start -- exercised by native Windows ABI and sandbox jobs. */
|
|
130
146
|
function bindingContext() {
|
|
131
147
|
if (cachedContext !== void 0) return cachedContext;
|
|
148
|
+
const koffi = requireKoffi();
|
|
132
149
|
const kernel32 = koffi.load("kernel32.dll");
|
|
133
150
|
const advapi32 = koffi.load("advapi32.dll");
|
|
134
151
|
const bind = (lib, name, result, args) => lib.func("__stdcall", name, result, args);
|
|
@@ -141,11 +158,14 @@ function bindingContext() {
|
|
|
141
158
|
}
|
|
142
159
|
function bindings() {
|
|
143
160
|
if (cached !== void 0) return cached;
|
|
161
|
+
const koffi = requireKoffi();
|
|
162
|
+
const { PVOID, PPVOID, STARTUPINFOW, PROCESS_INFORMATION } = win32Types();
|
|
144
163
|
const { kernel32, advapi32, bind } = bindingContext();
|
|
145
164
|
const node = koffi.load(null);
|
|
146
165
|
cached = {
|
|
147
166
|
closeHandle: bind(kernel32, "CloseHandle", "int", [PVOID]),
|
|
148
167
|
getLastError: bind(kernel32, "GetLastError", "uint32", []),
|
|
168
|
+
getFileType: bind(kernel32, "GetFileType", "uint32", [PVOID]),
|
|
149
169
|
formatMessageW: bind(kernel32, "FormatMessageW", "uint32", [
|
|
150
170
|
"uint32",
|
|
151
171
|
PVOID,
|
|
@@ -284,6 +304,44 @@ function throwWin32(api, name, win32Code, detail) {
|
|
|
284
304
|
throw new Win32Error(name, win32Code, detail ?? errorText(api, win32Code));
|
|
285
305
|
}
|
|
286
306
|
//#endregion
|
|
307
|
+
//#region lib/types/control-stdio.js
|
|
308
|
+
/** Windows CRT startup descriptors for a Node payload with one inherited control pipe. */
|
|
309
|
+
const HANDLE_BYTES = 8;
|
|
310
|
+
const INVALID_HANDLE = 18446744073709551615n;
|
|
311
|
+
const FOPEN = 1;
|
|
312
|
+
const FPIPE = 8;
|
|
313
|
+
const FDEV = 64;
|
|
314
|
+
const FILE_TYPE_CHAR = 2;
|
|
315
|
+
const FILE_TYPE_PIPE = 3;
|
|
316
|
+
/**
|
|
317
|
+
* Encode the CRT's descriptor table before the child runtime allocates descriptors.
|
|
318
|
+
* Supported Windows targets use 64-bit handles. Empty slots remain closed, and the
|
|
319
|
+
* backing Buffer must remain alive until CreateProcess returns.
|
|
320
|
+
* @param api - native file-type inspection for inherited handles.
|
|
321
|
+
* @param stdio - standard handles and the provider-owned fd-7 control pipe.
|
|
322
|
+
* @returns descriptor count, flag bytes, and handle values for STARTUPINFO's reserved CRT fields.
|
|
323
|
+
*/
|
|
324
|
+
function inheritedControlStdio(api, stdio) {
|
|
325
|
+
const count = stdio.control.fileDescriptor + 1;
|
|
326
|
+
const handleOffset = 4 + count;
|
|
327
|
+
const bytes = Buffer.alloc(handleOffset + count * HANDLE_BYTES);
|
|
328
|
+
bytes.writeUInt32LE(count, 0);
|
|
329
|
+
for (let index = 0; index < count; index++) bytes.writeBigUInt64LE(INVALID_HANDLE, handleOffset + index * HANDLE_BYTES);
|
|
330
|
+
const entries = [
|
|
331
|
+
[0, stdio.stdin],
|
|
332
|
+
[1, stdio.stdout],
|
|
333
|
+
[2, stdio.stderr],
|
|
334
|
+
[stdio.control.fileDescriptor, stdio.control.handle]
|
|
335
|
+
];
|
|
336
|
+
for (const [fd, handle] of entries) {
|
|
337
|
+
const kind = api.getFileType(handle);
|
|
338
|
+
if (fd === stdio.control.fileDescriptor && kind !== FILE_TYPE_PIPE) throw new Error("subprocess control descriptor is not a Windows pipe");
|
|
339
|
+
bytes[4 + fd] = FOPEN | (kind === FILE_TYPE_PIPE ? FPIPE : kind === FILE_TYPE_CHAR ? FDEV : 0);
|
|
340
|
+
bytes.writeBigUInt64LE(handle, handleOffset + fd * HANDLE_BYTES);
|
|
341
|
+
}
|
|
342
|
+
return bytes;
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
287
345
|
//#region lib/types/process.js
|
|
288
346
|
/** Typed Win32 process operations over the shared binding table. */
|
|
289
347
|
/**
|
|
@@ -326,7 +384,7 @@ function encodeWindowsEnvironment(env) {
|
|
|
326
384
|
return Buffer.from(`${strings.join("\0")}\0\0`, "utf16le");
|
|
327
385
|
}
|
|
328
386
|
function freeNative(pointer) {
|
|
329
|
-
if (pointer !== void 0)
|
|
387
|
+
if (pointer !== void 0) requireKoffi().free(pointer);
|
|
330
388
|
}
|
|
331
389
|
function closeBestEffort(api, handle) {
|
|
332
390
|
if (!isNullPtr(handle)) api.closeHandle(handle);
|
|
@@ -352,7 +410,7 @@ function createPipe(api, owned) {
|
|
|
352
410
|
};
|
|
353
411
|
} finally {
|
|
354
412
|
freeNative(writeSlot);
|
|
355
|
-
|
|
413
|
+
requireKoffi().free(readSlot);
|
|
356
414
|
}
|
|
357
415
|
}
|
|
358
416
|
function closeOwned(api, owned, handle) {
|
|
@@ -369,6 +427,7 @@ function createRestrictedProcess(api, options, commandLine, creationFlags, start
|
|
|
369
427
|
}
|
|
370
428
|
/**
|
|
371
429
|
* Spawn a process with anonymous-pipe stdout/stderr and immediate stdin EOF.
|
|
430
|
+
* New console windows start hidden without changing console inheritance.
|
|
372
431
|
* @param api - active binding table.
|
|
373
432
|
* @param options - command, cwd, args, and restricted primary token.
|
|
374
433
|
* @returns caller-owned process and pipe read handles.
|
|
@@ -389,7 +448,8 @@ function spawnPipedProcess(api, options) {
|
|
|
389
448
|
startupInfo = allocStartupInfo();
|
|
390
449
|
encodeStartupInfo(startupInfo, {
|
|
391
450
|
cb: 104,
|
|
392
|
-
dwFlags:
|
|
451
|
+
dwFlags: 257,
|
|
452
|
+
wShowWindow: 0,
|
|
393
453
|
hStdInput: stdIn.read,
|
|
394
454
|
hStdOutput: stdOut.write,
|
|
395
455
|
hStdError: stdErr.write
|
|
@@ -488,7 +548,7 @@ function createKillOnCloseJob(api) {
|
|
|
488
548
|
}
|
|
489
549
|
const UV_INVALID_OS_FILE_HANDLE = 18446744073709551615n;
|
|
490
550
|
const UV_INVALID_FILE_DESCRIPTOR = 18446744073709551614n;
|
|
491
|
-
function inheritedStandardHandles(api) {
|
|
551
|
+
function inheritedStandardHandles(api, controlFileDescriptor) {
|
|
492
552
|
const get = (selector, label) => {
|
|
493
553
|
const handle = api.getStdHandle(selector);
|
|
494
554
|
if (!isNullPtr(handle)) return handle;
|
|
@@ -497,19 +557,27 @@ function inheritedStandardHandles(api) {
|
|
|
497
557
|
return {
|
|
498
558
|
stdin: get(-10, "stdin"),
|
|
499
559
|
stdout: get(-11, "stdout"),
|
|
500
|
-
stderr: get(-12, "stderr")
|
|
560
|
+
stderr: get(-12, "stderr"),
|
|
561
|
+
...controlFileDescriptor === void 0 ? {} : { control: {
|
|
562
|
+
fileDescriptor: controlFileDescriptor,
|
|
563
|
+
handle: descriptorHandle(api, controlFileDescriptor, "control")
|
|
564
|
+
} }
|
|
501
565
|
};
|
|
502
566
|
}
|
|
567
|
+
function descriptorHandle(api, fileDescriptor, label) {
|
|
568
|
+
const handle = api.uvGetOsfhandle(fileDescriptor);
|
|
569
|
+
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)}`);
|
|
570
|
+
return handle;
|
|
571
|
+
}
|
|
503
572
|
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
573
|
return {
|
|
510
|
-
stdin:
|
|
511
|
-
stdout:
|
|
512
|
-
stderr:
|
|
574
|
+
stdin: descriptorHandle(api, descriptors.stdin, "stdin"),
|
|
575
|
+
stdout: descriptorHandle(api, descriptors.stdout, "stdout"),
|
|
576
|
+
stderr: descriptorHandle(api, descriptors.stderr, "stderr"),
|
|
577
|
+
...descriptors.control === void 0 ? {} : { control: {
|
|
578
|
+
fileDescriptor: descriptors.control,
|
|
579
|
+
handle: descriptorHandle(api, descriptors.control, "control")
|
|
580
|
+
} }
|
|
513
581
|
};
|
|
514
582
|
}
|
|
515
583
|
/** Shared suspended-create, Job-assignment, and resume lifecycle. */
|
|
@@ -518,25 +586,45 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
|
|
|
518
586
|
const enabled = [];
|
|
519
587
|
let startupInfo;
|
|
520
588
|
let processInfo;
|
|
589
|
+
let controlDescriptorBlock;
|
|
521
590
|
let created = 0;
|
|
522
591
|
let createFailureCode = 0;
|
|
523
592
|
try {
|
|
524
593
|
const stdio = resolveStdio();
|
|
525
|
-
|
|
594
|
+
const inherited = [
|
|
526
595
|
[stdio.stdin, "stdin"],
|
|
527
596
|
[stdio.stdout, "stdout"],
|
|
528
597
|
[stdio.stderr, "stderr"]
|
|
529
|
-
]
|
|
598
|
+
];
|
|
599
|
+
if (stdio.control !== void 0) inherited.push([stdio.control.handle, "control"]);
|
|
600
|
+
for (const [handle, label] of inherited) {
|
|
530
601
|
if (api.setHandleInformation(handle, 1, 1) === 0) throwLastError(api, "SetHandleInformation", `${label} (enable inherit)`);
|
|
531
602
|
enabled.push(handle);
|
|
532
603
|
}
|
|
604
|
+
const controlBytes = stdio.control === void 0 ? void 0 : inheritedControlStdio(api, {
|
|
605
|
+
...stdio,
|
|
606
|
+
control: stdio.control
|
|
607
|
+
});
|
|
608
|
+
if (controlBytes !== void 0) {
|
|
609
|
+
const koffi = requireKoffi();
|
|
610
|
+
controlDescriptorBlock = {
|
|
611
|
+
pointer: koffi.alloc("uint8", controlBytes.length),
|
|
612
|
+
length: controlBytes.length
|
|
613
|
+
};
|
|
614
|
+
koffi.encode(controlDescriptorBlock.pointer, "uint8", controlBytes, controlBytes.length);
|
|
615
|
+
}
|
|
533
616
|
startupInfo = allocStartupInfo();
|
|
534
617
|
encodeStartupInfo(startupInfo, {
|
|
535
618
|
cb: 104,
|
|
536
|
-
dwFlags:
|
|
619
|
+
dwFlags: 257,
|
|
620
|
+
wShowWindow: 0,
|
|
537
621
|
hStdInput: stdio.stdin,
|
|
538
622
|
hStdOutput: stdio.stdout,
|
|
539
|
-
hStdError: stdio.stderr
|
|
623
|
+
hStdError: stdio.stderr,
|
|
624
|
+
...controlDescriptorBlock === void 0 ? {} : {
|
|
625
|
+
cbReserved2: controlDescriptorBlock.length,
|
|
626
|
+
lpReserved2: controlDescriptorBlock.pointer
|
|
627
|
+
}
|
|
540
628
|
});
|
|
541
629
|
processInfo = allocProcessInfo();
|
|
542
630
|
created = create(startupInfo, processInfo);
|
|
@@ -547,6 +635,7 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
|
|
|
547
635
|
throw error;
|
|
548
636
|
} finally {
|
|
549
637
|
freeNative(startupInfo);
|
|
638
|
+
freeNative(controlDescriptorBlock?.pointer);
|
|
550
639
|
for (const handle of enabled) api.setHandleInformation(handle, 1, 0);
|
|
551
640
|
}
|
|
552
641
|
if (created === 0) {
|
|
@@ -590,7 +679,7 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
|
|
|
590
679
|
};
|
|
591
680
|
}
|
|
592
681
|
/**
|
|
593
|
-
* Spawn a restricted-token process suspended, assign its Job, then resume it.
|
|
682
|
+
* Spawn a restricted-token process suspended with hidden initial windows, assign its Job, then resume it.
|
|
594
683
|
* @param api - active binding table.
|
|
595
684
|
* @param options - command, cwd, args, and restricted primary token.
|
|
596
685
|
* @returns caller-owned process and Job handles after successful resume.
|
|
@@ -601,10 +690,10 @@ function spawnJobProcess(api, options, resolveStdio, createName, create) {
|
|
|
601
690
|
*/
|
|
602
691
|
function spawnInheritedJobProcess(api, options) {
|
|
603
692
|
const commandLine = buildCommandLine(options.command, options.args);
|
|
604
|
-
return spawnJobProcess(api, options, () => inheritedStandardHandles(api), "CreateProcessAsUserW", (startupInfo, processInfo) => createRestrictedProcess(api, options, commandLine, 4, startupInfo, processInfo));
|
|
693
|
+
return spawnJobProcess(api, options, () => inheritedStandardHandles(api, options.controlFileDescriptor), "CreateProcessAsUserW", (startupInfo, processInfo) => createRestrictedProcess(api, options, commandLine, 4, startupInfo, processInfo));
|
|
605
694
|
}
|
|
606
695
|
/**
|
|
607
|
-
* Spawn an ordinary process suspended, assign its Job, then resume it.
|
|
696
|
+
* Spawn an ordinary process suspended with hidden initial windows, assign its Job, then resume it.
|
|
608
697
|
* @param api - active binding table.
|
|
609
698
|
* @param options - command, cwd, argv, and target carrier descriptors.
|
|
610
699
|
* @returns caller-owned process and Job handles after successful resume.
|
|
@@ -636,7 +725,7 @@ function pollProcessExit(api, process) {
|
|
|
636
725
|
if (api.getExitCodeProcess(process, exitCodeSlot) === 0) throwLastError(api, "GetExitCodeProcess");
|
|
637
726
|
return decodeUint32(exitCodeSlot);
|
|
638
727
|
} finally {
|
|
639
|
-
|
|
728
|
+
requireKoffi().free(exitCodeSlot);
|
|
640
729
|
}
|
|
641
730
|
}
|
|
642
731
|
/**
|
package/lib/types/abi.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/** Generic Win32 process, stdio, and Job Object constants verified on x64. */
|
|
2
2
|
/** STARTUPINFOW uses the standard input, output, and error handles. */
|
|
3
3
|
export declare const STARTF_USESTDHANDLES = 256;
|
|
4
|
+
/** STARTUPINFOW applies wShowWindow when creating a console window. */
|
|
5
|
+
export declare const STARTF_USESHOWWINDOW = 1;
|
|
6
|
+
/** Initial window visibility that preserves the child's console attachment. */
|
|
7
|
+
export declare const SW_HIDE = 0;
|
|
4
8
|
/** HandleInformation flag that permits child inheritance. */
|
|
5
9
|
export declare const HANDLE_FLAG_INHERIT = 1;
|
|
6
10
|
/** Infinite WaitForSingleObject timeout. */
|
|
@@ -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
|
package/lib/types/ffi.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/** Lazy Koffi bindings for generic Win32 process, stdio, and Job operations. */
|
|
2
|
-
import
|
|
2
|
+
import { type Koffi } from './koffi.ts';
|
|
3
3
|
declare const nativePtr: unique symbol;
|
|
4
4
|
/** Koffi native pointer branded against accidental numeric use. */
|
|
5
5
|
export type NativePtr = bigint & {
|
|
6
6
|
readonly [nativePtr]: true;
|
|
7
7
|
};
|
|
8
|
-
type Ptr = ReturnType<
|
|
8
|
+
type Ptr = ReturnType<Koffi['pointer']>;
|
|
9
9
|
/** Loaded Win32 libraries and the shared stdcall binder used by process extensions. */
|
|
10
10
|
export interface Win32BindingContext {
|
|
11
11
|
/** Kernel process, handle, pipe, and Job APIs. */
|
|
12
|
-
readonly kernel32: ReturnType<
|
|
12
|
+
readonly kernel32: ReturnType<Koffi['load']>;
|
|
13
13
|
/** Token and security APIs. */
|
|
14
|
-
readonly advapi32: ReturnType<
|
|
14
|
+
readonly advapi32: ReturnType<Koffi['load']>;
|
|
15
15
|
/** Bind one stdcall function from a loaded Win32 library. */
|
|
16
|
-
readonly bind: (library: ReturnType<
|
|
16
|
+
readonly bind: (library: ReturnType<Koffi['load']>, name: string, result: Ptr | string, args: Array<Ptr | string>) => unknown;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Return whether a Koffi pointer represents NULL.
|
|
@@ -25,9 +25,12 @@ export declare function isNullPtr(value: NativePtr | null | undefined): value is
|
|
|
25
25
|
export interface StartupInfoInput {
|
|
26
26
|
cb: number;
|
|
27
27
|
dwFlags: number;
|
|
28
|
+
wShowWindow: number;
|
|
28
29
|
hStdInput: NativePtr;
|
|
29
30
|
hStdOutput: NativePtr;
|
|
30
31
|
hStdError: NativePtr;
|
|
32
|
+
cbReserved2?: number;
|
|
33
|
+
lpReserved2?: NativePtr;
|
|
31
34
|
}
|
|
32
35
|
/** Decoded PROCESS_INFORMATION result. */
|
|
33
36
|
export interface ProcessInfoOutput {
|
|
@@ -40,6 +43,8 @@ export interface ProcessInfoOutput {
|
|
|
40
43
|
export interface Win32ProcessBindings {
|
|
41
44
|
closeHandle(handle: NativePtr): number;
|
|
42
45
|
getLastError(): number;
|
|
46
|
+
getFileType(handle: NativePtr): number;
|
|
47
|
+
uvGetOsfhandle(fileDescriptor: number): NativePtr | null;
|
|
43
48
|
formatMessageW(flags: number, source: null, messageId: number, languageId: number, buffer: Buffer, size: number, args: null): number;
|
|
44
49
|
createPipe(readHandle: NativePtr, writeHandle: NativePtr, attributes: null, size: number): number;
|
|
45
50
|
setHandleInformation(handle: NativePtr, mask: number, flags: number): number;
|
|
@@ -62,10 +67,16 @@ export interface Win32ProcessBindings {
|
|
|
62
67
|
export interface CurrentTokenProcessBindings extends Win32ProcessBindings {
|
|
63
68
|
uvGetOsfhandle(fileDescriptor: number): NativePtr | null;
|
|
64
69
|
}
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Materialize the Koffi STARTUPINFOW layout on first native use.
|
|
72
|
+
* @returns the cached native struct type.
|
|
73
|
+
*/
|
|
74
|
+
export declare function startupInfoType(): ReturnType<Koffi['struct']>;
|
|
75
|
+
/**
|
|
76
|
+
* Materialize the Koffi PROCESS_INFORMATION layout on first native use.
|
|
77
|
+
* @returns the cached native struct type.
|
|
78
|
+
*/
|
|
79
|
+
export declare function processInformationType(): ReturnType<Koffi['struct']>;
|
|
69
80
|
/**
|
|
70
81
|
* Allocate a pointer-sized out-parameter slot.
|
|
71
82
|
* @returns allocated native slot.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Process-realm lazy access to Koffi's CommonJS entry. */
|
|
2
|
+
import type koffi from 'koffi';
|
|
3
|
+
/** Koffi runtime export type. */
|
|
4
|
+
export type Koffi = typeof koffi;
|
|
5
|
+
/** Load Koffi on the first Win32 native operation. */
|
|
6
|
+
export declare const requireKoffi: () => {
|
|
7
|
+
LibraryHandle: import("koffi").LibraryHandle;
|
|
8
|
+
TypeObject: import("koffi").TypeObject;
|
|
9
|
+
Union: import("koffi").Union;
|
|
10
|
+
address: typeof import("koffi").address;
|
|
11
|
+
alias: typeof import("koffi").alias;
|
|
12
|
+
alignof: typeof import("koffi").alignof;
|
|
13
|
+
alloc: typeof import("koffi").alloc;
|
|
14
|
+
array: typeof import("koffi").array;
|
|
15
|
+
as: typeof import("koffi").as;
|
|
16
|
+
call: typeof import("koffi").call;
|
|
17
|
+
config: typeof import("koffi").config;
|
|
18
|
+
decode: typeof import("koffi").decode;
|
|
19
|
+
disposable: typeof import("koffi").disposable;
|
|
20
|
+
encode: typeof import("koffi").encode;
|
|
21
|
+
enumeration: typeof import("koffi").enumeration;
|
|
22
|
+
errno: typeof import("koffi").errno;
|
|
23
|
+
extension: typeof import("koffi").extension;
|
|
24
|
+
free: typeof import("koffi").free;
|
|
25
|
+
inout: typeof import("koffi").inout;
|
|
26
|
+
introspect: typeof import("koffi").introspect;
|
|
27
|
+
load: typeof import("koffi").load;
|
|
28
|
+
node: typeof import("koffi").node;
|
|
29
|
+
offsetof: typeof import("koffi").offsetof;
|
|
30
|
+
opaque: typeof import("koffi").opaque;
|
|
31
|
+
os: typeof import("koffi").os;
|
|
32
|
+
out: typeof import("koffi").out;
|
|
33
|
+
pack: typeof import("koffi").pack;
|
|
34
|
+
pointer: typeof import("koffi").pointer;
|
|
35
|
+
proto: typeof import("koffi").proto;
|
|
36
|
+
register: typeof import("koffi").register;
|
|
37
|
+
reset: typeof import("koffi").reset;
|
|
38
|
+
resolve: typeof import("koffi").resolve;
|
|
39
|
+
sizeof: typeof import("koffi").sizeof;
|
|
40
|
+
stats: typeof import("koffi").stats;
|
|
41
|
+
struct: typeof import("koffi").struct;
|
|
42
|
+
type: typeof import("koffi").type;
|
|
43
|
+
types: typeof import("koffi").types;
|
|
44
|
+
union: typeof import("koffi").union;
|
|
45
|
+
unregister: typeof import("koffi").unregister;
|
|
46
|
+
version: typeof import("koffi").version;
|
|
47
|
+
view: typeof import("koffi").view;
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=koffi.d.ts.map
|
package/lib/types/process.d.ts
CHANGED
|
@@ -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 {
|
|
@@ -63,6 +67,7 @@ export interface SpawnedJobProcess {
|
|
|
63
67
|
}
|
|
64
68
|
/**
|
|
65
69
|
* Spawn a process with anonymous-pipe stdout/stderr and immediate stdin EOF.
|
|
70
|
+
* New console windows start hidden without changing console inheritance.
|
|
66
71
|
* @param api - active binding table.
|
|
67
72
|
* @param options - command, cwd, args, and restricted primary token.
|
|
68
73
|
* @returns caller-owned process and pipe read handles.
|
|
@@ -84,7 +89,7 @@ export declare function drainPipe(api: Win32ProcessBindings, handle: NativePtr):
|
|
|
84
89
|
*/
|
|
85
90
|
export declare function waitForProcessExit(api: Win32ProcessBindings, process: NativePtr): number;
|
|
86
91
|
/**
|
|
87
|
-
* Spawn a restricted-token process suspended, assign its Job, then resume it.
|
|
92
|
+
* Spawn a restricted-token process suspended with hidden initial windows, assign its Job, then resume it.
|
|
88
93
|
* @param api - active binding table.
|
|
89
94
|
* @param options - command, cwd, args, and restricted primary token.
|
|
90
95
|
* @returns caller-owned process and Job handles after successful resume.
|
|
@@ -95,7 +100,7 @@ export declare function waitForProcessExit(api: Win32ProcessBindings, process: N
|
|
|
95
100
|
*/
|
|
96
101
|
export declare function spawnInheritedJobProcess(api: Win32ProcessBindings, options: RestrictedProcessSpawnOptions): SpawnedJobProcess;
|
|
97
102
|
/**
|
|
98
|
-
* Spawn an ordinary process suspended, assign its Job, then resume it.
|
|
103
|
+
* Spawn an ordinary process suspended with hidden initial windows, assign its Job, then resume it.
|
|
99
104
|
* @param api - active binding table.
|
|
100
105
|
* @param options - command, cwd, argv, and target carrier descriptors.
|
|
101
106
|
* @returns caller-owned process and Job handles after successful resume.
|
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.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"koffi": "^3.1.0"
|
|
33
|
+
"koffi": "^3.1.0",
|
|
34
|
+
"@deepseek-ai/dsh-lazy-require": "^0.1.6-alpha.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@deepseek-ai/cordis": "^4.0.2"
|