@nylorun/cli 0.6.0-beta → 0.8.0-beta
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/CHANGELOG.md +58 -0
- package/README.md +46 -54
- package/dist/access/commands.js +1 -5
- package/dist/cli.js +32 -44
- package/dist/doctor.js +2 -6
- package/dist/home.d.ts +3 -3
- package/dist/home.js +3 -3
- package/dist/installation/commands.d.ts +20 -0
- package/dist/installation/commands.js +141 -0
- package/dist/model/configure.d.ts +0 -1
- package/dist/model/configure.js +1 -2
- package/dist/model/host-model.d.ts +3 -11
- package/dist/model/host-model.js +6 -86
- package/dist/project/connection.d.ts +13 -0
- package/dist/project/connection.js +18 -0
- package/dist/project/credentials.d.ts +2 -8
- package/dist/project/credentials.js +6 -29
- package/dist/project/env.d.ts +1 -1
- package/dist/project/env.js +2 -3
- package/dist/project/link.d.ts +10 -17
- package/dist/project/link.js +31 -60
- package/package.json +4 -4
- package/dist/project/create-tenant.d.ts +0 -28
- package/dist/project/create-tenant.js +0 -66
- package/dist/project/seed.d.ts +0 -25
- package/dist/project/seed.js +0 -96
- package/dist/tenant/commands.d.ts +0 -18
- package/dist/tenant/commands.js +0 -394
package/dist/tenant/commands.js
DELETED
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
import { createInterface } from "node:readline/promises";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { PROJECT_PRINCIPAL_ID, createAdmin, } from "@nylorun/admin";
|
|
4
|
-
import { createClient, isTenantId } from "@nylorun/agents";
|
|
5
|
-
import { CliError } from "../errors.js";
|
|
6
|
-
import { resolveHome } from "../home.js";
|
|
7
|
-
import { readCredentials, removeCredentials, writeCredentials, } from "../project/credentials.js";
|
|
8
|
-
import { readLink, removeLink, writeLink, } from "../project/link.js";
|
|
9
|
-
import { findProjectRoot, requireProjectRoot } from "../project/root.js";
|
|
10
|
-
import { createProjectTenant, defaultTenantName, } from "../project/create-tenant.js";
|
|
11
|
-
import { seedTenantFromProject } from "../project/seed.js";
|
|
12
|
-
import { loadProjectEnvironment } from "../environment.js";
|
|
13
|
-
import { shellQuote } from "../project/env.js";
|
|
14
|
-
/** Exact-and-unique name or id match. */
|
|
15
|
-
export function matchTenant(tenants, nameOrId) {
|
|
16
|
-
if (isTenantId(nameOrId)) {
|
|
17
|
-
const byId = tenants.find((t) => t.id === nameOrId);
|
|
18
|
-
if (!byId)
|
|
19
|
-
throw new CliError(`No Tenant with id ${nameOrId}.`, 1);
|
|
20
|
-
return byId;
|
|
21
|
-
}
|
|
22
|
-
const matches = tenants.filter((t) => t.name === nameOrId);
|
|
23
|
-
if (matches.length === 0) {
|
|
24
|
-
throw new CliError(`No Tenant named ${JSON.stringify(nameOrId)}.`, 1);
|
|
25
|
-
}
|
|
26
|
-
if (matches.length > 1) {
|
|
27
|
-
throw new CliError(`Tenant name ${JSON.stringify(nameOrId)} is not unique (${matches.length} matches). Use the Tenant id.`, 1);
|
|
28
|
-
}
|
|
29
|
-
return matches[0];
|
|
30
|
-
}
|
|
31
|
-
function localAdmin(home) {
|
|
32
|
-
return createAdmin(home ? { home } : undefined);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* `nylo tenant create [name]`. Inside a Project: create the Tenant, write the
|
|
36
|
-
* Project link and credentials, and seed it from the Project's `.env` (sandbox
|
|
37
|
-
* and model provider). Outside one: create the Tenant and print its
|
|
38
|
-
* connection variables once, since the Host keeps only a hash of the key.
|
|
39
|
-
*/
|
|
40
|
-
async function createTenant(options) {
|
|
41
|
-
const admin = localAdmin(options.home);
|
|
42
|
-
let hostId;
|
|
43
|
-
try {
|
|
44
|
-
hostId = (await admin.status()).host?.hostId;
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
throw new CliError(`No Runtime answers at ${admin.url} (${error instanceof Error ? error.message : String(error)}). Start the local stack with "npx nylorun up".`, 6);
|
|
48
|
-
}
|
|
49
|
-
if (!hostId)
|
|
50
|
-
throw new CliError(`Host at ${admin.url} did not report hostId in admin status.`, 1);
|
|
51
|
-
const projectRoot = findProjectRoot();
|
|
52
|
-
if (!projectRoot) {
|
|
53
|
-
const created = await admin.createTenant({ name: options.name ?? "tenant" });
|
|
54
|
-
console.log(`Tenant ${created.tenant.name} ${created.tenant.id} (created)`);
|
|
55
|
-
console.log("# Not in a Project: nothing was linked. The key is shown only once.");
|
|
56
|
-
console.log(`export NYLORUN_RUNTIME_URL=${shellQuote(admin.url.replace(/\/$/, ""))}`);
|
|
57
|
-
console.log(`export NYLORUN_SERVER_KEY=${shellQuote(created.applicationKey)}`);
|
|
58
|
-
console.log(`export NYLORUN_TENANT=${shellQuote(created.tenant.id)}`);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const link = await readLink(projectRoot);
|
|
62
|
-
if (link)
|
|
63
|
-
throw new CliError(`This Project is already linked to Tenant ${link.tenantId}. Use "nylo tenant use <name-or-id>" to switch, or "nylo tenant delete" first.`, 1);
|
|
64
|
-
const created = await createProjectTenant({
|
|
65
|
-
admin,
|
|
66
|
-
hostId,
|
|
67
|
-
projectRoot,
|
|
68
|
-
name: options.name ?? (await defaultTenantName(projectRoot)),
|
|
69
|
-
});
|
|
70
|
-
console.log(`Tenant ${created.envelope.name} ${created.envelope.id} (created)`);
|
|
71
|
-
console.log(`Linked ${projectRoot}/.nylorun/`);
|
|
72
|
-
const seeded = await seedTenantFromProject({
|
|
73
|
-
hostUrl: created.link.hostUrl,
|
|
74
|
-
tenantId: created.link.tenantId,
|
|
75
|
-
applicationKey: created.credentials.applicationKey,
|
|
76
|
-
projectRoot,
|
|
77
|
-
env: loadProjectEnvironment(projectRoot),
|
|
78
|
-
});
|
|
79
|
-
console.log(seeded.model
|
|
80
|
-
? `Model ${seeded.model} (from .env)`
|
|
81
|
-
: "Model not configured: set it in Studio, or run nylo configure");
|
|
82
|
-
}
|
|
83
|
-
export async function tenantCommand(args) {
|
|
84
|
-
const [verb, ...rest] = args;
|
|
85
|
-
if (!verb || verb === "--help" || verb === "-h") {
|
|
86
|
-
console.log(`nylo tenant create [name]|current|list [--json]|use <name-or-id>|status [--json]|endpoints [--json]|endpoints ping <agent>|reset [--sessions|--sandboxes|--all] [--yes]|delete <name-or-id> [--yes]`);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const home = resolveHome();
|
|
90
|
-
if (verb === "create") {
|
|
91
|
-
if (rest.length > 1 || rest.some((arg) => arg.startsWith("-")))
|
|
92
|
-
throw new CliError("Usage: nylo tenant create [name]", 2);
|
|
93
|
-
await createTenant({ home, ...(rest[0] ? { name: rest[0] } : {}) });
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (verb === "current") {
|
|
97
|
-
const root = requireProjectRoot();
|
|
98
|
-
const link = await readLink(root);
|
|
99
|
-
if (!link) {
|
|
100
|
-
console.log("No Project link. Run nylo tenant create to create one.");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const admin = localAdmin(home);
|
|
104
|
-
const tenants = await admin.listTenants();
|
|
105
|
-
const match = tenants.find((t) => t.id === link.tenantId);
|
|
106
|
-
const name = match?.name ?? match?.envelope?.name ?? "(unknown)";
|
|
107
|
-
console.log(`${name} ${link.tenantId}`);
|
|
108
|
-
console.log(`Host ${link.hostUrl} ${link.hostId}`);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (verb === "list") {
|
|
112
|
-
const json = rest.includes("--json");
|
|
113
|
-
if (rest.some((a) => a !== "--json")) {
|
|
114
|
-
throw new CliError("Usage: nylo tenant list [--json]", 2);
|
|
115
|
-
}
|
|
116
|
-
const admin = localAdmin(home);
|
|
117
|
-
const tenants = await admin.listTenants();
|
|
118
|
-
if (json) {
|
|
119
|
-
console.log(JSON.stringify(tenants, null, 2));
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (tenants.length === 0) {
|
|
123
|
-
console.log("No Tenants on this Host.");
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
for (const tenant of tenants) {
|
|
127
|
-
const name = tenant.name ?? "(unreadable)";
|
|
128
|
-
console.log(`${name} ${tenant.id} ${tenant.state}`);
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (verb === "use") {
|
|
133
|
-
const nameOrId = rest[0];
|
|
134
|
-
if (!nameOrId || rest.length > 1) {
|
|
135
|
-
throw new CliError("Usage: nylo tenant use <name-or-id>", 2);
|
|
136
|
-
}
|
|
137
|
-
const root = requireProjectRoot();
|
|
138
|
-
const admin = localAdmin(home);
|
|
139
|
-
const status = await admin.status();
|
|
140
|
-
const hostId = status.host?.hostId;
|
|
141
|
-
if (!hostId) {
|
|
142
|
-
throw new CliError(`Host at ${admin.url} did not report hostId in admin status.`, 1);
|
|
143
|
-
}
|
|
144
|
-
const tenants = await admin.listTenants();
|
|
145
|
-
const selected = matchTenant(tenants, nameOrId);
|
|
146
|
-
console.warn("Sharing a Tenant shares Action endpoint registrations across Projects that link to it.");
|
|
147
|
-
const authorizes = async (key) => {
|
|
148
|
-
try {
|
|
149
|
-
await createClient({ url: admin.url, key, tenant: selected.id }).transport.json("/v1/tenant", "GET");
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
const link = await readLink(root);
|
|
157
|
-
const current = await readCredentials(root);
|
|
158
|
-
const kept = await readCredentials(root, selected.id);
|
|
159
|
-
// The Project's own key, then one kept when it last left this Tenant, then
|
|
160
|
-
// the key derived from this machine's admin key (Tenants Studio creates).
|
|
161
|
-
let chosen;
|
|
162
|
-
for (const candidate of [current, kept])
|
|
163
|
-
if (!chosen && candidate && (await authorizes(candidate.applicationKey)))
|
|
164
|
-
chosen = candidate;
|
|
165
|
-
if (!chosen) {
|
|
166
|
-
const derived = admin.deriveTenantKey(selected.id, PROJECT_PRINCIPAL_ID);
|
|
167
|
-
if (await authorizes(derived))
|
|
168
|
-
chosen = { format: 1, applicationKey: derived, principalId: PROJECT_PRINCIPAL_ID };
|
|
169
|
-
}
|
|
170
|
-
if (!chosen) {
|
|
171
|
-
throw new CliError(`No key of this Project authorizes Tenant ${selected.id}, and it has no derived "${PROJECT_PRINCIPAL_ID}" principal (Tenants created in Studio have one). Link a Tenant created in Studio, or create one with nylo tenant create.`, 1);
|
|
172
|
-
}
|
|
173
|
-
if (chosen !== current) {
|
|
174
|
-
// An application key is shown only once: keep the one being replaced.
|
|
175
|
-
if (current &&
|
|
176
|
-
link &&
|
|
177
|
-
link.tenantId !== selected.id &&
|
|
178
|
-
current.principalId !== PROJECT_PRINCIPAL_ID) {
|
|
179
|
-
await writeCredentials(root, current, link.tenantId);
|
|
180
|
-
console.warn(`Kept the key for Tenant ${link.tenantId} in .nylorun/credentials.${link.tenantId}.json; nylo tenant use ${link.tenantId} switches back.`);
|
|
181
|
-
}
|
|
182
|
-
await writeCredentials(root, chosen);
|
|
183
|
-
if (chosen === kept)
|
|
184
|
-
await removeCredentials(root, selected.id);
|
|
185
|
-
}
|
|
186
|
-
await writeLink(root, {
|
|
187
|
-
format: 1,
|
|
188
|
-
hostUrl: admin.url,
|
|
189
|
-
hostId,
|
|
190
|
-
tenantId: selected.id,
|
|
191
|
-
});
|
|
192
|
-
console.log(`Linked to ${selected.name ?? selected.id} ${selected.id}`);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
if (verb === "endpoints") {
|
|
196
|
-
await endpointsCommand(rest);
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
if (verb === "status") {
|
|
200
|
-
const json = rest.includes("--json");
|
|
201
|
-
if (rest.some((a) => a !== "--json")) {
|
|
202
|
-
throw new CliError("Usage: nylo tenant status [--json]", 2);
|
|
203
|
-
}
|
|
204
|
-
const root = requireProjectRoot();
|
|
205
|
-
const link = await readLink(root);
|
|
206
|
-
const credentials = await readCredentials(root);
|
|
207
|
-
if (!link || !credentials) {
|
|
208
|
-
throw new CliError("No Project link. Run nylo tenant create to create one.", 1);
|
|
209
|
-
}
|
|
210
|
-
const client = createClient({
|
|
211
|
-
url: link.hostUrl,
|
|
212
|
-
key: credentials.applicationKey,
|
|
213
|
-
tenant: link.tenantId,
|
|
214
|
-
});
|
|
215
|
-
try {
|
|
216
|
-
const body = await client.transport.json("/v1/tenant", "GET");
|
|
217
|
-
if (json) {
|
|
218
|
-
console.log(JSON.stringify(body, null, 2));
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
console.log(`${body.tenant.name} ${body.tenant.id}`);
|
|
222
|
-
console.log(`path ${body.path}`);
|
|
223
|
-
console.log(`checks ${Object.entries(body.checks)
|
|
224
|
-
.map(([k, v]) => `${k}=${v ? "ok" : "fail"}`)
|
|
225
|
-
.join(" ")}`);
|
|
226
|
-
console.log(`counts sessions=${body.counts.sessions} running=${body.counts.runningSessions} pending=${body.counts.pendingActions}`);
|
|
227
|
-
console.log(`sandbox ${body.sandbox.backend ?? "none"}`);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
// Quarantined / opaque — fall back to admin status.
|
|
232
|
-
}
|
|
233
|
-
const admin = localAdmin(home);
|
|
234
|
-
try {
|
|
235
|
-
const body = await admin.getTenant(link.tenantId);
|
|
236
|
-
if (json) {
|
|
237
|
-
console.log(JSON.stringify(body, null, 2));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
console.log(`${body.name ?? "(unreadable)"} ${body.id} ${body.state}`);
|
|
241
|
-
if (body.quarantine) {
|
|
242
|
-
console.log(`reason ${body.quarantine.code}: ${body.quarantine.message}`);
|
|
243
|
-
console.log(`repair ${body.quarantine.repair}`);
|
|
244
|
-
}
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
catch (error) {
|
|
248
|
-
throw new CliError(error instanceof Error
|
|
249
|
-
? error.message
|
|
250
|
-
: `Tenant ${link.tenantId} is not reachable.`, 1);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
if (verb === "reset") {
|
|
254
|
-
const yes = rest.includes("--yes");
|
|
255
|
-
const flags = rest.filter((a) => a !== "--yes");
|
|
256
|
-
let scope = "sessions";
|
|
257
|
-
if (flags.includes("--all"))
|
|
258
|
-
scope = "all";
|
|
259
|
-
else if (flags.includes("--sandboxes"))
|
|
260
|
-
scope = "sandboxes";
|
|
261
|
-
else if (flags.includes("--sessions") || flags.length === 0)
|
|
262
|
-
scope = "sessions";
|
|
263
|
-
else
|
|
264
|
-
throw new CliError("Usage: nylo tenant reset [--sessions|--sandboxes|--all] [--yes]", 2);
|
|
265
|
-
if (flags.filter((f) => ["--sessions", "--sandboxes", "--all"].includes(f)).length > 1) {
|
|
266
|
-
throw new CliError("Pass only one of --sessions, --sandboxes, --all.", 2);
|
|
267
|
-
}
|
|
268
|
-
const root = requireProjectRoot();
|
|
269
|
-
const link = await readLink(root);
|
|
270
|
-
const credentials = await readCredentials(root);
|
|
271
|
-
if (!link || !credentials) {
|
|
272
|
-
throw new CliError("No Project link. Run nylo tenant create first.", 1);
|
|
273
|
-
}
|
|
274
|
-
const admin = localAdmin(home);
|
|
275
|
-
const tenants = await admin.listTenants();
|
|
276
|
-
const match = tenants.find((t) => t.id === link.tenantId);
|
|
277
|
-
const name = match?.name ?? link.tenantId;
|
|
278
|
-
if (scope === "all" && !yes) {
|
|
279
|
-
await confirmOrThrow(`Reset ALL data for Tenant ${name} (${link.tenantId})? Link and credentials are kept. [y/N] `);
|
|
280
|
-
}
|
|
281
|
-
const client = createClient({
|
|
282
|
-
url: link.hostUrl,
|
|
283
|
-
key: credentials.applicationKey,
|
|
284
|
-
tenant: link.tenantId,
|
|
285
|
-
});
|
|
286
|
-
try {
|
|
287
|
-
await client.transport.json("/v1/tenant/reset", "POST", {
|
|
288
|
-
requestId: randomUUID(),
|
|
289
|
-
scope,
|
|
290
|
-
activeWork: "drain",
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
throw new CliError(error instanceof Error
|
|
295
|
-
? error.message
|
|
296
|
-
: `Tenant reset failed: ${String(error)}`, 1);
|
|
297
|
-
}
|
|
298
|
-
console.log(`Reset Tenant ${name} (${scope}).`);
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
if (verb === "delete") {
|
|
302
|
-
const yes = rest.includes("--yes");
|
|
303
|
-
const nameOrId = rest.find((a) => a !== "--yes");
|
|
304
|
-
if (!nameOrId || rest.filter((a) => a !== "--yes").length !== 1) {
|
|
305
|
-
throw new CliError("Usage: nylo tenant delete <name-or-id> [--yes]", 2);
|
|
306
|
-
}
|
|
307
|
-
const root = requireProjectRoot();
|
|
308
|
-
const admin = localAdmin(home);
|
|
309
|
-
const tenants = await admin.listTenants();
|
|
310
|
-
const selected = matchTenant(tenants, nameOrId);
|
|
311
|
-
if (!yes) {
|
|
312
|
-
await confirmOrThrow(`Delete Tenant ${selected.name ?? selected.id} (${selected.id})?\nThe KEK moves to trash with the Tenant directory. [y/N] `);
|
|
313
|
-
}
|
|
314
|
-
try {
|
|
315
|
-
await admin.deleteTenant(selected.id, { activeWork: "refuse" });
|
|
316
|
-
}
|
|
317
|
-
catch (error) {
|
|
318
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
319
|
-
if (/active_work|409|live work/i.test(message)) {
|
|
320
|
-
throw new CliError(`Tenant ${selected.id} has live work. Retry with the Host idle, or drain sessions first.`, 1);
|
|
321
|
-
}
|
|
322
|
-
throw new CliError(message, 1);
|
|
323
|
-
}
|
|
324
|
-
const link = await readLink(root);
|
|
325
|
-
if (link?.tenantId === selected.id) {
|
|
326
|
-
await removeLink(root);
|
|
327
|
-
await removeCredentials(root);
|
|
328
|
-
console.log("Removed Project link and credentials for the deleted Tenant.");
|
|
329
|
-
}
|
|
330
|
-
console.log(`Deleted Tenant ${selected.name ?? selected.id}.`);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
throw new CliError(`Unknown tenant command ${verb}.\nUsage: nylo tenant current|list|use|status|reset|delete`, 2);
|
|
334
|
-
}
|
|
335
|
-
async function confirmOrThrow(prompt) {
|
|
336
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
337
|
-
throw new CliError("Confirmation required; pass --yes for non-interactive use.", 2);
|
|
338
|
-
}
|
|
339
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
340
|
-
try {
|
|
341
|
-
const answer = (await rl.question(prompt)).trim().toLowerCase();
|
|
342
|
-
if (answer !== "y" && answer !== "yes") {
|
|
343
|
-
throw new CliError("Cancelled.", 1);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
finally {
|
|
347
|
-
rl.close();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
/** The Project's application client, from its link and credentials. */
|
|
351
|
-
async function projectClient() {
|
|
352
|
-
const root = requireProjectRoot();
|
|
353
|
-
const link = await readLink(root);
|
|
354
|
-
const credentials = await readCredentials(root);
|
|
355
|
-
if (!link || !credentials)
|
|
356
|
-
throw new CliError("No Project link. Run nylo tenant create to create one.", 1);
|
|
357
|
-
return createClient({ url: link.hostUrl, key: credentials.applicationKey, tenant: link.tenantId });
|
|
358
|
-
}
|
|
359
|
-
/** `nylo tenant endpoints [--json]` and `nylo tenant endpoints ping <agent>`. */
|
|
360
|
-
async function endpointsCommand(args) {
|
|
361
|
-
const client = await projectClient();
|
|
362
|
-
if (args[0] === "ping") {
|
|
363
|
-
const agentId = args[1];
|
|
364
|
-
if (!agentId || args.length > 2)
|
|
365
|
-
throw new CliError("Usage: nylo tenant endpoints ping <agent>", 2);
|
|
366
|
-
const answer = await client.transport.json(`/v1/endpoints/${encodeURIComponent(agentId)}/ping`, "POST");
|
|
367
|
-
console.log(`${answer.agentId} serves ${answer.implementationVersion}${answer.manifestHash ? ` ${answer.manifestHash}` : ""}`);
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
const json = args.includes("--json");
|
|
371
|
-
if (args.some((a) => a !== "--json"))
|
|
372
|
-
throw new CliError("Usage: nylo tenant endpoints [--json] | nylo tenant endpoints ping <agent>", 2);
|
|
373
|
-
const body = await client.transport.json("/v1/endpoints", "GET");
|
|
374
|
-
if (json) {
|
|
375
|
-
console.log(JSON.stringify(body, null, 2));
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
if (body.endpoints.length === 0) {
|
|
379
|
-
console.log("No Action endpoints. Register one with createActionHandler(...).register({ url }).");
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
for (const endpoint of body.endpoints)
|
|
383
|
-
console.log(endpointLine(endpoint));
|
|
384
|
-
}
|
|
385
|
-
/** One line of `nylo tenant endpoints`: the agent, its URL and version, and how it is doing. */
|
|
386
|
-
export function endpointLine(endpoint) {
|
|
387
|
-
const { health } = endpoint;
|
|
388
|
-
const state = health.consecutiveFailures > 0
|
|
389
|
-
? `failing (${health.consecutiveFailures}): ${health.lastError?.message || health.lastError?.code || "unknown"}`
|
|
390
|
-
: health.lastSuccessAt
|
|
391
|
-
? `ok (last ${health.lastSuccessAt})`
|
|
392
|
-
: "no deliveries yet";
|
|
393
|
-
return `${endpoint.agentId} ${endpoint.url} ${endpoint.implementationVersion} ${state}`;
|
|
394
|
-
}
|