@aalis/plugin-process-local 0.5.0 → 0.6.0
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.md +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +136 -22
- package/dist/index.js.map +1 -1
- package/package.json +14 -4
- package/dist/sandbox.d.ts +0 -21
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/sandbox.js +0 -83
- package/dist/sandbox.js.map +0 -1
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { ExecResult, ProcessService, SpawnHandle, SpawnOptions, TempDirHandle } from '@aalis/api-process';
|
|
2
|
+
import type { StorageService } from '@aalis/api-storage';
|
|
3
|
+
import type { Context } from '@aalis/core';
|
|
4
4
|
export declare const name = "@aalis/plugin-process-local";
|
|
5
5
|
export declare const provides: string[];
|
|
6
6
|
export declare class LocalProcessService implements ProcessService {
|
|
7
7
|
private readonly storage;
|
|
8
|
+
/**
|
|
9
|
+
* 存活进程组登记表(键 = 直接子进程 pid = pgid)。detached 后终端 Ctrl+C 不再直达子进程,改由 killAll
|
|
10
|
+
* 在停机时统一收尸;`cmd &` 留下的孙进程在直接子进程退出后仍占着这个组,所以按组而非按子进程登记。
|
|
11
|
+
*/
|
|
12
|
+
private readonly groups;
|
|
8
13
|
constructor(storage: StorageService);
|
|
14
|
+
/** 宿主停机时把仍有成员的进程组整组杀掉,维持「Aalis 退出,工具子进程一起退出」。 */
|
|
15
|
+
killAll(): void;
|
|
9
16
|
spawn(cmd: string, args: readonly string[], opts?: SpawnOptions): SpawnHandle;
|
|
10
17
|
execFile(cmd: string, args: readonly string[], opts?: SpawnOptions): Promise<ExecResult>;
|
|
11
18
|
makeTempDir(prefix: string): Promise<TempDirHandle>;
|
|
12
19
|
readExternalFile(path: string): Promise<Uint8Array>;
|
|
13
20
|
}
|
|
14
21
|
export declare function apply(ctx: Context): Promise<void>;
|
|
15
|
-
declare const plugin: PluginModule;
|
|
16
|
-
export default plugin;
|
|
17
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE/G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,eAAO,MAAM,IAAI,gCAAgC,CAAC;AAClD,eAAO,MAAM,QAAQ,UAAc,CAAC;AAuEpC,qBAAa,mBAAoB,YAAW,cAAc;IAO5C,OAAO,CAAC,QAAQ,CAAC,OAAO;IANpC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;gBAE7B,OAAO,EAAE,cAAc;IAEpD,iDAAiD;IACjD,OAAO,IAAI,IAAI;IAKf,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,YAAiB,GAAG,WAAW;IAkH3E,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IAa5F,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAInD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAI1D;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CASvD"}
|
package/dist/index.js
CHANGED
|
@@ -3,25 +3,117 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
5
5
|
import { readFile as fsReadFile } from 'node:fs/promises';
|
|
6
|
-
import { makeTempDirViaStorage } from '@aalis/
|
|
7
|
-
import { createStorageGateway } from '@aalis/
|
|
6
|
+
import { makeTempDirViaStorage } from '@aalis/api-process';
|
|
7
|
+
import { createStorageGateway } from '@aalis/api-storage';
|
|
8
8
|
export const name = '@aalis/plugin-process-local';
|
|
9
9
|
export const provides = ['process'];
|
|
10
10
|
/** wait() 默认累计缓冲上限(stdout+stderr 合计):10MB,足够正常输出,又防失控输出 OOM。 */
|
|
11
11
|
const DEFAULT_MAX_BUFFER = 10 * 1024 * 1024;
|
|
12
|
+
/**
|
|
13
|
+
* wait() 在子进程 'exit' 之后等 'close' 的宽限(毫秒)。
|
|
14
|
+
* 孙进程继承同一对 pipe,只要它还活着 'close' 就永远不来(`sh -c 'sleep 20 & echo hi'`
|
|
15
|
+
* 实测要等满 20 秒,`npm run dev &` 则永不返回)。宽限到点即用已收集的输出返回。
|
|
16
|
+
*/
|
|
17
|
+
const CLOSE_GRACE_MS = 200;
|
|
18
|
+
const isWindows = process.platform === 'win32';
|
|
19
|
+
/**
|
|
20
|
+
* 对子进程所在的**整个进程组**发信号(POSIX)。子进程 spawn 时 detached,自身即进程组组长,
|
|
21
|
+
* 负 pid 才能打到它 fork 出的孙进程——只打子进程(`/bin/sh`)杀不掉 `sh -c 'cmd &'` 的后台任务。
|
|
22
|
+
* 组已不存在(ESRCH)/无权限时回落到只打子进程本身。
|
|
23
|
+
*/
|
|
24
|
+
function killGroup(child, signal) {
|
|
25
|
+
const pid = child.pid;
|
|
26
|
+
if (pid === undefined)
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
process.kill(-pid, signal);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
try {
|
|
34
|
+
return child.kill(signal);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 强制收尾(超时 / 宿主停机)。POSIX 打整个进程组 SIGKILL;
|
|
43
|
+
* Windows 无进程组与负 pid 语义,改用 `taskkill /T /F` 杀整棵进程树。
|
|
44
|
+
*/
|
|
45
|
+
function killTree(child) {
|
|
46
|
+
const pid = child.pid;
|
|
47
|
+
if (pid === undefined)
|
|
48
|
+
return;
|
|
49
|
+
if (isWindows) {
|
|
50
|
+
const fallback = () => {
|
|
51
|
+
try {
|
|
52
|
+
child.kill('SIGKILL');
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* ignore */
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
const killer = nodeSpawn('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore' });
|
|
60
|
+
killer.on('error', fallback); // spawn 失败(ENOENT 等)是异步 'error',无监听器会崩宿主
|
|
61
|
+
killer.unref();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
fallback();
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
killGroup(child, 'SIGKILL');
|
|
69
|
+
}
|
|
70
|
+
/** 进程组是否还有成员:负 pid 发 0 号信号只探测不打(组已空则 ESRCH)。 */
|
|
71
|
+
function groupAlive(pid) {
|
|
72
|
+
try {
|
|
73
|
+
process.kill(-pid, 0);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
12
80
|
export class LocalProcessService {
|
|
13
81
|
storage;
|
|
82
|
+
/**
|
|
83
|
+
* 存活进程组登记表(键 = 直接子进程 pid = pgid)。detached 后终端 Ctrl+C 不再直达子进程,改由 killAll
|
|
84
|
+
* 在停机时统一收尸;`cmd &` 留下的孙进程在直接子进程退出后仍占着这个组,所以按组而非按子进程登记。
|
|
85
|
+
*/
|
|
86
|
+
groups = new Map();
|
|
14
87
|
constructor(storage) {
|
|
15
88
|
this.storage = storage;
|
|
16
89
|
}
|
|
90
|
+
/** 宿主停机时把仍有成员的进程组整组杀掉,维持「Aalis 退出,工具子进程一起退出」。 */
|
|
91
|
+
killAll() {
|
|
92
|
+
for (const child of this.groups.values())
|
|
93
|
+
killTree(child);
|
|
94
|
+
this.groups.clear();
|
|
95
|
+
}
|
|
17
96
|
spawn(cmd, args, opts = {}) {
|
|
18
97
|
const stdioMode = opts.stdio ?? 'pipe';
|
|
19
98
|
const child = nodeSpawn(cmd, [...args], {
|
|
20
99
|
cwd: opts.cwd,
|
|
21
100
|
env: { ...process.env, ...(opts.env ?? {}) },
|
|
22
101
|
stdio: stdioMode === 'pipe' ? ['pipe', 'pipe', 'pipe'] : stdioMode,
|
|
23
|
-
|
|
102
|
+
// POSIX 一律 detached:让子进程自成进程组组长,超时/停机才能对整组发信号(见 killGroup)。
|
|
103
|
+
// opts.detached 的 fire-and-forget 语义仍由调用方的 stdio:'ignore' + unref() 兑现。
|
|
104
|
+
detached: isWindows ? opts.detached === true : true,
|
|
24
105
|
});
|
|
106
|
+
// 调用方显式 detached 的(fire-and-forget,如拉起浏览器)不登记:它本就该独立于宿主生存
|
|
107
|
+
if (child.pid !== undefined && opts.detached !== true) {
|
|
108
|
+
const pid = child.pid;
|
|
109
|
+
this.groups.set(pid, child);
|
|
110
|
+
// 直接子进程退了不等于组空:孙进程还在就留到停机收尸。pgid 被占用期间该 pid 不会被复用,
|
|
111
|
+
// 负 pid 只会指向我们自己建的这个组。
|
|
112
|
+
child.on('exit', () => {
|
|
113
|
+
if (isWindows || !groupAlive(pid))
|
|
114
|
+
this.groups.delete(pid);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
25
117
|
if (opts.input != null && stdioMode === 'pipe') {
|
|
26
118
|
// stdin 写入的 EPIPE 等是异步 'error' 事件;无监听器会 uncaughtException 崩整个宿主进程。
|
|
27
119
|
child.stdin?.on('error', () => { });
|
|
@@ -34,21 +126,20 @@ export class LocalProcessService {
|
|
|
34
126
|
}
|
|
35
127
|
let timer;
|
|
36
128
|
if (opts.timeout && opts.timeout > 0) {
|
|
37
|
-
timer = setTimeout(() =>
|
|
38
|
-
try {
|
|
39
|
-
child.kill('SIGKILL');
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
/* ignore */
|
|
43
|
-
}
|
|
44
|
-
}, opts.timeout);
|
|
129
|
+
timer = setTimeout(() => killTree(child), opts.timeout);
|
|
45
130
|
}
|
|
131
|
+
// 契约不要求调用方 wait():子进程自己退了也要清掉定时器,免得到点对可能已被复用的组发 SIGKILL
|
|
132
|
+
child.on('exit', () => {
|
|
133
|
+
if (timer)
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
});
|
|
46
136
|
const handle = {
|
|
47
137
|
pid: child.pid,
|
|
48
138
|
stdin: child.stdin,
|
|
49
139
|
stdout: child.stdout,
|
|
50
140
|
stderr: child.stderr,
|
|
51
|
-
|
|
141
|
+
// POSIX 打整个进程组(孙进程同死);Windows 无此语义,维持只打子进程本身。
|
|
142
|
+
kill: signal => (isWindows ? child.kill(signal) : killGroup(child, signal)),
|
|
52
143
|
unref: () => {
|
|
53
144
|
try {
|
|
54
145
|
child.unref();
|
|
@@ -66,9 +157,10 @@ export class LocalProcessService {
|
|
|
66
157
|
const chunksErr = [];
|
|
67
158
|
let total = 0;
|
|
68
159
|
let truncated = false;
|
|
160
|
+
let settled = false;
|
|
69
161
|
const collect = (arr, d) => {
|
|
70
|
-
if (truncated)
|
|
71
|
-
return;
|
|
162
|
+
if (truncated || settled)
|
|
163
|
+
return; // settled:宽限已过,管道保持打开但之后的输出丢弃
|
|
72
164
|
const b = Buffer.from(d);
|
|
73
165
|
const room = maxBuffer - total;
|
|
74
166
|
if (b.length >= room) {
|
|
@@ -83,14 +175,18 @@ export class LocalProcessService {
|
|
|
83
175
|
};
|
|
84
176
|
child.stdout?.on('data', d => collect(chunksOut, d));
|
|
85
177
|
child.stderr?.on('data', d => collect(chunksErr, d));
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
clearTimeout(timer);
|
|
89
|
-
reject(err);
|
|
90
|
-
});
|
|
91
|
-
child.on('close', (code, signal) => {
|
|
178
|
+
let graceTimer;
|
|
179
|
+
const clearTimers = () => {
|
|
92
180
|
if (timer)
|
|
93
181
|
clearTimeout(timer);
|
|
182
|
+
if (graceTimer)
|
|
183
|
+
clearTimeout(graceTimer);
|
|
184
|
+
};
|
|
185
|
+
const finish = (code, signal) => {
|
|
186
|
+
if (settled)
|
|
187
|
+
return;
|
|
188
|
+
settled = true;
|
|
189
|
+
clearTimers();
|
|
94
190
|
resolve({
|
|
95
191
|
code,
|
|
96
192
|
signal,
|
|
@@ -98,6 +194,23 @@ export class LocalProcessService {
|
|
|
98
194
|
stderr: Buffer.concat(chunksErr).toString('utf-8'),
|
|
99
195
|
...(truncated ? { truncated: true } : {}),
|
|
100
196
|
});
|
|
197
|
+
};
|
|
198
|
+
child.on('error', err => {
|
|
199
|
+
if (settled)
|
|
200
|
+
return;
|
|
201
|
+
settled = true;
|
|
202
|
+
clearTimers();
|
|
203
|
+
reject(err);
|
|
204
|
+
});
|
|
205
|
+
// 正常情况 'close'(pipe 全关)先于宽限到点,输出完整。
|
|
206
|
+
child.on('close', (code, signal) => finish(code, signal));
|
|
207
|
+
child.on('exit', (code, signal) => {
|
|
208
|
+
if (settled)
|
|
209
|
+
return;
|
|
210
|
+
// 子进程已死但 'close' 未到 = 孙进程扣着 pipe 不放。给一个短宽限收尾,到点仍不来就用已收集的输出返回。
|
|
211
|
+
// 不销毁读端:销毁会让 `npm run dev &` 这类后台进程在下次写日志时吃 EPIPE 死掉;
|
|
212
|
+
// 管道保持打开、之后的输出经 collect 丢弃,该进程组留待停机收尸。
|
|
213
|
+
graceTimer = setTimeout(() => finish(code, signal), CLOSE_GRACE_MS);
|
|
101
214
|
});
|
|
102
215
|
}),
|
|
103
216
|
};
|
|
@@ -126,8 +239,9 @@ export async function apply(ctx) {
|
|
|
126
239
|
const storage = createStorageGateway(ctx);
|
|
127
240
|
const service = new LocalProcessService(storage);
|
|
128
241
|
ctx.provide('process', service);
|
|
242
|
+
// detached 让子进程脱离宿主进程组,终端 Ctrl+C 不再直达它们——停机时在此补杀。
|
|
243
|
+
// 挂 app:stopping 而非插件 dispose:本插件被 bounce 时,别的插件正在跑的子进程(ffmpeg 等)不该陪葬。
|
|
244
|
+
ctx.on('app:stopping', () => service.killAll());
|
|
129
245
|
logger.info('process-local 就绪');
|
|
130
246
|
}
|
|
131
|
-
const plugin = { name, apply };
|
|
132
|
-
export default plugin;
|
|
133
247
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,kDAAkD;AAClD,+DAA+D;AAE/D,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,kDAAkD;AAClD,+DAA+D;AAE/D,OAAO,EAAqB,KAAK,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAG1D,MAAM,CAAC,MAAM,IAAI,GAAG,6BAA6B,CAAC;AAClD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,CAAC,CAAC;AAEpC,gEAAgE;AAChE,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;;;GAIG;AACH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAE/C;;;;GAIG;AACH,SAAS,SAAS,CAAC,KAAmB,EAAE,MAAuB;IAC7D,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,KAAmB;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO;IAC9B,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,QAAQ,GAAG,GAAS,EAAE;YAC1B,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC,CAAC;QACF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7F,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,yCAAyC;YACvE,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,EAAE,CAAC;QACb,CAAC;QACD,OAAO;IACT,CAAC;IACD,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AAC9B,CAAC;AAED,gDAAgD;AAChD,SAAS,UAAU,CAAC,GAAW;IAC7B,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,OAAO,mBAAmB;IAOD;IAN7B;;;OAGG;IACc,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE1D,YAA6B,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAExD,iDAAiD;IACjD,OAAO;QACL,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,GAAW,EAAE,IAAuB,EAAE,OAAqB,EAAE;QACjE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;QACvC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE;YACtC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAAuB;YACjE,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;YAClE,4DAA4D;YAC5D,wEAAwE;YACxE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI;SACpD,CAAC,CAAC;QACH,0DAA0D;QAC1D,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtD,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC5B,mDAAmD;YACnD,uBAAuB;YACvB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;gBACpB,IAAI,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;QACL,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YAC/C,mEAAmE;YACnE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACnC,IAAI,CAAC;gBACH,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;QACD,IAAI,KAAiC,CAAC;QACtC,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACrC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1D,CAAC;QACD,wDAAwD;QACxD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACpB,IAAI,KAAK;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAgB;YAC1B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,8CAA8C;YAC9C,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC3E,KAAK,EAAE,GAAG,EAAE;gBACV,IAAI,CAAC;oBACH,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY;gBACd,CAAC;YACH,CAAC;YACD,IAAI,EAAE,GAAG,EAAE,CACT,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,4DAA4D;gBAC5D,kDAAkD;gBAClD,0EAA0E;gBAC1E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC;gBAC7F,MAAM,SAAS,GAAa,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;gBAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;gBACd,IAAI,SAAS,GAAG,KAAK,CAAC;gBACtB,IAAI,OAAO,GAAG,KAAK,CAAC;gBACpB,MAAM,OAAO,GAAG,CAAC,GAAa,EAAE,CAAU,EAAQ,EAAE;oBAClD,IAAI,SAAS,IAAI,OAAO;wBAAE,OAAO,CAAC,8BAA8B;oBAChE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAe,CAAC,CAAC;oBACvC,MAAM,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;oBAC/B,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;wBACrB,IAAI,IAAI,GAAG,CAAC;4BAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;wBAC5C,KAAK,GAAG,SAAS,CAAC;wBAClB,SAAS,GAAG,IAAI,CAAC;wBACjB,OAAO,CAAC,sDAAsD;oBAChE,CAAC;oBACD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBACZ,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;gBACpB,CAAC,CAAC;gBACF,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;gBACrD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;gBACrD,IAAI,UAAsC,CAAC;gBAC3C,MAAM,WAAW,GAAG,GAAS,EAAE;oBAC7B,IAAI,KAAK;wBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;oBAC/B,IAAI,UAAU;wBAAE,YAAY,CAAC,UAAU,CAAC,CAAC;gBAC3C,CAAC,CAAC;gBACF,MAAM,MAAM,GAAG,CAAC,IAAmB,EAAE,MAA6B,EAAQ,EAAE;oBAC1E,IAAI,OAAO;wBAAE,OAAO;oBACpB,OAAO,GAAG,IAAI,CAAC;oBACf,WAAW,EAAE,CAAC;oBACd,OAAO,CAAC;wBACN,IAAI;wBACJ,MAAM;wBACN,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;wBAClD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;wBAClD,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAC1C,CAAC,CAAC;gBACL,CAAC,CAAC;gBACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;oBACtB,IAAI,OAAO;wBAAE,OAAO;oBACpB,OAAO,GAAG,IAAI,CAAC;oBACf,WAAW,EAAE,CAAC;oBACd,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC,CAAC,CAAC;gBACH,oCAAoC;gBACpC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC1D,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;oBAChC,IAAI,OAAO;wBAAE,OAAO;oBACpB,8DAA8D;oBAC9D,sDAAsD;oBACtD,uCAAuC;oBACvC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC;gBACtE,CAAC,CAAC,CAAC;YACL,CAAC,CAAC;SACL,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,GAAW,EAAE,IAAuB,EAAE,OAAqB,EAAE;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,IAAI,KAAK,CACnB,YAAY,GAAG,QAAQ,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACpF,CAAC;YACrC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAc;QAC9B,OAAO,qBAAqB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAY;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY;IACtC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACjD,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChC,kDAAkD;IAClD,uEAAuE;IACvE,GAAG,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAChD,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;AAClC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aalis/plugin-process-local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/AalisLabs/Aalis.git",
|
|
8
|
+
"directory": "packages/plugin-process-local"
|
|
9
|
+
},
|
|
10
|
+
"author": "Ace Nyan <ace@acenyan.com>",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/AalisLabs/Aalis/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/AalisLabs/Aalis#readme",
|
|
5
15
|
"description": "进程网关本地实现:受控执行子进程",
|
|
6
16
|
"type": "module",
|
|
7
17
|
"keywords": [
|
|
@@ -14,13 +24,13 @@
|
|
|
14
24
|
"dist"
|
|
15
25
|
],
|
|
16
26
|
"dependencies": {
|
|
17
|
-
"@aalis/
|
|
18
|
-
"@aalis/
|
|
27
|
+
"@aalis/api-process": ">=0.5.0 <1.0.0",
|
|
28
|
+
"@aalis/api-storage": ">=0.5.5 <1.0.0"
|
|
19
29
|
},
|
|
20
30
|
"devDependencies": {
|
|
21
31
|
"@types/node": "^22.0.0",
|
|
22
32
|
"typescript": "^5.7.0",
|
|
23
|
-
"@aalis/core": "0.
|
|
33
|
+
"@aalis/core": "0.12.1"
|
|
24
34
|
},
|
|
25
35
|
"peerDependencies": {
|
|
26
36
|
"@aalis/core": ">=0.2.0 <1.0.0"
|
package/dist/sandbox.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { SandboxPolicy } from '@aalis/plugin-process-api';
|
|
2
|
-
export type SandboxBackend = 'seatbelt' | 'bwrap' | 'none';
|
|
3
|
-
export interface SandboxWrap {
|
|
4
|
-
cmd: string;
|
|
5
|
-
args: string[];
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* 按平台 + 命令存在性探测可用后端(纯函数;commandExists 注入便于测试)。
|
|
9
|
-
* 注意:仅判「启动器是否存在」,bwrap 是否真能跑(unprivileged userns)由调用方再做功能性 smoke。
|
|
10
|
-
*/
|
|
11
|
-
export declare function detectSandboxBackend(platform: NodeJS.Platform, commandExists: (cmd: string) => boolean): SandboxBackend;
|
|
12
|
-
/**
|
|
13
|
-
* 生成 macOS Seatbelt (SBPL) profile:默认拒绝;放开 进程/读/常见系统调用;
|
|
14
|
-
* 写仅限 fsWrite + /dev 标准设备;网络按 policy。
|
|
15
|
-
*/
|
|
16
|
-
export declare function buildSeatbeltProfile(policy: SandboxPolicy): string;
|
|
17
|
-
/** 生成 Linux bubblewrap 参数:只读绑定根(可读+取解释器/库)、rw 绑定写白名单、按 policy 隔离网络。 */
|
|
18
|
-
export declare function buildBwrapArgs(policy: SandboxPolicy, cmd: string, args: readonly string[], cwd?: string): SandboxWrap;
|
|
19
|
-
/** 按后端把 (cmd,args) 改写为「经沙箱启动器运行」。backend==='none' 不应调用(调用方先 fail-closed)。 */
|
|
20
|
-
export declare function wrapForSandbox(backend: SandboxBackend, policy: SandboxPolicy, cmd: string, args: readonly string[], cwd?: string): SandboxWrap;
|
|
21
|
-
//# sourceMappingURL=sandbox.d.ts.map
|
package/dist/sandbox.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACtC,cAAc,CAIhB;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAgBlE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,CAAC,EAAE,MAAM,GACX,WAAW,CAqBb;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,CAAC,EAAE,MAAM,GACX,WAAW,CAQb"}
|
package/dist/sandbox.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// sandbox.ts — 子进程 OS 沙箱:后端探测 + 命令改写(纯逻辑,便于单测)
|
|
3
|
-
//
|
|
4
|
-
// 把不可信代码的子进程包到 OS 沙箱启动器里运行(仍 shell-free):
|
|
5
|
-
// - macOS → sandbox-exec -p '<SBPL>' <cmd> <args>
|
|
6
|
-
// - Linux → bwrap <flags> -- <cmd> <args>
|
|
7
|
-
// v1 语义:读放开(解释器需系统库)、写限定 fsWrite、网络粗粒度开关。
|
|
8
|
-
// 实际可用性探测(含功能性 smoke)在 index.ts 做;本文件只做纯生成/判定。
|
|
9
|
-
// ============================================================
|
|
10
|
-
/**
|
|
11
|
-
* 按平台 + 命令存在性探测可用后端(纯函数;commandExists 注入便于测试)。
|
|
12
|
-
* 注意:仅判「启动器是否存在」,bwrap 是否真能跑(unprivileged userns)由调用方再做功能性 smoke。
|
|
13
|
-
*/
|
|
14
|
-
export function detectSandboxBackend(platform, commandExists) {
|
|
15
|
-
if (platform === 'darwin')
|
|
16
|
-
return commandExists('sandbox-exec') ? 'seatbelt' : 'none';
|
|
17
|
-
if (platform === 'linux')
|
|
18
|
-
return commandExists('bwrap') ? 'bwrap' : 'none';
|
|
19
|
-
return 'none';
|
|
20
|
-
}
|
|
21
|
-
/** SBPL 字符串字面量转义 */
|
|
22
|
-
function sbplQuote(p) {
|
|
23
|
-
return `"${p.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* 生成 macOS Seatbelt (SBPL) profile:默认拒绝;放开 进程/读/常见系统调用;
|
|
27
|
-
* 写仅限 fsWrite + /dev 标准设备;网络按 policy。
|
|
28
|
-
*/
|
|
29
|
-
export function buildSeatbeltProfile(policy) {
|
|
30
|
-
const lines = [
|
|
31
|
-
'(version 1)',
|
|
32
|
-
'(deny default)',
|
|
33
|
-
'(allow process-fork)',
|
|
34
|
-
'(allow process-exec)',
|
|
35
|
-
'(allow file-read*)',
|
|
36
|
-
'(allow sysctl-read)',
|
|
37
|
-
'(allow mach-lookup)',
|
|
38
|
-
'(allow ipc-posix-shm)',
|
|
39
|
-
'(allow signal)',
|
|
40
|
-
'(allow file-write-data (literal "/dev/null") (literal "/dev/stdout") (literal "/dev/stderr") (literal "/dev/dtracehelper") (literal "/dev/urandom"))',
|
|
41
|
-
];
|
|
42
|
-
for (const dir of policy.fsWrite)
|
|
43
|
-
lines.push(`(allow file-write* (subpath ${sbplQuote(dir)}))`);
|
|
44
|
-
lines.push(policy.network === 'allow' ? '(allow network*)' : '(deny network*)');
|
|
45
|
-
return `${lines.join('\n')}\n`;
|
|
46
|
-
}
|
|
47
|
-
/** 生成 Linux bubblewrap 参数:只读绑定根(可读+取解释器/库)、rw 绑定写白名单、按 policy 隔离网络。 */
|
|
48
|
-
export function buildBwrapArgs(policy, cmd, args, cwd) {
|
|
49
|
-
const a = [
|
|
50
|
-
'--ro-bind',
|
|
51
|
-
'/',
|
|
52
|
-
'/',
|
|
53
|
-
'--dev',
|
|
54
|
-
'/dev',
|
|
55
|
-
'--proc',
|
|
56
|
-
'/proc',
|
|
57
|
-
'--tmpfs',
|
|
58
|
-
'/tmp', // 私有可写 /tmp(解释器/库常用);写白名单的 bind 在其后,故仍可覆盖回真实子目录
|
|
59
|
-
'--unshare-all', // 含 net 命名空间隔离(断网)
|
|
60
|
-
'--die-with-parent',
|
|
61
|
-
'--new-session',
|
|
62
|
-
];
|
|
63
|
-
if (policy.network === 'allow')
|
|
64
|
-
a.push('--share-net');
|
|
65
|
-
// rw 绑定写白名单(顺序在 --tmpfs /tmp 之后,确保 /tmp 下的真实临时目录被重新暴露为可写)
|
|
66
|
-
for (const dir of policy.fsWrite)
|
|
67
|
-
a.push('--bind', dir, dir);
|
|
68
|
-
if (cwd)
|
|
69
|
-
a.push('--chdir', cwd);
|
|
70
|
-
a.push('--', cmd, ...args);
|
|
71
|
-
return { cmd: 'bwrap', args: a };
|
|
72
|
-
}
|
|
73
|
-
/** 按后端把 (cmd,args) 改写为「经沙箱启动器运行」。backend==='none' 不应调用(调用方先 fail-closed)。 */
|
|
74
|
-
export function wrapForSandbox(backend, policy, cmd, args, cwd) {
|
|
75
|
-
if (backend === 'seatbelt') {
|
|
76
|
-
return { cmd: 'sandbox-exec', args: ['-p', buildSeatbeltProfile(policy), cmd, ...args] };
|
|
77
|
-
}
|
|
78
|
-
if (backend === 'bwrap') {
|
|
79
|
-
return buildBwrapArgs(policy, cmd, args, cwd);
|
|
80
|
-
}
|
|
81
|
-
throw new Error(`不支持的沙箱后端: ${backend}`);
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=sandbox.js.map
|
package/dist/sandbox.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|