withruntime 0.33.1 → 0.33.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ci-cli.d.ts +1 -1
- package/dist/ci-cli.js +20 -1
- package/dist/keys.js +8 -8
- package/dist/login.js +8 -8
- package/dist/products/ci.d.ts +9 -0
- package/dist/products/ci.js +11 -0
- package/dist/sealing.d.ts +6 -0
- package/dist/sealing.js +14 -0
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +1 -1
- package/package.json +1 -1
package/dist/ci-cli.d.ts
CHANGED
|
@@ -5,6 +5,6 @@ type Out = {
|
|
|
5
5
|
write: (text: string) => void;
|
|
6
6
|
error: (text: string) => void;
|
|
7
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";
|
|
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 repos undo <repoId | owner/name> on|off When main's check fails, find the first commit that\n fails it and open a pull request reverting it, which\n lands once its check passes. Off until turned on;\n takes an owner's or admin's full key\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 repos undo acme/web on\n runtime ci ls --commit $(git rev-parse HEAD)\n runtime ci logs <jobId>\n";
|
|
9
9
|
export declare function ciCommand(argv: string[], out: Out, client: () => Promise<Runtime>, kit: CliKit): Promise<number>;
|
|
10
10
|
export {};
|
package/dist/ci-cli.js
CHANGED
|
@@ -25,6 +25,10 @@ export const CI_HELP = `runtime ci <command>
|
|
|
25
25
|
keeping the Apps it trusts already
|
|
26
26
|
repos untrust <repoId | owner/name> <app-slug>[bot]...
|
|
27
27
|
Stop trusting those Apps, keeping the rest
|
|
28
|
+
repos undo <repoId | owner/name> on|off When main's check fails, find the first commit that
|
|
29
|
+
fails it and open a pull request reverting it, which
|
|
30
|
+
lands once its check passes. Off until turned on;
|
|
31
|
+
takes an owner's or admin's full key
|
|
28
32
|
disconnect <repoId | owner/name> Stop checking it; its checks still going are cancelled
|
|
29
33
|
ls [--repository <repoId>] [--branch <name>] [--commit <sha>] [--pr <number>]
|
|
30
34
|
[--state <state>]... [--limit 20] Checks, newest first, with their jobs by state
|
|
@@ -48,6 +52,7 @@ Examples
|
|
|
48
52
|
runtime ci import --write
|
|
49
53
|
runtime ci connect acme/web --config .runtime/ci.yml
|
|
50
54
|
runtime ci repos trust acme/web acme-bot[bot]
|
|
55
|
+
runtime ci repos undo acme/web on
|
|
51
56
|
runtime ci ls --commit $(git rev-parse HEAD)
|
|
52
57
|
runtime ci logs <jobId>
|
|
53
58
|
`;
|
|
@@ -181,6 +186,18 @@ export async function ciCommand(argv, out, client, kit) {
|
|
|
181
186
|
print(`${repo.repository} trusts ${trusted(repo)}.`, repo);
|
|
182
187
|
return 0;
|
|
183
188
|
}
|
|
189
|
+
if (action === "undo") {
|
|
190
|
+
const id = need(more[0], "the repository's id or owner/name");
|
|
191
|
+
const say = need(more[1], "on or off");
|
|
192
|
+
if (say !== "on" && say !== "off")
|
|
193
|
+
throw usage(`${say} is neither on nor off: ${me} ci repos undo ${id} on`);
|
|
194
|
+
const found = await api.repositories.find(id);
|
|
195
|
+
const repo = await api.repositories.setUndo(found?.id ?? id, say === "on");
|
|
196
|
+
print(repo.undoCulprits
|
|
197
|
+
? `${repo.repository}: when ${repo.productionBranch}'s check fails, its culprit is found and a pull request reverting it opens.`
|
|
198
|
+
: `${repo.repository}: a failing ${repo.productionBranch} is reported, never undone.`, repo);
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
184
201
|
if (action === "connect")
|
|
185
202
|
return ciCommand(["connect", ...more], out, client, kit);
|
|
186
203
|
if (action === "disconnect" || action === "rm")
|
|
@@ -192,6 +209,7 @@ export async function ciCommand(argv, out, client, kit) {
|
|
|
192
209
|
"connect",
|
|
193
210
|
"trust",
|
|
194
211
|
"untrust",
|
|
212
|
+
"undo",
|
|
195
213
|
"disconnect",
|
|
196
214
|
]);
|
|
197
215
|
const args = parse(listing, ["all"], []);
|
|
@@ -201,13 +219,14 @@ export async function ciCommand(argv, out, client, kit) {
|
|
|
201
219
|
})).toArray(Infinity);
|
|
202
220
|
print(all.length
|
|
203
221
|
? table([
|
|
204
|
-
["ID", "REPOSITORY", "BRANCH", "CONFIG", "TRUSTED APPS", "STATE"],
|
|
222
|
+
["ID", "REPOSITORY", "BRANCH", "CONFIG", "TRUSTED APPS", "UNDO", "STATE"],
|
|
205
223
|
...all.map((r) => [
|
|
206
224
|
r.id,
|
|
207
225
|
r.repository,
|
|
208
226
|
r.productionBranch,
|
|
209
227
|
r.configPath,
|
|
210
228
|
trusted(r),
|
|
229
|
+
r.undoCulprits ? "on" : "off",
|
|
211
230
|
r.state,
|
|
212
231
|
]),
|
|
213
232
|
])
|
package/dist/keys.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { privateDecrypt } from "node:crypto";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { connectionOrigins } from "./credentials.js";
|
|
@@ -7,6 +7,7 @@ import { RuntimeError } from "./errors.js";
|
|
|
7
7
|
import { named } from "./cli-name.js";
|
|
8
8
|
import { sandboxIdentityHeaders } from "./identity.js";
|
|
9
9
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
10
|
+
import { sealing } from "./sealing.js";
|
|
10
11
|
const KEY = /^rtcloud_([a-f0-9-]{36})_[A-Za-z0-9_-]{43}$/;
|
|
11
12
|
/** How often to ask whether the owner has answered. The website answers
|
|
12
13
|
* "pending" to anything faster than every 4.5 seconds. */
|
|
@@ -79,16 +80,19 @@ export async function createKey(request, env, options = {}) {
|
|
|
79
80
|
async function post(path, body, key) {
|
|
80
81
|
const address = `${origins.auth}/api/connect/${path}`;
|
|
81
82
|
let response;
|
|
83
|
+
// The sandbox's identity first: its own lookup has its own deadline, and
|
|
84
|
+
// the call's 20 s start when the call does.
|
|
85
|
+
const identity = await sandboxIdentityHeaders();
|
|
82
86
|
try {
|
|
83
87
|
response = await fetcher(address, {
|
|
84
88
|
method: "POST",
|
|
85
89
|
redirect: "error",
|
|
86
|
-
signal: AbortSignal.timeout(20_000),
|
|
87
90
|
headers: {
|
|
88
91
|
"content-type": "application/json",
|
|
89
92
|
...(key ? { authorization: `Bearer ${key}` } : {}),
|
|
90
|
-
...
|
|
93
|
+
...identity,
|
|
91
94
|
},
|
|
95
|
+
signal: AbortSignal.timeout(20_000),
|
|
92
96
|
body: JSON.stringify(body),
|
|
93
97
|
});
|
|
94
98
|
}
|
|
@@ -101,11 +105,7 @@ export async function createKey(request, env, options = {}) {
|
|
|
101
105
|
throw failure(response.status === 429 ? "rate_limited" : "key_request_failed", typeof result.error === "string" ? result.error : "Runtime could not take this request.", response.status);
|
|
102
106
|
return result;
|
|
103
107
|
}
|
|
104
|
-
const pair = await
|
|
105
|
-
modulusLength: 2048,
|
|
106
|
-
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
107
|
-
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
108
|
-
});
|
|
108
|
+
const pair = await sealing.pair();
|
|
109
109
|
const started = await post("start", {
|
|
110
110
|
purpose: "key",
|
|
111
111
|
agentName: name,
|
package/dist/login.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { privateDecrypt } from "node:crypto";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { me, named } from "./cli-name.js";
|
|
@@ -7,6 +7,7 @@ import { Runtime } from "./client.js";
|
|
|
7
7
|
import { checkBrowserOrigins, connectionOrigins, connectionStore } from "./credentials.js";
|
|
8
8
|
import { sandboxIdentityHeaders } from "./identity.js";
|
|
9
9
|
import { describeRoute, envFetch } from "./proxy.js";
|
|
10
|
+
import { sealing } from "./sealing.js";
|
|
10
11
|
class ConnectionError extends Error {
|
|
11
12
|
status;
|
|
12
13
|
constructor(status, message) {
|
|
@@ -73,16 +74,19 @@ function connection(env, options) {
|
|
|
73
74
|
async function request(path, body, key) {
|
|
74
75
|
let response;
|
|
75
76
|
const address = `${origins.auth}/api/connect/${path}`;
|
|
77
|
+
// The sandbox's identity first: its own lookup has its own deadline, and
|
|
78
|
+
// the call's 20 s start when the call does.
|
|
79
|
+
const identity = await sandboxIdentityHeaders();
|
|
76
80
|
try {
|
|
77
81
|
response = await fetcher(address, {
|
|
78
82
|
method: "POST",
|
|
79
83
|
redirect: "error",
|
|
80
|
-
signal: AbortSignal.timeout(20_000),
|
|
81
84
|
headers: {
|
|
82
85
|
"content-type": "application/json",
|
|
83
86
|
...(key ? { authorization: `Bearer ${key}` } : {}),
|
|
84
|
-
...
|
|
87
|
+
...identity,
|
|
85
88
|
},
|
|
89
|
+
signal: AbortSignal.timeout(20_000),
|
|
86
90
|
body: JSON.stringify(body ?? {}),
|
|
87
91
|
});
|
|
88
92
|
}
|
|
@@ -259,11 +263,7 @@ export async function beginDeviceLogin(env, agentName, options = {}) {
|
|
|
259
263
|
let pending = await store.readPending();
|
|
260
264
|
const resumed = pending !== null;
|
|
261
265
|
if (!pending) {
|
|
262
|
-
const pair = await
|
|
263
|
-
modulusLength: 2048,
|
|
264
|
-
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
265
|
-
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
266
|
-
});
|
|
266
|
+
const pair = await sealing.pair();
|
|
267
267
|
// The computer's own name goes to the approval page beside the code, so an
|
|
268
268
|
// owner can tell this request from a link somebody else started.
|
|
269
269
|
const started = await request("start", {
|
package/dist/products/ci.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ export type CiRepository = {
|
|
|
27
27
|
/** The GitHub Apps (`<app-slug>[bot]`, lowercase, sorted) whose pushes run
|
|
28
28
|
* as the code of someone who can write. */
|
|
29
29
|
trustedApps: string[];
|
|
30
|
+
/** Whether a red production branch's culprit is undone by a pull request
|
|
31
|
+
* CI/CD opens: off until `setUndo` turns it on. */
|
|
32
|
+
undoCulprits: boolean;
|
|
30
33
|
state: "connected" | "disconnected";
|
|
31
34
|
};
|
|
32
35
|
export type ConnectCiRepository = {
|
|
@@ -302,6 +305,12 @@ export declare function ci(t: Transport): {
|
|
|
302
305
|
trust: (id: string, apps: string[], options?: RequestOptions) => Promise<CiRepository>;
|
|
303
306
|
/** Takes Apps off those it trusts, keeping the rest. */
|
|
304
307
|
untrust: (id: string, apps: string[], options?: RequestOptions) => Promise<CiRepository>;
|
|
308
|
+
/** Turn on (or off) the undo of a red production branch's culprit:
|
|
309
|
+
* when main's check fails, CI/CD finds the first commit that fails it
|
|
310
|
+
* and opens a pull request reverting it, which lands once its own
|
|
311
|
+
* check passes. Off until set; only an owner's or admin's full key
|
|
312
|
+
* sets it. */
|
|
313
|
+
setUndo: (id: string, on: boolean, options?: RequestOptions) => Promise<CiRepository>;
|
|
305
314
|
/** Disconnect: its checks still going are cancelled; what ended stays. */
|
|
306
315
|
disconnect: (id: string, options?: RequestOptions) => Promise<CiRepository>;
|
|
307
316
|
};
|
package/dist/products/ci.js
CHANGED
|
@@ -61,6 +61,17 @@ export function ci(t) {
|
|
|
61
61
|
const drop = new Set(apps.map((a) => a.toLowerCase()));
|
|
62
62
|
return setTrustedApps(repo.id, repo.trustedApps.filter((a) => !drop.has(a)), options);
|
|
63
63
|
},
|
|
64
|
+
/** Turn on (or off) the undo of a red production branch's culprit:
|
|
65
|
+
* when main's check fails, CI/CD finds the first commit that fails it
|
|
66
|
+
* and opens a pull request reverting it, which lands once its own
|
|
67
|
+
* check passes. Off until set; only an owner's or admin's full key
|
|
68
|
+
* sets it. */
|
|
69
|
+
setUndo: (id, on, options) => t.json({
|
|
70
|
+
method: "POST",
|
|
71
|
+
path: `${at("/v1/ci/repositories", id)}:update`,
|
|
72
|
+
body: { undoCulprits: on },
|
|
73
|
+
...options,
|
|
74
|
+
}),
|
|
64
75
|
/** Disconnect: its checks still going are cancelled; what ended stays. */
|
|
65
76
|
disconnect: (id, options) => t.json({
|
|
66
77
|
method: "DELETE",
|
package/dist/sealing.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { generateKeyPair } from "node:crypto";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
/* The key pair a browser approval seals its answer to (RSA-OAEP, SHA-256):
|
|
4
|
+
a login's credential, or a key asked for with `keys create`. Made fresh
|
|
5
|
+
for each request, so only this process can open what the page sends. An
|
|
6
|
+
object, so a test file can make one pair and reuse it: each takes 80 ms of
|
|
7
|
+
CPU to make, most of the login and key tests' time (24 October 2026). */
|
|
8
|
+
export const sealing = {
|
|
9
|
+
pair: () => promisify(generateKeyPair)("rsa", {
|
|
10
|
+
modulusLength: 2048,
|
|
11
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
12
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
13
|
+
}),
|
|
14
|
+
};
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RuntimeError } from "./errors.js";
|
|
2
|
-
export declare const VERSION = "0.33.
|
|
2
|
+
export declare const VERSION = "0.33.2";
|
|
3
3
|
export declare const DEFAULT_BASE_URL = "https://api.withruntime.com";
|
|
4
4
|
/** Runtime's API as code inside a Runtime sandbox reaches it: the sandbox's
|
|
5
5
|
* own host sends each request on to DEFAULT_BASE_URL over HTTPS. The API runs
|
package/dist/transport.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConnectionError, DELIBERATE, errorFor, NOTHING_RAN, RuntimeError, WAITS_FOR_ROOM, } from "./errors.js";
|
|
2
2
|
import { describeRoute, envFetch, openWebSocket, untrustedCertificate } from "./proxy.js";
|
|
3
3
|
import { warningText } from "./product-renames.js";
|
|
4
|
-
export const VERSION = "0.33.
|
|
4
|
+
export const VERSION = "0.33.2";
|
|
5
5
|
export const DEFAULT_BASE_URL = "https://api.withruntime.com";
|
|
6
6
|
/** Runtime's API as code inside a Runtime sandbox reaches it: the sandbox's
|
|
7
7
|
* own host sends each request on to DEFAULT_BASE_URL over HTTPS. The API runs
|