@treeseed/sdk 0.10.28 → 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 (148) 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 +10 -3
  20. package/dist/index.js +63 -6
  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/verification-cache.d.ts +25 -0
  67. package/dist/operations/services/verification-cache.js +71 -0
  68. package/dist/operations/services/workspace-dependency-mode.js +9 -1
  69. package/dist/operations/services/workspace-save.js +1 -1
  70. package/dist/operations/services/workspace-tools.js +2 -1
  71. package/dist/platform/contracts.d.ts +32 -1
  72. package/dist/platform/deploy-config.js +73 -8
  73. package/dist/platform/env.yaml +163 -35
  74. package/dist/platform/environment.d.ts +1 -0
  75. package/dist/platform/environment.js +74 -5
  76. package/dist/platform/plugin.d.ts +9 -0
  77. package/dist/platform-operation-store.js +2 -2
  78. package/dist/platform-operations.js +1 -1
  79. package/dist/reconcile/bootstrap-systems.js +2 -2
  80. package/dist/reconcile/builtin-adapters.js +372 -189
  81. package/dist/reconcile/contracts.d.ts +9 -5
  82. package/dist/reconcile/desired-state.d.ts +1 -0
  83. package/dist/reconcile/desired-state.js +5 -5
  84. package/dist/reconcile/engine.d.ts +5 -2
  85. package/dist/reconcile/engine.js +53 -32
  86. package/dist/reconcile/index.d.ts +2 -0
  87. package/dist/reconcile/index.js +2 -0
  88. package/dist/reconcile/live-acceptance.d.ts +79 -0
  89. package/dist/reconcile/live-acceptance.js +1615 -0
  90. package/dist/reconcile/platform.d.ts +104 -0
  91. package/dist/reconcile/platform.js +100 -0
  92. package/dist/reconcile/state.js +4 -4
  93. package/dist/reconcile/units.js +2 -2
  94. package/dist/scripts/deployment-readiness.js +20 -0
  95. package/dist/scripts/generate-treedx-openapi-types.js +186 -0
  96. package/dist/scripts/operations-runner-smoke.js +16 -0
  97. package/dist/scripts/release-verify.js +4 -1
  98. package/dist/scripts/tenant-workflow-action.js +10 -1
  99. package/dist/sdk-types.d.ts +169 -4
  100. package/dist/sdk-types.js +20 -2
  101. package/dist/sdk.d.ts +35 -24
  102. package/dist/sdk.js +186 -17
  103. package/dist/template-launch-requirements.js +9 -0
  104. package/dist/treedx/adapters.d.ts +6 -0
  105. package/dist/treedx/adapters.js +36 -0
  106. package/dist/treedx/client.d.ts +222 -0
  107. package/dist/treedx/client.js +871 -0
  108. package/dist/treedx/errors.d.ts +13 -0
  109. package/dist/treedx/errors.js +17 -0
  110. package/dist/treedx/federated-client.d.ts +27 -0
  111. package/dist/treedx/federated-client.js +158 -0
  112. package/dist/treedx/generated/openapi-types.d.ts +3558 -0
  113. package/dist/treedx/generated/openapi-types.js +0 -0
  114. package/dist/treedx/graph-adapter.d.ts +33 -0
  115. package/dist/treedx/graph-adapter.js +156 -0
  116. package/dist/treedx/index.d.ts +14 -0
  117. package/dist/treedx/index.js +48 -0
  118. package/dist/treedx/market-integration.d.ts +27 -0
  119. package/dist/treedx/market-integration.js +131 -0
  120. package/dist/treedx/ports.d.ts +166 -0
  121. package/dist/treedx/ports.js +231 -0
  122. package/dist/treedx/query-adapter.d.ts +19 -0
  123. package/dist/treedx/query-adapter.js +62 -0
  124. package/dist/treedx/registry-client.d.ts +11 -0
  125. package/dist/treedx/registry-client.js +19 -0
  126. package/dist/treedx/repository-adapter.d.ts +45 -0
  127. package/dist/treedx/repository-adapter.js +308 -0
  128. package/dist/treedx/sdk-integration.d.ts +27 -0
  129. package/dist/treedx/sdk-integration.js +63 -0
  130. package/dist/treedx/types.d.ts +1084 -0
  131. package/dist/treedx/types.js +8 -0
  132. package/dist/treedx/workspace-adapter.d.ts +27 -0
  133. package/dist/treedx/workspace-adapter.js +65 -0
  134. package/dist/treedx-backends.d.ts +218 -0
  135. package/dist/treedx-backends.js +632 -0
  136. package/dist/treedx-client.d.ts +86 -0
  137. package/dist/treedx-client.js +175 -0
  138. package/dist/treeseed/template-catalog/catalog.fixture.json +23 -23
  139. package/dist/workflow/operations.d.ts +119 -13
  140. package/dist/workflow/operations.js +309 -53
  141. package/dist/workflow-state.d.ts +13 -0
  142. package/dist/workflow-state.js +43 -26
  143. package/dist/workflow-support.d.ts +11 -3
  144. package/dist/workflow-support.js +67 -3
  145. package/dist/workflow.d.ts +5 -0
  146. package/drizzle/market/0004_treedx_market_integration.sql +99 -0
  147. package/package.json +34 -3
  148. package/templates/github/deploy-web.workflow.yml +39 -6
