devchain-cli 0.2.2 → 0.3.2

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 (151) hide show
  1. package/README.md +2 -0
  2. package/dist/drizzle/0018_whole_zodiak.sql +43 -0
  3. package/dist/drizzle/0019_flat_avengers.sql +3 -0
  4. package/dist/drizzle/0020_statuses_mcp_hidden.sql +1 -0
  5. package/dist/drizzle/meta/0018_snapshot.json +2920 -0
  6. package/dist/drizzle/meta/0019_snapshot.json +2943 -0
  7. package/dist/drizzle/meta/0020_snapshot.json +2951 -0
  8. package/dist/drizzle/meta/_journal.json +21 -0
  9. package/dist/server/app.module.js +11 -1
  10. package/dist/server/app.module.js.map +1 -1
  11. package/dist/server/common/config/env.config.d.ts +1 -0
  12. package/dist/server/common/config/env.config.js +4 -0
  13. package/dist/server/common/config/env.config.js.map +1 -1
  14. package/dist/server/common/filters/http-exception.filter.js +24 -1
  15. package/dist/server/common/filters/http-exception.filter.js.map +1 -1
  16. package/dist/server/common/logging/logger.js +4 -3
  17. package/dist/server/common/logging/logger.js.map +1 -1
  18. package/dist/server/main.js +0 -3
  19. package/dist/server/main.js.map +1 -1
  20. package/dist/server/modules/agents/agents.module.js +2 -1
  21. package/dist/server/modules/agents/agents.module.js.map +1 -1
  22. package/dist/server/modules/agents/controllers/agents.controller.d.ts +17 -2
  23. package/dist/server/modules/agents/controllers/agents.controller.js +84 -3
  24. package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -1
  25. package/dist/server/modules/chat/dtos/chat.dto.d.ts +18 -18
  26. package/dist/server/modules/chat/dtos/chat.dto.js +1 -1
  27. package/dist/server/modules/chat/dtos/chat.dto.js.map +1 -1
  28. package/dist/server/modules/chat/services/invite-template.util.js +1 -1
  29. package/dist/server/modules/chat/services/invite-template.util.js.map +1 -1
  30. package/dist/server/modules/events/catalog/index.d.ts +40 -0
  31. package/dist/server/modules/events/catalog/index.js +2 -0
  32. package/dist/server/modules/events/catalog/index.js.map +1 -1
  33. package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +45 -0
  34. package/dist/server/modules/events/catalog/terminal.watcher.triggered.js +22 -0
  35. package/dist/server/modules/events/catalog/terminal.watcher.triggered.js.map +1 -0
  36. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.d.ts +1 -0
  37. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js +30 -12
  38. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js.map +1 -1
  39. package/dist/server/modules/mcp/constants.d.ts +2 -0
  40. package/dist/server/modules/mcp/constants.js +11 -2
  41. package/dist/server/modules/mcp/constants.js.map +1 -1
  42. package/dist/server/modules/mcp/controllers/mcp-http.controller.js +78 -81
  43. package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
  44. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +78 -77
  45. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
  46. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +125 -97
  47. package/dist/server/modules/mcp/dtos/mcp.dto.js +23 -26
  48. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  49. package/dist/server/modules/mcp/services/instructions-resolver.d.ts +3 -0
  50. package/dist/server/modules/mcp/services/instructions-resolver.js +83 -2
  51. package/dist/server/modules/mcp/services/instructions-resolver.js.map +1 -1
  52. package/dist/server/modules/mcp/services/mcp.service.d.ts +3 -2
  53. package/dist/server/modules/mcp/services/mcp.service.js +549 -263
  54. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  55. package/dist/server/modules/projects/controllers/projects.controller.d.ts +43 -0
  56. package/dist/server/modules/projects/projects.module.js +2 -1
  57. package/dist/server/modules/projects/projects.module.js.map +1 -1
  58. package/dist/server/modules/projects/services/projects.service.d.ts +48 -1
  59. package/dist/server/modules/projects/services/projects.service.js +341 -19
  60. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  61. package/dist/server/modules/prompts/controllers/prompts.controller.d.ts +1 -1
  62. package/dist/server/modules/prompts/controllers/prompts.controller.js +26 -4
  63. package/dist/server/modules/prompts/controllers/prompts.controller.js.map +1 -1
  64. package/dist/server/modules/sessions/utils/template-renderer.js +1 -0
  65. package/dist/server/modules/sessions/utils/template-renderer.js.map +1 -1
  66. package/dist/server/modules/statuses/controllers/statuses.controller.js +2 -0
  67. package/dist/server/modules/statuses/controllers/statuses.controller.js.map +1 -1
  68. package/dist/server/modules/storage/db/schema.d.ts +613 -0
  69. package/dist/server/modules/storage/db/schema.js +50 -1
  70. package/dist/server/modules/storage/db/schema.js.map +1 -1
  71. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +40 -2
  72. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  73. package/dist/server/modules/storage/local/local-storage.service.d.ts +18 -3
  74. package/dist/server/modules/storage/local/local-storage.service.js +407 -11
  75. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  76. package/dist/server/modules/storage/models/domain.models.d.ts +59 -1
  77. package/dist/server/modules/subscribers/actions/action.interface.d.ts +67 -0
  78. package/dist/server/modules/subscribers/actions/action.interface.js +3 -0
  79. package/dist/server/modules/subscribers/actions/action.interface.js.map +1 -0
  80. package/dist/server/modules/subscribers/actions/actions.registry.d.ts +7 -0
  81. package/dist/server/modules/subscribers/actions/actions.registry.js +37 -0
  82. package/dist/server/modules/subscribers/actions/actions.registry.js.map +1 -0
  83. package/dist/server/modules/subscribers/actions/restart-agent.action.d.ts +8 -0
  84. package/dist/server/modules/subscribers/actions/restart-agent.action.js +119 -0
  85. package/dist/server/modules/subscribers/actions/restart-agent.action.js.map +1 -0
  86. package/dist/server/modules/subscribers/actions/send-message.action.d.ts +2 -0
  87. package/dist/server/modules/subscribers/actions/send-message.action.js +83 -0
  88. package/dist/server/modules/subscribers/actions/send-message.action.js.map +1 -0
  89. package/dist/server/modules/subscribers/controllers/actions.controller.d.ts +6 -0
  90. package/dist/server/modules/subscribers/controllers/actions.controller.js +51 -0
  91. package/dist/server/modules/subscribers/controllers/actions.controller.js.map +1 -0
  92. package/dist/server/modules/subscribers/controllers/subscribers.controller.d.ts +17 -0
  93. package/dist/server/modules/subscribers/controllers/subscribers.controller.js +178 -0
  94. package/dist/server/modules/subscribers/controllers/subscribers.controller.js.map +1 -0
  95. package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +251 -0
  96. package/dist/server/modules/subscribers/dtos/subscriber.dto.js +68 -0
  97. package/dist/server/modules/subscribers/dtos/subscriber.dto.js.map +1 -0
  98. package/dist/server/modules/subscribers/events/event-fields-catalog.d.ts +19 -0
  99. package/dist/server/modules/subscribers/events/event-fields-catalog.js +98 -0
  100. package/dist/server/modules/subscribers/events/event-fields-catalog.js.map +1 -0
  101. package/dist/server/modules/subscribers/services/automation-scheduler.service.d.ts +49 -0
  102. package/dist/server/modules/subscribers/services/automation-scheduler.service.js +300 -0
  103. package/dist/server/modules/subscribers/services/automation-scheduler.service.js.map +1 -0
  104. package/dist/server/modules/subscribers/services/subscriber-executor.service.d.ts +77 -0
  105. package/dist/server/modules/subscribers/services/subscriber-executor.service.js +576 -0
  106. package/dist/server/modules/subscribers/services/subscriber-executor.service.js.map +1 -0
  107. package/dist/server/modules/subscribers/services/subscribers.service.d.ts +14 -0
  108. package/dist/server/modules/subscribers/services/subscribers.service.js +70 -0
  109. package/dist/server/modules/subscribers/services/subscribers.service.js.map +1 -0
  110. package/dist/server/modules/subscribers/subscribers.module.d.ts +2 -0
  111. package/dist/server/modules/subscribers/subscribers.module.js +36 -0
  112. package/dist/server/modules/subscribers/subscribers.module.js.map +1 -0
  113. package/dist/server/modules/terminal/services/tmux.service.js +9 -6
  114. package/dist/server/modules/terminal/services/tmux.service.js.map +1 -1
  115. package/dist/server/modules/watchers/controllers/watchers.controller.d.ts +16 -0
  116. package/dist/server/modules/watchers/controllers/watchers.controller.js +180 -0
  117. package/dist/server/modules/watchers/controllers/watchers.controller.js.map +1 -0
  118. package/dist/server/modules/watchers/dtos/watcher.dto.d.ts +206 -0
  119. package/dist/server/modules/watchers/dtos/watcher.dto.js +54 -0
  120. package/dist/server/modules/watchers/dtos/watcher.dto.js.map +1 -0
  121. package/dist/server/modules/watchers/services/watcher-runner.service.d.ts +68 -0
  122. package/dist/server/modules/watchers/services/watcher-runner.service.js +477 -0
  123. package/dist/server/modules/watchers/services/watcher-runner.service.js.map +1 -0
  124. package/dist/server/modules/watchers/services/watchers.service.d.ts +29 -0
  125. package/dist/server/modules/watchers/services/watchers.service.js +98 -0
  126. package/dist/server/modules/watchers/services/watchers.service.js.map +1 -0
  127. package/dist/server/modules/watchers/watchers.module.d.ts +2 -0
  128. package/dist/server/modules/watchers/watchers.module.js +34 -0
  129. package/dist/server/modules/watchers/watchers.module.js.map +1 -0
  130. package/dist/server/templates/claude-codex-advanced-swe.json +130 -0
  131. package/dist/server/templates/claude-codex-advanced.json +377 -0
  132. package/dist/server/templates/claude-opus.json +241 -34
  133. package/dist/server/templates/simple-codex.json +138 -42
  134. package/dist/server/test-setup-node.d.ts +1 -0
  135. package/dist/server/test-setup-node.js +8 -0
  136. package/dist/server/test-setup-node.js.map +1 -0
  137. package/dist/server/test-setup.js +2 -0
  138. package/dist/server/test-setup.js.map +1 -1
  139. package/dist/server/tsconfig.tsbuildinfo +1 -1
  140. package/dist/server/ui/assets/index-C9GXCjnF.js +700 -0
  141. package/dist/server/ui/assets/index-o0FbZg-1.css +32 -0
  142. package/dist/server/ui/index.html +2 -2
  143. package/dist/templates/claude-codex-advanced-swe.json +130 -0
  144. package/dist/templates/claude-codex-advanced.json +377 -0
  145. package/dist/templates/claude-opus.json +241 -34
  146. package/dist/templates/simple-codex.json +138 -42
  147. package/package.json +47 -27
  148. package/dist/server/templates/codex-claude.json +0 -178
  149. package/dist/server/ui/assets/index-CbYIbCQV.css +0 -32
  150. package/dist/server/ui/assets/index-sfYpjMjv.js +0 -641
  151. package/dist/templates/codex-claude.json +0 -178
