devchain-cli 0.10.5 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/README.md +13 -7
  2. package/dist/cli.js +32 -28
  3. package/dist/drizzle/0048_sessions_transcript_fields.sql +6 -0
  4. package/dist/drizzle/0049_provider_models_and_agent_model_override.sql +24 -0
  5. package/dist/drizzle/meta/_journal.json +14 -0
  6. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +30 -0
  7. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts.map +1 -1
  8. package/dist/node_modules/@devchain/shared/schemas/export-schema.js +10 -3
  9. package/dist/node_modules/@devchain/shared/schemas/export-schema.js.map +1 -1
  10. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  11. package/dist/server/app.main.module.js +2 -0
  12. package/dist/server/app.main.module.js.map +1 -1
  13. package/dist/server/app.normal.module.js +2 -0
  14. package/dist/server/app.normal.module.js.map +1 -1
  15. package/dist/server/modules/agents/controllers/agents.controller.d.ts +1 -0
  16. package/dist/server/modules/agents/controllers/agents.controller.js +3 -0
  17. package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -1
  18. package/dist/server/modules/chat/dtos/chat.dto.d.ts +4 -4
  19. package/dist/server/modules/core/services/preflight.service.js +7 -0
  20. package/dist/server/modules/core/services/preflight.service.js.map +1 -1
  21. package/dist/server/modules/core/services/provider-mcp-ensure.service.js +8 -0
  22. package/dist/server/modules/core/services/provider-mcp-ensure.service.js.map +1 -1
  23. package/dist/server/modules/documents/controllers/documents.controller.d.ts +2 -2
  24. package/dist/server/modules/documents/controllers/documents.controller.js.map +1 -1
  25. package/dist/server/modules/epics/controllers/epic-comments.controller.d.ts +2 -2
  26. package/dist/server/modules/epics/controllers/epic-comments.controller.js.map +1 -1
  27. package/dist/server/modules/events/catalog/claude.hooks.session.started.d.ts +4 -4
  28. package/dist/server/modules/events/catalog/index.d.ts +115 -4
  29. package/dist/server/modules/events/catalog/index.js +6 -0
  30. package/dist/server/modules/events/catalog/index.js.map +1 -1
  31. package/dist/server/modules/events/catalog/session.transcript.discovered.d.ts +24 -0
  32. package/dist/server/modules/events/catalog/session.transcript.discovered.js +15 -0
  33. package/dist/server/modules/events/catalog/session.transcript.discovered.js.map +1 -0
  34. package/dist/server/modules/events/catalog/session.transcript.ended.d.ts +51 -0
  35. package/dist/server/modules/events/catalog/session.transcript.ended.js +20 -0
  36. package/dist/server/modules/events/catalog/session.transcript.ended.js.map +1 -0
  37. package/dist/server/modules/events/catalog/session.transcript.updated.d.ts +51 -0
  38. package/dist/server/modules/events/catalog/session.transcript.updated.js +20 -0
  39. package/dist/server/modules/events/catalog/session.transcript.updated.js.map +1 -0
  40. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.d.ts +2 -2
  41. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js.map +1 -1
  42. package/dist/server/modules/events/subscribers/index.js +2 -0
  43. package/dist/server/modules/events/subscribers/index.js.map +1 -1
  44. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.d.ts +2 -2
  45. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js.map +1 -1
  46. package/dist/server/modules/events/subscribers/transcript-broadcaster.subscriber.d.ts +12 -0
  47. package/dist/server/modules/events/subscribers/transcript-broadcaster.subscriber.js +88 -0
  48. package/dist/server/modules/events/subscribers/transcript-broadcaster.subscriber.js.map +1 -0
  49. package/dist/server/modules/guests/services/guest-health.service.d.ts +2 -2
  50. package/dist/server/modules/guests/services/guest-health.service.js.map +1 -1
  51. package/dist/server/modules/hooks/dtos/hook-event.dto.d.ts +4 -4
  52. package/dist/server/modules/hooks/services/hooks.service.d.ts +2 -2
  53. package/dist/server/modules/hooks/services/hooks.service.js.map +1 -1
  54. package/dist/server/modules/mcp/dtos/mcp.dto.js +8 -3
  55. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  56. package/dist/server/modules/mcp/services/handlers/activity-tools.d.ts +4 -0
  57. package/dist/server/modules/mcp/services/handlers/activity-tools.js +193 -0
  58. package/dist/server/modules/mcp/services/handlers/activity-tools.js.map +1 -0
  59. package/dist/server/modules/mcp/services/handlers/chat-tools.d.ts +6 -0
  60. package/dist/server/modules/mcp/services/handlers/chat-tools.js +605 -0
  61. package/dist/server/modules/mcp/services/handlers/chat-tools.js.map +1 -0
  62. package/dist/server/modules/mcp/services/handlers/document-tools.d.ts +6 -0
  63. package/dist/server/modules/mcp/services/handlers/document-tools.js +141 -0
  64. package/dist/server/modules/mcp/services/handlers/document-tools.js.map +1 -0
  65. package/dist/server/modules/mcp/services/handlers/epic-tools.d.ts +11 -0
  66. package/dist/server/modules/mcp/services/handlers/epic-tools.js +913 -0
  67. package/dist/server/modules/mcp/services/handlers/epic-tools.js.map +1 -0
  68. package/dist/server/modules/mcp/services/handlers/prompt-tools.d.ts +4 -0
  69. package/dist/server/modules/mcp/services/handlers/prompt-tools.js +109 -0
  70. package/dist/server/modules/mcp/services/handlers/prompt-tools.js.map +1 -0
  71. package/dist/server/modules/mcp/services/handlers/record-tools.d.ts +8 -0
  72. package/dist/server/modules/mcp/services/handlers/record-tools.js +114 -0
  73. package/dist/server/modules/mcp/services/handlers/record-tools.js.map +1 -0
  74. package/dist/server/modules/mcp/services/handlers/review-tools.d.ts +8 -0
  75. package/dist/server/modules/mcp/services/handlers/review-tools.js +672 -0
  76. package/dist/server/modules/mcp/services/handlers/review-tools.js.map +1 -0
  77. package/dist/server/modules/mcp/services/handlers/session-tools.d.ts +4 -0
  78. package/dist/server/modules/mcp/services/handlers/session-tools.js +114 -0
  79. package/dist/server/modules/mcp/services/handlers/session-tools.js.map +1 -0
  80. package/dist/server/modules/mcp/services/handlers/skill-tools.d.ts +4 -0
  81. package/dist/server/modules/mcp/services/handlers/skill-tools.js +124 -0
  82. package/dist/server/modules/mcp/services/handlers/skill-tools.js.map +1 -0
  83. package/dist/server/modules/mcp/services/handlers/types.d.ts +33 -0
  84. package/dist/server/modules/mcp/services/handlers/types.js +3 -0
  85. package/dist/server/modules/mcp/services/handlers/types.js.map +1 -0
  86. package/dist/server/modules/mcp/services/mappers/dto-mappers.d.ts +14 -0
  87. package/dist/server/modules/mcp/services/mappers/dto-mappers.js +138 -0
  88. package/dist/server/modules/mcp/services/mappers/dto-mappers.js.map +1 -0
  89. package/dist/server/modules/mcp/services/mcp-provider-registration.service.d.ts +8 -0
  90. package/dist/server/modules/mcp/services/mcp-provider-registration.service.js +258 -51
  91. package/dist/server/modules/mcp/services/mcp-provider-registration.service.js.map +1 -1
  92. package/dist/server/modules/mcp/services/mcp.service.d.ts +6 -66
  93. package/dist/server/modules/mcp/services/mcp.service.js +97 -3329
  94. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  95. package/dist/server/modules/mcp/services/utils/document-link-resolver.d.ts +10 -0
  96. package/dist/server/modules/mcp/services/utils/document-link-resolver.js +124 -0
  97. package/dist/server/modules/mcp/services/utils/document-link-resolver.js.map +1 -0
  98. package/dist/server/modules/mcp/services/utils/resolve-epic-id.d.ts +3 -0
  99. package/dist/server/modules/mcp/services/utils/resolve-epic-id.js +40 -0
  100. package/dist/server/modules/mcp/services/utils/resolve-epic-id.js.map +1 -0
  101. package/dist/server/modules/mcp/services/utils/resource-resolver.d.ts +10 -0
  102. package/dist/server/modules/mcp/services/utils/resource-resolver.js +124 -0
  103. package/dist/server/modules/mcp/services/utils/resource-resolver.js.map +1 -0
  104. package/dist/server/modules/mcp/services/utils/session-context-resolver.d.ts +14 -0
  105. package/dist/server/modules/mcp/services/utils/session-context-resolver.js +169 -0
  106. package/dist/server/modules/mcp/services/utils/session-context-resolver.js.map +1 -0
  107. package/dist/server/modules/mcp/tool-definitions.js +12 -3
  108. package/dist/server/modules/mcp/tool-definitions.js.map +1 -1
  109. package/dist/server/modules/orchestrator/git/controllers/git.controller.d.ts +4 -2
  110. package/dist/server/modules/orchestrator/git/controllers/git.controller.js +44 -7
  111. package/dist/server/modules/orchestrator/git/controllers/git.controller.js.map +1 -1
  112. package/dist/server/modules/orchestrator/git/git.module.js +2 -0
  113. package/dist/server/modules/orchestrator/git/git.module.js.map +1 -1
  114. package/dist/server/modules/orchestrator/git/services/git-worktree.service.d.ts +6 -0
  115. package/dist/server/modules/orchestrator/git/services/git-worktree.service.js +55 -0
  116. package/dist/server/modules/orchestrator/git/services/git-worktree.service.js.map +1 -1
  117. package/dist/server/modules/orchestrator/ui/app/lib/worktrees.d.ts +17 -1
  118. package/dist/server/modules/orchestrator/ui/app/lib/worktrees.js +27 -2
  119. package/dist/server/modules/orchestrator/ui/app/lib/worktrees.js.map +1 -1
  120. package/dist/server/modules/orchestrator/ui/app/orchestrator-app.d.ts +2 -1
  121. package/dist/server/modules/orchestrator/ui/app/orchestrator-app.js +146 -8
  122. package/dist/server/modules/orchestrator/ui/app/orchestrator-app.js.map +1 -1
  123. package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.d.ts +6 -1
  124. package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.js +53 -3
  125. package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.js.map +1 -1
  126. package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.d.ts +17 -4
  127. package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.js +1 -1
  128. package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.js.map +1 -1
  129. package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.d.ts +7 -1
  130. package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.js +130 -4
  131. package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.js.map +1 -1
  132. package/dist/server/modules/orchestrator/worktrees/worktrees.module.js +2 -0
  133. package/dist/server/modules/orchestrator/worktrees/worktrees.module.js.map +1 -1
  134. package/dist/server/modules/profiles/controllers/provider-configs.controller.d.ts +2 -2
  135. package/dist/server/modules/profiles/controllers/provider-configs.controller.js.map +1 -1
  136. package/dist/server/modules/profiles/dto.d.ts +2 -2
  137. package/dist/server/modules/projects/controllers/projects.controller.d.ts +34 -28
  138. package/dist/server/modules/projects/controllers/projects.controller.js +12 -7
  139. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  140. package/dist/server/modules/projects/dtos/export.dto.d.ts +7 -0
  141. package/dist/server/modules/projects/dtos/export.dto.js +1 -0
  142. package/dist/server/modules/projects/dtos/export.dto.js.map +1 -1
  143. package/dist/server/modules/projects/helpers/profile-mapping.helpers.d.ts +102 -0
  144. package/dist/server/modules/projects/helpers/profile-mapping.helpers.js +369 -0
  145. package/dist/server/modules/projects/helpers/profile-mapping.helpers.js.map +1 -0
  146. package/dist/server/modules/projects/helpers/project-export.d.ts +142 -0
  147. package/dist/server/modules/projects/helpers/project-export.js +315 -0
  148. package/dist/server/modules/projects/helpers/project-export.js.map +1 -0
  149. package/dist/server/modules/projects/helpers/project-import.d.ts +132 -0
  150. package/dist/server/modules/projects/helpers/project-import.js +601 -0
  151. package/dist/server/modules/projects/helpers/project-import.js.map +1 -0
  152. package/dist/server/modules/projects/helpers/project-presets.helpers.d.ts +26 -0
  153. package/dist/server/modules/projects/helpers/project-presets.helpers.js +125 -0
  154. package/dist/server/modules/projects/helpers/project-presets.helpers.js.map +1 -0
  155. package/dist/server/modules/projects/helpers/project-runtime.helpers.d.ts +69 -0
  156. package/dist/server/modules/projects/helpers/project-runtime.helpers.js +201 -0
  157. package/dist/server/modules/projects/helpers/project-runtime.helpers.js.map +1 -0
  158. package/dist/server/modules/projects/helpers/project-template-manifest.helpers.d.ts +16 -0
  159. package/dist/server/modules/projects/helpers/project-template-manifest.helpers.js +107 -0
  160. package/dist/server/modules/projects/helpers/project-template-manifest.helpers.js.map +1 -0
  161. package/dist/server/modules/projects/helpers/template-file.helpers.d.ts +9 -0
  162. package/dist/server/modules/projects/helpers/template-file.helpers.js +92 -0
  163. package/dist/server/modules/projects/helpers/template-file.helpers.js.map +1 -0
  164. package/dist/server/modules/projects/helpers/template-loader.d.ts +85 -0
  165. package/dist/server/modules/projects/helpers/template-loader.js +407 -0
  166. package/dist/server/modules/projects/helpers/template-loader.js.map +1 -0
  167. package/dist/server/modules/projects/services/main-project-bootstrap.service.js +4 -0
  168. package/dist/server/modules/projects/services/main-project-bootstrap.service.js.map +1 -1
  169. package/dist/server/modules/projects/services/projects.service.d.ts +24 -49
  170. package/dist/server/modules/projects/services/projects.service.js +46 -1848
  171. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  172. package/dist/server/modules/prompts/controllers/prompts.controller.d.ts +2 -2
  173. package/dist/server/modules/prompts/controllers/prompts.controller.js.map +1 -1
  174. package/dist/server/modules/providers/adapters/index.d.ts +1 -0
  175. package/dist/server/modules/providers/adapters/index.js +1 -0
  176. package/dist/server/modules/providers/adapters/index.js.map +1 -1
  177. package/dist/server/modules/providers/adapters/opencode.adapter.d.ts +16 -0
  178. package/dist/server/modules/providers/adapters/opencode.adapter.js +64 -0
  179. package/dist/server/modules/providers/adapters/opencode.adapter.js.map +1 -0
  180. package/dist/server/modules/providers/adapters/provider-adapter.factory.d.ts +2 -1
  181. package/dist/server/modules/providers/adapters/provider-adapter.factory.js +5 -2
  182. package/dist/server/modules/providers/adapters/provider-adapter.factory.js.map +1 -1
  183. package/dist/server/modules/providers/adapters/provider-adapter.interface.d.ts +1 -0
  184. package/dist/server/modules/providers/adapters/provider-adapters.module.js +2 -1
  185. package/dist/server/modules/providers/adapters/provider-adapters.module.js.map +1 -1
  186. package/dist/server/modules/providers/controllers/provider-models.controller.d.ts +22 -0
  187. package/dist/server/modules/providers/controllers/provider-models.controller.js +173 -0
  188. package/dist/server/modules/providers/controllers/provider-models.controller.js.map +1 -0
  189. package/dist/server/modules/providers/providers.module.js +2 -1
  190. package/dist/server/modules/providers/providers.module.js.map +1 -1
  191. package/dist/server/modules/records/controllers/records.controller.d.ts +2 -2
  192. package/dist/server/modules/records/controllers/records.controller.js.map +1 -1
  193. package/dist/server/modules/registry/services/template-upgrade.service.js +1 -9
  194. package/dist/server/modules/registry/services/template-upgrade.service.js.map +1 -1
  195. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.d.ts +21 -0
  196. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.js +207 -0
  197. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.js.map +1 -0
  198. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.d.ts +22 -0
  199. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.js +214 -0
  200. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.js.map +1 -0
  201. package/dist/server/modules/session-reader/adapters/gemini-session-reader.adapter.d.ts +23 -0
  202. package/dist/server/modules/session-reader/adapters/gemini-session-reader.adapter.js +224 -0
  203. package/dist/server/modules/session-reader/adapters/gemini-session-reader.adapter.js.map +1 -0
  204. package/dist/server/modules/session-reader/adapters/session-reader-adapter.factory.d.ts +9 -0
  205. package/dist/server/modules/session-reader/adapters/session-reader-adapter.factory.js +40 -0
  206. package/dist/server/modules/session-reader/adapters/session-reader-adapter.factory.js.map +1 -0
  207. package/dist/server/modules/session-reader/adapters/session-reader-adapter.interface.d.ts +37 -0
  208. package/dist/server/modules/session-reader/adapters/session-reader-adapter.interface.js +3 -0
  209. package/dist/server/modules/session-reader/adapters/session-reader-adapter.interface.js.map +1 -0
  210. package/dist/server/modules/session-reader/adapters/utils/estimate-content-tokens.d.ts +11 -0
  211. package/dist/server/modules/session-reader/adapters/utils/estimate-content-tokens.js +77 -0
  212. package/dist/server/modules/session-reader/adapters/utils/estimate-content-tokens.js.map +1 -0
  213. package/dist/server/modules/session-reader/adapters/utils/file-search.util.d.ts +1 -0
  214. package/dist/server/modules/session-reader/adapters/utils/file-search.util.js +22 -0
  215. package/dist/server/modules/session-reader/adapters/utils/file-search.util.js.map +1 -0
  216. package/dist/server/modules/session-reader/builders/chunk-builder.d.ts +5 -0
  217. package/dist/server/modules/session-reader/builders/chunk-builder.js +131 -0
  218. package/dist/server/modules/session-reader/builders/chunk-builder.js.map +1 -0
  219. package/dist/server/modules/session-reader/builders/semantic-step-extractor.d.ts +3 -0
  220. package/dist/server/modules/session-reader/builders/semantic-step-extractor.js +93 -0
  221. package/dist/server/modules/session-reader/builders/semantic-step-extractor.js.map +1 -0
  222. package/dist/server/modules/session-reader/builders/turn-builder.d.ts +3 -0
  223. package/dist/server/modules/session-reader/builders/turn-builder.js +102 -0
  224. package/dist/server/modules/session-reader/builders/turn-builder.js.map +1 -0
  225. package/dist/server/modules/session-reader/controllers/session-reader.controller.d.ts +430 -0
  226. package/dist/server/modules/session-reader/controllers/session-reader.controller.js +252 -0
  227. package/dist/server/modules/session-reader/controllers/session-reader.controller.js.map +1 -0
  228. package/dist/server/modules/session-reader/data/pricing.json +5113 -0
  229. package/dist/server/modules/session-reader/dtos/index.d.ts +2 -0
  230. package/dist/server/modules/session-reader/dtos/index.js +10 -0
  231. package/dist/server/modules/session-reader/dtos/index.js.map +1 -0
  232. package/dist/server/modules/session-reader/dtos/unified-chunk.types.d.ts +92 -0
  233. package/dist/server/modules/session-reader/dtos/unified-chunk.types.js +19 -0
  234. package/dist/server/modules/session-reader/dtos/unified-chunk.types.js.map +1 -0
  235. package/dist/server/modules/session-reader/dtos/unified-session.types.d.ts +118 -0
  236. package/dist/server/modules/session-reader/dtos/unified-session.types.js +23 -0
  237. package/dist/server/modules/session-reader/dtos/unified-session.types.js.map +1 -0
  238. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.d.ts +15 -0
  239. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.js +363 -0
  240. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.js.map +1 -0
  241. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.d.ts +16 -0
  242. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.js +622 -0
  243. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.js.map +1 -0
  244. package/dist/server/modules/session-reader/parsers/gemini-json.parser.d.ts +15 -0
  245. package/dist/server/modules/session-reader/parsers/gemini-json.parser.js +380 -0
  246. package/dist/server/modules/session-reader/parsers/gemini-json.parser.js.map +1 -0
  247. package/dist/server/modules/session-reader/services/pricing.interface.d.ts +9 -0
  248. package/dist/server/modules/session-reader/services/pricing.interface.js +24 -0
  249. package/dist/server/modules/session-reader/services/pricing.interface.js.map +1 -0
  250. package/dist/server/modules/session-reader/services/pricing.service.d.ts +21 -0
  251. package/dist/server/modules/session-reader/services/pricing.service.js +106 -0
  252. package/dist/server/modules/session-reader/services/pricing.service.js.map +1 -0
  253. package/dist/server/modules/session-reader/services/session-cache.service.d.ts +24 -0
  254. package/dist/server/modules/session-reader/services/session-cache.service.js +203 -0
  255. package/dist/server/modules/session-reader/services/session-cache.service.js.map +1 -0
  256. package/dist/server/modules/session-reader/services/session-reader.service.d.ts +56 -0
  257. package/dist/server/modules/session-reader/services/session-reader.service.js +305 -0
  258. package/dist/server/modules/session-reader/services/session-reader.service.js.map +1 -0
  259. package/dist/server/modules/session-reader/services/subagent-locator.service.d.ts +10 -0
  260. package/dist/server/modules/session-reader/services/subagent-locator.service.js +102 -0
  261. package/dist/server/modules/session-reader/services/subagent-locator.service.js.map +1 -0
  262. package/dist/server/modules/session-reader/services/subagent-resolver.service.d.ts +22 -0
  263. package/dist/server/modules/session-reader/services/subagent-resolver.service.js +211 -0
  264. package/dist/server/modules/session-reader/services/subagent-resolver.service.js.map +1 -0
  265. package/dist/server/modules/session-reader/services/transcript-path-validator.service.d.ts +7 -0
  266. package/dist/server/modules/session-reader/services/transcript-path-validator.service.js +150 -0
  267. package/dist/server/modules/session-reader/services/transcript-path-validator.service.js.map +1 -0
  268. package/dist/server/modules/session-reader/services/transcript-persistence.listener.d.ts +30 -0
  269. package/dist/server/modules/session-reader/services/transcript-persistence.listener.js +336 -0
  270. package/dist/server/modules/session-reader/services/transcript-persistence.listener.js.map +1 -0
  271. package/dist/server/modules/session-reader/services/transcript-watcher.service.d.ts +28 -0
  272. package/dist/server/modules/session-reader/services/transcript-watcher.service.js +325 -0
  273. package/dist/server/modules/session-reader/services/transcript-watcher.service.js.map +1 -0
  274. package/dist/server/modules/session-reader/session-reader.module.d.ts +13 -0
  275. package/dist/server/modules/session-reader/session-reader.module.js +82 -0
  276. package/dist/server/modules/session-reader/session-reader.module.js.map +1 -0
  277. package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +2 -0
  278. package/dist/server/modules/sessions/dtos/sessions.dto.js.map +1 -1
  279. package/dist/server/modules/sessions/services/sessions-message-pool.service.d.ts +2 -2
  280. package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
  281. package/dist/server/modules/sessions/services/sessions.service.js +19 -4
  282. package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
  283. package/dist/server/modules/sessions/utils/profile-options.d.ts +1 -0
  284. package/dist/server/modules/sessions/utils/profile-options.js +18 -0
  285. package/dist/server/modules/sessions/utils/profile-options.js.map +1 -1
  286. package/dist/server/modules/settings/controllers/settings.controller.d.ts +2 -2
  287. package/dist/server/modules/settings/controllers/settings.controller.js.map +1 -1
  288. package/dist/server/modules/settings/dtos/settings.dto.d.ts +14 -2
  289. package/dist/server/modules/settings/dtos/settings.dto.js +1 -0
  290. package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
  291. package/dist/server/modules/skills/services/skill-source-registry.service.d.ts +2 -2
  292. package/dist/server/modules/skills/services/skill-source-registry.service.js.map +1 -1
  293. package/dist/server/modules/statuses/controllers/statuses.controller.d.ts +2 -2
  294. package/dist/server/modules/statuses/controllers/statuses.controller.js.map +1 -1
  295. package/dist/server/modules/storage/db/schema.d.ts +176 -0
  296. package/dist/server/modules/storage/db/schema.js +17 -1
  297. package/dist/server/modules/storage/db/schema.js.map +1 -1
  298. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +54 -9
  299. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  300. package/dist/server/modules/storage/local/delegates/agent-profile.delegate.d.ts +39 -0
  301. package/dist/server/modules/storage/local/delegates/agent-profile.delegate.js +286 -0
  302. package/dist/server/modules/storage/local/delegates/agent-profile.delegate.js.map +1 -0
  303. package/dist/server/modules/storage/local/delegates/agent.delegate.d.ts +21 -0
  304. package/dist/server/modules/storage/local/delegates/agent.delegate.js +202 -0
  305. package/dist/server/modules/storage/local/delegates/agent.delegate.js.map +1 -0
  306. package/dist/server/modules/storage/local/delegates/base-storage.delegate.d.ts +12 -0
  307. package/dist/server/modules/storage/local/delegates/base-storage.delegate.js +19 -0
  308. package/dist/server/modules/storage/local/delegates/base-storage.delegate.js.map +1 -0
  309. package/dist/server/modules/storage/local/delegates/document.delegate.d.ts +20 -0
  310. package/dist/server/modules/storage/local/delegates/document.delegate.js +267 -0
  311. package/dist/server/modules/storage/local/delegates/document.delegate.js.map +1 -0
  312. package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +38 -0
  313. package/dist/server/modules/storage/local/delegates/epic.delegate.js +686 -0
  314. package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -0
  315. package/dist/server/modules/storage/local/delegates/guest.delegate.d.ts +14 -0
  316. package/dist/server/modules/storage/local/delegates/guest.delegate.js +172 -0
  317. package/dist/server/modules/storage/local/delegates/guest.delegate.js.map +1 -0
  318. package/dist/server/modules/storage/local/delegates/profile-provider-config.delegate.d.ts +17 -0
  319. package/dist/server/modules/storage/local/delegates/profile-provider-config.delegate.js +249 -0
  320. package/dist/server/modules/storage/local/delegates/profile-provider-config.delegate.js.map +1 -0
  321. package/dist/server/modules/storage/local/delegates/project.delegate.d.ts +16 -0
  322. package/dist/server/modules/storage/local/delegates/project.delegate.js +631 -0
  323. package/dist/server/modules/storage/local/delegates/project.delegate.js.map +1 -0
  324. package/dist/server/modules/storage/local/delegates/prompt.delegate.d.ts +17 -0
  325. package/dist/server/modules/storage/local/delegates/prompt.delegate.js +318 -0
  326. package/dist/server/modules/storage/local/delegates/prompt.delegate.js.map +1 -0
  327. package/dist/server/modules/storage/local/delegates/provider-model.delegate.d.ts +15 -0
  328. package/dist/server/modules/storage/local/delegates/provider-model.delegate.js +210 -0
  329. package/dist/server/modules/storage/local/delegates/provider-model.delegate.js.map +1 -0
  330. package/dist/server/modules/storage/local/delegates/provider.delegate.d.ts +18 -0
  331. package/dist/server/modules/storage/local/delegates/provider.delegate.js +151 -0
  332. package/dist/server/modules/storage/local/delegates/provider.delegate.js.map +1 -0
  333. package/dist/server/modules/storage/local/delegates/record.delegate.d.ts +16 -0
  334. package/dist/server/modules/storage/local/delegates/record.delegate.js +190 -0
  335. package/dist/server/modules/storage/local/delegates/record.delegate.js.map +1 -0
  336. package/dist/server/modules/storage/local/delegates/review.delegate.d.ts +25 -0
  337. package/dist/server/modules/storage/local/delegates/review.delegate.js +426 -0
  338. package/dist/server/modules/storage/local/delegates/review.delegate.js.map +1 -0
  339. package/dist/server/modules/storage/local/delegates/skill-source.delegate.d.ts +28 -0
  340. package/dist/server/modules/storage/local/delegates/skill-source.delegate.js +347 -0
  341. package/dist/server/modules/storage/local/delegates/skill-source.delegate.js.map +1 -0
  342. package/dist/server/modules/storage/local/delegates/status.delegate.d.ts +12 -0
  343. package/dist/server/modules/storage/local/delegates/status.delegate.js +130 -0
  344. package/dist/server/modules/storage/local/delegates/status.delegate.js.map +1 -0
  345. package/dist/server/modules/storage/local/delegates/subscriber.delegate.d.ts +11 -0
  346. package/dist/server/modules/storage/local/delegates/subscriber.delegate.js +144 -0
  347. package/dist/server/modules/storage/local/delegates/subscriber.delegate.js.map +1 -0
  348. package/dist/server/modules/storage/local/delegates/tag.delegate.d.ts +11 -0
  349. package/dist/server/modules/storage/local/delegates/tag.delegate.js +102 -0
  350. package/dist/server/modules/storage/local/delegates/tag.delegate.js.map +1 -0
  351. package/dist/server/modules/storage/local/delegates/watcher.delegate.d.ts +12 -0
  352. package/dist/server/modules/storage/local/delegates/watcher.delegate.js +183 -0
  353. package/dist/server/modules/storage/local/delegates/watcher.delegate.js.map +1 -0
  354. package/dist/server/modules/storage/local/helpers/storage-helpers.d.ts +32 -0
  355. package/dist/server/modules/storage/local/helpers/storage-helpers.js +276 -0
  356. package/dist/server/modules/storage/local/helpers/storage-helpers.js.map +1 -0
  357. package/dist/server/modules/storage/local/local-storage.service.d.ts +30 -22
  358. package/dist/server/modules/storage/local/local-storage.service.js +227 -3627
  359. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  360. package/dist/server/modules/storage/models/domain.models.d.ts +14 -1
  361. package/dist/server/modules/subscribers/services/subscribers.service.d.ts +2 -2
  362. package/dist/server/modules/subscribers/services/subscribers.service.js.map +1 -1
  363. package/dist/server/modules/watchers/services/watchers.service.d.ts +2 -2
  364. package/dist/server/templates/3-agents-dev.json +180 -162
  365. package/dist/server/templates/5-agents-dev.json +244 -194
  366. package/dist/server/test-setup-node.js +13 -0
  367. package/dist/server/test-setup-node.js.map +1 -1
  368. package/dist/server/test-setup.js +14 -12
  369. package/dist/server/test-setup.js.map +1 -1
  370. package/dist/server/tsconfig.tsbuildinfo +1 -1
  371. package/dist/server/ui/assets/{ReviewDetailPage-DNFeBzB6.js → ReviewDetailPage-BFLK1NAe.js} +1 -1
  372. package/dist/server/ui/assets/{ReviewsPage-DCM_dJlA.js → ReviewsPage-BqF2XSr0.js} +1 -1
  373. package/dist/server/ui/assets/index-Bs5Kat1T.js +1011 -0
  374. package/dist/server/ui/assets/index-CW3WYriQ.css +32 -0
  375. package/dist/server/ui/assets/{useReviewSubscription-Bx6D-cpf.js → useReviewSubscription-B73OxTf6.js} +32 -35
  376. package/dist/server/ui/index.html +2 -2
  377. package/dist/templates/3-agents-dev.json +180 -162
  378. package/dist/templates/5-agents-dev.json +244 -194
  379. package/package.json +23 -2
  380. package/dist/server/ui/assets/index-BgvrDIBb.js +0 -972
  381. package/dist/server/ui/assets/index-CzjcxP26.css +0 -32
