@treeseed/sdk 0.5.2 → 0.6.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 (66) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +46 -0
  3. package/dist/operations/providers/default.js +1 -1
  4. package/dist/operations/services/config-runtime.d.ts +49 -42
  5. package/dist/operations/services/config-runtime.js +465 -142
  6. package/dist/operations/services/deploy.d.ts +298 -0
  7. package/dist/operations/services/deploy.js +381 -137
  8. package/dist/operations/services/git-workflow.d.ts +9 -0
  9. package/dist/operations/services/git-workflow.js +32 -0
  10. package/dist/operations/services/github-api.d.ts +115 -0
  11. package/dist/operations/services/github-api.js +455 -0
  12. package/dist/operations/services/github-automation.d.ts +19 -33
  13. package/dist/operations/services/github-automation.js +44 -131
  14. package/dist/operations/services/key-agent.d.ts +20 -1
  15. package/dist/operations/services/key-agent.js +267 -102
  16. package/dist/operations/services/knowledge-coop-launch.d.ts +2 -3
  17. package/dist/operations/services/knowledge-coop-launch.js +26 -12
  18. package/dist/operations/services/project-platform.d.ts +157 -150
  19. package/dist/operations/services/project-platform.js +129 -26
  20. package/dist/operations/services/railway-api.d.ts +244 -0
  21. package/dist/operations/services/railway-api.js +882 -0
  22. package/dist/operations/services/railway-deploy.d.ts +171 -27
  23. package/dist/operations/services/railway-deploy.js +672 -172
  24. package/dist/operations/services/runtime-tools.d.ts +18 -0
  25. package/dist/operations/services/runtime-tools.js +19 -6
  26. package/dist/operations/services/workspace-preflight.js +2 -2
  27. package/dist/platform/contracts.d.ts +7 -0
  28. package/dist/platform/deploy-config.js +23 -0
  29. package/dist/platform/deploy-runtime.d.ts +1 -0
  30. package/dist/platform/deploy-runtime.js +7 -9
  31. package/dist/platform/env.yaml +10 -9
  32. package/dist/platform/environment.js +4 -0
  33. package/dist/platform/plugin.d.ts +6 -0
  34. package/dist/platform/plugins/constants.d.ts +1 -0
  35. package/dist/platform/plugins/constants.js +1 -0
  36. package/dist/platform/plugins/runtime.d.ts +4 -0
  37. package/dist/platform/plugins/runtime.js +8 -1
  38. package/dist/platform/published-content.js +27 -4
  39. package/dist/platform/tenant/runtime-config.js +33 -24
  40. package/dist/plugin-default.d.ts +1 -0
  41. package/dist/plugin-default.js +1 -0
  42. package/dist/reconcile/builtin-adapters.d.ts +3 -0
  43. package/dist/reconcile/builtin-adapters.js +2093 -0
  44. package/dist/reconcile/contracts.d.ts +155 -0
  45. package/dist/reconcile/contracts.js +0 -0
  46. package/dist/reconcile/desired-state.d.ts +179 -0
  47. package/dist/reconcile/desired-state.js +319 -0
  48. package/dist/reconcile/engine.d.ts +405 -0
  49. package/dist/reconcile/engine.js +356 -0
  50. package/dist/reconcile/errors.d.ts +5 -0
  51. package/dist/reconcile/errors.js +13 -0
  52. package/dist/reconcile/index.d.ts +7 -0
  53. package/dist/reconcile/index.js +7 -0
  54. package/dist/reconcile/registry.d.ts +7 -0
  55. package/dist/reconcile/registry.js +64 -0
  56. package/dist/reconcile/state.d.ts +7 -0
  57. package/dist/reconcile/state.js +303 -0
  58. package/dist/reconcile/units.d.ts +6 -0
  59. package/dist/reconcile/units.js +68 -0
  60. package/dist/scripts/config-treeseed.js +27 -19
  61. package/dist/scripts/tenant-deploy.js +35 -14
  62. package/dist/workflow/operations.js +127 -22
  63. package/dist/workflow-support.d.ts +3 -1
  64. package/dist/workflow-support.js +50 -0
  65. package/dist/workflow.d.ts +2 -0
  66. package/package.json +7 -1
@@ -95,6 +95,7 @@ export declare function loadCliDeployConfig(tenantRoot: any): {
95
95
  research: string;
96
96
  };
97
97
  deploy: string;
