claude-flow-novice 2.15.11 → 2.16.1

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 (741) hide show
  1. package/.claude/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  2. package/.claude/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
  3. package/.claude/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
  4. package/.claude/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
  5. package/.claude/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
  6. package/.claude/commands/CFN_LOOP_FRONTEND.md +1 -1
  7. package/.claude/commands/cfn-loop-cli.md +214 -442
  8. package/.claude/commands/cfn-loop-frontend.md +1 -1
  9. package/.claude/commands/cfn-loop-task.md +2 -2
  10. package/.claude/commands/cfn-loop-trigger.md +114 -0
  11. package/.claude/commands/deprecated/cfn-loop.md +2 -2
  12. package/.claude/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  13. package/.claude/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  14. package/.claude/hooks/cfn-invoke-post-edit.sh +53 -5
  15. package/.claude/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  16. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  17. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  18. package/.claude/hooks/cfn-invoke-pre-edit.sh +22 -0
  19. package/.claude/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  20. package/.claude/hooks/cfn-post-edit.config.json +9 -2
  21. package/.claude/root-claude-distribute/CFN-CLAUDE.md +1 -1
  22. package/.claude/skills/cfn-agent-spawning/SKILL.md +48 -1
  23. package/.claude/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  24. package/.claude/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  25. package/.claude/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  26. package/.claude/skills/{cfn-redis-coordination/check-dependencies.sh → cfn-agent-spawning/check-dependencies.sh.backup} +3 -5
  27. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  28. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  29. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  30. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  31. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  32. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  33. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  34. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  35. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  36. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  37. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  38. package/.claude/skills/cfn-backlog-management/SKILL.md +1 -1
  39. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  40. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  41. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  42. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  43. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  44. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  45. package/.claude/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  46. package/.claude/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  47. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  48. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  49. package/.claude/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  50. package/.claude/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  51. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  52. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  53. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  54. package/.claude/skills/cfn-loop-orchestration/SKILL.md +159 -48
  55. package/.claude/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  56. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  57. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  58. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  59. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  60. package/.claude/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  61. package/.claude/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  62. package/.claude/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  63. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  64. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  65. package/.claude/skills/cfn-loop-orchestration/{orchestrate-wrapper.sh → archive/legacy-bash/orchestrate-wrapper.sh} +11 -1
  66. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  67. package/.claude/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  68. package/.claude/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  69. package/.claude/skills/cfn-loop-orchestration/package-lock.json +3 -0
  70. package/.claude/skills/cfn-loop-orchestration/package.json +4 -0
  71. package/.claude/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  72. package/.claude/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  73. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  74. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  75. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  76. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  77. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  78. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  79. package/.claude/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  80. package/.claude/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  81. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  82. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  83. package/.claude/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  84. package/.claude/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  85. package/.claude/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  86. package/.claude/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  87. package/.claude/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  88. package/.claude/skills/cfn-loop-orchestration/src/index.ts +2 -0
  89. package/.claude/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  90. package/.claude/skills/cfn-loop-orchestration/src/types.ts +56 -0
  91. package/.claude/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  92. package/.claude/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  93. package/.claude/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  94. package/.claude/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  95. package/.claude/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  96. package/.claude/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  97. package/.claude/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  98. package/.claude/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  99. package/.claude/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  100. package/.claude/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  101. package/.claude/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  102. package/.claude/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  103. package/.claude/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  104. package/.claude/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  105. package/.claude/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  106. package/.claude/skills/cfn-loop-validation/INDEX.md +531 -0
  107. package/.claude/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  108. package/.claude/skills/cfn-loop-validation/SKILL.md +48 -1
  109. package/.claude/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  110. package/.claude/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  111. package/.claude/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  112. package/.claude/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  113. package/{claude-assets/skills/cfn-redis-coordination/check-dependencies.sh → .claude/skills/cfn-loop-validation/check-dependencies.sh.backup} +4 -5
  114. package/.claude/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  115. package/.claude/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  116. package/.claude/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  117. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  118. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  119. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  120. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  121. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  122. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  123. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  124. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  125. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  126. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  127. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  128. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  129. package/.claude/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  130. package/.claude/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  131. package/.claude/skills/cfn-loop-validation/dist/types.js +66 -0
  132. package/.claude/skills/cfn-loop-validation/dist/types.js.map +1 -0
  133. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  134. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  135. package/.claude/skills/cfn-loop-validation/dist/validator.js +411 -0
  136. package/.claude/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  137. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  138. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  139. package/.claude/skills/cfn-loop-validation/package.json +93 -0
  140. package/.claude/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  141. package/.claude/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  142. package/.claude/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  143. package/.claude/skills/cfn-loop-validation/src/types.ts +215 -0
  144. package/.claude/skills/cfn-loop-validation/src/validator.ts +503 -0
  145. package/.claude/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  146. package/.claude/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  147. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  148. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  149. package/.claude/skills/cfn-loop-validation/validate-gate.sh +63 -0
  150. package/.claude/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  151. package/.claude/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  152. package/.claude/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  153. package/.claude/skills/cfn-product-owner-decision/SKILL.md +479 -147
  154. package/.claude/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  155. package/.claude/skills/cfn-product-owner-decision/{execute-decision.sh → archive/legacy-bash/execute-decision.sh} +24 -2
  156. package/.claude/skills/pre-edit-backup/SKILL.md +324 -0
  157. package/.claude/skills/pre-edit-backup/SKILL.md.backup +277 -0
  158. package/.claude/skills/pre-edit-backup/backup.sh +22 -0
  159. package/.claude/skills/pre-edit-backup/backup.sh.backup +107 -0
  160. package/claude-assets/agents/cfn-dev-team/analysts/root-cause-analyst.md +2 -2
  161. package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +1 -1
  162. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +3 -2
  163. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +1 -0
  164. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +2 -1
  165. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +1 -0
  166. package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +11 -1
  167. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +58 -35
  168. package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +2 -2
  169. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +47 -37
  170. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +18 -18
  171. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +40 -58
  172. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +19 -21
  173. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +20 -29
  174. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +15 -19
  175. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +15 -10
  176. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +16 -11
  177. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +16 -26
  178. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +18 -22
  179. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +17 -21
  180. package/claude-assets/agents/cfn-dev-team/documentation/pseudocode.md +1 -1
  181. package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +1 -1
  182. package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +1 -1
  183. package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +1 -1
  184. package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +1 -5
  185. package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +20 -51
  186. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +22 -71
  187. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +21 -64
  188. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +22 -67
  189. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +24 -68
  190. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +8 -36
  191. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +9 -38
  192. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +17 -55
  193. package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
  194. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +18 -56
  195. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +9 -37
  196. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +18 -56
  197. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +18 -49
  198. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +8 -37
  199. package/claude-assets/agents/cfn-dev-team/testers/tester.md +7 -27
  200. package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +1 -1
  201. package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +11 -0
  202. package/claude-assets/agents/cfn-dev-team/utility/analyst.md +13 -29
  203. package/claude-assets/agents/cfn-dev-team/utility/claude-code-expert.md +1 -1
  204. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +13 -13
  205. package/claude-assets/agents/cfn-dev-team/utility/context-curator.md +7 -2
  206. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +6 -11
  207. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +121 -715
  208. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +13 -22
  209. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +147 -573
  210. package/claude-assets/agents/custom/cfn-docker-expert.md +103 -0
  211. package/claude-assets/agents/custom/cfn-loops-cli-expert.md +438 -0
  212. package/claude-assets/agents/custom/cfn-redis-operations.md +529 -529
  213. package/claude-assets/agents/custom/cfn-system-expert.md +1 -1
  214. package/claude-assets/agents/custom/trigger-dev-expert.md +369 -0
  215. package/claude-assets/agents/docker-team/micro-sprint-planner.md +747 -747
  216. package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
  217. package/claude-assets/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  218. package/claude-assets/cfn-extras/skills/GOOGLE_SHEETS_SKILLS_README.md +1 -1
  219. package/claude-assets/cfn-extras/skills/google-sheets-api-coordinator/SKILL.md +1 -1
  220. package/claude-assets/cfn-extras/skills/google-sheets-formula-builder/SKILL.md +1 -1
  221. package/claude-assets/cfn-extras/skills/google-sheets-progress/SKILL.md +1 -1
  222. package/claude-assets/commands/CFN_LOOP_FRONTEND.md +1 -1
  223. package/claude-assets/commands/cfn-loop-cli.md +214 -442
  224. package/claude-assets/commands/cfn-loop-frontend.md +1 -1
  225. package/claude-assets/commands/cfn-loop-task.md +2 -2
  226. package/claude-assets/commands/cfn-loop-trigger.md +114 -0
  227. package/claude-assets/commands/deprecated/cfn-loop.md +2 -2
  228. package/claude-assets/hooks/GIT-HOOKS-USAGE-EXAMPLES.md +116 -0
  229. package/claude-assets/hooks/README-GIT-HOOKS.md +443 -0
  230. package/claude-assets/hooks/SKILL.md +518 -0
  231. package/claude-assets/hooks/SKILL.md.backup +471 -0
  232. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  233. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  234. package/claude-assets/hooks/cfn-invoke-post-edit.sh +53 -5
  235. package/claude-assets/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  236. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  237. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  238. package/claude-assets/hooks/cfn-invoke-pre-edit.sh +22 -0
  239. package/claude-assets/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  240. package/claude-assets/hooks/cfn-post-edit.config.json +9 -2
  241. package/claude-assets/hooks/install-git-hooks.sh +243 -0
  242. package/claude-assets/hooks/subagent-start.sh +98 -0
  243. package/claude-assets/hooks/subagent-stop.sh +93 -0
  244. package/claude-assets/hooks/validators/credential-scanner.sh +172 -0
  245. package/claude-assets/root-claude-distribute/CFN-CLAUDE.md +1 -1
  246. package/claude-assets/skills/cfn-agent-selection-with-fallback/DELIVERABLES.md +409 -0
  247. package/claude-assets/skills/cfn-agent-selection-with-fallback/IMPLEMENTATION_SUMMARY.md +396 -0
  248. package/claude-assets/skills/cfn-agent-selection-with-fallback/INTEGRATION_GUIDE.md +308 -0
  249. package/claude-assets/skills/cfn-agent-selection-with-fallback/QUICK_REFERENCE.md +239 -0
  250. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md +107 -1
  251. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md.backup +302 -0
  252. package/claude-assets/skills/cfn-agent-selection-with-fallback/TYPESCRIPT_MIGRATION.md +295 -0
  253. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.cjs +297 -0
  254. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.js +297 -0
  255. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.cjs +96 -0
  256. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.js +96 -0
  257. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh +45 -0
  258. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh.backup +23 -0
  259. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh +22 -0
  260. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh.backup +173 -0
  261. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.test.ts +357 -0
  262. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.ts +350 -0
  263. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/cli.ts +74 -0
  264. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh +22 -0
  265. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh.backup +71 -0
  266. package/claude-assets/skills/cfn-agent-selection-with-fallback/tsconfig.json +18 -0
  267. package/claude-assets/skills/cfn-agent-spawning/SKILL.md +48 -1
  268. package/claude-assets/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  269. package/claude-assets/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  270. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  271. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh.backup +30 -0
  272. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  273. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  274. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  275. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  276. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  277. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  278. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  279. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  280. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  281. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  282. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  283. package/claude-assets/skills/cfn-backlog-management/SKILL.md +1 -1
  284. package/claude-assets/skills/cfn-coordination/agent-completion.sh.backup +36 -0
  285. package/claude-assets/skills/cfn-coordination/coordination-signal.sh.backup +36 -0
  286. package/claude-assets/skills/cfn-coordination/coordination-wait.sh.backup +36 -0
  287. package/claude-assets/skills/cfn-dependency-ingestion/README.md +101 -0
  288. package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +397 -0
  289. package/claude-assets/skills/cfn-dependency-ingestion/build.sh +23 -0
  290. package/claude-assets/skills/cfn-dependency-ingestion/dist/ingest-dependencies.js +478 -0
  291. package/claude-assets/skills/cfn-dependency-ingestion/ingest-dependencies.sh +295 -0
  292. package/claude-assets/skills/cfn-dependency-ingestion/ingest.sh +237 -0
  293. package/claude-assets/skills/cfn-dependency-ingestion/manifests/cli-mode-dependencies.txt +73 -0
  294. package/claude-assets/skills/cfn-dependency-ingestion/manifests/shared-dependencies.txt +57 -0
  295. package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-dev-dependencies.txt +82 -0
  296. package/claude-assets/skills/cfn-dependency-ingestion/manifests/trigger-mode-dependencies.txt +80 -0
  297. package/claude-assets/skills/cfn-dependency-ingestion/src/ingest-dependencies.ts +563 -0
  298. package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +14 -4
  299. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  300. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  301. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  302. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  303. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  304. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  305. package/claude-assets/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  306. package/claude-assets/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  307. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  308. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  309. package/claude-assets/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  310. package/claude-assets/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  311. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  312. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  313. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  314. package/claude-assets/skills/cfn-loop-orchestration/SKILL.md +159 -48
  315. package/claude-assets/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  316. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  317. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  318. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  319. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  320. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  321. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  322. package/claude-assets/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  323. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  324. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  325. package/claude-assets/skills/cfn-loop-orchestration/{orchestrate-wrapper.sh → archive/legacy-bash/orchestrate-wrapper.sh} +11 -1
  326. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  327. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  328. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  329. package/claude-assets/skills/cfn-loop-orchestration/package-lock.json +3 -0
  330. package/claude-assets/skills/cfn-loop-orchestration/package.json +4 -0
  331. package/claude-assets/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  332. package/claude-assets/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  333. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  334. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  335. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  336. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  337. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  338. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  339. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  340. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  341. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  342. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  343. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  344. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  345. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  346. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  347. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  348. package/claude-assets/skills/cfn-loop-orchestration/src/index.ts +2 -0
  349. package/claude-assets/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  350. package/claude-assets/skills/cfn-loop-orchestration/src/types.ts +56 -0
  351. package/claude-assets/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  352. package/claude-assets/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  353. package/claude-assets/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  354. package/claude-assets/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  355. package/claude-assets/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  356. package/claude-assets/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  357. package/claude-assets/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  358. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  359. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  360. package/claude-assets/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  361. package/claude-assets/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  362. package/claude-assets/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  363. package/claude-assets/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  364. package/claude-assets/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  365. package/claude-assets/skills/cfn-loop-output-processing/.eslintrc.json +33 -0
  366. package/claude-assets/skills/cfn-loop-output-processing/DELIVERY_SUMMARY.txt +462 -0
  367. package/claude-assets/skills/cfn-loop-output-processing/DEPRECATION_NOTICE.md +183 -0
  368. package/claude-assets/skills/cfn-loop-output-processing/EXAMPLES.md +609 -0
  369. package/claude-assets/skills/cfn-loop-output-processing/IMPLEMENTATION_SUMMARY.md +418 -0
  370. package/claude-assets/skills/cfn-loop-output-processing/INDEX.md +531 -0
  371. package/claude-assets/skills/cfn-loop-output-processing/MIGRATION.md +362 -0
  372. package/claude-assets/skills/cfn-loop-output-processing/README.md +114 -0
  373. package/claude-assets/skills/cfn-loop-output-processing/SKILL.md +633 -0
  374. package/{.claude/skills/cfn-docker-redis-coordination → claude-assets/skills/cfn-loop-output-processing}/jest.config.js +7 -15
  375. package/claude-assets/skills/cfn-loop-output-processing/package.json +50 -0
  376. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop2.ts +195 -0
  377. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop3.ts +157 -0
  378. package/claude-assets/skills/cfn-loop-output-processing/src/output-processor.ts +632 -0
  379. package/claude-assets/skills/cfn-loop-output-processing/tests/output-processor.test.ts +617 -0
  380. package/claude-assets/skills/{cfn-docker-redis-coordination → cfn-loop-output-processing}/tsconfig.json +16 -7
  381. package/claude-assets/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  382. package/claude-assets/skills/cfn-loop-validation/INDEX.md +531 -0
  383. package/claude-assets/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  384. package/claude-assets/skills/cfn-loop-validation/SKILL.md +48 -1
  385. package/claude-assets/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  386. package/claude-assets/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  387. package/claude-assets/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  388. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  389. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh.backup +31 -0
  390. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  391. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  392. package/claude-assets/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  393. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  394. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  395. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  396. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  397. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  398. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  399. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  400. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  401. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  402. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  403. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  404. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  405. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  406. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  407. package/claude-assets/skills/cfn-loop-validation/dist/types.js +66 -0
  408. package/claude-assets/skills/cfn-loop-validation/dist/types.js.map +1 -0
  409. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  410. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  411. package/claude-assets/skills/cfn-loop-validation/dist/validator.js +411 -0
  412. package/claude-assets/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  413. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  414. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  415. package/claude-assets/skills/cfn-loop-validation/package.json +93 -0
  416. package/claude-assets/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  417. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  418. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  419. package/claude-assets/skills/cfn-loop-validation/src/types.ts +215 -0
  420. package/claude-assets/skills/cfn-loop-validation/src/validator.ts +503 -0
  421. package/claude-assets/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  422. package/claude-assets/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  423. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  424. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  425. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh +63 -0
  426. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  427. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  428. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  429. package/claude-assets/skills/cfn-product-owner-decision/SKILL.md +479 -147
  430. package/claude-assets/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  431. package/claude-assets/skills/cfn-product-owner-decision/{execute-decision.sh → archive/legacy-bash/execute-decision.sh} +24 -2
  432. package/claude-assets/skills/cfn-provider-routing/README.md +129 -0
  433. package/claude-assets/skills/cfn-provider-routing/SKILL.md +215 -0
  434. package/claude-assets/skills/cfn-provider-routing/resolve-provider-model.ts +223 -0
  435. package/claude-assets/skills/docker-build/build.sh +1 -1
  436. package/claude-assets/skills/pre-edit-backup/SKILL.md +324 -0
  437. package/claude-assets/skills/pre-edit-backup/SKILL.md.backup +277 -0
  438. package/claude-assets/skills/pre-edit-backup/backup.sh +22 -0
  439. package/claude-assets/skills/pre-edit-backup/backup.sh.backup +107 -0
  440. package/dist/agent/skill-mcp-selector.js +2 -1
  441. package/dist/agent/skill-mcp-selector.js.map +1 -1
  442. package/dist/api/auth-endpoints.js +415 -0
  443. package/dist/api/auth-endpoints.js.map +1 -0
  444. package/dist/api/task-endpoints.js +562 -0
  445. package/dist/api/task-endpoints.js.map +1 -0
  446. package/dist/backend/server.js +418 -0
  447. package/dist/backend/server.js.map +1 -0
  448. package/dist/cfn-loop/product-owner/decision-parser.js +356 -0
  449. package/dist/cfn-loop/product-owner/decision-parser.js.map +1 -0
  450. package/dist/cfn-loop/product-owner/index.js +1 -0
  451. package/dist/cfn-loop/product-owner/index.js.map +1 -1
  452. package/dist/cli/agent-command.js +1 -1
  453. package/dist/cli/agent-command.js.map +1 -1
  454. package/dist/cli/agent-completion.js +273 -0
  455. package/dist/cli/agent-completion.js.map +1 -0
  456. package/dist/cli/agent-executor.js +470 -26
  457. package/dist/cli/agent-executor.js.map +1 -1
  458. package/dist/cli/agent-prompt-builder.js +83 -48
  459. package/dist/cli/agent-prompt-builder.js.map +1 -1
  460. package/dist/cli/agent-spawn.js +7 -4
  461. package/dist/cli/agent-spawn.js.map +1 -1
  462. package/dist/cli/agent-spawner.js +546 -0
  463. package/dist/cli/agent-spawner.js.map +1 -0
  464. package/dist/cli/agent-token-manager.js +2 -1
  465. package/dist/cli/agent-token-manager.js.map +1 -1
  466. package/dist/cli/anthropic-client.js +127 -14
  467. package/dist/cli/anthropic-client.js.map +1 -1
  468. package/dist/cli/cfn-context.js +2 -1
  469. package/dist/cli/cfn-context.js.map +1 -1
  470. package/dist/cli/cfn-metrics.js +2 -1
  471. package/dist/cli/cfn-metrics.js.map +1 -1
  472. package/dist/cli/cfn-redis.js +2 -1
  473. package/dist/cli/cfn-redis.js.map +1 -1
  474. package/dist/cli/cli-agent-context.js +2 -0
  475. package/dist/cli/cli-agent-context.js.map +1 -1
  476. package/dist/cli/config-manager.js +90 -356
  477. package/dist/cli/config-manager.js.map +1 -1
  478. package/dist/cli/conversation-fork-cleanup.js +2 -1
  479. package/dist/cli/conversation-fork-cleanup.js.map +1 -1
  480. package/dist/cli/conversation-fork.js +2 -1
  481. package/dist/cli/conversation-fork.js.map +1 -1
  482. package/dist/cli/coordination/agent-messaging.js +415 -0
  483. package/dist/cli/coordination/agent-messaging.js.map +1 -0
  484. package/dist/cli/coordination/wait-for-threshold.js +232 -0
  485. package/dist/cli/coordination/wait-for-threshold.js.map +1 -0
  486. package/dist/cli/index.js +11 -0
  487. package/dist/cli/index.js.map +1 -1
  488. package/dist/cli/iteration-history.js +2 -1
  489. package/dist/cli/iteration-history.js.map +1 -1
  490. package/dist/cli/parse-decision-cli.js +268 -0
  491. package/dist/cli/parse-decision-cli.js.map +1 -0
  492. package/dist/cli/post-edit-hook.js +83 -0
  493. package/dist/cli/post-edit-hook.js.map +1 -0
  494. package/dist/cli/pre-edit-hook.js +77 -0
  495. package/dist/cli/pre-edit-hook.js.map +1 -0
  496. package/dist/cli/process-lifecycle.js +5 -1
  497. package/dist/cli/process-lifecycle.js.map +1 -1
  498. package/dist/cli/spawn-agent-cli.js +244 -0
  499. package/dist/cli/spawn-agent-cli.js.map +1 -0
  500. package/dist/coordination/coordination-wrapper.js +383 -0
  501. package/dist/coordination/coordination-wrapper.js.map +1 -0
  502. package/dist/coordination/redis-waiting-mode.js +4 -0
  503. package/dist/coordination/redis-waiting-mode.js.map +1 -1
  504. package/dist/coordination/store-success-criteria.js +68 -0
  505. package/dist/coordination/store-success-criteria.js.map +1 -0
  506. package/dist/coordination/store-task-context.js +65 -0
  507. package/dist/coordination/store-task-context.js.map +1 -0
  508. package/dist/hooks/backup-manager.js +273 -0
  509. package/dist/hooks/backup-manager.js.map +1 -0
  510. package/dist/hooks/post-edit-validator.js +388 -0
  511. package/dist/hooks/post-edit-validator.js.map +1 -0
  512. package/dist/integration/index.js +19 -0
  513. package/dist/integration/index.js.map +1 -0
  514. package/dist/integration/task-mode-adapter.js +297 -0
  515. package/dist/integration/task-mode-adapter.js.map +1 -0
  516. package/dist/integration/trigger-dev-client.js +253 -0
  517. package/dist/integration/trigger-dev-client.js.map +1 -0
  518. package/dist/integration/trigger-dev-webhooks.js +362 -0
  519. package/dist/integration/trigger-dev-webhooks.js.map +1 -0
  520. package/dist/lib/artifact-registry.js +4 -0
  521. package/dist/lib/artifact-registry.js.map +1 -1
  522. package/dist/lib/connection-pool.js +390 -0
  523. package/dist/lib/connection-pool.js.map +1 -0
  524. package/dist/lib/environment-contract.js +258 -0
  525. package/dist/lib/environment-contract.js.map +1 -0
  526. package/dist/lib/path-validator.js +14 -5
  527. package/dist/lib/path-validator.js.map +1 -1
  528. package/dist/lib/query-optimizer.js +388 -0
  529. package/dist/lib/query-optimizer.js.map +1 -0
  530. package/dist/lib/redis-queue-manager.js +5 -1
  531. package/dist/lib/redis-queue-manager.js.map +1 -1
  532. package/dist/lib/result-cache.js +285 -0
  533. package/dist/lib/result-cache.js.map +1 -0
  534. package/dist/mcp/auth-middleware.js +2 -1
  535. package/dist/mcp/auth-middleware.js.map +1 -1
  536. package/dist/mcp/playwright-mcp-server-auth.js +2 -1
  537. package/dist/mcp/playwright-mcp-server-auth.js.map +1 -1
  538. package/dist/middleware/authentication.js +317 -0
  539. package/dist/middleware/authentication.js.map +1 -0
  540. package/dist/services/authentication.js +669 -0
  541. package/dist/services/authentication.js.map +1 -0
  542. package/dist/services/session-management.js +436 -0
  543. package/dist/services/session-management.js.map +1 -0
  544. package/dist/services/skill-deployment.js +8 -6
  545. package/dist/services/skill-deployment.js.map +1 -1
  546. package/dist/services/user-service.js +710 -0
  547. package/dist/services/user-service.js.map +1 -0
  548. package/dist/types/trigger-dev-events.d.js +10 -0
  549. package/dist/types/trigger-dev-events.d.js.map +1 -0
  550. package/docs/README.md +240 -0
  551. package/package.json +15 -4
  552. package/scripts/build-agent-image.sh +1 -1
  553. package/scripts/compare-workflow-performance.sh +556 -0
  554. package/scripts/cost-allocation-tracker.sh +632 -0
  555. package/scripts/docker-rebuild-all-agents.sh +2 -2
  556. package/scripts/migrate-to-optimized-workflows.sh +438 -0
  557. package/scripts/organize-docs.sh +338 -0
  558. package/scripts/reorganize-tests.sh +280 -0
  559. package/scripts/trigger-dev-setup.sh +279 -0
  560. package/tests/README.md +45 -0
  561. package/.claude/commands/cost-savings-status.md +0 -34
  562. package/.claude/commands/metrics-summary.md +0 -58
  563. package/.claude/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  564. package/.claude/skills/cfn-docker-redis-coordination/README.md +0 -294
  565. package/.claude/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  566. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  567. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  568. package/.claude/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  569. package/.claude/skills/cfn-docker-redis-coordination/package.json +0 -40
  570. package/.claude/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  571. package/.claude/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  572. package/.claude/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  573. package/.claude/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  574. package/.claude/skills/cfn-docker-redis-coordination/tsconfig.json +0 -30
  575. package/.claude/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  576. package/.claude/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  577. package/.claude/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  578. package/.claude/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  579. package/.claude/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  580. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  581. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  582. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  583. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  584. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  585. package/.claude/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  586. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  587. package/.claude/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  588. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  589. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  590. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  591. package/.claude/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  592. package/.claude/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  593. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  594. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  595. package/.claude/skills/cfn-redis-coordination/config.json +0 -61
  596. package/.claude/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  597. package/.claude/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  598. package/.claude/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  599. package/.claude/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  600. package/.claude/skills/cfn-redis-coordination/jest.config.js +0 -23
  601. package/.claude/skills/cfn-redis-coordination/package-lock.json +0 -5272
  602. package/.claude/skills/cfn-redis-coordination/package.json +0 -45
  603. package/.claude/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  604. package/.claude/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  605. package/.claude/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  606. package/.claude/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  607. package/.claude/skills/cfn-redis-coordination/src/index.ts +0 -82
  608. package/.claude/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  609. package/.claude/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  610. package/.claude/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  611. package/.claude/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  612. package/.claude/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  613. package/.claude/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  614. package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  615. package/.claude/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  616. package/.claude/skills/cfn-redis-coordination/src/types.ts +0 -235
  617. package/.claude/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  618. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  619. package/.claude/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  620. package/.claude/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  621. package/.claude/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  622. package/.claude/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  623. package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  624. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  625. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +0 -980
  626. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +0 -759
  627. package/claude-assets/agents/custom/test-mcp-access.md +0 -24
  628. package/claude-assets/agents/typescript-specialist.md +0 -280
  629. package/claude-assets/commands/cost-savings-status.md +0 -34
  630. package/claude-assets/commands/metrics-summary.md +0 -58
  631. package/claude-assets/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  632. package/claude-assets/skills/cfn-docker-redis-coordination/README.md +0 -294
  633. package/claude-assets/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  634. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  635. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  636. package/claude-assets/skills/cfn-docker-redis-coordination/jest.config.js +0 -37
  637. package/claude-assets/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  638. package/claude-assets/skills/cfn-docker-redis-coordination/package.json +0 -40
  639. package/claude-assets/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  640. package/claude-assets/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  641. package/claude-assets/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  642. package/claude-assets/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  643. package/claude-assets/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  644. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  645. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  646. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  647. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  648. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  649. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  650. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  651. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  652. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  653. package/claude-assets/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  654. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  655. package/claude-assets/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  656. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  657. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  658. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  659. package/claude-assets/skills/cfn-redis-cleanup/cleanup-redis.sh +0 -130
  660. package/claude-assets/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  661. package/claude-assets/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  662. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  663. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  664. package/claude-assets/skills/cfn-redis-coordination/config.json +0 -61
  665. package/claude-assets/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  666. package/claude-assets/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  667. package/claude-assets/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  668. package/claude-assets/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  669. package/claude-assets/skills/cfn-redis-coordination/jest.config.js +0 -23
  670. package/claude-assets/skills/cfn-redis-coordination/package-lock.json +0 -5272
  671. package/claude-assets/skills/cfn-redis-coordination/package.json +0 -45
  672. package/claude-assets/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  673. package/claude-assets/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  674. package/claude-assets/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  675. package/claude-assets/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  676. package/claude-assets/skills/cfn-redis-coordination/src/index.ts +0 -82
  677. package/claude-assets/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  678. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  679. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  680. package/claude-assets/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  681. package/claude-assets/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  682. package/claude-assets/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  683. package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  684. package/claude-assets/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  685. package/claude-assets/skills/cfn-redis-coordination/src/types.ts +0 -235
  686. package/claude-assets/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  687. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  688. package/claude-assets/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  689. package/claude-assets/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  690. package/claude-assets/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  691. package/claude-assets/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  692. package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  693. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  694. package/claude-assets/skills/cfn-redis-data-extraction/SKILL.md +0 -442
  695. package/claude-assets/skills/cfn-redis-data-extraction/extract.sh +0 -306
  696. package/dist/coordination/index.js +0 -25
  697. package/dist/coordination/index.js.map +0 -1
  698. package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +0 -405
  699. package/docs/MEMORY_CLEANUP_GUIDE.md +0 -358
  700. package/docs/MEMORY_LEAK_FIX_SUMMARY.md +0 -322
  701. package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +0 -319
  702. package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +0 -574
  703. package/tests/test-memory-leak-task-mode.sh +0 -435
  704. /package/.claude/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  705. /package/.claude/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  706. /package/.claude/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  707. /package/.claude/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  708. /package/.claude/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  709. /package/.claude/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  710. /package/.claude/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  711. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  712. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  713. /package/.claude/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  714. /package/.claude/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  715. /package/.claude/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  716. /package/.claude/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  717. /package/.claude/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  718. /package/.claude/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  719. /package/.claude/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  720. /package/.claude/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  721. /package/.claude/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  722. /package/.claude/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
  723. /package/claude-assets/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  724. /package/claude-assets/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  725. /package/claude-assets/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  726. /package/claude-assets/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  727. /package/claude-assets/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  728. /package/claude-assets/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  729. /package/claude-assets/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  730. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  731. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  732. /package/claude-assets/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  733. /package/claude-assets/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  734. /package/claude-assets/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  735. /package/claude-assets/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  736. /package/claude-assets/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  737. /package/claude-assets/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  738. /package/claude-assets/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  739. /package/claude-assets/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  740. /package/claude-assets/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  741. /package/claude-assets/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/authentication.ts"],"sourcesContent":["/**\r\n * Authentication Service\r\n * \r\n * Comprehensive authentication service with JWT tokens, refresh tokens,\r\n * session management, and security features.\r\n */\r\n\r\nimport * as bcrypt from 'bcrypt';\r\nimport * as jwt from 'jsonwebtoken';\r\nimport { Redis } from 'ioredis';\r\nimport { Database } from 'better-sqlite3';\r\nimport { createLogger } from '../lib/logging.js';\r\nimport { StandardError, ErrorCode } from '../lib/errors.js';\r\nimport { AuthMiddleware } from '../middleware/auth-middleware.js';\r\n\r\nconst logger = createLogger('authentication-service');\r\n\r\nexport interface AuthUser {\r\n id: string;\r\n username: string;\r\n email: string;\r\n role: string;\r\n isActive?: boolean;\r\n}\r\n\r\nexport interface UserRegistrationRequest {\r\n username: string;\r\n email: string;\r\n password: string;\r\n role?: string;\r\n}\r\n\r\nexport interface LoginRequest {\r\n email: string;\r\n password: string;\r\n}\r\n\r\nexport interface TokenPair {\r\n accessToken: string;\r\n refreshToken: string;\r\n expiresIn: number;\r\n tokenType: string;\r\n}\r\n\r\nexport interface AuthConfig {\r\n redis: Redis;\r\n database: Database;\r\n jwtSecret: string;\r\n jwtExpiration?: string;\r\n refreshExpiration?: string;\r\n maxSessionsPerUser?: number;\r\n maxLoginAttempts?: number;\r\n lockoutDuration?: number;\r\n}\r\n\r\nexport interface UserProfile {\r\n id: string;\r\n username: string;\r\n email: string;\r\n role: string;\r\n createdAt: Date;\r\n lastLogin?: Date;\r\n}\r\n\r\nexport class AuthenticationService {\r\n private redis: Redis;\r\n private database: Database;\r\n private authMiddleware: AuthMiddleware;\r\n private config: Required<Omit<AuthConfig, 'redis' | 'database' | 'authMiddleware'>>;\r\n\r\n constructor(config: AuthConfig) {\r\n this.redis = config.redis;\r\n this.database = config.database;\r\n this.authMiddleware = new AuthMiddleware(config.jwtSecret);\r\n \r\n this.config = {\r\n jwtExpiration: config.jwtExpiration || '15m',\r\n refreshExpiration: config.refreshExpiration || '7d',\r\n maxSessionsPerUser: config.maxSessionsPerUser || 3,\r\n maxLoginAttempts: config.maxLoginAttempts || 5,\r\n lockoutDuration: config.lockoutDuration || 15 * 60 * 1000, // 15 minutes\r\n };\r\n\r\n this.initializeDatabase();\r\n }\r\n\r\n /**\r\n * Initialize database tables for user management\r\n */\r\n private initializeDatabase(): void {\r\n try {\r\n this.database.exec(`\r\n CREATE TABLE IF NOT EXISTS users (\r\n id TEXT PRIMARY KEY,\r\n username TEXT NOT NULL UNIQUE,\r\n email TEXT NOT NULL UNIQUE,\r\n password_hash TEXT NOT NULL,\r\n role TEXT NOT NULL DEFAULT 'developer',\r\n is_active BOOLEAN DEFAULT true,\r\n failed_login_attempts INTEGER DEFAULT 0,\r\n locked_until TIMESTAMP,\r\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\r\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\r\n last_login TIMESTAMP\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);\r\n CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);\r\n CREATE INDEX IF NOT EXISTS idx_users_active ON users(is_active);\r\n `);\r\n\r\n logger.info('Database tables initialized for authentication service');\r\n } catch (error) {\r\n logger.error('Failed to initialize database tables:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to initialize authentication service',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Register a new user\r\n */\r\n async registerUser(userData: UserRegistrationRequest): Promise<{ user: UserProfile; tokens: TokenPair }> {\r\n try {\r\n // Validate input\r\n this.validateRegistrationData(userData);\r\n\r\n // Check if user already exists\r\n const existingUser = this.database.prepare(`\r\n SELECT id FROM users WHERE email = ? OR username = ?\r\n `).get(userData.email, userData.username);\r\n\r\n if (existingUser) {\r\n throw new StandardError(\r\n ErrorCode.CONFLICT,\r\n 'User with this email or username already exists',\r\n { field: existingUser.id ? 'email' : 'username' }\r\n );\r\n }\r\n\r\n // Hash password\r\n const passwordHash = await bcrypt.hash(userData.password, 12);\r\n\r\n // Create user\r\n const userId = this.generateId();\r\n const now = new Date().toISOString();\r\n\r\n this.database.prepare(`\r\n INSERT INTO users (id, username, email, password_hash, role, created_at, updated_at)\r\n VALUES (?, ?, ?, ?, ?, ?, ?)\r\n `).run(\r\n userId,\r\n userData.username,\r\n userData.email,\r\n passwordHash,\r\n userData.role || 'developer',\r\n now,\r\n now\r\n );\r\n\r\n const user: UserProfile = {\r\n id: userId,\r\n username: userData.username,\r\n email: userData.email,\r\n role: userData.role || 'developer',\r\n createdAt: new Date(now),\r\n };\r\n\r\n // Generate tokens\r\n const tokens = await this.generateTokenPair(user);\r\n\r\n logger.info('User registered successfully', { userId, email: userData.email });\r\n\r\n return { user, tokens };\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('User registration failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Registration failed',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Authenticate user and generate tokens\r\n */\r\n async loginUser(loginData: LoginRequest, ipAddress?: string, userAgent?: string): Promise<{ user: UserProfile; tokens: TokenPair }> {\r\n try {\r\n // Get user by email\r\n const user = this.database.prepare(`\r\n SELECT id, username, email, password_hash, role, is_active, failed_login_attempts, locked_until\r\n FROM users WHERE email = ?\r\n `).get(loginData.email) as any;\r\n\r\n if (!user) {\r\n await this.recordFailedLogin(loginData.email, ipAddress);\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid email or password',\r\n { code: 'INVALID_CREDENTIALS' }\r\n );\r\n }\r\n\r\n // Check if account is locked\r\n if (user.locked_until && new Date(user.locked_until) > new Date()) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Account temporarily locked due to too many failed login attempts',\r\n { \r\n code: 'ACCOUNT_LOCKED',\r\n lockedUntil: user.locked_until\r\n }\r\n );\r\n }\r\n\r\n // Check if account is active\r\n if (!user.is_active) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Account is deactivated',\r\n { code: 'ACCOUNT_DEACTIVATED' }\r\n );\r\n }\r\n\r\n // Verify password\r\n const isPasswordValid = await bcrypt.compare(loginData.password, user.password_hash);\r\n if (!isPasswordValid) {\r\n await this.recordFailedLogin(loginData.email, ipAddress);\r\n await this.incrementFailedAttempts(user.id);\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid email or password',\r\n { code: 'INVALID_CREDENTIALS' }\r\n );\r\n }\r\n\r\n // Reset failed attempts on successful login\r\n await this.resetFailedAttempts(user.id);\r\n\r\n // Update last login\r\n this.database.prepare(`\r\n UPDATE users SET last_login = ?, updated_at = ? WHERE id = ?\r\n `).run(new Date().toISOString(), new Date().toISOString(), user.id);\r\n\r\n const userProfile: UserProfile = {\r\n id: user.id,\r\n username: user.username,\r\n email: user.email,\r\n role: user.role,\r\n createdAt: new Date(user.created_at),\r\n lastLogin: new Date(),\r\n };\r\n\r\n // Manage concurrent sessions\r\n await this.manageConcurrentSessions(user.id);\r\n\r\n // Generate tokens\r\n const tokens = await this.generateTokenPair(userProfile, ipAddress, userAgent);\r\n\r\n logger.info('User logged in successfully', { \r\n userId: user.id, \r\n email: user.email,\r\n ipAddress \r\n });\r\n\r\n return { user: userProfile, tokens };\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('Login failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Login failed',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Refresh access token using refresh token\r\n */\r\n async refreshToken(refreshToken: string): Promise<TokenPair> {\r\n try {\r\n // Validate refresh token\r\n const tokenData = await this.validateRefreshToken(refreshToken);\r\n \r\n // Get current user data\r\n const user = this.database.prepare(`\r\n SELECT id, username, email, role, is_active\r\n FROM users WHERE id = ? AND is_active = true\r\n `).get(tokenData.userId) as any;\r\n\r\n if (!user) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'User not found or inactive',\r\n { code: 'USER_INACTIVE' }\r\n );\r\n }\r\n\r\n const userProfile: UserProfile = {\r\n id: user.id,\r\n username: user.username,\r\n email: user.email,\r\n role: user.role,\r\n createdAt: new Date(),\r\n };\r\n\r\n // Generate new token pair (refresh token rotation)\r\n const tokens = await this.generateTokenPair(userProfile);\r\n\r\n // Invalidate old refresh token (rotation)\r\n await this.invalidateRefreshToken(refreshToken);\r\n\r\n logger.info('Token refreshed successfully', { userId: user.id });\r\n\r\n return tokens;\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('Token refresh failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Token refresh failed',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Logout user and invalidate tokens\r\n */\r\n async logout(userId: string, accessToken: string, refreshToken?: string): Promise<void> {\r\n try {\r\n const tokenPayload = this.authMiddleware.validateToken(accessToken);\r\n \r\n // Invalidate access token (add to blacklist)\r\n await this.addToBlacklist(accessToken, tokenPayload.exp);\r\n\r\n // Invalidate refresh token if provided\r\n if (refreshToken) {\r\n await this.invalidateRefreshToken(refreshToken);\r\n }\r\n\r\n // Remove session\r\n await this.removeSession(userId, tokenPayload.jti);\r\n\r\n logger.info('User logged out successfully', { userId });\r\n } catch (error) {\r\n logger.error('Logout failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Logout failed',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Logout from all sessions\r\n */\r\n async logoutAllSessions(userId: string, accessToken: string): Promise<void> {\r\n try {\r\n // Get all user sessions\r\n const sessionKey = `sessions:${userId}`;\r\n const sessionIds = await this.redis.smembers(sessionKey);\r\n\r\n // Remove all sessions\r\n for (const sessionId of sessionIds) {\r\n await this.redis.del(`session:${sessionId}`);\r\n }\r\n\r\n // Clear session set\r\n await this.redis.del(sessionKey);\r\n\r\n // Blacklist current access token\r\n const tokenPayload = this.authMiddleware.validateToken(accessToken);\r\n await this.addToBlacklist(accessToken, tokenPayload.exp);\r\n\r\n logger.info('All sessions terminated', { userId, sessionCount: sessionIds.length });\r\n } catch (error) {\r\n logger.error('Logout all sessions failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to terminate all sessions',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get user profile\r\n */\r\n async getUserProfile(userId: string): Promise<UserProfile> {\r\n try {\r\n const user = this.database.prepare(`\r\n SELECT id, username, email, role, created_at, last_login\r\n FROM users WHERE id = ? AND is_active = true\r\n `).get(userId) as any;\r\n\r\n if (!user) {\r\n throw new StandardError(\r\n ErrorCode.NOT_FOUND,\r\n 'User not found',\r\n { code: 'USER_NOT_FOUND' }\r\n );\r\n }\r\n\r\n return {\r\n id: user.id,\r\n username: user.username,\r\n email: user.email,\r\n role: user.role,\r\n createdAt: new Date(user.created_at),\r\n lastLogin: user.last_login ? new Date(user.last_login) : undefined,\r\n };\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('Get user profile failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to get user profile',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Update user profile\r\n */\r\n async updateUserProfile(userId: string, updates: Partial<{ username: string; email: string }>): Promise<UserProfile> {\r\n try {\r\n // Validate updates\r\n if (updates.username) {\r\n this.validateUsername(updates.username);\r\n \r\n // Check if username is already taken\r\n const existingUser = this.database.prepare(`\r\n SELECT id FROM users WHERE username = ? AND id != ?\r\n `).get(updates.username, userId);\r\n\r\n if (existingUser) {\r\n throw new StandardError(\r\n ErrorCode.CONFLICT,\r\n 'Username already taken',\r\n { field: 'username' }\r\n );\r\n }\r\n }\r\n\r\n if (updates.email) {\r\n this.validateEmail(updates.email);\r\n \r\n // Check if email is already taken\r\n const existingUser = this.database.prepare(`\r\n SELECT id FROM users WHERE email = ? AND id != ?\r\n `).get(updates.email, userId);\r\n\r\n if (existingUser) {\r\n throw new StandardError(\r\n ErrorCode.CONFLICT,\r\n 'Email already taken',\r\n { field: 'email' }\r\n );\r\n }\r\n }\r\n\r\n // Build update query\r\n const fields = [];\r\n const values = [];\r\n \r\n if (updates.username) {\r\n fields.push('username = ?');\r\n values.push(updates.username);\r\n }\r\n \r\n if (updates.email) {\r\n fields.push('email = ?');\r\n values.push(updates.email);\r\n }\r\n \r\n fields.push('updated_at = ?');\r\n values.push(new Date().toISOString());\r\n values.push(userId);\r\n\r\n // Update user\r\n if (fields.length > 1) {\r\n this.database.prepare(`\r\n UPDATE users SET ${fields.join(', ')} WHERE id = ?\r\n `).run(...values);\r\n }\r\n\r\n // Return updated profile\r\n return await this.getUserProfile(userId);\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('Update user profile failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to update profile',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Change user password\r\n */\r\n async changePassword(userId: string, currentPassword: string, newPassword: string): Promise<void> {\r\n try {\r\n // Validate new password\r\n this.validatePasswordStrength(newPassword);\r\n\r\n // Get current user\r\n const user = this.database.prepare(`\r\n SELECT id, password_hash\r\n FROM users WHERE id = ? AND is_active = true\r\n `).get(userId) as any;\r\n\r\n if (!user) {\r\n throw new StandardError(\r\n ErrorCode.NOT_FOUND,\r\n 'User not found',\r\n { code: 'USER_NOT_FOUND' }\r\n );\r\n }\r\n\r\n // Verify current password\r\n const isCurrentPasswordValid = await bcrypt.compare(currentPassword, user.password_hash);\r\n if (!isCurrentPasswordValid) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Current password is incorrect',\r\n { code: 'INVALID_CURRENT_PASSWORD' }\r\n );\r\n }\r\n\r\n // Hash new password\r\n const newPasswordHash = await bcrypt.hash(newPassword, 12);\r\n\r\n // Update password\r\n this.database.prepare(`\r\n UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?\r\n `).run(newPasswordHash, new Date().toISOString(), userId);\r\n\r\n // Invalidate all sessions (force re-login)\r\n await this.logoutAllSessions(userId, '');\r\n\r\n logger.info('Password changed successfully', { userId });\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n logger.error('Change password failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to change password',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Generate token pair for user\r\n */\r\n private async generateTokenPair(\r\n user: UserProfile, \r\n ipAddress?: string, \r\n userAgent?: string\r\n ): Promise<TokenPair> {\r\n try {\r\n // Generate access token\r\n const accessToken = this.authMiddleware.generateToken(\r\n user.id,\r\n user.username,\r\n user.role as any,\r\n user.email\r\n );\r\n\r\n // Generate refresh token\r\n const refreshToken = jwt.sign(\r\n { \r\n userId: user.id,\r\n type: 'refresh',\r\n sessionId: this.generateId(),\r\n },\r\n process.env.JWT_SECRET || 'default-secret',\r\n { expiresIn: this.config.refreshExpiration }\r\n );\r\n\r\n // Store refresh token in Redis\r\n const refreshKey = `refresh:${refreshToken}`;\r\n const refreshData = {\r\n userId: user.id,\r\n type: 'refresh',\r\n ipAddress,\r\n userAgent,\r\n createdAt: new Date().toISOString(),\r\n };\r\n\r\n await this.redis.setex(\r\n refreshKey,\r\n this.parseExpiration(this.config.refreshExpiration),\r\n JSON.stringify(refreshData)\r\n );\r\n\r\n // Store session\r\n const sessionId = this.generateId();\r\n const sessionKey = `session:${sessionId}`;\r\n const sessionData = {\r\n userId: user.id,\r\n username: user.username,\r\n email: user.email,\r\n role: user.role,\r\n ipAddress,\r\n userAgent,\r\n createdAt: new Date().toISOString(),\r\n };\r\n\r\n await this.redis.setex(\r\n sessionKey,\r\n this.parseExpiration(this.config.jwtExpiration),\r\n JSON.stringify(sessionData)\r\n );\r\n\r\n // Add session to user's session set\r\n await this.redis.sadd(`sessions:${user.id}`, sessionId);\r\n await this.redis.expire(`sessions:${user.id}`, this.parseExpiration(this.config.refreshExpiration));\r\n\r\n return {\r\n accessToken,\r\n refreshToken,\r\n expiresIn: this.parseExpiration(this.config.jwtExpiration),\r\n tokenType: 'Bearer',\r\n };\r\n } catch (error) {\r\n logger.error('Token generation failed:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to generate tokens',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Validate refresh token\r\n */\r\n private async validateRefreshToken(refreshToken: string): Promise<any> {\r\n try {\r\n // Check if refresh token exists in Redis\r\n const refreshKey = `refresh:${refreshToken}`;\r\n const tokenData = await this.redis.get(refreshKey);\r\n\r\n if (!tokenData) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid or expired refresh token',\r\n { code: 'INVALID_REFRESH_TOKEN' }\r\n );\r\n }\r\n\r\n // Verify JWT token\r\n const decoded = jwt.verify(refreshToken, process.env.JWT_SECRET || 'default-secret') as any;\r\n \r\n if (decoded.type !== 'refresh') {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid token type',\r\n { code: 'INVALID_TOKEN_TYPE' }\r\n );\r\n }\r\n\r\n return decoded;\r\n } catch (error) {\r\n if (error instanceof StandardError) {\r\n throw error;\r\n }\r\n if (error instanceof jwt.TokenExpiredError) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Refresh token has expired',\r\n { code: 'REFRESH_TOKEN_EXPIRED' }\r\n );\r\n }\r\n if (error instanceof jwt.JsonWebTokenError) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid refresh token',\r\n { code: 'INVALID_REFRESH_TOKEN' }\r\n );\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Invalidate refresh token\r\n */\r\n private async invalidateRefreshToken(refreshToken: string): Promise<void> {\r\n try {\r\n const refreshKey = `refresh:${refreshToken}`;\r\n await this.redis.del(refreshKey);\r\n } catch (error) {\r\n logger.error('Failed to invalidate refresh token:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Add token to blacklist\r\n */\r\n private async addToBlacklist(token: string, expirationTime: number): Promise<void> {\r\n try {\r\n const blacklistKey = `blacklist:${this.hashToken(token)}`;\r\n const ttl = expirationTime - Math.floor(Date.now() / 1000);\r\n \r\n if (ttl > 0) {\r\n await this.redis.setex(blacklistKey, ttl, '1');\r\n }\r\n } catch (error) {\r\n logger.error('Failed to add token to blacklist:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Remove session\r\n */\r\n private async removeSession(userId: string, sessionId: string): Promise<void> {\r\n try {\r\n await this.redis.del(`session:${sessionId}`);\r\n await this.redis.srem(`sessions:${userId}`, sessionId);\r\n } catch (error) {\r\n logger.error('Failed to remove session:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Manage concurrent sessions\r\n */\r\n private async manageConcurrentSessions(userId: string): Promise<void> {\r\n try {\r\n const sessionKey = `sessions:${userId}`;\r\n const sessionIds = await this.redis.smembers(sessionKey);\r\n\r\n if (sessionIds.length >= this.config.maxSessionsPerUser) {\r\n // Remove oldest session\r\n const oldestSessionId = sessionIds[0];\r\n await this.removeSession(userId, oldestSessionId);\r\n }\r\n } catch (error) {\r\n logger.error('Failed to manage concurrent sessions:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Record failed login attempt\r\n */\r\n private async recordFailedLogin(email: string, ipAddress?: string): Promise<void> {\r\n try {\r\n const key = `failed_login:${email}:${ipAddress || 'unknown'}`;\r\n await this.redis.incr(key);\r\n await this.redis.expire(key, this.config.lockoutDuration / 1000);\r\n } catch (error) {\r\n logger.error('Failed to record failed login:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Increment failed login attempts for user\r\n */\r\n private async incrementFailedAttempts(userId: string): Promise<void> {\r\n try {\r\n const user = this.database.prepare(`\r\n SELECT failed_login_attempts FROM users WHERE id = ?\r\n `).get(userId) as any;\r\n\r\n if (user) {\r\n const newAttempts = user.failed_login_attempts + 1;\r\n const lockedUntil = newAttempts >= this.config.maxLoginAttempts \r\n ? new Date(Date.now() + this.config.lockoutDuration).toISOString()\r\n : null;\r\n\r\n this.database.prepare(`\r\n UPDATE users SET failed_login_attempts = ?, locked_until = ?, updated_at = ?\r\n WHERE id = ?\r\n `).run(newAttempts, lockedUntil, new Date().toISOString(), userId);\r\n }\r\n } catch (error) {\r\n logger.error('Failed to increment failed attempts:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Reset failed login attempts for user\r\n */\r\n private async resetFailedAttempts(userId: string): Promise<void> {\r\n try {\r\n this.database.prepare(`\r\n UPDATE users SET failed_login_attempts = 0, locked_until = null, updated_at = ?\r\n WHERE id = ?\r\n `).run(new Date().toISOString(), userId);\r\n } catch (error) {\r\n logger.error('Failed to reset failed attempts:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Validation methods\r\n */\r\n private validateRegistrationData(data: UserRegistrationRequest): void {\r\n if (!data.username || data.username.trim().length < 3) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Username must be at least 3 characters long',\r\n { field: 'username' }\r\n );\r\n }\r\n\r\n this.validateUsername(data.username);\r\n this.validateEmail(data.email);\r\n this.validatePasswordStrength(data.password);\r\n\r\n if (data.role && !['admin', 'developer', 'readonly'].includes(data.role)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid role specified',\r\n { field: 'role' }\r\n );\r\n }\r\n }\r\n\r\n private validateUsername(username: string): void {\r\n if (!/^[a-zA-Z0-9_-]+$/.test(username)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Username can only contain letters, numbers, underscores, and hyphens',\r\n { field: 'username' }\r\n );\r\n }\r\n }\r\n\r\n private validateEmail(email: string): void {\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n if (!emailRegex.test(email)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Invalid email format',\r\n { field: 'email' }\r\n );\r\n }\r\n }\r\n\r\n private validatePasswordStrength(password: string): void {\r\n if (password.length < 8) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Password must be at least 8 characters long',\r\n { field: 'password' }\r\n );\r\n }\r\n\r\n if (!/[A-Z]/.test(password)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Password must contain at least one uppercase letter',\r\n { field: 'password' }\r\n );\r\n }\r\n\r\n if (!/[a-z]/.test(password)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Password must contain at least one lowercase letter',\r\n { field: 'password' }\r\n );\r\n }\r\n\r\n if (!/\\d/.test(password)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Password must contain at least one number',\r\n { field: 'password' }\r\n );\r\n }\r\n\r\n if (!/[!@#$%^&*(),.?\":{}|<>]/.test(password)) {\r\n throw new StandardError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Password must contain at least one special character',\r\n { field: 'password' }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Utility methods\r\n */\r\n private generateId(): string {\r\n return Math.random().toString(36).substring(2, 15) + \r\n Math.random().toString(36).substring(2, 15);\r\n }\r\n\r\n private parseExpiration(expiration: string): number {\r\n const match = expiration.match(/^(\\d+)([smhd])$/);\r\n if (!match) {\r\n return 3600; // Default to 1 hour\r\n }\r\n\r\n const value = parseInt(match[1], 10);\r\n const unit = match[2];\r\n\r\n switch (unit) {\r\n case 's': return value;\r\n case 'm': return value * 60;\r\n case 'h': return value * 3600;\r\n case 'd': return value * 86400;\r\n default: return 3600;\r\n }\r\n }\r\n\r\n private hashToken(token: string): string {\r\n return token.split('').reduce((acc, char) => {\r\n acc = ((acc << 5) - acc) + char.charCodeAt(0);\r\n return acc & acc;\r\n }, 0).toString(36);\r\n }\r\n}"],"names":["bcrypt","jwt","createLogger","StandardError","ErrorCode","AuthMiddleware","logger","AuthenticationService","redis","database","authMiddleware","config","jwtSecret","jwtExpiration","refreshExpiration","maxSessionsPerUser","maxLoginAttempts","lockoutDuration","initializeDatabase","exec","info","error","INTERNAL_ERROR","registerUser","userData","validateRegistrationData","existingUser","prepare","get","email","username","CONFLICT","field","id","passwordHash","hash","password","userId","generateId","now","Date","toISOString","run","role","user","createdAt","tokens","generateTokenPair","loginUser","loginData","ipAddress","userAgent","recordFailedLogin","VALIDATION_FAILED","code","locked_until","lockedUntil","is_active","isPasswordValid","compare","password_hash","incrementFailedAttempts","resetFailedAttempts","userProfile","created_at","lastLogin","manageConcurrentSessions","refreshToken","tokenData","validateRefreshToken","invalidateRefreshToken","logout","accessToken","tokenPayload","validateToken","addToBlacklist","exp","removeSession","jti","logoutAllSessions","sessionKey","sessionIds","smembers","sessionId","del","sessionCount","length","getUserProfile","NOT_FOUND","last_login","undefined","updateUserProfile","updates","validateUsername","validateEmail","fields","values","push","join","changePassword","currentPassword","newPassword","validatePasswordStrength","isCurrentPasswordValid","newPasswordHash","generateToken","sign","type","process","env","JWT_SECRET","expiresIn","refreshKey","refreshData","setex","parseExpiration","JSON","stringify","sessionData","sadd","expire","tokenType","decoded","verify","TokenExpiredError","JsonWebTokenError","token","expirationTime","blacklistKey","hashToken","ttl","Math","floor","srem","oldestSessionId","key","incr","newAttempts","failed_login_attempts","data","trim","includes","test","emailRegex","random","toString","substring","expiration","match","value","parseInt","unit","split","reduce","acc","char","charCodeAt"],"mappings":"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"}
@@ -0,0 +1,436 @@
1
+ /**
2
+ * Session Management Service
3
+ *
4
+ * Advanced session management with concurrent session limits,
5
+ * session monitoring, and security features.
6
+ */ import { createLogger } from '../lib/logging.js';
7
+ import { StandardError, ErrorCode } from '../lib/errors.js';
8
+ const logger = createLogger('session-management');
9
+ export class SessionManagementService {
10
+ redis;
11
+ config;
12
+ cleanupInterval;
13
+ constructor(redis, config = {}){
14
+ this.redis = redis;
15
+ this.config = {
16
+ maxSessionsPerUser: config.maxSessionsPerUser || 3,
17
+ sessionTimeoutMs: config.sessionTimeoutMs || 24 * 60 * 60 * 1000,
18
+ cleanupIntervalMs: config.cleanupIntervalMs || 60 * 60 * 1000,
19
+ enableSessionMonitoring: config.enableSessionMonitoring ?? true,
20
+ enableGeolocation: config.enableGeolocation ?? false
21
+ };
22
+ if (this.config.enableSessionMonitoring) {
23
+ this.startSessionCleanup();
24
+ }
25
+ }
26
+ /**
27
+ * Start automatic session cleanup
28
+ */ startSessionCleanup() {
29
+ this.cleanupInterval = setInterval(async ()=>{
30
+ try {
31
+ await this.cleanupExpiredSessions();
32
+ } catch (error) {
33
+ logger.error('Session cleanup failed:', error);
34
+ }
35
+ }, this.config.cleanupIntervalMs);
36
+ logger.info('Session cleanup started', {
37
+ interval: this.config.cleanupIntervalMs
38
+ });
39
+ }
40
+ /**
41
+ * Stop session cleanup
42
+ */ stopSessionCleanup() {
43
+ if (this.cleanupInterval) {
44
+ clearInterval(this.cleanupInterval);
45
+ this.cleanupInterval = undefined;
46
+ logger.info('Session cleanup stopped');
47
+ }
48
+ }
49
+ /**
50
+ * Create a new session
51
+ */ async createSession(sessionId, userId, sessionData) {
52
+ try {
53
+ const sessionInfo = {
54
+ ...sessionData,
55
+ sessionId,
56
+ lastActivity: new Date(),
57
+ isActive: true
58
+ };
59
+ // Check concurrent session limit
60
+ await this.enforceSessionLimit(userId);
61
+ // Store session data
62
+ const sessionKey = `session:${sessionId}`;
63
+ await this.redis.setex(sessionKey, Math.ceil(this.config.sessionTimeoutMs / 1000), JSON.stringify(sessionInfo));
64
+ // Add to user's session set
65
+ const userSessionsKey = `sessions:${userId}`;
66
+ await this.redis.sadd(userSessionsKey, sessionId);
67
+ await this.redis.expire(userSessionsKey, Math.ceil(this.config.sessionTimeoutMs / 1000));
68
+ // Track session statistics
69
+ if (this.config.enableSessionMonitoring) {
70
+ await this.trackSessionStats(sessionInfo);
71
+ }
72
+ logger.debug('Session created', {
73
+ sessionId,
74
+ userId
75
+ });
76
+ } catch (error) {
77
+ logger.error('Failed to create session:', error);
78
+ throw new StandardError(ErrorCode.INTERNAL_ERROR, 'Failed to create session', {}, error);
79
+ }
80
+ }
81
+ /**
82
+ * Get session information
83
+ */ async getSession(sessionId) {
84
+ try {
85
+ const sessionKey = `session:${sessionId}`;
86
+ const sessionData = await this.redis.get(sessionKey);
87
+ if (!sessionData) {
88
+ return null;
89
+ }
90
+ const sessionInfo = JSON.parse(sessionData);
91
+ sessionInfo.createdAt = new Date(sessionInfo.createdAt);
92
+ sessionInfo.lastActivity = new Date(sessionInfo.lastActivity);
93
+ // Update last activity
94
+ sessionInfo.lastActivity = new Date();
95
+ await this.updateSessionActivity(sessionId, sessionInfo);
96
+ return sessionInfo;
97
+ } catch (error) {
98
+ logger.error('Failed to get session:', error);
99
+ return null;
100
+ }
101
+ }
102
+ /**
103
+ * Update session activity
104
+ */ async updateSessionActivity(sessionId, sessionInfo) {
105
+ try {
106
+ const sessionKey = `session:${sessionId}`;
107
+ sessionInfo.lastActivity = new Date();
108
+ await this.redis.setex(sessionKey, Math.ceil(this.config.sessionTimeoutMs / 1000), JSON.stringify(sessionInfo));
109
+ } catch (error) {
110
+ logger.error('Failed to update session activity:', error);
111
+ }
112
+ }
113
+ /**
114
+ * Remove a session
115
+ */ async removeSession(sessionId, userId) {
116
+ try {
117
+ // Get session info before deletion for logging
118
+ const sessionInfo = await this.getSession(sessionId);
119
+ // Delete session data
120
+ const sessionKey = `session:${sessionId}`;
121
+ await this.redis.del(sessionKey);
122
+ // Remove from user's session set
123
+ if (userId || sessionInfo?.userId) {
124
+ const userSessionsKey = `sessions:${userId || sessionInfo.userId}`;
125
+ await this.redis.srem(userSessionsKey, sessionId);
126
+ }
127
+ logger.debug('Session removed', {
128
+ sessionId,
129
+ userId: userId || sessionInfo?.userId
130
+ });
131
+ } catch (error) {
132
+ logger.error('Failed to remove session:', error);
133
+ }
134
+ }
135
+ /**
136
+ * Remove all sessions for a user
137
+ */ async removeAllUserSessions(userId) {
138
+ try {
139
+ const userSessionsKey = `sessions:${userId}`;
140
+ const sessionIds = await this.redis.smembers(userSessionsKey);
141
+ // Remove all sessions
142
+ const removePromises = sessionIds.map((sessionId)=>this.removeSession(sessionId, userId));
143
+ await Promise.all(removePromises);
144
+ // Clear session set
145
+ await this.redis.del(userSessionsKey);
146
+ logger.info('All user sessions removed', {
147
+ userId,
148
+ sessionCount: sessionIds.length
149
+ });
150
+ return sessionIds;
151
+ } catch (error) {
152
+ logger.error('Failed to remove all user sessions:', error);
153
+ throw new StandardError(ErrorCode.INTERNAL_ERROR, 'Failed to remove all sessions', {}, error);
154
+ }
155
+ }
156
+ /**
157
+ * Get all active sessions for a user
158
+ */ async getUserSessions(userId) {
159
+ try {
160
+ const userSessionsKey = `sessions:${userId}`;
161
+ const sessionIds = await this.redis.smembers(userSessionsKey);
162
+ if (sessionIds.length === 0) {
163
+ return [];
164
+ }
165
+ // Get session data for all sessions
166
+ const sessionPromises = sessionIds.map(async (sessionId)=>{
167
+ const sessionKey = `session:${sessionId}`;
168
+ const sessionData = await this.redis.get(sessionKey);
169
+ if (sessionData) {
170
+ const sessionInfo = JSON.parse(sessionData);
171
+ sessionInfo.createdAt = new Date(sessionInfo.createdAt);
172
+ sessionInfo.lastActivity = new Date(sessionInfo.lastActivity);
173
+ return sessionInfo;
174
+ }
175
+ return null;
176
+ });
177
+ const sessions = (await Promise.all(sessionPromises)).filter((session)=>session !== null);
178
+ // Clean up any stale session IDs
179
+ const validSessionIds = sessions.map((s)=>s.sessionId);
180
+ const staleSessionIds = sessionIds.filter((id)=>!validSessionIds.includes(id));
181
+ if (staleSessionIds.length > 0) {
182
+ for (const staleId of staleSessionIds){
183
+ await this.redis.srem(userSessionsKey, staleId);
184
+ }
185
+ }
186
+ return sessions.sort((a, b)=>b.lastActivity.getTime() - a.lastActivity.getTime());
187
+ } catch (error) {
188
+ logger.error('Failed to get user sessions:', error);
189
+ return [];
190
+ }
191
+ }
192
+ /**
193
+ * Enforce concurrent session limit
194
+ */ async enforceSessionLimit(userId) {
195
+ try {
196
+ const sessions = await this.getUserSessions(userId);
197
+ if (sessions.length >= this.config.maxSessionsPerUser) {
198
+ // Remove oldest session(s) to make room
199
+ const sessionsToRemove = sessions.sort((a, b)=>a.lastActivity.getTime() - b.lastActivity.getTime()).slice(0, sessions.length - this.config.maxSessionsPerUser + 1);
200
+ for (const session of sessionsToRemove){
201
+ await this.removeSession(session.sessionId, userId);
202
+ logger.info('Session removed due to limit enforcement', {
203
+ sessionId: session.sessionId,
204
+ userId,
205
+ lastActivity: session.lastActivity
206
+ });
207
+ }
208
+ }
209
+ } catch (error) {
210
+ logger.error('Failed to enforce session limit:', error);
211
+ }
212
+ }
213
+ /**
214
+ * Clean up expired sessions
215
+ */ async cleanupExpiredSessions() {
216
+ try {
217
+ // Get all session keys
218
+ const sessionKeys = await this.redis.keys('session:*');
219
+ let cleanedCount = 0;
220
+ for (const sessionKey of sessionKeys){
221
+ const sessionData = await this.redis.get(sessionKey);
222
+ if (sessionData) {
223
+ try {
224
+ const sessionInfo = JSON.parse(sessionData);
225
+ const lastActivity = new Date(sessionInfo.lastActivity);
226
+ const now = new Date();
227
+ // Check if session has expired
228
+ if (now.getTime() - lastActivity.getTime() > this.config.sessionTimeoutMs) {
229
+ const sessionId = sessionKey.replace('session:', '');
230
+ await this.removeSession(sessionId, sessionInfo.userId);
231
+ cleanedCount++;
232
+ }
233
+ } catch (parseError) {
234
+ // Remove corrupted session data
235
+ await this.redis.del(sessionKey);
236
+ cleanedCount++;
237
+ }
238
+ } else {
239
+ // Remove expired key
240
+ await this.redis.del(sessionKey);
241
+ cleanedCount++;
242
+ }
243
+ }
244
+ if (cleanedCount > 0) {
245
+ logger.info('Session cleanup completed', {
246
+ cleanedCount,
247
+ totalKeys: sessionKeys.length
248
+ });
249
+ }
250
+ } catch (error) {
251
+ logger.error('Session cleanup failed:', error);
252
+ }
253
+ }
254
+ /**
255
+ * Track session statistics
256
+ */ async trackSessionStats(sessionInfo) {
257
+ try {
258
+ const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
259
+ // Track daily session count
260
+ await this.redis.incr(`stats:sessions:daily:${today}`);
261
+ await this.redis.expire(`stats:sessions:daily:${today}`, 7 * 24 * 60 * 60); // Keep for 7 days
262
+ // Track sessions by device type (simple user agent parsing)
263
+ const deviceType = this.parseDeviceType(sessionInfo.userAgent || '');
264
+ await this.redis.incr(`stats:sessions:device:${deviceType}`);
265
+ await this.redis.expire(`stats:sessions:device:${deviceType}`, 30 * 24 * 60 * 60); // Keep for 30 days
266
+ // Track sessions by location (if geolocation is enabled)
267
+ if (this.config.enableGeolocation && sessionInfo.location) {
268
+ const country = sessionInfo.location.split(',')[0] || 'unknown';
269
+ await this.redis.incr(`stats:sessions:location:${country}`);
270
+ await this.redis.expire(`stats:sessions:location:${country}`, 30 * 24 * 60 * 60);
271
+ }
272
+ } catch (error) {
273
+ logger.error('Failed to track session stats:', error);
274
+ }
275
+ }
276
+ /**
277
+ * Get session statistics
278
+ */ async getSessionStats() {
279
+ try {
280
+ const today = new Date().toISOString().split('T')[0];
281
+ // Get total active sessions
282
+ const sessionKeys = await this.redis.keys('session:*');
283
+ const totalSessions = sessionKeys.length;
284
+ // Get active sessions (non-expired)
285
+ let activeSessions = 0;
286
+ const sessionsByDevice = {};
287
+ const sessionsByLocation = {};
288
+ let oldestSession;
289
+ let newestSession;
290
+ for (const sessionKey of sessionKeys){
291
+ const sessionData = await this.redis.get(sessionKey);
292
+ if (sessionData) {
293
+ try {
294
+ const sessionInfo = JSON.parse(sessionData);
295
+ activeSessions++;
296
+ const createdAt = new Date(sessionInfo.createdAt);
297
+ if (!oldestSession || createdAt < oldestSession) {
298
+ oldestSession = createdAt;
299
+ }
300
+ if (!newestSession || createdAt > newestSession) {
301
+ newestSession = createdAt;
302
+ }
303
+ // Count by device type
304
+ const deviceType = this.parseDeviceType(sessionInfo.userAgent || '');
305
+ sessionsByDevice[deviceType] = (sessionsByDevice[deviceType] || 0) + 1;
306
+ // Count by location
307
+ if (sessionInfo.location) {
308
+ const country = sessionInfo.location.split(',')[0] || 'unknown';
309
+ sessionsByLocation[country] = (sessionsByLocation[country] || 0) + 1;
310
+ }
311
+ } catch (parseError) {
312
+ // Skip corrupted data
313
+ }
314
+ }
315
+ }
316
+ // Get average session duration (approximate)
317
+ const averageSessionDuration = 0; // Would need additional tracking
318
+ return {
319
+ totalSessions,
320
+ activeSessions,
321
+ sessionsByDevice,
322
+ sessionsByLocation,
323
+ averageSessionDuration,
324
+ oldestSession,
325
+ newestSession
326
+ };
327
+ } catch (error) {
328
+ logger.error('Failed to get session stats:', error);
329
+ return {
330
+ totalSessions: 0,
331
+ activeSessions: 0,
332
+ sessionsByDevice: {},
333
+ sessionsByLocation: {},
334
+ averageSessionDuration: 0
335
+ };
336
+ }
337
+ }
338
+ /**
339
+ * Parse device type from user agent
340
+ */ parseDeviceType(userAgent) {
341
+ const ua = userAgent.toLowerCase();
342
+ if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
343
+ return 'mobile';
344
+ } else if (ua.includes('tablet') || ua.includes('ipad')) {
345
+ return 'tablet';
346
+ } else if (ua.includes('bot') || ua.includes('crawler') || ua.includes('spider')) {
347
+ return 'bot';
348
+ } else {
349
+ return 'desktop';
350
+ }
351
+ }
352
+ /**
353
+ * Terminate suspicious sessions
354
+ */ async terminateSuspiciousSessions(userId, criteria) {
355
+ try {
356
+ const sessions = await this.getUserSessions(userId);
357
+ const terminatedSessions = [];
358
+ const now = new Date();
359
+ for (const session of sessions){
360
+ let shouldTerminate = false;
361
+ // Check age
362
+ if (criteria.maxAgeHours) {
363
+ const ageHours = (now.getTime() - session.createdAt.getTime()) / (1000 * 60 * 60);
364
+ if (ageHours > criteria.maxAgeHours) {
365
+ shouldTerminate = true;
366
+ }
367
+ }
368
+ // Check IP
369
+ if (criteria.suspiciousIPs?.includes(session.ipAddress || '')) {
370
+ shouldTerminate = true;
371
+ }
372
+ // Check user agent
373
+ if (criteria.suspiciousUserAgents?.some((ua)=>session.userAgent?.toLowerCase().includes(ua.toLowerCase()))) {
374
+ shouldTerminate = true;
375
+ }
376
+ if (shouldTerminate) {
377
+ await this.removeSession(session.sessionId, userId);
378
+ terminatedSessions.push(session.sessionId);
379
+ logger.warn('Suspicious session terminated', {
380
+ sessionId: session.sessionId,
381
+ userId,
382
+ ipAddress: session.ipAddress,
383
+ userAgent: session.userAgent
384
+ });
385
+ }
386
+ }
387
+ return terminatedSessions;
388
+ } catch (error) {
389
+ logger.error('Failed to terminate suspicious sessions:', error);
390
+ throw new StandardError(ErrorCode.INTERNAL_ERROR, 'Failed to terminate suspicious sessions', {}, error);
391
+ }
392
+ }
393
+ /**
394
+ * Get session activity timeline
395
+ */ async getSessionActivityTimeline(userId, hours = 24) {
396
+ try {
397
+ const sessions = await this.getUserSessions(userId);
398
+ const timeline = [];
399
+ for (const session of sessions){
400
+ timeline.push({
401
+ timestamp: session.createdAt,
402
+ action: 'session_created',
403
+ details: {
404
+ sessionId: session.sessionId,
405
+ ipAddress: session.ipAddress,
406
+ userAgent: session.userAgent
407
+ }
408
+ });
409
+ timeline.push({
410
+ timestamp: session.lastActivity,
411
+ action: 'last_activity',
412
+ details: {
413
+ sessionId: session.sessionId,
414
+ duration: session.lastActivity.getTime() - session.createdAt.getTime()
415
+ }
416
+ });
417
+ }
418
+ // Sort by timestamp
419
+ timeline.sort((a, b)=>b.timestamp.getTime() - a.timestamp.getTime());
420
+ // Filter by time range
421
+ const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
422
+ return timeline.filter((entry)=>entry.timestamp > cutoff);
423
+ } catch (error) {
424
+ logger.error('Failed to get session activity timeline:', error);
425
+ return [];
426
+ }
427
+ }
428
+ /**
429
+ * Cleanup resources
430
+ */ cleanup() {
431
+ this.stopSessionCleanup();
432
+ logger.info('Session management service cleaned up');
433
+ }
434
+ }
435
+
436
+ //# sourceMappingURL=session-management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/session-management.ts"],"sourcesContent":["/**\r\n * Session Management Service\r\n * \r\n * Advanced session management with concurrent session limits,\r\n * session monitoring, and security features.\r\n */\r\n\r\nimport { Redis } from 'ioredis';\r\nimport { createLogger } from '../lib/logging.js';\r\nimport { StandardError, ErrorCode } from '../lib/errors.js';\r\n\r\nconst logger = createLogger('session-management');\r\n\r\nexport interface SessionInfo {\r\n sessionId: string;\r\n userId: string;\r\n username: string;\r\n email: string;\r\n role: string;\r\n ipAddress?: string;\r\n userAgent?: string;\r\n createdAt: Date;\r\n lastActivity: Date;\r\n isActive: boolean;\r\n deviceInfo?: string;\r\n location?: string;\r\n}\r\n\r\nexport interface SessionStats {\r\n totalSessions: number;\r\n activeSessions: number;\r\n sessionsByDevice: Record<string, number>;\r\n sessionsByLocation: Record<string, number>;\r\n averageSessionDuration: number;\r\n oldestSession?: Date;\r\n newestSession?: Date;\r\n}\r\n\r\nexport interface SessionConfig {\r\n maxSessionsPerUser: number;\r\n sessionTimeoutMs: number;\r\n cleanupIntervalMs: number;\r\n enableSessionMonitoring: boolean;\r\n enableGeolocation: boolean;\r\n}\r\n\r\nexport class SessionManagementService {\r\n private redis: Redis;\r\n private config: SessionConfig;\r\n private cleanupInterval?: NodeJS.Timeout;\r\n\r\n constructor(redis: Redis, config: Partial<SessionConfig> = {}) {\r\n this.redis = redis;\r\n this.config = {\r\n maxSessionsPerUser: config.maxSessionsPerUser || 3,\r\n sessionTimeoutMs: config.sessionTimeoutMs || 24 * 60 * 60 * 1000, // 24 hours\r\n cleanupIntervalMs: config.cleanupIntervalMs || 60 * 60 * 1000, // 1 hour\r\n enableSessionMonitoring: config.enableSessionMonitoring ?? true,\r\n enableGeolocation: config.enableGeolocation ?? false,\r\n };\r\n\r\n if (this.config.enableSessionMonitoring) {\r\n this.startSessionCleanup();\r\n }\r\n }\r\n\r\n /**\r\n * Start automatic session cleanup\r\n */\r\n private startSessionCleanup(): void {\r\n this.cleanupInterval = setInterval(async () => {\r\n try {\r\n await this.cleanupExpiredSessions();\r\n } catch (error) {\r\n logger.error('Session cleanup failed:', error);\r\n }\r\n }, this.config.cleanupIntervalMs);\r\n\r\n logger.info('Session cleanup started', { interval: this.config.cleanupIntervalMs });\r\n }\r\n\r\n /**\r\n * Stop session cleanup\r\n */\r\n public stopSessionCleanup(): void {\r\n if (this.cleanupInterval) {\r\n clearInterval(this.cleanupInterval);\r\n this.cleanupInterval = undefined;\r\n logger.info('Session cleanup stopped');\r\n }\r\n }\r\n\r\n /**\r\n * Create a new session\r\n */\r\n async createSession(\r\n sessionId: string,\r\n userId: string,\r\n sessionData: Omit<SessionInfo, 'sessionId' | 'lastActivity' | 'isActive'>\r\n ): Promise<void> {\r\n try {\r\n const sessionInfo: SessionInfo = {\r\n ...sessionData,\r\n sessionId,\r\n lastActivity: new Date(),\r\n isActive: true,\r\n };\r\n\r\n // Check concurrent session limit\r\n await this.enforceSessionLimit(userId);\r\n\r\n // Store session data\r\n const sessionKey = `session:${sessionId}`;\r\n await this.redis.setex(\r\n sessionKey,\r\n Math.ceil(this.config.sessionTimeoutMs / 1000),\r\n JSON.stringify(sessionInfo)\r\n );\r\n\r\n // Add to user's session set\r\n const userSessionsKey = `sessions:${userId}`;\r\n await this.redis.sadd(userSessionsKey, sessionId);\r\n await this.redis.expire(userSessionsKey, Math.ceil(this.config.sessionTimeoutMs / 1000));\r\n\r\n // Track session statistics\r\n if (this.config.enableSessionMonitoring) {\r\n await this.trackSessionStats(sessionInfo);\r\n }\r\n\r\n logger.debug('Session created', { sessionId, userId });\r\n } catch (error) {\r\n logger.error('Failed to create session:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to create session',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get session information\r\n */\r\n async getSession(sessionId: string): Promise<SessionInfo | null> {\r\n try {\r\n const sessionKey = `session:${sessionId}`;\r\n const sessionData = await this.redis.get(sessionKey);\r\n\r\n if (!sessionData) {\r\n return null;\r\n }\r\n\r\n const sessionInfo: SessionInfo = JSON.parse(sessionData);\r\n sessionInfo.createdAt = new Date(sessionInfo.createdAt);\r\n sessionInfo.lastActivity = new Date(sessionInfo.lastActivity);\r\n\r\n // Update last activity\r\n sessionInfo.lastActivity = new Date();\r\n await this.updateSessionActivity(sessionId, sessionInfo);\r\n\r\n return sessionInfo;\r\n } catch (error) {\r\n logger.error('Failed to get session:', error);\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Update session activity\r\n */\r\n async updateSessionActivity(sessionId: string, sessionInfo: SessionInfo): Promise<void> {\r\n try {\r\n const sessionKey = `session:${sessionId}`;\r\n sessionInfo.lastActivity = new Date();\r\n\r\n await this.redis.setex(\r\n sessionKey,\r\n Math.ceil(this.config.sessionTimeoutMs / 1000),\r\n JSON.stringify(sessionInfo)\r\n );\r\n } catch (error) {\r\n logger.error('Failed to update session activity:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Remove a session\r\n */\r\n async removeSession(sessionId: string, userId?: string): Promise<void> {\r\n try {\r\n // Get session info before deletion for logging\r\n const sessionInfo = await this.getSession(sessionId);\r\n\r\n // Delete session data\r\n const sessionKey = `session:${sessionId}`;\r\n await this.redis.del(sessionKey);\r\n\r\n // Remove from user's session set\r\n if (userId || sessionInfo?.userId) {\r\n const userSessionsKey = `sessions:${userId || sessionInfo!.userId}`;\r\n await this.redis.srem(userSessionsKey, sessionId);\r\n }\r\n\r\n logger.debug('Session removed', { sessionId, userId: userId || sessionInfo?.userId });\r\n } catch (error) {\r\n logger.error('Failed to remove session:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Remove all sessions for a user\r\n */\r\n async removeAllUserSessions(userId: string): Promise<string[]> {\r\n try {\r\n const userSessionsKey = `sessions:${userId}`;\r\n const sessionIds = await this.redis.smembers(userSessionsKey);\r\n\r\n // Remove all sessions\r\n const removePromises = sessionIds.map(sessionId => \r\n this.removeSession(sessionId, userId)\r\n );\r\n await Promise.all(removePromises);\r\n\r\n // Clear session set\r\n await this.redis.del(userSessionsKey);\r\n\r\n logger.info('All user sessions removed', { userId, sessionCount: sessionIds.length });\r\n return sessionIds;\r\n } catch (error) {\r\n logger.error('Failed to remove all user sessions:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to remove all sessions',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get all active sessions for a user\r\n */\r\n async getUserSessions(userId: string): Promise<SessionInfo[]> {\r\n try {\r\n const userSessionsKey = `sessions:${userId}`;\r\n const sessionIds = await this.redis.smembers(userSessionsKey);\r\n\r\n if (sessionIds.length === 0) {\r\n return [];\r\n }\r\n\r\n // Get session data for all sessions\r\n const sessionPromises = sessionIds.map(async (sessionId) => {\r\n const sessionKey = `session:${sessionId}`;\r\n const sessionData = await this.redis.get(sessionKey);\r\n \r\n if (sessionData) {\r\n const sessionInfo: SessionInfo = JSON.parse(sessionData);\r\n sessionInfo.createdAt = new Date(sessionInfo.createdAt);\r\n sessionInfo.lastActivity = new Date(sessionInfo.lastActivity);\r\n return sessionInfo;\r\n }\r\n \r\n return null;\r\n });\r\n\r\n const sessions = (await Promise.all(sessionPromises)).filter(\r\n (session): session is SessionInfo => session !== null\r\n );\r\n\r\n // Clean up any stale session IDs\r\n const validSessionIds = sessions.map(s => s.sessionId);\r\n const staleSessionIds = sessionIds.filter(id => !validSessionIds.includes(id));\r\n \r\n if (staleSessionIds.length > 0) {\r\n for (const staleId of staleSessionIds) {\r\n await this.redis.srem(userSessionsKey, staleId);\r\n }\r\n }\r\n\r\n return sessions.sort((a, b) => b.lastActivity.getTime() - a.lastActivity.getTime());\r\n } catch (error) {\r\n logger.error('Failed to get user sessions:', error);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Enforce concurrent session limit\r\n */\r\n private async enforceSessionLimit(userId: string): Promise<void> {\r\n try {\r\n const sessions = await this.getUserSessions(userId);\r\n \r\n if (sessions.length >= this.config.maxSessionsPerUser) {\r\n // Remove oldest session(s) to make room\r\n const sessionsToRemove = sessions\r\n .sort((a, b) => a.lastActivity.getTime() - b.lastActivity.getTime())\r\n .slice(0, sessions.length - this.config.maxSessionsPerUser + 1);\r\n\r\n for (const session of sessionsToRemove) {\r\n await this.removeSession(session.sessionId, userId);\r\n logger.info('Session removed due to limit enforcement', {\r\n sessionId: session.sessionId,\r\n userId,\r\n lastActivity: session.lastActivity,\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n logger.error('Failed to enforce session limit:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Clean up expired sessions\r\n */\r\n private async cleanupExpiredSessions(): Promise<void> {\r\n try {\r\n // Get all session keys\r\n const sessionKeys = await this.redis.keys('session:*');\r\n let cleanedCount = 0;\r\n\r\n for (const sessionKey of sessionKeys) {\r\n const sessionData = await this.redis.get(sessionKey);\r\n \r\n if (sessionData) {\r\n try {\r\n const sessionInfo: SessionInfo = JSON.parse(sessionData);\r\n const lastActivity = new Date(sessionInfo.lastActivity);\r\n const now = new Date();\r\n \r\n // Check if session has expired\r\n if (now.getTime() - lastActivity.getTime() > this.config.sessionTimeoutMs) {\r\n const sessionId = sessionKey.replace('session:', '');\r\n await this.removeSession(sessionId, sessionInfo.userId);\r\n cleanedCount++;\r\n }\r\n } catch (parseError) {\r\n // Remove corrupted session data\r\n await this.redis.del(sessionKey);\r\n cleanedCount++;\r\n }\r\n } else {\r\n // Remove expired key\r\n await this.redis.del(sessionKey);\r\n cleanedCount++;\r\n }\r\n }\r\n\r\n if (cleanedCount > 0) {\r\n logger.info('Session cleanup completed', { cleanedCount, totalKeys: sessionKeys.length });\r\n }\r\n } catch (error) {\r\n logger.error('Session cleanup failed:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Track session statistics\r\n */\r\n private async trackSessionStats(sessionInfo: SessionInfo): Promise<void> {\r\n try {\r\n const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD\r\n \r\n // Track daily session count\r\n await this.redis.incr(`stats:sessions:daily:${today}`);\r\n await this.redis.expire(`stats:sessions:daily:${today}`, 7 * 24 * 60 * 60); // Keep for 7 days\r\n\r\n // Track sessions by device type (simple user agent parsing)\r\n const deviceType = this.parseDeviceType(sessionInfo.userAgent || '');\r\n await this.redis.incr(`stats:sessions:device:${deviceType}`);\r\n await this.redis.expire(`stats:sessions:device:${deviceType}`, 30 * 24 * 60 * 60); // Keep for 30 days\r\n\r\n // Track sessions by location (if geolocation is enabled)\r\n if (this.config.enableGeolocation && sessionInfo.location) {\r\n const country = sessionInfo.location.split(',')[0] || 'unknown';\r\n await this.redis.incr(`stats:sessions:location:${country}`);\r\n await this.redis.expire(`stats:sessions:location:${country}`, 30 * 24 * 60 * 60);\r\n }\r\n } catch (error) {\r\n logger.error('Failed to track session stats:', error);\r\n }\r\n }\r\n\r\n /**\r\n * Get session statistics\r\n */\r\n async getSessionStats(): Promise<SessionStats> {\r\n try {\r\n const today = new Date().toISOString().split('T')[0];\r\n \r\n // Get total active sessions\r\n const sessionKeys = await this.redis.keys('session:*');\r\n const totalSessions = sessionKeys.length;\r\n\r\n // Get active sessions (non-expired)\r\n let activeSessions = 0;\r\n const sessionsByDevice: Record<string, number> = {};\r\n const sessionsByLocation: Record<string, number> = {};\r\n let oldestSession: Date | undefined;\r\n let newestSession: Date | undefined;\r\n\r\n for (const sessionKey of sessionKeys) {\r\n const sessionData = await this.redis.get(sessionKey);\r\n \r\n if (sessionData) {\r\n try {\r\n const sessionInfo: SessionInfo = JSON.parse(sessionData);\r\n activeSessions++;\r\n\r\n const createdAt = new Date(sessionInfo.createdAt);\r\n if (!oldestSession || createdAt < oldestSession) {\r\n oldestSession = createdAt;\r\n }\r\n if (!newestSession || createdAt > newestSession) {\r\n newestSession = createdAt;\r\n }\r\n\r\n // Count by device type\r\n const deviceType = this.parseDeviceType(sessionInfo.userAgent || '');\r\n sessionsByDevice[deviceType] = (sessionsByDevice[deviceType] || 0) + 1;\r\n\r\n // Count by location\r\n if (sessionInfo.location) {\r\n const country = sessionInfo.location.split(',')[0] || 'unknown';\r\n sessionsByLocation[country] = (sessionsByLocation[country] || 0) + 1;\r\n }\r\n } catch (parseError) {\r\n // Skip corrupted data\r\n }\r\n }\r\n }\r\n\r\n // Get average session duration (approximate)\r\n const averageSessionDuration = 0; // Would need additional tracking\r\n\r\n return {\r\n totalSessions,\r\n activeSessions,\r\n sessionsByDevice,\r\n sessionsByLocation,\r\n averageSessionDuration,\r\n oldestSession,\r\n newestSession,\r\n };\r\n } catch (error) {\r\n logger.error('Failed to get session stats:', error);\r\n return {\r\n totalSessions: 0,\r\n activeSessions: 0,\r\n sessionsByDevice: {},\r\n sessionsByLocation: {},\r\n averageSessionDuration: 0,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Parse device type from user agent\r\n */\r\n private parseDeviceType(userAgent: string): string {\r\n const ua = userAgent.toLowerCase();\r\n \r\n if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {\r\n return 'mobile';\r\n } else if (ua.includes('tablet') || ua.includes('ipad')) {\r\n return 'tablet';\r\n } else if (ua.includes('bot') || ua.includes('crawler') || ua.includes('spider')) {\r\n return 'bot';\r\n } else {\r\n return 'desktop';\r\n }\r\n }\r\n\r\n /**\r\n * Terminate suspicious sessions\r\n */\r\n async terminateSuspiciousSessions(userId: string, criteria: {\r\n maxAgeHours?: number;\r\n suspiciousIPs?: string[];\r\n suspiciousUserAgents?: string[];\r\n }): Promise<string[]> {\r\n try {\r\n const sessions = await this.getUserSessions(userId);\r\n const terminatedSessions: string[] = [];\r\n const now = new Date();\r\n\r\n for (const session of sessions) {\r\n let shouldTerminate = false;\r\n\r\n // Check age\r\n if (criteria.maxAgeHours) {\r\n const ageHours = (now.getTime() - session.createdAt.getTime()) / (1000 * 60 * 60);\r\n if (ageHours > criteria.maxAgeHours) {\r\n shouldTerminate = true;\r\n }\r\n }\r\n\r\n // Check IP\r\n if (criteria.suspiciousIPs?.includes(session.ipAddress || '')) {\r\n shouldTerminate = true;\r\n }\r\n\r\n // Check user agent\r\n if (criteria.suspiciousUserAgents?.some(ua => \r\n session.userAgent?.toLowerCase().includes(ua.toLowerCase())\r\n )) {\r\n shouldTerminate = true;\r\n }\r\n\r\n if (shouldTerminate) {\r\n await this.removeSession(session.sessionId, userId);\r\n terminatedSessions.push(session.sessionId);\r\n \r\n logger.warn('Suspicious session terminated', {\r\n sessionId: session.sessionId,\r\n userId,\r\n ipAddress: session.ipAddress,\r\n userAgent: session.userAgent,\r\n });\r\n }\r\n }\r\n\r\n return terminatedSessions;\r\n } catch (error) {\r\n logger.error('Failed to terminate suspicious sessions:', error);\r\n throw new StandardError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Failed to terminate suspicious sessions',\r\n {},\r\n error as Error\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get session activity timeline\r\n */\r\n async getSessionActivityTimeline(userId: string, hours: number = 24): Promise<Array<{\r\n timestamp: Date;\r\n action: string;\r\n details: any;\r\n }>> {\r\n try {\r\n const sessions = await this.getUserSessions(userId);\r\n const timeline: Array<{ timestamp: Date; action: string; details: any }> = [];\r\n\r\n for (const session of sessions) {\r\n timeline.push({\r\n timestamp: session.createdAt,\r\n action: 'session_created',\r\n details: {\r\n sessionId: session.sessionId,\r\n ipAddress: session.ipAddress,\r\n userAgent: session.userAgent,\r\n },\r\n });\r\n\r\n timeline.push({\r\n timestamp: session.lastActivity,\r\n action: 'last_activity',\r\n details: {\r\n sessionId: session.sessionId,\r\n duration: session.lastActivity.getTime() - session.createdAt.getTime(),\r\n },\r\n });\r\n }\r\n\r\n // Sort by timestamp\r\n timeline.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());\r\n\r\n // Filter by time range\r\n const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);\r\n return timeline.filter(entry => entry.timestamp > cutoff);\r\n } catch (error) {\r\n logger.error('Failed to get session activity timeline:', error);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Cleanup resources\r\n */\r\n public cleanup(): void {\r\n this.stopSessionCleanup();\r\n logger.info('Session management service cleaned up');\r\n }\r\n}"],"names":["createLogger","StandardError","ErrorCode","logger","SessionManagementService","redis","config","cleanupInterval","maxSessionsPerUser","sessionTimeoutMs","cleanupIntervalMs","enableSessionMonitoring","enableGeolocation","startSessionCleanup","setInterval","cleanupExpiredSessions","error","info","interval","stopSessionCleanup","clearInterval","undefined","createSession","sessionId","userId","sessionData","sessionInfo","lastActivity","Date","isActive","enforceSessionLimit","sessionKey","setex","Math","ceil","JSON","stringify","userSessionsKey","sadd","expire","trackSessionStats","debug","INTERNAL_ERROR","getSession","get","parse","createdAt","updateSessionActivity","removeSession","del","srem","removeAllUserSessions","sessionIds","smembers","removePromises","map","Promise","all","sessionCount","length","getUserSessions","sessionPromises","sessions","filter","session","validSessionIds","s","staleSessionIds","id","includes","staleId","sort","a","b","getTime","sessionsToRemove","slice","sessionKeys","keys","cleanedCount","now","replace","parseError","totalKeys","today","toISOString","split","incr","deviceType","parseDeviceType","userAgent","location","country","getSessionStats","totalSessions","activeSessions","sessionsByDevice","sessionsByLocation","oldestSession","newestSession","averageSessionDuration","ua","toLowerCase","terminateSuspiciousSessions","criteria","terminatedSessions","shouldTerminate","maxAgeHours","ageHours","suspiciousIPs","ipAddress","suspiciousUserAgents","some","push","warn","getSessionActivityTimeline","hours","timeline","timestamp","action","details","duration","cutoff","entry","cleanup"],"mappings":"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"}