@actiondock/core 2.7.0 → 2.8.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 +61 -68
- package/dist/catalog/action-catalog.d.ts +56 -0
- package/dist/catalog/action-catalog.js +202 -0
- package/dist/catalog/discovery.d.ts +60 -0
- package/dist/catalog/discovery.js +119 -0
- package/dist/catalog/graph.d.ts +190 -0
- package/dist/catalog/graph.js +589 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -5
- package/dist/catalog/resolve-action.d.ts +43 -0
- package/dist/catalog/resolve-action.js +119 -0
- package/dist/catalog/resolve-playbook.d.ts +26 -0
- package/dist/catalog/resolve-playbook.js +114 -0
- package/dist/doctor/checks/project.js +27 -8
- package/dist/doctor/checks/registry.js +2 -2
- package/dist/doctor/checks/runtime.js +34 -1
- package/dist/errors.d.ts +102 -10
- package/dist/errors.js +103 -10
- package/dist/execution/service.d.ts +18 -16
- package/dist/execution/service.js +183 -252
- package/dist/execution/types.d.ts +25 -69
- package/dist/execution/types.js +2 -1
- package/dist/filter/intent.d.ts +8 -9
- package/dist/filter/intent.js +49 -10
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +4 -0
- package/dist/host/host.d.ts +74 -32
- package/dist/host/host.js +585 -336
- package/dist/host/routing.d.ts +15 -48
- package/dist/host/routing.js +90 -153
- package/dist/host/types.d.ts +50 -17
- package/dist/index.d.ts +21 -24
- package/dist/index.js +19 -23
- package/dist/input/advice.d.ts +5 -1
- package/dist/input/advice.js +163 -214
- package/dist/input/describe.d.ts +1 -1
- package/dist/input/flat-errors.d.ts +5 -6
- package/dist/input/flat-errors.js +6 -6
- package/dist/input/flat-materializer.js +42 -44
- package/dist/invocation/index.d.ts +2 -0
- package/dist/invocation/index.js +2 -0
- package/dist/invocation/policy.d.ts +157 -0
- package/dist/invocation/policy.js +231 -0
- package/dist/invocation/types.d.ts +172 -0
- package/dist/invocation/types.js +97 -0
- package/dist/ipc/diagnostic.d.ts +0 -12
- package/dist/ipc/diagnostic.js +0 -21
- package/dist/ipc/host.d.ts +4 -4
- package/dist/ipc/host.js +44 -47
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/ipc/service.d.ts +40 -0
- package/dist/ipc/service.js +349 -0
- package/dist/ipc/types.d.ts +21 -15
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/module-loader.d.ts +63 -0
- package/dist/node/module-loader.js +112 -0
- package/dist/package/index.d.ts +23 -0
- package/dist/package/index.js +18 -0
- package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
- package/dist/{app/app.js → package/runtime.js} +140 -95
- package/dist/{app → package}/static-index.js +58 -41
- package/dist/package/storage-view.d.ts +13 -0
- package/dist/package/storage-view.js +22 -0
- package/dist/{app → package}/types.d.ts +76 -57
- package/dist/package/types.js +22 -0
- package/dist/platform/index.d.ts +1 -1
- package/dist/platform/index.js +1 -1
- package/dist/platform/node.d.ts +48 -0
- package/dist/platform/node.js +96 -0
- package/dist/platform/types.d.ts +0 -9
- package/dist/process/control-arbiter.d.ts +2 -1
- package/dist/process/control-arbiter.js +1 -2
- package/dist/process/driver.d.ts +9 -8
- package/dist/process/driver.js +11 -22
- package/dist/process/index.d.ts +2 -0
- package/dist/process/index.js +2 -0
- package/dist/process/managed-record.d.ts +2 -2
- package/dist/process/process-driver.d.ts +109 -0
- package/dist/process/process-driver.js +650 -0
- package/dist/process/process-executor.d.ts +47 -0
- package/dist/process/process-executor.js +296 -0
- package/dist/process/process-manager.js +48 -26
- package/dist/process/run-executor.js +12 -12
- package/dist/profile/client-actions.js +2 -2
- package/dist/profile/client-health.js +2 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +47 -80
- package/dist/profile/client.d.ts +2 -2
- package/dist/profile/client.js +1 -1
- package/dist/profile/index.d.ts +1 -0
- package/dist/profile/index.js +1 -0
- package/dist/profile/manager.d.ts +5 -0
- package/dist/profile/manager.js +41 -6
- package/dist/project/closure.js +2 -2
- package/dist/project/index.d.ts +8 -1
- package/dist/project/index.js +10 -1
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +1 -1
- package/dist/project/loader.js +26 -29
- package/dist/project/transactions.js +5 -10
- package/dist/registry/index.d.ts +3 -1
- package/dist/registry/index.js +2 -1
- package/dist/registry/lock.d.ts +28 -7
- package/dist/registry/lock.js +136 -16
- package/dist/registry/registry.d.ts +1 -5
- package/dist/registry/registry.js +1 -53
- package/dist/registry/scan.d.ts +0 -15
- package/dist/registry/scan.js +0 -59
- package/dist/registry/store.d.ts +42 -0
- package/dist/registry/store.js +43 -0
- package/dist/registry/types.d.ts +0 -24
- package/dist/runtime/action-collection.d.ts +1 -1
- package/dist/runtime/action-registry.d.ts +0 -4
- package/dist/runtime/action-registry.js +0 -7
- package/dist/runtime/context.js +5 -11
- package/dist/runtime/events.js +3 -6
- package/dist/runtime/identity.d.ts +22 -0
- package/dist/runtime/identity.js +10 -0
- package/dist/runtime/index.d.ts +1 -2
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/module-loader.d.ts +2 -34
- package/dist/runtime/module-loader.js +1 -16
- package/dist/runtime/process.d.ts +6 -24
- package/dist/runtime/process.js +1 -118
- package/dist/runtime/run-persistence.js +2 -5
- package/dist/runtime/runner.d.ts +33 -93
- package/dist/runtime/runner.js +132 -364
- package/dist/runtime/standalone.d.ts +23 -8
- package/dist/runtime/standalone.js +100 -147
- package/dist/server/body.d.ts +16 -8
- package/dist/server/body.js +33 -16
- package/dist/server/dispatcher.d.ts +6 -10
- package/dist/server/dispatcher.js +24 -32
- package/dist/server/http-server.d.ts +87 -0
- package/dist/server/http-server.js +304 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +10 -0
- package/dist/server/mcp-endpoint.d.ts +61 -0
- package/dist/server/mcp-endpoint.js +118 -0
- package/dist/server/routes/actions.js +34 -28
- package/dist/server/routes/common.d.ts +10 -14
- package/dist/server/routes/common.js +32 -56
- package/dist/server/routes/config.js +54 -64
- package/dist/server/routes/doctor.d.ts +1 -1
- package/dist/server/routes/doctor.js +10 -18
- package/dist/server/routes/health.d.ts +1 -1
- package/dist/server/routes/health.js +2 -3
- package/dist/server/routes/info.d.ts +2 -2
- package/dist/server/routes/info.js +12 -13
- package/dist/server/routes/playbooks.js +16 -29
- package/dist/server/routes/runs.js +95 -117
- package/dist/server/routes/state.js +63 -62
- package/dist/server/server.d.ts +2 -2
- package/dist/server/server.js +43 -227
- package/dist/server/types.d.ts +8 -19
- package/dist/service/factory.d.ts +12 -0
- package/dist/service/factory.js +85 -0
- package/dist/{target → service}/index.d.ts +1 -1
- package/dist/{target → service}/index.js +1 -1
- package/dist/service/local.d.ts +27 -0
- package/dist/service/local.js +138 -0
- package/dist/{target → service}/remote-errors.d.ts +3 -6
- package/dist/service/remote-errors.js +38 -0
- package/dist/{target → service}/remote-polling.d.ts +2 -2
- package/dist/{target → service}/remote-polling.js +8 -8
- package/dist/service/remote.d.ts +34 -0
- package/dist/service/remote.js +634 -0
- package/dist/service/sse-stream.d.ts +24 -0
- package/dist/service/sse-stream.js +139 -0
- package/dist/service/types.d.ts +272 -0
- package/dist/service/types.js +32 -0
- package/dist/storage/data-dir-lock.js +10 -5
- package/dist/storage/driver.d.ts +2 -1
- package/dist/storage/driver.js +3 -52
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/lock-core.d.ts +2 -6
- package/dist/storage/lock-core.js +4 -17
- package/dist/storage/params.d.ts +2 -8
- package/dist/storage/params.js +2 -8
- package/dist/storage/sqlite-driver.d.ts +40 -0
- package/dist/storage/sqlite-driver.js +116 -0
- package/dist/storage/sqlite.js +21 -6
- package/dist/storage/types.d.ts +4 -4
- package/dist/storage/types.js +2 -2
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.js +99 -4
- package/dist/utils/process.d.ts +10 -0
- package/dist/utils/process.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +41 -3
- package/dist/app/index.d.ts +0 -2
- package/dist/app/index.js +0 -2
- package/dist/app/types.js +0 -1
- package/dist/catalog/action-index.d.ts +0 -9
- package/dist/catalog/action-index.js +0 -95
- package/dist/catalog/action-resolver.d.ts +0 -20
- package/dist/catalog/action-resolver.js +0 -95
- package/dist/catalog/location-registry.d.ts +0 -15
- package/dist/catalog/location-registry.js +0 -138
- package/dist/catalog/package-catalog.d.ts +0 -7
- package/dist/catalog/package-catalog.js +0 -77
- package/dist/catalog/types.d.ts +0 -68
- package/dist/catalog/types.js +0 -1
- package/dist/export/index.d.ts +0 -1
- package/dist/export/index.js +0 -1
- package/dist/export/templates.d.ts +0 -71
- package/dist/export/templates.js +0 -635
- package/dist/ipc/target.d.ts +0 -74
- package/dist/ipc/target.js +0 -365
- package/dist/platform/default.d.ts +0 -38
- package/dist/platform/default.js +0 -78
- package/dist/project/resolver.d.ts +0 -116
- package/dist/project/resolver.js +0 -376
- package/dist/registry/resolve.d.ts +0 -62
- package/dist/registry/resolve.js +0 -195
- package/dist/runtime/execution-manager.d.ts +0 -48
- package/dist/runtime/execution-manager.js +0 -66
- package/dist/runtime/package-runner-factory.d.ts +0 -82
- package/dist/runtime/package-runner-factory.js +0 -186
- package/dist/target/local.d.ts +0 -53
- package/dist/target/local.js +0 -349
- package/dist/target/remote-errors.js +0 -51
- package/dist/target/remote.d.ts +0 -86
- package/dist/target/remote.js +0 -818
- package/dist/target/target.d.ts +0 -6
- package/dist/target/target.js +0 -37
- package/dist/target/types.d.ts +0 -261
- package/dist/target/types.js +0 -35
- /package/dist/{app → package}/static-index.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.js +0 -0
package/dist/registry/lock.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFile, mkdir, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isProcessAlive } from "../utils/process.js";
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
6
|
+
* 锁目录最近一次心跳(创建或续期)后超过该时长且持有进程已死亡,
|
|
7
|
+
* 才视为持有进程异常退出的残留锁。
|
|
4
8
|
*/
|
|
5
9
|
export const REGISTRY_LOCK_STALE_MS = 10000;
|
|
6
10
|
/**
|
|
@@ -12,29 +16,120 @@ export const REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS = 15000;
|
|
|
12
16
|
* 两次获取尝试之间的退避间隔,休眠让出事件循环,避免忙等阻塞。
|
|
13
17
|
*/
|
|
14
18
|
export const REGISTRY_LOCK_RETRY_DELAY_MS = 25;
|
|
19
|
+
/**
|
|
20
|
+
* 持锁期间的心跳续期间隔。持有者定期 touch 锁目录刷新 mtime,
|
|
21
|
+
* 使长耗时持锁操作(如 linkPackage 的深度扫描)不会仅因耗时超过
|
|
22
|
+
* stale 阈值而被等待方误判为残留锁抢占。
|
|
23
|
+
*/
|
|
24
|
+
export const REGISTRY_LOCK_HEARTBEAT_MS = 3000;
|
|
25
|
+
/**
|
|
26
|
+
* 锁目录内的持有者元数据文件名。
|
|
27
|
+
*/
|
|
28
|
+
const LOCK_METADATA_FILE = "metadata.json";
|
|
15
29
|
function sleep(ms) {
|
|
16
30
|
return new Promise((r) => setTimeout(r, ms));
|
|
17
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* 读取锁目录中的持有者元数据;缺失、不可读或结构非法时返回 undefined。
|
|
34
|
+
* 元数据信息缺失时陈旧判定退化为仅看 mtime 年龄(与历史行为兼容)。
|
|
35
|
+
*/
|
|
36
|
+
async function readLockMetadata(lockDir) {
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = await readFile(join(lockDir, LOCK_METADATA_FILE), "utf-8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
if (parsed &&
|
|
47
|
+
typeof parsed === "object" &&
|
|
48
|
+
typeof parsed.pid === "number" &&
|
|
49
|
+
typeof parsed.token === "string") {
|
|
50
|
+
return {
|
|
51
|
+
pid: parsed.pid,
|
|
52
|
+
token: parsed.token,
|
|
53
|
+
createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : "",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// 损坏的元数据视同缺失
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 本进程当前持有的注册表锁目录集合。
|
|
64
|
+
* 用于区分「同进程在途持锁」(不可回收,需等待)与「同进程历史泄漏锁」
|
|
65
|
+
* (释放阶段 rm 失败遗留,pid 存活复核下仍应可回收),
|
|
66
|
+
* 避免存活 pid 保守判定把自身泄漏锁变成永久死锁。
|
|
67
|
+
*/
|
|
68
|
+
const activeLockDirs = new Set();
|
|
69
|
+
/**
|
|
70
|
+
* 判定锁目录是否为可回收的残留锁。
|
|
71
|
+
*
|
|
72
|
+
* 判定语义(保守优先,保护并发正确性):
|
|
73
|
+
* - mtime 未超龄:一律不回收(正常持锁或心跳间隔内的活跃锁)。
|
|
74
|
+
* - mtime 超龄 且元数据含存活 pid:不回收(慢磁盘下长扫描的持锁者仍活着;
|
|
75
|
+
* 同进程在途持锁同样不回收,交由释放后重试获取)。
|
|
76
|
+
* - mtime 超龄 且元数据 pid 已死:回收。
|
|
77
|
+
* - mtime 超龄 且元数据缺失/非法:仅看 mtime 维持历史行为回收
|
|
78
|
+
* (兼容旧版本无元数据锁目录与竞态窗口内的半初始化锁目录)。
|
|
79
|
+
*/
|
|
80
|
+
async function isStaleLock(lockDir, staleMs = REGISTRY_LOCK_STALE_MS) {
|
|
81
|
+
let lockAgeMs;
|
|
82
|
+
try {
|
|
83
|
+
const info = await stat(lockDir);
|
|
84
|
+
lockAgeMs = Date.now() - info.mtimeMs;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
// ENOENT:持有者恰好在两次探测之间释放,交由调用方立即重试获取
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
if (lockAgeMs <= staleMs) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const metadata = await readLockMetadata(lockDir);
|
|
94
|
+
if (metadata === undefined) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (metadata.pid === process.pid) {
|
|
98
|
+
// 同进程:仅在途持锁不可回收,历史泄漏锁(不在活跃集合)可回收
|
|
99
|
+
return !activeLockDirs.has(lockDir);
|
|
100
|
+
}
|
|
101
|
+
return !isProcessAlive(metadata.pid);
|
|
102
|
+
}
|
|
18
103
|
/**
|
|
19
104
|
* 异步注册表锁(防止多进程并发读写导致 registry.json 损坏)。
|
|
20
105
|
*
|
|
21
|
-
* 获取方式:fs/promises mkdir
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
106
|
+
* 获取方式:fs/promises mkdir 的原子性保证同一时刻仅一个进程能创建锁目录,
|
|
107
|
+
* 创建成功后立即写入含 pid 与随机 token 的 metadata.json。
|
|
108
|
+
* 冲突处理:EEXIST 时按保守语义判定残留(见 isStaleLock):mtime 超龄且
|
|
109
|
+
* pid 已死才抢占——抢占仍通过原子 rename 到隔离名实现,rename 仅有一个
|
|
110
|
+
* 进程能成功,失败方回到等待重试,消除「检查后删除再重建」的竞态窗口;
|
|
111
|
+
* 未过期的锁通过 sleep 让出事件循环后重试。
|
|
112
|
+
* 心跳续期:持锁期间以固定间隔 touch 锁目录刷新 mtime,使 linkPackage 等
|
|
113
|
+
* 长耗时持锁操作不会仅因耗时被误判残留;fn 完成后停止心跳并删除锁目录。
|
|
25
114
|
*
|
|
26
115
|
* @param filePath 注册表文件路径,锁目录为其同级 `${filePath}.lock`
|
|
27
116
|
* @param fn 持锁期间执行的操作(同步或异步)
|
|
117
|
+
* @param options 可选锁超时与重试配置
|
|
28
118
|
* @returns fn 的返回值
|
|
29
|
-
* @throws 超过
|
|
119
|
+
* @throws 超过 acquireTimeoutMs 仍未获取时抛出描述性错误
|
|
30
120
|
*/
|
|
31
|
-
export async function withRegistryLock(filePath, fn) {
|
|
121
|
+
export async function withRegistryLock(filePath, fn, options) {
|
|
122
|
+
const staleMs = options?.staleMs ?? REGISTRY_LOCK_STALE_MS;
|
|
123
|
+
const acquireTimeoutMs = options?.acquireTimeoutMs ?? REGISTRY_LOCK_ACQUIRE_TIMEOUT_MS;
|
|
124
|
+
const retryDelayMs = options?.retryDelayMs ?? REGISTRY_LOCK_RETRY_DELAY_MS;
|
|
125
|
+
const heartbeatMs = options?.heartbeatMs ?? REGISTRY_LOCK_HEARTBEAT_MS;
|
|
32
126
|
const lockDir = `${filePath}.lock`;
|
|
33
|
-
const deadline = Date.now() +
|
|
127
|
+
const deadline = Date.now() + acquireTimeoutMs;
|
|
34
128
|
while (true) {
|
|
35
129
|
try {
|
|
36
130
|
// 原子获取:目录创建成功即持有锁,无需二次确认
|
|
37
131
|
await mkdir(lockDir);
|
|
132
|
+
activeLockDirs.add(lockDir);
|
|
38
133
|
break;
|
|
39
134
|
}
|
|
40
135
|
catch (err) {
|
|
@@ -42,11 +137,10 @@ export async function withRegistryLock(filePath, fn) {
|
|
|
42
137
|
throw err;
|
|
43
138
|
}
|
|
44
139
|
}
|
|
45
|
-
//
|
|
46
|
-
let
|
|
140
|
+
// 锁被占用,检查是否为可回收的残留锁
|
|
141
|
+
let stale;
|
|
47
142
|
try {
|
|
48
|
-
|
|
49
|
-
lockAgeMs = Date.now() - info.mtimeMs;
|
|
143
|
+
stale = await isStaleLock(lockDir, staleMs);
|
|
50
144
|
}
|
|
51
145
|
catch (err) {
|
|
52
146
|
if (err.code === "ENOENT") {
|
|
@@ -55,7 +149,7 @@ export async function withRegistryLock(filePath, fn) {
|
|
|
55
149
|
}
|
|
56
150
|
throw err;
|
|
57
151
|
}
|
|
58
|
-
if (
|
|
152
|
+
if (stale) {
|
|
59
153
|
// 原子抢占:rename 到含进程号与时间戳的隔离名,仅一个进程能成功;
|
|
60
154
|
// 失败方说明已被其他等待者抢先处理,回到等待重试即可
|
|
61
155
|
const quarantine = `${lockDir}.stale.${process.pid}.${Date.now()}`;
|
|
@@ -73,15 +167,41 @@ export async function withRegistryLock(filePath, fn) {
|
|
|
73
167
|
}
|
|
74
168
|
}
|
|
75
169
|
if (Date.now() > deadline) {
|
|
76
|
-
throw new Error(`Failed to acquire registry lock '${lockDir}' within ${
|
|
170
|
+
throw new Error(`Failed to acquire registry lock '${lockDir}' within ${acquireTimeoutMs}ms. ` +
|
|
77
171
|
`The lock may be held by another ActionDock process; retry after it exits or remove the stale lock directory manually.`);
|
|
78
172
|
}
|
|
79
|
-
await sleep(
|
|
173
|
+
await sleep(retryDelayMs);
|
|
174
|
+
}
|
|
175
|
+
// 持有者元数据写入失败不阻断持锁(退化为仅 mtime 判定),但保证可观测
|
|
176
|
+
try {
|
|
177
|
+
const metadata = {
|
|
178
|
+
pid: process.pid,
|
|
179
|
+
token: randomUUID(),
|
|
180
|
+
createdAt: new Date().toISOString(),
|
|
181
|
+
};
|
|
182
|
+
await writeFile(join(lockDir, LOCK_METADATA_FILE), JSON.stringify(metadata, null, 2), "utf-8");
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
186
|
+
console.warn(`[Registry] Failed to write lock metadata in '${lockDir}': ${reason}`);
|
|
80
187
|
}
|
|
188
|
+
// 心跳续期:定期 touch 锁目录刷新 mtime,防止长耗时持锁操作被误判残留。
|
|
189
|
+
// 单次 touch 失败仅告警不中断(mtime 未刷新仅会提前暴露为陈旧候选,
|
|
190
|
+
// 由 pid 存活复核兜底,不会破坏互斥)。
|
|
191
|
+
const heartbeat = setInterval(() => {
|
|
192
|
+
const now = new Date();
|
|
193
|
+
utimes(lockDir, now, now).catch((err) => {
|
|
194
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
195
|
+
console.warn(`[Registry] Failed to refresh lock heartbeat for '${lockDir}': ${reason}`);
|
|
196
|
+
});
|
|
197
|
+
}, heartbeatMs);
|
|
198
|
+
heartbeat.unref?.();
|
|
81
199
|
try {
|
|
82
200
|
return await fn();
|
|
83
201
|
}
|
|
84
202
|
finally {
|
|
203
|
+
clearInterval(heartbeat);
|
|
204
|
+
activeLockDirs.delete(lockDir);
|
|
85
205
|
try {
|
|
86
206
|
await rm(lockDir, { recursive: true, force: true });
|
|
87
207
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport,
|
|
1
|
+
import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, UnlinkResult } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* 注册表内容解析与 links 迁移(纯函数,同步与异步加载共享的唯一事实源)。
|
|
4
4
|
*
|
|
@@ -34,11 +34,7 @@ export declare function linkPackage(targetPath?: string, customHome?: string, op
|
|
|
34
34
|
}): Promise<LinkResult>;
|
|
35
35
|
export declare function unlinkPackage(identifier?: string, customHome?: string): Promise<UnlinkResult | null>;
|
|
36
36
|
export declare function listLinkedPackages(customHome?: string): LinkedPackageEntry[];
|
|
37
|
-
export declare function listLinkedPackagesAsync(customHome?: string): Promise<LinkedPackageEntry[]>;
|
|
38
37
|
export declare function listLinkedWorkspaces(customHome?: string): LinkedWorkspaceEntry[];
|
|
39
|
-
export declare function resolveActionProjectSync(actionIdentifier: string, cwd?: string, customHome?: string): ResolvedActionProject;
|
|
40
|
-
export declare function resolveActionProject(actionIdentifier: string, cwd?: string, customHome?: string): Promise<ResolvedActionProject>;
|
|
41
|
-
export declare function resolvePlaybookProject(playbookIdentifier: string, cwd?: string, customHome?: string): ResolvedPlaybookProject;
|
|
42
38
|
export declare function resolvePackageRoot(packageIdOrPath?: string, cwd?: string, customHome?: string): string | null;
|
|
43
39
|
export declare function getRegistryStatus(customHome?: string): RegistryStatusReport;
|
|
44
40
|
export declare function pruneRegistry(customHome?: string): Promise<PruneResult>;
|
|
@@ -4,8 +4,7 @@ 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";
|
|
7
|
-
import { buildLinkedPackageEntry, discoverProjectConfigs,
|
|
8
|
-
import { probeActionAsync, probeActionSync, probePlaybook, resolveEntityFlow, runFlowAsync, runFlowSync, } from "./resolve.js";
|
|
7
|
+
import { buildLinkedPackageEntry, discoverProjectConfigs, discoverProjects, } from "./scan.js";
|
|
9
8
|
/**
|
|
10
9
|
* 空注册表初始结构。
|
|
11
10
|
*/
|
|
@@ -400,61 +399,10 @@ export function listLinkedPackages(customHome) {
|
|
|
400
399
|
}
|
|
401
400
|
return Object.values(result);
|
|
402
401
|
}
|
|
403
|
-
export async function listLinkedPackagesAsync(customHome) {
|
|
404
|
-
const registry = await loadRegistryAsync(customHome);
|
|
405
|
-
const result = { ...registry.packages };
|
|
406
|
-
if (registry.workspaces) {
|
|
407
|
-
for (const ws of Object.values(registry.workspaces)) {
|
|
408
|
-
if (!(await pathExistsAsync(ws.path)))
|
|
409
|
-
continue;
|
|
410
|
-
for (const { root, config } of await discoverProjectConfigsAsync(ws.path)) {
|
|
411
|
-
if (!result[config.id] || result[config.id].workspaceRoot === ws.path) {
|
|
412
|
-
result[config.id] = buildLinkedPackageEntry(config, root, ws.linkedAt, ws.path);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return Object.values(result);
|
|
418
|
-
}
|
|
419
402
|
export function listLinkedWorkspaces(customHome) {
|
|
420
403
|
const registry = loadRegistry(customHome);
|
|
421
404
|
return Object.values(registry.workspaces || {});
|
|
422
405
|
}
|
|
423
|
-
export function resolveActionProjectSync(actionIdentifier, cwd = process.cwd(), customHome) {
|
|
424
|
-
return runFlowSync(resolveEntityFlow({
|
|
425
|
-
identifier: actionIdentifier,
|
|
426
|
-
cwd,
|
|
427
|
-
entityNoun: "Action",
|
|
428
|
-
listLinkedPackages: () => listLinkedPackages(customHome),
|
|
429
|
-
probe: (root, config, id) => probeActionSync(root, config, id),
|
|
430
|
-
buildResult: (projectRoot, packageId, actionId) => ({ projectRoot, packageId, actionId }),
|
|
431
|
-
}));
|
|
432
|
-
}
|
|
433
|
-
export async function resolveActionProject(actionIdentifier, cwd = process.cwd(), customHome) {
|
|
434
|
-
return runFlowAsync(resolveEntityFlow({
|
|
435
|
-
identifier: actionIdentifier,
|
|
436
|
-
cwd,
|
|
437
|
-
entityNoun: "Action",
|
|
438
|
-
listLinkedPackages: () => listLinkedPackagesAsync(customHome),
|
|
439
|
-
probe: (root, config, id) => probeActionAsync(root, config, id),
|
|
440
|
-
buildResult: (projectRoot, packageId, actionId) => ({ projectRoot, packageId, actionId }),
|
|
441
|
-
}));
|
|
442
|
-
}
|
|
443
|
-
export function resolvePlaybookProject(playbookIdentifier, cwd = process.cwd(), customHome) {
|
|
444
|
-
return runFlowSync(resolveEntityFlow({
|
|
445
|
-
identifier: playbookIdentifier,
|
|
446
|
-
cwd,
|
|
447
|
-
entityNoun: "Playbook",
|
|
448
|
-
listLinkedPackages: () => listLinkedPackages(customHome),
|
|
449
|
-
probe: (root, config, id) => probePlaybook(root, config, id),
|
|
450
|
-
buildResult: (projectRoot, packageId, playbookId, playbook) => ({
|
|
451
|
-
projectRoot,
|
|
452
|
-
packageId,
|
|
453
|
-
playbookId,
|
|
454
|
-
playbook,
|
|
455
|
-
}),
|
|
456
|
-
}));
|
|
457
|
-
}
|
|
458
406
|
export function resolvePackageRoot(packageIdOrPath, cwd, customHome) {
|
|
459
407
|
if (!packageIdOrPath) {
|
|
460
408
|
return findProjectRoot(cwd);
|
package/dist/registry/scan.d.ts
CHANGED
|
@@ -8,10 +8,6 @@ export declare const IGNORED_SCAN_DIRS: Set<string>;
|
|
|
8
8
|
* 递归扫描包含 actiondock.json 的子项目根目录(同步版本)。
|
|
9
9
|
*/
|
|
10
10
|
export declare function discoverProjects(dir: string, maxDepth?: number): string[];
|
|
11
|
-
/**
|
|
12
|
-
* 递归扫描包含 actiondock.json 的子项目根目录(异步版本,逻辑与同步版一致)。
|
|
13
|
-
*/
|
|
14
|
-
export declare function discoverProjectsAsync(dir: string, maxDepth?: number): Promise<string[]>;
|
|
15
11
|
/**
|
|
16
12
|
* 扫描并加载全部子项目配置(同步版本)。
|
|
17
13
|
* 损坏项目(配置缺失或非法)跳过不中断:注册表操作面对的是外部任意目录,
|
|
@@ -21,18 +17,7 @@ export declare function discoverProjectConfigs(dir: string, maxDepth?: number):
|
|
|
21
17
|
root: string;
|
|
22
18
|
config: ReturnType<typeof loadProjectConfig>;
|
|
23
19
|
}>;
|
|
24
|
-
/**
|
|
25
|
-
* 扫描并加载全部子项目配置(异步版本,跳过策略与同步版一致)。
|
|
26
|
-
*/
|
|
27
|
-
export declare function discoverProjectConfigsAsync(dir: string, maxDepth?: number): Promise<Array<{
|
|
28
|
-
root: string;
|
|
29
|
-
config: ReturnType<typeof loadProjectConfig>;
|
|
30
|
-
}>>;
|
|
31
20
|
/**
|
|
32
21
|
* 由项目配置构造注册表链接条目(纯函数,无副作用)。
|
|
33
22
|
*/
|
|
34
23
|
export declare function buildLinkedPackageEntry(config: ReturnType<typeof loadProjectConfig>, path: string, linkedAt: string, workspaceRoot?: string): LinkedPackageEntry;
|
|
35
|
-
/**
|
|
36
|
-
* 异步探测路径是否存在。
|
|
37
|
-
*/
|
|
38
|
-
export declare function pathExistsAsync(path: string): Promise<boolean>;
|
package/dist/registry/scan.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { readdirSync } from "node:fs";
|
|
2
|
-
import { readdir, stat } from "node:fs/promises";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import { loadProjectConfig } from "../project/loader.js";
|
|
5
4
|
/**
|
|
@@ -45,37 +44,6 @@ export function discoverProjects(dir, maxDepth = 3) {
|
|
|
45
44
|
walk(resolvedDir, 1);
|
|
46
45
|
return results;
|
|
47
46
|
}
|
|
48
|
-
/**
|
|
49
|
-
* 递归扫描包含 actiondock.json 的子项目根目录(异步版本,逻辑与同步版一致)。
|
|
50
|
-
*/
|
|
51
|
-
export async function discoverProjectsAsync(dir, maxDepth = 3) {
|
|
52
|
-
const results = [];
|
|
53
|
-
const resolvedDir = resolve(dir);
|
|
54
|
-
async function walk(currentDir, currentDepth) {
|
|
55
|
-
if (currentDepth > maxDepth)
|
|
56
|
-
return;
|
|
57
|
-
let entries;
|
|
58
|
-
try {
|
|
59
|
-
entries = await readdir(currentDir, { withFileTypes: true });
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
// 忽略无法读取的目录:扫描是尽力而为的探测,单目录不可读不应中断整体发现
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const hasActiondock = entries.some((e) => e.isFile() && e.name === "actiondock.json");
|
|
66
|
-
if (hasActiondock && currentDir !== resolvedDir) {
|
|
67
|
-
results.push(currentDir);
|
|
68
|
-
return; // 不再向项目内部子目录递归
|
|
69
|
-
}
|
|
70
|
-
for (const entry of entries) {
|
|
71
|
-
if (entry.isDirectory() && !IGNORED_SCAN_DIRS.has(entry.name)) {
|
|
72
|
-
await walk(join(currentDir, entry.name), currentDepth + 1);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
await walk(resolvedDir, 1);
|
|
77
|
-
return results;
|
|
78
|
-
}
|
|
79
47
|
/**
|
|
80
48
|
* 扫描并加载全部子项目配置(同步版本)。
|
|
81
49
|
* 损坏项目(配置缺失或非法)跳过不中断:注册表操作面对的是外部任意目录,
|
|
@@ -93,21 +61,6 @@ export function discoverProjectConfigs(dir, maxDepth = 3) {
|
|
|
93
61
|
}
|
|
94
62
|
return results;
|
|
95
63
|
}
|
|
96
|
-
/**
|
|
97
|
-
* 扫描并加载全部子项目配置(异步版本,跳过策略与同步版一致)。
|
|
98
|
-
*/
|
|
99
|
-
export async function discoverProjectConfigsAsync(dir, maxDepth = 3) {
|
|
100
|
-
const results = [];
|
|
101
|
-
for (const root of await discoverProjectsAsync(dir, maxDepth)) {
|
|
102
|
-
try {
|
|
103
|
-
results.push({ root, config: loadProjectConfig(root) });
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
// 跳过损坏项目:其 actiondock.json 缺失必要字段或非法,无法构成有效链接条目
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return results;
|
|
110
|
-
}
|
|
111
64
|
/**
|
|
112
65
|
* 由项目配置构造注册表链接条目(纯函数,无副作用)。
|
|
113
66
|
*/
|
|
@@ -121,15 +74,3 @@ export function buildLinkedPackageEntry(config, path, linkedAt, workspaceRoot) {
|
|
|
121
74
|
...(workspaceRoot ? { workspaceRoot } : {}),
|
|
122
75
|
};
|
|
123
76
|
}
|
|
124
|
-
/**
|
|
125
|
-
* 异步探测路径是否存在。
|
|
126
|
-
*/
|
|
127
|
-
export async function pathExistsAsync(path) {
|
|
128
|
-
try {
|
|
129
|
-
await stat(path);
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { GlobalRegistryData, LinkedPackageEntry, LinkedWorkspaceEntry, LinkResult, PruneResult, RegistryStatusReport, UnlinkResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* 用户显式注册位置存储契约。
|
|
4
|
+
* 职责严格限制为存储与读取用户显式 link/unlink 的物理位置(packages 与 workspaces)。
|
|
5
|
+
*/
|
|
6
|
+
export interface RegistryStore {
|
|
7
|
+
readonly customHome?: string;
|
|
8
|
+
getFilePath(): string;
|
|
9
|
+
load(): GlobalRegistryData;
|
|
10
|
+
loadAsync(): Promise<GlobalRegistryData>;
|
|
11
|
+
save(data: GlobalRegistryData): Promise<void>;
|
|
12
|
+
listPackages(): LinkedPackageEntry[];
|
|
13
|
+
listWorkspaces(): LinkedWorkspaceEntry[];
|
|
14
|
+
link(path: string, options?: {
|
|
15
|
+
recursive?: boolean;
|
|
16
|
+
}): Promise<LinkResult>;
|
|
17
|
+
unlink(idOrPath: string): Promise<UnlinkResult | null>;
|
|
18
|
+
prune(): Promise<PruneResult>;
|
|
19
|
+
getStatus(): RegistryStatusReport;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 默认注册表物理位置存储实现。
|
|
23
|
+
* 直接基于文件系统注册表文件(~/.actiondock/registry.json)读写;
|
|
24
|
+
* 全部方法均为向 registry.ts 自由函数透传 customHome 的零逻辑转发,
|
|
25
|
+
* 收敛为单行委托以消除重复样板。
|
|
26
|
+
*/
|
|
27
|
+
export declare class DefaultRegistryStore implements RegistryStore {
|
|
28
|
+
readonly customHome?: string;
|
|
29
|
+
constructor(customHome?: string);
|
|
30
|
+
getFilePath(): string;
|
|
31
|
+
load(): GlobalRegistryData;
|
|
32
|
+
loadAsync(): Promise<GlobalRegistryData>;
|
|
33
|
+
save(data: GlobalRegistryData): Promise<void>;
|
|
34
|
+
listPackages(): LinkedPackageEntry[];
|
|
35
|
+
listWorkspaces(): LinkedWorkspaceEntry[];
|
|
36
|
+
link(path: string, options?: {
|
|
37
|
+
recursive?: boolean;
|
|
38
|
+
}): Promise<LinkResult>;
|
|
39
|
+
unlink(idOrPath: string): Promise<UnlinkResult | null>;
|
|
40
|
+
prune(): Promise<PruneResult>;
|
|
41
|
+
getStatus(): RegistryStatusReport;
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getRegistryFilePath, getRegistryStatus, linkPackage, listLinkedPackages, listLinkedWorkspaces, loadRegistry, loadRegistryAsync, pruneRegistry, saveRegistry, unlinkPackage, } from "./registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* 默认注册表物理位置存储实现。
|
|
4
|
+
* 直接基于文件系统注册表文件(~/.actiondock/registry.json)读写;
|
|
5
|
+
* 全部方法均为向 registry.ts 自由函数透传 customHome 的零逻辑转发,
|
|
6
|
+
* 收敛为单行委托以消除重复样板。
|
|
7
|
+
*/
|
|
8
|
+
export class DefaultRegistryStore {
|
|
9
|
+
customHome;
|
|
10
|
+
constructor(customHome) {
|
|
11
|
+
this.customHome = customHome;
|
|
12
|
+
}
|
|
13
|
+
getFilePath() {
|
|
14
|
+
return getRegistryFilePath(this.customHome);
|
|
15
|
+
}
|
|
16
|
+
load() {
|
|
17
|
+
return loadRegistry(this.customHome);
|
|
18
|
+
}
|
|
19
|
+
loadAsync() {
|
|
20
|
+
return loadRegistryAsync(this.customHome);
|
|
21
|
+
}
|
|
22
|
+
save(data) {
|
|
23
|
+
return saveRegistry(data, this.customHome);
|
|
24
|
+
}
|
|
25
|
+
listPackages() {
|
|
26
|
+
return listLinkedPackages(this.customHome);
|
|
27
|
+
}
|
|
28
|
+
listWorkspaces() {
|
|
29
|
+
return listLinkedWorkspaces(this.customHome);
|
|
30
|
+
}
|
|
31
|
+
link(path, options) {
|
|
32
|
+
return linkPackage(path, this.customHome, options);
|
|
33
|
+
}
|
|
34
|
+
unlink(idOrPath) {
|
|
35
|
+
return unlinkPackage(idOrPath, this.customHome);
|
|
36
|
+
}
|
|
37
|
+
prune() {
|
|
38
|
+
return pruneRegistry(this.customHome);
|
|
39
|
+
}
|
|
40
|
+
getStatus() {
|
|
41
|
+
return getRegistryStatus(this.customHome);
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/registry/types.d.ts
CHANGED
|
@@ -91,27 +91,3 @@ export interface PruneResult {
|
|
|
91
91
|
prunedPackages: LinkedPackageEntry[];
|
|
92
92
|
prunedWorkspaces: LinkedWorkspaceEntry[];
|
|
93
93
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Action 归属项目解析结果。
|
|
96
|
-
*/
|
|
97
|
-
export interface ResolvedActionProject {
|
|
98
|
-
/** 所在项目的根目录绝对路径 */
|
|
99
|
-
projectRoot: string;
|
|
100
|
-
/** 所属 Package ID */
|
|
101
|
-
packageId: string;
|
|
102
|
-
/** 匹配到的 Action ID */
|
|
103
|
-
actionId: string;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Playbook 归属项目解析结果。
|
|
107
|
-
*/
|
|
108
|
-
export interface ResolvedPlaybookProject {
|
|
109
|
-
/** 所在项目的根目录绝对路径 */
|
|
110
|
-
projectRoot: string;
|
|
111
|
-
/** 所属 Package ID */
|
|
112
|
-
packageId: string;
|
|
113
|
-
/** 匹配到的 Playbook ID */
|
|
114
|
-
playbookId: string;
|
|
115
|
-
/** Playbook 实体数据 */
|
|
116
|
-
playbook: import("../project/types.js").PlaybookDefinition;
|
|
117
|
-
}
|
|
@@ -39,7 +39,3 @@ export declare function findLocalAction(actions: Map<string, ActionDefinition>,
|
|
|
39
39
|
* 不额外吞异或静默改道,保持边界行为零变化。
|
|
40
40
|
*/
|
|
41
41
|
export declare function isActionDefinitionObject(value: unknown): value is ActionDefinition;
|
|
42
|
-
/**
|
|
43
|
-
* 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
|
|
44
|
-
*/
|
|
45
|
-
export declare function parseActionRef(ref: ActionRef | string): ActionRef;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ActionResolver } from "../catalog/action-resolver.js";
|
|
2
1
|
/**
|
|
3
2
|
* Action 注册表(单一职责模块)。
|
|
4
3
|
*
|
|
@@ -100,9 +99,3 @@ export function isActionDefinitionObject(value) {
|
|
|
100
99
|
"run" in value &&
|
|
101
100
|
typeof value.run === "function");
|
|
102
101
|
}
|
|
103
|
-
/**
|
|
104
|
-
* 解析 Action 引用或标识符(解析失败抛出原始异常,由调用方分类处理)。
|
|
105
|
-
*/
|
|
106
|
-
export function parseActionRef(ref) {
|
|
107
|
-
return ActionResolver.parseRef(ref);
|
|
108
|
-
}
|
package/dist/runtime/context.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { MemoryProcessDriver, ProcessManager } from "../process/index.js";
|
|
2
|
+
import { createDefaultProcessOwner } from "../invocation/types.js";
|
|
3
3
|
import { resolveEnvValue } from "./env.js";
|
|
4
|
+
import { ActionDockError, INVALID_ACTION_REF } from "../errors.js";
|
|
4
5
|
/**
|
|
5
6
|
* 生产级配置解析器实现。
|
|
6
7
|
* 严格践行 5 层配置解析优先级链:
|
|
@@ -166,14 +167,12 @@ export function createActionContext(options) {
|
|
|
166
167
|
const state = new RuntimeStateStore(options.storage);
|
|
167
168
|
const log = options.logger || new StderrLogger();
|
|
168
169
|
const signal = options.signal ?? new AbortController().signal;
|
|
169
|
-
const currentRunId = options.runId || randomUUID();
|
|
170
|
+
const currentRunId = options.runId || crypto.randomUUID();
|
|
170
171
|
const currentRootRunId = options.rootRunId || options.parentRunId || currentRunId;
|
|
171
172
|
const invoke = async (action, input) => {
|
|
172
173
|
if (typeof action !== "string" &&
|
|
173
174
|
(!action || typeof action !== "object" || typeof action.run === "function" || !("actionId" in action))) {
|
|
174
|
-
|
|
175
|
-
err.code = "INVALID_ACTION_REF";
|
|
176
|
-
throw err;
|
|
175
|
+
throw new ActionDockError(INVALID_ACTION_REF, "INVALID_ACTION_REF: ctx.actions.invoke strictly accepts only ActionRef or string, passing ActionDefinition or function is prohibited");
|
|
177
176
|
}
|
|
178
177
|
if (options.onActionInvoke) {
|
|
179
178
|
return (await options.onActionInvoke(action, input, currentRunId));
|
|
@@ -182,12 +181,7 @@ export function createActionContext(options) {
|
|
|
182
181
|
};
|
|
183
182
|
const invokerFn = (ref, input) => invoke(ref, input);
|
|
184
183
|
const invoker = Object.assign(invokerFn, { invoke });
|
|
185
|
-
const defaultOwner =
|
|
186
|
-
tenantId: "default",
|
|
187
|
-
principalId: "default",
|
|
188
|
-
packageInstanceId: "default",
|
|
189
|
-
generationId: "default",
|
|
190
|
-
};
|
|
184
|
+
const defaultOwner = createDefaultProcessOwner();
|
|
191
185
|
const effectiveOwner = options.owner || defaultOwner;
|
|
192
186
|
// 外部注入的可能是平台级共享 ContextProcessAPI(未绑定 runId),直接复用会让
|
|
193
187
|
// 多个 run 共享同一隔离与回收状态;此处派生 run 级实例保证隔离与自动回收均以 run 为单位
|
package/dist/runtime/events.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED } from "../errors.js";
|
|
1
|
+
import { ActionDockError, EVENT_BACKPRESSURE_LIMIT, EVENT_CURSOR_EXPIRED } from "../errors.js";
|
|
2
2
|
/**
|
|
3
3
|
* 订阅者通道:封装实时事件队列、监听器与终止状态。
|
|
4
4
|
* 职责:
|
|
@@ -409,16 +409,13 @@ export class InMemoryEventSink {
|
|
|
409
409
|
const earliestSeq = run.earliestRetainedSequence ?? 0;
|
|
410
410
|
if (afterSequence !== undefined && afterSequence >= 0 && afterSequence < earliestSeq - 1) {
|
|
411
411
|
const earliestCursor = run.earliestRetainedEventId ?? String(earliestSeq);
|
|
412
|
-
|
|
413
|
-
expiredErr.code = EVENT_CURSOR_EXPIRED;
|
|
414
|
-
expiredErr.details = {
|
|
412
|
+
throw new ActionDockError(EVENT_CURSOR_EXPIRED, `Event cursor '${after}' has expired; earliest available cursor is '${earliestCursor}'`, {
|
|
415
413
|
cursor: after,
|
|
416
414
|
earliestCursor,
|
|
417
415
|
earliestRetainedCursor: earliestCursor,
|
|
418
416
|
earliestSequence: earliestSeq,
|
|
419
417
|
earliestEventId: run.earliestRetainedEventId,
|
|
420
|
-
};
|
|
421
|
-
throw expiredErr;
|
|
418
|
+
});
|
|
422
419
|
}
|
|
423
420
|
}
|
|
424
421
|
/**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 包唯一物理与快照身份标识值对象(PackageIdentity)。
|
|
3
|
+
*
|
|
4
|
+
* 铁律 7:只在 PackageRuntime 创建时生成一次,统一传递给 ExecutionService、
|
|
5
|
+
* Runner、ProcessOwner、RunRecord 等,禁止内部组件自行 fallback。
|
|
6
|
+
*/
|
|
7
|
+
export interface PackageIdentity {
|
|
8
|
+
/** 包唯一逻辑标识符 */
|
|
9
|
+
readonly id: string;
|
|
10
|
+
/** 包物理实例标识符(单次加载/运行周期的全局唯一物理实例标识) */
|
|
11
|
+
readonly instanceId: string;
|
|
12
|
+
/** 快照代次标识符 */
|
|
13
|
+
readonly generation: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 创建 PackageIdentity 值对象单一事实工厂。
|
|
17
|
+
*/
|
|
18
|
+
export declare function createPackageIdentity(options: {
|
|
19
|
+
id: string;
|
|
20
|
+
instanceId?: string;
|
|
21
|
+
generation?: string;
|
|
22
|
+
}): PackageIdentity;
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
export * from "./identity.js";
|
|
1
2
|
export * from "./context.js";
|
|
2
3
|
export * from "./action-collection.js";
|
|
3
4
|
export * from "./action-registry.js";
|
|
4
5
|
export * from "./run-persistence.js";
|
|
5
|
-
export * from "./package-runner-factory.js";
|
|
6
6
|
export * from "./runner.js";
|
|
7
|
-
export * from "./execution-manager.js";
|
|
8
7
|
export * from "./standalone.js";
|
|
9
8
|
export * from "./env.js";
|
|
10
9
|
export * from "./clock.js";
|