@cat-factory/node-server 0.77.0 → 0.78.0

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.
@@ -2,7 +2,7 @@ import { LLM_WARNING_FINISH_REASONS } from '@cat-factory/kernel';
2
2
  import { agentRunKindSchema, decodeInitiativeRow } from '@cat-factory/contracts';
3
3
  import { decodeEnum, tryDecodeRows, blockInsertValues, blockPatchToColumns, parseIssueIntakeColumn, serializeIssueIntakeColumn, rowToBlock, rowToExecution, executionToDetail, rowToPipeline, rowToSandboxExperiment, rowToSandboxFixture, rowToSandboxGrade, rowToSandboxPromptVersion, rowToSandboxRun, rowToWorkspace, } from '@cat-factory/server';
4
4
  import { and, asc, count, desc, eq, gte, inArray, isNull, lt, ne, notInArray, or, sql, } from 'drizzle-orm';
5
- import { accountInvitations, passwordResetTokens, accountSettings, localSettings, accounts, agentContextSnapshots, agentRuns, blocks, consensusSessions, incidentEnrichmentConnections, observabilityConnections, packageRegistryConnections, emailConnections, llmCallMetrics, provisioningLog, memberships, mergeThresholdPresets, releaseHealthConfigs, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, docInterviewSessions, kaizenGradings, kaizenVerifiedCombos, clarityReviews, binaryArtifacts, brainstormSessions, initiatives, sandboxPromptVersions, sandboxFixtures, sandboxExperiments, sandboxRuns, sandboxGrades, services, tokenUsage, trackerSettings, modelPresets, userIdentities, users, workspaceFragmentDefaults, workspaceServices, workspaceSettings, workspaces, } from '../db/schema.js';
5
+ import { accountInvitations, passwordResetTokens, accountSettings, localSettings, accounts, agentContextSnapshots, agentRuns, blocks, consensusSessions, incidentEnrichmentConnections, observabilityConnections, packageRegistryConnections, emailConnections, llmCallMetrics, provisioningLog, sharedStacks, memberships, mergeThresholdPresets, releaseHealthConfigs, pipelineScheduleRuns, pipelineSchedules, pipelines, requirementReviews, docInterviewSessions, kaizenGradings, kaizenVerifiedCombos, clarityReviews, binaryArtifacts, brainstormSessions, initiatives, sandboxPromptVersions, sandboxFixtures, sandboxExperiments, sandboxRuns, sandboxGrades, services, tokenUsage, trackerSettings, modelPresets, userIdentities, users, workspaceFragmentDefaults, workspaceServices, workspaceSettings, workspaces, } from '../db/schema.js';
6
6
  // Drizzle/Postgres implementations of the core kernel repository ports. The
7
7
  // row<->domain mapping is the SAME shared mapping the Cloudflare D1 repos use
8
8
  // (@cat-factory/server), so behaviour matches across stores; this layer only owns
@@ -2785,6 +2785,126 @@ export class DrizzleMergePresetRepository {
2785
2785
  .where(and(eq(mergeThresholdPresets.workspace_id, workspaceId), eq(mergeThresholdPresets.id, id), eq(mergeThresholdPresets.is_default, 0)));
2786
2786
  }
2787
2787
  }
