claude-flow-novice 2.15.3 → 2.15.4

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 (461) hide show
  1. package/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
  2. package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
  3. package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
  4. package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
  5. package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
  6. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
  7. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
  8. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
  9. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
  10. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
  11. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
  12. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
  13. package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
  14. package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
  15. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
  16. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
  17. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
  18. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
  19. package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
  20. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
  21. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
  22. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
  23. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
  24. package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
  25. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
  26. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
  27. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
  28. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
  29. package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
  30. package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
  31. package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
  32. package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
  33. package/.claude/commands/cfn-loop-cli.md +16 -2
  34. package/.claude/commands/switch-api.md +31 -10
  35. package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
  36. package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
  37. package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
  38. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
  39. package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
  40. package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
  41. package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
  42. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
  43. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
  44. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
  45. package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
  46. package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
  47. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
  48. package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
  49. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
  50. package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
  51. package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
  52. package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
  53. package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
  54. package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
  55. package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
  56. package/.claude/skills/cfn-redis-coordination/redis-functions.sh +33 -0
  57. package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
  58. package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
  59. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
  60. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
  61. package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
  62. package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
  63. package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
  64. package/README.md +116 -475
  65. package/claude-assets/agents/cfn-dev-team/README.md +103 -0
  66. package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
  67. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
  68. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
  69. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
  70. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
  71. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
  72. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
  73. package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
  74. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
  75. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
  76. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
  77. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
  78. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
  79. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
  80. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
  81. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
  82. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
  83. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
  84. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
  85. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
  86. package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
  87. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
  88. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
  89. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
  90. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
  91. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
  92. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
  93. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
  94. package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
  95. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
  96. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
  97. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
  98. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
  99. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
  100. package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
  101. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
  102. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
  103. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
  104. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
  105. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
  106. package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
  107. package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
  108. package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
  109. package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
  110. package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
  111. package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
  112. package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
  113. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
  114. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
  115. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
  116. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
  117. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
  118. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
  119. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
  120. package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
  121. package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
  122. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
  123. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
  124. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
  125. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
  126. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
  127. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
  128. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
  129. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
  130. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
  131. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
  132. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
  133. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
  134. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
  135. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
  136. package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
  137. package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
  138. package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
  139. package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
  140. package/claude-assets/commands/cfn-loop-cli.md +16 -2
  141. package/claude-assets/commands/switch-api.md +31 -10
  142. package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
  143. package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
  144. package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
  145. package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
  146. package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
  147. package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
  148. package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
  149. package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
  150. package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
  151. package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
  152. package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
  153. package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
  154. package/claude-assets/skills/bootstrap/database-connection.md +464 -0
  155. package/claude-assets/skills/bootstrap/error-handling.md +580 -0
  156. package/claude-assets/skills/bootstrap/file-operations.md +699 -0
  157. package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
  158. package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
  159. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
  160. package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
  161. package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
  162. package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
  163. package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
  164. package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
  165. package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
  166. package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
  167. package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
  168. package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
  169. package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
  170. package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
  171. package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
  172. package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
  173. package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
  174. package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
  175. package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
  176. package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
  177. package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
  178. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
  179. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
  180. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
  181. package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
  182. package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
  183. package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
  184. package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
  185. package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
  186. package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
  187. package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
  188. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
  189. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
  190. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
  191. package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
  192. package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
  193. package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
  194. package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
  195. package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
  196. package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
  197. package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +33 -0
  198. package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
  199. package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
  200. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
  201. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
  202. package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
  203. package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
  204. package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
  205. package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
  206. package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
  207. package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
  208. package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
  209. package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
  210. package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
  211. package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
  212. package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
  213. package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
  214. package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
  215. package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
  216. package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
  217. package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
  218. package/claude-assets/skills/cfn-utilities/test.sh +317 -0
  219. package/claude-assets/skills/integration/agent-handoff.sh +62 -64
  220. package/claude-assets/skills/json-validation/SKILL.md +431 -0
  221. package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
  222. package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
  223. package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
  224. package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
  225. package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
  226. package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
  227. package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
  228. package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
  229. package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
  230. package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
  231. package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
  232. package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
  233. package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
  234. package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
  235. package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
  236. package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
  237. package/dist/ace/ace-curator.js +10 -2
  238. package/dist/ace/ace-curator.js.map +1 -1
  239. package/dist/ace/ace-generator.js +4 -0
  240. package/dist/ace/ace-generator.js.map +1 -1
  241. package/dist/ace/ace-reflector.js +1 -1
  242. package/dist/ace/ace-reflector.js.map +1 -1
  243. package/dist/ace/context-injection.js +24 -2
  244. package/dist/ace/context-injection.js.map +1 -1
  245. package/dist/agents/agent-loader.js +146 -165
  246. package/dist/agents/agent-loader.js.map +1 -1
  247. package/dist/agents/task-agent-integration.js +1 -1
  248. package/dist/agents/task-agent-integration.js.map +1 -1
  249. package/dist/api/health-endpoints.js +390 -0
  250. package/dist/api/health-endpoints.js.map +1 -0
  251. package/dist/cli/agent-executor.js +4 -1
  252. package/dist/cli/agent-executor.js.map +1 -1
  253. package/dist/cli/agent-prompt-builder.js +89 -1
  254. package/dist/cli/agent-prompt-builder.js.map +1 -1
  255. package/dist/cli/agent-spawn.js +130 -37
  256. package/dist/cli/agent-spawn.js.map +1 -1
  257. package/dist/cli/skill-cache-validator.js +412 -0
  258. package/dist/cli/skill-cache-validator.js.map +1 -0
  259. package/dist/cli/skill-cli.js +991 -0
  260. package/dist/cli/skill-cli.js.map +1 -0
  261. package/dist/cli/skill-execution-logger.js +284 -0
  262. package/dist/cli/skill-execution-logger.js.map +1 -0
  263. package/dist/cli/skill-loader.js +457 -0
  264. package/dist/cli/skill-loader.js.map +1 -0
  265. package/dist/coordination/event-bus.js +2 -2
  266. package/dist/coordination/event-bus.js.map +1 -1
  267. package/dist/coordination/fleet-manager.js +1 -1
  268. package/dist/coordination/fleet-manager.js.map +1 -1
  269. package/dist/coordination/index.js +23 -9
  270. package/dist/coordination/index.js.map +1 -1
  271. package/dist/coordination/types/fleet-manager.types.js.map +1 -1
  272. package/dist/db/migration-manager.js +483 -0
  273. package/dist/db/migration-manager.js.map +1 -0
  274. package/dist/db/skills-query.js +535 -0
  275. package/dist/db/skills-query.js.map +1 -0
  276. package/dist/integration/DatabaseHandoff.js +1 -1
  277. package/dist/integration/DatabaseHandoff.js.map +1 -1
  278. package/dist/jobs/edge-case-analyzer.js +367 -0
  279. package/dist/jobs/edge-case-analyzer.js.map +1 -0
  280. package/dist/jobs/promotion-sla-enforcer.js +288 -0
  281. package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
  282. package/dist/lib/agent-output-parser.js.map +1 -1
  283. package/dist/lib/agent-output-validator.js.map +1 -1
  284. package/dist/lib/agent-workspace.js +281 -0
  285. package/dist/lib/agent-workspace.js.map +1 -0
  286. package/dist/lib/atomic-file-writer.js +377 -0
  287. package/dist/lib/atomic-file-writer.js.map +1 -0
  288. package/dist/lib/backup-manager.js +779 -0
  289. package/dist/lib/backup-manager.js.map +1 -0
  290. package/dist/lib/checkpoint-manager.js +837 -0
  291. package/dist/lib/checkpoint-manager.js.map +1 -0
  292. package/dist/lib/circuit-breaker.js +340 -0
  293. package/dist/lib/circuit-breaker.js.map +1 -0
  294. package/dist/lib/completion-signal-handler.js +243 -0
  295. package/dist/lib/completion-signal-handler.js.map +1 -0
  296. package/dist/lib/config-manager.js +312 -0
  297. package/dist/lib/config-manager.js.map +1 -0
  298. package/dist/lib/config-migrator.js +386 -0
  299. package/dist/lib/config-migrator.js.map +1 -0
  300. package/dist/lib/config-validator.js.map +1 -1
  301. package/dist/lib/correlation-cache.js +311 -0
  302. package/dist/lib/correlation-cache.js.map +1 -0
  303. package/dist/lib/correlation.js +263 -0
  304. package/dist/lib/correlation.js.map +1 -0
  305. package/dist/lib/database-service/connection-pool-manager.js +520 -0
  306. package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
  307. package/dist/lib/database-service/correlation.js +329 -0
  308. package/dist/lib/database-service/correlation.js.map +1 -0
  309. package/dist/lib/database-service/errors.js +120 -0
  310. package/dist/lib/database-service/errors.js.map +1 -0
  311. package/dist/lib/database-service/index.js +168 -0
  312. package/dist/lib/database-service/index.js.map +1 -0
  313. package/dist/lib/database-service/postgres-adapter.js +526 -0
  314. package/dist/lib/database-service/postgres-adapter.js.map +1 -0
  315. package/dist/lib/database-service/redis-adapter.js +360 -0
  316. package/dist/lib/database-service/redis-adapter.js.map +1 -0
  317. package/dist/lib/database-service/sqlite-adapter.js +544 -0
  318. package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
  319. package/dist/lib/database-service/transaction-manager.js +773 -0
  320. package/dist/lib/database-service/transaction-manager.js.map +1 -0
  321. package/dist/lib/database-service/types.js +23 -0
  322. package/dist/lib/database-service/types.js.map +1 -0
  323. package/dist/lib/deadlock-resolver.js +292 -0
  324. package/dist/lib/deadlock-resolver.js.map +1 -0
  325. package/dist/lib/distributed-lock.js +451 -0
  326. package/dist/lib/distributed-lock.js.map +1 -0
  327. package/dist/lib/edge-case-deduplicator.js +227 -0
  328. package/dist/lib/edge-case-deduplicator.js.map +1 -0
  329. package/dist/lib/encryption-manager.js +322 -0
  330. package/dist/lib/encryption-manager.js.map +1 -0
  331. package/dist/lib/error-aggregator.js +234 -0
  332. package/dist/lib/error-aggregator.js.map +1 -0
  333. package/dist/lib/errors.js +287 -0
  334. package/dist/lib/errors.js.map +1 -0
  335. package/dist/lib/file-lock-manager.js +578 -0
  336. package/dist/lib/file-lock-manager.js.map +1 -0
  337. package/dist/lib/file-operations.js +367 -0
  338. package/dist/lib/file-operations.js.map +1 -0
  339. package/dist/lib/idempotent-write.js +237 -0
  340. package/dist/lib/idempotent-write.js.map +1 -0
  341. package/dist/lib/integration-schema-validator.js +522 -0
  342. package/dist/lib/integration-schema-validator.js.map +1 -0
  343. package/dist/lib/lock-health-monitor.js +298 -0
  344. package/dist/lib/lock-health-monitor.js.map +1 -0
  345. package/dist/lib/log-shipper.js +422 -0
  346. package/dist/lib/log-shipper.js.map +1 -0
  347. package/dist/lib/logging.js +146 -0
  348. package/dist/lib/logging.js.map +1 -0
  349. package/dist/lib/message-deduplicator.js +439 -0
  350. package/dist/lib/message-deduplicator.js.map +1 -0
  351. package/dist/lib/multi-system-query.js +604 -0
  352. package/dist/lib/multi-system-query.js.map +1 -0
  353. package/dist/lib/orphan-detector.js +332 -0
  354. package/dist/lib/orphan-detector.js.map +1 -0
  355. package/dist/lib/password-generator.js +166 -0
  356. package/dist/lib/password-generator.js.map +1 -0
  357. package/dist/lib/path-validator.js +429 -0
  358. package/dist/lib/path-validator.js.map +1 -0
  359. package/dist/lib/query-translator.js +905 -0
  360. package/dist/lib/query-translator.js.map +1 -0
  361. package/dist/lib/queue-recovery.js +469 -0
  362. package/dist/lib/queue-recovery.js.map +1 -0
  363. package/dist/lib/redis-queue-manager.js +512 -0
  364. package/dist/lib/redis-queue-manager.js.map +1 -0
  365. package/dist/lib/reflection-archiver.js +272 -0
  366. package/dist/lib/reflection-archiver.js.map +1 -0
  367. package/dist/lib/retry-manager.js +453 -0
  368. package/dist/lib/retry-manager.js.map +1 -0
  369. package/dist/lib/retry.js +262 -0
  370. package/dist/lib/retry.js.map +1 -0
  371. package/dist/lib/schema-transform.js +695 -0
  372. package/dist/lib/schema-transform.js.map +1 -0
  373. package/dist/lib/schema-validator.js +491 -0
  374. package/dist/lib/schema-validator.js.map +1 -0
  375. package/dist/lib/skill-cache.js +297 -0
  376. package/dist/lib/skill-cache.js.map +1 -0
  377. package/dist/lib/skill-content-manager.js +337 -0
  378. package/dist/lib/skill-content-manager.js.map +1 -0
  379. package/dist/lib/skill-frontmatter-parser.js +237 -0
  380. package/dist/lib/skill-frontmatter-parser.js.map +1 -0
  381. package/dist/lib/skill-git-integration.js +275 -0
  382. package/dist/lib/skill-git-integration.js.map +1 -0
  383. package/dist/lib/skill-markdown-validator.js +396 -0
  384. package/dist/lib/skill-markdown-validator.js.map +1 -0
  385. package/dist/lib/skill-output-parser.js +312 -0
  386. package/dist/lib/skill-output-parser.js.map +1 -0
  387. package/dist/lib/unified-query-api.js +467 -0
  388. package/dist/lib/unified-query-api.js.map +1 -0
  389. package/dist/middleware/auth-middleware.js +350 -0
  390. package/dist/middleware/auth-middleware.js.map +1 -0
  391. package/dist/middleware/schema-validation.js +347 -0
  392. package/dist/middleware/schema-validation.js.map +1 -0
  393. package/dist/providers/anthropic-provider.js +1 -1
  394. package/dist/providers/anthropic-provider.js.map +1 -1
  395. package/dist/providers/provider-factory.js +2 -2
  396. package/dist/providers/provider-factory.js.map +1 -1
  397. package/dist/services/edge-case-analyzer.js +321 -0
  398. package/dist/services/edge-case-analyzer.js.map +1 -0
  399. package/dist/services/edge-case-deduplicator.js +266 -0
  400. package/dist/services/edge-case-deduplicator.js.map +1 -0
  401. package/dist/services/edge-case-detector.js +337 -0
  402. package/dist/services/edge-case-detector.js.map +1 -0
  403. package/dist/services/edge-case-tracker.js +547 -0
  404. package/dist/services/edge-case-tracker.js.map +1 -0
  405. package/dist/services/health-check-system.js +586 -0
  406. package/dist/services/health-check-system.js.map +1 -0
  407. package/dist/services/metrics-logger.js +412 -0
  408. package/dist/services/metrics-logger.js.map +1 -0
  409. package/dist/services/patch-generator.js +378 -0
  410. package/dist/services/patch-generator.js.map +1 -0
  411. package/dist/services/patch-validator.js +337 -0
  412. package/dist/services/patch-validator.js.map +1 -0
  413. package/dist/services/performance-monitor.js +811 -0
  414. package/dist/services/performance-monitor.js.map +1 -0
  415. package/dist/services/promotion-pipeline.js +918 -0
  416. package/dist/services/promotion-pipeline.js.map +1 -0
  417. package/dist/services/promotion-validator.js +394 -0
  418. package/dist/services/promotion-validator.js.map +1 -0
  419. package/dist/services/reflection-logger.js +388 -0
  420. package/dist/services/reflection-logger.js.map +1 -0
  421. package/dist/services/skill-deployment.js +472 -0
  422. package/dist/services/skill-deployment.js.map +1 -0
  423. package/dist/services/skill-loader.js +427 -0
  424. package/dist/services/skill-loader.js.map +1 -0
  425. package/dist/services/skill-promotion.js +372 -0
  426. package/dist/services/skill-promotion.js.map +1 -0
  427. package/dist/services/skill-validator.js +454 -0
  428. package/dist/services/skill-validator.js.map +1 -0
  429. package/dist/services/skill-versioning.js +244 -0
  430. package/dist/services/skill-versioning.js.map +1 -0
  431. package/dist/services/workspace-supervisor.js +597 -0
  432. package/dist/services/workspace-supervisor.js.map +1 -0
  433. package/dist/types/edge-case.js +45 -0
  434. package/dist/types/edge-case.js.map +1 -0
  435. package/package.json +201 -177
  436. package/readme/README.md +19 -4
  437. package/scripts/backup-cleanup.sh +627 -0
  438. package/scripts/cleanup-workspaces.sh +412 -0
  439. package/scripts/cleanup-yaml-configs.sh +141 -0
  440. package/scripts/deploy-approved-skills.sh +263 -0
  441. package/scripts/health-check.sh +447 -0
  442. package/scripts/log-aggregator.sh +554 -0
  443. package/scripts/log-monitor.sh +629 -0
  444. package/scripts/manage-agent-workspaces.sh +434 -0
  445. package/scripts/migrate-schema.sh +533 -0
  446. package/scripts/promote-staged-skills.sh +423 -0
  447. package/scripts/verify-no-secrets.sh +88 -35
  448. package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
  449. package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
  450. package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
  451. package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
  452. package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
  453. package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
  454. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
  455. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
  456. package/README.md.backup_before_replace +0 -781
  457. package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
  458. package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
  459. package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
  460. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
  461. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/multi-system-query.ts"],"sourcesContent":["/**\r\n * Multi-System Query Engine\r\n *\r\n * Provides fluent interface for cross-database queries with priority-ordered execution.\r\n * Part of Task 3.3: Query Correlation Key Layer\r\n *\r\n * @example\r\n * ```typescript\r\n * const query = new MultiSystemQuery(dbService);\r\n * const results = await query\r\n * .forTask('task-001')\r\n * .includingEntities(['agent', 'skill', 'artifact'])\r\n * .fromSystems(['redis', 'sqlite', 'postgres'])\r\n * .withCache(true)\r\n * .execute();\r\n * // Returns: { agents: [...], skills: [...], artifacts: [...] }\r\n * ```\r\n */\r\n\r\nimport { DatabaseService } from './database-service.js';\r\nimport {\r\n CorrelationKey,\r\n DatabaseError,\r\n DatabaseErrorCode,\r\n QueryFilter,\r\n QueryOptions,\r\n} from './database-service/types.js';\r\nimport {\r\n buildCorrelationKey,\r\n buildWildcardPattern,\r\n getEntityTypes,\r\n WildcardPattern,\r\n} from './database-service/correlation.js';\r\nimport { CorrelationCache } from './correlation-cache.js';\r\nimport { createLogger, Logger } from './logging.js';\r\nimport { createDatabaseError } from './database-service/errors.js';\r\nimport {\r\n ErrorAggregator,\r\n createErrorAggregator,\r\n ErrorSeverity,\r\n} from './error-aggregator.js';\r\n\r\n/**\r\n * Database system type\r\n */\r\nexport type DatabaseSystem = 'redis' | 'sqlite' | 'postgres';\r\n\r\n/**\r\n * Query execution priority\r\n */\r\nexport type ExecutionPriority = 'fastest' | 'balanced' | 'comprehensive';\r\n\r\n/**\r\n * Multi-system query result\r\n */\r\nexport interface MultiSystemResult<T = any> {\r\n /** Correlation key used for query */\r\n correlationKey: string;\r\n /** Results from Redis */\r\n redis?: T[];\r\n /** Results from SQLite */\r\n sqlite?: T[];\r\n /** Results from PostgreSQL */\r\n postgres?: T[];\r\n /** Merged and deduplicated results */\r\n merged: T[];\r\n /** Query execution time in milliseconds */\r\n executionTime: number;\r\n /** Timestamp of query execution */\r\n timestamp: Date;\r\n /** Cache hit status */\r\n cacheHit?: boolean;\r\n /** Errors encountered during query */\r\n errors?: DatabaseError[];\r\n}\r\n\r\n/**\r\n * Query builder configuration\r\n */\r\nexport interface QueryBuilderConfig {\r\n /** Database service instance */\r\n dbService: DatabaseService;\r\n /** Correlation cache instance (optional) */\r\n cache?: CorrelationCache;\r\n /** Enable caching (default: false) */\r\n enableCache?: boolean;\r\n /** Default timeout in milliseconds (default: 2000) */\r\n defaultTimeout?: number;\r\n /** Logger instance (optional) */\r\n logger?: Logger;\r\n}\r\n\r\n/**\r\n * Multi-system query builder\r\n *\r\n * Provides fluent interface for building and executing cross-database queries.\r\n */\r\nexport class MultiSystemQuery {\r\n private dbService: DatabaseService;\r\n private cache?: CorrelationCache;\r\n private logger: Logger;\r\n private errorAggregator?: ErrorAggregator;\r\n\r\n // Query configuration\r\n private correlationKey?: CorrelationKey;\r\n private wildcardPattern?: WildcardPattern;\r\n private entities: string[] = [];\r\n private systems: DatabaseSystem[] = ['redis', 'sqlite', 'postgres'];\r\n private priority: ExecutionPriority = 'balanced';\r\n private useCache: boolean = false;\r\n private timeout: number = 2000; // 2 seconds\r\n private filters: QueryFilter[] = [];\r\n private strictMode: boolean = false; // Fail operation if any system fails\r\n\r\n constructor(config: QueryBuilderConfig) {\r\n this.dbService = config.dbService;\r\n this.cache = config.cache;\r\n this.useCache = config.enableCache || false;\r\n this.timeout = config.defaultTimeout || 2000;\r\n this.logger = config.logger || createLogger('multi-system-query');\r\n }\r\n\r\n /**\r\n * Query for specific task\r\n *\r\n * @param taskId - Task ID to query\r\n * @returns Query builder (fluent)\r\n */\r\n forTask(taskId: string): this {\r\n this.correlationKey = {\r\n type: 'task',\r\n id: taskId,\r\n };\r\n return this;\r\n }\r\n\r\n /**\r\n * Query for specific agent\r\n *\r\n * @param agentId - Agent ID to query\r\n * @returns Query builder (fluent)\r\n */\r\n forAgent(agentId: string): this {\r\n this.correlationKey = {\r\n type: 'agent',\r\n id: agentId,\r\n };\r\n return this;\r\n }\r\n\r\n /**\r\n * Query for specific skill\r\n *\r\n * @param skillId - Skill ID to query\r\n * @returns Query builder (fluent)\r\n */\r\n forSkill(skillId: string): this {\r\n this.correlationKey = {\r\n type: 'skill',\r\n id: skillId,\r\n };\r\n return this;\r\n }\r\n\r\n /**\r\n * Query for specific execution\r\n *\r\n * @param executionId - Execution ID to query\r\n * @returns Query builder (fluent)\r\n */\r\n forExecution(executionId: string): this {\r\n this.correlationKey = {\r\n type: 'execution',\r\n id: executionId,\r\n };\r\n return this;\r\n }\r\n\r\n /**\r\n * Query with custom correlation key\r\n *\r\n * @param key - Correlation key\r\n * @returns Query builder (fluent)\r\n */\r\n withKey(key: CorrelationKey): this {\r\n this.correlationKey = key;\r\n return this;\r\n }\r\n\r\n /**\r\n * Query with wildcard pattern\r\n *\r\n * @param pattern - Wildcard pattern\r\n * @returns Query builder (fluent)\r\n */\r\n withPattern(pattern: WildcardPattern): this {\r\n this.wildcardPattern = pattern;\r\n return this;\r\n }\r\n\r\n /**\r\n * Include specific entity types\r\n *\r\n * @param entities - Entity types to include\r\n * @returns Query builder (fluent)\r\n */\r\n includingEntities(entities: string[]): this {\r\n this.entities = entities;\r\n return this;\r\n }\r\n\r\n /**\r\n * Query from specific database systems\r\n *\r\n * @param systems - Database systems to query\r\n * @returns Query builder (fluent)\r\n */\r\n fromSystems(systems: DatabaseSystem[]): this {\r\n this.systems = systems;\r\n return this;\r\n }\r\n\r\n /**\r\n * Set execution priority\r\n *\r\n * @param priority - Execution priority\r\n * @returns Query builder (fluent)\r\n */\r\n withPriority(priority: ExecutionPriority): this {\r\n this.priority = priority;\r\n return this;\r\n }\r\n\r\n /**\r\n * Enable or disable caching\r\n *\r\n * @param enabled - Enable cache\r\n * @returns Query builder (fluent)\r\n */\r\n withCache(enabled: boolean): this {\r\n this.useCache = enabled;\r\n return this;\r\n }\r\n\r\n /**\r\n * Set query timeout\r\n *\r\n * @param timeout - Timeout in milliseconds\r\n * @returns Query builder (fluent)\r\n */\r\n withTimeout(timeout: number): this {\r\n this.timeout = timeout;\r\n return this;\r\n }\r\n\r\n /**\r\n * Add query filter\r\n *\r\n * @param filter - Query filter\r\n * @returns Query builder (fluent)\r\n */\r\n addFilter(filter: QueryFilter): this {\r\n this.filters.push(filter);\r\n return this;\r\n }\r\n\r\n /**\r\n * Set whether to fail on partial errors\r\n *\r\n * @param fail - Fail if any system fails\r\n * @returns Query builder (fluent)\r\n */\r\n failOnPartialError(fail: boolean = true): this {\r\n this.strictMode = fail;\r\n return this;\r\n }\r\n\r\n /**\r\n * Execute multi-system query\r\n *\r\n * @returns Multi-system query results\r\n * @throws {DatabaseError} If all systems fail or critical error occurs\r\n */\r\n async execute<T = any>(): Promise<MultiSystemResult<T>> {\r\n const startTime = Date.now();\r\n\r\n // Validate query configuration\r\n this.validateQuery();\r\n\r\n // Initialize error aggregator with correlation ID\r\n this.errorAggregator = createErrorAggregator();\r\n const correlationId = this.errorAggregator.getCorrelationId();\r\n\r\n this.logger.info('Starting multi-system query', {\r\n correlationId,\r\n systems: this.systems,\r\n priority: this.priority,\r\n });\r\n\r\n // Build cache key\r\n const cacheKey = this.buildCacheKey();\r\n\r\n // Check cache first\r\n if (this.useCache && this.cache) {\r\n const cached = this.cache.get<MultiSystemResult<T>>(cacheKey);\r\n if (cached) {\r\n this.logger.debug('Cache hit', { cacheKey, correlationId });\r\n return {\r\n ...cached,\r\n cacheHit: true,\r\n };\r\n }\r\n }\r\n\r\n // Execute query based on priority\r\n let result: MultiSystemResult<T>;\r\n try {\r\n result = await this.executeByPriority<T>();\r\n } catch (error) {\r\n // Critical error during execution\r\n this.logger.error(\r\n 'Critical error during query execution',\r\n error instanceof Error ? error : undefined,\r\n {\r\n correlationId,\r\n errorMessage: error instanceof Error ? error.message : String(error),\r\n }\r\n );\r\n\r\n throw createDatabaseError(\r\n DatabaseErrorCode.QUERY_FAILED,\r\n 'Multi-system query failed with critical error',\r\n error instanceof Error ? error : undefined,\r\n { correlationId, systems: this.systems }\r\n );\r\n }\r\n\r\n // Check if we should fail based on errors\r\n if (this.errorAggregator.shouldFailOperation(this.systems)) {\r\n const errorReport = this.errorAggregator.createReport();\r\n this.logger.error(\r\n 'Multi-system query failed',\r\n undefined,\r\n {\r\n correlationId,\r\n errorReport,\r\n }\r\n );\r\n\r\n const aggregationResult = this.errorAggregator.getResult(this.systems);\r\n\r\n throw createDatabaseError(\r\n aggregationResult.hasCriticalErrors\r\n ? DatabaseErrorCode.QUERY_FAILED\r\n : DatabaseErrorCode.QUERY_FAILED,\r\n aggregationResult.allSystemsFailed\r\n ? 'All database systems failed'\r\n : 'Critical errors occurred during query execution',\r\n undefined,\r\n {\r\n correlationId,\r\n errorCount: aggregationResult.totalErrors,\r\n failedSystems: Object.keys(aggregationResult.errorsBySystem),\r\n errors: aggregationResult.allErrors.map(e => ({\r\n system: e.system,\r\n code: e.error.code,\r\n message: e.error.message,\r\n severity: e.severity,\r\n })),\r\n }\r\n );\r\n }\r\n\r\n // Check for partial errors\r\n if (this.strictMode && result.errors && result.errors.length > 0) {\r\n const errorReport = this.errorAggregator.createReport();\r\n this.logger.error(\r\n 'Query failed due to partial errors',\r\n undefined,\r\n {\r\n correlationId,\r\n errorCount: result.errors.length,\r\n errorReport,\r\n }\r\n );\r\n\r\n throw createDatabaseError(\r\n DatabaseErrorCode.QUERY_FAILED,\r\n `Query failed with ${result.errors.length} error(s)`,\r\n undefined,\r\n {\r\n correlationId,\r\n errors: result.errors,\r\n }\r\n );\r\n }\r\n\r\n // Merge and deduplicate results\r\n result.merged = this.mergeResults(result);\r\n\r\n // Calculate execution time\r\n result.executionTime = Date.now() - startTime;\r\n result.timestamp = new Date();\r\n result.cacheHit = false;\r\n\r\n // Validate performance requirement (<2s)\r\n if (result.executionTime >= this.timeout) {\r\n this.logger.warn('Query exceeded timeout', {\r\n correlationId,\r\n executionTime: result.executionTime,\r\n timeout: this.timeout,\r\n });\r\n }\r\n\r\n // Log success\r\n this.logger.info('Multi-system query completed', {\r\n correlationId,\r\n executionTime: result.executionTime,\r\n resultCount: result.merged.length,\r\n errorCount: result.errors?.length || 0,\r\n });\r\n\r\n // Store in cache\r\n if (this.useCache && this.cache) {\r\n this.cache.set(cacheKey, result);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute query based on priority strategy\r\n */\r\n private async executeByPriority<T>(): Promise<MultiSystemResult<T>> {\r\n const result: MultiSystemResult<T> = {\r\n correlationKey: this.buildCorrelationKeyString(),\r\n merged: [],\r\n executionTime: 0,\r\n timestamp: new Date(),\r\n errors: [],\r\n };\r\n\r\n switch (this.priority) {\r\n case 'fastest':\r\n return this.executeFastest<T>(result);\r\n\r\n case 'balanced':\r\n return this.executeBalanced<T>(result);\r\n\r\n case 'comprehensive':\r\n return this.executeComprehensive<T>(result);\r\n\r\n default:\r\n return this.executeBalanced<T>(result);\r\n }\r\n }\r\n\r\n /**\r\n * Execute fastest strategy (stop on first result)\r\n */\r\n private async executeFastest<T>(result: MultiSystemResult<T>): Promise<MultiSystemResult<T>> {\r\n if (!this.errorAggregator) {\r\n throw new Error('Error aggregator not initialized');\r\n }\r\n\r\n // Query in priority order: Redis → SQLite → PostgreSQL\r\n const orderedSystems = this.getOrderedSystems();\r\n\r\n for (const system of orderedSystems) {\r\n // Check circuit breaker\r\n if (this.errorAggregator.isCircuitOpen(system)) {\r\n this.logger.warn(`Circuit breaker open for ${system}, skipping`, {\r\n correlationId: this.errorAggregator.getCorrelationId(),\r\n });\r\n const error = this.createError(system, new Error('Circuit breaker open'));\r\n result.errors?.push(error);\r\n this.errorAggregator.addError(system, error, { strategy: 'fastest' });\r\n continue;\r\n }\r\n\r\n try {\r\n const data = await this.querySystem<T>(system);\r\n this.errorAggregator.recordSuccess(system);\r\n\r\n if (data && data.length > 0) {\r\n result[system] = data;\r\n return result; // Stop on first non-empty result\r\n }\r\n } catch (error) {\r\n const dbError = this.createError(system, error);\r\n this.logger.warn(`Query failed for ${system}`, {\r\n error: dbError,\r\n correlationId: this.errorAggregator.getCorrelationId(),\r\n });\r\n result.errors?.push(dbError);\r\n this.errorAggregator.addError(system, dbError, { strategy: 'fastest' });\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute balanced strategy (priority-ordered parallel)\r\n */\r\n private async executeBalanced<T>(result: MultiSystemResult<T>): Promise<MultiSystemResult<T>> {\r\n if (!this.errorAggregator) {\r\n throw new Error('Error aggregator not initialized');\r\n }\r\n\r\n const orderedSystems = this.getOrderedSystems();\r\n const promises = orderedSystems.map(async (system) => {\r\n // Check circuit breaker\r\n if (this.errorAggregator!.isCircuitOpen(system)) {\r\n this.logger.warn(`Circuit breaker open for ${system}, skipping`, {\r\n correlationId: this.errorAggregator!.getCorrelationId(),\r\n });\r\n const error = this.createError(system, new Error('Circuit breaker open'));\r\n result.errors?.push(error);\r\n this.errorAggregator!.addError(system, error, { strategy: 'balanced' });\r\n return;\r\n }\r\n\r\n try {\r\n const data = await this.querySystem<T>(system);\r\n this.errorAggregator!.recordSuccess(system);\r\n\r\n if (data && data.length > 0) {\r\n result[system] = data;\r\n }\r\n } catch (error) {\r\n const dbError = this.createError(system, error);\r\n this.logger.warn(`Query failed for ${system}`, {\r\n error: dbError,\r\n correlationId: this.errorAggregator!.getCorrelationId(),\r\n });\r\n result.errors?.push(dbError);\r\n this.errorAggregator!.addError(system, dbError, { strategy: 'balanced' });\r\n }\r\n });\r\n\r\n await Promise.all(promises);\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute comprehensive strategy (all systems in parallel)\r\n */\r\n private async executeComprehensive<T>(result: MultiSystemResult<T>): Promise<MultiSystemResult<T>> {\r\n if (!this.errorAggregator) {\r\n throw new Error('Error aggregator not initialized');\r\n }\r\n\r\n const promises = this.systems.map(async (system) => {\r\n // Check circuit breaker\r\n if (this.errorAggregator!.isCircuitOpen(system)) {\r\n this.logger.warn(`Circuit breaker open for ${system}, skipping`, {\r\n correlationId: this.errorAggregator!.getCorrelationId(),\r\n });\r\n const error = this.createError(system, new Error('Circuit breaker open'));\r\n result.errors?.push(error);\r\n this.errorAggregator!.addError(system, error, { strategy: 'comprehensive' });\r\n result[system] = [];\r\n return;\r\n }\r\n\r\n try {\r\n const data = await this.querySystem<T>(system);\r\n this.errorAggregator!.recordSuccess(system);\r\n result[system] = data || [];\r\n } catch (error) {\r\n const dbError = this.createError(system, error);\r\n this.logger.warn(`Query failed for ${system}`, {\r\n error: dbError,\r\n correlationId: this.errorAggregator!.getCorrelationId(),\r\n });\r\n result.errors?.push(dbError);\r\n this.errorAggregator!.addError(system, dbError, { strategy: 'comprehensive' });\r\n result[system] = [];\r\n }\r\n });\r\n\r\n await Promise.all(promises);\r\n return result;\r\n }\r\n\r\n /**\r\n * Query specific database system\r\n */\r\n private async querySystem<T>(system: DatabaseSystem): Promise<T[]> {\r\n const adapter = this.dbService.getAdapter(system);\r\n const results: T[] = [];\r\n\r\n // If wildcard pattern, use pattern matching\r\n if (this.wildcardPattern) {\r\n const pattern = buildWildcardPattern(this.wildcardPattern);\r\n // For Redis, use SCAN with pattern\r\n if (system === 'redis') {\r\n const keys = await this.scanRedisKeys(pattern);\r\n for (const key of keys) {\r\n const data = await adapter.get<T>(key);\r\n if (data) results.push(data);\r\n }\r\n } else {\r\n // For SQLite/PostgreSQL, use raw query with LIKE\r\n const data = await adapter.raw<T>(\r\n 'SELECT * FROM correlation_data WHERE key LIKE ?',\r\n [pattern.replace('*', '%')]\r\n );\r\n results.push(...(Array.isArray(data) ? data : []));\r\n }\r\n } else if (this.correlationKey) {\r\n // Query specific entities for correlation key\r\n if (this.entities.length > 0) {\r\n for (const entity of this.entities) {\r\n const key = buildCorrelationKey({\r\n ...this.correlationKey,\r\n entity,\r\n });\r\n const data = await adapter.get<T>(key);\r\n if (data) results.push(data);\r\n }\r\n } else {\r\n // Query just the correlation key\r\n const key = buildCorrelationKey(this.correlationKey);\r\n const data = await adapter.get<T>(key);\r\n if (data) results.push(data);\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Scan Redis keys with pattern\r\n */\r\n private async scanRedisKeys(pattern: string): Promise<string[]> {\r\n const adapter = this.dbService.getAdapter('redis');\r\n const keys: string[] = [];\r\n\r\n // Use SCAN command for efficient key iteration\r\n let cursor = '0';\r\n do {\r\n const result = await adapter.raw<any>('SCAN', [cursor, 'MATCH', pattern, 'COUNT', '100']);\r\n cursor = result[0];\r\n keys.push(...result[1]);\r\n } while (cursor !== '0');\r\n\r\n return keys;\r\n }\r\n\r\n /**\r\n * Merge results from multiple databases\r\n */\r\n private mergeResults<T>(result: MultiSystemResult<T>): T[] {\r\n const allResults: T[] = [];\r\n const seen = new Set<string>();\r\n\r\n // Merge in priority order: Redis → SQLite → PostgreSQL\r\n const orderedSystems = this.getOrderedSystems();\r\n\r\n for (const system of orderedSystems) {\r\n const systemResults = result[system];\r\n if (!systemResults) continue;\r\n\r\n for (const item of systemResults) {\r\n const key = this.getDeduplicationKey(item);\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n allResults.push(item);\r\n }\r\n }\r\n }\r\n\r\n return allResults;\r\n }\r\n\r\n /**\r\n * Get deduplication key for result item\r\n */\r\n private getDeduplicationKey(item: any): string {\r\n if (item && typeof item === 'object') {\r\n // Try common ID fields\r\n if (item.id) return `id:${item.id}`;\r\n if (item._id) return `_id:${item._id}`;\r\n if (item.key) return `key:${item.key}`;\r\n // Fallback to JSON stringify\r\n return JSON.stringify(item);\r\n }\r\n return String(item);\r\n }\r\n\r\n /**\r\n * Get ordered systems based on priority\r\n */\r\n private getOrderedSystems(): DatabaseSystem[] {\r\n // Priority order: Redis (fastest) → SQLite → PostgreSQL\r\n const order: DatabaseSystem[] = ['redis', 'sqlite', 'postgres'];\r\n return order.filter(system => this.systems.includes(system));\r\n }\r\n\r\n /**\r\n * Validate query configuration\r\n */\r\n private validateQuery(): void {\r\n if (!this.correlationKey && !this.wildcardPattern) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.VALIDATION_FAILED,\r\n 'Query must specify either correlationKey or wildcardPattern',\r\n undefined,\r\n { query: this }\r\n );\r\n }\r\n\r\n if (this.systems.length === 0) {\r\n throw createDatabaseError(\r\n DatabaseErrorCode.VALIDATION_FAILED,\r\n 'Query must specify at least one database system',\r\n undefined,\r\n { query: this }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Build cache key for query\r\n */\r\n private buildCacheKey(): string {\r\n const parts: string[] = ['msq']; // multi-system-query\r\n\r\n if (this.correlationKey) {\r\n parts.push(buildCorrelationKey(this.correlationKey));\r\n } else if (this.wildcardPattern) {\r\n parts.push(buildWildcardPattern(this.wildcardPattern));\r\n }\r\n\r\n if (this.entities.length > 0) {\r\n parts.push(`entities:${this.entities.join(',')}`);\r\n }\r\n\r\n parts.push(`systems:${this.systems.join(',')}`);\r\n parts.push(`priority:${this.priority}`);\r\n\r\n return parts.join(':');\r\n }\r\n\r\n /**\r\n * Build correlation key string\r\n */\r\n private buildCorrelationKeyString(): string {\r\n if (this.correlationKey) {\r\n return buildCorrelationKey(this.correlationKey);\r\n } else if (this.wildcardPattern) {\r\n return buildWildcardPattern(this.wildcardPattern);\r\n }\r\n return 'unknown';\r\n }\r\n\r\n /**\r\n * Create database error\r\n */\r\n private createError(system: DatabaseSystem, error: any): DatabaseError {\r\n return {\r\n code: DatabaseErrorCode.QUERY_FAILED,\r\n message: `Query failed for ${system}: ${error.message || error}`,\r\n originalError: error instanceof Error ? error : undefined,\r\n context: { system },\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Create multi-system query builder\r\n *\r\n * @param config - Query builder configuration\r\n * @returns Multi-system query builder instance\r\n */\r\nexport function createMultiSystemQuery(config: QueryBuilderConfig): MultiSystemQuery {\r\n return new MultiSystemQuery(config);\r\n}\r\n"],"names":["DatabaseErrorCode","buildCorrelationKey","buildWildcardPattern","createLogger","createDatabaseError","createErrorAggregator","MultiSystemQuery","dbService","cache","logger","errorAggregator","correlationKey","wildcardPattern","entities","systems","priority","useCache","timeout","filters","strictMode","config","enableCache","defaultTimeout","forTask","taskId","type","id","forAgent","agentId","forSkill","skillId","forExecution","executionId","withKey","key","withPattern","pattern","includingEntities","fromSystems","withPriority","withCache","enabled","withTimeout","addFilter","filter","push","failOnPartialError","fail","execute","startTime","Date","now","validateQuery","correlationId","getCorrelationId","info","cacheKey","buildCacheKey","cached","get","debug","cacheHit","result","executeByPriority","error","Error","undefined","errorMessage","message","String","QUERY_FAILED","shouldFailOperation","errorReport","createReport","aggregationResult","getResult","hasCriticalErrors","allSystemsFailed","errorCount","totalErrors","failedSystems","Object","keys","errorsBySystem","errors","allErrors","map","e","system","code","severity","length","merged","mergeResults","executionTime","timestamp","warn","resultCount","set","buildCorrelationKeyString","executeFastest","executeBalanced","executeComprehensive","orderedSystems","getOrderedSystems","isCircuitOpen","createError","addError","strategy","data","querySystem","recordSuccess","dbError","promises","Promise","all","adapter","getAdapter","results","scanRedisKeys","raw","replace","Array","isArray","entity","cursor","allResults","seen","Set","systemResults","item","getDeduplicationKey","has","add","_id","JSON","stringify","order","includes","VALIDATION_FAILED","query","parts","join","originalError","context","createMultiSystemQuery"],"mappings":"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"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Orphan Workspace Detector
3
+ *
4
+ * Detects and cleans up orphaned workspaces (no active agent process).
5
+ * Implements grace period logic to prevent premature cleanup during restarts.
6
+ *
7
+ * Part of Task P2-1.3: Supervised Workspace Cleanup (Phase 2)
8
+ *
9
+ * Features:
10
+ * - Detect orphaned workspaces (no active process)
11
+ * - Process monitoring (PID tracking)
12
+ * - Cleanup stale workspaces
13
+ * - Grace period (10 minutes default before cleanup)
14
+ * - Automatic background scanning
15
+ * - Audit trail for orphan cleanup
16
+ *
17
+ * Usage:
18
+ * const detector = new OrphanDetector({
19
+ * workspaceRoot: '/tmp/cfn-workspaces',
20
+ * gracePeriodMinutes: 10
21
+ * });
22
+ *
23
+ * const orphans = await detector.detectOrphans();
24
+ * console.log(`Found ${orphans.length} orphaned workspaces`);
25
+ *
26
+ * const stats = await detector.cleanupOrphans();
27
+ * console.log(`Cleaned up ${stats.cleanedCount} workspaces`);
28
+ */ import * as fs from 'fs/promises';
29
+ import * as path from 'path';
30
+ import { createLogger } from './logging.js';
31
+ import { createError, ErrorCode } from './errors.js';
32
+ const logger = createLogger('orphan-detector');
33
+ /**
34
+ * OrphanDetector: Detects and cleans up orphaned workspaces
35
+ */ export class OrphanDetector {
36
+ config;
37
+ scanInterval = null;
38
+ constructor(config){
39
+ this.config = {
40
+ gracePeriodMinutes: 10,
41
+ scanIntervalMinutes: 30,
42
+ ...config
43
+ };
44
+ }
45
+ /**
46
+ * Start background orphan detection scanner
47
+ */ start() {
48
+ if (this.scanInterval) {
49
+ return; // Already running
50
+ }
51
+ const intervalMs = (this.config.scanIntervalMinutes || 30) * 60 * 1000;
52
+ this.scanInterval = setInterval(async ()=>{
53
+ try {
54
+ const stats = await this.cleanupOrphans();
55
+ if (stats.cleanedCount > 0) {
56
+ logger.info('Background orphan cleanup completed', {
57
+ cleanedCount: stats.cleanedCount,
58
+ totalFreed: stats.totalSizeFreed
59
+ });
60
+ }
61
+ } catch (error) {
62
+ logger.error('Error in orphan detection scan', {
63
+ error: String(error)
64
+ });
65
+ }
66
+ }, intervalMs);
67
+ logger.info('Orphan detector started', {
68
+ gracePeriodMinutes: this.config.gracePeriodMinutes,
69
+ scanIntervalMinutes: this.config.scanIntervalMinutes
70
+ });
71
+ }
72
+ /**
73
+ * Stop background scanner
74
+ */ stop() {
75
+ if (this.scanInterval) {
76
+ clearInterval(this.scanInterval);
77
+ this.scanInterval = null;
78
+ logger.info('Orphan detector stopped');
79
+ }
80
+ }
81
+ /**
82
+ * Detect orphaned workspaces
83
+ */ async detectOrphans() {
84
+ const orphans = [];
85
+ const gracePeriodMs = (this.config.gracePeriodMinutes || 10) * 60 * 1000;
86
+ const now = Date.now();
87
+ try {
88
+ const entries = await fs.readdir(this.config.workspaceRoot, {
89
+ withFileTypes: true
90
+ });
91
+ for (const entry of entries){
92
+ if (!entry.isDirectory()) continue;
93
+ try {
94
+ const workspaceInfo = await this.analyzeWorkspace(entry.path);
95
+ if (!workspaceInfo) continue;
96
+ // Check if process is still active
97
+ const isProcessActive = this.isProcessActive(workspaceInfo.processId);
98
+ if (!isProcessActive) {
99
+ // Check grace period
100
+ const timeSinceLastAccess = now - workspaceInfo.lastAccessedAt.getTime();
101
+ if (timeSinceLastAccess > gracePeriodMs) {
102
+ orphans.push(workspaceInfo);
103
+ }
104
+ }
105
+ } catch (error) {
106
+ logger.warn('Error analyzing workspace', {
107
+ path: entry.path,
108
+ error: String(error)
109
+ });
110
+ }
111
+ }
112
+ logger.info('Orphan detection scan completed', {
113
+ found: orphans.length,
114
+ gracePeriodMs
115
+ });
116
+ } catch (error) {
117
+ logger.error('Error scanning for orphans', {
118
+ error: String(error)
119
+ });
120
+ }
121
+ return orphans;
122
+ }
123
+ /**
124
+ * Clean up detected orphaned workspaces
125
+ */ async cleanupOrphans() {
126
+ const orphans = await this.detectOrphans();
127
+ let cleanedCount = 0;
128
+ let totalSizeFreed = 0;
129
+ let totalFilesRemoved = 0;
130
+ let gracePeriodCount = 0;
131
+ const gracePeriodMs = (this.config.gracePeriodMinutes || 10) * 60 * 1000;
132
+ const now = Date.now();
133
+ for (const orphan of orphans){
134
+ try {
135
+ // Double-check if in grace period
136
+ const timeSinceAccess = now - orphan.lastAccessedAt.getTime();
137
+ if (timeSinceAccess <= gracePeriodMs) {
138
+ gracePeriodCount++;
139
+ continue;
140
+ }
141
+ // Clean up workspace
142
+ const sizeFreed = await this.removeWorkspace(orphan.path);
143
+ cleanedCount++;
144
+ totalSizeFreed += sizeFreed;
145
+ totalFilesRemoved += orphan.fileCount;
146
+ logger.info('Cleaned orphaned workspace', {
147
+ id: orphan.id,
148
+ agentId: orphan.agentId,
149
+ sizeFreed
150
+ });
151
+ } catch (error) {
152
+ logger.error('Error cleaning up orphan workspace', {
153
+ id: orphan.id,
154
+ error: String(error)
155
+ });
156
+ }
157
+ }
158
+ return {
159
+ cleanedCount,
160
+ totalSizeFreed,
161
+ filesRemoved: totalFilesRemoved,
162
+ gracePeriodCount
163
+ };
164
+ }
165
+ /**
166
+ * Force cleanup of specific orphan (skip grace period)
167
+ */ async forceCleanupOrphan(workspacePath) {
168
+ try {
169
+ const size = await this.removeWorkspace(workspacePath);
170
+ logger.info('Force cleaned orphan workspace', {
171
+ path: workspacePath,
172
+ size
173
+ });
174
+ return size;
175
+ } catch (error) {
176
+ logger.error('Error force cleaning orphan', {
177
+ path: workspacePath,
178
+ error: String(error)
179
+ });
180
+ throw createError(ErrorCode.FILE_WRITE_FAILED, 'Failed to cleanup orphan workspace', {
181
+ cause: String(error)
182
+ });
183
+ }
184
+ }
185
+ // ============================================================================
186
+ // Private Helper Methods
187
+ // ============================================================================
188
+ /**
189
+ * Analyze workspace to extract metadata
190
+ */ async analyzeWorkspace(workspacePath) {
191
+ try {
192
+ const stats = await fs.stat(workspacePath);
193
+ const name = path.basename(workspacePath);
194
+ // Parse workspace name: sanitizedAgentId-sanitizedTaskId-uuid
195
+ // UUID format: 8hex-4hex-4hex-4hex-12hex (36 chars total)
196
+ // We need to find where the UUID starts
197
+ const uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
198
+ const uuidMatch = name.match(uuidRegex);
199
+ if (!uuidMatch) {
200
+ return null;
201
+ }
202
+ const id = uuidMatch[0];
203
+ const prefix = name.substring(0, name.length - id.length - 1); // -1 for the dash before UUID
204
+ // Parse agent and task IDs from prefix
205
+ // Format: sanitizedAgentId-sanitizedTaskId
206
+ // Since both can contain characters, we use a simple heuristic:
207
+ // Find "task" in the prefix and assume that's where taskId starts
208
+ let agentId = '';
209
+ let taskId = '';
210
+ const taskStartIndex = prefix.indexOf('task-');
211
+ if (taskStartIndex > 0) {
212
+ agentId = prefix.substring(0, taskStartIndex - 1); // -1 to remove trailing dash
213
+ taskId = prefix.substring(taskStartIndex);
214
+ } else {
215
+ // Fallback: just use the whole prefix as agentId
216
+ agentId = prefix;
217
+ taskId = 'unknown';
218
+ }
219
+ // Get workspace metadata if it exists
220
+ let processId;
221
+ let lastAccessedAt = stats.mtime;
222
+ try {
223
+ const metadataPath = path.join(workspacePath, '.metadata.json');
224
+ const metadata = await fs.readFile(metadataPath, 'utf-8');
225
+ const parsed = JSON.parse(metadata);
226
+ processId = parsed.processId;
227
+ if (parsed.lastAccessedAt) {
228
+ lastAccessedAt = new Date(parsed.lastAccessedAt);
229
+ }
230
+ } catch (e) {
231
+ // Metadata file doesn't exist - use current mtime
232
+ }
233
+ const size = await this.getDirectorySize(workspacePath);
234
+ const fileCount = await this.countFiles(workspacePath);
235
+ return {
236
+ id,
237
+ agentId,
238
+ taskId,
239
+ path: workspacePath,
240
+ createdAt: stats.birthtime || stats.mtime,
241
+ lastAccessedAt,
242
+ processId,
243
+ sizeBytes: size,
244
+ fileCount
245
+ };
246
+ } catch (error) {
247
+ logger.debug('Error analyzing workspace', {
248
+ path: workspacePath,
249
+ error: String(error)
250
+ });
251
+ return null;
252
+ }
253
+ }
254
+ /**
255
+ * Check if process is still active
256
+ */ isProcessActive(processId) {
257
+ if (!processId) {
258
+ return false; // No process ID = definitely orphaned
259
+ }
260
+ try {
261
+ // Send signal 0 (check if process exists without sending signal)
262
+ // Returns true if process is still running
263
+ process.kill(processId, 0);
264
+ return true;
265
+ } catch (error) {
266
+ // Process does not exist
267
+ return false;
268
+ }
269
+ }
270
+ /**
271
+ * Remove workspace directory and return freed size
272
+ */ async removeWorkspace(workspacePath) {
273
+ const size = await this.getDirectorySize(workspacePath);
274
+ try {
275
+ await fs.rm(workspacePath, {
276
+ recursive: true,
277
+ force: true
278
+ });
279
+ logger.debug('Removed workspace directory', {
280
+ path: workspacePath
281
+ });
282
+ return size;
283
+ } catch (error) {
284
+ logger.error('Error removing workspace', {
285
+ path: workspacePath,
286
+ error: String(error)
287
+ });
288
+ throw error;
289
+ }
290
+ }
291
+ /**
292
+ * Get directory size in bytes
293
+ */ async getDirectorySize(dir) {
294
+ try {
295
+ const entries = await fs.readdir(dir, {
296
+ recursive: true,
297
+ withFileTypes: false
298
+ });
299
+ let totalSize = 0;
300
+ for (const file of entries){
301
+ try {
302
+ const filePath = path.join(dir, file);
303
+ const stats = await fs.stat(filePath).catch(()=>null);
304
+ if (stats?.isFile()) {
305
+ totalSize += stats.size;
306
+ }
307
+ } catch (e) {
308
+ // Ignore inaccessible files
309
+ }
310
+ }
311
+ return totalSize;
312
+ } catch (error) {
313
+ return 0;
314
+ }
315
+ }
316
+ /**
317
+ * Count files in directory
318
+ */ async countFiles(dir) {
319
+ try {
320
+ const entries = await fs.readdir(dir, {
321
+ recursive: true,
322
+ withFileTypes: false
323
+ });
324
+ return Array.isArray(entries) ? entries.length : 0;
325
+ } catch (error) {
326
+ return 0;
327
+ }
328
+ }
329
+ }
330
+ export default OrphanDetector;
331
+
332
+ //# sourceMappingURL=orphan-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/orphan-detector.ts"],"sourcesContent":["/**\r\n * Orphan Workspace Detector\r\n *\r\n * Detects and cleans up orphaned workspaces (no active agent process).\r\n * Implements grace period logic to prevent premature cleanup during restarts.\r\n *\r\n * Part of Task P2-1.3: Supervised Workspace Cleanup (Phase 2)\r\n *\r\n * Features:\r\n * - Detect orphaned workspaces (no active process)\r\n * - Process monitoring (PID tracking)\r\n * - Cleanup stale workspaces\r\n * - Grace period (10 minutes default before cleanup)\r\n * - Automatic background scanning\r\n * - Audit trail for orphan cleanup\r\n *\r\n * Usage:\r\n * const detector = new OrphanDetector({\r\n * workspaceRoot: '/tmp/cfn-workspaces',\r\n * gracePeriodMinutes: 10\r\n * });\r\n *\r\n * const orphans = await detector.detectOrphans();\r\n * console.log(`Found ${orphans.length} orphaned workspaces`);\r\n *\r\n * const stats = await detector.cleanupOrphans();\r\n * console.log(`Cleaned up ${stats.cleanedCount} workspaces`);\r\n */\r\n\r\nimport * as fs from 'fs/promises';\r\nimport * as path from 'path';\r\nimport { createLogger } from './logging.js';\r\nimport { createError, ErrorCode, StandardError } from './errors.js';\r\n\r\nconst logger = createLogger('orphan-detector');\r\n\r\n/**\r\n * Orphan workspace metadata\r\n */\r\nexport interface OrphanWorkspace {\r\n id: string;\r\n agentId: string;\r\n taskId: string;\r\n path: string;\r\n createdAt: Date;\r\n lastAccessedAt: Date;\r\n processId?: number;\r\n sizeBytes: number;\r\n fileCount: number;\r\n}\r\n\r\n/**\r\n * Cleanup statistics\r\n */\r\nexport interface OrphanCleanupStats {\r\n /** Number of workspaces cleaned */\r\n cleanedCount: number;\r\n /** Total size freed in bytes */\r\n totalSizeFreed: number;\r\n /** Number of files removed */\r\n filesRemoved: number;\r\n /** Number of workspaces still in grace period */\r\n gracePeriodCount: number;\r\n}\r\n\r\n/**\r\n * Detector configuration\r\n */\r\nexport interface OrphanDetectorConfig {\r\n /** Root directory for workspaces */\r\n workspaceRoot: string;\r\n /** Grace period in minutes (default: 10) */\r\n gracePeriodMinutes?: number;\r\n /** Scan interval in minutes (default: 30) */\r\n scanIntervalMinutes?: number;\r\n}\r\n\r\n/**\r\n * OrphanDetector: Detects and cleans up orphaned workspaces\r\n */\r\nexport class OrphanDetector {\r\n private config: OrphanDetectorConfig;\r\n private scanInterval: NodeJS.Timer | null = null;\r\n\r\n constructor(config: OrphanDetectorConfig) {\r\n this.config = {\r\n gracePeriodMinutes: 10,\r\n scanIntervalMinutes: 30,\r\n ...config,\r\n };\r\n }\r\n\r\n /**\r\n * Start background orphan detection scanner\r\n */\r\n start(): void {\r\n if (this.scanInterval) {\r\n return; // Already running\r\n }\r\n\r\n const intervalMs = (this.config.scanIntervalMinutes || 30) * 60 * 1000;\r\n\r\n this.scanInterval = setInterval(async () => {\r\n try {\r\n const stats = await this.cleanupOrphans();\r\n if (stats.cleanedCount > 0) {\r\n logger.info('Background orphan cleanup completed', {\r\n cleanedCount: stats.cleanedCount,\r\n totalFreed: stats.totalSizeFreed,\r\n });\r\n }\r\n } catch (error) {\r\n logger.error('Error in orphan detection scan', { error: String(error) });\r\n }\r\n }, intervalMs);\r\n\r\n logger.info('Orphan detector started', {\r\n gracePeriodMinutes: this.config.gracePeriodMinutes,\r\n scanIntervalMinutes: this.config.scanIntervalMinutes,\r\n });\r\n }\r\n\r\n /**\r\n * Stop background scanner\r\n */\r\n stop(): void {\r\n if (this.scanInterval) {\r\n clearInterval(this.scanInterval);\r\n this.scanInterval = null;\r\n logger.info('Orphan detector stopped');\r\n }\r\n }\r\n\r\n /**\r\n * Detect orphaned workspaces\r\n */\r\n async detectOrphans(): Promise<OrphanWorkspace[]> {\r\n const orphans: OrphanWorkspace[] = [];\r\n const gracePeriodMs = (this.config.gracePeriodMinutes || 10) * 60 * 1000;\r\n const now = Date.now();\r\n\r\n try {\r\n const entries = await fs.readdir(this.config.workspaceRoot, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n if (!entry.isDirectory()) continue;\r\n\r\n try {\r\n const workspaceInfo = await this.analyzeWorkspace(entry.path);\r\n if (!workspaceInfo) continue;\r\n\r\n // Check if process is still active\r\n const isProcessActive = this.isProcessActive(workspaceInfo.processId);\r\n\r\n if (!isProcessActive) {\r\n // Check grace period\r\n const timeSinceLastAccess = now - workspaceInfo.lastAccessedAt.getTime();\r\n\r\n if (timeSinceLastAccess > gracePeriodMs) {\r\n orphans.push(workspaceInfo);\r\n }\r\n }\r\n } catch (error) {\r\n logger.warn('Error analyzing workspace', { path: entry.path, error: String(error) });\r\n }\r\n }\r\n\r\n logger.info('Orphan detection scan completed', {\r\n found: orphans.length,\r\n gracePeriodMs,\r\n });\r\n } catch (error) {\r\n logger.error('Error scanning for orphans', { error: String(error) });\r\n }\r\n\r\n return orphans;\r\n }\r\n\r\n /**\r\n * Clean up detected orphaned workspaces\r\n */\r\n async cleanupOrphans(): Promise<OrphanCleanupStats> {\r\n const orphans = await this.detectOrphans();\r\n let cleanedCount = 0;\r\n let totalSizeFreed = 0;\r\n let totalFilesRemoved = 0;\r\n let gracePeriodCount = 0;\r\n\r\n const gracePeriodMs = (this.config.gracePeriodMinutes || 10) * 60 * 1000;\r\n const now = Date.now();\r\n\r\n for (const orphan of orphans) {\r\n try {\r\n // Double-check if in grace period\r\n const timeSinceAccess = now - orphan.lastAccessedAt.getTime();\r\n if (timeSinceAccess <= gracePeriodMs) {\r\n gracePeriodCount++;\r\n continue;\r\n }\r\n\r\n // Clean up workspace\r\n const sizeFreed = await this.removeWorkspace(orphan.path);\r\n cleanedCount++;\r\n totalSizeFreed += sizeFreed;\r\n totalFilesRemoved += orphan.fileCount;\r\n\r\n logger.info('Cleaned orphaned workspace', {\r\n id: orphan.id,\r\n agentId: orphan.agentId,\r\n sizeFreed,\r\n });\r\n } catch (error) {\r\n logger.error('Error cleaning up orphan workspace', {\r\n id: orphan.id,\r\n error: String(error),\r\n });\r\n }\r\n }\r\n\r\n return {\r\n cleanedCount,\r\n totalSizeFreed,\r\n filesRemoved: totalFilesRemoved,\r\n gracePeriodCount,\r\n };\r\n }\r\n\r\n /**\r\n * Force cleanup of specific orphan (skip grace period)\r\n */\r\n async forceCleanupOrphan(workspacePath: string): Promise<number> {\r\n try {\r\n const size = await this.removeWorkspace(workspacePath);\r\n logger.info('Force cleaned orphan workspace', { path: workspacePath, size });\r\n return size;\r\n } catch (error) {\r\n logger.error('Error force cleaning orphan', { path: workspacePath, error: String(error) });\r\n throw createError(ErrorCode.FILE_WRITE_FAILED, 'Failed to cleanup orphan workspace', {\r\n cause: String(error),\r\n });\r\n }\r\n }\r\n\r\n // ============================================================================\r\n // Private Helper Methods\r\n // ============================================================================\r\n\r\n /**\r\n * Analyze workspace to extract metadata\r\n */\r\n private async analyzeWorkspace(workspacePath: string): Promise<OrphanWorkspace | null> {\r\n try {\r\n const stats = await fs.stat(workspacePath);\r\n const name = path.basename(workspacePath);\r\n\r\n // Parse workspace name: sanitizedAgentId-sanitizedTaskId-uuid\r\n // UUID format: 8hex-4hex-4hex-4hex-12hex (36 chars total)\r\n // We need to find where the UUID starts\r\n const uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;\r\n const uuidMatch = name.match(uuidRegex);\r\n\r\n if (!uuidMatch) {\r\n return null;\r\n }\r\n\r\n const id = uuidMatch[0];\r\n const prefix = name.substring(0, name.length - id.length - 1); // -1 for the dash before UUID\r\n\r\n // Parse agent and task IDs from prefix\r\n // Format: sanitizedAgentId-sanitizedTaskId\r\n // Since both can contain characters, we use a simple heuristic:\r\n // Find \"task\" in the prefix and assume that's where taskId starts\r\n let agentId = '';\r\n let taskId = '';\r\n\r\n const taskStartIndex = prefix.indexOf('task-');\r\n if (taskStartIndex > 0) {\r\n agentId = prefix.substring(0, taskStartIndex - 1); // -1 to remove trailing dash\r\n taskId = prefix.substring(taskStartIndex);\r\n } else {\r\n // Fallback: just use the whole prefix as agentId\r\n agentId = prefix;\r\n taskId = 'unknown';\r\n }\r\n\r\n // Get workspace metadata if it exists\r\n let processId: number | undefined;\r\n let lastAccessedAt = stats.mtime;\r\n\r\n try {\r\n const metadataPath = path.join(workspacePath, '.metadata.json');\r\n const metadata = await fs.readFile(metadataPath, 'utf-8');\r\n const parsed = JSON.parse(metadata);\r\n processId = parsed.processId;\r\n if (parsed.lastAccessedAt) {\r\n lastAccessedAt = new Date(parsed.lastAccessedAt);\r\n }\r\n } catch (e) {\r\n // Metadata file doesn't exist - use current mtime\r\n }\r\n\r\n const size = await this.getDirectorySize(workspacePath);\r\n const fileCount = await this.countFiles(workspacePath);\r\n\r\n return {\r\n id,\r\n agentId,\r\n taskId,\r\n path: workspacePath,\r\n createdAt: stats.birthtime || stats.mtime,\r\n lastAccessedAt,\r\n processId,\r\n sizeBytes: size,\r\n fileCount,\r\n };\r\n } catch (error) {\r\n logger.debug('Error analyzing workspace', { path: workspacePath, error: String(error) });\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Check if process is still active\r\n */\r\n private isProcessActive(processId?: number): boolean {\r\n if (!processId) {\r\n return false; // No process ID = definitely orphaned\r\n }\r\n\r\n try {\r\n // Send signal 0 (check if process exists without sending signal)\r\n // Returns true if process is still running\r\n process.kill(processId, 0);\r\n return true;\r\n } catch (error) {\r\n // Process does not exist\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Remove workspace directory and return freed size\r\n */\r\n private async removeWorkspace(workspacePath: string): Promise<number> {\r\n const size = await this.getDirectorySize(workspacePath);\r\n\r\n try {\r\n await fs.rm(workspacePath, { recursive: true, force: true });\r\n logger.debug('Removed workspace directory', { path: workspacePath });\r\n return size;\r\n } catch (error) {\r\n logger.error('Error removing workspace', { path: workspacePath, error: String(error) });\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Get directory size in bytes\r\n */\r\n private async getDirectorySize(dir: string): Promise<number> {\r\n try {\r\n const entries = await fs.readdir(dir, { recursive: true, withFileTypes: false });\r\n let totalSize = 0;\r\n\r\n for (const file of entries as string[]) {\r\n try {\r\n const filePath = path.join(dir, file);\r\n const stats = await fs.stat(filePath).catch(() => null);\r\n if (stats?.isFile()) {\r\n totalSize += stats.size;\r\n }\r\n } catch (e) {\r\n // Ignore inaccessible files\r\n }\r\n }\r\n\r\n return totalSize;\r\n } catch (error) {\r\n return 0;\r\n }\r\n }\r\n\r\n /**\r\n * Count files in directory\r\n */\r\n private async countFiles(dir: string): Promise<number> {\r\n try {\r\n const entries = await fs.readdir(dir, { recursive: true, withFileTypes: false });\r\n return Array.isArray(entries) ? entries.length : 0;\r\n } catch (error) {\r\n return 0;\r\n }\r\n }\r\n}\r\n\r\nexport default OrphanDetector;\r\n"],"names":["fs","path","createLogger","createError","ErrorCode","logger","OrphanDetector","config","scanInterval","gracePeriodMinutes","scanIntervalMinutes","start","intervalMs","setInterval","stats","cleanupOrphans","cleanedCount","info","totalFreed","totalSizeFreed","error","String","stop","clearInterval","detectOrphans","orphans","gracePeriodMs","now","Date","entries","readdir","workspaceRoot","withFileTypes","entry","isDirectory","workspaceInfo","analyzeWorkspace","isProcessActive","processId","timeSinceLastAccess","lastAccessedAt","getTime","push","warn","found","length","totalFilesRemoved","gracePeriodCount","orphan","timeSinceAccess","sizeFreed","removeWorkspace","fileCount","id","agentId","filesRemoved","forceCleanupOrphan","workspacePath","size","FILE_WRITE_FAILED","cause","stat","name","basename","uuidRegex","uuidMatch","match","prefix","substring","taskId","taskStartIndex","indexOf","mtime","metadataPath","join","metadata","readFile","parsed","JSON","parse","e","getDirectorySize","countFiles","createdAt","birthtime","sizeBytes","debug","process","kill","rm","recursive","force","dir","totalSize","file","filePath","catch","isFile","Array","isArray"],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Secure Password Generator
3
+ *
4
+ * Generates cryptographically secure passwords for database authentication
5
+ * with configurable complexity requirements and validation.
6
+ *
7
+ * Security Standards:
8
+ * - Uses crypto.randomBytes() for cryptographic randomness
9
+ * - Default minimum 32 characters for high entropy
10
+ * - Requires mixed character types (uppercase, lowercase, digits, special)
11
+ * - No ambiguous characters (0/O, 1/l, etc.)
12
+ * - Suitable for Redis requirepass and PostgreSQL authentication
13
+ */ import { randomBytes } from 'crypto';
14
+ /**
15
+ * Default character sets for password generation
16
+ */ const CHAR_SETS = {
17
+ uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
18
+ uppercaseNoAmbiguous: 'ABCDEFGHJKMNPQRSTUVWXYZ',
19
+ lowercase: 'abcdefghijklmnopqrstuvwxyz',
20
+ lowercaseNoAmbiguous: 'abcdefghjkmnpqrstuvwxyz',
21
+ digits: '0123456789',
22
+ digitsNoAmbiguous: '23456789',
23
+ special: '!@#$%^&*()_+-=[]{}|;:,.<>?',
24
+ specialSafe: '!@#%^&*_+-='
25
+ };
26
+ /**
27
+ * Generate a cryptographically secure random password
28
+ */ export function generatePassword(options = {}) {
29
+ const { length = 32, uppercase = true, lowercase = true, digits = true, special = true, excludeAmbiguous = true } = options;
30
+ if (length < 16) {
31
+ throw new Error('Password length must be at least 16 characters');
32
+ }
33
+ // Build character pool
34
+ let charPool = '';
35
+ const selectedSets = [];
36
+ if (uppercase) {
37
+ const chars = excludeAmbiguous ? CHAR_SETS.uppercaseNoAmbiguous : CHAR_SETS.uppercase;
38
+ charPool += chars;
39
+ selectedSets.push('uppercase');
40
+ }
41
+ if (lowercase) {
42
+ const chars = excludeAmbiguous ? CHAR_SETS.lowercaseNoAmbiguous : CHAR_SETS.lowercase;
43
+ charPool += chars;
44
+ selectedSets.push('lowercase');
45
+ }
46
+ if (digits) {
47
+ const chars = excludeAmbiguous ? CHAR_SETS.digitsNoAmbiguous : CHAR_SETS.digits;
48
+ charPool += chars;
49
+ selectedSets.push('digits');
50
+ }
51
+ if (special) {
52
+ charPool += CHAR_SETS.specialSafe;
53
+ selectedSets.push('special');
54
+ }
55
+ if (charPool.length === 0) {
56
+ throw new Error('At least one character type must be enabled');
57
+ }
58
+ // Ensure password contains at least one character from each required type
59
+ const password = new Array(length);
60
+ let generated = 0;
61
+ // First, place one character from each required set
62
+ for (const setName of selectedSets){
63
+ let setChars;
64
+ switch(setName){
65
+ case 'uppercase':
66
+ setChars = uppercase ? excludeAmbiguous ? CHAR_SETS.uppercaseNoAmbiguous : CHAR_SETS.uppercase : '';
67
+ break;
68
+ case 'lowercase':
69
+ setChars = lowercase ? excludeAmbiguous ? CHAR_SETS.lowercaseNoAmbiguous : CHAR_SETS.lowercase : '';
70
+ break;
71
+ case 'digits':
72
+ setChars = digits ? excludeAmbiguous ? CHAR_SETS.digitsNoAmbiguous : CHAR_SETS.digits : '';
73
+ break;
74
+ case 'special':
75
+ setChars = special ? CHAR_SETS.specialSafe : '';
76
+ break;
77
+ default:
78
+ setChars = '';
79
+ }
80
+ if (setChars.length > 0 && generated < length) {
81
+ const index = cryptoRandom(0, setChars.length - 1);
82
+ password[generated] = setChars[index];
83
+ generated++;
84
+ }
85
+ }
86
+ // Fill remaining positions with random characters from pool
87
+ while(generated < length){
88
+ const index = cryptoRandom(0, charPool.length - 1);
89
+ password[generated] = charPool[index];
90
+ generated++;
91
+ }
92
+ // Shuffle password to avoid predictable patterns
93
+ return shuffleArray(password).join('');
94
+ }
95
+ /**
96
+ * Validate password meets security requirements
97
+ */ export function validatePassword(password, minLength = 32) {
98
+ const errors = [];
99
+ const hasUppercase = /[A-Z]/.test(password);
100
+ const hasLowercase = /[a-z]/.test(password);
101
+ const hasDigits = /\d/.test(password);
102
+ const hasSpecial = /[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]/.test(password);
103
+ if (password.length < minLength) {
104
+ errors.push(`Password must be at least ${minLength} characters long (current: ${password.length})`);
105
+ }
106
+ if (!hasUppercase) {
107
+ errors.push('Password must contain at least one uppercase letter');
108
+ }
109
+ if (!hasLowercase) {
110
+ errors.push('Password must contain at least one lowercase letter');
111
+ }
112
+ if (!hasDigits) {
113
+ errors.push('Password must contain at least one digit');
114
+ }
115
+ if (!hasSpecial) {
116
+ errors.push('Password must contain at least one special character');
117
+ }
118
+ return {
119
+ valid: errors.length === 0,
120
+ length: password.length,
121
+ hasUppercase,
122
+ hasLowercase,
123
+ hasDigits,
124
+ hasSpecial,
125
+ errors
126
+ };
127
+ }
128
+ /**
129
+ * Generate a cryptographically secure random integer in range [min, max]
130
+ */ function cryptoRandom(min, max) {
131
+ if (min < 0 || max < 0 || min > max) {
132
+ throw new Error('Invalid range: min must be >= 0 and min must be <= max');
133
+ }
134
+ const range = max - min + 1;
135
+ const bytesNeeded = Math.ceil(Math.log2(range) / 8);
136
+ const randomBytes_ = randomBytes(bytesNeeded);
137
+ // Convert bytes to integer and apply modulo to ensure uniform distribution
138
+ let randomValue = 0;
139
+ for(let i = 0; i < bytesNeeded; i++){
140
+ randomValue = randomValue << 8 | randomBytes_[i];
141
+ }
142
+ // Use rejection sampling to ensure uniform distribution
143
+ const limit = Math.floor(256 ** bytesNeeded / range) * range;
144
+ if (randomValue < limit) {
145
+ return min + randomValue % range;
146
+ }
147
+ // Recursively try again if we exceeded the limit (very rare)
148
+ return cryptoRandom(min, max);
149
+ }
150
+ /**
151
+ * Shuffle array in-place using Fisher-Yates algorithm
152
+ */ function shuffleArray(array) {
153
+ const shuffled = [
154
+ ...array
155
+ ];
156
+ for(let i = shuffled.length - 1; i > 0; i--){
157
+ const j = cryptoRandom(0, i);
158
+ [shuffled[i], shuffled[j]] = [
159
+ shuffled[j],
160
+ shuffled[i]
161
+ ];
162
+ }
163
+ return shuffled;
164
+ }
165
+
166
+ //# sourceMappingURL=password-generator.js.map