@cat-factory/node-server 0.6.0 → 0.7.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 (41) hide show
  1. package/LICENSE +21 -21
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +43 -2
  4. package/dist/config.js.map +1 -1
  5. package/dist/container.d.ts +10 -1
  6. package/dist/container.d.ts.map +1 -1
  7. package/dist/container.js +133 -26
  8. package/dist/container.js.map +1 -1
  9. package/dist/db/schema.d.ts +362 -18
  10. package/dist/db/schema.d.ts.map +1 -1
  11. package/dist/db/schema.js +55 -14
  12. package/dist/db/schema.js.map +1 -1
  13. package/dist/execution/config.d.ts +0 -2
  14. package/dist/execution/config.d.ts.map +1 -1
  15. package/dist/execution/config.js +1 -2
  16. package/dist/execution/config.js.map +1 -1
  17. package/dist/execution/pgBossRunner.d.ts +0 -11
  18. package/dist/execution/pgBossRunner.d.ts.map +1 -1
  19. package/dist/execution/pgBossRunner.js +5 -45
  20. package/dist/execution/pgBossRunner.js.map +1 -1
  21. package/dist/notifications.d.ts +11 -0
  22. package/dist/notifications.d.ts.map +1 -0
  23. package/dist/notifications.js +32 -0
  24. package/dist/notifications.js.map +1 -0
  25. package/dist/repositories/drizzle.d.ts +35 -13
  26. package/dist/repositories/drizzle.d.ts.map +1 -1
  27. package/dist/repositories/drizzle.js +177 -46
  28. package/dist/repositories/drizzle.js.map +1 -1
  29. package/dist/repositories/notifications.d.ts.map +1 -1
  30. package/dist/repositories/notifications.js +3 -0
  31. package/dist/repositories/notifications.js.map +1 -1
  32. package/dist/server.d.ts.map +1 -1
  33. package/dist/server.js +8 -5
  34. package/dist/server.js.map +1 -1
  35. package/drizzle/20260624145108_volatile_the_renegades/migration.sql +4 -0
  36. package/drizzle/20260624145108_volatile_the_renegades/snapshot.json +9178 -0
  37. package/package.json +19 -14
  38. package/dist/tasks/JiraProvider.d.ts +0 -27
  39. package/dist/tasks/JiraProvider.d.ts.map +0 -1
  40. package/dist/tasks/JiraProvider.js +0 -79
  41. package/dist/tasks/JiraProvider.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { LLM_WARNING_FINISH_REASONS } from '@cat-factory/kernel';
2
2
  import { blockInsertValues, blockPatchToColumns, rowToBlock, rowToExecution, executionToDetail, rowToPipeline, rowToWorkspace, } from '@cat-factory/server';
3
3
  import { and, desc, eq, gte, inArray, isNull, lt, or, sql } from 'drizzle-orm';
4
- import { accountInvitations, accounts, agentRuns, blocks, consensusSessions, emailConnections, llmCallMetrics, memberships, mergeThresholdPresets, repoBlueprints, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, clarityReviews, services, tokenUsage, trackerSettings, userIdentities, users, workspaceFragmentDefaults, workspaceModelDefaults, workspaceServices, workspaces, } from '../db/schema.js';
4
+ import { accountInvitations, accounts, agentRuns, blocks, consensusSessions, datadogConnections, emailConnections, llmCallMetrics, memberships, mergeThresholdPresets, releaseHealthConfigs, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, clarityReviews, services, tokenUsage, trackerSettings, userIdentities, users, workspaceFragmentDefaults, workspaceModelDefaults, workspaceServices, workspaceSettings, workspaces, } from '../db/schema.js';
5
5
  // Drizzle/Postgres implementations of the core kernel repository ports. The
6
6
  // row<->domain mapping is the SAME shared mapping the Cloudflare D1 repos use
7
7
  // (@cat-factory/server), so behaviour matches across stores; this layer only owns
