@treeseed/sdk 0.6.7 → 0.6.8

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 (48) hide show
  1. package/dist/copilot.d.ts +15 -0
  2. package/dist/copilot.js +75 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +18 -0
  5. package/dist/managed-dependencies.d.ts +56 -0
  6. package/dist/managed-dependencies.js +668 -0
  7. package/dist/operations/providers/default.js +30 -1
  8. package/dist/operations/services/commit-message-provider.d.ts +33 -0
  9. package/dist/operations/services/commit-message-provider.js +319 -0
  10. package/dist/operations/services/config-runtime.js +41 -20
  11. package/dist/operations/services/git-remote-policy.d.ts +9 -0
  12. package/dist/operations/services/git-remote-policy.js +55 -0
  13. package/dist/operations/services/git-workflow.js +22 -3
  14. package/dist/operations/services/github-api.js +9 -4
  15. package/dist/operations/services/knowledge-coop-launch.js +4 -0
  16. package/dist/operations/services/local-dev.js +7 -2
  17. package/dist/operations/services/package-reference-policy.d.ts +70 -0
  18. package/dist/operations/services/package-reference-policy.js +314 -0
  19. package/dist/operations/services/project-platform.d.ts +4 -0
  20. package/dist/operations/services/project-platform.js +28 -4
  21. package/dist/operations/services/railway-deploy.d.ts +4 -1
  22. package/dist/operations/services/railway-deploy.js +76 -38
  23. package/dist/operations/services/repository-save-orchestrator.d.ts +172 -0
  24. package/dist/operations/services/repository-save-orchestrator.js +1462 -0
  25. package/dist/operations/services/workspace-dependency-mode.d.ts +70 -0
  26. package/dist/operations/services/workspace-dependency-mode.js +404 -0
  27. package/dist/operations/services/workspace-preflight.js +5 -0
  28. package/dist/operations/services/workspace-save.js +10 -6
  29. package/dist/operations-registry.js +5 -0
  30. package/dist/operations-types.d.ts +1 -0
  31. package/dist/platform/books-data.js +4 -1
  32. package/dist/platform/env.yaml +6 -3
  33. package/dist/reconcile/builtin-adapters.js +37 -7
  34. package/dist/scripts/cleanup-markdown.js +4 -0
  35. package/dist/scripts/publish-package.js +5 -0
  36. package/dist/scripts/tenant-workflow-action.js +11 -2
  37. package/dist/verification.js +24 -12
  38. package/dist/workflow/operations.d.ts +381 -55
  39. package/dist/workflow/operations.js +718 -258
  40. package/dist/workflow-state.d.ts +40 -1
  41. package/dist/workflow-state.js +220 -17
  42. package/dist/workflow-support.d.ts +3 -0
  43. package/dist/workflow-support.js +34 -0
  44. package/dist/workflow.d.ts +19 -3
  45. package/dist/workflow.js +3 -3
  46. package/dist/wrangler-d1.js +6 -1
  47. package/package.json +17 -1
  48. package/templates/github/deploy.workflow.yml +24 -14
@@ -1,7 +1,42 @@
1
+ import { inspectWorkspaceDependencyMode } from './operations/services/workspace-dependency-mode.ts';
1
2
  import type { TreeseedWorkflowNextStep } from './workflow.ts';
2
3
  import { type TreeseedWorkflowBranchRole } from './workflow/policy.ts';
3
4
  export type TreeseedBranchRole = TreeseedWorkflowBranchRole;
4
5
  export type TreeseedWorkflowRecommendation = TreeseedWorkflowNextStep;
