@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
@@ -1,6 +1,16 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { getTreeseedMachineConfigPaths, resolveTreeseedRemoteSession } from "./operations/services/config-runtime.js";
3
+ import {
4
+ getTreeseedMachineConfigPaths,
5
+ inspectTreeseedKeyAgentStatus,
6
+ loadTreeseedMachineConfig,
7
+ resolveTreeseedMachineEnvironmentValues,
8
+ resolveTreeseedRemoteSession,
9
+ collectTreeseedEnvironmentContext,
10
+ withTreeseedKeyAgentAutopromptDisabled
11
+ } from "./operations/services/config-runtime.js";
12
+ import { validateTreeseedEnvironmentValues } from "./platform/environment.js";
13
+ import { resolveTreeseedWebCachePolicy } from "./platform/deploy-config.js";
4
14
  import {
5
15
  createBranchPreviewDeployTarget,
6
16
  createPersistentDeployTarget,
@@ -8,39 +18,42 @@ import {
8
18
  } from "./operations/services/deploy.js";
9
19
  import { loadCliDeployConfig } from "./operations/services/runtime-tools.js";
10
20
  import { collectCliPreflight } from "./operations/services/workspace-preflight.js";
11
- import { gitStatusPorcelain } from "./operations/services/workspace-save.js";
12
- import { isWorkspaceRoot } from "./operations/services/workspace-tools.js";
21
+ import { currentBranch, gitStatusPorcelain } from "./operations/services/workspace-save.js";
22
+ import { hasCompleteTreeseedPackageCheckout, isWorkspaceRoot, run, workspacePackages } from "./operations/services/workspace-tools.js";
23
+ import { inspectWorkflowLock, listInterruptedWorkflowRuns } from "./workflow/runs.js";
13
24
  import {
14
25
  resolveTreeseedWorkflowPaths,
15
26
  workflowEnvironmentForBranchRole
16
27
  } from "./workflow/policy.js";
17
28
  function emptyPersistentEnvironments() {
18
29
  return {
19
- local: { initialized: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null },
20
- staging: { initialized: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null },
21
- prod: { initialized: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null }
30
+ local: { initialized: false, phase: "pending", configured: false, provisioned: false, deployable: false, blockers: [], warnings: [], lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null },
31
+ staging: { initialized: false, phase: "pending", configured: false, provisioned: false, deployable: false, blockers: [], warnings: [], lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null },
32
+ prod: { initialized: false, phase: "pending", configured: false, provisioned: false, deployable: false, blockers: [], warnings: [], lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null }
22
33
  };
23
34
  }
24
35
  function readinessForEnvironment(state, scope) {
25
- const blockers = [];
26
- const warnings = [];
36
+ const blockers = [...state.persistentEnvironments[scope].blockers];
37
+ const warnings = [...state.persistentEnvironments[scope].warnings];
27
38
  if (!state.deployConfigPresent) {
28
39
  blockers.push("Missing treeseed.site.yaml.");
29
40
  }
30
41
  if (!state.files.machineConfig) {
31
42
  blockers.push("Missing Treeseed machine config.");
32
43
  }
33
- if (scope === "local") {
34
- if (!state.files.envLocal) {
35
- blockers.push("Missing .env.local.");
36
- }
37
- if (!state.files.devVars) {
38
- warnings.push("Missing .dev.vars.");
39
- }
40
- } else {
44
+ if (!state.secrets.wrappedKeyPresent) {
45
+ blockers.push("Missing wrapped Treeseed machine key.");
46
+ }
47
+ if (state.secrets.migrationRequired) {
48
+ blockers.push("Treeseed machine key migration is still required.");
49
+ }
50
+ if (scope !== "local") {
41
51
  if (!state.persistentEnvironments[scope].initialized) {
42
52
  blockers.push(`Environment ${scope} is not initialized.`);
43
53
  }
54
+ if (state.persistentEnvironments[scope].configured && !state.persistentEnvironments[scope].provisioned) {
55
+ warnings.push(`Environment ${scope} is configured but foundational infrastructure has not been provisioned yet.`);
56
+ }
44
57
  }
45
58
  return {
46
59
  ready: blockers.length === 0,
@@ -48,6 +61,28 @@ function readinessForEnvironment(state, scope) {
48
61
  warnings
49
62
  };
50
63
  }
64
+ function safeResolveRemoteSession(cwd, hostId) {
65
+ try {
66
+ return withTreeseedKeyAgentAutopromptDisabled(() => resolveTreeseedRemoteSession(cwd, hostId ?? void 0));
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+ function safeResolveMachineEnvironmentValues(cwd, scope) {
72
+ try {
73
+ return withTreeseedKeyAgentAutopromptDisabled(() => resolveTreeseedMachineEnvironmentValues(cwd, scope));
74
+ } catch {
75
+ return {};
76
+ }
77
+ }
78
+ function knownRemoteTrackingBranchExists(repoDir, branchName) {
79
+ try {
80
+ run("git", ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], { cwd: repoDir, capture: true });
81
+ return true;
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
51
86
  function resolveTreeseedWorkflowState(cwd) {
52
87
  const resolved = resolveTreeseedWorkflowPaths(cwd);
53
88
  const effectiveCwd = resolved.cwd;
@@ -58,8 +93,72 @@ function resolveTreeseedWorkflowState(cwd) {
58
93
  const branchName = resolved.branchName;
59
94
  const branchRole = resolved.branchRole;
60
95
  const dirtyWorktree = root ? gitStatusPorcelain(root).length > 0 : false;
96
+ const completePackageCheckout = hasCompleteTreeseedPackageCheckout(effectiveCwd);
97
+ const packageSyncRepos = completePackageCheckout ? workspacePackages(effectiveCwd).filter((pkg) => pkg.name?.startsWith("@treeseed/")).map((pkg) => {
98
+ const repoBranch = currentBranch(pkg.dir) || null;
99
+ const dirty = gitStatusPorcelain(pkg.dir).length > 0;
100
+ const expectedBranch = branchName;
101
+ let localBranch = false;
102
+ if (expectedBranch) {
103
+ if (repoBranch === expectedBranch) {
104
+ localBranch = true;
105
+ } else {
106
+ try {
107
+ run("git", ["show-ref", "--verify", "--quiet", `refs/heads/${expectedBranch}`], { cwd: pkg.dir, capture: true });
108
+ localBranch = true;
109
+ } catch {
110
+ localBranch = false;
111
+ }
112
+ }
113
+ }
114
+ const remoteBranch = Boolean(expectedBranch) ? knownRemoteTrackingBranchExists(pkg.dir, expectedBranch) : false;
115
+ return {
116
+ name: pkg.name,
117
+ path: pkg.relativeDir,
118
+ branchName: repoBranch,
119
+ dirty,
120
+ aligned: expectedBranch ? repoBranch === expectedBranch : true,
121
+ localBranch,
122
+ remoteBranch
123
+ };
124
+ }) : [];
125
+ const packageSyncBlockers = [];
126
+ for (const repo of packageSyncRepos) {
127
+ if (repo.dirty) {
128
+ packageSyncBlockers.push(`${repo.name} has uncommitted changes.`);
129
+ }
130
+ if (branchName && !repo.localBranch && !repo.remoteBranch) {
131
+ packageSyncBlockers.push(`${repo.name} is missing branch ${branchName}.`);
132
+ continue;
133
+ }
134
+ if (branchName && !repo.aligned) {
135
+ packageSyncBlockers.push(`${repo.name} is on ${repo.branchName ?? "(detached)"} instead of ${branchName}.`);
136
+ }
137
+ }
61
138
  const preflight = collectCliPreflight({ cwd: effectiveCwd, requireAuth: false });
62
139
  const { configPath, keyPath } = getTreeseedMachineConfigPaths(effectiveCwd);
140
+ const machineConfig = existsSync(configPath) ? loadTreeseedMachineConfig(effectiveCwd) : null;
141
+ const keyStatus = inspectTreeseedKeyAgentStatus(effectiveCwd);
142
+ const marketSettings = machineConfig?.settings?.market && typeof machineConfig.settings.market === "object" ? machineConfig.settings.market : null;
143
+ const runnerHostId = typeof marketSettings?.runnerHostId === "string" && marketSettings.runnerHostId.trim() ? marketSettings.runnerHostId.trim() : typeof marketSettings?.projectId === "string" && marketSettings.projectId.trim() ? `market-runner:${marketSettings.projectId.trim()}` : null;
144
+ const runnerSession = runnerHostId ? safeResolveRemoteSession(effectiveCwd, runnerHostId) : null;
145
+ const workflowLock = inspectWorkflowLock(effectiveCwd);
146
+ const interruptedRuns = listInterruptedWorkflowRuns(effectiveCwd).map((journal) => ({
147
+ runId: journal.runId,
148
+ command: journal.command,
149
+ updatedAt: journal.updatedAt,
150
+ nextStep: journal.steps.find((step) => step.status === "pending")?.description ?? null
151
+ }));
152
+ const workflowBlockers = [];
153
+ if (workflowLock.active && workflowLock.lock) {
154
+ workflowBlockers.push(`Workflow lock active for ${workflowLock.lock.command} (${workflowLock.lock.runId}).`);
155
+ }
156
+ if (workflowLock.stale && workflowLock.lock) {
157
+ workflowBlockers.push(`Workflow lock is stale: ${workflowLock.staleReason}.`);
158
+ }
159
+ if (interruptedRuns.length > 0) {
160
+ workflowBlockers.push(`Interrupted workflow runs detected: ${interruptedRuns.map((run2) => run2.runId).join(", ")}.`);
161
+ }
63
162
  const state = {
64
163
  cwd: effectiveCwd,
65
164
  workspaceRoot,
@@ -70,34 +169,101 @@ function resolveTreeseedWorkflowState(cwd) {
70
169
  branchRole,
71
170
  environment: workflowEnvironmentForBranchRole(branchRole),
72
171
  dirtyWorktree,
172
+ workflowControl: {
173
+ lock: {
174
+ active: workflowLock.active,
175
+ stale: workflowLock.stale,
176
+ runId: workflowLock.lock?.runId ?? null,
177
+ command: workflowLock.lock?.command ?? null,
178
+ updatedAt: workflowLock.lock?.updatedAt ?? null,
179
+ staleReason: workflowLock.staleReason
180
+ },
181
+ interruptedRuns,
182
+ blockers: workflowBlockers
183
+ },
184
+ packageSync: {
185
+ mode: completePackageCheckout ? "recursive-workspace" : "root-only",
186
+ completeCheckout: completePackageCheckout,
187
+ expectedBranch: branchName,
188
+ aligned: packageSyncRepos.every((repo) => repo.aligned),
189
+ dirty: packageSyncRepos.some((repo) => repo.dirty),
190
+ repos: packageSyncRepos,
191
+ blockers: packageSyncBlockers
192
+ },
73
193
  preview: {
74
194
  enabled: false,
75
195
  url: null,
76
196
  lastDeploymentTimestamp: null
77
197
  },
198
+ webCache: {
199
+ webHost: null,
200
+ contentHost: null,
201
+ sourcePagePolicy: null,
202
+ contentPagePolicy: null,
203
+ r2ObjectPolicy: null,
204
+ cloudflareRulesManaged: false,
205
+ lastDeployPurgeAt: null,
206
+ lastDeployPurgeCount: null,
207
+ lastContentPurgeAt: null,
208
+ lastContentPurgeCount: null
209
+ },
78
210
  persistentEnvironments: emptyPersistentEnvironments(),
79
211
  auth: {
80
212
  gh: preflight.checks.auth.gh?.authenticated === true,
81
213
  wrangler: preflight.checks.auth.wrangler?.authenticated === true,
82
214
  railway: preflight.checks.auth.railway?.authenticated === true,
83
215
  copilot: preflight.checks.auth.copilot?.configured === true,
84
- remoteApi: Boolean(resolveTreeseedRemoteSession(cwd))
216
+ remoteApi: Boolean(safeResolveRemoteSession(cwd))
217
+ },
218
+ marketConnection: {
219
+ configured: Boolean(marketSettings?.baseUrl && marketSettings?.projectId),
220
+ baseUrl: typeof marketSettings?.baseUrl === "string" ? marketSettings.baseUrl : null,
221
+ hostId: typeof marketSettings?.hostId === "string" ? marketSettings.hostId : null,
222
+ teamId: typeof marketSettings?.teamId === "string" ? marketSettings.teamId : null,
223
+ teamSlug: typeof marketSettings?.teamSlug === "string" ? marketSettings.teamSlug : null,
224
+ projectId: typeof marketSettings?.projectId === "string" ? marketSettings.projectId : null,
225
+ projectSlug: typeof marketSettings?.projectSlug === "string" ? marketSettings.projectSlug : null,
226
+ connectionMode: typeof marketSettings?.connectionMode === "string" ? marketSettings.connectionMode : null,
227
+ projectApiBaseUrl: typeof marketSettings?.projectApiBaseUrl === "string" ? marketSettings.projectApiBaseUrl : null,
228
+ hubMode: null,
229
+ runtimeMode: null,
230
+ runtimeRegistration: null,
231
+ runtimeAttached: false,
232
+ runtimeReady: true,
233
+ runnerHostId,
234
+ runnerReady: Boolean(
235
+ marketSettings?.runnerReady === true || typeof runnerSession?.accessToken === "string" && runnerSession.accessToken.length > 0
236
+ ),
237
+ runnerRegisteredAt: typeof marketSettings?.runnerRegisteredAt === "string" ? marketSettings.runnerRegisteredAt : null,
238
+ runnerLastSeenAt: typeof marketSettings?.runnerLastSeenAt === "string" ? marketSettings.runnerLastSeenAt : null,
239
+ launchPhase: typeof marketSettings?.launchPhase === "string" ? marketSettings.launchPhase : null,
240
+ lastSuccessfulPhase: typeof marketSettings?.lastSuccessfulPhase === "string" ? marketSettings.lastSuccessfulPhase : null,
241
+ githubRepository: typeof marketSettings?.githubRepository === "string" ? marketSettings.githubRepository : null,
242
+ workflowBootstrapReady: marketSettings?.workflowBootstrapReady === true,
243
+ currentWorkstreamId: branchRole === "feature" ? branchName : null,
244
+ verificationPosture: typeof marketSettings?.launchPhase === "string" && marketSettings.launchPhase === "failed" ? "blocked" : "pending",
245
+ approvalBlockers: Array.isArray(marketSettings?.approvalBlockers) ? marketSettings.approvalBlockers.map(String) : []
85
246
  },
86
247
  managedServices: {
87
248
  api: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
88
- agents: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
89
249
  manager: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
90
250
  worker: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
91
- runner: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
92
251
  workdayStart: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null },
93
252
  workdayReport: { enabled: false, initialized: false, lastDeploymentTimestamp: null, lastDeployedUrl: null, provider: null }
94
253
  },
95
254
  files: {
96
255
  treeseedConfig: tenantRoot,
97
256
  machineConfig: existsSync(configPath),
98
- machineKey: existsSync(keyPath),
99
- envLocal: existsSync(resolve(cwd, ".env.local")),
100
- devVars: existsSync(resolve(cwd, ".dev.vars"))
257
+ machineKey: existsSync(keyPath)
258
+ },
259
+ secrets: {
260
+ keyAgentRunning: keyStatus.running,
261
+ keyAgentUnlocked: keyStatus.unlocked,
262
+ wrappedKeyPresent: keyStatus.wrappedKeyPresent,
263
+ migrationRequired: keyStatus.migrationRequired,
264
+ idleTimeoutMs: keyStatus.idleTimeoutMs,
265
+ idleRemainingMs: keyStatus.idleRemainingMs,
266
+ startupPassphraseConfigured: Boolean(process.env.TREESEED_KEY_PASSPHRASE?.trim())
101
267
  },
102
268
  releaseReady: branchRole === "staging" && !dirtyWorktree,
103
269
  readiness: {
@@ -111,10 +277,59 @@ function resolveTreeseedWorkflowState(cwd) {
111
277
  if (tenantRoot) {
112
278
  try {
113
279
  const deployConfig = loadCliDeployConfig(effectiveCwd);
280
+ const environmentContext = collectTreeseedEnvironmentContext(effectiveCwd);
281
+ const sharedConfigValues = safeResolveMachineEnvironmentValues(effectiveCwd, "prod");
282
+ const runtimeMode = deployConfig.runtime?.mode ?? "none";
283
+ const runtimeRegistration = deployConfig.runtime?.registration ?? "none";
284
+ const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
285
+ const registrationRequired2 = runtimeRegistration === "required";
286
+ const registrationEnabled = runtimeRegistration === "required" || runtimeRegistration === "optional";
287
+ const runtimeSessionReady = Boolean(
288
+ marketSettings?.runnerReady === true || typeof runnerSession?.accessToken === "string" && runnerSession.accessToken.length > 0
289
+ );
290
+ state.marketConnection.baseUrl = state.marketConnection.baseUrl ?? sharedConfigValues.TREESEED_MARKET_API_BASE_URL ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? null;
291
+ state.marketConnection.teamId = state.marketConnection.teamId ?? sharedConfigValues.TREESEED_HOSTING_TEAM_ID ?? deployConfig.runtime?.teamId ?? deployConfig.hosting?.teamId ?? null;
292
+ state.marketConnection.projectId = state.marketConnection.projectId ?? sharedConfigValues.TREESEED_PROJECT_ID ?? deployConfig.runtime?.projectId ?? deployConfig.hosting?.projectId ?? null;
293
+ state.marketConnection.hubMode = deployConfig.hub?.mode ?? null;
294
+ state.marketConnection.runtimeMode = runtimeMode;
295
+ state.marketConnection.runtimeRegistration = runtimeRegistration;
296
+ state.marketConnection.runtimeAttached = runtimeMode !== "none" && (!registrationEnabled || state.marketConnection.configured);
297
+ state.marketConnection.runtimeReady = runtimeMode === "none" || !registrationEnabled || runtimeSessionReady;
298
+ state.marketConnection.runnerReady = runtimeSessionReady;
299
+ state.webCache.webHost = deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl ?? null;
300
+ state.webCache.contentHost = sharedConfigValues.TREESEED_CONTENT_PUBLIC_BASE_URL ?? deployConfig.cloudflare.r2?.publicBaseUrl ?? null;
301
+ state.webCache.sourcePagePolicy = `browser=${webCachePolicy.sourcePages.browserTtlSeconds}s edge=${webCachePolicy.sourcePages.edgeTtlSeconds}s swr=${webCachePolicy.sourcePages.staleWhileRevalidateSeconds}s sie=${webCachePolicy.sourcePages.staleIfErrorSeconds}s`;
302
+ state.webCache.contentPagePolicy = `browser=${webCachePolicy.contentPages.browserTtlSeconds}s edge=${webCachePolicy.contentPages.edgeTtlSeconds}s swr=${webCachePolicy.contentPages.staleWhileRevalidateSeconds}s sie=${webCachePolicy.contentPages.staleIfErrorSeconds}s`;
303
+ state.webCache.r2ObjectPolicy = `browser=${webCachePolicy.r2PublishedObjects.browserTtlSeconds}s edge=${webCachePolicy.r2PublishedObjects.edgeTtlSeconds}s swr=${webCachePolicy.r2PublishedObjects.staleWhileRevalidateSeconds}s sie=${webCachePolicy.r2PublishedObjects.staleIfErrorSeconds}s`;
304
+ state.marketConnection.configured = registrationRequired2 ? Boolean(state.marketConnection.baseUrl && state.marketConnection.projectId) : state.marketConnection.configured;
114
305
  for (const scope of ["local", "staging", "prod"]) {
115
306
  const deployState = loadDeployState(effectiveCwd, deployConfig, { target: createPersistentDeployTarget(scope) });
307
+ const validation = validateTreeseedEnvironmentValues({
308
+ values: safeResolveMachineEnvironmentValues(effectiveCwd, scope),
309
+ scope,
310
+ purpose: "config",
311
+ deployConfig: environmentContext.context.deployConfig,
312
+ tenantConfig: environmentContext.context.tenantConfig,
313
+ plugins: environmentContext.context.plugins
314
+ });
315
+ const validationProblems = [...validation.missing, ...validation.invalid].map((problem) => problem.message);
316
+ const persistentBlockers = Array.isArray(deployState.readiness?.blockers) ? deployState.readiness.blockers.map(String) : [];
317
+ const persistentWarnings = Array.isArray(deployState.readiness?.warnings) ? deployState.readiness.warnings.map(String) : [];
318
+ const configured = scope === "local" ? validation.ok : deployState.readiness?.configured === true && validation.ok;
319
+ const provisioned = scope === "local" ? true : configured && deployState.readiness?.provisioned === true;
320
+ const deployable = scope === "local" ? validation.ok : configured && deployState.readiness?.deployable === true;
321
+ const initialized = deployState.readiness?.initialized === true || scope === "local";
116
322
  state.persistentEnvironments[scope] = {
117
- initialized: deployState.readiness?.initialized === true || scope === "local",
323
+ initialized,
324
+ phase: validation.ok ? scope === "local" ? "code_ready" : provisioned ? "provisioned" : configured ? "config_complete" : initialized ? "config_complete" : "pending" : "config_incomplete",
325
+ configured,
326
+ provisioned,
327
+ deployable,
328
+ blockers: [
329
+ ...validationProblems,
330
+ ...persistentBlockers
331
+ ],
332
+ warnings: persistentWarnings,
118
333
  lastValidatedAt: deployState.readiness?.lastValidatedAt ?? deployState.readiness?.initializedAt ?? null,
119
334
  lastDeploymentTimestamp: deployState.lastDeploymentTimestamp ?? null,
120
335
  lastDeployedUrl: deployState.lastDeployedUrl ?? null
@@ -129,7 +344,14 @@ function resolveTreeseedWorkflowState(cwd) {
129
344
  url: typeof latestHistory?.url === "string" ? latestHistory.url : deployState.lastDeployedUrl ?? null
130
345
  });
131
346
  }
132
- for (const serviceKey of ["api", "agents", "manager", "worker", "runner", "workdayStart", "workdayReport"]) {
347
+ if (scope === "prod") {
348
+ state.webCache.cloudflareRulesManaged = deployState.webCache?.rulesManaged === true;
349
+ state.webCache.lastDeployPurgeAt = deployState.webCache?.deployPurge?.lastPurgedAt ?? null;
350
+ state.webCache.lastDeployPurgeCount = deployState.webCache?.deployPurge?.purgeCount ?? null;
351
+ state.webCache.lastContentPurgeAt = deployState.webCache?.contentPurge?.lastPurgedAt ?? null;
352
+ state.webCache.lastContentPurgeCount = deployState.webCache?.contentPurge?.purgeCount ?? null;
353
+ }
354
+ for (const serviceKey of ["api", "manager", "worker", "workdayStart", "workdayReport"]) {
133
355
  const service = deployState.services?.[serviceKey];
134
356
  if (!service) continue;
135
357
  state.managedServices[serviceKey] = {
@@ -155,6 +377,12 @@ function resolveTreeseedWorkflowState(cwd) {
155
377
  state.readiness.local = readinessForEnvironment(state, "local");
156
378
  state.readiness.staging = readinessForEnvironment(state, "staging");
157
379
  state.readiness.prod = readinessForEnvironment(state, "prod");
380
+ state.marketConnection.verificationPosture = state.readiness.local.ready ? "ready" : state.files.machineConfig ? "blocked" : "pending";
381
+ const registrationRequired = state.marketConnection.runtimeRegistration === "required";
382
+ state.marketConnection.approvalBlockers = [
383
+ ...registrationRequired && !state.marketConnection.configured ? ["Knowledge Coop runtime attachment is not configured."] : [],
384
+ ...registrationRequired && !state.marketConnection.runtimeReady ? ["Knowledge Coop runtime credential is missing or not ready."] : []
385
+ ];
158
386
  state.recommendations = recommendTreeseedNextSteps(state);
159
387
  return state;
160
388
  }
@@ -168,10 +396,37 @@ function recommendTreeseedNextSteps(state) {
168
396
  }
169
397
  if (!state.files.machineConfig) {
170
398
  recommendations.push({ operation: "status", reason: "Validate tooling, auth, and repository readiness first." });
171
- recommendations.push({ operation: "config", reason: "Bootstrap the local machine config and local environment files." });
399
+ recommendations.push({ operation: "config", reason: "Bootstrap the local machine config and injected runtime environment." });
400
+ return recommendations;
401
+ }
402
+ if (!state.secrets.wrappedKeyPresent || state.secrets.migrationRequired) {
403
+ recommendations.push({
404
+ operation: state.secrets.migrationRequired ? "secrets:migrate-key" : "secrets:unlock",
405
+ reason: state.secrets.migrationRequired ? "Wrap the local machine key before running secret-backed commands." : "Create and unlock the local wrapped machine key before running secret-backed commands."
406
+ });
172
407
  return recommendations;
173
408
  }
409
+ if (state.workflowControl.interruptedRuns.length > 0) {
410
+ recommendations.push({
411
+ operation: "resume",
412
+ reason: "Resume the most recent interrupted workflow run before making new branch changes.",
413
+ input: { runId: state.workflowControl.interruptedRuns[0].runId }
414
+ });
415
+ recommendations.push({ operation: "recover", reason: "Inspect active workflow locks and interrupted runs." });
416
+ return recommendations.slice(0, 3);
417
+ }
418
+ if (state.workflowControl.lock.active && state.workflowControl.lock.runId) {
419
+ recommendations.push({ operation: "recover", reason: "Inspect the active workflow lock before starting another mutating command." });
420
+ return recommendations.slice(0, 3);
421
+ }
174
422
  if (state.branchRole === "feature") {
423
+ if (state.packageSync.mode === "recursive-workspace" && state.packageSync.blockers.length > 0 && state.branchName) {
424
+ recommendations.push({
425
+ operation: "switch",
426
+ reason: "Realign the checked-out package repos to the current task branch before continuing.",
427
+ input: { branch: state.branchName }
428
+ });
429
+ }
175
430
  recommendations.push({ operation: "stage", reason: "Merge this task branch into staging and clean up branch artifacts.", input: { message: "describe the resolution" } });
176
431
  recommendations.push({ operation: "save", reason: "Persist, verify, and push the current task branch before or independently of staging it.", input: { message: "describe your change" } });
177
432
  if (state.preview.enabled && state.branchName) {
@@ -183,11 +438,18 @@ function recommendTreeseedNextSteps(state) {
183
438
  return recommendations.slice(0, 3);
184
439
  }
185
440
  if (state.branchRole === "staging") {
441
+ if (state.packageSync.mode === "recursive-workspace" && state.packageSync.blockers.length > 0 && state.branchName) {
442
+ recommendations.push({
443
+ operation: "switch",
444
+ reason: "Realign the checked-out package repos to staging before releasing.",
445
+ input: { branch: state.branchName }
446
+ });
447
+ }
186
448
  if (!state.persistentEnvironments.staging.initialized) {
187
449
  recommendations.push({ operation: "config", reason: "Initialize the staging environment before releasing.", input: { environment: ["staging"] } });
188
450
  } else {
189
451
  recommendations.push({ operation: "release", reason: "Promote staging into main when the integration branch is ready for production.", input: { bump: "patch" } });
190
- if (state.managedServices.api.enabled || state.managedServices.agents.enabled) {
452
+ if (state.managedServices.api.enabled) {
191
453
  recommendations.push({ operation: "auth:login", reason: "Keep the local runtime authenticated to the remote API used by managed services." });
192
454
  }
193
455
  }
@@ -1,4 +1,4 @@
1
- export { applyTreeseedConfigValues, applyTreeseedEnvironmentToProcess, applyTreeseedSafeRepairs, assertTreeseedCommandEnvironment, checkTreeseedProviderConnections, clearTreeseedRemoteSession, collectTreeseedConfigContext, collectTreeseedPrintEnvReport, createDefaultTreeseedMachineConfig, ensureTreeseedGitignoreEntries, getTreeseedMachineConfigPaths, loadTreeseedMachineConfig, listRelevantTreeseedConfigEntries, finalizeTreeseedConfig, resolveTreeseedMachineEnvironmentValues, resolveTreeseedRemoteConfig, resolveTreeseedRemoteSession, rotateTreeseedMachineKey, setTreeseedRemoteSession, writeTreeseedLocalEnvironmentFiles, writeTreeseedMachineConfig, } from './operations/services/config-runtime.ts';
1
+ export { applyTreeseedConfigValues, applyTreeseedEnvironmentToProcess, applyTreeseedSafeRepairs, assertTreeseedCommandEnvironment, checkTreeseedProviderConnections, clearTreeseedRemoteSession, collectTreeseedConfigContext, collectTreeseedPrintEnvReport, createDefaultTreeseedMachineConfig, ensureTreeseedActVerificationTooling, ensureTreeseedSecretSessionForConfig, ensureTreeseedGitignoreEntries, getTreeseedMachineConfigPaths, loadTreeseedMachineConfig, listRelevantTreeseedConfigEntries, finalizeTreeseedConfig, listDeprecatedTreeseedLocalEnvFiles, inspectTreeseedKeyAgentStatus, lockTreeseedSecretSession, migrateTreeseedMachineKeyToWrapped, resolveTreeseedMachineEnvironmentValues, resolveTreeseedLaunchEnvironment, resolveTreeseedRemoteConfig, resolveTreeseedRemoteSession, rotateTreeseedMachineKey, rotateTreeseedMachineKeyPassphrase, setTreeseedRemoteSession, TREESEED_MACHINE_KEY_PASSPHRASE_ENV, TreeseedKeyAgentError, updateTreeseedDeployConfigFeatureToggles, unlockTreeseedSecretSessionFromEnv, unlockTreeseedSecretSessionInteractive, unlockTreeseedSecretSessionWithPassphrase, withTreeseedKeyAgentAutopromptDisabled, warnDeprecatedTreeseedLocalEnvFiles, writeTreeseedMachineConfig, } from './operations/services/config-runtime.ts';
2
2
  export { exportTreeseedCodebase } from './operations/services/export-runtime.ts';
3
3
  export { assertDeploymentInitialized, cleanupDestroyedState, createBranchPreviewDeployTarget, createPersistentDeployTarget, deployTargetLabel, destroyCloudflareResources, ensureGeneratedWranglerConfig, finalizeDeploymentState, loadDeployState, printDeploySummary, printDestroySummary, provisionCloudflareResources, runRemoteD1Migrations, syncCloudflareSecrets, validateDeployPrerequisites, validateDestroyPrerequisites, } from './operations/services/deploy.ts';
4
4
  export { assertCleanWorktree, assertFeatureBranch, branchExists, checkoutBranch, createDeprecatedTaskTag, createFeatureBranchFromStaging, currentManagedBranch, deleteLocalBranch, deleteRemoteBranch, ensureLocalBranchTracking, gitWorkflowRoot, listTaskBranches, mergeCurrentBranchIntoStaging, mergeStagingIntoMain, prepareReleaseBranches, PRODUCTION_BRANCH, pushBranch, remoteBranchExists, STAGING_BRANCH, syncBranchWithOrigin, waitForStagingAutomation, } from './operations/services/git-workflow.ts';
@@ -8,17 +8,32 @@ import {
8
8
  collectTreeseedConfigContext,
9
9
  collectTreeseedPrintEnvReport,
10
10
  createDefaultTreeseedMachineConfig,
11
+ ensureTreeseedActVerificationTooling,
12
+ ensureTreeseedSecretSessionForConfig,
11
13
  ensureTreeseedGitignoreEntries,
12
14
  getTreeseedMachineConfigPaths,
13
15
  loadTreeseedMachineConfig,
14
16
  listRelevantTreeseedConfigEntries,
15
17
  finalizeTreeseedConfig,
18
+ listDeprecatedTreeseedLocalEnvFiles,
19
+ inspectTreeseedKeyAgentStatus,
20
+ lockTreeseedSecretSession,
21
+ migrateTreeseedMachineKeyToWrapped,
16
22
  resolveTreeseedMachineEnvironmentValues,
23
+ resolveTreeseedLaunchEnvironment,
17
24
  resolveTreeseedRemoteConfig,
18
25
  resolveTreeseedRemoteSession,
19
26
  rotateTreeseedMachineKey,
27
+ rotateTreeseedMachineKeyPassphrase,
20
28
  setTreeseedRemoteSession,
21
- writeTreeseedLocalEnvironmentFiles,
29
+ TREESEED_MACHINE_KEY_PASSPHRASE_ENV,
30
+ TreeseedKeyAgentError,
31
+ updateTreeseedDeployConfigFeatureToggles,
32
+ unlockTreeseedSecretSessionFromEnv,
33
+ unlockTreeseedSecretSessionInteractive,
34
+ unlockTreeseedSecretSessionWithPassphrase,
35
+ withTreeseedKeyAgentAutopromptDisabled,
36
+ warnDeprecatedTreeseedLocalEnvFiles,
22
37
  writeTreeseedMachineConfig
23
38
  } from "./operations/services/config-runtime.js";
24
39
  import { exportTreeseedCodebase } from "./operations/services/export-runtime.js";
@@ -100,6 +115,8 @@ import {
100
115
  export {
101
116
  PRODUCTION_BRANCH,
102
117
  STAGING_BRANCH,
118
+ TREESEED_MACHINE_KEY_PASSPHRASE_ENV,
119
+ TreeseedKeyAgentError,
103
120
  applyTreeseedConfigValues,
104
121
  applyTreeseedEnvironmentToProcess,
105
122
  applyTreeseedSafeRepairs,
@@ -132,7 +149,9 @@ export {
132
149
  destroyCloudflareResources,
133
150
  ensureGeneratedWranglerConfig,
134
151
  ensureLocalBranchTracking,
152
+ ensureTreeseedActVerificationTooling,
135
153
  ensureTreeseedGitignoreEntries,
154
+ ensureTreeseedSecretSessionForConfig,
136
155
  exportTreeseedCodebase,
137
156
  finalizeDeploymentState,
138
157
  finalizeTreeseedConfig,
@@ -144,14 +163,18 @@ export {
144
163
  gitWorkflowRoot,
145
164
  hasMeaningfulChanges,
146
165
  incrementVersion,
166
+ inspectTreeseedKeyAgentStatus,
147
167
  isWorkspaceRoot,
168
+ listDeprecatedTreeseedLocalEnvFiles,
148
169
  listRelevantTreeseedConfigEntries,
149
170
  listTaskBranches,
150
171
  loadCliDeployConfig,
151
172
  loadDeployState,
152
173
  loadTreeseedMachineConfig,
174
+ lockTreeseedSecretSession,
153
175
  mergeCurrentBranchIntoStaging,
154
176
  mergeStagingIntoMain,
177
+ migrateTreeseedMachineKeyToWrapped,
155
178
  originRemoteUrl,
156
179
  packageScriptPath,
157
180
  planWorkspaceReleaseBump,
@@ -162,11 +185,13 @@ export {
162
185
  pushBranch,
163
186
  remoteBranchExists,
164
187
  repoRoot,
188
+ resolveTreeseedLaunchEnvironment,
165
189
  resolveTreeseedMachineEnvironmentValues,
166
190
  resolveTreeseedRemoteConfig,
167
191
  resolveTreeseedRemoteSession,
168
192
  resolveWranglerBin,
169
193
  rotateTreeseedMachineKey,
194
+ rotateTreeseedMachineKeyPassphrase,
170
195
  run,
171
196
  runRemoteD1Migrations,
172
197
  runTenantDeployPreflight,
@@ -174,11 +199,16 @@ export {
174
199
  setTreeseedRemoteSession,
175
200
  syncBranchWithOrigin,
176
201
  syncCloudflareSecrets,
202
+ unlockTreeseedSecretSessionFromEnv,
203
+ unlockTreeseedSecretSessionInteractive,
204
+ unlockTreeseedSecretSessionWithPassphrase,
205
+ updateTreeseedDeployConfigFeatureToggles,
177
206
  validateDeployPrerequisites,
178
207
  validateDestroyPrerequisites,
179
208
  validateRailwayDeployPrerequisites,
180
209
  waitForStagingAutomation,
210
+ warnDeprecatedTreeseedLocalEnvFiles,
211
+ withTreeseedKeyAgentAutopromptDisabled,
181
212
  workspaceRoot,
182
- writeTreeseedLocalEnvironmentFiles,
183
213
  writeTreeseedMachineConfig
184
214
  };