@treeseed/sdk 0.6.0 → 0.6.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.
- package/dist/operations/services/bootstrap-runner.d.ts +33 -0
- package/dist/operations/services/bootstrap-runner.js +136 -0
- package/dist/operations/services/config-runtime.d.ts +27 -8
- package/dist/operations/services/config-runtime.js +297 -124
- package/dist/operations/services/github-api.d.ts +33 -0
- package/dist/operations/services/github-api.js +118 -4
- package/dist/operations/services/github-automation.d.ts +30 -0
- package/dist/operations/services/github-automation.js +107 -1
- package/dist/operations/services/project-platform.d.ts +38 -2
- package/dist/operations/services/project-platform.js +281 -9
- package/dist/operations/services/railway-deploy.d.ts +6 -2
- package/dist/operations/services/railway-deploy.js +26 -18
- package/dist/operations/services/runtime-tools.d.ts +0 -2
- package/dist/operations/services/runtime-tools.js +0 -2
- package/dist/platform/env.yaml +68 -96
- package/dist/platform/environment.js +51 -0
- package/dist/reconcile/bootstrap-systems.d.ts +32 -0
- package/dist/reconcile/bootstrap-systems.js +175 -0
- package/dist/reconcile/builtin-adapters.js +24 -9
- package/dist/reconcile/desired-state.js +16 -14
- package/dist/reconcile/engine.d.ts +9 -4
- package/dist/reconcile/engine.js +57 -14
- package/dist/reconcile/index.d.ts +1 -0
- package/dist/reconcile/index.js +1 -0
- package/dist/scripts/config-treeseed.js +30 -0
- package/dist/scripts/package-tools.js +0 -2
- package/dist/scripts/tenant-deploy.js +16 -36
- package/dist/scripts/test-cloudflare-local.js +0 -2
- package/dist/workflow/operations.js +23 -4
- package/dist/workflow.d.ts +5 -0
- package/package.json +1 -1
- package/templates/github/deploy.workflow.yml +15 -15
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type TreeseedBootstrapExecution = 'parallel' | 'sequential';
|
|
2
|
+
export type TreeseedBootstrapStream = 'stdout' | 'stderr';
|
|
3
|
+
export type TreeseedBootstrapWriter = (line: string, stream?: TreeseedBootstrapStream) => void;
|
|
4
|
+
export type TreeseedBootstrapTaskPrefix = {
|
|
5
|
+
scope: string;
|
|
6
|
+
system: string;
|
|
7
|
+
task: string;
|
|
8
|
+
stage: string;
|
|
9
|
+
};
|
|
10
|
+
export type TreeseedBootstrapDagNode<TResult = unknown> = {
|
|
11
|
+
id: string;
|
|
12
|
+
dependencies?: string[];
|
|
13
|
+
run: () => Promise<TResult> | TResult;
|
|
14
|
+
};
|
|
15
|
+
export declare function formatTreeseedBootstrapPrefix(prefix: TreeseedBootstrapTaskPrefix): string;
|
|
16
|
+
export declare function formatTreeseedBootstrapLine(prefix: TreeseedBootstrapTaskPrefix, line: string): string;
|
|
17
|
+
export declare function writeTreeseedBootstrapLine(write: TreeseedBootstrapWriter | undefined, prefix: TreeseedBootstrapTaskPrefix, line: string, stream?: TreeseedBootstrapStream): void;
|
|
18
|
+
export declare function runTreeseedBootstrapDag<TResult = unknown>({ nodes, execution, }: {
|
|
19
|
+
nodes: Array<TreeseedBootstrapDagNode<TResult>>;
|
|
20
|
+
execution?: TreeseedBootstrapExecution;
|
|
21
|
+
}): Promise<Map<string, TResult>>;
|
|
22
|
+
export declare function sleep(milliseconds: number): Promise<void>;
|
|
23
|
+
export declare function runPrefixedCommand(command: string, args: string[], { cwd, env, input, write, prefix, }: {
|
|
24
|
+
cwd: string;
|
|
25
|
+
env?: NodeJS.ProcessEnv | Record<string, string | undefined>;
|
|
26
|
+
input?: string;
|
|
27
|
+
write?: TreeseedBootstrapWriter;
|
|
28
|
+
prefix: TreeseedBootstrapTaskPrefix;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
status: number | null;
|
|
31
|
+
stdout: string;
|
|
32
|
+
stderr: string;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
function formatTreeseedBootstrapPrefix(prefix) {
|
|
3
|
+
return `[${prefix.scope}][${prefix.system}][${prefix.task}][${prefix.stage}]`;
|
|
4
|
+
}
|
|
5
|
+
function formatTreeseedBootstrapLine(prefix, line) {
|
|
6
|
+
return `${formatTreeseedBootstrapPrefix(prefix)} ${line}`;
|
|
7
|
+
}
|
|
8
|
+
function writeTreeseedBootstrapLine(write, prefix, line, stream = "stdout") {
|
|
9
|
+
const formatted = formatTreeseedBootstrapLine(prefix, line);
|
|
10
|
+
if (write) {
|
|
11
|
+
write(formatted, stream);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
(stream === "stderr" ? process.stderr : process.stdout).write(`${formatted}
|
|
15
|
+
`);
|
|
16
|
+
}
|
|
17
|
+
function dependencyLevels(nodes) {
|
|
18
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
19
|
+
const remaining = new Map(byId);
|
|
20
|
+
const completed = /* @__PURE__ */ new Set();
|
|
21
|
+
const levels = [];
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
for (const dependency of node.dependencies ?? []) {
|
|
24
|
+
if (!byId.has(dependency)) {
|
|
25
|
+
throw new Error(`Bootstrap DAG node "${node.id}" depends on missing node "${dependency}".`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
while (remaining.size > 0) {
|
|
30
|
+
const ready = [...remaining.values()].filter(
|
|
31
|
+
(node) => (node.dependencies ?? []).every((dependency) => completed.has(dependency))
|
|
32
|
+
);
|
|
33
|
+
if (ready.length === 0) {
|
|
34
|
+
throw new Error("Bootstrap DAG contains a dependency cycle.");
|
|
35
|
+
}
|
|
36
|
+
for (const node of ready) {
|
|
37
|
+
remaining.delete(node.id);
|
|
38
|
+
completed.add(node.id);
|
|
39
|
+
}
|
|
40
|
+
levels.push(ready);
|
|
41
|
+
}
|
|
42
|
+
return levels;
|
|
43
|
+
}
|
|
44
|
+
async function runTreeseedBootstrapDag({
|
|
45
|
+
nodes,
|
|
46
|
+
execution = "parallel"
|
|
47
|
+
}) {
|
|
48
|
+
const results = /* @__PURE__ */ new Map();
|
|
49
|
+
for (const level of dependencyLevels(nodes)) {
|
|
50
|
+
if (execution === "sequential") {
|
|
51
|
+
for (const node of level) {
|
|
52
|
+
results.set(node.id, await Promise.resolve(node.run()));
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
await Promise.all(level.map(async (node) => {
|
|
57
|
+
results.set(node.id, await Promise.resolve(node.run()));
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
async function sleep(milliseconds) {
|
|
63
|
+
if (!Number.isFinite(milliseconds) || milliseconds <= 0) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
67
|
+
}
|
|
68
|
+
async function runPrefixedCommand(command, args, {
|
|
69
|
+
cwd,
|
|
70
|
+
env = process.env,
|
|
71
|
+
input,
|
|
72
|
+
write,
|
|
73
|
+
prefix
|
|
74
|
+
}) {
|
|
75
|
+
return await new Promise((resolvePromise, reject) => {
|
|
76
|
+
const child = spawn(command, args, {
|
|
77
|
+
cwd,
|
|
78
|
+
env: { ...process.env, ...env },
|
|
79
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
80
|
+
});
|
|
81
|
+
let stdout = "";
|
|
82
|
+
let stderr = "";
|
|
83
|
+
let stdoutBuffer = "";
|
|
84
|
+
let stderrBuffer = "";
|
|
85
|
+
const flush = (stream, force = false) => {
|
|
86
|
+
let buffer = stream === "stdout" ? stdoutBuffer : stderrBuffer;
|
|
87
|
+
let newlineIndex = buffer.search(/\r?\n/u);
|
|
88
|
+
while (newlineIndex >= 0) {
|
|
89
|
+
const line = buffer.slice(0, newlineIndex).replace(/\r$/u, "");
|
|
90
|
+
writeTreeseedBootstrapLine(write, prefix, line, stream);
|
|
91
|
+
buffer = buffer.slice(buffer[newlineIndex] === "\r" && buffer[newlineIndex + 1] === "\n" ? newlineIndex + 2 : newlineIndex + 1);
|
|
92
|
+
newlineIndex = buffer.search(/\r?\n/u);
|
|
93
|
+
}
|
|
94
|
+
if (force && buffer.length > 0) {
|
|
95
|
+
writeTreeseedBootstrapLine(write, prefix, buffer.replace(/\r$/u, ""), stream);
|
|
96
|
+
buffer = "";
|
|
97
|
+
}
|
|
98
|
+
if (stream === "stdout") {
|
|
99
|
+
stdoutBuffer = buffer;
|
|
100
|
+
} else {
|
|
101
|
+
stderrBuffer = buffer;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
child.stdout.on("data", (chunk) => {
|
|
105
|
+
const text = String(chunk);
|
|
106
|
+
stdout += text;
|
|
107
|
+
stdoutBuffer += text;
|
|
108
|
+
flush("stdout");
|
|
109
|
+
});
|
|
110
|
+
child.stderr.on("data", (chunk) => {
|
|
111
|
+
const text = String(chunk);
|
|
112
|
+
stderr += text;
|
|
113
|
+
stderrBuffer += text;
|
|
114
|
+
flush("stderr");
|
|
115
|
+
});
|
|
116
|
+
child.on("error", reject);
|
|
117
|
+
child.on("close", (status) => {
|
|
118
|
+
flush("stdout", true);
|
|
119
|
+
flush("stderr", true);
|
|
120
|
+
resolvePromise({ status, stdout, stderr });
|
|
121
|
+
});
|
|
122
|
+
if (input !== void 0) {
|
|
123
|
+
child.stdin.end(input);
|
|
124
|
+
} else {
|
|
125
|
+
child.stdin.end();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
formatTreeseedBootstrapLine,
|
|
131
|
+
formatTreeseedBootstrapPrefix,
|
|
132
|
+
runPrefixedCommand,
|
|
133
|
+
runTreeseedBootstrapDag,
|
|
134
|
+
sleep,
|
|
135
|
+
writeTreeseedBootstrapLine
|
|
136
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RemoteTreeseedConfig } from '../../remote.ts';
|
|
2
2
|
import { TREESEED_ENVIRONMENT_SCOPES, type TreeseedEnvironmentValidation, validateTreeseedEnvironmentValues } from '../../platform/environment.ts';
|
|
3
|
+
import { resolveTreeseedBootstrapSelection, type TreeseedBootstrapSystem } from '../../reconcile/index.ts';
|
|
3
4
|
import { type TreeseedKeyAgentStatus } from './key-agent.ts';
|
|
4
5
|
export { TREESEED_MACHINE_KEY_PASSPHRASE_ENV, TreeseedKeyAgentError } from './key-agent.ts';
|
|
5
6
|
export declare const DEFAULT_TREESEED_API_BASE_URL = "https://api.treeseed.ai";
|
|
@@ -312,14 +313,26 @@ export declare function checkTreeseedProviderConnections({ tenantRoot, scope, en
|
|
|
312
313
|
issues: any[];
|
|
313
314
|
}>;
|
|
314
315
|
export declare function formatTreeseedProviderConnectionReport(report: any): string;
|
|
315
|
-
export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun }
|
|
316
|
-
|
|
317
|
-
|
|
316
|
+
export declare function syncTreeseedGitHubEnvironment({ tenantRoot, scope, dryRun, repository: repositoryInput, execution, concurrency, onProgress, }: {
|
|
317
|
+
tenantRoot: string;
|
|
318
|
+
scope?: TreeseedConfigScope;
|
|
319
|
+
dryRun?: boolean;
|
|
320
|
+
repository?: string | null;
|
|
321
|
+
execution?: 'parallel' | 'sequential';
|
|
322
|
+
concurrency?: number;
|
|
323
|
+
onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
|
|
318
324
|
}): Promise<{
|
|
319
|
-
secrets:
|
|
320
|
-
|
|
325
|
+
secrets: Array<{
|
|
326
|
+
name: string;
|
|
327
|
+
existed: boolean;
|
|
328
|
+
}>;
|
|
329
|
+
variables: Array<{
|
|
330
|
+
name: string;
|
|
331
|
+
existed: boolean;
|
|
332
|
+
}>;
|
|
321
333
|
repository: string;
|
|
322
|
-
scope:
|
|
334
|
+
scope: "local" | "staging" | "prod";
|
|
335
|
+
environment: string;
|
|
323
336
|
}>;
|
|
324
337
|
export declare function syncTreeseedCloudflareEnvironment({ tenantRoot, scope, dryRun }?: {
|
|
325
338
|
scope?: string | undefined;
|
|
@@ -404,14 +417,17 @@ export declare function applyTreeseedConfigValues({ tenantRoot, updates, applyLo
|
|
|
404
417
|
}[];
|
|
405
418
|
sharedStorageMigrations: TreeseedSharedStorageMigrationNotice[];
|
|
406
419
|
};
|
|
407
|
-
export declare function finalizeTreeseedConfig({ tenantRoot, scopes, sync, env, checkConnections, initializePersistent, onProgress, }: {
|
|
420
|
+
export declare function finalizeTreeseedConfig({ tenantRoot, scopes, sync, env, checkConnections, initializePersistent, systems, skipUnavailable, bootstrapExecution, onProgress, }: {
|
|
408
421
|
tenantRoot: string;
|
|
409
422
|
scopes?: TreeseedConfigScope[];
|
|
410
423
|
sync?: 'none' | 'github' | 'cloudflare' | 'railway' | 'all';
|
|
411
424
|
env?: NodeJS.ProcessEnv;
|
|
412
425
|
checkConnections?: boolean;
|
|
413
426
|
initializePersistent?: boolean;
|
|
414
|
-
|
|
427
|
+
systems?: TreeseedBootstrapSystem[] | TreeseedBootstrapSystem;
|
|
428
|
+
skipUnavailable?: boolean;
|
|
429
|
+
bootstrapExecution?: 'parallel' | 'sequential';
|
|
430
|
+
onProgress?: (message: string, stream?: 'stdout' | 'stderr') => void;
|
|
415
431
|
}): Promise<{
|
|
416
432
|
scopes: ("local" | "staging" | "prod")[];
|
|
417
433
|
synced: Record<string, unknown>;
|
|
@@ -437,6 +453,8 @@ export declare function finalizeTreeseedConfig({ tenantRoot, scopes, sync, env,
|
|
|
437
453
|
resourceInventoryByScope: Record<TreeseedConfigScope, Record<string, unknown>>;
|
|
438
454
|
connectionChecks: ReturnType<typeof checkTreeseedProviderConnections>[];
|
|
439
455
|
validationByScope: Record<TreeseedConfigScope, ReturnType<typeof validateTreeseedEnvironmentValues>>;
|
|
456
|
+
bootstrapSystemsByScope: Record<TreeseedConfigScope, ReturnType<typeof resolveTreeseedBootstrapSelection>>;
|
|
457
|
+
githubRepository: Record<string, unknown> | null;
|
|
440
458
|
readinessByScope: Record<TreeseedConfigScope, {
|
|
441
459
|
phase: string;
|
|
442
460
|
configured: boolean;
|
|
@@ -446,6 +464,7 @@ export declare function finalizeTreeseedConfig({ tenantRoot, scopes, sync, env,
|
|
|
446
464
|
warnings: string[];
|
|
447
465
|
checks: Record<string, unknown>;
|
|
448
466
|
}>;
|
|
467
|
+
bootstrapExecution: "parallel" | "sequential";
|
|
449
468
|
}>;
|
|
450
469
|
export declare function collectTreeseedPrintEnvReport({ tenantRoot, scope, env, revealSecrets, }: {
|
|
451
470
|
tenantRoot: string;
|