6
+ export type TreeseedWorkflowEnvironmentStatus = {
7
+ phase: string;
8
+ ready: boolean;
9
+ configured: boolean;
10
+ initialized: boolean;
11
+ provisioned: boolean;
12
+ deployable: boolean;
13
+ lastValidatedAt: string | null;
14
+ lastDeploymentTimestamp: string | null;
15
+ lastDeployedUrl: string | null;
16
+ blockers: string[];
17
+ warnings: string[];
18
+ };
19
+ export type TreeseedWorkflowProviderCheck = {
20
+ configured: boolean;
21
+ applicable?: boolean;
22
+ detail?: string;
23
+ live?: {
24
+ checked: boolean;
25
+ ready: boolean;
26
+ skipped?: boolean;
27
+ detail: string;
28
+ };
29
+ };
30
+ export type TreeseedWorkflowProviderStatus = Record<'local' | 'staging' | 'prod', {
31
+ github: TreeseedWorkflowProviderCheck;
32
+ cloudflare: TreeseedWorkflowProviderCheck;
33
+ railway: TreeseedWorkflowProviderCheck;
34
+ localDevelopment: TreeseedWorkflowProviderCheck;
35
+ }>;
36
+ export type TreeseedWorkflowStatusOptions = {
37
+ live?: boolean;
38
+ env?: NodeJS.ProcessEnv;
39
+ };
5
40
  export type TreeseedWorkflowState = {
6
41
  cwd: string;
7
42
  workspaceRoot: boolean;
@@ -32,6 +67,8 @@ export type TreeseedWorkflowState = {
32
67
  packageSync: {
33
68
  mode: 'root-only' | 'recursive-workspace';
34
69
  completeCheckout: boolean;
70
+ dependencyMode: string;
71
+ workspaceLinks: ReturnType<typeof inspectWorkspaceDependencyMode>;
35
72
  expectedBranch: string | null;
36
73
  aligned: boolean;
37
74
  dirty: boolean;
@@ -75,6 +112,8 @@ export type TreeseedWorkflowState = {
75
112
  lastDeploymentTimestamp: string | null;
76
113
  lastDeployedUrl: string | null;
77
114
  }>;
115
+ environmentStatus: Record<'local' | 'staging' | 'prod', TreeseedWorkflowEnvironmentStatus>;
116
+ providerStatus: TreeseedWorkflowProviderStatus;
78
117
  auth: {
79
118
  gh: boolean;
80
119
  wrangler: boolean;
@@ -156,5 +195,5 @@ export type TreeseedWorkflowState = {
156
195
  }>;
157
196
  recommendations: TreeseedWorkflowRecommendation[];
158
197
  };
159
- export declare function resolveTreeseedWorkflowState(cwd: string): TreeseedWorkflowState;
198
+ export declare function resolveTreeseedWorkflowState(cwd: string, options?: TreeseedWorkflowStatusOptions): TreeseedWorkflowState;
160
199
  export declare function recommendTreeseedNextSteps(state: TreeseedWorkflowState): TreeseedWorkflowRecommendation[];
@@ -1,15 +1,18 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
+ import { spawnSync } from "node:child_process";
3
4
  import {
4
5
  getTreeseedMachineConfigPaths,
5
6
  inspectTreeseedKeyAgentStatus,
6
7
  loadTreeseedMachineConfig,
8
+ collectTreeseedConfigSeedValues,
7
9
  resolveTreeseedMachineEnvironmentValues,
8
10
  resolveTreeseedRemoteSession,
9
11
  collectTreeseedEnvironmentContext,
10
12
  withTreeseedKeyAgentAutopromptDisabled
11
13
  } from "./operations/services/config-runtime.js";
12
- import { validateTreeseedEnvironmentValues } from "./platform/environment.js";
14
+ import { resolveWranglerBin } from "./operations/services/runtime-tools.js";
15
+ import { getTreeseedEnvironmentSuggestedValues, validateTreeseedEnvironmentValues } from "./platform/environment.js";
13
16
  import { resolveTreeseedWebCachePolicy } from "./platform/deploy-config.js";
14
17
  import {
15
18
  createBranchPreviewDeployTarget,
@@ -20,7 +23,9 @@ import { loadCliDeployConfig } from "./operations/services/runtime-tools.js";
20
23
  import { collectCliPreflight } from "./operations/services/workspace-preflight.js";
21
24
  import { currentBranch, gitStatusPorcelain } from "./operations/services/workspace-save.js";
22
25
  import { hasCompleteTreeseedPackageCheckout, isWorkspaceRoot, run, workspacePackages } from "./operations/services/workspace-tools.js";
26
+ import { inspectWorkspaceDependencyMode } from "./operations/services/workspace-dependency-mode.js";
23
27
  import { inspectWorkflowLock, listInterruptedWorkflowRuns } from "./workflow/runs.js";
28
+ import { createTreeseedManagedToolEnv, resolveTreeseedToolCommand } from "./managed-dependencies.js";
24
29
  import {
25
30
  resolveTreeseedWorkflowPaths,
26
31
  workflowEnvironmentForBranchRole
@@ -32,6 +37,29 @@ function emptyPersistentEnvironments() {
32
37
  prod: { initialized: false, phase: "pending", configured: false, provisioned: false, deployable: false, blockers: [], warnings: [], lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null }
33
38
  };
34
39
  }
40
+ function emptyEnvironmentStatus() {
41
+ return {
42
+ local: { phase: "pending", ready: false, configured: false, initialized: false, provisioned: false, deployable: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null, blockers: [], warnings: [] },
43
+ staging: { phase: "pending", ready: false, configured: false, initialized: false, provisioned: false, deployable: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null, blockers: [], warnings: [] },
44
+ prod: { phase: "pending", ready: false, configured: false, initialized: false, provisioned: false, deployable: false, lastValidatedAt: null, lastDeploymentTimestamp: null, lastDeployedUrl: null, blockers: [], warnings: [] }
45
+ };
46
+ }
47
+ function emptyProviderStatus() {
48
+ const emptyScope = () => ({
49
+ github: { configured: false },
50
+ cloudflare: { configured: false },
51
+ railway: { configured: false },
52
+ localDevelopment: { configured: false }
53
+ });
54
+ return {
55
+ local: {
56
+ ...emptyScope(),
57
+ railway: { configured: true, applicable: false, detail: "Railway services run locally in the local environment." }
58
+ },
59
+ staging: emptyScope(),
60
+ prod: emptyScope()
61
+ };
62
+ }
35
63
  function readinessForEnvironment(state, scope) {
36
64
  const blockers = [...state.persistentEnvironments[scope].blockers];
37
65
  const warnings = [...state.persistentEnvironments[scope].warnings];
@@ -75,6 +103,143 @@ function safeResolveMachineEnvironmentValues(cwd, scope) {
75
103
  return {};
76
104
  }
77
105
  }
106
+ function collectStatusConfigScope(cwd, scope, environmentContext, env = process.env) {
107
+ const values = collectTreeseedConfigSeedValues(cwd, scope, env);
108
+ const suggestedValues = getTreeseedEnvironmentSuggestedValues({
109
+ scope,
110
+ purpose: "config",
111
+ deployConfig: environmentContext.context.deployConfig,
112
+ tenantConfig: environmentContext.context.tenantConfig,
113
+ plugins: environmentContext.context.plugins,
114
+ values
115
+ });
116
+ const validation = validateTreeseedEnvironmentValues({
117
+ values: {
118
+ ...suggestedValues,
119
+ ...values
120
+ },
121
+ scope,
122
+ purpose: "config",
123
+ deployConfig: environmentContext.context.deployConfig,
124
+ tenantConfig: environmentContext.context.tenantConfig,
125
+ plugins: environmentContext.context.plugins
126
+ });
127
+ return {
128
+ values,
129
+ suggestedValues,
130
+ resolvedValues: {
131
+ ...suggestedValues,
132
+ ...values
133
+ },
134
+ validation
135
+ };
136
+ }
137
+ function providerProblems(validation, provider) {
138
+ const problems = [...validation.missing, ...validation.invalid];
139
+ return problems.filter((problem) => {
140
+ const id = problem.id.toUpperCase();
141
+ const group = problem.entry.group;
142
+ if (provider === "github") {
143
+ return id === "GH_TOKEN" || id === "GITHUB_TOKEN" || group === "github";
144
+ }
145
+ if (provider === "cloudflare") {
146
+ return id.startsWith("CLOUDFLARE_") || id.includes("TURNSTILE") || group === "cloudflare";
147
+ }
148
+ if (provider === "railway") {
149
+ return id.startsWith("RAILWAY_") || group === "railway";
150
+ }
151
+ return group === "local-development";
152
+ });
153
+ }
154
+ function isCloudflareProviderProblem(problem) {
155
+ const id = problem.id.toUpperCase();
156
+ return id.startsWith("CLOUDFLARE_") || id.includes("TURNSTILE") || problem.entry.group === "cloudflare";
157
+ }
158
+ function liveCheckResult(configured, live) {
159
+ return live ? { configured, live } : { configured };
160
+ }
161
+ function spawnLiveCheck(command, args, cwd, env) {
162
+ const result = spawnSync(command, args, {
163
+ cwd,
164
+ env: { ...process.env, ...env },
165
+ stdio: "pipe",
166
+ encoding: "utf8",
167
+ timeout: 15e3
168
+ });
169
+ const output = `${result.stderr ?? ""}
170
+ ${result.stdout ?? ""}`.trim();
171
+ return {
172
+ ok: result.status === 0,
173
+ detail: output || (result.status === 0 ? "Provider check succeeded." : `Provider check failed with status ${result.status ?? "unknown"}.`)
174
+ };
175
+ }
176
+ function providerLiveCheck(provider, configured, cwd, env) {
177
+ if (!configured) {
178
+ return { checked: true, ready: false, skipped: true, detail: `${provider} token/config is missing.` };
179
+ }
180
+ try {
181
+ const result = (() => {
182
+ if (provider === "github") {
183
+ const gh = resolveTreeseedToolCommand("gh", { env });
184
+ if (!gh) return { ok: false, detail: "GitHub CLI `gh` is unavailable." };
185
+ return spawnLiveCheck(gh.command, [...gh.argsPrefix, "api", "user", "--jq", ".login"], cwd, createTreeseedManagedToolEnv(env));
186
+ }
187
+ if (provider === "cloudflare") {
188
+ return spawnLiveCheck(process.execPath, [resolveWranglerBin(), "whoami"], cwd, env);
189
+ }
190
+ const railway = resolveTreeseedToolCommand("railway", { env });
191
+ if (!railway) return { ok: false, detail: "Railway CLI is unavailable." };
192
+ return spawnLiveCheck(railway.command, [...railway.argsPrefix, "whoami"], cwd, env);
193
+ })();
194
+ return {
195
+ checked: true,
196
+ ready: result.ok,
197
+ detail: result.detail
198
+ };
199
+ } catch (error) {
200
+ return {
201
+ checked: true,
202
+ ready: false,
203
+ detail: error instanceof Error ? error.message : `${provider} live check failed.`
204
+ };
205
+ }
206
+ }
207
+ function providerStatusForScope(cwd, scope, statusConfig, options) {
208
+ const values = statusConfig.values;
209
+ const githubConfigured = typeof values.GH_TOKEN === "string" && values.GH_TOKEN.trim().length > 0;
210
+ const cloudflareConfigured = typeof values.CLOUDFLARE_API_TOKEN === "string" && values.CLOUDFLARE_API_TOKEN.trim().length > 0;
211
+ const railwayConfigured = typeof values.RAILWAY_API_TOKEN === "string" && values.RAILWAY_API_TOKEN.trim().length > 0;
212
+ const localDevelopmentConfigured = providerProblems(statusConfig.validation, "localDevelopment").length === 0;
213
+ const live = options.live === true;
214
+ const env = statusConfig.resolvedValues;
215
+ const cloudflare = scope === "local" ? {
216
+ configured: cloudflareConfigured,
217
+ applicable: false,
218
+ detail: cloudflareConfigured ? "Cloudflare is used locally only for optional AI-backed features." : "Cloudflare provider deployment is not used for the local runtime.",
219
+ ...live ? { live: { checked: true, ready: true, skipped: true, detail: "Wrangler provider checks are not used for the local runtime." } } : {}
220
+ } : liveCheckResult(cloudflareConfigured, live ? providerLiveCheck("cloudflare", cloudflareConfigured, cwd, env) : void 0);
221
+ const railway = scope === "local" ? {
222
+ configured: true,
223
+ applicable: false,
224
+ detail: "Railway services run locally in the local environment.",
225
+ ...live ? { live: { checked: true, ready: true, skipped: true, detail: "Railway is not used for the local environment." } } : {}
226
+ } : liveCheckResult(railwayConfigured, live ? providerLiveCheck("railway", railwayConfigured, cwd, env) : void 0);
227
+ return {
228
+ github: liveCheckResult(githubConfigured, live ? providerLiveCheck("github", githubConfigured, cwd, env) : void 0),
229
+ cloudflare,
230
+ railway,
231
+ localDevelopment: {
232
+ configured: localDevelopmentConfigured,
233
+ ...live ? { live: { checked: true, ready: localDevelopmentConfigured, skipped: true, detail: "Local development readiness is validated from saved configuration." } } : {}
234
+ }
235
+ };
236
+ }
237
+ function hasStatusConfigValue(statusConfigByScope, key) {
238
+ return ["local", "staging", "prod"].some((scope) => {
239
+ const value = statusConfigByScope[scope].values[key];
240
+ return typeof value === "string" && value.trim().length > 0;
241
+ });
242
+ }
78
243
  function knownRemoteTrackingBranchExists(repoDir, branchName) {
79
244
  try {
80
245
  run("git", ["show-ref", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], { cwd: repoDir, capture: true });
@@ -83,7 +248,10 @@ function knownRemoteTrackingBranchExists(repoDir, branchName) {
83
248
  return false;
84
249
  }
85
250
  }
86
- function resolveTreeseedWorkflowState(cwd) {
251
+ function resolveLocalStatusUrl(deployConfig) {
252
+ return deployConfig.surfaces?.web?.localBaseUrl ?? deployConfig.surfaces?.api?.localBaseUrl ?? Object.values(deployConfig.services ?? {}).find((service) => service?.enabled !== false && service.environments?.local?.baseUrl)?.environments?.local?.baseUrl ?? null;
253
+ }
254
+ function resolveTreeseedWorkflowState(cwd, options = {}) {
87
255
  const resolved = resolveTreeseedWorkflowPaths(cwd);
88
256
  const effectiveCwd = resolved.cwd;
89
257
  const workspaceRoot = isWorkspaceRoot(effectiveCwd);
@@ -94,6 +262,7 @@ function resolveTreeseedWorkflowState(cwd) {
94
262
  const branchRole = resolved.branchRole;
95
263
  const dirtyWorktree = root ? gitStatusPorcelain(root).length > 0 : false;
96
264
  const completePackageCheckout = hasCompleteTreeseedPackageCheckout(effectiveCwd);
265
+ const workspaceDependencyMode = inspectWorkspaceDependencyMode(effectiveCwd);
97
266
  const packageSyncRepos = completePackageCheckout ? workspacePackages(effectiveCwd).filter((pkg) => pkg.name?.startsWith("@treeseed/")).map((pkg) => {
98
267
  const repoBranch = currentBranch(pkg.dir) || null;
99
268
  const dirty = gitStatusPorcelain(pkg.dir).length > 0;
@@ -184,6 +353,8 @@ function resolveTreeseedWorkflowState(cwd) {
184
353
  packageSync: {
185
354
  mode: completePackageCheckout ? "recursive-workspace" : "root-only",
186
355
  completeCheckout: completePackageCheckout,
356
+ dependencyMode: workspaceDependencyMode.mode,
357
+ workspaceLinks: workspaceDependencyMode,
187
358
  expectedBranch: branchName,
188
359
  aligned: packageSyncRepos.every((repo) => repo.aligned),
189
360
  dirty: packageSyncRepos.some((repo) => repo.dirty),
@@ -208,6 +379,8 @@ function resolveTreeseedWorkflowState(cwd) {
208
379
  lastContentPurgeCount: null
209
380
  },
210
381
  persistentEnvironments: emptyPersistentEnvironments(),
382
+ environmentStatus: emptyEnvironmentStatus(),
383
+ providerStatus: emptyProviderStatus(),
211
384
  auth: {
212
385
  gh: preflight.checks.auth.gh?.authenticated === true,
213
386
  wrangler: preflight.checks.auth.wrangler?.authenticated === true,
@@ -278,7 +451,19 @@ function resolveTreeseedWorkflowState(cwd) {
278
451
  try {
279
452
  const deployConfig = loadCliDeployConfig(effectiveCwd);
280
453
  const environmentContext = collectTreeseedEnvironmentContext(effectiveCwd);
281
- const sharedConfigValues = safeResolveMachineEnvironmentValues(effectiveCwd, "prod");
454
+ const statusConfigByScope = Object.fromEntries(
455
+ ["local", "staging", "prod"].map((scope) => [
456
+ scope,
457
+ collectStatusConfigScope(effectiveCwd, scope, environmentContext, options.env)
458
+ ])
459
+ );
460
+ state.providerStatus = Object.fromEntries(
461
+ ["local", "staging", "prod"].map((scope) => [
462
+ scope,
463
+ providerStatusForScope(effectiveCwd, scope, statusConfigByScope[scope], options)
464
+ ])
465
+ );
466
+ const sharedConfigValues = statusConfigByScope.prod.resolvedValues;
282
467
  const runtimeMode = deployConfig.runtime?.mode ?? "none";
283
468
  const runtimeRegistration = deployConfig.runtime?.registration ?? "none";
284
469
  const webCachePolicy = resolveTreeseedWebCachePolicy(deployConfig);
@@ -287,6 +472,10 @@ function resolveTreeseedWorkflowState(cwd) {
287
472
  const runtimeSessionReady = Boolean(
288
473
  marketSettings?.runnerReady === true || typeof runnerSession?.accessToken === "string" && runnerSession.accessToken.length > 0
289
474
  );
475
+ state.auth.gh = state.auth.gh || hasStatusConfigValue(statusConfigByScope, "GH_TOKEN");
476
+ state.auth.wrangler = state.auth.wrangler || hasStatusConfigValue(statusConfigByScope, "CLOUDFLARE_API_TOKEN");
477
+ state.auth.railway = state.auth.railway || hasStatusConfigValue(statusConfigByScope, "RAILWAY_API_TOKEN");
478
+ state.auth.copilot = state.auth.copilot || state.auth.gh;
290
479
  state.marketConnection.baseUrl = state.marketConnection.baseUrl ?? sharedConfigValues.TREESEED_MARKET_API_BASE_URL ?? deployConfig.runtime?.marketBaseUrl ?? deployConfig.hosting?.marketBaseUrl ?? null;
291
480
  state.marketConnection.teamId = state.marketConnection.teamId ?? sharedConfigValues.TREESEED_HOSTING_TEAM_ID ?? deployConfig.runtime?.teamId ?? deployConfig.hosting?.teamId ?? null;
292
481
  state.marketConnection.projectId = state.marketConnection.projectId ?? sharedConfigValues.TREESEED_PROJECT_ID ?? deployConfig.runtime?.projectId ?? deployConfig.hosting?.projectId ?? null;
@@ -302,26 +491,23 @@ function resolveTreeseedWorkflowState(cwd) {
302
491
  state.webCache.contentPagePolicy = `browser=${webCachePolicy.contentPages.browserTtlSeconds}s edge=${webCachePolicy.contentPages.edgeTtlSeconds}s swr=${webCachePolicy.contentPages.staleWhileRevalidateSeconds}s sie=${webCachePolicy.contentPages.staleIfErrorSeconds}s`;
303
492
  state.webCache.r2ObjectPolicy = `browser=${webCachePolicy.r2PublishedObjects.browserTtlSeconds}s edge=${webCachePolicy.r2PublishedObjects.edgeTtlSeconds}s swr=${webCachePolicy.r2PublishedObjects.staleWhileRevalidateSeconds}s sie=${webCachePolicy.r2PublishedObjects.staleIfErrorSeconds}s`;
304
493
  state.marketConnection.configured = registrationRequired2 ? Boolean(state.marketConnection.baseUrl && state.marketConnection.projectId) : state.marketConnection.configured;
494
+ const localStatusUrl = resolveLocalStatusUrl(deployConfig);
305
495
  for (const scope of ["local", "staging", "prod"]) {
306
496
  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);
497
+ const validation = statusConfigByScope[scope].validation;
498
+ const rawValidationProblems = [...validation.missing, ...validation.invalid];
499
+ const statusValidationProblems = scope === "local" ? rawValidationProblems.filter((problem) => !isCloudflareProviderProblem(problem)) : rawValidationProblems;
500
+ const validationProblems = statusValidationProblems.map((problem) => problem.message);
501
+ const statusValidationOk = statusValidationProblems.length === 0;
316
502
  const persistentBlockers = Array.isArray(deployState.readiness?.blockers) ? deployState.readiness.blockers.map(String) : [];
317
503
  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;
504
+ const configured = scope === "local" ? statusValidationOk : deployState.readiness?.configured === true && statusValidationOk;
319
505
  const provisioned = scope === "local" ? true : configured && deployState.readiness?.provisioned === true;
320
- const deployable = scope === "local" ? validation.ok : configured && deployState.readiness?.deployable === true;
506
+ const deployable = scope === "local" ? statusValidationOk : configured && deployState.readiness?.deployable === true;
321
507
  const initialized = deployState.readiness?.initialized === true || scope === "local";
322
508
  state.persistentEnvironments[scope] = {
323
509
  initialized,
324
- phase: validation.ok ? scope === "local" ? "code_ready" : provisioned ? "provisioned" : configured ? "config_complete" : initialized ? "config_complete" : "pending" : "config_incomplete",
510
+ phase: statusValidationOk ? scope === "local" ? "code_ready" : provisioned ? "provisioned" : configured ? "config_complete" : initialized ? "config_complete" : "pending" : "config_incomplete",
325
511
  configured,
326
512
  provisioned,
327
513
  deployable,
@@ -331,8 +517,8 @@ function resolveTreeseedWorkflowState(cwd) {
331
517
  ],
332
518
  warnings: persistentWarnings,
333
519
  lastValidatedAt: deployState.readiness?.lastValidatedAt ?? deployState.readiness?.initializedAt ?? null,
334
- lastDeploymentTimestamp: deployState.lastDeploymentTimestamp ?? null,
335
- lastDeployedUrl: deployState.lastDeployedUrl ?? null
520
+ lastDeploymentTimestamp: scope === "local" ? null : deployState.lastDeploymentTimestamp ?? null,
521
+ lastDeployedUrl: scope === "local" ? localStatusUrl : deployState.lastDeployedUrl ?? null
336
522
  };
337
523
  if (scope !== "local") {
338
524
  const history = Array.isArray(deployState.deploymentHistory) ? deployState.deploymentHistory ?? [] : [];
@@ -377,6 +563,23 @@ function resolveTreeseedWorkflowState(cwd) {
377
563
  state.readiness.local = readinessForEnvironment(state, "local");
378
564
  state.readiness.staging = readinessForEnvironment(state, "staging");
379
565
  state.readiness.prod = readinessForEnvironment(state, "prod");
566
+ for (const scope of ["local", "staging", "prod"]) {
567
+ const persistent = state.persistentEnvironments[scope];
568
+ const readiness = state.readiness[scope];
569
+ state.environmentStatus[scope] = {
570
+ phase: persistent.phase,
571
+ ready: readiness.ready,
572
+ configured: persistent.configured,
573
+ initialized: persistent.initialized,
574
+ provisioned: persistent.provisioned,
575
+ deployable: persistent.deployable,
576
+ lastValidatedAt: persistent.lastValidatedAt,
577
+ lastDeploymentTimestamp: persistent.lastDeploymentTimestamp,
578
+ lastDeployedUrl: persistent.lastDeployedUrl,
579
+ blockers: readiness.blockers,
580
+ warnings: readiness.warnings
581
+ };
582
+ }
380
583
  state.marketConnection.verificationPosture = state.readiness.local.ready ? "ready" : state.files.machineConfig ? "blocked" : "pending";
381
584
  const registrationRequired = state.marketConnection.runtimeRegistration === "required";
382
585
  state.marketConnection.approvalBlockers = [
@@ -7,6 +7,9 @@ export { configuredRailwayServices, deployRailwayService, validateRailwayDeployP
7
7
  export { ensureRailwayEnvironment, ensureRailwayProject, ensureRailwayService, getRailwayAuthProfile, listRailwayEnvironments, listRailwayProjects, listRailwayServices, listRailwayVariables, railwayGraphqlRequest, resolveRailwayApiToken, resolveRailwayApiUrl, resolveRailwayWorkspace, resolveRailwayWorkspaceContext, upsertRailwayVariables, } from './operations/services/railway-api.ts';
8
8
  export { runTenantDeployPreflight, runWorkspaceSavePreflight, } from './operations/services/save-deploy-preflight.ts';
9
9
  export { collectCliPreflight } from './operations/services/workspace-preflight.ts';
10
+ export { collectTreeseedDependencyStatus, createTreeseedManagedToolEnv, formatTreeseedDependencyFailureDetails, formatTreeseedDependencyReport, installTreeseedDependencies, resolveTreeseedToolBinary, resolveTreeseedToolCommand, } from './managed-dependencies.ts';
11
+ export { runTreeseedCopilotTask, type TreeseedCopilotTaskInput, type TreeseedCopilotTaskResult, } from './copilot.ts';
10
12
  export { applyWorkspaceVersionChanges, collectMergeConflictReport, currentBranch, formatMergeConflictReport, gitStatusPorcelain, hasMeaningfulChanges, incrementVersion, originRemoteUrl, planWorkspaceReleaseBump, repoRoot, } from './operations/services/workspace-save.ts';
13
+ export { assertNoWorkspaceLinksInDeploymentLockfiles, collectDeploymentLockfileWorkspaceIssues, discoverWorkspaceLinks, ensureLocalWorkspaceLinks, inspectWorkspaceDependencyMode, unlinkLocalWorkspaceLinks, type DependencyResolutionMode, type DeploymentLockfileWorkspaceIssue, type WorkspaceLinksMode, } from './operations/services/workspace-dependency-mode.ts';
11
14
  export { findNearestTreeseedRoot, findNearestTreeseedWorkspaceRoot, isWorkspaceRoot, run, workspaceRoot, } from './operations/services/workspace-tools.ts';
12
15
  export { collectTreeseedReconcileStatus, createTreeseedReconcileRegistry, deriveTreeseedDesiredUnits, destroyTreeseedTargetUnits, observeTreeseedUnits, planTreeseedReconciliation, reconcileTreeseedTarget, } from './reconcile/index.ts';
@@ -111,6 +111,18 @@ import {
111
111
  runWorkspaceSavePreflight
112
112
  } from "./operations/services/save-deploy-preflight.js";
113
113
  import { collectCliPreflight } from "./operations/services/workspace-preflight.js";
114
+ import {
115
+ collectTreeseedDependencyStatus,
116
+ createTreeseedManagedToolEnv,
117
+ formatTreeseedDependencyFailureDetails,
118
+ formatTreeseedDependencyReport,
119
+ installTreeseedDependencies,
120
+ resolveTreeseedToolBinary,
121
+ resolveTreeseedToolCommand
122
+ } from "./managed-dependencies.js";
123
+ import {
124
+ runTreeseedCopilotTask
125
+ } from "./copilot.js";
114
126
  import {
115
127
  applyWorkspaceVersionChanges,
116
128
  collectMergeConflictReport,
@@ -123,6 +135,14 @@ import {
123
135
  planWorkspaceReleaseBump,
124
136
  repoRoot
125
137
  } from "./operations/services/workspace-save.js";
138
+ import {
139
+ assertNoWorkspaceLinksInDeploymentLockfiles,
140
+ collectDeploymentLockfileWorkspaceIssues,
141
+ discoverWorkspaceLinks,
142
+ ensureLocalWorkspaceLinks,
143
+ inspectWorkspaceDependencyMode,
144
+ unlinkLocalWorkspaceLinks
145
+ } from "./operations/services/workspace-dependency-mode.js";
126
146
  import {
127
147
  findNearestTreeseedRoot,
128
148
  findNearestTreeseedWorkspaceRoot,
@@ -151,6 +171,7 @@ export {
151
171
  assertCleanWorktree,
152
172
  assertDeploymentInitialized,
153
173
  assertFeatureBranch,
174
+ assertNoWorkspaceLinksInDeploymentLockfiles,
154
175
  assertTreeseedCommandEnvironment,
155
176
  branchExists,
156
177
  checkTreeseedProviderConnections,
@@ -158,8 +179,10 @@ export {
158
179
  cleanupDestroyedState,
159
180
  clearTreeseedRemoteSession,
160
181
  collectCliPreflight,
182
+ collectDeploymentLockfileWorkspaceIssues,
161
183
  collectMergeConflictReport,
162
184
  collectTreeseedConfigContext,
185
+ collectTreeseedDependencyStatus,
163
186
  collectTreeseedPrintEnvReport,
164
187
  collectTreeseedReconcileStatus,
165
188
  configuredRailwayServices,
@@ -168,6 +191,7 @@ export {
168
191
  createDeprecatedTaskTag,
169
192
  createFeatureBranchFromStaging,
170
193
  createPersistentDeployTarget,
194
+ createTreeseedManagedToolEnv,
171
195
  createTreeseedReconcileRegistry,
172
196
  currentBranch,
173
197
  currentManagedBranch,
@@ -178,8 +202,10 @@ export {
178
202
  deriveTreeseedDesiredUnits,
179
203
  destroyCloudflareResources,
180
204
  destroyTreeseedTargetUnits,
205
+ discoverWorkspaceLinks,
181
206
  ensureGeneratedWranglerConfig,
182
207
  ensureLocalBranchTracking,
208
+ ensureLocalWorkspaceLinks,
183
209
  ensureRailwayEnvironment,
184
210
  ensureRailwayProject,
185
211
  ensureRailwayService,
@@ -192,6 +218,8 @@ export {
192
218
  findNearestTreeseedRoot,
193
219
  findNearestTreeseedWorkspaceRoot,
194
220
  formatMergeConflictReport,
221
+ formatTreeseedDependencyFailureDetails,
222
+ formatTreeseedDependencyReport,
195
223
  getRailwayAuthProfile,
196
224
  getTreeseedMachineConfigPaths,
197
225
  gitStatusPorcelain,
@@ -201,6 +229,8 @@ export {
201
229
  inspectTreeseedKeyAgentStatus,
202
230
  inspectTreeseedKeyAgentTransportDiagnostic,
203
231
  inspectTreeseedPassphraseEnvDiagnostic,
232
+ inspectWorkspaceDependencyMode,
233
+ installTreeseedDependencies,
204
234
  isWorkspaceRoot,
205
235
  listDeprecatedTreeseedLocalEnvFiles,
206
236
  listRailwayEnvironments,
@@ -238,16 +268,20 @@ export {
238
268
  resolveTreeseedMachineEnvironmentValues,
239
269
  resolveTreeseedRemoteConfig,
240
270
  resolveTreeseedRemoteSession,
271
+ resolveTreeseedToolBinary,
272
+ resolveTreeseedToolCommand,
241
273
  resolveWranglerBin,
242
274
  rotateTreeseedMachineKey,
243
275
  rotateTreeseedMachineKeyPassphrase,
244
276
  run,
245
277
  runRemoteD1Migrations,
246
278
  runTenantDeployPreflight,
279
+ runTreeseedCopilotTask,
247
280
  runWorkspaceSavePreflight,
248
281
  setTreeseedRemoteSession,
249
282
  syncBranchWithOrigin,
250
283
  syncCloudflareSecrets,
284
+ unlinkLocalWorkspaceLinks,
251
285
  unlockTreeseedSecretSessionFromEnv,
252
286
  unlockTreeseedSecretSessionInteractive,
253
287
  unlockTreeseedSecretSessionWithPassphrase,
@@ -1,4 +1,4 @@
1
- import { resolveTreeseedWorkflowState } from './workflow-state.ts';
1
+ import { resolveTreeseedWorkflowState, type TreeseedWorkflowStatusOptions } from './workflow-state.ts';
2
2
  import { listTaskBranches } from './operations/services/git-workflow.ts';
3
3
  import { TreeseedWorkflowError, type TreeseedWorkflowErrorCode } from './workflow/operations.ts';
4
4
  export type TreeseedWorkflowOperationId = 'status' | 'config' | 'tasks' | 'switch' | 'dev' | 'save' | 'close' | 'stage' | 'release' | 'resume' | 'recover' | 'destroy' | 'export';
@@ -90,12 +90,20 @@ export type TreeseedWorkflowWorkstreamSummary = {
90
90
  archived: boolean;
91
91
  };
92
92
  export type TreeseedSaveInput = {
93
- message: string;
93
+ message?: string;
94
94
  hotfix?: boolean;
95
95
  verify?: boolean;
96
96
  refreshPreview?: boolean;
97
97
  preview?: boolean;
98
98
  rebase?: boolean;
99
+ bump?: 'major' | 'minor' | 'patch';
100
+ devVersionStrategy?: 'prerelease';
101
+ devDependencyReferenceMode?: 'git-tag' | 'registry-prerelease';
102
+ gitDependencyProtocol?: 'preserve-origin' | 'https' | 'ssh';
103
+ gitRemoteWriteMode?: 'ssh-pushurl' | 'off';
104
+ verifyMode?: 'action-first' | 'local-only' | 'skip';
105
+ commitMessageMode?: 'auto' | 'cloudflare' | 'generated' | 'fallback';
106
+ workspaceLinks?: 'auto' | 'off';
99
107
  plan?: boolean;
100
108
  dryRun?: boolean;
101
109
  };
@@ -104,6 +112,7 @@ export type TreeseedCloseInput = {
104
112
  deletePreview?: boolean;
105
113
  deleteBranch?: boolean;
106
114
  autoSave?: boolean;
115
+ workspaceLinks?: 'auto' | 'off';
107
116
  plan?: boolean;
108
117
  dryRun?: boolean;
109
118
  };
@@ -113,6 +122,7 @@ export type TreeseedStageInput = {
113
122
  deletePreview?: boolean;
114
123
  deleteBranch?: boolean;
115
124
  autoSave?: boolean;
125
+ workspaceLinks?: 'auto' | 'off';
116
126
  plan?: boolean;
117
127
  dryRun?: boolean;
118
128
  };
@@ -122,6 +132,7 @@ export type TreeseedSwitchInput = {
122
132
  preview?: boolean;
123
133
  createIfMissing?: boolean;
124
134
  baseBranch?: string;
135
+ workspaceLinks?: 'auto' | 'off';
125
136
  plan?: boolean;
126
137
  dryRun?: boolean;
127
138
  };
@@ -166,6 +177,10 @@ export type TreeseedExportInput = {
166
177
  };
167
178
  export type TreeseedReleaseInput = {
168
179
  bump: 'major' | 'minor' | 'patch';
180
+ devTagCleanup?: 'safe-after-release' | 'off';
181
+ gitDependencyProtocol?: 'preserve-origin' | 'https' | 'ssh';
182
+ gitRemoteWriteMode?: 'ssh-pushurl' | 'off';
183
+ workspaceLinks?: 'auto' | 'off';
169
184
  plan?: boolean;
170
185
  dryRun?: boolean;
171
186
  };
@@ -191,6 +206,7 @@ export type TreeseedWorkflowDevInput = {
191
206
  port?: number | string;
192
207
  background?: boolean;
193
208
  stdio?: 'inherit' | 'pipe';
209
+ workspaceLinks?: 'auto' | 'off';
194
210
  };
195
211
  export { TreeseedWorkflowError };
196
212
  export type { TreeseedWorkflowErrorCode };
@@ -202,7 +218,7 @@ export declare class TreeseedWorkflowSdk {
202
218
  tasks: TreeseedTaskBranchMetadata[];
203
219
  workstreams: TreeseedWorkflowWorkstreamSummary[];
204
220
  }> | TreeseedWorkflowResult<Record<string, unknown>>>;
205
- status(): Promise<TreeseedWorkflowResult<ReturnType<typeof resolveTreeseedWorkflowState>>>;
221
+ status(input?: TreeseedWorkflowStatusOptions): Promise<TreeseedWorkflowResult<ReturnType<typeof resolveTreeseedWorkflowState>>>;
206
222
  tasks(): Promise<TreeseedWorkflowResult<{
207
223
  tasks: TreeseedTaskBranchMetadata[];
208
224
  workstreams: TreeseedWorkflowWorkstreamSummary[];
package/dist/workflow.js CHANGED
@@ -41,7 +41,7 @@ class TreeseedWorkflowSdk {
41
41
  async execute(operation, input = {}) {
42
42
  switch (operation) {
43
43
  case "status":
44
- return this.status();
44
+ return this.status(input);
45
45
  case "tasks":
46
46
  return this.tasks();
47
47
  case "config":
@@ -70,8 +70,8 @@ class TreeseedWorkflowSdk {
70
70
  throw new Error(`Unsupported workflow operation "${operation}".`);
71
71
  }
72
72
  }
73
- async status() {
74
- return workflowStatus(this.helpers());
73
+ async status(input = {}) {
74
+ return workflowStatus(this.helpers(), input);
75
75
  }
76
76
  async tasks() {
77
77
  return workflowTasks(this.helpers());
@@ -1,5 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
+ import { resolveTreeseedToolCommand } from "./managed-dependencies.js";
3
4
  const execFileAsync = promisify(execFile);
4
5
  function toSqlValue(value) {
5
6
  if (value === null || value === void 0) {
@@ -41,7 +42,11 @@ class WranglerD1PreparedStatement {
41
42
  if (this.persistTo) {
42
43
  args.splice(3, 0, "--local", "--persist-to", this.persistTo);
43
44
  }
44
- const { stdout } = await execFileAsync("wrangler", args, {
45
+ const wrangler = resolveTreeseedToolCommand("wrangler");
46
+ if (!wrangler) {
47
+ throw new Error("Wrangler CLI is unavailable.");
48
+ }
49
+ const { stdout } = await execFileAsync(wrangler.command, [...wrangler.argsPrefix, ...args], {
45
50
  cwd: this.cwd,
46
51
  env: process.env
47
52
  });