scene-capability-engine 3.0.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 (336) hide show
  1. package/CHANGELOG.md +2513 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/README.zh.md +630 -0
  5. package/bin/kiro-spec-engine.js +796 -0
  6. package/bin/kse.js +3 -0
  7. package/bin/sce.js +3 -0
  8. package/bin/sco.js +3 -0
  9. package/docs/331-poc-adaptation-roadmap.md +156 -0
  10. package/docs/331-poc-dual-track-integration-guide.md +120 -0
  11. package/docs/331-poc-weekly-delivery-checklist.md +52 -0
  12. package/docs/OFFLINE_INSTALL.md +96 -0
  13. package/docs/README.md +279 -0
  14. package/docs/adopt-migration-guide.md +599 -0
  15. package/docs/adoption-guide.md +616 -0
  16. package/docs/agent-hooks-analysis.md +815 -0
  17. package/docs/architecture.md +733 -0
  18. package/docs/articles/ai-driven-development-philosophy-and-practice-review.md +208 -0
  19. package/docs/articles/ai-driven-development-philosophy-and-practice.en.md +459 -0
  20. package/docs/articles/ai-driven-development-philosophy-and-practice.md +492 -0
  21. package/docs/autonomous-control-guide.md +851 -0
  22. package/docs/command-reference.md +1368 -0
  23. package/docs/community.md +115 -0
  24. package/docs/cross-tool-guide.md +555 -0
  25. package/docs/developer-guide.md +619 -0
  26. package/docs/document-governance.md +865 -0
  27. package/docs/environment-management-guide.md +526 -0
  28. package/docs/examples/add-export-command/design.md +194 -0
  29. package/docs/examples/add-export-command/requirements.md +110 -0
  30. package/docs/examples/add-export-command/tasks.md +88 -0
  31. package/docs/examples/add-rest-api/design.md +855 -0
  32. package/docs/examples/add-rest-api/requirements.md +323 -0
  33. package/docs/examples/add-rest-api/tasks.md +355 -0
  34. package/docs/examples/add-user-dashboard/design.md +192 -0
  35. package/docs/examples/add-user-dashboard/requirements.md +143 -0
  36. package/docs/examples/add-user-dashboard/tasks.md +91 -0
  37. package/docs/faq.md +697 -0
  38. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.json +156 -0
  39. package/docs/handoffs/evidence/ontology/moqui-template-baseline-2026-02-17-232922.md +24 -0
  40. package/docs/images/wechat-qr.png +0 -0
  41. package/docs/integration-modes.md +529 -0
  42. package/docs/integration-philosophy.md +313 -0
  43. package/docs/knowledge-management-guide.md +263 -0
  44. package/docs/manual-workflows-guide.md +418 -0
  45. package/docs/moqui-capability-matrix.md +73 -0
  46. package/docs/moqui-template-core-library-playbook.md +109 -0
  47. package/docs/multi-agent-coordination-guide.md +553 -0
  48. package/docs/multi-repo-management-guide.md +1344 -0
  49. package/docs/quick-start-with-ai-tools.md +375 -0
  50. package/docs/quick-start.md +146 -0
  51. package/docs/release-checklist.md +121 -0
  52. package/docs/releases/README.md +13 -0
  53. package/docs/releases/v1.46.2-validation.md +45 -0
  54. package/docs/releases/v1.46.2.md +50 -0
  55. package/docs/scene-runtime-guide.md +347 -0
  56. package/docs/spec-collaboration-guide.md +369 -0
  57. package/docs/spec-locking-guide.md +225 -0
  58. package/docs/spec-numbering-guide.md +348 -0
  59. package/docs/spec-workflow.md +519 -0
  60. package/docs/steering-strategy-guide.md +196 -0
  61. package/docs/team-collaboration-guide.md +465 -0
  62. package/docs/testing-strategy.md +272 -0
  63. package/docs/tools/claude-guide.md +654 -0
  64. package/docs/tools/cursor-guide.md +706 -0
  65. package/docs/tools/generic-guide.md +446 -0
  66. package/docs/tools/kiro-guide.md +308 -0
  67. package/docs/tools/vscode-guide.md +445 -0
  68. package/docs/tools/windsurf-guide.md +391 -0
  69. package/docs/troubleshooting.md +1135 -0
  70. package/docs/upgrade-guide.md +639 -0
  71. package/docs/value-observability-guide.md +127 -0
  72. package/docs/zh/README.md +341 -0
  73. package/docs/zh/quick-start.md +764 -0
  74. package/docs/zh/release-checklist.md +121 -0
  75. package/docs/zh/releases/README.md +13 -0
  76. package/docs/zh/releases/v1.46.2-validation.md +45 -0
  77. package/docs/zh/releases/v1.46.2.md +50 -0
  78. package/docs/zh/spec-numbering-guide.md +348 -0
  79. package/docs/zh/tools/claude-guide.md +349 -0
  80. package/docs/zh/tools/cursor-guide.md +281 -0
  81. package/docs/zh/tools/generic-guide.md +499 -0
  82. package/docs/zh/tools/kiro-guide.md +342 -0
  83. package/docs/zh/tools/vscode-guide.md +449 -0
  84. package/docs/zh/tools/windsurf-guide.md +378 -0
  85. package/docs/zh/value-observability-guide.md +127 -0
  86. package/docs//344/272/244/344/273/230/346/270/205/345/215/225.md +75 -0
  87. package/lib/adoption/adoption-logger.js +487 -0
  88. package/lib/adoption/adoption-strategy.js +538 -0
  89. package/lib/adoption/backup-manager.js +420 -0
  90. package/lib/adoption/conflict-resolver.js +410 -0
  91. package/lib/adoption/detection-engine.js +275 -0
  92. package/lib/adoption/diff-viewer.js +226 -0
  93. package/lib/adoption/error-formatter.js +509 -0
  94. package/lib/adoption/file-classifier.js +385 -0
  95. package/lib/adoption/progress-reporter.js +534 -0
  96. package/lib/adoption/smart-orchestrator.js +470 -0
  97. package/lib/adoption/strategy-selector.js +218 -0
  98. package/lib/adoption/summary-generator.js +493 -0
  99. package/lib/adoption/template-sync.js +605 -0
  100. package/lib/auto/autonomous-engine.js +485 -0
  101. package/lib/auto/checkpoint-manager.js +300 -0
  102. package/lib/auto/close-loop-runner.js +2476 -0
  103. package/lib/auto/config-schema.js +176 -0
  104. package/lib/auto/decision-engine.js +344 -0
  105. package/lib/auto/error-recovery-manager.js +580 -0
  106. package/lib/auto/goal-decomposer.js +278 -0
  107. package/lib/auto/progress-tracker.js +502 -0
  108. package/lib/auto/safety-manager.js +186 -0
  109. package/lib/auto/semantic-decomposer.js +137 -0
  110. package/lib/auto/state-manager.js +126 -0
  111. package/lib/auto/task-queue-manager.js +340 -0
  112. package/lib/backup/backup-system.js +372 -0
  113. package/lib/backup/selective-backup.js +207 -0
  114. package/lib/collab/agent-registry.js +240 -0
  115. package/lib/collab/collab-manager.js +285 -0
  116. package/lib/collab/contract-manager.js +320 -0
  117. package/lib/collab/coordinator.js +370 -0
  118. package/lib/collab/dependency-manager.js +280 -0
  119. package/lib/collab/index.js +20 -0
  120. package/lib/collab/integration-manager.js +202 -0
  121. package/lib/collab/merge-coordinator.js +252 -0
  122. package/lib/collab/metadata-manager.js +233 -0
  123. package/lib/collab/multi-agent-config.js +120 -0
  124. package/lib/collab/spec-lifecycle-manager.js +304 -0
  125. package/lib/collab/sync-barrier.js +88 -0
  126. package/lib/collab/visualizer.js +208 -0
  127. package/lib/commands/adopt.js +749 -0
  128. package/lib/commands/auto.js +19559 -0
  129. package/lib/commands/collab.js +275 -0
  130. package/lib/commands/context.js +99 -0
  131. package/lib/commands/docs.js +808 -0
  132. package/lib/commands/doctor.js +273 -0
  133. package/lib/commands/env.js +420 -0
  134. package/lib/commands/knowledge.js +309 -0
  135. package/lib/commands/lock.js +235 -0
  136. package/lib/commands/ops.js +409 -0
  137. package/lib/commands/orchestrate.js +446 -0
  138. package/lib/commands/prompt.js +105 -0
  139. package/lib/commands/repo.js +118 -0
  140. package/lib/commands/rollback.js +219 -0
  141. package/lib/commands/scene.js +15549 -0
  142. package/lib/commands/spec-bootstrap.js +147 -0
  143. package/lib/commands/spec-gate.js +157 -0
  144. package/lib/commands/spec-pipeline.js +205 -0
  145. package/lib/commands/status.js +321 -0
  146. package/lib/commands/task.js +199 -0
  147. package/lib/commands/templates.js +654 -0
  148. package/lib/commands/upgrade.js +231 -0
  149. package/lib/commands/value.js +569 -0
  150. package/lib/commands/watch.js +684 -0
  151. package/lib/commands/workflows.js +240 -0
  152. package/lib/commands/workspace-multi.js +325 -0
  153. package/lib/commands/workspace.js +189 -0
  154. package/lib/context/context-exporter.js +378 -0
  155. package/lib/context/prompt-generator.js +482 -0
  156. package/lib/data/moqui-capability-lexicon.json +45 -0
  157. package/lib/environment/backup-system.js +189 -0
  158. package/lib/environment/environment-manager.js +379 -0
  159. package/lib/environment/environment-registry.js +168 -0
  160. package/lib/gitignore/gitignore-backup.js +229 -0
  161. package/lib/gitignore/gitignore-detector.js +239 -0
  162. package/lib/gitignore/gitignore-integration.js +267 -0
  163. package/lib/gitignore/gitignore-transformer.js +193 -0
  164. package/lib/gitignore/layered-rules-template.js +42 -0
  165. package/lib/governance/archive-tool.js +284 -0
  166. package/lib/governance/cleanup-tool.js +237 -0
  167. package/lib/governance/config-manager.js +186 -0
  168. package/lib/governance/diagnostic-engine.js +271 -0
  169. package/lib/governance/doc-reference-checker.js +200 -0
  170. package/lib/governance/execution-logger.js +243 -0
  171. package/lib/governance/file-scanner.js +285 -0
  172. package/lib/governance/hooks-manager.js +333 -0
  173. package/lib/governance/reporter.js +337 -0
  174. package/lib/governance/validation-engine.js +181 -0
  175. package/lib/i18n.js +79 -0
  176. package/lib/knowledge/entry-manager.js +208 -0
  177. package/lib/knowledge/index-manager.js +261 -0
  178. package/lib/knowledge/knowledge-manager.js +273 -0
  179. package/lib/knowledge/template-manager.js +191 -0
  180. package/lib/lock/index.js +21 -0
  181. package/lib/lock/lock-file.js +192 -0
  182. package/lib/lock/lock-manager.js +321 -0
  183. package/lib/lock/machine-identifier.js +135 -0
  184. package/lib/lock/steering-file-lock.js +207 -0
  185. package/lib/lock/task-lock-manager.js +345 -0
  186. package/lib/operations/audit-logger.js +293 -0
  187. package/lib/operations/feedback-manager.js +1147 -0
  188. package/lib/operations/index.js +23 -0
  189. package/lib/operations/models/index.js +170 -0
  190. package/lib/operations/operations-manager.js +151 -0
  191. package/lib/operations/operations-validator.js +280 -0
  192. package/lib/operations/permission-manager.js +354 -0
  193. package/lib/operations/template-loader.js +143 -0
  194. package/lib/orchestrator/agent-spawner.js +629 -0
  195. package/lib/orchestrator/bootstrap-prompt-builder.js +236 -0
  196. package/lib/orchestrator/index.js +19 -0
  197. package/lib/orchestrator/orchestration-engine.js +1270 -0
  198. package/lib/orchestrator/orchestrator-config.js +173 -0
  199. package/lib/orchestrator/status-monitor.js +591 -0
  200. package/lib/python-checker.js +209 -0
  201. package/lib/repo/config-manager.js +580 -0
  202. package/lib/repo/errors/config-error.js +13 -0
  203. package/lib/repo/errors/git-error.js +15 -0
  204. package/lib/repo/errors/repo-error.js +14 -0
  205. package/lib/repo/git-operations.js +181 -0
  206. package/lib/repo/handlers/.gitkeep +1 -0
  207. package/lib/repo/handlers/exec-handler.js +155 -0
  208. package/lib/repo/handlers/health-handler.js +169 -0
  209. package/lib/repo/handlers/init-handler.js +197 -0
  210. package/lib/repo/handlers/status-handler.js +176 -0
  211. package/lib/repo/output-formatter.js +184 -0
  212. package/lib/repo/path-resolver.js +178 -0
  213. package/lib/repo/repo-manager.js +514 -0
  214. package/lib/scene-runtime/audit-emitter.js +59 -0
  215. package/lib/scene-runtime/binding-plugin-loader.js +351 -0
  216. package/lib/scene-runtime/binding-registry.js +349 -0
  217. package/lib/scene-runtime/eval-bridge.js +44 -0
  218. package/lib/scene-runtime/index.js +19 -0
  219. package/lib/scene-runtime/moqui-adapter.js +620 -0
  220. package/lib/scene-runtime/moqui-client.js +606 -0
  221. package/lib/scene-runtime/moqui-extractor.js +2029 -0
  222. package/lib/scene-runtime/plan-compiler.js +208 -0
  223. package/lib/scene-runtime/policy-gate.js +58 -0
  224. package/lib/scene-runtime/runtime-executor.js +358 -0
  225. package/lib/scene-runtime/scene-loader.js +96 -0
  226. package/lib/scene-runtime/scene-ontology.js +959 -0
  227. package/lib/scene-runtime/scene-template-linter.js +852 -0
  228. package/lib/scene-runtime/templates/scene-template-erp-query-v0.1.yaml +28 -0
  229. package/lib/scene-runtime/templates/scene-template-hybrid-shadow-v0.1.yaml +34 -0
  230. package/lib/spec/bootstrap/context-collector.js +48 -0
  231. package/lib/spec/bootstrap/draft-generator.js +158 -0
  232. package/lib/spec/bootstrap/questionnaire-engine.js +70 -0
  233. package/lib/spec/bootstrap/trace-emitter.js +59 -0
  234. package/lib/spec/multi-spec-orchestrate.js +93 -0
  235. package/lib/spec/pipeline/constants.js +6 -0
  236. package/lib/spec/pipeline/stage-adapters.js +118 -0
  237. package/lib/spec/pipeline/stage-runner.js +146 -0
  238. package/lib/spec/pipeline/state-store.js +119 -0
  239. package/lib/spec-gate/engine/gate-engine.js +165 -0
  240. package/lib/spec-gate/policy/default-policy.js +22 -0
  241. package/lib/spec-gate/policy/policy-loader.js +103 -0
  242. package/lib/spec-gate/result-emitter.js +81 -0
  243. package/lib/spec-gate/rules/default-rules.js +156 -0
  244. package/lib/spec-gate/rules/rule-registry.js +51 -0
  245. package/lib/steering/adoption-config.js +164 -0
  246. package/lib/steering/compliance-auto-fixer.js +204 -0
  247. package/lib/steering/compliance-cache.js +99 -0
  248. package/lib/steering/compliance-error-reporter.js +70 -0
  249. package/lib/steering/context-sync-manager.js +273 -0
  250. package/lib/steering/index.js +92 -0
  251. package/lib/steering/spec-steering.js +230 -0
  252. package/lib/steering/steering-compliance-checker.js +73 -0
  253. package/lib/steering/steering-loader.js +144 -0
  254. package/lib/steering/steering-manager.js +289 -0
  255. package/lib/task/index.js +12 -0
  256. package/lib/task/task-claimer.js +489 -0
  257. package/lib/task/task-status-store.js +418 -0
  258. package/lib/templates/cache-manager.js +440 -0
  259. package/lib/templates/content-generalizer.js +247 -0
  260. package/lib/templates/frontmatter-generator.js +128 -0
  261. package/lib/templates/git-handler.js +471 -0
  262. package/lib/templates/metadata-collector.js +328 -0
  263. package/lib/templates/path-utils.js +144 -0
  264. package/lib/templates/registry-parser.js +505 -0
  265. package/lib/templates/spec-reader.js +216 -0
  266. package/lib/templates/template-applicator.js +249 -0
  267. package/lib/templates/template-creator.js +256 -0
  268. package/lib/templates/template-error.js +143 -0
  269. package/lib/templates/template-exporter.js +502 -0
  270. package/lib/templates/template-manager.js +782 -0
  271. package/lib/templates/template-validator.js +361 -0
  272. package/lib/upgrade/migration-engine.js +382 -0
  273. package/lib/upgrade/migrations/.gitkeep +52 -0
  274. package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +78 -0
  275. package/lib/utils/file-diff.js +177 -0
  276. package/lib/utils/fs-utils.js +274 -0
  277. package/lib/utils/tool-detector.js +383 -0
  278. package/lib/utils/validation.js +324 -0
  279. package/lib/value/gate-summary-emitter.js +99 -0
  280. package/lib/value/metric-contract-loader.js +210 -0
  281. package/lib/value/risk-evaluator.js +117 -0
  282. package/lib/value/weekly-snapshot-builder.js +61 -0
  283. package/lib/version/version-checker.js +156 -0
  284. package/lib/version/version-manager.js +327 -0
  285. package/lib/watch/action-executor.js +458 -0
  286. package/lib/watch/event-debouncer.js +323 -0
  287. package/lib/watch/execution-logger.js +550 -0
  288. package/lib/watch/file-watcher.js +499 -0
  289. package/lib/watch/presets.js +266 -0
  290. package/lib/watch/watch-manager.js +533 -0
  291. package/lib/workspace/multi/global-config.js +150 -0
  292. package/lib/workspace/multi/index.js +22 -0
  293. package/lib/workspace/multi/path-utils.js +173 -0
  294. package/lib/workspace/multi/workspace-context-resolver.js +244 -0
  295. package/lib/workspace/multi/workspace-registry.js +196 -0
  296. package/lib/workspace/multi/workspace-state-manager.js +537 -0
  297. package/lib/workspace/multi/workspace.js +90 -0
  298. package/lib/workspace/workspace-manager.js +370 -0
  299. package/lib/workspace/workspace-sync.js +356 -0
  300. package/locales/en.json +114 -0
  301. package/locales/zh.json +114 -0
  302. package/package.json +102 -0
  303. package/template/.kiro/README.md +247 -0
  304. package/template/.kiro/hooks/check-spec-on-create.kiro.hook +17 -0
  305. package/template/.kiro/hooks/run-tests-on-save.kiro.hook +13 -0
  306. package/template/.kiro/hooks/sync-tasks-on-edit.kiro.hook +16 -0
  307. package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
  308. package/template/.kiro/steering/CORE_PRINCIPLES.md +133 -0
  309. package/template/.kiro/steering/CURRENT_CONTEXT.md +30 -0
  310. package/template/.kiro/steering/ENVIRONMENT.md +35 -0
  311. package/template/.kiro/steering/RULES_GUIDE.md +46 -0
  312. package/template/.kiro/templates/operations/default/change-impact.md +112 -0
  313. package/template/.kiro/templates/operations/default/deployment.md +91 -0
  314. package/template/.kiro/templates/operations/default/feedback-response.md +269 -0
  315. package/template/.kiro/templates/operations/default/migration-plan.md +172 -0
  316. package/template/.kiro/templates/operations/default/monitoring.md +135 -0
  317. package/template/.kiro/templates/operations/default/operations.md +135 -0
  318. package/template/.kiro/templates/operations/default/rollback.md +143 -0
  319. package/template/.kiro/templates/operations/default/tools.yaml +364 -0
  320. package/template/.kiro/templates/operations/default/troubleshooting.md +123 -0
  321. package/template/.kiro/tools/backup_manager.py +295 -0
  322. package/template/.kiro/tools/configuration_manager.py +218 -0
  323. package/template/.kiro/tools/document_evaluator.py +550 -0
  324. package/template/.kiro/tools/enhancement_logger.py +168 -0
  325. package/template/.kiro/tools/error_handler.py +335 -0
  326. package/template/.kiro/tools/improvement_identifier.py +444 -0
  327. package/template/.kiro/tools/modification_applicator.py +737 -0
  328. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  329. package/template/.kiro/tools/quality_scorer.py +305 -0
  330. package/template/.kiro/tools/report_generator.py +154 -0
  331. package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
  332. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  333. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  334. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  335. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
  336. package/template/README.md +111 -0