@@ -17,6 +17,8 @@ const common_1 = require("@nestjs/common");
17
17
  const storage_interface_1 = require("../../storage/interfaces/storage.interface");
18
18
  const sessions_service_1 = require("../../sessions/services/sessions.service");
19
19
  const settings_service_1 = require("../../settings/services/settings.service");
20
+ const watchers_service_1 = require("../../watchers/services/watchers.service");
21
+ const watcher_runner_service_1 = require("../../watchers/services/watcher-runner.service");
20
22
  const logger_1 = require("../../../common/logging/logger");
21
23
  const error_types_1 = require("../../../common/errors/error-types");
22
24
  const path_1 = require("path");
@@ -65,6 +67,7 @@ const ExportSchema = zod_1.z
65
67
  label: zod_1.z.string().min(1),
66
68
  color: zod_1.z.string().min(1),
67
69
  position: zod_1.z.number().int(),
70
+ mcpHidden: zod_1.z.boolean().optional().default(false),
68
71
  }))
69
72
  .optional()
70
73
  .default([]),
@@ -79,13 +82,66 @@ const ExportSchema = zod_1.z
79
82
  epicAssignedTemplate: zod_1.z.string().optional(),
80
83
  })
81
84
  .optional(),
85
+ watchers: zod_1.z
86
+ .array(zod_1.z.object({
87
+ id: zod_1.z.string().uuid().optional(),
88
+ name: zod_1.z.string().min(1),
89
+ description: zod_1.z.string().nullable().optional(),
90
+ enabled: zod_1.z.boolean(),
91
+ scope: zod_1.z.enum(['all', 'agent', 'profile', 'provider']),
92
+ scopeFilterName: zod_1.z.string().nullable().optional(),
93
+ pollIntervalMs: zod_1.z.number().int(),
94
+ viewportLines: zod_1.z.number().int(),
95
+ condition: zod_1.z.object({
96
+ type: zod_1.z.enum(['contains', 'regex', 'not_contains']),
97
+ pattern: zod_1.z.string(),
98
+ flags: zod_1.z.string().optional(),
99
+ }),
100
+ cooldownMs: zod_1.z.number().int(),
101
+ cooldownMode: zod_1.z.enum(['time', 'until_clear']),
102
+ eventName: zod_1.z.string(),
103
+ }))
104
+ .optional()
105
+ .default([]),
106
+ subscribers: zod_1.z
107
+ .array(zod_1.z.object({
108
+ id: zod_1.z.string().uuid().optional(),
109
+ name: zod_1.z.string().min(1),
110
+ description: zod_1.z.string().nullable().optional(),
111
+ enabled: zod_1.z.boolean(),
112
+ eventName: zod_1.z.string(),
113
+ eventFilter: zod_1.z
114
+ .object({
115
+ field: zod_1.z.string(),
116
+ operator: zod_1.z.enum(['equals', 'contains', 'regex']),
117
+ value: zod_1.z.string(),
118
+ })
119
+ .nullable()
120
+ .optional(),
121
+ actionType: zod_1.z.string(),
122
+ actionInputs: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
123
+ source: zod_1.z.enum(['event_field', 'custom']),
124
+ eventField: zod_1.z.string().optional(),
125
+ customValue: zod_1.z.string().optional(),
126
+ })),
127
+ delayMs: zod_1.z.number().int(),
128
+ cooldownMs: zod_1.z.number().int(),
129
+ retryOnError: zod_1.z.boolean(),
130
+ groupName: zod_1.z.string().nullable().optional(),
131
+ position: zod_1.z.number().int().optional().default(0),
132
+ priority: zod_1.z.number().int().optional().default(0),
133
+ }))
134
+ .optional()
135
+ .default([]),
82
136
  })