2788
+ // Shape-guarded parsers matching the D1 mirror (`D1SharedStackRepository`) EXACTLY, so a
2789
+ // malformed/hand-edited JSON column coerces identically on both stores (a non-array ⇒ `[]`, a
2790
+ // non-object health gate ⇒ `null`) rather than the Node facade handing the domain a raw value the
2791
+ // Worker would have dropped — the "keep the runtimes symmetric" guarantee holds for bad data too.
2792
+ function parseSharedStackArray(json) {
2793
+ try {
2794
+ const parsed = JSON.parse(json);
2795
+ return Array.isArray(parsed) ? parsed : [];
2796
+ }
2797
+ catch {
2798
+ return [];
2799
+ }
2800
+ }
2801
+ function parseSharedStackHealthGate(json) {
2802
+ if (!json)
2803
+ return null;
2804
+ try {
2805
+ const parsed = JSON.parse(json);
2806
+ return parsed && typeof parsed === 'object'
2807
+ ? parsed
2808
+ : null;
2809
+ }
2810
+ catch {
2811
+ return null;
2812
+ }
2813
+ }
2814
+ function rowToSharedStack(row) {
2815
+ return {
2816
+ id: row.id,
2817
+ workspaceId: row.workspace_id,
2818
+ name: row.name,
2819
+ cloneUrl: row.clone_url,
2820
+ gitRef: row.git_ref,
2821
+ composeFiles: parseSharedStackArray(row.compose_files),
2822
+ composeProfiles: parseSharedStackArray(row.compose_profiles),
2823
+ envFiles: parseSharedStackArray(row.env_files),
2824
+ managedNetworks: parseSharedStackArray(row.managed_networks),
2825
+ setupSteps: parseSharedStackArray(row.setup_steps),
2826
+ healthGate: parseSharedStackHealthGate(row.health_gate),
2827
+ allowHostCommands: row.allow_host_commands === 1,
2828
+ status: row.status,
2829
+ lastError: row.last_error,
2830
+ createdAt: row.created_at,
2831
+ updatedAt: row.updated_at,
2832
+ };
2833
+ }
2834
+ /**
2835
+ * A workspace's shared stacks over Postgres (the Drizzle mirror of the Worker's
2836
+ * `D1SharedStackRepository`, migration 0041). JSON-shaped fields are stored as text JSON and
2837
+ * `allow_host_commands` as 0/1; behaviourally identical to the D1 repo so the cross-runtime
2838
+ * conformance suite asserts the same round-trip.
2839
+ */
2840
+ export class DrizzleSharedStackRepository {
2841
+ db;
2842
+ constructor(db) {
2843
+ this.db = db;
2844
+ }
2845
+ async get(workspaceId, id) {
2846
+ const rows = await this.db
2847
+ .select()
2848
+ .from(sharedStacks)
2849
+ .where(and(eq(sharedStacks.workspace_id, workspaceId), eq(sharedStacks.id, id)))
2850
+ .limit(1);
2851
+ return rows[0] ? rowToSharedStack(rows[0]) : null;
2852
+ }
2853
+ async list(workspaceId) {
2854
+ const rows = await this.db
2855
+ .select()
2856
+ .from(sharedStacks)
2857
+ .where(eq(sharedStacks.workspace_id, workspaceId))
2858
+ .orderBy(sharedStacks.created_at);
2859
+ return rows.map(rowToSharedStack);
2860
+ }
2861
+ async upsert(workspaceId, stack) {
2862
+ const values = {
2863
+ workspace_id: workspaceId,
2864
+ id: stack.id,
2865
+ name: stack.name,
2866
+ clone_url: stack.cloneUrl,
2867
+ git_ref: stack.gitRef,
2868
+ compose_files: JSON.stringify(stack.composeFiles),
2869
+ compose_profiles: JSON.stringify(stack.composeProfiles),
2870
+ env_files: JSON.stringify(stack.envFiles),
2871
+ managed_networks: JSON.stringify(stack.managedNetworks),
2872
+ setup_steps: JSON.stringify(stack.setupSteps),
2873
+ health_gate: stack.healthGate ? JSON.stringify(stack.healthGate) : null,
2874
+ allow_host_commands: stack.allowHostCommands ? 1 : 0,
2875
+ status: stack.status,
2876
+ last_error: stack.lastError,
2877
+ created_at: stack.createdAt,
2878
+ updated_at: stack.updatedAt,
2879
+ };
2880
+ await this.db
2881
+ .insert(sharedStacks)
2882
+ .values(values)
2883
+ .onConflictDoUpdate({
2884
+ target: [sharedStacks.workspace_id, sharedStacks.id],
2885
+ set: {
2886
+ name: values.name,
2887
+ clone_url: values.clone_url,
2888
+ git_ref: values.git_ref,
2889
+ compose_files: values.compose_files,
2890
+ compose_profiles: values.compose_profiles,
2891
+ env_files: values.env_files,
2892
+ managed_networks: values.managed_networks,
2893
+ setup_steps: values.setup_steps,
2894
+ health_gate: values.health_gate,
2895
+ allow_host_commands: values.allow_host_commands,
2896
+ status: values.status,
2897
+ last_error: values.last_error,
2898
+ updated_at: values.updated_at,
2899
+ },
2900
+ });
2901
+ }
2902
+ async remove(workspaceId, id) {
2903
+ await this.db
2904
+ .delete(sharedStacks)
2905
+ .where(and(eq(sharedStacks.workspace_id, workspaceId), eq(sharedStacks.id, id)));
2906
+ }
2907
+ }
2788
2908
  // ---- Sandbox (parallel prompt/model testing surface; migration 0012) --------
2789
2909
  // The Drizzle mirror of the Worker's five `D1Sandbox*Repository` classes. JSON-shaped
2790
2910
  // fields are stored as text JSON, parsed defensively; behaviourally identical to the D1
@@ -3601,6 +3721,7 @@ export function createDrizzleRepositories(db, clock) {
3601
3721
  brainstormSessionRepository: new DrizzleBrainstormSessionRepository(db),
3602
3722
  initiativeRepository: new DrizzleInitiativeRepository(db),
3603
3723
  mergePresetRepository: new DrizzleMergePresetRepository(db),
3724
+ sharedStackRepository: new DrizzleSharedStackRepository(db),
3604
3725
  workspaceSettingsRepository: new DrizzleWorkspaceSettingsRepository(db),
3605
3726
  observabilityConnectionRepository: new DrizzleObservabilityConnectionRepository(db),
3606
3727
  packageRegistryConnectionRepository: new DrizzlePackageRegistryConnectionRepository(db),