@treeseed/sdk 0.4.12 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/control-plane-client.d.ts +60 -1
  2. package/dist/control-plane-client.js +59 -0
  3. package/dist/control-plane.d.ts +1 -1
  4. package/dist/control-plane.js +11 -4
  5. package/dist/d1-store.d.ts +58 -0
  6. package/dist/d1-store.js +64 -0
  7. package/dist/dispatch.js +6 -0
  8. package/dist/graph/schema.js +4 -0
  9. package/dist/index.d.ts +5 -1
  10. package/dist/index.js +32 -0
  11. package/dist/knowledge-coop.d.ts +223 -0
  12. package/dist/knowledge-coop.js +82 -0
  13. package/dist/model-registry.js +79 -0
  14. package/dist/operations/providers/default.js +128 -7
  15. package/dist/operations/services/config-runtime.d.ts +102 -24
  16. package/dist/operations/services/config-runtime.js +896 -160
  17. package/dist/operations/services/deploy.d.ts +223 -15
  18. package/dist/operations/services/deploy.js +626 -55
  19. package/dist/operations/services/git-workflow.d.ts +47 -3
  20. package/dist/operations/services/git-workflow.js +125 -19
  21. package/dist/operations/services/github-automation.d.ts +85 -0
  22. package/dist/operations/services/github-automation.js +220 -1
  23. package/dist/operations/services/key-agent.d.ts +118 -0
  24. package/dist/operations/services/key-agent.js +476 -0
  25. package/dist/operations/services/knowledge-coop-launch.d.ts +90 -0
  26. package/dist/operations/services/knowledge-coop-launch.js +753 -0
  27. package/dist/operations/services/knowledge-coop-packaging.d.ts +59 -0
  28. package/dist/operations/services/knowledge-coop-packaging.js +234 -0
  29. package/dist/operations/services/local-dev.d.ts +0 -1
  30. package/dist/operations/services/local-dev.js +1 -14
  31. package/dist/operations/services/project-platform.d.ts +42 -182
  32. package/dist/operations/services/project-platform.js +162 -59
  33. package/dist/operations/services/railway-deploy.d.ts +1 -0
  34. package/dist/operations/services/railway-deploy.js +31 -13
  35. package/dist/operations/services/runtime-tools.d.ts +52 -5
  36. package/dist/operations/services/runtime-tools.js +186 -26
  37. package/dist/operations/services/watch-dev.js +2 -4
  38. package/dist/operations/services/workspace-preflight.d.ts +4 -4
  39. package/dist/operations/services/workspace-preflight.js +22 -20
  40. package/dist/operations/services/workspace-save.d.ts +10 -1
  41. package/dist/operations/services/workspace-save.js +54 -3
  42. package/dist/operations/services/workspace-tools.d.ts +1 -0
  43. package/dist/operations/services/workspace-tools.js +20 -5
  44. package/dist/operations-registry.js +15 -8
  45. package/dist/operations-types.d.ts +2 -2
  46. package/dist/platform/contracts.d.ts +39 -3
  47. package/dist/platform/deploy-config.d.ts +12 -1
  48. package/dist/platform/deploy-config.js +214 -15
  49. package/dist/platform/deploy-runtime.d.ts +1 -0
  50. package/dist/platform/deploy-runtime.js +10 -2
  51. package/dist/platform/env.yaml +93 -61
  52. package/dist/platform/environment.d.ts +13 -2
  53. package/dist/platform/environment.js +90 -20
  54. package/dist/platform/plugins/constants.d.ts +1 -0
  55. package/dist/platform/plugins/constants.js +7 -6
  56. package/dist/platform/tenant/runtime-config.js +8 -1
  57. package/dist/platform/tenant-config.js +4 -0
  58. package/dist/platform/utils/site-config-schema.js +18 -0
  59. package/dist/plugin-default.js +2 -2
  60. package/dist/scripts/key-agent.js +165 -0
  61. package/dist/scripts/tenant-build.js +4 -1
  62. package/dist/scripts/tenant-check.js +4 -1
  63. package/dist/scripts/tenant-deploy.js +43 -4
  64. package/dist/scripts/tenant-dev.js +0 -1
  65. package/dist/scripts/workspace-start-warning.js +2 -2
  66. package/dist/sdk-types.d.ts +2 -2
  67. package/dist/sdk-types.js +2 -0
  68. package/dist/sdk.d.ts +13 -0
  69. package/dist/sdk.js +40 -0
  70. package/dist/stores/knowledge-coop-store.d.ts +56 -0
  71. package/dist/stores/knowledge-coop-store.js +482 -0
  72. package/dist/treeseed/template-catalog/templates/starter-basic/template/package.json +6 -2
  73. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/api/server.js +4 -0
  74. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/config.yaml +25 -0
  75. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/decisions/adopt-initial-proposal-loop.mdx +22 -0
  76. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/people/starter-steward.mdx +11 -0
  77. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/content/proposals/establish-initial-proposal-loop.mdx +17 -0
  78. package/dist/treeseed/template-catalog/templates/starter-basic/template/src/manifest.yaml +17 -10
  79. package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +69 -7
  80. package/dist/treeseed/template-catalog/templates/starter-basic/template.config.json +1 -0
  81. package/dist/workflow/operations.d.ts +592 -243
  82. package/dist/workflow/operations.js +1908 -219
  83. package/dist/workflow/runs.d.ts +90 -0
  84. package/dist/workflow/runs.js +242 -0
  85. package/dist/workflow/session.d.ts +31 -0
  86. package/dist/workflow/session.js +97 -0
  87. package/dist/workflow-state.d.ts +88 -2
  88. package/dist/workflow-state.js +288 -26
  89. package/dist/workflow-support.d.ts +1 -1
  90. package/dist/workflow-support.js +32 -2
  91. package/dist/workflow.d.ts +93 -3
  92. package/dist/workflow.js +12 -0
  93. package/package.json +1 -1
  94. package/templates/github/deploy.workflow.yml +11 -1
  95. package/dist/scripts/sync-dev-vars.js +0 -6
  96. package/dist/scripts/workspace-close.js +0 -24
  97. package/dist/scripts/workspace-release.js +0 -42
  98. package/dist/scripts/workspace-start.js +0 -71
