withruntime 0.23.0 → 0.33.1
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/COMPATIBILITY.md +66 -5
- package/E2B.md +1 -1
- package/README.md +36 -2
- package/README.src.md +35 -1
- package/compatibility-lock.json +3 -3
- package/dist/agents/browser.d.ts +12 -0
- package/dist/agents/browser.js +12 -0
- package/dist/agents/index.d.ts +7 -7
- package/dist/agents/index.js +7 -8
- package/dist/agents-cli.d.ts +1 -1
- package/dist/agents-cli.js +250 -3
- package/dist/agents-deploy.d.ts +186 -0
- package/dist/agents-deploy.js +752 -0
- package/dist/agents-file.d.ts +19 -0
- package/dist/agents-file.js +135 -0
- package/dist/apps/index.d.ts +10 -0
- package/dist/apps/index.js +11 -0
- package/dist/apps-cli.d.ts +3 -1
- package/dist/apps-cli.js +285 -38
- package/dist/blaxel/errors.js +2 -1
- package/dist/ci/index.d.ts +13 -0
- package/dist/ci/index.js +14 -0
- package/dist/ci-cli.d.ts +10 -0
- package/dist/ci-cli.js +402 -0
- package/dist/ci-import.d.ts +23 -0
- package/dist/ci-import.js +565 -0
- package/dist/cli.d.ts +18 -7
- package/dist/cli.js +775 -85
- package/dist/daytona/errors.js +2 -1
- package/dist/e2b/errors.js +2 -1
- package/dist/e2b/filesystem.d.ts +1 -0
- package/dist/e2b/filesystem.js +1 -1
- package/dist/e2b/sandbox.d.ts +11 -2
- package/dist/e2b/sandbox.js +22 -6
- package/dist/errors.d.ts +11 -4
- package/dist/errors.js +27 -4
- package/dist/fly/index.d.ts +102 -0
- package/dist/fly/index.js +930 -0
- package/dist/index.d.ts +7 -3
- package/dist/machine.d.ts +10 -6
- package/dist/machine.js +14 -6
- package/dist/modal/index.d.ts +34 -0
- package/dist/modal/index.js +128 -22
- package/dist/products/agents.d.ts +101 -3
- package/dist/products/agents.js +49 -0
- package/dist/products/apps.d.ts +206 -10
- package/dist/products/apps.js +172 -1
- package/dist/products/ci.d.ts +335 -0
- package/dist/products/ci.js +108 -0
- package/dist/products/index.d.ts +8 -0
- package/dist/products/index.js +15 -0
- package/dist/products/jobs.d.ts +105 -5
- package/dist/products/jobs.js +47 -3
- package/dist/products/network.d.ts +13 -0
- package/dist/products/observability.d.ts +1 -1
- package/dist/products/plans.d.ts +131 -0
- package/dist/products/plans.js +24 -0
- package/dist/products/reservations.d.ts +108 -0
- package/dist/products/reservations.js +41 -0
- package/dist/products/resources.d.ts +2 -1
- package/dist/products/secrets.d.ts +90 -6
- package/dist/products/secrets.js +8 -2
- package/dist/proxy.d.ts +3 -3
- package/dist/proxy.js +20 -0
- package/dist/sandbox.d.ts +29 -1
- package/dist/sandbox.js +56 -3
- package/dist/snapshots.d.ts +1 -1
- package/dist/switched-off.d.ts +5 -0
- package/dist/switched-off.js +8 -0
- package/dist/tar.d.ts +1 -1
- package/dist/tar.js +19 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +48 -9
- package/dist/types.d.ts +35 -4
- package/dist/vercel/errors.d.ts +1 -1
- package/dist/vercel/errors.js +2 -1
- package/dist/vercel/snapshot.js +4 -2
- package/package.json +23 -6
package/dist/apps-cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { me, named } from "./cli-name.js";
|
|
2
2
|
import { RuntimeError } from "./errors.js";
|
|
3
3
|
import { logStream } from "./terminal.js";
|
|
4
|
-
import { apps, } from "./products/apps.js";
|
|
4
|
+
import { apps, functions, } from "./products/apps.js";
|
|
5
5
|
import { builds, } from "./products/builds.js";
|
|
6
6
|
import { containers, } from "./products/containers.js";
|
|
7
7
|
export const APP_HELP = `runtime apps <command>
|
|
@@ -12,13 +12,26 @@ export const APP_HELP = `runtime apps <command>
|
|
|
12
12
|
|
|
13
13
|
create [<name>] --vcpu <1-16> --memory <128-65536> --max-copies <1-1000>
|
|
14
14
|
[--min-copies 0] [--concurrency 100] [--spares 2] [--port 8080]
|
|
15
|
-
[--health-path /healthz] [--
|
|
16
|
-
|
|
15
|
+
[--health-path /healthz] [--health-fail 60] [--stop-grace 30] [--cdn on|off]
|
|
16
|
+
[--volume <volumeId>:<path>] [--label key=value]...
|
|
17
|
+
Make an App, waiting for its first deploy; prints its id.
|
|
18
|
+
--health-fail: seconds a live copy may fail its health
|
|
19
|
+
check before it is replaced. --stop-grace: seconds a
|
|
20
|
+
stopped copy has after SIGTERM. --volume mounts one
|
|
21
|
+
volume of yours at a path under /data, /mnt, /state,
|
|
22
|
+
/storage or /volumes, in the App's one copy: it needs
|
|
23
|
+
--max-copies 1 (--min-copies is then 1, --spares 0), and
|
|
24
|
+
each deploy stops the old copy before the new one boots
|
|
17
25
|
ls [--all] Your Apps: state, live version, copies; --all, deleted too
|
|
18
26
|
get <app> One App, in full (an id or its name)
|
|
19
27
|
update <app> [--name <name>] [--vcpu n] [--memory n] [--min-copies n] [--max-copies n]
|
|
20
|
-
[--concurrency n] [--spares n] [--port n] [--health-path /path] [--
|
|
21
|
-
|
|
28
|
+
[--concurrency n] [--spares n] [--port n] [--health-path /path] [--health-fail n]
|
|
29
|
+
[--stop-grace n] [--volume <volumeId>:<path> | none] [--cdn on|off]
|
|
30
|
+
[--label key=value]...
|
|
31
|
+
Change its settings; --volume none takes the volume
|
|
32
|
+
away, only before its first deploy;
|
|
33
|
+
--cdn off sends visitors straight to Runtime's routers,
|
|
34
|
+
with nothing cached (on: Cloudflare's CDN in front)
|
|
22
35
|
rm <app> Delete it and stop its copies
|
|
23
36
|
deploy <app> (--image <image> | --build <buildId>) [--env KEY=VALUE]... [--keep-env]
|
|
24
37
|
[--port 8080] [--health-path /healthz] [--no-wait]
|
|
@@ -32,12 +45,76 @@ export const APP_HELP = `runtime apps <command>
|
|
|
32
45
|
that one (--wait: until it ends)
|
|
33
46
|
versions <app> Its versions; the live and previous ones keep saved copies
|
|
34
47
|
copies <app> Its copies now: version, region, state
|
|
48
|
+
logs <app> [--follow] [--since 1h] [--version n] [--copy <copyId>] [--stream stdout|stderr]
|
|
49
|
+
[--limit 100] [--raw] What its copies printed: the newest lines since --since
|
|
50
|
+
(a time, or 30s, 15m, 2h, 7d; kept 14 days), or with
|
|
51
|
+
--follow every line as it comes, until Ctrl-C. On a
|
|
52
|
+
terminal control characters show as \\uXXXX unless --raw
|
|
53
|
+
exec <app> [--copy <id>] [--root] [--cwd <dir>] [--env K=V]... [--timeout <ms>] [--trace-files <file.json>] -- <command>
|
|
54
|
+
Run a command in its live copy
|
|
55
|
+
cp <file> <app>:/<path> [--copy <id>] Copy a file onto its live copy
|
|
56
|
+
cp <app>:/<path> <file> [--copy <id>] Copy a file off it
|
|
57
|
+
exec and cp go to a serving copy of the live version unless --copy names
|
|
58
|
+
one. A copy holds the App's secrets, so only an owner's or admin's key
|
|
59
|
+
reaches into one. A file written to a copy lasts as long as that copy; a
|
|
60
|
+
deploy replaces its copies.
|
|
35
61
|
rollback <app> [--no-wait] Put the previous version live again
|
|
62
|
+
restart <app> [--no-wait] Start the live version afresh, as a deploy of its own;
|
|
63
|
+
with a volume, the old copy stops first and the
|
|
64
|
+
downtime is said
|
|
36
65
|
|
|
37
66
|
Examples
|
|
38
67
|
runtime apps create web --vcpu 1 --memory 512 --max-copies 3
|
|
39
68
|
runtime apps deploy web --image web:latest --env NODE_ENV=production
|
|
69
|
+
runtime apps logs web --follow
|
|
40
70
|
runtime apps rollback web
|
|
71
|
+
runtime apps create db --vcpu 1 --memory 1024 --max-copies 1 --volume <volumeId>:/data
|
|
72
|
+
`;
|
|
73
|
+
export const FUNCTION_HELP = `runtime functions <command>
|
|
74
|
+
|
|
75
|
+
A Function serves your HTTP handler from an image or a build, at its own
|
|
76
|
+
address. Copies start when calls come, up to --max-copies, and sleep when
|
|
77
|
+
they stop: an idle Function has no copy awake.
|
|
78
|
+
In a private beta: outside it, each command says how to ask to join.
|
|
79
|
+
|
|
80
|
+
create [<name>] --vcpu <1-16> --memory <128-65536> --max-copies <1-1000>
|
|
81
|
+
[--concurrency 100] [--spares 2] [--port 8080]
|
|
82
|
+
[--health-path /healthz] [--health-fail 60] [--label key=value]...
|
|
83
|
+
Make a Function, waiting for its first deploy; prints its id.
|
|
84
|
+
--health-fail: seconds a live copy may fail its health
|
|
85
|
+
check before it is replaced
|
|
86
|
+
ls [--all] Your Functions: state, live version, copies; --all, deleted too
|
|
87
|
+
get <function> One Function, in full (an id or its name)
|
|
88
|
+
update <function> [--name <name>] [--vcpu n] [--memory n] [--max-copies n]
|
|
89
|
+
[--concurrency n] [--spares n] [--port n] [--health-path /path] [--health-fail n]
|
|
90
|
+
[--label key=value]...
|
|
91
|
+
Change its settings
|
|
92
|
+
rm <function> Delete it and stop its copies
|
|
93
|
+
deploy <function> (--image <image> | --build <buildId>) [--env KEY=VALUE]... [--keep-env]
|
|
94
|
+
[--port 8080] [--health-path /healthz] [--no-wait]
|
|
95
|
+
A new version. Waits until it is live, or says why it
|
|
96
|
+
failed; --no-wait prints the deploy's id at once.
|
|
97
|
+
--image: an image of yours (id, name, name:tag);
|
|
98
|
+
--build: a build that succeeded (runtime builds).
|
|
99
|
+
--env KEY alone sends KEY's value from your environment.
|
|
100
|
+
--keep-env keeps the last deploy's environment, sealed
|
|
101
|
+
deploys <function> [<deployId>] [--wait] Each version's deploy: state and address; with an id,
|
|
102
|
+
that one (--wait: until it ends)
|
|
103
|
+
versions <function> Its versions; the live and previous ones keep saved copies
|
|
104
|
+
copies <function> Its copies now: version, region, state
|
|
105
|
+
logs <function> [--follow] [--since 1h] [--version n] [--copy <copyId>] [--stream stdout|stderr]
|
|
106
|
+
[--limit 100] [--raw] What its copies printed: the newest lines since --since
|
|
107
|
+
(a time, or 30s, 15m, 2h, 7d; kept 14 days), or with
|
|
108
|
+
--follow every line as it comes, until Ctrl-C. On a
|
|
109
|
+
terminal control characters show as \\uXXXX unless --raw
|
|
110
|
+
rollback <function> [--no-wait] Put the previous version live again
|
|
111
|
+
restart <function> [--no-wait] Start the live version afresh, as a deploy of its own
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
runtime functions create resize --vcpu 1 --memory 512 --max-copies 10
|
|
115
|
+
runtime functions deploy resize --image resize:latest
|
|
116
|
+
runtime functions logs resize --follow
|
|
117
|
+
runtime functions rollback resize
|
|
41
118
|
`;
|
|
42
119
|
export const BUILD_HELP = `runtime builds <command>
|
|
43
120
|
|
|
@@ -105,31 +182,82 @@ Examples
|
|
|
105
182
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
106
183
|
const when = (ms) => (ms == null ? "-" : new Date(ms).toISOString());
|
|
107
184
|
const ENDED = ["succeeded", "failed", "cancelled", "superseded"];
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
185
|
+
const seconds = (ms) => `${(ms / 1000).toFixed(1)} s`;
|
|
186
|
+
/** A stop-then-start's downtime, in a person's words. */
|
|
187
|
+
const downtime = (d) => d ? `down ${seconds(d.totalMs)} (stop ${seconds(d.stopMs)}, boot ${seconds(d.bootMs)})` : "-";
|
|
188
|
+
const APP = {
|
|
189
|
+
api: apps,
|
|
190
|
+
command: "apps",
|
|
191
|
+
one: "App",
|
|
192
|
+
many: "Apps",
|
|
193
|
+
help: APP_HELP,
|
|
194
|
+
minCopies: true,
|
|
195
|
+
example: "web --vcpu 1 --memory 512 --max-copies 2",
|
|
196
|
+
};
|
|
197
|
+
const FUNCTION = {
|
|
198
|
+
// The same calls under /v1/functions; the API answers minCopies 0.
|
|
199
|
+
api: functions,
|
|
200
|
+
command: "functions",
|
|
201
|
+
one: "Function",
|
|
202
|
+
many: "Functions",
|
|
203
|
+
help: FUNCTION_HELP,
|
|
204
|
+
minCopies: false,
|
|
205
|
+
example: "resize --vcpu 1 --memory 512 --max-copies 10",
|
|
206
|
+
};
|
|
207
|
+
/** An App's or Function's id from its id or its name: a name is looked up
|
|
208
|
+
* among those not deleted, and must be one's alone. */
|
|
209
|
+
async function appIdOf(api, target, kit, kind) {
|
|
111
210
|
if (UUID.test(target))
|
|
112
211
|
return target;
|
|
113
212
|
const found = (await api.list({ name: target, state: ["waiting", "live", "failed"] })).data;
|
|
114
213
|
if (found.length === 1)
|
|
115
214
|
return found[0].id;
|
|
116
215
|
throw kit.usage(found.length
|
|
117
|
-
? `${found.length}
|
|
118
|
-
: `No
|
|
216
|
+
? `${found.length} ${kind.many} are named ${target}; give one's id (${me} ${kind.command} ls).`
|
|
217
|
+
: `No ${kind.one} is named ${target}. Your ${kind.many}: ${me} ${kind.command} ls`);
|
|
218
|
+
}
|
|
219
|
+
/** `--cdn on` or `--cdn off`. */
|
|
220
|
+
function onOff(value, kit) {
|
|
221
|
+
if (value === "on")
|
|
222
|
+
return true;
|
|
223
|
+
if (value === "off")
|
|
224
|
+
return false;
|
|
225
|
+
throw kit.usage(`--cdn takes on or off, not ${value}.`);
|
|
119
226
|
}
|
|
120
|
-
/**
|
|
121
|
-
function
|
|
227
|
+
/** --volume <volumeId>:<path>, or none (an update's: no volume). */
|
|
228
|
+
function volume(args, kit) {
|
|
229
|
+
const given = kit.flag(args, "volume");
|
|
230
|
+
if (given === undefined)
|
|
231
|
+
return {};
|
|
232
|
+
if (given === "none")
|
|
233
|
+
return { volume: null };
|
|
234
|
+
const at = given.indexOf(":");
|
|
235
|
+
const volumeId = given.slice(0, at);
|
|
236
|
+
const path = given.slice(at + 1);
|
|
237
|
+
if (at < 0 || !UUID.test(volumeId) || !path.startsWith("/"))
|
|
238
|
+
throw kit.usage(`--volume takes a volume's id and where it mounts: --volume <volumeId>:/data (${me} volumes ls)`);
|
|
239
|
+
return { volume: { volumeId, path } };
|
|
240
|
+
}
|
|
241
|
+
/** The flags an App's settings are given with, as the API names them. A
|
|
242
|
+
* Function's leave out --min-copies, --stop-grace and --volume, which its
|
|
243
|
+
* parse does not take. */
|
|
244
|
+
function settings(args, kit, kind) {
|
|
122
245
|
const { integer, flag } = kit;
|
|
123
246
|
const number = (name, field) => integer(args, name) === undefined ? {} : { [field]: integer(args, name) };
|
|
124
247
|
return {
|
|
125
248
|
...number("vcpu", "vcpu"),
|
|
126
249
|
...number("memory", "memoryMiB"),
|
|
127
|
-
...number("min-copies", "minCopies"),
|
|
250
|
+
...(kind.minCopies ? number("min-copies", "minCopies") : {}),
|
|
128
251
|
...number("max-copies", "maxCopies"),
|
|
129
252
|
...number("concurrency", "concurrency"),
|
|
130
253
|
...number("spares", "spares"),
|
|
131
254
|
...number("port", "port"),
|
|
132
255
|
...(flag(args, "health-path") === undefined ? {} : { healthPath: flag(args, "health-path") }),
|
|
256
|
+
...(flag(args, "cdn") === undefined ? {} : { cdn: onOff(flag(args, "cdn"), kit) }),
|
|
257
|
+
...number("health-fail", "healthFailSeconds"),
|
|
258
|
+
...(kind.minCopies
|
|
259
|
+
? { ...number("stop-grace", "stopGraceSeconds"), ...volume(args, kit) }
|
|
260
|
+
: {}),
|
|
133
261
|
};
|
|
134
262
|
}
|
|
135
263
|
const SETTINGS = [
|
|
@@ -141,8 +269,26 @@ const SETTINGS = [
|
|
|
141
269
|
"spares",
|
|
142
270
|
"port",
|
|
143
271
|
"health-path",
|
|
272
|
+
"cdn",
|
|
273
|
+
"health-fail",
|
|
274
|
+
"stop-grace",
|
|
275
|
+
"volume",
|
|
144
276
|
"label",
|
|
145
277
|
];
|
|
278
|
+
const settingsOf = (kind) => kind.minCopies
|
|
279
|
+
? SETTINGS
|
|
280
|
+
: SETTINGS.filter((name) => !["min-copies", "stop-grace", "volume"].includes(name));
|
|
281
|
+
/** A version's copies replaced while live: how many, and why. */
|
|
282
|
+
function replaced(list) {
|
|
283
|
+
if (!list?.length)
|
|
284
|
+
return "-";
|
|
285
|
+
const health = list.filter((r) => r.reason === "health").length;
|
|
286
|
+
const why = [
|
|
287
|
+
health ? `${health} health` : "",
|
|
288
|
+
list.length - health ? `${list.length - health} exited` : "",
|
|
289
|
+
];
|
|
290
|
+
return `${list.length} (${why.filter(Boolean).join(", ")})`;
|
|
291
|
+
}
|
|
146
292
|
/** A deploy that ended badly, said with its own words, hint and last output. */
|
|
147
293
|
function deployFailure(deploy) {
|
|
148
294
|
if (!deploy.error)
|
|
@@ -156,29 +302,40 @@ function deployFailure(deploy) {
|
|
|
156
302
|
details: { deploy },
|
|
157
303
|
});
|
|
158
304
|
}
|
|
159
|
-
export
|
|
305
|
+
export function appsCommand(argv, env, out, client, kit) {
|
|
306
|
+
return copiesCommand(APP, argv, env, out, client, kit);
|
|
307
|
+
}
|
|
308
|
+
export function functionsCommand(argv, env, out, client, kit) {
|
|
309
|
+
return copiesCommand(FUNCTION, argv, env, out, client, kit);
|
|
310
|
+
}
|
|
311
|
+
async function copiesCommand(kind, argv, env, out, client, kit) {
|
|
160
312
|
const { parse, flag, has, need, pairs, envPairs, table, usage } = kit;
|
|
161
313
|
const [verb, ...rest] = argv;
|
|
162
314
|
const print = (human, value) => out.write(out.json ? JSON.stringify(value) : human);
|
|
163
315
|
if (!verb || verb === "help" || verb === "--help") {
|
|
164
|
-
print(named(
|
|
316
|
+
print(named(kind.help), { usage: named(kind.help) });
|
|
165
317
|
return 0;
|
|
166
318
|
}
|
|
167
|
-
const api =
|
|
168
|
-
const
|
|
319
|
+
const api = kind.api((await client()).transport);
|
|
320
|
+
const { command, one, many } = kind;
|
|
321
|
+
const idOf = (target) => appIdOf(api, need(target, `${one === "App" ? "an" : "a"} ${one}'s id or name`), kit, kind);
|
|
169
322
|
switch (verb) {
|
|
170
323
|
case "create": {
|
|
171
|
-
const args = parse(rest, [],
|
|
324
|
+
const args = parse(rest, [], settingsOf(kind));
|
|
172
325
|
if (args.positional.length > 1)
|
|
173
|
-
throw usage(`Give one name: ${me}
|
|
174
|
-
const given = settings(args, kit);
|
|
326
|
+
throw usage(`Give one name: ${me} ${command} create ${kind.example}`);
|
|
327
|
+
const given = settings(args, kit, kind);
|
|
328
|
+
if (given.volume === null)
|
|
329
|
+
throw usage(`--volume none is for update: leave --volume out to make an App without one.`);
|
|
175
330
|
for (const [option, field] of [
|
|
176
331
|
["vcpu", "vcpu"],
|
|
177
332
|
["memory", "memoryMiB"],
|
|
178
333
|
["max-copies", "maxCopies"],
|
|
179
334
|
])
|
|
180
335
|
if (given[field] === undefined)
|
|
181
|
-
throw usage(`Give --${option}: ${me}
|
|
336
|
+
throw usage(`Give --${option}: ${me} ${command} create ${args.positional[0]
|
|
337
|
+
? `${args.positional[0]} ${kind.example.replace(/^\S+ /, "")}`
|
|
338
|
+
: kind.example}`);
|
|
182
339
|
const input = {
|
|
183
340
|
...given,
|
|
184
341
|
...(args.positional[0] ? { name: args.positional[0] } : {}),
|
|
@@ -186,7 +343,7 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
186
343
|
};
|
|
187
344
|
const app = await api.create(input);
|
|
188
345
|
if (!out.json)
|
|
189
|
-
out.error(`Created ${app.name ?? app.id}, waiting for its first version. Deploy one: ${me}
|
|
346
|
+
out.error(`Created ${app.name ?? app.id}, waiting for its first version. Deploy one: ${me} ${command} deploy ${app.name ?? app.id} --image <image>`);
|
|
190
347
|
print(app.id, app);
|
|
191
348
|
return 0;
|
|
192
349
|
}
|
|
@@ -201,18 +358,21 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
201
358
|
})).toArray(Infinity);
|
|
202
359
|
print(all.length
|
|
203
360
|
? table([
|
|
204
|
-
["ID", "NAME", "STATE", "LIVE", "COPIES", "SIZE", "CREATED"],
|
|
361
|
+
["ID", "NAME", "ADDRESS", "STATE", "LIVE", "COPIES", "SIZE", "CREATED"],
|
|
205
362
|
...all.map((a) => [
|
|
206
363
|
a.id,
|
|
207
364
|
a.name ?? "-",
|
|
208
|
-
a.
|
|
365
|
+
a.url ?? "-",
|
|
366
|
+
// A copy waiting for room: its place in its region's line.
|
|
367
|
+
a.line ? `${a.state} (in line, place ${a.line.place})` : a.state,
|
|
209
368
|
a.liveVersion === null ? "-" : `v${a.liveVersion}`,
|
|
210
|
-
|
|
369
|
+
// A Function's copies all sleep when idle: up to its maximum.
|
|
370
|
+
kind.minCopies ? `${a.minCopies}-${a.maxCopies}` : `0-${a.maxCopies}`,
|
|
211
371
|
`${a.vcpu} vCPU, ${a.memoryMiB} MiB`,
|
|
212
372
|
when(a.createdAt),
|
|
213
373
|
]),
|
|
214
374
|
])
|
|
215
|
-
: `No
|
|
375
|
+
: `No ${many}. Make one: ${me} ${command} create ${kind.example}`, all);
|
|
216
376
|
return 0;
|
|
217
377
|
}
|
|
218
378
|
case "get": {
|
|
@@ -221,15 +381,15 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
221
381
|
return 0;
|
|
222
382
|
}
|
|
223
383
|
case "update": {
|
|
224
|
-
const args = parse(rest, [], [...
|
|
384
|
+
const args = parse(rest, [], [...settingsOf(kind), "name"]);
|
|
225
385
|
const id = await idOf(args.positional[0]);
|
|
226
386
|
const change = {
|
|
227
|
-
...settings(args, kit),
|
|
387
|
+
...settings(args, kit, kind),
|
|
228
388
|
...(flag(args, "name") === undefined ? {} : { name: flag(args, "name") }),
|
|
229
389
|
...(args.flags.has("label") ? { labels: pairs(args, "label") } : {}),
|
|
230
390
|
};
|
|
231
391
|
if (!Object.keys(change).length)
|
|
232
|
-
throw usage(`Give a setting to change, such as ${me}
|
|
392
|
+
throw usage(`Give a setting to change, such as ${me} ${command} update ${args.positional[0]} --max-copies 4`);
|
|
233
393
|
const app = await api.update(id, change);
|
|
234
394
|
print(`${app.name ?? app.id} updated.`, app);
|
|
235
395
|
return 0;
|
|
@@ -265,8 +425,8 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
265
425
|
if (failed && !out.json)
|
|
266
426
|
throw failed;
|
|
267
427
|
print(deploy.state === "live"
|
|
268
|
-
? `Version ${deploy.version} is live at ${deploy.url}`
|
|
269
|
-
: `Version ${deploy.version}: ${deploy.state}. Follow it: ${me}
|
|
428
|
+
? `Version ${deploy.version} is live at ${deploy.url}${deploy.downtime ? `, ${downtime(deploy.downtime)}` : ""}`
|
|
429
|
+
: `Version ${deploy.version}: ${deploy.state}. Follow it: ${me} ${command} deploys ${args.positional[0]} ${deploy.id} --wait`, deploy);
|
|
270
430
|
return failed ? 1 : 0;
|
|
271
431
|
}
|
|
272
432
|
case "deploys": {
|
|
@@ -289,16 +449,21 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
289
449
|
const deploys = await Promise.all(versions.map((v) => api.getDeploy(id, v.deploy)));
|
|
290
450
|
print(deploys.length
|
|
291
451
|
? table([
|
|
292
|
-
["VERSION", "DEPLOY", "STATE", "FROM", "ADDRESS"],
|
|
452
|
+
["VERSION", "DEPLOY", "STATE", "FROM", "DOWNTIME", "ADDRESS"],
|
|
293
453
|
...deploys.map((d, i) => [
|
|
294
454
|
`v${d.version}${versions[i].live ? " (live)" : versions[i].previous ? " (previous)" : ""}`,
|
|
295
455
|
d.id,
|
|
296
456
|
d.state,
|
|
297
|
-
|
|
457
|
+
d.restartOf != null
|
|
458
|
+
? `restart of v${d.restartOf}`
|
|
459
|
+
: versions[i].build
|
|
460
|
+
? `build ${versions[i].build}`
|
|
461
|
+
: "image",
|
|
462
|
+
downtime(d.downtime),
|
|
298
463
|
d.url,
|
|
299
464
|
]),
|
|
300
465
|
])
|
|
301
|
-
: `No deploys yet. Deploy one: ${me}
|
|
466
|
+
: `No deploys yet. Deploy one: ${me} ${command} deploy ${args.positional[0]} --image <image>`, deploys);
|
|
302
467
|
return 0;
|
|
303
468
|
}
|
|
304
469
|
case "versions": {
|
|
@@ -306,14 +471,16 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
306
471
|
const versions = (await api.versions(id)).sort((a, b) => b.version - a.version);
|
|
307
472
|
print(versions.length
|
|
308
473
|
? table([
|
|
309
|
-
["VERSION", "LIVE", "PREVIOUS", "SAVED", "DEPLOY", "BUILD"],
|
|
474
|
+
["VERSION", "LIVE", "PREVIOUS", "SAVED", "BOOT", "DEPLOY", "BUILD", "REPLACED"],
|
|
310
475
|
...versions.map((v) => [
|
|
311
|
-
`v${v.version}`,
|
|
476
|
+
`v${v.version}${v.restartOf == null ? "" : ` (restart of v${v.restartOf})`}`,
|
|
312
477
|
v.live ? "yes" : "-",
|
|
313
478
|
v.previous ? "yes" : "-",
|
|
314
479
|
v.saved ? "yes" : "-",
|
|
480
|
+
v.boot ?? "-",
|
|
315
481
|
v.deploy,
|
|
316
482
|
v.build ?? "-",
|
|
483
|
+
replaced(v.replacements),
|
|
317
484
|
]),
|
|
318
485
|
])
|
|
319
486
|
: "No versions yet.", versions);
|
|
@@ -327,7 +494,68 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
327
494
|
["COPY", "VERSION", "REGION", "STATE", "SINCE"],
|
|
328
495
|
...copies.map((c) => [c.id, `v${c.version}`, c.region, c.state, when(c.since)]),
|
|
329
496
|
])
|
|
330
|
-
:
|
|
497
|
+
: `No copies awake. ${one === "App" ? "A request" : "A call"} starts one.`, copies);
|
|
498
|
+
return 0;
|
|
499
|
+
}
|
|
500
|
+
case "exec": {
|
|
501
|
+
const args = parse(rest, ["root"], ["cwd", "env", "timeout", "copy", "trace-files"]);
|
|
502
|
+
const command = args.rest ?? args.positional.slice(1);
|
|
503
|
+
if (!command.length)
|
|
504
|
+
throw usage(`Give the command after --: ${me} apps exec <app> -- ls -la`);
|
|
505
|
+
const copy = await api.copy(await idOf(args.positional[0]), {
|
|
506
|
+
...(flag(args, "copy") ? { copy: flag(args, "copy") } : {}),
|
|
507
|
+
});
|
|
508
|
+
return kit.execute(copy, command, args, out, env);
|
|
509
|
+
}
|
|
510
|
+
case "cp": {
|
|
511
|
+
const args = parse(rest, [], ["copy"]);
|
|
512
|
+
const [from, to] = args.positional;
|
|
513
|
+
await kit.copyFile(need(from, "a source"), need(to, "a destination"), async (app) => api.copy(await idOf(app), {
|
|
514
|
+
...(flag(args, "copy") ? { copy: flag(args, "copy") } : {}),
|
|
515
|
+
}), "<app>");
|
|
516
|
+
print(`Copied ${from} to ${to}.`, { from, to });
|
|
517
|
+
return 0;
|
|
518
|
+
}
|
|
519
|
+
case "logs": {
|
|
520
|
+
const args = parse(rest, ["f", "follow", "raw"], ["since", "version", "copy", "stream", "limit"]);
|
|
521
|
+
const id = await idOf(args.positional[0]);
|
|
522
|
+
const named = flag(args, "stream");
|
|
523
|
+
const stream = ["stdout", "stderr"].find((s) => s === named);
|
|
524
|
+
if (named !== undefined && stream === undefined)
|
|
525
|
+
throw usage(`--stream is stdout or stderr: ${me} ${command} logs ${args.positional[0]} --stream stderr`);
|
|
526
|
+
const version = kit.integer(args, "version");
|
|
527
|
+
const limit = kit.integer(args, "limit");
|
|
528
|
+
const query = {
|
|
529
|
+
...(flag(args, "since") === undefined ? {} : { since: flag(args, "since") }),
|
|
530
|
+
...(version === undefined ? {} : { version }),
|
|
531
|
+
...(flag(args, "copy") === undefined ? {} : { copy: flag(args, "copy") }),
|
|
532
|
+
...(stream === undefined ? {} : { stream }),
|
|
533
|
+
...(limit === undefined ? {} : { limit }),
|
|
534
|
+
};
|
|
535
|
+
const raw = has(args, "raw");
|
|
536
|
+
const [stdout, stderr] = [logStream(process.stdout, raw), logStream(process.stderr, raw)];
|
|
537
|
+
const write = (line) => (line.stream === "stderr" ? stderr : stdout)(line.text.endsWith("\n") ? line.text : `${line.text}\n`);
|
|
538
|
+
if (!has(args, "f") && !has(args, "follow")) {
|
|
539
|
+
const page = await api.logs(id, query);
|
|
540
|
+
if (out.json)
|
|
541
|
+
out.write(JSON.stringify(page));
|
|
542
|
+
else if (page.lines.length)
|
|
543
|
+
page.lines.forEach(write);
|
|
544
|
+
else
|
|
545
|
+
out.error(`[no lines ${query.since ? `since ${query.since}` : "in the last hour"}; try --since 1d, or --follow]`);
|
|
546
|
+
if (!out.json && page.hasMore)
|
|
547
|
+
out.error(`[older lines are kept: --since further back, or --limit up to 500]`);
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
// Until Ctrl-C: the SDK reconnects across the server's two-minute calls.
|
|
551
|
+
for await (const event of api.logs(id, { ...query, follow: true }, { timeoutMs: 0 })) {
|
|
552
|
+
if (out.json)
|
|
553
|
+
out.write(JSON.stringify(event));
|
|
554
|
+
else if (event.type === "line")
|
|
555
|
+
write(event);
|
|
556
|
+
else
|
|
557
|
+
out.error("[some lines were dropped before they were read; going on]");
|
|
558
|
+
}
|
|
331
559
|
return 0;
|
|
332
560
|
}
|
|
333
561
|
case "rollback": {
|
|
@@ -345,8 +573,23 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
345
573
|
: `Rolling back to version ${deploy.version}: ${deploy.state}.`, deploy);
|
|
346
574
|
return failed ? 1 : 0;
|
|
347
575
|
}
|
|
576
|
+
case "restart": {
|
|
577
|
+
const args = parse(rest, ["no-wait"], []);
|
|
578
|
+
const id = await idOf(args.positional[0]);
|
|
579
|
+
const deploy = await api.restart(id, {
|
|
580
|
+
wait: !has(args, "no-wait"),
|
|
581
|
+
timeoutMs: 15 * 60_000,
|
|
582
|
+
});
|
|
583
|
+
const failed = deployFailure(deploy);
|
|
584
|
+
if (failed && !out.json)
|
|
585
|
+
throw failed;
|
|
586
|
+
print(deploy.state === "live"
|
|
587
|
+
? `Version ${deploy.version} restarted, live at ${deploy.url}${deploy.downtime ? `, ${downtime(deploy.downtime)}` : ""}`
|
|
588
|
+
: `Restarting version ${deploy.version}: ${deploy.state}. Follow it: ${me} ${command} deploys ${args.positional[0]} ${deploy.id} --wait`, deploy);
|
|
589
|
+
return failed ? 1 : 0;
|
|
590
|
+
}
|
|
348
591
|
}
|
|
349
|
-
throw kit.unknown(
|
|
592
|
+
throw kit.unknown(`${command} command`, verb, [
|
|
350
593
|
"create",
|
|
351
594
|
"ls",
|
|
352
595
|
"get",
|
|
@@ -356,7 +599,11 @@ export async function appsCommand(argv, env, out, client, kit) {
|
|
|
356
599
|
"deploys",
|
|
357
600
|
"versions",
|
|
358
601
|
"copies",
|
|
602
|
+
"exec",
|
|
603
|
+
"cp",
|
|
604
|
+
"logs",
|
|
359
605
|
"rollback",
|
|
606
|
+
"restart",
|
|
360
607
|
]);
|
|
361
608
|
}
|
|
362
609
|
/** A repository as connect takes it: GitHub's owner/name, a github.com
|
package/dist/blaxel/errors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RuntimeError } from "../errors.js";
|
|
2
|
+
import { productSwitchedOff } from "../switched-off.js";
|
|
2
3
|
/* Blaxel's error classes by the same names, so a
|
|
3
4
|
`catch (e) { if (e instanceof ResponseError) ... }` or an
|
|
4
5
|
`isGatewayError(e)` written for Blaxel keeps working. One made from a
|
|
@@ -143,7 +144,7 @@ export function translate(error) {
|
|
|
143
144
|
return Object.assign(new CredentialsError("No Runtime key found. A Blaxel key (BL_API_KEY) is never sent anywhere: set RUNTIME_API_KEY to a " +
|
|
144
145
|
"Runtime key (https://withruntime.com/account/keys), put a Runtime key (rtcloud_...) in BL_API_KEY, " +
|
|
145
146
|
"or run `npx withruntime login` once."), { cause: error });
|
|
146
|
-
if (error
|
|
147
|
+
if (productSwitchedOff(error))
|
|
147
148
|
return new NotSupportedError(error.code.replace(/_unavailable$/, ""), error.hint ?? "", error.message);
|
|
148
149
|
if (error.status < 200 || error.status > 599)
|
|
149
150
|
return error;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** `withruntime/ci`: CI/CD alone. Each push and pull request of a connected
|
|
2
|
+
* GitHub repository is checked in sandboxes. In a private beta.
|
|
3
|
+
*
|
|
4
|
+
* import { ci } from "withruntime/ci";
|
|
5
|
+
* const client = ci(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
6
|
+
*/
|
|
7
|
+
import { Runtime, type RuntimeOptions } from "../client.js";
|
|
8
|
+
export type * from "../products/ci.js";
|
|
9
|
+
/** `runtime.ci` of a client made with `options`. */
|
|
10
|
+
export declare function ci(options?: RuntimeOptions): Runtime["ci"];
|
|
11
|
+
/** GitHub Actions workflows and a CircleCI config as a `runtime.yml`, with
|
|
12
|
+
* notes on what was not carried over (`runtime ci import`). */
|
|
13
|
+
export { importCi, type CiImport, type CiSource } from "../ci-import.js";
|
package/dist/ci/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** `withruntime/ci`: CI/CD alone. Each push and pull request of a connected
|
|
2
|
+
* GitHub repository is checked in sandboxes. In a private beta.
|
|
3
|
+
*
|
|
4
|
+
* import { ci } from "withruntime/ci";
|
|
5
|
+
* const client = ci(); // RUNTIME_API_KEY, or this machine's saved connection
|
|
6
|
+
*/
|
|
7
|
+
import { Runtime } from "../client.js";
|
|
8
|
+
/** `runtime.ci` of a client made with `options`. */
|
|
9
|
+
export function ci(options) {
|
|
10
|
+
return new Runtime(options).ci;
|
|
11
|
+
}
|
|
12
|
+
/** GitHub Actions workflows and a CircleCI config as a `runtime.yml`, with
|
|
13
|
+
* notes on what was not carried over (`runtime ci import`). */
|
|
14
|
+
export { importCi } from "../ci-import.js";
|
package/dist/ci-cli.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CliKit } from "./cli.js";
|
|
2
|
+
import type { Runtime } from "./client.js";
|
|
3
|
+
type Out = {
|
|
4
|
+
json: boolean;
|
|
5
|
+
write: (text: string) => void;
|
|
6
|
+
error: (text: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const CI_HELP = "runtime ci <command>\n\n CI/CD checks each push and pull request of a connected GitHub repository in\n sandboxes, as the steps its config file names. A fork's pull request waits\n until a writer re-runs it on GitHub. In a private beta: outside it, each\n command says how to ask to join.\n\n import [dir] [--write] [--force] This folder's GitHub Actions workflows and CircleCI\n config as a runtime.yml: printed, or written with\n --write; what isn't carried over is noted at its top.\n Needs no key and sends nothing\n connect <owner/name> [--branch main] [--config runtime.yml] [--installation <id>]\n [--trust <app-slug>[bot]]... [--name <name>]\n Connect a repository; prints its id. Takes a full key.\n --trust runs that App's pushes as its own from the start\n repos [ls] [--all] Connected repositories, with the Apps each trusts;\n --all: disconnected ones too\n repos trust <repoId | owner/name> <app-slug>[bot]...\n Run that App's pushes as the repository's own code,\n keeping the Apps it trusts already\n repos untrust <repoId | owner/name> <app-slug>[bot]...\n Stop trusting those Apps, keeping the rest\n disconnect <repoId | owner/name> Stop checking it; its checks still going are cancelled\n ls [--repository <repoId>] [--branch <name>] [--commit <sha>] [--pr <number>]\n [--state <state>]... [--limit 20] Checks, newest first, with their jobs by state\n get <checkId> One check and its jobs in plan order\n cancel <checkId> Cancel a queued or running check: its jobs end,\n its machines are let go, GitHub is told\n logs <jobId> A job's output, its last 256 KiB, and its sandboxes\n tests <repoId> [--quarantined] Its test files: time, recent outcomes, quarantine\n flakes [--repository <repoId>] [--all] Flaky tests: each quarantined (or still gating), its\n owner and 48-hour deadline; --all: ended ones too\n flake <flakeId> One flaky test: its proof, owner, and recent runs\n flake prove <flakeId> Its 100 proof runs again at main's next commit\n flake hand <flakeId> <login | org/team> Hand it to someone else\n flake delete <flakeId> --reason <why> Say why its test is deleted; it ends once main\n no longer holds the file\n\n A failed job's sandbox may be kept paused: `runtime sandboxes wake <id>`, then\n `runtime sandboxes shell <id>` to look around where it failed.\n\nExamples\n runtime ci import --write\n runtime ci connect acme/web --config .runtime/ci.yml\n runtime ci repos trust acme/web acme-bot[bot]\n runtime ci ls --commit $(git rev-parse HEAD)\n runtime ci logs <jobId>\n";
|
|
9
|
+
export declare function ciCommand(argv: string[], out: Out, client: () => Promise<Runtime>, kit: CliKit): Promise<number>;
|
|
10
|
+
export {};
|