@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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { normalizeSqliteParams } from "./params.js";
|
|
3
|
+
import type { SqliteDriver, SqliteStatement } from "./types.js";
|
|
4
|
+
export { normalizeSqliteParams };
|
|
5
|
+
/**
|
|
6
|
+
* 基于 Node.js 内置 node:sqlite 实现的 SQLite 驱动适配器。
|
|
7
|
+
*/
|
|
8
|
+
export declare class NodeSqliteDriver implements SqliteDriver {
|
|
9
|
+
private readonly db;
|
|
10
|
+
private closed;
|
|
11
|
+
constructor(dbPath?: string | DatabaseSync, options?: any);
|
|
12
|
+
/**
|
|
13
|
+
* 检查底层连接是否处于开启状态。
|
|
14
|
+
*/
|
|
15
|
+
get isOpen(): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 获取底层原始 DatabaseSync 实例。
|
|
18
|
+
*/
|
|
19
|
+
get rawDatabase(): DatabaseSync;
|
|
20
|
+
/**
|
|
21
|
+
* 执行单条或多条无需返回结果的 SQL 脚本语句。
|
|
22
|
+
*/
|
|
23
|
+
exec(sql: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* 预编译 SQL 语句并封装为统一的 SqliteStatement 接口。
|
|
26
|
+
*/
|
|
27
|
+
prepare(sql: string): SqliteStatement;
|
|
28
|
+
/**
|
|
29
|
+
* 执行同步事务,严格拒绝并拦截异步 Promise。
|
|
30
|
+
*/
|
|
31
|
+
transaction<T>(fn: () => T extends PromiseLike<unknown> ? never : T): T;
|
|
32
|
+
/**
|
|
33
|
+
* 关闭底层数据库连接并释放资源。
|
|
34
|
+
*/
|
|
35
|
+
close(): void;
|
|
36
|
+
/**
|
|
37
|
+
* 断言数据库连接未关闭。
|
|
38
|
+
*/
|
|
39
|
+
private assertOpen;
|
|
40
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { normalizeSqliteParams } from "./params.js";
|
|
3
|
+
export { normalizeSqliteParams };
|
|
4
|
+
/**
|
|
5
|
+
* 基于 Node.js 内置 node:sqlite 实现的 SQLite 驱动适配器。
|
|
6
|
+
*/
|
|
7
|
+
export class NodeSqliteDriver {
|
|
8
|
+
db;
|
|
9
|
+
closed = false;
|
|
10
|
+
constructor(dbPath = ":memory:", options) {
|
|
11
|
+
if (typeof dbPath === "string") {
|
|
12
|
+
this.db = options !== undefined ? new DatabaseSync(dbPath, options) : new DatabaseSync(dbPath);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.db = dbPath;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 检查底层连接是否处于开启状态。
|
|
20
|
+
*/
|
|
21
|
+
get isOpen() {
|
|
22
|
+
return !this.closed;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 获取底层原始 DatabaseSync 实例。
|
|
26
|
+
*/
|
|
27
|
+
get rawDatabase() {
|
|
28
|
+
return this.db;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 执行单条或多条无需返回结果的 SQL 脚本语句。
|
|
32
|
+
*/
|
|
33
|
+
exec(sql) {
|
|
34
|
+
this.assertOpen();
|
|
35
|
+
this.db.exec(sql);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 预编译 SQL 语句并封装为统一的 SqliteStatement 接口。
|
|
39
|
+
*/
|
|
40
|
+
prepare(sql) {
|
|
41
|
+
this.assertOpen();
|
|
42
|
+
const stmt = this.db.prepare(sql);
|
|
43
|
+
return {
|
|
44
|
+
run: (...args) => {
|
|
45
|
+
this.assertOpen();
|
|
46
|
+
const params = normalizeSqliteParams(args);
|
|
47
|
+
const res = stmt.run(...params);
|
|
48
|
+
return {
|
|
49
|
+
changes: Number(res.changes),
|
|
50
|
+
lastInsertRowid: res.lastInsertRowid,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
get: (...args) => {
|
|
54
|
+
this.assertOpen();
|
|
55
|
+
const params = normalizeSqliteParams(args);
|
|
56
|
+
return stmt.get(...params);
|
|
57
|
+
},
|
|
58
|
+
all: (...args) => {
|
|
59
|
+
this.assertOpen();
|
|
60
|
+
const params = normalizeSqliteParams(args);
|
|
61
|
+
return stmt.all(...params);
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 执行同步事务,严格拒绝并拦截异步 Promise。
|
|
67
|
+
*/
|
|
68
|
+
transaction(fn) {
|
|
69
|
+
this.assertOpen();
|
|
70
|
+
this.db.exec("BEGIN");
|
|
71
|
+
let result;
|
|
72
|
+
try {
|
|
73
|
+
result = fn();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
try {
|
|
77
|
+
this.db.exec("ROLLBACK");
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// 忽略回滚阶段发生的级联异常
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
// 严格检查是否返回了异步 Promise 对象
|
|
85
|
+
if (result !== null &&
|
|
86
|
+
(typeof result === "object" || typeof result === "function") &&
|
|
87
|
+
typeof result.then === "function") {
|
|
88
|
+
try {
|
|
89
|
+
this.db.exec("ROLLBACK");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// 忽略回滚阶段发生的级联异常
|
|
93
|
+
}
|
|
94
|
+
throw new Error("Async transactions are not allowed in SQLite");
|
|
95
|
+
}
|
|
96
|
+
this.db.exec("COMMIT");
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 关闭底层数据库连接并释放资源。
|
|
101
|
+
*/
|
|
102
|
+
close() {
|
|
103
|
+
if (!this.closed) {
|
|
104
|
+
this.closed = true;
|
|
105
|
+
this.db.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 断言数据库连接未关闭。
|
|
110
|
+
*/
|
|
111
|
+
assertOpen() {
|
|
112
|
+
if (this.closed) {
|
|
113
|
+
throw new Error("Database connection is closed");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
package/dist/storage/sqlite.js
CHANGED
|
@@ -4,6 +4,7 @@ import { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegmen
|
|
|
4
4
|
import { SystemClock } from "./clock.js";
|
|
5
5
|
import { createDefaultSqliteDriver } from "./driver.js";
|
|
6
6
|
import { safeParseStoredJson } from "./utils.js";
|
|
7
|
+
import { ActionDockError, AMBIGUOUS_STATE_KEY, STORED_ERROR_DECODE_FAILED, UNSUPPORTED_STORAGE_SCHEMA } from "../errors.js";
|
|
7
8
|
import { IDEMPOTENCY_RETENTION_MS, STORAGE_SCHEMA_VERSION, isTerminalRunStatus, } from "./types.js";
|
|
8
9
|
export { decodeStateKey, encodeStateKey, escapeStateSegment, unescapeStateSegment, };
|
|
9
10
|
/**
|
|
@@ -76,6 +77,10 @@ export class SqliteRuntimeStorage {
|
|
|
76
77
|
init() {
|
|
77
78
|
this.driver.exec("PRAGMA journal_mode = WAL;");
|
|
78
79
|
this.driver.exec("PRAGMA synchronous = NORMAL;");
|
|
80
|
+
// 跨进程写并发基线:SQLite 默认忙等为零,持有者与旁观写进程的写窗口重叠时
|
|
81
|
+
// 会立即抛 SQLITE_BUSY。统一设置 5 秒忙等,使默认同步驱动、工作线程驱动
|
|
82
|
+
// (worker 脚本已内置同值)与任何注入驱动的行为基线一致。
|
|
83
|
+
this.driver.exec("PRAGMA busy_timeout = 5000;");
|
|
79
84
|
// 读取 Schema 版本号
|
|
80
85
|
const versionRes = this.driver.prepare("PRAGMA user_version;").get();
|
|
81
86
|
const version = Number(versionRes?.user_version ?? 0);
|
|
@@ -142,7 +147,7 @@ export class SqliteRuntimeStorage {
|
|
|
142
147
|
}
|
|
143
148
|
else if (version !== STORAGE_SCHEMA_VERSION) {
|
|
144
149
|
const err = new Error(`UNSUPPORTED_STORAGE_SCHEMA: Database schema version ${version} is incompatible (expected ${STORAGE_SCHEMA_VERSION}). Silent upgrade and database overwriting are strictly prohibited.`);
|
|
145
|
-
err.code =
|
|
150
|
+
err.code = UNSUPPORTED_STORAGE_SCHEMA;
|
|
146
151
|
throw err;
|
|
147
152
|
}
|
|
148
153
|
// 确保已有历史数据库补充迁移 host_session_id 列
|
|
@@ -248,7 +253,11 @@ export class SqliteRuntimeStorage {
|
|
|
248
253
|
if (row.expires_at) {
|
|
249
254
|
const expires = new Date(row.expires_at).getTime();
|
|
250
255
|
if (this.clock.now().getTime() >= expires) {
|
|
251
|
-
|
|
256
|
+
// 惰性删除失败仅降级不影响读取语义,但必须可观测,与其他存储降级点对齐
|
|
257
|
+
this.deleteState(namespace, key).catch((err) => {
|
|
258
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
259
|
+
console.warn(`[actiondock] expired state lazy delete failed (namespace='${namespace}' key='${key}'): ${reason}`);
|
|
260
|
+
});
|
|
252
261
|
return undefined;
|
|
253
262
|
}
|
|
254
263
|
}
|
|
@@ -276,7 +285,13 @@ export class SqliteRuntimeStorage {
|
|
|
276
285
|
const uniqueMap = new Map();
|
|
277
286
|
for (const r of rawRows) {
|
|
278
287
|
if (r.expires_at && now >= new Date(r.expires_at).getTime()) {
|
|
279
|
-
|
|
288
|
+
// 惰性删除失败仅降级不影响匹配语义,但必须可观测,与其他存储降级点对齐
|
|
289
|
+
const ns = r.namespace;
|
|
290
|
+
const k = r.key;
|
|
291
|
+
this.deleteState(ns, k).catch((err) => {
|
|
292
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
293
|
+
console.warn(`[actiondock] expired state lazy delete failed (namespace='${ns}' key='${k}'): ${reason}`);
|
|
294
|
+
});
|
|
280
295
|
continue;
|
|
281
296
|
}
|
|
282
297
|
uniqueMap.set(`${r.namespace}\0${r.key}`, r);
|
|
@@ -302,7 +317,7 @@ export class SqliteRuntimeStorage {
|
|
|
302
317
|
}
|
|
303
318
|
const matchingRows = await this.findMatchingStateRows(targetKey);
|
|
304
319
|
if (matchingRows.length > 1) {
|
|
305
|
-
throw new
|
|
320
|
+
throw new ActionDockError(AMBIGUOUS_STATE_KEY, `Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
|
|
306
321
|
}
|
|
307
322
|
if (matchingRows.length === 0) {
|
|
308
323
|
return undefined;
|
|
@@ -348,7 +363,7 @@ export class SqliteRuntimeStorage {
|
|
|
348
363
|
}
|
|
349
364
|
const matchingRows = await this.findMatchingStateRows(targetKey);
|
|
350
365
|
if (matchingRows.length > 1) {
|
|
351
|
-
throw new
|
|
366
|
+
throw new ActionDockError(AMBIGUOUS_STATE_KEY, `Ambiguous state key '${targetKey}': matches ${matchingRows.length} entries for deletion (${matchingRows.map((r) => `${r.namespace}:${r.key}`).join(", ")})`);
|
|
352
367
|
}
|
|
353
368
|
if (matchingRows.length === 1) {
|
|
354
369
|
return this.deleteState(matchingRows[0].namespace, matchingRows[0].key);
|
|
@@ -644,7 +659,7 @@ export class SqliteRuntimeStorage {
|
|
|
644
659
|
const normalizedError = error !== undefined
|
|
645
660
|
? typeof error === "object" && error !== null && typeof error.message === "string"
|
|
646
661
|
? error
|
|
647
|
-
: { code:
|
|
662
|
+
: { code: STORED_ERROR_DECODE_FAILED, message: String(error) }
|
|
648
663
|
: undefined;
|
|
649
664
|
return {
|
|
650
665
|
id: row.id,
|
package/dist/storage/types.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import type { Clock } from "./clock.js";
|
|
|
5
5
|
*/
|
|
6
6
|
export declare const STORAGE_SCHEMA_VERSION = 2;
|
|
7
7
|
/**
|
|
8
|
-
* 幂等去重记录保留窗口(24 小时),与
|
|
9
|
-
*
|
|
8
|
+
* 幂等去重记录保留窗口(24 小时),与 ActionDockService.info()
|
|
9
|
+
* 中的 idempotencyPolicy.retentionMs 保持单一事实源。
|
|
10
10
|
*/
|
|
11
11
|
export declare const IDEMPOTENCY_RETENTION_MS = 86400000;
|
|
12
12
|
/**
|
|
@@ -34,8 +34,8 @@ export interface SqliteStatement {
|
|
|
34
34
|
* 本契约为同步驱动:exec、prepare 与 transaction 全部以同步语义返回结果,
|
|
35
35
|
* 不接受任何 Promise 或异步实现。SqliteRuntimeStorage 的公共同步方法
|
|
36
36
|
* (getConfig、listConfig、getRun、listRuns、checkAndRecordIdempotency 等)
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* 直接依赖这一同步保证。异步驱动不应注入本契约,
|
|
38
|
+
* 需通过 RuntimeStorage 的异步外观层另行适配。
|
|
39
39
|
*/
|
|
40
40
|
export interface SqliteDriver {
|
|
41
41
|
exec(sql: string): void;
|
package/dist/storage/types.js
CHANGED
|
@@ -4,8 +4,8 @@ import { ACTION_CANCELLED, ACTION_TIMEOUT } from "../errors.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export const STORAGE_SCHEMA_VERSION = 2;
|
|
6
6
|
/**
|
|
7
|
-
* 幂等去重记录保留窗口(24 小时),与
|
|
8
|
-
*
|
|
7
|
+
* 幂等去重记录保留窗口(24 小时),与 ActionDockService.info()
|
|
8
|
+
* 中的 idempotencyPolicy.retentionMs 保持单一事实源。
|
|
9
9
|
*/
|
|
10
10
|
export const IDEMPOTENCY_RETENTION_MS = 86_400_000;
|
|
11
11
|
/**
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,57 @@
|
|
|
1
1
|
export { isLoopbackHost } from "./net.js";
|
|
2
|
+
export { isProcessAlive } from "./process.js";
|
|
3
|
+
/**
|
|
4
|
+
* 语义化版本元数据结构。
|
|
5
|
+
*/
|
|
6
|
+
export interface SemVer {
|
|
7
|
+
major: number;
|
|
8
|
+
minor: number;
|
|
9
|
+
patch: number;
|
|
10
|
+
prerelease?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 解析基础 SemVer 版本字符串(单一事实源)。
|
|
14
|
+
* 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
|
|
15
|
+
* 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseSemVer(v: string): SemVer | null;
|
|
18
|
+
/**
|
|
19
|
+
* 目录遍历条目描述。
|
|
20
|
+
*/
|
|
21
|
+
export interface TraverseDirectoryEntry {
|
|
22
|
+
/** 相对遍历根目录的 POSIX 风格路径 */
|
|
23
|
+
relPath: string;
|
|
24
|
+
/** 条目绝对路径 */
|
|
25
|
+
fullPath: string;
|
|
26
|
+
/** 是否为目录 */
|
|
27
|
+
isDir: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 目录遍历配置选项。
|
|
31
|
+
*/
|
|
32
|
+
export interface TraverseDirectoryOptions {
|
|
33
|
+
/**
|
|
34
|
+
* 条目忽略谓词:返回 true 的相对路径(含目录自身)不进入结果,目录不再下钻。
|
|
35
|
+
* 各调用方的业务忽略规则(如 node_modules、测试文件)以谓词传入保持现有行为。
|
|
36
|
+
*/
|
|
37
|
+
ignore?: (relPath: string, isDir: boolean) => boolean;
|
|
38
|
+
/** 是否在结果中包含目录条目(默认仅返回文件) */
|
|
39
|
+
includeDirs?: boolean;
|
|
40
|
+
/** 目录不可读时的错误回调(默认静默跳过该目录) */
|
|
41
|
+
onReadError?: (dir: string, err: unknown) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 递归遍历目录的单一事实源实现(同步)。
|
|
45
|
+
*
|
|
46
|
+
* 防护策略(以归档与摘要收集链路的历史行为为准):
|
|
47
|
+
* - 软链接越界防护:条目真实路径越出遍历根目录边界时直接跳过;
|
|
48
|
+
* - 循环防护:以真实路径去重,重复访问即跳过(含根目录自身入集合);
|
|
49
|
+
* - 无法 stat 或损坏的条目直接跳过,不中断整体遍历;
|
|
50
|
+
* - 同级条目按名称排序,保证遍历顺序稳定。
|
|
51
|
+
*
|
|
52
|
+
* 根目录自身不作为条目返回;目录条目(includeDirs 开启时)先于其子条目输出。
|
|
53
|
+
*/
|
|
54
|
+
export declare function traverseDirectory(root: string, options?: TraverseDirectoryOptions): TraverseDirectoryEntry[];
|
|
2
55
|
/**
|
|
3
56
|
* 跨运行时安全查找可执行文件绝对物理路径。
|
|
4
57
|
*
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,102 @@
|
|
|
1
|
-
import { existsSync, realpathSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { ActionDockError, INVALID_PACKAGE_ID, PATH_TRAVERSAL } from "../errors.js";
|
|
4
5
|
export { isLoopbackHost } from "./net.js";
|
|
6
|
+
export { isProcessAlive } from "./process.js";
|
|
7
|
+
/**
|
|
8
|
+
* 解析基础 SemVer 版本字符串(单一事实源)。
|
|
9
|
+
* 仅接受可选 v/= 前缀的严格三段式语义化版本,可携带预发布后缀;
|
|
10
|
+
* 其余形态(两段式、非数值等)返回 null,由调用方自行兜底。
|
|
11
|
+
*/
|
|
12
|
+
export function parseSemVer(v) {
|
|
13
|
+
const match = v.trim().replace(/^[v=]/, "").match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/);
|
|
14
|
+
if (!match)
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
major: parseInt(match[1], 10),
|
|
18
|
+
minor: parseInt(match[2], 10),
|
|
19
|
+
patch: parseInt(match[3], 10),
|
|
20
|
+
prerelease: match[4],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 递归遍历目录的单一事实源实现(同步)。
|
|
25
|
+
*
|
|
26
|
+
* 防护策略(以归档与摘要收集链路的历史行为为准):
|
|
27
|
+
* - 软链接越界防护:条目真实路径越出遍历根目录边界时直接跳过;
|
|
28
|
+
* - 循环防护:以真实路径去重,重复访问即跳过(含根目录自身入集合);
|
|
29
|
+
* - 无法 stat 或损坏的条目直接跳过,不中断整体遍历;
|
|
30
|
+
* - 同级条目按名称排序,保证遍历顺序稳定。
|
|
31
|
+
*
|
|
32
|
+
* 根目录自身不作为条目返回;目录条目(includeDirs 开启时)先于其子条目输出。
|
|
33
|
+
*/
|
|
34
|
+
export function traverseDirectory(root, options = {}) {
|
|
35
|
+
const results = [];
|
|
36
|
+
const visited = new Set();
|
|
37
|
+
const resolvedRoot = resolve(root);
|
|
38
|
+
try {
|
|
39
|
+
visited.add(existsSync(resolvedRoot) ? realpathSync(resolvedRoot) : resolvedRoot);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// 根目录真实路径解析异常时退化为不携带根真实路径的循环拦截
|
|
43
|
+
}
|
|
44
|
+
const walk = (dir) => {
|
|
45
|
+
let names;
|
|
46
|
+
try {
|
|
47
|
+
names = readdirSync(dir).sort();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
options.onReadError?.(dir, err);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
for (const name of names) {
|
|
54
|
+
const fullPath = join(dir, name);
|
|
55
|
+
const relPath = relative(resolvedRoot, fullPath).split(sep).join("/");
|
|
56
|
+
let isDir;
|
|
57
|
+
try {
|
|
58
|
+
isDir = statSync(fullPath).isDirectory();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// 无法访问或损坏的文件/符号链接直接跳过
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (options.ignore?.(relPath, isDir)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// 软链接越界防护:条目真实路径越出根目录边界时直接跳过
|
|
68
|
+
let real;
|
|
69
|
+
try {
|
|
70
|
+
real = existsSync(fullPath) ? realpathSync(fullPath) : fullPath;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
assertPathWithinRoot(resolvedRoot, real, "traverse path");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
// 循环防护:真实路径重复出现即跳过
|
|
82
|
+
if (visited.has(real)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
visited.add(real);
|
|
86
|
+
if (isDir) {
|
|
87
|
+
if (options.includeDirs) {
|
|
88
|
+
results.push({ relPath, fullPath, isDir: true });
|
|
89
|
+
}
|
|
90
|
+
walk(fullPath);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
results.push({ relPath, fullPath, isDir: false });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
walk(resolvedRoot);
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
5
100
|
/**
|
|
6
101
|
* 跨运行时安全查找可执行文件绝对物理路径。
|
|
7
102
|
*
|
|
@@ -105,7 +200,7 @@ export function assertValidPackageId(packageId) {
|
|
|
105
200
|
typeof packageId !== "string" ||
|
|
106
201
|
packageId.includes("..") ||
|
|
107
202
|
(!PACKAGE_ID_REGEX.test(packageId) && !isScopedLegacy)) {
|
|
108
|
-
throw new
|
|
203
|
+
throw new ActionDockError(INVALID_PACKAGE_ID, `Invalid packageId '${packageId}': must match ${PACKAGE_ID_REGEX} and not contain path traversal characters`);
|
|
109
204
|
}
|
|
110
205
|
}
|
|
111
206
|
/**
|
|
@@ -161,13 +256,13 @@ export function assertPathWithinRoot(rootDir, targetPath, fieldName = "path") {
|
|
|
161
256
|
const resolvedTarget = resolve(rootDir, targetPath);
|
|
162
257
|
const rel = relative(resolvedRoot, resolvedTarget);
|
|
163
258
|
if (isPathOutsideBoundary(rel)) {
|
|
164
|
-
throw new
|
|
259
|
+
throw new ActionDockError(PATH_TRAVERSAL, `'${fieldName}' escapes boundary '${rootDir}': ${targetPath}`);
|
|
165
260
|
}
|
|
166
261
|
const canonicalRoot = canonicalizePath(resolvedRoot);
|
|
167
262
|
const canonicalTarget = canonicalizePath(resolvedTarget);
|
|
168
263
|
const relReal = relative(canonicalRoot, canonicalTarget);
|
|
169
264
|
if (isPathOutsideBoundary(relReal)) {
|
|
170
|
-
throw new
|
|
265
|
+
throw new ActionDockError(PATH_TRAVERSAL, `'${fieldName}' symlink resolves outside boundary '${rootDir}': ${targetPath}`);
|
|
171
266
|
}
|
|
172
267
|
}
|
|
173
268
|
/**
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查目标进程是否处于存活状态(跨域通用谓词单一事实源)。
|
|
3
|
+
*
|
|
4
|
+
* 基于 process.kill(pid, 0) 探测:无异常视为存活;ESRCH(进程不存在)
|
|
5
|
+
* 视为已死;其他异常(如 EPERM,权限不足但进程存在)保守视为存活,
|
|
6
|
+
* 避免权限受限环境下误判他人持有进程死亡而抢占其锁。
|
|
7
|
+
*
|
|
8
|
+
* @param pid 待检测的进程标识符
|
|
9
|
+
*/
|
|
10
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 检查目标进程是否处于存活状态(跨域通用谓词单一事实源)。
|
|
3
|
+
*
|
|
4
|
+
* 基于 process.kill(pid, 0) 探测:无异常视为存活;ESRCH(进程不存在)
|
|
5
|
+
* 视为已死;其他异常(如 EPERM,权限不足但进程存在)保守视为存活,
|
|
6
|
+
* 避免权限受限环境下误判他人持有进程死亡而抢占其锁。
|
|
7
|
+
*
|
|
8
|
+
* @param pid 待检测的进程标识符
|
|
9
|
+
*/
|
|
10
|
+
export function isProcessAlive(pid) {
|
|
11
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
process.kill(pid, 0);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return err?.code !== "ESRCH";
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actiondock/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,8 +9,45 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
|
+
"bun": "./src/index.ts",
|
|
12
13
|
"import": "./dist/index.js",
|
|
13
14
|
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./server": {
|
|
17
|
+
"types": "./dist/server/index.d.ts",
|
|
18
|
+
"bun": "./src/server/index.ts",
|
|
19
|
+
"import": "./dist/server/index.js",
|
|
20
|
+
"default": "./dist/server/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./project": {
|
|
23
|
+
"types": "./dist/project/index.d.ts",
|
|
24
|
+
"bun": "./src/project/index.ts",
|
|
25
|
+
"import": "./dist/project/index.js",
|
|
26
|
+
"default": "./dist/project/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./registry": {
|
|
29
|
+
"types": "./dist/registry/index.d.ts",
|
|
30
|
+
"bun": "./src/registry/index.ts",
|
|
31
|
+
"import": "./dist/registry/index.js",
|
|
32
|
+
"default": "./dist/registry/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./profile": {
|
|
35
|
+
"types": "./dist/profile/index.d.ts",
|
|
36
|
+
"bun": "./src/profile/index.ts",
|
|
37
|
+
"import": "./dist/profile/index.js",
|
|
38
|
+
"default": "./dist/profile/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./graph": {
|
|
41
|
+
"types": "./dist/graph/index.d.ts",
|
|
42
|
+
"bun": "./src/graph/index.ts",
|
|
43
|
+
"import": "./dist/graph/index.js",
|
|
44
|
+
"default": "./dist/graph/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./package": {
|
|
47
|
+
"types": "./dist/package/index.d.ts",
|
|
48
|
+
"bun": "./src/package/index.ts",
|
|
49
|
+
"import": "./dist/package/index.js",
|
|
50
|
+
"default": "./dist/package/index.js"
|
|
14
51
|
}
|
|
15
52
|
},
|
|
16
53
|
"files": [
|
|
@@ -29,9 +66,10 @@
|
|
|
29
66
|
"test": "node ../../scripts/run-tests.ts"
|
|
30
67
|
},
|
|
31
68
|
"dependencies": {
|
|
32
|
-
"@actiondock/sdk": "^2.
|
|
69
|
+
"@actiondock/sdk": "^2.8.0",
|
|
33
70
|
"ajv": "^8.17.1",
|
|
34
|
-
"ajv-formats": "^3.0.1"
|
|
71
|
+
"ajv-formats": "^3.0.1",
|
|
72
|
+
"undici": "^7.29.1"
|
|
35
73
|
},
|
|
36
74
|
"devDependencies": {
|
|
37
75
|
"typescript": "^5.7.0"
|
package/dist/app/index.d.ts
DELETED
package/dist/app/index.js
DELETED
package/dist/app/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CatalogSnapshot, IndexedAction } from "./types.js";
|
|
2
|
-
export declare class ActionIndex {
|
|
3
|
-
private actions;
|
|
4
|
-
constructor(snapshot: CatalogSnapshot);
|
|
5
|
-
private indexSnapshot;
|
|
6
|
-
private add;
|
|
7
|
-
list(packageId?: string): IndexedAction[];
|
|
8
|
-
find(actionId: string, packageId?: string): IndexedAction[];
|
|
9
|
-
}
|