@treeseed/cli 0.10.21 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -116
- package/dist/cli/handlers/audit.js +28 -3
- package/dist/cli/handlers/capacity.js +2 -2
- package/dist/cli/handlers/destroy.js +6 -1
- package/dist/cli/handlers/dev.js +16 -2
- package/dist/cli/handlers/doctor.js +13 -3
- package/dist/cli/handlers/hosting.d.ts +2 -0
- package/dist/cli/handlers/hosting.js +257 -0
- package/dist/cli/handlers/operations.d.ts +2 -0
- package/dist/cli/handlers/operations.js +46 -0
- package/dist/cli/handlers/package-image.d.ts +10 -0
- package/dist/cli/handlers/package-image.js +132 -0
- package/dist/cli/handlers/package.d.ts +2 -0
- package/dist/cli/handlers/package.js +14 -0
- package/dist/cli/handlers/projects.js +3 -3
- package/dist/cli/handlers/ready.d.ts +2 -0
- package/dist/cli/handlers/ready.js +84 -0
- package/dist/cli/handlers/reconcile.d.ts +2 -0
- package/dist/cli/handlers/reconcile.js +157 -0
- package/dist/cli/handlers/release.js +12 -3
- package/dist/cli/handlers/save.js +12 -2
- package/dist/cli/handlers/seed.js +2 -2
- package/dist/cli/handlers/stage.js +8 -2
- package/dist/cli/handlers/status.js +30 -1
- package/dist/cli/handlers/tool-wrapper.js +71 -3
- package/dist/cli/handlers/treedx.d.ts +2 -0
- package/dist/cli/handlers/treedx.js +310 -0
- package/dist/cli/handlers/workflow.d.ts +40 -0
- package/dist/cli/handlers/workflow.js +41 -0
- package/dist/cli/operations-registry.js +300 -34
- package/dist/cli/registry.d.ts +6 -0
- package/dist/cli/registry.js +12 -0
- package/dist/cli/runtime.js +17 -2
- package/package.json +3 -3
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { createMarketClientForInvocation } from "./market-utils.js";
|
|
2
|
+
import { runPackageImageCommand } from "./package-image.js";
|
|
3
|
+
import { fail, guidedResult } from "./utils.js";
|
|
4
|
+
function stringArg(invocation, key) {
|
|
5
|
+
const value = invocation.args[key];
|
|
6
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
7
|
+
}
|
|
8
|
+
function boolArg(invocation, key) {
|
|
9
|
+
return invocation.args[key] === true;
|
|
10
|
+
}
|
|
11
|
+
function marketRequest(client, path, options = {}) {
|
|
12
|
+
return client.request(path, options);
|
|
13
|
+
}
|
|
14
|
+
function teamId(invocation) {
|
|
15
|
+
return stringArg(invocation, "team");
|
|
16
|
+
}
|
|
17
|
+
function projectId(invocation) {
|
|
18
|
+
return stringArg(invocation, "project");
|
|
19
|
+
}
|
|
20
|
+
function environmentArg(invocation) {
|
|
21
|
+
const value = stringArg(invocation, "environment") ?? "staging";
|
|
22
|
+
return value === "prod" ? "prod" : "staging";
|
|
23
|
+
}
|
|
24
|
+
function recordValue(record, key) {
|
|
25
|
+
return record && typeof record === "object" && key in record ? record[key] : void 0;
|
|
26
|
+
}
|
|
27
|
+
function jsonResult(invocation, context, report) {
|
|
28
|
+
if (context.outputFormat === "json" || boolArg(invocation, "json")) {
|
|
29
|
+
return { exitCode: 0, stdout: [JSON.stringify(report, null, 2)], stderr: [], report };
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
async function status(invocation, context) {
|
|
34
|
+
const team = teamId(invocation);
|
|
35
|
+
if (!team) return fail("Missing --team. Use `trsd db status --team <team-id>`.");
|
|
36
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
37
|
+
const response = await marketRequest(
|
|
38
|
+
client,
|
|
39
|
+
`/v1/teams/${encodeURIComponent(team)}/treedx`,
|
|
40
|
+
{ requireAuth: true }
|
|
41
|
+
);
|
|
42
|
+
const payload = response.payload;
|
|
43
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, team, payload });
|
|
44
|
+
if (json) return json;
|
|
45
|
+
const instance = recordValue(payload, "instance");
|
|
46
|
+
return guidedResult({
|
|
47
|
+
command: "db status",
|
|
48
|
+
summary: instance ? "TreeDX binding is configured." : "No TreeDX binding is configured.",
|
|
49
|
+
facts: [
|
|
50
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
51
|
+
{ label: "Team", value: team },
|
|
52
|
+
{ label: "Instance", value: String(recordValue(instance, "name") ?? "none") },
|
|
53
|
+
{ label: "Status", value: String(recordValue(instance, "status") ?? "not configured") },
|
|
54
|
+
{ label: "Provider", value: String(recordValue(instance, "provider") ?? "n/a") }
|
|
55
|
+
],
|
|
56
|
+
sections: [
|
|
57
|
+
{ title: "Mirrors", lines: (recordValue(payload, "mirrors") ?? []).map((mirror) => `${mirror.name}: ${mirror.status} ${mirror.targetUrl ?? mirror.targetKind ?? ""}`) },
|
|
58
|
+
{ title: "Shares", lines: (recordValue(payload, "shares") ?? []).map((share) => `${share.scope}: ${share.status} ${share.libraryId ?? share.projectId ?? ""}`) }
|
|
59
|
+
],
|
|
60
|
+
report: { ok: true, market: profile.id, team, payload }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async function provision(invocation, context) {
|
|
64
|
+
const team = teamId(invocation);
|
|
65
|
+
if (!team) return fail("Missing --team. Use `trsd db provision --team <team-id>`.");
|
|
66
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
67
|
+
const body = {
|
|
68
|
+
publicRead: boolArg(invocation, "public"),
|
|
69
|
+
baseUrl: stringArg(invocation, "url"),
|
|
70
|
+
imageRef: stringArg(invocation, "image") ?? void 0
|
|
71
|
+
};
|
|
72
|
+
const response = await marketRequest(
|
|
73
|
+
client,
|
|
74
|
+
`/v1/teams/${encodeURIComponent(team)}/treedx/provision`,
|
|
75
|
+
{ method: "POST", body, requireAuth: true }
|
|
76
|
+
);
|
|
77
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, team, payload: response.payload });
|
|
78
|
+
if (json) return json;
|
|
79
|
+
return guidedResult({
|
|
80
|
+
command: "db provision",
|
|
81
|
+
summary: body.publicRead ? "Attached the team to the public TreeSeed federation." : "Queued a private managed TreeDX binding.",
|
|
82
|
+
facts: [
|
|
83
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
84
|
+
{ label: "Team", value: team },
|
|
85
|
+
{ label: "Mode", value: body.publicRead ? "public federation" : "private managed Railway service" }
|
|
86
|
+
],
|
|
87
|
+
report: { ok: true, market: profile.id, team, payload: response.payload }
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async function connect(invocation, context) {
|
|
91
|
+
const team = teamId(invocation);
|
|
92
|
+
const url = stringArg(invocation, "url");
|
|
93
|
+
if (!team || !url) return fail("Missing --team or --url. Use `trsd db connect --team <team-id> --url <base-url>`.");
|
|
94
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
95
|
+
const response = await marketRequest(
|
|
96
|
+
client,
|
|
97
|
+
`/v1/teams/${encodeURIComponent(team)}/treedx`,
|
|
98
|
+
{
|
|
99
|
+
method: "PUT",
|
|
100
|
+
body: {
|
|
101
|
+
kind: boolArg(invocation, "selfHosted") ? "self_hosted" : "managed_private",
|
|
102
|
+
provider: boolArg(invocation, "selfHosted") ? "self_hosted" : "railway",
|
|
103
|
+
baseUrl: url,
|
|
104
|
+
registryUrl: stringArg(invocation, "registryUrl") ?? url,
|
|
105
|
+
status: "active",
|
|
106
|
+
publicRead: boolArg(invocation, "public")
|
|
107
|
+
},
|
|
108
|
+
requireAuth: true
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, team, payload: response.payload });
|
|
112
|
+
if (json) return json;
|
|
113
|
+
return guidedResult({
|
|
114
|
+
command: "db connect",
|
|
115
|
+
summary: "Connected a TreeDX primary binding.",
|
|
116
|
+
facts: [
|
|
117
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
118
|
+
{ label: "Team", value: team },
|
|
119
|
+
{ label: "TreeDX URL", value: url }
|
|
120
|
+
],
|
|
121
|
+
report: { ok: true, market: profile.id, team, payload: response.payload }
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async function mirrors(invocation, context) {
|
|
125
|
+
const team = teamId(invocation);
|
|
126
|
+
if (!team) return fail("Missing --team. Use `trsd db mirrors --team <team-id>`.");
|
|
127
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
128
|
+
const create = stringArg(invocation, "name") || stringArg(invocation, "targetUrl");
|
|
129
|
+
const response = await marketRequest(
|
|
130
|
+
client,
|
|
131
|
+
`/v1/teams/${encodeURIComponent(team)}/treedx/mirrors`,
|
|
132
|
+
create ? {
|
|
133
|
+
method: "POST",
|
|
134
|
+
body: {
|
|
135
|
+
name: stringArg(invocation, "name") ?? "TreeDX mirror",
|
|
136
|
+
targetUrl: stringArg(invocation, "targetUrl"),
|
|
137
|
+
targetKind: stringArg(invocation, "targetKind") ?? "git",
|
|
138
|
+
direction: stringArg(invocation, "direction") ?? "bidirectional"
|
|
139
|
+
},
|
|
140
|
+
requireAuth: true
|
|
141
|
+
} : { requireAuth: true }
|
|
142
|
+
);
|
|
143
|
+
const payload = response.payload;
|
|
144
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, team, payload });
|
|
145
|
+
if (json) return json;
|
|
146
|
+
const lines = Array.isArray(payload) ? payload.map((mirror) => `${mirror.name}: ${mirror.status} ${mirror.targetUrl ?? mirror.targetKind ?? ""}`) : [`${payload.name}: ${payload.status}`];
|
|
147
|
+
return guidedResult({
|
|
148
|
+
command: "db mirrors",
|
|
149
|
+
summary: create ? "Created TreeDX mirror record." : "TreeDX mirrors listed.",
|
|
150
|
+
facts: [{ label: "Market", value: `${profile.id} (${profile.baseUrl})` }, { label: "Team", value: team }],
|
|
151
|
+
sections: [{ title: "Mirrors", lines }],
|
|
152
|
+
report: { ok: true, market: profile.id, team, payload }
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function shares(invocation, context) {
|
|
156
|
+
const team = teamId(invocation);
|
|
157
|
+
if (!team) return fail("Missing --team. Use `trsd db shares --team <team-id>`.");
|
|
158
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
159
|
+
const create = stringArg(invocation, "scope") || stringArg(invocation, "library") || boolArg(invocation, "public");
|
|
160
|
+
const response = await marketRequest(
|
|
161
|
+
client,
|
|
162
|
+
`/v1/teams/${encodeURIComponent(team)}/treedx/shares`,
|
|
163
|
+
create ? {
|
|
164
|
+
method: "POST",
|
|
165
|
+
body: {
|
|
166
|
+
scope: stringArg(invocation, "scope") ?? (boolArg(invocation, "public") ? "public_federation" : "team"),
|
|
167
|
+
libraryId: stringArg(invocation, "library"),
|
|
168
|
+
projectId: projectId(invocation),
|
|
169
|
+
targetTeamId: stringArg(invocation, "targetTeam"),
|
|
170
|
+
publicRead: boolArg(invocation, "public")
|
|
171
|
+
},
|
|
172
|
+
requireAuth: true
|
|
173
|
+
} : { requireAuth: true }
|
|
174
|
+
);
|
|
175
|
+
const payload = response.payload;
|
|
176
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, team, payload });
|
|
177
|
+
if (json) return json;
|
|
178
|
+
const lines = Array.isArray(payload) ? payload.map((share) => `${share.scope}: ${share.status} ${share.libraryId ?? share.projectId ?? ""}`) : [`${payload.scope}: ${payload.status}`];
|
|
179
|
+
return guidedResult({
|
|
180
|
+
command: "db shares",
|
|
181
|
+
summary: create ? "Created TreeDX share record." : "TreeDX shares listed.",
|
|
182
|
+
facts: [{ label: "Market", value: `${profile.id} (${profile.baseUrl})` }, { label: "Team", value: team }],
|
|
183
|
+
sections: [{ title: "Shares", lines }],
|
|
184
|
+
report: { ok: true, market: profile.id, team, payload }
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
async function library(invocation, context) {
|
|
188
|
+
const project = projectId(invocation);
|
|
189
|
+
if (!project) return fail("Missing --project. Use `trsd db library --project <project-id>`.");
|
|
190
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
191
|
+
const bind = stringArg(invocation, "library") || stringArg(invocation, "instance");
|
|
192
|
+
const response = await marketRequest(
|
|
193
|
+
client,
|
|
194
|
+
`/v1/projects/${encodeURIComponent(project)}/treedx-library`,
|
|
195
|
+
bind ? {
|
|
196
|
+
method: "POST",
|
|
197
|
+
body: {
|
|
198
|
+
instanceId: stringArg(invocation, "instance"),
|
|
199
|
+
libraryId: stringArg(invocation, "library"),
|
|
200
|
+
repositoryId: stringArg(invocation, "repository"),
|
|
201
|
+
contentRepositoryUrl: stringArg(invocation, "contentRepositoryUrl")
|
|
202
|
+
},
|
|
203
|
+
requireAuth: true
|
|
204
|
+
} : { requireAuth: true }
|
|
205
|
+
);
|
|
206
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, project, payload: response.payload });
|
|
207
|
+
if (json) return json;
|
|
208
|
+
return guidedResult({
|
|
209
|
+
command: "db library",
|
|
210
|
+
summary: bind ? "Project TreeDX library binding saved." : "Project TreeDX library binding loaded.",
|
|
211
|
+
facts: [
|
|
212
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
213
|
+
{ label: "Project", value: project },
|
|
214
|
+
{ label: "Library", value: String(recordValue(response.payload, "libraryId") ?? "not bound") }
|
|
215
|
+
],
|
|
216
|
+
report: { ok: true, market: profile.id, project, payload: response.payload }
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
async function topology(invocation, context) {
|
|
220
|
+
const project = projectId(invocation);
|
|
221
|
+
if (!project) return fail("Missing --project. Use `trsd db topology --project <project-id>`.");
|
|
222
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
223
|
+
const response = await marketRequest(
|
|
224
|
+
client,
|
|
225
|
+
`/v1/projects/${encodeURIComponent(project)}/repository-topology`,
|
|
226
|
+
{ requireAuth: true }
|
|
227
|
+
);
|
|
228
|
+
const payload = response.payload;
|
|
229
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, project, payload });
|
|
230
|
+
if (json) return json;
|
|
231
|
+
const content = recordValue(payload, "contentRepository");
|
|
232
|
+
const site = recordValue(payload, "siteRepository");
|
|
233
|
+
const parent = recordValue(payload, "projectRepository");
|
|
234
|
+
return guidedResult({
|
|
235
|
+
command: "db topology",
|
|
236
|
+
summary: "Project repository topology loaded.",
|
|
237
|
+
facts: [
|
|
238
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
239
|
+
{ label: "Project", value: project }
|
|
240
|
+
],
|
|
241
|
+
sections: [
|
|
242
|
+
{ title: "Content", lines: [`${recordValue(content, "accessMode")} ${JSON.stringify(recordValue(content, "treeDx") ?? {})}`] },
|
|
243
|
+
{ title: "Site", lines: [`${recordValue(site, "accessMode")} ${recordValue(site, "url") ?? recordValue(site, "name") ?? ""} -> ${recordValue(site, "volumePath") ?? recordValue(site, "checkoutPath") ?? ""}`] },
|
|
244
|
+
{ title: "Project", lines: parent ? [`${recordValue(parent, "accessMode")} ${recordValue(parent, "url") ?? recordValue(parent, "name") ?? ""} -> ${recordValue(parent, "volumePath") ?? recordValue(parent, "checkoutPath") ?? ""}`] : ["No parent project repository configured."] }
|
|
245
|
+
],
|
|
246
|
+
report: { ok: true, market: profile.id, project, payload }
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async function publish(invocation, context) {
|
|
250
|
+
const project = projectId(invocation);
|
|
251
|
+
if (!project) return fail("Missing --project. Use `trsd db publish --project <project-id>`.");
|
|
252
|
+
const environment = environmentArg(invocation);
|
|
253
|
+
if (environment === "prod" && !boolArg(invocation, "yes")) return fail("Production content publish requires --yes and was not queued.");
|
|
254
|
+
const { profile, client } = createMarketClientForInvocation(invocation, context, { requireAuth: true });
|
|
255
|
+
const response = await marketRequest(
|
|
256
|
+
client,
|
|
257
|
+
`/v1/projects/${encodeURIComponent(project)}/deployments/web`,
|
|
258
|
+
{
|
|
259
|
+
method: "POST",
|
|
260
|
+
body: {
|
|
261
|
+
environment,
|
|
262
|
+
action: "publish_content",
|
|
263
|
+
source: "cli",
|
|
264
|
+
reason: stringArg(invocation, "reason") ?? "TreeDX content publish",
|
|
265
|
+
confirmProduction: environment === "prod",
|
|
266
|
+
dryRun: boolArg(invocation, "dryRun")
|
|
267
|
+
},
|
|
268
|
+
requireAuth: true
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
const json = jsonResult(invocation, context, { ok: true, market: profile.id, project, payload: response.payload });
|
|
272
|
+
if (json) return json;
|
|
273
|
+
const deployment = recordValue(response.payload, "deployment");
|
|
274
|
+
const operation = recordValue(response.payload, "operation");
|
|
275
|
+
return guidedResult({
|
|
276
|
+
command: "db publish",
|
|
277
|
+
summary: "TreeDX content publish queued.",
|
|
278
|
+
facts: [
|
|
279
|
+
{ label: "Market", value: `${profile.id} (${profile.baseUrl})` },
|
|
280
|
+
{ label: "Project", value: project },
|
|
281
|
+
{ label: "Environment", value: environment },
|
|
282
|
+
{ label: "Deployment", value: String(recordValue(deployment, "id") ?? "queued") },
|
|
283
|
+
{ label: "Operation", value: String(recordValue(operation, "id") ?? "queued") }
|
|
284
|
+
],
|
|
285
|
+
report: { ok: true, market: profile.id, project, payload: response.payload }
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async function image(invocation, context) {
|
|
289
|
+
return runPackageImageCommand(invocation, context, { packageId: "treedx", commandName: "db image" });
|
|
290
|
+
}
|
|
291
|
+
const handleTreeDx = async (invocation, context) => {
|
|
292
|
+
const action = invocation.positionals[0] ?? "status";
|
|
293
|
+
try {
|
|
294
|
+
if (action === "status") return status(invocation, context);
|
|
295
|
+
if (action === "provision") return provision(invocation, context);
|
|
296
|
+
if (action === "connect") return connect(invocation, context);
|
|
297
|
+
if (action === "mirrors") return mirrors(invocation, context);
|
|
298
|
+
if (action === "shares") return shares(invocation, context);
|
|
299
|
+
if (action === "library") return library(invocation, context);
|
|
300
|
+
if (action === "topology") return topology(invocation, context);
|
|
301
|
+
if (action === "publish") return publish(invocation, context);
|
|
302
|
+
if (action === "image") return image(invocation, context);
|
|
303
|
+
return fail("Unknown db action. Use status, provision, connect, mirrors, shares, library, topology, publish, or image.");
|
|
304
|
+
} catch (error) {
|
|
305
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
export {
|
|
309
|
+
handleTreeDx
|
|
310
|
+
};
|
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
import { TreeseedWorkflowSdk, type TreeseedWorkflowContext, type TreeseedWorkflowNextStep, type TreeseedWorkflowResult } from '@treeseed/sdk/workflow';
|
|
2
|
+
import { type TreeseedHostingEnvironment } from '@treeseed/sdk/hosting';
|
|
2
3
|
import type { TreeseedCommandContext, TreeseedCommandResult } from '../types.js';
|
|
3
4
|
export declare function createWorkflowSdk(context: TreeseedCommandContext, overrides?: Partial<TreeseedWorkflowContext>): TreeseedWorkflowSdk;
|
|
4
5
|
export declare function workflowErrorResult(error: unknown): TreeseedCommandResult;
|
|
5
6
|
export declare function renderWorkflowNextStep(step: TreeseedWorkflowNextStep): string;
|
|
6
7
|
export declare function renderWorkflowNextSteps(result: TreeseedWorkflowResult): string[];
|
|
8
|
+
export declare function resolveWorkflowHostingGraph(context: TreeseedCommandContext, environment: TreeseedHostingEnvironment, applicationSelection?: {
|
|
9
|
+
selected?: string[];
|
|
10
|
+
}): {
|
|
11
|
+
environment: TreeseedHostingEnvironment;
|
|
12
|
+
selectedApplications: string[];
|
|
13
|
+
placements: import("@treeseed/sdk/hosting").TreeseedHostingPlacementSummary[];
|
|
14
|
+
units: {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
serviceType: string;
|
|
18
|
+
placement: import("@treeseed/sdk/hosting").TreeseedServicePlacement;
|
|
19
|
+
hostId: string;
|
|
20
|
+
environment: TreeseedHostingEnvironment;
|
|
21
|
+
projectGroupId: string | null;
|
|
22
|
+
requiredCapabilities: import("@treeseed/sdk/hosting").TreeseedHostCapability[];
|
|
23
|
+
secretRefs: string[];
|
|
24
|
+
variableRefs: string[];
|
|
25
|
+
application: {
|
|
26
|
+
id: string;
|
|
27
|
+
relativeRoot: string;
|
|
28
|
+
roles: string[];
|
|
29
|
+
} | null;
|
|
30
|
+
config: unknown;
|
|
31
|
+
metadata: unknown;
|
|
32
|
+
}[];
|
|
33
|
+
warnings: string[];
|
|
34
|
+
error?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
environment: TreeseedHostingEnvironment;
|
|
37
|
+
selectedApplications: never[];
|
|
38
|
+
error: string;
|
|
39
|
+
placements: never[];
|
|
40
|
+
units: never[];
|
|
41
|
+
warnings: never[];
|
|
42
|
+
};
|
|
43
|
+
export declare function hostingGraphSections(hostingGraph: ReturnType<typeof resolveWorkflowHostingGraph>): {
|
|
44
|
+
title: string;
|
|
45
|
+
lines: string[];
|
|
46
|
+
}[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TreeseedWorkflowError, TreeseedWorkflowSdk } from "@treeseed/sdk/workflow";
|
|
2
2
|
import { TreeseedKeyAgentError } from "@treeseed/sdk/workflow-support";
|
|
3
|
+
import { compileTreeseedHostingGraph, serializeHostingUnit } from "@treeseed/sdk/hosting";
|
|
3
4
|
function createWorkflowSdk(context, overrides = {}) {
|
|
4
5
|
return new TreeseedWorkflowSdk({
|
|
5
6
|
cwd: context.cwd,
|
|
@@ -136,9 +137,49 @@ function renderWorkflowNextSteps(result) {
|
|
|
136
137
|
return step.reason ? `${command} # ${step.reason}` : command;
|
|
137
138
|
});
|
|
138
139
|
}
|
|
140
|
+
function resolveWorkflowHostingGraph(context, environment, applicationSelection) {
|
|
141
|
+
try {
|
|
142
|
+
const selectedApps = Array.isArray(applicationSelection?.selected) ? applicationSelection.selected.filter((app) => typeof app === "string") : [];
|
|
143
|
+
const graph = compileTreeseedHostingGraph({
|
|
144
|
+
tenantRoot: context.cwd,
|
|
145
|
+
environment,
|
|
146
|
+
appId: selectedApps.length === 1 ? selectedApps[0] : void 0
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
environment: graph.environment,
|
|
150
|
+
selectedApplications: selectedApps,
|
|
151
|
+
placements: graph.placements,
|
|
152
|
+
units: graph.units.map((unit) => serializeHostingUnit(unit)),
|
|
153
|
+
warnings: graph.warnings
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
environment,
|
|
158
|
+
selectedApplications: [],
|
|
159
|
+
error: error instanceof Error ? error.message : String(error),
|
|
160
|
+
placements: [],
|
|
161
|
+
units: [],
|
|
162
|
+
warnings: []
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function hostingGraphSections(hostingGraph) {
|
|
167
|
+
if (hostingGraph.error) {
|
|
168
|
+
return [{
|
|
169
|
+
title: "Hosting graph",
|
|
170
|
+
lines: [hostingGraph.error]
|
|
171
|
+
}];
|
|
172
|
+
}
|
|
173
|
+
return [{
|
|
174
|
+
title: "Hosting graph",
|
|
175
|
+
lines: hostingGraph.placements.length > 0 ? hostingGraph.placements.map((placement) => `${placement.label}: ${placement.serviceIds.join(", ")} on ${placement.hostIds.join(", ")}`) : ["No hosting placements resolved."]
|
|
176
|
+
}];
|
|
177
|
+
}
|
|
139
178
|
export {
|
|
140
179
|
createWorkflowSdk,
|
|
180
|
+
hostingGraphSections,
|
|
141
181
|
renderWorkflowNextStep,
|
|
142
182
|
renderWorkflowNextSteps,
|
|
183
|
+
resolveWorkflowHostingGraph,
|
|
143
184
|
workflowErrorResult
|
|
144
185
|
};
|