@actiondock/core 2.2.2 → 2.3.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.
Files changed (46) hide show
  1. package/README.md +8 -3
  2. package/dist/app/app.d.ts +2 -0
  3. package/dist/app/app.js +6 -0
  4. package/dist/app/types.d.ts +12 -0
  5. package/dist/errors.d.ts +38 -0
  6. package/dist/errors.js +44 -0
  7. package/dist/execution/service.d.ts +2 -0
  8. package/dist/execution/service.js +13 -2
  9. package/dist/execution/types.d.ts +17 -0
  10. package/dist/host/host.js +46 -5
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/ipc/host.d.ts +5 -0
  14. package/dist/ipc/host.js +43 -1
  15. package/dist/ipc/target.d.ts +18 -0
  16. package/dist/ipc/target.js +74 -6
  17. package/dist/ipc/types.d.ts +10 -1
  18. package/dist/platform/default.d.ts +7 -1
  19. package/dist/platform/default.js +40 -2
  20. package/dist/process/context-process.d.ts +83 -0
  21. package/dist/process/context-process.js +168 -0
  22. package/dist/process/cursor.d.ts +52 -0
  23. package/dist/process/cursor.js +144 -0
  24. package/dist/process/driver.d.ts +214 -0
  25. package/dist/process/driver.js +197 -0
  26. package/dist/process/index.d.ts +6 -0
  27. package/dist/process/index.js +6 -0
  28. package/dist/process/metadata-store.d.ts +205 -0
  29. package/dist/process/metadata-store.js +462 -0
  30. package/dist/process/output-log.d.ts +136 -0
  31. package/dist/process/output-log.js +331 -0
  32. package/dist/process/process-manager.d.ts +279 -0
  33. package/dist/process/process-manager.js +1879 -0
  34. package/dist/profile/client.js +63 -21
  35. package/dist/project/init.js +2 -2
  36. package/dist/registry/registry.js +12 -3
  37. package/dist/runtime/context.d.ts +9 -6
  38. package/dist/runtime/context.js +31 -8
  39. package/dist/runtime/process.d.ts +19 -2
  40. package/dist/runtime/process.js +103 -131
  41. package/dist/runtime/runner.d.ts +45 -24
  42. package/dist/runtime/runner.js +106 -25
  43. package/dist/target/remote.js +7 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +2 -2
