@actiondock/core 2.19.0 → 2.20.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/client/config.d.ts +17 -14
- package/dist/client/config.js +112 -14
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +2 -0
- package/dist/execution/service.js +8 -5
- package/dist/execution/types.d.ts +3 -3
- package/dist/host/host.d.ts +9 -1
- package/dist/host/host.js +183 -38
- package/dist/host/types.d.ts +3 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/input/config-override.d.ts +5 -0
- package/dist/input/config-override.js +23 -0
- package/dist/input/input-resolver.d.ts +24 -7
- package/dist/input/input-resolver.js +73 -5
- package/dist/invocation/bindings.d.ts +53 -0
- package/dist/invocation/bindings.js +248 -0
- package/dist/invocation/policy.d.ts +5 -1
- package/dist/invocation/policy.js +16 -9
- package/dist/invocation/remote.d.ts +40 -0
- package/dist/invocation/remote.js +489 -0
- package/dist/invocation/types.d.ts +2 -0
- package/dist/invocation/types.js +1 -0
- package/dist/package/index.d.ts +1 -1
- package/dist/package/runtime.js +31 -12
- package/dist/platform/types.d.ts +48 -1
- package/dist/project/config-declarations.d.ts +61 -0
- package/dist/project/config-declarations.js +297 -0
- package/dist/project/index.d.ts +5 -2
- package/dist/project/index.js +5 -2
- package/dist/project/init.js +2 -2
- package/dist/project/loader.js +4 -0
- package/dist/project/manifest.js +10 -0
- package/dist/project/types.d.ts +30 -0
- package/dist/runtime/context.d.ts +6 -3
- package/dist/runtime/context.js +60 -13
- package/dist/runtime/env.js +15 -1
- package/dist/runtime/standalone-commands.d.ts +2 -2
- package/dist/runtime/standalone-commands.js +113 -47
- package/dist/runtime/standalone-help.d.ts +1 -0
- package/dist/runtime/standalone-help.js +15 -9
- package/dist/runtime/standalone.js +2 -2
- package/dist/server/policy.d.ts +24 -107
- package/dist/server/policy.js +134 -291
- package/dist/server/routes/actions.js +10 -8
- package/dist/server/routes/common.d.ts +5 -20
- package/dist/server/routes/common.js +22 -58
- package/dist/server/routes/config.js +65 -7
- package/dist/server/routes/doctor.d.ts +1 -0
- package/dist/server/routes/doctor.js +4 -3
- package/dist/server/routes/info.js +100 -13
- package/dist/server/routes/playbooks.js +1 -3
- package/dist/server/routes/runs.js +96 -18
- package/dist/server/server.js +25 -53
- package/dist/server/types.d.ts +6 -6
- package/dist/service/factory.js +1 -0
- package/dist/service/remote.js +14 -29
- package/dist/service/types.d.ts +20 -5
- package/dist/service/types.js +8 -0
- package/dist/storage/mask.d.ts +33 -6
- package/dist/storage/mask.js +298 -16
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ConfigItemDefinition } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* 判定指定配置键名是否为通配模板键。
|
|
4
|
+
*
|
|
5
|
+
* @param key 待检测的配置键名
|
|
6
|
+
*/
|
|
7
|
+
export declare function isConfigTemplate(key: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* 断言键名为具体配置键,拒绝空字符串、非字符串以及模板通配键。
|
|
10
|
+
*
|
|
11
|
+
* @param key 待检查的具体配置键名
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertConcreteConfigKey(key: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* 解析配置项命中结果结构。
|
|
16
|
+
*/
|
|
17
|
+
export interface ResolvedConfigDeclaration {
|
|
18
|
+
/** 命中声明所登记的原始键名(具体键或模板键) */
|
|
19
|
+
key: string;
|
|
20
|
+
/** 声明的配置定义元数据 */
|
|
21
|
+
item: ConfigItemDefinition;
|
|
22
|
+
/** 若通过模板匹配命中,则返回命中的模板表达式 */
|
|
23
|
+
template?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 解析并匹配指定配置键在声明字典中的定义项。
|
|
27
|
+
*
|
|
28
|
+
* 匹配优先级规则:
|
|
29
|
+
* - 具体声明键严格优先于任何模板通配键;
|
|
30
|
+
* - 模板只允许包含单个星号(*),匹配一个或多个合法标识字符([A-Za-z0-9_.-]);
|
|
31
|
+
* - 多个模板同时命中时,优先选取前缀最长者,前缀长度相同时选取后缀最长者,杜绝对象枚举遍历顺序影响。
|
|
32
|
+
*
|
|
33
|
+
* @param config 声明的配置定义字典
|
|
34
|
+
* @param key 实际访问或配置的具体键名
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveConfigDeclaration(config: Record<string, ConfigItemDefinition> | undefined, key: string): ResolvedConfigDeclaration | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* 校验配置声明集合中的通配模板与受限 Schema 规范。
|
|
39
|
+
* 针对历史无模板、无 Schema 项保持完全兼容。
|
|
40
|
+
*
|
|
41
|
+
* @param config 待校验的配置声明定义
|
|
42
|
+
* @returns 错误信息字符串数组,无错误返回空数组
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateConfigDeclarations(config: unknown): string[];
|
|
45
|
+
/**
|
|
46
|
+
* 断言配置声明集合完全合法。
|
|
47
|
+
* 若存在非法模板或不合规 Schema 抛出 MANIFEST_INVALID 异常。
|
|
48
|
+
*
|
|
49
|
+
* @param config 待校验的配置声明定义
|
|
50
|
+
*/
|
|
51
|
+
export declare function assertValidConfigDeclarations(config: unknown): void;
|
|
52
|
+
/**
|
|
53
|
+
* 校验指定配置数据值是否符合绑定的 Schema 契约。
|
|
54
|
+
* 仅在声明了 schema 且数据值不为 undefined 时执行校验。
|
|
55
|
+
* 错误信息输出键名与失败路径原因,严禁包含实际数据值。
|
|
56
|
+
*
|
|
57
|
+
* @param key 配置项键名
|
|
58
|
+
* @param value 待校验的具体数据值
|
|
59
|
+
* @param item 配置项元数据定义
|
|
60
|
+
*/
|
|
61
|
+
export declare function validateConfigValue(key: string, value: unknown, item?: ConfigItemDefinition): void;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { ActionDockError, CONFIG_VALUE_INVALID, INVALID_ARGUMENT, MANIFEST_INVALID, } from "../errors.js";
|
|
2
|
+
import { validateSchema, validateSchemaOnly } from "../schema/validator.js";
|
|
3
|
+
/**
|
|
4
|
+
* 判定指定配置键名是否为通配模板键。
|
|
5
|
+
*
|
|
6
|
+
* @param key 待检测的配置键名
|
|
7
|
+
*/
|
|
8
|
+
export function isConfigTemplate(key) {
|
|
9
|
+
return typeof key === "string" && key.includes("*");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 断言键名为具体配置键,拒绝空字符串、非字符串以及模板通配键。
|
|
13
|
+
*
|
|
14
|
+
* @param key 待检查的具体配置键名
|
|
15
|
+
*/
|
|
16
|
+
export function assertConcreteConfigKey(key) {
|
|
17
|
+
if (typeof key !== "string" || key.length === 0 || key.includes("*")) {
|
|
18
|
+
throw new ActionDockError(INVALID_ARGUMENT, `Concrete configuration key expected, received invalid key: '${key}'`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function escapeRegex(str) {
|
|
22
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 解析并匹配指定配置键在声明字典中的定义项。
|
|
26
|
+
*
|
|
27
|
+
* 匹配优先级规则:
|
|
28
|
+
* - 具体声明键严格优先于任何模板通配键;
|
|
29
|
+
* - 模板只允许包含单个星号(*),匹配一个或多个合法标识字符([A-Za-z0-9_.-]);
|
|
30
|
+
* - 多个模板同时命中时,优先选取前缀最长者,前缀长度相同时选取后缀最长者,杜绝对象枚举遍历顺序影响。
|
|
31
|
+
*
|
|
32
|
+
* @param config 声明的配置定义字典
|
|
33
|
+
* @param key 实际访问或配置的具体键名
|
|
34
|
+
*/
|
|
35
|
+
export function resolveConfigDeclaration(config, key) {
|
|
36
|
+
if (!config || typeof config !== "object" || typeof key !== "string") {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
// 具体键严格优先
|
|
40
|
+
if (Object.prototype.hasOwnProperty.call(config, key)) {
|
|
41
|
+
return {
|
|
42
|
+
key,
|
|
43
|
+
item: config[key],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const candidates = [];
|
|
47
|
+
for (const [declaredKey, item] of Object.entries(config)) {
|
|
48
|
+
if (!isConfigTemplate(declaredKey)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const parts = declaredKey.split("*");
|
|
52
|
+
if (parts.length !== 2) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const [prefix, suffix] = parts;
|
|
56
|
+
const pattern = new RegExp(`^${escapeRegex(prefix)}([A-Za-z0-9_.-]+)${escapeRegex(suffix)}$`);
|
|
57
|
+
if (pattern.exec(key)?.[0] === key) {
|
|
58
|
+
candidates.push({
|
|
59
|
+
templateKey: declaredKey,
|
|
60
|
+
item,
|
|
61
|
+
prefixLength: prefix.length,
|
|
62
|
+
suffixLength: suffix.length,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (candidates.length === 0) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
// 判定最长前缀再最长后缀,稳定按字典序平局兜底
|
|
70
|
+
candidates.sort((a, b) => {
|
|
71
|
+
if (b.prefixLength !== a.prefixLength) {
|
|
72
|
+
return b.prefixLength - a.prefixLength;
|
|
73
|
+
}
|
|
74
|
+
if (b.suffixLength !== a.suffixLength) {
|
|
75
|
+
return b.suffixLength - a.suffixLength;
|
|
76
|
+
}
|
|
77
|
+
return a.templateKey.localeCompare(b.templateKey);
|
|
78
|
+
});
|
|
79
|
+
const best = candidates[0];
|
|
80
|
+
return {
|
|
81
|
+
key: best.templateKey,
|
|
82
|
+
item: best.item,
|
|
83
|
+
template: best.templateKey,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** 受限 JSON Schema 允许的白名单关键字集合 */
|
|
87
|
+
const ALLOWED_SCHEMA_KEYWORDS = new Set([
|
|
88
|
+
"type",
|
|
89
|
+
"properties",
|
|
90
|
+
"required",
|
|
91
|
+
"additionalProperties",
|
|
92
|
+
"items",
|
|
93
|
+
"enum",
|
|
94
|
+
"minLength",
|
|
95
|
+
"maxLength",
|
|
96
|
+
"minimum",
|
|
97
|
+
"maximum",
|
|
98
|
+
"secret",
|
|
99
|
+
]);
|
|
100
|
+
/** 模板键中严禁声明的保留字段 */
|
|
101
|
+
const FORBIDDEN_TEMPLATE_FIELDS = ["required", "default", "env"];
|
|
102
|
+
function validateSchemaNode(schemaNode, path, errors) {
|
|
103
|
+
if (typeof schemaNode !== "object" || schemaNode === null || Array.isArray(schemaNode)) {
|
|
104
|
+
errors.push(`Schema at '${path}' must be an object`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const k of Object.keys(schemaNode)) {
|
|
108
|
+
if (!ALLOWED_SCHEMA_KEYWORDS.has(k)) {
|
|
109
|
+
errors.push(`Schema at '${path}' contains unsupported keyword '${k}'`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const node = schemaNode;
|
|
113
|
+
if (node.type !== undefined) {
|
|
114
|
+
if (typeof node.type !== "string" ||
|
|
115
|
+
!["object", "array", "string", "integer", "number", "boolean"].includes(node.type)) {
|
|
116
|
+
errors.push(`Schema at '${path}' property 'type' must be one of "object", "array", "string", "integer", "number", "boolean"`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (node.properties !== undefined) {
|
|
120
|
+
if (typeof node.properties !== "object" ||
|
|
121
|
+
node.properties === null ||
|
|
122
|
+
Array.isArray(node.properties)) {
|
|
123
|
+
errors.push(`Schema at '${path}' property 'properties' must be an object`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
for (const [propName, child] of Object.entries(node.properties)) {
|
|
127
|
+
validateSchemaNode(child, `${path}/properties/${propName}`, errors);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (node.required !== undefined) {
|
|
132
|
+
if (!Array.isArray(node.required) ||
|
|
133
|
+
node.required.some((r) => typeof r !== "string")) {
|
|
134
|
+
errors.push(`Schema at '${path}' property 'required' must be an array of strings`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (node.additionalProperties !== undefined) {
|
|
138
|
+
if (typeof node.additionalProperties === "boolean") {
|
|
139
|
+
// 允许布尔配置
|
|
140
|
+
}
|
|
141
|
+
else if (typeof node.additionalProperties === "object" &&
|
|
142
|
+
node.additionalProperties !== null &&
|
|
143
|
+
!Array.isArray(node.additionalProperties)) {
|
|
144
|
+
validateSchemaNode(node.additionalProperties, `${path}/additionalProperties`, errors);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
errors.push(`Schema at '${path}' property 'additionalProperties' must be a boolean or a schema object`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (node.items !== undefined) {
|
|
151
|
+
if (typeof node.items === "object" &&
|
|
152
|
+
node.items !== null &&
|
|
153
|
+
!Array.isArray(node.items)) {
|
|
154
|
+
validateSchemaNode(node.items, `${path}/items`, errors);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
errors.push(`Schema at '${path}' property 'items' must be a schema object`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (node.enum !== undefined) {
|
|
161
|
+
if (!Array.isArray(node.enum)) {
|
|
162
|
+
errors.push(`Schema at '${path}' property 'enum' must be an array`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (node.minLength !== undefined) {
|
|
166
|
+
if (typeof node.minLength !== "number" ||
|
|
167
|
+
!Number.isInteger(node.minLength) ||
|
|
168
|
+
node.minLength < 0) {
|
|
169
|
+
errors.push(`Schema at '${path}' property 'minLength' must be a non-negative integer`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (node.maxLength !== undefined) {
|
|
173
|
+
if (typeof node.maxLength !== "number" ||
|
|
174
|
+
!Number.isInteger(node.maxLength) ||
|
|
175
|
+
node.maxLength < 0) {
|
|
176
|
+
errors.push(`Schema at '${path}' property 'maxLength' must be a non-negative integer`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (node.minimum !== undefined) {
|
|
180
|
+
if (typeof node.minimum !== "number" || !Number.isFinite(node.minimum)) {
|
|
181
|
+
errors.push(`Schema at '${path}' property 'minimum' must be a number`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (node.maximum !== undefined) {
|
|
185
|
+
if (typeof node.maximum !== "number" || !Number.isFinite(node.maximum)) {
|
|
186
|
+
errors.push(`Schema at '${path}' property 'maximum' must be a number`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (node.secret !== undefined) {
|
|
190
|
+
if (typeof node.secret !== "boolean") {
|
|
191
|
+
errors.push(`Schema at '${path}' property 'secret' must be a boolean`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* 校验配置声明集合中的通配模板与受限 Schema 规范。
|
|
197
|
+
* 针对历史无模板、无 Schema 项保持完全兼容。
|
|
198
|
+
*
|
|
199
|
+
* @param config 待校验的配置声明定义
|
|
200
|
+
* @returns 错误信息字符串数组,无错误返回空数组
|
|
201
|
+
*/
|
|
202
|
+
export function validateConfigDeclarations(config) {
|
|
203
|
+
if (config === undefined || config === null) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
if (typeof config !== "object" || Array.isArray(config)) {
|
|
207
|
+
return ["Config declaration must be an object"];
|
|
208
|
+
}
|
|
209
|
+
const errors = [];
|
|
210
|
+
for (const [key, item] of Object.entries(config)) {
|
|
211
|
+
const isTemplate = isConfigTemplate(key);
|
|
212
|
+
if (isTemplate) {
|
|
213
|
+
const starCount = (key.match(/\*/g) || []).length;
|
|
214
|
+
if (starCount !== 1) {
|
|
215
|
+
errors.push(`Config template key '${key}' must contain exactly one '*' wildcard`);
|
|
216
|
+
}
|
|
217
|
+
if (item && typeof item === "object") {
|
|
218
|
+
for (const forbidden of FORBIDDEN_TEMPLATE_FIELDS) {
|
|
219
|
+
if (forbidden in item) {
|
|
220
|
+
errors.push(`Config template '${key}' must not declare '${forbidden}' property`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) {
|
|
226
|
+
if (isTemplate)
|
|
227
|
+
errors.push(`Config template '${key}' must declare an object`);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const itemDef = item;
|
|
231
|
+
if ((isTemplate || "schema" in itemDef) && itemDef.secret !== undefined && typeof itemDef.secret !== "boolean") {
|
|
232
|
+
errors.push(`Config item '${key}' property 'secret' must be a boolean`);
|
|
233
|
+
}
|
|
234
|
+
if ("schema" in itemDef && itemDef.schema !== undefined) {
|
|
235
|
+
if (typeof itemDef.schema !== "object" ||
|
|
236
|
+
itemDef.schema === null ||
|
|
237
|
+
Array.isArray(itemDef.schema)) {
|
|
238
|
+
errors.push(`Config item '${key}' schema must be an object`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
// 递归白名单与属性类型校验
|
|
242
|
+
validateSchemaNode(itemDef.schema, `config['${key}'].schema`, errors);
|
|
243
|
+
// 外层 type 与根 schema.type 一致性检查(外层 number 与模式 integer 允许兼容)
|
|
244
|
+
if (itemDef.type !== undefined && itemDef.schema.type !== undefined) {
|
|
245
|
+
if (itemDef.type === "number") {
|
|
246
|
+
if (itemDef.schema.type !== "number" && itemDef.schema.type !== "integer") {
|
|
247
|
+
errors.push(`Config item '${key}' outer type 'number' is incompatible with schema type '${itemDef.schema.type}'`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (itemDef.type !== itemDef.schema.type) {
|
|
251
|
+
errors.push(`Config item '${key}' outer type '${itemDef.type}' is incompatible with schema type '${itemDef.schema.type}'`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// 利用现有 Ajv 进行 Schema 编译合法性校验
|
|
255
|
+
const ajvCheck = validateSchemaOnly(itemDef.schema, {});
|
|
256
|
+
if (!ajvCheck.valid && ajvCheck.errors) {
|
|
257
|
+
for (const err of ajvCheck.errors) {
|
|
258
|
+
if (err.startsWith("Schema compilation error:")) {
|
|
259
|
+
errors.push(`Config item '${key}' schema failed compilation: ${err}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return errors;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 断言配置声明集合完全合法。
|
|
269
|
+
* 若存在非法模板或不合规 Schema 抛出 MANIFEST_INVALID 异常。
|
|
270
|
+
*
|
|
271
|
+
* @param config 待校验的配置声明定义
|
|
272
|
+
*/
|
|
273
|
+
export function assertValidConfigDeclarations(config) {
|
|
274
|
+
const errors = validateConfigDeclarations(config);
|
|
275
|
+
if (errors.length > 0) {
|
|
276
|
+
throw new ActionDockError(MANIFEST_INVALID, `Invalid configuration declarations: ${errors.join("; ")}`, { errors });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* 校验指定配置数据值是否符合绑定的 Schema 契约。
|
|
281
|
+
* 仅在声明了 schema 且数据值不为 undefined 时执行校验。
|
|
282
|
+
* 错误信息输出键名与失败路径原因,严禁包含实际数据值。
|
|
283
|
+
*
|
|
284
|
+
* @param key 配置项键名
|
|
285
|
+
* @param value 待校验的具体数据值
|
|
286
|
+
* @param item 配置项元数据定义
|
|
287
|
+
*/
|
|
288
|
+
export function validateConfigValue(key, value, item) {
|
|
289
|
+
if (!item?.schema || value === undefined) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const result = validateSchema(item.schema, value);
|
|
293
|
+
if (!result.valid) {
|
|
294
|
+
const reasons = (result.errors || []).join("; ");
|
|
295
|
+
throw new ActionDockError(CONFIG_VALUE_INVALID, `Configuration value for '${key}' failed schema validation: ${reasons}`, { key, errors: result.errors });
|
|
296
|
+
}
|
|
297
|
+
}
|
package/dist/project/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { loadActionBindings, validateActionBindings, resolveActionBinding, hasRemoteActionBinding, validateCanonicalActionRef, validateBindingName, type ActionBindings, type ActionBindingTarget, } from "../invocation/bindings.js";
|
|
1
2
|
export * from "./digest.js";
|
|
2
3
|
export * from "./init.js";
|
|
3
4
|
export * from "./loader.js";
|
|
@@ -7,10 +8,12 @@ export { SNAPSHOT_TRACKED_FILES, type TransactionFileRecord, type TransactionMet
|
|
|
7
8
|
export * from "./types.js";
|
|
8
9
|
export * from "./types-generator.js";
|
|
9
10
|
export { assertPathWithinRoot, isPathOutsideBoundary, getPackageSlug, parseDuration, traverseDirectory, type TraverseDirectoryEntry, type TraverseDirectoryOptions, } from "../utils/index.js";
|
|
10
|
-
export { isSecretConfigKey, maskSecretValue, sanitizeConfigDefinitions, } from "../storage/mask.js";
|
|
11
|
+
export { isSecretConfigKey, maskSecretValue, sanitizeConfigDefinitions, redactConfigValue, redactConfigValueForDeclarations, type RedactResult, } from "../storage/mask.js";
|
|
12
|
+
export { isConfigTemplate, resolveConfigDeclaration, validateConfigDeclarations, assertValidConfigDeclarations, assertConcreteConfigKey, validateConfigValue, type ResolvedConfigDeclaration, } from "./config-declarations.js";
|
|
11
13
|
export { runDoctorChecks, } from "../doctor/doctor.js";
|
|
12
|
-
export { resolveActionInput, buildActionDescribePayload, formatActionDetail, type FormatActionDetailOptions, ACTION_DESCRIBE_SYNTAX_REFERENCE, inspectActionTextFieldAnnotation, analyzeActionOutputContract, type AnnotationInspectionResult, type ActionOutputContractInput, type ActionOutputContractAnalysis, type OutputContractDiagnostic, mapInputValidationFailure, stripBom, readStdinBounded, type ResolveActionInputOptions, type ReadStdinBoundedOptions, } from "../input/index.js";
|
|
14
|
+
export { resolveInput, resolveActionInput, resolveValueInput, hasValueInput, parseValueLiteral, buildActionDescribePayload, formatActionDetail, type FormatActionDetailOptions, ACTION_DESCRIBE_SYNTAX_REFERENCE, inspectActionTextFieldAnnotation, analyzeActionOutputContract, type AnnotationInspectionResult, type ActionOutputContractInput, type ActionOutputContractAnalysis, type OutputContractDiagnostic, mapInputValidationFailure, stripBom, readStdinBounded, type ResolveInputOptions, type ResolveValueInputOptions, type ResolveActionInputOptions, type InputResolutionPolicy, type ReadStdinBoundedOptions, } from "../input/index.js";
|
|
13
15
|
export { validateActionInputValue } from "../value-validator.js";
|
|
14
16
|
export { parseJson } from "../input/input-resolver.js";
|
|
17
|
+
export { parseConfigOverrideAssignment } from "../input/config-override.js";
|
|
15
18
|
export { InputError, FlatInputError, } from "../input/flat-errors.js";
|
|
16
19
|
export { filterWithFallbackInfo, compileIntentRegex, matchIntent, type Extractor, } from "../utils/intent.js";
|
package/dist/project/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { loadActionBindings, validateActionBindings, resolveActionBinding, hasRemoteActionBinding, validateCanonicalActionRef, validateBindingName, } from "../invocation/bindings.js";
|
|
1
2
|
export * from "./digest.js";
|
|
2
3
|
export * from "./init.js";
|
|
3
4
|
export * from "./loader.js";
|
|
@@ -7,12 +8,14 @@ export { SNAPSHOT_TRACKED_FILES, isProjectLockHeld, acquireProjectLock, runFroze
|
|
|
7
8
|
export * from "./types.js";
|
|
8
9
|
export * from "./types-generator.js";
|
|
9
10
|
export { assertPathWithinRoot, isPathOutsideBoundary, getPackageSlug, parseDuration, traverseDirectory, } from "../utils/index.js";
|
|
10
|
-
export { isSecretConfigKey, maskSecretValue, sanitizeConfigDefinitions, } from "../storage/mask.js";
|
|
11
|
+
export { isSecretConfigKey, maskSecretValue, sanitizeConfigDefinitions, redactConfigValue, redactConfigValueForDeclarations, } from "../storage/mask.js";
|
|
12
|
+
export { isConfigTemplate, resolveConfigDeclaration, validateConfigDeclarations, assertValidConfigDeclarations, assertConcreteConfigKey, validateConfigValue, } from "./config-declarations.js";
|
|
11
13
|
export { runDoctorChecks, } from "../doctor/doctor.js";
|
|
12
14
|
// 动作入参解析、输出契约分析、校验与格式化(含 BOM 剥离与标准输入有界读取的单一事实源转引)
|
|
13
|
-
export { resolveActionInput, buildActionDescribePayload, formatActionDetail, ACTION_DESCRIBE_SYNTAX_REFERENCE, inspectActionTextFieldAnnotation, analyzeActionOutputContract, mapInputValidationFailure, stripBom, readStdinBounded, } from "../input/index.js";
|
|
15
|
+
export { resolveInput, resolveActionInput, resolveValueInput, hasValueInput, parseValueLiteral, buildActionDescribePayload, formatActionDetail, ACTION_DESCRIBE_SYNTAX_REFERENCE, inspectActionTextFieldAnnotation, analyzeActionOutputContract, mapInputValidationFailure, stripBom, readStdinBounded, } from "../input/index.js";
|
|
14
16
|
export { validateActionInputValue } from "../value-validator.js";
|
|
15
17
|
export { parseJson } from "../input/input-resolver.js";
|
|
18
|
+
export { parseConfigOverrideAssignment } from "../input/config-override.js";
|
|
16
19
|
export { InputError, FlatInputError, } from "../input/flat-errors.js";
|
|
17
20
|
// 意图过滤与查询
|
|
18
21
|
export { filterWithFallbackInfo, compileIntentRegex, matchIntent, } from "../utils/intent.js";
|
package/dist/project/init.js
CHANGED
|
@@ -86,10 +86,10 @@ export function initProject(targetDir, options = {}) {
|
|
|
86
86
|
node: ">=24.12.0",
|
|
87
87
|
},
|
|
88
88
|
dependencies: {
|
|
89
|
-
"@actiondock/sdk": "^2.
|
|
89
|
+
"@actiondock/sdk": "^2.20.0",
|
|
90
90
|
},
|
|
91
91
|
devDependencies: {
|
|
92
|
-
"@actiondock/testing": "^2.
|
|
92
|
+
"@actiondock/testing": "^2.20.0",
|
|
93
93
|
"@types/node": "^22.13.0",
|
|
94
94
|
"tsx": "^4.19.0",
|
|
95
95
|
"typescript": "^5.7.0",
|
package/dist/project/loader.js
CHANGED
|
@@ -4,6 +4,7 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
4
4
|
import { NodeModuleLoader, unwrapDefaultExport } from "../platform/module-loader.js";
|
|
5
5
|
import { ActionDockError, ACTION_LOAD_FAILED, describeActionLoadFailure, INVALID_ACTION_ID, INVALID_JSON, INVALID_PACKAGE_ID, INVALID_PLAYBOOK_ID, MANIFEST_LOAD_FAILED, PLAYBOOK_NOT_FOUND, PROJECT_CONFIG_NOT_FOUND, } from "../errors.js";
|
|
6
6
|
import { loadManifest, ACTION_ID_REGEX, PLAYBOOK_ID_REGEX } from "./manifest.js";
|
|
7
|
+
import { assertValidConfigDeclarations } from "./config-declarations.js";
|
|
7
8
|
/**
|
|
8
9
|
* 从指定目录开始向上逐级递归查找包含 `actiondock.json` 的项目根目录。
|
|
9
10
|
*
|
|
@@ -68,6 +69,9 @@ export function loadProjectConfig(projectRoot) {
|
|
|
68
69
|
if (parsed.playbooksDir) {
|
|
69
70
|
assertWithinProjectRoot(projectRoot, parsed.playbooksDir, "playbooksDir");
|
|
70
71
|
}
|
|
72
|
+
if (parsed.config !== undefined) {
|
|
73
|
+
assertValidConfigDeclarations(parsed.config);
|
|
74
|
+
}
|
|
71
75
|
return parsed;
|
|
72
76
|
}
|
|
73
77
|
catch (err) {
|
package/dist/project/manifest.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { assertValidConfigDeclarations, validateConfigDeclarations } from "./config-declarations.js";
|
|
3
4
|
import { isPathOutsideBoundary, PACKAGE_ID_REGEX } from "../utils/index.js";
|
|
4
5
|
import { ActionDockError, MANIFEST_CORRUPTED, MANIFEST_INVALID, MANIFEST_READ_FAILED, UNSUPPORTED_MANIFEST_SCHEMA, } from "../errors.js";
|
|
5
6
|
export const MANIFEST_FILE_NAME = "actiondock.json";
|
|
@@ -38,6 +39,9 @@ export function loadManifest(projectRoot) {
|
|
|
38
39
|
if (!parsed.id) {
|
|
39
40
|
parsed.id = basename(projectRoot);
|
|
40
41
|
}
|
|
42
|
+
if (parsed.config !== undefined) {
|
|
43
|
+
assertValidConfigDeclarations(parsed.config);
|
|
44
|
+
}
|
|
41
45
|
return parsed;
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
@@ -65,6 +69,12 @@ export function validateManifest(manifest, options) {
|
|
|
65
69
|
errors.push(`Manifest 'id' is invalid: '${m.id}' (must match ${PACKAGE_ID_REGEX})`);
|
|
66
70
|
}
|
|
67
71
|
}
|
|
72
|
+
if (m.config !== undefined) {
|
|
73
|
+
const configErrors = validateConfigDeclarations(m.config);
|
|
74
|
+
for (const err of configErrors) {
|
|
75
|
+
errors.push(err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
68
78
|
if (m.actions !== undefined) {
|
|
69
79
|
if (!m.actions || typeof m.actions !== "object" || Array.isArray(m.actions)) {
|
|
70
80
|
errors.push("Manifest 'actions' must be an object");
|
package/dist/project/types.d.ts
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
* 配置项的值类型枚举。用于自动类型推断与环境变量自动转换。
|
|
3
3
|
*/
|
|
4
4
|
export type ConfigValueType = "string" | "number" | "boolean" | "object" | "array";
|
|
5
|
+
/**
|
|
6
|
+
* 配置值受限模式定义契约。
|
|
7
|
+
* 仅允许受支持的纯受限关键字集合,禁止动态计算或高级条件指令。
|
|
8
|
+
*/
|
|
9
|
+
export interface ConfigValueSchema {
|
|
10
|
+
/** 节点数据类型 */
|
|
11
|
+
type?: "object" | "array" | "string" | "integer" | "number" | "boolean";
|
|
12
|
+
/** 对象属性字段子模式映射 */
|
|
13
|
+
properties?: Record<string, ConfigValueSchema>;
|
|
14
|
+
/** 必填属性字段列表 */
|
|
15
|
+
required?: string[];
|
|
16
|
+
/** 额外属性放行策略(布尔开关或子模式) */
|
|
17
|
+
additionalProperties?: boolean | ConfigValueSchema;
|
|
18
|
+
/** 数组元素子模式 */
|
|
19
|
+
items?: ConfigValueSchema;
|
|
20
|
+
/** 枚举有效值列表 */
|
|
21
|
+
enum?: unknown[];
|
|
22
|
+
/** 字符串最小字符长度 */
|
|
23
|
+
minLength?: number;
|
|
24
|
+
/** 字符串最大字符长度 */
|
|
25
|
+
maxLength?: number;
|
|
26
|
+
/** 数值最小值约束 */
|
|
27
|
+
minimum?: number;
|
|
28
|
+
/** 数值最大值约束 */
|
|
29
|
+
maximum?: number;
|
|
30
|
+
/** 是否为敏感字段(仅可在模式节点中扩展指定) */
|
|
31
|
+
secret?: boolean;
|
|
32
|
+
}
|
|
5
33
|
/**
|
|
6
34
|
* 在 actiondock.json 中声明的单项配置定义。
|
|
7
35
|
*/
|
|
@@ -20,6 +48,8 @@ export interface ConfigItemDefinition {
|
|
|
20
48
|
required?: boolean;
|
|
21
49
|
/** 是否允许单次调用覆盖 */
|
|
22
50
|
allowInvocationOverride?: boolean;
|
|
51
|
+
/** 配置值验证与字段级脱敏所绑定的受限模式定义 */
|
|
52
|
+
schema?: ConfigValueSchema;
|
|
23
53
|
}
|
|
24
54
|
/**
|
|
25
55
|
* 单个 Action 在 actiondock.json 清单中的声明项。
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ActionContext, ActionRef, Config, Logger, ProcessAPI, ProgressReporter, StateStore } from "@actiondock/sdk";
|
|
1
|
+
import type { ActionContext, ActionInvokeOptions, ActionRef, Config, Logger, ProcessAPI, ProgressReporter, StateStore } from "@actiondock/sdk";
|
|
2
2
|
import type { ProjectConfig } from "../project/types.js";
|
|
3
3
|
import type { RuntimeStorage } from "../storage/types.js";
|
|
4
4
|
import { ProcessManager, type ProcessOwner } from "../process/index.js";
|
|
5
|
+
import type { ConfigSource } from "../service/types.js";
|
|
5
6
|
/**
|
|
6
7
|
* 生产级配置解析器实现。
|
|
7
8
|
* 严格践行 5 层配置解析优先级链:
|
|
@@ -26,9 +27,11 @@ export declare class RuntimeConfig implements Config {
|
|
|
26
27
|
*
|
|
27
28
|
* 作为五层优先级链的唯一事实源读取视图,供 app 层 getConfig 安全视图直接委托,
|
|
28
29
|
* 避免重复实现解析链导致行为漂移。
|
|
30
|
+
* source 仅表达实际生效值来源;undefined 统一视为未命中并继续向低优先级解析,
|
|
31
|
+
* null/false/0/空串均为真实值;整链未命中时 source 为 null。
|
|
29
32
|
*/
|
|
30
33
|
describe<T = unknown>(key: string): {
|
|
31
|
-
source:
|
|
34
|
+
source: ConfigSource | null;
|
|
32
35
|
value: T | undefined;
|
|
33
36
|
};
|
|
34
37
|
}
|
|
@@ -81,7 +84,7 @@ export interface ContextOptions {
|
|
|
81
84
|
owner?: ProcessOwner;
|
|
82
85
|
progress?: ProgressReporter;
|
|
83
86
|
logger?: Logger;
|
|
84
|
-
onActionInvoke?: (action: ActionRef | string, input: unknown, parentRunId?: string) => Promise<unknown>;
|
|
87
|
+
onActionInvoke?: (action: ActionRef | string, input: unknown, parentRunId?: string, options?: ActionInvokeOptions) => Promise<unknown>;
|
|
85
88
|
}
|
|
86
89
|
/**
|
|
87
90
|
* 构建并装配传递给 Action 的完整 ActionContext 运行时上下文。
|