@@ -181,6 +181,9 @@ class DrizzlePipelineRepository {
181
181
  thresholds: pipeline.thresholds ? JSON.stringify(pipeline.thresholds) : null,
182
182
  enabled: pipeline.enabled ? JSON.stringify(pipeline.enabled) : null,
183
183
  consensus: pipeline.consensus ? JSON.stringify(pipeline.consensus) : null,
184
+ gating: pipeline.gating ? JSON.stringify(pipeline.gating) : null,
185
+ labels: pipeline.labels ? JSON.stringify(pipeline.labels) : null,
186
+ archived: pipeline.archived ? 1 : null,
184
187
  builtin: pipeline.builtin ? 1 : null,
185
188
  });
186
189
  }
@@ -196,6 +199,9 @@ class DrizzlePipelineRepository {
196
199
  thresholds: pipeline.thresholds ? JSON.stringify(pipeline.thresholds) : null,
197
200
  enabled: pipeline.enabled ? JSON.stringify(pipeline.enabled) : null,
198
201
  consensus: pipeline.consensus ? JSON.stringify(pipeline.consensus) : null,
202
+ gating: pipeline.gating ? JSON.stringify(pipeline.gating) : null,
203
+ labels: pipeline.labels ? JSON.stringify(pipeline.labels) : null,
204
+ archived: pipeline.archived ? 1 : null,
199
205
  })
200
206
  .where(and(eq(pipelines.workspace_id, workspaceId), eq(pipelines.id, pipeline.id)));
201
207
  }
@@ -1148,6 +1154,8 @@ class DrizzleTrackerSettingsRepository {
1148
1154
  return {
1149
1155
  tracker: row.tracker ?? null,
1150
1156
  jiraProjectKey: row.jira_project_key,
1157
+ writebackCommentOnPrOpen: row.writeback_comment_on_pr_open === 1,
1158
+ writebackResolveOnMerge: row.writeback_resolve_on_merge === 1,
1151
1159
  updatedAt: row.updated_at,
1152
1160
  };
1153
1161
  }
@@ -1158,6 +1166,8 @@ class DrizzleTrackerSettingsRepository {
1158
1166
  workspace_id: workspaceId,
1159
1167
  tracker: settings.tracker,
1160
1168
  jira_project_key: settings.jiraProjectKey,
1169
+ writeback_comment_on_pr_open: settings.writebackCommentOnPrOpen ? 1 : 0,
1170
+ writeback_resolve_on_merge: settings.writebackResolveOnMerge ? 1 : 0,
1161
1171
  updated_at: settings.updatedAt,
1162
1172
  })
1163
1173
  .onConflictDoUpdate({
@@ -1165,6 +1175,8 @@ class DrizzleTrackerSettingsRepository {
1165
1175
  set: {
1166
1176
  tracker: settings.tracker,
1167
1177
  jira_project_key: settings.jiraProjectKey,
1178
+ writeback_comment_on_pr_open: settings.writebackCommentOnPrOpen ? 1 : 0,
1179
+ writeback_resolve_on_merge: settings.writebackResolveOnMerge ? 1 : 0,
1168
1180
  updated_at: settings.updatedAt,
1169
1181
  },
1170
1182
  });
@@ -1674,6 +1686,8 @@ function rowToMergePreset(row) {
1674
1686
  ciMaxAttempts: row.ci_max_attempts,
1675
1687
  maxRequirementIterations: row.max_requirement_iterations,
1676
1688
  maxRequirementConcernAllowed: row.max_requirement_concern_allowed,
1689
+ releaseWatchWindowMinutes: row.release_watch_window_minutes,
1690
+ releaseMaxAttempts: row.release_max_attempts,
1677
1691
  isDefault: row.is_default === 1,
1678
1692
  createdAt: row.created_at,
1679
1693
  };
@@ -1727,6 +1741,8 @@ export class DrizzleMergePresetRepository {
1727
1741
  ci_max_attempts: preset.ciMaxAttempts,
1728
1742
  max_requirement_iterations: preset.maxRequirementIterations,
1729
1743
  max_requirement_concern_allowed: preset.maxRequirementConcernAllowed,
1744
+ release_watch_window_minutes: preset.releaseWatchWindowMinutes,
1745
+ release_max_attempts: preset.releaseMaxAttempts,
1730
1746
  is_default: preset.isDefault ? 1 : 0,
1731
1747
  created_at: preset.createdAt,
1732
1748
  };
@@ -1753,6 +1769,8 @@ export class DrizzleMergePresetRepository {
1753
1769
  ci_max_attempts: values.ci_max_attempts,
1754
1770
  max_requirement_iterations: values.max_requirement_iterations,
1755
1771
  max_requirement_concern_allowed: values.max_requirement_concern_allowed,
1772
+ release_watch_window_minutes: values.release_watch_window_minutes,
1773
+ release_max_attempts: values.release_max_attempts,
1756
1774
  is_default: values.is_default,
1757
1775
  },
1758
1776
  });
