@actiondock/core 2.2.1 → 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
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { normalizeServerUrl } from "./manager.js";
3
- import { ACTION_CANCELLED, NETWORK_ERROR } from "../errors.js";
3
+ import { ACTION_CANCELLED, ACTION_TIMEOUT, NETWORK_ERROR } from "../errors.js";
4
4
  import { isLoopbackHost } from "../server/security.js";
5
5
  /**
6
6
  * 校验在携带认证 Token 时传输层协议是否安全。
@@ -154,6 +154,9 @@ export async function executeRemoteAction(serverUrl, actionId, input = {}, confi
154
154
  if (requestId) {
155
155
  executionPayload.requestId = requestId;
156
156
  }
157
+ // 组合外部取消信号与本地超时守卫:服务端僵死时仍能在 timeoutMs 内本地中断,避免永久挂起
158
+ let timeoutTimer;
159
+ let localTimedOut = false;
157
160
  try {
158
161
  const headers = {
159
162
  ...buildHeaders(token),
@@ -168,34 +171,62 @@ export async function executeRemoteAction(serverUrl, actionId, input = {}, confi
168
171
  config: configOverrides,
169
172
  execution: Object.keys(executionPayload).length > 0 ? executionPayload : undefined,
170
173
  });
171
- let res = await fetch(v2Url, {
172
- method: "POST",
173
- headers,
174
- body: reqBody,
175
- signal,
176
- });
177
- if (res.status === 404) {
178
- try {
179
- const v1Res = await fetch(`${base}/api/v1/actions/${encodeURIComponent(actionId)}/run`, {
180
- method: "POST",
181
- headers,
182
- body: reqBody,
183
- signal,
184
- });
185
- if (v1Res.ok || v1Res.status !== 404) {
186
- res = v1Res;
174
+ const controller = new AbortController();
175
+ const onExternalAbort = () => controller.abort(signal?.reason);
176
+ if (signal) {
177
+ if (signal.aborted) {
178
+ controller.abort(signal.reason);
179
+ }
180
+ else {
181
+ signal.addEventListener("abort", onExternalAbort, { once: true });
182
+ }
183
+ }
184
+ if (typeof timeoutMs === "number" && timeoutMs > 0) {
185
+ timeoutTimer = setTimeout(() => {
186
+ localTimedOut = true;
187
+ controller.abort();
188
+ }, timeoutMs);
189
+ }
190
+ let res;
191
+ try {
192
+ res = await fetch(v2Url, {
193
+ method: "POST",
194
+ headers,
195
+ body: reqBody,
196
+ signal: controller.signal,
197
+ });
198
+ if (res.status === 404) {
199
+ try {
200
+ const v1Res = await fetch(`${base}/api/v1/actions/${encodeURIComponent(actionId)}/run`, {
201
+ method: "POST",
202
+ headers,
203
+ body: reqBody,
204
+ signal: controller.signal,
205
+ });
206
+ if (v1Res.ok || v1Res.status !== 404) {
207
+ res = v1Res;
208
+ }
187
209
  }
210
+ catch { }
211
+ }
212
+ }
213
+ finally {
214
+ if (timeoutTimer !== undefined) {
215
+ clearTimeout(timeoutTimer);
216
+ }
217
+ if (signal) {
218
+ signal.removeEventListener("abort", onExternalAbort);
188
219
  }
189
- catch { }
190
220
  }
191
221
  const data = (await res.json().catch(() => null));
192
222
  if (data && typeof data === "object" && typeof data.ok === "boolean") {
193
223
  return data;
194
224
  }
195
225
  if (!res.ok) {
226
+ // 错误信封严禁伪造 runId:与任何真实运行无关的标识会让调用方查询永远 not_found
196
227
  return {
197
228
  ok: false,
198
- runId: randomUUID(),
229
+ runId: "",
199
230
  error: {
200
231
  code: res.status === 401 ? "UNAUTHORIZED" : "REMOTE_EXECUTION_FAILED",
201
232
  message: `Remote server HTTP ${res.status}: ${res.statusText}`,
@@ -210,10 +241,21 @@ export async function executeRemoteAction(serverUrl, actionId, input = {}, confi
210
241
  };
211
242
  }
212
243
  catch (err) {
244
+ // 本地超时守卫触发时归类为超时;外部信号中止时归类为取消
245
+ if (localTimedOut) {
246
+ return {
247
+ ok: false,
248
+ runId: "",
249
+ error: {
250
+ code: ACTION_TIMEOUT,
251
+ message: `Remote execution exceeded local timeout of ${timeoutMs}ms`,
252
+ },
253
+ };
254
+ }
213
255
  if (err.name === "AbortError" || signal?.aborted) {
214
256
  return {
215
257
  ok: false,
216
- runId: randomUUID(),
258
+ runId: "",
217
259
  error: {
218
260
  code: ACTION_CANCELLED,
219
261
  message: "Action execution was cancelled",
@@ -222,7 +264,7 @@ export async function executeRemoteAction(serverUrl, actionId, input = {}, confi
222
264
  }
223
265
  return {
224
266
  ok: false,
225
- runId: randomUUID(),
267
+ runId: "",
226
268
  error: {
227
269
  code: NETWORK_ERROR,
228
270
  message: `Failed to connect to remote ActionDock server at ${serverUrl}: ${err.message}`,
@@ -86,10 +86,10 @@ export function initProject(targetDir, options = {}) {
86
86
  node: ">=24.12.0",
87
87
  },
88
88
  dependencies: {
89
- "@actiondock/sdk": "^2.2.1",
89
+ "@actiondock/sdk": "^2.3.0",
90
90
  },
91
91
  devDependencies: {
92
- "@actiondock/testing": "^2.2.1",
92
+ "@actiondock/testing": "^2.3.0",
93
93
  "@types/node": "^22.13.0",
94
94
  "tsx": "^4.19.0",
95
95
  "typescript": "^5.7.0",
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, renameSync, statSync } from "node:fs";
2
2
  import { readFile, rename, writeFile } from "node:fs/promises";
3
- import { basename, dirname, join, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve, sep } from "node:path";
4
4
  import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
5
5
  import { getActionDockHome, getPackageSlug } from "../utils/index.js";
6
6
  import { withRegistryLock } from "./lock.js";
@@ -12,6 +12,13 @@ import { probeActionAsync, probeActionSync, probePlaybook, resolveEntityFlow, ru
12
12
  function emptyRegistry() {
13
13
  return { version: "2.0.0", packages: {}, workspaces: {} };
14
14
  }
15
+ /**
16
+ * 判定 entryPath 是否严格位于 baseDir 目录之内(携带路径分隔符边界)。
17
+ * 避免裸 startsWith 前缀匹配把兄弟目录(如 /home/ws 与 /home/ws2)误判为子路径。
18
+ */
19
+ function isPathWithin(entryPath, baseDir) {
20
+ return entryPath === baseDir || entryPath.startsWith(baseDir + sep);
21
+ }
15
22
  /**
16
23
  * 注册表内容解析与 links 迁移(纯函数,同步与异步加载共享的唯一事实源)。
17
24
  *
@@ -304,7 +311,8 @@ export async function unlinkPackage(identifier = process.cwd(), customHome) {
304
311
  delete registry.workspaces[absPath];
305
312
  let removedCount = 0;
306
313
  for (const [id, entry] of Object.entries(registry.packages)) {
307
- if (entry.workspaceRoot === absPath || entry.path.startsWith(absPath)) {
314
+ // 路径前缀匹配必须携带分隔符边界,避免 /home/ws 误删 /home/ws2 的记录
315
+ if (isPathWithin(entry.path, absPath) || entry.workspaceRoot === absPath) {
308
316
  delete registry.packages[id];
309
317
  removedCount++;
310
318
  }
@@ -325,7 +333,8 @@ export async function unlinkPackage(identifier = process.cwd(), customHome) {
325
333
  delete registry.workspaces[wsPath];
326
334
  let removedCount = 0;
327
335
  for (const [id, entry] of Object.entries(registry.packages)) {
328
- if (entry.workspaceRoot === wsPath || entry.path.startsWith(wsPath)) {
336
+ // 路径前缀匹配必须携带分隔符边界,避免 /home/ws 误删 /home/ws2 的记录
337
+ if (isPathWithin(entry.path, wsPath) || entry.workspaceRoot === wsPath) {
329
338
  delete registry.packages[id];
330
339
  removedCount++;
331
340
  }
@@ -1,15 +1,16 @@
1
1
  import type { ActionContext, ActionRef, Config, Logger, ProcessAPI, ProgressReporter, StateStore } from "@actiondock/sdk";
2
2
  import type { ProjectConfig } from "../project/types.js";
3
3
  import type { RuntimeStorage } from "../storage/types.js";
4
+ import { ProcessManager, type ProcessOwner } from "../process/index.js";
4
5
  /**
5
6
  * 生产级配置解析器实现。
6
7
  * 严格践行 5 层配置解析优先级链:
7
- * 1. CLI 临时参数覆写 (Overrides)
8
- * 2. 包级持久化存储 (Package SQLite: ~/.actiondock/data/<package-id>/runtime.db)
9
- * 3. 全局共享持久化存储 (Global SQLite: ~/.actiondock/global.db)
10
- * 4. 操作系统环境变量 (process.env: 显式绑定 / 包名前缀 / SNAKE_CASE / 类型转换)
11
- * 5. 项目默认配置 (actiondock.json 中的 default 字段)
12
- * 6. 代码级默认回退值 (fallback)
8
+ * - CLI 临时参数覆写 (Overrides)
9
+ * - 包级持久化存储 (Package SQLite: ~/.actiondock/data/<package-id>/runtime.db)
10
+ * - 全局共享持久化存储 (Global SQLite: ~/.actiondock/global.db)
11
+ * - 操作系统环境变量 (process.env: 显式绑定 / 包名前缀 / SNAKE_CASE / 类型转换)
12
+ * - 项目默认配置 (actiondock.json 中的 default 字段)
13
+ * - 代码级默认回退值 (fallback)
13
14
  */
