@treeseed/sdk 0.6.40 → 0.6.41

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.
@@ -245,8 +245,8 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
245
245
  healthcheckTimeoutSeconds: number | null;
246
246
  runtimeMode: string | null;
247
247
  volume: {
248
- id: string;
249
- name: string;
248
+ id: any;
249
+ name: any;
250
250
  mountPath: any;
251
251
  created: boolean;
252
252
  updated: boolean;
@@ -745,8 +745,8 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
745
745
  healthcheckTimeoutSeconds: number | null;
746
746
  runtimeMode: string | null;
747
747
  volume: {
748
- id: string;
749
- name: string;
748
+ id: any;
749
+ name: any;
750
750
  mountPath: any;
751
751
  created: boolean;
752
752
  updated: boolean;
@@ -312,8 +312,8 @@ export declare function deployRailwayService(tenantRoot: any, service: any, { dr
312
312
  healthcheckTimeoutSeconds: number | null;
313
313
  runtimeMode: string | null;
314
314
  volume: {
315
- id: string;
316
- name: string;
315
+ id: any;
316
+ name: any;
317
317
  mountPath: any;
318
318
  created: boolean;
319
319
  updated: boolean;
@@ -73,6 +73,61 @@ function configuredEnvValue(env, name) {
73
73
  const value = env?.[name];
74
74
  return typeof value === "string" && value.trim() ? value.trim() : "";
75
75
  }
76
+ function parseRailwayJsonOutput(output) {
77
+ const trimmed = typeof output === "string" ? output.trim() : "";
78
+ if (!trimmed) {
79
+ return null;
80
+ }
81
+ try {
82
+ return JSON.parse(trimmed);
83
+ } catch {
84
+ }
85
+ const lines = trimmed.split(/\r?\n/u);
86
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
87
+ const candidate = lines.slice(index).join("\n").trim();
88
+ if (!candidate.startsWith("{") && !candidate.startsWith("[")) {
89
+ continue;
90
+ }
91
+ try {
92
+ return JSON.parse(candidate);
93
+ } catch {
94
+ }
95
+ }
96
+ return null;
97
+ }
98
+ function normalizeRailwayCliVolume(value, { serviceId, environmentId, fallbackName, fallbackMountPath }) {
99
+ if (!value || typeof value !== "object") {
100
+ return null;
101
+ }
102
+ const record = value;
103
+ const id = typeof record.id === "string" && record.id.trim() ? record.id.trim() : "";
104
+ if (!id) {
105
+ return null;
106
+ }
107
+ const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : fallbackName;
108
+ const mountPath = typeof record.mountPath === "string" && record.mountPath.trim() ? record.mountPath.trim() : fallbackMountPath;
109
+ const sizeMb = typeof record.sizeMB === "number" ? record.sizeMB : null;
110
+ const currentSizeMb = typeof record.currentSizeMB === "number" ? record.currentSizeMB : null;
111
+ return {
112
+ id,
113
+ name,
114
+ projectId: null,
115
+ instances: [{
116
+ id,
117
+ serviceId,
118
+ environmentId,
119
+ mountPath,
120
+ sizeGb: sizeMb === null ? null : sizeMb / 1e3,
121
+ usedGb: currentSizeMb === null ? null : currentSizeMb / 1e3
122
+ }]
123
+ };
124
+ }
125
+ function normalizeRailwayCliVolumeList(value, options) {
126
+ if (!value || typeof value !== "object" || !Array.isArray(value.volumes)) {
127
+ return [];
128
+ }
129
+ return value.volumes.map((entry) => normalizeRailwayCliVolume(entry, options)).filter(Boolean);
130
+ }
76
131
  function isUsableRailwayToken(value) {
77
132
  return typeof value === "string" && value.trim().length >= 8;
78
133
  }
@@ -959,10 +1014,13 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
959
1014
  env
960
1015
  }) : null;
961
1016
  const volumeMountPath = service.runnerPool?.volumeMountPath ?? WORKER_RUNNER_VOLUME_MOUNT_PATH;
962
- const volumeConfiguration = wantsRunnerVolume ? await ensureRailwayServiceVolume({
1017
+ const volumeConfiguration = wantsRunnerVolume ? await ensureRailwayServiceVolumeWithCliFallback({
1018
+ tenantRoot,
963
1019
  projectId: project.id,
964
1020
  environmentId: environment.id,
1021
+ environmentName: environment.name,
965
1022
  serviceId: railwayService.id,
1023
+ serviceName: railwayService.name,
966
1024
  name: deriveRailwayWorkerRunnerVolumeName(railwayService.name),
967
1025
  mountPath: volumeMountPath,
968
1026
  env
@@ -994,6 +1052,84 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
994
1052
  } : null
995
1053
  };
996
1054
  }
1055
+ async function ensureRailwayServiceVolumeWithCliFallback({
1056
+ tenantRoot,
1057
+ projectId,
1058
+ environmentId,
1059
+ environmentName,
1060
+ serviceId,
1061
+ serviceName,
1062
+ name,
1063
+ mountPath,
1064
+ env = process.env
1065
+ }) {
1066
+ try {
1067
+ return await ensureRailwayServiceVolume({
1068
+ projectId,
1069
+ environmentId,
1070
+ serviceId,
1071
+ name,
1072
+ mountPath,
1073
+ env
1074
+ });
1075
+ } catch (error) {
1076
+ const message = error instanceof Error ? error.message : String(error);
1077
+ if (!message.includes("Problem processing request")) {
1078
+ throw error;
1079
+ }
1080
+ }
1081
+ const cliOptions = {
1082
+ cwd: tenantRoot,
1083
+ capture: true,
1084
+ env
1085
+ };
1086
+ const volumeArgs = ["volume", "--service", serviceId, "--environment", environmentId];
1087
+ const listResult = runRailway([...volumeArgs, "list", "--json"], cliOptions);
1088
+ const existingVolumes = normalizeRailwayCliVolumeList(parseRailwayJsonOutput(listResult.stdout ?? ""), {
1089
+ serviceId,
1090
+ environmentId,
1091
+ fallbackName: name,
1092
+ fallbackMountPath: mountPath
1093
+ });
1094
+ let volume = existingVolumes.find((entry) => entry.name === name) ?? existingVolumes.find((entry) => entry.instances.some((instance2) => instance2.mountPath === mountPath)) ?? existingVolumes[0] ?? null;
1095
+ let created = false;
1096
+ let updated = false;
1097
+ if (!volume) {
1098
+ const createResult = runRailway([...volumeArgs, "add", "--mount-path", mountPath, "--json"], cliOptions);
1099
+ volume = normalizeRailwayCliVolume(parseRailwayJsonOutput(createResult.stdout ?? ""), {
1100
+ serviceId,
1101
+ environmentId,
1102
+ fallbackName: name,
1103
+ fallbackMountPath: mountPath
1104
+ });
1105
+ if (!volume) {
1106
+ throw new Error(`Railway CLI volume add did not return a usable volume for ${serviceName} in ${environmentName}.`);
1107
+ }
1108
+ created = true;
1109
+ }
1110
+ const instance = volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null;
1111
+ if (volume.name !== name || instance?.mountPath !== mountPath) {
1112
+ const updateResult = runRailway([...volumeArgs, "update", "--volume", volume.id, "--name", name, "--mount-path", mountPath, "--json"], cliOptions);
1113
+ const updatedVolume = normalizeRailwayCliVolume(parseRailwayJsonOutput(updateResult.stdout ?? ""), {
1114
+ serviceId,
1115
+ environmentId,
1116
+ fallbackName: name,
1117
+ fallbackMountPath: mountPath
1118
+ });
1119
+ volume = updatedVolume ?? {
1120
+ ...volume,
1121
+ name,
1122
+ instances: volume.instances.map((entry) => ({ ...entry, mountPath }))
1123
+ };
1124
+ updated = true;
1125
+ }
1126
+ return {
1127
+ volume,
1128
+ instance: volume.instances.find((entry) => entry.serviceId === serviceId && entry.environmentId === environmentId) ?? volume.instances[0] ?? null,
1129
+ created,
1130
+ updated
1131
+ };
1132
+ }
997
1133
  async function deployRailwayService(tenantRoot, service, {
998
1134
  dryRun = false,
999
1135
  write,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.6.40",
3
+ "version": "0.6.41",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {