@treeseed/sdk 0.6.29 → 0.6.31

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.
@@ -1,4 +1,4 @@
1
- import type { AgentPool, AgentPoolRegistration, AgentPoolScaleDecision, CatalogArtifactVersion, CatalogItem, CatalogItemFilters, CreateProjectDeploymentRequest, PriorityOverride, PrioritySnapshot, ProjectConnection, ProjectDeployment, ProjectEnvironment, ProjectEnvironmentName, ProjectHosting, ProjectInfrastructureResource, ProjectWorkdaySummary, RecordAgentPoolRegistrationRequest, ScaleDecision, TeamStorageLocator, UpsertAgentPoolRequest, UpsertCatalogArtifactVersionRequest, UpsertCatalogItemRequest, UpsertProjectEnvironmentRequest, UpsertProjectHostingRequest, UpsertProjectInfrastructureResourceRequest, UpsertTeamStorageLocatorRequest, WorkdayPolicy, SdkPriorityOverrideRequest, SdkUpsertWorkPolicyRequest } from './sdk-types.ts';
1
+ import type { AgentPool, AgentPoolRegistration, AgentPoolScaleDecision, CatalogArtifactVersion, CatalogItem, CatalogItemFilters, CreateProjectDeploymentRequest, PriorityOverride, PrioritySnapshot, ProjectConnection, ProjectDeployment, ProjectEnvironment, ProjectEnvironmentName, ProjectHosting, ProjectInfrastructureResource, ProjectWorkdaySummary, RecordAgentPoolRegistrationRequest, ScaleDecision, TeamStorageLocator, TeamWebHost, UpsertAgentPoolRequest, UpsertCatalogArtifactVersionRequest, UpsertCatalogItemRequest, UpsertProjectEnvironmentRequest, UpsertProjectHostingRequest, UpsertProjectInfrastructureResourceRequest, UpsertTeamStorageLocatorRequest, UpsertTeamWebHostRequest, WorkdayPolicy, SdkPriorityOverrideRequest, SdkUpsertWorkPolicyRequest } from './sdk-types.ts';
2
2
  import type { AgentMessageRecord, AgentStatusRecord, DirectBoardItemSummary, InboxItem, LaunchProjectRequest, LaunchProjectResult, ProjectOverviewSummary, ReleaseDetail, ReleaseSummary, SharePackageStatus, TeamHomeSummary, TeamMemberSummary, WorkstreamDetail, WorkstreamSummary } from './knowledge-coop.ts';
