@replayio/app-building 1.31.0 → 1.33.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 +33 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,14 +74,45 @@ The secrets server spawns the command with the requested secrets in its environm
|
|
|
74
74
|
|
|
75
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.
|
|
76
76
|
|
|
77
|
+
### Allowlist mode
|
|
78
|
+
|
|
79
|
+
Set `ContainerConfig.secretAllowlist` to restrict which commands the agent may invoke via `exec-secrets`. The allowlist constrains the *target* — the agent still names which secrets to inject in each call. Each entry is `{ name, helpString, shellCommand }`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const config: ContainerConfig = {
|
|
83
|
+
...,
|
|
84
|
+
secretAllowlist: [
|
|
85
|
+
{
|
|
86
|
+
name: "neon-query",
|
|
87
|
+
helpString: "Run a SQL query against the project's Neon database. Usage: exec-secrets DATABASE_URL -- neon-query <sql>",
|
|
88
|
+
shellCommand: 'psql "$DATABASE_URL" -c "$1"',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "netlify-deploy",
|
|
92
|
+
helpString: "Deploy the app to production.",
|
|
93
|
+
shellCommand: "netlify deploy --prod --auth $NETLIFY_AUTH_TOKEN --site $NETLIFY_SITE_ID",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When configured:
|
|
100
|
+
|
|
101
|
+
- `list-secrets` returns both `secrets` (available secret names) and `allowlist` (`name — helpString` per entry).
|
|
102
|
+
- `exec-secrets` keeps the same surface: `exec-secrets <SECRET1> [SECRET2 …] -- <name> [args…]`. The named secrets are still injected into env (and only those values are redacted from output); `<name>` must match an allowlist entry, and `[args…]` become positional params (`$1`, `$2`, …) inside that entry's `shellCommand`. `$0` is `"exec-secrets"`.
|
|
103
|
+
- Targets not in the allowlist are rejected — the agent can't invoke arbitrary binaries.
|
|
104
|
+
|
|
105
|
+
`secretAllowlist` is serialized into the container as `SECRET_ALLOWLIST_JSON`; the secrets server parses it on startup. To swap or clear the allowlist on a running container without restarting, POST to `/reconfigure` (see Container HTTP API).
|
|
106
|
+
|
|
77
107
|
## Exported API
|
|
78
108
|
|
|
79
109
|
### Domain objects
|
|
80
110
|
|
|
81
111
|
| Export | Description |
|
|
82
112
|
|---|---|
|
|
83
|
-
| `ContainerConfig` | `infisical` (required `InfisicalConfig`), optional `projectRoot` (local Docker only), `registry`, `flyToken`/`flyApp` (set both for remote Fly.io), `flyGuest` (override Fly Machine guest sizing; default: 16 performance CPUs / 32 GiB), `flyVolumeSizeGb` (override Fly Volume size in GiB; default: 50), `imageRef`, `webhookUrl`/`webhookSecret`, `taskWebhookUrl` (GET endpoint for external task queue), `addTaskWebhookUrl` (POST endpoint for tasks added by `add-task` script), `detached`, `initialPrompt`, `localPort`, `absorbTasks`, `namePrefix` (default: `"app-building"`), `env` (extra env vars to inject into the container; cannot clobber package-reserved vars). |
|
|
113
|
+
| `ContainerConfig` | `infisical` (required `InfisicalConfig`), optional `projectRoot` (local Docker only), `registry`, `flyToken`/`flyApp` (set both for remote Fly.io), `flyGuest` (override Fly Machine guest sizing; default: 16 performance CPUs / 32 GiB), `flyVolumeSizeGb` (override Fly Volume size in GiB; default: 50), `imageRef`, `webhookUrl`/`webhookSecret`, `taskWebhookUrl` (GET endpoint for external task queue), `addTaskWebhookUrl` (POST endpoint for tasks added by `add-task` script), `detached`, `initialPrompt`, `localPort`, `absorbTasks`, `namePrefix` (default: `"app-building"`), `env` (extra env vars to inject into the container; cannot clobber package-reserved vars), `secretAllowlist` (curated `exec-secrets` commands — see Secrets architecture > Allowlist mode). |
|
|
84
114
|
| `FlyGuest` | Fly Machine guest spec: `cpu_kind` (`"shared"` \| `"performance"`), `cpus`, `memory_mb`. |
|
|
115
|
+
| `SecretAllowlistEntry` | `name` (verb used with `exec-secrets`), `helpString` (one-line description shown by `list-secrets`), `shellCommand` (sh script body; args become `$1`, `$2`, …). |
|
|
85
116
|
| `RepoOptions` | Per-invocation git settings: `repoUrl`, `cloneBranch`, `pushBranch`. |
|
|
86
117
|
| `AgentState` | Returned by `startContainer`. Contains `type`, `containerName`, `port`, `baseUrl`, and Fly-specific fields for remote containers. |
|
|
87
118
|
| `ContainerRegistry` | Interface for container registry storage. Methods: `log`, `markStopped`, `clearStopped`, `getRecent`, `find`, `findAlive`. |
|
|
@@ -162,6 +193,7 @@ Each container runs an HTTP server that accepts the following requests:
|
|
|
162
193
|
| `POST /detach` | | Signal the container to exit once all tasks are done. |
|
|
163
194
|
| `POST /stop` | | Force-stop the container immediately. Interrupts any running work, commits remaining changes, then exits. |
|
|
164
195
|
| `POST /interrupt` | | Kill the currently running Claude process without stopping the container. |
|
|
196
|
+
| `POST /reconfigure` | `{ secretAllowlist?: SecretAllowlistEntry[] \| null }` | Live-update container config. Omit to leave unchanged; `null` for unrestricted mode (`exec-secrets <SECRETS…> -- <cmd>` runs any binary); `[]` for restricted mode with zero entries (rejects every target); an array of entries to replace. Returns `{ ok: true }`. |
|
|
165
197
|
| `GET /status` | | Container state, queue depth, iteration count, cost, revision, etc. |
|
|
166
198
|
| `GET /events?offset=N` | | Stream of Claude events (JSON lines) since offset. |
|
|
167
199
|
| `GET /logs?offset=N` | | Stream of log lines since offset. |
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,29 @@ interface ContainerConfig {
|
|
|
125
125
|
* always take precedence — values here cannot clobber them.
|
|
126
126
|
*/
|
|
127
127
|
env?: Record<string, string>;
|
|
128
|
+
/**
|
|
129
|
+
* Allowlist of named shell commands the agent may invoke via `exec-secrets`.
|
|
130
|
+
* When set, `list-secrets` returns the allowlist (instead of raw secret
|
|
131
|
+
* names) and `exec-secrets <name> [args…]` runs the named entry's
|
|
132
|
+
* `shellCommand` with `args` mapped to positional params (`$1`, `$2`, …).
|
|
133
|
+
* All secrets are available in the command's environment; their values are
|
|
134
|
+
* redacted from output. When no allowlist is set, the container runs in
|
|
135
|
+
* unrestricted mode and `exec-secrets <SECRET…> -- <cmd>` may invoke any
|
|
136
|
+
* binary; with an allowlist set, the target after `--` must name an entry.
|
|
137
|
+
*/
|
|
138
|
+
secretAllowlist?: SecretAllowlistEntry[];
|
|
139
|
+
}
|
|
140
|
+
interface SecretAllowlistEntry {
|
|
141
|
+
/** Name the agent uses with `exec-secrets <name>` (e.g. "neon-query"). */
|
|
142
|
+
name: string;
|
|
143
|
+
/** One-line description shown by `list-secrets`. */
|
|
144
|
+
helpString: string;
|
|
145
|
+
/**
|
|
146
|
+
* Shell script body. Invoked as `sh -c <shellCommand> exec-secrets <args…>`,
|
|
147
|
+
* so caller-supplied args become `$1`, `$2`, … and `$0` is `exec-secrets`.
|
|
148
|
+
* All secrets are present in the environment.
|
|
149
|
+
*/
|
|
150
|
+
shellCommand: string;
|
|
128
151
|
}
|
|
129
152
|
interface RepoOptions {
|
|
130
153
|
repoUrl: string;
|
|
@@ -202,4 +225,4 @@ interface Task {
|
|
|
202
225
|
*/
|
|
203
226
|
declare function findReadyTask(pendingTasks: Task[], completedTasks: Pick<Task, "id" | "parentTaskId">[]): Task | null;
|
|
204
227
|
|
|
205
|
-
export { type AgentState, type ContainerConfig, type ContainerRegistry, FileContainerRegistry, type FlyGuest, 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 };
|
|
228
|
+
export { type AgentState, type ContainerConfig, type ContainerRegistry, FileContainerRegistry, type FlyGuest, type HttpOptions, type InfisicalConfig, type RegistryEntry, type RepoOptions, type SecretAllowlistEntry, type Task, buildImage, createBranchSecret, fetchBranchSecrets, fetchGlobalSecrets, fetchInfisicalSecrets, findReadyTask, getImageRef, getInfisicalConfig, httpGet, httpOptsFor, httpPost, infisicalLogin, loadDotEnv, probeAlive, spawnTestContainer, startContainer, stopContainer };
|
package/dist/index.js
CHANGED
|
@@ -253,6 +253,12 @@ function buildExtraEnv(config, containerName) {
|
|
|
253
253
|
if (config.initialPrompt) extra.INITIAL_PROMPT = config.initialPrompt;
|
|
254
254
|
if (config.absorbTasks) extra.ABSORB_TASKS = "1";
|
|
255
255
|
if (config.agentCommand) extra.AGENT_COMMAND = config.agentCommand;
|
|
256
|
+
if (config.env && Object.keys(config.env).length > 0) {
|
|
257
|
+
extra.AGENT_ENV_PASSTHROUGH = Object.keys(config.env).join(",");
|
|
258
|
+
}
|
|
259
|
+
if (config.secretAllowlist !== void 0) {
|
|
260
|
+
extra.SECRET_ALLOWLIST_JSON = JSON.stringify(config.secretAllowlist);
|
|
261
|
+
}
|
|
256
262
|
return extra;
|
|
257
263
|
}
|
|
258
264
|
function isRemote(config) {
|