@@ -37,12 +37,12 @@ const TREESEED_DEFAULT_PLUGIN_REFERENCES = [
37
37
  const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
38
38
  forms: "store_only",
39
39
  agents: {
40
- execution: "stub",
40
+ execution: "copilot",
41
41
  mutation: "local_branch",
42
- repository: "stub",
43
- verification: "stub",
44
- notification: "stub",
45
- research: "stub"
42
+ repository: "git",
43
+ verification: "local",
44
+ notification: "sdk_message",
45
+ research: "project_graph"
46
46
  },
47
47
  deploy: "cloudflare",
48
48
  content: {
@@ -52,9 +52,57 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
52
52
  },
53
53
  site: "default"
54
54
  };
55
- const TRESEED_MANAGED_SERVICE_KEYS = ["api", "agents", "manager", "worker", "runner", "workdayStart", "workdayReport"];
55
+ const TRESEED_MANAGED_SERVICE_KEYS = ["api", "manager", "worker", "workdayStart", "workdayReport"];
56
56
  const TRESEED_WORKSPACE_PACKAGE_DIRS = ["sdk", "core", "cli"];
57
57
  const CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER = "replace-with-cloudflare-account-id";
58
+ function normalizePlanesFromLegacyHosting(hosting) {
59
+ if (!hosting || Object.keys(hosting).length === 0) {
60
+ return {
61
+ hub: { mode: "treeseed_hosted" },
62
+ runtime: { mode: "none", registration: "none" }
63
+ };
64
+ }
65
+ if (hosting.kind === "market_control_plane" || hosting.kind === "hosted_project") {
66
+ return {
67
+ hub: { mode: "treeseed_hosted" },
68
+ runtime: {
69
+ mode: "treeseed_managed",
70
+ registration: hosting.kind === "market_control_plane" ? "none" : hosting.registration ?? "none",
71
+ marketBaseUrl: hosting.marketBaseUrl,
72
+ teamId: hosting.teamId,
73
+ projectId: hosting.projectId
74
+ }
75
+ };
76
+ }
77
+ return {
78
+ hub: { mode: "customer_hosted" },
79
+ runtime: {
80
+ mode: "byo_attached",
81
+ registration: hosting.registration ?? "none",
82
+ marketBaseUrl: hosting.marketBaseUrl,
83
+ teamId: hosting.teamId,
84
+ projectId: hosting.projectId
85
+ }
86
+ };
87
+ }
88
+ function normalizeLegacyHostingFromPlanes(hub, runtime) {
89
+ if (runtime.mode === "treeseed_managed" && hub.mode === "treeseed_hosted") {
90
+ return {
91
+ kind: "hosted_project",
92
+ registration: runtime.registration === "required" ? "optional" : runtime.registration ?? "none",
93
+ marketBaseUrl: runtime.marketBaseUrl,
94
+ teamId: runtime.teamId,
95
+ projectId: runtime.projectId
96
+ };
97
+ }
98
+ return {
99
+ kind: "self_hosted_project",
100
+ registration: runtime.registration === "required" ? "optional" : runtime.registration ?? "none",
101
+ marketBaseUrl: runtime.marketBaseUrl,
102
+ teamId: runtime.teamId,
103
+ projectId: runtime.projectId
104
+ };
105
+ }
58
106
  function parseServiceEnvironmentConfig(value) {
59
107
  const record = optionalRecord(value, "service environment") ?? {};
60
108
  return {
@@ -108,6 +156,81 @@ function parseManagedServicesConfig(value) {
108
156
  ])
109
157
  );
110
158
  }
