@replayio/app-building 1.18.0 → 1.19.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 CHANGED
@@ -15,12 +15,11 @@ import {
15
15
  loadDotEnv,
16
16
  FileContainerRegistry,
17
17
  getInfisicalConfig,
18
- createMachine,
19
- destroyMachine,
18
+ startContainer,
19
+ stopContainer,
20
20
  type ContainerConfig,
21
21
  type RepoOptions,
22
22
  httpGet,
23
- httpPost,
24
23
  httpOptsFor,
25
24
  } from "@replayio/app-building";
26
25
 
@@ -28,36 +27,41 @@ import {
28
27
  const orchestrationVars = loadDotEnv("/path/to/project");
29
28
  const infisicalConfig = await getInfisicalConfig(orchestrationVars);
30
29
 
30
+ // Local container (no flyToken/flyApp)
31
31
  const config: ContainerConfig = {
32
- projectRoot: "/path/to/project", // optional — only needed for local Docker operations
32
+ projectRoot: "/path/to/project",
33
33
  infisical: infisicalConfig,
34
34
  registry: new FileContainerRegistry("/path/to/.container-registry.jsonl"),
35
+ };
36
+
37
+ // Remote container (set flyToken + flyApp)
38
+ const remoteConfig: ContainerConfig = {
39
+ ...config,
35
40
  flyToken: orchestrationVars.FLY_API_TOKEN,
36
41
  flyApp: orchestrationVars.FLY_APP_NAME,
37
42
  };
38
43
 
39
- // Create a Fly machine (automatically provisions a volume)
40
- const { machineId, volumeId } = await createMachine(
41
- config.flyApp, config.flyToken, imageRef, containerEnv, machineName,
42
- );
44
+ // Start automatically chooses local or remote based on config
45
+ const repo: RepoOptions = { repoUrl: "https://...", cloneBranch: "main", pushBranch: "feature/x" };
46
+ const state = await startContainer(config, repo);
43
47
 
44
48
  // Check status
45
- const status = await httpGet(`https://${config.flyApp}.fly.dev/status`);
49
+ const status = await httpGet(`${state.baseUrl}/status`, httpOptsFor(state));
46
50
 
47
51
  // Query the registry
48
52
  const alive = await config.registry.findAlive();
49
53
 
50
- // Clean up (destroys machine and its volume)
51
- await destroyMachine(config.flyApp, config.flyToken, machineId, volumeId);
54
+ // Stop handles both local and remote
55
+ await stopContainer(config, state);
52
56
  ```
53
57
 
54
58
  ## Secrets architecture
55
59
 
56
60
  Secrets are never passed directly to the container or agent. Instead:
57
61
 
58
- 1. The orchestration host passes **Infisical credentials** (token, project ID, environment) to the container.
59
- 2. At startup, the container fetches all secrets from Infisical and stores them in memory.
60
- 3. A **secrets server** (`127.0.0.1:9119`) runs inside the container, accessible only locally.
62
+ 1. The orchestration host passes **Infisical credentials** (`InfisicalConfig`) to the container via `ContainerConfig.infisical`.
63
+ 2. At startup, the container fetches global secrets from Infisical for internal use (clone token, agent API key).
64
+ 3. A **secrets server** (`127.0.0.1:9119`) runs inside the container, accessible only locally. It fetches secrets live from Infisical on every request — no caching.
61
65
  4. The agent process runs with a **restricted environment** — only `ANTHROPIC_API_KEY` (required for the Claude CLI) is present.
62
66
  5. When the agent needs to run a command that requires secrets, it uses `exec-secrets`:
63
67
 
@@ -66,9 +70,9 @@ exec-secrets NEON_API_KEY -- curl -s -H "Authorization: Bearer $NEON_API_KEY" ht
66
70
  exec-secrets NETLIFY_AUTH_TOKEN NETLIFY_ACCOUNT_SLUG -- netlify deploy --prod
67
71
  ```
68
72
 
69
- The secrets server spawns the command with the requested secrets in its environment and **redacts all secret values** from the output.
73
+ The secrets server spawns the command with the requested secrets in its environment and **redacts requested secret values** from the output.
70
74
 
71
- The agent can also run `list-secrets` to see which secrets are available, and `set-branch-secret` to store new branch-level secrets (e.g., `DATABASE_URL` created at deploy time). The server rejects values that have already appeared in logs.
75
+ The agent can also run `list-secrets` to see which secrets are available, and `set-branch-secret` to store new branch-level secrets (e.g., `DATABASE_URL` created at deploy time). The server rejects credential values that have already appeared in logs.
72
76
 
73
77
  ## Exported API
74
78
 
@@ -76,8 +80,9 @@ The agent can also run `list-secrets` to see which secrets are available, and `s
76
80
 
77
81
  | Export | Description |
78
82
  |---|---|
79
- | `ContainerConfig` | Interface bundling all external state: `infisical` (required `InfisicalConfig`), optional `projectRoot` (only needed for local Docker operations), `registry`, optional `flyToken`/`flyApp`/`imageRef`/`webhookUrl`/`webhookSecret`/`detached`/`initialPrompt`/`localPort`/`absorbTasks`. See [Webhooks](#webhooks) and [Container lifecycle](#container-lifecycle) below. |
83
+ | `ContainerConfig` | `infisical` (required `InfisicalConfig`), optional `projectRoot` (local Docker only), `registry`, `flyToken`/`flyApp` (set both for remote Fly.io), `imageRef`, `webhookUrl`/`webhookSecret`, `detached`, `initialPrompt`, `localPort`, `absorbTasks`. |
80
84
  | `RepoOptions` | Per-invocation git settings: `repoUrl`, `cloneBranch`, `pushBranch`. |
85
+ | `AgentState` | Returned by `startContainer`. Contains `type`, `containerName`, `port`, `baseUrl`, and Fly-specific fields for remote containers. |
81
86
  | `ContainerRegistry` | Interface for container registry storage. Methods: `log`, `markStopped`, `clearStopped`, `getRecent`, `find`, `findAlive`. |
82
87
  | `FileContainerRegistry` | Built-in file-backed implementation of `ContainerRegistry`, backed by a `.jsonl` file. |
83
88
 
@@ -85,14 +90,12 @@ The agent can also run `list-secrets` to see which secrets are available, and `s
85
90
 
86
91
  | Export | Description |
87
92
  |---|---|
88
- | `startContainer(config, repo)` | Build the Docker image locally and start a container with `--network host`. Returns `AgentState`. |
89
- | `stopContainer(config, containerName)` | Stop a local Docker container by name. |
90
- | `buildImage(config)` | Build the Docker image locally (called automatically by `startContainer`). |
91
- | `spawnTestContainer(config)` | Start an interactive (`-it`) container with the repo mounted at `/repo`. |
93
+ | `startContainer(config, repo)` | Start a container. Uses local Docker if `flyToken`/`flyApp` are not set, Fly.io if they are. Returns `AgentState`. |
94
+ | `stopContainer(config, state)` | Stop a container by its `AgentState` or `RegistryEntry`. Handles both local and remote. |
95
+ | `buildImage(config)` | Build the Docker image locally (called automatically by `startContainer` for local containers). |
96
+ | `spawnTestContainer(config)` | Start an interactive (`-it`) local container with the repo mounted at `/repo`. |
92
97
  | `loadDotEnv(projectRoot)` | Parse a `.env` file and return key-value pairs. |
93
98
 
94
- **Types:** `AgentState`, `ContainerConfig`, `RepoOptions`
95
-
96
99
  ### Container registry (`ContainerRegistry` interface / `FileContainerRegistry` class)
97
100
 
98
101
  | Method | Description |
@@ -122,20 +125,6 @@ The agent can also run `list-secrets` to see which secrets are available, and `s
122
125
  | `httpOptsFor(state)` | Return `HttpOptions` for a container (adds `fly-force-instance-id` header for remote containers). |
123
126
  | `probeAlive(entry)` | Check if a container is responding to `/status`. |
124
127
 
125
- ### Fly.io utilities
126
-
127
- | Export | Description |
128
- |---|---|
129
- | `createApp(token, name, org?)` | Create a Fly app and allocate IPs. |
130
- | `createMachine(app, token, image, env, name)` | Create a Fly machine with a 50GB volume mounted at `/repo`. Returns `{ machineId, volumeId }`. |
131
- | `waitForMachine(app, token, machineId)` | Poll until a machine reaches `started` state. |
132
- | `listMachines(app, token)` | List all machines for an app. |
133
- | `destroyMachine(app, token, machineId, volumeId?)` | Force-destroy a machine and optionally its volume. |
134
- | `listVolumes(app, token)` | List all volumes for an app. |
135
- | `deleteVolume(app, token, volumeId)` | Delete a Fly volume. |
136
-
137
- **Types:** `FlyMachineInfo`, `FlyVolumeInfo`, `CreateMachineResult`
138
-
139
128
  ### Secrets (Infisical)
140
129
 
141
130
  | Export | Description |
@@ -144,25 +133,11 @@ The agent can also run `list-secrets` to see which secrets are available, and `s
144
133
  | `getInfisicalConfig(envVars)` | Extract `InfisicalConfig` from env vars and log in. Requires `INFISICAL_CLIENT_ID`, `INFISICAL_CLIENT_SECRET`, `INFISICAL_PROJECT_ID`, `INFISICAL_ENVIRONMENT`. |
145
134
  | `fetchGlobalSecrets(config)` | Fetch secrets from the `/global/` path. |
146
135
  | `fetchBranchSecrets(config, branch)` | Fetch secrets from `/branches/<branch>/`. |
147
- | `createBranchSecret(config, branch, name, value)` | Create or update a secret in `/branches/<branch>/`. |
136
+ | `createBranchSecret(config, branch, name, value)` | Create or update a secret in `/branches/<branch>/`. Creates the folder if needed. |
148
137
  | `fetchInfisicalSecrets(config, path)` | Raw fetch from any Infisical folder path. |
149
138
 
150
139
  **Types:** `InfisicalConfig`
151
140
 
152
- **Usage pattern** (orchestration scripts):
153
-
154
- ```ts
155
- const orchestrationVars = loadDotEnv(projectRoot);
156
- const infisicalConfig = await getInfisicalConfig(orchestrationVars);
157
-
158
- const config: ContainerConfig = {
159
- infisical: infisicalConfig,
160
- flyToken: orchestrationVars.FLY_API_TOKEN,
161
- flyApp: orchestrationVars.FLY_APP_NAME,
162
- ...
163
- };
164
- ```
165
-
166
141
  ### Image ref
167
142
 
168
143
  | Export | Description |
@@ -1,4 +1,4 @@
1
- import type { ContainerRegistry } from "./container-registry";
1
+ import type { ContainerRegistry, RegistryEntry } from "./container-registry";
2
2
  import type { InfisicalConfig } from "./secrets";
3
3
  export interface AgentState {
4
4
  type: "local" | "remote";
@@ -35,6 +35,16 @@ export interface RepoOptions {
35
35
  }
36
36
  export declare function loadDotEnv(projectRoot: string): Record<string, string>;
37
37
  export declare function buildImage(config: ContainerConfig): void;
38
+ /**
39
+ * Start a container (local Docker or remote Fly.io based on config).
40
+ * If flyToken and flyApp are set, starts remotely; otherwise locally.
41
+ */
38
42
  export declare function startContainer(config: ContainerConfig, repo: RepoOptions): Promise<AgentState>;
39
- export declare function stopContainer(config: ContainerConfig, containerName: string): void;
43
+ /**
44
+ * Stop a container by its state or registry entry.
45
+ */
46
+ export declare function stopContainer(config: ContainerConfig, state: AgentState | RegistryEntry): Promise<void>;
47
+ /**
48
+ * Spawn an interactive test container (local only).
49
+ */
40
50
  export declare function spawnTestContainer(config: ContainerConfig): Promise<void>;
package/dist/container.js CHANGED
@@ -1,6 +1,8 @@
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";
5
+ import { getImageRef } from "./image-ref";
4
6
  const IMAGE_NAME = "app-building";
5
7
  function debugLog(...args) {
6
8
  if (process.env.DEBUG)
@@ -73,13 +75,6 @@ function findFreePort() {
73
75
  }
74
76
  return port;
75
77
  }
76
- function infisicalEnvVars(config) {
77
- return {
78
- INFISICAL_TOKEN: config.token,
79
- INFISICAL_PROJECT_ID: config.projectId,
80
- INFISICAL_ENVIRONMENT: config.environment,
81
- };
82
- }
83
78
  function buildContainerEnv(repo, infisical, extra = {}) {
84
79
  const env = {
85
80
  REPO_URL: repo.repoUrl,
@@ -90,7 +85,9 @@ function buildContainerEnv(repo, infisical, extra = {}) {
90
85
  GIT_COMMITTER_NAME: "App Builder",
91
86
  GIT_COMMITTER_EMAIL: "app-builder@localhost",
92
87
  PLAYWRIGHT_BROWSERS_PATH: "/opt/playwright",
93
- ...infisicalEnvVars(infisical),
88
+ INFISICAL_TOKEN: infisical.token,
89
+ INFISICAL_PROJECT_ID: infisical.projectId,
90
+ INFISICAL_ENVIRONMENT: infisical.environment,
94
91
  ...extra,
95
92
  };
96
93
  if (process.env.DEBUG) {
@@ -98,23 +95,9 @@ function buildContainerEnv(repo, infisical, extra = {}) {
98
95
  }
99
96
  return env;
100
97
  }
101
- export async function startContainer(config, repo) {
102
- debugLog("startContainer config:", {
103
- projectRoot: config.projectRoot,
104
- flyApp: config.flyApp,
105
- imageRef: config.imageRef,
106
- webhookUrl: config.webhookUrl,
107
- detached: config.detached,
108
- initialPrompt: config.initialPrompt ? `${config.initialPrompt.slice(0, 100)}...` : undefined,
109
- });
110
- debugLog("startContainer repo:", repo);
111
- buildImage(config);
112
- const uniqueId = Math.random().toString(36).slice(2, 8);
113
- const containerName = `app-building-${uniqueId}`;
114
- const containerPort = 3000;
115
- const hostPort = config.localPort ?? findFreePort();
98
+ function buildExtraEnv(config, containerName) {
116
99
  const extra = {
117
- PORT: String(containerPort),
100
+ PORT: "3000",
118
101
  CONTAINER_NAME: containerName,
119
102
  };
120
103
  if (config.webhookUrl)
@@ -127,35 +110,44 @@ export async function startContainer(config, repo) {
127
110
  extra.INITIAL_PROMPT = config.initialPrompt;
128
111
  if (config.absorbTasks)
129
112
  extra.ABSORB_TASKS = "1";
113
+ return extra;
114
+ }
115
+ function isRemote(config) {
116
+ return !!(config.flyToken && config.flyApp);
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Local container
120
+ // ---------------------------------------------------------------------------
121
+ async function startLocalContainer(config, repo) {
122
+ buildImage(config);
123
+ const uniqueId = Math.random().toString(36).slice(2, 8);
124
+ const containerName = `app-building-${uniqueId}`;
125
+ const containerPort = 3000;
126
+ const hostPort = config.localPort ?? findFreePort();
127
+ const extra = buildExtraEnv(config, containerName);
128
+ extra.PORT = String(containerPort);
130
129
  const containerEnv = buildContainerEnv(repo, config.infisical, extra);
131
- // Build docker run args
132
130
  const args = ["run", "-d", "--rm", "--name", containerName];
133
- // Use explicit port mapping for macOS Docker Desktop compatibility
134
- // (--network host only works on Linux)
135
131
  args.push("-p", `${hostPort}:${containerPort}`);
136
132
  for (const [k, v] of Object.entries(containerEnv)) {
137
133
  args.push("--env", `${k}=${v}`);
138
134
  }
139
- // Image name — CMD is baked in (server.ts)
140
135
  args.push(IMAGE_NAME);
141
136
  const containerId = execFileSync("docker", args, {
142
137
  encoding: "utf-8",
143
138
  timeout: 30000,
144
139
  }).trim();
145
140
  console.log(`Container started: ${containerId.slice(0, 12)} (${containerName})`);
146
- // Wait for the HTTP server to become ready
147
141
  const baseUrl = `http://127.0.0.1:${hostPort}`;
148
- const maxWait = 120000; // clone can take a while
142
+ const maxWait = 120000;
149
143
  const interval = 1000;
150
144
  const start = Date.now();
151
145
  let ready = false;
152
146
  while (Date.now() - start < maxWait) {
153
- // Check if the container is still alive (--rm removes it on exit)
154
147
  try {
155
148
  execFileSync("docker", ["inspect", "--format", "{{.State.Running}}", containerName], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 5000 });
156
149
  }
157
150
  catch {
158
- // Container is gone — grab logs from docker if possible, otherwise just report
159
151
  let logs = "";
160
152
  try {
161
153
  logs = execFileSync("docker", ["logs", "--tail", "30", containerName], {
@@ -183,26 +175,151 @@ export async function startContainer(config, repo) {
183
175
  if (!ready) {
184
176
  throw new Error("Container did not become ready within timeout");
185
177
  }
186
- const agentState = { type: "local", containerName, port: hostPort, baseUrl };
187
- config.registry.log(agentState);
188
- return agentState;
178
+ return { type: "local", containerName, port: hostPort, baseUrl };
189
179
  }
190
- export function stopContainer(config, containerName) {
180
+ function stopLocalContainer(containerName) {
191
181
  try {
192
182
  execFileSync("docker", ["stop", containerName], { stdio: "ignore", timeout: 30000 });
193
183
  }
194
184
  catch {
195
185
  // Container may already be stopped
196
186
  }
197
- config.registry.markStopped(containerName);
198
187
  }
188
+ // ---------------------------------------------------------------------------
189
+ // Remote container (Fly.io)
190
+ // ---------------------------------------------------------------------------
191
+ async function startRemoteContainerImpl(config, repo) {
192
+ if (!config.flyToken)
193
+ throw new Error("flyToken is required for remote containers");
194
+ if (!config.flyApp)
195
+ throw new Error("flyApp is required for remote containers");
196
+ const imageRef = config.imageRef ?? getImageRef();
197
+ const uniqueId = Math.random().toString(36).slice(2, 8);
198
+ const machineName = `app-building-${uniqueId}`;
199
+ const extra = buildExtraEnv(config, machineName);
200
+ const containerEnv = buildContainerEnv(repo, config.infisical, extra);
201
+ const existing = await listMachines(config.flyApp, config.flyToken);
202
+ if (existing.length > 0) {
203
+ console.log(`${existing.length} existing machine(s) in ${config.flyApp}:`);
204
+ for (const m of existing) {
205
+ console.log(` ${m.id} (${m.name}) — ${m.state}`);
206
+ }
207
+ }
208
+ console.log("Creating Fly machine (with volume)...");
209
+ let machineId = "";
210
+ let volumeId = "";
211
+ for (let attempt = 0; attempt < 5; attempt++) {
212
+ try {
213
+ const result = await createMachine(config.flyApp, config.flyToken, imageRef, containerEnv, machineName);
214
+ machineId = result.machineId;
215
+ volumeId = result.volumeId;
216
+ break;
217
+ }
218
+ catch (err) {
219
+ const msg = err instanceof Error ? err.message : String(err);
220
+ if (msg.includes("MANIFEST_UNKNOWN") && attempt < 4) {
221
+ console.log("Image not yet available in registry, retrying in 5s...");
222
+ await new Promise((r) => setTimeout(r, 5000));
223
+ continue;
224
+ }
225
+ throw err;
226
+ }
227
+ }
228
+ console.log(`Machine created: ${machineId} (volume: ${volumeId})`);
229
+ const baseUrl = `https://${config.flyApp}.fly.dev`;
230
+ const agentState = {
231
+ type: "remote",
232
+ containerName: machineName,
233
+ port: 443,
234
+ baseUrl,
235
+ flyApp: config.flyApp,
236
+ flyMachineId: machineId,
237
+ flyVolumeId: volumeId,
238
+ };
239
+ console.log("Waiting for machine to start...");
240
+ await waitForMachine(config.flyApp, config.flyToken, machineId);
241
+ console.log("Machine started.");
242
+ const maxWait = 180000;
243
+ const interval = 2000;
244
+ const start = Date.now();
245
+ let ready = false;
246
+ while (Date.now() - start < maxWait) {
247
+ try {
248
+ const res = await fetch(`${baseUrl}/status`, {
249
+ headers: { "fly-force-instance-id": machineId },
250
+ });
251
+ if (res.ok) {
252
+ ready = true;
253
+ break;
254
+ }
255
+ }
256
+ catch {
257
+ // Not ready yet
258
+ }
259
+ await new Promise((r) => setTimeout(r, interval));
260
+ }
261
+ if (!ready) {
262
+ console.log("Timed out waiting for machine, destroying...");
263
+ await destroyMachine(config.flyApp, config.flyToken, machineId, volumeId).catch(() => { });
264
+ throw new Error("Remote container did not become ready within timeout");
265
+ }
266
+ return agentState;
267
+ }
268
+ async function stopRemoteContainerImpl(config, state) {
269
+ if (!state.flyApp || !state.flyMachineId) {
270
+ throw new Error("Missing flyApp or flyMachineId in agent state");
271
+ }
272
+ if (!config.flyToken)
273
+ throw new Error("flyToken is required to stop remote container");
274
+ console.log(`Destroying Fly machine ${state.flyMachineId}...`);
275
+ await destroyMachine(state.flyApp, config.flyToken, state.flyMachineId, state.flyVolumeId);
276
+ console.log("Machine destroyed.");
277
+ }
278
+ // ---------------------------------------------------------------------------
279
+ // Public API
280
+ // ---------------------------------------------------------------------------
281
+ /**
282
+ * Start a container (local Docker or remote Fly.io based on config).
283
+ * If flyToken and flyApp are set, starts remotely; otherwise locally.
284
+ */
285
+ export async function startContainer(config, repo) {
286
+ debugLog("startContainer config:", {
287
+ projectRoot: config.projectRoot,
288
+ flyApp: config.flyApp,
289
+ imageRef: config.imageRef,
290
+ webhookUrl: config.webhookUrl,
291
+ detached: config.detached,
292
+ remote: isRemote(config),
293
+ initialPrompt: config.initialPrompt ? `${config.initialPrompt.slice(0, 100)}...` : undefined,
294
+ });
295
+ debugLog("startContainer repo:", repo);
296
+ const state = isRemote(config)
297
+ ? await startRemoteContainerImpl(config, repo)
298
+ : await startLocalContainer(config, repo);
299
+ config.registry.log(state);
300
+ return state;
301
+ }
302
+ /**
303
+ * Stop a container by its state or registry entry.
304
+ */
305
+ export async function stopContainer(config, state) {
306
+ if (state.type === "remote") {
307
+ await stopRemoteContainerImpl(config, state);
308
+ }
309
+ else {
310
+ stopLocalContainer(state.containerName);
311
+ }
312
+ config.registry.markStopped(state.containerName);
313
+ }
314
+ /**
315
+ * Spawn an interactive test container (local only).
316
+ */
199
317
  export function spawnTestContainer(config) {
200
318
  if (!config.projectRoot)
201
319
  throw new Error("projectRoot is required for local Docker operations");
202
320
  ensureImageExists(config.projectRoot);
203
321
  const uniqueId = Math.random().toString(36).slice(2, 8);
204
322
  const containerName = `app-building-test-${uniqueId}`;
205
- const infisicalVars = infisicalEnvVars(config.infisical);
206
323
  const args = ["run", "-it", "--rm", "--name", containerName];
207
324
  args.push("-v", `${config.projectRoot}:/repo`);
208
325
  args.push("-w", "/repo");
@@ -210,9 +327,9 @@ export function spawnTestContainer(config) {
210
327
  args.push("--user", `${process.getuid()}:${process.getgid()}`);
211
328
  args.push("--env", "HOME=/repo/.agent-home");
212
329
  args.push("--env", "PLAYWRIGHT_BROWSERS_PATH=/opt/playwright");
213
- for (const [k, v] of Object.entries(infisicalVars)) {
214
- args.push("--env", `${k}=${v}`);
215
- }
330
+ args.push("--env", `INFISICAL_TOKEN=${config.infisical.token}`);
331
+ args.push("--env", `INFISICAL_PROJECT_ID=${config.infisical.projectId}`);
332
+ args.push("--env", `INFISICAL_ENVIRONMENT=${config.infisical.environment}`);
216
333
  args.push(IMAGE_NAME, "bash");
217
334
  return new Promise((resolvePromise, reject) => {
218
335
  const child = spawn("docker", args, { stdio: "inherit" });
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./fly";
2
1
  export * from "./container";
3
2
  export * from "./container-registry";
4
3
  export * from "./container-utils";
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./fly";
2
1
  export * from "./container";
3
2
  export * from "./container-registry";
4
3
  export * from "./container-utils";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replayio/app-building",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "Library for managing agentic app-building containers",
5
5
  "type": "module",
6
6
  "exports": {