@treeseed/sdk 0.10.22 → 0.10.23
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/config-runtime.d.ts +1 -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.js +16 -0
- package/dist/operations/services/railway-api.js +2 -1
- package/dist/operations/services/railway-deploy.d.ts +2 -1
- package/dist/operations/services/railway-deploy.js +15 -18
- 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/units.js +1 -0
- package/dist/sdk-types.d.ts +1 -1
- package/dist/sdk-types.js +2 -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
|
@@ -49,7 +49,19 @@ function runGit(args, { cwd, allowFailure = false, capture = true } = {}) {
|
|
|
49
49
|
encoding: "utf8"
|
|
50
50
|
});
|
|
51
51
|
if (result.status !== 0 && !allowFailure) {
|
|
52
|
-
|
|
52
|
+
if (args[0] === "push" && !args.includes("--force")) {
|
|
53
|
+
const retryArgs = ["push", "--force", ...args.slice(1)];
|
|
54
|
+
const retry = spawnSync("git", retryArgs, {
|
|
55
|
+
cwd,
|
|
56
|
+
stdio: capture ? "pipe" : "inherit",
|
|
57
|
+
encoding: "utf8"
|
|
58
|
+
});
|
|
59
|
+
if (retry.status === 0) return retry;
|
|
60
|
+
const retryDetail = retry.stderr?.trim() || retry.stdout?.trim();
|
|
61
|
+
throw new Error(`git ${retryArgs.join(" ")} failed${retryDetail ? `: ${retryDetail}` : ""}`);
|
|
62
|
+
}
|
|
63
|
+
const detail = result.stderr?.trim() || result.stdout?.trim();
|
|
64
|
+
throw new Error(`git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
|
|
53
65
|
}
|
|
54
66
|
return result;
|
|
55
67
|
}
|
|
@@ -223,7 +235,8 @@ function initializeGitHubRepositoryWorkingTree(cwd, repository, {
|
|
|
223
235
|
createStaging = true,
|
|
224
236
|
commitMessage = "Initialize TreeSeed hub",
|
|
225
237
|
remoteName = "origin",
|
|
226
|
-
push = true
|
|
238
|
+
push = true,
|
|
239
|
+
forcePush = false
|
|
227
240
|
} = {}) {
|
|
228
241
|
runGit(["init", "-b", defaultBranch], { cwd, allowFailure: true });
|
|
229
242
|
ensureGitIdentity(cwd);
|
|
@@ -239,12 +252,12 @@ function initializeGitHubRepositoryWorkingTree(cwd, repository, {
|
|
|
239
252
|
runGit(["commit", "-m", commitMessage], { cwd });
|
|
240
253
|
}
|
|
241
254
|
if (push) {
|
|
242
|
-
runGit(["push", "-u", remoteName, defaultBranch], { cwd, capture: false });
|
|
255
|
+
runGit(["push", ...forcePush ? ["--force"] : [], "-u", remoteName, defaultBranch], { cwd, capture: false });
|
|
243
256
|
}
|
|
244
257
|
if (createStaging) {
|
|
245
258
|
runGit(["checkout", "-B", "staging"], { cwd });
|
|
246
259
|
if (push) {
|
|
247
|
-
runGit(["push", "-u", remoteName, "staging"], { cwd, capture: false });
|
|
260
|
+
runGit(["push", ...forcePush ? ["--force"] : [], "-u", remoteName, "staging"], { cwd, capture: false });
|
|
248
261
|
}
|
|
249
262
|
runGit(["checkout", defaultBranch], { cwd });
|
|
250
263
|
}
|
|
@@ -501,6 +514,7 @@ export {
|
|
|
501
514
|
requiredGitHubEnvironment,
|
|
502
515
|
requiredGitHubSecrets,
|
|
503
516
|
resolveDefaultGitHubOwner,
|
|
517
|
+
resolveGitHubRemoteUrls,
|
|
504
518
|
resolveGitHubRepositorySlug,
|
|
505
519
|
resolveGitHubRepositoryTarget,
|
|
506
520
|
resolveGitRepositoryRoot,
|
|
@@ -53,6 +53,7 @@ export type TreeseedHostingAuditOptions = {
|
|
|
53
53
|
valuesOverlay?: Record<string, string | undefined>;
|
|
54
54
|
hostKinds?: TreeseedHostingAuditHostKind[];
|
|
55
55
|
providerConnectionChecks?: boolean;
|
|
56
|
+
resourceChecks?: boolean;
|
|
56
57
|
write?: (line: string) => void;
|
|
57
58
|
};
|
|
58
59
|
export declare function resolveTreeseedHostingAuditTarget({ tenantRoot, environment, }: {
|
|
@@ -64,5 +65,5 @@ export declare function resolveTreeseedHostingAuditTarget({ tenantRoot, environm
|
|
|
64
65
|
target: TreeseedReconcileTarget;
|
|
65
66
|
branchName: string | null;
|
|
66
67
|
};
|
|
67
|
-
export declare function runTreeseedHostingAudit({ tenantRoot, environment, repair, env, valuesOverlay, hostKinds: requestedHostKinds, providerConnectionChecks: shouldCheckProviderConnections, write, }: TreeseedHostingAuditOptions): Promise<TreeseedHostingAuditReport>;
|
|
68
|
+
export declare function runTreeseedHostingAudit({ tenantRoot, environment, repair, env, valuesOverlay, hostKinds: requestedHostKinds, providerConnectionChecks: shouldCheckProviderConnections, resourceChecks: shouldCheckResources, write, }: TreeseedHostingAuditOptions): Promise<TreeseedHostingAuditReport>;
|
|
68
69
|
export declare function formatTreeseedHostingAuditReport(report: TreeseedHostingAuditReport): string;
|
|
@@ -463,6 +463,7 @@ async function runTreeseedHostingAudit({
|
|
|
463
463
|
valuesOverlay = {},
|
|
464
464
|
hostKinds: requestedHostKinds,
|
|
465
465
|
providerConnectionChecks: shouldCheckProviderConnections = true,
|
|
466
|
+
resourceChecks: shouldCheckResources = true,
|
|
466
467
|
write
|
|
467
468
|
}) {
|
|
468
469
|
const resolved = resolveTreeseedHostingAuditTarget({ tenantRoot, environment });
|
|
@@ -509,7 +510,17 @@ async function runTreeseedHostingAudit({
|
|
|
509
510
|
const systems = reconcileSystemsForHostKinds(hostKinds);
|
|
510
511
|
let resources = {};
|
|
511
512
|
let repaired = false;
|
|
512
|
-
if (
|
|
513
|
+
if (!shouldCheckResources) {
|
|
514
|
+
checks.push({
|
|
515
|
+
id: "resources.skipped",
|
|
516
|
+
hostType: "platform",
|
|
517
|
+
provider: "treeseed",
|
|
518
|
+
category: "resource",
|
|
519
|
+
status: "skipped",
|
|
520
|
+
severity: "info",
|
|
521
|
+
summary: "Hosted provider resource checks are skipped for this audit."
|
|
522
|
+
});
|
|
523
|
+
} else if (resolved.environment !== "local" && systems.length > 0) {
|
|
513
524
|
try {
|
|
514
525
|
const state = loadDeployState(tenantRoot, deployConfig, { target: resolved.target });
|
|
515
526
|
resources = buildProvisioningSummary(deployConfig, state, resolved.target);
|
|
@@ -193,6 +193,7 @@ function providerLaunchInputFromIntent(plan) {
|
|
|
193
193
|
projectSlug: intent.hub.slug,
|
|
194
194
|
projectName: intent.hub.name,
|
|
195
195
|
summary: intent.hub.purpose ?? null,
|
|
196
|
+
coreObjective: providerInput.coreObjective ?? intent.hub.coreObjective ?? intent.hub.purpose ?? null,
|
|
196
197
|
sourceKind: sourceKind === "blank_hub" ? "blank" : sourceKind === "market_listing" ? "template" : sourceKind,
|
|
197
198
|
sourceRef: intent.source?.ref ?? null,
|
|
198
199
|
hostingMode: intent.hosting?.mode === "treeseed_managed" ? "managed" : intent.hosting?.mode ?? "managed",
|
|
@@ -9,6 +9,7 @@ export interface KnowledgeHubProviderLaunchInput {
|
|
|
9
9
|
projectSlug: string;
|
|
10
10
|
projectName: string;
|
|
11
11
|
summary?: string | null;
|
|
12
|
+
coreObjective?: string | null;
|
|
12
13
|
sourceKind: 'blank' | 'template' | 'knowledge_pack';
|
|
13
14
|
sourceRef?: string | null;
|
|
14
15
|
hostingMode?: 'managed' | 'hybrid' | 'self_hosted';
|
|
@@ -38,6 +39,14 @@ export interface KnowledgeHubProviderLaunchInput {
|
|
|
38
39
|
enableDefaultAgents?: boolean;
|
|
39
40
|
preserveWorkingTree?: boolean;
|
|
40
41
|
cloudflareHost?: KnowledgeHubCloudflareHostLaunchInput | null;
|
|
42
|
+
domains?: {
|
|
43
|
+
productionDomain?: string | null;
|
|
44
|
+
stagingDomain?: string | null;
|
|
45
|
+
zoneName?: string | null;
|
|
46
|
+
zoneId?: string | null;
|
|
47
|
+
manageDns?: boolean;
|
|
48
|
+
provider?: string | null;
|
|
49
|
+
} | null;
|
|
41
50
|
}
|
|
42
51
|
export interface KnowledgeHubCloudflareHostConfig {
|
|
43
52
|
CLOUDFLARE_API_TOKEN?: string;
|
|
@@ -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;
|
|
@@ -1087,6 +1087,22 @@ async function provisionProjectPlatform(options) {
|
|
|
1087
1087
|
locator: state.lastDeployedUrl ?? null,
|
|
1088
1088
|
metadata: { workerName: state.workerName }
|
|
1089
1089
|
},
|
|
1090
|
+
{
|
|
1091
|
+
environment: options.scope,
|
|
1092
|
+
provider: "cloudflare",
|
|
1093
|
+
resourceKind: "kv",
|
|
1094
|
+
logicalName: state.kvNamespaces?.FORM_GUARD_KV?.name ?? "form-guard",
|
|
1095
|
+
locator: state.kvNamespaces?.FORM_GUARD_KV?.id ?? null,
|
|
1096
|
+
metadata: state.kvNamespaces?.FORM_GUARD_KV ?? {}
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
environment: options.scope,
|
|
1100
|
+
provider: "cloudflare",
|
|
1101
|
+
resourceKind: "turnstile-widget",
|
|
1102
|
+
logicalName: state.turnstileWidgets?.formGuard?.name ?? "form-guard-turnstile",
|
|
1103
|
+
locator: state.turnstileWidgets?.formGuard?.sitekey ?? null,
|
|
1104
|
+
metadata: state.turnstileWidgets?.formGuard ?? {}
|
|
1105
|
+
},
|
|
1090
1106
|
{
|
|
1091
1107
|
environment: options.scope,
|
|
1092
1108
|
provider: "cloudflare",
|
|
@@ -1295,7 +1295,8 @@ async function ensureRailwayServiceVolume({
|
|
|
1295
1295
|
if (!volume) {
|
|
1296
1296
|
volume = await createReplacementVolume();
|
|
1297
1297
|
}
|
|
1298
|
-
|
|
1298
|
+
const desiredNameInUse = volumes.some((candidate) => candidate.id !== volume?.id && candidate.name === name);
|
|
1299
|
+
if (volume.name && volume.name !== name && !desiredNameInUse) {
|
|
1299
1300
|
try {
|
|
1300
1301
|
volume = await updateRailwayVolumeName({ volumeId: volume.id, name, env, fetchImpl }) ?? { ...volume, name };
|
|
1301
1302
|
} catch (error) {
|
|
@@ -6,9 +6,10 @@ export declare function deriveRailwayMarketOperationsRunnerVolumeName(serviceNam
|
|
|
6
6
|
export declare function railwayServiceRuntimeStartCommand(service: any): any;
|
|
7
7
|
export declare function parseRailwayJsonOutput(output: any): any;
|
|
8
8
|
export declare function collectRailwayDeploymentStatusChecks(statusPayload: any, scope: any, services: any): any;
|
|
9
|
-
export declare function listRailwayServiceVolumesWithCli({ cwd, serviceId, environmentId, name, mountPath, env, }: {
|
|
9
|
+
export declare function listRailwayServiceVolumesWithCli({ cwd, serviceId, serviceName, environmentId, name, mountPath, env, }: {
|
|
10
10
|
cwd: any;
|
|
11
11
|
serviceId: any;
|
|
12
|
+
serviceName: any;
|
|
12
13
|
environmentId: any;
|
|
13
14
|
name: any;
|
|
14
15
|
mountPath: any;
|