159
+ function parseWebSurfaceCacheConfig(value, label) {
160
+ const record = optionalRecord(value, label) ?? {};
161
+ if (Object.keys(record).length === 0) {
162
+ return void 0;
163
+ }
164
+ return {
165
+ sourcePages: parseWebCachePolicyRecord(record.sourcePages ?? record.source_pages, `${label}.sourcePages`, {
166
+ paths: ["/", "/contact", "/404"]
167
+ }),
168
+ contentPages: parseWebCachePolicyRecord(record.contentPages ?? record.content_pages, `${label}.contentPages`),
169
+ r2PublishedObjects: parseWebCachePolicyRecord(
170
+ record.r2PublishedObjects ?? record.r2_published_objects,
171
+ `${label}.r2PublishedObjects`
172
+ )
173
+ };
174
+ }
175
+ function parseWebCachePolicyRecord(value, label, options = {}) {
176
+ const record = optionalRecord(value, label) ?? {};
177
+ if (Object.keys(record).length === 0) {
178
+ return options.paths ? { paths: [...options.paths] } : void 0;
179
+ }
180
+ const parsed = {
181
+ browserTtlSeconds: optionalNonNegativeNumber(
182
+ record.browserTtlSeconds ?? record.browser_ttl_seconds,
183
+ `${label}.browserTtlSeconds`
184
+ ),
185
+ edgeTtlSeconds: optionalNonNegativeNumber(
186
+ record.edgeTtlSeconds ?? record.edge_ttl_seconds,
187
+ `${label}.edgeTtlSeconds`
188
+ ),
189
+ staleWhileRevalidateSeconds: optionalNonNegativeNumber(
190
+ record.staleWhileRevalidateSeconds ?? record.stale_while_revalidate_seconds,
191
+ `${label}.staleWhileRevalidateSeconds`
192
+ ),
193
+ staleIfErrorSeconds: optionalNonNegativeNumber(
194
+ record.staleIfErrorSeconds ?? record.stale_if_error_seconds,
195
+ `${label}.staleIfErrorSeconds`
196
+ )
197
+ };
198
+ if (options.paths) {
199
+ parsed.paths = [...new Set(Array.isArray(record.paths) ? record.paths.map((entry) => optionalString(entry)).filter(Boolean) : options.paths)];
200
+ }
201
+ return parsed;
202
+ }
203
+ function parseSurfaceConfig(value, label) {
204
+ const record = optionalRecord(value, label);
205
+ if (!record) {
206
+ return void 0;
207
+ }
208
+ return {
209
+ enabled: record.enabled === void 0 ? void 0 : optionalBoolean(record.enabled, `${label}.enabled`),
210
+ provider: optionalString(record.provider),
211
+ rootDir: optionalString(record.rootDir),
212
+ publicBaseUrl: optionalString(record.publicBaseUrl),
213
+ localBaseUrl: optionalString(record.localBaseUrl),
214
+ cache: parseWebSurfaceCacheConfig(record.cache, `${label}.cache`)
215
+ };
216
+ }
217
+ function parsePlatformSurfacesConfig(value) {
218
+ const record = optionalRecord(value, "surfaces");
219
+ if (!record) {
220
+ return void 0;
221
+ }
222
+ return Object.fromEntries(
223
+ Object.entries(record).map(([surfaceKey, surfaceValue]) => [
224
+ surfaceKey,
225
+ parseSurfaceConfig(surfaceValue, `surfaces.${surfaceKey}`)
226
+ ])
227
+ );
228
+ }
229
+ function inferManagedRuntimeFromServices(services) {
230
+ return Object.values(services ?? {}).some(
231
+ (service) => service && service.enabled !== false && (service.provider ?? "railway") === "railway"
232
+ );
233
+ }
111
234
  const packageRoot = packageRootFromSource;
112
235
  const packageScriptRoot = resolve(packageRoot, "scripts");
113
236
  const packageDistScriptRoot = resolve(packageRoot, "dist", "scripts");
@@ -272,6 +395,15 @@ function optionalPositiveNumber(value, label) {
272
395
  }
273
396
  return value;
274
397
  }
