@treeseed/sdk 0.10.17 → 0.10.19

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.
@@ -199,7 +199,7 @@ export declare function getRailwayServiceInstance({ serviceId, environmentId, en
199
199
  sleepApplication: null;
200
200
  runtimeConfigSupported: false;
201
201
  }>;
202
- export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, environmentId, buildCommand, startCommand, cronSchedule, rootDirectory, healthcheckPath, healthcheckTimeoutSeconds, healthcheckIntervalSeconds, restartPolicy, runtimeMode, env, fetchImpl, }: {
202
+ export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, environmentId, buildCommand, startCommand, cronSchedule, rootDirectory, healthcheckPath, healthcheckTimeoutSeconds, healthcheckIntervalSeconds, restartPolicy, runtimeMode, env, fetchImpl, settleAttempts, settleDelayMs, }: {
203
203
  serviceId: string;
204
204
  environmentId: string;
205
205
  buildCommand?: string | null;
@@ -213,6 +213,8 @@ export declare function ensureRailwayServiceInstanceConfiguration({ serviceId, e
213
213
  runtimeMode?: string | null;
214
214
  env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
215
215
  fetchImpl?: typeof fetch;
216
+ settleAttempts?: number;
217
+ settleDelayMs?: number;
216
218
  }): Promise<{
217
219
  instance: {
218
220
  id: string | null;
@@ -288,12 +290,7 @@ export declare function ensureRailwayServiceVolume({ projectId, environmentId, s
288
290
  env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
289
291
  fetchImpl?: typeof fetch;
290
292
  }): Promise<{
291
- volume: {
292
- instances: RailwayVolumeInstanceSummary[];
293
- id: string;
294
- name: string;
295
- projectId: string | null;
296
- } | null;
293
+ volume: RailwayVolumeSummary | null;
297
294
  instance: RailwayVolumeInstanceSummary | null;
298
295
  created: boolean;
299
296
  updated: boolean;
@@ -838,9 +838,17 @@ async function ensureRailwayServiceInstanceConfiguration({
838
838
  restartPolicy,
839
839
  runtimeMode,
840
840
  env = process.env,
841
- fetchImpl = fetch
841
+ fetchImpl = fetch,
842
+ settleAttempts = 24,
843
+ settleDelayMs = 5e3
842
844
  }) {
843
- const current = await getRailwayServiceInstance({ serviceId, environmentId, env, fetchImpl });
845
+ let current = await getRailwayServiceInstance({ serviceId, environmentId, env, fetchImpl });
846
+ if (!current.id) {
847
+ for (let attempt = 0; attempt < 8 && !current.id; attempt += 1) {
848
+ await new Promise((resolve) => setTimeout(resolve, 1500));
849
+ current = await getRailwayServiceInstance({ serviceId, environmentId, env, fetchImpl });
850
+ }
851
+ }
844
852
  if (!current.id) {
845
853
  return { instance: current, updated: false };
846
854
  }
@@ -905,30 +913,40 @@ mutation TreeseedRailwayServiceInstanceUpdateLegacy($serviceId: String!, $enviro
905
913
  }
906
914
  throw error;
907
915
  }
908
- const instance = await getRailwayServiceInstance({
909
- serviceId,
910
- environmentId,
911
- env,
912
- fetchImpl
913
- });
916
+ let instance = current;
917
+ for (let attempt = 0; attempt <= settleAttempts; attempt += 1) {
918
+ instance = await getRailwayServiceInstance({
919
+ serviceId,
920
+ environmentId,
921
+ env,
922
+ fetchImpl
923
+ });
924
+ if (!serviceInstanceDrifted(instance, desired) || attempt >= settleAttempts) {
925
+ break;
926
+ }
927
+ await new Promise((resolve) => setTimeout(resolve, settleDelayMs));
928
+ }
914
929
  return {
915
930
  instance: {
916
931
  id: instance.id || current.id,
917
- buildCommand: instance.buildCommand ?? desired.buildCommand,
918
- startCommand: instance.startCommand ?? desired.startCommand,
919
- cronSchedule: instance.cronSchedule ?? desired.cronSchedule,
920
- rootDirectory: instance.rootDirectory ?? desired.rootDirectory,
921
- healthcheckPath: instance.healthcheckPath ?? desired.healthcheckPath,
922
- healthcheckTimeoutSeconds: instance.healthcheckTimeoutSeconds ?? desired.healthcheckTimeoutSeconds,
923
- healthcheckIntervalSeconds: instance.healthcheckIntervalSeconds ?? desired.healthcheckIntervalSeconds,
924
- restartPolicy: instance.restartPolicy ?? desired.restartPolicy,
925
- runtimeMode: instance.runtimeMode ?? desired.runtimeMode,
926
- sleepApplication: instance.sleepApplication ?? desired.sleepApplication,
932
+ buildCommand: instance.buildCommand,
933
+ startCommand: instance.startCommand,
934
+ cronSchedule: instance.cronSchedule,
935
+ rootDirectory: instance.rootDirectory,
936
+ healthcheckPath: instance.healthcheckPath,
937
+ healthcheckTimeoutSeconds: instance.healthcheckTimeoutSeconds,
938
+ healthcheckIntervalSeconds: instance.healthcheckIntervalSeconds,
939
+ restartPolicy: instance.restartPolicy,
940
+ runtimeMode: instance.runtimeMode,
941
+ sleepApplication: instance.sleepApplication,
927
942
  runtimeConfigSupported: instance.runtimeConfigSupported
928
943
  },
929
944
  updated: true
930
945
  };
931
946
  }
947
+ function serviceInstanceDrifted(current, desired) {
948
+ return desired.buildCommand !== null && desired.buildCommand !== current.buildCommand || desired.startCommand !== null && desired.startCommand !== current.startCommand || desired.cronSchedule !== null && desired.cronSchedule !== current.cronSchedule || desired.rootDirectory !== null && desired.rootDirectory !== current.rootDirectory || desired.healthcheckPath !== null && desired.healthcheckPath !== current.healthcheckPath || desired.healthcheckTimeoutSeconds !== null && desired.healthcheckTimeoutSeconds !== current.healthcheckTimeoutSeconds || desired.runtimeMode !== null && desired.runtimeMode !== current.runtimeMode;
949
+ }
932
950
  async function listRailwayVariables({
933
951
  projectId,
934
952
  environmentId,
@@ -1193,23 +1211,37 @@ async function ensureRailwayServiceVolume({
1193
1211
  ...candidate,
1194
1212
  instances: candidate.instances.filter(isActiveRailwayVolumeInstance)
1195
1213
  })).filter((candidate) => candidate.instances.length > 0);
1196
- let volume = activeVolumes.find(
1197
- (candidate) => candidate.instances.some((instance2) => instance2.serviceId === serviceId && instance2.environmentId === environmentId)
1198
- ) ?? activeVolumes.find(
1214
+ let volume = findRailwayVolumeForService(volumes, serviceId, environmentId) ?? activeVolumes.find(
1199
1215
  (candidate) => candidate.name === name && candidate.instances.some((instance2) => instance2.environmentId === environmentId)
1200
1216
  ) ?? null;
1201
1217
  let created = false;
1202
1218
  let updated = false;
1203
1219
  const createReplacementVolume = async () => {
1204
- const replacement = await createRailwayVolume({
1205
- projectId,
1206
- environmentId,
1207
- serviceId,
1208
- name,
1209
- mountPath,
1210
- env,
1211
- fetchImpl
1212
- });
1220
+ let replacement;
1221
+ try {
1222
+ replacement = await createRailwayVolume({
1223
+ projectId,
1224
+ environmentId,
1225
+ serviceId,
1226
+ name,
1227
+ mountPath,
1228
+ env,
1229
+ fetchImpl
1230
+ });
1231
+ } catch (error) {
1232
+ if (!looksLikeRailwayVolumeCreateRace(error)) {
1233
+ throw error;
1234
+ }
1235
+ for (let attempt = 0; attempt < 8; attempt += 1) {
1236
+ await new Promise((resolve) => setTimeout(resolve, 1500));
1237
+ const refreshed = await listRailwayVolumes({ projectId, env, fetchImpl });
1238
+ const existing = findRailwayVolumeForService(refreshed, serviceId, environmentId);
1239
+ if (existing) {
1240
+ return existing;
1241
+ }
1242
+ }
1243
+ throw error;
1244
+ }
1213
1245
  created = true;
1214
1246
  return replacement;
1215
1247
  };
@@ -1259,6 +1291,17 @@ async function ensureRailwayServiceVolume({
1259
1291
  }
1260
1292
  return { volume, instance, created, updated };
1261
1293
  }
1294
+ function findRailwayVolumeForService(volumes, serviceId, environmentId) {
1295
+ return volumes.find(
1296
+ (candidate) => candidate.instances.some(
1297
+ (instance) => instance.serviceId === serviceId && instance.environmentId === environmentId && isActiveRailwayVolumeInstance(instance)
1298
+ )
1299
+ ) ?? null;
1300
+ }
1301
+ function looksLikeRailwayVolumeCreateRace(error) {
1302
+ const message = error instanceof Error ? error.message : String(error ?? "");
1303
+ return /would have \d+ volumes attached|can only have one volume|not authorized/iu.test(message);
1304
+ }
1262
1305
  async function listRailwayCustomDomains({
1263
1306
  projectId,
1264
1307
  environmentId,
@@ -4,7 +4,16 @@ export declare function deriveRailwayMarketOperationsRunnerServiceName(baseServi
4
4
  export declare function deriveRailwayWorkerRunnerVolumeName(serviceName: any, environmentName?: string): string;
5
5
  export declare function deriveRailwayMarketOperationsRunnerVolumeName(serviceName: any, environmentName?: string): string;
6
6
  export declare function railwayServiceRuntimeStartCommand(service: any): any;
7
+ export declare function parseRailwayJsonOutput(output: any): any;
7
8
  export declare function collectRailwayDeploymentStatusChecks(statusPayload: any, scope: any, services: any): any;
9
+ export declare function listRailwayServiceVolumesWithCli({ cwd, serviceId, environmentId, name, mountPath, env, }: {
10
+ cwd: any;
11
+ serviceId: any;
12
+ environmentId: any;
13
+ name: any;
14
+ mountPath: any;
15
+ env?: NodeJS.ProcessEnv | undefined;
16
+ }): any;
8
17
  export declare function isUsableRailwayToken(value: any): boolean;
9
18
  export declare function resolveRailwayAuthToken(env?: NodeJS.ProcessEnv): string;
10
19
  export declare function buildRailwayCommandEnv(env?: NodeJS.ProcessEnv): {
@@ -144,6 +153,23 @@ export declare function planRailwayServiceLink(service: any, { env }?: {
144
153
  args: string[];
145
154
  cwd: any;
146
155
  };
156
+ export declare function ensureRailwayServiceVolumeWithCliFallback({ tenantRoot, projectId, environmentId, environmentName, serviceId, serviceName, name, mountPath, preferCli, env, }: {
157
+ tenantRoot: any;
158
+ projectId: any;
159
+ environmentId: any;
160
+ environmentName: any;
161
+ serviceId: any;
162
+ serviceName: any;
163
+ name: any;
164
+ mountPath: any;
165
+ preferCli?: boolean | undefined;
166
+ env?: NodeJS.ProcessEnv | undefined;
167
+ }): Promise<{
168
+ volume: any;
169
+ instance: any;
170
+ created: boolean;
171
+ updated: boolean;
172
+ }>;
147
173
  export declare function deployRailwayService(tenantRoot: any, service: any, { dryRun, write, prefix, env, }?: {
148
174
  dryRun?: boolean;
149
175
  write?: TreeseedBootstrapWriter;
@@ -225,6 +225,30 @@ function normalizeRailwayCliVolumeList(value, options) {
225
225
  }
226
226
  return value.volumes.map((entry) => normalizeRailwayCliVolume(entry, options)).filter(Boolean);
227
227
  }
228
+ function listRailwayServiceVolumesWithCli({
229
+ cwd,
230
+ serviceId,
231
+ environmentId,
232
+ name,
233
+ mountPath,
234
+ env = process.env
235
+ }) {
236
+ const listResult = runRailway(["volume", "--service", serviceId, "--environment", environmentId, "list", "--json"], {
237
+ cwd,
238
+ capture: true,
239
+ allowFailure: true,
240
+ env
241
+ });
242
+ if ((listResult.status ?? 1) !== 0) {
243
+ return [];
244
+ }
245
+ return normalizeRailwayCliVolumeList(parseRailwayJsonOutput(listResult.stdout ?? ""), {
246
+ serviceId,
247
+ environmentId,
248
+ fallbackName: name,
249
+ fallbackMountPath: mountPath
250
+ });
251
+ }
228
252
  function isUsableRailwayToken(value) {
229
253
  return typeof value === "string" && value.trim().length >= 8;
230
254
  }
@@ -1758,7 +1782,43 @@ async function ensureRailwayServiceVolumeWithCliFallback({
1758
1782
  }
1759
1783
  created = true;
1760
1784
  }
1761
- const instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null;
1785
+ let instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null;
1786
+ if (!instance || instance.mountPath !== mountPath) {
1787
+ const attachResult = runRailway([...volumeArgs, "attach", "--volume", volume.id, "--yes", "--json"], {
1788
+ ...cliOptions,
1789
+ allowFailure: true
1790
+ });
1791
+ if ((attachResult.status ?? 1) !== 0) {
1792
+ const attachMessage = attachResult.stderr?.trim() || attachResult.stdout?.trim() || "";
1793
+ if (!/already mounted/iu.test(attachMessage)) {
1794
+ throw new Error(attachMessage || `Railway volume attach failed for ${serviceName} in ${environmentName}.`);
1795
+ }
1796
+ }
1797
+ const attachedVolume = (attachResult.status ?? 1) === 0 ? normalizeRailwayCliVolume(parseRailwayJsonOutput(attachResult.stdout ?? ""), {
1798
+ serviceId,
1799
+ environmentId,
1800
+ fallbackName: name,
1801
+ fallbackMountPath: mountPath
1802
+ }) : null;
1803
+ volume = attachedVolume ?? {
1804
+ ...volume,
1805
+ instances: [{
1806
+ ...instance ?? {
1807
+ id: volume.id,
1808
+ serviceId,
1809
+ environmentId,
1810
+ state: "READY",
1811
+ sizeGb: null,
1812
+ usedGb: null
1813
+ },
1814
+ serviceId,
1815
+ environmentId,
1816
+ mountPath
1817
+ }]
1818
+ };
1819
+ instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null;
1820
+ updated = true;
1821
+ }
1762
1822
  if (volume.name !== name || instance?.mountPath !== mountPath) {
1763
1823
  const updateResult = runRailway([...volumeArgs, "update", "--volume", volume.id, "--name", name, "--mount-path", mountPath, "--json"], cliOptions);
1764
1824
  const updatedVolume = normalizeRailwayCliVolume(parseRailwayJsonOutput(updateResult.stdout ?? ""), {
@@ -1774,6 +1834,18 @@ async function ensureRailwayServiceVolumeWithCliFallback({
1774
1834
  };
1775
1835
  updated = true;
1776
1836
  }
1837
+ const apiVolume = await waitForRailwayServiceVolumeMount({
1838
+ projectId,
1839
+ volumeId: volume.id,
1840
+ volumeName: name,
1841
+ serviceId,
1842
+ environmentId,
1843
+ mountPath,
1844
+ env
1845
+ });
1846
+ if (apiVolume) {
1847
+ volume = apiVolume;
1848
+ }
1777
1849
  return {
1778
1850
  volume,
1779
1851
  instance: volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null,
@@ -1781,6 +1853,33 @@ async function ensureRailwayServiceVolumeWithCliFallback({
1781
1853
  updated
1782
1854
  };
1783
1855
  }
1856
+ async function waitForRailwayServiceVolumeMount({
1857
+ projectId,
1858
+ volumeId,
1859
+ volumeName,
1860
+ serviceId,
1861
+ environmentId,
1862
+ mountPath,
1863
+ env
1864
+ }) {
1865
+ for (let attempt = 0; attempt <= 24; attempt += 1) {
1866
+ const volumes = await listRailwayVolumes({ projectId, env });
1867
+ const match = volumes.find(
1868
+ (entry) => entry.id === volumeId || entry.name === volumeName || entry.instances.some(
1869
+ (instance) => instance.serviceId === serviceId && instance.environmentId === environmentId && instance.mountPath === mountPath
1870
+ )
1871
+ ) ?? null;
1872
+ if (match?.instances.some(
1873
+ (instance) => instance.serviceId === serviceId && instance.environmentId === environmentId && instance.mountPath === mountPath
1874
+ )) {
1875
+ return match;
1876
+ }
1877
+ if (attempt < 24) {
1878
+ await sleep(5e3);
1879
+ }
1880
+ }
1881
+ return null;
1882
+ }
1784
1883
  async function deployRailwayService(tenantRoot, service, {
1785
1884
  dryRun = false,
1786
1885
  write,
@@ -1925,8 +2024,11 @@ export {
1925
2024
  ensureRailwayProjectExists,
1926
2025
  ensureRailwayScheduledJobs,
1927
2026
  ensureRailwayServiceExists,
2027
+ ensureRailwayServiceVolumeWithCliFallback,
1928
2028
  isRailwayTransientFailure,
1929
2029
  isUsableRailwayToken,
2030
+ listRailwayServiceVolumesWithCli,
2031
+ parseRailwayJsonOutput,
1930
2032
  planRailwayServiceDeploy,
1931
2033
  planRailwayServiceLink,
1932
2034
  railwayServiceRuntimeStartCommand,
@@ -32,6 +32,8 @@ import {
32
32
  configuredRailwayServices,
33
33
  deriveRailwayMarketOperationsRunnerVolumeName,
34
34
  ensureRailwayProjectContext,
35
+ ensureRailwayServiceVolumeWithCliFallback,
36
+ listRailwayServiceVolumesWithCli,
35
37
  runRailway,
36
38
  validateRailwayDeployPrerequisites
37
39
  } from "../operations/services/railway-deploy.js";
@@ -42,7 +44,6 @@ import {
42
44
  ensureRailwayProject,
43
45
  ensureRailwayService,
44
46
  ensureRailwayServiceInstanceConfiguration,
45
- ensureRailwayServiceVolume,
46
47
  getRailwayServiceInstance,
47
48
  getRailwayProject,
48
49
  listRailwayCustomDomains,
@@ -1568,12 +1569,16 @@ async function syncRailwayEnvironmentForScope(input, { dryRun = false } = {}) {
1568
1569
  });
1569
1570
  if (entry.configuredService.volumeMountPath) {
1570
1571
  const volumeName = entry.configuredService.key === "marketOperationsRunner" ? deriveRailwayMarketOperationsRunnerVolumeName(entry.service.name, entry.environment.name) : `${entry.service.name}-volume`;
1571
- const volume = await ensureRailwayServiceVolume({
1572
+ const volume = await ensureRailwayServiceVolumeWithCliFallback({
1573
+ tenantRoot: input.context.tenantRoot,
1572
1574
  projectId: entry.project.id,
1573
1575
  environmentId: entry.environment.id,
1576
+ environmentName: entry.environment.name,
1574
1577
  serviceId: entry.service.id,
1578
+ serviceName: entry.service.name,
1575
1579
  name: volumeName,
1576
1580
  mountPath: entry.configuredService.volumeMountPath,
1581
+ preferCli: entry.configuredService.key === "marketOperationsRunner",
1577
1582
  env: topology.env
1578
1583
  });
1579
1584
  if (!volume.instance?.serviceId) {
@@ -1660,7 +1665,7 @@ async function ensureRailwayMarketDatabaseForScope(input, topology) {
1660
1665
  env: topology.env
1661
1666
  });
1662
1667
  }
1663
- for (let attempt = 0; attempt < 8; attempt += 1) {
1668
+ for (let attempt = 0; attempt < 20; attempt += 1) {
1664
1669
  const existingVolumes = await listRailwayVolumes({
1665
1670
  projectId: firstService.project.id,
1666
1671
  env: topology.env
@@ -1669,18 +1674,10 @@ async function ensureRailwayMarketDatabaseForScope(input, topology) {
1669
1674
  (instance) => instance.serviceId === postgresService.id && instance.environmentId === firstService.environment?.id && instance.mountPath === "/var/lib/postgresql/data"
1670
1675
  ));
1671
1676
  if (attached) {
1672
- break;
1677
+ return;
1673
1678
  }
1674
- await new Promise((resolve2) => setTimeout(resolve2, 1500));
1679
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
1675
1680
  }
1676
- await ensureRailwayServiceVolume({
1677
- projectId: firstService.project.id,
1678
- environmentId: firstService.environment.id,
1679
- serviceId: postgresService.id,
1680
- name: `postgres-${topology.scope === "prod" ? "prod" : topology.scope}-data`,
1681
- mountPath: "/var/lib/postgresql/data",
1682
- env: topology.env
1683
- });
1684
1681
  }
1685
1682
  async function observeRailwayUnit(input, { refresh = false } = {}) {
1686
1683
  let attempt = 0;
@@ -2220,16 +2217,33 @@ async function verifyRailwayUnit(input) {
2220
2217
  const volumes = entry.project?.id ? await listRailwayVolumes({ projectId: entry.project.id, env: topology.env }) : [];
2221
2218
  const expectedServiceId = entry.service?.id ?? null;
2222
2219
  const expectedEnvironmentId = entry.environment?.id ?? null;
2223
- const mountedVolume = volumes.find((volume) => volume.instances.some(
2220
+ let mountedVolume = volumes.find((volume) => volume.instances.some(
2224
2221
  (instance) => instance.serviceId === expectedServiceId && instance.environmentId === expectedEnvironmentId && instance.mountPath === service.volumeMountPath
2225
2222
  )) ?? null;
2223
+ let mountedVolumeSource = "api";
2224
+ if (!mountedVolume && serviceKey === "marketOperationsRunner" && expectedServiceId && expectedEnvironmentId) {
2225
+ ensureRailwayProjectContext(service, { env: topology.env, capture: true });
2226
+ const cliVolumes = listRailwayServiceVolumesWithCli({
2227
+ cwd: service.rootDir,
2228
+ serviceId: expectedServiceId,
2229
+ environmentId: expectedEnvironmentId,
2230
+ name: deriveRailwayMarketOperationsRunnerVolumeName(entry.service?.name ?? service.serviceName ?? service.key, entry.environment?.name ?? service.railwayEnvironment),
2231
+ mountPath: service.volumeMountPath,
2232
+ env: topology.env
2233
+ });
2234
+ mountedVolume = cliVolumes.find((volume) => volume.instances.some(
2235
+ (instance) => instance.serviceId === expectedServiceId && instance.environmentId === expectedEnvironmentId && instance.mountPath === service.volumeMountPath
2236
+ )) ?? null;
2237
+ mountedVolumeSource = mountedVolume ? "cli" : "api";
2238
+ }
2226
2239
  checks.push(verificationCheck("railway.volume:data", "Railway service has persistent data volume mounted", "api", {
2227
2240
  exists: Boolean(mountedVolume),
2228
2241
  configured: Boolean(mountedVolume),
2229
2242
  expected: service.volumeMountPath,
2230
2243
  observed: mountedVolume ? {
2231
2244
  name: mountedVolume.name,
2232
- mountPath: service.volumeMountPath
2245
+ mountPath: service.volumeMountPath,
2246
+ source: mountedVolumeSource
2233
2247
  } : null,
2234
2248
  issues: mountedVolume ? [] : [`Railway service ${service.serviceName ?? service.key} is missing a persistent volume mounted at ${service.volumeMountPath}.`]
2235
2249
  }));
@@ -2251,7 +2265,13 @@ async function verifyRailwayUnit(input) {
2251
2265
  issues: Object.hasOwn(entry.currentVariables, key) ? [] : [`Railway secret ${key} is missing.`]
2252
2266
  }));
2253
2267
  }
2254
- return summarizeVerification(input.unit.unitId, checks);
2268
+ const verification = summarizeVerification(input.unit.unitId, checks);
2269
+ if (!verification.verified && attempt < 12 && railwayVerificationMaySettle(verification)) {
2270
+ attempt += 1;
2271
+ sleepMs(5e3);
2272
+ continue;
2273
+ }
2274
+ return verification;
2255
2275
  } catch (error) {
2256
2276
  if (attempt >= 2 || !isTransientRailwayReconcileError(error)) {
2257
2277
  throw error;
@@ -2261,6 +2281,11 @@ async function verifyRailwayUnit(input) {
2261
2281
  }
2262
2282
  }
2263
2283
  }
2284
+ function railwayVerificationMaySettle(verification) {
2285
+ return verification.checks.some(
2286
+ (check) => !check.verified && (check.key === "railway.instance" || check.key.startsWith("railway.instance.") || check.key === "railway.volume:data")
2287
+ );
2288
+ }
2264
2289
  function railwayStartCommandMatches(serviceKey, observed, expected) {
2265
2290
  if (observed === expected) {
2266
2291
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.10.17",
3
+ "version": "0.10.19",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {