nylorun 0.16.0-beta → 0.17.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.
@@ -1,295 +0,0 @@
1
- import { manifestsCommand, manifestsUsage } from "./environment-manifests.js";
2
- import { readSecret } from "./io.js";
3
- import { integerFlag, labelsOf, parseFlags, positional, printCursor, printJson, printTable, readJson, runtimeCall, runtimeClient, runtimeFlagsUsage, usageError, } from "./runtime.js";
4
- export const environmentsUsage = `nylorun environments <command>
5
-
6
- The Tenant's Environments (one sandbox and one vault each), through the Runtime API with an
7
- application key (or NYLORUN_TOKEN).
8
-
9
- list [--label <key=value>]... [--behind] [--limit <n>] [--cursor <c>] [--json]
10
- id, kind, manifest, state, sessions and labels;
11
- --behind: those made from an older manifest version
12
- get <id> the Environment, as JSON
13
- put <id> --manifest <name[@n]> [--label <key=value>]...
14
- put <id> [<inline.json|->] [--kind <virtual|pod|none>] [--label <key=value>]...
15
- create it from a manifest (its latest version, or n)
16
- or inline fields, or find it and change its labels
17
- (what it runs never changes: fork it for that)
18
- fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>]
19
- [--secrets <copy|none>] [--label <key=value>]... [--dry-run]
20
- a new Environment from a manifest version (default
21
- its manifest's latest), with this one's disk and the
22
- secrets it declares; --dry-run prints the plan
23
- stop <id> | reset <id> stop its sandbox; reset its disk
24
- delete <id> delete it: its sandbox, files and vault (refused while a
25
- turn runs in it)
26
- snapshot <id> keep a version of its disk
27
- versions <id> its disk versions
28
- restore <id> <version|latest> put a version of its disk back
29
- delete-version <id> <version>
30
- events <id> [--from <n>] [--types <type,…>] [--since <time>] [--until <time>]
31
- its events (lifecycle, and environment.egress for every
32
- connection, naming the turns running), as JSON
33
- secrets list <id> its secrets' names, types and bindings (never a value)
34
- secrets get <id> <name>
35
- secrets put <id> <name> the value of a slot its manifest declares, piped in
36
- on standard input
37
- secrets put <id> <name> <secret.json|->
38
- create or replace a secret: {"type": "bearer", ...}, read
39
- from a file or standard input
40
- secrets rm <id> <name>
41
- ${manifestsUsage}
42
-
43
- ${runtimeFlagsUsage}`;
44
- async function list(deps, args) {
45
- const usage = "nylorun environments list [--label <key=value>]... [--behind] [--limit <n>] [--cursor <c>] [--json]";
46
- const flags = parseFlags(args, { booleans: ["--json", "--behind"], values: ["--limit", "--cursor"], lists: ["--label"] }, usage);
47
- positional(flags, 0, usage);
48
- const labels = labelsOf(flags);
49
- const limit = integerFlag(flags, "--limit");
50
- const cursor = flags.values.get("--cursor");
51
- const { client, connection } = await runtimeClient(deps, flags);
52
- const paged = limit !== undefined || cursor !== undefined;
53
- const page = paged
54
- ? await runtimeCall(() => client.environments.page({
55
- ...(labels ? { labels } : {}),
56
- ...(limit === undefined ? {} : { limit }),
57
- ...(cursor ? { cursor } : {}),
58
- }))
59
- : {
60
- environments: await runtimeCall(() => client.environments.list(labels ? { labels } : {})),
61
- nextCursor: null,
62
- };
63
- let environments = page.environments;
64
- if (flags.booleans.has("--behind")) {
65
- // Made from a manifest version older than its latest: fork them to move on (D63).
66
- const latest = new Map((await runtimeCall(() => client.environments.manifests.list())).map((manifest) => [manifest.name, manifest.version]));
67
- environments = environments.filter((environment) => environment.manifestName !== undefined &&
68
- (environment.manifestVersion ?? 0) < (latest.get(environment.manifestName) ?? 0));
69
- }
70
- if (flags.booleans.has("--json")) {
71
- printJson(deps, paged ? page : environments);
72
- return 0;
73
- }
74
- if (environments.length === 0)
75
- deps.out(`No Environments on ${connection.label}.`);
76
- else
77
- printTable(deps, [
78
- ["ID", "KIND", "MANIFEST", "STATE", "SESSIONS", "LABELS"],
79
- ...environments.map((environment) => [
80
- environment.id,
81
- environment.kind,
82
- environment.manifestName === undefined ? "-" : `${environment.manifestName}@${environment.manifestVersion}`,
83
- environment.state,
84
- String(environment.sessions.length),
85
- Object.entries(environment.labels)
86
- .map(([key, value]) => `${key}=${value}`)
87
- .join(",") || "-",
88
- ]),
89
- ]);
90
- printCursor(deps, page.nextCursor);
91
- return 0;
92
- }
93
- /** A command that takes one Environment id and prints the Runtime's answer. */
94
- function byId(name, call) {
95
- return async (deps, args) => {
96
- const usage = `nylorun environments ${name} <id>`;
97
- const flags = parseFlags(args, {}, usage);
98
- const [id] = positional(flags, 1, usage);
99
- const { client } = await runtimeClient(deps, flags);
100
- printJson(deps, await runtimeCall(() => call(client, id)));
101
- return 0;
102
- };
103
- }
104
- async function put(deps, args) {
105
- const usage = "nylorun environments put <id> [--manifest <name[@n]> | <inline.json|-> [--kind <virtual|pod|none>]] [--label <key=value>]...";
106
- const flags = parseFlags(args, { values: ["--kind", "--manifest"], lists: ["--label"] }, usage);
107
- if (flags.rest.length < 1 || flags.rest.length > 2)
108
- throw usageError(`Usage: ${usage}`);
109
- const [id, path] = flags.rest;
110
- const manifest = flags.values.get("--manifest");
111
- const kind = flags.values.get("--kind");
112
- if (manifest !== undefined && (path !== undefined || kind !== undefined))
113
- throw usageError("--manifest makes it from the manifest: give no inline fields or --kind with it.");
114
- const recipe = path === undefined ? {} : await readJson(path, "The inline fields");
115
- const labels = labelsOf(flags);
116
- const body = {
117
- ...recipe,
118
- ...(manifest ? { manifest } : {}),
119
- ...(kind ? { kind } : {}),
120
- ...(labels ? { labels: { ...recipe.labels, ...labels } } : {}),
121
- };
122
- const { client } = await runtimeClient(deps, flags);
123
- printJson(deps, await runtimeCall(() => client.environments.put(id, body)));
124
- return 0;
125
- }
126
- async function fork(deps, args) {
127
- const usage = "nylorun environments fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>] [--secrets <copy|none>] [--label <key=value>]... [--dry-run]";
128
- const flags = parseFlags(args, { booleans: ["--dry-run"], values: ["--manifest", "--disk", "--secrets"], lists: ["--label"] }, usage);
129
- const [id, next] = positional(flags, 2, usage);
130
- const disk = flags.values.get("--disk");
131
- if (disk !== undefined && disk !== "latest" && disk !== "empty" && !/^[1-9]\d*$/.test(disk))
132
- throw usageError("--disk is latest, empty or a version number.");
133
- const secrets = flags.values.get("--secrets");
134
- if (secrets !== undefined && secrets !== "copy" && secrets !== "none")
135
- throw usageError("--secrets is copy or none.");
136
- const manifest = flags.values.get("--manifest");
137
- const labels = labelsOf(flags);
138
- const options = {
139
- id: next,
140
- ...(manifest ? { manifest } : {}),
141
- ...(disk === undefined ? {} : { disk: disk === "latest" || disk === "empty" ? disk : Number(disk) }),
142
- ...(secrets ? { secrets: secrets } : {}),
143
- ...(labels ? { labels } : {}),
144
- };
145
- const { client, connection } = await runtimeClient(deps, flags);
146
- if (flags.booleans.has("--dry-run")) {
147
- printJson(deps, await runtimeCall(() => client.environments.planFork(id, options)));
148
- return 0;
149
- }
150
- const forked = await runtimeCall(() => client.environments.fork(id, options));
151
- const { plan } = forked;
152
- deps.err(`Forked Environment ${id} into ${next} on ${connection.label}: ${plan.manifest ? `${plan.manifest.name}@${plan.manifest.version}` : "its fields"}, ${plan.disk === null ? "an empty disk" : `disk version ${plan.disk}`}, ${plan.secrets.copied.length} secrets copied${plan.setup ? "; its setup runs before the first turn" : ""}.`);
153
- if (plan.secrets.missing.length > 0)
154
- deps.err(` set before use: ${plan.secrets.missing.join(", ")} (nylorun environments secrets put ${next} <name>)`);
155
- printJson(deps, forked);
156
- return 0;
157
- }
158
- async function deleteEnvironment(deps, args) {
159
- const usage = "nylorun environments delete <id>";
160
- const flags = parseFlags(args, {}, usage);
161
- const [id] = positional(flags, 1, usage);
162
- const { client, connection } = await runtimeClient(deps, flags);
163
- const deleted = await runtimeCall(async () => {
164
- try {
165
- return await client.environments.delete(id);
166
- }
167
- catch (error) {
168
- if (error.status === 404)
169
- return undefined;
170
- throw error;
171
- }
172
- });
173
- if (!deleted || deleted.deleted === false) {
174
- deps.err(`No Environment ${id} on ${connection.label}.`);
175
- return 1;
176
- }
177
- deps.out(`Deleted Environment ${id}: its sandbox, files and vault.`);
178
- return 0;
179
- }
180
- async function restore(deps, args) {
181
- const usage = "nylorun environments restore <id> <version|latest>";
182
- const flags = parseFlags(args, {}, usage);
183
- const [id, version] = positional(flags, 2, usage);
184
- if (version !== "latest" && !/^[1-9]\d*$/.test(version))
185
- throw usageError("The version is a positive number or latest.");
186
- const { client } = await runtimeClient(deps, flags);
187
- printJson(deps, await runtimeCall(() => client.environments.restore(id, version === "latest" ? "latest" : Number(version))));
188
- return 0;
189
- }
190
- async function deleteVersion(deps, args) {
191
- const usage = "nylorun environments delete-version <id> <version>";
192
- const flags = parseFlags(args, {}, usage);
193
- const [id, version] = positional(flags, 2, usage);
194
- if (!/^[1-9]\d*$/.test(version))
195
- throw usageError("The version is a positive number.");
196
- const { client } = await runtimeClient(deps, flags);
197
- printJson(deps, await runtimeCall(() => client.environments.deleteVersion(id, Number(version))));
198
- return 0;
199
- }
200
- async function events(deps, args) {
201
- const usage = "nylorun environments events <id> [--from <n>] [--types <type,…>] [--since <time>] [--until <time>]";
202
- const flags = parseFlags(args, { values: ["--from", "--types", "--since", "--until"] }, usage);
203
- const [id] = positional(flags, 1, usage);
204
- const from = integerFlag(flags, "--from");
205
- const types = flags.values.get("--types")?.split(",").map((type) => type.trim()).filter(Boolean);
206
- const since = flags.values.get("--since");
207
- const until = flags.values.get("--until");
208
- const { client } = await runtimeClient(deps, flags);
209
- printJson(deps, await runtimeCall(() => client.environments.events(id, {
210
- ...(from === undefined ? {} : { from }),
211
- ...(types === undefined || types.length === 0 ? {} : { types }),
212
- ...(since === undefined ? {} : { since }),
213
- ...(until === undefined ? {} : { until }),
214
- })));
215
- return 0;
216
- }
217
- async function secrets(deps, args) {
218
- const [verb, ...rest] = args;
219
- if (verb === "list" || verb === "ls") {
220
- const usage = "nylorun environments secrets list <id>";
221
- const flags = parseFlags(rest, {}, usage);
222
- const [id] = positional(flags, 1, usage);
223
- const { client } = await runtimeClient(deps, flags);
224
- printJson(deps, await runtimeCall(() => client.environments.secrets.list(id)));
225
- return 0;
226
- }
227
- if (verb === "get") {
228
- const usage = "nylorun environments secrets get <id> <name>";
229
- const flags = parseFlags(rest, {}, usage);
230
- const [id, name] = positional(flags, 2, usage);
231
- const { client } = await runtimeClient(deps, flags);
232
- printJson(deps, await runtimeCall(() => client.environments.secrets.get(id, name)));
233
- return 0;
234
- }
235
- if (verb === "put") {
236
- const usage = "nylorun environments secrets put <id> <name> [<secret.json|->]";
237
- const flags = parseFlags(rest, {}, usage);
238
- if (flags.rest.length < 2 || flags.rest.length > 3)
239
- throw usageError(`Usage: ${usage}`);
240
- const [id, name, path] = flags.rest;
241
- if (path === undefined) {
242
- // A slot its manifest declares (D64): the value only, piped in; the binding is the slot's.
243
- const value = await readSecret(deps, `value of ${name}`, `printf %s "$TOKEN" | nylorun environments secrets put ${id} ${name}`);
244
- const { client } = await runtimeClient(deps, flags);
245
- printJson(deps, await runtimeCall(() => client.environments.secrets.put(id, name, { value })));
246
- return 0;
247
- }
248
- const secret = await readJson(path, "The secret");
249
- if (typeof secret.type !== "string")
250
- throw usageError('The secret needs a "type".');
251
- const { client } = await runtimeClient(deps, flags);
252
- printJson(deps, await runtimeCall(() => client.environments.secrets.put(id, name, secret)));
253
- return 0;
254
- }
255
- if (verb === "rm" || verb === "delete") {
256
- const usage = "nylorun environments secrets rm <id> <name>";
257
- const flags = parseFlags(rest, {}, usage);
258
- const [id, name] = positional(flags, 2, usage);
259
- const { client } = await runtimeClient(deps, flags);
260
- await runtimeCall(() => client.environments.secrets.delete(id, name));
261
- deps.out(`Deleted secret ${name} of Environment ${id}.`);
262
- return 0;
263
- }
264
- throw usageError(`Usage: nylorun environments secrets <list|get|put|rm> …\n\n${environmentsUsage}`);
265
- }
266
- const COMMANDS = {
267
- list,
268
- ls: list,
269
- get: byId("get", (client, id) => client.environments.get(id)),
270
- put,
271
- stop: byId("stop", (client, id) => client.environments.stop(id)),
272
- reset: byId("reset", (client, id) => client.environments.reset(id)),
273
- delete: deleteEnvironment,
274
- rm: deleteEnvironment,
275
- snapshot: byId("snapshot", (client, id) => client.environments.snapshot(id)),
276
- versions: byId("versions", (client, id) => client.environments.versions(id)),
277
- restore,
278
- "delete-version": deleteVersion,
279
- events,
280
- secrets,
281
- fork,
282
- manifests: (deps, args) => manifestsCommand(deps, args, manifestsUsage),
283
- };
284
- /** `nylorun environments <command> ...` */
285
- export async function environmentsCommand(deps, args) {
286
- const [sub, ...rest] = args;
287
- if (sub === "--help" || sub === "-h") {
288
- deps.out(environmentsUsage);
289
- return 0;
290
- }
291
- const command = sub !== undefined && Object.hasOwn(COMMANDS, sub) ? COMMANDS[sub] : undefined;
292
- if (!command)
293
- throw usageError(sub ? `Unknown environments command ${sub}.\n${environmentsUsage}` : environmentsUsage);
294
- return command(deps, rest);
295
- }