@replayio/app-building 1.5.0 → 1.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/README.md +7 -4
- package/dist/container.d.ts +1 -0
- package/dist/container.js +18 -3
- package/dist/fly.d.ts +24 -1
- package/dist/fly.js +58 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,12 +108,15 @@ await stopRemoteContainer(config, state);
|
|
|
108
108
|
| Export | Description |
|
|
109
109
|
|---|---|
|
|
110
110
|
| `createApp(token, name, org?)` | Create a Fly app and allocate IPs. |
|
|
111
|
-
| `createMachine(app, token, image, env, name)` | Create a Fly machine. Returns machine ID. |
|
|
111
|
+
| `createMachine(app, token, image, env, name, volumeId?)` | Create a Fly machine. If `volumeId` is provided, mounts it at `/repo`. Returns machine ID. |
|
|
112
112
|
| `waitForMachine(app, token, machineId, timeout?)` | Wait for a machine to reach `started` state. |
|
|
113
113
|
| `destroyMachine(app, token, machineId)` | Force-destroy a machine. |
|
|
114
114
|
| `listMachines(app, token)` | List all machines for an app. |
|
|
115
|
+
| `createVolume(app, token, name, sizeGb?)` | Create a Fly volume (default 50 GB). Returns volume ID. |
|
|
116
|
+
| `deleteVolume(app, token, volumeId)` | Delete a Fly volume. |
|
|
117
|
+
| `listVolumes(app, token)` | List all volumes for an app. |
|
|
115
118
|
|
|
116
|
-
**Types:** `FlyMachineInfo`
|
|
119
|
+
**Types:** `FlyMachineInfo`, `FlyVolumeInfo`
|
|
117
120
|
|
|
118
121
|
### Image ref
|
|
119
122
|
|
|
@@ -190,8 +193,8 @@ Every POST body has this shape:
|
|
|
190
193
|
| `message.started` | Message processing begins | `iteration`, `prompt` |
|
|
191
194
|
| `message.done` | Message processing complete | `messageId`, `cost_usd`, `duration_ms`, `num_turns` |
|
|
192
195
|
| `message.error` | Message processing failed | `messageId`, `error` |
|
|
193
|
-
| `task.started` | Task processing begins | `iteration`, `
|
|
194
|
-
| `task.done` | Task processing complete | `
|
|
196
|
+
| `task.started` | Task processing begins | `iteration`, `skill`, `subtasks` |
|
|
197
|
+
| `task.done` | Task processing complete | `skill`, `cost`, `totalCost`, `failed` |
|
|
195
198
|
| `log` | Each log line | `line` |
|
|
196
199
|
|
|
197
200
|
### Example
|
package/dist/container.d.ts
CHANGED
package/dist/container.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFileSync, spawn } from "child_process";
|
|
2
2
|
import { readFileSync, existsSync } from "fs";
|
|
3
3
|
import { resolve } from "path";
|
|
4
|
-
import { createMachine, waitForMachine, destroyMachine, listMachines } from "./fly";
|
|
4
|
+
import { createMachine, waitForMachine, destroyMachine, listMachines, createVolume, deleteVolume } from "./fly";
|
|
5
5
|
import { getImageRef } from "./image-ref";
|
|
6
6
|
const IMAGE_NAME = "app-building";
|
|
7
7
|
function debugLog(...args) {
|
|
@@ -215,12 +215,16 @@ export async function startRemoteContainer(config, repo) {
|
|
|
215
215
|
console.log(` ${m.id} (${m.name}) — ${m.state}`);
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
|
+
// Create a volume for /repo storage
|
|
219
|
+
console.log("Creating Fly volume...");
|
|
220
|
+
const volumeId = await createVolume(config.flyApp, config.flyToken, `repo-${uniqueId}`);
|
|
221
|
+
console.log(`Volume created: ${volumeId}`);
|
|
218
222
|
// Retry machine creation — the registry tag may take a moment to propagate
|
|
219
223
|
console.log("Creating Fly machine...");
|
|
220
224
|
let machineId = "";
|
|
221
225
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
222
226
|
try {
|
|
223
|
-
machineId = await createMachine(config.flyApp, config.flyToken, imageRef, containerEnv, machineName);
|
|
227
|
+
machineId = await createMachine(config.flyApp, config.flyToken, imageRef, containerEnv, machineName, volumeId);
|
|
224
228
|
break;
|
|
225
229
|
}
|
|
226
230
|
catch (err) {
|
|
@@ -230,6 +234,8 @@ export async function startRemoteContainer(config, repo) {
|
|
|
230
234
|
await new Promise((r) => setTimeout(r, 5000));
|
|
231
235
|
continue;
|
|
232
236
|
}
|
|
237
|
+
// Clean up volume if machine creation fails
|
|
238
|
+
await deleteVolume(config.flyApp, config.flyToken, volumeId).catch(() => { });
|
|
233
239
|
throw err;
|
|
234
240
|
}
|
|
235
241
|
}
|
|
@@ -243,6 +249,7 @@ export async function startRemoteContainer(config, repo) {
|
|
|
243
249
|
baseUrl,
|
|
244
250
|
flyApp: config.flyApp,
|
|
245
251
|
flyMachineId: machineId,
|
|
252
|
+
flyVolumeId: volumeId,
|
|
246
253
|
};
|
|
247
254
|
config.registry.log(agentState);
|
|
248
255
|
console.log("Waiting for machine to start...");
|
|
@@ -269,9 +276,10 @@ export async function startRemoteContainer(config, repo) {
|
|
|
269
276
|
await new Promise((r) => setTimeout(r, interval));
|
|
270
277
|
}
|
|
271
278
|
if (!ready) {
|
|
272
|
-
// Clean up machine if we can't reach it
|
|
279
|
+
// Clean up machine and volume if we can't reach it
|
|
273
280
|
console.log("Timed out waiting for machine, destroying...");
|
|
274
281
|
await destroyMachine(config.flyApp, config.flyToken, machineId).catch(() => { });
|
|
282
|
+
await deleteVolume(config.flyApp, config.flyToken, volumeId).catch(() => { });
|
|
275
283
|
throw new Error("Remote container did not become ready within timeout");
|
|
276
284
|
}
|
|
277
285
|
return agentState;
|
|
@@ -285,6 +293,13 @@ export async function stopRemoteContainer(config, state) {
|
|
|
285
293
|
console.log(`Destroying Fly machine ${state.flyMachineId}...`);
|
|
286
294
|
await destroyMachine(state.flyApp, config.flyToken, state.flyMachineId);
|
|
287
295
|
console.log("Machine destroyed.");
|
|
296
|
+
if (state.flyVolumeId) {
|
|
297
|
+
console.log(`Deleting Fly volume ${state.flyVolumeId}...`);
|
|
298
|
+
await deleteVolume(state.flyApp, config.flyToken, state.flyVolumeId).catch((err) => {
|
|
299
|
+
console.log(`Warning: failed to delete volume: ${err instanceof Error ? err.message : err}`);
|
|
300
|
+
});
|
|
301
|
+
console.log("Volume deleted.");
|
|
302
|
+
}
|
|
288
303
|
config.registry.markStopped(state.containerName);
|
|
289
304
|
}
|
|
290
305
|
export function stopContainer(config, containerName) {
|
package/dist/fly.d.ts
CHANGED
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
* Create a Fly app via the Machines API and allocate IPs so .fly.dev DNS works.
|
|
3
3
|
*/
|
|
4
4
|
export declare function createApp(token: string, name: string, org?: string): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Create a Fly Volume in the app's primary region.
|
|
7
|
+
* Returns the volume ID.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createVolume(app: string, token: string, name: string, sizeGb?: number): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Delete a Fly Volume.
|
|
12
|
+
*/
|
|
13
|
+
export declare function deleteVolume(app: string, token: string, volumeId: string): Promise<void>;
|
|
5
14
|
/**
|
|
6
15
|
* Create a Fly Machine with the given image and env vars.
|
|
16
|
+
* If volumeId is provided, it is mounted at /repo.
|
|
7
17
|
* Returns the machine ID.
|
|
8
18
|
*/
|
|
9
|
-
export declare function createMachine(app: string, token: string, image: string, env: Record<string, string>, name: string): Promise<string>;
|
|
19
|
+
export declare function createMachine(app: string, token: string, image: string, env: Record<string, string>, name: string, volumeId?: string): Promise<string>;
|
|
10
20
|
/**
|
|
11
21
|
* Wait for a Fly Machine to reach the "started" state.
|
|
12
22
|
*/
|
|
@@ -26,3 +36,16 @@ export interface FlyMachineInfo {
|
|
|
26
36
|
* List all machines for a Fly app.
|
|
27
37
|
*/
|
|
28
38
|
export declare function listMachines(app: string, token: string): Promise<FlyMachineInfo[]>;
|
|
39
|
+
export interface FlyVolumeInfo {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
state: string;
|
|
43
|
+
size_gb: number;
|
|
44
|
+
region: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
attached_machine_id: string | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* List all volumes for a Fly app.
|
|
50
|
+
*/
|
|
51
|
+
export declare function listVolumes(app: string, token: string): Promise<FlyVolumeInfo[]>;
|
package/dist/fly.js
CHANGED
|
@@ -52,39 +52,66 @@ export async function createApp(token, name, org) {
|
|
|
52
52
|
await gqlFetch(allocateMutation, { input: { appId: name, type: "v6" } });
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* Create a Fly
|
|
56
|
-
* Returns the
|
|
55
|
+
* Create a Fly Volume in the app's primary region.
|
|
56
|
+
* Returns the volume ID.
|
|
57
57
|
*/
|
|
58
|
-
export async function
|
|
59
|
-
const res = await flyFetch(`/apps/${app}/
|
|
58
|
+
export async function createVolume(app, token, name, sizeGb = 50) {
|
|
59
|
+
const res = await flyFetch(`/apps/${app}/volumes`, token, {
|
|
60
60
|
method: "POST",
|
|
61
61
|
body: JSON.stringify({
|
|
62
62
|
name,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
auto_destroy: true,
|
|
67
|
-
restart: { policy: "no" },
|
|
68
|
-
guest: {
|
|
69
|
-
cpu_kind: "performance",
|
|
70
|
-
cpus: 16,
|
|
71
|
-
memory_mb: 32768,
|
|
72
|
-
},
|
|
73
|
-
services: [
|
|
74
|
-
{
|
|
75
|
-
ports: [{ port: 443, handlers: ["tls", "http"] }],
|
|
76
|
-
protocol: "tcp",
|
|
77
|
-
internal_port: 3000,
|
|
78
|
-
autostart: false,
|
|
79
|
-
autostop: "off",
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
},
|
|
63
|
+
size_gb: sizeGb,
|
|
64
|
+
encrypted: true,
|
|
65
|
+
require_unique_zone: false,
|
|
83
66
|
}),
|
|
84
67
|
});
|
|
85
68
|
const data = (await res.json());
|
|
86
69
|
return data.id;
|
|
87
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Delete a Fly Volume.
|
|
73
|
+
*/
|
|
74
|
+
export async function deleteVolume(app, token, volumeId) {
|
|
75
|
+
await flyFetch(`/apps/${app}/volumes/${volumeId}`, token, {
|
|
76
|
+
method: "DELETE",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a Fly Machine with the given image and env vars.
|
|
81
|
+
* If volumeId is provided, it is mounted at /repo.
|
|
82
|
+
* Returns the machine ID.
|
|
83
|
+
*/
|
|
84
|
+
export async function createMachine(app, token, image, env, name, volumeId) {
|
|
85
|
+
const config = {
|
|
86
|
+
image,
|
|
87
|
+
env,
|
|
88
|
+
auto_destroy: true,
|
|
89
|
+
restart: { policy: "no" },
|
|
90
|
+
guest: {
|
|
91
|
+
cpu_kind: "performance",
|
|
92
|
+
cpus: 16,
|
|
93
|
+
memory_mb: 32768,
|
|
94
|
+
},
|
|
95
|
+
services: [
|
|
96
|
+
{
|
|
97
|
+
ports: [{ port: 443, handlers: ["tls", "http"] }],
|
|
98
|
+
protocol: "tcp",
|
|
99
|
+
internal_port: 3000,
|
|
100
|
+
autostart: false,
|
|
101
|
+
autostop: "off",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
if (volumeId) {
|
|
106
|
+
config.mounts = [{ volume: volumeId, path: "/repo" }];
|
|
107
|
+
}
|
|
108
|
+
const res = await flyFetch(`/apps/${app}/machines`, token, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
body: JSON.stringify({ name, config }),
|
|
111
|
+
});
|
|
112
|
+
const data = (await res.json());
|
|
113
|
+
return data.id;
|
|
114
|
+
}
|
|
88
115
|
/**
|
|
89
116
|
* Wait for a Fly Machine to reach the "started" state.
|
|
90
117
|
*/
|
|
@@ -117,3 +144,10 @@ export async function listMachines(app, token) {
|
|
|
117
144
|
const res = await flyFetch(`/apps/${app}/machines`, token);
|
|
118
145
|
return (await res.json());
|
|
119
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* List all volumes for a Fly app.
|
|
149
|
+
*/
|
|
150
|
+
export async function listVolumes(app, token) {
|
|
151
|
+
const res = await flyFetch(`/apps/${app}/volumes`, token);
|
|
152
|
+
return (await res.json());
|
|
153
|
+
}
|