@@ -1764,81 +1782,192 @@ export class DrizzleMergePresetRepository {
1764
1782
  .where(and(eq(mergeThresholdPresets.workspace_id, workspaceId), eq(mergeThresholdPresets.id, id), eq(mergeThresholdPresets.is_default, 0)));
1765
1783
  }
1766
1784
  }
1767
- function rowToBlueprintRecord(row) {
1768
- return {
1769
- id: row.id,
1770
- workspaceId: row.workspace_id,
1771
- repoOwner: row.repo_owner,
1772
- repoName: row.repo_name,
1773
- source: row.source,
1774
- service: JSON.parse(row.service_json),
1775
- createdAt: row.created_at,
1776
- updatedAt: row.updated_at,
1777
- };
1785
+ /**
1786
+ * Per-workspace runtime settings over Postgres (the Drizzle mirror of the Worker's
1787
+ * `D1WorkspaceSettingsRepository`, migration 0004). One row per workspace; the service
1788
+ * lazily seeds the default, so an absent row reads as null. Per-type task limits are a
1789
+ * JSON column.
1790
+ */
1791
+ export class DrizzleWorkspaceSettingsRepository {
1792
+ db;
1793
+ constructor(db) {
1794
+ this.db = db;
1795
+ }
1796
+ async get(workspaceId) {
1797
+ const rows = await this.db
1798
+ .select()
1799
+ .from(workspaceSettings)
1800
+ .where(eq(workspaceSettings.workspace_id, workspaceId))
1801
+ .limit(1);
1802
+ const row = rows[0];
1803
+ if (!row)
1804
+ return null;
1805
+ let perType = null;
1806
+ if (row.task_limit_per_type) {
1807
+ try {
1808
+ perType = JSON.parse(row.task_limit_per_type);
1809
+ }
1810
+ catch {
1811
+ perType = null;
1812
+ }
1813
+ }
1814
+ return {
1815
+ waitingEscalationMinutes: row.waiting_escalation_minutes,
1816
+ taskLimitMode: row.task_limit_mode,
1817
+ taskLimitShared: row.task_limit_shared,
1818
+ taskLimitPerType: perType,
1819
+ };
1820
+ }
1821
+ async upsert(workspaceId, settings) {
1822
+ const values = {
1823
+ workspace_id: workspaceId,
1824
+ waiting_escalation_minutes: settings.waitingEscalationMinutes,
1825
+ task_limit_mode: settings.taskLimitMode,
1826
+ task_limit_shared: settings.taskLimitShared,
1827
+ task_limit_per_type: settings.taskLimitPerType
1828
+ ? JSON.stringify(settings.taskLimitPerType)
1829
+ : null,
1830
+ };
1831
+ await this.db
1832
+ .insert(workspaceSettings)
1833
+ .values(values)
1834
+ .onConflictDoUpdate({
1835
+ target: [workspaceSettings.workspace_id],
1836
+ set: {
1837
+ waiting_escalation_minutes: values.waiting_escalation_minutes,
1838
+ task_limit_mode: values.task_limit_mode,
1839
+ task_limit_shared: values.task_limit_shared,
1840
+ task_limit_per_type: values.task_limit_per_type,
1841
+ },
1842
+ });
1843
+ }
1778
1844
  }
1779
1845
  /**
1780
- * Repository blueprints over Postgres (the Drizzle mirror of the Worker's
1781
- * `D1RepoBlueprintRepository`, migration 0011). One row per (workspace, repo):
1782
- * `upsert` replaces the existing blueprint in place, keyed by the unique
1783
- * `(workspace_id, repo_owner, repo_name)` index, so the row is always the single
1784
- * current decomposition. The service → modules tree is persisted whole as JSON.
1846
+ * A workspace's Datadog connection over Postgres (the Drizzle mirror of the Worker's
1847
+ * `D1DatadogConnectionRepository`, migration 0003). One row per workspace; the keys are
1848
+ * stored as sealed envelopes (encrypted by the caller).
1785
1849
  */
1786
- export class DrizzleRepoBlueprintRepository {
1850
+ export class DrizzleDatadogConnectionRepository {
1787
1851
  db;
1788
1852
  constructor(db) {
1789
1853
  this.db = db;
1790
1854
  }
1855
+ async get(workspaceId) {
1856
+ const rows = await this.db
1857
+ .select()
1858
+ .from(datadogConnections)
1859
+ .where(eq(datadogConnections.workspace_id, workspaceId))
1860
+ .limit(1);
1861
+ const row = rows[0];
1862
+ if (!row)
1863
+ return null;
1864
+ return {
1865
+ workspaceId: row.workspace_id,
1866
+ site: row.site,
1867
+ apiKey: row.api_key,
1868
+ appKey: row.app_key,
1869
+ createdAt: row.created_at,
1870
+ updatedAt: row.updated_at,
1871
+ };
1872
+ }
1791
1873
  async upsert(record) {
1792
1874
  const values = {
1793
- id: record.id,
1794
1875
  workspace_id: record.workspaceId,
1795
- repo_owner: record.repoOwner,
1796
- repo_name: record.repoName,
1797
- source: record.source,
1798
- service_json: JSON.stringify(record.service),
1876
+ site: record.site,
1877
+ api_key: record.apiKey,
1878
+ app_key: record.appKey,
1799
1879
  created_at: record.createdAt,
1800
1880
  updated_at: record.updatedAt,
1801
1881
  };
1802
1882
  await this.db
1803
- .insert(repoBlueprints)
1883
+ .insert(datadogConnections)
1804
1884
  .values(values)
1805
1885
  .onConflictDoUpdate({
1806
- target: [repoBlueprints.workspace_id, repoBlueprints.repo_owner, repoBlueprints.repo_name],
1886
+ target: datadogConnections.workspace_id,
1807
1887
  set: {
1808
- source: values.source,
1809
- service_json: values.service_json,
1888
+ site: values.site,
1889
+ api_key: values.api_key,
1890
+ app_key: values.app_key,
1810
1891
  updated_at: values.updated_at,
1811
1892
  },
1812
1893
  });
1813
1894
  }
1814
- async get(workspaceId, id) {
1815
- const rows = await this.db
1816
- .select()
1817
- .from(repoBlueprints)
1818
- .where(and(eq(repoBlueprints.workspace_id, workspaceId), eq(repoBlueprints.id, id)))
1819
- .limit(1);
1820
- return rows[0] ? rowToBlueprintRecord(rows[0]) : null;
1895
+ async delete(workspaceId) {
1896
+ await this.db.delete(datadogConnections).where(eq(datadogConnections.workspace_id, workspaceId));
1897
+ }
1898
+ }
1899
+ function parseReleaseIds(json) {
1900
+ try {
1901
+ const parsed = JSON.parse(json);
1902
+ return Array.isArray(parsed) ? parsed.map((x) => String(x)) : [];
1903
+ }
1904
+ catch {
1905
+ return [];
1906
+ }
1907
+ }
1908
+ function rowToReleaseHealthConfig(row) {
1909
+ return {
1910
+ workspaceId: row.workspace_id,
1911
+ blockId: row.block_id,
1912
+ monitorIds: parseReleaseIds(row.monitor_ids),
1913
+ sloIds: parseReleaseIds(row.slo_ids),
1914
+ envTag: row.env_tag,
1915
+ createdAt: row.created_at,
1916
+ updatedAt: row.updated_at,
1917
+ };
1918
+ }
1919
+ /**
1920
+ * Per-block monitor/SLO mapping for the post-release-health gate over Postgres (the
1921
+ * Drizzle mirror of the Worker's `D1ReleaseHealthConfigRepository`, migration 0003).
1922
+ */
1923
+ export class DrizzleReleaseHealthConfigRepository {
1924
+ db;
1925
+ constructor(db) {
1926
+ this.db = db;
1821
1927
  }
1822
- async getByRepo(workspaceId, repoOwner, repoName) {
1928
+ async getByBlock(workspaceId, blockId) {
1823
1929
  const rows = await this.db
1824
1930
  .select()
1825
- .from(repoBlueprints)
1826
- .where(and(eq(repoBlueprints.workspace_id, workspaceId), eq(repoBlueprints.repo_owner, repoOwner), eq(repoBlueprints.repo_name, repoName)))
1931
+ .from(releaseHealthConfigs)
1932
+ .where(and(eq(releaseHealthConfigs.workspace_id, workspaceId), eq(releaseHealthConfigs.block_id, blockId)))
1827
1933
  .limit(1);
1828
- return rows[0] ? rowToBlueprintRecord(rows[0]) : null;
1934
+ return rows[0] ? rowToReleaseHealthConfig(rows[0]) : null;
1829
1935
  }
1830
1936
  async listByWorkspace(workspaceId) {
1831
1937
  const rows = await this.db
1832
1938
  .select()
1833
- .from(repoBlueprints)
1834
- .where(eq(repoBlueprints.workspace_id, workspaceId))
1835
- .orderBy(desc(repoBlueprints.updated_at));
1836
- return rows.map(rowToBlueprintRecord);
1939
+ .from(releaseHealthConfigs)
1940
+ .where(eq(releaseHealthConfigs.workspace_id, workspaceId))
1941
+ .orderBy(releaseHealthConfigs.block_id);
1942
+ return rows.map(rowToReleaseHealthConfig);
1837
1943
  }
1838
- async delete(workspaceId, id) {
1944
+ async upsert(record) {
1945
+ const values = {
1946
+ workspace_id: record.workspaceId,
1947
+ block_id: record.blockId,
1948
+ monitor_ids: JSON.stringify(record.monitorIds),
1949
+ slo_ids: JSON.stringify(record.sloIds),
1950
+ env_tag: record.envTag,
1951
+ created_at: record.createdAt,
1952
+ updated_at: record.updatedAt,
1953
+ };
1954
+ await this.db
1955
+ .insert(releaseHealthConfigs)
1956
+ .values(values)
1957
+ .onConflictDoUpdate({
1958
+ target: [releaseHealthConfigs.workspace_id, releaseHealthConfigs.block_id],
1959
+ set: {
1960
+ monitor_ids: values.monitor_ids,
1961
+ slo_ids: values.slo_ids,
1962
+ env_tag: values.env_tag,
1963
+ updated_at: values.updated_at,
1964
+ },
1965
+ });
1966
+ }
1967
+ async delete(workspaceId, blockId) {
1839
1968
  await this.db
1840
- .delete(repoBlueprints)
1841
- .where(and(eq(repoBlueprints.workspace_id, workspaceId), eq(repoBlueprints.id, id)));
1969
+ .delete(releaseHealthConfigs)
1970
+ .where(and(eq(releaseHealthConfigs.workspace_id, workspaceId), eq(releaseHealthConfigs.block_id, blockId)));
1842
1971
  }
1843
1972
  }
1844
1973
  /** Build the Drizzle/Postgres-backed core repositories. */
@@ -1866,7 +1995,9 @@ export function createDrizzleRepositories(db, clock) {
1866
1995
  consensusSessionRepository: new DrizzleConsensusSessionRepository(db),
1867
1996
  clarityReviewRepository: new DrizzleClarityReviewRepository(db),
1868
1997
  mergePresetRepository: new DrizzleMergePresetRepository(db),
1869
- repoBlueprintRepository: new DrizzleRepoBlueprintRepository(db),
1998
+ workspaceSettingsRepository: new DrizzleWorkspaceSettingsRepository(db),
1999
+ datadogConnectionRepository: new DrizzleDatadogConnectionRepository(db),
2000
+ releaseHealthConfigRepository: new DrizzleReleaseHealthConfigRepository(db),
1870
2001
  };
1871
2002
  }
1872
2003
  //# sourceMappingURL=drizzle.js.map