@@ -0,0 +1,331 @@
1
+ import { INVALID_CURSOR, OUTPUT_GAP, PROCESS_CANCELLED, QUOTA_EXCEEDED, ProcessError, } from "../errors.js";
2
+ import { compareCursorPos, encodeCursor, parseCursor } from "./cursor.js";
3
+ /**
4
+ * 基于内存有界环形缓冲区的受管进程原始字节输出日志。
5
+ *
6
+ * 职责与不变量:
7
+ * - 保留原始字节流,不做字符集猜测与跨流重排序。
8
+ * - 维护单调推进的游标系统,支持按记录切分与不透明游标寻址。
9
+ * - 缓冲区超额时淘汰最旧数据并更新最早保留游标。
10
+ * - 长轮询等待机制保证状态检查与等待者注册处于同一同步边界,杜绝漏唤醒。
11
+ * - 严格遵循等待者配额,超额抛出配额超限异常。
12
+ */
13
+ export class ProcessOutputLog {
14
+ hostEpoch;
15
+ processId;
16
+ maxBufferBytes;
17
+ maxWaiters;
18
+ records = [];
19
+ totalBytes = 0;
20
+ nextSequence;
21
+ earliestCursorState;
22
+ tailCursorState;
23
+ outputClosedState = false;
24
+ outputEndReasonState;
25
+ waiters = new Set();
26
+ constructor(hostEpoch, processId, options = {}) {
27
+ this.hostEpoch = hostEpoch;
28
+ this.processId = processId;
29
+ this.maxBufferBytes = options.maxBufferBytes ?? 4 * 1024 * 1024;
30
+ this.maxWaiters = options.maxWaiters ?? 8;
31
+ this.nextSequence = options.initialSequence ?? 0;
32
+ const initial = encodeCursor(this.hostEpoch, this.processId, this.nextSequence, 0);
33
+ this.earliestCursorState = initial;
34
+ this.tailCursorState = initial;
35
+ }
36
+ /**
37
+ * 当前保留日志的最早可用游标。
38
+ */
39
+ get earliestCursor() {
40
+ return this.earliestCursorState;
41
+ }
42
+ /**
43
+ * 当前日志尾部的下一写入游标。
44
+ */
45
+ get tailCursor() {
46
+ return this.tailCursorState;
47
+ }
48
+ /**
49
+ * 输出通道是否已关闭。
50
+ */
51
+ get outputClosed() {
52
+ return this.outputClosedState;
53
+ }
54
+ /**
55
+ * 输出通道关闭原因。
56
+ */
57
+ get outputEndReason() {
58
+ return this.outputEndReasonState;
59
+ }
60
+ /**
61
+ * 当前缓冲区占用的原始字节总数。
62
+ */
63
+ get currentBytes() {
64
+ return this.totalBytes;
65
+ }
66
+ /**
67
+ * 当前正在挂起等待的读取者数量。
68
+ */
69
+ get waiterCount() {
70
+ return this.waiters.size;
71
+ }
72
+ /**
73
+ * 向输出日志追加新的原始字节数据。
74
+ *
75
+ * 行为约束:
76
+ * - 记录新数据并更新日志尾部游标。
77
+ * - 若总字节数超出缓冲区上限,淘汰最旧记录并更新最早保留游标。
78
+ * - 同步唤醒所有挂起等待者。
79
+ */
80
+ append(stream, data) {
81
+ if (data.byteLength === 0) {
82
+ return;
83
+ }
84
+ const sequence = this.nextSequence++;
85
+ const record = {
86
+ sequence,
87
+ stream,
88
+ data: new Uint8Array(data.buffer, data.byteOffset, data.byteLength),
89
+ offset: 0,
90
+ originalLength: data.byteLength,
91
+ };
92
+ this.records.push(record);
93
+ this.totalBytes += record.data.byteLength;
94
+ this.tailCursorState = encodeCursor(this.hostEpoch, this.processId, this.nextSequence, 0);
95
+ while (this.totalBytes > this.maxBufferBytes && this.records.length > 0) {
96
+ if (this.records.length > 1) {
97
+ const evicted = this.records.shift();
98
+ this.totalBytes -= evicted.data.byteLength;
99
+ const first = this.records[0];
100
+ this.earliestCursorState = encodeCursor(this.hostEpoch, this.processId, first.sequence, first.offset);
101
+ }
102
+ else {
103
+ const single = this.records[0];
104
+ const overflow = this.totalBytes - this.maxBufferBytes;
105
+ single.offset += overflow;
106
+ single.data = single.data.subarray(overflow);
107
+ this.totalBytes = single.data.byteLength;
108
+ this.earliestCursorState = encodeCursor(this.hostEpoch, this.processId, single.sequence, single.offset);
109
+ break;
110
+ }
111
+ }
112
+ this.notifyWaiters();
113
+ }
114
+ /**
115
+ * 标记输出通道关闭并唤醒所有等待者。
116
+ */
117
+ closeOutput(reason = "natural") {
118
+ if (this.outputClosedState) {
119
+ return;
120
+ }
121
+ this.outputClosedState = true;
122
+ this.outputEndReasonState = reason;
123
+ this.notifyWaiters();
124
+ }
125
+ /**
126
+ * 从指定游标处读取输出数据。
127
+ *
128
+ * 行为约束:
129
+ * - 校验游标是否超出当前日志末尾,超出则抛出 INVALID_CURSOR 异常。
130
+ * - 检查游标是否落后于最早保留游标:
131
+ * - 若落后且 onGap 为 error,抛出 OUTPUT_GAP 异常并附带当前最早游标。
132
+ * - 若落后且 onGap 为 skip,从最早可用游标开始读取,标记 truncated 为 true 并提供缺口范围。
133
+ * - 支持记录切分并生成带有内部偏移量的不透明推进游标。
134
+ */
135
+ read(cursor, maxBytes, onGap) {
136
+ const limitBytes = maxBytes !== undefined && maxBytes > 0 ? maxBytes : 64 * 1024;
137
+ const gapMode = onGap ?? "error";
138
+ const requestedPos = parseCursor(cursor, this.hostEpoch, this.processId);
139
+ const tailPos = parseCursor(this.tailCursorState, this.hostEpoch, this.processId);
140
+ const earliestPos = parseCursor(this.earliestCursorState, this.hostEpoch, this.processId);
141
+ if (compareCursorPos(requestedPos, tailPos) > 0) {
142
+ throw new ProcessError(INVALID_CURSOR, "Cursor is beyond tail cursor", {
143
+ cursor,
144
+ tailCursor: this.tailCursorState,
145
+ });
146
+ }
147
+ let readPos = requestedPos;
148
+ let skippedGap = false;
149
+ let gapInfo;
150
+ if (compareCursorPos(requestedPos, earliestPos) < 0) {
151
+ if (gapMode === "error") {
152
+ throw new ProcessError(OUTPUT_GAP, "Output log cursor has fallen behind earliest retained cursor", {
153
+ cursor,
154
+ earliestCursor: this.earliestCursorState,
155
+ });
156
+ }
157
+ skippedGap = true;
158
+ readPos = earliestPos;
159
+ gapInfo = {
160
+ fromCursor: cursor,
161
+ toCursor: this.earliestCursorState,
162
+ };
163
+ }
164
+ if (compareCursorPos(readPos, tailPos) === 0) {
165
+ const eof = Boolean(this.outputClosedState);
166
+ return {
167
+ chunks: [],
168
+ nextCursor: skippedGap ? this.earliestCursorState : cursor,
169
+ earliestCursor: this.earliestCursorState,
170
+ tailCursor: this.tailCursorState,
171
+ truncated: skippedGap,
172
+ gap: gapInfo,
173
+ eof,
174
+ };
175
+ }
176
+ let curSeq = readPos.sequence;
177
+ let curOffset = readPos.offset;
178
+ let remainingBytes = limitBytes;
179
+ const chunks = [];
180
+ for (const record of this.records) {
181
+ if (record.sequence < curSeq) {
182
+ continue;
183
+ }
184
+ if (record.sequence > curSeq) {
185
+ curSeq = record.sequence;
186
+ curOffset = record.offset;
187
+ }
188
+ if (remainingBytes <= 0) {
189
+ break;
190
+ }
191
+ if (curOffset > record.originalLength) {
192
+ throw new ProcessError(INVALID_CURSOR, "Cursor offset exceeds record length", {
193
+ cursor,
194
+ sequence: curSeq,
195
+ offset: curOffset,
196
+ recordLength: record.originalLength,
197
+ });
198
+ }
199
+ if (curOffset < record.offset) {
200
+ curOffset = record.offset;
201
+ }
202
+ const localOffset = curOffset - record.offset;
203
+ const availableInRecord = record.data.byteLength - localOffset;
204
+ if (availableInRecord > 0) {
205
+ const take = Math.min(availableInRecord, remainingBytes);
206
+ const chunkBytes = record.data.subarray(localOffset, localOffset + take);
207
+ chunks.push({
208
+ stream: record.stream,
209
+ data: chunkBytes,
210
+ });
211
+ remainingBytes -= take;
212
+ curOffset += take;
213
+ }
214
+ if (curOffset >= record.originalLength) {
215
+ curSeq = record.sequence + 1;
216
+ curOffset = 0;
217
+ }
218
+ if (remainingBytes <= 0) {
219
+ break;
220
+ }
221
+ }
222
+ const nextCursor = encodeCursor(this.hostEpoch, this.processId, curSeq, curOffset);
223
+ const currentEndPos = { sequence: curSeq, offset: curOffset };
224
+ const isAtTail = compareCursorPos(currentEndPos, tailPos) === 0;
225
+ const eof = Boolean(this.outputClosedState && isAtTail);
226
+ return {
227
+ chunks,
228
+ nextCursor,
229
+ earliestCursor: this.earliestCursorState,
230
+ tailCursor: this.tailCursorState,
231
+ truncated: skippedGap,
232
+ gap: gapInfo,
233
+ eof,
234
+ };
235
+ }
236
+ /**
237
+ * 长轮询等待输出数据或通道关闭。
238
+ *
239
+ * 行为约束:
240
+ * - 状态检查与等待者登记位于同一同步边界,杜绝漏唤醒竞态。
241
+ * - 若已有数据、通道已关闭或超时时长小于等于 0,立即返回读取结果。
242
+ * - 严格校验等待者配额,超额抛出 QUOTA_EXCEEDED 异常。
243
+ * - 外部信号中止时立即清理等待者并不修改日志游标状态。
244
+ * - 等待超时且无新数据时返回空分块结果。
245
+ */
246
+ waitForData(cursor, waitMs, signal, options) {
247
+ const requestedPos = parseCursor(cursor, this.hostEpoch, this.processId);
248
+ const tailPos = parseCursor(this.tailCursorState, this.hostEpoch, this.processId);
249
+ const earliestPos = parseCursor(this.earliestCursorState, this.hostEpoch, this.processId);
250
+ if (compareCursorPos(requestedPos, tailPos) > 0) {
251
+ throw new ProcessError(INVALID_CURSOR, "Cursor is beyond tail cursor", {
252
+ cursor,
253
+ tailCursor: this.tailCursorState,
254
+ });
255
+ }
256
+ const isBehind = compareCursorPos(requestedPos, earliestPos) < 0;
257
+ const hasData = compareCursorPos(requestedPos, tailPos) < 0;
258
+ if (isBehind || hasData || this.outputClosedState || waitMs <= 0) {
259
+ return Promise.resolve(this.read(cursor, options?.maxBytes, options?.onGap));
260
+ }
261
+ if (signal?.aborted) {
262
+ return Promise.reject(new ProcessError(PROCESS_CANCELLED, "Wait aborted", { reason: signal.reason }));
263
+ }
264
+ if (this.waiters.size >= this.maxWaiters) {
265
+ return Promise.reject(new ProcessError(QUOTA_EXCEEDED, `Read waiters quota exceeded: ${this.maxWaiters}`, {
266
+ maxWaiters: this.maxWaiters,
267
+ currentWaiters: this.waiters.size,
268
+ }));
269
+ }
270
+ return new Promise((resolve, reject) => {
271
+ let timer = null;
272
+ let abortHandler = null;
273
+ const cleanup = () => {
274
+ if (timer !== null) {
275
+ clearTimeout(timer);
276
+ timer = null;
277
+ }
278
+ if (signal && abortHandler) {
279
+ signal.removeEventListener("abort", abortHandler);
280
+ abortHandler = null;
281
+ }
282
+ this.waiters.delete(wake);
283
+ };
284
+ const wake = () => {
285
+ cleanup();
286
+ try {
287
+ const result = this.read(cursor, options?.maxBytes, options?.onGap);
288
+ resolve(result);
289
+ }
290
+ catch (err) {
291
+ reject(err);
292
+ }
293
+ };
294
+ if (signal) {
295
+ abortHandler = () => {
296
+ cleanup();
297
+ reject(new ProcessError(PROCESS_CANCELLED, "Wait aborted by signal", {
298
+ reason: signal.reason,
299
+ }));
300
+ };
301
+ signal.addEventListener("abort", abortHandler, { once: true });
302
+ }
303
+ if (waitMs > 0 && waitMs !== Infinity) {
304
+ timer = setTimeout(() => {
305
+ cleanup();
306
+ try {
307
+ const result = this.read(cursor, options?.maxBytes, options?.onGap);
308
+ resolve(result);
309
+ }
310
+ catch (err) {
311
+ reject(err);
312
+ }
313
+ }, waitMs);
314
+ if (typeof timer?.unref === "function") {
315
+ timer.unref();
316
+ }
317
+ }
318
+ this.waiters.add(wake);
319
+ });
320
+ }
321
+ notifyWaiters() {
322
+ if (this.waiters.size === 0) {
323
+ return;
324
+ }
325
+ const pending = Array.from(this.waiters);
326
+ this.waiters.clear();
327
+ for (const wake of pending) {
328
+ wake();
329
+ }
330
+ }
331
+ }
@@ -0,0 +1,279 @@
1
+ import { type CallOptions, type ControlGrant, type Limits, type Logger, type OperationReceipt, type ProcessAcquireInput, type ProcessControlInput, type ProcessInfo, type ProcessListInput, type ProcessListResult, type ProcessReadInput, type ProcessRunInput, type ProcessRunResult, type ProcessStartInput, type ProcessStartResult, type ProcessStopInput, type ProcessWriteInput, type ReadResult } from "@actiondock/sdk";
2
+ import { ContextProcessAPI } from "./context-process.js";
3
+ import type { ProcessDriver } from "./driver.js";
4
+ import { type ProcessMetadataStore } from "./metadata-store.js";
5
+ /**
6
+ * 受管进程归属所有者身份。
7
+ */
8
+ export interface ProcessOwner {
9
+ tenantId: string;
10
+ principalId: string;
11
+ packageInstanceId: string;
12
+ generationId: string;
13
+ }
14
+ /**
15
+ * 宿主与作用域资源配额配置。
16
+ */
17
+ export interface ProcessManagerQuotas {
18
+ /** 每个作用域允许并发存在的活跃进程上限,默认 8 */
19
+ maxActiveProcessesPerScope: number;
20
+ /** 每个宿主允许并发存在的活跃进程上限,默认 64 */
21
+ maxActiveProcessesPerHost: number;
22
+ /** 单个进程输出缓冲区保留字节数上限,默认 4MB */
23
+ maxOutputBufferBytesPerProcess: number;
24
+ /** 宿主所有活跃进程累计输出缓冲区字节数总上限,默认 128MB */
25
+ maxOutputBufferBytesPerHost: number;
26
+ /** 单个进程待写入输入队列保留字节数上限,默认 1MB */
27
+ maxPendingQueueBytesPerProcess: number;
28
+ /** 宿主所有进程待写入输入队列累计字节数总上限,默认 16MB */
29
+ maxPendingQueueBytesPerHost: number;
30
+ /** 单个进程并发等待独占控制权或输出日志的长轮询等待者上限,默认 8 */
31
+ maxWaitersPerProcess: number;
32
+ /** 宿主所有长轮询等待者累计上限,默认 256 */
33
+ maxWaitersPerHost: number;
34
+ }
35
+ /**
36
+ * 进程管理器构造选项。
37
+ */
38
+ export interface ProcessManagerOptions {
39
+ /** 宿主纪元代次标识,不传时自动生成 */
40
+ hostEpoch?: string;
41
+ /** 底层进程驱动实例 */
42
+ driver: ProcessDriver;
43
+ /** 元数据存储后端,默认采用内存存储 */
44
+ metadataStore?: ProcessMetadataStore;
45
+ /** 资源限额与配额覆盖 */
46
+ quotas?: Partial<ProcessManagerQuotas>;
47
+ /** 默认单进程硬性资源限额 */
48
+ defaultLimits?: Partial<Limits>;
49
+ /** 进程退出后输出排空宽限期(毫秒),默认 5000 */
50
+ drainDeadlineMs?: number;
51
+ /** 终态保留输出日志最大缓存保留时长(毫秒),默认 10 分钟 (600,000 毫秒) */
52
+ terminalLogRetentionMs?: number;
53
+ /** 可选结构化日志接口,用于透出持久化失败与驱动终止失败等诊断信息 */
54
+ logger?: Logger;
55
+ }
56
+ /**
57
+ * 格式化生成作用域唯一隔离字符串。
58
+ */
59
+ export declare function formatProcessScope(owner: ProcessOwner): string;
60
+ /**
61
+ * 受管进程核心管理器。
62
+ *
63
+ * 职责范畴:
64
+ * - 宿主身份与配额检查(每作用域活跃进程、每宿主进程、输出缓冲预算、等待者上限、输入队列容量)。
65
+ * - 严格归属所有者鉴权校验(基于 tenantId, principalId, packageInstanceId, generationId)。
66
+ * - 控制权状态机推进(free -> held -> quarantined -> closed)。
67
+ * - 输入队列去重与串行调度执行。
68
+ * - 资源生命周期定时器管理(idleTimeout, maxLifetime, drainDeadline)。
69
+ */
70
+ export declare class ProcessManager {
71
+ readonly hostEpoch: string;
72
+ readonly driver: ProcessDriver;
73
+ readonly metadataStore: ProcessMetadataStore;
74
+ readonly quotas: ProcessManagerQuotas;
75
+ readonly defaultLimits: Required<Limits>;
76
+ readonly drainDeadlineMs: number;
77
+ readonly terminalLogRetentionMs: number;
78
+ private readonly logger?;
79
+ private processes;
80
+ /** 已驱逐进程的输出日志缓存:附带时间戳支撑 TTL 过期回收与 LRU 淘汰 */
81
+ private evictedOutputLogs;
82
+ /** 已淘汰输出日志的墓碑缓存:防止因日志淘汰静默丢失输出数据,支撑缺口明确告知与不可用异常 */
83
+ private evictedOutputTombstones;
84
+ /** 同步幂等预占表:以复合键在异步落盘窗口内锁定并发重复请求 */
85
+ private requestReservations;
86
+ private initPromise;
87
+ private isShutdown;
88
+ /** 内部诊断日志:未注入 logger 时保留最近的持久化与驱动错误,避免静默吞没异常 */
89
+ private diagnostics;
90
+ constructor(options: ProcessManagerOptions);
91
+ /**
92
+ * 初始化宿主环境,原子性收敛旧宿主遗留的非终态进程。
93
+ * 幂等:重复调用返回同一份缓存 Promise。
94
+ */
95
+ initialize(): Promise<number>;
96
+ /**
97
+ * 惰性收敛入口:所有公共操作前调用,确保崩溃恢复无需平台显式接线。
98
+ */
99
+ private ensureInitialized;
100
+ /**
101
+ * 记录内部诊断信息:优先写入注入的 logger,缺失时保留在内存环形缓冲区。
102
+ */
103
+ private recordDiagnostic;
104
+ /**
105
+ * 获取最近的内部诊断日志快照(最近条目在前)。
106
+ */
107
+ get recentDiagnostics(): string[];
108
+ /**
109
+ * 异步落盘进程状态:捕获并记录持久化失败,杜绝静默吞没异常。
110
+ */
111
+ private persistState;
112
+ /**
113
+ * 异步落盘请求凭据:捕获并记录持久化失败,杜绝静默吞没异常。
114
+ */
115
+ private persistReceipt;
116
+ /**
117
+ * 同步预占幂等请求:跨 await 的检查与落盘窗口内锁定同复合键并发调用。
118
+ * 返回 undefined 表示预占成功,调用方继续异步路径并在完成时调用 commit/cancel。
119
+ * 返回 RequestReservation 表示已有同请求进行中,调用方可等待其结果。
120
+ * 若已有请求负载或操作类型不同,立即抛出 REQUEST_CONFLICT 杜绝混用结果。
121
+ */
122
+ private reserveRequest;
123
+ private commitReservation;
124
+ private rejectReservation;
125
+ /**
126
+ * 停止全部受管进程、清理全部定时器并置为关闭态,供宿主优雅退出使用。
127
+ */
128
+ shutdown(): Promise<void>;
129
+ private assertNotShutdown;
130
+ /**
131
+ * 为指定所有者与运行上下文创建 ProcessAPI 代理适配器。
132
+ */
133
+ forOwner(owner: ProcessOwner, runId?: string, signal?: AbortSignal): ContextProcessAPI;
134
+ /**
135
+ * 启动新的受管进程资源。
136
+ */
137
+ start(owner: ProcessOwner, input: ProcessStartInput, call?: CallOptions): Promise<ProcessStartResult>;
138
+ /**
139
+ * 查看指定受管进程资源的当前最新状态。
140
+ */
141
+ inspect(owner: ProcessOwner, id: string, call?: CallOptions): Promise<ProcessInfo>;
142
+ /**
143
+ * 分页列出指定归属所有者可见的受管进程列表。
144
+ */
145
+ list(owner: ProcessOwner, input: ProcessListInput, call?: CallOptions): Promise<ProcessListResult>;
146
+ /**
147
+ * 申请指定受管进程的独占控制令牌。
148
+ */
149
+ acquire(owner: ProcessOwner, id: string, input: ProcessAcquireInput, call?: CallOptions, runId?: string): Promise<ControlGrant>;
150
+ /**
151
+ * 延长当前有效控制令牌的存活时间。
152
+ */
153
+ renew(owner: ProcessOwner, id: string, token: string, ttlMs: number, call?: CallOptions): Promise<ControlGrant>;
154
+ /**
155
+ * 显式释放控制令牌,允许后续控制者申请。
156
+ */
157
+ release(owner: ProcessOwner, id: string, token: string, call?: CallOptions): Promise<void>;
158
+ /**
159
+ * 向受管进程输入流写入原始字节数据。
160
+ */
161
+ write(owner: ProcessOwner, id: string, input: ProcessWriteInput, call?: CallOptions): Promise<OperationReceipt>;
162
+ /**
163
+ * 向受管进程发送结构化控制指令。
164
+ */
165
+ control(owner: ProcessOwner, id: string, input: ProcessControlInput, call?: CallOptions): Promise<OperationReceipt>;
166
+ /**
167
+ * 查询指定请求标识的操作执行收据。
168
+ */
169
+ operation(owner: ProcessOwner, id: string, requestId: string, call?: CallOptions): Promise<OperationReceipt>;
170
+ /**
171
+ * 按游标读取受管进程输出流。
172
+ */
173
+ read(owner: ProcessOwner, id: string, input: ProcessReadInput, call?: CallOptions): Promise<ReadResult>;
174
+ /**
175
+ * 独立鉴权紧急终止通道,强行回收资源。
176
+ */
177
+ stop(owner: ProcessOwner, id: string, input: ProcessStopInput, call?: CallOptions): Promise<ProcessInfo>;
178
+ /**
179
+ * 将进程置入隔离状态,撤销有效控制权并取消待 dispatch 的输入队列。
180
+ */
181
+ quarantineProcess(owner: ProcessOwner, processId: string, token?: string, reason?: string): Promise<void>;
182
+ /**
183
+ * 一次性运行外部命令,收集有限输出并支持协作式取消与超时回收。
184
+ */
185
+ run(owner: ProcessOwner, input: ProcessRunInput, call?: CallOptions): Promise<ProcessRunResult>;
186
+ /**
187
+ * 授予指定受管进程控制令牌。
188
+ */
189
+ private grantControl;
190
+ /**
191
+ * 控制权到期未释放时自动转为隔离状态。
192
+ */
193
+ private handleGrantTtlExpired;
194
+ /**
195
+ * 唤醒排队等待控制权的下一个调用者。
196
+ */
197
+ private wakeNextAcquireWaiter;
198
+ /**
199
+ * 串行调度执行输入队列操作。
200
+ */
201
+ private dispatchNext;
202
+ /**
203
+ * 操作提交前校验持有者令牌与授权。
204
+ */
205
+ private checkOperationAuth;
206
+ /**
207
+ * 启动空闲超时定时器。
208
+ */
209
+ private startIdleTimer;
210
+ /**
211
+ * 刷新空闲超时定时器(仅在成功输入与续租时刷新,输出不刷新)。
212
+ */
213
+ private refreshIdleTimer;
214
+ /**
215
+ * 启动硬性存活上限定时器。
216
+ */
217
+ private startLifetimeTimer;
218
+ /**
219
+ * 处理空闲超时到期。
220
+ */
221
+ private handleIdleTimeout;
222
+ /**
223
+ * 处理存活上限到期。
224
+ */
225
+ private handleLifetimeTimeout;
226
+ /**
227
+ * 处理驱动通知的自然退出或信号退出。
228
+ */
229
+ private handleProcessExit;
230
+ /**
231
+ * 处理驱动通知的输出流彻底关闭事件。
232
+ */
233
+ private handleOutputClosed;
234
+ /**
235
+ * 处理驱动通知的底层故障。
236
+ */
237
+ private handleProcessError;
238
+ /**
239
+ * 加载或读取受管进程记录并校验所有者鉴权。
240
+ */
241
+ private getOrLoadProcess;
242
+ /**
243
+ * 记录已淘汰输出日志的墓碑信息(保留最近 2048 条,防止内存无限积压)。
244
+ */
245
+ private recordTombstone;
246
+ /**
247
+ * 清理已过期的终态输出日志条目。
248
+ */
249
+ private cleanExpiredTerminalLogs;
250
+ /**
251
+ * 统计终态保留日志当前在内存中实际占用的输出缓冲字节总数。
252
+ */
253
+ private countRetainedOutputBufferBytes;
254
+ /**
255
+ * 将终态输出日志存入保留缓存,并根据宿主配额执行 LRU 与 TTL 淘汰。
256
+ */
257
+ private retainTerminalOutputLog;
258
+ /**
259
+ * 获取终态保留日志(附带过期清理与 LRU 触达更新)。
260
+ */
261
+ private getRetainedOutputLog;
262
+ /**
263
+ * 终态驱逐与驱动释放:进程到达终态且输出已关闭后移除内存记录,
264
+ * 并防御性释放底层驱动句柄(失败仅记录诊断不中断)。
265
+ */
266
+ private maybeEvictProcess;
267
+ /**
268
+ * 启动受管进程时的配额容量校验。
269
+ */
270
+ private checkSpawnQuotas;
271
+ /**
272
+ * 统计当前宿主所有活跃进程累计排队等待控制权的调用者总数。
273
+ */
274
+ private countHostAcquireWaiters;
275
+ /**
276
+ * 统计当前宿主所有活跃进程累计输入队列待写入字节总数。
277
+ */
278
+ private countHostPendingInputBytes;
279
+ }