83
137
  .strict();
84
138
  let ProjectsService = class ProjectsService {
85
- constructor(storage, sessions, settings) {
139
+ constructor(storage, sessions, settings, watchersService, watcherRunner) {
86
140
  this.storage = storage;
87
141
  this.sessions = sessions;
88
142
  this.settings = settings;
143
+ this.watchersService = watchersService;
144
+ this.watcherRunner = watcherRunner;
89
145
  }
90
146
  findTemplatesDirectory() {
91
147
  const env = (0, env_config_1.getEnvConfig)();
@@ -276,6 +332,7 @@ let ProjectsService = class ProjectsService {
276
332
  label: s.label,
277
333
  color: s.color,
278
334
  position: s.position,
335
+ mcpHidden: s.mcpHidden,
279
336
  })),
280
337
  initialPrompt: payload.initialPrompt,
281
338
  };
@@ -285,6 +342,18 @@ let ProjectsService = class ProjectsService {
285
342
  rootPath: input.rootPath,
286
343
  isTemplate: false,
287
344
  }, templatePayload);
345
+ const agentNameToNewId = new Map();
346
+ for (const a of templatePayload.agents) {
347
+ if (a.id && result.mappings.agentIdMap[a.id]) {
348
+ agentNameToNewId.set(a.name.trim().toLowerCase(), result.mappings.agentIdMap[a.id]);
349
+ }
350
+ }
351
+ const profileNameToNewId = new Map();
352
+ for (const prof of templatePayload.profiles) {
353
+ if (prof.id && result.mappings.profileIdMap[prof.id]) {
354
+ profileNameToNewId.set(prof.name.trim().toLowerCase(), result.mappings.profileIdMap[prof.id]);
355
+ }
356
+ }
288
357
  let initialPromptSet = false;
