@boxd-sh/convex 0.1.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/LICENSE +21 -0
- package/README.md +264 -0
- package/dist/client/_generated/_ignore.d.ts +1 -0
- package/dist/client/_generated/_ignore.d.ts.map +1 -0
- package/dist/client/_generated/_ignore.js +3 -0
- package/dist/client/_generated/_ignore.js.map +1 -0
- package/dist/client/index.d.ts +327 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +126 -0
- package/dist/client/index.js.map +1 -0
- package/dist/component/_generated/api.d.ts +52 -0
- package/dist/component/_generated/api.d.ts.map +1 -0
- package/dist/component/_generated/api.js +31 -0
- package/dist/component/_generated/api.js.map +1 -0
- package/dist/component/_generated/component.d.ts +357 -0
- package/dist/component/_generated/component.d.ts.map +1 -0
- package/dist/component/_generated/component.js +11 -0
- package/dist/component/_generated/component.js.map +1 -0
- package/dist/component/_generated/dataModel.d.ts +46 -0
- package/dist/component/_generated/dataModel.d.ts.map +1 -0
- package/dist/component/_generated/dataModel.js +11 -0
- package/dist/component/_generated/dataModel.js.map +1 -0
- package/dist/component/_generated/server.d.ts +135 -0
- package/dist/component/_generated/server.d.ts.map +1 -0
- package/dist/component/_generated/server.js +80 -0
- package/dist/component/_generated/server.js.map +1 -0
- package/dist/component/boxd.d.ts +35 -0
- package/dist/component/boxd.d.ts.map +1 -0
- package/dist/component/boxd.js +144 -0
- package/dist/component/boxd.js.map +1 -0
- package/dist/component/convex.config.d.ts +6 -0
- package/dist/component/convex.config.d.ts.map +1 -0
- package/dist/component/convex.config.js +9 -0
- package/dist/component/convex.config.js.map +1 -0
- package/dist/component/errors.d.ts +25 -0
- package/dist/component/errors.d.ts.map +1 -0
- package/dist/component/errors.js +75 -0
- package/dist/component/errors.js.map +1 -0
- package/dist/component/exec.d.ts +25 -0
- package/dist/component/exec.d.ts.map +1 -0
- package/dist/component/exec.js +100 -0
- package/dist/component/exec.js.map +1 -0
- package/dist/component/executions.d.ts +70 -0
- package/dist/component/executions.d.ts.map +1 -0
- package/dist/component/executions.js +106 -0
- package/dist/component/executions.js.map +1 -0
- package/dist/component/files.d.ts +44 -0
- package/dist/component/files.d.ts.map +1 -0
- package/dist/component/files.js +105 -0
- package/dist/component/files.js.map +1 -0
- package/dist/component/limits.d.ts +33 -0
- package/dist/component/limits.d.ts.map +1 -0
- package/dist/component/limits.js +44 -0
- package/dist/component/limits.js.map +1 -0
- package/dist/component/machines.d.ts +337 -0
- package/dist/component/machines.d.ts.map +1 -0
- package/dist/component/machines.js +411 -0
- package/dist/component/machines.js.map +1 -0
- package/dist/component/records.d.ts +87 -0
- package/dist/component/records.d.ts.map +1 -0
- package/dist/component/records.js +51 -0
- package/dist/component/records.js.map +1 -0
- package/dist/component/schema.d.ts +138 -0
- package/dist/component/schema.d.ts.map +1 -0
- package/dist/component/schema.js +70 -0
- package/dist/component/schema.js.map +1 -0
- package/dist/component/sessions.d.ts +21 -0
- package/dist/component/sessions.d.ts.map +1 -0
- package/dist/component/sessions.js +59 -0
- package/dist/component/sessions.js.map +1 -0
- package/dist/component/validate.d.ts +15 -0
- package/dist/component/validate.d.ts.map +1 -0
- package/dist/component/validate.js +44 -0
- package/dist/component/validate.js.map +1 -0
- package/package.json +99 -0
- package/src/client/_generated/_ignore.ts +1 -0
- package/src/client/index.ts +199 -0
- package/src/component/_generated/api.ts +68 -0
- package/src/component/_generated/component.ts +430 -0
- package/src/component/_generated/dataModel.ts +60 -0
- package/src/component/_generated/server.ts +171 -0
- package/src/component/boxd.ts +188 -0
- package/src/component/convex.config.ts +9 -0
- package/src/component/errors.ts +105 -0
- package/src/component/exec.ts +134 -0
- package/src/component/executions.ts +115 -0
- package/src/component/files.ts +128 -0
- package/src/component/limits.ts +48 -0
- package/src/component/machines.ts +500 -0
- package/src/component/records.ts +71 -0
- package/src/component/schema.ts +77 -0
- package/src/component/sessions.ts +61 -0
- package/src/component/validate.ts +58 -0
- package/src/test.ts +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Azin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# boxd for Convex
|
|
4
|
+
|
|
5
|
+
Run [boxd](https://boxd.sh) machines from your Convex backend. Each machine is a
|
|
6
|
+
full Linux microVM with its own disk, memory and HTTPS URL. It cold-boots in
|
|
7
|
+
under 10 ms, forks like a git branch in under 200 ms, and sleeps and wakes with
|
|
8
|
+
its memory intact.
|
|
9
|
+
|
|
10
|
+
The component keeps a reactive row for every machine and every command it runs,
|
|
11
|
+
so your UI subscribes to machine state and command output with a plain Convex
|
|
12
|
+
query.
|
|
13
|
+
|
|
14
|
+
- Create, fork, pause, resume, hibernate, wake, stop, start and destroy
|
|
15
|
+
machines.
|
|
16
|
+
- Run commands and read and write files.
|
|
17
|
+
- Put a port on the public internet: `https://<machine>.boxd.sh`.
|
|
18
|
+
- Scope every machine to a user or tenant of your app.
|
|
19
|
+
- Get stable error codes to branch on (`NOT_FOUND`, `CONFLICT`, ...).
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @boxd-sh/convex
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Create an API key with the [boxd CLI](https://docs.boxd.sh) and set it on your
|
|
28
|
+
deployment:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
boxd auth keys create convex
|
|
32
|
+
npx convex env set BOXD_API_KEY bxd_...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A key is fenced to one boxd org. Machines are created in, and billed to, that
|
|
36
|
+
org.
|
|
37
|
+
|
|
38
|
+
Install the component in `convex/convex.config.ts`, and bind the key into it:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { defineApp } from "convex/server";
|
|
42
|
+
import { v } from "convex/values";
|
|
43
|
+
import boxd from "@boxd-sh/convex/convex.config.js";
|
|
44
|
+
|
|
45
|
+
const app = defineApp({
|
|
46
|
+
env: { BOXD_API_KEY: v.string() },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
app.use(boxd, { env: { BOXD_API_KEY: app.env.BOXD_API_KEY } });
|
|
50
|
+
|
|
51
|
+
export default app;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The key goes into the component's own environment, so it never travels as a
|
|
55
|
+
function argument.
|
|
56
|
+
|
|
57
|
+
## Use it
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// convex/machines.ts
|
|
61
|
+
import { Boxd } from "@boxd-sh/convex";
|
|
62
|
+
import { v } from "convex/values";
|
|
63
|
+
import { components } from "./_generated/api";
|
|
64
|
+
import {
|
|
65
|
+
action,
|
|
66
|
+
query,
|
|
67
|
+
type ActionCtx,
|
|
68
|
+
type QueryCtx,
|
|
69
|
+
} from "./_generated/server";
|
|
70
|
+
|
|
71
|
+
const boxd = new Boxd(components.boxd);
|
|
72
|
+
|
|
73
|
+
// The component can't see your users, so derive the owner from your auth.
|
|
74
|
+
// Never take it from a client argument.
|
|
75
|
+
async function ownerId(ctx: QueryCtx | ActionCtx) {
|
|
76
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
77
|
+
if (!identity) throw new Error("Unauthenticated");
|
|
78
|
+
return identity.subject;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const createMachine = action({
|
|
82
|
+
args: {},
|
|
83
|
+
handler: async (ctx) => {
|
|
84
|
+
return await boxd.create(ctx, {
|
|
85
|
+
ownerId: await ownerId(ctx),
|
|
86
|
+
vcpu: 1,
|
|
87
|
+
autoSuspendSeconds: 300,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const run = action({
|
|
93
|
+
args: { machineId: v.string(), command: v.string() },
|
|
94
|
+
handler: async (ctx, { machineId, command }) => {
|
|
95
|
+
return await boxd.exec(ctx, {
|
|
96
|
+
ownerId: await ownerId(ctx),
|
|
97
|
+
machineId,
|
|
98
|
+
command,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Reactive: re-runs whenever a machine changes state.
|
|
104
|
+
export const myMachines = query({
|
|
105
|
+
args: {},
|
|
106
|
+
handler: async (ctx) => boxd.list(ctx, { ownerId: await ownerId(ctx) }),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Reactive: every command's status and output, newest first.
|
|
110
|
+
export const history = query({
|
|
111
|
+
args: { machineId: v.string() },
|
|
112
|
+
handler: async (ctx, { machineId }) =>
|
|
113
|
+
boxd.listExecutions(ctx, { machineId, ownerId: await ownerId(ctx) }),
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`example/convex/example.ts` wraps every method this way.
|
|
118
|
+
|
|
119
|
+
## API
|
|
120
|
+
|
|
121
|
+
Every method takes the Convex `ctx` first. Reads run in queries. Everything that
|
|
122
|
+
calls boxd runs in actions.
|
|
123
|
+
|
|
124
|
+
| Method | What it does |
|
|
125
|
+
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
126
|
+
| `create(ctx, args)` | Create a machine. Waits until it accepts a command, unless `waitUntilReady: false` |
|
|
127
|
+
| `fork(ctx, { machineId })` | Copy a machine, disk and memory, into a new one with the same owner |
|
|
128
|
+
| `exec(ctx, { machineId, command })` | Run a command, wait for it to exit, record it. `command` is a string or an argv |
|
|
129
|
+
| `readFile` / `readFileBytes` | Read a UTF-8 file as text, or any file byte for byte |
|
|
130
|
+
| `writeFile(ctx, { path, content })` | Write text or bytes. The parent directory must exist |
|
|
131
|
+
| `listDir(ctx, { path })` | List a directory |
|
|
132
|
+
| `expose(ctx, { machineId, port, name? })` | Route public HTTPS to a port. Returns the URL |
|
|
133
|
+
| `pause` / `resume` | Freeze in RAM, and thaw |
|
|
134
|
+
| `hibernate` / `wake` | Save memory to disk and free the host, and restore |
|
|
135
|
+
| `stop` / `start` | Shut down and cold boot. The disk is kept |
|
|
136
|
+
| `refresh(ctx, { machineId })` | Re-read the machine from boxd into its row |
|
|
137
|
+
| `destroy(ctx, { machineId })` | Destroy the machine. Its row and history stay, with status `destroyed` |
|
|
138
|
+
| `get` / `list` | Machine rows, reactive |
|
|
139
|
+
| `listExecutions` / `getExecution` | Command history, reactive |
|
|
140
|
+
|
|
141
|
+
`create` takes `name`, `image`, `env`, `vcpu` or `memory` (size classes 1, 2 or
|
|
142
|
+
4 vCPU with 4, 8 or 16 GiB), `autoSuspendSeconds`, `autoDestroySeconds` and
|
|
143
|
+
`isolated`. The component never stores `env`.
|
|
144
|
+
|
|
145
|
+
### Owners
|
|
146
|
+
|
|
147
|
+
Pass `ownerId` on every call. A machine created with an `ownerId` is reachable
|
|
148
|
+
only with that same `ownerId`, and one created without one only without one. For
|
|
149
|
+
any other owner, reads return `null` or `[]`, and actions fail with `NOT_FOUND`,
|
|
150
|
+
the same as for a machine that doesn't exist. A fork gets its source's owner.
|
|
151
|
+
|
|
152
|
+
### Sleeping machines
|
|
153
|
+
|
|
154
|
+
A paused or hibernated machine wakes by itself when it gets a command or an
|
|
155
|
+
HTTPS request, so `exec` works without a `resume` first. Every transition is
|
|
156
|
+
idempotent: `resume` on a running machine succeeds.
|
|
157
|
+
|
|
158
|
+
### Errors
|
|
159
|
+
|
|
160
|
+
Errors are `ConvexError`s with `data: { code, message }`. Branch on the code
|
|
161
|
+
with `isBoxdError`:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { isBoxdError } from "@boxd-sh/convex";
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await boxd.refresh(ctx, { machineId, ownerId });
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (isBoxdError(error, "NOT_FOUND")) return null;
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
| Code | Meaning |
|
|
175
|
+
| ------------------- | --------------------------------------------------------------------- |
|
|
176
|
+
| `INVALID_ARGUMENT` | An argument is wrong. Nothing was sent to boxd |
|
|
177
|
+
| `NOT_FOUND` | No such machine for this owner, or no such file |
|
|
178
|
+
| `CONFLICT` | The machine's state doesn't allow this, e.g. `pause` when stopped |
|
|
179
|
+
| `UNAUTHENTICATED` | `BOXD_API_KEY` is missing, invalid, expired or revoked |
|
|
180
|
+
| `PERMISSION_DENIED` | The key's org may not do this |
|
|
181
|
+
| `RATE_LIMITED` | A quota or rate limit, e.g. the org's machine quota |
|
|
182
|
+
| `TIMEOUT` | A command ran past its `timeoutMs`, or a machine wasn't ready in time |
|
|
183
|
+
| `UNAVAILABLE` | boxd could not be reached |
|
|
184
|
+
| `BOXD_ERROR` | Anything else |
|
|
185
|
+
|
|
186
|
+
A failed lifecycle call also stores its message in the machine's `lastError`.
|
|
187
|
+
The next successful call clears it.
|
|
188
|
+
|
|
189
|
+
When `create` or `fork` makes a machine that then fails to become ready, the
|
|
190
|
+
error's `data.machineId` names it. The machine exists, and bills, until you
|
|
191
|
+
destroy it, so act on that id instead of creating another machine.
|
|
192
|
+
|
|
193
|
+
## Limits
|
|
194
|
+
|
|
195
|
+
- **10 minute actions.** Convex stops an action after 10 minutes, so `exec`
|
|
196
|
+
kills its command after `timeoutMs`: default 9 minutes, at most 9.5. For
|
|
197
|
+
longer work, start it in the background (`nohup ... &`) and check on it with
|
|
198
|
+
later commands.
|
|
199
|
+
- **Output.** `exec` returns up to 2 million characters per stream. The
|
|
200
|
+
execution row keeps the first 64,000.
|
|
201
|
+
- **Files.** `readFile`, `readFileBytes` and `writeFile` move at most 8 MiB.
|
|
202
|
+
Move bigger files with `exec`, for example with `curl`.
|
|
203
|
+
- **New named routes.** `expose` with a `name` returns at once, but the new URL
|
|
204
|
+
answers HTTPS only after boxd issues its certificate, typically 15 to 20
|
|
205
|
+
seconds later. The default URL answers at once.
|
|
206
|
+
|
|
207
|
+
## How it works
|
|
208
|
+
|
|
209
|
+
The component calls boxd through the
|
|
210
|
+
[`@boxd-sh/sdk`](https://www.npmjs.com/package/@boxd-sh/sdk) web entry, which
|
|
211
|
+
runs in Convex's default runtime. It exchanges the API key for a session token
|
|
212
|
+
once, keeps the token in its own table, and reuses it until it nears expiry.
|
|
213
|
+
boxd rate-limits that exchange per source IP, and Convex deployments share their
|
|
214
|
+
egress IPs, so each action must not exchange the key again. If boxd revokes the
|
|
215
|
+
token early, the component exchanges the key again and retries the call once.
|
|
216
|
+
|
|
217
|
+
## Testing your app
|
|
218
|
+
|
|
219
|
+
Register the component with `convex-test`:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { convexTest } from "convex-test";
|
|
223
|
+
import boxdTest from "@boxd-sh/convex/test";
|
|
224
|
+
import schema from "./schema";
|
|
225
|
+
|
|
226
|
+
const modules = import.meta.glob("./**/*.ts");
|
|
227
|
+
|
|
228
|
+
const t = convexTest(schema, modules);
|
|
229
|
+
boxdTest.register(t);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
To keep tests off the network, mock `@boxd-sh/sdk/web` with `vi.mock`. The
|
|
233
|
+
component's own tests do this: see `src/component/setup.test.ts`.
|
|
234
|
+
|
|
235
|
+
## Developing this component
|
|
236
|
+
|
|
237
|
+
```sh
|
|
238
|
+
npm install
|
|
239
|
+
npm run build:codegen # codegen and build dist/
|
|
240
|
+
npm run check # build, typecheck, lint, format check, unit tests
|
|
241
|
+
npm run test:live # the example app against real boxd machines
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`test:live` runs the example app on an anonymous local Convex backend, with no
|
|
245
|
+
Convex account and nothing uploaded, against real boxd machines. It needs
|
|
246
|
+
`BOXD_API_KEY` in the environment or in `.env.e2e.local`, and destroys every
|
|
247
|
+
machine it creates.
|
|
248
|
+
|
|
249
|
+
`demo/` is the public demo: a small React page (`demo/src`) that boots a
|
|
250
|
+
machine, runs commands and forks it, all through the component, with its own
|
|
251
|
+
Convex backend in `demo/convex`. That backend adds the parts a public page needs
|
|
252
|
+
on top of the component: anonymous sign-in, an optional password
|
|
253
|
+
(`DEMO_PASSWORD`), per-visitor machine caps, rate limits, and a cron that
|
|
254
|
+
destroys each machine after ten minutes. Demo machines are `isolated`, so
|
|
255
|
+
visitor commands can't reach boxd or other machines. Run it with
|
|
256
|
+
`npx convex dev` and `npm run dev:demo`, or build it with `npm run build:demo`.
|
|
257
|
+
|
|
258
|
+
`example/convex` is a reference app that wraps the whole API with no limits. It
|
|
259
|
+
backs the unit and live tests and is never deployed alongside the demo, since
|
|
260
|
+
its functions are open to any signed-in user.
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=_ignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_ignore.d.ts","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_ignore.js","sourceRoot":"","sources":["../../../src/client/_generated/_ignore.ts"],"names":[],"mappings":";AAAA,kEAAkE"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App-facing client for the boxd Convex component.
|
|
3
|
+
*
|
|
4
|
+
* Create one with `components.boxd` in your `convex/` directory and call it
|
|
5
|
+
* from your queries and actions. Reads (`get`, `list`, `listExecutions`,
|
|
6
|
+
* `getExecution`) run in queries and are reactive. Everything that talks to
|
|
7
|
+
* boxd runs in actions.
|
|
8
|
+
*
|
|
9
|
+
* The component can't see your users: `ctx.auth` doesn't reach into a
|
|
10
|
+
* component. Derive `ownerId` from your own auth and pass it on every call.
|
|
11
|
+
* Never take it from a client argument. A machine created with an `ownerId`
|
|
12
|
+
* is reachable only with that same `ownerId`.
|
|
13
|
+
*/
|
|
14
|
+
import { ConvexError, type Infer } from "convex/values";
|
|
15
|
+
import type { FunctionArgs, FunctionReference, FunctionReturnType, GenericActionCtx, GenericDataModel, GenericQueryCtx } from "convex/server";
|
|
16
|
+
import type { ComponentApi } from "../component/_generated/component.js";
|
|
17
|
+
import type { BoxdErrorData, errorCode } from "../component/errors.js";
|
|
18
|
+
type QueryCtx = Pick<GenericQueryCtx<GenericDataModel>, "runQuery">;
|
|
19
|
+
type ActionCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery" | "runMutation" | "runAction">;
|
|
20
|
+
type Machines = ComponentApi["machines"];
|
|
21
|
+
type Args<F extends FunctionReference<any, "internal">> = FunctionArgs<F>;
|
|
22
|
+
type Returns<F extends FunctionReference<any, "internal">> = FunctionReturnType<F>;
|
|
23
|
+
/** A machine's row: boxd's last observed state plus the component's fields. */
|
|
24
|
+
export type MachineRecord = Returns<Machines["create"]>;
|
|
25
|
+
/** One `exec`, as stored in the component. */
|
|
26
|
+
export type ExecutionRecord = NonNullable<Returns<ComponentApi["executions"]["get"]>>;
|
|
27
|
+
export type ExecResult = Returns<ComponentApi["exec"]["run"]>;
|
|
28
|
+
export type DirListing = Returns<ComponentApi["files"]["listDir"]>;
|
|
29
|
+
export type CreateArgs = Args<Machines["create"]>;
|
|
30
|
+
export type ForkArgs = Args<Machines["fork"]>;
|
|
31
|
+
export type ExecArgs = Args<ComponentApi["exec"]["run"]>;
|
|
32
|
+
export type ExposeArgs = Args<Machines["expose"]>;
|
|
33
|
+
/** Addresses one machine: its boxd id, and the owner it belongs to. */
|
|
34
|
+
export type MachineRef = {
|
|
35
|
+
machineId: string;
|
|
36
|
+
ownerId?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Addresses a machine that wakes up: optionally wait until it takes an exec. */
|
|
39
|
+
export type WakeArgs = Args<Machines["start"]>;
|
|
40
|
+
export type FileRef = MachineRef & {
|
|
41
|
+
path: string;
|
|
42
|
+
};
|
|
43
|
+
export type BoxdErrorCode = Infer<typeof errorCode>;
|
|
44
|
+
export type { BoxdErrorData, ComponentApi };
|
|
45
|
+
/**
|
|
46
|
+
* True when `error` is a `ConvexError` this component threw, and, with
|
|
47
|
+
* `code`, carries that code:
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* if (isBoxdError(error, "NOT_FOUND")) return null;
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function isBoxdError(error: unknown, code?: BoxdErrorCode): error is ConvexError<BoxdErrorData>;
|
|
54
|
+
export declare class Boxd {
|
|
55
|
+
readonly component: ComponentApi;
|
|
56
|
+
constructor(component: ComponentApi);
|
|
57
|
+
/** The machine's row, or null if it doesn't exist for this owner. */
|
|
58
|
+
get(ctx: QueryCtx, args: MachineRef): Promise<{
|
|
59
|
+
_creationTime: number;
|
|
60
|
+
_id: string;
|
|
61
|
+
forkedFrom?: string;
|
|
62
|
+
image?: string;
|
|
63
|
+
lastError?: string;
|
|
64
|
+
machineId: string;
|
|
65
|
+
memoryBytes?: number;
|
|
66
|
+
name: string;
|
|
67
|
+
ownerId?: string;
|
|
68
|
+
status: string;
|
|
69
|
+
updatedAt: number;
|
|
70
|
+
url?: string;
|
|
71
|
+
vcpu?: number;
|
|
72
|
+
} | null>;
|
|
73
|
+
/** The owner's machines, newest first, destroyed ones included. */
|
|
74
|
+
list(ctx: QueryCtx, args?: {
|
|
75
|
+
ownerId?: string;
|
|
76
|
+
limit?: number;
|
|
77
|
+
}): Promise<{
|
|
78
|
+
_creationTime: number;
|
|
79
|
+
_id: string;
|
|
80
|
+
forkedFrom?: string;
|
|
81
|
+
image?: string;
|
|
82
|
+
lastError?: string;
|
|
83
|
+
machineId: string;
|
|
84
|
+
memoryBytes?: number;
|
|
85
|
+
name: string;
|
|
86
|
+
ownerId?: string;
|
|
87
|
+
status: string;
|
|
88
|
+
updatedAt: number;
|
|
89
|
+
url?: string;
|
|
90
|
+
vcpu?: number;
|
|
91
|
+
}[]>;
|
|
92
|
+
/** A machine's executions, newest first. */
|
|
93
|
+
listExecutions(ctx: QueryCtx, args: MachineRef & {
|
|
94
|
+
limit?: number;
|
|
95
|
+
}): Promise<{
|
|
96
|
+
_creationTime: number;
|
|
97
|
+
_id: string;
|
|
98
|
+
command: string;
|
|
99
|
+
cwd?: string;
|
|
100
|
+
error?: string;
|
|
101
|
+
exitCode?: number;
|
|
102
|
+
finishedAt?: number;
|
|
103
|
+
machineId: string;
|
|
104
|
+
ownerId?: string;
|
|
105
|
+
startedAt: number;
|
|
106
|
+
status: "running" | "completed" | "failed";
|
|
107
|
+
stderr?: string;
|
|
108
|
+
stdout?: string;
|
|
109
|
+
}[]>;
|
|
110
|
+
/** One execution by the id `exec` returned, or null. */
|
|
111
|
+
getExecution(ctx: QueryCtx, args: {
|
|
112
|
+
executionId: string;
|
|
113
|
+
ownerId?: string;
|
|
114
|
+
}): Promise<{
|
|
115
|
+
_creationTime: number;
|
|
116
|
+
_id: string;
|
|
117
|
+
command: string;
|
|
118
|
+
cwd?: string;
|
|
119
|
+
error?: string;
|
|
120
|
+
exitCode?: number;
|
|
121
|
+
finishedAt?: number;
|
|
122
|
+
machineId: string;
|
|
123
|
+
ownerId?: string;
|
|
124
|
+
startedAt: number;
|
|
125
|
+
status: "running" | "completed" | "failed";
|
|
126
|
+
stderr?: string;
|
|
127
|
+
stdout?: string;
|
|
128
|
+
} | null>;
|
|
129
|
+
/** Create a machine and, by default, wait until it accepts an exec. */
|
|
130
|
+
create(ctx: ActionCtx, args?: CreateArgs): Promise<{
|
|
131
|
+
_creationTime: number;
|
|
132
|
+
_id: string;
|
|
133
|
+
forkedFrom?: string;
|
|
134
|
+
image?: string;
|
|
135
|
+
lastError?: string;
|
|
136
|
+
machineId: string;
|
|
137
|
+
memoryBytes?: number;
|
|
138
|
+
name: string;
|
|
139
|
+
ownerId?: string;
|
|
140
|
+
status: string;
|
|
141
|
+
updatedAt: number;
|
|
142
|
+
url?: string;
|
|
143
|
+
vcpu?: number;
|
|
144
|
+
}>;
|
|
145
|
+
/** Fork a machine, disk and memory, into a new one with the same owner. */
|
|
146
|
+
fork(ctx: ActionCtx, args: ForkArgs): Promise<{
|
|
147
|
+
_creationTime: number;
|
|
148
|
+
_id: string;
|
|
149
|
+
forkedFrom?: string;
|
|
150
|
+
image?: string;
|
|
151
|
+
lastError?: string;
|
|
152
|
+
machineId: string;
|
|
153
|
+
memoryBytes?: number;
|
|
154
|
+
name: string;
|
|
155
|
+
ownerId?: string;
|
|
156
|
+
status: string;
|
|
157
|
+
updatedAt: number;
|
|
158
|
+
url?: string;
|
|
159
|
+
vcpu?: number;
|
|
160
|
+
}>;
|
|
161
|
+
/** Re-read the machine from boxd into its row. */
|
|
162
|
+
refresh(ctx: ActionCtx, args: MachineRef): Promise<{
|
|
163
|
+
_creationTime: number;
|
|
164
|
+
_id: string;
|
|
165
|
+
forkedFrom?: string;
|
|
166
|
+
image?: string;
|
|
167
|
+
lastError?: string;
|
|
168
|
+
machineId: string;
|
|
169
|
+
memoryBytes?: number;
|
|
170
|
+
name: string;
|
|
171
|
+
ownerId?: string;
|
|
172
|
+
status: string;
|
|
173
|
+
updatedAt: number;
|
|
174
|
+
url?: string;
|
|
175
|
+
vcpu?: number;
|
|
176
|
+
}>;
|
|
177
|
+
/** Boot a stopped machine. */
|
|
178
|
+
start(ctx: ActionCtx, args: WakeArgs): Promise<{
|
|
179
|
+
_creationTime: number;
|
|
180
|
+
_id: string;
|
|
181
|
+
forkedFrom?: string;
|
|
182
|
+
image?: string;
|
|
183
|
+
lastError?: string;
|
|
184
|
+
machineId: string;
|
|
185
|
+
memoryBytes?: number;
|
|
186
|
+
name: string;
|
|
187
|
+
ownerId?: string;
|
|
188
|
+
status: string;
|
|
189
|
+
updatedAt: number;
|
|
190
|
+
url?: string;
|
|
191
|
+
vcpu?: number;
|
|
192
|
+
}>;
|
|
193
|
+
/** Shut the machine down. The disk is kept. */
|
|
194
|
+
stop(ctx: ActionCtx, args: MachineRef): Promise<{
|
|
195
|
+
_creationTime: number;
|
|
196
|
+
_id: string;
|
|
197
|
+
forkedFrom?: string;
|
|
198
|
+
image?: string;
|
|
199
|
+
lastError?: string;
|
|
200
|
+
machineId: string;
|
|
201
|
+
memoryBytes?: number;
|
|
202
|
+
name: string;
|
|
203
|
+
ownerId?: string;
|
|
204
|
+
status: string;
|
|
205
|
+
updatedAt: number;
|
|
206
|
+
url?: string;
|
|
207
|
+
vcpu?: number;
|
|
208
|
+
}>;
|
|
209
|
+
/** Suspend to RAM. An exec or an HTTPS request wakes it again. */
|
|
210
|
+
pause(ctx: ActionCtx, args: MachineRef): Promise<{
|
|
211
|
+
_creationTime: number;
|
|
212
|
+
_id: string;
|
|
213
|
+
forkedFrom?: string;
|
|
214
|
+
image?: string;
|
|
215
|
+
lastError?: string;
|
|
216
|
+
machineId: string;
|
|
217
|
+
memoryBytes?: number;
|
|
218
|
+
name: string;
|
|
219
|
+
ownerId?: string;
|
|
220
|
+
status: string;
|
|
221
|
+
updatedAt: number;
|
|
222
|
+
url?: string;
|
|
223
|
+
vcpu?: number;
|
|
224
|
+
}>;
|
|
225
|
+
/** Resume a paused machine. */
|
|
226
|
+
resume(ctx: ActionCtx, args: WakeArgs): Promise<{
|
|
227
|
+
_creationTime: number;
|
|
228
|
+
_id: string;
|
|
229
|
+
forkedFrom?: string;
|
|
230
|
+
image?: string;
|
|
231
|
+
lastError?: string;
|
|
232
|
+
machineId: string;
|
|
233
|
+
memoryBytes?: number;
|
|
234
|
+
name: string;
|
|
235
|
+
ownerId?: string;
|
|
236
|
+
status: string;
|
|
237
|
+
updatedAt: number;
|
|
238
|
+
url?: string;
|
|
239
|
+
vcpu?: number;
|
|
240
|
+
}>;
|
|
241
|
+
/** Suspend to disk, freeing the host's memory. */
|
|
242
|
+
hibernate(ctx: ActionCtx, args: MachineRef): Promise<{
|
|
243
|
+
_creationTime: number;
|
|
244
|
+
_id: string;
|
|
245
|
+
forkedFrom?: string;
|
|
246
|
+
image?: string;
|
|
247
|
+
lastError?: string;
|
|
248
|
+
machineId: string;
|
|
249
|
+
memoryBytes?: number;
|
|
250
|
+
name: string;
|
|
251
|
+
ownerId?: string;
|
|
252
|
+
status: string;
|
|
253
|
+
updatedAt: number;
|
|
254
|
+
url?: string;
|
|
255
|
+
vcpu?: number;
|
|
256
|
+
}>;
|
|
257
|
+
/** Restore a hibernated machine, memory included. */
|
|
258
|
+
wake(ctx: ActionCtx, args: WakeArgs): Promise<{
|
|
259
|
+
_creationTime: number;
|
|
260
|
+
_id: string;
|
|
261
|
+
forkedFrom?: string;
|
|
262
|
+
image?: string;
|
|
263
|
+
lastError?: string;
|
|
264
|
+
machineId: string;
|
|
265
|
+
memoryBytes?: number;
|
|
266
|
+
name: string;
|
|
267
|
+
ownerId?: string;
|
|
268
|
+
status: string;
|
|
269
|
+
updatedAt: number;
|
|
270
|
+
url?: string;
|
|
271
|
+
vcpu?: number;
|
|
272
|
+
}>;
|
|
273
|
+
/** Destroy the machine. Its row stays, with status "destroyed". */
|
|
274
|
+
destroy(ctx: ActionCtx, args: MachineRef): Promise<{
|
|
275
|
+
_creationTime: number;
|
|
276
|
+
_id: string;
|
|
277
|
+
forkedFrom?: string;
|
|
278
|
+
image?: string;
|
|
279
|
+
lastError?: string;
|
|
280
|
+
machineId: string;
|
|
281
|
+
memoryBytes?: number;
|
|
282
|
+
name: string;
|
|
283
|
+
ownerId?: string;
|
|
284
|
+
status: string;
|
|
285
|
+
updatedAt: number;
|
|
286
|
+
url?: string;
|
|
287
|
+
vcpu?: number;
|
|
288
|
+
}>;
|
|
289
|
+
/**
|
|
290
|
+
* Route public HTTPS to a port. Without `name`: the machine's default URL.
|
|
291
|
+
* With `name`: an extra `https://<name>.<machine>.<zone>`.
|
|
292
|
+
*/
|
|
293
|
+
expose(ctx: ActionCtx, args: ExposeArgs): Promise<{
|
|
294
|
+
port: number;
|
|
295
|
+
url: string;
|
|
296
|
+
}>;
|
|
297
|
+
/** Run a command and wait for it to exit. Recorded in `executions`. */
|
|
298
|
+
exec(ctx: ActionCtx, args: ExecArgs): Promise<{
|
|
299
|
+
executionId: string;
|
|
300
|
+
exitCode: number;
|
|
301
|
+
stderr: string;
|
|
302
|
+
stdout: string;
|
|
303
|
+
success: boolean;
|
|
304
|
+
}>;
|
|
305
|
+
/** Read a file as UTF-8 text. */
|
|
306
|
+
readFile(ctx: ActionCtx, args: FileRef): Promise<string>;
|
|
307
|
+
/** Read a file byte for byte. */
|
|
308
|
+
readFileBytes(ctx: ActionCtx, args: FileRef): Promise<ArrayBuffer>;
|
|
309
|
+
/** Create or replace a file from text or bytes. */
|
|
310
|
+
writeFile(ctx: ActionCtx, args: FileRef & {
|
|
311
|
+
content: string | ArrayBuffer;
|
|
312
|
+
}): Promise<{
|
|
313
|
+
bytesWritten: number;
|
|
314
|
+
}>;
|
|
315
|
+
/** The immediate contents of a directory. */
|
|
316
|
+
listDir(ctx: ActionCtx, args: FileRef): Promise<{
|
|
317
|
+
entries: Array<{
|
|
318
|
+
isDir: boolean;
|
|
319
|
+
modifiedAt: number;
|
|
320
|
+
name: string;
|
|
321
|
+
permissions: string;
|
|
322
|
+
sizeBytes: number;
|
|
323
|
+
}>;
|
|
324
|
+
truncated: boolean;
|
|
325
|
+
}>;
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"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"}
|