@treeseed/sdk 0.10.27 → 0.11.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.
Files changed (168) hide show
  1. package/README.md +207 -6
  2. package/dist/capacity-provider.d.ts +3 -1
  3. package/dist/capacity-provider.js +25 -5
  4. package/dist/control-plane.d.ts +1 -0
  5. package/dist/control-plane.js +38 -13
  6. package/dist/db/market-schema.d.ts +8860 -6172
  7. package/dist/db/market-schema.js +108 -0
  8. package/dist/db/node-sqlite.js +7 -2
  9. package/dist/hosting/apps.d.ts +12 -0
  10. package/dist/hosting/apps.js +107 -0
  11. package/dist/hosting/builtins.d.ts +25 -0
  12. package/dist/hosting/builtins.js +791 -0
  13. package/dist/hosting/contracts.d.ts +207 -0
  14. package/dist/hosting/contracts.js +0 -0
  15. package/dist/hosting/graph.d.ts +192 -0
  16. package/dist/hosting/graph.js +1106 -0
  17. package/dist/hosting/index.d.ts +4 -0
  18. package/dist/hosting/index.js +4 -0
  19. package/dist/index.d.ts +11 -4
  20. package/dist/index.js +71 -7
  21. package/dist/managed-dependencies.js +1 -2
  22. package/dist/market-client.d.ts +63 -3
  23. package/dist/market-client.js +83 -11
  24. package/dist/operations/services/bootstrap-runner.d.ts +3 -1
  25. package/dist/operations/services/bootstrap-runner.js +22 -2
  26. package/dist/operations/services/config-runtime.d.ts +10 -5
  27. package/dist/operations/services/config-runtime.js +209 -66
  28. package/dist/operations/services/deploy.d.ts +70 -7
  29. package/dist/operations/services/deploy.js +579 -64
  30. package/dist/operations/services/deployment-readiness.d.ts +30 -0
  31. package/dist/operations/services/deployment-readiness.js +175 -0
  32. package/dist/operations/services/git-workflow.d.ts +2 -1
  33. package/dist/operations/services/git-workflow.js +9 -3
  34. package/dist/operations/services/github-actions-verification.d.ts +1 -0
  35. package/dist/operations/services/github-actions-verification.js +1 -0
  36. package/dist/operations/services/github-api.js +1 -1
  37. package/dist/operations/services/github-automation.d.ts +1 -1
  38. package/dist/operations/services/github-automation.js +4 -3
  39. package/dist/operations/services/github-credentials.d.ts +13 -0
  40. package/dist/operations/services/github-credentials.js +58 -0
  41. package/dist/operations/services/hosted-service-checks.d.ts +63 -0
  42. package/dist/operations/services/hosted-service-checks.js +327 -0
  43. package/dist/operations/services/hub-provider-launch.js +3 -3
  44. package/dist/operations/services/live-hosted-service-checks.d.ts +25 -0
  45. package/dist/operations/services/live-hosted-service-checks.js +350 -0
  46. package/dist/operations/services/managed-host-security.js +1 -1
  47. package/dist/operations/services/operations-runner-smoke.d.ts +30 -0
  48. package/dist/operations/services/operations-runner-smoke.js +180 -0
  49. package/dist/operations/services/package-adapters.d.ts +95 -0
  50. package/dist/operations/services/package-adapters.js +288 -0
  51. package/dist/operations/services/package-reference-policy.d.ts +1 -0
  52. package/dist/operations/services/package-reference-policy.js +15 -2
  53. package/dist/operations/services/project-platform.d.ts +80 -22
  54. package/dist/operations/services/project-platform.js +49 -8
  55. package/dist/operations/services/project-web-monitor.js +26 -4
  56. package/dist/operations/services/railway-api.d.ts +88 -5
  57. package/dist/operations/services/railway-api.js +626 -35
  58. package/dist/operations/services/railway-deploy.d.ts +46 -40
  59. package/dist/operations/services/railway-deploy.js +261 -293
  60. package/dist/operations/services/release-candidate.d.ts +19 -0
  61. package/dist/operations/services/release-candidate.js +375 -38
  62. package/dist/operations/services/repository-save-orchestrator.d.ts +3 -1
  63. package/dist/operations/services/repository-save-orchestrator.js +279 -66
  64. package/dist/operations/services/runtime-tools.d.ts +1 -0
  65. package/dist/operations/services/runtime-tools.js +10 -9
  66. package/dist/operations/services/template-registry.js +14 -7
  67. package/dist/operations/services/verification-cache.d.ts +25 -0
  68. package/dist/operations/services/verification-cache.js +71 -0
  69. package/dist/operations/services/workspace-dependency-mode.js +9 -1
  70. package/dist/operations/services/workspace-save.js +1 -1
  71. package/dist/operations/services/workspace-tools.js +2 -1
  72. package/dist/platform/contracts.d.ts +32 -1
  73. package/dist/platform/deploy-config.js +73 -8
  74. package/dist/platform/env.yaml +163 -35
  75. package/dist/platform/environment.d.ts +1 -0
  76. package/dist/platform/environment.js +74 -5
  77. package/dist/platform/plugin.d.ts +9 -0
  78. package/dist/platform-operation-store.js +2 -2
  79. package/dist/platform-operations.js +1 -1
  80. package/dist/reconcile/bootstrap-systems.js +2 -2
  81. package/dist/reconcile/builtin-adapters.js +372 -189
  82. package/dist/reconcile/contracts.d.ts +9 -5
  83. package/dist/reconcile/desired-state.d.ts +1 -0
  84. package/dist/reconcile/desired-state.js +5 -5
  85. package/dist/reconcile/engine.d.ts +5 -2
  86. package/dist/reconcile/engine.js +53 -32
  87. package/dist/reconcile/index.d.ts +2 -0
  88. package/dist/reconcile/index.js +2 -0
  89. package/dist/reconcile/live-acceptance.d.ts +79 -0
  90. package/dist/reconcile/live-acceptance.js +1615 -0
  91. package/dist/reconcile/platform.d.ts +104 -0
  92. package/dist/reconcile/platform.js +100 -0
  93. package/dist/reconcile/state.js +4 -4
  94. package/dist/reconcile/units.js +2 -2
  95. package/dist/scripts/deployment-readiness.js +20 -0
  96. package/dist/scripts/generate-treedx-openapi-types.js +186 -0
  97. package/dist/scripts/operations-runner-smoke.js +16 -0
  98. package/dist/scripts/release-verify.js +4 -1
  99. package/dist/scripts/template-catalog.test.js +7 -7
  100. package/dist/scripts/tenant-workflow-action.js +10 -1
  101. package/dist/sdk-types.d.ts +172 -5
  102. package/dist/sdk-types.js +28 -3
  103. package/dist/sdk.d.ts +35 -24
  104. package/dist/sdk.js +186 -17
  105. package/dist/template-launch-requirements.js +9 -0
  106. package/dist/treedx/adapters.d.ts +6 -0
  107. package/dist/treedx/adapters.js +36 -0
  108. package/dist/treedx/client.d.ts +222 -0
  109. package/dist/treedx/client.js +871 -0
  110. package/dist/treedx/errors.d.ts +13 -0
  111. package/dist/treedx/errors.js +17 -0
  112. package/dist/treedx/federated-client.d.ts +27 -0
  113. package/dist/treedx/federated-client.js +158 -0
  114. package/dist/treedx/generated/openapi-types.d.ts +3558 -0
  115. package/dist/treedx/generated/openapi-types.js +0 -0
  116. package/dist/treedx/graph-adapter.d.ts +33 -0
  117. package/dist/treedx/graph-adapter.js +156 -0
  118. package/dist/treedx/index.d.ts +14 -0
  119. package/dist/treedx/index.js +48 -0
  120. package/dist/treedx/market-integration.d.ts +27 -0
  121. package/dist/treedx/market-integration.js +131 -0
  122. package/dist/treedx/ports.d.ts +166 -0
  123. package/dist/treedx/ports.js +231 -0
  124. package/dist/treedx/query-adapter.d.ts +19 -0
  125. package/dist/treedx/query-adapter.js +62 -0
  126. package/dist/treedx/registry-client.d.ts +11 -0
  127. package/dist/treedx/registry-client.js +19 -0
  128. package/dist/treedx/repository-adapter.d.ts +45 -0
  129. package/dist/treedx/repository-adapter.js +308 -0
  130. package/dist/treedx/sdk-integration.d.ts +27 -0
  131. package/dist/treedx/sdk-integration.js +63 -0
  132. package/dist/treedx/types.d.ts +1084 -0
  133. package/dist/treedx/types.js +8 -0
  134. package/dist/treedx/workspace-adapter.d.ts +27 -0
  135. package/dist/treedx/workspace-adapter.js +65 -0
  136. package/dist/treedx-backends.d.ts +218 -0
  137. package/dist/treedx-backends.js +632 -0
  138. package/dist/treedx-client.d.ts +86 -0
  139. package/dist/treedx-client.js +175 -0
  140. package/dist/treeseed/template-catalog/catalog.fixture.json +497 -138
  141. package/dist/workflow/operations.d.ts +119 -13
  142. package/dist/workflow/operations.js +309 -53
  143. package/dist/workflow-state.d.ts +13 -0
  144. package/dist/workflow-state.js +43 -26
  145. package/dist/workflow-support.d.ts +11 -3
  146. package/dist/workflow-support.js +67 -3
  147. package/dist/workflow.d.ts +5 -0
  148. package/drizzle/market/0004_treedx_market_integration.sql +99 -0
  149. package/package.json +34 -3
  150. package/templates/github/deploy-web.workflow.yml +39 -6
  151. package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.d.ts +0 -3
  152. package/dist/treeseed/template-catalog/templates/starter-basic/template/astro.config.ts +0 -6
  153. package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +0 -35
  154. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/api/server.js +0 -4
  155. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +0 -65
  156. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/decisions/adopt-initial-proposal-loop.mdx +0 -22
  157. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/empty/.gitkeep +0 -1
  158. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/knowledge/handbook/index.mdx +0 -11
  159. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/pages/welcome.mdx +0 -11
  160. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/people/starter-steward.mdx +0 -11
  161. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/proposals/establish-initial-proposal-loop.mdx +0 -17
  162. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.d.ts +0 -1
  163. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content.config.ts +0 -3
  164. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/env.yaml +0 -1
  165. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +0 -26
  166. package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +0 -74
  167. package/dist/treeseed/template-catalog/templates/starter-basic/template/tsconfig.json +0 -9
  168. package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +0 -103
