@treeseed/sdk 0.6.39 → 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.
- package/dist/capacity.d.ts +53 -0
- package/dist/capacity.js +100 -0
- package/dist/control-plane-client.d.ts +41 -1
- package/dist/control-plane-client.js +154 -0
- package/dist/control-plane.d.ts +6 -1
- package/dist/control-plane.js +39 -2
- package/dist/d1-store.d.ts +63 -1
- package/dist/d1-store.js +190 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -0
- package/dist/operations/services/config-runtime.js +2 -2
- package/dist/operations/services/deploy.js +3 -2
- package/dist/operations/services/knowledge-coop-launch.js +5 -28
- package/dist/operations/services/package-reference-policy.d.ts +68 -0
- package/dist/operations/services/package-reference-policy.js +135 -0
- package/dist/operations/services/project-platform.d.ts +14 -0
- package/dist/operations/services/project-platform.js +3 -2
- package/dist/operations/services/railway-api.d.ts +33 -0
- package/dist/operations/services/railway-api.js +273 -0
- package/dist/operations/services/railway-deploy.d.ts +22 -0
- package/dist/operations/services/railway-deploy.js +216 -18
- package/dist/operations/services/release-candidate.d.ts +2 -0
- package/dist/operations/services/release-candidate.js +28 -0
- package/dist/operations/services/runtime-tools.js +1 -1
- package/dist/operations-registry.js +1 -0
- package/dist/reconcile/bootstrap-systems.js +1 -1
- package/dist/reconcile/builtin-adapters.js +5 -9
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.js +9 -17
- package/dist/reconcile/state.js +4 -4
- package/dist/reconcile/units.js +4 -8
- package/dist/sdk-types.d.ts +566 -3
- package/dist/sdk.d.ts +12 -1
- package/dist/sdk.js +44 -0
- package/dist/stores/operational-store.d.ts +12 -1
- package/dist/stores/operational-store.js +283 -5
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +5 -24
- package/dist/types/agents.d.ts +27 -0
- package/dist/workflow/operations.d.ts +94 -2
- package/dist/workflow/operations.js +90 -32
- package/dist/workflow-state.js +3 -5
- package/dist/workflow.d.ts +8 -1
- package/dist/workflow.js +6 -0
- package/package.json +5 -1
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ensureRailwayProject,
|
|
11
11
|
ensureRailwayService,
|
|
12
12
|
ensureRailwayServiceInstanceConfiguration,
|
|
13
|
+
ensureRailwayServiceVolume,
|
|
13
14
|
listRailwayEnvironments,
|
|
14
15
|
listRailwayProjects,
|
|
15
16
|
listRailwayServices,
|
|
@@ -18,7 +19,8 @@ import {
|
|
|
18
19
|
resolveRailwayApiToken,
|
|
19
20
|
resolveRailwayApiUrl,
|
|
20
21
|
resolveRailwayWorkspace,
|
|
21
|
-
resolveRailwayWorkspaceContext
|
|
22
|
+
resolveRailwayWorkspaceContext,
|
|
23
|
+
upsertRailwayVariables
|
|
22
24
|
} from "./railway-api.js";
|
|
23
25
|
function normalizeScope(scope) {
|
|
24
26
|
return scope === "prod" ? "prod" : scope === "staging" ? "staging" : "local";
|
|
@@ -26,13 +28,29 @@ function normalizeScope(scope) {
|
|
|
26
28
|
function resolveRailwayEnvironmentForScope(scope, configuredEnvironment) {
|
|
27
29
|
return normalizeRailwayEnvironmentName(configuredEnvironment || normalizeScope(scope));
|
|
28
30
|
}
|
|
29
|
-
const RAILWAY_SERVICE_KEYS = ["api", "
|
|
30
|
-
const HOSTED_PROJECT_SERVICE_KEYS = ["api", "
|
|
31
|
+
const RAILWAY_SERVICE_KEYS = ["api", "workdayManager", "workerRunner"];
|
|
32
|
+
const HOSTED_PROJECT_SERVICE_KEYS = ["api", "workdayManager", "workerRunner"];
|
|
33
|
+
const WORKER_RUNNER_BOOTSTRAP_INDEX = 1;
|
|
34
|
+
const WORKER_RUNNER_VOLUME_MOUNT_PATH = "/data";
|
|
31
35
|
function shouldManageRailwaySchedules(scope, phase = "deploy") {
|
|
32
|
-
|
|
36
|
+
const environment = normalizeRailwayEnvironmentName(scope);
|
|
37
|
+
return phase === "deploy" && (environment === "staging" || environment === "production");
|
|
33
38
|
}
|
|
34
39
|
function railwayServiceNameSuffix(serviceKey) {
|
|
35
|
-
return serviceKey === "
|
|
40
|
+
return serviceKey === "workdayManager" ? "workday-manager" : serviceKey === "workerRunner" ? "worker-runner" : serviceKey;
|
|
41
|
+
}
|
|
42
|
+
function deriveRailwayWorkerRunnerServiceName(projectSlug, index = WORKER_RUNNER_BOOTSTRAP_INDEX) {
|
|
43
|
+
const normalizedIndex = Math.max(1, Number.parseInt(String(index), 10) || WORKER_RUNNER_BOOTSTRAP_INDEX);
|
|
44
|
+
return `${projectSlug}-worker-runner-${String(normalizedIndex).padStart(2, "0")}`;
|
|
45
|
+
}
|
|
46
|
+
function deriveRailwayWorkerRunnerVolumeName(serviceName) {
|
|
47
|
+
return `${serviceName}-data`;
|
|
48
|
+
}
|
|
49
|
+
function railwayServiceRuntimeStartCommand(service) {
|
|
50
|
+
if (service.key === "workdayManager" && Array.isArray(service.schedule) && service.schedule.length > 0) {
|
|
51
|
+
return `node -e "console.log('workday-manager scheduled-only service idle')"`;
|
|
52
|
+
}
|
|
53
|
+
return service.startCommand;
|
|
36
54
|
}
|
|
37
55
|
function normalizeScheduleExpressions(value) {
|
|
38
56
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -55,6 +73,61 @@ function configuredEnvValue(env, name) {
|
|
|
55
73
|
const value = env?.[name];
|
|
56
74
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
57
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
|
+
}
|
|
58
131
|
function isUsableRailwayToken(value) {
|
|
59
132
|
return typeof value === "string" && value.trim().length >= 8;
|
|
60
133
|
}
|
|
@@ -455,7 +528,7 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
455
528
|
if (!service || service.enabled === false || (service.provider ?? "railway") !== "railway") {
|
|
456
529
|
return null;
|
|
457
530
|
}
|
|
458
|
-
const defaultRootDir = ["api", "
|
|
531
|
+
const defaultRootDir = ["api", "workdayManager", "workerRunner"].includes(serviceKey) ? "." : "packages/core";
|
|
459
532
|
const serviceRoot = resolve(tenantRoot, service.railway?.rootDir ?? service.rootDir ?? defaultRootDir);
|
|
460
533
|
const railwayEnvironment = resolveRailwayEnvironmentForScope(
|
|
461
534
|
normalizedScope,
|
|
@@ -468,7 +541,7 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
468
541
|
projectId: service.railway?.projectId ?? null,
|
|
469
542
|
projectName: service.railway?.projectName ?? identity.deploymentKey,
|
|
470
543
|
serviceId: service.railway?.serviceId ?? null,
|
|
471
|
-
serviceName: service.railway?.serviceName ?? `${identity.deploymentKey}-${railwayServiceNameSuffix(serviceKey)}
|
|
544
|
+
serviceName: service.railway?.serviceName ?? (serviceKey === "workerRunner" ? deriveRailwayWorkerRunnerServiceName(identity.deploymentKey) : `${identity.deploymentKey}-${railwayServiceNameSuffix(serviceKey)}`),
|
|
472
545
|
rootDir: serviceRoot,
|
|
473
546
|
publicBaseUrl,
|
|
474
547
|
railwayEnvironment,
|
|
@@ -480,7 +553,11 @@ function configuredRailwayServices(tenantRoot, scope) {
|
|
|
480
553
|
restartPolicy: service.railway?.restartPolicy ?? null,
|
|
481
554
|
runtimeMode: service.railway?.runtimeMode ?? null,
|
|
482
555
|
schedule: normalizeScheduleExpressions(service.railway?.schedule),
|
|
483
|
-
hostingKind
|
|
556
|
+
hostingKind,
|
|
557
|
+
runnerPool: serviceKey === "workerRunner" ? {
|
|
558
|
+
bootstrapIndex: WORKER_RUNNER_BOOTSTRAP_INDEX,
|
|
559
|
+
volumeMountPath: WORKER_RUNNER_VOLUME_MOUNT_PATH
|
|
560
|
+
} : null
|
|
484
561
|
};
|
|
485
562
|
}).filter(Boolean);
|
|
486
563
|
}
|
|
@@ -879,7 +956,8 @@ async function resolveRailwayDeployProjectContext(service, { env = process.env }
|
|
|
879
956
|
}
|
|
880
957
|
async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, service, { env = process.env } = {}) {
|
|
881
958
|
const wantsInstanceConfig = service.buildCommand || service.startCommand || service.rootDir || service.healthcheckPath || service.healthcheckTimeoutSeconds !== null || service.healthcheckTimeoutSeconds !== void 0 || service.healthcheckIntervalSeconds !== null || service.healthcheckIntervalSeconds !== void 0 || service.restartPolicy || service.runtimeMode;
|
|
882
|
-
|
|
959
|
+
const wantsRunnerVolume = service.key === "workerRunner";
|
|
960
|
+
if (!wantsInstanceConfig && !wantsRunnerVolume) {
|
|
883
961
|
return null;
|
|
884
962
|
}
|
|
885
963
|
const workspace = await resolveRailwayWorkspaceContext({ env });
|
|
@@ -922,11 +1000,11 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
922
1000
|
env
|
|
923
1001
|
}).then((result) => result.service);
|
|
924
1002
|
}
|
|
925
|
-
|
|
1003
|
+
const runtimeConfiguration = wantsInstanceConfig ? await ensureRailwayServiceInstanceConfiguration({
|
|
926
1004
|
serviceId: railwayService.id,
|
|
927
1005
|
environmentId: environment.id,
|
|
928
1006
|
buildCommand: service.buildCommand,
|
|
929
|
-
startCommand: service
|
|
1007
|
+
startCommand: railwayServiceRuntimeStartCommand(service),
|
|
930
1008
|
rootDirectory: relativeRailwayRootDir(tenantRoot, service.rootDir),
|
|
931
1009
|
healthcheckPath: service.healthcheckPath,
|
|
932
1010
|
healthcheckTimeoutSeconds: service.healthcheckTimeoutSeconds,
|
|
@@ -934,7 +1012,123 @@ async function syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, ser
|
|
|
934
1012
|
restartPolicy: service.restartPolicy,
|
|
935
1013
|
runtimeMode: service.runtimeMode,
|
|
936
1014
|
env
|
|
1015
|
+
}) : null;
|
|
1016
|
+
const volumeMountPath = service.runnerPool?.volumeMountPath ?? WORKER_RUNNER_VOLUME_MOUNT_PATH;
|
|
1017
|
+
const volumeConfiguration = wantsRunnerVolume ? await ensureRailwayServiceVolumeWithCliFallback({
|
|
1018
|
+
tenantRoot,
|
|
1019
|
+
projectId: project.id,
|
|
1020
|
+
environmentId: environment.id,
|
|
1021
|
+
environmentName: environment.name,
|
|
1022
|
+
serviceId: railwayService.id,
|
|
1023
|
+
serviceName: railwayService.name,
|
|
1024
|
+
name: deriveRailwayWorkerRunnerVolumeName(railwayService.name),
|
|
1025
|
+
mountPath: volumeMountPath,
|
|
1026
|
+
env
|
|
1027
|
+
}) : null;
|
|
1028
|
+
if (wantsRunnerVolume) {
|
|
1029
|
+
await upsertRailwayVariables({
|
|
1030
|
+
projectId: project.id,
|
|
1031
|
+
environmentId: environment.id,
|
|
1032
|
+
serviceId: railwayService.id,
|
|
1033
|
+
variables: {
|
|
1034
|
+
TREESEED_RUNNER_SERVICE_NAME: railwayService.name,
|
|
1035
|
+
TREESEED_RUNNER_VOLUME_ROOT: volumeMountPath,
|
|
1036
|
+
TREESEED_RUNNER_VOLUME_NAME: volumeConfiguration?.volume.name ?? deriveRailwayWorkerRunnerVolumeName(railwayService.name),
|
|
1037
|
+
TREESEED_WORKER_IDLE_EXIT_MS: configuredEnvValue(env, "TREESEED_WORKER_IDLE_EXIT_MS") || "60000",
|
|
1038
|
+
...volumeConfiguration?.volume.id ? { TREESEED_RUNNER_VOLUME_ID: volumeConfiguration.volume.id } : {}
|
|
1039
|
+
},
|
|
1040
|
+
env
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
instance: runtimeConfiguration?.instance ?? null,
|
|
1045
|
+
updated: Boolean(runtimeConfiguration?.updated || volumeConfiguration?.updated || volumeConfiguration?.created),
|
|
1046
|
+
volume: volumeConfiguration ? {
|
|
1047
|
+
id: volumeConfiguration.volume.id,
|
|
1048
|
+
name: volumeConfiguration.volume.name,
|
|
1049
|
+
mountPath: volumeConfiguration.instance?.mountPath ?? volumeMountPath,
|
|
1050
|
+
created: volumeConfiguration.created,
|
|
1051
|
+
updated: volumeConfiguration.updated
|
|
1052
|
+
} : null
|
|
1053
|
+
};
|
|
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
|
|
937
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
|
+
};
|
|
938
1132
|
}
|
|
939
1133
|
async function deployRailwayService(tenantRoot, service, {
|
|
940
1134
|
dryRun = false,
|
|
@@ -954,13 +1148,16 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
954
1148
|
}
|
|
955
1149
|
const deployService = await resolveRailwayDeployProjectContext(service, { env });
|
|
956
1150
|
const plan = planRailwayServiceDeploy(deployService, { env });
|
|
1151
|
+
const commandEnv = buildRailwayCommandEnv({ ...process.env, ...env });
|
|
957
1152
|
const taskPrefix = prefix ?? {
|
|
958
1153
|
scope: normalizeScope(deployService.scope ?? deployService.railwayEnvironment ?? "railway"),
|
|
959
1154
|
system: deployService.key === "api" ? "api" : "agents",
|
|
960
1155
|
task: `${deployService.key}-railway-deploy`,
|
|
961
1156
|
stage: "deploy"
|
|
962
1157
|
};
|
|
963
|
-
const
|
|
1158
|
+
const runtimeConfiguration = await syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
1159
|
+
env: commandEnv
|
|
1160
|
+
});
|
|
964
1161
|
if (deployService.buildCommand) {
|
|
965
1162
|
const buildResult = await runPrefixedCommand("bash", ["-lc", deployService.buildCommand], {
|
|
966
1163
|
cwd: deployService.rootDir,
|
|
@@ -996,9 +1193,6 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
996
1193
|
if (lastFailure) {
|
|
997
1194
|
throw new Error(lastFailure.stderr?.trim() || lastFailure.stdout?.trim() || `railway ${plan.args.join(" ")} failed`);
|
|
998
1195
|
}
|
|
999
|
-
const runtimeConfiguration = await syncRailwayServiceRuntimeConfigurationAfterDeploy(tenantRoot, deployService, {
|
|
1000
|
-
env: commandEnv
|
|
1001
|
-
});
|
|
1002
1196
|
return {
|
|
1003
1197
|
service: deployService.key,
|
|
1004
1198
|
status: "deployed",
|
|
@@ -1007,9 +1201,10 @@ async function deployRailwayService(tenantRoot, service, {
|
|
|
1007
1201
|
publicBaseUrl: deployService.publicBaseUrl,
|
|
1008
1202
|
runtimeConfiguration: runtimeConfiguration ? {
|
|
1009
1203
|
updated: runtimeConfiguration.updated,
|
|
1010
|
-
healthcheckPath: runtimeConfiguration.instance
|
|
1011
|
-
healthcheckTimeoutSeconds: runtimeConfiguration.instance
|
|
1012
|
-
runtimeMode: runtimeConfiguration.instance
|
|
1204
|
+
healthcheckPath: runtimeConfiguration.instance?.healthcheckPath ?? null,
|
|
1205
|
+
healthcheckTimeoutSeconds: runtimeConfiguration.instance?.healthcheckTimeoutSeconds ?? null,
|
|
1206
|
+
runtimeMode: runtimeConfiguration.instance?.runtimeMode ?? null,
|
|
1207
|
+
volume: runtimeConfiguration.volume ?? null
|
|
1013
1208
|
} : null
|
|
1014
1209
|
};
|
|
1015
1210
|
}
|
|
@@ -1018,6 +1213,8 @@ export {
|
|
|
1018
1213
|
configuredRailwayScheduledJobs,
|
|
1019
1214
|
configuredRailwayServices,
|
|
1020
1215
|
deployRailwayService,
|
|
1216
|
+
deriveRailwayWorkerRunnerServiceName,
|
|
1217
|
+
deriveRailwayWorkerRunnerVolumeName,
|
|
1021
1218
|
ensureRailwayEnvironmentExists,
|
|
1022
1219
|
ensureRailwayProjectContext,
|
|
1023
1220
|
ensureRailwayProjectExists,
|
|
@@ -1026,6 +1223,7 @@ export {
|
|
|
1026
1223
|
isRailwayTransientFailure,
|
|
1027
1224
|
isUsableRailwayToken,
|
|
1028
1225
|
planRailwayServiceDeploy,
|
|
1226
|
+
railwayServiceRuntimeStartCommand,
|
|
1029
1227
|
resolveRailwayAuthToken,
|
|
1030
1228
|
resolveRailwayDeploymentProfile,
|
|
1031
1229
|
runRailway,
|
|
@@ -8,6 +8,7 @@ export type ReleaseCandidateFailure = {
|
|
|
8
8
|
};
|
|
9
9
|
export type ReleaseCandidateFingerprint = {
|
|
10
10
|
key: string;
|
|
11
|
+
policyVersion: string;
|
|
11
12
|
rootSha: string | null;
|
|
12
13
|
packageShas: Record<string, string | null>;
|
|
13
14
|
plannedVersions: Record<string, string>;
|
|
@@ -36,4 +37,5 @@ export type ReleaseCandidateInput = {
|
|
|
36
37
|
export declare function buildReleaseCandidateFingerprint(input: ReleaseCandidateInput): ReleaseCandidateFingerprint;
|
|
37
38
|
export declare function readCachedReleaseCandidateReport(root: string, key: string): ReleaseCandidateReport | null;
|
|
38
39
|
export declare function writeReleaseCandidateReport(root: string, report: ReleaseCandidateReport): ReleaseCandidateReport;
|
|
40
|
+
export declare function collectReleaseCandidateOutputFailures(line: string): string[];
|
|
39
41
|
export declare function runReleaseCandidateGate(input: ReleaseCandidateInput): Promise<ReleaseCandidateReport>;
|
|
@@ -13,6 +13,7 @@ import { loadCliDeployConfig } from "./runtime-tools.js";
|
|
|
13
13
|
import { packagesWithScript, run, workspacePackages } from "./workspace-tools.js";
|
|
14
14
|
import { createBuildWarningSummary, formatAllowedBuildWarnings } from "./build-warning-policy.js";
|
|
15
15
|
const RELEASE_CANDIDATE_CACHE_DIR = ".treeseed/workflow/release-candidates";
|
|
16
|
+
const RELEASE_CANDIDATE_POLICY_VERSION = "strict-output-v1";
|
|
16
17
|
const STABLE_SEMVER = /^\d+\.\d+\.\d+$/u;
|
|
17
18
|
const REHEARSAL_IGNORED_SEGMENTS = /* @__PURE__ */ new Set([
|
|
18
19
|
".git",
|
|
@@ -86,6 +87,7 @@ function buildReleaseCandidateFingerprint(input) {
|
|
|
86
87
|
...Object.fromEntries(packages.map((pkg) => [pkg.name, fileSha256(resolve(pkg.dir, "package-lock.json"))]))
|
|
87
88
|
});
|
|
88
89
|
const base = {
|
|
90
|
+
policyVersion: RELEASE_CANDIDATE_POLICY_VERSION,
|
|
89
91
|
rootSha: safeGitHead(input.root),
|
|
90
92
|
packageShas,
|
|
91
93
|
plannedVersions,
|
|
@@ -241,11 +243,15 @@ function runNpmRehearsalCommand(args, options) {
|
|
|
241
243
|
throw new Error(message);
|
|
242
244
|
}
|
|
243
245
|
const warningSummary = createBuildWarningSummary();
|
|
246
|
+
const outputFailures = [];
|
|
244
247
|
const emitFiltered = (text, stream) => {
|
|
245
248
|
for (const line of text.split(/\r?\n/u)) {
|
|
246
249
|
if (!line) continue;
|
|
247
250
|
const classified = warningSummary.record(line);
|
|
248
251
|
if (classified.kind === "allowed") continue;
|
|
252
|
+
for (const failure of collectReleaseCandidateOutputFailures(line)) {
|
|
253
|
+
outputFailures.push(failure);
|
|
254
|
+
}
|
|
249
255
|
stream.write(`${line}
|
|
250
256
|
`);
|
|
251
257
|
}
|
|
@@ -256,6 +262,27 @@ function runNpmRehearsalCommand(args, options) {
|
|
|
256
262
|
process.stdout.write(`${line}
|
|
257
263
|
`);
|
|
258
264
|
}
|
|
265
|
+
if (outputFailures.length > 0) {
|
|
266
|
+
throw new Error([
|
|
267
|
+
`npm ${args.join(" ")} completed with error output despite exit code 0.`,
|
|
268
|
+
...outputFailures.slice(0, 12)
|
|
269
|
+
].join("\n"));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function collectReleaseCandidateOutputFailures(line) {
|
|
273
|
+
const value = String(line ?? "").trim();
|
|
274
|
+
if (!value) return [];
|
|
275
|
+
const failures = [];
|
|
276
|
+
if (/^stderr\s+\|\s+/u.test(value)) {
|
|
277
|
+
failures.push(`Captured test stderr: ${value}`);
|
|
278
|
+
}
|
|
279
|
+
if (/(^|\s)ERROR(?:\s|\[|:)/u.test(value)) {
|
|
280
|
+
failures.push(`Error output: ${value}`);
|
|
281
|
+
}
|
|
282
|
+
if (/\bFailed to run background task\b/u.test(value)) {
|
|
283
|
+
failures.push(`Background task failure output: ${value}`);
|
|
284
|
+
}
|
|
285
|
+
return failures;
|
|
259
286
|
}
|
|
260
287
|
function buildRehearsalWorkspacePackageArtifacts(root) {
|
|
261
288
|
for (const pkg of packagesWithScript("build:dist", root)) {
|
|
@@ -561,6 +588,7 @@ async function runReleaseCandidateGate(input) {
|
|
|
561
588
|
}
|
|
562
589
|
export {
|
|
563
590
|
buildReleaseCandidateFingerprint,
|
|
591
|
+
collectReleaseCandidateOutputFailures,
|
|
564
592
|
readCachedReleaseCandidateReport,
|
|
565
593
|
runReleaseCandidateGate,
|
|
566
594
|
writeReleaseCandidateReport
|
|
@@ -53,7 +53,7 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
|
|
|
53
53
|
},
|
|
54
54
|
site: "default"
|
|
55
55
|
};
|
|
56
|
-
const TRESEED_MANAGED_SERVICE_KEYS = ["api", "
|
|
56
|
+
const TRESEED_MANAGED_SERVICE_KEYS = ["api", "workdayManager", "workerRunner"];
|
|
57
57
|
const TRESEED_WORKSPACE_PACKAGE_DIRS = ["sdk", "core", "cli"];
|
|
58
58
|
const CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER = "replace-with-cloudflare-account-id";
|
|
59
59
|
function normalizePlanesFromLegacyHosting(hosting) {
|
|
@@ -10,6 +10,7 @@ const TRESEED_OPERATION_SPECS = [
|
|
|
10
10
|
operation({ id: "branch.save", name: "save", aliases: [], group: "Workflow", summary: "Recursively verify, commit, and push the current task checkpoint.", description: "Save dirty package repos in dependency order, then verify, commit, push, and optionally refresh preview for market.", provider: "default", related: ["switch", "stage", "status"] }),
|
|
11
11
|
operation({ id: "branch.close", name: "close", aliases: [], group: "Workflow", summary: "Recursively archive and delete a task branch.", description: "Auto-save if needed, clean preview resources, create deprecated tags, and remove the task branch across market and checked-out package repos.", provider: "default", related: ["tasks", "switch", "stage"] }),
|
|
12
12
|
operation({ id: "branch.stage", name: "stage", aliases: [], group: "Workflow", summary: "Squash a task branch into staging across market and packages.", description: "Auto-save if needed, squash-merge package task branches into staging first, update market submodule pointers, then squash-merge market into staging and clean up the task branch.", provider: "default", related: ["save", "release", "close"] }),
|
|
13
|
+
operation({ id: "workspace.tags.cleanup", name: "tags:cleanup", aliases: [], group: "Utilities", summary: "Clean stale Treeseed-managed package dev tags.", description: "Plan or delete stale staging and preview package dev tags that are older than each package current version line while preserving active references and non-Treeseed tags.", provider: "default", related: ["release", "status"] }),
|
|
13
14
|
operation({ id: "workspace.resume", name: "resume", aliases: [], group: "Workflow", summary: "Resume an interrupted workflow run.", description: "Continue a failed journaled workflow run from its next incomplete step after validating current workspace preconditions.", provider: "default", related: ["recover", "status"] }),
|
|
14
15
|
operation({ id: "workspace.recover", name: "recover", aliases: [], group: "Workflow", summary: "Inspect active workflow locks and interrupted runs.", description: "List active workflow locks, resumable interrupted runs, and the exact commands needed to continue or recover the workspace state.", provider: "default", related: ["resume", "status"] }),
|
|
15
16
|
operation({ id: "workspace.links.status", name: workspaceCommand("status"), aliases: [], group: "Utilities", summary: "Inspect local workspace dependency links.", description: "Inspect whether Treeseed package dependencies are linked to local package checkouts or resolved through deployment references.", provider: "default", related: ["dev", "save"] }),
|
|
@@ -55,7 +55,7 @@ function agentsSystemDisabled(config) {
|
|
|
55
55
|
if (config.runtime?.mode === "none") {
|
|
56
56
|
return "runtime.mode is none.";
|
|
57
57
|
}
|
|
58
|
-
const enabled = ["
|
|
58
|
+
const enabled = ["workdayManager", "workerRunner"].some((serviceKey) => serviceEnabled(config, serviceKey));
|
|
59
59
|
return enabled ? null : "No agent Railway services are enabled.";
|
|
60
60
|
}
|
|
61
61
|
function hasValue(env, key) {
|
|
@@ -1439,7 +1439,7 @@ async function syncRailwayEnvironmentForScope(input, { dryRun = false } = {}) {
|
|
|
1439
1439
|
includeInstances: !dryRun,
|
|
1440
1440
|
includeVariables: false
|
|
1441
1441
|
});
|
|
1442
|
-
const workerEntry = topology.services.get("worker") ?? null;
|
|
1442
|
+
const workerEntry = topology.services.get("workerRunner") ?? topology.services.get("worker") ?? null;
|
|
1443
1443
|
const railwayRuntimeVariables = Object.fromEntries(
|
|
1444
1444
|
[
|
|
1445
1445
|
["TREESEED_RAILWAY_PROJECT_ID", workerEntry?.project?.id],
|
|
@@ -2140,16 +2140,12 @@ function createCloudflareReconcileAdapters() {
|
|
|
2140
2140
|
function createRailwayReconcileAdapters() {
|
|
2141
2141
|
return [
|
|
2142
2142
|
buildRailwayAdapter("railway-service:api"),
|
|
2143
|
-
buildRailwayAdapter("railway-service:manager"),
|
|
2144
|
-
buildRailwayAdapter("railway-service:worker"),
|
|
2145
|
-
buildRailwayAdapter("railway-service:workday-start"),
|
|
2146
|
-
buildRailwayAdapter("railway-service:workday-report"),
|
|
2143
|
+
buildRailwayAdapter("railway-service:workday-manager"),
|
|
2144
|
+
buildRailwayAdapter("railway-service:worker-runner"),
|
|
2147
2145
|
buildCustomDomainAdapter("custom-domain:api", "railway"),
|
|
2148
2146
|
buildCompositeAdapter("api-runtime"),
|
|
2149
|
-
buildCompositeAdapter("manager-runtime"),
|
|
2150
|
-
buildCompositeAdapter("worker-runtime")
|
|
2151
|
-
buildCompositeAdapter("workday-start-runtime"),
|
|
2152
|
-
buildCompositeAdapter("workday-report-runtime")
|
|
2147
|
+
buildCompositeAdapter("workday-manager-runtime"),
|
|
2148
|
+
buildCompositeAdapter("worker-runner-runtime")
|
|
2153
2149
|
];
|
|
2154
2150
|
}
|
|
2155
2151
|
export {
|
|
@@ -3,7 +3,7 @@ export type TreeseedReconcileProviderId = string;
|
|
|
3
3
|
export type TreeseedReconcileActionKind = 'noop' | 'create' | 'update' | 'reuse' | 'drift_correct' | 'destroy';
|
|
4
4
|
export type TreeseedReconcileStatusKind = 'pending' | 'ready' | 'drifted' | 'error';
|
|
5
5
|
export type TreeseedReconcileVerificationSource = 'cli' | 'api' | 'sdk' | 'derived';
|
|
6
|
-
export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'manager-runtime' | 'worker-
|
|
6
|
+
export type TreeseedReconcileUnitType = 'web-ui' | 'api-runtime' | 'workday-manager-runtime' | 'worker-runner-runtime' | 'edge-worker' | 'content-store' | 'queue' | 'database' | 'kv-form-guard' | 'pages-project' | 'custom-domain:web' | 'custom-domain:api' | 'dns-record' | 'railway-service:api' | 'railway-service:workday-manager' | 'railway-service:worker-runner';
|
|
7
7
|
export type TreeseedReconcileTarget = {
|
|
8
8
|
kind: 'persistent';
|
|
9
9
|
scope: 'local' | 'staging' | 'prod';
|
|
@@ -11,14 +11,10 @@ function railwayConcreteUnitTypeForServiceKey(serviceKey) {
|
|
|
11
11
|
switch (serviceKey) {
|
|
12
12
|
case "api":
|
|
13
13
|
return "railway-service:api";
|
|
14
|
-
case "
|
|
15
|
-
return "railway-service:manager";
|
|
16
|
-
case "
|
|
17
|
-
return "railway-service:worker";
|
|
18
|
-
case "workdayStart":
|
|
19
|
-
return "railway-service:workday-start";
|
|
20
|
-
case "workdayReport":
|
|
21
|
-
return "railway-service:workday-report";
|
|
14
|
+
case "workdayManager":
|
|
15
|
+
return "railway-service:workday-manager";
|
|
16
|
+
case "workerRunner":
|
|
17
|
+
return "railway-service:worker-runner";
|
|
22
18
|
default:
|
|
23
19
|
return "railway-service:api";
|
|
24
20
|
}
|
|
@@ -227,7 +223,7 @@ function deriveTreeseedDesiredUnits({
|
|
|
227
223
|
serviceKey,
|
|
228
224
|
scheduleManaged: Array.isArray(configuredService.schedule) && configuredService.schedule.length > 0,
|
|
229
225
|
scheduleBootstrap: false,
|
|
230
|
-
scheduleDeployScopes: ["prod"],
|
|
226
|
+
scheduleDeployScopes: ["staging", "prod"],
|
|
231
227
|
bootstrapSystem: serviceBootstrapSystem
|
|
232
228
|
}
|
|
233
229
|
});
|
|
@@ -270,14 +266,10 @@ function deriveTreeseedDesiredUnits({
|
|
|
270
266
|
switch (serviceKey) {
|
|
271
267
|
case "api":
|
|
272
268
|
return "api-runtime";
|
|
273
|
-
case "
|
|
274
|
-
return "manager-runtime";
|
|
275
|
-
case "
|
|
276
|
-
return "worker-runtime";
|
|
277
|
-
case "workdayStart":
|
|
278
|
-
return "workday-start-runtime";
|
|
279
|
-
case "workdayReport":
|
|
280
|
-
return "workday-report-runtime";
|
|
269
|
+
case "workdayManager":
|
|
270
|
+
return "workday-manager-runtime";
|
|
271
|
+
case "workerRunner":
|
|
272
|
+
return "worker-runner-runtime";
|
|
281
273
|
default:
|
|
282
274
|
return "api-runtime";
|
|
283
275
|
}
|
package/dist/reconcile/state.js
CHANGED
|
@@ -6,11 +6,11 @@ function stableHash(value) {
|
|
|
6
6
|
return createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
7
7
|
}
|
|
8
8
|
function railwayUnitTypeForServiceKey(serviceKey) {
|
|
9
|
-
if (serviceKey === "
|
|
10
|
-
return "railway-service:workday-
|
|
9
|
+
if (serviceKey === "workdayManager") {
|
|
10
|
+
return "railway-service:workday-manager";
|
|
11
11
|
}
|
|
12
|
-
if (serviceKey === "
|
|
13
|
-
return "railway-service:
|
|
12
|
+
if (serviceKey === "workerRunner") {
|
|
13
|
+
return "railway-service:worker-runner";
|
|
14
14
|
}
|
|
15
15
|
return `railway-service:${serviceKey}`;
|
|
16
16
|
}
|
package/dist/reconcile/units.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
const TRESEED_RECONCILE_UNIT_TYPES = [
|
|
2
2
|
"web-ui",
|
|
3
3
|
"api-runtime",
|
|
4
|
-
"manager-runtime",
|
|
5
|
-
"worker-runtime",
|
|
6
|
-
"workday-start-runtime",
|
|
7
|
-
"workday-report-runtime",
|
|
4
|
+
"workday-manager-runtime",
|
|
5
|
+
"worker-runner-runtime",
|
|
8
6
|
"edge-worker",
|
|
9
7
|
"content-store",
|
|
10
8
|
"queue",
|
|
@@ -15,10 +13,8 @@ const TRESEED_RECONCILE_UNIT_TYPES = [
|
|
|
15
13
|
"custom-domain:api",
|
|
16
14
|
"dns-record",
|
|
17
15
|
"railway-service:api",
|
|
18
|
-
"railway-service:manager",
|
|
19
|
-
"railway-service:worker"
|
|
20
|
-
"railway-service:workday-start",
|
|
21
|
-
"railway-service:workday-report"
|
|
16
|
+
"railway-service:workday-manager",
|
|
17
|
+
"railway-service:worker-runner"
|
|
22
18
|
];
|
|
23
19
|
function targetKey(target) {
|
|
24
20
|
return target.kind === "persistent" ? target.scope : `branch:${target.branchName}`;
|