devchain-cli 0.2.1 → 0.3.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 (152) hide show
  1. package/README.md +2 -0
  2. package/dist/cli.js +199 -42
  3. package/dist/drizzle/0018_whole_zodiak.sql +43 -0
  4. package/dist/drizzle/0019_flat_avengers.sql +3 -0
  5. package/dist/drizzle/0020_statuses_mcp_hidden.sql +1 -0
  6. package/dist/drizzle/meta/0018_snapshot.json +2920 -0
  7. package/dist/drizzle/meta/0019_snapshot.json +2943 -0
  8. package/dist/drizzle/meta/0020_snapshot.json +2951 -0
  9. package/dist/drizzle/meta/_journal.json +21 -0
  10. package/dist/server/app.module.js +11 -1
  11. package/dist/server/app.module.js.map +1 -1
  12. package/dist/server/common/config/env.config.d.ts +1 -0
  13. package/dist/server/common/config/env.config.js +4 -0
  14. package/dist/server/common/config/env.config.js.map +1 -1
  15. package/dist/server/common/filters/http-exception.filter.js +24 -1
  16. package/dist/server/common/filters/http-exception.filter.js.map +1 -1
  17. package/dist/server/common/logging/logger.js +4 -3
  18. package/dist/server/common/logging/logger.js.map +1 -1
  19. package/dist/server/main.js +0 -3
  20. package/dist/server/main.js.map +1 -1
  21. package/dist/server/modules/agents/agents.module.js +2 -1
  22. package/dist/server/modules/agents/agents.module.js.map +1 -1
  23. package/dist/server/modules/agents/controllers/agents.controller.d.ts +17 -2
  24. package/dist/server/modules/agents/controllers/agents.controller.js +84 -3
  25. package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -1
  26. package/dist/server/modules/chat/dtos/chat.dto.d.ts +18 -18
  27. package/dist/server/modules/chat/services/invite-template.util.js +1 -1
  28. package/dist/server/modules/chat/services/invite-template.util.js.map +1 -1
  29. package/dist/server/modules/core/controllers/health.controller.d.ts +1 -0
  30. package/dist/server/modules/core/controllers/health.controller.js +23 -0
  31. package/dist/server/modules/core/controllers/health.controller.js.map +1 -1
  32. package/dist/server/modules/events/catalog/index.d.ts +40 -0
  33. package/dist/server/modules/events/catalog/index.js +2 -0
  34. package/dist/server/modules/events/catalog/index.js.map +1 -1
  35. package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +45 -0
  36. package/dist/server/modules/events/catalog/terminal.watcher.triggered.js +22 -0
  37. package/dist/server/modules/events/catalog/terminal.watcher.triggered.js.map +1 -0
  38. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.d.ts +1 -0
  39. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js +30 -12
  40. package/dist/server/modules/events/subscribers/chat-message-delivery.subscriber.js.map +1 -1
  41. package/dist/server/modules/mcp/constants.js +5 -1
  42. package/dist/server/modules/mcp/constants.js.map +1 -1
  43. package/dist/server/modules/mcp/controllers/mcp-http.controller.js +78 -77
  44. package/dist/server/modules/mcp/controllers/mcp-http.controller.js.map +1 -1
  45. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js +78 -77
  46. package/dist/server/modules/mcp/controllers/mcp-sdk.controller.js.map +1 -1
  47. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +125 -97
  48. package/dist/server/modules/mcp/dtos/mcp.dto.js +23 -26
  49. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  50. package/dist/server/modules/mcp/services/instructions-resolver.d.ts +3 -0
  51. package/dist/server/modules/mcp/services/instructions-resolver.js +83 -2
  52. package/dist/server/modules/mcp/services/instructions-resolver.js.map +1 -1
  53. package/dist/server/modules/mcp/services/mcp.service.d.ts +3 -2
  54. package/dist/server/modules/mcp/services/mcp.service.js +549 -263
  55. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  56. package/dist/server/modules/projects/controllers/projects.controller.d.ts +42 -0
  57. package/dist/server/modules/projects/controllers/projects.controller.js +11 -0
  58. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  59. package/dist/server/modules/projects/projects.module.js +2 -1
  60. package/dist/server/modules/projects/projects.module.js.map +1 -1
  61. package/dist/server/modules/projects/services/projects.service.d.ts +47 -1
  62. package/dist/server/modules/projects/services/projects.service.js +278 -22
  63. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  64. package/dist/server/modules/prompts/controllers/prompts.controller.d.ts +1 -1
  65. package/dist/server/modules/prompts/controllers/prompts.controller.js +26 -4
  66. package/dist/server/modules/prompts/controllers/prompts.controller.js.map +1 -1
  67. package/dist/server/modules/sessions/utils/template-renderer.js +1 -0
  68. package/dist/server/modules/sessions/utils/template-renderer.js.map +1 -1
  69. package/dist/server/modules/statuses/controllers/statuses.controller.js +2 -0
  70. package/dist/server/modules/statuses/controllers/statuses.controller.js.map +1 -1
  71. package/dist/server/modules/storage/db/schema.d.ts +613 -0
  72. package/dist/server/modules/storage/db/schema.js +50 -1
  73. package/dist/server/modules/storage/db/schema.js.map +1 -1
  74. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +40 -2
  75. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  76. package/dist/server/modules/storage/local/local-storage.service.d.ts +18 -3
  77. package/dist/server/modules/storage/local/local-storage.service.js +407 -11
  78. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  79. package/dist/server/modules/storage/models/domain.models.d.ts +59 -1
  80. package/dist/server/modules/subscribers/actions/action.interface.d.ts +67 -0
  81. package/dist/server/modules/subscribers/actions/action.interface.js +3 -0
  82. package/dist/server/modules/subscribers/actions/action.interface.js.map +1 -0
  83. package/dist/server/modules/subscribers/actions/actions.registry.d.ts +7 -0
  84. package/dist/server/modules/subscribers/actions/actions.registry.js +37 -0
  85. package/dist/server/modules/subscribers/actions/actions.registry.js.map +1 -0
  86. package/dist/server/modules/subscribers/actions/restart-agent.action.d.ts +8 -0
  87. package/dist/server/modules/subscribers/actions/restart-agent.action.js +119 -0
  88. package/dist/server/modules/subscribers/actions/restart-agent.action.js.map +1 -0
  89. package/dist/server/modules/subscribers/actions/send-message.action.d.ts +2 -0
  90. package/dist/server/modules/subscribers/actions/send-message.action.js +83 -0
  91. package/dist/server/modules/subscribers/actions/send-message.action.js.map +1 -0
  92. package/dist/server/modules/subscribers/controllers/actions.controller.d.ts +6 -0
  93. package/dist/server/modules/subscribers/controllers/actions.controller.js +51 -0
  94. package/dist/server/modules/subscribers/controllers/actions.controller.js.map +1 -0
  95. package/dist/server/modules/subscribers/controllers/subscribers.controller.d.ts +17 -0
  96. package/dist/server/modules/subscribers/controllers/subscribers.controller.js +178 -0
  97. package/dist/server/modules/subscribers/controllers/subscribers.controller.js.map +1 -0
  98. package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +251 -0
  99. package/dist/server/modules/subscribers/dtos/subscriber.dto.js +68 -0
  100. package/dist/server/modules/subscribers/dtos/subscriber.dto.js.map +1 -0
  101. package/dist/server/modules/subscribers/events/event-fields-catalog.d.ts +19 -0
  102. package/dist/server/modules/subscribers/events/event-fields-catalog.js +98 -0
  103. package/dist/server/modules/subscribers/events/event-fields-catalog.js.map +1 -0
  104. package/dist/server/modules/subscribers/services/automation-scheduler.service.d.ts +49 -0
  105. package/dist/server/modules/subscribers/services/automation-scheduler.service.js +300 -0
  106. package/dist/server/modules/subscribers/services/automation-scheduler.service.js.map +1 -0
  107. package/dist/server/modules/subscribers/services/subscriber-executor.service.d.ts +77 -0
  108. package/dist/server/modules/subscribers/services/subscriber-executor.service.js +576 -0
  109. package/dist/server/modules/subscribers/services/subscriber-executor.service.js.map +1 -0
  110. package/dist/server/modules/subscribers/services/subscribers.service.d.ts +14 -0
  111. package/dist/server/modules/subscribers/services/subscribers.service.js +70 -0
  112. package/dist/server/modules/subscribers/services/subscribers.service.js.map +1 -0
  113. package/dist/server/modules/subscribers/subscribers.module.d.ts +2 -0
  114. package/dist/server/modules/subscribers/subscribers.module.js +36 -0
  115. package/dist/server/modules/subscribers/subscribers.module.js.map +1 -0
  116. package/dist/server/modules/terminal/services/tmux.service.js +9 -6
  117. package/dist/server/modules/terminal/services/tmux.service.js.map +1 -1
  118. package/dist/server/modules/watchers/controllers/watchers.controller.d.ts +16 -0
  119. package/dist/server/modules/watchers/controllers/watchers.controller.js +180 -0
  120. package/dist/server/modules/watchers/controllers/watchers.controller.js.map +1 -0
  121. package/dist/server/modules/watchers/dtos/watcher.dto.d.ts +206 -0
  122. package/dist/server/modules/watchers/dtos/watcher.dto.js +54 -0
  123. package/dist/server/modules/watchers/dtos/watcher.dto.js.map +1 -0
  124. package/dist/server/modules/watchers/services/watcher-runner.service.d.ts +68 -0
  125. package/dist/server/modules/watchers/services/watcher-runner.service.js +477 -0
  126. package/dist/server/modules/watchers/services/watcher-runner.service.js.map +1 -0
  127. package/dist/server/modules/watchers/services/watchers.service.d.ts +29 -0
  128. package/dist/server/modules/watchers/services/watchers.service.js +98 -0
  129. package/dist/server/modules/watchers/services/watchers.service.js.map +1 -0
  130. package/dist/server/modules/watchers/watchers.module.d.ts +2 -0
  131. package/dist/server/modules/watchers/watchers.module.js +34 -0
  132. package/dist/server/modules/watchers/watchers.module.js.map +1 -0
  133. package/dist/server/templates/claude-codex-advanced.json +377 -0
  134. package/dist/server/templates/claude-opus.json +29 -25
  135. package/dist/server/templates/simple-codex.json +29 -25
  136. package/dist/server/test-setup-node.d.ts +1 -0
  137. package/dist/server/test-setup-node.js +8 -0
  138. package/dist/server/test-setup-node.js.map +1 -0
  139. package/dist/server/test-setup.js +2 -0
  140. package/dist/server/test-setup.js.map +1 -1
  141. package/dist/server/tsconfig.tsbuildinfo +1 -1
  142. package/dist/server/ui/assets/index-C9GXCjnF.js +700 -0
  143. package/dist/server/ui/assets/index-o0FbZg-1.css +32 -0
  144. package/dist/server/ui/index.html +2 -2
  145. package/dist/templates/claude-codex-advanced.json +377 -0
  146. package/dist/templates/claude-opus.json +29 -25
  147. package/dist/templates/simple-codex.json +29 -25
  148. package/package.json +58 -27
  149. package/dist/server/templates/codex-claude.json +0 -178
  150. package/dist/server/ui/assets/index-5Xb7jFMJ.js +0 -641
  151. package/dist/server/ui/assets/index-CbYIbCQV.css +0 -32
  152. 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)();