398
+ function optionalNonNegativeNumber(value, label) {
399
+ if (value === void 0) {
400
+ return void 0;
401
+ }
402
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
403
+ throw new Error(`Invalid deploy config: expected ${label} to be a non-negative number when provided.`);
404
+ }
405
+ return value;
406
+ }
275
407
  function optionalEnum(value, label, allowed) {
276
408
  const normalized = optionalString(value);
277
409
  if (!normalized) {
@@ -314,6 +446,35 @@ function parseFallbackDeployConfig(configPath) {
314
446
  const cloudflarePages = optionalRecord(cloudflare.pages, "cloudflare.pages") ?? {};
315
447
  const cloudflareR2 = optionalRecord(cloudflare.r2, "cloudflare.r2") ?? {};
316
448
  const hosting = optionalRecord(record.hosting, "hosting") ?? {};
449
+ const parsedHosting = Object.keys(hosting).length === 0 ? void 0 : {
450
+ kind: optionalEnum(hosting.kind, "hosting.kind", [
451
+ "market_control_plane",
452
+ "hosted_project",
453
+ "self_hosted_project"
454
+ ]) ?? "self_hosted_project",
455
+ registration: optionalEnum(hosting.registration, "hosting.registration", ["optional", "none"]) ?? "none",
456
+ marketBaseUrl: optionalString(process.env.TREESEED_MARKET_API_BASE_URL),
457
+ teamId: optionalString(process.env.TREESEED_HOSTING_TEAM_ID),
458
+ projectId: optionalString(process.env.TREESEED_PROJECT_ID)
459
+ };
460
+ const services = parseManagedServicesConfig(record.services);
461
+ const normalizedPlanes = normalizePlanesFromLegacyHosting(parsedHosting);
462
+ const inferredPlanes = !parsedHosting && record.hub === void 0 && record.runtime === void 0 && inferManagedRuntimeFromServices(services) ? {
463
+ hub: { mode: "customer_hosted" },
464
+ runtime: { mode: "treeseed_managed", registration: "none" }
465
+ } : normalizedPlanes;
466
+ const hubRecord = optionalRecord(record.hub, "hub") ?? {};
467
+ const runtimeRecord = optionalRecord(record.runtime, "runtime") ?? {};
468
+ const hub = {
469
+ mode: optionalEnum(hubRecord.mode, "hub.mode", ["treeseed_hosted", "customer_hosted"]) ?? inferredPlanes.hub.mode
470
+ };
471
+ const runtime = {
472
+ mode: optionalEnum(runtimeRecord.mode, "runtime.mode", ["none", "byo_attached", "treeseed_managed"]) ?? inferredPlanes.runtime.mode,
473
+ registration: optionalEnum(runtimeRecord.registration, "runtime.registration", ["optional", "required", "none"]) ?? inferredPlanes.runtime.registration ?? "none",
474
+ marketBaseUrl: optionalString(process.env.TREESEED_MARKET_API_BASE_URL) ?? inferredPlanes.runtime.marketBaseUrl,
475
+ teamId: optionalString(process.env.TREESEED_HOSTING_TEAM_ID) ?? inferredPlanes.runtime.teamId,
476
+ projectId: optionalString(process.env.TREESEED_PROJECT_ID) ?? inferredPlanes.runtime.projectId
477
+ };
317
478
  const smtp = optionalRecord(record.smtp, "smtp") ?? {};
318
479
  const turnstile = optionalRecord(record.turnstile, "turnstile") ?? {};
319
480
  const agentProviders = optionalRecord(optionalRecord(record.providers, "providers")?.agents, "providers.agents") ?? {};
@@ -323,35 +484,28 @@ function parseFallbackDeployConfig(configPath) {
323
484
  slug: expectString(record.slug, "slug"),
324
485
  siteUrl: expectString(record.siteUrl, "siteUrl"),
325
486
  contactEmail: expectString(record.contactEmail, "contactEmail"),
326
- hosting: Object.keys(hosting).length === 0 ? void 0 : {
327
- kind: optionalEnum(hosting.kind, "hosting.kind", [
328
- "market_control_plane",
329
- "hosted_project",
330
- "self_hosted_project"
331
- ]) ?? "self_hosted_project",
332
- registration: optionalEnum(hosting.registration, "hosting.registration", ["optional", "none"]) ?? "none",
333
- marketBaseUrl: optionalString(hosting.marketBaseUrl),
334
- teamId: optionalString(hosting.teamId),
335
- projectId: optionalString(hosting.projectId)
336
- },
487
+ hosting: parsedHosting && record.hub === void 0 && record.runtime === void 0 ? parsedHosting : normalizeLegacyHostingFromPlanes(hub, runtime),
488
+ hub,
489
+ runtime,
337
490
  cloudflare: {
338
- accountId: optionalCloudflareAccountId(cloudflare.accountId) ?? optionalCloudflareAccountId(process.env.CLOUDFLARE_ACCOUNT_ID) ?? CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER,
491
+ accountId: optionalCloudflareAccountId(process.env.CLOUDFLARE_ACCOUNT_ID) ?? CLOUDFLARE_ACCOUNT_ID_PLACEHOLDER,
492
+ zoneId: optionalString(cloudflare.zoneId ?? cloudflare.zone_id),
339
493
  workerName: optionalString(cloudflare.workerName),
340
494
  queueName: optionalString(cloudflare.queueName),
341
495
  dlqName: optionalString(cloudflare.dlqName),
342
496
  d1Binding: optionalString(cloudflare.d1Binding),
343
497
  queueBinding: optionalString(cloudflare.queueBinding),
344
498
  pages: cloudflare.pages === void 0 ? void 0 : {
345
- projectName: optionalString(cloudflarePages.projectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME),
346
- previewProjectName: optionalString(cloudflarePages.previewProjectName) ?? optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME),
499
+ projectName: optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME) ?? optionalString(cloudflarePages.projectName),
500
+ previewProjectName: optionalString(process.env.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME) ?? optionalString(cloudflarePages.previewProjectName),
347
501
  productionBranch: optionalString(cloudflarePages.productionBranch) ?? "main",
348
502
  stagingBranch: optionalString(cloudflarePages.stagingBranch) ?? "staging",
349
503
  buildOutputDir: optionalString(cloudflarePages.buildOutputDir)
350
504
  },
351
505
  r2: cloudflare.r2 === void 0 ? void 0 : {
352
- binding: optionalString(cloudflareR2.binding) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_BINDING),
353
- bucketName: optionalString(cloudflareR2.bucketName) ?? optionalString(process.env.TREESEED_CONTENT_BUCKET_NAME),
354
- publicBaseUrl: optionalString(cloudflareR2.publicBaseUrl) ?? optionalString(process.env.TREESEED_CONTENT_PUBLIC_BASE_URL),
506
+ binding: optionalString(process.env.TREESEED_CONTENT_BUCKET_BINDING),
507
+ bucketName: optionalString(process.env.TREESEED_CONTENT_BUCKET_NAME),
508
+ publicBaseUrl: optionalString(process.env.TREESEED_CONTENT_PUBLIC_BASE_URL),
355
509
  manifestKeyTemplate: optionalString(cloudflareR2.manifestKeyTemplate ?? cloudflareR2.manifestKey) ?? "teams/{teamId}/published/common.json",
356
510
  previewRootTemplate: optionalString(cloudflareR2.previewRootTemplate ?? cloudflareR2.previewRoot) ?? "teams/{teamId}/previews",
357
511
  previewTtlHours: optionalPositiveNumber(cloudflareR2.previewTtlHours, "cloudflare.r2.previewTtlHours") ?? 168
@@ -381,16 +535,22 @@ function parseFallbackDeployConfig(configPath) {
381
535
  docs: expectString(
382
536
  contentProviders.docs ?? contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.docs ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
383
537
  "providers.content.docs"
384
- )
538
+ ),
539
+ serving: optionalEnum(
540
+ contentProviders.serving,
541
+ "providers.content.serving",
542
+ ["local_collections", "published_runtime"]
543
+ ) ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.serving
385
544
  },
386
545
  site: expectString(record.providers?.site ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.site, "providers.site")
387
546
  },
388
- services: parseManagedServicesConfig(record.services),
547
+ surfaces: parsePlatformSurfacesConfig(record.surfaces),
548
+ services,
389
549
  smtp: {
390
550
  enabled: optionalBoolean(smtp.enabled, "smtp.enabled")
391
551
  },
392
552
  turnstile: {
393
- enabled: optionalBoolean(turnstile.enabled, "turnstile.enabled") ?? true
553
+ enabled: optionalBoolean(turnstile.enabled, "turnstile.enabled") ?? false
394
554
  }
395
555
  };
