@actiondock/core 2.10.0 → 2.11.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 -2
- package/dist/catalog/action-catalog.js +3 -33
- package/dist/catalog/discovery.d.ts +0 -3
- package/dist/catalog/discovery.js +4 -4
- package/dist/catalog/graph.d.ts +0 -4
- package/dist/catalog/graph.js +8 -14
- package/dist/{profile/client-actions.d.ts → client/actions.d.ts} +1 -1
- package/dist/{profile/client-actions.js → client/actions.js} +2 -8
- package/dist/{profile/client-config.d.ts → client/config.d.ts} +1 -1
- package/dist/{profile/client-config.js → client/config.js} +1 -1
- package/dist/client/dispatcher.d.ts +4 -0
- package/dist/client/dispatcher.js +21 -0
- package/dist/client/health.d.ts +38 -0
- package/dist/{profile/client-health.js → client/health.js} +9 -6
- package/dist/client/index.d.ts +16 -0
- package/dist/client/index.js +20 -0
- package/dist/{profile/client-playbooks.d.ts → client/playbooks.d.ts} +1 -1
- package/dist/{profile/client-playbooks.js → client/playbooks.js} +1 -1
- package/dist/{profile/client-runs.d.ts → client/runs.d.ts} +4 -2
- package/dist/{profile/client-runs.js → client/runs.js} +7 -1
- package/dist/{profile/client-state.d.ts → client/state.d.ts} +1 -1
- package/dist/{profile/client-state.js → client/state.js} +1 -1
- package/dist/{profile/client-transport.d.ts → client/transport.d.ts} +29 -10
- package/dist/{profile/client-transport.js → client/transport.js} +125 -35
- package/dist/doctor/checks/index.d.ts +1 -1
- package/dist/doctor/checks/index.js +1 -1
- package/dist/doctor/checks/project.js +20 -88
- package/dist/doctor/checks/runtime.d.ts +0 -4
- package/dist/doctor/checks/runtime.js +0 -28
- package/dist/doctor/doctor.js +1 -2
- package/dist/errors.d.ts +53 -3
- package/dist/errors.js +53 -3
- package/dist/execution/service.d.ts +55 -37
- package/dist/execution/service.js +601 -212
- package/dist/execution/types.d.ts +24 -5
- package/dist/host/host.d.ts +42 -22
- package/dist/host/host.js +562 -149
- package/dist/host/routing.js +6 -6
- package/dist/host/types.d.ts +25 -13
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/input/advice.d.ts +0 -44
- package/dist/input/advice.js +7 -141
- package/dist/input/flat-materializer.js +1 -1
- package/dist/input/flat-parser.js +1 -1
- package/dist/input/index.d.ts +0 -1
- package/dist/input/index.js +0 -1
- package/dist/input/input-resolver.js +1 -1
- package/dist/input/validation-mapper.d.ts +1 -1
- package/dist/ipc/diagnostic.d.ts +8 -21
- package/dist/ipc/diagnostic.js +24 -57
- package/dist/ipc/host.js +21 -7
- package/dist/ipc/service.d.ts +14 -0
- package/dist/ipc/service.js +132 -26
- package/dist/ipc/types.d.ts +0 -4
- package/dist/package/index.d.ts +4 -5
- package/dist/package/index.js +1 -2
- package/dist/package/runtime.d.ts +94 -23
- package/dist/package/runtime.js +628 -274
- package/dist/package/static-index.d.ts +3 -6
- package/dist/package/static-index.js +11 -40
- package/dist/package/types.d.ts +19 -4
- package/dist/platform/index.d.ts +2 -1
- package/dist/platform/index.js +2 -1
- package/dist/platform/loader-hook.d.ts +32 -0
- package/dist/platform/loader-hook.js +90 -0
- package/dist/{node → platform}/module-loader.d.ts +13 -0
- package/dist/{node → platform}/module-loader.js +51 -4
- package/dist/platform/node.d.ts +19 -8
- package/dist/platform/node.js +50 -20
- package/dist/platform/types.d.ts +7 -47
- package/dist/process/context-process.d.ts +3 -18
- package/dist/process/context-process.js +3 -44
- package/dist/process/control.d.ts +21 -0
- package/dist/process/control.js +34 -0
- package/dist/process/converters.d.ts +23 -0
- package/dist/process/converters.js +92 -0
- package/dist/process/cursor.js +2 -22
- package/dist/process/{diagnostics-sink.d.ts → diagnostics.d.ts} +0 -7
- package/dist/process/{diagnostics-sink.js → diagnostics.js} +6 -8
- package/dist/process/index.d.ts +1 -3
- package/dist/process/index.js +1 -3
- package/dist/process/input-dispatcher.d.ts +4 -13
- package/dist/process/input-dispatcher.js +48 -69
- package/dist/process/managed-record.d.ts +3 -31
- package/dist/process/metadata-store.d.ts +0 -41
- package/dist/process/metadata-store.js +2 -341
- package/dist/process/process-driver.js +2 -2
- package/dist/process/process-manager.d.ts +8 -50
- package/dist/process/process-manager.js +28 -417
- package/dist/process/quotas.d.ts +14 -0
- package/dist/process/quotas.js +29 -0
- package/dist/process/run-executor.js +0 -1
- package/dist/process/terminal-output-cache.d.ts +20 -34
- package/dist/process/terminal-output-cache.js +43 -64
- package/dist/profile/index.d.ts +1 -2
- package/dist/profile/index.js +1 -2
- package/dist/profile/manager.d.ts +2 -4
- package/dist/profile/manager.js +10 -40
- package/dist/profile/types.d.ts +1 -17
- package/dist/project/digest.d.ts +5 -1
- package/dist/project/digest.js +81 -219
- package/dist/project/index.d.ts +4 -5
- package/dist/project/index.js +4 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +3 -3
- package/dist/project/loader.js +72 -50
- package/dist/project/lockfile.js +6 -5
- package/dist/project/manifest.js +5 -4
- package/dist/project/transactions.d.ts +4 -42
- package/dist/project/transactions.js +83 -138
- package/dist/project/types-generator.js +2 -1
- package/dist/project/types.d.ts +2 -0
- package/dist/registry/index.d.ts +0 -1
- package/dist/registry/index.js +0 -1
- package/dist/registry/lock.d.ts +2 -11
- package/dist/registry/lock.js +14 -183
- package/dist/registry/registry.d.ts +1 -6
- package/dist/registry/registry.js +5 -17
- package/dist/runtime/action-registry.d.ts +1 -5
- package/dist/runtime/action-registry.js +0 -12
- package/dist/runtime/context.js +2 -2
- package/dist/runtime/events.d.ts +11 -50
- package/dist/runtime/events.js +88 -368
- package/dist/runtime/index.d.ts +0 -4
- package/dist/runtime/index.js +0 -4
- package/dist/runtime/run-persistence.d.ts +2 -0
- package/dist/runtime/run-persistence.js +2 -1
- package/dist/runtime/standalone-commands.d.ts +18 -0
- package/dist/runtime/standalone-commands.js +466 -0
- package/dist/runtime/standalone-help.d.ts +20 -0
- package/dist/runtime/standalone-help.js +72 -0
- package/dist/runtime/standalone.d.ts +1 -44
- package/dist/runtime/standalone.js +32 -573
- package/dist/schema/validator.d.ts +2 -2
- package/dist/schema/validator.js +2 -9
- package/dist/server/index.d.ts +0 -2
- package/dist/server/index.js +0 -2
- package/dist/server/routes/actions.js +1 -1
- package/dist/server/routes/info.js +1 -1
- package/dist/server/routes/playbooks.js +1 -1
- package/dist/server/routes/runs.js +123 -16
- package/dist/server/routes/state.js +43 -18
- package/dist/server/security.d.ts +1 -1
- package/dist/server/security.js +2 -2
- package/dist/server/server.js +3 -7
- package/dist/service/factory.js +4 -3
- package/dist/service/index.d.ts +1 -1
- package/dist/service/index.js +1 -1
- package/dist/service/remote-errors.d.ts +1 -1
- package/dist/service/remote-errors.js +6 -6
- package/dist/service/remote-polling.d.ts +3 -0
- package/dist/service/remote-polling.js +10 -5
- package/dist/service/remote.d.ts +5 -1
- package/dist/service/remote.js +108 -17
- package/dist/service/sse-stream.js +2 -2
- package/dist/service/types.d.ts +22 -19
- package/dist/service/types.js +1 -24
- package/dist/storage/clock.d.ts +1 -1
- package/dist/storage/clock.js +1 -1
- package/dist/storage/data-dir-lock.d.ts +10 -22
- package/dist/storage/data-dir-lock.js +28 -56
- package/dist/storage/file-lock.d.ts +64 -0
- package/dist/storage/file-lock.js +447 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +5 -4
- package/dist/storage/run-liveness.d.ts +48 -0
- package/dist/storage/run-liveness.js +44 -0
- package/dist/storage/sqlite-config.d.ts +16 -0
- package/dist/storage/sqlite-config.js +59 -0
- package/dist/storage/sqlite-driver.d.ts +3 -1
- package/dist/storage/sqlite-driver.js +14 -4
- package/dist/storage/sqlite-events.d.ts +13 -0
- package/dist/storage/sqlite-events.js +48 -0
- package/dist/storage/sqlite-runs.d.ts +107 -0
- package/dist/storage/sqlite-runs.js +622 -0
- package/dist/storage/sqlite-schema.d.ts +5 -0
- package/dist/storage/sqlite-schema.js +143 -0
- package/dist/storage/sqlite-state.d.ts +33 -0
- package/dist/storage/sqlite-state.js +270 -0
- package/dist/storage/sqlite.d.ts +30 -57
- package/dist/storage/sqlite.js +84 -741
- package/dist/storage/types.d.ts +35 -2
- package/dist/utils/index.d.ts +18 -14
- package/dist/utils/index.js +37 -29
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +19 -0
- package/dist/utils/warning.d.ts +1 -0
- package/dist/utils/warning.js +52 -0
- package/dist/{json/value-validator.d.ts → value-validator.d.ts} +9 -0
- package/dist/value-validator.js +330 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -12
- package/dist/execution/index.d.ts +0 -2
- package/dist/execution/index.js +0 -2
- package/dist/filter/index.d.ts +0 -1
- package/dist/filter/index.js +0 -1
- package/dist/graph/index.d.ts +0 -4
- package/dist/graph/index.js +0 -4
- package/dist/input/metadata.d.ts +0 -99
- package/dist/input/metadata.js +0 -99
- package/dist/invocation/index.d.ts +0 -2
- package/dist/invocation/index.js +0 -2
- package/dist/ipc/index.d.ts +0 -4
- package/dist/ipc/index.js +0 -4
- package/dist/json/index.d.ts +0 -1
- package/dist/json/index.js +0 -1
- package/dist/json/value-validator.js +0 -330
- package/dist/node/index.d.ts +0 -1
- package/dist/node/index.js +0 -1
- package/dist/platform/node-fs.d.ts +0 -37
- package/dist/platform/node-fs.js +0 -81
- package/dist/process/control-arbiter.d.ts +0 -108
- package/dist/process/control-arbiter.js +0 -250
- package/dist/process/process-executor.d.ts +0 -47
- package/dist/process/process-executor.js +0 -296
- package/dist/profile/client-health.d.ts +0 -14
- package/dist/profile/client-query.d.ts +0 -30
- package/dist/profile/client-query.js +0 -69
- package/dist/profile/client.d.ts +0 -26
- package/dist/profile/client.js +0 -31
- package/dist/project/closure.d.ts +0 -25
- package/dist/project/closure.js +0 -74
- package/dist/registry/store.d.ts +0 -42
- package/dist/registry/store.js +0 -43
- package/dist/runtime/clock.d.ts +0 -7
- package/dist/runtime/clock.js +0 -7
- package/dist/runtime/module-loader.d.ts +0 -2
- package/dist/runtime/module-loader.js +0 -1
- package/dist/runtime/process.d.ts +0 -8
- package/dist/runtime/process.js +0 -1
- package/dist/runtime/runner.d.ts +0 -234
- package/dist/runtime/runner.js +0 -609
- package/dist/schema/index.d.ts +0 -1
- package/dist/schema/index.js +0 -1
- package/dist/server/dispatcher.d.ts +0 -25
- package/dist/server/dispatcher.js +0 -54
- package/dist/service/local.d.ts +0 -27
- package/dist/service/local.js +0 -141
- package/dist/storage/driver.d.ts +0 -10
- package/dist/storage/driver.js +0 -10
- package/dist/storage/lazy.d.ts +0 -8
- package/dist/storage/lazy.js +0 -45
- package/dist/storage/lock-core.d.ts +0 -158
- package/dist/storage/lock-core.js +0 -872
- package/dist/utils/net.d.ts +0 -10
- package/dist/utils/net.js +0 -16
- package/dist/utils/process.d.ts +0 -10
- package/dist/utils/process.js +0 -21
- /package/dist/{filter → utils}/intent.d.ts +0 -0
- /package/dist/{filter → utils}/intent.js +0 -0
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { FileStat, FileSystem } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* NodeFileSystem 配置选项。
|
|
4
|
-
*/
|
|
5
|
-
export interface NodeFileSystemOptions {
|
|
6
|
-
/**
|
|
7
|
-
* 安全沙箱根路径。
|
|
8
|
-
* 若指定,则所有文件系统操作将被强制约束在 rootDir 边界内,防止路径遍历逃逸。
|
|
9
|
-
*/
|
|
10
|
-
rootDir?: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* 基于 node:fs 和 node:path 的安全受限文件系统实现。
|
|
14
|
-
*/
|
|
15
|
-
export declare class NodeFileSystem implements FileSystem {
|
|
16
|
-
private readonly rootDir?;
|
|
17
|
-
constructor(options?: NodeFileSystemOptions);
|
|
18
|
-
/**
|
|
19
|
-
* 安全解析并校验路径边界。
|
|
20
|
-
*/
|
|
21
|
-
private resolvePath;
|
|
22
|
-
readFile(path: string, encoding?: string): Promise<string>;
|
|
23
|
-
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
24
|
-
readdir(dir: string): Promise<string[]>;
|
|
25
|
-
stat(path: string): Promise<FileStat>;
|
|
26
|
-
exists(path: string): Promise<boolean>;
|
|
27
|
-
mkdir(dir: string, options?: {
|
|
28
|
-
recursive?: boolean;
|
|
29
|
-
}): Promise<void>;
|
|
30
|
-
rm(path: string, options?: {
|
|
31
|
-
recursive?: boolean;
|
|
32
|
-
force?: boolean;
|
|
33
|
-
}): Promise<void>;
|
|
34
|
-
copy(src: string, dest: string, options?: {
|
|
35
|
-
recursive?: boolean;
|
|
36
|
-
}): Promise<void>;
|
|
37
|
-
}
|
package/dist/platform/node-fs.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
-
import { assertPathWithinRoot } from "../utils/index.js";
|
|
4
|
-
/**
|
|
5
|
-
* 基于 node:fs 和 node:path 的安全受限文件系统实现。
|
|
6
|
-
*/
|
|
7
|
-
export class NodeFileSystem {
|
|
8
|
-
rootDir;
|
|
9
|
-
constructor(options = {}) {
|
|
10
|
-
if (options.rootDir) {
|
|
11
|
-
this.rootDir = resolve(options.rootDir);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* 安全解析并校验路径边界。
|
|
16
|
-
*/
|
|
17
|
-
resolvePath(targetPath) {
|
|
18
|
-
if (typeof targetPath !== "string" || targetPath.includes("\0")) {
|
|
19
|
-
throw new Error("Invalid path: contains null byte or non-string argument");
|
|
20
|
-
}
|
|
21
|
-
if (this.rootDir) {
|
|
22
|
-
const resolved = isAbsolute(targetPath)
|
|
23
|
-
? resolve(targetPath)
|
|
24
|
-
: resolve(this.rootDir, targetPath);
|
|
25
|
-
assertPathWithinRoot(this.rootDir, resolved);
|
|
26
|
-
return resolved;
|
|
27
|
-
}
|
|
28
|
-
return resolve(targetPath);
|
|
29
|
-
}
|
|
30
|
-
async readFile(path, encoding = "utf-8") {
|
|
31
|
-
const resolved = this.resolvePath(path);
|
|
32
|
-
return await fs.readFile(resolved, { encoding: (encoding || "utf-8") });
|
|
33
|
-
}
|
|
34
|
-
async writeFile(path, content) {
|
|
35
|
-
const resolved = this.resolvePath(path);
|
|
36
|
-
const parentDir = dirname(resolved);
|
|
37
|
-
await fs.mkdir(parentDir, { recursive: true });
|
|
38
|
-
await fs.writeFile(resolved, content);
|
|
39
|
-
}
|
|
40
|
-
async readdir(dir) {
|
|
41
|
-
const resolved = this.resolvePath(dir);
|
|
42
|
-
return await fs.readdir(resolved);
|
|
43
|
-
}
|
|
44
|
-
async stat(path) {
|
|
45
|
-
const resolved = this.resolvePath(path);
|
|
46
|
-
const s = await fs.stat(resolved);
|
|
47
|
-
return {
|
|
48
|
-
isDirectory: () => s.isDirectory(),
|
|
49
|
-
isFile: () => s.isFile(),
|
|
50
|
-
size: s.size,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
async exists(path) {
|
|
54
|
-
try {
|
|
55
|
-
const resolved = this.resolvePath(path);
|
|
56
|
-
await fs.access(resolved);
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
async mkdir(dir, options) {
|
|
64
|
-
const resolved = this.resolvePath(dir);
|
|
65
|
-
await fs.mkdir(resolved, { recursive: options?.recursive ?? true });
|
|
66
|
-
}
|
|
67
|
-
async rm(path, options) {
|
|
68
|
-
const resolved = this.resolvePath(path);
|
|
69
|
-
await fs.rm(resolved, {
|
|
70
|
-
recursive: options?.recursive ?? true,
|
|
71
|
-
force: options?.force ?? true,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
async copy(src, dest, options) {
|
|
75
|
-
const resolvedSrc = this.resolvePath(src);
|
|
76
|
-
const resolvedDest = this.resolvePath(dest);
|
|
77
|
-
const parentDir = dirname(resolvedDest);
|
|
78
|
-
await fs.mkdir(parentDir, { recursive: true });
|
|
79
|
-
await fs.cp(resolvedSrc, resolvedDest, { recursive: options?.recursive ?? true });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { ControlGrant } from "@actiondock/sdk";
|
|
2
|
-
import { type ErrorCode } from "../errors.js";
|
|
3
|
-
import type { ManagedProcessRecord, ProcessOwner } from "./managed-record.js";
|
|
4
|
-
import type { StoredProcessRecord } from "./metadata-store.js";
|
|
5
|
-
/**
|
|
6
|
-
* 控制权仲裁器宿主协作回调集合。
|
|
7
|
-
*
|
|
8
|
-
* 持有者始终为 ProcessManager:仲裁器仅经回调透传副作用,不反向依赖管理器。
|
|
9
|
-
*/
|
|
10
|
-
export interface ControlArbiterHost {
|
|
11
|
-
/** 控制权状态落盘(异步尽力而为) */
|
|
12
|
-
persistState(processId: string, patch: Partial<StoredProcessRecord>): unknown;
|
|
13
|
-
/** 排队路径授予成功时的凭据落盘(异步尽力而为) */
|
|
14
|
-
persistGrantReceipt(grant: ControlGrant, wait: {
|
|
15
|
-
requestId: string;
|
|
16
|
-
waitMs: number;
|
|
17
|
-
ttlMs: number;
|
|
18
|
-
}, proc: ManagedProcessRecord): unknown;
|
|
19
|
-
/** 控制权事件后的空闲定时器刷新(授予、续租与成功输入均触发) */
|
|
20
|
-
refreshIdleTimer(proc: ManagedProcessRecord): void;
|
|
21
|
-
/** 到期复核通过后的隔离处置(隔离而非释放) */
|
|
22
|
-
quarantineProcess(owner: ProcessOwner, processId: string, token?: string, reason?: string): unknown;
|
|
23
|
-
/** 宿主级等待者配额统计(含全部进程的排队等待者) */
|
|
24
|
-
countHostAcquireWaiters(): number;
|
|
25
|
-
/** 宿主等待者配额上限 */
|
|
26
|
-
readonly maxWaitersPerProcess: number;
|
|
27
|
-
/** 单进程等待者配额上限 */
|
|
28
|
-
readonly maxWaitersPerHost: number;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* 控制权仲裁器。
|
|
32
|
-
*
|
|
33
|
-
* 单一职责:独占控制权状态机的全部推进路径——
|
|
34
|
-
* - 直接授予与 FIFO 等待队列(唯一唤醒)
|
|
35
|
-
* - TTL 定时器生命周期(设置、续租换新、清理)
|
|
36
|
-
* - 到期复核(复合控制态与凭据双重校验,转隔离而非释放)
|
|
37
|
-
* - 操作授权校验(write/control 提交前与调度复核共用)
|
|
38
|
-
* - 终态等待者单一拒绝入口
|
|
39
|
-
*/
|
|
40
|
-
export declare class ControlArbiter {
|
|
41
|
-
private readonly host;
|
|
42
|
-
constructor(host: ControlArbiterHost);
|
|
43
|
-
/**
|
|
44
|
-
* 尝试立即授予控制权:控制权空闲且无等待者时直接授予,
|
|
45
|
-
* 否则按 FIFO 进入等待队列并返回等待 Promise。
|
|
46
|
-
*
|
|
47
|
-
* 调用方(原 acquire 请求)在排队路径 resolve 后负责预约结算;
|
|
48
|
-
* 唤醒方负责凭据落盘,保持两阶段时序不变。
|
|
49
|
-
*/
|
|
50
|
-
tryAcquire(proc: ManagedProcessRecord, input: {
|
|
51
|
-
requestId: string;
|
|
52
|
-
waitMs: number;
|
|
53
|
-
ttlMs: number;
|
|
54
|
-
}, call?: {
|
|
55
|
-
signal?: AbortSignal;
|
|
56
|
-
}, runId?: string): {
|
|
57
|
-
granted: boolean;
|
|
58
|
-
waiter?: Promise<ControlGrant>;
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* FIFO 入队单个等待者:注册超时定时器、中止监听与自清理钩子。
|
|
62
|
-
*/
|
|
63
|
-
private enqueueWaiter;
|
|
64
|
-
/**
|
|
65
|
-
* 授予指定受管进程控制令牌:递增控制纪元、登记凭据并设置 TTL 定时器。
|
|
66
|
-
*/
|
|
67
|
-
grantControl(proc: ManagedProcessRecord, requestId: string, ttlMs: number, runId?: string): ControlGrant;
|
|
68
|
-
/**
|
|
69
|
-
* 续租当前有效控制令牌:先清理旧 TTL 定时器再设置新定时器,
|
|
70
|
-
* 刷新到期时刻并联动空闲定时器。
|
|
71
|
-
*/
|
|
72
|
-
renewGrant(proc: ManagedProcessRecord, ttlMs: number): ControlGrant;
|
|
73
|
-
/**
|
|
74
|
-
* 显式释放控制令牌:清理 TTL 定时器、凭据置空并唤醒下一个等待者。
|
|
75
|
-
*/
|
|
76
|
-
releaseGrant(proc: ManagedProcessRecord): void;
|
|
77
|
-
/**
|
|
78
|
-
* 唤醒排队等待控制权的下一个调用者(唯一唤醒入口):
|
|
79
|
-
* 授予令牌、由唤醒方落盘排队凭据并 resolve 原等待 Promise。
|
|
80
|
-
*/
|
|
81
|
-
private wakeNextAcquireWaiter;
|
|
82
|
-
/**
|
|
83
|
-
* 控制权到期未释放时的复核:仍处于 held 且凭据有效时转隔离(隔离而非释放)。
|
|
84
|
-
*/
|
|
85
|
-
handleGrantTtlExpired(proc: ManagedProcessRecord): void;
|
|
86
|
-
/**
|
|
87
|
-
* 操作提交前校验持有者令牌与授权(write/control 入队前与调度链路共用)。
|
|
88
|
-
*/
|
|
89
|
-
validateOperation(proc: ManagedProcessRecord, token: string): void;
|
|
90
|
-
/**
|
|
91
|
-
* 校验当前持有凭据有效性(token 匹配且未过期),供 renew/release 提交前判定。
|
|
92
|
-
*/
|
|
93
|
-
validateHeldGrant(proc: ManagedProcessRecord, token: string): void;
|
|
94
|
-
/**
|
|
95
|
-
* 终态等待者单一拒绝入口:按 FIFO 拒绝全部排队等待者。
|
|
96
|
-
*
|
|
97
|
-
* 终态转移(exit/error/stop/quarantine)必须经由此方法清空等待队列,
|
|
98
|
-
* 禁止直接操作 acquireWaiters 数组。
|
|
99
|
-
*/
|
|
100
|
-
revokeAllWaiters(proc: ManagedProcessRecord, reason: {
|
|
101
|
-
code: ErrorCode;
|
|
102
|
-
message: string;
|
|
103
|
-
}): void;
|
|
104
|
-
/**
|
|
105
|
-
* 清理进程全部控制权相关定时器与等待队列(stop/退出/故障路径统一入口)。
|
|
106
|
-
*/
|
|
107
|
-
disposeProcess(proc: ManagedProcessRecord): void;
|
|
108
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { ACCESS_DENIED, CONTROL_BUSY, CONTROL_EXPIRED, CONTROL_REVOKED, PROCESS_QUARANTINED, QUOTA_EXCEEDED, ProcessError, } from "../errors.js";
|
|
2
|
-
/**
|
|
3
|
-
* 控制权仲裁器。
|
|
4
|
-
*
|
|
5
|
-
* 单一职责:独占控制权状态机的全部推进路径——
|
|
6
|
-
* - 直接授予与 FIFO 等待队列(唯一唤醒)
|
|
7
|
-
* - TTL 定时器生命周期(设置、续租换新、清理)
|
|
8
|
-
* - 到期复核(复合控制态与凭据双重校验,转隔离而非释放)
|
|
9
|
-
* - 操作授权校验(write/control 提交前与调度复核共用)
|
|
10
|
-
* - 终态等待者单一拒绝入口
|
|
11
|
-
*/
|
|
12
|
-
export class ControlArbiter {
|
|
13
|
-
host;
|
|
14
|
-
constructor(host) {
|
|
15
|
-
this.host = host;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 尝试立即授予控制权:控制权空闲且无等待者时直接授予,
|
|
19
|
-
* 否则按 FIFO 进入等待队列并返回等待 Promise。
|
|
20
|
-
*
|
|
21
|
-
* 调用方(原 acquire 请求)在排队路径 resolve 后负责预约结算;
|
|
22
|
-
* 唤醒方负责凭据落盘,保持两阶段时序不变。
|
|
23
|
-
*/
|
|
24
|
-
tryAcquire(proc, input, call, runId) {
|
|
25
|
-
// 若控制权空闲且无等待者,直接授予
|
|
26
|
-
if (proc.info.control === "free" && proc.acquireWaiters.length === 0) {
|
|
27
|
-
const grant = this.grantControl(proc, input.requestId, input.ttlMs, runId);
|
|
28
|
-
return { granted: true, waiter: Promise.resolve(grant) };
|
|
29
|
-
}
|
|
30
|
-
// 控制权已被持有,检查等待队列配额并进入 FIFO 排队
|
|
31
|
-
if (proc.acquireWaiters.length >= this.host.maxWaitersPerProcess) {
|
|
32
|
-
throw new ProcessError(QUOTA_EXCEEDED, "Process acquire waiter quota exceeded", {
|
|
33
|
-
limit: this.host.maxWaitersPerProcess,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
const totalHostWaiters = this.host.countHostAcquireWaiters();
|
|
37
|
-
if (totalHostWaiters >= this.host.maxWaitersPerHost) {
|
|
38
|
-
throw new ProcessError(QUOTA_EXCEEDED, "Host acquire waiter quota exceeded", {
|
|
39
|
-
limit: this.host.maxWaitersPerHost,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
// 排队路径:由唤醒方在授予时落盘凭据;原请求 resolve 后负责预约 commit
|
|
43
|
-
const waiter = this.enqueueWaiter(proc, input, call, runId);
|
|
44
|
-
return { granted: false, waiter };
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* FIFO 入队单个等待者:注册超时定时器、中止监听与自清理钩子。
|
|
48
|
-
*/
|
|
49
|
-
enqueueWaiter(proc, input, call, runId) {
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
let timer;
|
|
52
|
-
const waiter = {
|
|
53
|
-
requestId: input.requestId,
|
|
54
|
-
waitMs: input.waitMs,
|
|
55
|
-
ttlMs: input.ttlMs,
|
|
56
|
-
runId,
|
|
57
|
-
resolve: (granted) => {
|
|
58
|
-
cleanup();
|
|
59
|
-
resolve(granted);
|
|
60
|
-
},
|
|
61
|
-
reject: (err) => {
|
|
62
|
-
cleanup();
|
|
63
|
-
reject(err);
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
const cleanup = () => {
|
|
67
|
-
if (timer)
|
|
68
|
-
clearTimeout(timer);
|
|
69
|
-
const idx = proc.acquireWaiters.indexOf(waiter);
|
|
70
|
-
if (idx !== -1) {
|
|
71
|
-
proc.acquireWaiters.splice(idx, 1);
|
|
72
|
-
}
|
|
73
|
-
if (call?.signal && waiter.onAbort) {
|
|
74
|
-
call.signal.removeEventListener("abort", waiter.onAbort);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
if (call?.signal) {
|
|
78
|
-
if (call.signal.aborted) {
|
|
79
|
-
reject(call.signal.reason ?? new ProcessError(CONTROL_BUSY, "Acquire cancelled"));
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
waiter.onAbort = () => {
|
|
83
|
-
cleanup();
|
|
84
|
-
reject(call.signal?.reason ?? new ProcessError(CONTROL_BUSY, "Acquire cancelled"));
|
|
85
|
-
};
|
|
86
|
-
call.signal.addEventListener("abort", waiter.onAbort, { once: true });
|
|
87
|
-
}
|
|
88
|
-
timer = setTimeout(() => {
|
|
89
|
-
cleanup();
|
|
90
|
-
reject(new ProcessError(CONTROL_BUSY, "Timed out waiting for process control", {
|
|
91
|
-
waitMs: input.waitMs,
|
|
92
|
-
}));
|
|
93
|
-
}, input.waitMs);
|
|
94
|
-
if (typeof timer?.unref === "function") {
|
|
95
|
-
timer.unref();
|
|
96
|
-
}
|
|
97
|
-
proc.acquireWaiters.push(waiter);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* 授予指定受管进程控制令牌:递增控制纪元、登记凭据并设置 TTL 定时器。
|
|
102
|
-
*/
|
|
103
|
-
grantControl(proc, requestId, ttlMs, runId) {
|
|
104
|
-
proc.controlEpoch += 1;
|
|
105
|
-
const token = `tok_${proc.info.id}_${proc.controlEpoch}_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
106
|
-
const expiresAt = new Date(Date.now() + ttlMs).toISOString();
|
|
107
|
-
proc.info.control = "held";
|
|
108
|
-
proc.currentGrant = {
|
|
109
|
-
token,
|
|
110
|
-
epoch: proc.controlEpoch,
|
|
111
|
-
ttlMs,
|
|
112
|
-
expiresAt,
|
|
113
|
-
runId,
|
|
114
|
-
requestId,
|
|
115
|
-
};
|
|
116
|
-
proc.ttlTimer = setTimeout(() => {
|
|
117
|
-
this.handleGrantTtlExpired(proc);
|
|
118
|
-
}, ttlMs);
|
|
119
|
-
if (typeof proc.ttlTimer.unref === "function") {
|
|
120
|
-
proc.ttlTimer.unref();
|
|
121
|
-
}
|
|
122
|
-
void this.host.persistState(proc.info.id, {
|
|
123
|
-
control: "held",
|
|
124
|
-
controlState: "held",
|
|
125
|
-
});
|
|
126
|
-
return {
|
|
127
|
-
token,
|
|
128
|
-
expiresAt,
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* 续租当前有效控制令牌:先清理旧 TTL 定时器再设置新定时器,
|
|
133
|
-
* 刷新到期时刻并联动空闲定时器。
|
|
134
|
-
*/
|
|
135
|
-
renewGrant(proc, ttlMs) {
|
|
136
|
-
if (proc.ttlTimer) {
|
|
137
|
-
clearTimeout(proc.ttlTimer);
|
|
138
|
-
}
|
|
139
|
-
const expiresAt = new Date(Date.now() + ttlMs).toISOString();
|
|
140
|
-
proc.currentGrant.ttlMs = ttlMs;
|
|
141
|
-
proc.currentGrant.expiresAt = expiresAt;
|
|
142
|
-
proc.ttlTimer = setTimeout(() => {
|
|
143
|
-
this.handleGrantTtlExpired(proc);
|
|
144
|
-
}, ttlMs);
|
|
145
|
-
if (typeof proc.ttlTimer.unref === "function") {
|
|
146
|
-
proc.ttlTimer.unref();
|
|
147
|
-
}
|
|
148
|
-
// 延长控制权刷新 idleTimeout
|
|
149
|
-
this.host.refreshIdleTimer(proc);
|
|
150
|
-
return {
|
|
151
|
-
token: proc.currentGrant.token,
|
|
152
|
-
expiresAt,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* 显式释放控制令牌:清理 TTL 定时器、凭据置空并唤醒下一个等待者。
|
|
157
|
-
*/
|
|
158
|
-
releaseGrant(proc) {
|
|
159
|
-
if (proc.ttlTimer) {
|
|
160
|
-
clearTimeout(proc.ttlTimer);
|
|
161
|
-
proc.ttlTimer = undefined;
|
|
162
|
-
}
|
|
163
|
-
proc.currentGrant = undefined;
|
|
164
|
-
proc.info.control = "free";
|
|
165
|
-
void this.host.persistState(proc.info.id, {
|
|
166
|
-
control: "free",
|
|
167
|
-
controlState: "free",
|
|
168
|
-
});
|
|
169
|
-
// 正常 release 唤醒下一个等待者
|
|
170
|
-
this.wakeNextAcquireWaiter(proc);
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 唤醒排队等待控制权的下一个调用者(唯一唤醒入口):
|
|
174
|
-
* 授予令牌、由唤醒方落盘排队凭据并 resolve 原等待 Promise。
|
|
175
|
-
*/
|
|
176
|
-
wakeNextAcquireWaiter(proc) {
|
|
177
|
-
if (proc.acquireWaiters.length === 0) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const next = proc.acquireWaiters.shift();
|
|
181
|
-
const grant = this.grantControl(proc, next.requestId, next.ttlMs, next.runId);
|
|
182
|
-
void this.host.persistGrantReceipt(grant, next, proc);
|
|
183
|
-
next.resolve(grant);
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* 控制权到期未释放时的复核:仍处于 held 且凭据有效时转隔离(隔离而非释放)。
|
|
187
|
-
*/
|
|
188
|
-
handleGrantTtlExpired(proc) {
|
|
189
|
-
if (proc.info.control !== "held" || !proc.currentGrant) {
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
void this.host.quarantineProcess(proc.owner, proc.info.id, proc.currentGrant.token, "Control token TTL expired");
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* 操作提交前校验持有者令牌与授权(write/control 入队前与调度链路共用)。
|
|
196
|
-
*/
|
|
197
|
-
validateOperation(proc, token) {
|
|
198
|
-
if (proc.info.control === "quarantined") {
|
|
199
|
-
throw new ProcessError(PROCESS_QUARANTINED, "Process is in quarantined state");
|
|
200
|
-
}
|
|
201
|
-
if (proc.info.control !== "held" || !proc.currentGrant) {
|
|
202
|
-
throw new ProcessError(ACCESS_DENIED, "Process control is not currently held");
|
|
203
|
-
}
|
|
204
|
-
if (proc.currentGrant.token !== token) {
|
|
205
|
-
throw new ProcessError(ACCESS_DENIED, "Invalid control token");
|
|
206
|
-
}
|
|
207
|
-
if (new Date(proc.currentGrant.expiresAt).getTime() <= Date.now()) {
|
|
208
|
-
throw new ProcessError(CONTROL_EXPIRED, "Control token has expired");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* 校验当前持有凭据有效性(token 匹配且未过期),供 renew/release 提交前判定。
|
|
213
|
-
*/
|
|
214
|
-
validateHeldGrant(proc, token) {
|
|
215
|
-
if (proc.info.control === "quarantined") {
|
|
216
|
-
throw new ProcessError(PROCESS_QUARANTINED, "Process is in quarantined state");
|
|
217
|
-
}
|
|
218
|
-
if (proc.info.control !== "held" || !proc.currentGrant) {
|
|
219
|
-
throw new ProcessError(CONTROL_REVOKED, "Process control is not currently held");
|
|
220
|
-
}
|
|
221
|
-
if (proc.currentGrant.token !== token) {
|
|
222
|
-
throw new ProcessError(ACCESS_DENIED, "Invalid control token");
|
|
223
|
-
}
|
|
224
|
-
if (new Date(proc.currentGrant.expiresAt).getTime() <= Date.now()) {
|
|
225
|
-
throw new ProcessError(CONTROL_EXPIRED, "Control token has expired");
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* 终态等待者单一拒绝入口:按 FIFO 拒绝全部排队等待者。
|
|
230
|
-
*
|
|
231
|
-
* 终态转移(exit/error/stop/quarantine)必须经由此方法清空等待队列,
|
|
232
|
-
* 禁止直接操作 acquireWaiters 数组。
|
|
233
|
-
*/
|
|
234
|
-
revokeAllWaiters(proc, reason) {
|
|
235
|
-
while (proc.acquireWaiters.length > 0) {
|
|
236
|
-
const waiter = proc.acquireWaiters.shift();
|
|
237
|
-
waiter.reject(new ProcessError(reason.code, reason.message));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* 清理进程全部控制权相关定时器与等待队列(stop/退出/故障路径统一入口)。
|
|
242
|
-
*/
|
|
243
|
-
disposeProcess(proc) {
|
|
244
|
-
if (proc.ttlTimer) {
|
|
245
|
-
clearTimeout(proc.ttlTimer);
|
|
246
|
-
proc.ttlTimer = undefined;
|
|
247
|
-
}
|
|
248
|
-
proc.currentGrant = undefined;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { spawn } from "node:child_process";
|
|
2
|
-
import { type CallOptions, type ControlGrant, type OperationReceipt, type ProcessAcquireInput, 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";
|
|
3
|
-
import type { ProcessDriver } from "./driver.js";
|
|
4
|
-
import { killProcessGroup } from "./process-driver.js";
|
|
5
|
-
import type { ProcessExecutor } from "../runtime/process.js";
|
|
6
|
-
export { killProcessGroup };
|
|
7
|
-
/**
|
|
8
|
-
* 基于 NodeProcessDriver 实现的兼容命令执行器。
|
|
9
|
-
* 核心 OS 进程派生与生命周期管理统一由 NodeProcessDriver 承担,不再重复派生与调度。
|
|
10
|
-
*/
|
|
11
|
-
export declare class NodeProcessExecutor implements ProcessExecutor {
|
|
12
|
-
private readonly driver;
|
|
13
|
-
private readonly gracePeriodMs;
|
|
14
|
-
constructor(options?: number | {
|
|
15
|
-
gracePeriodMs?: number;
|
|
16
|
-
spawnFn?: typeof spawn;
|
|
17
|
-
driver?: ProcessDriver;
|
|
18
|
-
});
|
|
19
|
-
/**
|
|
20
|
-
* 执行外部系统命令,完整支持标准输入管道、超时控制、取消信号、输出容量截断及错误拦截。
|
|
21
|
-
*
|
|
22
|
-
* @param command 执行命令
|
|
23
|
-
* @param args 参数列表
|
|
24
|
-
* @param options 执行选项
|
|
25
|
-
* @param internal 内部专用参数:envAlreadyResolved 为 true 时 options.env 视为已完整解析
|
|
26
|
-
*/
|
|
27
|
-
exec(command: string, args?: string[], options?: ProcessExecOptions, internal?: {
|
|
28
|
-
envAlreadyResolved?: boolean;
|
|
29
|
-
}): Promise<ProcessResult>;
|
|
30
|
-
spawn(command: string, args?: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
|
|
31
|
-
run(input: ProcessRunInput, call?: CallOptions): Promise<ProcessRunResult>;
|
|
32
|
-
start(_input: ProcessStartInput, _call?: CallOptions): Promise<ProcessStartResult>;
|
|
33
|
-
inspect(_id: string, _call?: CallOptions): Promise<ProcessInfo>;
|
|
34
|
-
list(_input: ProcessListInput, _call?: CallOptions): Promise<ProcessListResult>;
|
|
35
|
-
acquire(_id: string, _input: ProcessAcquireInput, _call?: CallOptions): Promise<ControlGrant>;
|
|
36
|
-
renew(_id: string, _token: string, _ttlMs: number, _call?: CallOptions): Promise<ControlGrant>;
|
|
37
|
-
release(_id: string, _token: string, _call?: CallOptions): Promise<void>;
|
|
38
|
-
write(_id: string, _input: ProcessWriteInput, _call?: CallOptions): Promise<OperationReceipt>;
|
|
39
|
-
operation(_id: string, _requestId: string, _call?: CallOptions): Promise<OperationReceipt>;
|
|
40
|
-
read(_id: string, _input: ProcessReadInput, _call?: CallOptions): Promise<ReadResult>;
|
|
41
|
-
control(_id: string, _input: ProcessControlInput, _call?: CallOptions): Promise<OperationReceipt>;
|
|
42
|
-
stop(_id: string, _input: ProcessStopInput, _call?: CallOptions): Promise<ProcessInfo>;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* 兼容原有类名导出。
|
|
46
|
-
*/
|
|
47
|
-
export { NodeProcessExecutor as ExecaProcessExecutor };
|