@@ -0,0 +1,327 @@
1
+ import { relative, resolve } from "node:path";
2
+ import { collectTreeseedEnvironmentContext, resolveTreeseedMachineEnvironmentValues } from "./config-runtime.js";
3
+ import { configuredRailwayServices } from "./railway-deploy.js";
4
+ import { loadCliDeployConfig } from "./runtime-tools.js";
5
+ import { discoverTreeseedApplications } from "../../hosting/apps.js";
6
+ const RAILWAY_SECRET_KEYS_BY_SERVICE = {
7
+ api: [
8
+ "TREESEED_DATABASE_URL",
9
+ "TREESEED_WEB_SERVICE_SECRET",
10
+ "TREESEED_PLATFORM_RUNNER_SECRET",
11
+ "TREESEED_CREDENTIAL_SESSION_SECRET"
12
+ ],
13
+ operationsRunner: [
14
+ "TREESEED_DATABASE_URL",
15
+ "TREESEED_PLATFORM_RUNNER_SECRET",
16
+ "TREESEED_CREDENTIAL_SESSION_SECRET"
17
+ ]
18
+ };
19
+ const RAILWAY_VARIABLE_KEYS_BY_SERVICE = {
20
+ operationsRunner: [
21
+ "TREESEED_PLATFORM_RUNNER_ID",
22
+ "TREESEED_PLATFORM_RUNNER_DATA_DIR",
23
+ "TREESEED_PLATFORM_RUNNER_ENVIRONMENT",
24
+ "TREESEED_MANAGER_ID"
25
+ ]
26
+ };
27
+ function check(input) {
28
+ return {
29
+ ...input,
30
+ issues: input.issues ?? []
31
+ };
32
+ }
33
+ function statusForMatch(observed, expected) {
34
+ return observed === expected ? "passed" : "failed";
35
+ }
36
+ function rootDirectory(tenantRoot, rootDir) {
37
+ const rel = relative(tenantRoot, rootDir).split("\\").join("/");
38
+ return rel || ".";
39
+ }
40
+ function railwayServiceRootDirectory(tenantRoot, service) {
41
+ return rootDirectory(service.application?.root ?? tenantRoot, service.rootDir);
42
+ }
43
+ function hasConfiguredValue(values, key) {
44
+ return typeof values[key] === "string" && Boolean(values[key]?.trim());
45
+ }
46
+ function hasProviderKey(record, key) {
47
+ if (Array.isArray(record)) return record.includes(key);
48
+ if (record && typeof record === "object") return Object.prototype.hasOwnProperty.call(record, key);
49
+ return false;
50
+ }
51
+ function variableObserved(observed, key) {
52
+ return hasProviderKey(observed?.variables, key) || hasProviderKey(observed?.secrets, key);
53
+ }
54
+ function valuePresence(values, observed, key, serviceTarget) {
55
+ const providerPresent = variableObserved(observed, key);
56
+ const machinePresent = hasConfiguredValue(values, key);
57
+ return {
58
+ key,
59
+ serviceTarget,
60
+ present: providerPresent || machinePresent,
61
+ source: providerPresent ? "provider" : machinePresent ? "machine-config" : "missing",
62
+ observation: observed ? "provider-live" : "not-observed"
63
+ };
64
+ }
65
+ function serviceTypeFor(key) {
66
+ if (key === "api") return "api";
67
+ if (key === "operationsRunner") return "operationsRunner";
68
+ if (key === "treeseedDatabase") return "treeseedDatabase";
69
+ return "unknown";
70
+ }
71
+ function observedFor(options, serviceName) {
72
+ return serviceName ? options.observedRailwayServices?.[serviceName] : void 0;
73
+ }
74
+ function webCheckConfig(deployConfig, selectedApplication) {
75
+ const selectedConfig = selectedApplication?.roles.includes("web") ? selectedApplication.config : deployConfig;
76
+ return {
77
+ appId: selectedApplication?.roles.includes("web") ? selectedApplication.id : "web",
78
+ config: selectedConfig,
79
+ web: selectedConfig.surfaces?.web
80
+ };
81
+ }
82
+ function httpStatus(url, options) {
83
+ const observed = options.httpChecks?.[url];
84
+ if (!observed) {
85
+ return check({
86
+ id: `http:${url}`,
87
+ provider: "http",
88
+ serviceType: "unknown",
89
+ target: options.target ?? "prod",
90
+ description: `HTTP check ${url}`,
91
+ expected: { ok: true },
92
+ observed: { skipped: true },
93
+ status: "skipped",
94
+ issues: ["No live HTTP observation was provided."]
95
+ });
96
+ }
97
+ const ok = observed.ok === true || typeof observed.status === "number" && observed.status >= 200 && observed.status < 400;
98
+ const fallbackOk = observed.fallbackOk === true || typeof observed.fallbackStatus === "number" && observed.fallbackStatus >= 200 && observed.fallbackStatus < 400;
99
+ return check({
100
+ id: `http:${url}`,
101
+ provider: "http",
102
+ serviceType: "unknown",
103
+ target: options.target ?? "prod",
104
+ description: `HTTP check ${url}`,
105
+ expected: { ok: true },
106
+ observed: {
107
+ status: observed.status ?? null,
108
+ ok,
109
+ skipped: observed.skipped === true,
110
+ fallbackUrl: observed.fallbackUrl,
111
+ fallbackStatus: observed.fallbackStatus,
112
+ fallbackOk
113
+ },
114
+ status: observed.skipped ? "skipped" : ok ? "passed" : fallbackOk ? "warning" : "failed",
115
+ issues: ok ? [] : fallbackOk ? [observed.error ?? `HTTP check ${url} did not return a successful status yet; fallback ${observed.fallbackUrl} responded.`] : [observed.error ?? `HTTP check ${url} did not return a successful status.`]
116
+ });
117
+ }
118
+ function collectTreeseedHostedServiceChecks(options) {
119
+ const target = options.target ?? "prod";
120
+ const tenantRoot = options.tenantRoot;
121
+ const deployConfig = loadCliDeployConfig(tenantRoot);
122
+ const registry = collectTreeseedEnvironmentContext(tenantRoot);
123
+ let machineValues = {};
124
+ try {
125
+ machineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, target);
126
+ } catch {
127
+ machineValues = {};
128
+ }
129
+ const values = { ...machineValues, ...options.valuesOverlay ?? {} };
130
+ const checks = [];
131
+ const selectedAppId = options.appId?.trim() || null;
132
+ const selectedApplication = selectedAppId ? discoverTreeseedApplications(tenantRoot).find((application) => application.id === selectedAppId || application.relativeRoot === selectedAppId) : null;
133
+ const includeWeb = !selectedAppId || selectedAppId === "web" || selectedApplication?.roles.includes("web") === true;
134
+ const includeApi = !selectedAppId || selectedAppId === "api" || selectedApplication?.roles.includes("api") === true;
135
+ const selectedAppHasApi = Boolean(
136
+ selectedApplication?.roles.includes("api") || selectedApplication?.config.surfaces?.api?.enabled === true || selectedApplication?.config.services?.api?.enabled !== false && selectedApplication?.config.services?.api
137
+ );
138
+ const selectedWeb = webCheckConfig(deployConfig, selectedApplication);
139
+ const web = selectedWeb.web;
140
+ if (includeWeb && web?.enabled !== false && web?.provider === "cloudflare") {
141
+ const domain = web.environments?.[target]?.domain ?? web.publicBaseUrl ?? selectedWeb.config.siteUrl ?? null;
142
+ checks.push(check({
143
+ id: `cloudflare:${selectedWeb.appId}:surface`,
144
+ provider: "cloudflare",
145
+ serviceKey: selectedWeb.appId,
146
+ serviceType: "web",
147
+ target,
148
+ description: "Cloudflare web surface is configured.",
149
+ expected: {
150
+ provider: "cloudflare",
151
+ domain,
152
+ pagesProjectName: selectedWeb.config.cloudflare?.pages?.projectName ?? null
153
+ },
154
+ observed: { configured: true },
155
+ status: "passed"
156
+ }));
157
+ if (domain) {
158
+ const url = String(domain).startsWith("http") ? String(domain) : `https://${domain}`;
159
+ checks.push({ ...httpStatus(url, options), id: `http:${selectedWeb.appId}`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web public URL responds." });
160
+ if (!selectedAppId || selectedAppHasApi) {
161
+ checks.push({ ...httpStatus(`${url.replace(/\/+$/u, "")}/v1/healthz`, options), id: `http:${selectedWeb.appId}:v1-healthz`, serviceKey: selectedWeb.appId, serviceType: "web", description: "Web proxy reaches API health." });
162
+ }
163
+ }
164
+ }
165
+ for (const [surfaceKey, surface] of Object.entries(deployConfig.surfaces ?? {})) {
166
+ if (selectedAppId === "api" && surfaceKey === "web") continue;
167
+ if (surface && typeof surface === "object" && surface.enabled !== false && surface.provider && !["cloudflare", "railway"].includes(surface.provider)) {
168
+ checks.push(check({
169
+ id: `surface-provider:${surfaceKey}:${surface.provider}`,
170
+ provider: surface.provider === "github" ? "github" : "http",
171
+ serviceKey: surfaceKey,
172
+ serviceType: surfaceKey === "web" ? "web" : surfaceKey === "api" ? "api" : "unknown",
173
+ target,
174
+ description: `Unsupported hosted surface provider ${surface.provider}.`,
175
+ expected: { supportedProviders: ["cloudflare", "railway"] },
176
+ observed: { provider: surface.provider },
177
+ status: "warning",
178
+ issues: [`Hosted service checker does not yet support provider ${surface.provider}.`]
179
+ }));
180
+ }
181
+ }
182
+ const configuredServices = configuredRailwayServices(tenantRoot, target).filter((service) => !selectedAppId || service.application?.id === selectedAppId);
183
+ for (const service of configuredServices) {
184
+ const serviceType = serviceTypeFor(service.key);
185
+ const observed = observedFor(options, service.serviceName);
186
+ const expectedRootDirectory = railwayServiceRootDirectory(tenantRoot, service);
187
+ checks.push(check({
188
+ id: `railway:${service.instanceKey}:service`,
189
+ provider: "railway",
190
+ serviceKey: service.key,
191
+ serviceType,
192
+ target,
193
+ description: `Railway service ${service.serviceName} is configured.`,
194
+ expected: { projectName: service.projectName, serviceName: service.serviceName, environmentName: service.railwayEnvironment },
195
+ observed: observed ? { projectName: observed.projectName ?? null, serviceName: observed.serviceName ?? null, environmentName: observed.environmentName ?? null } : { skipped: true },
196
+ status: observed ? "passed" : "skipped",
197
+ issues: observed ? [] : ["No live Railway observation was provided."]
198
+ }));
199
+ for (const [key, expected] of Object.entries({
200
+ rootDirectory: expectedRootDirectory,
201
+ buildCommand: service.buildCommand,
202
+ startCommand: service.startCommand,
203
+ healthcheckPath: service.healthcheckPath,
204
+ healthcheckTimeoutSeconds: service.healthcheckTimeoutSeconds,
205
+ runtimeMode: service.runtimeMode
206
+ })) {
207
+ if (expected == null) continue;
208
+ const actual = observed?.[key] ?? null;
209
+ checks.push(check({
210
+ id: `railway:${service.instanceKey}:${key}`,
211
+ provider: "railway",
212
+ serviceKey: service.key,
213
+ serviceType,
214
+ target,
215
+ description: `Railway ${service.serviceName} ${key} matches config.`,
216
+ expected: { [key]: expected },
217
+ observed: observed ? { [key]: actual } : { skipped: true },
218
+ status: observed ? statusForMatch(actual, expected) : "skipped",
219
+ issues: !observed || actual === expected ? [] : [`Expected ${key}=${expected}, observed ${actual ?? "(unset)"}.`]
220
+ }));
221
+ }
222
+ if (service.key === "operationsRunner" && service.volumeMountPath) {
223
+ checks.push(check({
224
+ id: `railway:${service.instanceKey}:volume`,
225
+ provider: "railway",
226
+ serviceKey: service.key,
227
+ serviceType,
228
+ target,
229
+ description: `Railway ${service.serviceName} volume mount matches config.`,
230
+ expected: { volumeMountPath: service.volumeMountPath },
231
+ observed: observed ? { volumeMountPath: observed.volumeMountPath ?? null } : { skipped: true },
232
+ status: observed ? statusForMatch(observed.volumeMountPath ?? null, service.volumeMountPath) : "skipped",
233
+ issues: !observed || observed.volumeMountPath === service.volumeMountPath ? [] : [`Expected volumeMountPath=${service.volumeMountPath}, observed ${observed.volumeMountPath ?? "(unset)"}.`]
234
+ }));
235
+ }
236
+ for (const key of [...RAILWAY_SECRET_KEYS_BY_SERVICE[service.key] ?? [], ...RAILWAY_VARIABLE_KEYS_BY_SERVICE[service.key] ?? []]) {
237
+ const presence = valuePresence(values, observed, key, service.key);
238
+ const status = presence.present ? "passed" : observed ? "failed" : "skipped";
239
+ checks.push(check({
240
+ id: `railway:${service.instanceKey}:env:${key}`,
241
+ provider: "railway",
242
+ serviceKey: service.key,
243
+ serviceType,
244
+ target,
245
+ description: `Railway ${service.serviceName} has ${key}.`,
246
+ expected: { key, serviceTarget: service.key, present: true },
247
+ observed: presence,
248
+ status,
249
+ issues: presence.present ? [] : observed ? [`${key} is missing for ${service.key}.`] : ["No live Railway variable observation was provided."]
250
+ }));
251
+ }
252
+ if (service.publicBaseUrl) {
253
+ const baseUrl = service.publicBaseUrl.replace(/\/+$/u, "");
254
+ checks.push({ ...httpStatus(`${baseUrl}${service.healthcheckPath ?? "/healthz"}`, options), id: `http:${service.instanceKey}:healthz`, serviceKey: service.key, serviceType, description: `${service.serviceName} health endpoint responds.` });
255
+ if (service.key === "api") {
256
+ checks.push({ ...httpStatus(`${baseUrl}/healthz/deep`, options), id: `http:${service.instanceKey}:healthz-deep`, serviceKey: service.key, serviceType, description: `${service.serviceName} deep health endpoint responds.` });
257
+ }
258
+ }
259
+ }
260
+ const applicationConfigs = [
261
+ ...includeWeb ? [deployConfig] : [],
262
+ ...discoverTreeseedApplications(tenantRoot).filter((application) => application.root !== resolve(tenantRoot)).filter((application) => !selectedAppId || application.id === selectedAppId).map((application) => application.config)
263
+ ];
264
+ const treeseedDatabaseService = applicationConfigs.map((config) => config.services?.treeseedDatabase).find((service) => service?.enabled !== false);
265
+ if (includeApi && treeseedDatabaseService?.enabled !== false && treeseedDatabaseService?.provider === "railway") {
266
+ const targets = treeseedDatabaseService.railway?.serviceTargets ?? [];
267
+ checks.push(check({
268
+ id: "railway:treeseedDatabase:targets",
269
+ provider: "railway",
270
+ serviceKey: "treeseedDatabase",
271
+ serviceType: "treeseedDatabase",
272
+ target,
273
+ description: "Treeseed database targets API and runner services.",
274
+ expected: { serviceTargets: ["api", "operationsRunner"] },
275
+ observed: { serviceTargets: targets },
276
+ status: targets.includes("api") && targets.includes("operationsRunner") ? "passed" : "failed",
277
+ issues: targets.includes("api") && targets.includes("operationsRunner") ? [] : ["Treeseed database must target api and operationsRunner."]
278
+ }));
279
+ }
280
+ for (const service of Object.values(selectedAppId ? {} : deployConfig.services ?? {})) {
281
+ if (service && typeof service === "object" && service.enabled !== false && service.provider && !["railway"].includes(service.provider)) {
282
+ checks.push(check({
283
+ id: `provider:${service.provider}`,
284
+ provider: "railway",
285
+ serviceType: "unknown",
286
+ target,
287
+ description: `Unsupported hosted service provider ${service.provider}.`,
288
+ expected: { supportedProviders: ["railway"] },
289
+ observed: { provider: service.provider },
290
+ status: "warning",
291
+ issues: [`Hosted service checker does not yet support provider ${service.provider}.`]
292
+ }));
293
+ }
294
+ }
295
+ const entryIds = new Set(registry.entries.map((entry) => entry.id));
296
+ for (const key of includeApi ? ["TREESEED_DATABASE_URL", "TREESEED_WEB_SERVICE_SECRET", "TREESEED_PLATFORM_RUNNER_SECRET"] : []) {
297
+ if (!entryIds.has(key)) {
298
+ checks.push(check({
299
+ id: `registry:${key}`,
300
+ provider: "railway",
301
+ serviceType: "unknown",
302
+ target,
303
+ description: `Environment registry declares ${key}.`,
304
+ expected: { registryEntry: key },
305
+ observed: { registryEntry: null },
306
+ status: "failed",
307
+ issues: [`${key} is missing from the environment registry.`]
308
+ }));
309
+ }
310
+ }
311
+ const summary = {
312
+ passed: checks.filter((entry) => entry.status === "passed").length,
313
+ failed: checks.filter((entry) => entry.status === "failed").length,
314
+ skipped: checks.filter((entry) => entry.status === "skipped").length,
315
+ warning: checks.filter((entry) => entry.status === "warning").length
316
+ };
317
+ return {
318
+ target,
319
+ tenantRoot: resolve(tenantRoot),
320
+ generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
321
+ summary,
322
+ checks
323
+ };
324
+ }
325
+ export {
326
+ collectTreeseedHostedServiceChecks
327
+ };
@@ -300,7 +300,7 @@ console.log(\`Treeseed project API listening on \${server.url}\`);
300
300
  }
301
301
  function applyManagedProjectDefaults(projectRoot, input) {
302
302
  const slug = slugify(input.projectSlug, "project");
303
- const marketBaseUrl = normalizeBaseUrl(input.marketBaseUrl ?? envOrNull("TREESEED_MARKET_API_BASE_URL") ?? "https://knowledge.coop");
303
+ const marketBaseUrl = normalizeBaseUrl(input.marketBaseUrl ?? envOrNull("TREESEED_API_BASE_URL") ?? "https://knowledge.coop");
304
304
  const productionDomain = String(input.domains?.productionDomain ?? "").trim() || null;
305
305
  const stagingDomain = String(input.domains?.stagingDomain ?? "").trim() || null;
306
306
  const productionSiteUrl = domainUrl(productionDomain) ?? resolveManagedWebUrl(slug);
@@ -344,7 +344,7 @@ function applyManagedProjectDefaults(projectRoot, input) {
344
344
  dlqName: `${slug}-agent-work-dlq`,
345
345
  pages: {
346
346
  projectName: slug,
347
- previewProjectName: `${slug}-staging`,
347
+ previewProjectName: slug,
348
348
  productionBranch: "main",
349
349
  stagingBranch: "staging",
350
350
  buildOutputDir: "dist",
@@ -608,7 +608,7 @@ function buildCloudflareHostEnvironmentOverlay(input, scope) {
608
608
  }
609
609
  overlay.CLOUDFLARE_ACCOUNT_ID = overlay.CLOUDFLARE_ACCOUNT_ID || "";
610
610
  overlayValue(overlay, "TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME", overlay.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME || projectSlug);
611
- overlayValue(overlay, "TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME", overlay.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME || `${projectSlug}-staging`);
611
+ overlayValue(overlay, "TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME", overlay.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME || overlay.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME || projectSlug);
612
612
  overlayValue(overlay, "TREESEED_CONTENT_BUCKET_NAME", overlay.TREESEED_CONTENT_BUCKET_NAME || `${projectSlug}-content`);
613
613
  overlayValue(overlay, "TREESEED_CONTENT_BUCKET_BINDING", overlay.TREESEED_CONTENT_BUCKET_BINDING || "TREESEED_CONTENT_BUCKET");
614
614
  return overlay;
@@ -0,0 +1,25 @@
1
+ import { type TreeseedHostedServiceCheckReport, type TreeseedHostedServiceTarget } from './hosted-service-checks.ts';
2
+ export interface TreeseedLiveHostedServiceCheckOptions {
3
+ tenantRoot: string;
4
+ target: TreeseedHostedServiceTarget;
5
+ appId?: string;
6
+ strict?: boolean;
7
+ requireLiveRailway?: boolean;
8
+ requireLiveHttp?: boolean;
9
+ timeoutMs?: number;
10
+ retry?: {
11
+ attempts: number;
12
+ intervalMs: number;
13
+ };
14
+ env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
15
+ fetchImpl?: typeof fetch;
16
+ }
17
+ export interface TreeseedLiveHostedServiceCheckReport extends TreeseedHostedServiceCheckReport {
18
+ live: true;
19
+ liveObservation: {
20
+ railway: 'observed' | 'skipped' | 'failed';
21
+ http: 'observed' | 'skipped' | 'failed';
22
+ issues: string[];
23
+ };
24
+ }
25
+ export declare function collectTreeseedLiveHostedServiceChecks(options: TreeseedLiveHostedServiceCheckOptions): Promise<TreeseedLiveHostedServiceCheckReport>;