@treeseed/sdk 0.10.22 → 0.10.24

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 (47) hide show
  1. package/dist/db/market-schema.js +3 -2
  2. package/dist/market-client.d.ts +4 -0
  3. package/dist/market-client.js +6 -0
  4. package/dist/operations/providers/default.js +26 -4
  5. package/dist/operations/repository-operations.js +6 -2
  6. package/dist/operations/services/bootstrap-runner.d.ts +5 -1
  7. package/dist/operations/services/bootstrap-runner.js +34 -5
  8. package/dist/operations/services/config-runtime.d.ts +2 -1
  9. package/dist/operations/services/deploy.d.ts +18 -1
  10. package/dist/operations/services/deploy.js +176 -24
  11. package/dist/operations/services/github-automation.d.ts +10 -1
  12. package/dist/operations/services/github-automation.js +18 -4
  13. package/dist/operations/services/hosting-audit.d.ts +2 -1
  14. package/dist/operations/services/hosting-audit.js +12 -1
  15. package/dist/operations/services/hub-launch.d.ts +1 -0
  16. package/dist/operations/services/hub-launch.js +1 -0
  17. package/dist/operations/services/hub-provider-launch.d.ts +9 -0
  18. package/dist/operations/services/hub-provider-launch.js +140 -40
  19. package/dist/operations/services/managed-host-security.d.ts +1 -1
  20. package/dist/operations/services/managed-host-security.js +4 -1
  21. package/dist/operations/services/project-platform.d.ts +25 -0
  22. package/dist/operations/services/project-platform.js +91 -23
  23. package/dist/operations/services/railway-api.js +2 -1
  24. package/dist/operations/services/railway-deploy.d.ts +32 -2
  25. package/dist/operations/services/railway-deploy.js +94 -27
  26. package/dist/operations/services/template-registry.js +33 -3
  27. package/dist/platform/contracts.d.ts +1 -0
  28. package/dist/platform/deploy-config.js +8 -1
  29. package/dist/platform/deploy-runtime.js +1 -0
  30. package/dist/platform/environment.d.ts +1 -1
  31. package/dist/platform/environment.js +1 -1
  32. package/dist/reconcile/builtin-adapters.js +155 -25
  33. package/dist/reconcile/contracts.d.ts +1 -1
  34. package/dist/reconcile/desired-state.js +17 -1
  35. package/dist/reconcile/engine.d.ts +2 -0
  36. package/dist/reconcile/engine.js +58 -3
  37. package/dist/reconcile/units.js +1 -0
  38. package/dist/sdk-types.d.ts +1 -1
  39. package/dist/sdk-types.js +2 -0
  40. package/dist/timing.d.ts +20 -0
  41. package/dist/timing.js +73 -0
  42. package/dist/treeseed/template-catalog/catalog.fixture.json +150 -0
  43. package/dist/workflow/operations.d.ts +2 -0
  44. package/drizzle/market/0000_market_control_plane.sql +3 -3
  45. package/drizzle/market/0003_project_team_slug_unique.sql +4 -0
  46. package/package.json +1 -1
  47. package/templates/github/deploy-web.workflow.yml +4 -0
@@ -10,6 +10,7 @@ import {
10
10
  createGitHubRepository,
11
11
  ensureGitHubDeployAutomation,
12
12
  initializeGitHubRepositoryWorkingTree,
13
+ resolveGitHubRemoteUrls,
13
14
  resolveDefaultGitHubOwner
14
15
  } from "./github-automation.js";
15
16
  import { configuredRailwayServices, deployRailwayService, ensureRailwayScheduledJobs, validateRailwayDeployPrerequisites, verifyRailwayScheduledJobs } from "./railway-deploy.js";
