devchain-cli 0.4.6 → 0.5.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 (97) hide show
  1. package/dist/cli.js +7 -4
  2. package/dist/drizzle/0021_agent_profiles_family_slug.sql +3 -0
  3. package/dist/drizzle/meta/0021_snapshot.json +2968 -0
  4. package/dist/drizzle/meta/_journal.json +8 -1
  5. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +5 -0
  6. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts.map +1 -1
  7. package/dist/node_modules/@devchain/shared/schemas/export-schema.js +1 -0
  8. package/dist/node_modules/@devchain/shared/schemas/export-schema.js.map +1 -1
  9. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.d.ts +2 -0
  10. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.d.ts.map +1 -0
  11. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.js +88 -0
  12. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.js.map +1 -0
  13. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  14. package/dist/server/common/errors/error-types.d.ts +4 -0
  15. package/dist/server/common/errors/error-types.js +8 -1
  16. package/dist/server/common/errors/error-types.js.map +1 -1
  17. package/dist/server/modules/core/core.module.js +2 -1
  18. package/dist/server/modules/core/core.module.js.map +1 -1
  19. package/dist/server/modules/core/services/preflight.service.d.ts +4 -1
  20. package/dist/server/modules/core/services/preflight.service.js +8 -3
  21. package/dist/server/modules/core/services/preflight.service.js.map +1 -1
  22. package/dist/server/modules/epics/services/epics.service.js +0 -51
  23. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  24. package/dist/server/modules/events/catalog/epic.created.d.ts +2 -2
  25. package/dist/server/modules/events/catalog/index.d.ts +2 -27
  26. package/dist/server/modules/events/catalog/index.js +0 -2
  27. package/dist/server/modules/events/catalog/index.js.map +1 -1
  28. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.d.ts +3 -0
  29. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js +87 -0
  30. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js.map +1 -1
  31. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +2 -2
  32. package/dist/server/modules/mcp/mcp.module.js +2 -34
  33. package/dist/server/modules/mcp/mcp.module.js.map +1 -1
  34. package/dist/server/modules/mcp/services/mcp-provider-registration.service.js +75 -31
  35. package/dist/server/modules/mcp/services/mcp-provider-registration.service.js.map +1 -1
  36. package/dist/server/modules/profiles/controllers/profiles.controller.d.ts +1 -0
  37. package/dist/server/modules/profiles/controllers/profiles.controller.js +13 -0
  38. package/dist/server/modules/profiles/controllers/profiles.controller.js.map +1 -1
  39. package/dist/server/modules/profiles/dto.d.ts +3 -0
  40. package/dist/server/modules/profiles/dto.js +1 -0
  41. package/dist/server/modules/profiles/dto.js.map +1 -1
  42. package/dist/server/modules/projects/controllers/projects.controller.d.ts +61 -11
  43. package/dist/server/modules/projects/controllers/projects.controller.js +81 -2
  44. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  45. package/dist/server/modules/projects/services/projects.service.d.ts +70 -10
  46. package/dist/server/modules/projects/services/projects.service.js +380 -22
  47. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  48. package/dist/server/modules/providers/adapters/claude.adapter.js +12 -2
  49. package/dist/server/modules/providers/adapters/claude.adapter.js.map +1 -1
  50. package/dist/server/modules/providers/adapters/codex.adapter.js +12 -2
  51. package/dist/server/modules/providers/adapters/codex.adapter.js.map +1 -1
  52. package/dist/server/modules/providers/adapters/gemini.adapter.d.ts +9 -0
  53. package/dist/server/modules/providers/adapters/gemini.adapter.js +56 -0
  54. package/dist/server/modules/providers/adapters/gemini.adapter.js.map +1 -0
  55. package/dist/server/modules/providers/adapters/index.d.ts +2 -0
  56. package/dist/server/modules/providers/adapters/index.js +2 -0
  57. package/dist/server/modules/providers/adapters/index.js.map +1 -1
  58. package/dist/server/modules/providers/adapters/provider-adapter.factory.d.ts +4 -1
  59. package/dist/server/modules/providers/adapters/provider-adapter.factory.js +13 -7
  60. package/dist/server/modules/providers/adapters/provider-adapter.factory.js.map +1 -1
  61. package/dist/server/modules/providers/adapters/provider-adapters.module.d.ts +2 -0
  62. package/dist/server/modules/providers/adapters/provider-adapters.module.js +24 -0
  63. package/dist/server/modules/providers/adapters/provider-adapters.module.js.map +1 -0
  64. package/dist/server/modules/providers/controllers/providers.controller.js +3 -4
  65. package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
  66. package/dist/server/modules/providers/providers.module.js +1 -3
  67. package/dist/server/modules/providers/providers.module.js.map +1 -1
  68. package/dist/server/modules/registry/services/template-upgrade.service.d.ts +3 -1
  69. package/dist/server/modules/registry/services/template-upgrade.service.js +66 -17
  70. package/dist/server/modules/registry/services/template-upgrade.service.js.map +1 -1
  71. package/dist/server/modules/registry/services/unified-template.service.d.ts +1 -1
  72. package/dist/server/modules/registry/services/unified-template.service.js +17 -2
  73. package/dist/server/modules/registry/services/unified-template.service.js.map +1 -1
  74. package/dist/server/modules/sessions/services/sessions.service.js +1 -1
  75. package/dist/server/modules/storage/db/schema.d.ts +19 -0
  76. package/dist/server/modules/storage/db/schema.js +1 -0
  77. package/dist/server/modules/storage/db/schema.js.map +1 -1
  78. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +1 -0
  79. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  80. package/dist/server/modules/storage/local/local-storage.service.js +10 -4
  81. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  82. package/dist/server/modules/storage/models/domain.models.d.ts +3 -1
  83. package/dist/server/templates/claude-codex-advanced.json +150 -120
  84. package/dist/server/templates/claude-opus.json +182 -79
  85. package/dist/server/templates/simple-codex.json +288 -74
  86. package/dist/server/tsconfig.tsbuildinfo +1 -1
  87. package/dist/server/ui/assets/index-Bgulh8ua.js +735 -0
  88. package/dist/server/ui/assets/{index-BkGGbapJ.css → index-BoDZOB7c.css} +1 -1
  89. package/dist/server/ui/index.html +2 -2
  90. package/dist/templates/claude-codex-advanced.json +150 -120
  91. package/dist/templates/claude-opus.json +182 -79
  92. package/dist/templates/simple-codex.json +288 -74
  93. package/package.json +10 -1
  94. package/dist/server/modules/events/catalog/epic.assigned.d.ts +0 -32
  95. package/dist/server/modules/events/catalog/epic.assigned.js +0 -19
  96. package/dist/server/modules/events/catalog/epic.assigned.js.map +0 -1
  97. package/dist/server/ui/assets/index-DLpDwHdv.js +0 -733
