@treeseed/sdk 0.8.0 → 0.8.2

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.
@@ -315,12 +315,13 @@ export declare function checkTreeseedProviderConnections({ tenantRoot, scope, en
315
315
  issues: any[];
316
316
  }>;
317
317
  export declare function formatTreeseedProviderConnectionReport(report: any): string;
318
- export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, valuesOverlay, execution, concurrency, onProgress, }: {
318
+ export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, valuesOverlay, managedHostMode, execution, concurrency, onProgress, }: {
319
319
  tenantRoot: string;
320
320
  scope?: TreeseedConfigScope;
321
321
  dryRun?: boolean;
322
322
  repository?: string | null;
323
323
  valuesOverlay?: Record<string, string | undefined>;
324
+ managedHostMode?: 'auto' | 'direct' | 'managed';
324
325
  execution?: 'parallel' | 'sequential';
325
326
  concurrency?: number;
326
327
  onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
@@ -52,6 +52,10 @@ import {
52
52
  resolveTreeseedToolBinary,
53
53
  resolveTreeseedToolCommand
54
54
  } from "../../managed-dependencies.js";
55
+ import {
56
+ filterManagedHostGitHubEnvironment,
57
+ usesManagedHostOperationRequests
58
+ } from "./managed-host-security.js";
55
59
  import {
56
60
  assertTreeseedKeyAgentResponse,
57
61
  getTreeseedKeyAgentPaths,
@@ -1979,6 +1983,7 @@ async function syncTreeseedGitHubEnvironment({
1979
1983
  dryRun = false,
1980
1984
  repository: repositoryInput,
1981
1985
  valuesOverlay = {},
1986
+ managedHostMode = "auto",
1982
1987
  execution = "parallel",
1983
1988
  concurrency = 4,
1984
1989
  onProgress
@@ -1992,7 +1997,22 @@ async function syncTreeseedGitHubEnvironment({
1992
1997
  ...resolveTreeseedMachineEnvironmentValues(tenantRoot, scope),
1993
1998
  ...nonEmptyEnvironmentValues(valuesOverlay)
1994
1999
  };
1995
- const relevant = registry.entries.filter((entry) => entry.scopes.includes(scope));
2000
+ const deployConfig = loadCliDeployConfig(tenantRoot);
2001
+ const managedBoundary = managedHostMode === "managed" || managedHostMode === "auto" && usesManagedHostOperationRequests(deployConfig);
2002
+ const allowed = managedBoundary ? filterManagedHostGitHubEnvironment({
2003
+ secrets: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-secret")).map((entry) => entry.id),
2004
+ variables: registry.entries.filter((entry) => entry.scopes.includes(scope) && entry.targets.includes("github-variable")).map((entry) => entry.id)
2005
+ }) : null;
2006
+ const allowedSecrets = allowed ? new Set(allowed.secrets) : null;
2007
+ const allowedVariables = allowed ? new Set(allowed.variables) : null;
2008
+ const relevant = registry.entries.filter((entry) => {
2009
+ if (!entry.scopes.includes(scope)) return false;
2010
+ if (!managedBoundary) return true;
2011
+ if (entry.sensitivity === "secret") {
2012
+ return Boolean(entry.targets.includes("github-secret") && allowedSecrets?.has(entry.id));
2013
+ }
2014
+ return Boolean(entry.targets.includes("github-variable") && allowedVariables?.has(entry.id));
2015
+ });
1996
2016
  const ghToken = values.GH_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
1997
2017
  const ghEnv = ghToken ? {
1998
2018
  GH_TOKEN: ghToken,
@@ -68,22 +68,25 @@ export declare function initializeGitHubRepositoryWorkingTree(cwd: any, reposito
68
68
  pushed: boolean;
69
69
  };
70
70
  export declare function resolveGitRepositoryRoot(tenantRoot: any): any;
71
- export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purpose }?: {
71
+ export declare function requiredGitHubEnvironment(tenantRoot: any, { scope, purpose, managedHostMode }?: {
72
72
  scope?: string | undefined;
73
73
  purpose?: string | undefined;
74
+ managedHostMode?: string | undefined;
74
75
  }): {
75
76
  secrets: string[];
76
77
  variables: string[];
77
78
  };
78
79
  export declare function requiredGitHubSecrets(tenantRoot: any): string[];
79
- export declare function renderDeployWorkflow({ workingDirectory }: {
80
+ export declare function renderDeployWorkflow({ workingDirectory, executionBoundary }: {
80
81
  workingDirectory: any;
82
+ executionBoundary?: string | undefined;
81
83
  }): string;
82
84
  export declare function renderHostedProjectWorkflow({ workingDirectory }: {
83
85
  workingDirectory: any;
84
86
  }): string;
85
87
  export declare function ensureDeployWorkflow(tenantRoot: any): {
86
88
  workingDirectory: string;
89
+ executionBoundary: string;
87
90
  workflowPath: string;
88
91
  changed: boolean;
89
92
  };
@@ -94,6 +97,7 @@ export declare function ensureHostedProjectWorkflow(tenantRoot: any): {
94
97
  };
95
98
  export declare function ensureStandardizedGitHubWorkflows(tenantRoot: any): {
96
99
  workingDirectory: string;
100
+ executionBoundary: string;
97
101
  workflowPath: string;
98
102
  changed: boolean;
99
103
  }[];
@@ -109,11 +113,12 @@ export declare function ensureGitHubSecrets(tenantRoot: any, { dryRun }?: {
109
113
  existing: string[];
110
114
  created: string[];
111
115
  }>;
112
- export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose, valuesOverlay }?: {
116
+ export declare function ensureGitHubEnvironment(tenantRoot: any, { dryRun, scope, purpose, valuesOverlay, managedHostMode }?: {
113
117
  dryRun?: boolean | undefined;
114
118
  scope?: string | undefined;
115
119
  purpose?: string | undefined;
116
120
  valuesOverlay?: {} | undefined;
121
+ managedHostMode?: string | undefined;
117
122
  }): Promise<{
118
123
  repository: null;
119
124
  secrets: {
@@ -144,11 +149,13 @@ export declare function ensureGitHubDeployAutomation(tenantRoot: any, { dryRun,
144
149
  mode: string;
145
150
  workflow: {
146
151
  workingDirectory: string;
152
+ executionBoundary: string;
147
153
  workflowPath: string;
148
154
  changed: boolean;
149
155
  };
150
156
  workflows: {
151
157
  workingDirectory: string;
158
+ executionBoundary: string;
152
159
  workflowPath: string;
153
160
  changed: boolean;
154
161
  }[];
@@ -3,6 +3,10 @@ import { dirname, relative, resolve } from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { resolveTreeseedEnvironmentRegistry } from "../../platform/environment.js";
5
5
  import { packageRoot, loadCliDeployConfig } from "./runtime-tools.js";
6
+ import {
7
+ filterManagedHostGitHubEnvironment,
8
+ usesManagedHostOperationRequests
9
+ } from "./managed-host-security.js";
6
10
  import {
7
11
  createGitHubApiClient,
8
12
  ensureGitHubRepository,
@@ -256,16 +260,18 @@ function resolveGitRepositoryRoot(tenantRoot) {
256
260
  const result = runGit(["rev-parse", "--show-toplevel"], { cwd: tenantRoot, allowFailure: true });
257
261
  return result.status === 0 ? result.stdout.trim() : tenantRoot;
258
262
  }
259
- function requiredGitHubEnvironment(tenantRoot, { scope = "prod", purpose = "save" } = {}) {
263
+ function requiredGitHubEnvironment(tenantRoot, { scope = "prod", purpose = "save", managedHostMode = "auto" } = {}) {
260
264
  const deployConfig = loadCliDeployConfig(tenantRoot);
261
265
  const registry = resolveTreeseedEnvironmentRegistry({ deployConfig });
262
266
  const relevant = registry.entries.filter(
263
267
  (entry) => entry.scopes.includes(scope) && entry.purposes.includes(purpose) && (!entry.isRelevant || entry.isRelevant(registry.context, scope, purpose))
264
268
  );
265
- return {
269
+ const required = {
266
270
  secrets: [...new Set(relevant.filter((entry) => entry.targets.includes("github-secret")).map((entry) => entry.id))],
267
271
  variables: [...new Set(relevant.filter((entry) => entry.targets.includes("github-variable")).map((entry) => entry.id))]
268
272
  };
273
+ const managedBoundary = managedHostMode === "managed" || managedHostMode === "auto" && usesManagedHostOperationRequests(deployConfig);
274
+ return managedBoundary ? filterManagedHostGitHubEnvironment(required) : required;
269
275
  }
270
276
  function requiredGitHubSecrets(tenantRoot) {
271
277
  return requiredGitHubEnvironment(tenantRoot).secrets;
@@ -299,8 +305,11 @@ function renderWorkflowTemplate(templateName, { workingDirectory }) {
299
305
  normalizedWorkingDirectory === "." ? "package-lock.json" : `${normalizedWorkingDirectory}/package-lock.json`
300
306
  );
301
307
  }
302
- function renderDeployWorkflow({ workingDirectory }) {
303
- return renderWorkflowTemplate("deploy.workflow.yml", { workingDirectory });
308
+ function deployWorkflowTemplateName(executionBoundary = "direct") {
309
+ return executionBoundary === "managed" ? "deploy.managed.workflow.yml" : "deploy.workflow.yml";
310
+ }
311
+ function renderDeployWorkflow({ workingDirectory, executionBoundary = "direct" }) {
312
+ return renderWorkflowTemplate(deployWorkflowTemplateName(executionBoundary), { workingDirectory });
304
313
  }
305
314
  function renderHostedProjectWorkflow({ workingDirectory }) {
306
315
  return renderWorkflowTemplate("hosted-project.workflow.yml", { workingDirectory });
@@ -318,10 +327,13 @@ function ensureWorkflowFile(tenantRoot, fileName, expected) {
318
327
  function ensureDeployWorkflow(tenantRoot) {
319
328
  const repositoryRoot = resolveGitRepositoryRoot(tenantRoot);
320
329
  const workingDirectory = relative(repositoryRoot, tenantRoot).replaceAll("\\", "/") || ".";
321
- const expected = renderDeployWorkflow({ workingDirectory });
330
+ const deployConfig = loadCliDeployConfig(tenantRoot);
331
+ const executionBoundary = usesManagedHostOperationRequests(deployConfig) ? "managed" : "direct";
332
+ const expected = renderDeployWorkflow({ workingDirectory, executionBoundary });
322
333
  return {
323
334
  ...ensureWorkflowFile(tenantRoot, "deploy.yml", expected),
324
- workingDirectory
335
+ workingDirectory,
336
+ executionBoundary
325
337
  };
326
338
  }
327
339
  function ensureHostedProjectWorkflow(tenantRoot) {
@@ -370,7 +382,7 @@ function nonEmptyValues(values = {}) {
370
382
  Object.entries(values).filter(([, value]) => typeof value === "string" && value.length > 0)
371
383
  );
372
384
  }
373
- async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save", valuesOverlay = {} } = {}) {
385
+ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "prod", purpose = "save", valuesOverlay = {}, managedHostMode = "auto" } = {}) {
374
386
  const repository = maybeResolveGitHubRepositorySlug(tenantRoot);
375
387
  if (!repository) {
376
388
  if (dryRun) {
@@ -383,7 +395,7 @@ async function ensureGitHubEnvironment(tenantRoot, { dryRun = false, scope = "pr
383
395
  }
384
396
  throw new Error("Unable to determine GitHub repository from the current tenant. Configure an origin remote before syncing GitHub secrets.");
385
397
  }
386
- const required = requiredGitHubEnvironment(tenantRoot, { scope, purpose });
398
+ const required = requiredGitHubEnvironment(tenantRoot, { scope, purpose, managedHostMode });
387
399
  const requiredSecrets = required.secrets;
388
400
  const requiredVariables = required.variables;
389
401
  const client = createGitHubApiClient();
@@ -0,0 +1,67 @@
1
+ import { type TreeseedEnvironmentScope } from '../../platform/environment.ts';
2
+ import type { TreeseedReconcileTarget } from '../../reconcile/contracts.ts';
3
+ export type TreeseedHostingAuditEnvironment = 'current' | 'local' | 'staging' | 'prod';
4
+ export type TreeseedHostingAuditResolvedEnvironment = 'local' | 'staging' | 'prod' | 'preview';
5
+ export type TreeseedHostingAuditHostKind = 'repository' | 'web' | 'processing' | 'email';
6
+ export type TreeseedHostingAuditCheckStatus = 'passed' | 'warning' | 'failed' | 'skipped' | 'repaired';
7
+ export type TreeseedHostingAuditSeverity = 'info' | 'warning' | 'critical';
8
+ export type TreeseedHostingAuditCheck = {
9
+ id: string;
10
+ hostType: TreeseedHostingAuditHostKind | 'platform';
11
+ provider: string;
12
+ category: 'config' | 'identity' | 'resource' | 'connectivity' | 'repair' | 'security';
13
+ status: TreeseedHostingAuditCheckStatus;
14
+ severity: TreeseedHostingAuditSeverity;
15
+ summary: string;
16
+ detail?: string;
17
+ resourceRef?: string;
18
+ repairAvailable?: boolean;
19
+ repaired?: boolean;
20
+ remediation?: string;
21
+ };
22
+ export type TreeseedHostingAuditReport = {
23
+ ok: boolean;
24
+ environment: TreeseedHostingAuditResolvedEnvironment;
25
+ requestedEnvironment: TreeseedHostingAuditEnvironment;
26
+ repairMode: boolean;
27
+ repaired: boolean;
28
+ target: {
29
+ kind: TreeseedReconcileTarget['kind'];
30
+ scope?: string;
31
+ branchName?: string;
32
+ label: string;
33
+ };
34
+ hostKinds: TreeseedHostingAuditHostKind[];
35
+ checkedAt: string;
36
+ checks: TreeseedHostingAuditCheck[];
37
+ missingConfig: Array<{
38
+ key: string;
39
+ hostType: TreeseedHostingAuditHostKind | 'platform';
40
+ severity: TreeseedHostingAuditSeverity;
41
+ summary: string;
42
+ }>;
43
+ resources: Record<string, unknown>;
44
+ warnings: string[];
45
+ blockers: string[];
46
+ nextActions: string[];
47
+ };
48
+ export type TreeseedHostingAuditOptions = {
49
+ tenantRoot: string;
50
+ environment?: TreeseedHostingAuditEnvironment;
51
+ repair?: boolean;
52
+ env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
53
+ valuesOverlay?: Record<string, string | undefined>;
54
+ hostKinds?: TreeseedHostingAuditHostKind[];
55
+ write?: (line: string) => void;
56
+ };
57
+ export declare function resolveTreeseedHostingAuditTarget({ tenantRoot, environment, }: {
58
+ tenantRoot: string;
59
+ environment?: TreeseedHostingAuditEnvironment;
60
+ }): {
61
+ environment: TreeseedHostingAuditResolvedEnvironment;
62
+ scope: TreeseedEnvironmentScope;
63
+ target: TreeseedReconcileTarget;
64
+ branchName: string | null;
65
+ };
66
+ export declare function runTreeseedHostingAudit({ tenantRoot, environment, repair, env, valuesOverlay, hostKinds: requestedHostKinds, write, }: TreeseedHostingAuditOptions): Promise<TreeseedHostingAuditReport>;
67
+ export declare function formatTreeseedHostingAuditReport(report: TreeseedHostingAuditReport): string;