@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.
Files changed (175) hide show
  1. package/dist/app/app.d.ts +14 -1
  2. package/dist/app/app.js +88 -222
  3. package/dist/app/static-index.d.ts +39 -0
  4. package/dist/app/static-index.js +164 -0
  5. package/dist/app/types.d.ts +5 -0
  6. package/dist/catalog/action-index.js +6 -4
  7. package/dist/catalog/package-catalog.js +2 -1
  8. package/dist/doctor/checks/index.d.ts +5 -0
  9. package/dist/doctor/checks/index.js +5 -0
  10. package/dist/doctor/checks/project-locate.d.ts +6 -0
  11. package/dist/doctor/checks/project-locate.js +36 -0
  12. package/dist/doctor/checks/project.d.ts +6 -0
  13. package/dist/doctor/checks/project.js +383 -0
  14. package/dist/doctor/checks/registry.d.ts +13 -0
  15. package/dist/doctor/checks/registry.js +207 -0
  16. package/dist/doctor/checks/runtime.d.ts +13 -0
  17. package/dist/doctor/checks/runtime.js +90 -0
  18. package/dist/doctor/checks/storage.d.ts +5 -0
  19. package/dist/doctor/checks/storage.js +34 -0
  20. package/dist/doctor/context.d.ts +35 -0
  21. package/dist/doctor/context.js +11 -0
  22. package/dist/doctor/doctor.d.ts +5 -0
  23. package/dist/doctor/doctor.js +33 -602
  24. package/dist/errors.d.ts +22 -0
  25. package/dist/errors.js +22 -0
  26. package/dist/execution/service.js +14 -9
  27. package/dist/export/templates.js +42 -30
  28. package/dist/host/host.d.ts +33 -0
  29. package/dist/host/host.js +222 -362
  30. package/dist/host/routing.d.ts +92 -0
  31. package/dist/host/routing.js +238 -0
  32. package/dist/host/types.d.ts +8 -0
  33. package/dist/index.d.ts +3 -1
  34. package/dist/index.js +3 -1
  35. package/dist/input/advice.d.ts +100 -0
  36. package/dist/input/advice.js +767 -0
  37. package/dist/input/describe.d.ts +106 -0
  38. package/dist/input/describe.js +176 -0
  39. package/dist/input/file-input.d.ts +54 -0
  40. package/dist/input/file-input.js +124 -0
  41. package/dist/input/flat-decode.d.ts +11 -0
  42. package/dist/input/flat-decode.js +13 -0
  43. package/dist/input/flat-errors.d.ts +72 -0
  44. package/dist/input/flat-errors.js +87 -0
  45. package/dist/input/flat-materializer.d.ts +29 -0
  46. package/dist/input/flat-materializer.js +179 -0
  47. package/dist/input/flat-parser.d.ts +50 -0
  48. package/dist/input/flat-parser.js +169 -0
  49. package/dist/input/flat-predicates.d.ts +34 -0
  50. package/dist/input/flat-predicates.js +50 -0
  51. package/dist/input/index.d.ts +14 -0
  52. package/dist/input/index.js +14 -0
  53. package/dist/input/input-resolver.d.ts +63 -0
  54. package/dist/input/input-resolver.js +254 -0
  55. package/dist/input/json-depth-scanner.d.ts +17 -0
  56. package/dist/input/json-depth-scanner.js +49 -0
  57. package/dist/input/metadata.d.ts +99 -0
  58. package/dist/input/metadata.js +99 -0
  59. package/dist/input/stdin-input.d.ts +26 -0
  60. package/dist/input/stdin-input.js +125 -0
  61. package/dist/input/utf8.d.ts +19 -0
  62. package/dist/input/utf8.js +36 -0
  63. package/dist/input/validation-mapper.d.ts +27 -0
  64. package/dist/input/validation-mapper.js +144 -0
  65. package/dist/ipc/host.js +38 -0
  66. package/dist/json/index.d.ts +1 -0
  67. package/dist/json/index.js +1 -0
  68. package/dist/json/value-validator.d.ts +88 -0
  69. package/dist/json/value-validator.js +330 -0
  70. package/dist/platform/types.d.ts +7 -0
  71. package/dist/process/control-arbiter.d.ts +107 -0
  72. package/dist/process/control-arbiter.js +251 -0
  73. package/dist/process/diagnostics-sink.d.ts +26 -0
  74. package/dist/process/diagnostics-sink.js +35 -0
  75. package/dist/process/index.d.ts +7 -0
  76. package/dist/process/index.js +7 -0
  77. package/dist/process/input-dispatcher.d.ts +88 -0
  78. package/dist/process/input-dispatcher.js +240 -0
  79. package/dist/process/managed-record.d.ts +78 -0
  80. package/dist/process/managed-record.js +1 -0
  81. package/dist/process/process-manager.d.ts +15 -56
  82. package/dist/process/process-manager.js +119 -707
  83. package/dist/process/reservation-table.d.ts +39 -0
  84. package/dist/process/reservation-table.js +80 -0
  85. package/dist/process/run-executor.d.ts +16 -0
  86. package/dist/process/run-executor.js +155 -0
  87. package/dist/process/terminal-output-cache.d.ts +66 -0
  88. package/dist/process/terminal-output-cache.js +132 -0
  89. package/dist/profile/client-actions.d.ts +56 -0
  90. package/dist/profile/client-actions.js +215 -0
  91. package/dist/profile/client-config.d.ts +20 -0
  92. package/dist/profile/client-config.js +51 -0
  93. package/dist/profile/client-health.d.ts +14 -0
  94. package/dist/profile/client-health.js +69 -0
  95. package/dist/profile/client-playbooks.d.ts +19 -0
  96. package/dist/profile/client-playbooks.js +36 -0
  97. package/dist/profile/client-query.d.ts +30 -0
  98. package/dist/profile/client-query.js +70 -0
  99. package/dist/profile/client-runs.d.ts +42 -0
  100. package/dist/profile/client-runs.js +58 -0
  101. package/dist/profile/client-state.d.ts +56 -0
  102. package/dist/profile/client-state.js +83 -0
  103. package/dist/profile/client-transport.d.ts +61 -0
  104. package/dist/profile/client-transport.js +156 -0
  105. package/dist/profile/client.d.ts +23 -142
  106. package/dist/profile/client.js +28 -588
  107. package/dist/profile/manager.js +1 -1
  108. package/dist/project/closure.d.ts +4 -4
  109. package/dist/project/closure.js +11 -5
  110. package/dist/project/init.js +2 -2
  111. package/dist/project/loader.d.ts +0 -25
  112. package/dist/project/loader.js +1 -154
  113. package/dist/project/transactions.d.ts +12 -14
  114. package/dist/project/transactions.js +60 -836
  115. package/dist/registry/registry.js +6 -4
  116. package/dist/registry/resolve.js +1 -1
  117. package/dist/runtime/action-registry.d.ts +45 -0
  118. package/dist/runtime/action-registry.js +108 -0
  119. package/dist/runtime/clock.d.ts +5 -17
  120. package/dist/runtime/clock.js +5 -15
  121. package/dist/runtime/context.d.ts +3 -0
  122. package/dist/runtime/context.js +17 -56
  123. package/dist/runtime/index.d.ts +3 -0
  124. package/dist/runtime/index.js +3 -0
  125. package/dist/runtime/package-runner-factory.d.ts +82 -0
  126. package/dist/runtime/package-runner-factory.js +186 -0
  127. package/dist/runtime/run-persistence.d.ts +85 -0
  128. package/dist/runtime/run-persistence.js +111 -0
  129. package/dist/runtime/runner.d.ts +25 -45
  130. package/dist/runtime/runner.js +130 -354
  131. package/dist/runtime/standalone.d.ts +25 -2
  132. package/dist/runtime/standalone.js +305 -87
  133. package/dist/schema/index.d.ts +1 -0
  134. package/dist/schema/index.js +1 -0
  135. package/dist/schema/validator.d.ts +10 -0
  136. package/dist/schema/validator.js +48 -54
  137. package/dist/server/dispatcher.d.ts +8 -3
  138. package/dist/server/dispatcher.js +16 -40
  139. package/dist/server/routes/runs.js +6 -4
  140. package/dist/server/security.d.ts +1 -7
  141. package/dist/server/security.js +2 -13
  142. package/dist/storage/clock.d.ts +25 -0
  143. package/dist/storage/clock.js +23 -0
  144. package/dist/storage/data-dir-lock.d.ts +3 -36
  145. package/dist/storage/data-dir-lock.js +48 -881
  146. package/dist/storage/index.d.ts +8 -1
  147. package/dist/storage/index.js +8 -2
  148. package/dist/storage/lazy.js +1 -0
  149. package/dist/storage/lock-core.d.ts +162 -0
  150. package/dist/storage/lock-core.js +885 -0
  151. package/dist/storage/sqlite.d.ts +19 -6
  152. package/dist/storage/sqlite.js +81 -89
  153. package/dist/storage/types.d.ts +27 -5
  154. package/dist/storage/types.js +21 -0
  155. package/dist/storage/utils.d.ts +22 -0
  156. package/dist/storage/utils.js +34 -0
  157. package/dist/target/local.d.ts +1 -0
  158. package/dist/target/local.js +23 -17
  159. package/dist/target/remote-errors.d.ts +21 -0
  160. package/dist/target/remote-errors.js +51 -0
  161. package/dist/target/remote-polling.d.ts +35 -0
  162. package/dist/target/remote-polling.js +104 -0
  163. package/dist/target/remote.d.ts +4 -0
  164. package/dist/target/remote.js +108 -301
  165. package/dist/target/sse-parser.d.ts +35 -0
  166. package/dist/target/sse-parser.js +163 -0
  167. package/dist/target/target.js +2 -0
  168. package/dist/target/types.d.ts +6 -0
  169. package/dist/utils/index.d.ts +1 -0
  170. package/dist/utils/index.js +1 -0
  171. package/dist/utils/net.d.ts +10 -0
  172. package/dist/utils/net.js +16 -0
  173. package/dist/version.d.ts +1 -1
  174. package/dist/version.js +1 -1
  175. package/package.json +2 -2
