@electric-agent/studio 1.0.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/dist/bridge/daytona.d.ts +35 -0
- package/dist/bridge/daytona.d.ts.map +1 -0
- package/dist/bridge/daytona.js +146 -0
- package/dist/bridge/daytona.js.map +1 -0
- package/dist/bridge/docker-stdio.d.ts +30 -0
- package/dist/bridge/docker-stdio.d.ts.map +1 -0
- package/dist/bridge/docker-stdio.js +141 -0
- package/dist/bridge/docker-stdio.js.map +1 -0
- package/dist/bridge/hosted.d.ts +28 -0
- package/dist/bridge/hosted.d.ts.map +1 -0
- package/dist/bridge/hosted.js +113 -0
- package/dist/bridge/hosted.js.map +1 -0
- package/dist/bridge/index.d.ts +6 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +5 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/sprites.d.ts +32 -0
- package/dist/bridge/sprites.d.ts.map +1 -0
- package/dist/bridge/sprites.js +138 -0
- package/dist/bridge/sprites.js.map +1 -0
- package/dist/bridge/types.d.ts +97 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +2 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/client/assets/OpenSauceOne-Bold-BeiFYFR5.woff2 +0 -0
- package/dist/client/assets/OpenSauceOne-ExtraBold-DO6BqiNe.woff2 +0 -0
- package/dist/client/assets/OpenSauceOne-Light-NEdTeQp-.woff2 +0 -0
- package/dist/client/assets/OpenSauceOne-Medium-Cu5cjAHY.woff2 +0 -0
- package/dist/client/assets/OpenSauceOne-Regular-BivIUdzJ.woff2 +0 -0
- package/dist/client/assets/SourceCodePro-Regular-CoIbWt_c.woff2 +0 -0
- package/dist/client/assets/index-CK__1-6e.css +1 -0
- package/dist/client/assets/index-DKL-jl7t.js +241 -0
- package/dist/client/favicon.ico +0 -0
- package/dist/client/img/brand/favicon.png +0 -0
- package/dist/client/img/brand/favicon.svg +4 -0
- package/dist/client/img/brand/icon.svg +4 -0
- package/dist/client/img/brand/logo.inverse.svg +13 -0
- package/dist/client/img/brand/logo.svg +13 -0
- package/dist/client/index.html +16 -0
- package/dist/electric-api.d.ts +14 -0
- package/dist/electric-api.d.ts.map +1 -0
- package/dist/electric-api.js +70 -0
- package/dist/electric-api.js.map +1 -0
- package/dist/gate.d.ts +28 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +72 -0
- package/dist/gate.js.map +1 -0
- package/dist/git.d.ts +30 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +115 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/project-utils.d.ts +9 -0
- package/dist/project-utils.d.ts.map +1 -0
- package/dist/project-utils.js +17 -0
- package/dist/project-utils.js.map +1 -0
- package/dist/sandbox/daytona-push.d.ts +3 -0
- package/dist/sandbox/daytona-push.d.ts.map +1 -0
- package/dist/sandbox/daytona-push.js +56 -0
- package/dist/sandbox/daytona-push.js.map +1 -0
- package/dist/sandbox/daytona-registry.d.ts +41 -0
- package/dist/sandbox/daytona-registry.d.ts.map +1 -0
- package/dist/sandbox/daytona-registry.js +127 -0
- package/dist/sandbox/daytona-registry.js.map +1 -0
- package/dist/sandbox/daytona.d.ts +41 -0
- package/dist/sandbox/daytona.d.ts.map +1 -0
- package/dist/sandbox/daytona.js +282 -0
- package/dist/sandbox/daytona.js.map +1 -0
- package/dist/sandbox/docker.d.ts +29 -0
- package/dist/sandbox/docker.d.ts.map +1 -0
- package/dist/sandbox/docker.js +465 -0
- package/dist/sandbox/docker.js.map +1 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +4 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/sprites-bootstrap.d.ts +26 -0
- package/dist/sandbox/sprites-bootstrap.d.ts.map +1 -0
- package/dist/sandbox/sprites-bootstrap.js +127 -0
- package/dist/sandbox/sprites-bootstrap.js.map +1 -0
- package/dist/sandbox/sprites.d.ts +55 -0
- package/dist/sandbox/sprites.d.ts.map +1 -0
- package/dist/sandbox/sprites.js +323 -0
- package/dist/sandbox/sprites.js.map +1 -0
- package/dist/sandbox/types.d.ts +73 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +5 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1266 -0
- package/dist/server.js.map +1 -0
- package/dist/sessions.d.ts +46 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +66 -0
- package/dist/sessions.js.map +1 -0
- package/dist/streams.d.ts +34 -0
- package/dist/streams.d.ts.map +1 -0
- package/dist/streams.js +42 -0
- package/dist/streams.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Constants
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
const SANDBOX_IMAGE = "electric-agent-sandbox";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Internal helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
function findFreePort() {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const server = net.createServer();
|
|
16
|
+
server.listen(0, "127.0.0.1", () => {
|
|
17
|
+
const addr = server.address();
|
|
18
|
+
if (addr && typeof addr === "object") {
|
|
19
|
+
const port = addr.port;
|
|
20
|
+
server.close(() => resolve(port));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
server.close(() => reject(new Error("Could not determine free port")));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
server.on("error", reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function resolveAuthEnv(opts) {
|
|
30
|
+
if (opts?.apiKey)
|
|
31
|
+
return ["ANTHROPIC_API_KEY", opts.apiKey];
|
|
32
|
+
if (opts?.oauthToken)
|
|
33
|
+
return ["CLAUDE_CODE_OAUTH_TOKEN", opts.oauthToken];
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function generateComposeFile(port, auth, infra = { mode: "local" }, streamEnv = {}, deferAgentStart = false, ghToken) {
|
|
37
|
+
const isCloud = infra.mode === "cloud";
|
|
38
|
+
const agentEnv = [
|
|
39
|
+
`DATABASE_URL=${isCloud ? infra.databaseUrl : "postgresql://postgres:password@postgres:5432/electric"}`,
|
|
40
|
+
`ELECTRIC_URL=${isCloud ? infra.electricUrl : "http://electric:3000"}`,
|
|
41
|
+
"VITE_PORT=5173",
|
|
42
|
+
"SANDBOX_MODE=1",
|
|
43
|
+
];
|
|
44
|
+
if (isCloud) {
|
|
45
|
+
agentEnv.push(`ELECTRIC_SOURCE_ID=${infra.sourceId}`);
|
|
46
|
+
agentEnv.push(`ELECTRIC_SECRET=${infra.secret}`);
|
|
47
|
+
}
|
|
48
|
+
if (auth) {
|
|
49
|
+
agentEnv.push(`${auth[0]}=${auth[1]}`);
|
|
50
|
+
}
|
|
51
|
+
if (ghToken) {
|
|
52
|
+
agentEnv.push(`GH_TOKEN=${ghToken}`);
|
|
53
|
+
}
|
|
54
|
+
// Add stream env vars for hosted Durable Streams communication
|
|
55
|
+
for (const [key, value] of Object.entries(streamEnv)) {
|
|
56
|
+
agentEnv.push(`${key}=${value}`);
|
|
57
|
+
}
|
|
58
|
+
const agentEnvYaml = agentEnv.map((e) => ` - ${e}`).join("\n");
|
|
59
|
+
// When deferAgentStart is true, keep the container alive without starting the agent.
|
|
60
|
+
// The bridge will start the agent via `docker exec -i`.
|
|
61
|
+
const agentCommand = deferAgentStart
|
|
62
|
+
? '["tail", "-f", "/dev/null"]'
|
|
63
|
+
: '["electric-agent", "headless"]';
|
|
64
|
+
if (isCloud) {
|
|
65
|
+
return `services:
|
|
66
|
+
agent:
|
|
67
|
+
image: ${SANDBOX_IMAGE}
|
|
68
|
+
ports:
|
|
69
|
+
- "${port}:5173"
|
|
70
|
+
environment:
|
|
71
|
+
${agentEnvYaml}
|
|
72
|
+
volumes:
|
|
73
|
+
- workspace:/home/agent/workspace
|
|
74
|
+
stdin_open: true
|
|
75
|
+
command: ${agentCommand}
|
|
76
|
+
|
|
77
|
+
volumes:
|
|
78
|
+
workspace:
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
return `services:
|
|
82
|
+
postgres:
|
|
83
|
+
image: postgres:17
|
|
84
|
+
environment:
|
|
85
|
+
POSTGRES_DB: electric
|
|
86
|
+
POSTGRES_USER: postgres
|
|
87
|
+
POSTGRES_PASSWORD: password
|
|
88
|
+
command: postgres -c wal_level=logical -c max_replication_slots=10 -c max_wal_senders=10
|
|
89
|
+
healthcheck:
|
|
90
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
91
|
+
interval: 2s
|
|
92
|
+
timeout: 5s
|
|
93
|
+
retries: 10
|
|
94
|
+
|
|
95
|
+
electric:
|
|
96
|
+
image: electricsql/electric:latest
|
|
97
|
+
environment:
|
|
98
|
+
DATABASE_URL: postgresql://postgres:password@postgres:5432/electric
|
|
99
|
+
ELECTRIC_INSECURE: "true"
|
|
100
|
+
depends_on:
|
|
101
|
+
postgres:
|
|
102
|
+
condition: service_healthy
|
|
103
|
+
|
|
104
|
+
agent:
|
|
105
|
+
image: ${SANDBOX_IMAGE}
|
|
106
|
+
ports:
|
|
107
|
+
- "${port}:5173"
|
|
108
|
+
environment:
|
|
109
|
+
${agentEnvYaml}
|
|
110
|
+
volumes:
|
|
111
|
+
- workspace:/home/agent/workspace
|
|
112
|
+
depends_on:
|
|
113
|
+
electric:
|
|
114
|
+
condition: service_started
|
|
115
|
+
stdin_open: true
|
|
116
|
+
command: ${agentCommand}
|
|
117
|
+
|
|
118
|
+
volumes:
|
|
119
|
+
workspace:
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
async function waitForElectric(project, composePath) {
|
|
123
|
+
const start = Date.now();
|
|
124
|
+
while (Date.now() - start < 60_000) {
|
|
125
|
+
try {
|
|
126
|
+
execSync(`docker compose -p ${project} -f ${composePath} exec -T electric curl -sf http://localhost:3000/v1/health`, { stdio: "ignore", timeout: 3000 });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Not ready yet
|
|
131
|
+
}
|
|
132
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
133
|
+
}
|
|
134
|
+
throw new Error("Electric did not become healthy in time");
|
|
135
|
+
}
|
|
136
|
+
function getAgentContainerId(state) {
|
|
137
|
+
try {
|
|
138
|
+
const composePath = path.join(state.composeDir, "docker-compose.yml");
|
|
139
|
+
const id = execFileSync("docker", ["compose", "-p", state.composeProject, "-f", composePath, "ps", "-q", "agent"], { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
140
|
+
return id || null;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function execInContainer(state, command, opts) {
|
|
147
|
+
const containerId = getAgentContainerId(state);
|
|
148
|
+
if (!containerId)
|
|
149
|
+
throw new Error("No running container");
|
|
150
|
+
return execFileSync("docker", ["exec", containerId, "sh", "-c", command], {
|
|
151
|
+
encoding: "utf-8",
|
|
152
|
+
timeout: opts?.timeout ?? 30_000,
|
|
153
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// DockerSandboxProvider
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
export class DockerSandboxProvider {
|
|
160
|
+
runtime = "docker";
|
|
161
|
+
activeContainers = new Map();
|
|
162
|
+
internalState = new Map();
|
|
163
|
+
getState(handle) {
|
|
164
|
+
const state = this.internalState.get(handle.sessionId);
|
|
165
|
+
if (!state)
|
|
166
|
+
throw new Error(`No internal state for session ${handle.sessionId}`);
|
|
167
|
+
return state;
|
|
168
|
+
}
|
|
169
|
+
async create(sessionId, opts) {
|
|
170
|
+
const port = await findFreePort();
|
|
171
|
+
const slug = (opts?.projectName || sessionId.slice(0, 8))
|
|
172
|
+
.replace(/[^a-z0-9-]/gi, "-")
|
|
173
|
+
.toLowerCase();
|
|
174
|
+
const project = `ea-${slug}`;
|
|
175
|
+
const infra = opts?.infra ?? { mode: "local" };
|
|
176
|
+
console.log(`[docker] Creating sandbox: session=${sessionId} project=${project} port=${port} infra=${infra.mode}`);
|
|
177
|
+
const composeDir = fs.mkdtempSync(path.join(os.tmpdir(), `${project}-`));
|
|
178
|
+
const composePath = path.join(composeDir, "docker-compose.yml");
|
|
179
|
+
const auth = resolveAuthEnv(opts);
|
|
180
|
+
fs.writeFileSync(composePath, generateComposeFile(port, auth, infra, opts?.streamEnv ?? {}, opts?.deferAgentStart, opts?.ghToken), "utf-8");
|
|
181
|
+
console.log(`[docker] Compose file written: ${composePath}`);
|
|
182
|
+
if (infra.mode === "local") {
|
|
183
|
+
console.log(`[docker] Starting postgres + electric...`);
|
|
184
|
+
execSync(`docker compose -p ${project} -f ${composePath} up -d postgres electric`, {
|
|
185
|
+
stdio: "pipe",
|
|
186
|
+
timeout: 120_000,
|
|
187
|
+
});
|
|
188
|
+
await waitForElectric(project, composePath);
|
|
189
|
+
console.log(`[docker] Electric is ready`);
|
|
190
|
+
}
|
|
191
|
+
// Start the agent service in detached mode — it communicates via the durable stream
|
|
192
|
+
console.log(`[docker] Starting agent container...`);
|
|
193
|
+
execSync(`docker compose -p ${project} -f ${composePath} up -d agent`, {
|
|
194
|
+
stdio: "pipe",
|
|
195
|
+
timeout: 60_000,
|
|
196
|
+
});
|
|
197
|
+
console.log(`[docker] Agent container started`);
|
|
198
|
+
const handle = {
|
|
199
|
+
sessionId,
|
|
200
|
+
runtime: "docker",
|
|
201
|
+
port,
|
|
202
|
+
projectDir: `/home/agent/workspace/${opts?.projectName || sessionId.slice(0, 8)}`,
|
|
203
|
+
};
|
|
204
|
+
const state = {
|
|
205
|
+
composeDir,
|
|
206
|
+
composeProject: project,
|
|
207
|
+
};
|
|
208
|
+
this.activeContainers.set(sessionId, handle);
|
|
209
|
+
this.internalState.set(sessionId, state);
|
|
210
|
+
return handle;
|
|
211
|
+
}
|
|
212
|
+
async destroy(handle) {
|
|
213
|
+
const state = this.internalState.get(handle.sessionId);
|
|
214
|
+
this.activeContainers.delete(handle.sessionId);
|
|
215
|
+
this.internalState.delete(handle.sessionId);
|
|
216
|
+
if (!state)
|
|
217
|
+
return;
|
|
218
|
+
const composePath = path.join(state.composeDir, "docker-compose.yml");
|
|
219
|
+
try {
|
|
220
|
+
execSync(`docker compose -p ${state.composeProject} -f ${composePath} down -v --remove-orphans`, { stdio: "ignore", timeout: 30_000 });
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Best effort
|
|
224
|
+
}
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
fs.rm(state.composeDir, { recursive: true, force: true }, () => { });
|
|
227
|
+
}, 5000);
|
|
228
|
+
}
|
|
229
|
+
async restartAgent(handle) {
|
|
230
|
+
const state = this.internalState.get(handle.sessionId);
|
|
231
|
+
if (!state) {
|
|
232
|
+
throw new Error("No active container for session");
|
|
233
|
+
}
|
|
234
|
+
const composePath = path.join(state.composeDir, "docker-compose.yml");
|
|
235
|
+
// Stop and restart the agent service
|
|
236
|
+
try {
|
|
237
|
+
execSync(`docker compose -p ${state.composeProject} -f ${composePath} stop agent`, {
|
|
238
|
+
stdio: "ignore",
|
|
239
|
+
timeout: 15_000,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// May already be stopped
|
|
244
|
+
}
|
|
245
|
+
execSync(`docker compose -p ${state.composeProject} -f ${composePath} up -d agent`, {
|
|
246
|
+
stdio: "pipe",
|
|
247
|
+
timeout: 60_000,
|
|
248
|
+
});
|
|
249
|
+
const newHandle = {
|
|
250
|
+
sessionId: handle.sessionId,
|
|
251
|
+
runtime: "docker",
|
|
252
|
+
port: handle.port,
|
|
253
|
+
projectDir: handle.projectDir,
|
|
254
|
+
};
|
|
255
|
+
this.activeContainers.set(handle.sessionId, newHandle);
|
|
256
|
+
return newHandle;
|
|
257
|
+
}
|
|
258
|
+
/** Get the Docker container ID for a session's agent service */
|
|
259
|
+
getContainerId(sessionId) {
|
|
260
|
+
const state = this.internalState.get(sessionId);
|
|
261
|
+
if (!state)
|
|
262
|
+
return null;
|
|
263
|
+
return getAgentContainerId(state);
|
|
264
|
+
}
|
|
265
|
+
get(sessionId) {
|
|
266
|
+
return this.activeContainers.get(sessionId);
|
|
267
|
+
}
|
|
268
|
+
list() {
|
|
269
|
+
return [...this.activeContainers.values()];
|
|
270
|
+
}
|
|
271
|
+
isAlive(handle) {
|
|
272
|
+
const state = this.internalState.get(handle.sessionId);
|
|
273
|
+
if (!state)
|
|
274
|
+
return false;
|
|
275
|
+
const containerId = getAgentContainerId(state);
|
|
276
|
+
return containerId !== null;
|
|
277
|
+
}
|
|
278
|
+
async exec(handle, command) {
|
|
279
|
+
const state = this.getState(handle);
|
|
280
|
+
return execInContainer(state, command).trim();
|
|
281
|
+
}
|
|
282
|
+
async listFiles(handle, dir) {
|
|
283
|
+
const state = this.internalState.get(handle.sessionId);
|
|
284
|
+
if (!state)
|
|
285
|
+
return [];
|
|
286
|
+
const containerId = getAgentContainerId(state);
|
|
287
|
+
if (!containerId)
|
|
288
|
+
return [];
|
|
289
|
+
try {
|
|
290
|
+
const output = execFileSync("docker", [
|
|
291
|
+
"exec",
|
|
292
|
+
containerId,
|
|
293
|
+
"find",
|
|
294
|
+
dir,
|
|
295
|
+
"-type",
|
|
296
|
+
"f",
|
|
297
|
+
"-not",
|
|
298
|
+
"-path",
|
|
299
|
+
"*/node_modules/*",
|
|
300
|
+
"-not",
|
|
301
|
+
"-path",
|
|
302
|
+
"*/.git/*",
|
|
303
|
+
"-not",
|
|
304
|
+
"-path",
|
|
305
|
+
"*/dist/*",
|
|
306
|
+
"-not",
|
|
307
|
+
"-path",
|
|
308
|
+
"*/.next/*",
|
|
309
|
+
"-not",
|
|
310
|
+
"-path",
|
|
311
|
+
"*/.cache/*",
|
|
312
|
+
"-not",
|
|
313
|
+
"-path",
|
|
314
|
+
"*/.electric/*",
|
|
315
|
+
"-not",
|
|
316
|
+
"-name",
|
|
317
|
+
"pnpm-lock.yaml",
|
|
318
|
+
"-not",
|
|
319
|
+
"-name",
|
|
320
|
+
"package-lock.json",
|
|
321
|
+
], { encoding: "utf-8", timeout: 10_000, stdio: ["ignore", "pipe", "ignore"] });
|
|
322
|
+
return output
|
|
323
|
+
.split("\n")
|
|
324
|
+
.map((l) => l.trim())
|
|
325
|
+
.filter(Boolean);
|
|
326
|
+
}
|
|
327
|
+
catch {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async readFile(handle, filePath) {
|
|
332
|
+
const state = this.internalState.get(handle.sessionId);
|
|
333
|
+
if (!state)
|
|
334
|
+
return null;
|
|
335
|
+
const containerId = getAgentContainerId(state);
|
|
336
|
+
if (!containerId)
|
|
337
|
+
return null;
|
|
338
|
+
try {
|
|
339
|
+
return execFileSync("docker", ["exec", containerId, "cat", filePath], {
|
|
340
|
+
encoding: "utf-8",
|
|
341
|
+
timeout: 5_000,
|
|
342
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async startApp(handle) {
|
|
350
|
+
const state = this.internalState.get(handle.sessionId);
|
|
351
|
+
if (!state)
|
|
352
|
+
return false;
|
|
353
|
+
const containerId = getAgentContainerId(state);
|
|
354
|
+
if (!containerId)
|
|
355
|
+
return false;
|
|
356
|
+
try {
|
|
357
|
+
execFileSync("docker", ["exec", containerId, "sh", "-c", "cd /home/agent/workspace/*/ && pnpm dev:start"], { timeout: 10_000, stdio: "ignore" });
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async stopApp(handle) {
|
|
365
|
+
const state = this.internalState.get(handle.sessionId);
|
|
366
|
+
if (!state)
|
|
367
|
+
return false;
|
|
368
|
+
const containerId = getAgentContainerId(state);
|
|
369
|
+
if (!containerId)
|
|
370
|
+
return false;
|
|
371
|
+
try {
|
|
372
|
+
execFileSync("docker", ["exec", containerId, "sh", "-c", "cd /home/agent/workspace/*/ && pnpm dev:stop"], { timeout: 5000, stdio: "ignore" });
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async isAppRunning(handle) {
|
|
380
|
+
const state = this.internalState.get(handle.sessionId);
|
|
381
|
+
if (!state)
|
|
382
|
+
return false;
|
|
383
|
+
const containerId = getAgentContainerId(state);
|
|
384
|
+
if (!containerId)
|
|
385
|
+
return false;
|
|
386
|
+
try {
|
|
387
|
+
execFileSync("docker", [
|
|
388
|
+
"exec",
|
|
389
|
+
containerId,
|
|
390
|
+
"sh",
|
|
391
|
+
"-c",
|
|
392
|
+
"kill -0 $(cat /tmp/dev-server.pid 2>/dev/null) 2>/dev/null",
|
|
393
|
+
], { timeout: 5000, stdio: "ignore" });
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async gitStatus(handle, projectDir) {
|
|
401
|
+
const state = this.internalState.get(handle.sessionId);
|
|
402
|
+
if (!state) {
|
|
403
|
+
return {
|
|
404
|
+
initialized: false,
|
|
405
|
+
branch: null,
|
|
406
|
+
hasUncommitted: false,
|
|
407
|
+
lastCommitHash: null,
|
|
408
|
+
lastCommitMessage: null,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
const output = execInContainer(state, `cd ${projectDir} && test -d .git && echo "GIT_INIT=yes" || echo "GIT_INIT=no"`);
|
|
413
|
+
if (!output.includes("GIT_INIT=yes")) {
|
|
414
|
+
return {
|
|
415
|
+
initialized: false,
|
|
416
|
+
branch: null,
|
|
417
|
+
hasUncommitted: false,
|
|
418
|
+
lastCommitHash: null,
|
|
419
|
+
lastCommitMessage: null,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const branch = execInContainer(state, `cd ${projectDir} && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ""`).trim();
|
|
423
|
+
const hash = execInContainer(state, `cd ${projectDir} && git rev-parse HEAD 2>/dev/null || echo ""`).trim();
|
|
424
|
+
const message = execInContainer(state, `cd ${projectDir} && git log -1 --format=%s 2>/dev/null || echo ""`).trim();
|
|
425
|
+
const statusOutput = execInContainer(state, `cd ${projectDir} && git status --porcelain 2>/dev/null || echo ""`).trim();
|
|
426
|
+
return {
|
|
427
|
+
initialized: true,
|
|
428
|
+
branch: branch || null,
|
|
429
|
+
hasUncommitted: statusOutput.length > 0,
|
|
430
|
+
lastCommitHash: hash || null,
|
|
431
|
+
lastCommitMessage: message || null,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
return {
|
|
436
|
+
initialized: false,
|
|
437
|
+
branch: null,
|
|
438
|
+
hasUncommitted: false,
|
|
439
|
+
lastCommitHash: null,
|
|
440
|
+
lastCommitMessage: null,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
async createFromRepo(sessionId, repoUrl, opts) {
|
|
445
|
+
const repoName = repoUrl
|
|
446
|
+
.split("/")
|
|
447
|
+
.pop()
|
|
448
|
+
?.replace(/\.git$/, "") || "resumed-project";
|
|
449
|
+
const handle = await this.create(sessionId, {
|
|
450
|
+
apiKey: opts?.apiKey,
|
|
451
|
+
oauthToken: opts?.oauthToken,
|
|
452
|
+
ghToken: opts?.ghToken,
|
|
453
|
+
projectName: repoName,
|
|
454
|
+
});
|
|
455
|
+
const state = this.getState(handle);
|
|
456
|
+
const targetDir = `/home/agent/workspace/${repoName}`;
|
|
457
|
+
execInContainer(state, `gh repo clone "${repoUrl}" "${targetDir}" 2>/dev/null || git clone "${repoUrl}" "${targetDir}"`, { timeout: 60_000 });
|
|
458
|
+
if (opts?.branch) {
|
|
459
|
+
execInContainer(state, `cd ${targetDir} && git checkout ${opts.branch}`);
|
|
460
|
+
}
|
|
461
|
+
handle.projectDir = targetDir;
|
|
462
|
+
return handle;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
//# sourceMappingURL=docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/sandbox/docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DaytonaSandboxProvider } from "./daytona.js";
|
|
2
|
+
export { DockerSandboxProvider } from "./docker.js";
|
|
3
|
+
export { SpritesSandboxProvider } from "./sprites.js";
|
|
4
|
+
export type { CreateSandboxOpts, GitStatus, InfraConfig, SandboxHandle, SandboxProvider, SandboxRuntime, } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,YAAY,EACX,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,aAAa,EACb,eAAe,EACf,cAAc,GACd,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap logic for Sprites sandboxes.
|
|
3
|
+
*
|
|
4
|
+
* Sprites run Ubuntu 24.04 with Node.js pre-installed but no project tooling.
|
|
5
|
+
* This module installs the required tools (pnpm, electric-agent) and creates
|
|
6
|
+
* a checkpoint so subsequent sprites can restore instantly.
|
|
7
|
+
*/
|
|
8
|
+
import type { Sprite } from "@fly/sprites";
|
|
9
|
+
export interface BootstrapOptions {
|
|
10
|
+
/** Custom package URL (e.g. pkg-pr-new preview) to install instead of the published electric-agent */
|
|
11
|
+
packageUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Bootstrap a sprite by installing required global tools.
|
|
15
|
+
* This runs inside a freshly-created sprite that has Node.js but nothing else.
|
|
16
|
+
*/
|
|
17
|
+
export declare function bootstrapSprite(sprite: Sprite, opts?: BootstrapOptions): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Ensure the sprite is bootstrapped. If a matching checkpoint exists,
|
|
20
|
+
* restore from it. Otherwise, run the full bootstrap and create a checkpoint.
|
|
21
|
+
*
|
|
22
|
+
* When a custom packageUrl is provided (e.g. PR preview), the checkpoint
|
|
23
|
+
* comment includes a hash of the URL so different versions don't collide.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ensureBootstrapped(sprite: Sprite, opts?: BootstrapOptions): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=sprites-bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sprites-bootstrap.d.ts","sourceRoot":"","sources":["../../src/sandbox/sprites-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAO1C,MAAM,WAAW,gBAAgB;IAChC,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD5F;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC/F"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap logic for Sprites sandboxes.
|
|
3
|
+
*
|
|
4
|
+
* Sprites run Ubuntu 24.04 with Node.js pre-installed but no project tooling.
|
|
5
|
+
* This module installs the required tools (pnpm, electric-agent) and creates
|
|
6
|
+
* a checkpoint so subsequent sprites can restore instantly.
|
|
7
|
+
*/
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const { version } = require("../../package.json");
|
|
11
|
+
const CHECKPOINT_COMMENT = `bootstrapped:${version}`;
|
|
12
|
+
/**
|
|
13
|
+
* Bootstrap a sprite by installing required global tools.
|
|
14
|
+
* This runs inside a freshly-created sprite that has Node.js but nothing else.
|
|
15
|
+
*/
|
|
16
|
+
export async function bootstrapSprite(sprite, opts) {
|
|
17
|
+
const packageSpec = opts?.packageUrl ?? "electric-agent";
|
|
18
|
+
console.log(`[sprites-bootstrap] Installing pnpm...`);
|
|
19
|
+
await sprite.exec("npm install -g pnpm", { maxBuffer: 50 * 1024 * 1024 });
|
|
20
|
+
console.log(`[sprites-bootstrap] Installing electric-agent from: ${packageSpec}`);
|
|
21
|
+
await sprite.execFile("bash", [
|
|
22
|
+
"-c",
|
|
23
|
+
`source /etc/profile.d/npm-global.sh 2>/dev/null; npm install -g ${packageSpec}`,
|
|
24
|
+
]);
|
|
25
|
+
// Create the workspace directory structure matching other runtimes
|
|
26
|
+
await sprite.exec("mkdir -p /home/agent/workspace");
|
|
27
|
+
// Write a profile script that adds npm global bin and nvm paths to PATH.
|
|
28
|
+
// Sprites use nvm-managed Node.js — the bin dir isn't in the default PATH
|
|
29
|
+
// when running commands via sprite.execFile("bash", ["-c", ...]).
|
|
30
|
+
await sprite.execFile("bash", [
|
|
31
|
+
"-c",
|
|
32
|
+
[
|
|
33
|
+
// Source nvm if present (sets up node/npm/npx in PATH)
|
|
34
|
+
'export NVM_DIR="/.sprite/languages/node/nvm"',
|
|
35
|
+
'[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"',
|
|
36
|
+
// Get the npm global bin path and write it to a profile script
|
|
37
|
+
'NPM_BIN="$(npm config get prefix)/bin"',
|
|
38
|
+
'echo "export PATH=\\"$NPM_BIN:\\$PATH\\"" > /etc/profile.d/npm-global.sh',
|
|
39
|
+
// Also add nvm init to the profile so node/npm are always available
|
|
40
|
+
'echo "export NVM_DIR=\\"/.sprite/languages/node/nvm\\"" >> /etc/profile.d/npm-global.sh',
|
|
41
|
+
'echo "[ -s \\"\\$NVM_DIR/nvm.sh\\" ] && . \\"\\$NVM_DIR/nvm.sh\\"" >> /etc/profile.d/npm-global.sh',
|
|
42
|
+
].join(" && "),
|
|
43
|
+
]);
|
|
44
|
+
// Configure git (needed for the git agent)
|
|
45
|
+
// Use execFile to avoid sprite.exec() splitting quoted args by whitespace
|
|
46
|
+
await sprite.execFile("git", ["config", "--global", "user.name", "electric-agent"]);
|
|
47
|
+
await sprite.execFile("git", ["config", "--global", "user.email", "agent@electric-sql.com"]);
|
|
48
|
+
await sprite.execFile("git", ["config", "--global", "init.defaultBranch", "main"]);
|
|
49
|
+
// Configure gh as the git credential helper so `git push` authenticates
|
|
50
|
+
// via GH_TOKEN. Without this, HTTPS pushes fail in the sandbox because
|
|
51
|
+
// there's no interactive terminal for git to prompt for credentials.
|
|
52
|
+
// Note: gh auth setup-git only writes the credential.helper config —
|
|
53
|
+
// it doesn't need GH_TOKEN at this point. The token is read at push time.
|
|
54
|
+
await sprite.execFile("git", [
|
|
55
|
+
"config",
|
|
56
|
+
"--global",
|
|
57
|
+
"credential.helper",
|
|
58
|
+
"!gh auth git-credential",
|
|
59
|
+
]);
|
|
60
|
+
console.log(`[sprites-bootstrap] Bootstrap complete (electric-agent@${version})`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Ensure the sprite is bootstrapped. If a matching checkpoint exists,
|
|
64
|
+
* restore from it. Otherwise, run the full bootstrap and create a checkpoint.
|
|
65
|
+
*
|
|
66
|
+
* When a custom packageUrl is provided (e.g. PR preview), the checkpoint
|
|
67
|
+
* comment includes a hash of the URL so different versions don't collide.
|
|
68
|
+
*/
|
|
69
|
+
export async function ensureBootstrapped(sprite, opts) {
|
|
70
|
+
const comment = opts?.packageUrl
|
|
71
|
+
? `${CHECKPOINT_COMMENT}:${shortHash(opts.packageUrl)}`
|
|
72
|
+
: CHECKPOINT_COMMENT;
|
|
73
|
+
// Check for existing checkpoint
|
|
74
|
+
try {
|
|
75
|
+
const checkpoints = await sprite.listCheckpoints();
|
|
76
|
+
const bootstrapped = checkpoints.find((cp) => cp.comment === comment);
|
|
77
|
+
if (bootstrapped) {
|
|
78
|
+
console.log(`[sprites-bootstrap] Restoring from checkpoint "${bootstrapped.id}" (electric-agent@${version})`);
|
|
79
|
+
const response = await sprite.restoreCheckpoint(bootstrapped.id);
|
|
80
|
+
// Consume the NDJSON response stream to completion
|
|
81
|
+
await consumeStream(response);
|
|
82
|
+
console.log(`[sprites-bootstrap] Restored from checkpoint`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// No checkpoints yet — proceed with bootstrap
|
|
88
|
+
}
|
|
89
|
+
// Run full bootstrap
|
|
90
|
+
await bootstrapSprite(sprite, opts);
|
|
91
|
+
// Create checkpoint for future reuse
|
|
92
|
+
console.log(`[sprites-bootstrap] Creating checkpoint...`);
|
|
93
|
+
try {
|
|
94
|
+
const response = await sprite.createCheckpoint(comment);
|
|
95
|
+
await consumeStream(response);
|
|
96
|
+
console.log(`[sprites-bootstrap] Checkpoint created`);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
// Non-fatal — next creation will just bootstrap again
|
|
100
|
+
console.warn(`[sprites-bootstrap] Failed to create checkpoint:`, err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Consume a streaming Response body to completion */
|
|
104
|
+
async function consumeStream(response) {
|
|
105
|
+
if (!response.body)
|
|
106
|
+
return;
|
|
107
|
+
const reader = response.body.getReader();
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const { done } = await reader.read();
|
|
111
|
+
if (done)
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
reader.releaseLock();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Simple string hash for checkpoint disambiguation */
|
|
120
|
+
function shortHash(input) {
|
|
121
|
+
let hash = 0;
|
|
122
|
+
for (let i = 0; i < input.length; i++) {
|
|
123
|
+
hash = ((hash << 5) - hash + input.charCodeAt(i)) | 0;
|
|
124
|
+
}
|
|
125
|
+
return (hash >>> 0).toString(36);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=sprites-bootstrap.js.map
|