396
556
  Object.defineProperty(deployConfig, "__tenantRoot", {
@@ -40,7 +40,7 @@ function shouldIgnoreWatchPath(filePath, rootPath) {
40
40
  return false;
41
41
  }
42
42
  const normalized = rel.split(sep).join("/");
43
- return normalized === ".git" || normalized.startsWith(".git/") || normalized === "node_modules" || normalized.startsWith("node_modules/") || normalized === ".astro" || normalized.startsWith(".astro/") || normalized === ".wrangler" || normalized.startsWith(".wrangler/") || normalized === ".local" || normalized.startsWith(".local/") || normalized === ".treeseed" || normalized.startsWith(".treeseed/") || normalized === "dist" || normalized.startsWith("dist/") || normalized === "coverage" || normalized.startsWith("coverage/") || normalized === ".dev.vars" || normalized === "books" || normalized.startsWith("books/") || normalized === "__treeseed" || normalized.startsWith("__treeseed/") || normalized.startsWith("public/books/") || normalized.startsWith("public/__treeseed/");
43
+ return normalized === ".git" || normalized.startsWith(".git/") || normalized === "node_modules" || normalized.startsWith("node_modules/") || normalized === ".astro" || normalized.startsWith(".astro/") || normalized === ".wrangler" || normalized.startsWith(".wrangler/") || normalized === ".local" || normalized.startsWith(".local/") || normalized === ".treeseed" || normalized.startsWith(".treeseed/") || normalized === "dist" || normalized.startsWith("dist/") || normalized === "coverage" || normalized.startsWith("coverage/") || normalized === "books" || normalized.startsWith("books/") || normalized === "__treeseed" || normalized.startsWith("__treeseed/") || normalized.startsWith("public/books/") || normalized.startsWith("public/__treeseed/");
44
44
  }
45
45
  function collectRootSnapshot(rootPath, snapshot) {
46
46
  if (!existsSync(rootPath)) {
@@ -135,9 +135,7 @@ function createTenantWatchEntries(tenantRoot) {
135
135
  { kind: "tenant", root: resolve(tenantRoot, "src") },
136
136
  { kind: "tenant", root: resolve(tenantRoot, "public") },
137
137
  { kind: "tenant", root: resolve(tenantRoot, "astro.config.ts") },
138
- { kind: "tenant", root: resolve(tenantRoot, "treeseed.site.yaml") },
139
- { kind: "tenant", root: resolve(tenantRoot, ".env.local") },
140
- { kind: "tenant", root: resolve(tenantRoot, ".env.local.example") }
138
+ { kind: "tenant", root: resolve(tenantRoot, "treeseed.site.yaml") }
141
139
  ];
142
140
  if (isEditablePackageWorkspace()) {
143
141
  const sdkRoot = workspaceSdkRoot();
@@ -2,19 +2,19 @@ export declare function createWranglerCommandEnv(overrides?: {}): {
2
2
  XDG_CONFIG_HOME: string;
3
3
  WRANGLER_SEND_METRICS: string;
4
4
  };
5
- export declare function parseGitHubAuthStatus(): {
5
+ export declare function parseGitHubAuthStatus(values?: NodeJS.ProcessEnv): {
6
6
  authenticated: boolean;
7
7
  detail: string;
8
8
  };
9
- export declare function parseWranglerWhoAmI(): {
9
+ export declare function parseWranglerWhoAmI(values?: NodeJS.ProcessEnv): {
10
10
  authenticated: boolean;
11
11
  detail: string;
12
12
  };
13
- export declare function parseRailwayWhoAmI(): {
13
+ export declare function parseRailwayWhoAmI(values?: NodeJS.ProcessEnv): {
14
14
  authenticated: boolean;
15
15
  detail: string;
16
16
  };
17
- export declare function parseCopilotSessionStatus(): {
17
+ export declare function parseCopilotSessionStatus(values?: NodeJS.ProcessEnv): {
18
18
  configured: boolean;
19
19
  detail: string;
20
20
  };
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { dirname, resolve } from "node:path";
4
+ import { collectTreeseedConfigSeedValues } from "./config-runtime.js";
4
5
  import { createTempDir } from "./workspace-tools.js";
5
6
  function runCapture(command, args, options = {}) {
6
7
  const result = spawnSync(command, args, {
@@ -32,43 +33,44 @@ function createWranglerCommandEnv(overrides = {}) {
32
33
  ...overrides
33
34
  };
34
35
  }
35
- function envTokenStatus(keys, label) {
36
+ function envTokenStatus(keys, label, values = process.env) {
36
37
  const foundKey = keys.find((key) => {
37
- const value = process.env[key];
38
+ const value = values[key];
38
39
  return typeof value === "string" && value.trim().length > 0;
39
40
  }) ?? null;
40
41
  return {
41
42
  ready: Boolean(foundKey),
42
- detail: foundKey ? `${label} token detected from ${foundKey}.` : `${label} token is not configured. Set ${keys.join(" or ")}.`,
43
- source: foundKey ? "env" : "missing"
43
+ detail: foundKey ? `${label} token detected from configured Treeseed environment (${foundKey}).` : `${label} token is not configured. Set ${keys.join(" or ")}.`,
44
+ source: foundKey ? "config" : "missing"
44
45
  };
45
46
  }
46
- function parseGitHubAuthStatus() {
47
+ function parseGitHubAuthStatus(values = process.env) {
47
48
  return {
48
- authenticated: envTokenStatus(["GH_TOKEN"], "GitHub").ready,
49
- detail: envTokenStatus(["GH_TOKEN"], "GitHub").detail
49
+ authenticated: envTokenStatus(["GH_TOKEN"], "GitHub", values).ready,
50
+ detail: envTokenStatus(["GH_TOKEN"], "GitHub", values).detail
50
51
  };
51
52
  }
52
- function parseWranglerWhoAmI() {
53
+ function parseWranglerWhoAmI(values = process.env) {
53
54
  return {
54
- authenticated: envTokenStatus(["CLOUDFLARE_API_TOKEN"], "Cloudflare").ready,
55
- detail: envTokenStatus(["CLOUDFLARE_API_TOKEN"], "Cloudflare").detail
55
+ authenticated: envTokenStatus(["CLOUDFLARE_API_TOKEN"], "Cloudflare", values).ready,
56
+ detail: envTokenStatus(["CLOUDFLARE_API_TOKEN"], "Cloudflare", values).detail
56
57
  };
57
58
  }
58
- function parseRailwayWhoAmI() {
59
+ function parseRailwayWhoAmI(values = process.env) {
59
60
  return {
60
- authenticated: envTokenStatus(["RAILWAY_API_TOKEN"], "Railway").ready,
61
- detail: envTokenStatus(["RAILWAY_API_TOKEN"], "Railway").detail
61
+ authenticated: envTokenStatus(["RAILWAY_API_TOKEN", "RAILWAY_TOKEN"], "Railway", values).ready,
62
+ detail: envTokenStatus(["RAILWAY_API_TOKEN", "RAILWAY_TOKEN"], "Railway", values).detail
62
63
  };
63
64
  }
64
- function parseCopilotSessionStatus() {
65
- const status = envTokenStatus(["GH_TOKEN"], "GitHub");
65
+ function parseCopilotSessionStatus(values = process.env) {
66
+ const status = envTokenStatus(["GH_TOKEN"], "GitHub", values);
66
67
  return {
67
68
  configured: status.ready,
68
- detail: status.ready ? "GitHub token detected from GH_TOKEN for Copilot-backed workflows." : "GitHub token is not configured. Set GH_TOKEN for Copilot-backed workflows."
69
+ detail: status.ready ? "GitHub token detected from configured Treeseed environment for Copilot-backed workflows." : "GitHub token is not configured. Set GH_TOKEN for Copilot-backed workflows."
69
70
  };
70
71
  }
71
72
  function collectCliPreflight({ cwd = process.cwd(), requireAuth = false } = {}) {
73
+ const configuredValues = collectTreeseedConfigSeedValues(cwd, "local");
72
74
  const binaries = {
73
75
  git: locateBinary("git"),
74
76
  npm: locateBinary("npm"),
@@ -87,22 +89,22 @@ function collectCliPreflight({ cwd = process.cwd(), requireAuth = false } = {})
87
89
  auth: {}
88
90
  };
89
91
  if (binaries.gh) {
90
- checks.auth.gh = parseGitHubAuthStatus();
92
+ checks.auth.gh = parseGitHubAuthStatus(configuredValues);
91
93
  } else {
92
94
  checks.auth.gh = { authenticated: false, detail: "GitHub CLI is not installed." };
93
95
  }
94
96
  if (binaries.wrangler) {
95
- checks.auth.wrangler = parseWranglerWhoAmI();
97
+ checks.auth.wrangler = parseWranglerWhoAmI(configuredValues);
96
98
  } else {
97
99
  checks.auth.wrangler = { authenticated: false, detail: "Wrangler CLI is not installed." };
98
100
  }
99
101
  if (binaries.railway) {
100
- checks.auth.railway = parseRailwayWhoAmI();
102
+ checks.auth.railway = parseRailwayWhoAmI(configuredValues);
101
103
  } else {
102
104
  checks.auth.railway = { authenticated: false, detail: "Railway CLI is not installed." };
103
105
  }
104
106
  if (binaries.copilot) {
105
- checks.auth.copilot = parseCopilotSessionStatus();
107
+ checks.auth.copilot = parseCopilotSessionStatus(configuredValues);
106
108
  } else {
107
109
  checks.auth.copilot = { configured: false, detail: "Copilot CLI is not installed." };
108
110
  }
@@ -8,12 +8,21 @@ export declare function planWorkspaceVersionChanges(root?: any): {
8
8
  touched: Set<unknown>;
9
9
  };
10
10
  export declare function applyWorkspaceVersionChanges(plan: any): any;
11
- export declare function planWorkspaceReleaseBump(level?: string, root?: any): {
11
+ export declare function planWorkspaceReleaseBump(level?: string, root?: any, options?: {}): {
12
12
  packages: any[];
13
13
  touched: Set<unknown>;
14
14
  versions: Map<any, any>;
15
15
  level: string;
16
+ selected: Set<any>;
16
17
  };
18
+ export declare function collectWorkspaceVersionConsistencyIssues(root?: any): {
19
+ packageName: any;
20
+ dependencyName: string;
21
+ field: string;
22
+ currentSpec: unknown;
23
+ expectedSpec: string;
24
+ }[];
25
+ export declare function assertWorkspaceVersionConsistency(root?: any): void;
17
26
  export declare function repoRoot(cwd?: any): string;
18
27
  export declare function currentBranch(repoDir: any): string;
19
28
  export declare function originRemoteUrl(repoDir: any): string;
@@ -113,17 +113,20 @@ function applyWorkspaceVersionChanges(plan) {
113
113
  }
114
114
  return plan;
115
115
  }
116
- function planWorkspaceReleaseBump(level = "patch", root = workspaceRoot()) {
116
+ function planWorkspaceReleaseBump(level = "patch", root = workspaceRoot(), options = {}) {
117
117
  const packages = workspacePackages(root).map((pkg) => ({
118
118
  ...pkg,
119
119
  packageJsonPath: resolve(pkg.dir, "package.json"),
120
120
  packageJson: readPackageJson(resolve(pkg.dir, "package.json"))
121
121
  }));
122
122
  const publishable = new Set(publishableWorkspacePackages(root).map((pkg) => pkg.name));
123
+ const selected = options.selectedPackageNames ? new Set(
124
+ [...options.selectedPackageNames].map((name) => String(name)).filter((name) => publishable.has(name))
125
+ ) : new Set(publishable);
123
126
  const touched = /* @__PURE__ */ new Set();
124
127
  const versions = /* @__PURE__ */ new Map();
125
128
  for (const pkg of packages) {
126
- if (!publishable.has(pkg.name)) {
129
+ if (!publishable.has(pkg.name) || !selected.has(pkg.name)) {
127
130
  continue;
128
131
  }
129
132
  const nextVersion = incrementVersion(pkg.packageJson.version, level);
@@ -132,6 +135,9 @@ function planWorkspaceReleaseBump(level = "patch", root = workspaceRoot()) {
132
135
  touched.add(pkg.name);
133
136
  }
134
137
  for (const pkg of packages) {
138
+ if (!selected.has(pkg.name)) {
139
+ continue;
140
+ }
135
141
  for (const field of internalDependencyFields(pkg.packageJson)) {
136
142
  for (const depName of Object.keys(pkg.packageJson[field] ?? {})) {
137
143
  if (!versions.has(depName)) {
@@ -146,9 +152,52 @@ function planWorkspaceReleaseBump(level = "patch", root = workspaceRoot()) {
146
152
  packages,
147
153
  touched,
148
154
  versions,
149
- level
155
+ level,
156
+ selected
150
157
  };
151
158
  }
159
+ function collectWorkspaceVersionConsistencyIssues(root = workspaceRoot()) {
160
+ const packages = workspacePackages(root).map((pkg) => ({
161
+ ...pkg,
162
+ packageJson: readPackageJson(resolve(pkg.dir, "package.json"))
163
+ }));
164
+ const versions = new Map(packages.map((pkg) => [pkg.name, pkg.packageJson.version]));
165
+ const issues = [];
166
+ for (const pkg of packages) {
167
+ for (const field of internalDependencyFields(pkg.packageJson)) {
168
+ for (const [depName, currentSpec] of Object.entries(pkg.packageJson[field] ?? {})) {
169
+ if (!versions.has(depName)) {
170
+ continue;
171
+ }
172
+ const expectedSpec = `^${versions.get(depName)}`;
173
+ if (currentSpec !== expectedSpec) {
174
+ issues.push({
175
+ packageName: pkg.name,
176
+ dependencyName: depName,
177
+ field,
178
+ currentSpec,
179
+ expectedSpec
180
+ });
181
+ }
182
+ }
183
+ }
184
+ }
185
+ return issues;
186
+ }
187
+ function assertWorkspaceVersionConsistency(root = workspaceRoot()) {
188
+ const issues = collectWorkspaceVersionConsistencyIssues(root);
189
+ if (issues.length === 0) {
190
+ return;
191
+ }
192
+ const rendered = issues.map((issue) => `${issue.packageName} ${issue.field}.${issue.dependencyName}=${issue.currentSpec} expected ${issue.expectedSpec}`).join("\n");
193
+ throw new Error(
194
+ [
195
+ "Treeseed save found inconsistent checked-out package dependency versions.",
196
+ "Resolve the package manifest drift before saving.",
197
+ rendered
198
+ ].join("\n")
199
+ );
200
+ }
152
201
  function repoRoot(cwd = workspaceRoot()) {
153
202
  return run("git", ["rev-parse", "--show-toplevel"], { cwd, capture: true }).trim();
154
203
  }
@@ -220,7 +269,9 @@ function formatMergeConflictReport(report, repoDir, targetBranch = "main") {
220
269
  export {
221
270
  MERGE_CONFLICT_EXIT_CODE,
222
271
  applyWorkspaceVersionChanges,
272
+ assertWorkspaceVersionConsistency,
223
273
  collectMergeConflictReport,
274
+ collectWorkspaceVersionConsistencyIssues,
224
275
  countConflictMarkers,
225
276
  currentBranch,
226
277
  formatMergeConflictReport,
@@ -1,3 +1,4 @@
1
+ export declare const TREESEED_WORKSPACE_PACKAGE_DIRS: string[];
1
2
  export declare function workspaceRoot(startCwd?: string): any;
2
3
  export declare function findNearestTreeseedRoot(startCwd?: string): string | null;
3
4
  export declare function isWorkspaceRoot(root?: string): any;
@@ -1,7 +1,21 @@
1
1
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { join, relative, resolve } from "node:path";
4
- const TREESEED_WORKSPACE_PACKAGE_DIRS = ["sdk", "core", "cli", "agent", "api"];
4
+ const TREESEED_WORKSPACE_PACKAGE_DIRS = ["sdk", "core", "cli"];
5
+ function packageSortWeight(pkg) {
6
+ const relativeDir = String(pkg.relativeDir ?? "");
7
+ const dirName = relativeDir.split("/").pop() ?? "";
8
+ const index = TREESEED_WORKSPACE_PACKAGE_DIRS.indexOf(dirName);
9
+ return index >= 0 ? index : Number.MAX_SAFE_INTEGER;
10
+ }
11
+ function compareWorkspacePackages(left, right) {
12
+ const leftWeight = packageSortWeight(left);
13
+ const rightWeight = packageSortWeight(right);
14
+ if (leftWeight !== rightWeight) {
15
+ return leftWeight - rightWeight;
16
+ }
17
+ return left.name.localeCompare(right.name);
18
+ }
5
19
  function escapeRegex(source) {
6
20
  return source.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
7
21
  }
@@ -103,7 +117,7 @@ function workspacePackages(root = workspaceRoot()) {
103
117
  });
104
118
  }
105
119
  }
106
- return [...discovered.values()].sort((left, right) => left.name.localeCompare(right.name));
120
+ return [...discovered.values()].sort(compareWorkspacePackages);
107
121
  }
108
122
  function internalDependenciesFor(pkg, packageNames) {
109
123
  const internalDeps = /* @__PURE__ */ new Set();
@@ -127,7 +141,7 @@ function sortWorkspacePackages(packages) {
127
141
  indegree.set(pkg.name, (indegree.get(pkg.name) ?? 0) + 1);
128
142
  }
129
143
  }
130
- const ready = [...packages].filter((pkg) => (indegree.get(pkg.name) ?? 0) === 0).sort((left, right) => left.name.localeCompare(right.name));
144
+ const ready = [...packages].filter((pkg) => (indegree.get(pkg.name) ?? 0) === 0).sort(compareWorkspacePackages);
131
145
  const ordered = [];
132
146
  while (ready.length > 0) {
133
147
  const next = ready.shift();
@@ -142,13 +156,13 @@ function sortWorkspacePackages(packages) {
142
156
  const dependent = packageMap.get(dependentName);
143
157
  if (dependent) {
144
158
  ready.push(dependent);
145
- ready.sort((left, right) => left.name.localeCompare(right.name));
159
+ ready.sort(compareWorkspacePackages);
146
160
  }
147
161
  }
148
162
  }
149
163
  }
150
164
  if (ordered.length !== packages.length) {
151
- return [...packages].sort((left, right) => left.name.localeCompare(right.name));
165
+ return [...packages].sort(compareWorkspacePackages);
152
166
  }
153
167
  return ordered;
154
168
  }
@@ -251,6 +265,7 @@ function cleanupDir(dirPath) {
251
265
  }
252
266
  }
253
267
  export {
268
+ TREESEED_WORKSPACE_PACKAGE_DIRS,
254
269
  changedWorkspacePackages,
255
270
  cleanupDir,
256
271
  createTempDir,