withruntime 0.6.0 → 0.7.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/E2B.md +32 -32
- package/README.md +8 -5
- package/dist/ai/index.d.ts +21 -5
- package/dist/ai/index.js +14 -21
- package/dist/ai-harness/index.d.ts +194 -0
- package/dist/ai-harness/index.js +517 -0
- package/dist/billing-cli.d.ts +21 -0
- package/dist/billing-cli.js +158 -0
- package/dist/cli-extras.d.ts +15 -0
- package/dist/cli-extras.js +60 -4
- package/dist/cli-name.d.ts +6 -0
- package/dist/cli-name.js +8 -0
- package/dist/cli.d.ts +17 -1
- package/dist/cli.js +537 -130
- package/dist/client.d.ts +35 -6
- package/dist/client.js +23 -0
- package/dist/credentials.js +1 -2
- package/dist/daytona/filesystem.d.ts +2 -0
- package/dist/daytona/filesystem.js +9 -7
- package/dist/e2b/filesystem.js +3 -2
- package/dist/e2b/sandbox.d.ts +9 -6
- package/dist/e2b/sandbox.js +31 -7
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +7 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js +1 -0
- package/dist/keys.js +1 -1
- package/dist/login.d.ts +2 -6
- package/dist/login.js +3 -8
- package/dist/network-cli.d.ts +1 -1
- package/dist/network-cli.js +64 -2
- package/dist/products/billing.d.ts +35 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/index.d.ts +4 -1
- package/dist/products/index.js +4 -1
- package/dist/products/network-products.d.ts +45 -0
- package/dist/products/network-products.js +27 -0
- package/dist/products/previews.d.ts +1 -2
- package/dist/products/secrets.d.ts +14 -0
- package/dist/sandbox.d.ts +17 -3
- package/dist/sandbox.js +233 -62
- package/dist/transport.d.ts +17 -1
- package/dist/transport.js +124 -7
- package/dist/types.d.ts +0 -2
- package/dist/wallet.d.ts +30 -0
- package/dist/wallet.js +48 -0
- package/package.json +44 -17
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { RuntimeError } from "./errors.js";
|
|
2
|
+
import { named } from "./cli-name.js";
|
|
3
|
+
export const BILLING_HELP = `runtime billing <command>
|
|
4
|
+
|
|
5
|
+
topup <usd> [--wait] Buy credit with stablecoins (USDC on Base, Solana or Tempo).
|
|
6
|
+
Prints an address per network and the exact amount to send.
|
|
7
|
+
Send exactly that, from any wallet, before the time shown.
|
|
8
|
+
--wait keeps checking until the credit lands.
|
|
9
|
+
status <id> [--wait] Whether a top-up's payment has arrived
|
|
10
|
+
|
|
11
|
+
No account yet? An agent can open one with a wallet alone:
|
|
12
|
+
topup <usd> --new-account --accept-terms [--wait]
|
|
13
|
+
Opens an account and its first top-up. Prints a claim code,
|
|
14
|
+
shown once. --wait saves the key here once the money lands.
|
|
15
|
+
claim <code> [--wait] Exchange a paid account's claim code for its key, saved here
|
|
16
|
+
|
|
17
|
+
$10 to $10,000 a top-up; $500 in an account's first week. By card:
|
|
18
|
+
https://withruntime.com/account/billing
|
|
19
|
+
`;
|
|
20
|
+
function usage(message) {
|
|
21
|
+
return new RuntimeError({
|
|
22
|
+
message,
|
|
23
|
+
code: "usage",
|
|
24
|
+
status: 0,
|
|
25
|
+
hint: `Run \`${named("runtime")} billing help\`.`,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const DONE = new Set(["paid", "failed", "expired"]);
|
|
29
|
+
function describe(topup) {
|
|
30
|
+
const me = named("runtime");
|
|
31
|
+
if (topup.status === "paid")
|
|
32
|
+
return `Paid: $${topup.amountUsd} of credit is on the account.`;
|
|
33
|
+
if (topup.status === "expired")
|
|
34
|
+
return `Expired: no payment arrived in time. Open a new one with \`${me} billing topup ${topup.amountUsd}\`.`;
|
|
35
|
+
if (topup.status === "failed")
|
|
36
|
+
return "Failed. Nothing was credited.";
|
|
37
|
+
if (topup.status === "pending")
|
|
38
|
+
return `Stripe has seen the payment and is confirming it. Check again with \`${me} billing status ${topup.purchaseId}\`.`;
|
|
39
|
+
const lines = [`Send exactly ${topup.amount} of one of these tokens, on one network:`, ""];
|
|
40
|
+
for (const network of topup.networks)
|
|
41
|
+
for (const token of network.tokens)
|
|
42
|
+
lines.push(` ${network.network.padEnd(7)} ${token.currency.toUpperCase().padEnd(6)} to ${network.address}`, ` ${"".padEnd(7)} ${"".padEnd(6)} token ${token.contract}`);
|
|
43
|
+
lines.push("", ...(topup.payBy ? [`Send before ${topup.payBy}.`] : []), "A different amount, token or network cannot be matched or returned automatically.", `The credit lands a few minutes after the transfer: \`${me} billing status ${topup.purchaseId}\`.`);
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
const FLAGS = new Set(["wait", "new-account", "accept-terms"]);
|
|
47
|
+
export async function billingCommand(argv, args, out, client, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), wallet) {
|
|
48
|
+
const [verb, value] = args.positional;
|
|
49
|
+
const has = (name) => args.flags.get(name)?.at(-1) === "true";
|
|
50
|
+
const wait = has("wait");
|
|
51
|
+
for (const name of args.flags.keys())
|
|
52
|
+
if (!FLAGS.has(name))
|
|
53
|
+
throw usage(`Unknown option --${name}.`);
|
|
54
|
+
if (!verb || verb === "help") {
|
|
55
|
+
out.write(BILLING_HELP);
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if ((verb === "topup" && has("new-account")) || verb === "claim") {
|
|
59
|
+
if (!wallet)
|
|
60
|
+
throw usage("Wallet accounts are not available here.");
|
|
61
|
+
return walletCommand(verb, value, has("accept-terms"), wait, out, sleep, wallet);
|
|
62
|
+
}
|
|
63
|
+
const api = (await client()).billing;
|
|
64
|
+
let topup;
|
|
65
|
+
if (verb === "topup") {
|
|
66
|
+
topup = await api.topup({ usd: dollarsOf(value) }, { idempotencyKey: crypto.randomUUID() });
|
|
67
|
+
}
|
|
68
|
+
else if (verb === "status") {
|
|
69
|
+
if (!value)
|
|
70
|
+
throw usage("Give the top-up's id.");
|
|
71
|
+
topup = await api.topupStatus(value);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
throw usage(`Unknown billing command ${verb}.`);
|
|
75
|
+
}
|
|
76
|
+
const shown = new Set();
|
|
77
|
+
const show = (current) => {
|
|
78
|
+
if (out.json)
|
|
79
|
+
out.write(JSON.stringify(current));
|
|
80
|
+
else if (!shown.has(current.status))
|
|
81
|
+
out.write(describe(current));
|
|
82
|
+
shown.add(current.status);
|
|
83
|
+
};
|
|
84
|
+
show(topup);
|
|
85
|
+
if (!wait)
|
|
86
|
+
return 0;
|
|
87
|
+
/* Stripe takes a few minutes to see a transfer; checking every ten seconds
|
|
88
|
+
is quick enough and costs nothing. Stops once the top-up has an end, or
|
|
89
|
+
an hour after its deadline. */
|
|
90
|
+
const until = (topup.payBy ? Date.parse(topup.payBy) : Date.now()) + 60 * 60_000;
|
|
91
|
+
while (!DONE.has(topup.status) && Date.now() < until) {
|
|
92
|
+
await sleep(10_000);
|
|
93
|
+
topup = await api.topupStatus(topup.purchaseId);
|
|
94
|
+
show(topup);
|
|
95
|
+
}
|
|
96
|
+
return topup.status === "paid" ? 0 : 1;
|
|
97
|
+
}
|
|
98
|
+
function dollarsOf(value) {
|
|
99
|
+
const usd = Number(value?.replace(/^\$/, ""));
|
|
100
|
+
if (!value || !Number.isFinite(usd) || usd <= 0)
|
|
101
|
+
throw usage("Say how many dollars of credit to buy, such as `billing topup 20`.");
|
|
102
|
+
return usd;
|
|
103
|
+
}
|
|
104
|
+
/** `topup <usd> --new-account` and `claim <code>`: an account with no key. */
|
|
105
|
+
async function walletCommand(verb, value, acceptTerms, wait, out, sleep, wallet) {
|
|
106
|
+
const me = named("runtime");
|
|
107
|
+
let code;
|
|
108
|
+
let payBy = null;
|
|
109
|
+
if (verb === "topup") {
|
|
110
|
+
if (!acceptTerms)
|
|
111
|
+
throw usage("Opening an account means agreeing to https://withruntime.com/legal/terms. Add --accept-terms once the person you work for agrees.");
|
|
112
|
+
const opened = await wallet.open({ usd: dollarsOf(value), acceptTerms: true });
|
|
113
|
+
code = opened.claimCode;
|
|
114
|
+
payBy = opened.payBy;
|
|
115
|
+
if (out.json)
|
|
116
|
+
out.write(JSON.stringify(opened));
|
|
117
|
+
else
|
|
118
|
+
out.write([
|
|
119
|
+
describe(opened),
|
|
120
|
+
"",
|
|
121
|
+
`Claim code (shown once, keep it secret): ${code}`,
|
|
122
|
+
`Once paid: \`${me} billing claim ${code}\` gives this machine the account's key.`,
|
|
123
|
+
"A person can take the account over at https://withruntime.com/account/claim with the same code.",
|
|
124
|
+
].join("\n"));
|
|
125
|
+
if (!wait)
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
if (!value)
|
|
130
|
+
throw usage("Give the claim code.");
|
|
131
|
+
code = value;
|
|
132
|
+
}
|
|
133
|
+
const until = (payBy ? Date.parse(payBy) : Date.now()) + 60 * 60_000;
|
|
134
|
+
for (let first = verb === "claim";; first = false) {
|
|
135
|
+
if (!first)
|
|
136
|
+
await sleep(10_000);
|
|
137
|
+
const claim = await wallet.claim(code);
|
|
138
|
+
if (claim.status === "paid") {
|
|
139
|
+
await wallet.save(claim.apiKey, claim.orgId);
|
|
140
|
+
out.write(out.json
|
|
141
|
+
? JSON.stringify({ status: "paid", orgId: claim.orgId, connected: true })
|
|
142
|
+
: "Paid. This machine is connected to the new account; its key is saved, not shown.");
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
if (claim.status === "expired" || claim.status === "failed") {
|
|
146
|
+
out.write(out.json
|
|
147
|
+
? JSON.stringify(claim)
|
|
148
|
+
: `The top-up ${claim.status}: nothing was credited. Open a new one with \`${me} billing topup <usd> --new-account --accept-terms\`.`);
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
if (!wait || Date.now() >= until) {
|
|
152
|
+
out.write(out.json
|
|
153
|
+
? JSON.stringify(claim)
|
|
154
|
+
: `Not paid yet (${claim.status}). Run \`${me} billing claim ${code}\` again once the transfer has gone through.`);
|
|
155
|
+
return wait ? 1 : 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
package/dist/cli-extras.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CliKit } from "./cli.js";
|
|
2
2
|
import type { Sandbox } from "./sandbox.js";
|
|
3
|
+
import type { Snapshot } from "./snapshots.js";
|
|
3
4
|
type Out = {
|
|
4
5
|
json: boolean;
|
|
5
6
|
write: (text: string) => void;
|
|
@@ -21,4 +22,18 @@ export declare function lostOutput(result: {
|
|
|
21
22
|
stdoutTruncated?: boolean;
|
|
22
23
|
stderrTruncated?: boolean;
|
|
23
24
|
}, id: string): string | undefined;
|
|
25
|
+
/** Feeds `runtime sandbox shell` from standard input. When piped input ends,
|
|
26
|
+
* the shell is told so the way a terminal says it, Ctrl-D, after a newline
|
|
27
|
+
* if the last line had none: `echo ls | runtime sandbox shell <id>` runs `ls`
|
|
28
|
+
* and exits, as ssh does, where it used to wait forever (25 September 2026).
|
|
29
|
+
* A keyboard never ends, so a terminal's input is left alone. Returns the
|
|
30
|
+
* function that unhooks it. */
|
|
31
|
+
export declare function pipeShellInput(stdin: NodeJS.ReadableStream & {
|
|
32
|
+
isTTY?: boolean;
|
|
33
|
+
}, terminal: {
|
|
34
|
+
write(data: Uint8Array | string): void;
|
|
35
|
+
}): () => void;
|
|
36
|
+
/** `runtime snapshot get`, as `runtime sandbox get` reads: one fact a line.
|
|
37
|
+
* It printed raw JSON until 25 September 2026; `--json` still does. */
|
|
38
|
+
export declare function describeSnapshot(s: Snapshot, table: (rows: string[][]) => string): string;
|
|
24
39
|
export {};
|
package/dist/cli-extras.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import { me } from "./
|
|
2
|
+
import { me } from "./cli-name.js";
|
|
3
3
|
export async function recordCommand(sbx, argv, out, kit) {
|
|
4
4
|
const { parse, integer, need, table, usage } = kit;
|
|
5
5
|
const [action, ...rest] = argv;
|
|
@@ -7,7 +7,7 @@ export async function recordCommand(sbx, argv, out, kit) {
|
|
|
7
7
|
const r = sbx.desktop.recordings;
|
|
8
8
|
switch (action) {
|
|
9
9
|
case "start": {
|
|
10
|
-
const args = parse(rest);
|
|
10
|
+
const args = parse(rest, [], ["fps", "max-seconds", "max-mib"]);
|
|
11
11
|
const rec = await r.start({
|
|
12
12
|
...(integer(args, "fps") ? { fps: integer(args, "fps") } : {}),
|
|
13
13
|
...(integer(args, "max-seconds") ? { maxSeconds: integer(args, "max-seconds") } : {}),
|
|
@@ -51,7 +51,7 @@ export async function recordCommand(sbx, argv, out, kit) {
|
|
|
51
51
|
}
|
|
52
52
|
export async function watchCommand(sbx, argv, out, kit) {
|
|
53
53
|
const { parse, flag, has, need } = kit;
|
|
54
|
-
const args = parse(argv, ["recursive"]);
|
|
54
|
+
const args = parse(argv, ["recursive"], ["events", "include", "exclude"]);
|
|
55
55
|
const path = need(args.positional[0], "a directory, e.g. /workspace");
|
|
56
56
|
const events = flag(args, "events")?.split(",");
|
|
57
57
|
const watch = await sbx.files.watch(path, (event) => {
|
|
@@ -108,7 +108,7 @@ export async function mcpCommand(sbx, argv, out, kit) {
|
|
|
108
108
|
};
|
|
109
109
|
switch (action) {
|
|
110
110
|
case "start": {
|
|
111
|
-
const args = parse(rest, ["replace"]);
|
|
111
|
+
const args = parse(rest, ["replace"], ["secret", "env", "option", "port"]);
|
|
112
112
|
if (!args.positional.length)
|
|
113
113
|
throw usage("Name the servers: runtime sandbox mcp <id> start github fetch");
|
|
114
114
|
const scoped = (name) => {
|
|
@@ -203,3 +203,59 @@ export function lostOutput(result, id) {
|
|
|
203
203
|
return undefined;
|
|
204
204
|
return `runtime: some of the command's output was lost: it wrote faster than it was read, and the sandbox keeps the latest 1 MiB. Write it to a file and copy it out: ${me} sandbox cp ${id}:/workspace/out.txt .`;
|
|
205
205
|
}
|
|
206
|
+
/** Feeds `runtime sandbox shell` from standard input. When piped input ends,
|
|
207
|
+
* the shell is told so the way a terminal says it, Ctrl-D, after a newline
|
|
208
|
+
* if the last line had none: `echo ls | runtime sandbox shell <id>` runs `ls`
|
|
209
|
+
* and exits, as ssh does, where it used to wait forever (25 September 2026).
|
|
210
|
+
* A keyboard never ends, so a terminal's input is left alone. Returns the
|
|
211
|
+
* function that unhooks it. */
|
|
212
|
+
export function pipeShellInput(stdin, terminal) {
|
|
213
|
+
let last = 10;
|
|
214
|
+
const onData = (chunk) => {
|
|
215
|
+
const bytes = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
|
|
216
|
+
if (bytes.length)
|
|
217
|
+
last = bytes[bytes.length - 1];
|
|
218
|
+
terminal.write(new Uint8Array(bytes));
|
|
219
|
+
};
|
|
220
|
+
const onEnd = () => {
|
|
221
|
+
if (stdin.isTTY)
|
|
222
|
+
return;
|
|
223
|
+
terminal.write(last === 10 ? "\x04" : "\n\x04");
|
|
224
|
+
};
|
|
225
|
+
stdin.on("data", onData);
|
|
226
|
+
stdin.on("end", onEnd);
|
|
227
|
+
return () => {
|
|
228
|
+
stdin.off("data", onData);
|
|
229
|
+
stdin.off("end", onEnd);
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** `runtime snapshot get`, as `runtime sandbox get` reads: one fact a line.
|
|
233
|
+
* It printed raw JSON until 25 September 2026; `--json` still does. */
|
|
234
|
+
export function describeSnapshot(s, table) {
|
|
235
|
+
const mib = (bytes) => bytes === null ? "-" : `${Math.ceil(bytes / 1_048_576).toLocaleString("en-US")} MiB`;
|
|
236
|
+
const labels = (s.labels ?? {});
|
|
237
|
+
return table([
|
|
238
|
+
["id", s.id],
|
|
239
|
+
["name", s.name ?? "-"],
|
|
240
|
+
["state", s.state],
|
|
241
|
+
["from", s.sourceSandboxId],
|
|
242
|
+
["shape", `${s.shape.vcpu} vCPU, ${s.shape.memoryMiB} MiB memory, ${s.shape.diskMiB} MiB disk`],
|
|
243
|
+
["stored", mib(s.storedBytes)],
|
|
244
|
+
["billed for", mib(s.meteredBytes)],
|
|
245
|
+
[
|
|
246
|
+
"backed up",
|
|
247
|
+
s.backedUp
|
|
248
|
+
? `yes${s.durability.durableAt ? `, ${s.durability.durableAt}` : ""}`
|
|
249
|
+
: s.durability.state,
|
|
250
|
+
],
|
|
251
|
+
["created", s.createdAt],
|
|
252
|
+
["kept until", `${s.expiresAt} (${s.retentionDays} days)`],
|
|
253
|
+
[
|
|
254
|
+
"labels",
|
|
255
|
+
Object.entries(labels)
|
|
256
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
257
|
+
.join(", ") || "-",
|
|
258
|
+
],
|
|
259
|
+
...(s.error ? [["error", s.error]] : []),
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** How this CLI was started, so what it tells you to type works: through npx
|
|
2
|
+
* the command is `npx withruntime`, installed it is `runtime`. Its own module
|
|
3
|
+
* so a command that only names itself does not load the login flow. */
|
|
4
|
+
export declare const me: string;
|
|
5
|
+
/** Text that names a command, with the command spelled the way it was run. */
|
|
6
|
+
export declare function named(text: string, name?: string): string;
|
package/dist/cli-name.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** How this CLI was started, so what it tells you to type works: through npx
|
|
2
|
+
* the command is `npx withruntime`, installed it is `runtime`. Its own module
|
|
3
|
+
* so a command that only names itself does not load the login flow. */
|
|
4
|
+
export const me = /[\\/]_npx[\\/]/.test(process.argv[1] ?? "") ? "npx withruntime" : "runtime";
|
|
5
|
+
/** Text that names a command, with the command spelled the way it was run. */
|
|
6
|
+
export function named(text, name = me) {
|
|
7
|
+
return name === "runtime" ? text : text.replace(/(^|[\s`(])runtime(?= [a-z<[])/g, `$1${name}`);
|
|
8
|
+
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { RuntimeError } from "./errors.js";
|
|
3
|
+
import type { Usage } from "./types.js";
|
|
4
|
+
import type { SecretRule } from "./products/secrets.js";
|
|
3
5
|
type Out = {
|
|
4
6
|
json: boolean;
|
|
5
7
|
write: (text: string) => void;
|
|
@@ -12,7 +14,7 @@ type Args = {
|
|
|
12
14
|
};
|
|
13
15
|
/** What the commands in cli-extras.ts use of this file. */
|
|
14
16
|
declare const kit: {
|
|
15
|
-
parse: (argv: string[], booleans
|
|
17
|
+
parse: (argv: string[], booleans: readonly string[], values: readonly string[]) => Args;
|
|
16
18
|
flag: (args: Args, name: string) => string | undefined;
|
|
17
19
|
has: (args: Args, name: string) => boolean;
|
|
18
20
|
integer: (args: Args, name: string) => number | undefined;
|
|
@@ -25,6 +27,20 @@ declare const kit: {
|
|
|
25
27
|
};
|
|
26
28
|
export type CliKit = typeof kit;
|
|
27
29
|
export declare function run(argv: string[], env?: NodeJS.ProcessEnv, out?: Out): Promise<number>;
|
|
30
|
+
/** `runtime usage` for a person: the balance, the trial and what each kind
|
|
31
|
+
* of resource was charged, in dollars. --json keeps every figure, and each
|
|
32
|
+
* resource with its rates and CPU time. */
|
|
33
|
+
/** One row per resource, for a spreadsheet: when it ran, how big it was, how
|
|
34
|
+
* long it ran, the CPU it used and what it cost. Money is exact dollars to the
|
|
35
|
+
* microdollar (the API's integer microdollars moved six places), so a column
|
|
36
|
+
* sums to the ledger to the last digit. */
|
|
37
|
+
export declare function usageCsv(u: Usage): string;
|
|
38
|
+
/** `--allow` values as secret rules: each is `[METHOD[,METHOD]...] PATH`, and
|
|
39
|
+
* rules naming the same methods share one rule's paths. Only the shape is read
|
|
40
|
+
* here; the API holds every rule to the same check the host makes
|
|
41
|
+
* (ARCHITECTURE.md section 10, "Per-path rules on an egress secret") and says
|
|
42
|
+
* which one it refused. */
|
|
43
|
+
export declare function parseSecretRules(values: string[]): SecretRule[];
|
|
28
44
|
export declare function secretsCommand(argv: string[], env: NodeJS.ProcessEnv, out: Out,
|
|
29
45
|
/** Tests give the value here; the CLI reads standard input. */
|
|
30
46
|
read?: () => Promise<string>): Promise<number>;
|