14
15
  export declare class RuntimeConfig implements Config {
15
16
  private overrides;
@@ -73,6 +74,8 @@ export interface ContextOptions {
73
74
  callStack?: string[];
74
75
  signal?: AbortSignal;
75
76
  process?: ProcessAPI;
77
+ processManager?: ProcessManager;
78
+ owner?: ProcessOwner;
76
79
  progress?: ProgressReporter;
77
80
  logger?: Logger;
78
81
  onActionInvoke?: (action: ActionRef | string, input: unknown, parentRunId?: string) => Promise<unknown>;
@@ -1,15 +1,15 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { MemoryProcessDriver, ProcessManager } from "../process/index.js";
2
3
  import { resolveEnvValue } from "./env.js";
3
- import { DefaultProcessExecutor } from "./process.js";
4
4
  /**
5
5
  * 生产级配置解析器实现。
6
6
  * 严格践行 5 层配置解析优先级链:
7
- * 1. CLI 临时参数覆写 (Overrides)
8
- * 2. 包级持久化存储 (Package SQLite: ~/.actiondock/data/<package-id>/runtime.db)
9
- * 3. 全局共享持久化存储 (Global SQLite: ~/.actiondock/global.db)
10
- * 4. 操作系统环境变量 (process.env: 显式绑定 / 包名前缀 / SNAKE_CASE / 类型转换)
11
- * 5. 项目默认配置 (actiondock.json 中的 default 字段)
12
- * 6. 代码级默认回退值 (fallback)
7
+ * - CLI 临时参数覆写 (Overrides)
8
+ * - 包级持久化存储 (Package SQLite: ~/.actiondock/data/<package-id>/runtime.db)
9
+ * - 全局共享持久化存储 (Global SQLite: ~/.actiondock/global.db)
10
+ * - 操作系统环境变量 (process.env: 显式绑定 / 包名前缀 / SNAKE_CASE / 类型转换)
11
+ * - 项目默认配置 (actiondock.json 中的 default 字段)
12
+ * - 代码级默认回退值 (fallback)
13
13
  */
14
14
  export class RuntimeConfig {
15
15
  overrides;
@@ -221,7 +221,30 @@ export function createActionContext(options) {
221
221
  };
222
222
  const invokerFn = (ref, input) => invoke(ref, input);
223
223
  const invoker = Object.assign(invokerFn, { invoke });
224
- const processApi = options.process || new DefaultProcessExecutor();
224
+ const defaultOwner = {
225
+ tenantId: "default",
226
+ principalId: "default",
227
+ packageInstanceId: "default",
228
+ generationId: "default",
229
+ };
230
+ const effectiveOwner = options.owner || defaultOwner;
231
+ // 外部注入的可能是平台级共享 ContextProcessAPI(未绑定 runId),直接复用会让
232
+ // 多个 run 共享同一隔离与回收状态;此处派生 run 级实例保证隔离与自动回收均以 run 为单位
233
+ const injectedProcess = options.process;
234
+ const isSharedContextProcessApi = injectedProcess &&
235
+ typeof injectedProcess === "object" &&
236
+ typeof injectedProcess.forOwner !== "function" &&
237
+ injectedProcess.manager &&
238
+ typeof injectedProcess.manager.forOwner === "function" &&
239
+ injectedProcess.runScoped !== true;
240
+ const processApi = injectedProcess && typeof injectedProcess.forOwner === "function"
241
+ ? injectedProcess.forOwner(effectiveOwner, currentRunId, signal)
242
+ : isSharedContextProcessApi
243
+ ? injectedProcess.manager.forOwner(effectiveOwner, currentRunId, signal)
244
+ : options.process ||
245
+ (options.processManager
246
+ ? options.processManager.forOwner(effectiveOwner, currentRunId, signal)
247
+ : new ProcessManager({ driver: new MemoryProcessDriver() }).forOwner(effectiveOwner, currentRunId, signal));
225
248
  const progressApi = options.progress || {
226
249
  report() { },
227
250
  };
@@ -1,9 +1,26 @@
1
- import type { ProcessAPI, ProcessExecOptions, ProcessResult } from "@actiondock/sdk";
1
+ import { type CallOptions, type ControlGrant, type OperationReceipt, type ProcessAcquireInput, type ProcessAPI, type ProcessControlInput, type ProcessExecOptions, type ProcessInfo, type ProcessListInput, type ProcessListResult, type ProcessReadInput, type ProcessResult, type ProcessRunInput, type ProcessRunResult, type ProcessStartInput, type ProcessStartResult, type ProcessStopInput, type ProcessWriteInput, type ReadResult } from "@actiondock/sdk";
2
+ import { ProcessManager, type ProcessOwner } from "../process/index.js";
2
3
  export type ProcessExecutor = ProcessAPI;
3
4
  /**
4
- * 基于 Node.js 标准 child_process 实现的基础进程执行器。
5
+ * 基于受管进程驱动的默认基础执行器实现。
6
+ * 保持内核彻底解耦与平台无关,不依赖操作系统原生进程句柄与 Node 模块。
5
7
  */
6
8
  export declare class DefaultProcessExecutor implements ProcessExecutor {
9
+ private readonly manager;
10
+ private readonly owner;
11
+ constructor(manager?: ProcessManager, owner?: ProcessOwner);
7
12
  exec(command: string, args?: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
8
13
  spawn(command: string, args?: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
14
+ run(input: ProcessRunInput, call?: CallOptions): Promise<ProcessRunResult>;
15
+ start(input: ProcessStartInput, call?: CallOptions): Promise<ProcessStartResult>;
16
+ inspect(id: string, call?: CallOptions): Promise<ProcessInfo>;
17
+ list(input: ProcessListInput, call?: CallOptions): Promise<ProcessListResult>;
18
+ acquire(id: string, input: ProcessAcquireInput, call?: CallOptions): Promise<ControlGrant>;
19
+ renew(id: string, token: string, ttlMs: number, call?: CallOptions): Promise<ControlGrant>;
20
+ release(id: string, token: string, call?: CallOptions): Promise<void>;
21
+ write(id: string, input: ProcessWriteInput, call?: CallOptions): Promise<OperationReceipt>;
22
+ operation(id: string, requestId: string, call?: CallOptions): Promise<OperationReceipt>;
23
+ read(id: string, input: ProcessReadInput, call?: CallOptions): Promise<ReadResult>;
24
+ control(id: string, input: ProcessControlInput, call?: CallOptions): Promise<OperationReceipt>;
25
+ stop(id: string, input: ProcessStopInput, call?: CallOptions): Promise<ProcessInfo>;
9
26
  }
@@ -1,146 +1,118 @@
1
- import { spawn } from "node:child_process";
2
- import { PROCESS_OUTPUT_LIMIT, PROCESS_SPAWN_ERROR } from "../errors.js";
1
+ import { MemoryProcessDriver, ProcessManager } from "../process/index.js";
3
2
  /**
4
- * 基于 Node.js 标准 child_process 实现的基础进程执行器。
3
+ * 基于受管进程驱动的默认基础执行器实现。
4
+ * 保持内核彻底解耦与平台无关,不依赖操作系统原生进程句柄与 Node 模块。
5
5
  */
6
6
  export class DefaultProcessExecutor {
7
+ manager;
8
+ owner;
9
+ constructor(manager, owner) {
10
+ this.manager = manager ?? new ProcessManager({ driver: new MemoryProcessDriver() });
11
+ this.owner = owner ?? {
12
+ tenantId: "default",
13
+ principalId: "default",
14
+ packageInstanceId: "default",
15
+ generationId: "default",
16
+ };
17
+ }
7
18
  async exec(command, args = [], options = {}) {
8
19
  const startTime = Date.now();
9
- const maxOutputBytes = options.maxOutputBytes ?? 10 * 1024 * 1024;
10
- return new Promise((resolve, reject) => {
11
- let stdoutBuf = "";
12
- let stderrBuf = "";
13
- let totalBytes = 0;
14
- let timedOut = false;
15
- let cancelled = false;
16
- let error;
17
- const cp = spawn(command, args, {
18
- cwd: options.cwd,
19
- env: options.env ? { ...process.env, ...options.env } : process.env,
20
- stdio: ["pipe", "pipe", "pipe"],
21
- });
22
- if (options.input) {
23
- cp.stdin.write(options.input);
24
- cp.stdin.end();
25
- }
26
- else {
27
- cp.stdin.end();
20
+ try {
21
+ const runResult = await this.manager.run(this.owner, {
22
+ spec: {
23
+ executable: command,
24
+ args,
25
+ cwd: options.cwd,
26
+ env: options.env ? { inherit: "none", set: options.env } : undefined,
27
+ io: { mode: "pipe" },
28
+ },
29
+ timeoutMs: options.timeoutMs ?? 0,
30
+ maxOutputBytes: options.maxOutputBytes ?? 10 * 1024 * 1024,
31
+ }, options.signal ? { signal: options.signal } : undefined);
32
+ let stdout = "";
33
+ let stderr = "";
34
+ for (const chunk of runResult.chunks) {
35
+ if (chunk.stream === "stdout") {
36
+ stdout += chunk.data.data;
37
+ }
38
+ else if (chunk.stream === "stderr") {
39
+ stderr += chunk.data.data;
40
+ }
28
41
  }
29
- let timer;
30
- let forceKillTimer;
31
- if (options.timeoutMs && options.timeoutMs > 0) {
32
- timer = setTimeout(() => {
33
- timedOut = true;
34
- cp.kill("SIGTERM");
35
- forceKillTimer = setTimeout(() => {
36
- if (!cp.killed)
37
- cp.kill("SIGKILL");
38
- }, 1000);
39
- }, options.timeoutMs);
42
+ const durationMs = Date.now() - startTime;
43
+ const ok = runResult.exit.code === 0;
44
+ if (!ok && options.throwOnError) {
45
+ throw new Error(stderr || `Process exited with code ${runResult.exit.code}`);
40
46
  }
41
- const onAbort = () => {
42
- cancelled = true;
43
- cp.kill("SIGTERM");
44
- forceKillTimer = setTimeout(() => {
45
- if (!cp.killed)
46
- cp.kill("SIGKILL");
47
- }, 1000);
47
+ return {
48
+ ok,
49
+ exitCode: runResult.exit.code,
50
+ signal: runResult.exit.signal ?? undefined,
51
+ stdout,
52
+ stderr,
53
+ raw: new Uint8Array(),
54
+ timedOut: false,
55
+ cancelled: false,
56
+ durationMs,
48
57
  };
49
- if (options.signal) {
50
- if (options.signal.aborted) {
51
- onAbort();
52
- }
53
- else {
54
- options.signal.addEventListener("abort", onAbort, { once: true });
55
- }
58
+ }
59
+ catch (err) {
60
+ if (options.throwOnError) {
61
+ throw err;
56
62
  }
57
- const cleanup = () => {
58
- if (timer) {
59
- clearTimeout(timer);
60
- timer = undefined;
61
- }
62
- if (forceKillTimer) {
63
- clearTimeout(forceKillTimer);
64
- forceKillTimer = undefined;
65
- }
66
- if (options.signal) {
67
- options.signal.removeEventListener("abort", onAbort);
68
- }
63
+ return {
64
+ ok: false,
65
+ exitCode: -1,
66
+ stdout: "",
67
+ stderr: err?.message || String(err),
68
+ raw: new Uint8Array(),
69
+ timedOut: false,
70
+ cancelled: false,
71
+ durationMs: Date.now() - startTime,
72
+ error: {
73
+ code: err?.code || "PROCESS_FAILED",
74
+ message: err?.message || String(err),
75
+ },
69
76
  };
70
- cp.stdout?.on("data", (chunk) => {
71
- totalBytes += chunk.length;
72
- if (totalBytes > maxOutputBytes) {
73
- error = {
74
- code: PROCESS_OUTPUT_LIMIT,
75
- message: `Process output exceeded limit of ${maxOutputBytes} bytes`,
76
- };
77
- cp.kill("SIGKILL");
78
- return;
79
- }
80
- stdoutBuf += chunk.toString("utf-8");
81
- });
82
- cp.stderr?.on("data", (chunk) => {
83
- totalBytes += chunk.length;
84
- if (totalBytes > maxOutputBytes) {
85
- error = {
86
- code: PROCESS_OUTPUT_LIMIT,
87
- message: `Process output exceeded limit of ${maxOutputBytes} bytes`,
88
- };
89
- cp.kill("SIGKILL");
90
- return;
91
- }
92
- stderrBuf += chunk.toString("utf-8");
93
- });
94
- cp.on("error", (err) => {
95
- cleanup();
96
- const durationMs = Date.now() - startTime;
97
- const res = {
98
- ok: false,
99
- exitCode: null,
100
- stdout: stdoutBuf.trim(),
101
- stderr: stderrBuf.trim() || err.message,
102
- raw: new TextEncoder().encode(stdoutBuf),
103
- timedOut,
104
- cancelled,
105
- durationMs,
106
- error: error || {
107
- code: PROCESS_SPAWN_ERROR,
108
- message: err.message,
109
- },
110
- };
111
- if (options.throwOnError) {
112
- reject(new Error(err.message));
113
- }
114
- else {
115
- resolve(res);
116
- }
117
- });
118
- cp.on("close", (exitCode, signal) => {
119
- cleanup();
120
- const durationMs = Date.now() - startTime;
121
- const ok = exitCode === 0 && !timedOut && !cancelled && !error;
122
- const res = {
123
- ok,
124
- exitCode,
125
- signal: signal || undefined,
126
- stdout: stdoutBuf.trim(),
127
- stderr: stderrBuf.trim(),
128
- raw: new TextEncoder().encode(stdoutBuf),
129
- timedOut,
130
- cancelled,
131
- durationMs,
132
- error,
133
- };
134
- if (!ok && options.throwOnError) {
135
- reject(new Error(stderrBuf.trim() || `Process exited with code ${exitCode}`));
136
- }
137
- else {
138
- resolve(res);
139
- }
140
- });
141
- });
77
+ }
142
78
  }
143
79
  async spawn(command, args = [], options = {}) {
144
80
  return this.exec(command, args, options);
145
81
  }
82
+ async run(input, call) {
83
+ return this.manager.run(this.owner, input, call);
84
+ }
85
+ async start(input, call) {
86
+ return this.manager.start(this.owner, input, call);
87
+ }
88
+ async inspect(id, call) {
89
+ return this.manager.inspect(this.owner, id, call);
90
+ }
91
+ async list(input, call) {
92
+ return this.manager.list(this.owner, input, call);
93
+ }
94
+ async acquire(id, input, call) {
95
+ return this.manager.acquire(this.owner, id, input, call);
96
+ }
97
+ async renew(id, token, ttlMs, call) {
98
+ return this.manager.renew(this.owner, id, token, ttlMs, call);
99
+ }
100
+ async release(id, token, call) {
101
+ return this.manager.release(this.owner, id, token, call);
102
+ }
103
+ async write(id, input, call) {
104
+ return this.manager.write(this.owner, id, input, call);
105
+ }
106
+ async operation(id, requestId, call) {
107
+ return this.manager.operation(this.owner, id, requestId, call);
108
+ }
109
+ async read(id, input, call) {
110
+ return this.manager.read(this.owner, id, input, call);
111
+ }
112
+ async control(id, input, call) {
113
+ return this.manager.control(this.owner, id, input, call);
114
+ }
115
+ async stop(id, input, call) {
116
+ return this.manager.stop(this.owner, id, input, call);
117
+ }
146
118
  }