@@ -0,0 +1,44 @@
1
+ class EvalBridge {
2
+ buildPayload({ sceneManifest, plan, runResult }) {
3
+ const metrics = {
4
+ success: runResult.status === 'success',
5
+ cycle_time_ms: runResult.duration_ms,
6
+ manual_takeover_rate: runResult.manual_takeover ? 1 : 0,
7
+ policy_violation_count: runResult.policy && runResult.policy.allowed ? 0 : 1,
8
+ node_failure_count: (runResult.node_results || []).filter((item) => item.status === 'failed').length
9
+ };
10
+
11
+ return {
12
+ trace_id: runResult.trace_id,
13
+ scene_ref: sceneManifest.metadata.obj_id,
14
+ scene_version: sceneManifest.metadata.obj_version,
15
+ plan_id: plan.plan_id,
16
+ status: runResult.status,
17
+ metrics,
18
+ evidence_summary: {
19
+ node_count: plan.nodes.length,
20
+ evidence_count: (runResult.evidence || []).length
21
+ }
22
+ };
23
+ }
24
+
25
+ score(payload, target = {}) {
26
+ let score = 1;
27
+
28
+ if (target.max_cycle_time_ms && payload.metrics.cycle_time_ms > target.max_cycle_time_ms) {
29
+ score -= 0.2;
30
+ }
31
+
32
+ if (payload.metrics.policy_violation_count > 0) {
33
+ score -= 0.5;
34
+ }
35
+
36
+ if (payload.metrics.node_failure_count > 0) {
37
+ score -= 0.3;
38
+ }
39
+
40
+ return Math.max(0, Number(score.toFixed(2)));
41
+ }
42
+ }
43
+
44
+ module.exports = EvalBridge;
@@ -0,0 +1,19 @@
1
+ const SceneLoader = require('./scene-loader');
2
+ const PlanCompiler = require('./plan-compiler');
3
+ const PolicyGate = require('./policy-gate');
4
+ const AuditEmitter = require('./audit-emitter');
5
+ const EvalBridge = require('./eval-bridge');
6
+ const RuntimeExecutor = require('./runtime-executor');
7
+ const BindingRegistry = require('./binding-registry');
8
+ const BindingPluginLoader = require('./binding-plugin-loader');
9
+
10
+ module.exports = {
11
+ SceneLoader,
12
+ PlanCompiler,
13
+ PolicyGate,
14
+ AuditEmitter,
15
+ EvalBridge,
16
+ RuntimeExecutor,
17
+ BindingRegistry,
18
+ BindingPluginLoader
19
+ };
@@ -0,0 +1,620 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const MoquiClient = require('./moqui-client');
6
+
7
+ const DEFAULT_TIMEOUT = 30000;
8
+ const DEFAULT_RETRY_COUNT = 2;
9
+ const DEFAULT_RETRY_DELAY = 1000;
10
+ const DEFAULT_CONFIG_FILENAME = 'moqui-adapter.json';
11
+
12
+ const ENTITY_OPERATIONS = ['list', 'get', 'create', 'update', 'delete'];
13
+ const SERVICE_OPERATIONS = ['invoke', 'async', 'job-status'];
14
+
15
+ function resolveAdapterConfigPath(configPath, projectRoot) {
16
+ if (configPath) {
17
+ return path.resolve(projectRoot || process.cwd(), configPath);
18
+ }
19
+
20
+ return path.resolve(projectRoot || process.cwd(), DEFAULT_CONFIG_FILENAME);
21
+ }
22
+
23
+ function hasAdapterConfigFile(configPath, projectRoot) {
24
+ const resolvedPath = resolveAdapterConfigPath(configPath, projectRoot);
25
+ return fs.pathExistsSync(resolvedPath);
26
+ }
27
+
28
+ /**
29
+ * Load and validate adapter config from file.
30
+ * @param {string} [configPath] - Path to moqui-adapter.json
31
+ * @param {string} [projectRoot] - Project root for relative path resolution
32
+ * @returns {{ config: Object, error?: string }}
33
+ */
34
+ function loadAdapterConfig(configPath, projectRoot) {
35
+ const resolvedPath = resolveAdapterConfigPath(configPath, projectRoot);
36
+
37
+ let rawContent;
38
+
39
+ try {
40
+ rawContent = fs.readFileSync(resolvedPath, 'utf8');
41
+ } catch (err) {
42
+ return {
43
+ config: null,
44
+ error: `CONFIG_NOT_FOUND: Could not read config file at "${resolvedPath}": ${err.message}`
45
+ };
46
+ }
47
+
48
+ let parsed;
49
+
50
+ try {
51
+ parsed = JSON.parse(rawContent);
52
+ } catch (err) {
53
+ return {
54
+ config: null,
55
+ error: `CONFIG_INVALID_JSON: Failed to parse JSON from "${resolvedPath}": ${err.message}`
56
+ };
57
+ }
58
+
59
+ const config = applyConfigDefaults(parsed);
60
+
61
+ return { config };
62
+ }
63
+
64
+ /**
65
+ * Apply default values for optional config fields.
66
+ * @param {Object} config - Raw parsed config
67
+ * @returns {Object} Config with defaults applied
68
+ */
69
+ function applyConfigDefaults(config) {
70
+ if (!config || typeof config !== 'object') {
71
+ return config;
72
+ }
73
+
74
+ return {
75
+ ...config,
76
+ timeout: (config.timeout != null && typeof config.timeout === 'number') ? config.timeout : DEFAULT_TIMEOUT,
77
+ retryCount: (config.retryCount != null && typeof config.retryCount === 'number') ? config.retryCount : DEFAULT_RETRY_COUNT,
78
+ retryDelay: (config.retryDelay != null && typeof config.retryDelay === 'number') ? config.retryDelay : DEFAULT_RETRY_DELAY
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Validate adapter config object.
84
+ * Required: baseUrl, credentials.username, credentials.password
85
+ * @param {Object} config
86
+ * @returns {{ valid: boolean, errors: string[] }}
87
+ */
88
+ function validateAdapterConfig(config) {
89
+ const errors = [];
90
+
91
+ if (!config || typeof config !== 'object') {
92
+ return { valid: false, errors: ['config must be a non-null object'] };
93
+ }
94
+
95
+ if (!config.baseUrl || typeof config.baseUrl !== 'string' || !config.baseUrl.trim()) {
96
+ errors.push('baseUrl is required');
97
+ }
98
+
99
+ if (!config.credentials || typeof config.credentials !== 'object') {
100
+ errors.push('credentials is required');
101
+ errors.push('credentials.username is required');
102
+ errors.push('credentials.password is required');
103
+ } else {
104
+ if (!config.credentials.username || typeof config.credentials.username !== 'string' || !config.credentials.username.trim()) {
105
+ errors.push('credentials.username is required');
106
+ }
107
+
108
+ if (!config.credentials.password || typeof config.credentials.password !== 'string' || !config.credentials.password.trim()) {
109
+ errors.push('credentials.password is required');
110
+ }
111
+ }
112
+
113
+ return {
114
+ valid: errors.length === 0,
115
+ errors
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Parse a binding ref into structured operation descriptor.
121
+ *
122
+ * Supported patterns:
123
+ * 'moqui.{Entity}.{op}' → { entity, operation }
124
+ * 'moqui.service.{Name}.invoke' → { service, operation: 'invoke' }
125
+ * 'moqui.service.{Name}.async' → { service, operation: 'invoke', mode: 'async' }
126
+ * 'moqui.service.{Name}.job-status' → { service, operation: 'job-status' }
127
+ * 'moqui.screen.catalog' → { operation: 'screen-catalog' }
128
+ * 'moqui.screen.{Path}' → { screen, operation: 'screen-definition' }
129
+ * 'spec.erp.{name}' → { service, operation: 'invoke' }
130
+ *
131
+ * @param {string} bindingRef
132
+ * @returns {{ entity?, service?, screen?, operation, mode? } | null}
133
+ */
134
+ function parseBindingRef(bindingRef) {
135
+ if (!bindingRef || typeof bindingRef !== 'string') {
136
+ return null;
137
+ }
138
+
139
+ const ref = bindingRef.trim();
140
+
141
+ if (!ref) {
142
+ return null;
143
+ }
144
+
145
+ // Handle spec.erp.{name} pattern
146
+ if (ref.startsWith('spec.erp.')) {
147
+ const name = ref.slice('spec.erp.'.length);
148
+
149
+ if (!name) {
150
+ return null;
151
+ }
152
+
153
+ return { service: name, operation: 'invoke' };
154
+ }
155
+
156
+ // Handle moqui.* patterns
157
+ if (!ref.startsWith('moqui.')) {
158
+ return null;
159
+ }
160
+
161
+ const parts = ref.slice('moqui.'.length).split('.');
162
+
163
+ if (parts.length < 1 || !parts[0]) {
164
+ return null;
165
+ }
166
+
167
+ // Handle moqui.service.{Name}.{mode} pattern
168
+ if (parts[0] === 'service') {
169
+ return parseServiceRef(parts.slice(1));
170
+ }
171
+
172
+ // Handle moqui.screen.{path} pattern
173
+ if (parts[0] === 'screen') {
174
+ return parseScreenRef(parts.slice(1));
175
+ }
176
+
177
+ // Handle moqui.{Entity}.{op} pattern
178
+ return parseEntityRef(parts);
179
+ }
180
+
181
+ /**
182
+ * Parse service binding ref parts: {Name}.{mode}
183
+ * @param {string[]} parts - Parts after 'moqui.service.'
184
+ * @returns {Object|null}
185
+ */
186
+ function parseServiceRef(parts) {
187
+ if (parts.length < 2 || !parts[0]) {
188
+ return null;
189
+ }
190
+
191
+ const serviceName = parts[0];
192
+ const mode = parts[1];
193
+
194
+ if (mode === 'invoke') {
195
+ return { service: serviceName, operation: 'invoke' };
196
+ }
197
+
198
+ if (mode === 'async') {
199
+ return { service: serviceName, operation: 'invoke', mode: 'async' };
200
+ }
201
+
202
+ if (mode === 'job-status') {
203
+ return { service: serviceName, operation: 'job-status' };
204
+ }
205
+
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Parse screen binding ref parts: {path} or 'catalog'
211
+ * @param {string[]} parts - Parts after 'moqui.screen.'
212
+ * @returns {Object|null}
213
+ */
214
+ function parseScreenRef(parts) {
215
+ if (parts.length < 1 || !parts[0]) {
216
+ return null;
217
+ }
218
+
219
+ if (parts[0] === 'catalog') {
220
+ return { operation: 'screen-catalog' };
221
+ }
222
+
223
+ const screenPath = parts.join('.');
224
+
225
+ return { screen: screenPath, operation: 'screen-definition' };
226
+ }
227
+
228
+ /**
229
+ * Parse entity binding ref parts: {Entity}.{op}
230
+ * @param {string[]} parts - Parts after 'moqui.'
231
+ * @returns {Object|null}
232
+ */
233
+ function parseEntityRef(parts) {
234
+ if (parts.length < 2) {
235
+ return null;
236
+ }
237
+
238
+ const entityName = parts[0];
239
+ const operation = parts[1];
240
+
241
+ if (!ENTITY_OPERATIONS.includes(operation)) {
242
+ return null;
243
+ }
244
+
245
+ return { entity: entityName, operation };
246
+ }
247
+
248
+ /**
249
+ * Map Moqui API response to KSE Execution_Result.
250
+ * @param {Object} moquiResponse - { success, data, meta, error }
251
+ * @param {string} handlerId - Handler identifier (e.g., 'moqui.adapter')
252
+ * @param {string} bindingRef - Original binding ref string
253
+ * @returns {Object} Execution_Result
254
+ */
255
+ function mapMoquiResponseToResult(moquiResponse, handlerId, bindingRef) {
256
+ if (!moquiResponse || typeof moquiResponse !== 'object') {
257
+ return {
258
+ status: 'failed',
259
+ handler_id: handlerId,
260
+ binding_ref: bindingRef,
261
+ error: {
262
+ code: 'INVALID_RESPONSE',
263
+ message: 'Moqui response is null or not an object',
264
+ details: null
265
+ }
266
+ };
267
+ }
268
+
269
+ if (moquiResponse.success) {
270
+ return {
271
+ status: 'success',
272
+ handler_id: handlerId,
273
+ binding_ref: bindingRef,
274
+ data: moquiResponse.data !== undefined ? moquiResponse.data : null,
275
+ meta: moquiResponse.meta !== undefined ? moquiResponse.meta : null
276
+ };
277
+ }
278
+
279
+ const error = moquiResponse.error || {};
280
+
281
+ return {
282
+ status: 'failed',
283
+ handler_id: handlerId,
284
+ binding_ref: bindingRef,
285
+ error: {
286
+ code: error.code || 'UNKNOWN_ERROR',
287
+ message: error.message || 'Unknown error occurred',
288
+ details: error.details !== undefined ? error.details : null
289
+ }
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Build HTTP request details (method + path + body/query) from a parsed binding ref descriptor.
295
+ * @param {Object} descriptor - Parsed binding ref descriptor from parseBindingRef
296
+ * @param {Object} [payload] - Execution payload with data, params, id, jobId, etc.
297
+ * @returns {{ method: string, path: string, body?: Object, query?: Object }}
298
+ */
299
+ function buildHttpRequest(descriptor, payload = {}) {
300
+ const { entity, service, screen, operation, mode } = descriptor;
301
+
302
+ switch (operation) {
303
+ case 'list': {
304
+ const query = {};
305
+
306
+ if (payload.pageIndex != null) {
307
+ query.pageIndex = payload.pageIndex;
308
+ }
309
+
310
+ if (payload.pageSize != null) {
311
+ query.pageSize = payload.pageSize;
312
+ }
313
+
314
+ if (payload.filter != null) {
315
+ query.filter = payload.filter;
316
+ }
317
+
318
+ if (payload.sort != null) {
319
+ query.sort = payload.sort;
320
+ }
321
+
322
+ return {
323
+ method: 'GET',
324
+ path: `/api/v1/entities/${entity}`,
325
+ query
326
+ };
327
+ }
328
+
329
+ case 'get': {
330
+ const id = payload.id || (payload.data && payload.data.id);
331
+
332
+ return {
333
+ method: 'GET',
334
+ path: `/api/v1/entities/${entity}/${id}`
335
+ };
336
+ }
337
+
338
+ case 'create': {
339
+ return {
340
+ method: 'POST',
341
+ path: `/api/v1/entities/${entity}`,
342
+ body: payload.data || {}
343
+ };
344
+ }
345
+
346
+ case 'update': {
347
+ const id = payload.id || (payload.data && payload.data.id);
348
+
349
+ return {
350
+ method: 'PUT',
351
+ path: `/api/v1/entities/${entity}/${id}`,
352
+ body: payload.data || {}
353
+ };
354
+ }
355
+
356
+ case 'delete': {
357
+ const id = payload.id || (payload.data && payload.data.id);
358
+
359
+ return {
360
+ method: 'DELETE',
361
+ path: `/api/v1/entities/${entity}/${id}`
362
+ };
363
+ }
364
+
365
+ case 'invoke': {
366
+ const body = payload.params || payload.data || {};
367
+
368
+ if (mode === 'async') {
369
+ body.async = true;
370
+ }
371
+
372
+ return {
373
+ method: 'POST',
374
+ path: `/api/v1/services/${service}`,
375
+ body
376
+ };
377
+ }
378
+
379
+ case 'job-status': {
380
+ const jobId = payload.jobId;
381
+
382
+ return {
383
+ method: 'GET',
384
+ path: `/api/v1/services/${service}/jobs/${jobId}`
385
+ };
386
+ }
387
+
388
+ case 'screen-catalog': {
389
+ return {
390
+ method: 'GET',
391
+ path: '/api/v1/screens'
392
+ };
393
+ }
394
+
395
+ case 'screen-definition': {
396
+ return {
397
+ method: 'GET',
398
+ path: `/api/v1/screens/${screen}`
399
+ };
400
+ }
401
+
402
+ default: {
403
+ return null;
404
+ }
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Create a MoquiAdapter handler object for BindingRegistry.register().
410
+ * @param {Object} [options] - { configPath, projectRoot, client }
411
+ * @param {string} [options.configPath] - Path to moqui-adapter.json
412
+ * @param {string} [options.projectRoot] - Project root for relative path resolution
413
+ * @param {Object} [options.client] - Pre-configured MoquiClient instance (for testing/DI)
414
+ * @returns {Object} handler with { id, match, execute, readiness }
415
+ */
416
+ function createMoquiAdapterHandler(options = {}) {
417
+ const HANDLER_ID = 'moqui.adapter';
418
+ const allowSpecErpFallback = options.allowSpecErpFallback !== false;
419
+ const strictMatch = options.strictMatch === true;
420
+
421
+ let client = options.client || null;
422
+ let configLoaded = false;
423
+ let loadedConfig = null;
424
+
425
+ /**
426
+ * Ensure the MoquiClient is initialized.
427
+ * Loads config and creates client if not already done.
428
+ * @returns {{ client: Object, error?: string }}
429
+ */
430
+ function ensureClient() {
431
+ if (client) {
432
+ return { client };
433
+ }
434
+
435
+ if (!configLoaded) {
436
+ const result = loadAdapterConfig(options.configPath, options.projectRoot);
437
+
438
+ if (result.error) {
439
+ return { client: null, error: result.error };
440
+ }
441
+
442
+ const validation = validateAdapterConfig(result.config);
443
+
444
+ if (!validation.valid) {
445
+ return { client: null, error: `CONFIG_VALIDATION: ${validation.errors.join(', ')}` };
446
+ }
447
+
448
+ loadedConfig = result.config;
449
+ configLoaded = true;
450
+ }
451
+
452
+ client = new MoquiClient(loadedConfig);
453
+
454
+ return { client };
455
+ }
456
+
457
+ function shouldHandleBindingRef(bindingRef) {
458
+ const ref = String(bindingRef || '').trim();
459
+
460
+ if (!ref) {
461
+ return false;
462
+ }
463
+
464
+ if (ref.startsWith('moqui.')) {
465
+ return true;
466
+ }
467
+
468
+ if (!ref.startsWith('spec.erp.')) {
469
+ return false;
470
+ }
471
+
472
+ if (strictMatch || !allowSpecErpFallback) {
473
+ return true;
474
+ }
475
+
476
+ if (client) {
477
+ return true;
478
+ }
479
+
480
+ return hasAdapterConfigFile(options.configPath, options.projectRoot);
481
+ }
482
+
483
+ return {
484
+ id: HANDLER_ID,
485
+
486
+ match: (node = {}) => shouldHandleBindingRef(node.binding_ref || node.ref),
487
+
488
+ /**
489
+ * Execute a binding node against the Moqui REST API.
490
+ * @param {Object} node - Binding node with binding_ref or ref
491
+ * @param {Object} [payload] - Execution payload
492
+ * @returns {Promise<Object>} Execution_Result
493
+ */
494
+ execute: async (node, payload = {}) => {
495
+ const bindingRef = node.binding_ref || node.ref;
496
+
497
+ // Parse the binding ref
498
+ const descriptor = parseBindingRef(bindingRef);
499
+
500
+ if (!descriptor) {
501
+ return {
502
+ status: 'failed',
503
+ handler_id: HANDLER_ID,
504
+ binding_ref: bindingRef,
505
+ error: {
506
+ code: 'INVALID_BINDING_REF',
507
+ message: `Failed to parse binding ref: "${bindingRef}"`,
508
+ details: null
509
+ }
510
+ };
511
+ }
512
+
513
+ // Ensure client is ready
514
+ const clientResult = ensureClient();
515
+
516
+ if (clientResult.error) {
517
+ return {
518
+ status: 'failed',
519
+ handler_id: HANDLER_ID,
520
+ binding_ref: bindingRef,
521
+ error: {
522
+ code: 'CLIENT_INIT_ERROR',
523
+ message: clientResult.error,
524
+ details: null
525
+ }
526
+ };
527
+ }
528
+
529
+ // Build HTTP request from descriptor
530
+ const httpRequest = buildHttpRequest(descriptor, payload);
531
+
532
+ if (!httpRequest) {
533
+ return {
534
+ status: 'failed',
535
+ handler_id: HANDLER_ID,
536
+ binding_ref: bindingRef,
537
+ error: {
538
+ code: 'UNSUPPORTED_OPERATION',
539
+ message: `Unsupported operation: "${descriptor.operation}"`,
540
+ details: null
541
+ }
542
+ };
543
+ }
544
+
545
+ // Execute the request via MoquiClient
546
+ const requestOptions = {};
547
+
548
+ if (httpRequest.body !== undefined) {
549
+ requestOptions.body = httpRequest.body;
550
+ }
551
+
552
+ if (httpRequest.query !== undefined) {
553
+ requestOptions.query = httpRequest.query;
554
+ }
555
+
556
+ const moquiResponse = await clientResult.client.request(
557
+ httpRequest.method,
558
+ httpRequest.path,
559
+ requestOptions
560
+ );
561
+
562
+ // Map Moqui response to KSE Execution_Result
563
+ return mapMoquiResponseToResult(moquiResponse, HANDLER_ID, bindingRef);
564
+ },
565
+
566
+ /**
567
+ * Check readiness by verifying Moqui connectivity and authentication.
568
+ * @param {Object} node - Binding node
569
+ * @param {Object} [payload] - Execution payload
570
+ * @returns {Promise<{ passed: boolean, reason: string }>}
571
+ */
572
+ readiness: async (node, payload = {}) => {
573
+ // Ensure config is loaded and client is created
574
+ const clientResult = ensureClient();
575
+
576
+ if (clientResult.error) {
577
+ return { passed: false, reason: 'moqui-config-error' };
578
+ }
579
+
580
+ // Attempt login to verify connectivity and authentication
581
+ try {
582
+ const loginResult = await clientResult.client.login();
583
+
584
+ if (loginResult.success) {
585
+ return { passed: true, reason: 'moqui-ready' };
586
+ }
587
+
588
+ // Distinguish between network errors and auth errors
589
+ const errorMsg = loginResult.error || '';
590
+
591
+ if (errorMsg.includes('Network error') || errorMsg.includes('ECONNREFUSED') || errorMsg.includes('ENOTFOUND')) {
592
+ return { passed: false, reason: 'moqui-unreachable' };
593
+ }
594
+
595
+ return { passed: false, reason: 'moqui-auth-failed' };
596
+ } catch (error) {
597
+ // Network-level errors indicate unreachable
598
+ return { passed: false, reason: 'moqui-unreachable' };
599
+ }
600
+ }
601
+ };
602
+ }
603
+
604
+ module.exports = {
605
+ loadAdapterConfig,
606
+ validateAdapterConfig,
607
+ parseBindingRef,
608
+ mapMoquiResponseToResult,
609
+ buildHttpRequest,
610
+ createMoquiAdapterHandler,
611
+ resolveAdapterConfigPath,
612
+ hasAdapterConfigFile,
613
+ // Exported for testing
614
+ applyConfigDefaults,
615
+ DEFAULT_TIMEOUT,
616
+ DEFAULT_RETRY_COUNT,
617
+ DEFAULT_RETRY_DELAY,
618
+ DEFAULT_CONFIG_FILENAME,
619
+ ENTITY_OPERATIONS
620
+ };