@replayio/app-building 1.3.0 → 1.5.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 +12 -14
- package/dist/container.d.ts +2 -0
- package/dist/container.js +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,15 +33,12 @@ const config: ContainerConfig = {
|
|
|
33
33
|
flyApp: envVars.FLY_APP_NAME,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
// Start a
|
|
36
|
+
// Start a detached container with an initial prompt — it will process and exit when done
|
|
37
37
|
config.detached = true;
|
|
38
|
+
config.initialPrompt = "Build the app";
|
|
38
39
|
const repo: RepoOptions = { repoUrl: "https://github.com/...", cloneBranch: "main", pushBranch: "main" };
|
|
39
40
|
const state = await startRemoteContainer(config, repo);
|
|
40
41
|
|
|
41
|
-
// Send a prompt — the container will process it and exit when done
|
|
42
|
-
const httpOpts = httpOptsFor(state);
|
|
43
|
-
const { id } = await httpPost(`${state.baseUrl}/message`, { prompt: "Build the app" }, httpOpts);
|
|
44
|
-
|
|
45
42
|
// Check status
|
|
46
43
|
const status = await httpGet(`${state.baseUrl}/status`, httpOpts);
|
|
47
44
|
|
|
@@ -58,7 +55,7 @@ await stopRemoteContainer(config, state);
|
|
|
58
55
|
|
|
59
56
|
| Export | Description |
|
|
60
57
|
|---|---|
|
|
61
|
-
| `ContainerConfig` | Interface bundling all external state: optional `projectRoot` (only needed for local Docker operations), `envVars`, `registry`, optional `flyToken`/`flyApp`/`imageRef`/`webhookUrl`/`detached`. See [Webhooks](#webhooks) and [Container lifecycle](#container-lifecycle) below. |
|
|
58
|
+
| `ContainerConfig` | Interface bundling all external state: optional `projectRoot` (only needed for local Docker operations), `envVars`, `registry`, optional `flyToken`/`flyApp`/`imageRef`/`webhookUrl`/`detached`/`initialPrompt`. See [Webhooks](#webhooks) and [Container lifecycle](#container-lifecycle) below. |
|
|
62
59
|
| `RepoOptions` | Per-invocation git settings: `repoUrl`, `cloneBranch`, `pushBranch`. |
|
|
63
60
|
| `ContainerRegistry` | Interface for container registry storage. Methods: `log`, `markStopped`, `clearStopped`, `getRecent`, `find`, `findAlive`. |
|
|
64
61
|
| `FileContainerRegistry` | Built-in file-backed implementation of `ContainerRegistry`, backed by a `.jsonl` file. |
|
|
@@ -144,9 +141,10 @@ Each container runs an HTTP server that accepts the following requests:
|
|
|
144
141
|
A container stays running and accepts messages until it receives a **detach** or **stop** signal:
|
|
145
142
|
|
|
146
143
|
- **Detached at startup** (`config.detached = true`): Set `detached` on `ContainerConfig` to start
|
|
147
|
-
the container in detached mode.
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
the container in detached mode. Use `config.initialPrompt` to provide a prompt that is queued
|
|
145
|
+
before the HTTP server starts accepting requests. The container processes the initial prompt
|
|
146
|
+
and any queued tasks, then exits cleanly. This is the preferred way to run fire-and-forget
|
|
147
|
+
jobs — no race between container startup and a subsequent `POST /message` or `POST /detach`.
|
|
150
148
|
- **Detach** (`POST /detach`): Signal a running container to exit once all in-flight and queued
|
|
151
149
|
work is done. In the CLI, interactive mode (`npm run agent -- -i`) sends `/detach` automatically
|
|
152
150
|
when the user disconnects (Ctrl+C/D).
|
|
@@ -185,14 +183,14 @@ Every POST body has this shape:
|
|
|
185
183
|
| Type | When | `data` fields |
|
|
186
184
|
|---|---|---|
|
|
187
185
|
| `container.started` | HTTP server is listening | `pushBranch`, `revision` |
|
|
188
|
-
| `container.idle` |
|
|
189
|
-
| `container.
|
|
190
|
-
| `container.
|
|
191
|
-
| `container.stopped` | State transitions to stopped | _(empty)_ |
|
|
186
|
+
| `container.idle` | Container is waiting for work | `pendingTasks`, `queueLength` |
|
|
187
|
+
| `container.stopping` | Container is shutting down | _(empty)_ |
|
|
188
|
+
| `container.stopped` | Container has stopped | _(empty)_ |
|
|
192
189
|
| `message.queued` | `POST /message` received | `messageId`, `prompt` |
|
|
190
|
+
| `message.started` | Message processing begins | `iteration`, `prompt` |
|
|
193
191
|
| `message.done` | Message processing complete | `messageId`, `cost_usd`, `duration_ms`, `num_turns` |
|
|
194
192
|
| `message.error` | Message processing failed | `messageId`, `error` |
|
|
195
|
-
| `task.started` | Task processing begins | `pendingTasks` |
|
|
193
|
+
| `task.started` | Task processing begins | `iteration`, `pendingTasks` |
|
|
196
194
|
| `task.done` | Task processing complete | `tasksProcessed`, `totalCost` |
|
|
197
195
|
| `log` | Each log line | `line` |
|
|
198
196
|
|
package/dist/container.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface ContainerConfig {
|
|
|
17
17
|
webhookUrl?: string;
|
|
18
18
|
/** Start the container in detached mode. It will exit after processing all messages and tasks. */
|
|
19
19
|
detached?: boolean;
|
|
20
|
+
/** Initial prompt to queue at container startup (before the HTTP server accepts external requests). */
|
|
21
|
+
initialPrompt?: string;
|
|
20
22
|
}
|
|
21
23
|
export interface RepoOptions {
|
|
22
24
|
repoUrl: string;
|
package/dist/container.js
CHANGED
|
@@ -4,6 +4,10 @@ import { resolve } from "path";
|
|
|
4
4
|
import { createMachine, waitForMachine, destroyMachine, listMachines } from "./fly";
|
|
5
5
|
import { getImageRef } from "./image-ref";
|
|
6
6
|
const IMAGE_NAME = "app-building";
|
|
7
|
+
function debugLog(...args) {
|
|
8
|
+
if (process.env.DEBUG)
|
|
9
|
+
console.log("[container]", ...args);
|
|
10
|
+
}
|
|
7
11
|
export function loadDotEnv(projectRoot) {
|
|
8
12
|
const envPath = resolve(projectRoot, ".env");
|
|
9
13
|
if (!existsSync(envPath)) {
|
|
@@ -90,6 +94,16 @@ function buildContainerEnv(repo, envVars, extra = {}) {
|
|
|
90
94
|
return env;
|
|
91
95
|
}
|
|
92
96
|
export async function startContainer(config, repo) {
|
|
97
|
+
debugLog("startContainer config:", {
|
|
98
|
+
projectRoot: config.projectRoot,
|
|
99
|
+
flyApp: config.flyApp,
|
|
100
|
+
imageRef: config.imageRef,
|
|
101
|
+
webhookUrl: config.webhookUrl,
|
|
102
|
+
detached: config.detached,
|
|
103
|
+
initialPrompt: config.initialPrompt ? `${config.initialPrompt.slice(0, 100)}...` : undefined,
|
|
104
|
+
envVarKeys: Object.keys(config.envVars),
|
|
105
|
+
});
|
|
106
|
+
debugLog("startContainer repo:", repo);
|
|
93
107
|
buildImage(config);
|
|
94
108
|
const uniqueId = Math.random().toString(36).slice(2, 8);
|
|
95
109
|
const containerName = `app-building-${uniqueId}`;
|
|
@@ -102,6 +116,8 @@ export async function startContainer(config, repo) {
|
|
|
102
116
|
extra.WEBHOOK_URL = config.webhookUrl;
|
|
103
117
|
if (config.detached)
|
|
104
118
|
extra.DETACHED = "1";
|
|
119
|
+
if (config.initialPrompt)
|
|
120
|
+
extra.INITIAL_PROMPT = config.initialPrompt;
|
|
105
121
|
const containerEnv = buildContainerEnv(repo, config.envVars, extra);
|
|
106
122
|
// Build docker run args
|
|
107
123
|
const args = ["run", "-d", "--rm", "--name", containerName];
|
|
@@ -162,6 +178,16 @@ export async function startContainer(config, repo) {
|
|
|
162
178
|
return agentState;
|
|
163
179
|
}
|
|
164
180
|
export async function startRemoteContainer(config, repo) {
|
|
181
|
+
debugLog("startRemoteContainer config:", {
|
|
182
|
+
projectRoot: config.projectRoot,
|
|
183
|
+
flyApp: config.flyApp,
|
|
184
|
+
imageRef: config.imageRef,
|
|
185
|
+
webhookUrl: config.webhookUrl,
|
|
186
|
+
detached: config.detached,
|
|
187
|
+
initialPrompt: config.initialPrompt ? `${config.initialPrompt.slice(0, 100)}...` : undefined,
|
|
188
|
+
envVarKeys: Object.keys(config.envVars),
|
|
189
|
+
});
|
|
190
|
+
debugLog("startRemoteContainer repo:", repo);
|
|
165
191
|
if (!config.flyToken)
|
|
166
192
|
throw new Error("flyToken is required for remote containers");
|
|
167
193
|
if (!config.flyApp)
|
|
@@ -178,6 +204,8 @@ export async function startRemoteContainer(config, repo) {
|
|
|
178
204
|
remoteExtra.WEBHOOK_URL = config.webhookUrl;
|
|
179
205
|
if (config.detached)
|
|
180
206
|
remoteExtra.DETACHED = "1";
|
|
207
|
+
if (config.initialPrompt)
|
|
208
|
+
remoteExtra.INITIAL_PROMPT = config.initialPrompt;
|
|
181
209
|
const containerEnv = buildContainerEnv(repo, config.envVars, remoteExtra);
|
|
182
210
|
// Log existing machines (but don't destroy — multiple containers may run concurrently)
|
|
183
211
|
const existing = await listMachines(config.flyApp, config.flyToken);
|