@@ -41,6 +42,10 @@ function envOrNull(name) {
41
42
  function normalizeBaseUrl(value) {
42
43
  return String(value ?? "").trim().replace(/\/+$/u, "");
43
44
  }
45
+ function domainUrl(domain) {
46
+ const value = String(domain ?? "").trim().replace(/^https?:\/\//u, "").replace(/\/+$/u, "");
47
+ return value ? `https://${value}` : null;
48
+ }
44
49
  function resolveManagedWebUrl(slug) {
45
50
  const baseDomain = envOrNull("TREESEED_MANAGED_WEB_BASE_DOMAIN");
46
51
  if (baseDomain) {
@@ -65,10 +70,25 @@ function runGit(cwd, args, capture = true) {
65
70
  encoding: "utf8"
66
71
  });
67
72
  if (result.status !== 0) {
68
- throw new Error(result.stderr?.trim() || result.stdout?.trim() || `git ${args.join(" ")} failed`);
73
+ if (args[0] === "push" && !args.includes("--force")) {
74
+ const retryArgs = ["push", "--force", ...args.slice(1)];
75
+ const retry = spawnSync("git", retryArgs, {
76
+ cwd,
77
+ stdio: capture ? "pipe" : "inherit",
78
+ encoding: "utf8"
79
+ });
80
+ if (retry.status === 0) return retry;
81
+ const retryDetail = retry.stderr?.trim() || retry.stdout?.trim();
82
+ throw new Error(`git ${retryArgs.join(" ")} failed${retryDetail ? `: ${retryDetail}` : ""}`);
83
+ }
84
+ const detail = result.stderr?.trim() || result.stdout?.trim();
85
+ throw new Error(`git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
69
86
  }
70
87
  return result;
71
88
  }
89
+ function gitOutput(cwd, args) {
90
+ return runGit(cwd, args, true).stdout?.trim() ?? "";
91
+ }
72
92
  function writeText(path, body) {
73
93
  ensureDir(dirname(path));
74
94
  writeFileSync(path, body, "utf8");
@@ -81,6 +101,20 @@ function updateYamlFile(path, updater) {
81
101
  function currentTemplateCatalogUrl() {
82
102
  return `file:${resolve(templateCatalogRoot, "catalog.fixture.json")}`;
83
103
  }
104
+ function frontmatter(fields) {
105
+ return `---
106
+ ${stringifyYaml(fields).trim()}
107
+ ---`;
108
+ }
109
+ function normalizeMarkdownBody(value, fallback) {
110
+ const markdown = String(value ?? "").trim();
111
+ return markdown || fallback;
112
+ }
113
+ function markdownToSummary(markdown, fallback) {
114
+ const text = markdown.replace(/^---[\s\S]*?---/u, " ").replace(/```[\s\S]*?```/gu, " ").replace(/`([^`]+)`/gu, "$1").replace(/!\[[^\]]*\]\([^)]+\)/gu, " ").replace(/\[([^\]]+)\]\([^)]+\)/gu, "$1").replace(/^#{1,6}\s+/gmu, "").replace(/^\s*[-*+]\s+/gmu, "").replace(/^\s*\d+\.\s+/gmu, "").replace(/[*_~>#]/gu, "").replace(/\s+/gu, " ").trim();
115
+ if (!text) return fallback;
116
+ return text.length > 240 ? `${text.slice(0, 237).trimEnd()}...` : text;
117
+ }
84
118
  function seedLaunchContent(projectRoot, input) {
85
119
  const objectiveId = `objective:launch-${slugify(input.projectSlug, "hub")}`;
86
120
  const questionId = `question:operating-${slugify(input.projectSlug, "hub")}`;
@@ -88,6 +122,13 @@ function seedLaunchContent(projectRoot, input) {
88
122
  const decisionId = `decision:launch-${slugify(input.projectSlug, "hub")}`;
89
123
  const stewardSlug = "launch-steward";
90
124
  const noteSlug = `${slugify(input.projectSlug, "hub")}-operating-model`;
125
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
126
+ const defaultCoreObjective = `# Core Objective
127
+
128
+ Build and maintain ${input.projectName} as a living TreeSeed project with clear direction, active work, reliable releases, and useful AI agent context.
129
+ `;
130
+ const coreObjective = normalizeMarkdownBody(input.coreObjective ?? input.summary, defaultCoreObjective);
131
+ const coreObjectiveSummary = markdownToSummary(coreObjective, `Define the enduring objective for ${input.projectName}.`);
91
132
  writeText(resolve(projectRoot, "src/content/people", `${stewardSlug}.mdx`), `---
92
133
  name: Launch Steward
93
134
  role: Team steward
@@ -99,17 +140,34 @@ tags:
99
140
  ---
100
141
 
101
142
  The launch steward keeps the first operating cycle legible while the hub moves from setup into active use.
143
+ `);
144
+ writeText(resolve(projectRoot, "src/content/objectives", "core.md"), `${frontmatter({
145
+ id: "objective:core",
146
+ title: `${input.projectName} Core Objective`,
147
+ description: coreObjectiveSummary,
148
+ date: today,
149
+ summary: coreObjectiveSummary,
150
+ status: "live",
151
+ timeHorizon: "strategic",
152
+ motivation: "The core objective anchors TreeSeed agent context and keeps project work aligned.",
153
+ primaryContributor: stewardSlug,
154
+ canonical: true
155
+ })}
156
+
157
+ ${coreObjective}
102
158
  `);
103
159
  writeText(resolve(projectRoot, "src/content/objectives", "launch-knowledge-hub.mdx"), `---
104
160
  id: ${objectiveId}
105
161
  title: Launch ${input.projectName}
106
162
  description: Bring the initial knowledge hub online with live managed infrastructure and a clear operating direction.
107
- date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
163
+ date: ${today}
108
164
  summary: Stand up the hub, connect the runtime, and make the first workstream visible to the team.
109
165
  status: live
110
166
  timeHorizon: near-term
111
167
  motivation: TreeSeed launches should create immediately usable hubs instead of leaving teams in setup limbo.
112
168
  primaryContributor: ${stewardSlug}
169
+ relatedObjectives:
170
+ - core
113
171
  ---
114
172
 
115
173
  Launch ${input.projectName} as a living knowledge hub with real GitHub, Cloudflare, and Railway infrastructure.
@@ -118,7 +176,7 @@ Launch ${input.projectName} as a living knowledge hub with real GitHub, Cloudfla
118
176
  id: ${questionId}
119
177
  title: What Should The First Release Cover?
120
178
  description: Scope the first release around the foundation of the hub and the initial operating routines.
121
- date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
179
+ date: ${today}
122
180
  summary: Define the first release around setup completion, clear direction, and baseline operating visibility.
123
181
  status: live
124
182
  questionType: strategy
@@ -133,7 +191,7 @@ The first release should verify that the hub is live, the core direction is visi
133
191
  writeText(resolve(projectRoot, "src/content/notes", `${noteSlug}.mdx`), `---
134
192
  title: ${input.projectName} Operating Model
135
193
  description: The initial working agreements for this Knowledge Hub.
136
- date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
194
+ date: ${today}
137
195
  summary: Managed launch created the default branches, runtime wiring, and first operational checkpoints.
138
196
  status: live
139
197
  ---
@@ -144,7 +202,7 @@ This hub starts with a Knowledge Hub launch, a seeded objective, and a visible f
144
202
  id: ${proposalId}
145
203
  title: Establish The Initial Operating Routine
146
204
  description: Turn the seeded objective and question into a concrete launch proposal for the first team cycle.
147
- date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
205
+ date: ${today}
148
206
  summary: Make the launch posture explicit so the team can move from setup into a concrete operating loop.
149
207
  status: live
150
208
  proposalType: strategy
@@ -165,7 +223,7 @@ Adopt a simple first operating routine: keep direction visible, keep the first r
165
223
  id: ${decisionId}
166
224
  title: Adopt The Initial Launch Posture
167
225
  description: Record the launch decision for the first operating cycle of the hub.
168
- date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
226
+ date: ${today}
169
227
  summary: The Knowledge Hub launch will begin with a narrow first release and explicit direction artifacts.
170
228
  status: live
171
229
  decisionType: approved
@@ -237,13 +295,17 @@ console.log(\`Treeseed project API listening on \${server.url}\`);
237
295
  function applyManagedProjectDefaults(projectRoot, input) {
238
296
  const slug = slugify(input.projectSlug, "project");
239
297
  const marketBaseUrl = normalizeBaseUrl(input.marketBaseUrl ?? envOrNull("TREESEED_MARKET_API_BASE_URL") ?? "https://knowledge.coop");
240
- const siteUrl = resolveManagedWebUrl(slug);
298
+ const productionDomain = String(input.domains?.productionDomain ?? "").trim() || null;
299
+ const stagingDomain = String(input.domains?.stagingDomain ?? "").trim() || null;
300
+ const productionSiteUrl = domainUrl(productionDomain) ?? resolveManagedWebUrl(slug);
301
+ const stagingSiteUrl = domainUrl(stagingDomain);
302
+ const siteUrl = productionSiteUrl;
241
303
  const projectApiBaseUrl = normalizeBaseUrl(input.projectApiBaseUrl ?? resolveManagedApiUrl(slug));
242
304
  const cloudflareAccountId = envOrNull("CLOUDFLARE_ACCOUNT_ID") ?? "replace-with-cloudflare-account-id";
243
- const runtimeMode = input.hostingMode === "managed" ? "treeseed_managed" : input.hostingMode === "hybrid" ? "byo_attached" : "none";
244
- const runtimeRegistration = input.hostingMode === "managed" ? "required" : input.hostingMode === "hybrid" ? "optional" : "none";
305
+ const runtimeMode = input.hostingMode === "hybrid" ? "byo_attached" : "none";
306
+ const runtimeRegistration = input.hostingMode === "hybrid" ? "optional" : "none";
245
307
  const hubMode = input.hostingMode === "self_hosted" ? "customer_hosted" : "treeseed_hosted";
246
- const managedRuntime = runtimeMode === "treeseed_managed";
308
+ const managedRuntime = false;
247
309
  updateYamlFile(resolve(projectRoot, "treeseed.site.yaml"), (config) => ({
248
310
  ...config,
249
311
  name: input.projectName,
@@ -270,6 +332,7 @@ function applyManagedProjectDefaults(projectRoot, input) {
270
332
  cloudflare: {
271
333
  ...config.cloudflare ?? {},
272
334
  accountId: cloudflareAccountId,
335
+ ...input.domains?.zoneId ? { zoneId: input.domains.zoneId } : {},
273
336
  workerName: slug,
274
337
  queueName: `${slug}-agent-work`,
275
338
  dlqName: `${slug}-agent-work-dlq`,
@@ -297,35 +360,34 @@ function applyManagedProjectDefaults(projectRoot, input) {
297
360
  rootDir: ".",
298
361
  publicBaseUrl: siteUrl,
299
362
  localBaseUrl: "http://127.0.0.1:4321",
300
- ...config.surfaces?.web ?? {}
363
+ ...config.surfaces?.web ?? {},
364
+ environments: {
365
+ ...config.surfaces?.web?.environments ?? {},
366
+ ...stagingDomain ? { staging: { ...config.surfaces?.web?.environments?.staging ?? {}, domain: stagingDomain, baseUrl: stagingSiteUrl } } : {},
367
+ ...productionDomain ? { prod: { ...config.surfaces?.web?.environments?.prod ?? {}, domain: productionDomain, baseUrl: productionSiteUrl } } : {}
368
+ }
301
369
  },
302
370
  api: {
303
371
  enabled: managedRuntime,
304
- provider: managedRuntime ? "railway" : "none",
372
+ provider: "none",
305
373
  rootDir: ".",
306
374
  localBaseUrl: "http://127.0.0.1:3000",
307
375
  ...config.surfaces?.api ?? {}
308
376
  }
309
377
  },
310
378
  services: {
379
+ ...config.services ?? {},
311
380
  api: {
312
381
  enabled: managedRuntime,
313
- provider: managedRuntime ? "railway" : "none",
382
+ provider: "none",
314
383
  rootDir: ".",
315
384
  publicBaseUrl: projectApiBaseUrl,
316
- railway: {
317
- serviceName: `${slug}-api`,
318
- buildCommand: "npm run build:api",
319
- startCommand: "node ./src/api/server.js",
320
- healthcheckTimeoutSeconds: 120
321
- },
322
385
  environments: {
323
386
  local: {
324
387
  baseUrl: "http://127.0.0.1:3000"
325
388
  }
326
389
  }
327
- },
328
- ...config.services ?? {}
390
+ }
329
391
  },
330
392
  plugins: [{ package: "@treeseed/core/plugin-default" }],
331
393
  providers: {
@@ -419,7 +481,19 @@ function createDefaultWorkstream(projectId, input, seed) {
419
481
  }
420
482
  function pushDefaultWorkstreamBranch(projectRoot) {
421
483
  runGit(projectRoot, ["checkout", "-B", "task/initial-launch"], false);
422
- runGit(projectRoot, ["push", "-u", "origin", "task/initial-launch"], false);
484
+ runGit(projectRoot, ["push", "--force", "-u", "origin", "task/initial-launch"], false);
485
+ runGit(projectRoot, ["checkout", "main"], false);
486
+ }
487
+ function commitAndPushLaunchRepository(projectRoot, message, { forcePush = false } = {}) {
488
+ runGit(projectRoot, ["checkout", "main"], false);
489
+ runGit(projectRoot, ["add", "-A"], false);
490
+ if (gitOutput(projectRoot, ["status", "--porcelain"])) {
491
+ runGit(projectRoot, ["commit", "-m", message], false);
492
+ }
493
+ runGit(projectRoot, ["push", ...forcePush ? ["--force"] : [], "-u", "origin", "main"], false);
494
+ runGit(projectRoot, ["checkout", "staging"], false);
495
+ runGit(projectRoot, ["merge", "--ff-only", "main"], false);
496
+ runGit(projectRoot, ["push", ...forcePush ? ["--force"] : [], "-u", "origin", "staging"], false);
423
497
  runGit(projectRoot, ["checkout", "main"], false);
424
498
  }
425
499
  function loadProjectMetadata(projectId, input, seed, workstream, siteUrl, projectApiBaseUrl, repository) {
@@ -567,7 +641,7 @@ function scaffoldLaunchSource(projectRoot, input) {
567
641
  });
568
642
  }
569
643
  function repositoryHostGitHubEnvOverlay() {
570
- const token = process.env.TREESEED_HOSTED_HUBS_GITHUB_TOKEN || "";
644
+ const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || process.env.TREESEED_HOSTED_HUBS_GITHUB_TOKEN || "";
571
645
  return token ? { ...process.env, GH_TOKEN: token, GITHUB_TOKEN: token } : process.env;
572
646
  }
573
647
  function prepareKnowledgeHubContentRepositoryRoot(sourceRoot, contentRoot, input) {
@@ -597,6 +671,8 @@ Content source for the ${input.projectName} TreeSeed Knowledge Hub.
597
671
  }
598
672
  function stripSoftwareContentOverlay(sourceRoot, input) {
599
673
  const contentRoot = resolve(sourceRoot, "src", "content");
674
+ const coreObjectiveSource = resolve(contentRoot, "objectives", "core.md");
675
+ const coreObjective = existsSync(coreObjectiveSource) ? readFileSync(coreObjectiveSource, "utf8") : null;
600
676
  rmSync(contentRoot, { recursive: true, force: true });
601
677
  mkdirSync(contentRoot, { recursive: true });
602
678
  writeFileSync(resolve(contentRoot, ".gitkeep"), "", "utf8");
@@ -611,6 +687,10 @@ Content source: ${input.contentRepository?.name ?? `${slugify(input.projectSlug,
611
687
  `,
612
688
  "utf8"
613
689
  );
690
+ if (coreObjective) {
691
+ mkdirSync(resolve(contentRoot, "objectives"), { recursive: true });
692
+ writeFileSync(resolve(contentRoot, "objectives", "core.md"), coreObjective, "utf8");
693
+ }
614
694
  }
615
695
  async function validateKnowledgeHubProviderLaunchPrerequisites(tenantRoot = process.cwd(), { valuesOverlay = {} } = {}) {
616
696
  const values = collectTreeseedConfigSeedValues(tenantRoot, "prod", process.env, valuesOverlay);
@@ -669,11 +749,13 @@ async function executeKnowledgeHubProviderLaunch(input, options = {}) {
669
749
  try {
670
750
  await appendPhase(phases, "repo_provision", "running", "Creating or connecting GitHub software repository.", reportPhase);
671
751
  const repository = input.existingRepository?.url ? {
752
+ ...resolveGitHubRemoteUrls(input.existingRepository.owner, input.existingRepository.name),
672
753
  slug: `${input.existingRepository.owner}/${input.existingRepository.name}`,
673
754
  owner: input.existingRepository.owner,
674
755
  name: input.existingRepository.name,
675
756
  url: input.existingRepository.url,
676
- visibility: input.existingRepository.visibility ?? input.repoVisibility ?? "private"
757
+ visibility: input.existingRepository.visibility ?? input.repoVisibility ?? "private",
758
+ defaultBranch: input.existingRepository.defaultBranch ?? "main"
677
759
  } : await createGitHubRepository({
678
760
  owner: repoOwner,
679
761
  name: repoName,
@@ -698,11 +780,13 @@ async function executeKnowledgeHubProviderLaunch(input, options = {}) {
698
780
  contentRepositoryWorkingRoot = mkdtempSync(join(tmpdir(), `market-content-${slugify(input.projectSlug, "project")}-`));
699
781
  prepareKnowledgeHubContentRepositoryRoot(workingRoot, contentRepositoryWorkingRoot, input);
700
782
  const createdContentRepository = input.contentRepository.url ? {
783
+ ...resolveGitHubRemoteUrls(input.contentRepository.owner ?? repoOwner, input.contentRepository.name),
701
784
  slug: `${slugify(input.contentRepository.owner ?? repoOwner, "treeseed-ai")}/${slugify(input.contentRepository.name, `${repoName}-content`)}`,
702
785
  owner: slugify(input.contentRepository.owner ?? repoOwner, "treeseed-ai"),
703
786
  name: slugify(input.contentRepository.name, `${repoName}-content`),
704
787
  url: input.contentRepository.url,
705
- visibility: input.contentRepository.visibility ?? input.repoVisibility ?? "private"
788
+ visibility: input.contentRepository.visibility ?? input.repoVisibility ?? "private",
789
+ defaultBranch: input.contentRepository.defaultBranch ?? "main"
706
790
  } : await createGitHubRepository({
707
791
  owner: slugify(input.contentRepository.owner ?? repoOwner, "treeseed-ai"),
708
792
  name: slugify(input.contentRepository.name, `${repoName}-content`),
@@ -714,7 +798,8 @@ async function executeKnowledgeHubProviderLaunch(input, options = {}) {
714
798
  const contentInitResult = initializeGitHubRepositoryWorkingTree(contentRepositoryWorkingRoot, createdContentRepository, {
715
799
  defaultBranch: input.contentRepository.defaultBranch ?? "main",
716
800
  createStaging: true,
717
- commitMessage: `Initialize ${input.projectName} content`
801
+ commitMessage: `Initialize ${input.projectName} content`,
802
+ forcePush: !input.contentRepository.url
718
803
  });
719
804
  contentRepository = {
720
805
  slug: createdContentRepository.slug,
@@ -732,22 +817,14 @@ async function executeKnowledgeHubProviderLaunch(input, options = {}) {
732
817
  const initResult = initializeGitHubRepositoryWorkingTree(workingRoot, repository, {
733
818
  defaultBranch: "main",
734
819
  createStaging: true,
735
- commitMessage: `Initialize ${input.projectName}`
820
+ commitMessage: `Initialize ${input.projectName}`,
821
+ push: false
736
822
  });
737
- pushDefaultWorkstreamBranch(workingRoot);
738
823
  const workflows = await ensureGitHubDeployAutomation(workingRoot, { valuesOverlay: prodEnvOverlay });
739
- const githubEnvironmentSync = [];
740
- for (const [scope, valuesOverlay] of [["staging", stagingEnvOverlay], ["prod", prodEnvOverlay]]) {
741
- githubEnvironmentSync.push(await syncTreeseedGitHubEnvironment({
742
- tenantRoot: workingRoot,
743
- scope,
744
- repository: repository.slug,
745
- valuesOverlay,
746
- execution: "sequential"
747
- }));
748
- }
749
- const workflowSummary = { ...workflows, environmentSync: githubEnvironmentSync };
750
- await appendPhase(phases, "workflow_bootstrap", "completed", "Configured GitHub workflows, secrets, and variables.", reportPhase);
824
+ commitAndPushLaunchRepository(workingRoot, `Configure ${input.projectName} deployment`, { forcePush: !input.existingRepository?.url });
825
+ pushDefaultWorkstreamBranch(workingRoot);
826
+ let workflowSummary = { ...workflows, environmentSync: [] };
827
+ await appendPhase(phases, "workflow_bootstrap", "completed", "Configured GitHub workflows.", reportPhase);
751
828
  await appendPhase(phases, "hosting_registration", "running", "Provisioning Cloudflare resources and deploy state.", reportPhase);
752
829
  const staging = await reconcileTreeseedTarget({
753
830
  tenantRoot: workingRoot,
@@ -759,6 +836,29 @@ async function executeKnowledgeHubProviderLaunch(input, options = {}) {
759
836
  target: createPersistentDeployTarget("prod"),
760
837
  env: { ...process.env, ...prodEnvOverlay }
761
838
  });
839
+ const turnstileOverlay = (state, base) => {
840
+ const widget = state?.turnstileWidgets?.formGuard ?? {};
841
+ return {
842
+ ...base,
843
+ ...typeof widget.sitekey === "string" && widget.sitekey.length > 0 ? { TREESEED_PUBLIC_TURNSTILE_SITE_KEY: widget.sitekey } : {},
844
+ ...typeof widget.secret === "string" && widget.secret.length > 0 ? { TREESEED_TURNSTILE_SECRET_KEY: widget.secret } : {}
845
+ };
846
+ };
847
+ const githubEnvironmentSync = [];
848
+ for (const [scope, valuesOverlay] of [
849
+ ["staging", turnstileOverlay(staging.state, stagingEnvOverlay)],
850
+ ["prod", turnstileOverlay(prod.state, prodEnvOverlay)]
851
+ ]) {
852
+ githubEnvironmentSync.push(await syncTreeseedGitHubEnvironment({
853
+ tenantRoot: workingRoot,
854
+ scope,
855
+ repository: repository.slug,
856
+ valuesOverlay,
857
+ execution: "sequential"
858
+ }));
859
+ }
860
+ workflowSummary = { ...workflowSummary, environmentSync: githubEnvironmentSync };
861
+ runRemoteD1Migrations(workingRoot, { scope: "staging" });
762
862
  runRemoteD1Migrations(workingRoot, { scope: "prod" });
763
863
  const verification = await collectTreeseedReconcileStatus({
764
864
  tenantRoot: workingRoot,
@@ -7,7 +7,7 @@ export declare function filterManagedHostGitHubEnvironment(required: {
7
7
  secrets: string[];
8
8
  variables: string[];
9
9
  }): {
10
- secrets: never[];
10
+ secrets: string[];
11
11
  variables: string[];
12
12
  };
13
13
  export declare function shouldExposeManagedHostRuntimeSecret(deployConfig: Pick<TreeseedDeployConfig, 'hosting' | 'hub' | 'runtime'> | null | undefined, _secretName: string, env?: Record<string, string | undefined>): boolean;
@@ -8,6 +8,9 @@ const SAFE_MANAGED_HOST_CI_VARIABLES = /* @__PURE__ */ new Set([
8
8
  "TREESEED_MARKET_API_BASE_URL",
9
9
  "TREESEED_PROJECT_ID"
10
10
  ]);
11
+ const SAFE_MANAGED_HOST_CI_SECRETS = /* @__PURE__ */ new Set([
12
+ "TREESEED_TURNSTILE_SECRET_KEY"
13
+ ]);
11
14
  const MANAGED_HOST_FORBIDDEN_VARIABLE_PREFIXES = [
12
15
  "CLOUDFLARE_",
13
16
  "RAILWAY_",
@@ -31,7 +34,7 @@ function usesManagedHostOperationRequests(deployConfig, env = process.env) {
31
34
  }
32
35
  function filterManagedHostGitHubEnvironment(required) {
33
36
  return {
34
- secrets: [],
37
+ secrets: required.secrets.filter((name) => SAFE_MANAGED_HOST_CI_SECRETS.has(name)),
35
38
  variables: required.variables.filter((name) => {
36
39
  if (SAFE_MANAGED_HOST_CI_VARIABLES.has(name)) {
37
40
  return true;
@@ -1,3 +1,4 @@
1
+ import { type TreeseedTimingEntry } from '../../timing.ts';
1
2
  import { type ControlPlaneReporter } from '../../control-plane.ts';
2
3
  import type { TreeseedRunnableBootstrapSystem } from '../../reconcile/index.ts';
3
4
  import { type TreeseedBootstrapExecution, type TreeseedBootstrapWriter } from './bootstrap-runner.ts';
@@ -59,6 +60,7 @@ export declare function provisionProjectPlatform(options: ProjectPlatformActionO
59
60
  plans: import("../../reconcile/contracts.ts").TreeseedReconcilePlan[];
60
61
  results: import("../../reconcile/contracts.ts").TreeseedReconcileResult[];
61
62
  state: import("../../reconcile/contracts.ts").TreeseedReconcileStateRecord;
63
+ timings: TreeseedTimingEntry[];
62
64
  };
63
65
  verification: {
64
66
  target: import("../../reconcile/contracts.ts").TreeseedReconcileTarget;
@@ -76,6 +78,7 @@ export declare function provisionProjectPlatform(options: ProjectPlatformActionO
76
78
  verification: import("../../reconcile/contracts.ts").TreeseedUnitVerificationResult | null;
77
79
  }[];
78
80
  };
81
+ timings: TreeseedTimingEntry[];
79
82
  railway: {
80
83
  services: any[];
81
84
  schedules: any[];
@@ -104,6 +107,10 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
104
107
  objectKey: string;
105
108
  skipped?: undefined;
106
109
  reason?: undefined;
110
+ } | {
111
+ ok: boolean;
112
+ skipped: boolean;
113
+ reason: string;
107
114
  };
108
115
  queue: {
109
116
  ok: boolean;
@@ -241,6 +248,7 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
241
248
  };
242
249
  };
243
250
  };
251
+ timings: TreeseedTimingEntry[];
244
252
  };
245
253
  hostingRepair: {
246
254
  ok: boolean;
@@ -285,6 +293,7 @@ export declare function deployProjectPlatform(options: ProjectPlatformActionOpti
285
293
  } | null;
286
294
  } | null;
287
295
  } | undefined)[];
296
+ timings: TreeseedTimingEntry[];
288
297
  }>;
289
298
  export declare function resolveRailwayServiceDeployDependencies({ includeDataDependency, previousRailwayDeployNodeId, }: {
290
299
  includeDataDependency: boolean;
@@ -313,6 +322,10 @@ export declare function monitorProjectPlatform(options: ProjectPlatformActionOpt
313
322
  objectKey: string;
314
323
  skipped?: undefined;
315
324
  reason?: undefined;
325
+ } | {
326
+ ok: boolean;
327
+ skipped: boolean;
328
+ reason: string;
316
329
  };
317
330
  queue: {
318
331
  ok: boolean;
@@ -450,6 +463,7 @@ export declare function monitorProjectPlatform(options: ProjectPlatformActionOpt
450
463
  };
451
464
  };
452
465
  };
466
+ timings: TreeseedTimingEntry[];
453
467
  }>;
454
468
  export declare function syncControlPlaneState(options: ProjectPlatformActionOptions): Promise<void>;
455
469
  export declare function runProjectPlatformAction(action: ProjectPlatformAction, options: ProjectPlatformActionOptions): Promise<{
@@ -474,6 +488,10 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
474
488
  objectKey: string;
475
489
  skipped?: undefined;
476
490
  reason?: undefined;
491
+ } | {
492
+ ok: boolean;
493
+ skipped: boolean;
494
+ reason: string;
477
495
  };
478
496
  queue: {
479
497
  ok: boolean;
@@ -611,6 +629,7 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
611
629
  };
612
630
  };
613
631
  };
632
+ timings: TreeseedTimingEntry[];
614
633
  } | {
615
634
  ok: boolean;
616
635
  scope: ProjectPlatformScope;
@@ -628,6 +647,10 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
628
647
  objectKey: string;
629
648
  skipped?: undefined;
630
649
  reason?: undefined;
650
+ } | {
651
+ ok: boolean;
652
+ skipped: boolean;
653
+ reason: string;
631
654
  };
632
655
  queue: {
633
656
  ok: boolean;
@@ -765,6 +788,7 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
765
788
  };
766
789
  };
767
790
  };
791
+ timings: TreeseedTimingEntry[];
768
792
  };
769
793
  hostingRepair: {
770
794
  ok: boolean;
@@ -809,5 +833,6 @@ export declare function runProjectPlatformAction(action: ProjectPlatformAction,
809
833
  } | null;
810
834
  } | null;
811
835
  } | undefined)[];
836
+ timings: TreeseedTimingEntry[];
812
837
  }>;
813
838
  export {};