@replayio/app-building 1.25.0 → 1.27.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
@@ -149,7 +149,7 @@ The agent can also run `list-secrets` to see which secrets are available, and `s
149
149
  | Export | Description |
150
150
  |---|---|
151
151
  | `findReadyTask(pendingTasks, completedTasks)` | Find the first task in `pendingTasks` whose `requiredTaskIds` are all satisfied. A required task is satisfied when it appears in `completedTasks` and has no children still pending or with incomplete children of their own (recursive). Returns the task, or `null` if all are blocked. |
152
- | `TaskInfo` | Interface with `id?`, `requiredTaskIds?`, `parentTaskId?` the minimum fields needed for dependency resolution. |
152
+ | `Task` | Interface for task queue entries — includes `skill`, `subtasks`, `timestamp`, and optional fields like `id`, `requiredTaskIds`, `parentTaskId`, `command`, etc. |
153
153
 
154
154
  ## Container HTTP API
155
155
 
@@ -215,8 +215,9 @@ when the required task has completed **and** all of its child tasks (tasks with
215
215
  `parentTaskId`) have also completed. This means a task that depends on a parent will
216
216
  automatically wait for all work the parent spawned.
217
217
 
218
- By default, `add-task` chains tasks serially — each task depends on the previous one. Use
219
- `--parallel` to add tasks that can run in any order:
218
+ By default, `add-task` chains tasks serially — each task depends on the previous one. Set
219
+ `"parallel": true` on individual tasks to run them concurrently. Non-parallel tasks act as
220
+ barriers:
220
221
 
221
222
  ```bash
222
223
  # Serial (default): B waits for A, C waits for B
@@ -224,9 +225,14 @@ npx tsx /repo/scripts/add-task.ts <<'EOF'
224
225
  [{ "skill": "...", "subtasks": ["A"] }, { "skill": "...", "subtasks": ["B"] }, { "skill": "...", "subtasks": ["C"] }]
225
226
  EOF
226
227
 
227
- # Parallel: A, B, C can run in any order
228
- npx tsx /repo/scripts/add-task.ts --parallel <<'EOF'
229
- [{ "skill": "...", "subtasks": ["A"] }, { "skill": "...", "subtasks": ["B"] }, { "skill": "...", "subtasks": ["C"] }]
228
+ # Mixed: A runs first, B and C run in parallel, D waits for both B and C
229
+ npx tsx /repo/scripts/add-task.ts <<'EOF'
230
+ [
231
+ { "skill": "...", "subtasks": ["A"] },
232
+ { "skill": "...", "parallel": true, "subtasks": ["B"] },
233
+ { "skill": "...", "parallel": true, "subtasks": ["C"] },
234
+ { "skill": "...", "subtasks": ["D"] }
235
+ ]
230
236
  EOF
