crewx 0.8.7-rc.21 → 0.8.7-rc.22

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 (222) hide show
  1. package/dist/assets/{MarketPage-aY16UW1_.js → MarketPage-lLBtAENs.js} +10 -15
  2. package/dist/assets/{PromptTab-C3aTJS6u.js → PromptTab-Daq54dMi.js} +1 -1
  3. package/dist/assets/_baseUniq-BJDqmZBO.js +1 -0
  4. package/dist/assets/{arc-BCQAokB1.js → arc-ByjRIa5p.js} +1 -1
  5. package/dist/assets/{architectureDiagram-UYN6MBPD-Bc87ujv2.js → architectureDiagram-UYN6MBPD-DcKpqSFb.js} +1 -1
  6. package/dist/assets/{blockDiagram-ZHA2E4KO-wC9uH8Lb.js → blockDiagram-ZHA2E4KO-YvUeULT5.js} +1 -1
  7. package/dist/assets/{c4Diagram-6F5ED5ID-CqB7lqni.js → c4Diagram-6F5ED5ID-vggzH2N5.js} +1 -1
  8. package/dist/assets/channel-1Y6YooBb.js +1 -0
  9. package/dist/assets/{chunk-5HRBRIJM-Cc1L17Eh.js → chunk-5HRBRIJM-BbaQwJkn.js} +1 -1
  10. package/dist/assets/{chunk-7U56Z5CX-Df3nu_7G.js → chunk-7U56Z5CX-DVYVpFaa.js} +1 -1
  11. package/dist/assets/{chunk-ASOPGD6M-DIXslLG-.js → chunk-ASOPGD6M-CXEvWb-8.js} +1 -1
  12. package/dist/assets/{chunk-KFBOBJHC-FytVljyP.js → chunk-KFBOBJHC-CadVXFV3.js} +1 -1
  13. package/dist/assets/{chunk-T2TOU4HS-CU5z-cXq.js → chunk-T2TOU4HS-lDZr1sVF.js} +1 -1
  14. package/dist/assets/{chunk-TMUBEWPD-CWo1AFyn.js → chunk-TMUBEWPD-D5A0MrFD.js} +1 -1
  15. package/dist/assets/classDiagram-LNE6IOMH-CDxkV2JV.js +1 -0
  16. package/dist/assets/classDiagram-v2-MQ7JQ4JX-CDxkV2JV.js +1 -0
  17. package/dist/assets/clone-6xznz_9G.js +1 -0
  18. package/dist/assets/{dagre-4EVJKHTY-CgZW5tg4.js → dagre-4EVJKHTY-Bncx7Ijq.js} +1 -1
  19. package/dist/assets/{diagram-QW4FP2JN-CJAng8RC.js → diagram-QW4FP2JN-RkxVNs99.js} +1 -1
  20. package/dist/assets/{erDiagram-6RL3IURR-C1uHztHX.js → erDiagram-6RL3IURR-DIiKqQdx.js} +1 -1
  21. package/dist/assets/{flowDiagram-7ASYPVHJ-Yiu2odO-.js → flowDiagram-7ASYPVHJ-DQ5PN7v6.js} +1 -1
  22. package/dist/assets/{ganttDiagram-NTVNEXSI-tutc0dJP.js → ganttDiagram-NTVNEXSI-BeNWGpB6.js} +1 -1
  23. package/dist/assets/{gitGraph-YCYPL57B-C3jM6T4G.js → gitGraph-YCYPL57B-CdMe059q.js} +1 -1
  24. package/dist/assets/{gitGraphDiagram-NRZ2UAAF-0aqHEKzz.js → gitGraphDiagram-NRZ2UAAF-BEke5TyK.js} +1 -1
  25. package/dist/assets/{graph-DC224ILV.js → graph-DyQIwWJd.js} +1 -1
  26. package/dist/assets/{infoDiagram-A4XQUW5V-Do7Mpzkx.js → infoDiagram-A4XQUW5V-CUcC8I_5.js} +1 -1
  27. package/dist/assets/{journeyDiagram-G5WM74LC-DAruEJc7.js → journeyDiagram-G5WM74LC-CQ5DqAH5.js} +1 -1
  28. package/dist/assets/{kanban-definition-QRCXZQQD-DB63vLR0.js → kanban-definition-QRCXZQQD-BBsgOii7.js} +1 -1
  29. package/dist/assets/{layout-BiYH5x6Q.js → layout-D7vBs_Wy.js} +1 -1
  30. package/dist/assets/{linear-CLyGW2s0.js → linear-CEOF9oJz.js} +1 -1
  31. package/dist/assets/{main-CEKkUqZb.js → main-Bx1nZ7_p.js} +278 -273
  32. package/dist/assets/main-C4ebvOIJ.css +10 -0
  33. package/dist/assets/{min-ChJMFU2N.js → min-RK9J5Utn.js} +1 -1
  34. package/dist/assets/{mindmap-definition-GWI6TPTV-CtADqQgd.js → mindmap-definition-GWI6TPTV-BXvEU7qC.js} +1 -1
  35. package/dist/assets/{pieDiagram-YF2LJOPJ-BSOfzo3O.js → pieDiagram-YF2LJOPJ-TrXqcpni.js} +1 -1
  36. package/dist/assets/{quadrantDiagram-OS5C2QUG-BLBQNFVb.js → quadrantDiagram-OS5C2QUG-B-chTEU4.js} +1 -1
  37. package/dist/assets/{requirementDiagram-MIRIMTAZ-DqorvuS6.js → requirementDiagram-MIRIMTAZ-BgJhNLp_.js} +1 -1
  38. package/dist/assets/{sankeyDiagram-Y46BX6SQ-4NDcrLkB.js → sankeyDiagram-Y46BX6SQ-BVsgYKK7.js} +1 -1
  39. package/dist/assets/{sequenceDiagram-G6AWOVSC-yha6BxK2.js → sequenceDiagram-G6AWOVSC-ByJOpkq3.js} +1 -1
  40. package/dist/assets/{stateDiagram-MAYHULR4-XJ_I41ez.js → stateDiagram-MAYHULR4-Dw7lUL1B.js} +1 -1
  41. package/dist/assets/stateDiagram-v2-4JROLMXI-DYJiM3bF.js +1 -0
  42. package/dist/assets/{timeline-definition-U7ZMHBDA-DCNrmvE7.js → timeline-definition-U7ZMHBDA-CSoEYs60.js} +1 -1
  43. package/dist/assets/{xychartDiagram-6QU3TZC5-9N-Ohhdp.js → xychartDiagram-6QU3TZC5-0_rG7p3L.js} +1 -1
  44. package/dist/index.html +2 -2
  45. package/package.json +8 -8
  46. package/packages/cli/package.json +1 -1
  47. package/dist/assets/_baseUniq-DxEFlwNt.js +0 -1
  48. package/dist/assets/channel-DNLHkiNp.js +0 -1
  49. package/dist/assets/classDiagram-LNE6IOMH-B430Onym.js +0 -1
  50. package/dist/assets/classDiagram-v2-MQ7JQ4JX-B430Onym.js +0 -1
  51. package/dist/assets/clone-B9O3E3P1.js +0 -1
  52. package/dist/assets/main-D3YeRndF.css +0 -10
  53. package/dist/assets/stateDiagram-v2-4JROLMXI-CxcyPJO2.js +0 -1
  54. package/dist-server/adapters/adapter.module.js +0 -35
  55. package/dist-server/adapters/adapter.service.js +0 -79
  56. package/dist-server/adapters/http-router.service.js +0 -91
  57. package/dist-server/app.module.js +0 -111
  58. package/dist-server/bootstrap/crewx-server.js +0 -80
  59. package/dist-server/bootstrap/tls.js +0 -138
  60. package/dist-server/common/analytics.client.js +0 -180
  61. package/dist-server/common/analytics.module.js +0 -21
  62. package/dist-server/common/cors.js +0 -15
  63. package/dist-server/common/decorators/project.decorator.js +0 -21
  64. package/dist-server/common/decorators/workspace.decorator.js +0 -24
  65. package/dist-server/common/device-id.js +0 -33
  66. package/dist-server/common/interceptor/api-id-header.interceptor.js +0 -53
  67. package/dist-server/common/interceptor/http-logging.interceptor.js +0 -124
  68. package/dist-server/common/limits/defaults.js +0 -13
  69. package/dist-server/common/limits/limits-provider.js +0 -6
  70. package/dist-server/common/limits/limits.controller.js +0 -38
  71. package/dist-server/common/limits/limits.module.js +0 -24
  72. package/dist-server/common/limits/local-limits.provider.js +0 -27
  73. package/dist-server/common/middleware/workspace.middleware.js +0 -210
  74. package/dist-server/common/repository.exception-filter.js +0 -48
  75. package/dist-server/common/workspace-context.store.js +0 -25
  76. package/dist-server/domain/agent/agent.controller.js +0 -96
  77. package/dist-server/domain/agent/agent.module.js +0 -27
  78. package/dist-server/domain/agent/agent.service.js +0 -335
  79. package/dist-server/domain/agent/agent.types.js +0 -3
  80. package/dist-server/domain/agent/dto/create-agent.dto.js +0 -80
  81. package/dist-server/domain/agent/dto/preview-prompt.dto.js +0 -77
  82. package/dist-server/domain/agent/dto/provider-meta.dto.js +0 -2
  83. package/dist-server/domain/agent/dto/update-agent.dto.js +0 -92
  84. package/dist-server/domain/agent/provider.controller.js +0 -38
  85. package/dist-server/domain/agent/template-processor.service.js +0 -71
  86. package/dist-server/domain/auth/auth.constants.js +0 -4
  87. package/dist-server/domain/auth/auth.controller.js +0 -123
  88. package/dist-server/domain/auth/auth.module.js +0 -47
  89. package/dist-server/domain/auth/decorators/public.decorator.js +0 -7
  90. package/dist-server/domain/auth/guards/base-auth.guard.js +0 -66
  91. package/dist-server/domain/auth/session-store.js +0 -26
  92. package/dist-server/domain/auth/utils/ip.utils.js +0 -31
  93. package/dist-server/domain/box/box.controller.js +0 -77
  94. package/dist-server/domain/box/box.module.js +0 -21
  95. package/dist-server/domain/box/box.service.js +0 -97
  96. package/dist-server/domain/box/dto/create-box.dto.js +0 -74
  97. package/dist-server/domain/cli/cli.controller.js +0 -136
  98. package/dist-server/domain/cli/cli.module.js +0 -21
  99. package/dist-server/domain/cli/cli.service.js +0 -598
  100. package/dist-server/domain/cli/dto/login-cli.dto.js +0 -25
  101. package/dist-server/domain/cli/dto/update-cli.dto.js +0 -31
  102. package/dist-server/domain/doc/doc.controller.js +0 -95
  103. package/dist-server/domain/doc/doc.module.js +0 -21
  104. package/dist-server/domain/doc/doc.service.js +0 -368
  105. package/dist-server/domain/doc/dto/doc.dto.js +0 -64
  106. package/dist-server/domain/document/document.controller.js +0 -57
  107. package/dist-server/domain/document/document.module.js +0 -22
  108. package/dist-server/domain/document/document.service.js +0 -274
  109. package/dist-server/domain/document/dto/register-document.dto.js +0 -24
  110. package/dist-server/domain/fs/dto/mkdir-request.dto.js +0 -47
  111. package/dist-server/domain/fs/dto/mkdir-response.dto.js +0 -2
  112. package/dist-server/domain/fs/dto/quick-access.dto.js +0 -2
  113. package/dist-server/domain/fs/dto/tree-node.dto.js +0 -2
  114. package/dist-server/domain/fs/dto/tree-query.dto.js +0 -35
  115. package/dist-server/domain/fs/fs.controller.js +0 -63
  116. package/dist-server/domain/fs/fs.module.js +0 -22
  117. package/dist-server/domain/fs/fs.service.js +0 -303
  118. package/dist-server/domain/git/git.module.js +0 -20
  119. package/dist-server/domain/git/git.service.js +0 -222
  120. package/dist-server/domain/goal/dto/goal.dto.js +0 -49
  121. package/dist-server/domain/goal/goal.controller.js +0 -86
  122. package/dist-server/domain/goal/goal.module.js +0 -22
  123. package/dist-server/domain/goal/goal.service.js +0 -197
  124. package/dist-server/domain/goal/parser/goal-md-parser.js +0 -166
  125. package/dist-server/domain/goal/parser/goal-md-serializer.js +0 -110
  126. package/dist-server/domain/health/health.controller.js +0 -57
  127. package/dist-server/domain/health/health.module.js +0 -19
  128. package/dist-server/domain/knowledge/dto/graph-knowledge.dto.js +0 -27
  129. package/dist-server/domain/knowledge/dto/knowledge.dto.js +0 -71
  130. package/dist-server/domain/knowledge/knowledge.controller.js +0 -122
  131. package/dist-server/domain/knowledge/knowledge.module.js +0 -21
  132. package/dist-server/domain/knowledge/knowledge.service.js +0 -538
  133. package/dist-server/domain/market/dto/add-registry.dto.js +0 -32
  134. package/dist-server/domain/market/dto/install-market.dto.js +0 -30
  135. package/dist-server/domain/market/market.controller.js +0 -163
  136. package/dist-server/domain/market/market.module.js +0 -22
  137. package/dist-server/domain/market/market.service.js +0 -1447
  138. package/dist-server/domain/mcp/browser-session.store.js +0 -247
  139. package/dist-server/domain/mcp/crewx-tool.factory.js +0 -119
  140. package/dist-server/domain/mcp/mcp-auth.guard.js +0 -61
  141. package/dist-server/domain/mcp/mcp.constants.js +0 -4
  142. package/dist-server/domain/mcp/mcp.controller.js +0 -125
  143. package/dist-server/domain/mcp/mcp.dto.js +0 -12
  144. package/dist-server/domain/mcp/mcp.module.js +0 -49
  145. package/dist-server/domain/mcp/mcp.service.js +0 -684
  146. package/dist-server/domain/mcp/tool-router.service.js +0 -120
  147. package/dist-server/domain/message/dto/list-messages.dto.js +0 -57
  148. package/dist-server/domain/message/dto/send-message.dto.js +0 -45
  149. package/dist-server/domain/message/message.controller.js +0 -73
  150. package/dist-server/domain/message/message.module.js +0 -25
  151. package/dist-server/domain/message/message.service.js +0 -160
  152. package/dist-server/domain/onboarding/onboarding.controller.js +0 -97
  153. package/dist-server/domain/onboarding/onboarding.module.js +0 -23
  154. package/dist-server/domain/onboarding/onboarding.service.js +0 -106
  155. package/dist-server/domain/onboarding/onboarding.types.js +0 -2
  156. package/dist-server/domain/onboarding/presets/custom.js +0 -10
  157. package/dist-server/domain/onboarding/presets/dev-team.js +0 -41
  158. package/dist-server/domain/onboarding/presets/index.js +0 -15
  159. package/dist-server/domain/onboarding/presets/marketing-team.js +0 -29
  160. package/dist-server/domain/onboarding/presets/planning-team.js +0 -29
  161. package/dist-server/domain/onboarding/presets/solo.js +0 -17
  162. package/dist-server/domain/planner/dto/planner.dto.js +0 -58
  163. package/dist-server/domain/planner/planner.controller.js +0 -74
  164. package/dist-server/domain/planner/planner.module.js +0 -22
  165. package/dist-server/domain/planner/planner.service.js +0 -269
  166. package/dist-server/domain/project/dto/create-project.dto.js +0 -46
  167. package/dist-server/domain/project/dto/update-project.dto.js +0 -60
  168. package/dist-server/domain/project/project.controller.js +0 -129
  169. package/dist-server/domain/project/project.module.js +0 -22
  170. package/dist-server/domain/project/project.service.js +0 -173
  171. package/dist-server/domain/settings/settings.controller.js +0 -52
  172. package/dist-server/domain/settings/settings.module.js +0 -22
  173. package/dist-server/domain/settings/settings.service.js +0 -105
  174. package/dist-server/domain/task/dto/all-tasks.dto.js +0 -55
  175. package/dist-server/domain/task/dto/list-tasks.dto.js +0 -69
  176. package/dist-server/domain/task/dto/search-tasks.dto.js +0 -66
  177. package/dist-server/domain/task/dto/send-message.dto.js +0 -31
  178. package/dist-server/domain/task/dto/task-status.dto.js +0 -34
  179. package/dist-server/domain/task/dto/workspace-usage.dto.js +0 -38
  180. package/dist-server/domain/task/task.constants.js +0 -5
  181. package/dist-server/domain/task/task.controller.js +0 -169
  182. package/dist-server/domain/task/task.module.js +0 -23
  183. package/dist-server/domain/task/task.service.js +0 -775
  184. package/dist-server/domain/thread/dto/build-context.dto.js +0 -42
  185. package/dist-server/domain/thread/dto/list-messages.dto.js +0 -49
  186. package/dist-server/domain/thread/dto/list-threads.dto.js +0 -70
  187. package/dist-server/domain/thread/dto/search-threads.dto.js +0 -13
  188. package/dist-server/domain/thread/dto/update-thread.dto.js +0 -23
  189. package/dist-server/domain/thread/thread.controller.js +0 -160
  190. package/dist-server/domain/thread/thread.module.js +0 -23
  191. package/dist-server/domain/thread/thread.service.js +0 -419
  192. package/dist-server/domain/usage/usage.controller.js +0 -118
  193. package/dist-server/domain/usage/usage.module.js +0 -21
  194. package/dist-server/domain/usage/usage.service.js +0 -358
  195. package/dist-server/domain/usage/usage.types.js +0 -4
  196. package/dist-server/domain/wbs/wbs.controller.js +0 -51
  197. package/dist-server/domain/wbs/wbs.module.js +0 -21
  198. package/dist-server/domain/wbs/wbs.service.js +0 -94
  199. package/dist-server/domain/workflow/workflow.controller.js +0 -53
  200. package/dist-server/domain/workflow/workflow.module.js +0 -21
  201. package/dist-server/domain/workflow/workflow.service.js +0 -209
  202. package/dist-server/domain/workspace/dto/create-workspace.dto.js +0 -45
  203. package/dist-server/domain/workspace/dto/switch-workspace.dto.js +0 -32
  204. package/dist-server/domain/workspace/dto/workspace-info.dto.js +0 -2
  205. package/dist-server/domain/workspace/workspace.controller.js +0 -98
  206. package/dist-server/domain/workspace/workspace.module.js +0 -22
  207. package/dist-server/domain/workspace/workspace.service.js +0 -198
  208. package/dist-server/main.js +0 -204
  209. package/dist-server/modules/crewx-pool.service.js +0 -75
  210. package/dist-server/modules/crewx.module.js +0 -47
  211. package/dist-server/repository/base-sqlite.repository.js +0 -18
  212. package/dist-server/repository/box.repository.js +0 -99
  213. package/dist-server/repository/message.repository.js +0 -22
  214. package/dist-server/repository/project.repository.js +0 -51
  215. package/dist-server/repository/repository.module.js +0 -34
  216. package/dist-server/repository/request-log.repository.js +0 -96
  217. package/dist-server/repository/task.repository.js +0 -105
  218. package/dist-server/repository/thread.repository.js +0 -165
  219. package/dist-server/shared/crewx-home.js +0 -58
  220. package/dist-server/shared/crewx-state.js +0 -66
  221. package/dist-server/test-utils/compat-database.js +0 -30
  222. package/dist-server/utils/tokenizer.js +0 -8