@@ -232,9 +232,7 @@ export declare function applyTreeseedSafeRepairs(tenantRoot: string): TreeseedRe
232
232
  export declare function resolveTreeseedMachineEnvironmentValues(tenantRoot: any, scope: any, additionalKeys?: never[]): {};
233
233
  export declare function setTreeseedMachineEnvironmentValue(tenantRoot: any, scope: any, entry: any, value: any): any;
234
234
  export declare function collectTreeseedEnvironmentContext(tenantRoot: any): import("../../platform/environment.ts").TreeseedResolvedEnvironmentRegistry;
235
- export declare function collectTreeseedConfigSeedValues(tenantRoot: any, scope: any, env?: NodeJS.ProcessEnv, valuesOverlay?: {}): {
236
- [k: string]: unknown;
237
- };
235
+ export declare function collectTreeseedConfigSeedValues(tenantRoot: any, scope: any, env?: NodeJS.ProcessEnv, valuesOverlay?: {}): any;
238
236
  export declare function resolveTreeseedLaunchEnvironment({ tenantRoot, scope, baseEnv, overrides, }: {
239
237
  tenantRoot: string;
240
238
  scope: TreeseedConfigScope;
@@ -341,6 +339,13 @@ export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRu
341
339
  repository: string;
342
340
  scope: "local" | "staging" | "prod";
343
341
  environment: string;
342
+ credential: {
343
+ repository: string;
344
+ envName: string;
345
+ configured: boolean;
346
+ source: "repository" | "fallback" | "missing";
347
+ fallbackUsed: boolean;
348
+ };
344
349
  entryIds: string[] | undefined;
345
350
  }>;
346
351
  export declare function syncTreeseedCloudflareEnvironment({ tenantRoot, scope, dryRun, valuesOverlay, entryIds, onProgress, }: {
@@ -368,7 +373,7 @@ export declare function syncTreeseedRailwayEnvironment({ tenantRoot, scope, dryR
368
373
  valuesOverlay?: Record<string, string | undefined>;
369
374
  entryIds?: string[];
370
375
  onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
371
- }): {
376
+ }): Promise<{
372
377
  scope: "local" | "staging" | "prod";
373
378
  entryIds: string[] | undefined;
374
379
  services: {
@@ -384,7 +389,7 @@ export declare function syncTreeseedRailwayEnvironment({ tenantRoot, scope, dryR
384
389
  variables: any;
385
390
  dryRun: boolean;
386
391
  }[];
387
- };
392
+ }>;
388
393
  export declare function initializeTreeseedPersistentEnvironment({ tenantRoot, scope, dryRun }?: {
389
394
  scope?: string | undefined;
390
395
  dryRun?: boolean | undefined;
@@ -34,10 +34,15 @@ import {
34
34
  configuredRailwayServices
35
35
  } from "./railway-deploy.js";
36
36
  import {
37
+ ensureRailwayEnvironment,
38
+ ensureRailwayProject,
39
+ ensureRailwayService,
37
40
  normalizeRailwayEnvironmentName,
38
41
  resolveRailwayWorkspace,
39
- resolveRailwayWorkspaceContext
42
+ resolveRailwayWorkspaceContext,
43
+ upsertRailwayVariables
40
44
  } from "./railway-api.js";
45
+ import { discoverTreeseedApplications } from "../../hosting/apps.js";
41
46
  import {
42
47
  createGitHubApiClient,
43
48
  ensureGitHubActionsEnvironment,
@@ -47,6 +52,7 @@ import {
47
52
  upsertGitHubEnvironmentSecret,
48
53
  upsertGitHubEnvironmentVariable
49
54
  } from "./github-api.js";
55
+ import { resolveGitHubCredentialForRepository } from "./github-credentials.js";
50
56
  import { loadCliDeployConfig, packageScriptPath, resolveWranglerBin, withProcessCwd } from "./runtime-tools.js";
51
57
  import { PRODUCTION_BRANCH, STAGING_BRANCH } from "./git-workflow.js";
52
58
  import {
@@ -86,6 +92,15 @@ const TREESEED_TEMPLATE_CATALOG_URL_ENV = "TREESEED_TEMPLATE_CATALOG_URL";
86
92
  const TREESEED_API_BASE_URL_ENV = "TREESEED_API_BASE_URL";
87
93
  const CLI_CHECK_TIMEOUT_MS = 5e3;
88
94
  const DEPRECATED_LOCAL_ENV_FILES = [".env.local", ".dev.vars"];
95
+ const PROVIDER_CONTROL_ENV_KEYS = [
96
+ "GH_TOKEN",
97
+ "GITHUB_TOKEN",
98
+ "CLOUDFLARE_API_TOKEN",
99
+ "CLOUDFLARE_ACCOUNT_ID",
100
+ "CLOUDFLARE_ZONE_ID",
101
+ "RAILWAY_API_TOKEN",
102
+ "TREESEED_RAILWAY_WORKSPACE"
103
+ ];
89
104
  const warnedDeprecatedLocalEnvRoots = /* @__PURE__ */ new Set();
90
105
  const inlineTreeseedSecretSessions = /* @__PURE__ */ new Map();
91
106
  const railwayConnectionCheckCache = /* @__PURE__ */ new Map();
@@ -1523,18 +1538,71 @@ function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env, v
1523
1538
  warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
1524
1539
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
1525
1540
  let machineValues = {};
1541
+ let localMachineValues = {};
1526
1542
  try {
1527
1543
  machineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, scope);
1544
+ if (scope !== "local") {
1545
+ localMachineValues = resolveTreeseedMachineEnvironmentValues(tenantRoot, "local");
1546
+ }
1528
1547
  } catch (error) {
1529
1548
  if (!(error instanceof TreeseedKeyAgentError)) {
1530
1549
  throw error;
1531
1550
  }
1532
1551
  }
1533
- return filterEnvironmentValuesByRegistry({
1552
+ const providerCredentialFallbacks = Object.fromEntries(
1553
+ PROVIDER_CONTROL_ENV_KEYS.map((key) => [key, localMachineValues[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
1554
+ );
1555
+ const values = {
1556
+ ...providerCredentialFallbacks,
1534
1557
  ...machineValues,
1535
1558
  ...nonEmptyEnvironmentValues(env),
1536
1559
  ...nonEmptyEnvironmentValues(valuesOverlay)
1537
- }, registry, scope);
1560
+ };
1561
+ const providerControlValues = Object.fromEntries(
1562
+ PROVIDER_CONTROL_ENV_KEYS.map((key) => [key, values[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
1563
+ );
1564
+ return {
1565
+ ...providerControlValues,
1566
+ ...filterEnvironmentValuesByRegistry(values, registry, scope)
1567
+ };
1568
+ }
1569
+ function workspaceBootstrapDeployConfig(tenantRoot, deployConfig) {
1570
+ const appConfigs = discoverTreeseedApplications(tenantRoot).map((application) => application.config).filter((config) => config && config !== deployConfig);
1571
+ if (appConfigs.length === 0) {
1572
+ return deployConfig;
1573
+ }
1574
+ const merged = {
1575
+ ...deployConfig,
1576
+ runtime: { ...deployConfig.runtime ?? {} },
1577
+ surfaces: { ...deployConfig.surfaces ?? {} },
1578
+ services: { ...deployConfig.services ?? {} }
1579
+ };
1580
+ for (const config of appConfigs) {
1581
+ if (config.runtime?.mode && merged.runtime?.mode !== "treeseed_managed") {
1582
+ merged.runtime = { ...merged.runtime, ...config.runtime };
1583
+ }
1584
+ merged.surfaces = { ...merged.surfaces, ...config.surfaces ?? {} };
1585
+ merged.services = { ...merged.services, ...config.services ?? {} };
1586
+ }
1587
+ return merged;
1588
+ }
1589
+ function configuredMarketDatabaseService(tenantRoot, deployConfig) {
1590
+ if (deployConfig.services?.treeseedDatabase) {
1591
+ return treeseedDatabaseDescriptor(deployConfig.services.treeseedDatabase, deployConfig.slug);
1592
+ }
1593
+ for (const application of discoverTreeseedApplications(tenantRoot)) {
1594
+ const service = application.config.services?.treeseedDatabase;
1595
+ if (service) {
1596
+ return treeseedDatabaseDescriptor(service, application.config.slug);
1597
+ }
1598
+ }
1599
+ return null;
1600
+ }
1601
+ function treeseedDatabaseDescriptor(service, slug) {
1602
+ return {
1603
+ service,
1604
+ serviceName: typeof service.railway?.serviceName === "string" && service.railway.serviceName.trim() ? service.railway.serviceName.trim() : `${slug ?? "treeseed-api"}-postgres`
1605
+ };
1538
1606
  }
1539
1607
  function nonEmptyEnvironmentValues(env = process.env) {
1540
1608
  return Object.fromEntries(
@@ -1600,7 +1668,7 @@ function resolveTreeseedLaunchEnvironment({
1600
1668
  );
1601
1669
  const systemSecretSuggestedValues = Object.fromEntries(
1602
1670
  registry.entries.filter(
1603
- (entry) => entry.id === "TREESEED_PLATFORM_RUNNER_SECRET" && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0
1671
+ (entry) => ["TREESEED_PLATFORM_RUNNER_SECRET", "TREESEED_CREDENTIAL_SESSION_SECRET"].includes(entry.id) && typeof suggestedValues[entry.id] === "string" && suggestedValues[entry.id].length > 0
1604
1672
  ).map((entry) => [entry.id, suggestedValues[entry.id]])
1605
1673
  );
1606
1674
  const scopedValues = scope === "local" ? { ...nonSecretSuggestedValues, ...systemSecretSuggestedValues, ...baseValues, ...machineValues } : { ...nonSecretSuggestedValues, ...systemSecretSuggestedValues, ...machineValues, ...baseValues };
@@ -1731,25 +1799,6 @@ function runGh(args, { cwd, dryRun = false, input, env } = {}) {
1731
1799
  }
1732
1800
  return result;
1733
1801
  }
1734
- function runRailway(args, { cwd, dryRun = false, input } = {}) {
1735
- if (dryRun) {
1736
- return { status: 0, stdout: "", stderr: "" };
1737
- }
1738
- const railway = resolveTreeseedToolCommand("railway");
1739
- if (!railway) {
1740
- throw new Error("Railway CLI is unavailable.");
1741
- }
1742
- const result = spawnSync(railway.command, [...railway.argsPrefix, ...args], {
1743
- cwd,
1744
- stdio: input !== void 0 ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
1745
- encoding: "utf8",
1746
- input
1747
- });
1748
- if (result.status !== 0) {
1749
- throw new Error(result.stderr?.trim() || result.stdout?.trim() || `railway ${args.join(" ")} failed`);
1750
- }
1751
- return result;
1752
- }
1753
1802
  function commandAvailable(command) {
1754
1803
  if (command === "gh" || command === "wrangler" || command === "railway" || command === "copilot") {
1755
1804
  return Boolean(resolveTreeseedToolBinary(command));
@@ -2117,6 +2166,50 @@ async function runBounded(items, limit, worker) {
2117
2166
  });
2118
2167
  await Promise.all(workers);
2119
2168
  }
2169
+ function repositorySlugFromPackageJson(root) {
2170
+ try {
2171
+ const packageJson = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
2172
+ const repository = packageJson.repository;
2173
+ const raw = typeof repository === "string" ? repository : repository && typeof repository === "object" && !Array.isArray(repository) ? repository.url : null;
2174
+ if (typeof raw !== "string") return null;
2175
+ const normalized = raw.trim().replace(/^git\+/u, "").replace(/^ssh:\/\/git@github\.com[:/]/u, "").replace(/^git@github\.com:/u, "").replace(/^https:\/\/github\.com\//u, "").replace(/\.git$/u, "").replace(/\/$/u, "");
2176
+ return /^[^/\s]+\/[^/\s]+$/u.test(normalized) ? normalized : null;
2177
+ } catch {
2178
+ return null;
2179
+ }
2180
+ }
2181
+ function resolveGitHubRepositorySlugForPath(root) {
2182
+ try {
2183
+ return maybeResolveGitHubRepositorySlug(root);
2184
+ } catch {
2185
+ return null;
2186
+ }
2187
+ }
2188
+ function discoverGitHubEnvironmentSyncTargets(tenantRoot, explicitRepository) {
2189
+ const targets = /* @__PURE__ */ new Map();
2190
+ const add = (repository, managedHostMode) => {
2191
+ if (!repository || !repository.trim()) return;
2192
+ const normalized = repository.trim();
2193
+ const existing = targets.get(normalized);
2194
+ if (!existing || existing.managedHostMode === "managed" && managedHostMode === "direct") {
2195
+ targets.set(normalized, { repository: normalized, managedHostMode });
2196
+ }
2197
+ };
2198
+ add(explicitRepository ?? resolveGitHubRepositorySlugForPath(tenantRoot), "auto");
2199
+ for (const application of discoverTreeseedApplications(tenantRoot)) {
2200
+ const repository = resolveGitHubRepositorySlugForPath(application.root) ?? repositorySlugFromPackageJson(application.root);
2201
+ const managedHostMode = usesManagedHostOperationRequests(application.config) ? "managed" : "direct";
2202
+ add(repository, managedHostMode);
2203
+ }
2204
+ return [...targets.values()];
2205
+ }
2206
+ function withGitHubEnvironmentCredentialContext(error, repository, credential) {
2207
+ const message = error instanceof Error && error.message.trim() ? error.message.trim() : String(error ?? "GitHub environment sync failed.");
2208
+ if (credential.fallbackUsed && /authentication failed|resource not accessible|403/iu.test(message)) {
2209
+ return new Error(`${message} Configure ${credential.envName} with Actions environment secrets and variables permissions for ${repository}; the fallback GH_TOKEN is not sufficient for this repository.`);
2210
+ }
2211
+ return error;
2212
+ }
2120
2213
  async function syncTreeseedGitHubEnvironment({
2121
2214
  tenantRoot,
2122
2215
  scope = "prod",
@@ -2156,10 +2249,10 @@ async function syncTreeseedGitHubEnvironment({
2156
2249
  }
2157
2250
  return Boolean(entry.targets.includes("github-variable") && allowedVariables?.has(entry.id));
2158
2251
  });
2159
- const ghToken = values.GH_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
2160
- const ghEnv = ghToken ? {
2161
- GH_TOKEN: ghToken,
2162
- GITHUB_TOKEN: ghToken
2252
+ const credential = resolveGitHubCredentialForRepository(repository, { values, env: process.env });
2253
+ const ghEnv = credential.token ? {
2254
+ GH_TOKEN: credential.token,
2255
+ GITHUB_TOKEN: credential.token
2163
2256
  } : {};
2164
2257
  const githubClient = createGitHubApiClient({ env: ghEnv });
2165
2258
  const environment = scope === "prod" ? "production" : scope;
@@ -2168,17 +2261,27 @@ async function syncTreeseedGitHubEnvironment({
2168
2261
  const progress = (message, stream = "stdout") => onProgress?.(message, stream);
2169
2262
  if (!dryRun) {
2170
2263
  progress(`[${scope}][github][environment] Ensuring GitHub environment ${environment} exists...`);
2171
- await ensureGitHubActionsEnvironment(repository, environment, {
2172
- client: githubClient,
2173
- branchName: deploymentBranch,
2174
- tagName: deploymentTag
2175
- });
2264
+ try {
2265
+ await ensureGitHubActionsEnvironment(repository, environment, {
2266
+ client: githubClient,
2267
+ branchName: deploymentBranch,
2268
+ tagName: deploymentTag
2269
+ });
2270
+ } catch (error) {
2271
+ throw withGitHubEnvironmentCredentialContext(error, repository, credential);
2272
+ }
2176
2273
  }
2177
2274
  progress(`[${scope}][github][sync] Loading existing GitHub secrets and variables...`);
2178
- const [secretNames, variableNames] = dryRun ? [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()] : await Promise.all([
2179
- listGitHubEnvironmentSecretNames(repository, environment, { client: githubClient }),
2180
- listGitHubEnvironmentVariableNames(repository, environment, { client: githubClient })
2181
- ]);
2275
+ const [secretNames, variableNames] = dryRun ? [/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()] : await (async () => {
2276
+ try {
2277
+ return await Promise.all([
2278
+ listGitHubEnvironmentSecretNames(repository, environment, { client: githubClient }),
2279
+ listGitHubEnvironmentVariableNames(repository, environment, { client: githubClient })
2280
+ ]);
2281
+ } catch (error) {
2282
+ throw withGitHubEnvironmentCredentialContext(error, repository, credential);
2283
+ }
2284
+ })();
2182
2285
  const synced = {
2183
2286
  secrets: [],
2184
2287
  variables: []
@@ -2201,10 +2304,14 @@ async function syncTreeseedGitHubEnvironment({
2201
2304
  const limit = execution === "sequential" ? 1 : concurrency;
2202
2305
  await runBounded(items, limit, async (item) => {
2203
2306
  if (!dryRun) {
2204
- if (item.kind === "secret") {
2205
- await upsertGitHubEnvironmentSecret(repository, environment, item.name, item.value, { client: githubClient });
2206
- } else {
2207
- await upsertGitHubEnvironmentVariable(repository, environment, item.name, item.value, { client: githubClient });
2307
+ try {
2308
+ if (item.kind === "secret") {
2309
+ await upsertGitHubEnvironmentSecret(repository, environment, item.name, item.value, { client: githubClient });
2310
+ } else {
2311
+ await upsertGitHubEnvironmentVariable(repository, environment, item.name, item.value, { client: githubClient });
2312
+ }
2313
+ } catch (error) {
2314
+ throw withGitHubEnvironmentCredentialContext(error, repository, credential);
2208
2315
  }
2209
2316
  }
2210
2317
  completed += 1;
@@ -2221,6 +2328,13 @@ async function syncTreeseedGitHubEnvironment({
2221
2328
  repository,
2222
2329
  scope,
2223
2330
  environment,
2331
+ credential: {
2332
+ repository: credential.repository,
2333
+ envName: credential.envName,
2334
+ configured: credential.configured,
2335
+ source: credential.source,
2336
+ fallbackUsed: credential.fallbackUsed
2337
+ },
2224
2338
  entryIds: entryFilter ? [...entryFilter] : void 0,
2225
2339
  ...synced
2226
2340
  };
@@ -2274,7 +2388,7 @@ function environmentEntryTargetsService(entry, serviceKey) {
2274
2388
  function railwayEnvironmentEntryIdsForService(registry, values, scope, target, serviceKey, entryFilter = null) {
2275
2389
  return registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes(target) && (!entryFilter || entryFilter.has(entry.id)) && environmentEntryTargetsService(entry, serviceKey)).map((entry) => entry.id).filter((key) => typeof values[key] === "string" && values[key].length > 0);
2276
2390
  }
2277
- function syncTreeseedRailwayEnvironment({
2391
+ async function syncTreeseedRailwayEnvironment({
2278
2392
  tenantRoot,
2279
2393
  scope = "prod",
2280
2394
  dryRun = false,
@@ -2288,10 +2402,10 @@ function syncTreeseedRailwayEnvironment({
2288
2402
  ...nonEmptyEnvironmentValues(valuesOverlay)
2289
2403
  };
2290
2404
  const deployConfig = loadCliDeployConfig(tenantRoot);
2291
- const marketDatabaseService = deployConfig.services?.marketDatabase;
2292
- const marketDatabaseBaseName = typeof marketDatabaseService?.railway?.serviceName === "string" && marketDatabaseService.railway.serviceName.trim() ? marketDatabaseService.railway.serviceName.trim() : `${deployConfig.slug ?? "treeseed-market"}-postgres`;
2293
- const marketDatabaseServiceName = `${marketDatabaseBaseName.replace(/-(staging|prod|production)$/u, "")}-${scope === "prod" ? "prod" : scope}`;
2294
- const marketDatabaseUrl = typeof values.TREESEED_MARKET_DATABASE_URL === "string" && values.TREESEED_MARKET_DATABASE_URL.length > 0 ? values.TREESEED_MARKET_DATABASE_URL : marketDatabaseService?.enabled !== false && marketDatabaseService?.provider === "railway" ? `\${{${marketDatabaseServiceName}.DATABASE_URL}}` : "";
2405
+ const treeseedDatabase = configuredMarketDatabaseService(tenantRoot, deployConfig);
2406
+ const treeseedDatabaseService = treeseedDatabase?.service;
2407
+ const treeseedDatabaseServiceName = treeseedDatabase?.serviceName ?? "";
2408
+ const treeseedDatabaseUrl = typeof values.TREESEED_DATABASE_URL === "string" && values.TREESEED_DATABASE_URL.length > 0 ? values.TREESEED_DATABASE_URL : treeseedDatabaseService?.enabled !== false && treeseedDatabaseService?.provider === "railway" ? `\${{${treeseedDatabaseServiceName}.DATABASE_URL}}` : "";
2295
2409
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
2296
2410
  const entryFilter = Array.isArray(entryIds) && entryIds.length > 0 ? new Set(entryIds) : null;
2297
2411
  const progress = (message, stream = "stdout") => onProgress?.(message, stream);
@@ -2301,12 +2415,12 @@ function syncTreeseedRailwayEnvironment({
2301
2415
  const environmentName = normalizeRailwayEnvironmentName(service.railwayEnvironment ?? scope);
2302
2416
  const serviceValues = {
2303
2417
  ...values,
2304
- ...service.key === "marketOperationsRunner" ? {
2418
+ ...service.key === "operationsRunner" ? {
2305
2419
  TREESEED_PLATFORM_RUNNER_ID: service.runnerId ?? service.serviceName,
2306
2420
  TREESEED_PLATFORM_RUNNER_DATA_DIR: service.volumeMountPath ?? values.TREESEED_PLATFORM_RUNNER_DATA_DIR,
2307
2421
  TREESEED_PLATFORM_RUNNER_ENVIRONMENT: scope === "prod" ? "production" : scope
2308
2422
  } : {},
2309
- ...marketDatabaseUrl && ["api", "marketOperationsRunner"].includes(service.key) ? { TREESEED_MARKET_DATABASE_URL: marketDatabaseUrl } : {}
2423
+ ...treeseedDatabaseUrl && ["api", "operationsRunner"].includes(service.key) ? { TREESEED_DATABASE_URL: treeseedDatabaseUrl } : {}
2310
2424
  };
2311
2425
  const serviceName = service.serviceName ?? fallbackServiceName;
2312
2426
  serviceValuesByName.set(serviceName || service.serviceId || service.instanceKey || service.key, serviceValues);
@@ -2327,17 +2441,36 @@ function syncTreeseedRailwayEnvironment({
2327
2441
  for (const service of services) {
2328
2442
  const serviceValues = serviceValuesByName.get(service.serviceName || service.serviceId || service.instanceKey || service.service) ?? values;
2329
2443
  progress(`[${scope}][railway][${service.service}] Syncing ${service.secrets.length} secrets and ${service.variables.length} variables...`);
2330
- for (const key of service.secrets) {
2331
- runRailway(
2332
- ["variable", "set", "--service", service.serviceName || service.serviceId, "--environment", service.environmentName, "--stdin", "--skip-deploys", key],
2333
- { cwd: service.rootDir, dryRun, input: serviceValues[key] }
2334
- );
2335
- }
2336
- for (const key of service.variables) {
2337
- runRailway(
2338
- ["variable", "set", "--service", service.serviceName || service.serviceId, "--environment", service.environmentName, "--stdin", "--skip-deploys", key],
2339
- { cwd: service.rootDir, dryRun, input: serviceValues[key] }
2444
+ if (!dryRun) {
2445
+ const railwayEnv = { ...process.env, ...values };
2446
+ const project = (await ensureRailwayProject({
2447
+ projectId: "",
2448
+ projectName: service.projectName,
2449
+ env: railwayEnv
2450
+ })).project;
2451
+ const environment = (await ensureRailwayEnvironment({
2452
+ projectId: project.id,
2453
+ environmentName: service.environmentName,
2454
+ env: railwayEnv
2455
+ })).environment;
2456
+ const railwayService = (await ensureRailwayService({
2457
+ projectId: project.id,
2458
+ serviceId: service.serviceId,
2459
+ serviceName: service.serviceName,
2460
+ env: railwayEnv
2461
+ })).service;
2462
+ const variableValues = Object.fromEntries(
2463
+ [...service.secrets, ...service.variables].map((key) => [key, serviceValues[key]]).filter(([, value]) => typeof value === "string" && value.length > 0)
2340
2464
  );
2465
+ if (Object.keys(variableValues).length > 0) {
2466
+ await upsertRailwayVariables({
2467
+ projectId: project.id,
2468
+ environmentId: environment.id,
2469
+ serviceId: railwayService.id,
2470
+ variables: variableValues,
2471
+ env: railwayEnv
2472
+ });
2473
+ }
2341
2474
  }
2342
2475
  progress(`[${scope}][railway][${service.service}] Complete.`);
2343
2476
  }
@@ -2621,7 +2754,7 @@ function buildConfigEntrySnapshot(scope, entry, currentValue, suggestedValue) {
2621
2754
  return true;
2622
2755
  }
2623
2756
  })();
2624
- const allowGeneratedSecretDefault = entry.id === "TREESEED_PLATFORM_RUNNER_SECRET";
2757
+ const allowGeneratedSecretDefault = ["TREESEED_PLATFORM_RUNNER_SECRET", "TREESEED_WEB_SERVICE_SECRET", "TREESEED_API_WEB_SERVICE_SECRET", "TREESEED_CREDENTIAL_SESSION_SECRET"].includes(entry.id);
2625
2758
  const allowSuggestedDefault = allowGeneratedSecretDefault || !(entry.sensitivity === "secret" && entry.requirement !== "optional");
2626
2759
  const effectiveValue = currentValueValid ? currentValue || (allowSuggestedDefault ? suggestedValue : "") || "" : (allowSuggestedDefault ? suggestedValue : "") || currentValue || "";
2627
2760
  return {
@@ -2820,7 +2953,7 @@ async function finalizeTreeseedConfig({
2820
2953
  );
2821
2954
  for (const scope of scopes) {
2822
2955
  const selection = resolveTreeseedBootstrapSelection({
2823
- deployConfig: registry.context.deployConfig,
2956
+ deployConfig: workspaceBootstrapDeployConfig(tenantRoot, registry.context.deployConfig),
2824
2957
  env: scopeSeedValues[scope],
2825
2958
  systems: scope === "local" ? ["github"] : systems,
2826
2959
  skipUnavailable: scope === "local" ? true : skipUnavailable
@@ -2968,7 +3101,7 @@ async function finalizeTreeseedConfig({
2968
3101
  result: {}
2969
3102
  });
2970
3103
  }
2971
- const deploySystems = selection.runnable.filter((system) => system === "data" || system === "web");
3104
+ const deploySystems = selection.runnable.filter((system) => system === "data" || system === "web" || system === "api" || system === "agents");
2972
3105
  if (deploySystems.length > 0) {
2973
3106
  progress(`[${scope}][bootstrap][deploy] Deploying ${deploySystems.join(", ")}...`);
2974
3107
  applyTreeseedEnvironmentToProcess({ tenantRoot, scope, override: true });
@@ -3026,27 +3159,37 @@ async function finalizeTreeseedConfig({
3026
3159
  }
3027
3160
  if (sync === "github" || sync === "all") {
3028
3161
  const githubScopes = scopes.filter((scope) => scope !== "local" && summary.bootstrapSystemsByScope[scope].runnable.includes("github"));
3029
- const syncScope = async (scope) => {
3030
- progress(`[${scope}][github][sync] Syncing GitHub environment...`);
3162
+ const githubTargets = discoverGitHubEnvironmentSyncTargets(tenantRoot, githubRepository);
3163
+ if (githubTargets.length === 0 && githubScopes.length > 0) {
3164
+ throw new Error("Unable to determine the GitHub repository from the origin remote.");
3165
+ }
3166
+ const syncScope = async (scope, target) => {
3167
+ progress(`[${scope}][github][sync] Syncing GitHub environment for ${target.repository}...`);
3031
3168
  return await syncTreeseedGitHubEnvironment({
3032
3169
  tenantRoot,
3033
3170
  scope,
3034
- repository: githubRepository,
3171
+ repository: target.repository,
3172
+ managedHostMode: target.managedHostMode,
3035
3173
  execution: bootstrapExecution,
3036
3174
  onProgress: progress
3037
3175
  });
3038
3176
  };
3039
3177
  const githubResults = [];
3040
3178
  if (bootstrapExecution === "sequential") {
3041
- for (const scope of githubScopes) {
3042
- githubResults.push(await syncScope(scope));
3179
+ for (const target of githubTargets) {
3180
+ for (const scope of githubScopes) {
3181
+ githubResults.push(await syncScope(scope, target));
3182
+ }
3043
3183
  }
3044
3184
  } else {
3045
- githubResults.push(...await Promise.all(githubScopes.map((scope) => syncScope(scope))));
3185
+ githubResults.push(...await Promise.all(githubTargets.flatMap(
3186
+ (target) => githubScopes.map((scope) => syncScope(scope, target))
3187
+ )));
3046
3188
  }
3047
3189
  summary.synced.github = {
3048
3190
  scopes: githubResults,
3049
3191
  repository: githubResults[0]?.repository ?? githubRepository ?? maybeResolveGitHubRepositorySlug(tenantRoot),
3192
+ repositories: githubResults.map((entry) => entry.repository).filter((repository, index, all) => all.indexOf(repository) === index),
3050
3193
  secrets: githubResults.flatMap((entry) => entry.secrets),
3051
3194
  variables: githubResults.flatMap((entry) => entry.variables)
3052
3195
  };
@@ -27,7 +27,7 @@ export declare function buildPublicVars(deployConfig: any, options?: {}): {
27
27
  TREESEED_RUNTIME_MODE: any;
28
28
  TREESEED_RUNTIME_REGISTRATION: any;
29
29
  TREESEED_CENTRAL_MARKET_API_BASE_URL: string;
30
- TREESEED_MARKET_API_BASE_URL: any;
30
+ TREESEED_API_BASE_URL: any;
31
31
  TREESEED_CATALOG_MARKET_API_BASE_URLS: any;
32
32
  TREESEED_HOSTING_TEAM_ID: string;
33
33
  TREESEED_PROJECT_ID: string;
@@ -129,6 +129,7 @@ export declare function ensureGeneratedWranglerConfig(tenantRoot: any, options?:
129
129
  previewProjectName: string | undefined;
130
130
  productionBranch: string;
131
131
  stagingBranch: string;
132
+ buildCommand: string | undefined;
132
133
  buildOutputDir: string | undefined;
133
134
  } | undefined;
134
135
  r2: {
@@ -308,6 +309,9 @@ export declare function listQueues(tenantRoot: any, env: any): any;
308
309
  export declare function listR2Buckets(tenantRoot: any, env: any): any;
309
310
  export declare function listPagesProjects(tenantRoot: any, env: any): any;
310
311
  export declare function listTurnstileWidgets(tenantRoot: any, env: any): any;
312
+ export declare function listWorkers(tenantRoot: any, env: any): any;
313
+ export declare function listDnsZones(env: any): any;
314
+ export declare function listDnsRecords(zoneId: any, env: any): any[];
311
315
  export declare function getTurnstileWidget(env: any, sitekey: any): any;
312
316
  export declare function createTurnstileWidget(env: any, input: any): any;
313
317
  export declare function updateTurnstileWidget(env: any, sitekey: any, input: any): any;
@@ -531,6 +535,7 @@ export declare function validateDeployPrerequisites(tenantRoot: any, { requireRe
531
535
  previewProjectName: string | undefined;
532
536
  productionBranch: string;
533
537
  stagingBranch: string;
538
+ buildCommand: string | undefined;
534
539
  buildOutputDir: string | undefined;
535
540
  } | undefined;
536
541
  r2: {
@@ -724,6 +729,7 @@ export declare function validateDestroyPrerequisites(tenantRoot: any, { requireR
724
729
  previewProjectName: string | undefined;
725
730
  productionBranch: string;
726
731
  stagingBranch: string;
732
+ buildCommand: string | undefined;
727
733
  buildOutputDir: string | undefined;
728
734
  } | undefined;
729
735
  r2: {
@@ -881,6 +887,26 @@ export declare function validateDestroyPrerequisites(tenantRoot: any, { requireR
881
887
  };
882
888
  };
883
889
  export declare function shouldDeleteRailwayProjectAfterEnvironmentDestroy(project: any, scope: any, deleteData: any, deletedEnvironmentId?: null): boolean;
890
+ export declare function setDestroyDockerRunnerForTests(runner: any): void;
891
+ export declare function dockerLocalRuntimeResourceOperations({ dryRun }?: {
892
+ dryRun?: boolean | undefined;
893
+ }): any[];
894
+ export declare function cloudflareDestroyVerification(tenantRoot: any, deployConfig: any, state: any, env: any): {
895
+ provider: string;
896
+ method: string;
897
+ status: string;
898
+ remaining: {
899
+ pages: any;
900
+ workers: any;
901
+ kvNamespaces: any;
902
+ queues: any;
903
+ d1Databases: any;
904
+ r2Buckets: any;
905
+ turnstileWidgets: any;
906
+ dnsRecords: any;
907
+ };
908
+ totalRemaining: any;
909
+ };
884
910
  export declare function destroyTreeseedEnvironmentResources(tenantRoot: any, options?: {}): Promise<{
885
911
  target: {
886
912
  kind: string;
@@ -890,6 +916,7 @@ export declare function destroyTreeseedEnvironmentResources(tenantRoot: any, opt
890
916
  branchName: string;
891
917
  };
892
918
  deleteData: boolean;
919
+ sweepTreeseed: boolean;
893
920
  summary: {
894
921
  target: any;
895
922
  identity: any;
@@ -947,13 +974,49 @@ export declare function destroyTreeseedEnvironmentResources(tenantRoot: any, opt
947
974
  name: any;
948
975
  status: any;
949
976
  }[];
950
- local: {
951
- provider: any;
952
- type: any;
953
- name: any;
954
- status: any;
955
- }[];
977
+ local: any[];
956
978
  };
979
+ verification: {
980
+ localDocker?: {
981
+ provider: string;
982
+ method: string;
983
+ status: string;
984
+ reason: string;
985
+ remaining: {
986
+ containers: number;
987
+ volumes: number;
988
+ networks: number;
989
+ };
990
+ totalRemaining: number;
991
+ } | {
992
+ provider: string;
993
+ method: string;
994
+ status: string;
995
+ remaining: {
996
+ containers: any;
997
+ volumes: any;
998
+ networks: any;
999
+ };
1000
+ totalRemaining: any;
1001
+ reason?: undefined;
1002
+ } | undefined;
1003
+ cloudflare: {
1004
+ provider: string;
1005
+ method: string;
1006
+ status: string;
1007
+ remaining: {
1008
+ pages: any;
1009
+ workers: any;
1010
+ kvNamespaces: any;
1011
+ queues: any;
1012
+ d1Databases: any;
1013
+ r2Buckets: any;
1014
+ turnstileWidgets: any;
1015
+ dnsRecords: any;
1016
+ };
1017
+ totalRemaining: any;
1018
+ };
1019
+ } | null;
957
1020
  }>;
958
1021
  export declare function destroyCloudflareResources(tenantRoot: any, options?: {}): {
959
1022
  target: {