289
358
  if (payload.initialPrompt) {
290
359
  let targetPromptId = null;
@@ -331,9 +400,11 @@ let ProjectsService = class ProjectsService {
331
400
  })
332
401
  .filter((id) => !!id);
333
402
  if (autoCleanStatusIds.length > 0) {
403
+ const currentSettings = this.settings.getSettings();
404
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
334
405
  await this.settings.updateSettings({
335
406
  autoClean: {
336
- statusIds: { [result.project.id]: autoCleanStatusIds },
407
+ statusIds: { ...existingAutoClean, [result.project.id]: autoCleanStatusIds },
337
408
  },
338
409
  });
339
410
  logger.info({ projectId: result.project.id, autoCleanStatusIds }, 'Applied autoClean statuses from projectSettings');
@@ -344,9 +415,11 @@ let ProjectsService = class ProjectsService {
344
415
  if (archiveTemplateStatus?.id) {
345
416
  const archiveNewId = result.mappings.statusIdMap[archiveTemplateStatus.id];
346
417
  if (archiveNewId) {
418
+ const currentSettings = this.settings.getSettings();
419
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
347
420
  await this.settings.updateSettings({
348
421
  autoClean: {
349
- statusIds: { [result.project.id]: [archiveNewId] },
422
+ statusIds: { ...existingAutoClean, [result.project.id]: [archiveNewId] },
350
423
  },
351
424
  });
352
425
  logger.info({ projectId: result.project.id, archiveStatusId: archiveNewId }, 'Auto-configured Archive status for auto-clean (fallback)');
@@ -367,19 +440,106 @@ let ProjectsService = class ProjectsService {
367
440
  if (archiveTemplateStatus?.id) {
368
441
  const archiveNewId = result.mappings.statusIdMap[archiveTemplateStatus.id];
369
442
  if (archiveNewId) {
443
+ const currentSettings = this.settings.getSettings();
444
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
370
445
  await this.settings.updateSettings({
371
446
  autoClean: {
372
- statusIds: { [result.project.id]: [archiveNewId] },
447
+ statusIds: { ...existingAutoClean, [result.project.id]: [archiveNewId] },
373
448
  },
374
449
  });
375
450
  logger.info({ projectId: result.project.id, archiveStatusId: archiveNewId }, 'Auto-configured Archive status for auto-clean');
376
451
  }
377
452
  }
378
453
  }
454
+ let watchersCreated = 0;
455
+ for (const w of payload.watchers) {
456
+ let scopeFilterId = null;
457
+ if (w.scopeFilterName && w.scope !== 'all') {
458
+ const scopeFilterNameLower = w.scopeFilterName.trim().toLowerCase();
459
+ switch (w.scope) {
460
+ case 'agent': {
461
+ scopeFilterId = agentNameToNewId.get(scopeFilterNameLower) ?? null;
462
+ break;
463
+ }
464
+ case 'profile': {
465
+ scopeFilterId = profileNameToNewId.get(scopeFilterNameLower) ?? null;
466
+ break;
467
+ }
468
+ case 'provider': {
469
+ const provider = providers.items.find((p) => p.name.trim().toLowerCase() === scopeFilterNameLower);
470
+ scopeFilterId = provider?.id ?? null;
471
+ break;
472
+ }
473
+ }
474
+ if (!scopeFilterId) {
475
+ logger.warn({
476
+ projectId: result.project.id,
477
+ watcherName: w.name,
478
+ scope: w.scope,
479
+ scopeFilterName: w.scopeFilterName,
480
+ }, 'Could not resolve scope filter for template watcher, setting scope to "all"');
481
+ }
482
+ }
483
+ try {
484
+ const created = await this.storage.createWatcher({
485
+ projectId: result.project.id,
486
+ name: w.name,
487
+ description: w.description ?? null,
488
+ enabled: w.enabled,
489
+ scope: scopeFilterId ? w.scope : 'all',
490
+ scopeFilterId,
491
+ pollIntervalMs: w.pollIntervalMs,
492
+ viewportLines: w.viewportLines,
493
+ condition: w.condition,
494
+ cooldownMs: w.cooldownMs,
495
+ cooldownMode: w.cooldownMode,
496
+ eventName: w.eventName,
497
+ });
498
+ watchersCreated++;
499
+ if (created.enabled) {
500
+ await this.watcherRunner.startWatcher(created);
501
+ }
502
+ }
503
+ catch (error) {
504
+ if (error instanceof Error &&
505
+ error.message.includes('UNIQUE constraint failed') &&
506
+ error.message.includes('event_name')) {
507
+ throw new common_1.BadRequestException({
508
+ message: `Duplicate watcher eventName: "${w.eventName}"`,
509
+ hint: 'Each watcher must have a unique eventName within the project.',
510
+ });
511
+ }
512
+ throw error;
513
+ }
514
+ }
515
+ let subscribersCreated = 0;
516
+ for (const s of payload.subscribers) {
517
+ await this.storage.createSubscriber({
518
+ projectId: result.project.id,
519
+ name: s.name,
520
+ description: s.description ?? null,
521
+ enabled: s.enabled,
522
+ eventName: s.eventName,
523
+ eventFilter: s.eventFilter ?? null,
524
+ actionType: s.actionType,
525
+ actionInputs: s.actionInputs,
526
+ delayMs: s.delayMs,
527
+ cooldownMs: s.cooldownMs,
528
+ retryOnError: s.retryOnError,
529
+ groupName: s.groupName ?? null,
530
+ position: s.position ?? 0,
531
+ priority: s.priority ?? 0,
532
+ });
533
+ subscribersCreated++;
534
+ }
379
535
  return {
380
536
  success: true,
381
537
  project: result.project,
382
- imported: result.imported,
538
+ imported: {
539
+ ...result.imported,
540
+ watchers: watchersCreated,
541
+ subscribers: subscribersCreated,
542
+ },
383
543
  mappings: result.mappings,
384
544
  initialPromptSet,
385
545
  message: 'Project created from template successfully.',
@@ -387,13 +547,15 @@ let ProjectsService = class ProjectsService {
387
547
  }
388
548
  async exportProject(projectId) {
389
549
  logger.info({ projectId }, 'exportProject');
390
- const [promptsRes, profilesRes, agentsRes, statusesRes, initialPrompt, settings] = await Promise.all([
391
- this.storage.listPrompts(projectId, { limit: 1000, offset: 0 }),
550
+ const [promptsRes, profilesRes, agentsRes, statusesRes, initialPrompt, settings, watchersRes, subscribersRes,] = await Promise.all([
551
+ this.storage.listPrompts({ projectId, limit: 1000, offset: 0 }),
392
552
  this.storage.listAgentProfiles({ projectId, limit: 1000, offset: 0 }),
393
553
  this.storage.listAgents(projectId, { limit: 1000, offset: 0 }),
394
554
  this.storage.listStatuses(projectId, { limit: 1000, offset: 0 }),
395
555
  this.storage.getInitialSessionPrompt(projectId),
396
556
  Promise.resolve(this.settings.getSettings()),
557
+ this.storage.listWatchers(projectId),
558
+ this.storage.listSubscribers(projectId),
397
559
  ]);
398
560
  const secretKeys = new Set([
399
561
  'apikey',
@@ -441,7 +603,8 @@ let ProjectsService = class ProjectsService {
441
603
  return null;
442
604
  return options;
443
605
  };
444
- const prompts = promptsRes.items.map((p) => ({
606
+ const fullPrompts = await Promise.all(promptsRes.items.map((p) => this.storage.getPrompt(p.id)));
607
+ const prompts = fullPrompts.map((p) => ({
445
608
  id: p.id,
446
609
  title: p.title,
447
610
  content: p.content,
@@ -471,6 +634,7 @@ let ProjectsService = class ProjectsService {
471
634
  label: s.label,
472
635
  color: s.color,
473
636
  position: s.position,
637
+ mcpHidden: s.mcpHidden,
474
638
  }));
475
639
  const projectSettings = {};
476
640
  if (initialPrompt?.title) {
@@ -490,6 +654,63 @@ let ProjectsService = class ProjectsService {
490
654
  if (epicAssignedTemplate) {
491
655
  projectSettings.epicAssignedTemplate = epicAssignedTemplate;
492
656
  }
657
+ const watchers = await Promise.all(watchersRes.map(async (w) => {
658
+ let scopeFilterName = null;
659
+ if (w.scopeFilterId) {
660
+ switch (w.scope) {
661
+ case 'agent': {
662
+ const agent = agentsRes.items.find((a) => a.id === w.scopeFilterId);
663
+ scopeFilterName = agent?.name ?? null;
664
+ break;
665
+ }
666
+ case 'profile': {
667
+ const profile = profilesRes.items.find((p) => p.id === w.scopeFilterId);
668
+ scopeFilterName = profile?.name ?? null;
669
+ break;
670
+ }
671
+ case 'provider': {
672
+ try {
673
+ const provider = await this.storage.getProvider(w.scopeFilterId);
674
+ scopeFilterName = provider?.name ?? null;
675
+ }
676
+ catch {
677
+ scopeFilterName = null;
678
+ }
679
+ break;
680
+ }
681
+ }
682
+ }
683
+ return {
684
+ id: w.id,
685
+ name: w.name,
686
+ description: w.description,
687
+ enabled: w.enabled,
688
+ scope: w.scope,
689
+ scopeFilterName,
690
+ pollIntervalMs: w.pollIntervalMs,
691
+ viewportLines: w.viewportLines,
692
+ condition: w.condition,
693
+ cooldownMs: w.cooldownMs,
694
+ cooldownMode: w.cooldownMode,
695
+ eventName: w.eventName,
696
+ };
697
+ }));
698
+ const subscribers = subscribersRes.map((s) => ({
699
+ id: s.id,
700
+ name: s.name,
701
+ description: s.description,
702
+ enabled: s.enabled,
703
+ eventName: s.eventName,
704
+ eventFilter: s.eventFilter,
705
+ actionType: s.actionType,
706
+ actionInputs: s.actionInputs,
707
+ delayMs: s.delayMs,
708
+ cooldownMs: s.cooldownMs,
709
+ retryOnError: s.retryOnError,
710
+ groupName: s.groupName,
711
+ position: s.position,
712
+ priority: s.priority,
713
+ }));
493
714
  const exportPayload = {
494
715
  version: 1,
495
716
  exportedAt: new Date().toISOString(),
@@ -501,6 +722,8 @@ let ProjectsService = class ProjectsService {
501
722
  ? { promptId: initialPrompt.id, title: initialPrompt.title }
502
723
  : null,
503
724
  ...(Object.keys(projectSettings).length > 0 && { projectSettings }),
725
+ watchers,
726
+ subscribers,
504
727
  };
505
728
  return exportPayload;
506
729
  }
@@ -515,11 +738,13 @@ let ProjectsService = class ProjectsService {
515
738
  available.set(prov.name.trim().toLowerCase(), prov.id);
516
739
  }
517
740
  const missingProviders = Array.from(providerNames).filter((n) => !available.has(n));
518
- const [existingPrompts, existingProfiles, existingAgents, existingStatuses] = await Promise.all([
519
- this.storage.listPrompts(input.projectId, { limit: 10000, offset: 0 }),
741
+ const [existingPrompts, existingProfiles, existingAgents, existingStatuses, existingWatchers, existingSubscribers,] = await Promise.all([
742
+ this.storage.listPrompts({ projectId: input.projectId, limit: 10000, offset: 0 }),
520
743
  this.storage.listAgentProfiles({ projectId: input.projectId, limit: 10000, offset: 0 }),
521
744
  this.storage.listAgents(input.projectId, { limit: 10000, offset: 0 }),
522
745
  this.storage.listStatuses(input.projectId, { limit: 10000, offset: 0 }),
746
+ this.storage.listWatchers(input.projectId),
747
+ this.storage.listSubscribers(input.projectId),
523
748
  ]);
524
749
  const templateStatusLabels = new Set(payload.statuses.map((s) => s.label.trim().toLowerCase()));
525
750
  const unmatchedStatuses = [];
@@ -552,12 +777,16 @@ let ProjectsService = class ProjectsService {
552
777
  profiles: payload.profiles.length,
553
778
  agents: payload.agents.length,
554
779
  statuses: payload.statuses.length,
780
+ watchers: payload.watchers.length,
781
+ subscribers: payload.subscribers.length,
555
782
  },
556
783
  toDelete: {
557
784
  prompts: existingPrompts.total,
558
785
  profiles: existingProfiles.total,
559
786
  agents: existingAgents.total,
560
787
  statuses: existingStatuses.total,
788
+ watchers: existingWatchers.length,
789
+ subscribers: existingSubscribers.length,
561
790
  },
562
791
  },
563
792
  };
@@ -591,6 +820,12 @@ let ProjectsService = class ProjectsService {
591
820
  for (const pr of existingPrompts.items) {
592
821
  await this.storage.deletePrompt(pr.id);
593
822
  }
823
+ for (const w of existingWatchers) {
824
+ await this.watchersService.deleteWatcher(w.id);
825
+ }
826
+ for (const s of existingSubscribers) {
827
+ await this.storage.deleteSubscriber(s.id);
828
+ }
594
829
  await this.settings.updateSettings({
595
830
  projectId: input.projectId,
596
831
  initialSessionPromptId: null,
@@ -603,6 +838,10 @@ let ProjectsService = class ProjectsService {
603
838
  for (const s of existingStatuses.items) {
604
839
  existingStatusByLabel.set(s.label.trim().toLowerCase(), s);
605
840
  }
841
+ const TEMP_POSITION_OFFSET = 100000;
842
+ for (const s of existingStatuses.items) {
843
+ await this.storage.updateStatus(s.id, { position: s.position + TEMP_POSITION_OFFSET });
844
+ }
606
845
  for (const s of payload.statuses.sort((a, b) => a.position - b.position)) {
607
846
  const labelKey = s.label.trim().toLowerCase();
608
847
  const existing = existingStatusByLabel.get(labelKey);
@@ -610,6 +849,7 @@ let ProjectsService = class ProjectsService {
610
849
  const updated = await this.storage.updateStatus(existing.id, {
611
850
  color: s.color,
612
851
  position: s.position,
852
+ mcpHidden: s.mcpHidden,
613
853
  });
614
854
  if (s.id)
615
855
  statusIdMap[s.id] = updated.id;
@@ -621,6 +861,7 @@ let ProjectsService = class ProjectsService {
621
861
  label: s.label,
622
862
  color: s.color,
623
863
  position: s.position,
864
+ mcpHidden: s.mcpHidden,
624
865
  });
625
866
  if (s.id)
626
867
  statusIdMap[s.id] = created.id;
@@ -660,6 +901,7 @@ let ProjectsService = class ProjectsService {
660
901
  promptIdMap[p.id] = created.id;
661
902
  createdPrompts.push({ id: created.id, title: created.title });
662
903
  }
904
+ const profileNameToNewId = new Map();
663
905
  for (const prof of payload.profiles) {
664
906
  const providerId = available.get(prof.provider.name.trim().toLowerCase());
665
907
  if (!providerId) {
@@ -689,7 +931,9 @@ let ProjectsService = class ProjectsService {
689
931
  });
690
932
  if (prof.id)
691
933
  profileIdMap[prof.id] = created.id;
934
+ profileNameToNewId.set(prof.name.trim().toLowerCase(), created.id);
692
935
  }
936
+ const agentNameToNewId = new Map();
693
937
  for (const a of payload.agents) {
694
938
  const oldProfileId = a.profileId ?? '';
695
939
  const newProfileId = oldProfileId && profileIdMap[oldProfileId] ? profileIdMap[oldProfileId] : undefined;
@@ -707,14 +951,77 @@ let ProjectsService = class ProjectsService {
707
951
  });
708
952
  if (a.id)
709
953
  agentIdMap[a.id] = created.id;
954
+ agentNameToNewId.set(a.name.trim().toLowerCase(), created.id);
710
955
  }
711
- const newAgentByName = new Map();
712
- for (const a of payload.agents) {
713
- const created = agentIdMap[a.id ?? ''];
714
- if (created) {
715
- newAgentByName.set(a.name.trim().toLowerCase(), created);
956
+ const watcherIdMap = {};
957
+ for (const w of payload.watchers) {
958
+ let scopeFilterId = null;
959
+ if (w.scopeFilterName && w.scope !== 'all') {
960
+ const scopeFilterNameLower = w.scopeFilterName.trim().toLowerCase();
961
+ switch (w.scope) {
962
+ case 'agent': {
963
+ scopeFilterId = agentNameToNewId.get(scopeFilterNameLower) ?? null;
964
+ break;
965
+ }
966
+ case 'profile': {
967
+ scopeFilterId = profileNameToNewId.get(scopeFilterNameLower) ?? null;
968
+ break;
969
+ }
970
+ case 'provider': {
971
+ const provider = providers.items.find((p) => p.name.trim().toLowerCase() === scopeFilterNameLower);
972
+ scopeFilterId = provider?.id ?? null;
973
+ break;
974
+ }
975
+ }
976
+ if (!scopeFilterId) {
977
+ logger.warn({ watcherName: w.name, scope: w.scope, scopeFilterName: w.scopeFilterName }, 'Could not resolve scope filter, setting scope to "all"');
978
+ }
979
+ }
980
+ const created = await this.storage.createWatcher({
981
+ projectId: input.projectId,
982
+ name: w.name,
983
+ description: w.description ?? null,
984
+ enabled: w.enabled,
985
+ scope: scopeFilterId ? w.scope : 'all',
986
+ scopeFilterId,
987
+ pollIntervalMs: w.pollIntervalMs,
988
+ viewportLines: w.viewportLines,
989
+ condition: w.condition,
990
+ cooldownMs: w.cooldownMs,
991
+ cooldownMode: w.cooldownMode,
992
+ eventName: w.eventName,
993
+ });
994
+ if (w.id)
995
+ watcherIdMap[w.id] = created.id;
996
+ if (created.enabled) {
997
+ await this.watcherRunner.startWatcher(created);
716
998
  }
717
999
  }
1000
+ const subscriberIdMap = {};
1001
+ for (const s of payload.subscribers) {
1002
+ const created = await this.storage.createSubscriber({
1003
+ projectId: input.projectId,
1004
+ name: s.name,
1005
+ description: s.description ?? null,
1006
+ enabled: s.enabled,
1007
+ eventName: s.eventName,
1008
+ eventFilter: s.eventFilter ?? null,
1009
+ actionType: s.actionType,
1010
+ actionInputs: s.actionInputs,
1011
+ delayMs: s.delayMs,
1012
+ cooldownMs: s.cooldownMs,
1013
+ retryOnError: s.retryOnError,
1014
+ groupName: s.groupName ?? null,
1015
+ position: s.position ?? 0,
1016
+ priority: s.priority ?? 0,
1017
+ });
1018
+ if (s.id)
1019
+ subscriberIdMap[s.id] = created.id;
1020
+ }
1021
+ logger.info({
1022
+ watchersCreated: payload.watchers.length,
1023
+ subscribersCreated: payload.subscribers.length,
1024
+ }, 'Watchers and subscribers imported');
718
1025
  const existingEpics = await this.storage.listEpics(input.projectId, {
719
1026
  limit: 100000,
720
1027
  offset: 0,
@@ -725,7 +1032,7 @@ let ProjectsService = class ProjectsService {
725
1032
  if (epic.agentId) {
726
1033
  const oldAgentName = oldAgentIdToName.get(epic.agentId);
727
1034
  if (oldAgentName) {
728
- const newAgentId = newAgentByName.get(oldAgentName);
1035
+ const newAgentId = agentNameToNewId.get(oldAgentName);
729
1036
  if (newAgentId) {
730
1037
  await this.storage.updateEpic(epic.id, { agentId: newAgentId }, epic.version);
731
1038
  epicsRemapped++;
@@ -779,9 +1086,11 @@ let ProjectsService = class ProjectsService {
779
1086
  .map((label) => templateLabelToStatusId.get(label.toLowerCase()))
780
1087
  .filter((id) => !!id);
781
1088
  if (autoCleanStatusIds.length > 0) {
1089
+ const currentSettings = this.settings.getSettings();
1090
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
782
1091
  await this.settings.updateSettings({
783
1092
  autoClean: {
784
- statusIds: { [input.projectId]: autoCleanStatusIds },
1093
+ statusIds: { ...existingAutoClean, [input.projectId]: autoCleanStatusIds },
785
1094
  },
786
1095
  });
787
1096
  logger.info({ projectId: input.projectId, autoCleanStatusIds }, 'Applied autoClean statuses from projectSettings');
@@ -807,12 +1116,16 @@ let ProjectsService = class ProjectsService {
807
1116
  profiles: payload.profiles.length,
808
1117
  agents: payload.agents.length,
809
1118
  statuses: payload.statuses.length,
1119
+ watchers: payload.watchers.length,
1120
+ subscribers: payload.subscribers.length,
810
1121
  },
811
1122
  deleted: {
812
1123
  prompts: existingPrompts.total,
813
1124
  profiles: existingProfiles.total,
814
1125
  agents: existingAgents.total,
815
1126
  statuses: 0,
1127
+ watchers: existingWatchers.length,
1128
+ subscribers: existingSubscribers.length,
816
1129
  },
817
1130
  epics: {
818
1131
  preserved: existingEpics.total,
@@ -820,7 +1133,14 @@ let ProjectsService = class ProjectsService {
820
1133
  agentCleared: epicsCleared,
821
1134
  },
822
1135
  },
823
- mappings: { promptIdMap, profileIdMap, agentIdMap, statusIdMap },
1136
+ mappings: {
1137
+ promptIdMap,
1138
+ profileIdMap,
1139
+ agentIdMap,
1140
+ statusIdMap,
1141
+ watcherIdMap,
1142
+ subscriberIdMap,
1143
+ },
824
1144
  initialPromptSet,
825
1145
  message: 'Project configuration replaced. Epics preserved.',
826
1146
  };
@@ -859,6 +1179,8 @@ exports.ProjectsService = ProjectsService = __decorate([
859
1179
  (0, common_1.Injectable)(),
860
1180
  __param(0, (0, common_1.Inject)(storage_interface_1.STORAGE_SERVICE)),
861
1181
  __metadata("design:paramtypes", [Object, sessions_service_1.SessionsService,
862
- settings_service_1.SettingsService])
1182
+ settings_service_1.SettingsService,
1183
+ watchers_service_1.WatchersService,
1184
+ watcher_runner_service_1.WatcherRunnerService])
863
1185
  ], ProjectsService);
864
1186
  //# sourceMappingURL=projects.service.js.map