@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.
- package/dist/db/market-schema.js +3 -2
- package/dist/market-client.d.ts +4 -0
- package/dist/market-client.js +6 -0
- package/dist/operations/providers/default.js +26 -4
- package/dist/operations/repository-operations.js +6 -2
- package/dist/operations/services/bootstrap-runner.d.ts +5 -1
- package/dist/operations/services/bootstrap-runner.js +34 -5
- package/dist/operations/services/config-runtime.d.ts +2 -1
- package/dist/operations/services/deploy.d.ts +18 -1
- package/dist/operations/services/deploy.js +176 -24
- package/dist/operations/services/github-automation.d.ts +10 -1
- package/dist/operations/services/github-automation.js +18 -4
- package/dist/operations/services/hosting-audit.d.ts +2 -1
- package/dist/operations/services/hosting-audit.js +12 -1
- package/dist/operations/services/hub-launch.d.ts +1 -0
- package/dist/operations/services/hub-launch.js +1 -0
- package/dist/operations/services/hub-provider-launch.d.ts +9 -0
- package/dist/operations/services/hub-provider-launch.js +140 -40
- package/dist/operations/services/managed-host-security.d.ts +1 -1
- package/dist/operations/services/managed-host-security.js +4 -1
- package/dist/operations/services/project-platform.d.ts +25 -0
- package/dist/operations/services/project-platform.js +91 -23
- package/dist/operations/services/railway-api.js +2 -1
- package/dist/operations/services/railway-deploy.d.ts +32 -2
- package/dist/operations/services/railway-deploy.js +94 -27
- package/dist/operations/services/template-registry.js +33 -3
- package/dist/platform/contracts.d.ts +1 -0
- package/dist/platform/deploy-config.js +8 -1
- package/dist/platform/deploy-runtime.js +1 -0
- package/dist/platform/environment.d.ts +1 -1
- package/dist/platform/environment.js +1 -1
- package/dist/reconcile/builtin-adapters.js +155 -25
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.js +17 -1
- package/dist/reconcile/engine.d.ts +2 -0
- package/dist/reconcile/engine.js +58 -3
- package/dist/reconcile/units.js +1 -0
- package/dist/sdk-types.d.ts +1 -1
- package/dist/sdk-types.js +2 -0
- package/dist/timing.d.ts +20 -0
- package/dist/timing.js +73 -0
- package/dist/treeseed/template-catalog/catalog.fixture.json +150 -0
- package/dist/workflow/operations.d.ts +2 -0
- package/drizzle/market/0000_market_control_plane.sql +3 -3
- package/drizzle/market/0003_project_team_slug_unique.sql +4 -0
- package/package.json +1 -1
- 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
|
-
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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
|
|
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 === "
|
|
244
|
-
const runtimeRegistration = input.hostingMode === "
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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:
|
|
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 {};
|