@actiondock/core 2.5.1 → 2.7.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/dist/app/app.d.ts +14 -1
- package/dist/app/app.js +88 -222
- package/dist/app/static-index.d.ts +39 -0
- package/dist/app/static-index.js +164 -0
- package/dist/app/types.d.ts +5 -0
- package/dist/catalog/action-index.js +6 -4
- package/dist/catalog/package-catalog.js +2 -1
- package/dist/doctor/checks/index.d.ts +5 -0
- package/dist/doctor/checks/index.js +5 -0
- package/dist/doctor/checks/project-locate.d.ts +6 -0
- package/dist/doctor/checks/project-locate.js +36 -0
- package/dist/doctor/checks/project.d.ts +6 -0
- package/dist/doctor/checks/project.js +383 -0
- package/dist/doctor/checks/registry.d.ts +13 -0
- package/dist/doctor/checks/registry.js +207 -0
- package/dist/doctor/checks/runtime.d.ts +13 -0
- package/dist/doctor/checks/runtime.js +90 -0
- package/dist/doctor/checks/storage.d.ts +5 -0
- package/dist/doctor/checks/storage.js +34 -0
- package/dist/doctor/context.d.ts +35 -0
- package/dist/doctor/context.js +11 -0
- package/dist/doctor/doctor.d.ts +5 -0
- package/dist/doctor/doctor.js +33 -602
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +22 -0
- package/dist/execution/service.js +14 -9
- package/dist/export/templates.js +42 -30
- package/dist/host/host.d.ts +33 -0
- package/dist/host/host.js +222 -362
- package/dist/host/routing.d.ts +92 -0
- package/dist/host/routing.js +238 -0
- package/dist/host/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/input/advice.d.ts +100 -0
- package/dist/input/advice.js +767 -0
- package/dist/input/describe.d.ts +106 -0
- package/dist/input/describe.js +176 -0
- package/dist/input/file-input.d.ts +54 -0
- package/dist/input/file-input.js +124 -0
- package/dist/input/flat-decode.d.ts +11 -0
- package/dist/input/flat-decode.js +13 -0
- package/dist/input/flat-errors.d.ts +72 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +179 -0
- package/dist/input/flat-parser.d.ts +50 -0
- package/dist/input/flat-parser.js +169 -0
- package/dist/input/flat-predicates.d.ts +34 -0
- package/dist/input/flat-predicates.js +50 -0
- package/dist/input/index.d.ts +14 -0
- package/dist/input/index.js +14 -0
- package/dist/input/input-resolver.d.ts +63 -0
- package/dist/input/input-resolver.js +254 -0
- package/dist/input/json-depth-scanner.d.ts +17 -0
- package/dist/input/json-depth-scanner.js +49 -0
- package/dist/input/metadata.d.ts +99 -0
- package/dist/input/metadata.js +99 -0
- package/dist/input/stdin-input.d.ts +26 -0
- package/dist/input/stdin-input.js +125 -0
- package/dist/input/utf8.d.ts +19 -0
- package/dist/input/utf8.js +36 -0
- package/dist/input/validation-mapper.d.ts +27 -0
- package/dist/input/validation-mapper.js +144 -0
- package/dist/ipc/host.js +38 -0
- package/dist/json/index.d.ts +1 -0
- package/dist/json/index.js +1 -0
- package/dist/json/value-validator.d.ts +88 -0
- package/dist/json/value-validator.js +330 -0
- package/dist/platform/types.d.ts +7 -0
- package/dist/process/control-arbiter.d.ts +107 -0
- package/dist/process/control-arbiter.js +251 -0
- package/dist/process/diagnostics-sink.d.ts +26 -0
- package/dist/process/diagnostics-sink.js +35 -0
- package/dist/process/index.d.ts +7 -0
- package/dist/process/index.js +7 -0
- package/dist/process/input-dispatcher.d.ts +88 -0
- package/dist/process/input-dispatcher.js +240 -0
- package/dist/process/managed-record.d.ts +78 -0
- package/dist/process/managed-record.js +1 -0
- package/dist/process/process-manager.d.ts +15 -56
- package/dist/process/process-manager.js +119 -707
- package/dist/process/reservation-table.d.ts +39 -0
- package/dist/process/reservation-table.js +80 -0
- package/dist/process/run-executor.d.ts +16 -0
- package/dist/process/run-executor.js +155 -0
- package/dist/process/terminal-output-cache.d.ts +66 -0
- package/dist/process/terminal-output-cache.js +132 -0
- package/dist/profile/client-actions.d.ts +56 -0
- package/dist/profile/client-actions.js +215 -0
- package/dist/profile/client-config.d.ts +20 -0
- package/dist/profile/client-config.js +51 -0
- package/dist/profile/client-health.d.ts +14 -0
- package/dist/profile/client-health.js +69 -0
- package/dist/profile/client-playbooks.d.ts +19 -0
- package/dist/profile/client-playbooks.js +36 -0
- package/dist/profile/client-query.d.ts +30 -0
- package/dist/profile/client-query.js +70 -0
- package/dist/profile/client-runs.d.ts +42 -0
- package/dist/profile/client-runs.js +58 -0
- package/dist/profile/client-state.d.ts +56 -0
- package/dist/profile/client-state.js +83 -0
- package/dist/profile/client-transport.d.ts +61 -0
- package/dist/profile/client-transport.js +156 -0
- package/dist/profile/client.d.ts +23 -142
- package/dist/profile/client.js +28 -588
- package/dist/profile/manager.js +1 -1
- package/dist/project/closure.d.ts +4 -4
- package/dist/project/closure.js +11 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +0 -25
- package/dist/project/loader.js +1 -154
- package/dist/project/transactions.d.ts +12 -14
- package/dist/project/transactions.js +60 -836
- package/dist/registry/registry.js +6 -4
- package/dist/registry/resolve.js +1 -1
- package/dist/runtime/action-registry.d.ts +45 -0
- package/dist/runtime/action-registry.js +108 -0
- package/dist/runtime/clock.d.ts +5 -17
- package/dist/runtime/clock.js +5 -15
- package/dist/runtime/context.d.ts +3 -0
- package/dist/runtime/context.js +17 -56
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/package-runner-factory.d.ts +82 -0
- package/dist/runtime/package-runner-factory.js +186 -0
- package/dist/runtime/run-persistence.d.ts +85 -0
- package/dist/runtime/run-persistence.js +111 -0
- package/dist/runtime/runner.d.ts +25 -45
- package/dist/runtime/runner.js +130 -354
- package/dist/runtime/standalone.d.ts +25 -2
- package/dist/runtime/standalone.js +305 -87
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.js +48 -54
- package/dist/server/dispatcher.d.ts +8 -3
- package/dist/server/dispatcher.js +16 -40
- package/dist/server/routes/runs.js +6 -4
- package/dist/server/security.d.ts +1 -7
- package/dist/server/security.js +2 -13
- package/dist/storage/clock.d.ts +25 -0
- package/dist/storage/clock.js +23 -0
- package/dist/storage/data-dir-lock.d.ts +3 -36
- package/dist/storage/data-dir-lock.js +48 -881
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.js +8 -2
- package/dist/storage/lazy.js +1 -0
- package/dist/storage/lock-core.d.ts +162 -0
- package/dist/storage/lock-core.js +885 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +81 -89
- package/dist/storage/types.d.ts +27 -5
- package/dist/storage/types.js +21 -0
- package/dist/storage/utils.d.ts +22 -0
- package/dist/storage/utils.js +34 -0
- package/dist/target/local.d.ts +1 -0
- package/dist/target/local.js +23 -17
- package/dist/target/remote-errors.d.ts +21 -0
- package/dist/target/remote-errors.js +51 -0
- package/dist/target/remote-polling.d.ts +35 -0
- package/dist/target/remote-polling.js +104 -0
- package/dist/target/remote.d.ts +4 -0
- package/dist/target/remote.js +108 -301
- package/dist/target/sse-parser.d.ts +35 -0
- package/dist/target/sse-parser.js +163 -0
- package/dist/target/target.js +2 -0
- package/dist/target/types.d.ts +6 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/net.d.ts +10 -0
- package/dist/utils/net.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { isForbiddenActionInputPropertyName } from "../input/flat-predicates.js";
|
|
2
|
+
/** 默认 JSON 最大嵌套深度限制(防止恶意超深结构) */
|
|
3
|
+
export const DEFAULT_MAX_JSON_DEPTH = 256;
|
|
4
|
+
/**
|
|
5
|
+
* 按照 RFC 6901 转义单个 JSON Pointer 路径段。
|
|
6
|
+
*
|
|
7
|
+
* @param segment 路径段(属性名或数组索引)
|
|
8
|
+
* @returns 转义后的路径段
|
|
9
|
+
*/
|
|
10
|
+
export function escapeJsonPointerSegment(segment) {
|
|
11
|
+
return String(segment).replace(/~/g, "~0").replace(/\//g, "~1");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 将路径段追加到基础 RFC 6901 JSON Pointer 路径后。
|
|
15
|
+
*
|
|
16
|
+
* @param basePath 基础路径
|
|
17
|
+
* @param segment 待追加的路径段
|
|
18
|
+
* @returns 拼接后的 JSON Pointer
|
|
19
|
+
*/
|
|
20
|
+
export function appendJsonPointer(basePath, segment) {
|
|
21
|
+
return `${basePath}/${escapeJsonPointerSegment(segment)}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 内部统一 JSON 值与输入策略遍历校验引擎。
|
|
25
|
+
*
|
|
26
|
+
* 遍历规范:
|
|
27
|
+
* - 迭代式显式栈遍历:模拟调用栈,杜绝深层结构造成的堆栈溢出。
|
|
28
|
+
* - 严格数值有限性:全链路检查所有数值满足 Number.isFinite,拦截 NaN 与 Infinity。
|
|
29
|
+
* - 活跃祖先集合环路检测:仅在当前子树遍历期间在祖先集合中保留,完美放行有向无环(DAG)共享子结构。
|
|
30
|
+
* - 严格对象与原型校验:仅允许 Object.prototype 与 null 原型,拒绝 Date、Map、Set 等非纯数据对象与类实例。
|
|
31
|
+
* - 严格数组紧凑性:校验 0..length-1 全部存在,拒绝稀疏数组、额外属性与访问器元素。
|
|
32
|
+
* - Proxy 异常防御:对所有反射与属性检查执行防护拦截,杜绝未分类异常泄漏。
|
|
33
|
+
* - RFC 6901 路径追踪:全链路维护转义标准 JSON Pointer 路径。
|
|
34
|
+
*/
|
|
35
|
+
function walkJsonValue(value, options, checkForbiddenProperties = false) {
|
|
36
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_JSON_DEPTH;
|
|
37
|
+
const ancestors = new Set();
|
|
38
|
+
const stack = [{ type: "ENTER", value, depth: 0, path: "" }];
|
|
39
|
+
while (stack.length > 0) {
|
|
40
|
+
const frame = stack.pop();
|
|
41
|
+
if (frame.type === "EXIT") {
|
|
42
|
+
ancestors.delete(frame.target);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const { value: val, depth, path } = frame;
|
|
46
|
+
if (val === null || typeof val === "boolean" || typeof val === "string") {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (typeof val === "number") {
|
|
50
|
+
if (!Number.isFinite(val) || Number.isNaN(val)) {
|
|
51
|
+
return {
|
|
52
|
+
valid: false,
|
|
53
|
+
kind: "json-value",
|
|
54
|
+
code: "NON_FINITE_NUMBER",
|
|
55
|
+
reason: `Number is non-finite or NaN (${val})`,
|
|
56
|
+
path,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (typeof val === "undefined") {
|
|
62
|
+
return {
|
|
63
|
+
valid: false,
|
|
64
|
+
kind: "json-value",
|
|
65
|
+
code: "UNDEFINED_JSON_VALUE",
|
|
66
|
+
reason: "Undefined value is not allowed in JSON",
|
|
67
|
+
path,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (typeof val === "function" ||
|
|
71
|
+
typeof val === "symbol" ||
|
|
72
|
+
typeof val === "bigint") {
|
|
73
|
+
return {
|
|
74
|
+
valid: false,
|
|
75
|
+
kind: "json-value",
|
|
76
|
+
code: "UNSUPPORTED_JSON_TYPE",
|
|
77
|
+
reason: `Unsupported JSON type '${typeof val}'`,
|
|
78
|
+
path,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (typeof val === "object") {
|
|
82
|
+
try {
|
|
83
|
+
if (ancestors.has(val)) {
|
|
84
|
+
return {
|
|
85
|
+
valid: false,
|
|
86
|
+
kind: "json-value",
|
|
87
|
+
code: "CIRCULAR_REFERENCE",
|
|
88
|
+
reason: "Circular reference detected in object structure",
|
|
89
|
+
path,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (depth > maxDepth) {
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
kind: "json-value",
|
|
96
|
+
code: "MAX_JSON_DEPTH",
|
|
97
|
+
reason: `Max JSON depth limit (${maxDepth}) exceeded`,
|
|
98
|
+
path,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const proto = Object.getPrototypeOf(val);
|
|
102
|
+
const isArr = Array.isArray(val);
|
|
103
|
+
if (isArr) {
|
|
104
|
+
if (proto !== Array.prototype) {
|
|
105
|
+
return {
|
|
106
|
+
valid: false,
|
|
107
|
+
kind: "json-value",
|
|
108
|
+
code: "INVALID_JSON_OBJECT",
|
|
109
|
+
reason: "Array prototype must be Array.prototype",
|
|
110
|
+
path,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const len = val.length;
|
|
114
|
+
if (typeof len !== "number" ||
|
|
115
|
+
!Number.isInteger(len) ||
|
|
116
|
+
len < 0 ||
|
|
117
|
+
len > Number.MAX_SAFE_INTEGER) {
|
|
118
|
+
return {
|
|
119
|
+
valid: false,
|
|
120
|
+
kind: "json-value",
|
|
121
|
+
code: "INVALID_JSON_OBJECT",
|
|
122
|
+
reason: "Array length is invalid",
|
|
123
|
+
path,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const ownKeys = Reflect.ownKeys(val);
|
|
127
|
+
if (ownKeys.length !== len + 1) {
|
|
128
|
+
return {
|
|
129
|
+
valid: false,
|
|
130
|
+
kind: "json-value",
|
|
131
|
+
code: "INVALID_JSON_OBJECT",
|
|
132
|
+
reason: "Array must be dense without extra properties or holes",
|
|
133
|
+
path,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
ancestors.add(val);
|
|
137
|
+
stack.push({ type: "EXIT", target: val });
|
|
138
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
139
|
+
const keyStr = String(i);
|
|
140
|
+
const desc = Object.getOwnPropertyDescriptor(val, keyStr);
|
|
141
|
+
const elemPath = appendJsonPointer(path, i);
|
|
142
|
+
if (!desc) {
|
|
143
|
+
return {
|
|
144
|
+
valid: false,
|
|
145
|
+
kind: "json-value",
|
|
146
|
+
code: "INVALID_JSON_OBJECT",
|
|
147
|
+
reason: `Sparse array (missing element at index ${i})`,
|
|
148
|
+
path: elemPath,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (desc.get !== undefined || desc.set !== undefined) {
|
|
152
|
+
return {
|
|
153
|
+
valid: false,
|
|
154
|
+
kind: "json-value",
|
|
155
|
+
code: "INVALID_JSON_OBJECT",
|
|
156
|
+
reason: `Accessor property not allowed at array index ${i}`,
|
|
157
|
+
path: elemPath,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (!desc.enumerable) {
|
|
161
|
+
return {
|
|
162
|
+
valid: false,
|
|
163
|
+
kind: "json-value",
|
|
164
|
+
code: "INVALID_JSON_OBJECT",
|
|
165
|
+
reason: `Non-enumerable property not allowed at array index ${i}`,
|
|
166
|
+
path: elemPath,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (desc.value === undefined) {
|
|
170
|
+
return {
|
|
171
|
+
valid: false,
|
|
172
|
+
kind: "json-value",
|
|
173
|
+
code: "UNDEFINED_JSON_VALUE",
|
|
174
|
+
reason: `Undefined value is not allowed at array index ${i}`,
|
|
175
|
+
path: elemPath,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
stack.push({
|
|
179
|
+
type: "ENTER",
|
|
180
|
+
value: desc.value,
|
|
181
|
+
depth: depth + 1,
|
|
182
|
+
path: elemPath,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
188
|
+
return {
|
|
189
|
+
valid: false,
|
|
190
|
+
kind: "json-value",
|
|
191
|
+
code: "INVALID_JSON_OBJECT",
|
|
192
|
+
reason: "Object prototype must be Object.prototype or null",
|
|
193
|
+
path,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const ownKeys = Reflect.ownKeys(val);
|
|
197
|
+
ancestors.add(val);
|
|
198
|
+
stack.push({ type: "EXIT", target: val });
|
|
199
|
+
for (let i = ownKeys.length - 1; i >= 0; i--) {
|
|
200
|
+
const key = ownKeys[i];
|
|
201
|
+
if (typeof key !== "string") {
|
|
202
|
+
return {
|
|
203
|
+
valid: false,
|
|
204
|
+
kind: "json-value",
|
|
205
|
+
code: "INVALID_JSON_OBJECT",
|
|
206
|
+
reason: "Symbol-keyed properties are not allowed in JSON object",
|
|
207
|
+
path: appendJsonPointer(path, String(key)),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const propPath = appendJsonPointer(path, key);
|
|
211
|
+
if (checkForbiddenProperties &&
|
|
212
|
+
isForbiddenActionInputPropertyName(key)) {
|
|
213
|
+
return {
|
|
214
|
+
valid: false,
|
|
215
|
+
kind: "input-policy",
|
|
216
|
+
code: "FORBIDDEN_PROPERTY",
|
|
217
|
+
property: key,
|
|
218
|
+
path: propPath,
|
|
219
|
+
reason: `Forbidden property "${key}" is not allowed in Action input`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const desc = Object.getOwnPropertyDescriptor(val, key);
|
|
223
|
+
if (!desc) {
|
|
224
|
+
return {
|
|
225
|
+
valid: false,
|
|
226
|
+
kind: "json-value",
|
|
227
|
+
code: "INVALID_JSON_OBJECT",
|
|
228
|
+
reason: `Property descriptor missing for key "${key}"`,
|
|
229
|
+
path: propPath,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (desc.get !== undefined || desc.set !== undefined) {
|
|
233
|
+
return {
|
|
234
|
+
valid: false,
|
|
235
|
+
kind: "json-value",
|
|
236
|
+
code: "INVALID_JSON_OBJECT",
|
|
237
|
+
reason: `Accessor properties (getters/setters) are not allowed for key "${key}"`,
|
|
238
|
+
path: propPath,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (!desc.enumerable) {
|
|
242
|
+
return {
|
|
243
|
+
valid: false,
|
|
244
|
+
kind: "json-value",
|
|
245
|
+
code: "INVALID_JSON_OBJECT",
|
|
246
|
+
reason: `Non-enumerable properties are not allowed for key "${key}"`,
|
|
247
|
+
path: propPath,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
if (desc.value === undefined) {
|
|
251
|
+
return {
|
|
252
|
+
valid: false,
|
|
253
|
+
kind: "json-value",
|
|
254
|
+
code: "UNDEFINED_JSON_VALUE",
|
|
255
|
+
reason: `Undefined value is not allowed for key "${key}"`,
|
|
256
|
+
path: propPath,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
stack.push({
|
|
260
|
+
type: "ENTER",
|
|
261
|
+
value: desc.value,
|
|
262
|
+
depth: depth + 1,
|
|
263
|
+
path: propPath,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
return {
|
|
270
|
+
valid: false,
|
|
271
|
+
kind: "json-value",
|
|
272
|
+
code: "INVALID_JSON_OBJECT",
|
|
273
|
+
reason: `Failed to inspect object: ${err instanceof Error ? err.message : String(err)}`,
|
|
274
|
+
path,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return { valid: true };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* 校验值是否为合法的 Canonical JSON 兼容结构。
|
|
283
|
+
*
|
|
284
|
+
* @param value 待校验的目标值
|
|
285
|
+
* @param options 校验配置选项
|
|
286
|
+
* @returns 校验结果对象
|
|
287
|
+
*/
|
|
288
|
+
export function validateJsonValue(value, options) {
|
|
289
|
+
const res = walkJsonValue(value, options, false);
|
|
290
|
+
if (res.valid) {
|
|
291
|
+
return { valid: true };
|
|
292
|
+
}
|
|
293
|
+
if (res.kind === "input-policy") {
|
|
294
|
+
return {
|
|
295
|
+
valid: false,
|
|
296
|
+
code: "INVALID_JSON_OBJECT",
|
|
297
|
+
reason: res.reason,
|
|
298
|
+
path: res.path,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
valid: false,
|
|
303
|
+
code: res.code,
|
|
304
|
+
reason: res.reason,
|
|
305
|
+
path: res.path,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* 校验 Action 输入值是否满足 Canonical JsonValue 规范且符合 ActionDock 安全策略。
|
|
310
|
+
* 在遍历过程中递归检查是否存在禁止属性(__proto__、constructor、prototype)。
|
|
311
|
+
*
|
|
312
|
+
* @param value 待校验的 Action 输入值
|
|
313
|
+
* @param options 校验配置选项
|
|
314
|
+
* @returns 校验结果对象
|
|
315
|
+
*/
|
|
316
|
+
export function validateActionInputValue(value, options) {
|
|
317
|
+
return walkJsonValue(value, options, true);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 断言值必须为合法的 Canonical JSON 兼容结构,若非法则抛出 TypeError。
|
|
321
|
+
*
|
|
322
|
+
* @param value 待断言的目标值
|
|
323
|
+
* @param options 校验配置选项
|
|
324
|
+
*/
|
|
325
|
+
export function assertJsonValue(value, options) {
|
|
326
|
+
const result = validateJsonValue(value, options);
|
|
327
|
+
if (!result.valid) {
|
|
328
|
+
throw new TypeError(`Invalid JSON value: ${result.reason}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
package/dist/platform/types.d.ts
CHANGED
|
@@ -53,6 +53,11 @@ export interface StorageFactoryOptions {
|
|
|
53
53
|
dataDir?: string;
|
|
54
54
|
inMemory?: boolean;
|
|
55
55
|
customHome?: string;
|
|
56
|
+
/**
|
|
57
|
+
* 是否以数据目录持有者身份打开:true 时构造阶段收割死亡会话遗留的非终态运行记录。
|
|
58
|
+
* 默认 false(旁观查询打开,不触碰在途记录),供 CLI 查询命令与执行宿主并发共存。
|
|
59
|
+
*/
|
|
60
|
+
recoverOrphans?: boolean;
|
|
56
61
|
}
|
|
57
62
|
/**
|
|
58
63
|
* 跨运行时全局存储工厂配置选项。
|
|
@@ -61,6 +66,8 @@ export interface GlobalStorageFactoryOptions {
|
|
|
61
66
|
dataDir?: string;
|
|
62
67
|
inMemory?: boolean;
|
|
63
68
|
customHome?: string;
|
|
69
|
+
/** 是否以持有者身份打开并收割遗留非终态运行记录(默认 false,旁观语义) */
|
|
70
|
+
recoverOrphans?: boolean;
|
|
64
71
|
}
|
|
65
72
|
/**
|
|
66
73
|
* 跨运行时持久化存储工厂契约。
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { ControlGrant } from "@actiondock/sdk";
|
|
2
|
+
import type { ManagedProcessRecord, ProcessOwner } from "./managed-record.js";
|
|
3
|
+
import type { StoredProcessRecord } from "./metadata-store.js";
|
|
4
|
+
/**
|
|
5
|
+
* 控制权仲裁器宿主协作回调集合。
|
|
6
|
+
*
|
|
7
|
+
* 持有者始终为 ProcessManager:仲裁器仅经回调透传副作用,不反向依赖管理器。
|
|
8
|
+
*/
|
|
9
|
+
export interface ControlArbiterHost {
|
|
10
|
+
/** 控制权状态落盘(异步尽力而为) */
|
|
11
|
+
persistState(processId: string, patch: Partial<StoredProcessRecord>): unknown;
|
|
12
|
+
/** 排队路径授予成功时的凭据落盘(异步尽力而为) */
|
|
13
|
+
persistGrantReceipt(grant: ControlGrant, wait: {
|
|
14
|
+
requestId: string;
|
|
15
|
+
waitMs: number;
|
|
16
|
+
ttlMs: number;
|
|
17
|
+
}, proc: ManagedProcessRecord): unknown;
|
|
18
|
+
/** 控制权事件后的空闲定时器刷新(授予、续租与成功输入均触发) */
|
|
19
|
+
refreshIdleTimer(proc: ManagedProcessRecord): void;
|
|
20
|
+
/** 到期复核通过后的隔离处置(隔离而非释放) */
|
|
21
|
+
quarantineProcess(owner: ProcessOwner, processId: string, token?: string, reason?: string): unknown;
|
|
22
|
+
/** 宿主级等待者配额统计(含全部进程的排队等待者) */
|
|
23
|
+
countHostAcquireWaiters(): number;
|
|
24
|
+
/** 宿主等待者配额上限 */
|
|
25
|
+
readonly maxWaitersPerProcess: number;
|
|
26
|
+
/** 单进程等待者配额上限 */
|
|
27
|
+
readonly maxWaitersPerHost: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 控制权仲裁器。
|
|
31
|
+
*
|
|
32
|
+
* 单一职责:独占控制权状态机的全部推进路径——
|
|
33
|
+
* - 直接授予与 FIFO 等待队列(唯一唤醒)
|
|
34
|
+
* - TTL 定时器生命周期(设置、续租换新、清理)
|
|
35
|
+
* - 到期复核(复合控制态与凭据双重校验,转隔离而非释放)
|
|
36
|
+
* - 操作授权校验(write/control 提交前与调度复核共用)
|
|
37
|
+
* - 终态等待者单一拒绝入口
|
|
38
|
+
*/
|
|
39
|
+
export declare class ControlArbiter {
|
|
40
|
+
private readonly host;
|
|
41
|
+
constructor(host: ControlArbiterHost);
|
|
42
|
+
/**
|
|
43
|
+
* 尝试立即授予控制权:控制权空闲且无等待者时直接授予,
|
|
44
|
+
* 否则按 FIFO 进入等待队列并返回等待 Promise。
|
|
45
|
+
*
|
|
46
|
+
* 调用方(原 acquire 请求)在排队路径 resolve 后负责预约结算;
|
|
47
|
+
* 唤醒方负责凭据落盘,保持两阶段时序不变。
|
|
48
|
+
*/
|
|
49
|
+
tryAcquire(proc: ManagedProcessRecord, input: {
|
|
50
|
+
requestId: string;
|
|
51
|
+
waitMs: number;
|
|
52
|
+
ttlMs: number;
|
|
53
|
+
}, call?: {
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
}, runId?: string): {
|
|
56
|
+
granted: boolean;
|
|
57
|
+
waiter?: Promise<ControlGrant>;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* FIFO 入队单个等待者:注册超时定时器、中止监听与自清理钩子。
|
|
61
|
+
*/
|
|
62
|
+
private enqueueWaiter;
|
|
63
|
+
/**
|
|
64
|
+
* 授予指定受管进程控制令牌:递增控制纪元、登记凭据并设置 TTL 定时器。
|
|
65
|
+
*/
|
|
66
|
+
grantControl(proc: ManagedProcessRecord, requestId: string, ttlMs: number, runId?: string): ControlGrant;
|
|
67
|
+
/**
|
|
68
|
+
* 续租当前有效控制令牌:先清理旧 TTL 定时器再设置新定时器,
|
|
69
|
+
* 刷新到期时刻并联动空闲定时器。
|
|
70
|
+
*/
|
|
71
|
+
renewGrant(proc: ManagedProcessRecord, ttlMs: number): ControlGrant;
|
|
72
|
+
/**
|
|
73
|
+
* 显式释放控制令牌:清理 TTL 定时器、凭据置空并唤醒下一个等待者。
|
|
74
|
+
*/
|
|
75
|
+
releaseGrant(proc: ManagedProcessRecord): void;
|
|
76
|
+
/**
|
|
77
|
+
* 唤醒排队等待控制权的下一个调用者(唯一唤醒入口):
|
|
78
|
+
* 授予令牌、由唤醒方落盘排队凭据并 resolve 原等待 Promise。
|
|
79
|
+
*/
|
|
80
|
+
private wakeNextAcquireWaiter;
|
|
81
|
+
/**
|
|
82
|
+
* 控制权到期未释放时的复核:仍处于 held 且凭据有效时转隔离(隔离而非释放)。
|
|
83
|
+
*/
|
|
84
|
+
handleGrantTtlExpired(proc: ManagedProcessRecord): void;
|
|
85
|
+
/**
|
|
86
|
+
* 操作提交前校验持有者令牌与授权(write/control 入队前与调度链路共用)。
|
|
87
|
+
*/
|
|
88
|
+
validateOperation(proc: ManagedProcessRecord, token: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* 校验当前持有凭据有效性(token 匹配且未过期),供 renew/release 提交前判定。
|
|
91
|
+
*/
|
|
92
|
+
validateHeldGrant(proc: ManagedProcessRecord, token: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* 终态等待者单一拒绝入口:按 FIFO 拒绝全部排队等待者。
|
|
95
|
+
*
|
|
96
|
+
* 终态转移(exit/error/stop/quarantine)必须经由此方法清空等待队列,
|
|
97
|
+
* 禁止直接操作 acquireWaiters 数组。
|
|
98
|
+
*/
|
|
99
|
+
revokeAllWaiters(proc: ManagedProcessRecord, reason: {
|
|
100
|
+
code: string;
|
|
101
|
+
message: string;
|
|
102
|
+
}): void;
|
|
103
|
+
/**
|
|
104
|
+
* 清理进程全部控制权相关定时器与等待队列(stop/退出/故障路径统一入口)。
|
|
105
|
+
*/
|
|
106
|
+
disposeProcess(proc: ManagedProcessRecord): void;
|
|
107
|
+
}
|