231
237
  ```
232
238
 
@@ -285,8 +291,8 @@ Every POST body has this shape:
285
291
  | `message.started` | Message processing begins | `iteration`, `prompt` |
286
292
  | `message.done` | Message processing complete | `messageId`, `cost_usd`, `duration_ms`, `num_turns` |
287
293
  | `message.error` | Message processing failed | `messageId`, `error` |
288
- | `task.started` | Task processing begins | `id`, `iteration`, `skill`, `subtasks`, `prompt` |
289
- | `task.done` | Task processing complete | `id`, `skill`, `subtasks`, `prompt`, `cost`, `totalCost`, `failed`, `pendingTasks`, `duration_ms` |
294
+ | `task.started` | Task processing begins | All `Task` fields (`id`, `skill`, `subtasks`, `timestamp`, `app`, `prompt`, `command`, `maxAttempts`, `timeoutMinutes`, `setup`, `requiredTaskIds`, `parentTaskId`) + `iteration` |
295
+ | `task.done` | Task processing complete | All `Task` fields + `cost`, `totalCost`, `failed`, `pendingTasks`, `duration_ms` |
290
296
  | `log` | Each log line | `line` |
291
297
 
292
298
  ### Example
package/dist/index.d.ts CHANGED
@@ -1,7 +1,188 @@
1
- export * from "./container";
2
- export * from "./container-registry";
3
- export * from "./container-utils";
4
- export * from "./http-client";
5
- export * from "./image-ref";
6
- export * from "./secrets";
7
- export * from "./tasks";
1
+ interface RegistryEntry extends AgentState {
2
+ startedAt: string;
3
+ stoppedAt?: string;
4
+ }
5
+ interface ContainerRegistry {
6
+ log(state: AgentState): void;
7
+ markStopped(containerName?: string): void;
8
+ clearStopped(containerName: string): void;
9
+ getRecent(limit?: number): RegistryEntry[];
10
+ find(containerName: string): RegistryEntry | null;
11
+ findAlive(): Promise<RegistryEntry[]>;
12
+ }
13
+ declare class FileContainerRegistry implements ContainerRegistry {
14
+ private filePath;
15
+ constructor(filePath: string);
16
+ private readRegistry;
17
+ private updateEntry;
18
+ log(state: AgentState): void;
19
+ markStopped(containerName?: string): void;
20
+ clearStopped(containerName: string): void;
21
+ getRecent(limit?: number): RegistryEntry[];
22
+ find(containerName: string): RegistryEntry | null;
23
+ findAlive(): Promise<RegistryEntry[]>;
24
+ }
25
+
26
+ /**
27
+ * Infisical secrets API.
28
+ *
29
+ * API versions used (per https://infisical.com/docs/api-reference):
30
+ * - Auth: POST /api/v1/auth/universal-auth/login
31
+ * - Secrets: GET/POST/PATCH /api/v4/secrets[/{secretName}]
32
+ * - Folders: POST /api/v2/folders
33
+ */
34
+ interface InfisicalConfig {
35
+ token: string;
36
+ projectId: string;
37
+ environment: string;
38
+ }
39
+ /**
40
+ * Log in to Infisical using Universal Auth (Client ID + Client Secret).
41
+ * POST /api/v1/auth/universal-auth/login
42
+ * Returns a short-lived access token.
43
+ */
44
+ declare function infisicalLogin(clientId: string, clientSecret: string): Promise<string>;
45
+ /**
46
+ * Fetch secrets from an Infisical folder path.
47
+ * GET /api/v4/secrets?projectId=…&environment=…&secretPath=…
48
+ * Returns a key→value record.
49
+ */
50
+ declare function fetchInfisicalSecrets(config: InfisicalConfig, secretPath: string): Promise<Record<string, string>>;
51
+ /**
52
+ * Fetch global build secrets from `/global/`.
53
+ */
54
+ declare function fetchGlobalSecrets(config: InfisicalConfig): Promise<Record<string, string>>;
55
+ /**
56
+ * Fetch per-branch deployment secrets from `/branches/<branch>/`.
57
+ */
58
+ declare function fetchBranchSecrets(config: InfisicalConfig, branch: string): Promise<Record<string, string>>;
59
+ /**
60
+ * Create or update a branch secret in Infisical.
61
+ * Creates the folder path if it doesn't exist yet.
62
+ *
63
+ * POST /api/v4/secrets/{name} — create
64
+ * PATCH /api/v4/secrets/{name} — update (if secret already exists)
65
+ *
66
+ * Body: { projectId, environment, secretPath, secretValue, type }
67
+ */
68
+ declare function createBranchSecret(config: InfisicalConfig, branch: string, name: string, value: string): Promise<void>;
69
+ /**
70
+ * Extract Infisical config from environment variables and log in.
71
+ * Reads INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_PROJECT_ID,
72
+ * and INFISICAL_ENVIRONMENT from the env vars.
73
+ * Throws if any required var is missing.
74
+ */
75
+ declare function getInfisicalConfig(envVars: Record<string, string>): Promise<InfisicalConfig>;
76
+
77
+ interface AgentState {
78
+ type: "local" | "remote";
79
+ containerName: string;
80
+ port: number;
81
+ baseUrl: string;
82
+ flyApp?: string;
83
+ flyMachineId?: string;
84
+ flyVolumeId?: string;
85
+ }
86
+ interface ContainerConfig {
87
+ projectRoot?: string;
88
+ /** Infisical credentials — required for all containers. */
89
+ infisical: InfisicalConfig;
90
+ registry: ContainerRegistry;
91
+ flyToken?: string;
92
+ flyApp?: string;
93
+ imageRef?: string;
94
+ webhookUrl?: string;
95
+ webhookSecret?: string;
96
+ /** GET endpoint to fetch the next task when the local queue is empty. Uses webhookSecret for auth. */
97
+ taskWebhookUrl?: string;
98
+ /** POST endpoint to receive tasks added by the add-task script. When set, add-task POSTs tasks here instead of writing to the local task file. Uses webhookSecret for auth. */
99
+ addTaskWebhookUrl?: string;
100
+ /** Start the container in detached mode. It will exit after processing all messages and tasks. */
101
+ detached?: boolean;
102
+ /** Initial prompt to queue at container startup (before the HTTP server accepts external requests). */
103
+ initialPrompt?: string;
104
+ /** Override the host port for local containers (default: auto-selected). */
105
+ localPort?: number;
106
+ /** Absorb task files from other containers at startup. Default: false. */
107
+ absorbTasks?: boolean;
108
+ /** Container name prefix. Default: "app-building". */
109
+ namePrefix?: string;
110
+ /** Agent command override (e.g. "codex", "gemini"). Default: "claude". */
111
+ agentCommand?: string;
112
+ }
113
+ interface RepoOptions {
114
+ repoUrl: string;
115
+ cloneBranch: string;
116
+ pushBranch: string;
117
+ }
118
+ declare function loadDotEnv(projectRoot: string): Record<string, string>;
119
+ declare function buildImage(config: ContainerConfig): void;
120
+ /**
121
+ * Start a container (local Docker or remote Fly.io based on config).
122
+ * If flyToken and flyApp are set, starts remotely; otherwise locally.
123
+ */
124
+ declare function startContainer(config: ContainerConfig, repo: RepoOptions): Promise<AgentState>;
125
+ /**
126
+ * Stop a container by its state or registry entry.
127
+ */
128
+ declare function stopContainer(config: ContainerConfig, state: AgentState | RegistryEntry): Promise<void>;
129
+ /**
130
+ * Spawn an interactive test container (local only).
131
+ */
132
+ declare function spawnTestContainer(config: ContainerConfig): Promise<void>;
133
+
134
+ interface HttpOptions {
135
+ timeout?: number;
136
+ headers?: Record<string, string>;
137
+ }
138
+ declare function httpGet(url: string, opts?: HttpOptions): Promise<any>;
139
+ declare function httpPost(url: string, body?: unknown, opts?: HttpOptions): Promise<any>;
140
+
141
+ declare function httpOptsFor(state: AgentState): HttpOptions;
142
+ declare function probeAlive(entry: RegistryEntry): Promise<boolean>;
143
+
144
+ declare function getImageRef(): string;
145
+
146
+ /**
147
+ * Task dependency resolution helpers.
148
+ *
149
+ * Pure functions that take task arrays and return ready tasks — no file I/O.
150
+ */
151
+ interface Task {
152
+ skill: string;
153
+ subtasks: string[];
154
+ timestamp: string;
155
+ app?: string;
156
+ /** Optional ID for coordination (e.g. from task webhook). Included in task.started/task.done events. */
157
+ id?: string;
158
+ /** Raw prompt for message-derived tasks (no skill file). */
159
+ prompt?: string;
160
+ /** Custom command (agent + args) to run instead of the default "claude" with extraArgs. */
161
+ command?: string;
162
+ /** Maximum number of attempts before giving up. Default: 5. */
163
+ maxAttempts?: number;
164
+ /** Maximum time in minutes for each attempt. Agent is killed if exceeded. */
165
+ timeoutMinutes?: number;
166
+ /** Shell command to run once when the task starts (before the first agent attempt). */
167
+ setup?: string;
168
+ /** Task IDs that must complete before this task can start. */
169
+ requiredTaskIds?: string[];
170
+ /** The ID of the task that spawned this task via add-task. */
171
+ parentTaskId?: string;
172
+ }
173
+ /**
174
+ * Find the first task in `pendingTasks` whose dependencies are all satisfied.
175
+ * Returns the task, or null if all tasks are blocked.
176
+ *
177
+ * A required task is "fully complete" when:
178
+ * 1. It appears in `completedTasks`, AND
179
+ * 2. Every task in `completedTasks` that has it as `parentTaskId` is also
180
+ * fully complete (recursive — children's children must also be done), AND
181
+ * 3. No task in `pendingTasks` has it as `parentTaskId`.
182
+ *
183
+ * @param pendingTasks - Tasks waiting to run (ordered by priority).
184
+ * @param completedTasks - Tasks that have finished (need id and parentTaskId).
185
+ */
186
+ declare function findReadyTask(pendingTasks: Task[], completedTasks: Pick<Task, "id" | "parentTaskId">[]): Task | null;
187
+
188
+ export { type AgentState, type ContainerConfig, type ContainerRegistry, FileContainerRegistry, type HttpOptions, type InfisicalConfig, type RegistryEntry, type RepoOptions, type Task, buildImage, createBranchSecret, fetchBranchSecrets, fetchGlobalSecrets, fetchInfisicalSecrets, findReadyTask, getImageRef, getInfisicalConfig, httpGet, httpOptsFor, httpPost, infisicalLogin, loadDotEnv, probeAlive, spawnTestContainer, startContainer, stopContainer };