@@ -1,775 +0,0 @@
1
- "use strict";
2
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
- return c > 3 && r && Object.defineProperty(target, key, r), r;
7
- };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
- var TaskService_1;
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.TaskService = void 0;
14
- const common_1 = require("@nestjs/common");
15
- const promises_1 = require("fs/promises");
16
- const path_1 = require("path");
17
- const fs_1 = require("fs");
18
- const child_process_1 = require("child_process");
19
- const js_yaml_1 = require("js-yaml");
20
- const task_constants_js_1 = require("./task.constants.js");
21
- const sdk_1 = require("@crewx/sdk");
22
- const task_repository_js_1 = require("../../repository/task.repository.js");
23
- const project_repository_js_1 = require("../../repository/project.repository.js");
24
- const workspace_context_store_js_1 = require("../../common/workspace-context.store.js");
25
- const crewx_pool_service_js_1 = require("../../modules/crewx-pool.service.js");
26
- const MAX_LOG_ENTRY_FIELD_LEN = 128 * 1024; // 128KB worth of UTF-16 code units
27
- // Data source: .crewx/conversations/ JSON files (per design doc "태스크 데이터 소스").
28
- // crewx.db is NOT used for task data — conversations JSON is the canonical source.
29
- // TSK005 uses crewx.db for execution status and logs.
30
- let TaskService = TaskService_1 = class TaskService {
31
- taskRepo;
32
- crewxPool;
33
- projectRepo;
34
- logger = new common_1.Logger(TaskService_1.name);
35
- reaperInterval;
36
- constructor(taskRepo, crewxPool, projectRepo) {
37
- this.taskRepo = taskRepo;
38
- this.crewxPool = crewxPool;
39
- this.projectRepo = projectRepo;
40
- }
41
- onModuleInit() {
42
- const reaped = this.taskRepo.reapOrphanedTasks();
43
- if (reaped > 0) {
44
- this.logger.warn(`Reaped ${reaped} orphaned task(s) on startup`);
45
- }
46
- this.reaperInterval = setInterval(() => {
47
- try {
48
- const n = this.taskRepo.reapOrphanedTasks();
49
- if (n > 0)
50
- this.logger.warn(`Reaped ${n} orphaned task(s)`);
51
- }
52
- catch { /* non-fatal */ }
53
- }, 5 * 60 * 1000);
54
- }
55
- onModuleDestroy() {
56
- if (this.reaperInterval !== undefined) {
57
- clearInterval(this.reaperInterval);
58
- }
59
- }
60
- // TSK.ALL: All tasks from crewx.db with filtering, search, and pagination
61
- getAllTasks(params, workspaceId) {
62
- const agents = params.agent?.split(',').map(s => s.trim()).filter(Boolean);
63
- const statuses = params.status?.split(',').map(s => s.trim()).filter(Boolean);
64
- const page = Math.max(1, params.page || 1);
65
- const limit = Math.min(100, Math.max(1, params.limit || 20));
66
- const offset = (page - 1) * limit;
67
- const sortDir = params.sort === 'started_at:asc' ? 'ASC' : 'DESC';
68
- const { tasks, total } = this.taskRepo.findAllTasks({
69
- workspaceId, agents, statuses, q: params.q, limit, offset, sortDir,
70
- });
71
- return {
72
- tasks: tasks.map(t => ({
73
- taskId: t.id,
74
- agentId: t.agent_id,
75
- status: t.status,
76
- prompt: this.truncate(t.prompt || '', 100),
77
- model: t.model || null,
78
- startedAt: t.started_at,
79
- completedAt: t.completed_at || null,
80
- durationMs: t.duration_ms ?? null,
81
- inputTokens: t.input_tokens ?? 0,
82
- outputTokens: t.output_tokens ?? 0,
83
- costUsd: t.cost_usd ?? 0,
84
- threadId: t.thread_id ?? null,
85
- provider: this.extractProviderFromMeta(t.metadata) ?? null,
86
- })),
87
- total,
88
- page,
89
- limit,
90
- };
91
- }
92
- // TSK004: Send message — fires crewx executeAgent in background and returns immediately
93
- async sendMessage(threadId, message, agent) {
94
- this.validateTaskId(threadId);
95
- if (!message || typeof message !== 'string' || !message.trim()) {
96
- throw new common_1.BadRequestException('message is required');
97
- }
98
- if (!agent || typeof agent !== 'string' || !agent.trim()) {
99
- throw new common_1.BadRequestException('agent is required');
100
- }
101
- // Fire-and-forget: get the current workspace's Crewx instance from the pool
102
- // (ConversationPlugin handles threads row upsert). Never close — pool owns lifecycle.
103
- this.crewxPool.getForCurrentRequest().then((crewx) => {
104
- crewx
105
- .execute(`@${agent}`, message, {
106
- threadId,
107
- platform: 'cli',
108
- metadata: { thread: threadId },
109
- })
110
- .catch((err) => {
111
- this.logger.error(`Background executeAgent failed [thread=${threadId}]: ${err.message}`, err.stack);
112
- });
113
- }).catch((err) => {
114
- this.logger.error(`CrewxPool.getForCurrentRequest failed [thread=${threadId}]: ${err.message}`, err.stack);
115
- });
116
- // Return the threadId as taskId — frontend polls getTaskStatus(threadId)
117
- // which searches crewx.db for tasks WHERE command LIKE '%--thread={threadId}%'
118
- return { taskId: threadId, status: 'pending', agent };
119
- }
120
- // TSK.STOP: Stop a running task by killing its process
121
- stopTask(taskId, workspaceId) {
122
- this.validateTaskId(taskId);
123
- const task = this.taskRepo.findTaskForStop(taskId, workspaceId);
124
- if (!task) {
125
- throw new common_1.HttpException({ code: 'TASK_NOT_FOUND', message: '태스크를 찾을 수 없습니다' }, 404);
126
- }
127
- if (task.status !== 'running') {
128
- throw new common_1.HttpException({ code: 'TASK_NOT_RUNNING', message: '실행 중인 태스크만 중지할 수 있습니다' }, 400);
129
- }
130
- if (task.pid == null) {
131
- throw new common_1.HttpException({ code: 'PID_NOT_FOUND', message: '프로세스 ID를 찾을 수 없습니다' }, 422);
132
- }
133
- // PID ownership verification: ensure PID belongs to a crewx-related process
134
- try {
135
- const cmdOutput = (0, child_process_1.execSync)(`ps -p ${task.pid} -o command=`, { encoding: 'utf-8', timeout: 3000 }).trim();
136
- if (!cmdOutput.includes('crewx') && !cmdOutput.includes('claude') && !cmdOutput.includes('node')) {
137
- throw new common_1.HttpException({ code: 'PID_NOT_FOUND', message: '프로세스 ID를 찾을 수 없습니다' }, 422);
138
- }
139
- }
140
- catch (err) {
141
- // Process not found (already exited) — proceed to update DB status
142
- if (err instanceof common_1.HttpException)
143
- throw err;
144
- }
145
- // Send SIGTERM to the process
146
- try {
147
- process.kill(task.pid, 'SIGTERM');
148
- }
149
- catch (err) {
150
- const code = err.code;
151
- if (code === 'EPERM') {
152
- throw new common_1.HttpException({ code: 'PERMISSION_DENIED', message: '프로세스 중지 권한이 없습니다' }, 403);
153
- }
154
- // ESRCH = process already gone — proceed to update DB
155
- if (code !== 'ESRCH')
156
- throw err;
157
- }
158
- // Conditionally update DB (race condition safe)
159
- this.taskRepo.markTaskFailed(taskId, '사용자에 의해 중지됨');
160
- return {
161
- taskId,
162
- status: 'failed',
163
- error: '사용자에 의해 중지됨',
164
- stoppedAt: new Date().toISOString(),
165
- };
166
- }
167
- // TSK.LIST: List tasks
168
- async getTasks(params, _workspaceId) {
169
- const tasksDir = this.resolveTasksPath(params.project);
170
- const files = await this.listJsonFiles(tasksDir);
171
- const allSummaries = [];
172
- const allAgents = new Set();
173
- const allPlatforms = new Set();
174
- for (const file of files) {
175
- try {
176
- const data = await this.readTaskFile((0, path_1.join)(tasksDir, file));
177
- const summary = this.toSummary(data);
178
- allPlatforms.add(summary.platform);
179
- summary.agents.forEach((a) => allAgents.add(a));
180
- allSummaries.push(summary);
181
- }
182
- catch {
183
- // Skip malformed files
184
- }
185
- }
186
- // Filter
187
- let filtered = allSummaries;
188
- const platform = params.platform || 'all';
189
- if (platform !== 'all') {
190
- filtered = filtered.filter((t) => t.platform === platform);
191
- }
192
- if (params.agent) {
193
- filtered = filtered.filter((t) => t.agents.includes(params.agent));
194
- }
195
- // Sort
196
- const sort = params.sort || 'updatedAt';
197
- const order = params.order || 'desc';
198
- filtered.sort((a, b) => {
199
- let cmp = 0;
200
- if (sort === 'messageCount') {
201
- cmp = a.messageCount - b.messageCount;
202
- }
203
- else if (sort === 'createdAt') {
204
- cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
205
- }
206
- else {
207
- cmp = new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime();
208
- }
209
- return order === 'asc' ? cmp : -cmp;
210
- });
211
- // Pagination
212
- const total = filtered.length;
213
- const limit = this.normalizeLimit(params.limit, 50, 200);
214
- const offset = this.normalizeOffset(params.offset);
215
- const tasks = filtered.slice(offset, offset + limit);
216
- return {
217
- tasks,
218
- total,
219
- agents: Array.from(allAgents).sort(),
220
- platforms: Array.from(allPlatforms).sort(),
221
- };
222
- }
223
- // TSK.GET: Get task detail
224
- async getTask(taskId, workspaceId) {
225
- this.validateTaskId(taskId);
226
- const tasksDir = this.resolveTasksPath(workspaceId);
227
- const filePath = (0, path_1.join)(tasksDir, `${taskId}.json`);
228
- // Ensure resolved path stays within tasksDir
229
- const resolved = (0, path_1.resolve)(filePath);
230
- if (!resolved.startsWith((0, path_1.resolve)(tasksDir))) {
231
- throw new common_1.BadRequestException('Invalid taskId');
232
- }
233
- let data;
234
- try {
235
- data = await this.readTaskFile(filePath);
236
- }
237
- catch {
238
- throw new common_1.NotFoundException(`Task not found: ${taskId}`);
239
- }
240
- // Enrich assistant messages with logs from crewx.db
241
- const messagesWithLogs = this.enrichMessagesWithLogs(data, workspaceId);
242
- // Aggregate token/cost data from crewx.db for this thread
243
- const tokenUsage = this.taskRepo.getThreadTokenUsage(data.threadId, workspaceId);
244
- return {
245
- taskId: data.threadId,
246
- platform: data.platform,
247
- messages: messagesWithLogs,
248
- metadata: data.metadata,
249
- ...tokenUsage,
250
- };
251
- }
252
- // TSK003: Search tasks
253
- async searchTasks(params, _workspaceId) {
254
- const query = params.q;
255
- if (!query || query.trim().length === 0) {
256
- throw new common_1.BadRequestException('Search query is required');
257
- }
258
- const tasksDir = this.resolveTasksPath(params.project);
259
- const files = await this.listJsonFiles(tasksDir);
260
- const limit = this.normalizeLimit(params.limit, 20, 100);
261
- const offset = this.normalizeOffset(params.offset);
262
- const queryLower = query.toLowerCase();
263
- const results = [];
264
- for (const file of files) {
265
- try {
266
- const data = await this.readTaskFile((0, path_1.join)(tasksDir, file));
267
- // Apply filters
268
- const platform = params.platform || 'all';
269
- if (platform !== 'all' && data.platform !== platform)
270
- continue;
271
- if (params.agent) {
272
- const hasAgent = data.messages.some((m) => m.metadata?.agent_id === params.agent);
273
- if (!hasAgent)
274
- continue;
275
- }
276
- // Search messages
277
- const matchedMessages = [];
278
- for (const msg of data.messages) {
279
- if (msg.text.toLowerCase().includes(queryLower)) {
280
- matchedMessages.push({
281
- id: msg.id,
282
- text: this.highlightMatch(msg.text, query),
283
- userId: msg.userId,
284
- isAssistant: msg.isAssistant,
285
- agentId: msg.metadata?.agent_id,
286
- timestamp: msg.timestamp,
287
- });
288
- }
289
- }
290
- if (matchedMessages.length > 0) {
291
- results.push({
292
- taskId: data.threadId,
293
- platform: data.platform,
294
- matchedMessages,
295
- score: matchedMessages.length,
296
- });
297
- }
298
- }
299
- catch {
300
- // Skip malformed files
301
- }
302
- }
303
- // Sort by score (most matches first)
304
- results.sort((a, b) => b.score - a.score);
305
- const paginated = results.slice(offset, offset + limit);
306
- return {
307
- results: paginated,
308
- total: results.length,
309
- query,
310
- };
311
- }
312
- // TSK005: Get task execution status from crewx.db
313
- getTaskStatus(taskId, params, workspaceId) {
314
- this.validateTaskId(taskId);
315
- const includes = (params.include || '').split(',').map((s) => s.trim());
316
- const wantLogs = includes.includes('logs');
317
- const wantChildren = includes.includes('children');
318
- const logLimit = this.normalizeLimit(params.logLimit, task_constants_js_1.DEFAULT_TASK_LOG_LIMIT, task_constants_js_1.MAX_TASK_LOG_LIMIT);
319
- const row = this.taskRepo.findTaskStatus(taskId, workspaceId);
320
- if (!row) {
321
- throw new common_1.NotFoundException(`Task not found in crewx.db: ${taskId}`);
322
- }
323
- const parsedMeta = this.tryParseJson(row.metadata);
324
- const result = {
325
- taskId: row.id,
326
- status: row.status,
327
- agent: row.agent_id,
328
- callerAgentId: row.caller_agent_id || undefined,
329
- parentTaskId: row.parent_task_id || undefined,
330
- startedAt: row.started_at,
331
- completedAt: row.completed_at || undefined,
332
- durationMs: row.duration_ms ?? undefined,
333
- error: row.error || undefined,
334
- prompt: row.prompt || undefined,
335
- result: row.result || undefined,
336
- model: row.model || undefined,
337
- threadId: row.thread_id || undefined,
338
- inputTokens: row.input_tokens ?? undefined,
339
- outputTokens: row.output_tokens ?? undefined,
340
- costUsd: row.cost_usd ?? undefined,
341
- mode: row.mode || undefined,
342
- crewxVersion: row.crewx_version || undefined,
343
- pid: row.status === 'running' ? (row.pid ?? undefined) : undefined,
344
- };
345
- if (parsedMeta) {
346
- result.metadata = parsedMeta;
347
- }
348
- const adapterKey = this.extractAdapterKey(row);
349
- if (wantLogs) {
350
- result.logs = this.parseLogs(row.logs, logLimit, adapterKey);
351
- }
352
- result.toolUsage = this.aggregateToolUsage(row.logs, adapterKey);
353
- if (wantChildren) {
354
- // Need to open DB to get children — use withDb
355
- const dbPaths = this.taskRepo.resolveDbPaths();
356
- for (const dbPath of dbPaths) {
357
- if (!(0, fs_1.existsSync)(dbPath))
358
- continue;
359
- const children = this.taskRepo.withDb((db) => this.taskRepo.findChildTasks(db, row.id), { dbPath, readOnly: true });
360
- if (children.length > 0) {
361
- result.childTasks = children.map((child) => this.toChildTaskStatus(child));
362
- break;
363
- }
364
- }
365
- if (!result.childTasks) {
366
- result.childTasks = [];
367
- }
368
- }
369
- return result;
370
- }
371
- // USG.MONTHLY: Workspace usage summary from crewx.db
372
- getWorkspaceUsageSummary(workspaceId) {
373
- const rows = this.taskRepo.getWorkspaceUsageSummary(workspaceId);
374
- const wsIds = rows.map((r) => r.workspace_id).filter((id) => id != null && id !== 'unknown');
375
- const wsNameMap = new Map();
376
- for (const id of new Set(wsIds)) {
377
- const project = this.projectRepo.findById(id);
378
- wsNameMap.set(id, project?.name ?? id);
379
- }
380
- const workspaces = rows.map((row) => {
381
- const inputTokens = row.input_tokens ?? 0;
382
- const outputTokens = row.output_tokens ?? 0;
383
- const wsId = row.workspace_id ?? 'unknown';
384
- return {
385
- workspaceId: wsId,
386
- workspaceName: wsNameMap.get(wsId) ?? 'Unknown Workspace',
387
- inputTokens,
388
- outputTokens,
389
- totalTokens: inputTokens + outputTokens,
390
- totalCost: row.cost_usd ?? 0,
391
- taskCount: row.task_count ?? 0,
392
- };
393
- });
394
- return { workspaces };
395
- }
396
- // --- TSK005 private helpers ---
397
- toChildTaskStatus(row) {
398
- const logs = this.parseLogs(row.logs, 1, this.extractAdapterKey(row));
399
- return {
400
- taskId: row.id,
401
- agentId: row.agent_id,
402
- status: row.status,
403
- startedAt: row.started_at,
404
- completedAt: row.completed_at || undefined,
405
- durationMs: row.duration_ms ?? undefined,
406
- error: row.error || undefined,
407
- latestLog: logs.length > 0 ? logs[logs.length - 1] : undefined,
408
- };
409
- }
410
- tryParseJson(value) {
411
- if (!value)
412
- return undefined;
413
- try {
414
- return JSON.parse(value);
415
- }
416
- catch {
417
- return undefined;
418
- }
419
- }
420
- /** @see ThreadService.parseLogs — 동일 역할, ACP 파싱 로직 동기화 필요 */
421
- parseLogs(logsJson, logLimit, providerId) {
422
- if (!logsJson)
423
- return [];
424
- let rawLogs;
425
- try {
426
- rawLogs = JSON.parse(logsJson);
427
- }
428
- catch {
429
- return [];
430
- }
431
- if (!Array.isArray(rawLogs))
432
- return [];
433
- const entries = [];
434
- for (const log of rawLogs) {
435
- if (log.level === 'stdout') {
436
- try {
437
- const parsed = (0, sdk_1.parseStdoutEvent)(log.timestamp, log.message, providerId);
438
- if (parsed.length > 0) {
439
- entries.push(...parsed);
440
- continue;
441
- }
442
- // Parsed successfully but no displayable entries (e.g. thinking-only assistant message).
443
- // Skip fallback if the raw message is a known Claude stream-json event type.
444
- const trimmed = log.message.trimStart();
445
- if (trimmed.startsWith('{')) {
446
- try {
447
- const obj = JSON.parse(trimmed);
448
- if (obj.type === 'assistant' ||
449
- obj.type === 'user' ||
450
- obj.type === 'system' ||
451
- obj.type === 'result' ||
452
- obj.type === 'rate_limit_event') {
453
- continue;
454
- }
455
- }
456
- catch { /* not valid JSON, fall through to text fallback */ }
457
- }
458
- }
459
- catch { /* adapter parse failure — fallback to text */ }
460
- entries.push({ timestamp: log.timestamp, type: 'text', content: log.message });
461
- }
462
- else if (log.level === 'info') {
463
- const infoEntries = (0, sdk_1.defaultParseEvent)(log.message, log.timestamp);
464
- if (infoEntries.length > 0) {
465
- entries.push(...infoEntries);
466
- }
467
- else if (log.message) {
468
- entries.push({ timestamp: log.timestamp, type: 'info', content: log.message });
469
- }
470
- }
471
- }
472
- // Pipeline: merge consecutive text → deduplicate tool_use → truncate → logLimit slice
473
- const merged = this.mergeConsecutiveText(entries);
474
- const deduped = this.deduplicateToolUse(merged);
475
- const truncated = deduped.map((e) => this.truncateEntry(e));
476
- // Return the latest N entries (trim from the beginning)
477
- if (truncated.length > logLimit) {
478
- return truncated.slice(truncated.length - logLimit);
479
- }
480
- return truncated;
481
- }
482
- aggregateToolUsage(logsJson, providerId) {
483
- const entries = this.parseLogs(logsJson, task_constants_js_1.MAX_TASK_LOG_LIMIT, providerId);
484
- const counts = new Map();
485
- for (const entry of entries) {
486
- if (entry.type === 'tool_use' && entry.toolName) {
487
- counts.set(entry.toolName, (counts.get(entry.toolName) || 0) + 1);
488
- }
489
- }
490
- return Object.fromEntries(counts);
491
- }
492
- /** Extract full provider string from metadata JSON (e.g. "cli/claude"). */
493
- extractProviderFromMeta(metadata) {
494
- if (!metadata)
495
- return undefined;
496
- try {
497
- const meta = JSON.parse(metadata);
498
- return typeof meta.provider === 'string' ? meta.provider : undefined;
499
- }
500
- catch {
501
- return undefined;
502
- }
503
- }
504
- /** Extract adapter key (e.g. "opencode") from "cli/opencode" style provider string. */
505
- extractAdapterKey(row) {
506
- if (row.metadata) {
507
- try {
508
- const meta = JSON.parse(row.metadata);
509
- const provider = meta.provider;
510
- if (provider) {
511
- const slash = provider.lastIndexOf('/');
512
- return slash >= 0 ? provider.slice(slash + 1) : provider;
513
- }
514
- }
515
- catch { /* ignore */ }
516
- }
517
- if (row.coding_agent_command) {
518
- return row.coding_agent_command.split(/\s/)[0] || undefined;
519
- }
520
- return undefined;
521
- }
522
- /** Merge consecutive type=text entries into one (fixes Gemini delta streaming fragmentation) */
523
- mergeConsecutiveText(entries) {
524
- const merged = [];
525
- for (const entry of entries) {
526
- const prev = merged[merged.length - 1];
527
- if (prev && prev.type === 'text' && entry.type === 'text') {
528
- prev.content = (prev.content || '') + (entry.content || '');
529
- }
530
- else {
531
- merged.push({ ...entry });
532
- }
533
- }
534
- return merged;
535
- }
536
- /**
537
- * When ACP streaming sends an initial tool_call with empty input followed by
538
- * a tool_call_update with refined input, both arrive as type=tool_use with
539
- * the same toolUseId. Keep only the latest (most complete) one per toolUseId.
540
- */
541
- deduplicateToolUse(entries) {
542
- const latestIdx = new Map();
543
- for (let i = 0; i < entries.length; i++) {
544
- const e = entries[i];
545
- if (e.type === 'tool_use' && e.toolUseId) {
546
- latestIdx.set(e.toolUseId, i);
547
- }
548
- }
549
- return entries.filter((e, i) => {
550
- if (e.type === 'tool_use' && e.toolUseId) {
551
- return latestIdx.get(e.toolUseId) === i;
552
- }
553
- return true;
554
- });
555
- }
556
- /** Apply truncation to a TaskLogEntry (task.service only) */
557
- truncateEntry(entry) {
558
- const result = { ...entry };
559
- if (result.content)
560
- result.content = this.truncate(result.content, MAX_LOG_ENTRY_FIELD_LEN);
561
- if (result.toolInput)
562
- result.toolInput = this.truncate(result.toolInput, MAX_LOG_ENTRY_FIELD_LEN);
563
- if (result.resultPreview)
564
- result.resultPreview = this.truncate(result.resultPreview, MAX_LOG_ENTRY_FIELD_LEN);
565
- if (result.errorMessage)
566
- result.errorMessage = this.truncate(result.errorMessage, MAX_LOG_ENTRY_FIELD_LEN);
567
- return result;
568
- }
569
- // --- TSK.GET logs enrichment ---
570
- enrichMessagesWithLogs(data, workspaceId) {
571
- const threadId = data.threadId;
572
- // Collect matching task rows from ALL available dbs
573
- let taskRows = this.taskRepo.findTasksByThread(threadId, workspaceId);
574
- // Fallback: if no matches by thread name, try matching by prompt text overlap
575
- if (taskRows.length === 0 && data.messages.length > 0) {
576
- const firstUserMsg = data.messages.find((m) => !m.isAssistant);
577
- if (firstUserMsg) {
578
- const hint = firstUserMsg.text.slice(0, 60).replace(/[%_]/g, '');
579
- if (hint.length >= 10) {
580
- taskRows = this.taskRepo.findTasksByPromptHint(hint, workspaceId);
581
- }
582
- }
583
- }
584
- // Sort combined rows by started_at ascending
585
- taskRows.sort((a, b) => new Date(a.started_at).getTime() - new Date(b.started_at).getTime());
586
- if (taskRows.length === 0) {
587
- return data.messages;
588
- }
589
- // Build time windows: each user message starts a "turn", the next user message ends it
590
- const turns = this.buildTurns(data.messages);
591
- const enriched = [...data.messages];
592
- for (let i = 0; i < turns.length; i++) {
593
- const turn = turns[i];
594
- if (turn.assistantIdx === -1)
595
- continue;
596
- // Find tasks whose started_at is within [turn.start, turn.end)
597
- const matchingTasks = taskRows.filter((t) => {
598
- const startMs = new Date(t.started_at).getTime();
599
- return startMs >= turn.startMs && startMs < turn.endMs;
600
- });
601
- if (matchingTasks.length === 0)
602
- continue;
603
- // Merge logs from all matching tasks
604
- const allLogs = [];
605
- for (const task of matchingTasks) {
606
- const parsed = this.parseLogs(task.logs, 100, this.extractAdapterKey(task));
607
- allLogs.push(...parsed);
608
- }
609
- // Sort by timestamp
610
- allLogs.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
611
- enriched[turn.assistantIdx] = {
612
- ...enriched[turn.assistantIdx],
613
- logs: allLogs,
614
- };
615
- }
616
- return enriched;
617
- }
618
- buildTurns(messages) {
619
- const turns = [];
620
- for (let i = 0; i < messages.length; i++) {
621
- const msg = messages[i];
622
- if (msg.isAssistant)
623
- continue;
624
- // This is a user message — find the assistant message that follows
625
- let assistantIdx = -1;
626
- for (let j = i + 1; j < messages.length; j++) {
627
- if (messages[j].isAssistant) {
628
- assistantIdx = j;
629
- break;
630
- }
631
- }
632
- const startMs = new Date(msg.timestamp).getTime();
633
- // End at the next user message, or far future
634
- let endMs = Number.MAX_SAFE_INTEGER;
635
- for (let j = i + 1; j < messages.length; j++) {
636
- if (!messages[j].isAssistant) {
637
- endMs = new Date(messages[j].timestamp).getTime();
638
- break;
639
- }
640
- }
641
- turns.push({ startMs, endMs, assistantIdx });
642
- }
643
- return turns;
644
- }
645
- // --- Private helpers ---
646
- resolveTasksPath(project) {
647
- const base = project || (0, workspace_context_store_js_1.getWorkspacePath)();
648
- const resolved = (0, path_1.resolve)(base);
649
- // Prevent path traversal
650
- if (project && project.includes('..')) {
651
- throw new common_1.BadRequestException('Invalid project path');
652
- }
653
- return (0, path_1.join)(resolved, '.crewx', 'conversations');
654
- }
655
- validateTaskId(taskId) {
656
- // Only allow safe filename characters
657
- if (!/^[a-zA-Z0-9._-]+$/.test(taskId)) {
658
- throw new common_1.BadRequestException('Invalid taskId format');
659
- }
660
- }
661
- async listJsonFiles(dir) {
662
- try {
663
- const entries = await (0, promises_1.readdir)(dir);
664
- return entries.filter((f) => f.endsWith('.json'));
665
- }
666
- catch {
667
- return [];
668
- }
669
- }
670
- async readTaskFile(filePath) {
671
- const content = await (0, promises_1.readFile)(filePath, 'utf-8');
672
- const data = JSON.parse(content);
673
- // Fallback: derive threadId from filename if not present
674
- if (!data.threadId) {
675
- data.threadId = (0, path_1.basename)(filePath, '.json');
676
- }
677
- return data;
678
- }
679
- toSummary(data) {
680
- const agents = new Set();
681
- const users = new Set();
682
- for (const msg of data.messages) {
683
- if (msg.isAssistant && msg.metadata?.agent_id) {
684
- agents.add(msg.metadata.agent_id);
685
- }
686
- if (!msg.isAssistant) {
687
- users.add(msg.userId);
688
- }
689
- }
690
- const firstMsg = data.messages[0]?.text || '';
691
- const lastMsg = data.messages[data.messages.length - 1]?.text || '';
692
- return {
693
- taskId: data.threadId,
694
- platform: data.platform,
695
- messageCount: data.metadata?.messageCount ?? data.messages.length,
696
- agents: Array.from(agents),
697
- users: Array.from(users),
698
- firstMessage: this.truncate(firstMsg, 100),
699
- lastMessage: this.truncate(lastMsg, 100),
700
- createdAt: data.metadata?.createdAt || data.messages[0]?.timestamp || '',
701
- updatedAt: data.metadata?.updatedAt || data.messages[data.messages.length - 1]?.timestamp || '',
702
- };
703
- }
704
- truncate(text, maxLen) {
705
- if (text.length <= maxLen)
706
- return text;
707
- return text.slice(0, maxLen) + '...';
708
- }
709
- normalizeLimit(limit, fallback, max) {
710
- const num = Number(limit);
711
- if (!Number.isFinite(num) || num <= 0)
712
- return fallback;
713
- return Math.min(Math.floor(num), max);
714
- }
715
- normalizeOffset(offset) {
716
- const num = Number(offset);
717
- if (!Number.isFinite(num) || num < 0)
718
- return 0;
719
- return Math.floor(num);
720
- }
721
- highlightMatch(text, query) {
722
- const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
723
- const regex = new RegExp(`(${escaped})`, 'gi');
724
- return text.replace(regex, '**$1**');
725
- }
726
- // TSK006: Get agent list from crewx.yaml
727
- getAgents() {
728
- const configPath = this.resolveCrewxConfigPath();
729
- if (!configPath || !(0, fs_1.existsSync)(configPath)) {
730
- return { agents: [] };
731
- }
732
- try {
733
- const content = (0, fs_1.readFileSync)(configPath, 'utf-8');
734
- const config = (0, js_yaml_1.load)(content);
735
- const rawAgents = config?.agents;
736
- if (!Array.isArray(rawAgents))
737
- return { agents: [] };
738
- const agents = rawAgents.map((a) => ({
739
- id: String(a.id || ''),
740
- name: String(a.name || a.id || ''),
741
- role: String(a.role || ''),
742
- team: String(a.team || ''),
743
- description: String(a.description || ''),
744
- }));
745
- return { agents };
746
- }
747
- catch {
748
- return { agents: [] };
749
- }
750
- }
751
- resolveCrewxConfigPath() {
752
- // Check CREWX_CONFIG env var first
753
- const envPath = process.env.CREWX_CONFIG;
754
- if (envPath && (0, fs_1.existsSync)(envPath))
755
- return envPath;
756
- // Fall back to project root crewx.yaml
757
- const base = (0, workspace_context_store_js_1.getWorkspacePath)();
758
- const candidates = [
759
- (0, path_1.join)(base, 'crewx.yaml'),
760
- (0, path_1.join)(base, 'crewx.yml'),
761
- ];
762
- for (const p of candidates) {
763
- if ((0, fs_1.existsSync)(p))
764
- return p;
765
- }
766
- return null;
767
- }
768
- };
769
- exports.TaskService = TaskService;
770
- exports.TaskService = TaskService = TaskService_1 = __decorate([
771
- (0, common_1.Injectable)(),
772
- __metadata("design:paramtypes", [task_repository_js_1.TaskRepository,
773
- crewx_pool_service_js_1.CrewxPoolService,
774
- project_repository_js_1.ProjectRepository])
775
- ], TaskService);