98
+ dns: string;
98
99
  content: {
99
100
  runtime: string;
100
101
  publish: string;
@@ -110,6 +111,23 @@ export declare function loadCliDeployConfig(tenantRoot: any): {
110
111
  rootDir: string | undefined;
111
112
  publicBaseUrl: string | undefined;
112
113
  localBaseUrl: string | undefined;
114
+ environments: {
115
+ local: {
116
+ baseUrl: string | undefined;
117
+ domain: string | undefined;
118
+ railwayEnvironment: string | undefined;
119
+ };
120
+ staging: {
121
+ baseUrl: string | undefined;
122
+ domain: string | undefined;
123
+ railwayEnvironment: string | undefined;
124
+ };
125
+ prod: {
126
+ baseUrl: string | undefined;
127
+ domain: string | undefined;
128
+ railwayEnvironment: string | undefined;
129
+ };
130
+ } | undefined;
113
131
  cache: {
114
132
  sourcePages: {
115
133
  browserTtlSeconds: number | undefined;
@@ -45,6 +45,7 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
45
45
  research: "project_graph"
46
46
  },
47
47
  deploy: "cloudflare",
48
+ dns: "cloudflare-dns",
48
49
  content: {
49
50
  runtime: "team_scoped_r2_overlay",
50
51
  publish: "team_scoped_r2_overlay",
@@ -211,6 +212,17 @@ function parseSurfaceConfig(value, label) {
211
212
  rootDir: optionalString(record.rootDir),
212
213
  publicBaseUrl: optionalString(record.publicBaseUrl),
213
214
  localBaseUrl: optionalString(record.localBaseUrl),
215
+ environments: (() => {
216
+ const environments = optionalRecord(record.environments, `${label}.environments`);
217
+ if (!environments) {
218
+ return void 0;
219
+ }
220
+ return {
221
+ local: parseServiceEnvironmentConfig(environments.local),
222
+ staging: parseServiceEnvironmentConfig(environments.staging),
223
+ prod: parseServiceEnvironmentConfig(environments.prod)
224
+ };
225
+ })(),
214
226
  cache: parseWebSurfaceCacheConfig(record.cache, `${label}.cache`)
215
227
  };
216
228
  }
@@ -453,9 +465,9 @@ function parseFallbackDeployConfig(configPath) {
453
465
  "self_hosted_project"
454
466
  ]) ?? "self_hosted_project",
455
467
  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)
468
+ marketBaseUrl: optionalString(process.env.TREESEED_MARKET_API_BASE_URL) ?? optionalString(hosting.marketBaseUrl),
469
+ teamId: optionalString(process.env.TREESEED_HOSTING_TEAM_ID) ?? optionalString(hosting.teamId),
470
+ projectId: optionalString(process.env.TREESEED_PROJECT_ID) ?? optionalString(hosting.projectId)
459
471
  };
460
472
  const services = parseManagedServicesConfig(record.services);
461
473
  const normalizedPlanes = normalizePlanesFromLegacyHosting(parsedHosting);