@@ -0,0 +1,767 @@
1
+ import { isFlatPathPropertyName, isForbiddenActionInputPropertyName, } from "./flat-predicates.js";
2
+ const RECOGNIZED_SCHEMA_TYPES = new Set([
3
+ "object",
4
+ "array",
5
+ "string",
6
+ "number",
7
+ "integer",
8
+ "boolean",
9
+ "null",
10
+ ]);
11
+ const COMPLEX_KEYWORDS = [
12
+ "oneOf",
13
+ "anyOf",
14
+ "allOf",
15
+ "$ref",
16
+ "not",
17
+ "if",
18
+ "then",
19
+ "else",
20
+ "patternProperties",
21
+ "dependentSchemas",
22
+ "dependentRequired",
23
+ "propertyNames",
24
+ "unevaluatedProperties",
25
+ ];
26
+ function isPlainObject(val) {
27
+ return typeof val === "object" && val !== null && !Array.isArray(val);
28
+ }
29
+ function isJsonValueLike(val) {
30
+ if (typeof val === "function" ||
31
+ typeof val === "symbol" ||
32
+ typeof val === "undefined" ||
33
+ typeof val === "bigint") {
34
+ return false;
35
+ }
36
+ if (typeof val === "number" && (!Number.isFinite(val) || Number.isNaN(val))) {
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ function createMalformedAdvice(message) {
42
+ return {
43
+ version: 1,
44
+ analysisMode: "declared-properties-only",
45
+ analysisStatus: "malformed",
46
+ analysisCode: "MALFORMED_SCHEMA",
47
+ analysisMessage: message,
48
+ schemaState: "complex",
49
+ inputFeasibility: "possible-or-unknown",
50
+ flatAvailable: false,
51
+ requiredSatisfiable: null,
52
+ flatCandidate: false,
53
+ schemaRecommendedMode: "full-json",
54
+ fields: [],
55
+ };
56
+ }
57
+ /**
58
+ * 依据 inputSchema 构造 v1 版本的 Action 输入编码顾问报告(机器契约)。
59
+ *
60
+ * @param schema Action 的 inputSchema 定义
61
+ * @returns 遵循技术设计文档规范的结构化输入建议
62
+ */
63
+ export function buildCliInputAdviceV1(schema) {
64
+ // 1. Schema Sanity 阶段(Section 44)
65
+ // 1.1 根形状检查(Section 44.1)
66
+ if (schema === undefined) {
67
+ return {
68
+ version: 1,
69
+ analysisMode: "declared-properties-only",
70
+ analysisStatus: "ok",
71
+ schemaState: "absent",
72
+ inputFeasibility: "possible-or-unknown",
73
+ flatAvailable: false,
74
+ requiredSatisfiable: null,
75
+ flatCandidate: false,
76
+ schemaRecommendedMode: "full-json",
77
+ fields: [],
78
+ };
79
+ }
80
+ if (typeof schema === "boolean") {
81
+ if (schema === false) {
82
+ return {
83
+ version: 1,
84
+ analysisMode: "declared-properties-only",
85
+ analysisStatus: "ok",
86
+ schemaState: "reject-all",
87
+ inputFeasibility: "known-impossible",
88
+ feasibilityCode: "SCHEMA_REJECTS_ALL",
89
+ flatAvailable: false,
90
+ requiredSatisfiable: false,
91
+ flatCandidate: false,
92
+ schemaRecommendedMode: "none",
93
+ fields: [],
94
+ };
95
+ }
96
+ return {
97
+ version: 1,
98
+ analysisMode: "declared-properties-only",
99
+ analysisStatus: "ok",
100
+ schemaState: "any",
101
+ inputFeasibility: "possible-or-unknown",
102
+ flatAvailable: false,
103
+ requiredSatisfiable: null,
104
+ flatCandidate: false,
105
+ schemaRecommendedMode: "full-json",
106
+ fields: [],
107
+ };
108
+ }
109
+ if (!isPlainObject(schema)) {
110
+ return {
111
+ version: 1,
112
+ analysisMode: "declared-properties-only",
113
+ analysisStatus: "malformed",
114
+ analysisCode: "MALFORMED_SCHEMA",
115
+ analysisMessage: "Schema root must be undefined, boolean, or a plain object",
116
+ schemaState: "complex",
117
+ inputFeasibility: "possible-or-unknown",
118
+ flatAvailable: false,
119
+ requiredSatisfiable: null,
120
+ flatCandidate: false,
121
+ schemaRecommendedMode: "full-json",
122
+ fields: [],
123
+ };
124
+ }
125
+ const s = schema;
126
+ // 1.2 关键字形状检查(Section 44.2)
127
+ // 检查 type
128
+ if (s.type !== undefined) {
129
+ if (typeof s.type === "string") {
130
+ if (!RECOGNIZED_SCHEMA_TYPES.has(s.type)) {
131
+ return createMalformedAdvice(`Unrecognized type keyword: '${s.type}'`);
132
+ }
133
+ }
134
+ else if (Array.isArray(s.type)) {
135
+ if (s.type.length === 0) {
136
+ return createMalformedAdvice("Type array must not be empty");
137
+ }
138
+ const seen = new Set();
139
+ for (const t of s.type) {
140
+ if (typeof t !== "string" || !RECOGNIZED_SCHEMA_TYPES.has(t)) {
141
+ return createMalformedAdvice(`Unrecognized type in type array: '${String(t)}'`);
142
+ }
143
+ if (seen.has(t)) {
144
+ return createMalformedAdvice(`Duplicate type in type array: '${t}'`);
145
+ }
146
+ seen.add(t);
147
+ }
148
+ }
149
+ else {
150
+ return createMalformedAdvice("Type keyword must be a string or array of strings");
151
+ }
152
+ }
153
+ // 检查 properties
154
+ if (s.properties !== undefined) {
155
+ if (!isPlainObject(s.properties)) {
156
+ return createMalformedAdvice("Properties keyword must be a plain object");
157
+ }
158
+ for (const [propKey, propVal] of Object.entries(s.properties)) {
159
+ if (typeof propVal !== "boolean" && !isPlainObject(propVal)) {
160
+ return createMalformedAdvice(`Property '${propKey}' schema must be a boolean or plain object`);
161
+ }
162
+ if (isPlainObject(propVal)) {
163
+ if (propVal.type !== undefined) {
164
+ if (typeof propVal.type === "string") {
165
+ if (!RECOGNIZED_SCHEMA_TYPES.has(propVal.type)) {
166
+ return createMalformedAdvice(`Unrecognized type '${propVal.type}' in property '${propKey}'`);
167
+ }
168
+ }
169
+ else if (Array.isArray(propVal.type)) {
170
+ if (propVal.type.length === 0) {
171
+ return createMalformedAdvice(`Type array in property '${propKey}' must not be empty`);
172
+ }
173
+ const seen = new Set();
174
+ for (const t of propVal.type) {
175
+ if (typeof t !== "string" || !RECOGNIZED_SCHEMA_TYPES.has(t)) {
176
+ return createMalformedAdvice(`Unrecognized type '${String(t)}' in property '${propKey}'`);
177
+ }
178
+ if (seen.has(t)) {
179
+ return createMalformedAdvice(`Duplicate type '${t}' in property '${propKey}'`);
180
+ }
181
+ seen.add(t);
182
+ }
183
+ }
184
+ else {
185
+ return createMalformedAdvice(`Type in property '${propKey}' must be string or array of strings`);
186
+ }
187
+ }
188
+ if (propVal.enum !== undefined) {
189
+ if (!Array.isArray(propVal.enum) || propVal.enum.length === 0) {
190
+ return createMalformedAdvice(`Enum in property '${propKey}' must be a non-empty array`);
191
+ }
192
+ }
193
+ if (propVal.const !== undefined) {
194
+ if (!isJsonValueLike(propVal.const)) {
195
+ return createMalformedAdvice(`Const in property '${propKey}' must be a valid JSON value literal`);
196
+ }
197
+ }
198
+ if (propVal.items !== undefined) {
199
+ if (typeof propVal.items !== "boolean" &&
200
+ !isPlainObject(propVal.items) &&
201
+ !Array.isArray(propVal.items)) {
202
+ return createMalformedAdvice(`Items in property '${propKey}' must be boolean, plain object, or array`);
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+ // 检查 required
209
+ if (s.required !== undefined) {
210
+ if (!Array.isArray(s.required)) {
211
+ return createMalformedAdvice("Required keyword must be an array of strings");
212
+ }
213
+ const seen = new Set();
214
+ for (const item of s.required) {
215
+ if (typeof item !== "string") {
216
+ return createMalformedAdvice("Every item in required array must be a string");
217
+ }
218
+ if (seen.has(item)) {
219
+ return createMalformedAdvice(`Duplicate required property: '${item}'`);
220
+ }
221
+ seen.add(item);
222
+ }
223
+ }
224
+ // 检查 additionalProperties
225
+ if (s.additionalProperties !== undefined) {
226
+ if (typeof s.additionalProperties !== "boolean" &&
227
+ !isPlainObject(s.additionalProperties)) {
228
+ return createMalformedAdvice("AdditionalProperties keyword must be a boolean or plain object");
229
+ }
230
+ }
231
+ // 检查 items
232
+ let isTupleItems = false;
233
+ if (s.items !== undefined) {
234
+ if (typeof s.items === "boolean" || isPlainObject(s.items)) {
235
+ // 合法
236
+ }
237
+ else if (Array.isArray(s.items)) {
238
+ isTupleItems = true;
239
+ }
240
+ else {
241
+ return createMalformedAdvice("Items keyword must be boolean, plain object, or array");
242
+ }
243
+ }
244
+ // 检查 enum
245
+ if (s.enum !== undefined) {
246
+ if (!Array.isArray(s.enum) || s.enum.length === 0) {
247
+ return createMalformedAdvice("Enum keyword must be a non-empty array");
248
+ }
249
+ }
250
+ // 检查 const
251
+ if (s.const !== undefined) {
252
+ if (!isJsonValueLike(s.const)) {
253
+ return createMalformedAdvice("Const keyword must be a valid JSON value literal");
254
+ }
255
+ }
256
+ // 检查 applicators
257
+ if (s.oneOf !== undefined && !Array.isArray(s.oneOf)) {
258
+ return createMalformedAdvice("OneOf keyword must be an array");
259
+ }
260
+ if (s.anyOf !== undefined && !Array.isArray(s.anyOf)) {
261
+ return createMalformedAdvice("AnyOf keyword must be an array");
262
+ }
263
+ if (s.allOf !== undefined && !Array.isArray(s.allOf)) {
264
+ return createMalformedAdvice("AllOf keyword must be an array");
265
+ }
266
+ if (s.$ref !== undefined && typeof s.$ref !== "string") {
267
+ return createMalformedAdvice("$ref keyword must be a string");
268
+ }
269
+ if (s.not !== undefined && typeof s.not !== "boolean" && !isPlainObject(s.not)) {
270
+ return createMalformedAdvice("Not keyword must be a boolean or plain object");
271
+ }
272
+ if (s.if !== undefined && typeof s.if !== "boolean" && !isPlainObject(s.if)) {
273
+ return createMalformedAdvice("If keyword must be a boolean or plain object");
274
+ }
275
+ if (s.then !== undefined && typeof s.then !== "boolean" && !isPlainObject(s.then)) {
276
+ return createMalformedAdvice("Then keyword must be a boolean or plain object");
277
+ }
278
+ if (s.else !== undefined && typeof s.else !== "boolean" && !isPlainObject(s.else)) {
279
+ return createMalformedAdvice("Else keyword must be a boolean or plain object");
280
+ }
281
+ // 2. 语义分类阶段(Section 45)
282
+ if (Object.keys(s).length === 0) {
283
+ return {
284
+ version: 1,
285
+ analysisMode: "declared-properties-only",
286
+ analysisStatus: "ok",
287
+ schemaState: "any",
288
+ inputFeasibility: "possible-or-unknown",
289
+ flatAvailable: false,
290
+ requiredSatisfiable: null,
291
+ flatCandidate: false,
292
+ schemaRecommendedMode: "full-json",
293
+ fields: [],
294
+ };
295
+ }
296
+ const hasComplexKeyword = isTupleItems ||
297
+ COMPLEX_KEYWORDS.some((kw) => s[kw] !== undefined);
298
+ let schemaState;
299
+ if (hasComplexKeyword) {
300
+ schemaState = "complex";
301
+ }
302
+ else if (s.type === "object") {
303
+ schemaState = "object";
304
+ }
305
+ else if (typeof s.type === "string" && s.type !== "object") {
306
+ schemaState = "json-only";
307
+ }
308
+ else if (Array.isArray(s.type)) {
309
+ schemaState = "complex";
310
+ }
311
+ else if (s.type === undefined &&
312
+ (s.properties !== undefined || s.required !== undefined || s.additionalProperties !== undefined)) {
313
+ schemaState = "complex";
314
+ }
315
+ else {
316
+ schemaState = "complex";
317
+ }
318
+ const analysisStatus = schemaState === "complex" ? "unsupported" : "ok";
319
+ const analysisCode = schemaState === "complex"
320
+ ? isTupleItems
321
+ ? "UNSUPPORTED_SCHEMA_SHAPE"
322
+ : "COMPLEX_SCHEMA"
323
+ : undefined;
324
+ const analysisMessage = schemaState === "complex"
325
+ ? "Complex schema structure is not supported for flat encoding"
326
+ : undefined;
327
+ // 3. 输入可行性判定(Section 47)
328
+ const requiredList = Array.isArray(s.required) ? s.required : [];
329
+ const properties = isPlainObject(s.properties) ? s.properties : {};
330
+ let inputFeasibility = "possible-or-unknown";
331
+ let feasibilityCode;
332
+ if (requiredList.some(isForbiddenActionInputPropertyName)) {
333
+ inputFeasibility = "known-impossible";
334
+ feasibilityCode = "REQUIRED_FIELD_FORBIDDEN";
335
+ }
336
+ else if (requiredList.some((k) => properties[k] === false)) {
337
+ inputFeasibility = "known-impossible";
338
+ feasibilityCode = "REQUIRED_FIELD_REJECTS_ALL";
339
+ }
340
+ else if (s.additionalProperties === false &&
341
+ requiredList.some((k) => !(k in properties))) {
342
+ inputFeasibility = "known-impossible";
343
+ feasibilityCode = "REQUIRED_FIELD_DISALLOWED_BY_ADDITIONAL_PROPERTIES";
344
+ }
345
+ // 4. 字段建议生成(Section 50)
346
+ const fields = [];
347
+ for (const [key, propSchema] of Object.entries(properties)) {
348
+ const isReq = requiredList.includes(key);
349
+ if (isForbiddenActionInputPropertyName(key)) {
350
+ fields.push({
351
+ path: key,
352
+ inputAllowed: false,
353
+ flatSafe: false,
354
+ required: isReq,
355
+ reason: "FORBIDDEN_PROPERTY",
356
+ fallback: null,
357
+ ...(isPlainObject(propSchema) && propSchema.description
358
+ ? { description: propSchema.description }
359
+ : {}),
360
+ hint: "Globally forbidden property",
361
+ });
362
+ continue;
363
+ }
364
+ if (propSchema === false) {
365
+ fields.push({
366
+ path: key,
367
+ inputAllowed: false,
368
+ flatSafe: false,
369
+ required: isReq,
370
+ reason: "PROPERTY_SCHEMA_FALSE",
371
+ fallback: null,
372
+ hint: "Property schema rejects all values",
373
+ });
374
+ continue;
375
+ }
376
+ if (!isFlatPathPropertyName(key)) {
377
+ const typeStr = isPlainObject(propSchema) && propSchema.type
378
+ ? String(propSchema.type)
379
+ : "any";
380
+ fields.push({
381
+ path: key,
382
+ inputAllowed: true,
383
+ flatSafe: false,
384
+ required: isReq,
385
+ type: typeStr,
386
+ reason: "UNSAFE_FLAT_PROPERTY",
387
+ fallback: "stdin-json",
388
+ ...(isPlainObject(propSchema) && propSchema.description
389
+ ? { description: propSchema.description }
390
+ : {}),
391
+ hint: "Property name contains unsafe characters for flat encoding",
392
+ });
393
+ continue;
394
+ }
395
+ // Flat-safe property
396
+ if (propSchema === true) {
397
+ fields.push({
398
+ path: key,
399
+ inputAllowed: true,
400
+ flatSafe: true,
401
+ required: isReq,
402
+ type: "any",
403
+ operator: ":=",
404
+ encoding: "json",
405
+ assignmentTemplate: `${key}:=JSON`,
406
+ });
407
+ continue;
408
+ }
409
+ const propObj = propSchema;
410
+ const propType = propObj.type;
411
+ const desc = propObj.description;
412
+ // Section 46: enum/const 规则:
413
+ // enum/const 绝不改变 operator 选择,仅显式单一 schema.type 决定 operator。
414
+ // 无显式 type 时不推断类型,推荐 full-json,flatSafe: false。
415
+ if (typeof propType === "string") {
416
+ if (propType === "string") {
417
+ fields.push({
418
+ path: key,
419
+ inputAllowed: true,
420
+ flatSafe: true,
421
+ required: isReq,
422
+ type: "string",
423
+ operator: "=",
424
+ encoding: "string",
425
+ assignmentTemplate: `${key}=TEXT`,
426
+ ...(desc ? { description: desc } : {}),
427
+ });
428
+ }
429
+ else if (propType === "number" || propType === "integer") {
430
+ fields.push({
431
+ path: key,
432
+ inputAllowed: true,
433
+ flatSafe: true,
434
+ required: isReq,
435
+ type: propType,
436
+ operator: ":=",
437
+ encoding: "json-number",
438
+ assignmentTemplate: `${key}:=NUMBER`,
439
+ ...(desc ? { description: desc } : {}),
440
+ });
441
+ }
442
+ else if (propType === "boolean") {
443
+ fields.push({
444
+ path: key,
445
+ inputAllowed: true,
446
+ flatSafe: true,
447
+ required: isReq,
448
+ type: "boolean",
449
+ operator: ":=",
450
+ encoding: "json-boolean",
451
+ assignmentTemplate: `${key}:=BOOLEAN`,
452
+ ...(desc ? { description: desc } : {}),
453
+ });
454
+ }
455
+ else if (propType === "null") {
456
+ fields.push({
457
+ path: key,
458
+ inputAllowed: true,
459
+ flatSafe: true,
460
+ required: isReq,
461
+ type: "null",
462
+ operator: ":=",
463
+ encoding: "json-null",
464
+ assignmentTemplate: `${key}:=null`,
465
+ ...(desc ? { description: desc } : {}),
466
+ });
467
+ }
468
+ else if (propType === "array") {
469
+ fields.push({
470
+ path: key,
471
+ inputAllowed: true,
472
+ flatSafe: true,
473
+ required: isReq,
474
+ type: "array",
475
+ operator: ":=",
476
+ encoding: "json-array",
477
+ assignmentTemplate: `${key}:=JSON`,
478
+ hint: "建议使用 --input-file 或标准输入传递数组结构",
479
+ ...(desc ? { description: desc } : {}),
480
+ });
481
+ }
482
+ else if (propType === "object") {
483
+ fields.push({
484
+ path: key,
485
+ inputAllowed: true,
486
+ flatSafe: true,
487
+ required: isReq,
488
+ type: "object",
489
+ operator: ":=",
490
+ encoding: "json-object",
491
+ assignmentTemplate: `${key}:=JSON`,
492
+ hint: "大型结构建议使用 --input-file",
493
+ ...(desc ? { description: desc } : {}),
494
+ });
495
+ }
496
+ else {
497
+ fields.push({
498
+ path: key,
499
+ inputAllowed: true,
500
+ flatSafe: false,
501
+ required: isReq,
502
+ type: propType,
503
+ reason: "UNSAFE_FLAT_PROPERTY",
504
+ fallback: "stdin-json",
505
+ ...(desc ? { description: desc } : {}),
506
+ });
507
+ }
508
+ }
509
+ else {
510
+ // 无显式单一类型(如 union type 或无 type 的 enum/const)
511
+ fields.push({
512
+ path: key,
513
+ inputAllowed: true,
514
+ flatSafe: false,
515
+ required: isReq,
516
+ type: Array.isArray(propType) ? propType.join(" | ") : "any",
517
+ reason: "UNSAFE_FLAT_PROPERTY",
518
+ fallback: "stdin-json",
519
+ ...(desc ? { description: desc } : {}),
520
+ });
521
+ }
522
+ }
523
+ // 5. requiredSatisfiable 计算(Section 48)
524
+ let requiredSatisfiable = null;
525
+ if (schemaState === "json-only") {
526
+ requiredSatisfiable = false;
527
+ }
528
+ else if (schemaState === "complex") {
529
+ requiredSatisfiable = null;
530
+ }
531
+ else if (schemaState === "object") {
532
+ if (requiredList.length === 0) {
533
+ requiredSatisfiable = true;
534
+ }
535
+ else {
536
+ let hasNull = false;
537
+ let allSafe = true;
538
+ for (const reqKey of requiredList) {
539
+ if (isForbiddenActionInputPropertyName(reqKey)) {
540
+ requiredSatisfiable = false;
541
+ allSafe = false;
542
+ break;
543
+ }
544
+ const field = fields.find((f) => f.path === reqKey);
545
+ if (!field) {
546
+ if (s.additionalProperties === false) {
547
+ requiredSatisfiable = false;
548
+ allSafe = false;
549
+ break;
550
+ }
551
+ else {
552
+ hasNull = true;
553
+ continue;
554
+ }
555
+ }
556
+ if (!field.inputAllowed ||
557
+ field.flatSafe === false ||
558
+ !field.operator ||
559
+ !field.encoding) {
560
+ requiredSatisfiable = false;
561
+ allSafe = false;
562
+ break;
563
+ }
564
+ }
565
+ if (allSafe) {
566
+ requiredSatisfiable = hasNull ? null : true;
567
+ }
568
+ }
569
+ }
570
+ // 6. flatAvailable, flatCandidate, schemaRecommendedMode(Section 49)
571
+ const flatAvailable = fields.some((f) => f.flatSafe && f.operator !== undefined);
572
+ const flatCandidate = schemaState === "object" && requiredSatisfiable === true && flatAvailable;
573
+ let schemaRecommendedMode;
574
+ if (inputFeasibility === "known-impossible") {
575
+ schemaRecommendedMode = "none";
576
+ }
577
+ else if (schemaState === "json-only" || schemaState === "complex") {
578
+ schemaRecommendedMode = "full-json";
579
+ }
580
+ else if (schemaState === "object" && flatCandidate) {
581
+ schemaRecommendedMode = "flat";
582
+ }
583
+ else {
584
+ schemaRecommendedMode = "full-json";
585
+ }
586
+ return {
587
+ version: 1,
588
+ analysisMode: "declared-properties-only",
589
+ analysisStatus,
590
+ ...(analysisCode ? { analysisCode } : {}),
591
+ ...(analysisMessage ? { analysisMessage } : {}),
592
+ schemaState,
593
+ inputFeasibility,
594
+ flatAvailable,
595
+ requiredSatisfiable,
596
+ flatCandidate,
597
+ schemaRecommendedMode,
598
+ ...(feasibilityCode ? { feasibilityCode } : {}),
599
+ fields,
600
+ };
601
+ }
602
+ export const FLAT_SAFE_KEY_REGEX = /^[A-Za-z_][A-Za-z0-9_-]*$/;
603
+ export const FLAT_ENCODING_GUIDELINES = [
604
+ "字符串: path=TEXT",
605
+ "JSON 标量与结构: path:=JSON (例如 count:=1, enabled:=true)",
606
+ "数组元素: path.INDEX=... (例如 items.0=first)",
607
+ "提示: 复杂嵌套或大段文本建议使用 --input 或 --input-file",
608
+ ];
609
+ /**
610
+ * 依据 inputSchema 构造统一的 Action 输入编码建议。
611
+ *
612
+ * @param schema Action 的 inputSchema 定义
613
+ * @returns 结构化编码建议报告
614
+ */
615
+ export function buildActionInputAdvice(schema) {
616
+ if (typeof schema === "boolean") {
617
+ if (schema === false) {
618
+ return {
619
+ flatSupported: false,
620
+ hasFlatFields: false,
621
+ fields: [],
622
+ requiredTemplates: [],
623
+ optionalTemplates: [],
624
+ notes: ["布尔模式 false:拒绝所有输入,任何调用参数均判定为非法"],
625
+ };
626
+ }
627
+ return {
628
+ flatSupported: false,
629
+ hasFlatFields: false,
630
+ fields: [],
631
+ requiredTemplates: [],
632
+ optionalTemplates: [],
633
+ notes: [
634
+ "布尔模式 true:接受任意合法 JSON 输入;调用时无需指定必填参数,非对象根输入请使用 --input-file 或 --input",
635
+ ],
636
+ };
637
+ }
638
+ if (!schema || typeof schema !== "object") {
639
+ return {
640
+ flatSupported: false,
641
+ hasFlatFields: false,
642
+ fields: [],
643
+ requiredTemplates: [],
644
+ optionalTemplates: [],
645
+ notes: ["根模式未声明输入字段,直接调用或使用 --input-file / --input '{}'"],
646
+ };
647
+ }
648
+ const s = schema;
649
+ // 校验根模式是否为 object 类型
650
+ if (s.type !== undefined && s.type !== "object") {
651
+ return {
652
+ flatSupported: false,
653
+ hasFlatFields: false,
654
+ fields: [],
655
+ requiredTemplates: [],
656
+ optionalTemplates: [],
657
+ notes: [
658
+ `根模式类型为 '${s.type}',不支持扁平赋值,建议使用 --input-file 或 --input`,
659
+ ],
660
+ };
661
+ }
662
+ const properties = (s.properties || {});
663
+ const required = Array.isArray(s.required) ? s.required : [];
664
+ const propKeys = Object.keys(properties);
665
+ if (propKeys.length === 0) {
666
+ return {
667
+ flatSupported: false,
668
+ hasFlatFields: false,
669
+ fields: [],
670
+ requiredTemplates: [],
671
+ optionalTemplates: [],
672
+ notes: ["根模式无声明属性,不支持扁平赋值,建议使用 --input-file 或 --input '{}'"],
673
+ };
674
+ }
675
+ const fields = [];
676
+ const requiredTemplates = [];
677
+ const optionalTemplates = [];
678
+ const notes = [];
679
+ for (const key of propKeys) {
680
+ const prop = properties[key] || {};
681
+ const typeStr = prop.type ? String(prop.type) : "any";
682
+ const isReq = required.includes(key);
683
+ const isFlatSafe = FLAT_SAFE_KEY_REGEX.test(key);
684
+ let assignmentTemplate;
685
+ let hint;
686
+ if (isFlatSafe) {
687
+ if (prop.type === "string") {
688
+ assignmentTemplate = `${key}=TEXT`;
689
+ }
690
+ else if (prop.type === "number" || prop.type === "integer") {
691
+ assignmentTemplate = `${key}:=NUMBER`;
692
+ }
693
+ else if (prop.type === "boolean") {
694
+ assignmentTemplate = `${key}:=BOOLEAN`;
695
+ }
696
+ else if (prop.type === "array" &&
697
+ prop.items &&
698
+ typeof prop.items === "object" &&
699
+ prop.items.type === "string") {
700
+ assignmentTemplate = `${key}.0=TEXT`;
701
+ }
702
+ else if (prop.type === "array" &&
703
+ prop.items &&
704
+ typeof prop.items === "object" &&
705
+ (prop.items.type === "number" || prop.items.type === "integer")) {
706
+ assignmentTemplate = `${key}.0:=NUMBER`;
707
+ }
708
+ else if (prop.type === "array" &&
709
+ prop.items &&
710
+ typeof prop.items === "object" &&
711
+ prop.items.type === "boolean") {
712
+ assignmentTemplate = `${key}.0:=BOOLEAN`;
713
+ }
714
+ else if (prop.type === "array") {
715
+ assignmentTemplate = `${key}:=JSON`;
716
+ hint = "数组结构建议使用 --input-file";
717
+ }
718
+ else if (prop.type === "object") {
719
+ assignmentTemplate = `${key}:=JSON`;
720
+ hint = "大型结构建议使用 --input-file";
721
+ }
722
+ else {
723
+ assignmentTemplate = `${key}:=JSON`;
724
+ hint = "复杂或未知类型建议使用 --input-file";
725
+ }
726
+ if (isReq) {
727
+ if (assignmentTemplate) {
728
+ requiredTemplates.push(assignmentTemplate);
729
+ }
730
+ }
731
+ else {
732
+ if (assignmentTemplate) {
733
+ optionalTemplates.push(assignmentTemplate);
734
+ }
735
+ }
736
+ }
737
+ else {
738
+ hint = "包含非安全字符,建议使用 --input-file";
739
+ notes.push(`属性 '${key}' 包含非安全字符,不支持扁平赋值,建议使用 --input-file`);
740
+ }
741
+ fields.push({
742
+ path: key,
743
+ type: typeStr,
744
+ required: isReq,
745
+ flatSafe: isFlatSafe,
746
+ assignmentTemplate,
747
+ hint,
748
+ description: prop.description,
749
+ });
750
+ }
751
+ const hasFlatFields = fields.some((f) => f.flatSafe && f.assignmentTemplate !== undefined);
752
+ // 仅当根模式为对象且所有 required 字段均具备 flatSafe 赋值方案时为 true
753
+ const allRequiredFlatSafe = required.every((reqKey) => {
754
+ const f = fields.find((field) => field.path === reqKey);
755
+ return f !== undefined && f.flatSafe && f.assignmentTemplate !== undefined;
756
+ });
757
+ const flatSupported = hasFlatFields && allRequiredFlatSafe;
758
+ return {
759
+ flatSupported,
760
+ hasFlatFields,
761
+ fields,
762
+ requiredTemplates,
763
+ optionalTemplates,
764
+ notes,
765
+ };
766
+ }
767
+ export { formatActionDetail } from "./describe.js";