@@ -19,14 +19,15 @@ const sessions_service_1 = require("../../sessions/services/sessions.service");
19
19
  const settings_service_1 = require("../../settings/services/settings.service");
20
20
  const watchers_service_1 = require("../../watchers/services/watchers.service");
21
21
  const watcher_runner_service_1 = require("../../watchers/services/watcher-runner.service");
22
- const logger_1 = require("../../../common/logging/logger");
23
- const error_types_1 = require("../../../common/errors/error-types");
24
- const path_1 = require("path");
25
- const fs_1 = require("fs");
26
- const shared_1 = require("@devchain/shared");
27
22
  const unified_template_service_1 = require("../../registry/services/unified-template.service");
28
- const templates_directory_1 = require("../../../common/templates-directory");
29
- const logger = (0, logger_1.createLogger)('ProjectsService');
23
+ const project_import_1 = require("../helpers/project-import");
24
+ const project_export_1 = require("../helpers/project-export");
25
+ const template_loader_1 = require("../helpers/template-loader");
26
+ const profile_mapping_helpers_1 = require("../helpers/profile-mapping.helpers");
27
+ const project_presets_helpers_1 = require("../helpers/project-presets.helpers");
28
+ const project_runtime_helpers_1 = require("../helpers/project-runtime.helpers");
29
+ const project_template_manifest_helpers_1 = require("../helpers/project-template-manifest.helpers");
30
+ const template_file_helpers_1 = require("../helpers/template-file.helpers");
30
31
  let ProjectsService = class ProjectsService {
31
32
  constructor(storage, sessions, settings, watchersService, watcherRunner, unifiedTemplateService) {
32
33
  this.storage = storage;
@@ -36,1870 +37,67 @@ let ProjectsService = class ProjectsService {
36
37
  this.watcherRunner = watcherRunner;
37
38
  this.unifiedTemplateService = unifiedTemplateService;
38
39
  }
39
- findTemplatesDirectory() {
40
- const templatesDir = (0, templates_directory_1.resolveTemplatesDirectory)(__dirname);
41
- if (templatesDir) {
42
- logger.debug({ path: templatesDir }, 'Found templates directory');
43
- return templatesDir;
44
- }
45
- logger.warn('Templates directory not found in known locations');
46
- return null;
47
- }
48
40
  async listTemplates() {
49
- logger.info('listTemplates');
50
- const templatesDir = this.findTemplatesDirectory();
51
- if (!templatesDir) {
52
- logger.error('Templates directory not found');
53
- throw new error_types_1.StorageError('Templates directory not found', {
54
- hint: 'Templates directory is not available in this deployment',
55
- });
56
- }
57
- try {
58
- const files = (0, fs_1.readdirSync)(templatesDir);
59
- const jsonFiles = files.filter((file) => file.endsWith('.json'));
60
- const templates = jsonFiles.map((fileName) => {
61
- const id = fileName.replace(/\.json$/, '');
62
- return { id, fileName };
63
- });
64
- logger.info({ templatesDir, count: templates.length }, 'Listed project templates');
65
- return templates;
66
- }
67
- catch (error) {
68
- logger.error({ error, templatesDir }, 'Failed to read templates directory');
69
- throw new error_types_1.StorageError('Failed to read templates directory', {
70
- hint: 'Error accessing templates',
71
- });
72
- }
41
+ return (0, template_file_helpers_1.listTemplatesWithHelper)(__dirname);
73
42
  }
74
43
  async getTemplateContent(templateId) {
75
- logger.info({ templateId }, 'getTemplateContent');
76
- const templatesDir = this.findTemplatesDirectory();
77
- if (!templatesDir) {
78
- throw new error_types_1.StorageError('Templates directory not found', {
79
- hint: 'Templates directory is not available in this deployment',
80
- });
81
- }
82
- const TEMPLATE_ID_REGEX = /^[a-zA-Z0-9_-]+$/;
83
- if (!TEMPLATE_ID_REGEX.test(templateId)) {
84
- throw new error_types_1.ValidationError('Invalid template ID: must contain only alphanumeric characters, hyphens, and underscores', { templateId });
85
- }
86
- const resolvedTemplatesDir = (0, path_1.resolve)(templatesDir);
87
- const templatePath = (0, path_1.resolve)(templatesDir, `${templateId}.json`);
88
- if (!templatePath.startsWith(resolvedTemplatesDir + path_1.sep)) {
89
- logger.warn({ templateId, templatePath, templatesDir: resolvedTemplatesDir }, 'Path traversal attempt detected');
90
- throw new error_types_1.ValidationError('Invalid template ID: path traversal not allowed', { templateId });
91
- }
92
- if (!(0, fs_1.existsSync)(templatePath)) {
93
- throw new error_types_1.NotFoundError('Template', templateId);
94
- }
95
- try {
96
- const content = (0, fs_1.readFileSync)(templatePath, 'utf-8');
97
- return JSON.parse(content);
98
- }
99
- catch (error) {
100
- logger.error({ error, templatePath }, 'Failed to read template file');
101
- throw new error_types_1.StorageError('Failed to read template file', {
102
- hint: 'Template file exists but cannot be read or parsed',
103
- });
104
- }
44
+ return (0, template_file_helpers_1.getTemplateContentWithHelper)(__dirname, templateId);
105
45
  }
106
46
  async createFromTemplate(input) {
107
- logger.info({ input }, 'createFromTemplate');
108
- let templateResult;
109
- let templateSlug;
110
- if (input.templatePath) {
111
- templateResult = this.unifiedTemplateService.getTemplateFromFilePath(input.templatePath);
112
- const manifest = templateResult.content._manifest;
113
- templateSlug = manifest?.slug ?? this.deriveSlugFromPath(input.templatePath);
114
- }
115
- else if (input.slug) {
116
- templateResult = await this.unifiedTemplateService.getTemplate(input.slug, input.version ?? undefined);
117
- templateSlug = input.slug;
118
- }
119
- else {
120
- throw new error_types_1.ValidationError('Either slug or templatePath is required', {});
121
- }
122
- let payload;
123
- try {
124
- payload = shared_1.ExportSchema.parse(templateResult.content);
125
- }
126
- catch (error) {
127
- logger.error({ error, slug: templateSlug, version: input.version }, 'Invalid template format');
128
- throw new error_types_1.ValidationError('Invalid template format', {
129
- hint: 'Template file does not match expected export schema',
130
- });
131
- }
132
- const presetCoveredAgentNames = new Set();
133
- const presetAgentResolvedProviders = new Map();
134
- let presetCoversAllAgents = false;
135
- if (input.presetName) {
136
- const selectedPreset = (payload.presets ?? []).find((p) => p.name === input.presetName);
137
- if (selectedPreset) {
138
- const profileConfigsByProfileId = new Map();
139
- for (const prof of payload.profiles ?? []) {
140
- if (!prof.id)
141
- continue;
142
- const providerConfigs = prof.providerConfigs;
143
- if (providerConfigs) {
144
- const configMap = new Map();
145
- for (const pc of providerConfigs) {
146
- configMap.set(pc.name.trim().toLowerCase(), pc.providerName.trim().toLowerCase());
147
- }
148
- profileConfigsByProfileId.set(prof.id, configMap);
149
- }
150
- }
151
- const agentNameToProfileId = new Map();
152
- for (const agent of payload.agents ?? []) {
153
- if (agent.profileId) {
154
- agentNameToProfileId.set(agent.name.trim().toLowerCase(), agent.profileId);
155
- }
156
- }
157
- const localProviders = await this.storage.listProviders();
158
- const localProviderNames = new Set(localProviders.items.map((p) => p.name.trim().toLowerCase()));
159
- let allCovered = true;
160
- for (const ac of selectedPreset.agentConfigs) {
161
- const agentKey = ac.agentName.trim().toLowerCase();
162
- const profileId = agentNameToProfileId.get(agentKey);
163
- if (!profileId) {
164
- allCovered = false;
165
- continue;
166
- }
167
- const configMap = profileConfigsByProfileId.get(profileId);
168
- const providerName = configMap?.get(ac.providerConfigName.trim().toLowerCase());
169
- if (providerName) {
170
- presetAgentResolvedProviders.set(agentKey, providerName);
171
- }
172
- if (providerName && localProviderNames.has(providerName)) {
173
- presetCoveredAgentNames.add(ac.agentName.trim().toLowerCase());
174
- }
175
- else {
176
- allCovered = false;
177
- }
178
- }
179
- const allTemplateAgentNames = new Set((payload.agents ?? []).map((a) => a.name.trim().toLowerCase()));
180
- presetCoversAllAgents =
181
- allCovered && [...allTemplateAgentNames].every((n) => presetCoveredAgentNames.has(n));
182
- if (presetCoversAllAgents) {
183
- logger.info({ presetName: input.presetName, coveredAgents: [...presetCoveredAgentNames] }, 'Preset covers all agents with available providers — skipping family mapping');
184
- }
185
- else if (presetCoveredAgentNames.size > 0) {
186
- logger.info({ presetName: input.presetName, coveredAgents: [...presetCoveredAgentNames] }, 'Preset partially covers agents — suppressing warnings for covered agents only');
187
- }
188
- }
189
- }
190
- const familyResult = await this.computeFamilyAlternatives(payload.profiles, payload.agents);
191
- let effectiveFamilyProviderMappings = input.familyProviderMappings;
192
- if (!presetCoversAllAgents) {
193
- const needsMapping = familyResult.alternatives.some((alt) => !alt.defaultProviderAvailable);
194
- if (needsMapping && !effectiveFamilyProviderMappings) {
195
- const autoMappings = {};
196
- let canAutoSelect = familyResult.canImport;
197
- for (const alt of familyResult.alternatives) {
198
- if (!alt.defaultProviderAvailable) {
199
- if (alt.availableProviders.length === 1) {
200
- autoMappings[alt.familySlug] = alt.availableProviders[0];
201
- }
202
- else {
203
- canAutoSelect = false;
204
- }
205
- }
206
- }
207
- if (canAutoSelect) {
208
- effectiveFamilyProviderMappings = autoMappings;
209
- logger.info({ autoMappings }, 'Auto-selected provider mappings for single-alternative families');
210
- }
211
- else {
212
- return {
213
- success: false,
214
- providerMappingRequired: {
215
- missingProviders: familyResult.missingProviders,
216
- familyAlternatives: familyResult.alternatives,
217
- canImport: familyResult.canImport,
218
- },
219
- };
220
- }
221
- }
222
- if (!familyResult.canImport) {
223
- return {
224
- success: false,
225
- providerMappingRequired: {
226
- missingProviders: familyResult.missingProviders,
227
- familyAlternatives: familyResult.alternatives,
228
- canImport: false,
229
- },
230
- };
231
- }
232
- }
233
- const providerNames = new Set((payload.profiles ?? []).map((p) => p.provider.name.trim().toLowerCase()));
234
- const { available } = await this.resolveProviders(providerNames);
235
- if (available.size === 0 && (payload.profiles ?? []).length > 0) {
236
- throw new error_types_1.ValidationError('No providers are installed. At least one provider is required to create a project from a template.');
237
- }
238
- const selectedProfilesByFamily = this.selectProfilesForFamilies(payload.profiles, payload.agents, effectiveFamilyProviderMappings, available, { presetCoveredAgentNames, presetAgentResolvedProviders });
239
- const warnings = [];
240
- for (const [, substitution] of selectedProfilesByFamily.providerSubstitutions) {
241
- warnings.push({
242
- type: 'provider_mismatch',
243
- originalProvider: substitution.originalProvider,
244
- substituteProvider: substitution.substituteProvider,
245
- agentNames: substitution.agentNames,
246
- });
247
- }
248
- const templatePayload = {
249
- prompts: payload.prompts.map((p) => ({
250
- id: p.id,
251
- title: p.title,
252
- content: p.content,
253
- version: p.version,
254
- tags: p.tags,
255
- })),
256
- profiles: selectedProfilesByFamily.profilesToCreate.map((prof) => {
257
- const providerId = available.get(prof.provider.name.trim().toLowerCase());
258
- if (!providerId) {
259
- throw new error_types_1.NotFoundError('Provider', prof.provider.name);
260
- }
261
- return {
262
- id: prof.id,
263
- name: prof.name,
264
- providerId,
265
- familySlug: prof.familySlug ?? null,
266
- options: this.normalizeProfileOptions(prof.options),
267
- instructions: prof.instructions ?? null,
268
- temperature: prof.temperature ?? null,
269
- maxTokens: prof.maxTokens ?? null,
270
- };
271
- }),
272
- agents: payload.agents.map((a) => {
273
- const remappedProfileId = selectedProfilesByFamily.agentProfileMap.get(a.id ?? '') ?? a.profileId;
274
- return {
275
- id: a.id,
276
- name: a.name,
277
- profileId: remappedProfileId,
278
- description: a.description,
279
- };
280
- }),
281
- statuses: payload.statuses.map((s) => ({
282
- id: s.id,
283
- label: s.label,
284
- color: s.color,
285
- position: s.position,
286
- mcpHidden: s.mcpHidden,
287
- })),
288
- initialPrompt: payload.initialPrompt,
289
- };
290
- const result = input.projectId
291
- ? await this.storage.createProjectWithTemplate({
292
- name: input.name,
293
- description: input.description ?? null,
294
- rootPath: input.rootPath,
295
- isTemplate: false,
296
- }, templatePayload, {
297
- projectId: input.projectId,
298
- })
299
- : await this.storage.createProjectWithTemplate({
300
- name: input.name,
301
- description: input.description ?? null,
302
- rootPath: input.rootPath,
303
- isTemplate: false,
304
- }, templatePayload);
305
- const { agentNameToId: agentNameToNewId, profileNameToId: profileNameToNewId } = this.buildNameToIdMaps(templatePayload, result.mappings);
306
- const configLookupMap = new Map();
307
- for (const prof of selectedProfilesByFamily.profilesToCreate) {
308
- if (!prof.id)
309
- continue;
310
- const newProfileId = result.mappings.profileIdMap[prof.id];
311
- if (!newProfileId)
312
- continue;
313
- const providerConfigs = prof.providerConfigs;
314
- if (providerConfigs && providerConfigs.length > 0) {
315
- for (const config of providerConfigs) {
316
- const configProviderId = available.get(config.providerName.trim().toLowerCase());
317
- if (!configProviderId) {
318
- logger.warn({ profileName: prof.name, providerName: config.providerName }, 'Provider not found for config in createFromTemplate, skipping');
319
- continue;
320
- }
321
- const createdConfig = await this.storage.createProfileProviderConfig({
322
- profileId: newProfileId,
323
- providerId: configProviderId,
324
- name: config.name,
325
- options: config.options ?? null,
326
- env: config.env ?? null,
327
- });
328
- const lookupKey = `${newProfileId}:${config.name.trim().toLowerCase()}`;
329
- configLookupMap.set(lookupKey, createdConfig.id);
330
- }
331
- }
332
- }
333
- const profilesWithProviderConfigs = new Map();
334
- for (const prof of selectedProfilesByFamily.profilesToCreate) {
335
- if (!prof.id)
336
- continue;
337
- const newProfileId = result.mappings.profileIdMap[prof.id];
338
- if (!newProfileId)
339
- continue;
340
- const providerConfigs = prof.providerConfigs;
341
- if (providerConfigs && providerConfigs.length > 0) {
342
- profilesWithProviderConfigs.set(newProfileId, {
343
- profileName: prof.name,
344
- configNames: new Set(providerConfigs.map((pc) => pc.name.trim().toLowerCase())),
345
- });
346
- }
347
- }
348
- for (const a of payload.agents) {
349
- const agentWithConfig = a;
350
- if (!agentWithConfig.providerConfigName || !a.id)
351
- continue;
352
- const newAgentId = result.mappings.agentIdMap[a.id];
353
- if (!newAgentId)
354
- continue;
355
- const remappedProfileId = selectedProfilesByFamily.agentProfileMap.get(a.id) ?? a.profileId;
356
- const newProfileId = remappedProfileId
357
- ? result.mappings.profileIdMap[remappedProfileId]
358
- : null;
359
- if (!newProfileId)
360
- continue;
361
- const lookupKey = `${newProfileId}:${agentWithConfig.providerConfigName.trim().toLowerCase()}`;
362
- const providerConfigId = configLookupMap.get(lookupKey);
363
- if (providerConfigId) {
364
- await this.storage.updateAgent(newAgentId, { providerConfigId });
365
- logger.debug({ agentName: a.name, providerConfigId }, 'Updated agent with providerConfigId');
366
- }
367
- else {
368
- const fallbackKey = Array.from(configLookupMap.keys()).find((key) => key.startsWith(`${newProfileId}:`));
369
- if (fallbackKey) {
370
- const fallbackConfigId = configLookupMap.get(fallbackKey);
371
- await this.storage.updateAgent(newAgentId, { providerConfigId: fallbackConfigId });
372
- logger.warn({
373
- agentName: a.name,
374
- providerConfigName: agentWithConfig.providerConfigName,
375
- fallbackConfigId,
376
- }, 'Agent providerConfigName unavailable, fell back to first available config');
377
- }
378
- else {
379
- logger.warn({ agentName: a.name, providerConfigName: agentWithConfig.providerConfigName }, 'No provider config available for agent in createFromTemplate');
380
- }
381
- }
382
- }
383
- for (const [newProfileId, { profileName, configNames }] of profilesWithProviderConfigs) {
384
- const existingConfigs = await this.storage.listProfileProviderConfigsByProfile(newProfileId);
385
- for (const existingConfig of existingConfigs) {
386
- const isFromProviderConfigs = configNames.has(existingConfig.name.trim().toLowerCase());
387
- if (!isFromProviderConfigs && existingConfig.name === profileName) {
388
- try {
389
- await this.storage.deleteProfileProviderConfig(existingConfig.id);
390
- logger.debug({ profileName, configId: existingConfig.id }, 'Deleted duplicate config created by storage layer');
391
- }
392
- catch {
393
- logger.debug({ profileName, configId: existingConfig.id }, 'Skipped deleting default config — still referenced by agents');
394
- }
395
- }
396
- }
397
- }
398
- let mergedInitialPromptTitle;
399
- if (payload.initialPrompt?.title) {
400
- mergedInitialPromptTitle = payload.initialPrompt.title;
401
- }
402
- else if (payload.initialPrompt?.promptId) {
403
- const matchingPrompt = payload.prompts.find((p) => p.id === payload.initialPrompt.promptId);
404
- if (matchingPrompt) {
405
- mergedInitialPromptTitle = matchingPrompt.title;
406
- }
407
- }
408
- else if (payload.projectSettings?.initialPromptTitle) {
409
- mergedInitialPromptTitle = payload.projectSettings.initialPromptTitle;
410
- }
411
- const mergedSettings = payload.projectSettings
412
- ? { ...payload.projectSettings, initialPromptTitle: mergedInitialPromptTitle }
413
- : mergedInitialPromptTitle
414
- ? { initialPromptTitle: mergedInitialPromptTitle }
415
- : undefined;
416
- const promptTitleToId = new Map();
417
- for (const p of payload.prompts) {
418
- if (p.id && result.mappings.promptIdMap[p.id]) {
419
- promptTitleToId.set(p.title.toLowerCase(), result.mappings.promptIdMap[p.id]);
420
- }
421
- }
422
- const statusLabelToId = new Map();
423
- for (const s of templatePayload.statuses) {
424
- if (s.id && result.mappings.statusIdMap[s.id]) {
425
- statusLabelToId.set(s.label.toLowerCase(), result.mappings.statusIdMap[s.id]);
426
- }
427
- }
428
- const archiveTemplateStatus = templatePayload.statuses.find((s) => s.label.toLowerCase() === 'archive');
429
- const archiveStatusId = archiveTemplateStatus?.id
430
- ? (result.mappings.statusIdMap[archiveTemplateStatus.id] ?? null)
431
- : null;
432
- const settingsResult = await this.applyProjectSettings(result.project.id, mergedSettings, { promptTitleToId, statusLabelToId }, archiveStatusId);
433
- const initialPromptSet = settingsResult.initialPromptSet;
434
- const { created: watchersCreated } = await this.createWatchersFromPayload(result.project.id, payload.watchers, {
435
- agentNameToId: agentNameToNewId,
436
- profileNameToId: profileNameToNewId,
437
- providerNameToId: available,
438
- profileNameRemapMap: selectedProfilesByFamily.profileNameRemapMap,
439
- });
440
- const { created: subscribersCreated } = await this.createSubscribersFromPayload(result.project.id, payload.subscribers);
441
- const manifestVersion = payload._manifest?.version ?? null;
442
- const installedVersion = templateResult.version ?? manifestVersion;
443
- const registryConfig = this.settings.getRegistryConfig();
444
- await this.settings.setProjectTemplateMetadata(result.project.id, {
445
- templateSlug,
446
- source: templateResult.source,
447
- installedVersion,
448
- registryUrl: templateResult.source === 'registry' ? registryConfig.url : null,
449
- installedAt: new Date().toISOString(),
47
+ return (0, template_loader_1.createFromTemplateWithHelper)(input, {
48
+ storage: this.storage,
49
+ settings: this.settings,
50
+ unifiedTemplateService: this.unifiedTemplateService,
51
+ deriveSlugFromPath: template_file_helpers_1.deriveSlugFromPath,
52
+ computeFamilyAlternatives: (profiles, agents) => this.computeFamilyAlternatives(profiles, agents),
53
+ normalizeProfileOptions: project_runtime_helpers_1.normalizeProfileOptions,
54
+ applyProjectSettings: (projectId, projectSettings, maps, archiveStatusId) => (0, project_runtime_helpers_1.applyProjectSettingsWithHelper)(projectId, projectSettings, maps, archiveStatusId, this.settings),
55
+ createWatchersFromPayload: (projectId, watchers, maps) => (0, project_runtime_helpers_1.createWatchersFromPayloadWithHelper)(projectId, watchers, maps, this.watchersService),
56
+ createSubscribersFromPayload: (projectId, subscribers) => (0, project_runtime_helpers_1.createSubscribersFromPayloadWithHelper)(projectId, subscribers, this.storage),
57
+ applyPreset: (projectId, presetName, nameMaps) => (0, project_presets_helpers_1.applyPresetWithHelper)(projectId, presetName, { storage: this.storage, settings: this.settings }, nameMaps),
450
58
  });
451
- logger.info({
452
- projectId: result.project.id,
453
- slug: templateSlug,
454
- source: templateResult.source,
455
- version: installedVersion,
456
- }, 'Template metadata set for project');
457
- const rawPresets = payload.presets;
458
- const templatePresets = Array.isArray(rawPresets) ? rawPresets : [];
459
- if (templatePresets.length > 0) {
460
- await this.settings.setProjectPresets(result.project.id, templatePresets);
461
- logger.info({ projectId: result.project.id, presetCount: templatePresets.length }, 'Presets stored for project');
462
- }
463
- if (input.presetName) {
464
- const selectedPreset = templatePresets.find((p) => typeof p === 'object' && p !== null && 'name' in p && p.name === input.presetName);
465
- if (!selectedPreset) {
466
- logger.warn({ projectId: result.project.id, presetName: input.presetName }, 'Selected preset not found in template');
467
- }
468
- else {
469
- await this.applyPreset(result.project.id, input.presetName, {
470
- agentNameToId: agentNameToNewId,
471
- configLookupMap,
472
- });
473
- logger.info({ projectId: result.project.id, presetName: input.presetName }, 'Applied preset to project');
474
- }
475
- }
476
- return {
477
- success: true,
478
- project: result.project,
479
- imported: {
480
- ...result.imported,
481
- watchers: watchersCreated,
482
- subscribers: subscribersCreated,
483
- },
484
- mappings: result.mappings,
485
- initialPromptSet,
486
- message: 'Project created from template successfully.',
487
- ...(warnings.length > 0 ? { warnings } : {}),
488
- };
489
59
  }
490
60
  async exportProject(projectId, opts) {
491
- logger.info({ projectId }, 'exportProject');
492
- const { manifestOverrides, presets: presetsOverride } = opts ?? {};
493
- const [project, promptsRes, profilesRes, agentsRes, statusesRes, initialPrompt, settings, watchersRes, subscribersRes,] = await Promise.all([
494
- this.storage.getProject(projectId),
495
- this.storage.listPrompts({ projectId, limit: 1000, offset: 0 }),
496
- this.storage.listAgentProfiles({ projectId, limit: 1000, offset: 0 }),
497
- this.storage.listAgents(projectId, { limit: 1000, offset: 0 }),
498
- this.storage.listStatuses(projectId, { limit: 1000, offset: 0 }),
499
- this.storage.getInitialSessionPrompt(projectId),
500
- Promise.resolve(this.settings.getSettings()),
501
- this.storage.listWatchers(projectId),
502
- this.storage.listSubscribers(projectId),
503
- ]);
504
- const secretKeys = new Set([
505
- 'apikey',
506
- 'api_key',
507
- 'api-key',
508
- 'api_key_id',
509
- 'api-secret',
510
- 'api_secret',
511
- 'token',
512
- 'access_token',
513
- 'access-token',
514
- 'refresh_token',
515
- 'refresh-token',
516
- 'secret',
517
- 'client_secret',
518
- 'clientsecret',
519
- 'password',
520
- 'openaiapikey',
521
- 'anthropicapikey',
522
- 'azureapikey',
523
- 'googleapikey',
524
- 'geminiapikey',
525
- ]);
526
- const _sanitize = (value) => {
527
- if (Array.isArray(value)) {
528
- return value.map((v) => _sanitize(v));
529
- }
530
- if (value && typeof value === 'object') {
531
- const out = {};
532
- for (const [k, v] of Object.entries(value)) {
533
- const keyLc = k.toLowerCase();
534
- if (secretKeys.has(keyLc)) {
535
- out[k] = '***';
536
- }
537
- else {
538
- out[k] = _sanitize(v);
539
- }
540
- }
541
- return out;
542
- }
543
- return value;
544
- };
545
- const fullPrompts = await Promise.all(promptsRes.items.map((p) => this.storage.getPrompt(p.id)));
546
- const prompts = fullPrompts.map((p) => ({
547
- id: p.id,
548
- title: p.title,
549
- content: p.content,
550
- version: p.version,
551
- tags: p.tags,
552
- }));
553
- const configIdToInfo = new Map();
554
- const profileIds = profilesRes.items.map((p) => p.id);
555
- const allConfigsByProfile = new Map();
556
- await Promise.all(profileIds.map(async (profileId) => {
557
- const configs = await this.storage.listProfileProviderConfigsByProfile(profileId);
558
- allConfigsByProfile.set(profileId, configs);
559
- }));
560
- const allProviderIds = new Set();
561
- for (const configs of allConfigsByProfile.values()) {
562
- for (const config of configs) {
563
- allProviderIds.add(config.providerId);
564
- }
565
- }
566
- const providersArray = await this.storage.listProvidersByIds([...allProviderIds]);
567
- const providersMap = new Map(providersArray.map((p) => [p.id, p]));
568
- const profiles = profilesRes.items.map((prof) => {
569
- const configs = allConfigsByProfile.get(prof.id) || [];
570
- let primaryProvider = null;
571
- const providerConfigs = configs.map((config) => {
572
- const configProvider = providersMap.get(config.providerId);
573
- if (!primaryProvider && configProvider) {
574
- primaryProvider = { id: configProvider.id, name: configProvider.name };
575
- }
576
- const configName = config.name;
577
- configIdToInfo.set(config.id, { name: configName, profileId: prof.id });
578
- return {
579
- name: configName,
580
- providerName: configProvider?.name || 'unknown',
581
- options: config.options,
582
- env: config.env,
583
- position: config.position,
584
- };
585
- });
586
- return {
587
- id: prof.id,
588
- name: prof.name,
589
- provider: primaryProvider,
590
- familySlug: prof.familySlug,
591
- instructions: prof.instructions,
592
- temperature: prof.temperature,
593
- maxTokens: prof.maxTokens,
594
- ...(providerConfigs.length > 0 && { providerConfigs }),
595
- };
61
+ return (0, project_export_1.exportProjectWithHelper)(projectId, opts, {
62
+ storage: this.storage,
63
+ settings: this.settings,
64
+ slugify: template_file_helpers_1.slugify,
596
65
  });
597
- const agents = agentsRes.items.map((a) => {
598
- let providerConfigName = null;
599
- if (a.providerConfigId) {
600
- const configInfo = configIdToInfo.get(a.providerConfigId);
601
- if (configInfo) {
602
- providerConfigName = configInfo.name;
603
- }
604
- }
605
- return {
606
- id: a.id,
607
- name: a.name,
608
- profileId: a.profileId,
609
- description: a.description,
610
- ...(providerConfigName && { providerConfigName }),
611
- };
612
- });
613
- const statuses = statusesRes.items.map((s) => ({
614
- id: s.id,
615
- label: s.label,
616
- color: s.color,
617
- position: s.position,
618
- mcpHidden: s.mcpHidden,
619
- }));
620
- const projectSettings = {};
621
- if (initialPrompt?.title) {
622
- projectSettings.initialPromptTitle = initialPrompt.title;
623
- }
624
- const autoCleanStatusIds = settings.autoClean?.statusIds?.[projectId] ?? [];
625
- if (autoCleanStatusIds.length > 0) {
626
- const statusMap = new Map(statusesRes.items.map((s) => [s.id, s.label]));
627
- const autoCleanLabels = autoCleanStatusIds
628
- .map((id) => statusMap.get(id))
629
- .filter((label) => !!label);
630
- if (autoCleanLabels.length > 0) {
631
- projectSettings.autoCleanStatusLabels = autoCleanLabels;
632
- }
633
- }
634
- const epicAssignedTemplate = settings.events?.epicAssigned?.template;
635
- if (epicAssignedTemplate) {
636
- projectSettings.epicAssignedTemplate = epicAssignedTemplate;
637
- }
638
- const poolSettings = settings.messagePool?.projects?.[projectId];
639
- if (poolSettings && Object.keys(poolSettings).length > 0) {
640
- projectSettings.messagePoolSettings = poolSettings;
641
- }
642
- const watchers = await Promise.all(watchersRes.map(async (w) => {
643
- let scopeFilterName = null;
644
- if (w.scopeFilterId) {
645
- switch (w.scope) {
646
- case 'agent': {
647
- const agent = agentsRes.items.find((a) => a.id === w.scopeFilterId);
648
- scopeFilterName = agent?.name ?? null;
649
- break;
650
- }
651
- case 'profile': {
652
- const profile = profilesRes.items.find((p) => p.id === w.scopeFilterId);
653
- scopeFilterName = profile?.name ?? null;
654
- break;
655
- }
656
- case 'provider': {
657
- try {
658
- const provider = await this.storage.getProvider(w.scopeFilterId);
659
- scopeFilterName = provider?.name ?? null;
660
- }
661
- catch {
662
- scopeFilterName = null;
663
- }
664
- break;
665
- }
666
- }
667
- }
668
- return {
669
- id: w.id,
670
- name: w.name,
671
- description: w.description,
672
- enabled: w.enabled,
673
- scope: w.scope,
674
- scopeFilterName,
675
- pollIntervalMs: w.pollIntervalMs,
676
- viewportLines: w.viewportLines,
677
- idleAfterSeconds: w.idleAfterSeconds,
678
- condition: w.condition,
679
- cooldownMs: w.cooldownMs,
680
- cooldownMode: w.cooldownMode,
681
- eventName: w.eventName,
682
- };
683
- }));
684
- const subscribers = subscribersRes.map((s) => ({
685
- id: s.id,
686
- name: s.name,
687
- description: s.description,
688
- enabled: s.enabled,
689
- eventName: s.eventName,
690
- eventFilter: s.eventFilter,
691
- actionType: s.actionType,
692
- actionInputs: s.actionInputs,
693
- delayMs: s.delayMs,
694
- cooldownMs: s.cooldownMs,
695
- retryOnError: s.retryOnError,
696
- groupName: s.groupName,
697
- position: s.position,
698
- priority: s.priority,
699
- }));
700
- const providerSettings = [];
701
- for (const prov of providersMap.values()) {
702
- if (prov.autoCompactThreshold != null) {
703
- providerSettings.push({
704
- name: prov.name,
705
- autoCompactThreshold: prov.autoCompactThreshold,
706
- });
707
- }
708
- }
709
- const existingMetadata = this.settings.getProjectTemplateMetadata(projectId);
710
- const manifest = {
711
- slug: existingMetadata?.templateSlug || this.slugify(project.name),
712
- name: project.name,
713
- description: project.description || null,
714
- version: existingMetadata?.installedVersion || '1.0.0',
715
- ...manifestOverrides,
716
- publishedAt: new Date().toISOString(),
717
- };
718
- const exportPayload = {
719
- _manifest: manifest,
720
- version: 1,
721
- exportedAt: new Date().toISOString(),
722
- prompts,
723
- profiles,
724
- agents,
725
- statuses,
726
- initialPrompt: initialPrompt
727
- ? { promptId: initialPrompt.id, title: initialPrompt.title }
728
- : null,
729
- ...(Object.keys(projectSettings).length > 0 && { projectSettings }),
730
- ...(providerSettings.length > 0 && { providerSettings }),
731
- watchers,
732
- subscribers,
733
- ...(presetsOverride !== undefined
734
- ? { presets: presetsOverride }
735
- : this.settings.getProjectPresets(projectId).length > 0
736
- ? { presets: this.settings.getProjectPresets(projectId) }
737
- : {}),
738
- };
739
- return exportPayload;
740
66
  }
741
67
  async doesProjectMatchPreset(projectId, preset) {
742
- const agentsRes = await this.storage.listAgents(projectId, { limit: 1000, offset: 0 });
743
- const agentsByName = new Map(agentsRes.items.map((a) => [a.name.toLowerCase(), a]));
744
- const uniqueProviderConfigIds = new Set();
745
- for (const agent of agentsRes.items) {
746
- if (agent.providerConfigId) {
747
- uniqueProviderConfigIds.add(agent.providerConfigId);
748
- }
749
- }
750
- const allProviderConfigs = uniqueProviderConfigIds.size > 0
751
- ? await this.storage.listProfileProviderConfigsByIds(Array.from(uniqueProviderConfigIds))
752
- : [];
753
- const providerConfigNames = new Map(allProviderConfigs.map((c) => [c.id, c.name]));
754
- for (const agentConfig of preset.agentConfigs) {
755
- const agentNameLower = agentConfig.agentName.trim().toLowerCase();
756
- const providerConfigNameLower = agentConfig.providerConfigName.trim().toLowerCase();
757
- const agent = agentsByName.get(agentNameLower);
758
- if (!agent) {
759
- return false;
760
- }
761
- const currentProviderConfigName = providerConfigNames.get(agent.providerConfigId ?? '');
762
- if (currentProviderConfigName?.toLowerCase() !== providerConfigNameLower) {
763
- return false;
764
- }
765
- }
766
- return true;
68
+ return (0, project_presets_helpers_1.doesProjectMatchPresetWithHelper)(projectId, preset, { storage: this.storage });
767
69
  }
768
70
  async applyPreset(projectId, presetName, nameMaps) {
769
- logger.info({ projectId, presetName }, 'applyPreset');
770
- const warnings = [];
771
- const presets = this.settings.getProjectPresets(projectId);
772
- const selectedPreset = presets.find((p) => p.name === presetName);
773
- if (!selectedPreset) {
774
- throw new error_types_1.NotFoundError('Preset', presetName);
775
- }
776
- if (!selectedPreset.agentConfigs || !Array.isArray(selectedPreset.agentConfigs)) {
777
- throw new error_types_1.ValidationError(`Preset "${presetName}" has invalid or missing agentConfigs`, {
778
- presetName,
779
- });
780
- }
781
- const agentsRes = await this.storage.listAgents(projectId, { limit: 1000, offset: 0 });
782
- let agentNameToId;
783
- if (nameMaps?.agentNameToId) {
784
- agentNameToId = nameMaps.agentNameToId;
785
- }
786
- else {
787
- agentNameToId = new Map();
788
- for (const agent of agentsRes.items) {
789
- agentNameToId.set(agent.name.toLowerCase(), agent.id);
790
- }
791
- }
792
- let configLookupMap;
793
- if (nameMaps?.configLookupMap) {
794
- configLookupMap = nameMaps.configLookupMap;
795
- }
796
- else {
797
- configLookupMap = new Map();
798
- const profilesRes = await this.storage.listAgentProfiles({
799
- projectId,
800
- limit: 1000,
801
- offset: 0,
802
- });
803
- for (const profile of profilesRes.items) {
804
- const configs = await this.storage.listProfileProviderConfigsByProfile(profile.id);
805
- for (const config of configs) {
806
- const lookupKey = `${profile.id}:${config.name.trim().toLowerCase()}`;
807
- configLookupMap.set(lookupKey, config.id);
808
- }
809
- }
810
- }
811
- let applied = 0;
812
- const agentsById = new Map(agentsRes.items.map((a) => [a.id, a]));
813
- for (const agentConfig of selectedPreset.agentConfigs) {
814
- const agentId = agentNameToId.get(agentConfig.agentName.trim().toLowerCase());
815
- if (!agentId) {
816
- warnings.push(`Agent "${agentConfig.agentName}" not found in project`);
817
- continue;
818
- }
819
- const agent = agentsById.get(agentId);
820
- if (!agent)
821
- continue;
822
- const profileId = agent.profileId;
823
- const lookupKey = `${profileId}:${agentConfig.providerConfigName.trim().toLowerCase()}`;
824
- const providerConfigId = configLookupMap.get(lookupKey);
825
- if (!providerConfigId) {
826
- warnings.push(`Provider config "${agentConfig.providerConfigName}" not found for agent "${agentConfig.agentName}"`);
827
- continue;
828
- }
829
- await this.storage.updateAgent(agentId, { providerConfigId });
830
- applied++;
831
- logger.debug({ projectId, agentId, agentName: agentConfig.agentName, providerConfigId }, 'Applied preset: updated agent provider config');
832
- }
833
- const fullMatch = warnings.length === 0 && applied === selectedPreset.agentConfigs.length;
834
- if (fullMatch) {
835
- await this.settings.setProjectActivePreset(projectId, presetName);
836
- logger.info({ projectId, presetName }, 'Active preset set (full match)');
837
- }
838
- logger.info({ projectId, presetName, applied, warnings: warnings.length }, 'Preset applied');
839
- return { applied, warnings };
71
+ return (0, project_presets_helpers_1.applyPresetWithHelper)(projectId, presetName, { storage: this.storage, settings: this.settings }, nameMaps);
840
72
  }
841
73
  async importProject(input) {
842
- logger.info({ projectId: input.projectId, dryRun: input.dryRun }, 'importProject');
843
- const isDryRun = input.dryRun ?? false;
844
- const payload = shared_1.ExportSchema.parse(input.payload ?? {});
845
- const familyResult = await this.computeFamilyAlternatives(payload.profiles, payload.agents);
846
- const needsMapping = familyResult.alternatives.some((alt) => !alt.defaultProviderAvailable);
847
- const providerNames = new Set((payload.profiles ?? []).map((p) => p.provider.name.trim().toLowerCase()));
848
- const { available, missing: missingProviders } = await this.resolveProviders(providerNames);
849
- if (!isDryRun && available.size === 0 && (payload.profiles ?? []).length > 0) {
850
- throw new error_types_1.ValidationError('No providers are installed. At least one provider is required to create a project from a template.');
851
- }
852
- const selectedProfilesByFamily = this.selectProfilesForFamilies(payload.profiles, payload.agents, input.familyProviderMappings, available);
853
- const [existingPrompts, existingProfiles, existingAgents, existingStatuses, existingWatchers, existingSubscribers,] = await Promise.all([
854
- this.storage.listPrompts({ projectId: input.projectId, limit: 10000, offset: 0 }),
855
- this.storage.listAgentProfiles({ projectId: input.projectId, limit: 10000, offset: 0 }),
856
- this.storage.listAgents(input.projectId, { limit: 10000, offset: 0 }),
857
- this.storage.listStatuses(input.projectId, { limit: 10000, offset: 0 }),
858
- this.storage.listWatchers(input.projectId),
859
- this.storage.listSubscribers(input.projectId),
860
- ]);
861
- const templateStatusLabels = new Set(payload.statuses.map((s) => s.label.trim().toLowerCase()));
862
- const unmatchedStatuses = [];
863
- for (const s of existingStatuses.items) {
864
- const labelKey = s.label.trim().toLowerCase();
865
- if (!templateStatusLabels.has(labelKey)) {
866
- const epicCount = await this.storage.countEpicsByStatus(s.id);
867
- if (epicCount > 0) {
868
- unmatchedStatuses.push({
869
- id: s.id,
870
- label: s.label,
871
- color: s.color,
872
- epicCount,
873
- });
874
- }
875
- }
876
- }
877
- if (isDryRun) {
878
- const dryRunResponse = {
879
- dryRun: true,
880
- missingProviders,
881
- unmatchedStatuses,
882
- templateStatuses: payload.statuses.map((s) => ({
883
- label: s.label,
884
- color: s.color,
885
- })),
886
- counts: {
887
- toImport: {
888
- prompts: payload.prompts.length,
889
- profiles: selectedProfilesByFamily.profilesToCreate.length,
890
- agents: payload.agents.length,
891
- statuses: payload.statuses.length,
892
- watchers: payload.watchers.length,
893
- subscribers: payload.subscribers.length,
894
- },
895
- toDelete: {
896
- prompts: existingPrompts.total,
897
- profiles: existingProfiles.total,
898
- agents: existingAgents.total,
899
- statuses: existingStatuses.total,
900
- watchers: existingWatchers.length,
901
- subscribers: existingSubscribers.length,
902
- },
903
- },
904
- };
905
- if (needsMapping && !input.familyProviderMappings) {
906
- dryRunResponse.providerMappingRequired = {
907
- missingProviders: familyResult.missingProviders,
908
- familyAlternatives: familyResult.alternatives,
909
- canImport: familyResult.canImport,
910
- };
911
- }
912
- return dryRunResponse;
913
- }
914
- if (needsMapping && !input.familyProviderMappings) {
915
- return {
916
- success: false,
917
- providerMappingRequired: {
918
- missingProviders: familyResult.missingProviders,
919
- familyAlternatives: familyResult.alternatives,
920
- canImport: familyResult.canImport,
921
- },
922
- };
923
- }
924
- if (!familyResult.canImport) {
925
- throw new error_types_1.ValidationError('Cannot import: some profile families have no available providers', {
926
- hint: 'Install the required providers or use a different template',
927
- missingProviders: familyResult.missingProviders,
928
- familyAlternatives: familyResult.alternatives,
929
- });
930
- }
931
- const selectedProviderNames = new Set(selectedProfilesByFamily.profilesToCreate.map((p) => p.provider.name.trim().toLowerCase()));
932
- const unavailableSelectedProviders = Array.from(selectedProviderNames).filter((name) => !available.has(name));
933
- if (unavailableSelectedProviders.length > 0) {
934
- throw new error_types_1.ValidationError('Import aborted: missing providers', {
935
- missingProviders: unavailableSelectedProviders,
936
- hint: 'Install/configure providers by name before importing profiles.',
937
- });
938
- }
939
- const activeSessions = this.sessions.getActiveSessionsForProject(input.projectId);
940
- if (activeSessions.length > 0) {
941
- throw new error_types_1.ConflictError('Import aborted: active agent sessions detected', {
942
- activeSessions: activeSessions.map((s) => ({ id: s.id, agentId: s.agentId })),
943
- hint: 'Terminate all running sessions for this project before importing.',
944
- });
945
- }
946
- try {
947
- const oldAgentIdToName = new Map();
948
- for (const a of existingAgents.items) {
949
- oldAgentIdToName.set(a.id, a.name.trim().toLowerCase());
950
- }
951
- for (const a of existingAgents.items) {
952
- await this.storage.deleteAgent(a.id);
953
- }
954
- for (const p of existingProfiles.items) {
955
- await this.storage.deleteAgentProfile(p.id);
956
- }
957
- for (const pr of existingPrompts.items) {
958
- await this.storage.deletePrompt(pr.id);
959
- }
960
- for (const w of existingWatchers) {
961
- await this.watchersService.deleteWatcher(w.id);
962
- }
963
- for (const s of existingSubscribers) {
964
- await this.storage.deleteSubscriber(s.id);
965
- }
966
- await this.settings.updateSettings({
967
- projectId: input.projectId,
968
- initialSessionPromptId: null,
969
- });
970
- const statusIdMap = {};
971
- const promptIdMap = {};
972
- const profileIdMap = {};
973
- const agentIdMap = {};
974
- const existingStatusByLabel = new Map();
975
- for (const s of existingStatuses.items) {
976
- existingStatusByLabel.set(s.label.trim().toLowerCase(), s);
977
- }
978
- const TEMP_POSITION_OFFSET = 100000;
979
- for (const s of existingStatuses.items) {
980
- await this.storage.updateStatus(s.id, { position: s.position + TEMP_POSITION_OFFSET });
981
- }
982
- for (const s of payload.statuses.sort((a, b) => a.position - b.position)) {
983
- const labelKey = s.label.trim().toLowerCase();
984
- const existing = existingStatusByLabel.get(labelKey);
985
- if (existing) {
986
- const updated = await this.storage.updateStatus(existing.id, {
987
- color: s.color,
988
- position: s.position,
989
- mcpHidden: s.mcpHidden,
990
- });
991
- if (s.id)
992
- statusIdMap[s.id] = updated.id;
993
- existingStatusByLabel.delete(labelKey);
994
- }
995
- else {
996
- const created = await this.storage.createStatus({
997
- projectId: input.projectId,
998
- label: s.label,
999
- color: s.color,
1000
- position: s.position,
1001
- mcpHidden: s.mcpHidden,
1002
- });
1003
- if (s.id)
1004
- statusIdMap[s.id] = created.id;
1005
- }
1006
- }
1007
- const templateLabelToStatusId = new Map();
1008
- const allStatuses = await this.storage.listStatuses(input.projectId, {
1009
- limit: 10000,
1010
- offset: 0,
1011
- });
1012
- for (const s of allStatuses.items) {
1013
- templateLabelToStatusId.set(s.label.trim().toLowerCase(), s.id);
1014
- }
1015
- if (input.statusMappings && Object.keys(input.statusMappings).length > 0) {
1016
- let epicsMapped = 0;
1017
- let statusesDeleted = 0;
1018
- for (const [oldStatusId, targetLabel] of Object.entries(input.statusMappings)) {
1019
- const targetStatusId = templateLabelToStatusId.get(targetLabel.trim().toLowerCase());
1020
- if (targetStatusId) {
1021
- const remapped = await this.storage.updateEpicsStatus(oldStatusId, targetStatusId);
1022
- epicsMapped += remapped;
1023
- await this.storage.deleteStatus(oldStatusId);
1024
- statusesDeleted++;
1025
- }
1026
- }
1027
- logger.info({ epicsMapped, statusesDeleted }, 'Applied status mappings: epics remapped and old statuses deleted');
1028
- }
1029
- const createdPrompts = [];
1030
- for (const p of payload.prompts) {
1031
- const created = await this.storage.createPrompt({
1032
- projectId: input.projectId,
1033
- title: p.title,
1034
- content: p.content,
1035
- tags: p.tags ?? [],
1036
- });
1037
- if (p.id)
1038
- promptIdMap[p.id] = created.id;
1039
- createdPrompts.push({ id: created.id, title: created.title });
1040
- }
1041
- for (const prof of selectedProfilesByFamily.profilesToCreate) {
1042
- const created = await this.storage.createAgentProfile({
1043
- projectId: input.projectId,
1044
- name: prof.name,
1045
- familySlug: prof.familySlug ?? null,
1046
- systemPrompt: null,
1047
- instructions: prof.instructions ?? null,
1048
- temperature: prof.temperature ?? null,
1049
- maxTokens: prof.maxTokens ?? null,
1050
- });
1051
- const profKey = prof.id || `name:${prof.name.trim().toLowerCase()}`;
1052
- profileIdMap[profKey] = created.id;
1053
- }
1054
- const configLookupMap = new Map();
1055
- const configIdMap = {};
1056
- for (const prof of selectedProfilesByFamily.profilesToCreate) {
1057
- const profKey = prof.id || `name:${prof.name.trim().toLowerCase()}`;
1058
- const newProfileId = profileIdMap[profKey];
1059
- if (!newProfileId)
1060
- continue;
1061
- const providerConfigs = prof.providerConfigs;
1062
- if (providerConfigs && providerConfigs.length > 0) {
1063
- for (const config of providerConfigs) {
1064
- const configProviderId = available.get(config.providerName.trim().toLowerCase());
1065
- if (!configProviderId) {
1066
- logger.warn({ profileName: prof.name, providerName: config.providerName }, 'Provider not found for config, skipping');
1067
- continue;
1068
- }
1069
- const createdConfig = await this.storage.createProfileProviderConfig({
1070
- profileId: newProfileId,
1071
- providerId: configProviderId,
1072
- name: config.name,
1073
- options: config.options ?? null,
1074
- env: config.env ?? null,
1075
- });
1076
- const lookupKey = `${newProfileId}:${config.name.trim().toLowerCase()}`;
1077
- configLookupMap.set(lookupKey, createdConfig.id);
1078
- configIdMap[`${profKey}:${config.name}`] = createdConfig.id;
1079
- }
1080
- }
1081
- else {
1082
- const providerName = prof.provider.name.trim().toLowerCase();
1083
- const configProviderId = available.get(providerName);
1084
- if (configProviderId) {
1085
- const options = prof.options != null
1086
- ? typeof prof.options === 'string'
1087
- ? prof.options
1088
- : JSON.stringify(prof.options)
1089
- : null;
1090
- const createdConfig = await this.storage.createProfileProviderConfig({
1091
- profileId: newProfileId,
1092
- providerId: configProviderId,
1093
- name: 'default',
1094
- options,
1095
- env: null,
1096
- });
1097
- const lookupKey = `${newProfileId}:default`;
1098
- configLookupMap.set(lookupKey, createdConfig.id);
1099
- configIdMap[`${profKey}:default`] = createdConfig.id;
1100
- }
1101
- }
1102
- }
1103
- for (const a of payload.agents) {
1104
- const remappedProfileId = selectedProfilesByFamily.agentProfileMap.get(a.id ?? '');
1105
- const oldProfileId = remappedProfileId ?? a.profileId ?? '';
1106
- const newProfileId = oldProfileId && profileIdMap[oldProfileId] ? profileIdMap[oldProfileId] : undefined;
1107
- if (!newProfileId) {
1108
- throw new error_types_1.ValidationError(`Profile mapping missing for agent ${a.name}`, {
1109
- profileId: oldProfileId || null,
1110
- });
1111
- }
1112
- let providerConfigId;
1113
- const agentWithConfig = a;
1114
- if (agentWithConfig.providerConfigName && newProfileId) {
1115
- const lookupKey = `${newProfileId}:${agentWithConfig.providerConfigName.trim().toLowerCase()}`;
1116
- providerConfigId = configLookupMap.get(lookupKey);
1117
- }
1118
- if (!providerConfigId && newProfileId) {
1119
- const profilePrefix = `${newProfileId}:`;
1120
- for (const [key, configId] of configLookupMap.entries()) {
1121
- if (key.startsWith(profilePrefix)) {
1122
- providerConfigId = configId;
1123
- break;
1124
- }
1125
- }
1126
- }
1127
- if (!providerConfigId) {
1128
- throw new error_types_1.ValidationError(`No provider config available for agent ${a.name}`, {
1129
- profileId: newProfileId,
1130
- providerConfigName: agentWithConfig.providerConfigName || null,
1131
- });
1132
- }
1133
- const created = await this.storage.createAgent({
1134
- projectId: input.projectId,
1135
- name: a.name,
1136
- profileId: newProfileId,
1137
- description: a.description ?? null,
1138
- providerConfigId,
1139
- });
1140
- const agentKey = a.id || `name:${a.name.trim().toLowerCase()}`;
1141
- agentIdMap[agentKey] = created.id;
1142
- }
1143
- const augmentedPayload = {
1144
- agents: payload.agents.map((a) => ({
1145
- ...a,
1146
- id: a.id || `name:${a.name.trim().toLowerCase()}`,
1147
- })),
1148
- profiles: selectedProfilesByFamily.profilesToCreate.map((p) => ({
1149
- ...p,
1150
- id: p.id || `name:${p.name.trim().toLowerCase()}`,
1151
- })),
1152
- };
1153
- const { agentNameToId: agentNameToNewId, profileNameToId: profileNameToNewId } = this.buildNameToIdMaps(augmentedPayload, { agentIdMap, profileIdMap });
1154
- const { watcherIdMap } = await this.createWatchersFromPayload(input.projectId, payload.watchers, {
1155
- agentNameToId: agentNameToNewId,
1156
- profileNameToId: profileNameToNewId,
1157
- providerNameToId: available,
1158
- profileNameRemapMap: selectedProfilesByFamily.profileNameRemapMap,
1159
- });
1160
- const { subscriberIdMap } = await this.createSubscribersFromPayload(input.projectId, payload.subscribers);
1161
- logger.info({
1162
- watchersCreated: payload.watchers.length,
1163
- subscribersCreated: payload.subscribers.length,
1164
- }, 'Watchers and subscribers imported');
1165
- const existingEpics = await this.storage.listEpics(input.projectId, {
1166
- limit: 100000,
1167
- offset: 0,
1168
- });
1169
- let epicsRemapped = 0;
1170
- let epicsCleared = 0;
1171
- for (const epic of existingEpics.items) {
1172
- if (epic.agentId) {
1173
- const oldAgentName = oldAgentIdToName.get(epic.agentId);
1174
- if (oldAgentName) {
1175
- const newAgentId = agentNameToNewId.get(oldAgentName);
1176
- if (newAgentId) {
1177
- await this.storage.updateEpic(epic.id, { agentId: newAgentId }, epic.version);
1178
- epicsRemapped++;
1179
- }
1180
- else {
1181
- await this.storage.updateEpic(epic.id, { agentId: null }, epic.version);
1182
- epicsCleared++;
1183
- }
1184
- }
1185
- else {
1186
- await this.storage.updateEpic(epic.id, { agentId: null }, epic.version);
1187
- epicsCleared++;
1188
- }
1189
- }
1190
- }
1191
- logger.info({ epicsRemapped, epicsCleared }, 'Epic agent references updated after import');
1192
- let mergedInitialPromptTitle;
1193
- if (payload.initialPrompt?.title) {
1194
- mergedInitialPromptTitle = payload.initialPrompt.title;
1195
- }
1196
- else if (payload.initialPrompt?.promptId) {
1197
- const matchingPrompt = payload.prompts.find((p) => p.id === payload.initialPrompt.promptId);
1198
- if (matchingPrompt) {
1199
- mergedInitialPromptTitle = matchingPrompt.title;
1200
- }
1201
- }
1202
- else if (payload.projectSettings?.initialPromptTitle) {
1203
- mergedInitialPromptTitle = payload.projectSettings.initialPromptTitle;
1204
- }
1205
- const mergedSettings = payload.projectSettings
1206
- ? { ...payload.projectSettings, initialPromptTitle: mergedInitialPromptTitle }
1207
- : mergedInitialPromptTitle
1208
- ? { initialPromptTitle: mergedInitialPromptTitle }
1209
- : undefined;
1210
- const promptTitleToId = new Map();
1211
- for (const cp of createdPrompts) {
1212
- promptTitleToId.set(cp.title.toLowerCase(), cp.id);
1213
- }
1214
- const archiveStatusId = templateLabelToStatusId.get('archive') ?? null;
1215
- const settingsResult = await this.applyProjectSettings(input.projectId, mergedSettings, { promptTitleToId, statusLabelToId: templateLabelToStatusId }, archiveStatusId);
1216
- const initialPromptSet = settingsResult.initialPromptSet;
1217
- if (payload._manifest?.slug) {
1218
- let templateSource = 'registry';
1219
- try {
1220
- this.unifiedTemplateService.getBundledTemplate(payload._manifest.slug);
1221
- templateSource = 'bundled';
1222
- }
1223
- catch {
1224
- templateSource = 'registry';
1225
- }
1226
- await this.settings.setProjectTemplateMetadata(input.projectId, {
1227
- templateSlug: payload._manifest.slug,
1228
- source: templateSource,
1229
- installedVersion: payload._manifest.version ?? null,
1230
- registryUrl: null,
1231
- installedAt: new Date().toISOString(),
1232
- });
1233
- logger.info({ projectId: input.projectId, slug: payload._manifest.slug, source: templateSource }, 'Updated template metadata after import');
1234
- }
1235
- const rawPresets = payload.presets;
1236
- const templatePresets = Array.isArray(rawPresets) ? rawPresets : [];
1237
- if (templatePresets.length > 0) {
1238
- await this.settings.setProjectPresets(input.projectId, templatePresets);
1239
- logger.info({ projectId: input.projectId, presetCount: templatePresets.length }, 'Presets replaced from template during import');
1240
- }
1241
- else {
1242
- await this.settings.clearProjectPresets(input.projectId);
1243
- logger.info({ projectId: input.projectId }, 'Presets cleared during import (template has none)');
1244
- }
1245
- const importedProviderSettings = payload.providerSettings;
1246
- if (importedProviderSettings && importedProviderSettings.length > 0) {
1247
- const allProviders = await this.storage.listProviders();
1248
- const providersByName = new Map(allProviders.items.map((p) => [p.name.trim().toLowerCase(), p]));
1249
- for (const setting of importedProviderSettings) {
1250
- const localProvider = providersByName.get(setting.name.trim().toLowerCase());
1251
- if (!localProvider) {
1252
- continue;
1253
- }
1254
- if (localProvider.autoCompactThreshold != null) {
1255
- logger.debug({ providerName: setting.name, existing: localProvider.autoCompactThreshold }, 'Skipping providerSettings import: local threshold already set');
1256
- continue;
1257
- }
1258
- if (setting.autoCompactThreshold != null) {
1259
- await this.storage.updateProvider(localProvider.id, {
1260
- autoCompactThreshold: setting.autoCompactThreshold,
1261
- });
1262
- logger.info({ providerName: setting.name, threshold: setting.autoCompactThreshold }, 'Applied autoCompactThreshold from template import');
1263
- }
1264
- }
1265
- }
1266
- return {
1267
- success: true,
1268
- mode: 'replace',
1269
- replaced: true,
1270
- missingProviders: [],
1271
- counts: {
1272
- imported: {
1273
- prompts: payload.prompts.length,
1274
- profiles: payload.profiles.length,
1275
- agents: payload.agents.length,
1276
- statuses: payload.statuses.length,
1277
- watchers: payload.watchers.length,
1278
- subscribers: payload.subscribers.length,
1279
- },
1280
- deleted: {
1281
- prompts: existingPrompts.total,
1282
- profiles: existingProfiles.total,
1283
- agents: existingAgents.total,
1284
- statuses: 0,
1285
- watchers: existingWatchers.length,
1286
- subscribers: existingSubscribers.length,
1287
- },
1288
- epics: {
1289
- preserved: existingEpics.total,
1290
- agentRemapped: epicsRemapped,
1291
- agentCleared: epicsCleared,
1292
- },
1293
- },
1294
- mappings: {
1295
- promptIdMap,
1296
- profileIdMap,
1297
- agentIdMap,
1298
- statusIdMap,
1299
- watcherIdMap,
1300
- subscriberIdMap,
1301
- },
1302
- initialPromptSet,
1303
- message: 'Project configuration replaced. Epics preserved.',
1304
- };
1305
- }
1306
- catch (error) {
1307
- logger.error({ error, projectId: input.projectId }, 'Import failed');
1308
- const message = this.getImportErrorMessage(error);
1309
- throw new error_types_1.StorageError(message);
1310
- }
1311
- }
1312
- async resolveProviders(providerNames) {
1313
- const providers = await this.storage.listProviders();
1314
- const available = new Map();
1315
- for (const prov of providers.items) {
1316
- available.set(prov.name.trim().toLowerCase(), prov.id);
1317
- }
1318
- const missing = Array.from(providerNames).filter((n) => !available.has(n));
1319
- return { available, missing };
74
+ return (0, project_import_1.importProjectWithHelper)(input, {
75
+ storage: this.storage,
76
+ settings: this.settings,
77
+ watchersService: this.watchersService,
78
+ sessions: this.sessions,
79
+ unifiedTemplateService: this.unifiedTemplateService,
80
+ computeFamilyAlternatives: (templateProfiles, templateAgents) => this.computeFamilyAlternatives(templateProfiles, templateAgents),
81
+ createWatchersFromPayload: (projectId, watchers, maps) => (0, project_runtime_helpers_1.createWatchersFromPayloadWithHelper)(projectId, watchers, maps, this.watchersService),
82
+ createSubscribersFromPayload: (projectId, subscribers) => (0, project_runtime_helpers_1.createSubscribersFromPayloadWithHelper)(projectId, subscribers, this.storage),
83
+ applyProjectSettings: (projectId, projectSettings, maps, archiveStatusId) => (0, project_runtime_helpers_1.applyProjectSettingsWithHelper)(projectId, projectSettings, maps, archiveStatusId, this.settings),
84
+ getImportErrorMessage: project_runtime_helpers_1.getImportErrorMessage,
85
+ });
1320
86
  }
1321
87
  async computeFamilyAlternatives(templateProfiles, templateAgents) {
1322
- const localProviders = await this.storage.listProviders();
1323
- const availableProviderNames = new Set(localProviders.items.map((p) => p.name.trim().toLowerCase()));
1324
- const profileById = new Map();
1325
- for (const prof of templateProfiles) {
1326
- if (prof.id) {
1327
- profileById.set(prof.id, prof);
1328
- }
1329
- }
1330
- const usedFamilySlugs = new Set();
1331
- for (const agent of templateAgents) {
1332
- if (agent.profileId) {
1333
- const profile = profileById.get(agent.profileId);
1334
- if (profile?.familySlug) {
1335
- usedFamilySlugs.add(profile.familySlug);
1336
- }
1337
- }
1338
- }
1339
- const familyProviders = new Map();
1340
- for (const prof of templateProfiles) {
1341
- const familySlug = prof.familySlug;
1342
- if (!familySlug)
1343
- continue;
1344
- if (!familyProviders.has(familySlug)) {
1345
- familyProviders.set(familySlug, new Map());
1346
- }
1347
- const providerName = prof.provider.name.trim().toLowerCase();
1348
- const familyMap = familyProviders.get(familySlug);
1349
- if (!familyMap.has(providerName)) {
1350
- familyMap.set(providerName, []);
1351
- }
1352
- familyMap.get(providerName).push(prof.name);
1353
- if (prof.providerConfigs) {
1354
- for (const config of prof.providerConfigs) {
1355
- const configProviderName = config.providerName.trim().toLowerCase();
1356
- if (!familyMap.has(configProviderName)) {
1357
- familyMap.set(configProviderName, []);
1358
- }
1359
- if (!familyMap.get(configProviderName).includes(prof.name)) {
1360
- familyMap.get(configProviderName).push(prof.name);
1361
- }
1362
- }
1363
- }
1364
- }
1365
- const alternatives = [];
1366
- const allMissingProviders = new Set();
1367
- let canImport = true;
1368
- for (const familySlug of usedFamilySlugs) {
1369
- const providersForFamily = familyProviders.get(familySlug);
1370
- if (!providersForFamily || providersForFamily.size === 0) {
1371
- logger.warn({ familySlug }, 'Family used by agent has no profiles');
1372
- continue;
1373
- }
1374
- const providerNamesForFamily = Array.from(providersForFamily.keys());
1375
- const defaultProvider = providerNamesForFamily[0];
1376
- const defaultProviderAvailable = availableProviderNames.has(defaultProvider);
1377
- const availableForFamily = providerNamesForFamily.filter((name) => availableProviderNames.has(name));
1378
- for (const provName of providerNamesForFamily) {
1379
- if (!availableProviderNames.has(provName)) {
1380
- allMissingProviders.add(provName);
1381
- }
1382
- }
1383
- const hasAlternatives = availableForFamily.length > 0;
1384
- if (!hasAlternatives) {
1385
- canImport = false;
1386
- }
1387
- alternatives.push({
1388
- familySlug,
1389
- defaultProvider,
1390
- defaultProviderAvailable,
1391
- availableProviders: availableForFamily.sort(),
1392
- hasAlternatives,
1393
- });
1394
- }
1395
- return {
1396
- alternatives,
1397
- missingProviders: Array.from(allMissingProviders).sort(),
1398
- canImport,
1399
- };
1400
- }
1401
- selectProfilesForFamilies(templateProfiles, templateAgents, familyProviderMappings, availableProviders, options) {
1402
- const profileById = new Map();
1403
- for (const prof of templateProfiles) {
1404
- if (prof.id) {
1405
- profileById.set(prof.id, prof);
1406
- }
1407
- }
1408
- const templateAgentResolvedProviders = new Map();
1409
- for (const agent of templateAgents) {
1410
- if (!agent.providerConfigName || !agent.profileId)
1411
- continue;
1412
- const profile = profileById.get(agent.profileId);
1413
- if (!profile?.providerConfigs)
1414
- continue;
1415
- const configName = agent.providerConfigName.trim().toLowerCase();
1416
- const config = profile.providerConfigs.find((pc) => pc.name.trim().toLowerCase() === configName);
1417
- if (config) {
1418
- templateAgentResolvedProviders.set(agent.name.trim().toLowerCase(), config.providerName.trim().toLowerCase());
1419
- }
1420
- }
1421
- const profilesByFamilyAndProvider = new Map();
1422
- for (const prof of templateProfiles) {
1423
- if (!prof.familySlug)
1424
- continue;
1425
- const family = prof.familySlug;
1426
- const providerName = prof.provider.name.trim().toLowerCase();
1427
- if (!profilesByFamilyAndProvider.has(family)) {
1428
- profilesByFamilyAndProvider.set(family, new Map());
1429
- }
1430
- const familyMap = profilesByFamilyAndProvider.get(family);
1431
- if (!familyMap.has(providerName)) {
1432
- familyMap.set(providerName, prof);
1433
- }
1434
- }
1435
- const familyOriginalProviders = new Map();
1436
- for (const agent of templateAgents) {
1437
- if (!agent.profileId)
1438
- continue;
1439
- const profile = profileById.get(agent.profileId);
1440
- if (!profile?.familySlug)
1441
- continue;
1442
- const providerName = profile.provider.name.trim().toLowerCase();
1443
- familyOriginalProviders.set(profile.familySlug, providerName);
1444
- }
1445
- const selectedProfileIdsByFamily = new Map();
1446
- for (const [familySlug, providerMap] of profilesByFamilyAndProvider) {
1447
- let selectedProvider;
1448
- if (familyProviderMappings?.[familySlug]) {
1449
- selectedProvider = familyProviderMappings[familySlug].trim().toLowerCase();
1450
- }
1451
- else {
1452
- const originalProvider = familyOriginalProviders.get(familySlug);
1453
- if (originalProvider &&
1454
- availableProviders.has(originalProvider) &&
1455
- providerMap.has(originalProvider)) {
1456
- selectedProvider = originalProvider;
1457
- }
1458
- else {
1459
- for (const provName of providerMap.keys()) {
1460
- if (availableProviders.has(provName)) {
1461
- selectedProvider = provName;
1462
- break;
1463
- }
1464
- }
1465
- }
1466
- }
1467
- if (selectedProvider && providerMap.has(selectedProvider)) {
1468
- const profile = providerMap.get(selectedProvider);
1469
- if (profile.id) {
1470
- selectedProfileIdsByFamily.set(familySlug, profile.id);
1471
- }
1472
- }
1473
- }
1474
- const coveredAgents = options?.presetCoveredAgentNames ?? new Set();
1475
- const profilesToCreate = [];
1476
- const usedProfileIds = new Set();
1477
- const providerSubstitutions = new Map();
1478
- const fallbackProviderName = availableProviders.size > 0 ? [...availableProviders.keys()][0] : undefined;
1479
- const profileAgentNames = new Map();
1480
- for (const agent of templateAgents) {
1481
- if (!agent.profileId)
1482
- continue;
1483
- const names = profileAgentNames.get(agent.profileId) ?? [];
1484
- names.push(agent.name);
1485
- profileAgentNames.set(agent.profileId, names);
1486
- }
1487
- for (const prof of templateProfiles) {
1488
- if (!prof.id || usedProfileIds.has(prof.id))
1489
- continue;
1490
- const providerName = prof.provider.name.trim().toLowerCase();
1491
- if (availableProviders.has(providerName)) {
1492
- usedProfileIds.add(prof.id);
1493
- profilesToCreate.push(prof);
1494
- }
1495
- else if (fallbackProviderName) {
1496
- if (prof.familySlug) {
1497
- const selectedId = selectedProfileIdsByFamily.get(prof.familySlug);
1498
- if (selectedId && selectedId !== prof.id)
1499
- continue;
1500
- }
1501
- usedProfileIds.add(prof.id);
1502
- const allAgents = profileAgentNames.get(prof.id) ?? [];
1503
- const uncoveredAgents = allAgents.filter((name) => {
1504
- const agentKey = name.trim().toLowerCase();
1505
- if (coveredAgents.has(agentKey))
1506
- return false;
1507
- const templateResolved = templateAgentResolvedProviders.get(agentKey);
1508
- if (templateResolved && availableProviders.has(templateResolved))
1509
- return false;
1510
- return true;
1511
- });
1512
- if (uncoveredAgents.length > 0) {
1513
- const presetResolved = options?.presetAgentResolvedProviders;
1514
- const agentsByMissingProvider = new Map();
1515
- for (const agentName of uncoveredAgents) {
1516
- const agentKey = agentName.trim().toLowerCase();
1517
- const missing = presetResolved?.get(agentKey) ??
1518
- templateAgentResolvedProviders.get(agentKey) ??
1519
- providerName;
1520
- const list = agentsByMissingProvider.get(missing) ?? [];
1521
- list.push(agentName);
1522
- agentsByMissingProvider.set(missing, list);
1523
- }
1524
- for (const [missingProvider, agents] of agentsByMissingProvider) {
1525
- const key = agentsByMissingProvider.size > 1 ? `${prof.name}:${missingProvider}` : prof.name;
1526
- providerSubstitutions.set(key, {
1527
- originalProvider: missingProvider,
1528
- substituteProvider: fallbackProviderName,
1529
- agentNames: agents,
1530
- });
1531
- }
1532
- }
1533
- profilesToCreate.push({
1534
- ...prof,
1535
- provider: { name: fallbackProviderName },
1536
- options: null,
1537
- });
1538
- }
1539
- }
1540
- const agentProfileMap = new Map();
1541
- for (const agent of templateAgents) {
1542
- if (!agent.id || !agent.profileId)
1543
- continue;
1544
- const originalProfile = profileById.get(agent.profileId);
1545
- if (!originalProfile) {
1546
- agentProfileMap.set(agent.id, agent.profileId);
1547
- continue;
1548
- }
1549
- if (originalProfile.familySlug) {
1550
- const selectedProfileId = selectedProfileIdsByFamily.get(originalProfile.familySlug);
1551
- agentProfileMap.set(agent.id, selectedProfileId ?? agent.profileId);
1552
- }
1553
- else {
1554
- agentProfileMap.set(agent.id, agent.profileId);
1555
- }
1556
- }
1557
- const profileNameRemapMap = new Map();
1558
- for (const [familySlug, providerMap] of profilesByFamilyAndProvider) {
1559
- const selectedProfileId = selectedProfileIdsByFamily.get(familySlug);
1560
- const selectedProfile = selectedProfileId
1561
- ? templateProfiles.find((p) => p.id === selectedProfileId)
1562
- : undefined;
1563
- if (selectedProfile) {
1564
- const selectedNameLower = selectedProfile.name.trim().toLowerCase();
1565
- for (const profile of providerMap.values()) {
1566
- const profileNameLower = profile.name.trim().toLowerCase();
1567
- if (profileNameLower !== selectedNameLower) {
1568
- profileNameRemapMap.set(profileNameLower, selectedNameLower);
1569
- }
1570
- }
1571
- }
1572
- }
1573
- return { profilesToCreate, agentProfileMap, profileNameRemapMap, providerSubstitutions };
1574
- }
1575
- buildNameToIdMaps(payload, mappings) {
1576
- const agentNameToId = new Map();
1577
- for (const a of payload.agents) {
1578
- if (a.id && mappings.agentIdMap[a.id]) {
1579
- const nameLower = a.name.trim().toLowerCase();
1580
- if (agentNameToId.has(nameLower)) {
1581
- logger.warn({
1582
- name: a.name,
1583
- existingId: agentNameToId.get(nameLower),
1584
- newId: mappings.agentIdMap[a.id],
1585
- }, 'Duplicate agent name detected, using last occurrence');
1586
- }
1587
- agentNameToId.set(nameLower, mappings.agentIdMap[a.id]);
1588
- }
1589
- }
1590
- const profileNameToId = new Map();
1591
- for (const prof of payload.profiles) {
1592
- if (prof.id && mappings.profileIdMap[prof.id]) {
1593
- const nameLower = prof.name.trim().toLowerCase();
1594
- if (profileNameToId.has(nameLower)) {
1595
- logger.warn({
1596
- name: prof.name,
1597
- existingId: profileNameToId.get(nameLower),
1598
- newId: mappings.profileIdMap[prof.id],
1599
- }, 'Duplicate profile name detected, using last occurrence');
1600
- }
1601
- profileNameToId.set(nameLower, mappings.profileIdMap[prof.id]);
1602
- }
1603
- }
1604
- return { agentNameToId, profileNameToId };
1605
- }
1606
- async createWatchersFromPayload(projectId, watchers, maps) {
1607
- const watcherIdMap = {};
1608
- let created = 0;
1609
- for (const w of watchers) {
1610
- let scopeFilterId = null;
1611
- if (w.scopeFilterName && w.scope !== 'all') {
1612
- const scopeFilterNameLower = w.scopeFilterName.trim().toLowerCase();
1613
- switch (w.scope) {
1614
- case 'agent': {
1615
- scopeFilterId = maps.agentNameToId.get(scopeFilterNameLower) ?? null;
1616
- break;
1617
- }
1618
- case 'profile': {
1619
- scopeFilterId = maps.profileNameToId.get(scopeFilterNameLower) ?? null;
1620
- if (!scopeFilterId && maps.profileNameRemapMap) {
1621
- const remappedName = maps.profileNameRemapMap.get(scopeFilterNameLower);
1622
- if (remappedName) {
1623
- scopeFilterId = maps.profileNameToId.get(remappedName) ?? null;
1624
- if (scopeFilterId) {
1625
- logger.info({
1626
- projectId,
1627
- watcherName: w.name,
1628
- originalProfile: w.scopeFilterName,
1629
- remappedProfile: remappedName,
1630
- }, 'Watcher profile scope remapped due to provider family selection');
1631
- }
1632
- }
1633
- }
1634
- break;
1635
- }
1636
- case 'provider': {
1637
- scopeFilterId = maps.providerNameToId.get(scopeFilterNameLower) ?? null;
1638
- break;
1639
- }
1640
- }
1641
- if (!scopeFilterId) {
1642
- logger.warn({ projectId, watcherName: w.name, scope: w.scope, scopeFilterName: w.scopeFilterName }, 'Could not resolve scope filter, setting scope to "all"');
1643
- }
1644
- }
1645
- const createdWatcher = await this.watchersService.createWatcher({
1646
- projectId,
1647
- name: w.name,
1648
- description: w.description ?? null,
1649
- enabled: w.enabled,
1650
- scope: scopeFilterId ? w.scope : 'all',
1651
- scopeFilterId,
1652
- pollIntervalMs: w.pollIntervalMs,
1653
- viewportLines: w.viewportLines,
1654
- idleAfterSeconds: w.idleAfterSeconds ?? 0,
1655
- condition: w.condition,
1656
- cooldownMs: w.cooldownMs,
1657
- cooldownMode: w.cooldownMode,
1658
- eventName: w.eventName,
1659
- });
1660
- if (w.id)
1661
- watcherIdMap[w.id] = createdWatcher.id;
1662
- created++;
1663
- }
1664
- return { created, watcherIdMap };
1665
- }
1666
- async createSubscribersFromPayload(projectId, subscribers) {
1667
- const subscriberIdMap = {};
1668
- let created = 0;
1669
- for (const s of subscribers) {
1670
- const createdSubscriber = await this.storage.createSubscriber({
1671
- projectId,
1672
- name: s.name,
1673
- description: s.description ?? null,
1674
- enabled: s.enabled,
1675
- eventName: s.eventName,
1676
- eventFilter: s.eventFilter ?? null,
1677
- actionType: s.actionType,
1678
- actionInputs: s.actionInputs,
1679
- delayMs: s.delayMs,
1680
- cooldownMs: s.cooldownMs,
1681
- retryOnError: s.retryOnError,
1682
- groupName: s.groupName ?? null,
1683
- position: s.position ?? 0,
1684
- priority: s.priority ?? 0,
1685
- });
1686
- if (s.id)
1687
- subscriberIdMap[s.id] = createdSubscriber.id;
1688
- created++;
1689
- }
1690
- return { created, subscriberIdMap };
1691
- }
1692
- async applyProjectSettings(projectId, projectSettings, maps, archiveStatusId) {
1693
- let initialPromptSet = false;
1694
- if (projectSettings) {
1695
- const ps = projectSettings;
1696
- if (ps.initialPromptTitle) {
1697
- const promptId = maps.promptTitleToId.get(ps.initialPromptTitle.toLowerCase());
1698
- if (promptId) {
1699
- await this.settings.updateSettings({
1700
- projectId,
1701
- initialSessionPromptId: promptId,
1702
- });
1703
- initialPromptSet = true;
1704
- logger.info({ projectId, promptTitle: ps.initialPromptTitle }, 'Applied initial prompt from projectSettings');
1705
- }
1706
- }
1707
- if (ps.autoCleanStatusLabels && ps.autoCleanStatusLabels.length > 0) {
1708
- const autoCleanStatusIds = ps.autoCleanStatusLabels
1709
- .map((label) => maps.statusLabelToId.get(label.toLowerCase()))
1710
- .filter((id) => !!id);
1711
- if (autoCleanStatusIds.length > 0) {
1712
- const currentSettings = this.settings.getSettings();
1713
- const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
1714
- await this.settings.updateSettings({
1715
- autoClean: {
1716
- statusIds: { ...existingAutoClean, [projectId]: autoCleanStatusIds },
1717
- },
1718
- });
1719
- logger.info({ projectId, autoCleanStatusIds }, 'Applied autoClean statuses from projectSettings');
1720
- }
1721
- }
1722
- else if (archiveStatusId) {
1723
- const currentSettings = this.settings.getSettings();
1724
- const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
1725
- await this.settings.updateSettings({
1726
- autoClean: {
1727
- statusIds: { ...existingAutoClean, [projectId]: [archiveStatusId] },
1728
- },
1729
- });
1730
- logger.info({ projectId, archiveStatusId }, 'Auto-configured Archive status for auto-clean (fallback)');
1731
- }
1732
- if (ps.epicAssignedTemplate) {
1733
- await this.settings.updateSettings({
1734
- events: {
1735
- epicAssigned: { template: ps.epicAssignedTemplate },
1736
- },
1737
- });
1738
- logger.info({ projectId }, 'Applied epicAssigned template from projectSettings');
1739
- }
1740
- if (ps.messagePoolSettings) {
1741
- await this.settings.setProjectPoolSettings(projectId, ps.messagePoolSettings);
1742
- logger.info({ projectId, poolSettings: ps.messagePoolSettings }, 'Applied message pool settings from projectSettings');
1743
- }
1744
- }
1745
- else if (archiveStatusId) {
1746
- const currentSettings = this.settings.getSettings();
1747
- const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
1748
- await this.settings.updateSettings({
1749
- autoClean: {
1750
- statusIds: { ...existingAutoClean, [projectId]: [archiveStatusId] },
1751
- },
1752
- });
1753
- logger.info({ projectId, archiveStatusId }, 'Auto-configured Archive status for auto-clean');
1754
- }
1755
- return { initialPromptSet };
1756
- }
1757
- normalizeProfileOptions(options) {
1758
- if (typeof options === 'string') {
1759
- return options;
1760
- }
1761
- if (options && typeof options === 'object') {
1762
- try {
1763
- return JSON.stringify(options);
1764
- }
1765
- catch {
1766
- return null;
1767
- }
1768
- }
1769
- return null;
1770
- }
1771
- getImportErrorMessage(error) {
1772
- const errorCode = error?.code;
1773
- if (errorCode === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
1774
- return 'Import failed: Cannot delete items that are still referenced. Check for cross-project agents using these profiles.';
1775
- }
1776
- if (errorCode === 'SQLITE_CONSTRAINT_UNIQUE') {
1777
- return 'Import failed: Duplicate entry detected.';
1778
- }
1779
- if (error instanceof Error) {
1780
- if (error.message.includes('FOREIGN KEY constraint failed')) {
1781
- return 'Import failed: Cannot delete items that are still referenced. Check for cross-project agents using these profiles.';
1782
- }
1783
- if (error.message.includes('UNIQUE constraint failed')) {
1784
- return 'Import failed: Duplicate entry detected.';
1785
- }
1786
- if (error.message.startsWith('Import failed')) {
1787
- return error.message;
1788
- }
1789
- return `Import failed: ${error.message}`;
1790
- }
1791
- return 'Import failed: An unexpected error occurred.';
1792
- }
1793
- slugify(name) {
1794
- return name
1795
- .toLowerCase()
1796
- .replace(/[^a-z0-9]+/g, '-')
1797
- .replace(/(^-|-$)/g, '');
1798
- }
1799
- deriveSlugFromPath(filePath) {
1800
- const filename = (0, path_1.basename)(filePath);
1801
- const nameWithoutExt = filename.replace(/\.json$/i, '');
1802
- return this.slugify(nameWithoutExt);
88
+ return (0, profile_mapping_helpers_1.computeFamilyAlternativesFromStorage)(this.storage, templateProfiles, templateAgents);
1803
89
  }
1804
90
  async getTemplateManifestForProject(projectId) {
1805
- const metadata = this.settings.getProjectTemplateMetadata(projectId);
1806
- if (!metadata?.templateSlug) {
1807
- logger.debug({ projectId }, 'No template metadata for project');
1808
- return null;
1809
- }
1810
- try {
1811
- if (metadata.source === 'file') {
1812
- logger.debug({ projectId, templateSlug: metadata.templateSlug }, 'File-based template - no manifest available');
1813
- return null;
1814
- }
1815
- if (metadata.source === 'bundled') {
1816
- const template = this.unifiedTemplateService.getBundledTemplate(metadata.templateSlug);
1817
- return template.content._manifest ?? null;
1818
- }
1819
- else {
1820
- const template = await this.unifiedTemplateService.getTemplate(metadata.templateSlug, metadata.installedVersion ?? undefined);
1821
- if (template.source !== 'registry') {
1822
- logger.debug({
1823
- projectId,
1824
- templateSlug: metadata.templateSlug,
1825
- expectedSource: 'registry',
1826
- actualSource: template.source,
1827
- }, 'Template source mismatch - registry template not available, rejecting bundled fallback');
1828
- return null;
1829
- }
1830
- return template.content._manifest ?? null;
1831
- }
1832
- }
1833
- catch (error) {
1834
- logger.debug({ projectId, templateSlug: metadata.templateSlug, error }, 'Failed to fetch template manifest for project');
1835
- return null;
1836
- }
91
+ return (0, project_template_manifest_helpers_1.getTemplateManifestForProjectWithHelper)(projectId, {
92
+ settings: this.settings,
93
+ unifiedTemplateService: this.unifiedTemplateService,
94
+ });
1837
95
  }
1838
96
  getBundledUpgradeVersion(templateSlug, installedVersion) {
1839
- if (!installedVersion) {
1840
- return null;
1841
- }
1842
- try {
1843
- const bundled = this.unifiedTemplateService.getBundledTemplate(templateSlug);
1844
- const manifest = bundled.content._manifest;
1845
- const bundledVersion = manifest?.version;
1846
- if (!bundledVersion) {
1847
- return null;
1848
- }
1849
- if (!(0, shared_1.isValidSemVer)(installedVersion) || !(0, shared_1.isValidSemVer)(bundledVersion)) {
1850
- logger.warn({ templateSlug, installedVersion, bundledVersion }, 'Invalid semver version detected, skipping upgrade check');
1851
- return null;
1852
- }
1853
- if ((0, shared_1.isLessThan)(installedVersion, bundledVersion)) {
1854
- return bundledVersion;
1855
- }
1856
- return null;
1857
- }
1858
- catch {
1859
- return null;
1860
- }
97
+ return (0, project_template_manifest_helpers_1.getBundledUpgradeVersionWithHelper)(templateSlug, installedVersion, this.unifiedTemplateService);
1861
98
  }
1862
99
  getBundledUpgradesForProjects(projects) {
1863
- const result = new Map();
1864
- const bundledVersionCache = new Map();
1865
- for (const project of projects) {
1866
- if (project.source !== 'bundled' || !project.templateSlug) {
1867
- result.set(project.projectId, null);
1868
- continue;
1869
- }
1870
- if (!bundledVersionCache.has(project.templateSlug)) {
1871
- try {
1872
- const bundled = this.unifiedTemplateService.getBundledTemplate(project.templateSlug);
1873
- const manifest = bundled.content._manifest;
1874
- bundledVersionCache.set(project.templateSlug, manifest?.version ?? null);
1875
- }
1876
- catch {
1877
- bundledVersionCache.set(project.templateSlug, null);
1878
- }
1879
- }
1880
- const bundledVersion = bundledVersionCache.get(project.templateSlug);
1881
- if (!bundledVersion || !project.installedVersion) {
1882
- result.set(project.projectId, null);
1883
- continue;
1884
- }
1885
- if (!(0, shared_1.isValidSemVer)(project.installedVersion) || !(0, shared_1.isValidSemVer)(bundledVersion)) {
1886
- logger.warn({
1887
- projectId: project.projectId,
1888
- templateSlug: project.templateSlug,
1889
- installedVersion: project.installedVersion,
1890
- bundledVersion,
1891
- }, 'Invalid semver version detected, skipping upgrade check');
1892
- result.set(project.projectId, null);
1893
- continue;
1894
- }
1895
- if ((0, shared_1.isLessThan)(project.installedVersion, bundledVersion)) {
1896
- result.set(project.projectId, bundledVersion);
1897
- }
1898
- else {
1899
- result.set(project.projectId, null);
1900
- }
1901
- }
1902
- return result;
100
+ return (0, project_template_manifest_helpers_1.getBundledUpgradesForProjectsWithHelper)(projects, this.unifiedTemplateService);
1903
101
  }
1904
102
  };
1905
103
  exports.ProjectsService = ProjectsService;