@@ -471,9 +483,9 @@ function parseFallbackDeployConfig(configPath) {
471
483
  const runtime = {
472
484
  mode: optionalEnum(runtimeRecord.mode, "runtime.mode", ["none", "byo_attached", "treeseed_managed"]) ?? inferredPlanes.runtime.mode,
473
485
  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
486
+ marketBaseUrl: optionalString(process.env.TREESEED_MARKET_API_BASE_URL) ?? optionalString(runtimeRecord.marketBaseUrl) ?? inferredPlanes.runtime.marketBaseUrl,
487
+ teamId: optionalString(process.env.TREESEED_HOSTING_TEAM_ID) ?? optionalString(runtimeRecord.teamId) ?? inferredPlanes.runtime.teamId,
488
+ projectId: optionalString(process.env.TREESEED_PROJECT_ID) ?? optionalString(runtimeRecord.projectId) ?? inferredPlanes.runtime.projectId
477
489
  };
478
490
  const smtp = optionalRecord(record.smtp, "smtp") ?? {};
479
491
  const turnstile = optionalRecord(record.turnstile, "turnstile") ?? {};
@@ -523,6 +535,7 @@ function parseFallbackDeployConfig(configPath) {
523
535
  research: expectString(agentProviders.research ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.agents.research, "providers.agents.research")
524
536
  },
525
537
  deploy: expectString(record.providers?.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy, "providers.deploy"),
538
+ dns: expectString(record.providers?.dns ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.dns, "providers.dns"),
526
539
  content: {
527
540
  runtime: expectString(
528
541
  contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
@@ -58,8 +58,8 @@ function parseWranglerWhoAmI(values = process.env) {
58
58
  }
59
59
  function parseRailwayWhoAmI(values = process.env) {
60
60
  return {
61
- authenticated: envTokenStatus(["RAILWAY_API_TOKEN", "RAILWAY_TOKEN"], "Railway", values).ready,
62
- detail: envTokenStatus(["RAILWAY_API_TOKEN", "RAILWAY_TOKEN"], "Railway", values).detail
61
+ authenticated: envTokenStatus(["RAILWAY_API_TOKEN"], "Railway", values).ready,
62
+ detail: envTokenStatus(["RAILWAY_API_TOKEN"], "Railway", values).detail
63
63
  };
64
64
  }
65
65
  function parseCopilotSessionStatus(values = process.env) {
@@ -122,6 +122,7 @@ export interface TreeseedPlatformSurfaceConfig {
122
122
  rootDir?: string;
123
123
  publicBaseUrl?: string;
124
124
  localBaseUrl?: string;
125
+ environments?: Partial<Record<'local' | 'staging' | 'prod', TreeseedManagedServiceEnvironmentConfig>>;
125
126
  cache?: TreeseedWebSurfaceCacheConfig;
126
127
  }
127
128
  export interface TreeseedWebCachePolicyConfig {
@@ -192,6 +193,11 @@ export interface TreeseedManagedServiceRailwayConfig {
192
193
  rootDir?: string;
193
194
  buildCommand?: string;
194
195
  startCommand?: string;
196
+ healthcheckPath?: string;
197
+ healthcheckTimeoutSeconds?: number;
198
+ healthcheckIntervalSeconds?: number;
199
+ restartPolicy?: string;
200
+ runtimeMode?: string;
195
201
  schedule?: string | string[];
196
202
  }
197
203
  export interface TreeseedManagedServiceConfig {
@@ -224,6 +230,7 @@ export interface TreeseedProviderSelections {
224
230
  research: string;
225
231
  };
226
232
  deploy: string;
233
+ dns?: string;
227
234
  content?: {
228
235
  runtime: string;
229
236
  publish: string;
@@ -295,6 +295,7 @@ function parseProviderSelections(value) {
295
295
  )
296
296
  },
297
297
  deploy: expectString(record.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy, "providers.deploy"),
298
+ dns: expectString(record.dns ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.dns, "providers.dns"),
298
299
  content: {
299
300
  runtime: expectString(
300
301
  contentProviders.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime,
@@ -345,6 +346,17 @@ function parseManagedServiceConfig(value, label) {
345
346
  rootDir: optionalString(railway.rootDir),
346
347
  buildCommand: optionalString(railway.buildCommand),
347
348
  startCommand: optionalString(railway.startCommand),
349
+ healthcheckPath: optionalString(railway.healthcheckPath),
350
+ healthcheckTimeoutSeconds: optionalPositiveNumber(
351
+ railway.healthcheckTimeoutSeconds,
352
+ `${label}.railway.healthcheckTimeoutSeconds`
353
+ ),
354
+ healthcheckIntervalSeconds: optionalPositiveNumber(
355
+ railway.healthcheckIntervalSeconds,
356
+ `${label}.railway.healthcheckIntervalSeconds`
357
+ ),
358
+ restartPolicy: optionalString(railway.restartPolicy),
359
+ runtimeMode: optionalString(railway.runtimeMode),
348
360
  schedule: Array.isArray(railway.schedule) ? railway.schedule.map((entry) => optionalString(entry)).filter(Boolean) : optionalString(railway.schedule)
349
361
  },
350
362
  environments: {
@@ -382,6 +394,17 @@ function parsePlatformSurfaceConfig(value, label) {
382
394
  rootDir: optionalString(record.rootDir),
383
395
  publicBaseUrl: optionalString(record.publicBaseUrl),
384
396
  localBaseUrl: optionalString(record.localBaseUrl),
397
+ environments: (() => {
398
+ const environments = optionalRecord(record.environments, `${label}.environments`);
399
+ if (!environments) {
400
+ return void 0;
401
+ }
402
+ return {
403
+ local: parseServiceEnvironmentConfig(environments.local, `${label}.environments.local`),
404
+ staging: parseServiceEnvironmentConfig(environments.staging, `${label}.environments.staging`),
405
+ prod: parseServiceEnvironmentConfig(environments.prod, `${label}.environments.prod`)
406
+ };
407
+ })(),
385
408
  cache: parseWebSurfaceCacheConfig(record.cache, `${label}.cache`)
386
409
  };
387
410
  }
@@ -12,6 +12,7 @@ export declare function getTreeseedAgentProviderSelections(): {
12
12
  research: string;
13
13
  };
14
14
  export declare function getTreeseedDeployProvider(): string;
15
+ export declare function getTreeseedDnsProvider(): string;
15
16
  export declare function getTreeseedContentRuntimeProvider(): string;
16
17
  export declare function getTreeseedContentPublishProvider(): string;
17
18
  export declare function getTreeseedContentServingMode(): string;
@@ -1,4 +1,3 @@
1
- import { loadTreeseedDeployConfig } from "./deploy-config.js";
2
1
  import { TREESEED_DEFAULT_PLUGIN_REFERENCES, TREESEED_DEFAULT_PROVIDER_SELECTIONS } from "./plugins/constants.js";
3
2
  let cachedDeployConfig = null;
4
3
  function defaultDeployConfig() {
@@ -29,13 +28,8 @@ function getTreeseedDeployConfig() {
29
28
  cachedDeployConfig = __TREESEED_DEPLOY_CONFIG__;
30
29
  return cachedDeployConfig;
31
30
  }
32
- try {
33
- cachedDeployConfig = loadTreeseedDeployConfig();
34
- return cachedDeployConfig;
35
- } catch {
36
- cachedDeployConfig = defaultDeployConfig();
37
- return cachedDeployConfig;
38
- }
31
+ cachedDeployConfig = defaultDeployConfig();
32
+ return cachedDeployConfig;
39
33
  }
40
34
  function resetTreeseedDeployConfigForTests() {
41
35
  cachedDeployConfig = null;
@@ -52,6 +46,9 @@ function getTreeseedAgentProviderSelections() {
52
46
  function getTreeseedDeployProvider() {
53
47
  return getTreeseedDeployConfig().providers?.deploy ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.deploy;
54
48
  }
49
+ function getTreeseedDnsProvider() {
50
+ return getTreeseedDeployConfig().providers?.dns ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.dns;
51
+ }
55
52
  function getTreeseedContentRuntimeProvider() {
56
53
  return getTreeseedDeployConfig().providers?.content?.runtime ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.runtime;
57
54
  }
@@ -59,7 +56,7 @@ function getTreeseedContentPublishProvider() {
59
56
  return getTreeseedDeployConfig().providers?.content?.publish ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.content.publish;
60
57
  }
61
58
  function getTreeseedContentServingMode() {
62
- const override = process.env.TREESEED_CONTENT_SERVING_MODE?.trim();
59
+ const override = globalThis.process?.env?.TREESEED_CONTENT_SERVING_MODE?.trim();
63
60
  if (override === "local_collections" || override === "published_runtime") {
64
61
  return override;
65
62
  }
@@ -84,6 +81,7 @@ export {
84
81
  getTreeseedContentServingMode,
85
82
  getTreeseedDeployConfig,
86
83
  getTreeseedDeployProvider,
84
+ getTreeseedDnsProvider,
87
85
  getTreeseedDocsProvider,
88
86
  getTreeseedFormsProvider,
89
87
  getTreeseedOperationsProvider,
@@ -76,32 +76,33 @@ entries:
76
76
  - process-env
77
77
  relevanceRef: railwayManagedEnabled
78
78
  requiredWhenRef: railwayManagedEnabled
79
- RAILWAY_TOKEN:
80
- label: Railway project token
81
- group: auth
82
- description: Optional Railway token for project-scoped resource management after a project already exists.
83
- howToGet: In Railway, generate a project-scoped token only if you need project resource management separate from the primary API token, then paste it here as RAILWAY_TOKEN.
84
- sensitivity: secret
79
+ TREESEED_RAILWAY_WORKSPACE:
80
+ label: Railway workspace
81
+ group: railway
82
+ description: Railway workspace Treeseed should use when listing or creating projects during bootstrap and config reconciliation.
83
+ howToGet: In Railway, use the workspace slug or name shown in the workspace switcher. Treeseed defaults this repository to knowledge-coop unless you override it here.
84
+ sensitivity: plain
85
85
  targets:
86
86
  - local-runtime
87
- - railway-secret
87
+ - railway-var
88
88
  scopes:
89
89
  - local
90
90
  - staging
91
91
  - prod
92
92
  storage: shared
93
- requirement: optional
93
+ requirement: conditional
94
94
  purposes:
95
95
  - deploy
96
96
  - destroy
97
97
  - config
98
98
  validation:
99
99
  kind: nonempty
100
- minLength: 8
101
100
  sourcePriority:
102
101
  - machine-config
103
102
  - process-env
103
+ defaultValueRef: railwayWorkspaceDefault
104
104
  relevanceRef: railwayManagedEnabled
105
+ requiredWhenRef: railwayManagedEnabled
105
106
  CLOUDFLARE_ACCOUNT_ID:
106
107
  label: Cloudflare account ID
107
108
  group: cloudflare
@@ -180,6 +180,9 @@ function resolveHostedTeamId(context) {
180
180
  function resolveHostedProjectId(context) {
181
181
  return context.deployConfig.slug;
182
182
  }
183
+ function resolveRailwayWorkspaceDefault() {
184
+ return "knowledge-coop";
185
+ }
183
186
  const VALUE_RESOLVERS = {
184
187
  generatedSecret: () => generatedSecret(),
185
188
  localFormsBypassDefault: () => "true",
@@ -202,6 +205,7 @@ const VALUE_RESOLVERS = {
202
205
  marketBaseUrlDefault: (context) => resolveMarketBaseUrl(context),
203
206
  hostingTeamIdDefault: (context) => resolveHostedTeamId(context),
204
207
  hostingProjectIdDefault: (context) => resolveHostedProjectId(context),
208
+ railwayWorkspaceDefault: () => resolveRailwayWorkspaceDefault(),
205
209
  agentPoolMinWorkersDefault: () => "0",
206
210
  agentPoolMaxWorkersDefault: () => "2",
207
211
  agentPoolTargetQueueDepthDefault: () => "1",
@@ -1,6 +1,7 @@
1
1
  import type { TreeseedDeployConfig, TreeseedPlatformLayerDefinition, TreeseedPlatformResourceKind, TreeseedPlatformSurfaceName, TreeseedTenantConfig } from './contracts.ts';
2
2
  import type { TreeseedEnvironmentRegistryOverlay } from './environment.ts';
3
3
  import type { SdkGraphRankingProvider } from '../sdk-types.ts';
4
+ import type { TreeseedReconcileAdapter } from '../reconcile/contracts.ts';
4
5
  export type TreeseedSiteLayerDefinition = TreeseedPlatformLayerDefinition & {
5
6
  kinds?: Array<'pages' | 'styles' | 'components'>;
6
7
  };
@@ -68,6 +69,10 @@ export interface TreeseedPlugin {
68
69
  id?: string;
69
70
  provides?: Record<string, any> & {
70
71
  operations?: string[];
72
+ dns?: string[];
73
+ reconcile?: {
74
+ providers?: string[];
75
+ };
71
76
  };
72
77
  operationProviders?: Record<string, unknown>;
73
78
  siteProviders?: Record<string, TreeseedSiteExtensionContribution | ((context: TreeseedPluginSiteContext) => TreeseedSiteExtensionContribution)>;
@@ -82,6 +87,7 @@ export interface TreeseedPlugin {
82
87
  platformHooks?: Partial<Record<TreeseedPlatformSurfaceName, TreeseedPlatformExtensionContribution>> | ((context: TreeseedPluginPlatformContext) => TreeseedPlatformExtensionContribution | undefined);
83
88
  platformLayers?: TreeseedPlatformLayerContribution[] | ((context: TreeseedPluginPlatformContext) => TreeseedPlatformLayerContribution[] | undefined);
84
89
  environmentRegistry?: TreeseedEnvironmentRegistryOverlay | ((context: TreeseedPluginEnvironmentContext) => TreeseedEnvironmentRegistryOverlay | undefined);
90
+ reconcileAdapters?: Record<string, TreeseedReconcileAdapter | ((context: TreeseedPluginEnvironmentContext) => TreeseedReconcileAdapter | undefined)> | ((context: TreeseedPluginEnvironmentContext) => Record<string, TreeseedReconcileAdapter | ((context: TreeseedPluginEnvironmentContext) => TreeseedReconcileAdapter | undefined)> | undefined);
85
91
  graphRankingProviders?: Record<string, TreeseedGraphRankingProviderContribution>;
86
92
  [key: string]: unknown;
87
93
  }
@@ -11,6 +11,7 @@ export declare const TREESEED_DEFAULT_PROVIDER_SELECTIONS: {
11
11
  research: string;
12
12
  };
13
13
  deploy: string;
14
+ dns: string;
14
15
  content: {
15
16
  runtime: string;
16
17
  publish: string;
@@ -11,6 +11,7 @@ const TREESEED_DEFAULT_PROVIDER_SELECTIONS = {
11
11
  research: "project_graph"
12
12
  },
13
13
  deploy: "cloudflare",
14
+ dns: "cloudflare-dns",
14
15
  content: {
15
16
  runtime: "team_scoped_r2_overlay",
16
17
  publish: "team_scoped_r2_overlay",
@@ -25,12 +25,16 @@ export declare function loadTreeseedPluginRuntime(config?: TreeseedDeployConfig)
25
25
  handlers: Set<string>;
26
26
  };
27
27
  deploy: Set<string>;
28
+ dns: Set<string>;
28
29
  content: {
29
30
  runtime: Set<string>;
30
31
  publish: Set<string>;
31
32
  docs: Set<string>;
32
33
  };
33
34
  site: Set<string>;
35
+ reconcile: {
36
+ providers: Set<string>;
37
+ };
34
38
  };
35
39
  };
36
40
  export declare function resolveTreeseedGraphRankingProvider(plugins: LoadedPluginEntry[], context: Omit<TreeseedPluginEnvironmentContext, 'pluginConfig'>): SdkGraphRankingProvider | null;
@@ -106,12 +106,16 @@ function collectProvidedIds(plugins) {
106
106
  handlers: /* @__PURE__ */ new Set()
107
107
  },
108
108
  deploy: /* @__PURE__ */ new Set(),
109
+ dns: /* @__PURE__ */ new Set(),
109
110
  content: {
110
111
  runtime: /* @__PURE__ */ new Set(),
111
112
  publish: /* @__PURE__ */ new Set(),
112
113
  docs: /* @__PURE__ */ new Set()
113
114
  },
114
- site: /* @__PURE__ */ new Set()
115
+ site: /* @__PURE__ */ new Set(),
116
+ reconcile: {
117
+ providers: /* @__PURE__ */ new Set()
118
+ }
115
119
  };
116
120
  for (const { plugin } of plugins) {
117
121
  for (const id of plugin.provides?.forms ?? []) provided.forms.add(id);
@@ -124,10 +128,12 @@ function collectProvidedIds(plugins) {
124
128
  for (const id of plugin.provides?.agents?.research ?? []) provided.agents.research.add(id);
125
129
  for (const id of plugin.provides?.agents?.handlers ?? []) provided.agents.handlers.add(id);
126
130
  for (const id of plugin.provides?.deploy ?? []) provided.deploy.add(id);
131
+ for (const id of plugin.provides?.dns ?? []) provided.dns.add(id);
127
132
  for (const id of plugin.provides?.content?.runtime ?? []) provided.content.runtime.add(id);
128
133
  for (const id of plugin.provides?.content?.publish ?? []) provided.content.publish.add(id);
129
134
  for (const id of plugin.provides?.content?.docs ?? []) provided.content.docs.add(id);
130
135
  for (const id of plugin.provides?.site ?? []) provided.site.add(id);
136
+ for (const id of plugin.provides?.reconcile?.providers ?? []) provided.reconcile.providers.add(id);
131
137
  }
132
138
  return provided;
133
139
  }
@@ -152,6 +158,7 @@ function loadTreeseedPluginRuntime(config = loadTreeseedDeployConfig()) {
152
158
  assertSelectedProvider(provided.agents.notification, "agents.notification", providers.agents.notification);
153
159
  assertSelectedProvider(provided.agents.research, "agents.research", providers.agents.research);
154
160
  assertSelectedProvider(provided.deploy, "deploy", providers.deploy);
161
+ assertSelectedProvider(provided.dns, "dns", providers.dns ?? TREESEED_DEFAULT_PROVIDER_SELECTIONS.dns);
155
162
  assertSelectedProvider(provided.content.runtime, "content.runtime", providers.content?.runtime);
156
163
  assertSelectedProvider(provided.content.publish, "content.publish", providers.content?.publish);
157
164
  if (providers.content?.docs) {
@@ -1,4 +1,3 @@
1
- import { createHash, createHmac } from "node:crypto";
2
1
  const PUBLISHED_CONTENT_MANIFEST_SCHEMA_VERSION = 2;
3
2
  const EDITORIAL_PREVIEW_COOKIE = "treeseed-content-preview";
4
3
  function isRecord(value) {
@@ -25,8 +24,27 @@ function optionalNumber(value) {
25
24
  function optionalBoolean(value) {
26
25
  return typeof value === "boolean" ? value : void 0;
27
26
  }
27
+ function getNodeCrypto() {
28
+ return globalThis.process?.getBuiltinModule?.("crypto") ?? null;
29
+ }
28
30
  function stableHash(value) {
29
- return createHash("sha256").update(value).digest("hex");
31
+ const crypto = getNodeCrypto();
32
+ if (crypto?.createHash) {
33
+ return crypto.createHash("sha256").update(value).digest("hex");
34
+ }
35
+ let hash = 2166136261;
36
+ for (let index = 0; index < value.length; index += 1) {
37
+ hash ^= value.charCodeAt(index);
38
+ hash = Math.imul(hash, 16777619);
39
+ }
40
+ return (hash >>> 0).toString(16).padStart(8, "0");
41
+ }
42
+ function hmacSha256Base64Url(value, secret) {
43
+ const crypto = getNodeCrypto();
44
+ if (!crypto?.createHmac) {
45
+ throw new Error("Editorial preview token signing requires a crypto runtime.");
46
+ }
47
+ return crypto.createHmac("sha256", secret).update(value).digest("base64url");
30
48
  }
31
49
  function base64UrlEncode(value) {
32
50
  return Buffer.from(value, "utf8").toString("base64url");
@@ -223,7 +241,7 @@ function signEditorialPreviewToken(payload, secret) {
223
241
  expiresAt: expectString(payload.expiresAt, "previewToken.expiresAt")
224
242
  };
225
243
  const encodedPayload = base64UrlEncode(JSON.stringify(normalized));
226
- const signature = createHmac("sha256", secret).update(encodedPayload).digest("base64url");
244
+ const signature = hmacSha256Base64Url(encodedPayload, secret);
227
245
  return `${encodedPayload}.${signature}`;
228
246
  }
229
247
  function verifyEditorialPreviewToken(token, secret) {
@@ -231,7 +249,12 @@ function verifyEditorialPreviewToken(token, secret) {
231
249
  if (!encodedPayload || !signature) {
232
250
  return null;
233
251
  }
234
- const expected = createHmac("sha256", secret).update(encodedPayload).digest("base64url");
252
+ let expected;
253
+ try {
254
+ expected = hmacSha256Base64Url(encodedPayload, secret);
255
+ } catch {
256
+ return null;
257
+ }
235
258
  if (expected !== signature) {
236
259
  return null;
237
260
  }
@@ -1,28 +1,37 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import { loadTreeseedManifest } from "../tenant-config.js";
4
1
  import { parseSiteConfig } from "../utils/site-config-schema.js";
5
2
  const injectedTenantConfig = typeof __TREESEED_TENANT_CONFIG__ !== "undefined" ? __TREESEED_TENANT_CONFIG__ : null;
6
3
  const injectedProjectRoot = typeof __TREESEED_PROJECT_ROOT__ !== "undefined" ? __TREESEED_PROJECT_ROOT__ : null;
7
4
  const injectedSiteConfig = typeof __TREESEED_SITE_CONFIG__ !== "undefined" ? __TREESEED_SITE_CONFIG__ : null;
5
+ function getNodeBuiltin(name) {
6
+ const getBuiltinModule = globalThis.process?.getBuiltinModule;
7
+ return getBuiltinModule?.(name) ?? null;
8
+ }
9
+ function getCwd() {
10
+ const cwd = globalThis.process?.cwd;
11
+ return cwd?.() ?? ".";
12
+ }
13
+ function resolveRuntimePath(projectRoot, path) {
14
+ const pathModule = getNodeBuiltin("path");
15
+ return pathModule?.resolve(projectRoot, path) ?? `${projectRoot.replace(/\/$/, "")}/${path}`;
16
+ }
8
17
  function fallbackTenantConfig(projectRoot) {
9
18
  return {
10
19
  id: "treeseed-runtime",
11
- siteConfigPath: resolve(projectRoot, "treeseed.site.yaml"),
20
+ siteConfigPath: resolveRuntimePath(projectRoot, "treeseed.site.yaml"),
12
21
  content: {
13
- pages: resolve(projectRoot, "src/content/pages"),
14
- notes: resolve(projectRoot, "src/content/notes"),
15
- questions: resolve(projectRoot, "src/content/questions"),
16
- objectives: resolve(projectRoot, "src/content/objectives"),
17
- proposals: resolve(projectRoot, "src/content/proposals"),
18
- decisions: resolve(projectRoot, "src/content/decisions"),
19
- people: resolve(projectRoot, "src/content/people"),
20
- agents: resolve(projectRoot, "src/content/agents"),
21
- books: resolve(projectRoot, "src/content/books"),
22
- docs: resolve(projectRoot, "src/content/knowledge"),
23
- templates: resolve(projectRoot, "src/content/templates"),
24
- knowledge_packs: resolve(projectRoot, "src/content/knowledge-packs"),
25
- workdays: resolve(projectRoot, "src/content/workdays")
22
+ pages: resolveRuntimePath(projectRoot, "src/content/pages"),
23
+ notes: resolveRuntimePath(projectRoot, "src/content/notes"),
24
+ questions: resolveRuntimePath(projectRoot, "src/content/questions"),
25
+ objectives: resolveRuntimePath(projectRoot, "src/content/objectives"),
26
+ proposals: resolveRuntimePath(projectRoot, "src/content/proposals"),
27
+ decisions: resolveRuntimePath(projectRoot, "src/content/decisions"),
28
+ people: resolveRuntimePath(projectRoot, "src/content/people"),
29
+ agents: resolveRuntimePath(projectRoot, "src/content/agents"),
30
+ books: resolveRuntimePath(projectRoot, "src/content/books"),
31
+ docs: resolveRuntimePath(projectRoot, "src/content/knowledge"),
32
+ templates: resolveRuntimePath(projectRoot, "src/content/templates"),
33
+ knowledge_packs: resolveRuntimePath(projectRoot, "src/content/knowledge-packs"),
34
+ workdays: resolveRuntimePath(projectRoot, "src/content/workdays")
26
35
  },
27
36
  features: {
28
37
  docs: true,
@@ -35,20 +44,20 @@ function fallbackTenantConfig(projectRoot) {
35
44
  }
36
45
  };
37
46
  }
38
- const RUNTIME_PROJECT_ROOT = injectedProjectRoot ?? process.cwd();
47
+ const RUNTIME_PROJECT_ROOT = injectedProjectRoot ?? getCwd();
39
48
  const RUNTIME_TENANT = (() => {
40
49
  if (injectedTenantConfig) {
41
50
  return injectedTenantConfig;
42
51
  }
43
- try {
44
- return loadTreeseedManifest();
45
- } catch {
46
- return fallbackTenantConfig(RUNTIME_PROJECT_ROOT);
47
- }
52
+ return fallbackTenantConfig(RUNTIME_PROJECT_ROOT);
48
53
  })();
49
54
  const RUNTIME_SITE_CONFIG = injectedSiteConfig ?? (() => {
55
+ const fs = getNodeBuiltin("fs");
56
+ if (!fs) {
57
+ return null;
58
+ }
50
59
  try {
51
- return parseSiteConfig(readFileSync(RUNTIME_TENANT.siteConfigPath, "utf8"));
60
+ return parseSiteConfig(fs.readFileSync(RUNTIME_TENANT.siteConfigPath, "utf8"));
52
61
  } catch {
53
62
  return null;
54
63
  }
@@ -13,6 +13,7 @@ declare const _default: {
13
13
  handlers: string[];
14
14
  };
15
15
  deploy: string[];
16
+ dns: string[];
16
17
  content: {
17
18
  runtime: string[];
18
19
  publish: string[];
@@ -23,6 +23,7 @@ var plugin_default_default = defineTreeseedPlugin({
23
23
  ]
24
24
  },
25
25
  deploy: ["cloudflare"],
26
+ dns: ["cloudflare-dns"],
26
27
  content: {
27
28
  runtime: ["filesystem", "team_scoped_r2_overlay"],
28
29
  publish: ["filesystem", "team_scoped_r2_overlay"],
@@ -0,0 +1,3 @@
1
+ import type { TreeseedReconcileAdapter } from './contracts.ts';
2
+ export declare function createCloudflareReconcileAdapters(): TreeseedReconcileAdapter[];
3
+ export declare function createRailwayReconcileAdapters(): TreeseedReconcileAdapter[];