@replayio/app-building 1.8.3 → 1.10.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 +3 -4
- package/dist/fly.d.ts +2 -2
- package/dist/fly.js +58 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,9 +126,8 @@ Each container runs an HTTP server that accepts the following requests:
|
|
|
126
126
|
|
|
127
127
|
| Method | Path | Description |
|
|
128
128
|
|---|---|---|
|
|
129
|
-
| `POST /message` | `{ prompt: string }` |
|
|
130
|
-
| `
|
|
131
|
-
| `POST /detach` | | Signal the container to exit once all queued messages and tasks are done. |
|
|
129
|
+
| `POST /message` | `{ prompt: string }` | Add a prompt as a task in the persistent task queue. Returns `{ ok: true }`. |
|
|
130
|
+
| `POST /detach` | | Signal the container to exit once all tasks are done. |
|
|
132
131
|
| `POST /stop` | | Force-stop the container immediately. Interrupts any running work, commits remaining changes, then exits. |
|
|
133
132
|
| `POST /interrupt` | | Kill the currently running Claude process without stopping the container. |
|
|
134
133
|
| `GET /status` | | Container state, queue depth, iteration count, cost, revision, etc. |
|
|
@@ -190,7 +189,7 @@ Every POST body has this shape:
|
|
|
190
189
|
| `message.done` | Message processing complete | `messageId`, `cost_usd`, `duration_ms`, `num_turns` |
|
|
191
190
|
| `message.error` | Message processing failed | `messageId`, `error` |
|
|
192
191
|
| `task.started` | Task processing begins | `iteration`, `skill`, `subtasks` |
|
|
193
|
-
| `task.done` | Task processing complete | `skill`, `cost`, `totalCost`, `failed` |
|
|
192
|
+
| `task.done` | Task processing complete | `skill`, `cost`, `totalCost`, `failed`, `pendingTasks` |
|
|
194
193
|
| `log` | Each log line | `line` |
|
|
195
194
|
|
|
196
195
|
### Example
|
package/dist/fly.d.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare function createApp(token: string, name: string, org?: string): Promise<void>;
|
|
5
5
|
/**
|
|
6
|
-
* Create a Fly Volume in the
|
|
6
|
+
* Create a Fly Volume in the given region.
|
|
7
7
|
* Returns the volume ID.
|
|
8
8
|
*/
|
|
9
|
-
export declare function createVolume(app: string, token: string, name: string, sizeGb?: number): Promise<string>;
|
|
9
|
+
export declare function createVolume(app: string, token: string, name: string, region: string, sizeGb?: number): Promise<string>;
|
|
10
10
|
/**
|
|
11
11
|
* Delete a Fly Volume.
|
|
12
12
|
*/
|
package/dist/fly.js
CHANGED
|
@@ -52,14 +52,15 @@ 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 Volume in the
|
|
55
|
+
* Create a Fly Volume in the given region.
|
|
56
56
|
* Returns the volume ID.
|
|
57
57
|
*/
|
|
58
|
-
export async function createVolume(app, token, name, sizeGb = 50) {
|
|
58
|
+
export async function createVolume(app, token, name, region, sizeGb = 50) {
|
|
59
59
|
const res = await flyFetch(`/apps/${app}/volumes`, token, {
|
|
60
60
|
method: "POST",
|
|
61
61
|
body: JSON.stringify({
|
|
62
62
|
name,
|
|
63
|
+
region,
|
|
63
64
|
size_gb: sizeGb,
|
|
64
65
|
encrypted: true,
|
|
65
66
|
require_unique_zone: false,
|
|
@@ -82,70 +83,66 @@ export async function deleteVolume(app, token, volumeId) {
|
|
|
82
83
|
* Returns the machine ID and volume ID.
|
|
83
84
|
*/
|
|
84
85
|
export async function createMachine(app, token, image, env, name) {
|
|
85
|
-
// Create a volume for /repo storage
|
|
86
86
|
const volumeName = `repo_${name.replace(/-/g, "_")}`.slice(0, 30);
|
|
87
|
-
|
|
87
|
+
// Regions to try in order. dfw and iad have the most reliable capacity for
|
|
88
|
+
// performance machines. Fall back to ord and sjc if needed.
|
|
89
|
+
const regions = ["dfw", "iad", "ord", "sjc"];
|
|
88
90
|
// Delete unattached volumes in parallel with creating the new machine.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
throw err;
|
|
91
|
+
let cleanupDone;
|
|
92
|
+
for (const region of regions) {
|
|
93
|
+
const volumeId = await createVolume(app, token, volumeName, region, 50);
|
|
94
|
+
// Start cleanup on first attempt only
|
|
95
|
+
if (!cleanupDone) {
|
|
96
|
+
cleanupDone = listVolumes(app, token).then(vols => Promise.all(vols.map(async ({ id, attached_machine_id }) => {
|
|
97
|
+
if (attached_machine_id || id === volumeId)
|
|
98
|
+
return;
|
|
99
|
+
await deleteVolume(app, token, id).catch(() => { });
|
|
100
|
+
})));
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const res = await flyFetch(`/apps/${app}/machines`, token, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
name,
|
|
107
|
+
region,
|
|
108
|
+
config: {
|
|
109
|
+
image,
|
|
110
|
+
env,
|
|
111
|
+
auto_destroy: true,
|
|
112
|
+
restart: { policy: "on-failure", max_retries: 3 },
|
|
113
|
+
guest: {
|
|
114
|
+
cpu_kind: "performance",
|
|
115
|
+
cpus: 16,
|
|
116
|
+
memory_mb: 32768,
|
|
117
|
+
},
|
|
118
|
+
mounts: [{ volume: volumeId, path: "/repo" }],
|
|
119
|
+
services: [
|
|
120
|
+
{
|
|
121
|
+
ports: [{ port: 443, handlers: ["tls", "http"] }],
|
|
122
|
+
protocol: "tcp",
|
|
123
|
+
internal_port: 3000,
|
|
124
|
+
autostart: false,
|
|
125
|
+
autostop: "off",
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
const data = (await res.json());
|
|
132
|
+
await cleanupDone;
|
|
133
|
+
return { machineId: data.id, volumeId };
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
await deleteVolume(app, token, volumeId).catch(() => { });
|
|
137
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
138
|
+
if (msg.includes("412")) {
|
|
139
|
+
console.log(`Insufficient resources in ${region}, trying next region...`);
|
|
140
|
+
continue;
|
|
140
141
|
}
|
|
142
|
+
throw err;
|
|
141
143
|
}
|
|
142
|
-
throw lastErr;
|
|
143
|
-
}
|
|
144
|
-
catch (err) {
|
|
145
|
-
// Clean up volume if machine creation fails
|
|
146
|
-
await deleteVolume(app, token, volumeId).catch(() => { });
|
|
147
|
-
throw err;
|
|
148
144
|
}
|
|
145
|
+
throw new Error("Failed to create machine after exhausting retries");
|
|
149
146
|
}
|
|
150
147
|
/**
|
|
151
148
|
* Wait for a Fly Machine to reach the "started" state.
|