@@ -97,11 +153,8 @@ let ProjectsService = class ProjectsService {
97
153
  logger.warn({ path: env.TEMPLATES_DIR }, 'TEMPLATES_DIR set but path does not exist');
98
154
  }
99
155
  const possibleTemplatePaths = [
100
- (0, path_1.join)(__dirname, '../../../../templates'),
101
- (0, path_1.join)(__dirname, '../../../../templates'),
102
- (0, path_1.join)(process.cwd(), 'dist', 'templates'),
156
+ (0, path_1.join)(__dirname, '..', '..', '..', '..', 'templates'),
103
157
  (0, path_1.join)(process.cwd(), 'apps', 'local-app', 'templates'),
104
- (0, path_1.join)(process.cwd(), 'templates'),
105
158
  ];
106
159
  for (const path of possibleTemplatePaths) {
107
160
  if ((0, fs_1.existsSync)(path)) {
@@ -139,6 +192,40 @@ let ProjectsService = class ProjectsService {
139
192
  });
140
193
  }
141
194
  }
195
+ async getTemplateContent(templateId) {
196
+ logger.info({ templateId }, 'getTemplateContent');
197
+ const templatesDir = this.findTemplatesDirectory();
198
+ if (!templatesDir) {
199
+ throw new common_1.InternalServerErrorException({
200
+ message: 'Templates directory not found',
201
+ hint: 'Templates directory is not available in this deployment',
202
+ });
203
+ }
204
+ const TEMPLATE_ID_REGEX = /^[a-zA-Z0-9_-]+$/;
205
+ if (!TEMPLATE_ID_REGEX.test(templateId)) {
206
+ throw new error_types_1.ValidationError('Invalid template ID: must contain only alphanumeric characters, hyphens, and underscores', { templateId });
207
+ }
208
+ const resolvedTemplatesDir = (0, path_1.resolve)(templatesDir);
209
+ const templatePath = (0, path_1.resolve)(templatesDir, `${templateId}.json`);
210
+ if (!templatePath.startsWith(resolvedTemplatesDir + path_1.sep)) {
211
+ logger.warn({ templateId, templatePath, templatesDir: resolvedTemplatesDir }, 'Path traversal attempt detected');
212
+ throw new error_types_1.ValidationError('Invalid template ID: path traversal not allowed', { templateId });
213
+ }
214
+ if (!(0, fs_1.existsSync)(templatePath)) {
215
+ throw new error_types_1.NotFoundError('Template', templateId);
216
+ }
217
+ try {
218
+ const content = (0, fs_1.readFileSync)(templatePath, 'utf-8');
219
+ return JSON.parse(content);
220
+ }
221
+ catch (error) {
222
+ logger.error({ error, templatePath }, 'Failed to read template file');
223
+ throw new common_1.InternalServerErrorException({
224
+ message: 'Failed to read template file',
225
+ hint: 'Template file exists but cannot be read or parsed',
226
+ });
227
+ }
228
+ }
142
229
  async createFromTemplate(input) {
143
230
  logger.info({ input }, 'createFromTemplate');
144
231
  const templatesDir = this.findTemplatesDirectory();
@@ -245,6 +332,7 @@ let ProjectsService = class ProjectsService {
245
332
  label: s.label,
246
333
  color: s.color,
247
334
  position: s.position,
335
+ mcpHidden: s.mcpHidden,
248
336
  })),