@@ -128,14 +128,28 @@ let ProjectsService = class ProjectsService {
128
128
  hint: 'Template file does not match expected export schema',
129
129
  });
130
130
  }
131
- const providerNames = new Set((payload.profiles ?? []).map((p) => p.provider.name.trim().toLowerCase()));
132
- const { available, missing: missingProviders } = await this.resolveProviders(providerNames);
133
- if (missingProviders.length > 0) {
134
- throw new error_types_1.ValidationError('Import aborted: missing providers', {
135
- missingProviders,
136
- hint: 'Install/configure providers by name before creating project from template.',
131
+ const familyResult = await this.computeFamilyAlternatives(payload.profiles, payload.agents);
132
+ const needsMapping = familyResult.alternatives.some((alt) => !alt.defaultProviderAvailable);
133
+ if (needsMapping && !input.familyProviderMappings) {
134
+ return {
135
+ success: false,
136
+ providerMappingRequired: {
137
+ missingProviders: familyResult.missingProviders,
138
+ familyAlternatives: familyResult.alternatives,
139
+ canImport: familyResult.canImport,
140
+ },
141
+ };
142
+ }
143
+ if (!familyResult.canImport) {
144
+ throw new error_types_1.ValidationError('Cannot import: some profile families have no available providers', {
145
+ hint: 'Install the required providers or use a different template',
146
+ missingProviders: familyResult.missingProviders,
147
+ familyAlternatives: familyResult.alternatives,
137
148
  });
138
149
  }
150
+ const providerNames = new Set((payload.profiles ?? []).map((p) => p.provider.name.trim().toLowerCase()));
151
+ const { available } = await this.resolveProviders(providerNames);
152
+ const selectedProfilesByFamily = this.selectProfilesForFamilies(payload.profiles, payload.agents, input.familyProviderMappings, available);
139
153
  const templatePayload = {
140
154
  prompts: payload.prompts.map((p) => ({
141
155
  id: p.id,
@@ -144,7 +158,7 @@ let ProjectsService = class ProjectsService {
144
158
  version: p.version,
145
159
  tags: p.tags,
146
160
  })),
147
- profiles: payload.profiles.map((prof) => {
161
+ profiles: selectedProfilesByFamily.profilesToCreate.map((prof) => {
148
162
  const providerId = available.get(prof.provider.name.trim().toLowerCase());
149
163
  if (!providerId) {
150
164
  throw new error_types_1.NotFoundError('Provider', prof.provider.name);
@@ -153,18 +167,22 @@ let ProjectsService = class ProjectsService {
153
167
  id: prof.id,
154
168
  name: prof.name,
155
169
  providerId,
170
+ familySlug: prof.familySlug ?? null,
156
171
  options: this.normalizeProfileOptions(prof.options),
157
172
  instructions: prof.instructions ?? null,
158
173
  temperature: prof.temperature ?? null,
159
174
  maxTokens: prof.maxTokens ?? null,
160
175
  };
161
176
  }),
162
- agents: payload.agents.map((a) => ({
163
- id: a.id,
164
- name: a.name,
165
- profileId: a.profileId,
166
- description: a.description,
167
- })),
177
+ agents: payload.agents.map((a) => {
178
+ const remappedProfileId = selectedProfilesByFamily.agentProfileMap.get(a.id ?? '') ?? a.profileId;
179
+ return {
180
+ id: a.id,
181
+ name: a.name,
182
+ profileId: remappedProfileId,
183
+ description: a.description,
184
+ };
185
+ }),
168
186
  statuses: payload.statuses.map((s) => ({
169
187
  id: s.id,
170
188
  label: s.label,
@@ -221,13 +239,16 @@ let ProjectsService = class ProjectsService {
221
239
  agentNameToId: agentNameToNewId,
222
240
  profileNameToId: profileNameToNewId,
223
241
  providerNameToId: available,
242
+ profileNameRemapMap: selectedProfilesByFamily.profileNameRemapMap,
224
243
  });
225
244
  const { created: subscribersCreated } = await this.createSubscribersFromPayload(result.project.id, payload.subscribers);
245
+ const manifestVersion = payload._manifest?.version ?? null;
246
+ const installedVersion = templateResult.version ?? manifestVersion;
226
247
  const registryConfig = this.settings.getRegistryConfig();
227
248
  await this.settings.setProjectTemplateMetadata(result.project.id, {
228
249
  templateSlug: input.slug,
229
250
  source: templateResult.source,
230
- installedVersion: templateResult.version,
251
+ installedVersion,
231
252
  registryUrl: templateResult.source === 'registry' ? registryConfig.url : null,
232
253
  installedAt: new Date().toISOString(),
233
254
  });
@@ -235,7 +256,7 @@ let ProjectsService = class ProjectsService {
235
256
  projectId: result.project.id,
236
257
  slug: input.slug,
237
258
  source: templateResult.source,
238
- version: templateResult.version,
259
+ version: installedVersion,
239
260
  }, 'Template metadata set for project');
240
261
  return {
241
262
  success: true,
@@ -324,6 +345,7 @@ let ProjectsService = class ProjectsService {
324
345
  id: prof.id,
325
346
  name: prof.name,
326
347
  provider: { id: provider.id, name: provider.name },
348
+ familySlug: prof.familySlug,
327
349
  options: sanitizeOptionsString(prof.options),
328
350
  instructions: prof.instructions,
329
351
  temperature: prof.temperature,
@@ -452,8 +474,11 @@ let ProjectsService = class ProjectsService {
452
474
  logger.info({ projectId: input.projectId, dryRun: input.dryRun }, 'importProject');
453
475
  const isDryRun = input.dryRun ?? false;
454
476
  const payload = shared_1.ExportSchema.parse(input.payload ?? {});
477
+ const familyResult = await this.computeFamilyAlternatives(payload.profiles, payload.agents);
478
+ const needsMapping = familyResult.alternatives.some((alt) => !alt.defaultProviderAvailable);
455
479
  const providerNames = new Set((payload.profiles ?? []).map((p) => p.provider.name.trim().toLowerCase()));
456
480
  const { available, missing: missingProviders } = await this.resolveProviders(providerNames);
481
+ const selectedProfilesByFamily = this.selectProfilesForFamilies(payload.profiles, payload.agents, input.familyProviderMappings, available);
457
482
  const [existingPrompts, existingProfiles, existingAgents, existingStatuses, existingWatchers, existingSubscribers,] = await Promise.all([
458
483
  this.storage.listPrompts({ projectId: input.projectId, limit: 10000, offset: 0 }),
459
484
  this.storage.listAgentProfiles({ projectId: input.projectId, limit: 10000, offset: 0 }),
@@ -479,7 +504,7 @@ let ProjectsService = class ProjectsService {
479
504
  }
480
505
  }
481
506
  if (isDryRun) {
482
- return {
507
+ const dryRunResponse = {
483
508
  dryRun: true,
484
509
  missingProviders,
485
510
  unmatchedStatuses,
@@ -490,7 +515,7 @@ let ProjectsService = class ProjectsService {
490
515
  counts: {
491
516
  toImport: {
492
517
  prompts: payload.prompts.length,
493
- profiles: payload.profiles.length,
518
+ profiles: selectedProfilesByFamily.profilesToCreate.length,
494
519
  agents: payload.agents.length,
495
520
  statuses: payload.statuses.length,
496
521
  watchers: payload.watchers.length,
@@ -506,10 +531,37 @@ let ProjectsService = class ProjectsService {
506
531
  },
507
532
  },
508
533
  };
534
+ if (needsMapping && !input.familyProviderMappings) {
535
+ dryRunResponse.providerMappingRequired = {
536
+ missingProviders: familyResult.missingProviders,
537
+ familyAlternatives: familyResult.alternatives,
538
+ canImport: familyResult.canImport,
539
+ };
540
+ }
541
+ return dryRunResponse;
509
542
  }
510
- if (missingProviders.length > 0) {
543
+ if (needsMapping && !input.familyProviderMappings) {
544
+ return {
545
+ success: false,
546
+ providerMappingRequired: {
547
+ missingProviders: familyResult.missingProviders,
548
+ familyAlternatives: familyResult.alternatives,
549
+ canImport: familyResult.canImport,
550
+ },
551
+ };
552
+ }
553
+ if (!familyResult.canImport) {
554
+ throw new error_types_1.ValidationError('Cannot import: some profile families have no available providers', {
555
+ hint: 'Install the required providers or use a different template',
556
+ missingProviders: familyResult.missingProviders,
557
+ familyAlternatives: familyResult.alternatives,
558
+ });
559
+ }
560
+ const selectedProviderNames = new Set(selectedProfilesByFamily.profilesToCreate.map((p) => p.provider.name.trim().toLowerCase()));
561
+ const unavailableSelectedProviders = Array.from(selectedProviderNames).filter((name) => !available.has(name));
562
+ if (unavailableSelectedProviders.length > 0) {
511
563
  throw new error_types_1.ValidationError('Import aborted: missing providers', {
512
- missingProviders,
564
+ missingProviders: unavailableSelectedProviders,
513
565
  hint: 'Install/configure providers by name before importing profiles.',
514
566
  });
515
567
  }
@@ -615,7 +667,7 @@ let ProjectsService = class ProjectsService {
615
667
  promptIdMap[p.id] = created.id;
616
668
  createdPrompts.push({ id: created.id, title: created.title });
617
669
  }
618
- for (const prof of payload.profiles) {
670
+ for (const prof of selectedProfilesByFamily.profilesToCreate) {
619
671
  const providerId = available.get(prof.provider.name.trim().toLowerCase());
620
672
  if (!providerId) {
621
673
  throw new error_types_1.NotFoundError('Provider', prof.provider.name);
@@ -624,6 +676,7 @@ let ProjectsService = class ProjectsService {
624
676
  projectId: input.projectId,
625
677
  name: prof.name,
626
678
  providerId,
679
+ familySlug: prof.familySlug ?? null,
627
680
  options: this.normalizeProfileOptions(prof.options),
628
681
  systemPrompt: null,
629
682
  instructions: prof.instructions ?? null,
@@ -634,7 +687,8 @@ let ProjectsService = class ProjectsService {
634
687
  profileIdMap[profKey] = created.id;
635
688
  }
636
689
  for (const a of payload.agents) {
637
- const oldProfileId = a.profileId ?? '';
690
+ const remappedProfileId = selectedProfilesByFamily.agentProfileMap.get(a.id ?? '');
691
+ const oldProfileId = remappedProfileId ?? a.profileId ?? '';
638
692
  const newProfileId = oldProfileId && profileIdMap[oldProfileId] ? profileIdMap[oldProfileId] : undefined;
639
693
  if (!newProfileId) {
640
694
  throw new error_types_1.ValidationError(`Profile mapping missing for agent ${a.name}`, {
@@ -655,7 +709,7 @@ let ProjectsService = class ProjectsService {
655
709
  ...a,
656
710
  id: a.id || `name:${a.name.trim().toLowerCase()}`,
657
711
  })),
658
- profiles: payload.profiles.map((p) => ({
712
+ profiles: selectedProfilesByFamily.profilesToCreate.map((p) => ({
659
713
  ...p,
660
714
  id: p.id || `name:${p.name.trim().toLowerCase()}`,
661
715
  })),
@@ -665,6 +719,7 @@ let ProjectsService = class ProjectsService {
665
719
  agentNameToId: agentNameToNewId,
666
720
  profileNameToId: profileNameToNewId,
667
721
  providerNameToId: available,
722
+ profileNameRemapMap: selectedProfilesByFamily.profileNameRemapMap,
668
723
  });
669
724
  const { subscriberIdMap } = await this.createSubscribersFromPayload(input.projectId, payload.subscribers);
670
725
  logger.info({
@@ -723,6 +778,24 @@ let ProjectsService = class ProjectsService {
723
778
  const archiveStatusId = templateLabelToStatusId.get('archive') ?? null;
724
779
  const settingsResult = await this.applyProjectSettings(input.projectId, mergedSettings, { promptTitleToId, statusLabelToId: templateLabelToStatusId }, archiveStatusId);
725
780
  const initialPromptSet = settingsResult.initialPromptSet;
781
+ if (payload._manifest?.slug) {
782
+ let templateSource = 'registry';
783
+ try {
784
+ this.unifiedTemplateService.getBundledTemplate(payload._manifest.slug);
785
+ templateSource = 'bundled';
786
+ }
787
+ catch {
788
+ templateSource = 'registry';
789
+ }
790
+ await this.settings.setProjectTemplateMetadata(input.projectId, {
791
+ templateSlug: payload._manifest.slug,
792
+ source: templateSource,
793
+ installedVersion: payload._manifest.version ?? null,
794
+ registryUrl: null,
795
+ installedAt: new Date().toISOString(),
796
+ });
797
+ logger.info({ projectId: input.projectId, slug: payload._manifest.slug, source: templateSource }, 'Updated template metadata after import');
798
+ }
726
799
  return {
727
800
  success: true,
728
801
  mode: 'replace',
@@ -778,6 +851,181 @@ let ProjectsService = class ProjectsService {
778
851
  const missing = Array.from(providerNames).filter((n) => !available.has(n));
779
852
  return { available, missing };
780
853
  }
854
+ async computeFamilyAlternatives(templateProfiles, templateAgents) {
855
+ const localProviders = await this.storage.listProviders();
856
+ const availableProviderNames = new Set(localProviders.items.map((p) => p.name.trim().toLowerCase()));
857
+ const profileById = new Map();
858
+ for (const prof of templateProfiles) {
859
+ if (prof.id) {
860
+ profileById.set(prof.id, prof);
861
+ }
862
+ }
863
+ const usedFamilySlugs = new Set();
864
+ for (const agent of templateAgents) {
865
+ if (agent.profileId) {
866
+ const profile = profileById.get(agent.profileId);
867
+ if (profile?.familySlug) {
868
+ usedFamilySlugs.add(profile.familySlug);
869
+ }
870
+ }
871
+ }
872
+ const familyProviders = new Map();
873
+ for (const prof of templateProfiles) {
874
+ const familySlug = prof.familySlug;
875
+ if (!familySlug)
876
+ continue;
877
+ if (!familyProviders.has(familySlug)) {
878
+ familyProviders.set(familySlug, new Map());
879
+ }
880
+ const providerName = prof.provider.name.trim().toLowerCase();
881
+ const familyMap = familyProviders.get(familySlug);
882
+ if (!familyMap.has(providerName)) {
883
+ familyMap.set(providerName, []);
884
+ }
885
+ familyMap.get(providerName).push(prof.name);
886
+ }
887
+ const alternatives = [];
888
+ const allMissingProviders = new Set();
889
+ let canImport = true;
890
+ for (const familySlug of usedFamilySlugs) {
891
+ const providersForFamily = familyProviders.get(familySlug);
892
+ if (!providersForFamily || providersForFamily.size === 0) {
893
+ logger.warn({ familySlug }, 'Family used by agent has no profiles');
894
+ continue;
895
+ }
896
+ const providerNamesForFamily = Array.from(providersForFamily.keys());
897
+ const defaultProvider = providerNamesForFamily[0];
898
+ const defaultProviderAvailable = availableProviderNames.has(defaultProvider);
899
+ const availableForFamily = providerNamesForFamily.filter((name) => availableProviderNames.has(name));
900
+ for (const provName of providerNamesForFamily) {
901
+ if (!availableProviderNames.has(provName)) {
902
+ allMissingProviders.add(provName);
903
+ }
904
+ }
905
+ const hasAlternatives = availableForFamily.length > 0;
906
+ if (!hasAlternatives) {
907
+ canImport = false;
908
+ }
909
+ alternatives.push({
910
+ familySlug,
911
+ defaultProvider,
912
+ defaultProviderAvailable,
913
+ availableProviders: availableForFamily.sort(),
914
+ hasAlternatives,
915
+ });
916
+ }
917
+ return {
918
+ alternatives,
919
+ missingProviders: Array.from(allMissingProviders).sort(),
920
+ canImport,
921
+ };
922
+ }
923
+ selectProfilesForFamilies(templateProfiles, templateAgents, familyProviderMappings, availableProviders) {
924
+ const profileById = new Map();
925
+ for (const prof of templateProfiles) {
926
+ if (prof.id) {
927
+ profileById.set(prof.id, prof);
928
+ }
929
+ }
930
+ const profilesByFamilyAndProvider = new Map();
931
+ for (const prof of templateProfiles) {
932
+ if (!prof.familySlug)
933
+ continue;
934
+ const family = prof.familySlug;
935
+ const providerName = prof.provider.name.trim().toLowerCase();
936
+ if (!profilesByFamilyAndProvider.has(family)) {
937
+ profilesByFamilyAndProvider.set(family, new Map());
938
+ }
939
+ const familyMap = profilesByFamilyAndProvider.get(family);
940
+ if (!familyMap.has(providerName)) {
941
+ familyMap.set(providerName, prof);
942
+ }
943
+ }
944
+ const familyOriginalProviders = new Map();
945
+ for (const agent of templateAgents) {
946
+ if (!agent.profileId)
947
+ continue;
948
+ const profile = profileById.get(agent.profileId);
949
+ if (!profile?.familySlug)
950
+ continue;
951
+ const providerName = profile.provider.name.trim().toLowerCase();
952
+ familyOriginalProviders.set(profile.familySlug, providerName);
953
+ }
954
+ const selectedProfileIdsByFamily = new Map();
955
+ for (const [familySlug, providerMap] of profilesByFamilyAndProvider) {
956
+ let selectedProvider;
957
+ if (familyProviderMappings?.[familySlug]) {
958
+ selectedProvider = familyProviderMappings[familySlug].trim().toLowerCase();
959
+ }
960
+ else {
961
+ const originalProvider = familyOriginalProviders.get(familySlug);
962
+ if (originalProvider &&
963
+ availableProviders.has(originalProvider) &&
964
+ providerMap.has(originalProvider)) {
965
+ selectedProvider = originalProvider;
966
+ }
967
+ else {
968
+ for (const provName of providerMap.keys()) {
969
+ if (availableProviders.has(provName)) {
970
+ selectedProvider = provName;
971
+ break;
972
+ }
973
+ }
974
+ }
975
+ }
976
+ if (selectedProvider && providerMap.has(selectedProvider)) {
977
+ const profile = providerMap.get(selectedProvider);
978
+ if (profile.id) {
979
+ selectedProfileIdsByFamily.set(familySlug, profile.id);
980
+ }
981
+ }
982
+ }
983
+ const profilesToCreate = [];
984
+ const usedProfileIds = new Set();
985
+ for (const prof of templateProfiles) {
986
+ if (!prof.id || usedProfileIds.has(prof.id))
987
+ continue;
988
+ const providerName = prof.provider.name.trim().toLowerCase();
989
+ if (availableProviders.has(providerName)) {
990
+ usedProfileIds.add(prof.id);
991
+ profilesToCreate.push(prof);
992
+ }
993
+ }
994
+ const agentProfileMap = new Map();
995
+ for (const agent of templateAgents) {
996
+ if (!agent.id || !agent.profileId)
997
+ continue;
998
+ const originalProfile = profileById.get(agent.profileId);
999
+ if (!originalProfile) {
1000
+ agentProfileMap.set(agent.id, agent.profileId);
1001
+ continue;
1002
+ }
1003
+ if (originalProfile.familySlug) {
1004
+ const selectedProfileId = selectedProfileIdsByFamily.get(originalProfile.familySlug);
1005
+ agentProfileMap.set(agent.id, selectedProfileId ?? agent.profileId);
1006
+ }
1007
+ else {
1008
+ agentProfileMap.set(agent.id, agent.profileId);
1009
+ }
1010
+ }
1011
+ const profileNameRemapMap = new Map();
1012
+ for (const [familySlug, providerMap] of profilesByFamilyAndProvider) {
1013
+ const selectedProfileId = selectedProfileIdsByFamily.get(familySlug);
1014
+ const selectedProfile = selectedProfileId
1015
+ ? templateProfiles.find((p) => p.id === selectedProfileId)
1016
+ : undefined;
1017
+ if (selectedProfile) {
1018
+ const selectedNameLower = selectedProfile.name.trim().toLowerCase();
1019
+ for (const profile of providerMap.values()) {
1020
+ const profileNameLower = profile.name.trim().toLowerCase();
1021
+ if (profileNameLower !== selectedNameLower) {
1022
+ profileNameRemapMap.set(profileNameLower, selectedNameLower);
1023
+ }
1024
+ }
1025
+ }
1026
+ }
1027
+ return { profilesToCreate, agentProfileMap, profileNameRemapMap };
1028
+ }
781
1029
  buildNameToIdMaps(payload, mappings) {
782
1030
  const agentNameToId = new Map();
783
1031
  for (const a of payload.agents) {
@@ -823,6 +1071,20 @@ let ProjectsService = class ProjectsService {
823
1071
  }
824
1072
  case 'profile': {
825
1073
  scopeFilterId = maps.profileNameToId.get(scopeFilterNameLower) ?? null;
1074
+ if (!scopeFilterId && maps.profileNameRemapMap) {
1075
+ const remappedName = maps.profileNameRemapMap.get(scopeFilterNameLower);
1076
+ if (remappedName) {
1077
+ scopeFilterId = maps.profileNameToId.get(remappedName) ?? null;
1078
+ if (scopeFilterId) {
1079
+ logger.info({
1080
+ projectId,
1081
+ watcherName: w.name,
1082
+ originalProfile: w.scopeFilterName,
1083
+ remappedProfile: remappedName,
1084
+ }, 'Watcher profile scope remapped due to provider family selection');
1085
+ }
1086
+ }
1087
+ }
826
1088
  break;
827
1089
  }
828
1090
  case 'provider': {
@@ -999,6 +1261,102 @@ let ProjectsService = class ProjectsService {
999
1261
  .replace(/[^a-z0-9]+/g, '-')
1000
1262
  .replace(/(^-|-$)/g, '');
1001
1263
  }
1264
+ async getTemplateManifestForProject(projectId) {
1265
+ const metadata = this.settings.getProjectTemplateMetadata(projectId);
1266
+ if (!metadata?.templateSlug) {
1267
+ logger.debug({ projectId }, 'No template metadata for project');
1268
+ return null;
1269
+ }
1270
+ try {
1271
+ if (metadata.source === 'bundled') {
1272
+ const template = this.unifiedTemplateService.getBundledTemplate(metadata.templateSlug);
1273
+ return template.content._manifest ?? null;
1274
+ }
1275
+ else {
1276
+ const template = await this.unifiedTemplateService.getTemplate(metadata.templateSlug, metadata.installedVersion ?? undefined);
1277
+ if (template.source !== 'registry') {
1278
+ logger.debug({
1279
+ projectId,
1280
+ templateSlug: metadata.templateSlug,
1281
+ expectedSource: 'registry',
1282
+ actualSource: template.source,
1283
+ }, 'Template source mismatch - registry template not available, rejecting bundled fallback');
1284
+ return null;
1285
+ }
1286
+ return template.content._manifest ?? null;
1287
+ }
1288
+ }
1289
+ catch (error) {
1290
+ logger.debug({ projectId, templateSlug: metadata.templateSlug, error }, 'Failed to fetch template manifest for project');
1291
+ return null;
1292
+ }
1293
+ }
1294
+ getBundledUpgradeVersion(templateSlug, installedVersion) {
1295
+ if (!installedVersion) {
1296
+ return null;
1297
+ }
1298
+ try {
1299
+ const bundled = this.unifiedTemplateService.getBundledTemplate(templateSlug);
1300
+ const manifest = bundled.content._manifest;
1301
+ const bundledVersion = manifest?.version;
1302
+ if (!bundledVersion) {
1303
+ return null;
1304
+ }
1305
+ if (!(0, shared_1.isValidSemVer)(installedVersion) || !(0, shared_1.isValidSemVer)(bundledVersion)) {
1306
+ logger.warn({ templateSlug, installedVersion, bundledVersion }, 'Invalid semver version detected, skipping upgrade check');
1307
+ return null;
1308
+ }
1309
+ if ((0, shared_1.isLessThan)(installedVersion, bundledVersion)) {
1310
+ return bundledVersion;
1311
+ }
1312
+ return null;
1313
+ }
1314
+ catch {
1315
+ return null;
1316
+ }
1317
+ }
1318
+ getBundledUpgradesForProjects(projects) {
1319
+ const result = new Map();
1320
+ const bundledVersionCache = new Map();
1321
+ for (const project of projects) {
1322
+ if (project.source !== 'bundled' || !project.templateSlug) {
1323
+ result.set(project.projectId, null);
1324
+ continue;
1325
+ }
1326
+ if (!bundledVersionCache.has(project.templateSlug)) {
1327
+ try {
1328
+ const bundled = this.unifiedTemplateService.getBundledTemplate(project.templateSlug);
1329
+ const manifest = bundled.content._manifest;
1330
+ bundledVersionCache.set(project.templateSlug, manifest?.version ?? null);
1331
+ }
1332
+ catch {
1333
+ bundledVersionCache.set(project.templateSlug, null);
1334
+ }
1335
+ }
1336
+ const bundledVersion = bundledVersionCache.get(project.templateSlug);
1337
+ if (!bundledVersion || !project.installedVersion) {
1338
+ result.set(project.projectId, null);
1339
+ continue;
1340
+ }
1341
+ if (!(0, shared_1.isValidSemVer)(project.installedVersion) || !(0, shared_1.isValidSemVer)(bundledVersion)) {
1342
+ logger.warn({
1343
+ projectId: project.projectId,
1344
+ templateSlug: project.templateSlug,
1345
+ installedVersion: project.installedVersion,
1346
+ bundledVersion,
1347
+ }, 'Invalid semver version detected, skipping upgrade check');
1348
+ result.set(project.projectId, null);
1349
+ continue;
1350
+ }
1351
+ if ((0, shared_1.isLessThan)(project.installedVersion, bundledVersion)) {
1352
+ result.set(project.projectId, bundledVersion);
1353
+ }
1354
+ else {
1355
+ result.set(project.projectId, null);
1356
+ }
1357
+ }
1358
+ return result;
1359
+ }
1002
1360
  };
1003
1361
  exports.ProjectsService = ProjectsService;
1004
1362
  exports.ProjectsService = ProjectsService = __decorate([