claude-flow-novice 2.15.11 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (636) hide show
  1. package/.claude/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  2. package/.claude/commands/cfn-loop-cli.md +158 -464
  3. package/.claude/commands/cfn-loop-trigger.md +114 -0
  4. package/.claude/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  5. package/.claude/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  6. package/.claude/hooks/cfn-invoke-post-edit.sh +22 -0
  7. package/.claude/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  8. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  9. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  10. package/.claude/hooks/cfn-invoke-pre-edit.sh +22 -0
  11. package/.claude/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  12. package/.claude/skills/cfn-agent-spawning/SKILL.md +48 -1
  13. package/.claude/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  14. package/.claude/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  15. package/.claude/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  16. package/.claude/skills/{cfn-redis-coordination/check-dependencies.sh → cfn-agent-spawning/check-dependencies.sh.backup} +3 -5
  17. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  18. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  19. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  20. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  21. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  22. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  23. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  24. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  25. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  26. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  27. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  28. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  29. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  30. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  31. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  32. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  33. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  34. package/.claude/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  35. package/.claude/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  36. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  37. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  38. package/.claude/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  39. package/.claude/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  40. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  41. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  42. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  43. package/.claude/skills/cfn-loop-orchestration/SKILL.md +159 -48
  44. package/.claude/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  45. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  46. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  47. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  48. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  49. package/.claude/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  50. package/.claude/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  51. package/.claude/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  52. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  53. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  54. package/{claude-assets/skills/cfn-loop-orchestration → .claude/skills/cfn-loop-orchestration/archive/legacy-bash}/orchestrate-wrapper.sh +11 -1
  55. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  56. package/.claude/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  57. package/.claude/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  58. package/.claude/skills/cfn-loop-orchestration/package-lock.json +3 -0
  59. package/.claude/skills/cfn-loop-orchestration/package.json +4 -0
  60. package/.claude/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  61. package/.claude/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  62. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  63. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  64. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  65. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  66. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  67. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  68. package/.claude/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  69. package/.claude/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  70. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  71. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  72. package/.claude/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  73. package/.claude/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  74. package/.claude/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  75. package/.claude/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  76. package/.claude/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  77. package/.claude/skills/cfn-loop-orchestration/src/index.ts +2 -0
  78. package/.claude/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  79. package/.claude/skills/cfn-loop-orchestration/src/types.ts +56 -0
  80. package/.claude/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  81. package/.claude/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  82. package/.claude/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  83. package/.claude/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  84. package/.claude/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  85. package/.claude/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  86. package/.claude/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  87. package/.claude/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  88. package/.claude/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  89. package/.claude/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  90. package/.claude/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  91. package/.claude/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  92. package/.claude/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  93. package/.claude/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  94. package/.claude/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  95. package/.claude/skills/cfn-loop-validation/INDEX.md +531 -0
  96. package/.claude/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  97. package/.claude/skills/cfn-loop-validation/SKILL.md +48 -1
  98. package/.claude/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  99. package/.claude/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  100. package/.claude/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  101. package/.claude/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  102. package/{claude-assets/skills/cfn-redis-coordination/check-dependencies.sh → .claude/skills/cfn-loop-validation/check-dependencies.sh.backup} +4 -5
  103. package/.claude/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  104. package/.claude/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  105. package/.claude/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  106. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  107. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  108. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  109. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  110. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  111. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  112. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  113. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  114. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  115. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  116. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  117. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  118. package/.claude/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  119. package/.claude/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  120. package/.claude/skills/cfn-loop-validation/dist/types.js +66 -0
  121. package/.claude/skills/cfn-loop-validation/dist/types.js.map +1 -0
  122. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  123. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  124. package/.claude/skills/cfn-loop-validation/dist/validator.js +411 -0
  125. package/.claude/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  126. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  127. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  128. package/.claude/skills/cfn-loop-validation/package.json +93 -0
  129. package/.claude/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  130. package/.claude/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  131. package/.claude/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  132. package/.claude/skills/cfn-loop-validation/src/types.ts +215 -0
  133. package/.claude/skills/cfn-loop-validation/src/validator.ts +503 -0
  134. package/.claude/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  135. package/.claude/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  136. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  137. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  138. package/.claude/skills/cfn-loop-validation/validate-gate.sh +63 -0
  139. package/.claude/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  140. package/.claude/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  141. package/.claude/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  142. package/.claude/skills/cfn-product-owner-decision/SKILL.md +479 -147
  143. package/.claude/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  144. package/{claude-assets/skills/cfn-product-owner-decision → .claude/skills/cfn-product-owner-decision/archive/legacy-bash}/execute-decision.sh +24 -2
  145. package/.claude/skills/pre-edit-backup/SKILL.md +324 -0
  146. package/.claude/skills/pre-edit-backup/SKILL.md.backup +277 -0
  147. package/.claude/skills/pre-edit-backup/backup.sh +22 -0
  148. package/.claude/skills/pre-edit-backup/backup.sh.backup +107 -0
  149. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +1 -0
  150. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +1 -0
  151. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -0
  152. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +1 -0
  153. package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +10 -0
  154. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +56 -33
  155. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +46 -36
  156. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +9 -0
  157. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +17 -17
  158. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +40 -58
  159. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +18 -20
  160. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +19 -28
  161. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +15 -19
  162. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +15 -10
  163. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +15 -10
  164. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +15 -25
  165. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +17 -21
  166. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +17 -21
  167. package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +1 -5
  168. package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +20 -51
  169. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +22 -71
  170. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +21 -64
  171. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +22 -67
  172. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +23 -67
  173. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +7 -35
  174. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +8 -37
  175. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +16 -54
  176. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +17 -55
  177. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +9 -37
  178. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +17 -55
  179. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +17 -48
  180. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +8 -37
  181. package/claude-assets/agents/cfn-dev-team/testers/tester.md +7 -27
  182. package/claude-assets/agents/cfn-dev-team/utility/analyst.md +12 -28
  183. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +13 -13
  184. package/claude-assets/agents/cfn-dev-team/utility/context-curator.md +7 -2
  185. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +5 -10
  186. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +120 -714
  187. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +12 -21
  188. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +146 -572
  189. package/claude-assets/agents/custom/cfn-docker-expert.md +102 -0
  190. package/claude-assets/agents/custom/cfn-loops-cli-expert.md +129 -0
  191. package/claude-assets/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  192. package/claude-assets/commands/cfn-loop-cli.md +158 -464
  193. package/claude-assets/commands/cfn-loop-trigger.md +114 -0
  194. package/claude-assets/hooks/SKILL.md +518 -0
  195. package/claude-assets/hooks/SKILL.md.backup +471 -0
  196. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  197. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  198. package/claude-assets/hooks/cfn-invoke-post-edit.sh +22 -0
  199. package/claude-assets/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  200. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  201. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  202. package/claude-assets/hooks/cfn-invoke-pre-edit.sh +22 -0
  203. package/claude-assets/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  204. package/claude-assets/skills/cfn-agent-selection-with-fallback/DELIVERABLES.md +409 -0
  205. package/claude-assets/skills/cfn-agent-selection-with-fallback/IMPLEMENTATION_SUMMARY.md +396 -0
  206. package/claude-assets/skills/cfn-agent-selection-with-fallback/INTEGRATION_GUIDE.md +308 -0
  207. package/claude-assets/skills/cfn-agent-selection-with-fallback/QUICK_REFERENCE.md +239 -0
  208. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md +107 -1
  209. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md.backup +302 -0
  210. package/claude-assets/skills/cfn-agent-selection-with-fallback/TYPESCRIPT_MIGRATION.md +295 -0
  211. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.cjs +297 -0
  212. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.js +297 -0
  213. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.cjs +96 -0
  214. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.js +96 -0
  215. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh +45 -0
  216. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh.backup +23 -0
  217. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh +22 -0
  218. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh.backup +173 -0
  219. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.test.ts +357 -0
  220. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.ts +350 -0
  221. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/cli.ts +74 -0
  222. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh +22 -0
  223. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh.backup +71 -0
  224. package/claude-assets/skills/cfn-agent-selection-with-fallback/tsconfig.json +18 -0
  225. package/claude-assets/skills/cfn-agent-spawning/SKILL.md +48 -1
  226. package/claude-assets/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  227. package/claude-assets/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  228. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  229. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh.backup +30 -0
  230. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  231. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  232. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  233. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  234. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  235. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  236. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  237. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  238. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  239. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  240. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  241. package/claude-assets/skills/cfn-coordination/agent-completion.sh.backup +36 -0
  242. package/claude-assets/skills/cfn-coordination/coordination-signal.sh.backup +36 -0
  243. package/claude-assets/skills/cfn-coordination/coordination-wait.sh.backup +36 -0
  244. package/claude-assets/skills/cfn-dependency-ingestion/README.md +101 -0
  245. package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +369 -0
  246. package/claude-assets/skills/cfn-dependency-ingestion/build.sh +23 -0
  247. package/claude-assets/skills/cfn-dependency-ingestion/dist/ingest-dependencies.js +478 -0
  248. package/claude-assets/skills/cfn-dependency-ingestion/ingest-dependencies.sh +295 -0
  249. package/claude-assets/skills/cfn-dependency-ingestion/src/ingest-dependencies.ts +563 -0
  250. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  251. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  252. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  253. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  254. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  255. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  256. package/claude-assets/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  257. package/claude-assets/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  258. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  259. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  260. package/claude-assets/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  261. package/claude-assets/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  262. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  263. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  264. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  265. package/claude-assets/skills/cfn-loop-orchestration/SKILL.md +159 -48
  266. package/claude-assets/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  267. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  268. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  269. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  270. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  271. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  272. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  273. package/claude-assets/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  274. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  275. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  276. package/{.claude/skills/cfn-loop-orchestration → claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash}/orchestrate-wrapper.sh +11 -1
  277. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  278. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  279. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  280. package/claude-assets/skills/cfn-loop-orchestration/package-lock.json +3 -0
  281. package/claude-assets/skills/cfn-loop-orchestration/package.json +4 -0
  282. package/claude-assets/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  283. package/claude-assets/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  284. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  285. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  286. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  287. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  288. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  289. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  290. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  291. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  292. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  293. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  294. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  295. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  296. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  297. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  298. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  299. package/claude-assets/skills/cfn-loop-orchestration/src/index.ts +2 -0
  300. package/claude-assets/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  301. package/claude-assets/skills/cfn-loop-orchestration/src/types.ts +56 -0
  302. package/claude-assets/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  303. package/claude-assets/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  304. package/claude-assets/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  305. package/claude-assets/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  306. package/claude-assets/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  307. package/claude-assets/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  308. package/claude-assets/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  309. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  310. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  311. package/claude-assets/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  312. package/claude-assets/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  313. package/claude-assets/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  314. package/claude-assets/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  315. package/claude-assets/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  316. package/claude-assets/skills/cfn-loop-output-processing/.eslintrc.json +33 -0
  317. package/claude-assets/skills/cfn-loop-output-processing/DELIVERY_SUMMARY.txt +462 -0
  318. package/claude-assets/skills/cfn-loop-output-processing/DEPRECATION_NOTICE.md +183 -0
  319. package/claude-assets/skills/cfn-loop-output-processing/EXAMPLES.md +609 -0
  320. package/claude-assets/skills/cfn-loop-output-processing/IMPLEMENTATION_SUMMARY.md +418 -0
  321. package/claude-assets/skills/cfn-loop-output-processing/INDEX.md +531 -0
  322. package/claude-assets/skills/cfn-loop-output-processing/MIGRATION.md +362 -0
  323. package/claude-assets/skills/cfn-loop-output-processing/README.md +114 -0
  324. package/claude-assets/skills/cfn-loop-output-processing/SKILL.md +633 -0
  325. package/{.claude/skills/cfn-docker-redis-coordination → claude-assets/skills/cfn-loop-output-processing}/jest.config.js +7 -15
  326. package/claude-assets/skills/cfn-loop-output-processing/package.json +50 -0
  327. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop2.ts +195 -0
  328. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop3.ts +157 -0
  329. package/claude-assets/skills/cfn-loop-output-processing/src/output-processor.ts +632 -0
  330. package/claude-assets/skills/cfn-loop-output-processing/tests/output-processor.test.ts +617 -0
  331. package/claude-assets/skills/{cfn-docker-redis-coordination → cfn-loop-output-processing}/tsconfig.json +16 -7
  332. package/claude-assets/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  333. package/claude-assets/skills/cfn-loop-validation/INDEX.md +531 -0
  334. package/claude-assets/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  335. package/claude-assets/skills/cfn-loop-validation/SKILL.md +48 -1
  336. package/claude-assets/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  337. package/claude-assets/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  338. package/claude-assets/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  339. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  340. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh.backup +31 -0
  341. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  342. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  343. package/claude-assets/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  344. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  345. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  346. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  347. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  348. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  349. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  350. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  351. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  352. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  353. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  354. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  355. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  356. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  357. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  358. package/claude-assets/skills/cfn-loop-validation/dist/types.js +66 -0
  359. package/claude-assets/skills/cfn-loop-validation/dist/types.js.map +1 -0
  360. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  361. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  362. package/claude-assets/skills/cfn-loop-validation/dist/validator.js +411 -0
  363. package/claude-assets/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  364. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  365. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  366. package/claude-assets/skills/cfn-loop-validation/package.json +93 -0
  367. package/claude-assets/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  368. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  369. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  370. package/claude-assets/skills/cfn-loop-validation/src/types.ts +215 -0
  371. package/claude-assets/skills/cfn-loop-validation/src/validator.ts +503 -0
  372. package/claude-assets/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  373. package/claude-assets/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  374. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  375. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  376. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh +63 -0
  377. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  378. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  379. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  380. package/claude-assets/skills/cfn-product-owner-decision/SKILL.md +479 -147
  381. package/claude-assets/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  382. package/{.claude/skills/cfn-product-owner-decision → claude-assets/skills/cfn-product-owner-decision/archive/legacy-bash}/execute-decision.sh +24 -2
  383. package/claude-assets/skills/cfn-provider-routing/README.md +129 -0
  384. package/claude-assets/skills/cfn-provider-routing/SKILL.md +192 -0
  385. package/claude-assets/skills/cfn-provider-routing/resolve-provider-model.ts +223 -0
  386. package/claude-assets/skills/pre-edit-backup/SKILL.md +324 -0
  387. package/claude-assets/skills/pre-edit-backup/SKILL.md.backup +277 -0
  388. package/claude-assets/skills/pre-edit-backup/backup.sh +22 -0
  389. package/claude-assets/skills/pre-edit-backup/backup.sh.backup +107 -0
  390. package/dist/agents/agent-loader.js +146 -165
  391. package/dist/agents/agent-loader.js.map +1 -1
  392. package/dist/api/auth-endpoints.js +415 -0
  393. package/dist/api/auth-endpoints.js.map +1 -0
  394. package/dist/api/task-endpoints.js +562 -0
  395. package/dist/api/task-endpoints.js.map +1 -0
  396. package/dist/backend/server.js +418 -0
  397. package/dist/backend/server.js.map +1 -0
  398. package/dist/cfn-loop/product-owner/decision-parser.js +356 -0
  399. package/dist/cfn-loop/product-owner/decision-parser.js.map +1 -0
  400. package/dist/cfn-loop/product-owner/index.js +1 -0
  401. package/dist/cfn-loop/product-owner/index.js.map +1 -1
  402. package/dist/cli/agent-command.js +1 -1
  403. package/dist/cli/agent-command.js.map +1 -1
  404. package/dist/cli/agent-completion.js +273 -0
  405. package/dist/cli/agent-completion.js.map +1 -0
  406. package/dist/cli/agent-prompt-builder.js +83 -48
  407. package/dist/cli/agent-prompt-builder.js.map +1 -1
  408. package/dist/cli/agent-spawner.js +499 -0
  409. package/dist/cli/agent-spawner.js.map +1 -0
  410. package/dist/cli/anthropic-client.js +10 -3
  411. package/dist/cli/anthropic-client.js.map +1 -1
  412. package/dist/cli/config-manager.js +91 -109
  413. package/dist/cli/index.js +11 -0
  414. package/dist/cli/index.js.map +1 -1
  415. package/dist/cli/parse-decision-cli.js +268 -0
  416. package/dist/cli/parse-decision-cli.js.map +1 -0
  417. package/dist/cli/post-edit-hook.js +83 -0
  418. package/dist/cli/post-edit-hook.js.map +1 -0
  419. package/dist/cli/pre-edit-hook.js +77 -0
  420. package/dist/cli/pre-edit-hook.js.map +1 -0
  421. package/dist/cli/spawn-agent-cli.js +209 -0
  422. package/dist/cli/spawn-agent-cli.js.map +1 -0
  423. package/dist/coordination/coordination-wrapper.js +383 -0
  424. package/dist/coordination/coordination-wrapper.js.map +1 -0
  425. package/dist/coordination/store-success-criteria.js +68 -0
  426. package/dist/coordination/store-success-criteria.js.map +1 -0
  427. package/dist/coordination/store-task-context.js +65 -0
  428. package/dist/coordination/store-task-context.js.map +1 -0
  429. package/dist/hooks/backup-manager.js +273 -0
  430. package/dist/hooks/backup-manager.js.map +1 -0
  431. package/dist/hooks/post-edit-validator.js +388 -0
  432. package/dist/hooks/post-edit-validator.js.map +1 -0
  433. package/dist/integration/index.js +19 -0
  434. package/dist/integration/index.js.map +1 -0
  435. package/dist/integration/task-mode-adapter.js +297 -0
  436. package/dist/integration/task-mode-adapter.js.map +1 -0
  437. package/dist/integration/trigger-dev-client.js +253 -0
  438. package/dist/integration/trigger-dev-client.js.map +1 -0
  439. package/dist/integration/trigger-dev-webhooks.js +362 -0
  440. package/dist/integration/trigger-dev-webhooks.js.map +1 -0
  441. package/dist/lib/path-validator.js +14 -5
  442. package/dist/lib/path-validator.js.map +1 -1
  443. package/dist/lib/redis-queue-manager.js +5 -1
  444. package/dist/lib/redis-queue-manager.js.map +1 -1
  445. package/dist/middleware/authentication.js +317 -0
  446. package/dist/middleware/authentication.js.map +1 -0
  447. package/dist/services/authentication.js +669 -0
  448. package/dist/services/authentication.js.map +1 -0
  449. package/dist/services/session-management.js +436 -0
  450. package/dist/services/session-management.js.map +1 -0
  451. package/dist/services/skill-deployment.js +8 -6
  452. package/dist/services/skill-deployment.js.map +1 -1
  453. package/dist/services/user-service.js +710 -0
  454. package/dist/services/user-service.js.map +1 -0
  455. package/dist/types/trigger-dev-events.d.js +10 -0
  456. package/dist/types/trigger-dev-events.d.js.map +1 -0
  457. package/docs/README.md +240 -0
  458. package/package.json +13 -4
  459. package/scripts/compare-workflow-performance.sh +556 -0
  460. package/scripts/migrate-to-optimized-workflows.sh +438 -0
  461. package/scripts/organize-docs.sh +338 -0
  462. package/scripts/trigger-dev-setup.sh +267 -0
  463. package/.claude/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  464. package/.claude/skills/cfn-docker-redis-coordination/README.md +0 -294
  465. package/.claude/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  466. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  467. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  468. package/.claude/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  469. package/.claude/skills/cfn-docker-redis-coordination/package.json +0 -40
  470. package/.claude/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  471. package/.claude/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  472. package/.claude/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  473. package/.claude/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  474. package/.claude/skills/cfn-docker-redis-coordination/tsconfig.json +0 -30
  475. package/.claude/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  476. package/.claude/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  477. package/.claude/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  478. package/.claude/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  479. package/.claude/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  480. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  481. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  482. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  483. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  484. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  485. package/.claude/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  486. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  487. package/.claude/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  488. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  489. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  490. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  491. package/.claude/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  492. package/.claude/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  493. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  494. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  495. package/.claude/skills/cfn-redis-coordination/config.json +0 -61
  496. package/.claude/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  497. package/.claude/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  498. package/.claude/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  499. package/.claude/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  500. package/.claude/skills/cfn-redis-coordination/jest.config.js +0 -23
  501. package/.claude/skills/cfn-redis-coordination/package-lock.json +0 -5272
  502. package/.claude/skills/cfn-redis-coordination/package.json +0 -45
  503. package/.claude/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  504. package/.claude/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  505. package/.claude/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  506. package/.claude/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  507. package/.claude/skills/cfn-redis-coordination/src/index.ts +0 -82
  508. package/.claude/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  509. package/.claude/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  510. package/.claude/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  511. package/.claude/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  512. package/.claude/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  513. package/.claude/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  514. package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  515. package/.claude/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  516. package/.claude/skills/cfn-redis-coordination/src/types.ts +0 -235
  517. package/.claude/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  518. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  519. package/.claude/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  520. package/.claude/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  521. package/.claude/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  522. package/.claude/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  523. package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  524. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  525. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +0 -980
  526. package/claude-assets/agents/typescript-specialist.md +0 -280
  527. package/claude-assets/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  528. package/claude-assets/skills/cfn-docker-redis-coordination/README.md +0 -294
  529. package/claude-assets/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  530. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  531. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  532. package/claude-assets/skills/cfn-docker-redis-coordination/jest.config.js +0 -37
  533. package/claude-assets/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  534. package/claude-assets/skills/cfn-docker-redis-coordination/package.json +0 -40
  535. package/claude-assets/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  536. package/claude-assets/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  537. package/claude-assets/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  538. package/claude-assets/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  539. package/claude-assets/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  540. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  541. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  542. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  543. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  544. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  545. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  546. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  547. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  548. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  549. package/claude-assets/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  550. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  551. package/claude-assets/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  552. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  553. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  554. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  555. package/claude-assets/skills/cfn-redis-cleanup/cleanup-redis.sh +0 -130
  556. package/claude-assets/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  557. package/claude-assets/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  558. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  559. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  560. package/claude-assets/skills/cfn-redis-coordination/config.json +0 -61
  561. package/claude-assets/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  562. package/claude-assets/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  563. package/claude-assets/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  564. package/claude-assets/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  565. package/claude-assets/skills/cfn-redis-coordination/jest.config.js +0 -23
  566. package/claude-assets/skills/cfn-redis-coordination/package-lock.json +0 -5272
  567. package/claude-assets/skills/cfn-redis-coordination/package.json +0 -45
  568. package/claude-assets/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  569. package/claude-assets/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  570. package/claude-assets/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  571. package/claude-assets/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  572. package/claude-assets/skills/cfn-redis-coordination/src/index.ts +0 -82
  573. package/claude-assets/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  574. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  575. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  576. package/claude-assets/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  577. package/claude-assets/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  578. package/claude-assets/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  579. package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  580. package/claude-assets/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  581. package/claude-assets/skills/cfn-redis-coordination/src/types.ts +0 -235
  582. package/claude-assets/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  583. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  584. package/claude-assets/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  585. package/claude-assets/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  586. package/claude-assets/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  587. package/claude-assets/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  588. package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  589. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  590. package/claude-assets/skills/cfn-redis-data-extraction/SKILL.md +0 -442
  591. package/claude-assets/skills/cfn-redis-data-extraction/extract.sh +0 -306
  592. package/dist/coordination/index.js +0 -25
  593. package/dist/coordination/index.js.map +0 -1
  594. package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +0 -405
  595. package/docs/MEMORY_CLEANUP_GUIDE.md +0 -358
  596. package/docs/MEMORY_LEAK_FIX_SUMMARY.md +0 -322
  597. package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +0 -319
  598. package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +0 -574
  599. /package/.claude/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  600. /package/.claude/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  601. /package/.claude/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  602. /package/.claude/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  603. /package/.claude/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  604. /package/.claude/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  605. /package/.claude/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  606. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  607. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  608. /package/.claude/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  609. /package/.claude/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  610. /package/.claude/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  611. /package/.claude/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  612. /package/.claude/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  613. /package/.claude/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  614. /package/.claude/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  615. /package/.claude/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  616. /package/.claude/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  617. /package/.claude/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
  618. /package/claude-assets/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  619. /package/claude-assets/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  620. /package/claude-assets/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  621. /package/claude-assets/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  622. /package/claude-assets/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  623. /package/claude-assets/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  624. /package/claude-assets/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  625. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  626. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  627. /package/claude-assets/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  628. /package/claude-assets/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  629. /package/claude-assets/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  630. /package/claude-assets/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  631. /package/claude-assets/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  632. /package/claude-assets/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  633. /package/claude-assets/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  634. /package/claude-assets/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  635. /package/claude-assets/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  636. /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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"}