249
337
  initialPrompt: payload.initialPrompt,
250
338
  };
@@ -300,9 +388,11 @@ let ProjectsService = class ProjectsService {
300
388
  })
301
389
  .filter((id) => !!id);
302
390
  if (autoCleanStatusIds.length > 0) {
391
+ const currentSettings = this.settings.getSettings();
392
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
303
393
  await this.settings.updateSettings({
304
394
  autoClean: {
305
- statusIds: { [result.project.id]: autoCleanStatusIds },
395
+ statusIds: { ...existingAutoClean, [result.project.id]: autoCleanStatusIds },
306
396
  },
307
397
  });
308
398
  logger.info({ projectId: result.project.id, autoCleanStatusIds }, 'Applied autoClean statuses from projectSettings');
@@ -313,9 +403,11 @@ let ProjectsService = class ProjectsService {
313
403
  if (archiveTemplateStatus?.id) {
314
404
  const archiveNewId = result.mappings.statusIdMap[archiveTemplateStatus.id];
315
405
  if (archiveNewId) {
406
+ const currentSettings = this.settings.getSettings();
407
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
316
408
  await this.settings.updateSettings({
317
409
  autoClean: {
318
- statusIds: { [result.project.id]: [archiveNewId] },
410
+ statusIds: { ...existingAutoClean, [result.project.id]: [archiveNewId] },
319
411
  },
320
412
  });
321
413
  logger.info({ projectId: result.project.id, archiveStatusId: archiveNewId }, 'Auto-configured Archive status for auto-clean (fallback)');
@@ -336,9 +428,11 @@ let ProjectsService = class ProjectsService {
336
428
  if (archiveTemplateStatus?.id) {
337
429
  const archiveNewId = result.mappings.statusIdMap[archiveTemplateStatus.id];
338
430
  if (archiveNewId) {
431
+ const currentSettings = this.settings.getSettings();
432
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
339
433
  await this.settings.updateSettings({
340
434
  autoClean: {
341
- statusIds: { [result.project.id]: [archiveNewId] },
435
+ statusIds: { ...existingAutoClean, [result.project.id]: [archiveNewId] },
342
436
  },
343
437
  });
344
438
  logger.info({ projectId: result.project.id, archiveStatusId: archiveNewId }, 'Auto-configured Archive status for auto-clean');
@@ -356,13 +450,15 @@ let ProjectsService = class ProjectsService {
356
450
  }
357
451
  async exportProject(projectId) {
358
452
  logger.info({ projectId }, 'exportProject');
359
- const [promptsRes, profilesRes, agentsRes, statusesRes, initialPrompt, settings] = await Promise.all([
360
- this.storage.listPrompts(projectId, { limit: 1000, offset: 0 }),
453
+ const [promptsRes, profilesRes, agentsRes, statusesRes, initialPrompt, settings, watchersRes, subscribersRes,] = await Promise.all([
454
+ this.storage.listPrompts({ projectId, limit: 1000, offset: 0 }),
361
455
  this.storage.listAgentProfiles({ projectId, limit: 1000, offset: 0 }),
362
456
  this.storage.listAgents(projectId, { limit: 1000, offset: 0 }),
363
457
  this.storage.listStatuses(projectId, { limit: 1000, offset: 0 }),
364
458
  this.storage.getInitialSessionPrompt(projectId),
365
459
  Promise.resolve(this.settings.getSettings()),
460
+ this.storage.listWatchers(projectId),
461
+ this.storage.listSubscribers(projectId),
366
462
  ]);
367
463
  const secretKeys = new Set([
368
464
  'apikey',
@@ -410,7 +506,8 @@ let ProjectsService = class ProjectsService {
410
506
  return null;
411
507
  return options;
412
508
  };
413
- const prompts = promptsRes.items.map((p) => ({
509
+ const fullPrompts = await Promise.all(promptsRes.items.map((p) => this.storage.getPrompt(p.id)));
510
+ const prompts = fullPrompts.map((p) => ({
414
511
  id: p.id,
415
512
  title: p.title,
416
513
  content: p.content,
@@ -440,6 +537,7 @@ let ProjectsService = class ProjectsService {
440
537
  label: s.label,
441
538
  color: s.color,
442
539
  position: s.position,
540
+ mcpHidden: s.mcpHidden,
443
541
  }));
444
542
  const projectSettings = {};
445
543
  if (initialPrompt?.title) {
@@ -459,6 +557,63 @@ let ProjectsService = class ProjectsService {
459
557
  if (epicAssignedTemplate) {
460
558
  projectSettings.epicAssignedTemplate = epicAssignedTemplate;
461
559
  }
560
+ const watchers = await Promise.all(watchersRes.map(async (w) => {
561
+ let scopeFilterName = null;
562
+ if (w.scopeFilterId) {
563
+ switch (w.scope) {
564
+ case 'agent': {
565
+ const agent = agentsRes.items.find((a) => a.id === w.scopeFilterId);
566
+ scopeFilterName = agent?.name ?? null;
567
+ break;
568
+ }
569
+ case 'profile': {
570
+ const profile = profilesRes.items.find((p) => p.id === w.scopeFilterId);
571
+ scopeFilterName = profile?.name ?? null;
572
+ break;
573
+ }
574
+ case 'provider': {
575
+ try {
576
+ const provider = await this.storage.getProvider(w.scopeFilterId);
577
+ scopeFilterName = provider?.name ?? null;
578
+ }
579
+ catch {
580
+ scopeFilterName = null;
581
+ }
582
+ break;
583
+ }
584
+ }
585
+ }
586
+ return {
587
+ id: w.id,
588
+ name: w.name,
589
+ description: w.description,
590
+ enabled: w.enabled,
591
+ scope: w.scope,
592
+ scopeFilterName,
593
+ pollIntervalMs: w.pollIntervalMs,
594
+ viewportLines: w.viewportLines,
595
+ condition: w.condition,
596
+ cooldownMs: w.cooldownMs,
597
+ cooldownMode: w.cooldownMode,
598
+ eventName: w.eventName,
599
+ };
600
+ }));
601
+ const subscribers = subscribersRes.map((s) => ({
602
+ id: s.id,
603
+ name: s.name,
604
+ description: s.description,
605
+ enabled: s.enabled,
606
+ eventName: s.eventName,
607
+ eventFilter: s.eventFilter,
608
+ actionType: s.actionType,
609
+ actionInputs: s.actionInputs,
610
+ delayMs: s.delayMs,
611
+ cooldownMs: s.cooldownMs,
612
+ retryOnError: s.retryOnError,
613
+ groupName: s.groupName,
614
+ position: s.position,
615
+ priority: s.priority,
616
+ }));
462
617
  const exportPayload = {
463
618
  version: 1,
464
619
  exportedAt: new Date().toISOString(),
@@ -470,6 +625,8 @@ let ProjectsService = class ProjectsService {
470
625
  ? { promptId: initialPrompt.id, title: initialPrompt.title }
471
626
  : null,
472
627
  ...(Object.keys(projectSettings).length > 0 && { projectSettings }),
628
+ watchers,
629
+ subscribers,
473
630
  };
474
631
  return exportPayload;
475
632
  }
@@ -484,11 +641,13 @@ let ProjectsService = class ProjectsService {
484
641
  available.set(prov.name.trim().toLowerCase(), prov.id);
485
642
  }
486
643
  const missingProviders = Array.from(providerNames).filter((n) => !available.has(n));
487
- const [existingPrompts, existingProfiles, existingAgents, existingStatuses] = await Promise.all([
488
- this.storage.listPrompts(input.projectId, { limit: 10000, offset: 0 }),
644
+ const [existingPrompts, existingProfiles, existingAgents, existingStatuses, existingWatchers, existingSubscribers,] = await Promise.all([
645
+ this.storage.listPrompts({ projectId: input.projectId, limit: 10000, offset: 0 }),
489
646
  this.storage.listAgentProfiles({ projectId: input.projectId, limit: 10000, offset: 0 }),
490
647
  this.storage.listAgents(input.projectId, { limit: 10000, offset: 0 }),
491
648
  this.storage.listStatuses(input.projectId, { limit: 10000, offset: 0 }),
649
+ this.storage.listWatchers(input.projectId),
650
+ this.storage.listSubscribers(input.projectId),
492
651
  ]);
493
652
  const templateStatusLabels = new Set(payload.statuses.map((s) => s.label.trim().toLowerCase()));
494
653
  const unmatchedStatuses = [];
@@ -521,12 +680,16 @@ let ProjectsService = class ProjectsService {
521
680
  profiles: payload.profiles.length,
522
681
  agents: payload.agents.length,
523
682
  statuses: payload.statuses.length,
683
+ watchers: payload.watchers.length,
684
+ subscribers: payload.subscribers.length,
524
685
  },
525
686
  toDelete: {
526
687
  prompts: existingPrompts.total,
527
688
  profiles: existingProfiles.total,
528
689
  agents: existingAgents.total,
529
690
  statuses: existingStatuses.total,
691
+ watchers: existingWatchers.length,
692
+ subscribers: existingSubscribers.length,
530
693
  },
531
694
  },
532
695
  };
@@ -560,6 +723,12 @@ let ProjectsService = class ProjectsService {
560
723
  for (const pr of existingPrompts.items) {
561
724
  await this.storage.deletePrompt(pr.id);
562
725
  }
726
+ for (const w of existingWatchers) {
727
+ await this.watchersService.deleteWatcher(w.id);
728
+ }
729
+ for (const s of existingSubscribers) {
730
+ await this.storage.deleteSubscriber(s.id);
731
+ }
563
732
  await this.settings.updateSettings({
564
733
  projectId: input.projectId,
565
734
  initialSessionPromptId: null,
@@ -572,6 +741,10 @@ let ProjectsService = class ProjectsService {
572
741
  for (const s of existingStatuses.items) {
573
742
  existingStatusByLabel.set(s.label.trim().toLowerCase(), s);
574
743
  }
744
+ const TEMP_POSITION_OFFSET = 100000;
745
+ for (const s of existingStatuses.items) {
746
+ await this.storage.updateStatus(s.id, { position: s.position + TEMP_POSITION_OFFSET });
747
+ }
575
748
  for (const s of payload.statuses.sort((a, b) => a.position - b.position)) {
576
749
  const labelKey = s.label.trim().toLowerCase();
577
750
  const existing = existingStatusByLabel.get(labelKey);
@@ -579,6 +752,7 @@ let ProjectsService = class ProjectsService {
579
752
  const updated = await this.storage.updateStatus(existing.id, {
580
753
  color: s.color,
581
754
  position: s.position,
755
+ mcpHidden: s.mcpHidden,
582
756
  });
583
757
  if (s.id)
584
758
  statusIdMap[s.id] = updated.id;
@@ -590,6 +764,7 @@ let ProjectsService = class ProjectsService {
590
764
  label: s.label,
591
765
  color: s.color,
592
766
  position: s.position,
767
+ mcpHidden: s.mcpHidden,
593
768
  });
594
769
  if (s.id)
595
770
  statusIdMap[s.id] = created.id;
@@ -629,6 +804,7 @@ let ProjectsService = class ProjectsService {
629
804
  promptIdMap[p.id] = created.id;
630
805
  createdPrompts.push({ id: created.id, title: created.title });
631
806
  }
807
+ const profileNameToNewId = new Map();
632
808
  for (const prof of payload.profiles) {
633
809
  const providerId = available.get(prof.provider.name.trim().toLowerCase());
634
810
  if (!providerId) {
@@ -658,7 +834,9 @@ let ProjectsService = class ProjectsService {
658
834
  });
659
835
  if (prof.id)
660
836
  profileIdMap[prof.id] = created.id;
837
+ profileNameToNewId.set(prof.name.trim().toLowerCase(), created.id);
661
838
  }
839
+ const agentNameToNewId = new Map();
662
840
  for (const a of payload.agents) {
663
841
  const oldProfileId = a.profileId ?? '';
664
842
  const newProfileId = oldProfileId && profileIdMap[oldProfileId] ? profileIdMap[oldProfileId] : undefined;
@@ -676,14 +854,77 @@ let ProjectsService = class ProjectsService {
676
854
  });
677
855
  if (a.id)
678
856
  agentIdMap[a.id] = created.id;
857
+ agentNameToNewId.set(a.name.trim().toLowerCase(), created.id);
679
858
  }
680
- const newAgentByName = new Map();
681
- for (const a of payload.agents) {
682
- const created = agentIdMap[a.id ?? ''];
683
- if (created) {
684
- newAgentByName.set(a.name.trim().toLowerCase(), created);
859
+ const watcherIdMap = {};
860
+ for (const w of payload.watchers) {
861
+ let scopeFilterId = null;
862
+ if (w.scopeFilterName && w.scope !== 'all') {
863
+ const scopeFilterNameLower = w.scopeFilterName.trim().toLowerCase();
864
+ switch (w.scope) {
865
+ case 'agent': {
866
+ scopeFilterId = agentNameToNewId.get(scopeFilterNameLower) ?? null;
867
+ break;
868
+ }
869
+ case 'profile': {
870
+ scopeFilterId = profileNameToNewId.get(scopeFilterNameLower) ?? null;
871
+ break;
872
+ }
873
+ case 'provider': {
874
+ const provider = providers.items.find((p) => p.name.trim().toLowerCase() === scopeFilterNameLower);
875
+ scopeFilterId = provider?.id ?? null;
876
+ break;
877
+ }
878
+ }
879
+ if (!scopeFilterId) {
880
+ logger.warn({ watcherName: w.name, scope: w.scope, scopeFilterName: w.scopeFilterName }, 'Could not resolve scope filter, setting scope to "all"');
881
+ }
882
+ }
883
+ const created = await this.storage.createWatcher({
884
+ projectId: input.projectId,
885
+ name: w.name,
886
+ description: w.description ?? null,
887
+ enabled: w.enabled,
888
+ scope: scopeFilterId ? w.scope : 'all',
889
+ scopeFilterId,
890
+ pollIntervalMs: w.pollIntervalMs,
891
+ viewportLines: w.viewportLines,
892
+ condition: w.condition,
893
+ cooldownMs: w.cooldownMs,
894
+ cooldownMode: w.cooldownMode,
895
+ eventName: w.eventName,
896
+ });
897
+ if (w.id)
898
+ watcherIdMap[w.id] = created.id;
899
+ if (created.enabled) {
900
+ await this.watcherRunner.startWatcher(created);
685
901
  }
686
902
  }
903
+ const subscriberIdMap = {};
904
+ for (const s of payload.subscribers) {
905
+ const created = await this.storage.createSubscriber({
906
+ projectId: input.projectId,
907
+ name: s.name,
908
+ description: s.description ?? null,
909
+ enabled: s.enabled,
910
+ eventName: s.eventName,
911
+ eventFilter: s.eventFilter ?? null,
912
+ actionType: s.actionType,
913
+ actionInputs: s.actionInputs,
914
+ delayMs: s.delayMs,
915
+ cooldownMs: s.cooldownMs,
916
+ retryOnError: s.retryOnError,
917
+ groupName: s.groupName ?? null,
918
+ position: s.position ?? 0,
919
+ priority: s.priority ?? 0,
920
+ });
921
+ if (s.id)
922
+ subscriberIdMap[s.id] = created.id;
923
+ }
924
+ logger.info({
925
+ watchersCreated: payload.watchers.length,
926
+ subscribersCreated: payload.subscribers.length,
927
+ }, 'Watchers and subscribers imported');
687
928
  const existingEpics = await this.storage.listEpics(input.projectId, {
688
929
  limit: 100000,
689
930
  offset: 0,
@@ -694,7 +935,7 @@ let ProjectsService = class ProjectsService {
694
935
  if (epic.agentId) {
695
936
  const oldAgentName = oldAgentIdToName.get(epic.agentId);
696
937
  if (oldAgentName) {
697
- const newAgentId = newAgentByName.get(oldAgentName);
938
+ const newAgentId = agentNameToNewId.get(oldAgentName);
698
939
  if (newAgentId) {
699
940
  await this.storage.updateEpic(epic.id, { agentId: newAgentId }, epic.version);
700
941
  epicsRemapped++;
@@ -748,9 +989,11 @@ let ProjectsService = class ProjectsService {
748
989
  .map((label) => templateLabelToStatusId.get(label.toLowerCase()))
749
990
  .filter((id) => !!id);
750
991
  if (autoCleanStatusIds.length > 0) {
992
+ const currentSettings = this.settings.getSettings();
993
+ const existingAutoClean = currentSettings.autoClean?.statusIds ?? {};
751
994
  await this.settings.updateSettings({
752
995
  autoClean: {
753
- statusIds: { [input.projectId]: autoCleanStatusIds },
996
+ statusIds: { ...existingAutoClean, [input.projectId]: autoCleanStatusIds },
754
997
  },
755
998
  });
756
999
  logger.info({ projectId: input.projectId, autoCleanStatusIds }, 'Applied autoClean statuses from projectSettings');
@@ -776,12 +1019,16 @@ let ProjectsService = class ProjectsService {
776
1019
  profiles: payload.profiles.length,
777
1020
  agents: payload.agents.length,
778
1021
  statuses: payload.statuses.length,
1022
+ watchers: payload.watchers.length,
1023
+ subscribers: payload.subscribers.length,
779
1024
  },
780
1025
  deleted: {
781
1026
  prompts: existingPrompts.total,
782
1027
  profiles: existingProfiles.total,
783
1028
  agents: existingAgents.total,
784
1029
  statuses: 0,
1030
+ watchers: existingWatchers.length,
1031
+ subscribers: existingSubscribers.length,
785
1032
  },
786
1033
  epics: {
787
1034
  preserved: existingEpics.total,
@@ -789,7 +1036,14 @@ let ProjectsService = class ProjectsService {
789
1036
  agentCleared: epicsCleared,
790
1037
  },
791
1038
  },
792
- mappings: { promptIdMap, profileIdMap, agentIdMap, statusIdMap },
1039
+ mappings: {
1040
+ promptIdMap,
1041
+ profileIdMap,
1042
+ agentIdMap,
1043
+ statusIdMap,
1044
+ watcherIdMap,
1045
+ subscriberIdMap,
1046
+ },
793
1047
  initialPromptSet,
794
1048
  message: 'Project configuration replaced. Epics preserved.',
795
1049
  };
@@ -828,6 +1082,8 @@ exports.ProjectsService = ProjectsService = __decorate([
828
1082
  (0, common_1.Injectable)(),
829
1083
  __param(0, (0, common_1.Inject)(storage_interface_1.STORAGE_SERVICE)),
830
1084
  __metadata("design:paramtypes", [Object, sessions_service_1.SessionsService,
831
- settings_service_1.SettingsService])
1085
+ settings_service_1.SettingsService,
1086
+ watchers_service_1.WatchersService,
1087
+ watcher_runner_service_1.WatcherRunnerService])
832
1088
  ], ProjectsService);
833
1089
  //# sourceMappingURL=projects.service.js.map