3
3
  export interface ControlPlaneClientOptions {
4
4
  baseUrl: string;
@@ -56,6 +56,20 @@ export declare class ControlPlaneClient {
56
56
  getTeamHomeSummary(teamId: string): Promise<TeamHomeSummary>;
57
57
  listTeamInboxItems(teamId: string): Promise<InboxItem[]>;
58
58
  listTeamMembers(teamId: string): Promise<TeamMemberSummary[]>;
59
+ listTeamWebHosts(teamId: string): Promise<TeamWebHost[]>;
60
+ createTeamWebHost(teamId: string, input: UpsertTeamWebHostRequest): Promise<TeamWebHost>;
61
+ updateTeamWebHost(teamId: string, hostId: string, input: Partial<UpsertTeamWebHostRequest>): Promise<TeamWebHost>;
62
+ deleteTeamWebHost(teamId: string, hostId: string): Promise<{
63
+ ok: boolean;
64
+ payload?: TeamWebHost;
65
+ error?: string;
66
+ }>;
67
+ validateTeamWebHost(teamId: string, hostId: string, input: {
68
+ decryptedConfig?: Record<string, unknown> | null;
69
+ }): Promise<{
70
+ host: TeamWebHost;
71
+ validation: Record<string, unknown> | null;
72
+ }>;
59
73
  listTeamProducts(teamId: string): Promise<CatalogItem[]>;
60
74
  launchProject(teamId: string, input: LaunchProjectRequest): Promise<LaunchProjectResult>;
61
75
  getProjectSummary(projectId: string): Promise<ProjectOverviewSummary>;
@@ -237,6 +237,29 @@ class ControlPlaneClient {
237
237
  listTeamMembers(teamId) {
238
238
  return this.requestJson("GET", `/v1/teams/${encodeURIComponent(teamId)}/members`);
239
239
  }
240
+ listTeamWebHosts(teamId) {
241
+ return this.requestJson("GET", `/v1/teams/${encodeURIComponent(teamId)}/hosts`);
242
+ }
243
+ createTeamWebHost(teamId, input) {
244
+ return this.requestJson("POST", `/v1/teams/${encodeURIComponent(teamId)}/hosts`, {
245
+ body: input
246
+ });
247
+ }
248
+ updateTeamWebHost(teamId, hostId, input) {
249
+ return this.requestJson("PUT", `/v1/teams/${encodeURIComponent(teamId)}/hosts/${encodeURIComponent(hostId)}`, {
250
+ body: input
251
+ });
252
+ }
253
+ deleteTeamWebHost(teamId, hostId) {
254
+ return this.requestJson("DELETE", `/v1/teams/${encodeURIComponent(teamId)}/hosts/${encodeURIComponent(hostId)}`);
255
+ }
256
+ validateTeamWebHost(teamId, hostId, input) {
257
+ return this.requestJson(
258
+ "POST",
259
+ `/v1/teams/${encodeURIComponent(teamId)}/hosts/${encodeURIComponent(hostId)}/validate`,
260
+ { body: input }
261
+ );
262
+ }
240
263
  listTeamProducts(teamId) {
241
264
  return this.requestJson("GET", `/v1/teams/${encodeURIComponent(teamId)}/products`);
242
265
  }
package/dist/index.d.ts CHANGED
@@ -30,7 +30,7 @@ export { TreeseedWorkflowSdk } from './workflow.ts';
30
30
  export * from './db/index.ts';
31
31
  export { collectTreeseedReconcileStatus, createTreeseedReconcileRegistry, deriveTreeseedDesiredUnits, destroyTreeseedTargetUnits, observeTreeseedUnits, planTreeseedReconciliation, reconcileTreeseedTarget, } from './reconcile/index.ts';
32
32
  export { getTreeseedVerifyDriverStatus, runTreeseedVerifyDriver } from './verification.ts';
33
- export type { KnowledgeCoopLaunchPreflightReport, KnowledgeCoopLaunchFailurePhase, KnowledgeCoopManagedLaunchInput, KnowledgeCoopManagedLaunchResult, AgentMessageKind, AgentMessageRecord, AgentStatusRecord, DirectBoardItemSummary, InboxItem, KnowledgeCoopJobStatus, LaunchProjectRequest, LaunchProjectResult, LinkedProjectRecordRef, ProjectConnectionStatus, ProjectOverviewSummary, ReleaseDetail, ReleaseState, ReleaseSummary, SharePackageState, SharePackageStatus, TeamCapability, TeamHomeSummary, TeamMemberSummary, WorkstreamDetail, WorkstreamEvent, WorkstreamState, WorkstreamSummary, SdkContentEntry, SdkDispatchCapability, SdkDispatchConfig, SdkDispatchCredentialSource, SdkDispatchExecutionClass, SdkDispatchNamespace, SdkDispatchPolicy, SdkDispatchRequest, SdkDispatchResult, SdkDispatchTarget, SdkCursorEntity, SdkFilterCondition, SdkFollowRequest, SdkGraphEdge, SdkGraphEdgeType, SdkGraphDslRelation, SdkGraphDslParseResult, SdkGraphModelConfig, SdkGraphNode, SdkGraphNodeType, SdkGraphPathExplanation, SdkGraphQueryStage, SdkGraphQueryView, SdkGraphQueryOptions, SdkGraphQueryRequest, SdkGraphQueryResult, SdkGraphRankingBuildInput, SdkGraphRankingDiagnostics, SdkGraphRankingIndex, SdkGraphRankingNodeResult, SdkGraphRankingProvider, SdkGraphRankingQueryRequest, SdkGraphRankingQueryResult, SdkGraphRankingSearchRequest, SdkGraphRefreshPayload, SdkGraphRefreshRequest, SdkGraphSearchOptions, SdkGraphSearchResult, SdkGraphSeed, SdkGraphSeedResolution, SdkGraphTraversalResult, SdkGraphWhereFilter, SdkContextPack, SdkContextPackRequest, SdkGetRequest, SdkJsonEnvelope, SdkLeaseEntity, SdkManagerContextPayload, SdkMessageEntity, SdkModelFieldBinding, SdkModelDefinition, SdkModelRegistry, SdkModelName, SdkMutationRequest, SdkOperation, SdkPickRequest, SdkPickResult, SdkQueueMessageEnvelope, SdkRunEntity, SdkSearchRequest, SdkTaskEntity, SdkTaskEventEntity, SdkTaskOutputEntity, SdkWorkDayEntity, SdkGraphRunEntity, SdkReportEntity, SdkSubscriptionEntity, SdkTemplateCatalogEntry, SdkTemplateCatalogPublisher, SdkTemplateCatalogResponse, SdkTemplateCatalogSource, SdkUpdateRequest, ProjectCapabilityGrant, ProjectConnection, ProjectConnectionMode, ProjectDeployment, ProjectDeploymentKind, ProjectDeploymentStatus, ProjectEnvironment, ProjectExecutionOwner, ProjectHosting, ProjectInfrastructureResource, ProjectInfrastructureResourceKind, ProjectInfrastructureResourceProvider, ProjectRunnerRegistrationState, RemoteJob, RemoteJobEvent, RemoteJobStatus, AgentPool, AgentPoolAutoscalePolicy, AgentPoolRegistration, AgentPoolScaleDecision, AgentPoolStatus, CatalogArtifactVersion, CatalogItem, CatalogItemFilters, CatalogItemOfferMode, ProjectEnvironmentName, ProjectWorkdaySummary, TreeseedHostingKind, TreeseedHostingRegistration, PriorityOverride, WorkdayWindow, WorkdaySchedule, TaskCreditWeight, TaskCreditBudget, WorkdayPolicy, PrioritySnapshotItem, PrioritySnapshot, TaskCreditLedgerEntry, ScaleDecision, TeamStorageLocator, UpsertAgentPoolRequest, UpsertCatalogArtifactVersionRequest, UpsertCatalogItemRequest, UpsertProjectEnvironmentRequest, UpsertProjectHostingRequest, UpsertProjectInfrastructureResourceRequest, UpsertTeamStorageLocatorRequest, CreateProjectDeploymentRequest, RecordAgentPoolRegistrationRequest, WorkerPoolScaleResult, WorkerPoolScaler, } from './sdk-types.ts';
33
+ export type { KnowledgeCoopLaunchPreflightReport, KnowledgeCoopLaunchFailurePhase, KnowledgeCoopManagedLaunchInput, KnowledgeCoopManagedLaunchResult, AgentMessageKind, AgentMessageRecord, AgentStatusRecord, DirectBoardItemSummary, InboxItem, KnowledgeCoopJobStatus, LaunchProjectRequest, LaunchProjectResult, LinkedProjectRecordRef, ProjectConnectionStatus, ProjectOverviewSummary, ReleaseDetail, ReleaseState, ReleaseSummary, SharePackageState, SharePackageStatus, TeamCapability, TeamHomeSummary, TeamMemberSummary, WorkstreamDetail, WorkstreamEvent, WorkstreamState, WorkstreamSummary, SdkContentEntry, SdkDispatchCapability, SdkDispatchConfig, SdkDispatchCredentialSource, SdkDispatchExecutionClass, SdkDispatchNamespace, SdkDispatchPolicy, SdkDispatchRequest, SdkDispatchResult, SdkDispatchTarget, SdkCursorEntity, SdkFilterCondition, SdkFollowRequest, SdkGraphEdge, SdkGraphEdgeType, SdkGraphDslRelation, SdkGraphDslParseResult, SdkGraphModelConfig, SdkGraphNode, SdkGraphNodeType, SdkGraphPathExplanation, SdkGraphQueryStage, SdkGraphQueryView, SdkGraphQueryOptions, SdkGraphQueryRequest, SdkGraphQueryResult, SdkGraphRankingBuildInput, SdkGraphRankingDiagnostics, SdkGraphRankingIndex, SdkGraphRankingNodeResult, SdkGraphRankingProvider, SdkGraphRankingQueryRequest, SdkGraphRankingQueryResult, SdkGraphRankingSearchRequest, SdkGraphRefreshPayload, SdkGraphRefreshRequest, SdkGraphSearchOptions, SdkGraphSearchResult, SdkGraphSeed, SdkGraphSeedResolution, SdkGraphTraversalResult, SdkGraphWhereFilter, SdkContextPack, SdkContextPackRequest, SdkGetRequest, SdkJsonEnvelope, SdkLeaseEntity, SdkManagerContextPayload, SdkMessageEntity, SdkModelFieldBinding, SdkModelDefinition, SdkModelRegistry, SdkModelName, SdkMutationRequest, SdkOperation, SdkPickRequest, SdkPickResult, SdkQueueMessageEnvelope, SdkRunEntity, SdkSearchRequest, SdkTaskEntity, SdkTaskEventEntity, SdkTaskOutputEntity, SdkWorkDayEntity, SdkGraphRunEntity, SdkReportEntity, SdkSubscriptionEntity, SdkTemplateCatalogEntry, SdkTemplateCatalogPublisher, SdkTemplateCatalogResponse, SdkTemplateCatalogSource, SdkUpdateRequest, ProjectCapabilityGrant, ProjectConnection, ProjectConnectionMode, ProjectDeployment, ProjectDeploymentKind, ProjectDeploymentStatus, ProjectEnvironment, ProjectExecutionOwner, ProjectHosting, ProjectInfrastructureResource, ProjectInfrastructureResourceKind, ProjectInfrastructureResourceProvider, ProjectRunnerRegistrationState, RemoteJob, RemoteJobEvent, RemoteJobStatus, AgentPool, AgentPoolAutoscalePolicy, AgentPoolRegistration, AgentPoolScaleDecision, AgentPoolStatus, CatalogArtifactVersion, CatalogItem, CatalogItemFilters, CatalogItemOfferMode, ProjectEnvironmentName, ProjectWorkdaySummary, TreeseedHostingKind, TreeseedHostingRegistration, PriorityOverride, WorkdayWindow, WorkdaySchedule, TaskCreditWeight, TaskCreditBudget, WorkdayPolicy, PrioritySnapshotItem, PrioritySnapshot, TaskCreditLedgerEntry, ScaleDecision, TeamStorageLocator, TeamWebHost, TeamWebHostOwnership, TeamWebHostProvider, EncryptedWebHostPayload, UpsertAgentPoolRequest, UpsertCatalogArtifactVersionRequest, UpsertCatalogItemRequest, UpsertProjectEnvironmentRequest, UpsertProjectHostingRequest, UpsertProjectInfrastructureResourceRequest, UpsertTeamStorageLocatorRequest, UpsertTeamWebHostRequest, CreateProjectDeploymentRequest, RecordAgentPoolRegistrationRequest, WorkerPoolScaleResult, WorkerPoolScaler, } from './sdk-types.ts';
34
34
  export type * from './knowledge-coop.ts';
35
35
  export type { ControlPlaneAgentPoolHeartbeat, ControlPlaneDeploymentReport, ControlPlaneEnvironmentReport, ControlPlaneReporter, ControlPlaneReporterKind, ControlPlaneResourceReport, ControlPlaneScaleDecisionReport, ControlPlaneWorkdaySummaryReport, } from './control-plane.ts';
36
36
  export type { ControlPlaneClientOptions } from './control-plane-client.ts';
@@ -201,6 +201,13 @@ export interface LaunchProjectRequest {
201
201
  sourceKind: 'blank' | 'template' | 'knowledge_pack';
202
202
  sourceRef?: string | null;
203
203
  hostingMode: 'managed' | 'hybrid' | 'self_hosted';
204
+ cloudflareHostId?: string | null;
205
+ cloudflareHostMode?: 'team_owned' | 'treeseed_managed' | null;
206
+ cloudflareHostConfig?: Record<string, unknown> | null;
207
+ processingHostId?: string | null;
208
+ processingHostMode?: 'team_owned' | 'treeseed_managed' | null;
209
+ processingHostConfig?: Record<string, unknown> | null;
210
+ targetEnvironments?: Array<'local' | 'staging' | 'prod'>;
204
211
  publicSite?: boolean;
205
212
  repoProvider?: 'github';
206
213
  repoVisibility?: 'private' | 'public';
@@ -229,7 +229,7 @@ export declare function applyTreeseedSafeRepairs(tenantRoot: string): TreeseedRe
229
229
  export declare function resolveTreeseedMachineEnvironmentValues(tenantRoot: any, scope: any): {};
230
230
  export declare function setTreeseedMachineEnvironmentValue(tenantRoot: any, scope: any, entry: any, value: any): any;
231
231
  export declare function collectTreeseedEnvironmentContext(tenantRoot: any): import("../../platform/environment.ts").TreeseedResolvedEnvironmentRegistry;
232
- export declare function collectTreeseedConfigSeedValues(tenantRoot: any, scope: any, env?: NodeJS.ProcessEnv): {
232
+ export declare function collectTreeseedConfigSeedValues(tenantRoot: any, scope: any, env?: NodeJS.ProcessEnv, valuesOverlay?: {}): {
233
233
  [k: string]: unknown;
234
234
  };
235
235
  export declare function resolveTreeseedLaunchEnvironment({ tenantRoot, scope, baseEnv, overrides, }: {
@@ -300,9 +300,10 @@ export declare function ensureTreeseedActVerificationTooling({ tenantRoot, insta
300
300
  actVerificationReady: any;
301
301
  remediation: string[];
302
302
  };
303
- export declare function checkTreeseedProviderConnections({ tenantRoot, scope, env }?: {
303
+ export declare function checkTreeseedProviderConnections({ tenantRoot, scope, env, valuesOverlay }?: {
304
304
  scope?: string | undefined;
305
305
  env?: NodeJS.ProcessEnv | undefined;
306
+ valuesOverlay?: {} | undefined;
306
307
  }): Promise<{
307
308
  scope: string;
308
309
  ok: boolean;
@@ -314,11 +315,12 @@ export declare function checkTreeseedProviderConnections({ tenantRoot, scope, en
314
315
  issues: any[];
315
316
  }>;
316
317
  export declare function formatTreeseedProviderConnectionReport(report: any): string;
317
- export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, execution, concurrency, onProgress, }: {
318
+ export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, valuesOverlay, execution, concurrency, onProgress, }: {
318
319
  tenantRoot: string;
319
320
  scope?: TreeseedConfigScope;
320
321
  dryRun?: boolean;
321
322
  repository?: string | null;
323
+ valuesOverlay?: Record<string, string | undefined>;
322
324
  execution?: 'parallel' | 'sequential';
323
325
  concurrency?: number;
324
326
  onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
@@ -1452,7 +1452,7 @@ function collectTreeseedEnvironmentContext(tenantRoot) {
1452
1452
  tenantConfig
1453
1453
  });
1454
1454
  }
1455
- function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env) {
1455
+ function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env, valuesOverlay = {}) {
1456
1456
  warnDeprecatedTreeseedLocalEnvFiles(tenantRoot);
1457
1457
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
1458
1458
  let machineValues = {};
@@ -1465,7 +1465,8 @@ function collectTreeseedConfigSeedValues(tenantRoot, scope, env = process.env) {
1465
1465
  }
1466
1466
  return filterEnvironmentValuesByRegistry({
1467
1467
  ...machineValues,
1468
- ...nonEmptyEnvironmentValues(env)
1468
+ ...nonEmptyEnvironmentValues(env),
1469
+ ...nonEmptyEnvironmentValues(valuesOverlay)
1469
1470
  }, registry, scope);
1470
1471
  }
1471
1472
  function nonEmptyEnvironmentValues(env = process.env) {
@@ -1912,8 +1913,8 @@ async function checkRailwayConnection({ tenantRoot, env }) {
1912
1913
  throw error;
1913
1914
  }
1914
1915
  }
1915
- async function checkTreeseedProviderConnections({ tenantRoot, scope = "prod", env = process.env } = {}) {
1916
- const values = collectTreeseedConfigSeedValues(tenantRoot, scope, env);
1916
+ async function checkTreeseedProviderConnections({ tenantRoot, scope = "prod", env = process.env, valuesOverlay = {} } = {}) {
1917
+ const values = collectTreeseedConfigSeedValues(tenantRoot, scope, env, valuesOverlay);
1917
1918
  const rawCommandEnv = {
1918
1919
  GH_TOKEN: values.GH_TOKEN,
1919
1920
  CLOUDFLARE_API_TOKEN: values.CLOUDFLARE_API_TOKEN,
@@ -1977,6 +1978,7 @@ async function syncTreeseedGitHubEnvironment({
1977
1978
  scope = "prod",
1978
1979
  dryRun = false,
1979
1980
  repository: repositoryInput,
1981
+ valuesOverlay = {},
1980
1982
  execution = "parallel",
1981
1983
  concurrency = 4,
1982
1984
  onProgress
@@ -1986,7 +1988,10 @@ async function syncTreeseedGitHubEnvironment({
1986
1988
  throw new Error("Unable to determine the GitHub repository from the origin remote.");
1987
1989
  }
1988
1990
  const registry = collectTreeseedEnvironmentContext(tenantRoot);
1989
- const values = resolveTreeseedMachineEnvironmentValues(tenantRoot, scope);
1991
+ const values = {
1992
+ ...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
1993
+ ...nonEmptyEnvironmentValues(valuesOverlay)
1994
+ };
1990
1995
  const relevant = registry.entries.filter((entry) => entry.scopes.includes(scope));
1991
1996
  const ghToken = values.GH_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
1992
1997
  const ghEnv = ghToken ? {
@@ -364,8 +364,12 @@ function buildLocalRuntimeVars(deployConfig, state, target, env) {
364
364
  TREESEED_BETTER_AUTH_SECRET: envValue(env, "TREESEED_BETTER_AUTH_SECRET") ?? state.generatedSecrets?.TREESEED_BETTER_AUTH_SECRET ?? state.generatedSecrets?.TREESEED_FORM_TOKEN_SECRET ?? "treeseed-local-better-auth-secret-minimum-32-characters",
365
365
  TREESEED_EDITORIAL_PREVIEW_SECRET: envValue(env, "TREESEED_EDITORIAL_PREVIEW_SECRET") ?? state.generatedSecrets?.TREESEED_EDITORIAL_PREVIEW_SECRET ?? "treeseed-local-editorial-preview-secret",
366
366
  TREESEED_FORMS_LOCAL_BYPASS_CLOUDFLARE_GUARDS: envValue(env, "TREESEED_FORMS_LOCAL_BYPASS_CLOUDFLARE_GUARDS") ?? "",
367
- TREESEED_SMTP_HOST: envValue(env, "TREESEED_SMTP_HOST") ?? "127.0.0.1",
368
- TREESEED_SMTP_PORT: envValue(env, "TREESEED_SMTP_PORT") ?? "1025"
367
+ TREESEED_MAILPIT_SMTP_HOST: "127.0.0.1",
368
+ TREESEED_MAILPIT_SMTP_PORT: "1025",
369
+ TREESEED_SMTP_HOST: "127.0.0.1",
370
+ TREESEED_SMTP_PORT: "1025",
371
+ TREESEED_SMTP_USERNAME: "",
372
+ TREESEED_SMTP_PASSWORD: ""
369
373
  };
370
374
  }
371
375
  function buildSecretMap(deployConfig, state) {
@@ -146,10 +146,11 @@ export declare function ensureGitHubSecrets(tenantRoot: any, { dryRun }?: {
146
146
  existing: string[];
147
147
  created: string[];
148
148
  }>;
149
- export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose }?: {
149
+ export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose, valuesOverlay }?: {
150
150
  dryRun?: boolean | undefined;
151
151
  scope?: string | undefined;
152
152
  purpose?: string | undefined;
153
+ valuesOverlay?: {} | undefined;
153
154
  }): Promise<{
154
155
  repository: string | null;
155
156
  secrets: {
@@ -187,8 +188,9 @@ export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope
187
188
  skipped?: undefined;
188
189
  mode?: undefined;
189
190
  }>;
190
- export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun }?: {
191
+ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun, valuesOverlay }?: {
191
192
  dryRun?: boolean | undefined;
193
+ valuesOverlay?: {} | undefined;
192
194
  }): Promise<{
193
195
  mode: string;
194
196
  workflow: {
@@ -413,7 +413,12 @@ function formatMissingSecretsReport(repository, missingSecrets, reason = "missin
413
413
  async function ensureGitHubSecrets(tenantRoot, { dryRun = false } = {}) {
414
414
  return (await ensureGitHubEnvironment(tenantRoot, { dryRun })).secrets;
415
415
  }
416
- async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save" } = {}) {
416
+ function nonEmptyValues(values = {}) {
417
+ return Object.fromEntries(
418
+ Object.entries(values).filter(([, value]) => typeof value === "string" && value.length > 0)
419
+ );
420
+ }
421
+ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save", valuesOverlay = {} } = {}) {
417
422
  if (isGitHubAutomationStubbed()) {
418
423
  return {
419
424
  repository: maybeResolveGitHubRepositorySlug(tenantRoot),
@@ -449,8 +454,16 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
449
454
  const existingVariables = await listGitHubRepositoryVariableNames(repository, { client });
450
455
  const missingRemote = requiredSecrets.filter((name) => !existingSecrets.has(name));
451
456
  const missingRemoteVariables = requiredVariables.filter((name) => !existingVariables.has(name));
452
- const missingLocal = missingRemote.filter((name) => !envOrNull(name)).map((name) => ({ name, localEnvPresent: false, remotePresent: false }));
453
- const missingLocalVariables = missingRemoteVariables.filter((name) => !envOrNull(name)).map((name) => ({ name, localEnvPresent: false, remotePresent: false }));
457
+ const resolvedValues = {
458
+ ...process.env,
459
+ ...nonEmptyValues(valuesOverlay)
460
+ };
461
+ const localValue = (name) => {
462
+ const value = resolvedValues[name];
463
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : "";
464
+ };
465
+ const missingLocal = missingRemote.filter((name) => !localValue(name)).map((name) => ({ name, localEnvPresent: false, remotePresent: false }));
466
+ const missingLocalVariables = missingRemoteVariables.filter((name) => !localValue(name)).map((name) => ({ name, localEnvPresent: false, remotePresent: false }));
454
467
  if (missingLocal.length > 0 || missingLocalVariables.length > 0) {
455
468
  throw new Error(formatMissingSecretsReport(repository, [...missingLocal, ...missingLocalVariables]));
456
469
  }
@@ -460,7 +473,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
460
473
  createdSecrets.push(name);
461
474
  continue;
462
475
  }
463
- await upsertGitHubRepositorySecret(repository, name, envOrNull(name) ?? "", { client });
476
+ await upsertGitHubRepositorySecret(repository, name, localValue(name), { client });
464
477
  createdSecrets.push(name);
465
478
  }
466
479
  const createdVariables = [];
@@ -469,7 +482,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
469
482
  createdVariables.push(name);
470
483
  continue;
471
484
  }
472
- await upsertGitHubRepositoryVariable(repository, name, envOrNull(name) ?? "", { client });
485
+ await upsertGitHubRepositoryVariable(repository, name, localValue(name), { client });
473
486
  createdVariables.push(name);
474
487
  }
475
488
  return {
@@ -484,9 +497,9 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
484
497
  }
485
498
  };
486
499
  }
487
- async function ensureGitHubDeployAutomation(tenantRoot, { dryRun = false } = {}) {
500
+ async function ensureGitHubDeployAutomation(tenantRoot, { dryRun = false, valuesOverlay = {} } = {}) {
488
501
  const workflows = ensureStandardizedGitHubWorkflows(tenantRoot);
489
- const environment = await ensureGitHubEnvironment(tenantRoot, { dryRun });
502
+ const environment = await ensureGitHubEnvironment(tenantRoot, { dryRun, valuesOverlay });
490
503
  return {
491
504
  mode: getGitHubAutomationMode(),
492
505
  workflow: workflows[0],
@@ -1,4 +1,4 @@
1
- import { checkTreeseedProviderConnections } from './config-runtime.ts';
1
+ import { checkTreeseedProviderConnections, syncTreeseedGitHubEnvironment } from './config-runtime.ts';
2
2
  import { configuredRailwayServices, deployRailwayService, ensureRailwayScheduledJobs, verifyRailwayScheduledJobs } from './railway-deploy.ts';
3
3
  import { buildKnowledgeCoopKnowledgePackPackage, buildKnowledgeCoopTemplatePackage } from './knowledge-coop-packaging.ts';
4
4
  export type KnowledgeCoopLaunchFailurePhase = 'repo_provision_failed' | 'content_bootstrap_failed' | 'workflow_bootstrap_failed' | 'hosting_registration_failed' | 'runtime_connection_failed';
@@ -20,6 +20,40 @@ export interface KnowledgeCoopManagedLaunchInput {
20
20
  contactEmail?: string | null;
21
21
  enableDefaultAgents?: boolean;
22
22
  preserveWorkingTree?: boolean;
23
+ cloudflareHost?: KnowledgeCoopCloudflareHostLaunchInput | null;
24
+ processingHost?: KnowledgeCoopProcessingHostLaunchInput | null;
25
+ }
26
+ export interface KnowledgeCoopCloudflareHostConfig {
27
+ CLOUDFLARE_API_TOKEN?: string;
28
+ CLOUDFLARE_ACCOUNT_ID?: string;
29
+ TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME?: string;
30
+ TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME?: string;
31
+ TREESEED_CONTENT_BUCKET_NAME?: string;
32
+ TREESEED_CONTENT_BUCKET_BINDING?: string;
33
+ TREESEED_PUBLIC_TURNSTILE_SITE_KEY?: string;
34
+ TREESEED_TURNSTILE_SECRET_KEY?: string;
35
+ environments?: Partial<Record<'staging' | 'prod', Record<string, unknown>>>;
36
+ [key: string]: unknown;
37
+ }
38
+ export interface KnowledgeCoopCloudflareHostLaunchInput {
39
+ mode: 'team_owned' | 'treeseed_managed';
40
+ hostId?: string | null;
41
+ targetEnvironments?: Array<'local' | 'staging' | 'prod'>;
42
+ config?: KnowledgeCoopCloudflareHostConfig | null;
43
+ }
44
+ export interface KnowledgeCoopProcessingHostConfig {
45
+ RAILWAY_API_TOKEN?: string;
46
+ TREESEED_RAILWAY_WORKSPACE?: string;
47
+ TREESEED_RAILWAY_API_URL?: string;
48
+ TREESEED_WORKER_POOL_SCALER?: string;
49
+ environments?: Partial<Record<'staging' | 'prod', Record<string, unknown>>>;
50
+ [key: string]: unknown;
51
+ }
52
+ export interface KnowledgeCoopProcessingHostLaunchInput {
53
+ mode: 'team_owned' | 'treeseed_managed';
54
+ hostId?: string | null;
55
+ targetEnvironments?: Array<'local' | 'staging' | 'prod'>;
56
+ config?: KnowledgeCoopProcessingHostConfig | null;
23
57
  }
24
58
  export interface KnowledgeCoopLaunchPhaseRecord {
25
59
  phase: string;
@@ -54,6 +88,7 @@ export interface KnowledgeCoopManagedLaunchResult {
54
88
  existing: string[];
55
89
  created: string[];
56
90
  };
91
+ environmentSync?: Array<Awaited<ReturnType<typeof syncTreeseedGitHubEnvironment>>>;
57
92
  };
58
93
  cloudflare: {
59
94
  staging: ReturnType<typeof provisionCloudflareResources>;
@@ -85,5 +120,7 @@ export interface KnowledgeCoopLaunchPreflightReport {
85
120
  railway: boolean;
86
121
  };
87
122
  }
88
- export declare function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot?: string): Promise<KnowledgeCoopLaunchPreflightReport>;
123
+ export declare function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot?: string, { valuesOverlay }?: {
124
+ valuesOverlay?: Record<string, string | undefined>;
125
+ }): Promise<KnowledgeCoopLaunchPreflightReport>;
89
126
  export declare function executeKnowledgeCoopManagedLaunch(input: KnowledgeCoopManagedLaunchInput): Promise<KnowledgeCoopManagedLaunchResult>;
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
6
6
  import { collectTreeseedReconcileStatus, reconcileTreeseedTarget } from "../../reconcile/index.js";
7
- import { checkTreeseedProviderConnections, collectTreeseedConfigSeedValues } from "./config-runtime.js";
7
+ import { checkTreeseedProviderConnections, collectTreeseedConfigSeedValues, syncTreeseedGitHubEnvironment } from "./config-runtime.js";
8
8
  import { createPersistentDeployTarget, runRemoteD1Migrations, finalizeDeploymentState } from "./deploy.js";
9
9
  import {
10
10
  createGitHubRepository,
@@ -545,6 +545,48 @@ function appendPhase(phases, phase, status, detail) {
545
545
  timestamp: nowIso()
546
546
  });
547
547
  }
548
+ function stringValue(value) {
549
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : "";
550
+ }
551
+ function overlayValue(target, key, value) {
552
+ const next = stringValue(value);
553
+ if (next) {
554
+ target[key] = next;
555
+ }
556
+ }
557
+ function buildCloudflareHostEnvironmentOverlay(input, scope) {
558
+ const config = input.cloudflareHost?.config ?? {};
559
+ const environmentConfig = config.environments?.[scope] ?? {};
560
+ const projectSlug = slugify(input.projectSlug, "project");
561
+ const overlay = {};
562
+ for (const [key, value] of Object.entries(config)) {
563
+ if (key === "environments") continue;
564
+ overlayValue(overlay, key, value);
565
+ }
566
+ for (const [key, value] of Object.entries(environmentConfig)) {
567
+ overlayValue(overlay, key, value);
568
+ }
569
+ overlay.CLOUDFLARE_ACCOUNT_ID = overlay.CLOUDFLARE_ACCOUNT_ID || overlay.TREESEED_CLOUDFLARE_ACCOUNT_ID || "";
570
+ overlayValue(overlay, "TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME", overlay.TREESEED_CLOUDFLARE_PAGES_PROJECT_NAME || projectSlug);
571
+ overlayValue(overlay, "TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME", overlay.TREESEED_CLOUDFLARE_PAGES_PREVIEW_PROJECT_NAME || `${projectSlug}-staging`);
572
+ overlayValue(overlay, "TREESEED_CONTENT_BUCKET_NAME", overlay.TREESEED_CONTENT_BUCKET_NAME || `${projectSlug}-content`);
573
+ overlayValue(overlay, "TREESEED_CONTENT_BUCKET_BINDING", overlay.TREESEED_CONTENT_BUCKET_BINDING || "TREESEED_CONTENT_BUCKET");
574
+ return overlay;
575
+ }
576
+ function buildProcessingHostEnvironmentOverlay(input, scope) {
577
+ const config = input.processingHost?.config ?? {};
578
+ const environmentConfig = config.environments?.[scope] ?? {};
579
+ const overlay = {};
580
+ for (const [key, value] of Object.entries(config)) {
581
+ if (key === "environments") continue;
582
+ overlayValue(overlay, key, value);
583
+ }
584
+ for (const [key, value] of Object.entries(environmentConfig)) {
585
+ overlayValue(overlay, key, value);
586
+ }
587
+ overlayValue(overlay, "TREESEED_WORKER_POOL_SCALER", overlay.TREESEED_WORKER_POOL_SCALER || "railway");
588
+ return overlay;
589
+ }
548
590
  function scaffoldKnowledgeCoopSource(projectRoot, input) {
549
591
  const templateId = input.sourceKind === "template" ? slugify(input.sourceRef ?? "starter-basic", "starter-basic") : "starter-basic";
550
592
  const templateCatalogEnv = { TREESEED_TEMPLATE_CATALOG_URL: currentTemplateCatalogUrl() };
@@ -577,8 +619,8 @@ function scaffoldKnowledgeCoopSource(projectRoot, input) {
577
619
  env: templateCatalogEnv
578
620
  });
579
621
  }
580
- async function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot = process.cwd()) {
581
- const values = collectTreeseedConfigSeedValues(tenantRoot, "prod", process.env);
622
+ async function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot = process.cwd(), { valuesOverlay = {} } = {}) {
623
+ const values = collectTreeseedConfigSeedValues(tenantRoot, "prod", process.env, valuesOverlay);
582
624
  const requiredConfig = [
583
625
  ["TREESEED_BETTER_AUTH_SECRET"],
584
626
  ["TREESEED_AGENT_POOL_MIN_WORKERS"],
@@ -594,7 +636,7 @@ async function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot = proc
594
636
  const value = values[name];
595
637
  return typeof value === "string" && value.trim().length > 0;
596
638
  })).map((group) => group.join(" or "));
597
- const providerChecks = await checkTreeseedProviderConnections({ tenantRoot, scope: "prod", env: process.env });
639
+ const providerChecks = await checkTreeseedProviderConnections({ tenantRoot, scope: "prod", env: process.env, valuesOverlay });
598
640
  const commands = {
599
641
  git: commandAvailable("git"),
600
642
  gh: commandAvailable("gh"),
@@ -611,7 +653,15 @@ async function validateKnowledgeCoopManagedLaunchPrerequisites(tenantRoot = proc
611
653
  }
612
654
  async function executeKnowledgeCoopManagedLaunch(input) {
613
655
  const phases = [];
614
- const preflight = await validateKnowledgeCoopManagedLaunchPrerequisites(process.cwd());
656
+ const prodEnvOverlay = {
657
+ ...buildCloudflareHostEnvironmentOverlay(input, "prod"),
658
+ ...buildProcessingHostEnvironmentOverlay(input, "prod")
659
+ };
660
+ const stagingEnvOverlay = {
661
+ ...buildCloudflareHostEnvironmentOverlay(input, "staging"),
662
+ ...buildProcessingHostEnvironmentOverlay(input, "staging")
663
+ };
664
+ const preflight = await validateKnowledgeCoopManagedLaunchPrerequisites(process.cwd(), { valuesOverlay: prodEnvOverlay });
615
665
  if (!preflight.ok) {
616
666
  throw new KnowledgeCoopLaunchError(
617
667
  "runtime_connection_failed",
@@ -646,24 +696,35 @@ async function executeKnowledgeCoopManagedLaunch(input) {
646
696
  commitMessage: `Initialize ${input.projectName}`
647
697
  });
648
698
  pushDefaultWorkstreamBranch(workingRoot);
649
- const workflows = await ensureGitHubDeployAutomation(workingRoot);
699
+ const workflows = await ensureGitHubDeployAutomation(workingRoot, { valuesOverlay: prodEnvOverlay });
700
+ const githubEnvironmentSync = [];
701
+ for (const [scope, valuesOverlay] of [["staging", stagingEnvOverlay], ["prod", prodEnvOverlay]]) {
702
+ githubEnvironmentSync.push(await syncTreeseedGitHubEnvironment({
703
+ tenantRoot: workingRoot,
704
+ scope,
705
+ repository: repository.slug,
706
+ valuesOverlay,
707
+ execution: "sequential"
708
+ }));
709
+ }
710
+ const workflowSummary = { ...workflows, environmentSync: githubEnvironmentSync };
650
711
  appendPhase(phases, "workflow_bootstrap", "completed", "Configured GitHub workflows, secrets, and variables.");
651
712
  appendPhase(phases, "hosting_registration", "running", "Provisioning Cloudflare resources and deploy state.");
652
713
  const staging = await reconcileTreeseedTarget({
653
714
  tenantRoot: workingRoot,
654
715
  target: createPersistentDeployTarget("staging"),
655
- env: process.env
716
+ env: { ...process.env, ...stagingEnvOverlay }
656
717
  });
657
718
  const prod = await reconcileTreeseedTarget({
658
719
  tenantRoot: workingRoot,
659
720
  target: createPersistentDeployTarget("prod"),
660
- env: process.env
721
+ env: { ...process.env, ...prodEnvOverlay }
661
722
  });
662
723
  runRemoteD1Migrations(workingRoot, { scope: "prod" });
663
724
  const verification = await collectTreeseedReconcileStatus({
664
725
  tenantRoot: workingRoot,
665
726
  target: createPersistentDeployTarget("prod"),
666
- env: process.env
727
+ env: { ...process.env, ...prodEnvOverlay }
667
728
  });
668
729
  appendPhase(phases, "hosting_registration", "completed", "Provisioned Cloudflare resources.");
669
730
  const launchConfig = loadCliDeployConfig(workingRoot);
@@ -674,14 +735,15 @@ async function executeKnowledgeCoopManagedLaunch(input) {
674
735
  let railwayVerification = [];
675
736
  if (managedRuntime) {
676
737
  appendPhase(phases, "runtime_connection", "running", "Deploying Railway services and registering runtime connectivity.");
677
- validateRailwayDeployPrerequisites(workingRoot, "prod");
738
+ const railwayEnv = { ...process.env, ...prodEnvOverlay };
739
+ validateRailwayDeployPrerequisites(workingRoot, "prod", { env: railwayEnv });
678
740
  services = configuredRailwayServices(workingRoot, "prod");
679
741
  deployments = [];
680
742
  for (const service of services) {
681
- deployments.push(await deployRailwayService(workingRoot, service));
743
+ deployments.push(await deployRailwayService(workingRoot, service, { env: railwayEnv }));
682
744
  }
683
- schedules = await ensureRailwayScheduledJobs(workingRoot, "prod");
684
- railwayVerification = await verifyRailwayScheduledJobs(workingRoot, "prod");
745
+ schedules = await ensureRailwayScheduledJobs(workingRoot, "prod", { env: railwayEnv });
746
+ railwayVerification = await verifyRailwayScheduledJobs(workingRoot, "prod", { env: railwayEnv });
685
747
  finalizeDeploymentState(workingRoot, { scope: "prod", serviceResults: deployments });
686
748
  appendPhase(phases, "runtime_connection", "completed", "Deployed Railway services and recorded runtime readiness.");
687
749
  } else {
@@ -734,7 +796,7 @@ async function executeKnowledgeCoopManagedLaunch(input) {
734
796
  stagingBranch: initResult.stagingBranch,
735
797
  visibility: repository.visibility
736
798
  },
737
- workflows,
799
+ workflows: workflowSummary,
738
800
  cloudflare: {
739
801
  staging,
740
802
  prod,
@@ -1,4 +1,5 @@
1
1
  export { TRESEED_OPERATION_SPECS, findTreeseedOperation, listTreeseedOperationNames, } from './operations-registry.ts';
2
+ export { collectTreeseedConfigSeedValues } from './operations/services/config-runtime.ts';
2
3
  export { TreeseedOperationsSdk } from './operations/runtime.ts';
3
4
  export type { TreeseedOperationContext, TreeseedOperationImplementation, TreeseedOperationId, TreeseedOperationMetadata, TreeseedOperationProvider, TreeseedOperationProviderId, TreeseedOperationRequest, TreeseedOperationResult, TreeseedOperationGroup, } from './operations-types.ts';
4
5
  export { TreeseedOperationError } from './operations-types.ts';
@@ -3,6 +3,7 @@ import {
3
3
  findTreeseedOperation,
4
4
  listTreeseedOperationNames
5
5
  } from "./operations-registry.js";
6
+ import { collectTreeseedConfigSeedValues } from "./operations/services/config-runtime.js";
6
7
  import { TreeseedOperationsSdk } from "./operations/runtime.js";
7
8
  import { TreeseedOperationError } from "./operations-types.js";
8
9
  import { TreeseedWorkflowSdk } from "./workflow.js";
@@ -11,6 +12,7 @@ export {
11
12
  TreeseedOperationError,
12
13
  TreeseedOperationsSdk,
13
14
  TreeseedWorkflowSdk,
15
+ collectTreeseedConfigSeedValues,
14
16
  findTreeseedOperation,
15
17
  listTreeseedOperationNames
16
18
  };
@@ -121,6 +121,48 @@ export interface ProjectHosting {
121
121
  createdAt: string;
122
122
  updatedAt: string;
123
123
  }
124
+ export interface EncryptedWebHostPayload {
125
+ version: number;
126
+ algorithm: string;
127
+ kdf: {
128
+ algorithm: string;
129
+ opsLimit?: number;
130
+ memLimit?: number;
131
+ [key: string]: unknown;
132
+ };
133
+ salt: string;
134
+ nonce: string;
135
+ ciphertext: string;
136
+ }
137
+ export type TeamWebHostProvider = 'cloudflare';
138
+ export type TeamWebHostOwnership = 'team_owned' | 'treeseed_managed';
139
+ export interface TeamWebHost {
140
+ id: string;
141
+ teamId: string;
142
+ provider: TeamWebHostProvider;
143
+ ownership: TeamWebHostOwnership;
144
+ name: string;
145
+ accountLabel: string | null;
146
+ allowedEnvironments: ProjectEnvironmentName[];
147
+ status: string;
148
+ encryptedPayload: EncryptedWebHostPayload | null;
149
+ metadata?: Record<string, unknown>;
150
+ createdById: string | null;
151
+ updatedById: string | null;
152
+ createdAt: string;
153
+ updatedAt: string;
154
+ }
155
+ export interface UpsertTeamWebHostRequest {
156
+ id?: string;
157
+ provider?: TeamWebHostProvider;
158
+ ownership?: TeamWebHostOwnership;
159
+ name: string;
160
+ accountLabel?: string | null;
161
+ allowedEnvironments?: ProjectEnvironmentName[];
162
+ status?: string;
163
+ encryptedPayload?: EncryptedWebHostPayload | null;
164
+ metadata?: Record<string, unknown> | null;
165
+ }
124
166
  export interface ProjectEnvironment {
125
167
  id: string;
126
168
  projectId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/sdk",
3
- "version": "0.6.29",
3
+ "version": "0.6.31",
4
4
  "description": "Shared Treeseed SDK for content-backed and D1-backed object models.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -208,7 +208,6 @@ __WORKING_DIRECTORY_BLOCK__
208
208
  needs.classify.outputs.release_tag == 'true'
209
209
  permissions:
210
210
  contents: read
211
- deployments: write
212
211
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
213
212
  env:
214
213
  TREESEED_BOOTSTRAP_MODE: auto
@@ -352,7 +351,6 @@ __WORKING_DIRECTORY_BLOCK__
352
351
  - provision
353
352
  permissions:
354
353
  contents: read
355
- deployments: write
356
354
  if: |
357
355
  needs.classify.outputs.workflow_action == 'deploy_code' ||
358
356
  needs.classify.outputs.code_changed == 'true' ||
@@ -494,7 +492,6 @@ __WORKING_DIRECTORY_BLOCK__
494
492
  - deploy-code
495
493
  permissions:
496
494
  contents: read
497
- deployments: write
498
495
  if: |
499
496
  always() &&
500
497
  needs.provision.result == 'success' &&
@@ -645,7 +642,6 @@ __WORKING_DIRECTORY_BLOCK__
645
642
  (needs['publish-content'].result == 'success' || needs['publish-content'].result == 'skipped')
646
643
  permissions:
647
644
  contents: read
648
- deployments: write
649
645
  environment: ${{ needs.classify.outputs.scope == 'prod' && 'production' || 'staging' }}
650
646
  env:
651
647
  TREESEED_BOOTSTRAP_MODE: auto