@electric-agent/studio 1.0.0 → 1.1.1
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/active-sessions.d.ts +28 -0
- package/dist/active-sessions.d.ts.map +1 -0
- package/dist/active-sessions.js +50 -0
- package/dist/active-sessions.js.map +1 -0
- package/dist/bridge/claude-code-docker.d.ts +74 -0
- package/dist/bridge/claude-code-docker.d.ts.map +1 -0
- package/dist/bridge/claude-code-docker.js +305 -0
- package/dist/bridge/claude-code-docker.js.map +1 -0
- package/dist/bridge/claude-code-sprites.d.ts +64 -0
- package/dist/bridge/claude-code-sprites.d.ts.map +1 -0
- package/dist/bridge/claude-code-sprites.js +293 -0
- package/dist/bridge/claude-code-sprites.js.map +1 -0
- package/dist/bridge/claude-md-generator.d.ts +24 -0
- package/dist/bridge/claude-md-generator.d.ts.map +1 -0
- package/dist/bridge/claude-md-generator.js +303 -0
- package/dist/bridge/claude-md-generator.js.map +1 -0
- package/dist/bridge/index.d.ts +3 -0
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/index.js +3 -0
- package/dist/bridge/index.js.map +1 -1
- package/dist/bridge/stream-json-parser.d.ts +30 -0
- package/dist/bridge/stream-json-parser.d.ts.map +1 -0
- package/dist/bridge/stream-json-parser.js +207 -0
- package/dist/bridge/stream-json-parser.js.map +1 -0
- package/dist/client/assets/index-BeZ6CTGd.css +1 -0
- package/dist/client/assets/index-DRLXdDNp.js +241 -0
- package/dist/client/index.html +2 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/project-utils.d.ts +2 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +2 -6
- package/dist/project-utils.js.map +1 -1
- package/dist/registry.d.ts +52 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +204 -0
- package/dist/registry.js.map +1 -0
- package/dist/room-registry.d.ts +40 -0
- package/dist/room-registry.d.ts.map +1 -0
- package/dist/room-registry.js +112 -0
- package/dist/room-registry.js.map +1 -0
- package/dist/sandbox/sprites-bootstrap.d.ts.map +1 -1
- package/dist/sandbox/sprites-bootstrap.js +7 -1
- package/dist/sandbox/sprites-bootstrap.js.map +1 -1
- package/dist/sandbox/sprites.d.ts +5 -0
- package/dist/sandbox/sprites.d.ts.map +1 -1
- package/dist/sandbox/sprites.js +22 -2
- package/dist/sandbox/sprites.js.map +1 -1
- package/dist/server.d.ts +9 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +625 -58
- package/dist/server.js.map +1 -1
- package/dist/sessions.d.ts +2 -0
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js.map +1 -1
- package/dist/shared-sessions.d.ts +16 -0
- package/dist/shared-sessions.d.ts.map +1 -0
- package/dist/shared-sessions.js +52 -0
- package/dist/shared-sessions.js.map +1 -0
- package/dist/streams.d.ts +8 -0
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +22 -0
- package/dist/streams.js.map +1 -1
- package/package.json +15 -2
- package/dist/client/assets/index-CK__1-6e.css +0 -1
- package/dist/client/assets/index-DKL-jl7t.js +0 -241
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SessionInfo } from "./sessions.js";
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight in-memory session store for the current server lifetime.
|
|
4
|
+
*
|
|
5
|
+
* Sessions are private to each user (stored in their browser's localStorage).
|
|
6
|
+
* The server only tracks active sessions for sandbox/bridge management.
|
|
7
|
+
* This store is NOT persisted — it resets on server restart.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ActiveSessions {
|
|
10
|
+
private sessions;
|
|
11
|
+
private transcriptToSession;
|
|
12
|
+
add(session: SessionInfo): void;
|
|
13
|
+
get(id: string): SessionInfo | undefined;
|
|
14
|
+
update(id: string, update: Partial<SessionInfo>): void;
|
|
15
|
+
delete(id: string): boolean;
|
|
16
|
+
/** Check if a session exists in the active store. */
|
|
17
|
+
has(id: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Look up the EA session ID for a Claude Code transcript path.
|
|
20
|
+
* Returns undefined if no mapping exists or the session has been deleted.
|
|
21
|
+
*/
|
|
22
|
+
getByTranscript(transcriptPath: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Map a transcript path to an EA session ID.
|
|
25
|
+
*/
|
|
26
|
+
mapTranscript(transcriptPath: string, sessionId: string): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=active-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-sessions.d.ts","sourceRoot":"","sources":["../src/active-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,mBAAmB,CAA4B;IAEvD,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAI/B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIxC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAStD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI3B,qDAAqD;IACrD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAMxB;;;OAGG;IACH,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM3D;;OAEG;IACH,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAG9D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight in-memory session store for the current server lifetime.
|
|
3
|
+
*
|
|
4
|
+
* Sessions are private to each user (stored in their browser's localStorage).
|
|
5
|
+
* The server only tracks active sessions for sandbox/bridge management.
|
|
6
|
+
* This store is NOT persisted — it resets on server restart.
|
|
7
|
+
*/
|
|
8
|
+
export class ActiveSessions {
|
|
9
|
+
sessions = new Map();
|
|
10
|
+
transcriptToSession = new Map();
|
|
11
|
+
add(session) {
|
|
12
|
+
this.sessions.set(session.id, session);
|
|
13
|
+
}
|
|
14
|
+
get(id) {
|
|
15
|
+
return this.sessions.get(id);
|
|
16
|
+
}
|
|
17
|
+
update(id, update) {
|
|
18
|
+
const session = this.sessions.get(id);
|
|
19
|
+
if (session) {
|
|
20
|
+
Object.assign(session, update, {
|
|
21
|
+
lastActiveAt: new Date().toISOString(),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
delete(id) {
|
|
26
|
+
return this.sessions.delete(id);
|
|
27
|
+
}
|
|
28
|
+
/** Check if a session exists in the active store. */
|
|
29
|
+
has(id) {
|
|
30
|
+
return this.sessions.has(id);
|
|
31
|
+
}
|
|
32
|
+
// --- Transcript → Session Mapping (for Claude Code hook integration) ---
|
|
33
|
+
/**
|
|
34
|
+
* Look up the EA session ID for a Claude Code transcript path.
|
|
35
|
+
* Returns undefined if no mapping exists or the session has been deleted.
|
|
36
|
+
*/
|
|
37
|
+
getByTranscript(transcriptPath) {
|
|
38
|
+
const sessionId = this.transcriptToSession.get(transcriptPath);
|
|
39
|
+
if (!sessionId)
|
|
40
|
+
return undefined;
|
|
41
|
+
return this.sessions.has(sessionId) ? sessionId : undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Map a transcript path to an EA session ID.
|
|
45
|
+
*/
|
|
46
|
+
mapTranscript(transcriptPath, sessionId) {
|
|
47
|
+
this.transcriptToSession.set(transcriptPath, sessionId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=active-sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-sessions.js","sourceRoot":"","sources":["../src/active-sessions.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,cAAc;IAClB,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAA;IACzC,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEvD,GAAG,CAAC,OAAoB;QACvB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACvC,CAAC;IAED,GAAG,CAAC,EAAU;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,MAAM,CAAC,EAAU,EAAE,MAA4B;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrC,IAAI,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE;gBAC9B,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC,CAAA;QACH,CAAC;IACF,CAAC;IAED,MAAM,CAAC,EAAU;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAChC,CAAC;IAED,qDAAqD;IACrD,GAAG,CAAC,EAAU;QACb,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,0EAA0E;IAE1E;;;OAGG;IACH,eAAe,CAAC,cAAsB;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAC9D,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAA;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;IAC5D,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,cAAsB,EAAE,SAAiB;QACtD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,EAAE,SAAS,CAAC,CAAA;IACxD,CAAC;CACD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionBridge implementation that runs Claude Code CLI inside a Docker
|
|
3
|
+
* container via `docker exec -i`, communicating via stream-json NDJSON.
|
|
4
|
+
*
|
|
5
|
+
* The bridge translates Claude Code's stream-json output into EngineEvents
|
|
6
|
+
* and writes them to the Durable Stream for the UI. User messages and
|
|
7
|
+
* gate responses are sent to Claude Code's stdin.
|
|
8
|
+
*
|
|
9
|
+
* Claude Code runs in one-shot mode (`-p`) and exits after completing.
|
|
10
|
+
* On iterate (follow-up message), the bridge respawns Claude Code with
|
|
11
|
+
* `--resume <sessionId>` so it picks up the previous conversation context.
|
|
12
|
+
*/
|
|
13
|
+
import type { EngineEvent } from "@electric-agent/protocol";
|
|
14
|
+
import type { StreamConnectionInfo } from "../streams.js";
|
|
15
|
+
import type { SessionBridge } from "./types.js";
|
|
16
|
+
export interface ClaudeCodeDockerConfig {
|
|
17
|
+
/** Initial prompt (the user's app description or task) */
|
|
18
|
+
prompt: string;
|
|
19
|
+
/** Working directory inside the container */
|
|
20
|
+
cwd: string;
|
|
21
|
+
/** Model to use (default: claude-sonnet-4-6) */
|
|
22
|
+
model?: string;
|
|
23
|
+
/** Allowed tools (default: all standard tools) */
|
|
24
|
+
allowedTools?: string[];
|
|
25
|
+
/** Additional CLI flags */
|
|
26
|
+
extraFlags?: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare class ClaudeCodeDockerBridge implements SessionBridge {
|
|
29
|
+
readonly sessionId: string;
|
|
30
|
+
readonly streamUrl: string;
|
|
31
|
+
readonly streamHeaders: Record<string, string>;
|
|
32
|
+
private containerId;
|
|
33
|
+
private config;
|
|
34
|
+
private writer;
|
|
35
|
+
private parser;
|
|
36
|
+
private agentEventCallbacks;
|
|
37
|
+
private completeCallbacks;
|
|
38
|
+
private closed;
|
|
39
|
+
private proc;
|
|
40
|
+
/** Claude Code session ID captured from stream-json system.init — used for --resume */
|
|
41
|
+
private claudeSessionId;
|
|
42
|
+
/** Whether a Claude Code process is currently running */
|
|
43
|
+
private running;
|
|
44
|
+
/** Whether the parser already emitted a session_end (from a "result" message) */
|
|
45
|
+
private resultReceived;
|
|
46
|
+
constructor(sessionId: string, connection: StreamConnectionInfo, containerId: string, config: ClaudeCodeDockerConfig);
|
|
47
|
+
emit(event: EngineEvent): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Send a follow-up user message to Claude Code by respawning with --resume.
|
|
50
|
+
* Used for iteration requests (the user types a new message in the UI).
|
|
51
|
+
*/
|
|
52
|
+
sendCommand(cmd: Record<string, unknown>): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Send a gate response back to Claude Code as a user message.
|
|
55
|
+
* For ask_user_question gates, the user's answer becomes a follow-up message.
|
|
56
|
+
*/
|
|
57
|
+
sendGateResponse(gate: string, value: Record<string, unknown>): Promise<void>;
|
|
58
|
+
onAgentEvent(cb: (event: EngineEvent) => void): void;
|
|
59
|
+
onComplete(cb: (success: boolean) => void): void;
|
|
60
|
+
start(): Promise<void>;
|
|
61
|
+
close(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Spawn a new Claude Code process. Called for both the initial prompt
|
|
64
|
+
* and follow-up iterate messages (with --resume).
|
|
65
|
+
*/
|
|
66
|
+
private spawnClaude;
|
|
67
|
+
private handleLine;
|
|
68
|
+
private dispatchEvent;
|
|
69
|
+
/**
|
|
70
|
+
* Write a user message to Claude Code's stdin in stream-json format.
|
|
71
|
+
*/
|
|
72
|
+
private writeUserMessage;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=claude-code-docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-docker.d.ts","sourceRoot":"","sources":["../../src/bridge/claude-code-docker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAE9D,MAAM,WAAW,sBAAsB;IACtC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAcD,qBAAa,sBAAuB,YAAW,aAAa;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE9C,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,mBAAmB,CAA0C;IACrE,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,IAAI,CAA4B;IAExC,uFAAuF;IACvF,OAAO,CAAC,eAAe,CAAsB;IAC7C,yDAAyD;IACzD,OAAO,CAAC,OAAO,CAAQ;IACvB,iFAAiF;IACjF,OAAO,CAAC,cAAc,CAAQ;gBAG7B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,oBAAoB,EAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,sBAAsB;IAezB,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAc9D;;;OAGG;IACG,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCnF,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIpD,UAAU,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAI1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,KAAK,IAAI,IAAI;IAiBb;;;OAGG;IACH,OAAO,CAAC,WAAW;IA2GnB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,aAAa;IAiDrB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAQxB"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionBridge implementation that runs Claude Code CLI inside a Docker
|
|
3
|
+
* container via `docker exec -i`, communicating via stream-json NDJSON.
|
|
4
|
+
*
|
|
5
|
+
* The bridge translates Claude Code's stream-json output into EngineEvents
|
|
6
|
+
* and writes them to the Durable Stream for the UI. User messages and
|
|
7
|
+
* gate responses are sent to Claude Code's stdin.
|
|
8
|
+
*
|
|
9
|
+
* Claude Code runs in one-shot mode (`-p`) and exits after completing.
|
|
10
|
+
* On iterate (follow-up message), the bridge respawns Claude Code with
|
|
11
|
+
* `--resume <sessionId>` so it picks up the previous conversation context.
|
|
12
|
+
*/
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import * as readline from "node:readline";
|
|
15
|
+
import { DurableStream } from "@durable-streams/client";
|
|
16
|
+
import { ts } from "@electric-agent/protocol";
|
|
17
|
+
import { createStreamJsonParser } from "./stream-json-parser.js";
|
|
18
|
+
const DEFAULT_ALLOWED_TOOLS = [
|
|
19
|
+
"Read",
|
|
20
|
+
"Write",
|
|
21
|
+
"Edit",
|
|
22
|
+
"Bash",
|
|
23
|
+
"Glob",
|
|
24
|
+
"Grep",
|
|
25
|
+
"WebSearch",
|
|
26
|
+
"TodoWrite",
|
|
27
|
+
"AskUserQuestion",
|
|
28
|
+
];
|
|
29
|
+
export class ClaudeCodeDockerBridge {
|
|
30
|
+
sessionId;
|
|
31
|
+
streamUrl;
|
|
32
|
+
streamHeaders;
|
|
33
|
+
containerId;
|
|
34
|
+
config;
|
|
35
|
+
writer;
|
|
36
|
+
parser = createStreamJsonParser();
|
|
37
|
+
agentEventCallbacks = [];
|
|
38
|
+
completeCallbacks = [];
|
|
39
|
+
closed = false;
|
|
40
|
+
proc = null;
|
|
41
|
+
/** Claude Code session ID captured from stream-json system.init — used for --resume */
|
|
42
|
+
claudeSessionId = null;
|
|
43
|
+
/** Whether a Claude Code process is currently running */
|
|
44
|
+
running = false;
|
|
45
|
+
/** Whether the parser already emitted a session_end (from a "result" message) */
|
|
46
|
+
resultReceived = false;
|
|
47
|
+
constructor(sessionId, connection, containerId, config) {
|
|
48
|
+
this.sessionId = sessionId;
|
|
49
|
+
this.streamUrl = connection.url;
|
|
50
|
+
this.streamHeaders = connection.headers;
|
|
51
|
+
this.containerId = containerId;
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.writer = new DurableStream({
|
|
54
|
+
url: connection.url,
|
|
55
|
+
headers: connection.headers,
|
|
56
|
+
contentType: "application/json",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async emit(event) {
|
|
60
|
+
if (this.closed)
|
|
61
|
+
return;
|
|
62
|
+
const msg = { source: "server", ...event };
|
|
63
|
+
await this.writer.append(JSON.stringify(msg));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Send a follow-up user message to Claude Code by respawning with --resume.
|
|
67
|
+
* Used for iteration requests (the user types a new message in the UI).
|
|
68
|
+
*/
|
|
69
|
+
async sendCommand(cmd) {
|
|
70
|
+
if (this.closed)
|
|
71
|
+
return;
|
|
72
|
+
// For iteration: respawn Claude Code with --resume
|
|
73
|
+
if (cmd.command === "iterate" && typeof cmd.request === "string") {
|
|
74
|
+
this.spawnClaude(cmd.request, this.claudeSessionId ?? undefined);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// For initial "new" command: this is handled in start() via the prompt
|
|
78
|
+
// Other commands are ignored — Claude Code doesn't understand them
|
|
79
|
+
console.log(`[claude-code-docker] Ignoring unsupported command: ${cmd.command}`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Send a gate response back to Claude Code as a user message.
|
|
83
|
+
* For ask_user_question gates, the user's answer becomes a follow-up message.
|
|
84
|
+
*/
|
|
85
|
+
async sendGateResponse(gate, value) {
|
|
86
|
+
if (this.closed || !this.proc?.stdin?.writable)
|
|
87
|
+
return;
|
|
88
|
+
if (gate === "ask_user_question" || gate.startsWith("ask_user_question:")) {
|
|
89
|
+
const answer = value.answer || "";
|
|
90
|
+
this.writeUserMessage(answer);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (gate === "clarification") {
|
|
94
|
+
const answers = value.answers;
|
|
95
|
+
if (answers?.length) {
|
|
96
|
+
this.writeUserMessage(answers.join("\n"));
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (gate === "approval") {
|
|
101
|
+
const decision = value.decision || "approve";
|
|
102
|
+
this.writeUserMessage(decision);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (gate === "continue") {
|
|
106
|
+
const proceed = value.proceed;
|
|
107
|
+
this.writeUserMessage(proceed ? "continue" : "stop");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Generic: send the value as JSON
|
|
111
|
+
this.writeUserMessage(JSON.stringify(value));
|
|
112
|
+
}
|
|
113
|
+
onAgentEvent(cb) {
|
|
114
|
+
this.agentEventCallbacks.push(cb);
|
|
115
|
+
}
|
|
116
|
+
onComplete(cb) {
|
|
117
|
+
this.completeCallbacks.push(cb);
|
|
118
|
+
}
|
|
119
|
+
async start() {
|
|
120
|
+
if (this.closed)
|
|
121
|
+
return;
|
|
122
|
+
this.spawnClaude(this.config.prompt);
|
|
123
|
+
}
|
|
124
|
+
close() {
|
|
125
|
+
this.closed = true;
|
|
126
|
+
if (this.proc) {
|
|
127
|
+
try {
|
|
128
|
+
this.proc.stdin?.end();
|
|
129
|
+
this.proc.kill("SIGTERM");
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Process may already be dead
|
|
133
|
+
}
|
|
134
|
+
this.proc = null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// -----------------------------------------------------------------------
|
|
138
|
+
// Private helpers
|
|
139
|
+
// -----------------------------------------------------------------------
|
|
140
|
+
/**
|
|
141
|
+
* Spawn a new Claude Code process. Called for both the initial prompt
|
|
142
|
+
* and follow-up iterate messages (with --resume).
|
|
143
|
+
*/
|
|
144
|
+
spawnClaude(prompt, resumeSessionId) {
|
|
145
|
+
// Kill any existing process
|
|
146
|
+
if (this.proc) {
|
|
147
|
+
try {
|
|
148
|
+
this.proc.stdin?.end();
|
|
149
|
+
this.proc.kill("SIGTERM");
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Already dead
|
|
153
|
+
}
|
|
154
|
+
this.proc = null;
|
|
155
|
+
}
|
|
156
|
+
// Reset parser state for the new process
|
|
157
|
+
this.parser = createStreamJsonParser();
|
|
158
|
+
this.resultReceived = false;
|
|
159
|
+
this.running = true;
|
|
160
|
+
const allowedTools = this.config.allowedTools ?? DEFAULT_ALLOWED_TOOLS;
|
|
161
|
+
const model = this.config.model ?? "claude-sonnet-4-6";
|
|
162
|
+
// Build the claude CLI command
|
|
163
|
+
const claudeArgs = [
|
|
164
|
+
"-p",
|
|
165
|
+
prompt,
|
|
166
|
+
"--output-format",
|
|
167
|
+
"stream-json",
|
|
168
|
+
"--verbose",
|
|
169
|
+
"--model",
|
|
170
|
+
model,
|
|
171
|
+
"--dangerously-skip-permissions",
|
|
172
|
+
"--allowedTools",
|
|
173
|
+
allowedTools.join(","),
|
|
174
|
+
...(this.config.extraFlags ?? []),
|
|
175
|
+
];
|
|
176
|
+
// Add --resume if we have a previous session ID
|
|
177
|
+
if (resumeSessionId) {
|
|
178
|
+
claudeArgs.push("--resume", resumeSessionId);
|
|
179
|
+
}
|
|
180
|
+
// Escape for bash
|
|
181
|
+
const escapedArgs = claudeArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
|
|
182
|
+
const cmd = `cd '${this.config.cwd}' && claude ${escapedArgs}`;
|
|
183
|
+
// Note: do NOT use -i flag — Claude Code detects interactive stdin and blocks
|
|
184
|
+
// waiting for input even when -p is provided. Without -i, stdout flows normally.
|
|
185
|
+
this.proc = spawn("docker", ["exec", this.containerId, "bash", "-c", cmd], {
|
|
186
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
187
|
+
});
|
|
188
|
+
console.log(`[claude-code-docker] Started: session=${this.sessionId} container=${this.containerId} pid=${this.proc.pid} resume=${resumeSessionId ?? "none"}`);
|
|
189
|
+
console.log(`[claude-code-docker] cmd: ${cmd}`);
|
|
190
|
+
const currentProc = this.proc;
|
|
191
|
+
// Read stdout line by line (stream-json NDJSON)
|
|
192
|
+
if (currentProc.stdout) {
|
|
193
|
+
const rl = readline.createInterface({
|
|
194
|
+
input: currentProc.stdout,
|
|
195
|
+
terminal: false,
|
|
196
|
+
});
|
|
197
|
+
rl.on("line", (line) => {
|
|
198
|
+
if (this.closed)
|
|
199
|
+
return;
|
|
200
|
+
console.log(`[claude-code-docker:stdout] ${line.slice(0, 120)}...`);
|
|
201
|
+
this.handleLine(line);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// Log stderr
|
|
205
|
+
if (currentProc.stderr) {
|
|
206
|
+
const stderrRl = readline.createInterface({
|
|
207
|
+
input: currentProc.stderr,
|
|
208
|
+
terminal: false,
|
|
209
|
+
});
|
|
210
|
+
stderrRl.on("line", (line) => {
|
|
211
|
+
if (!this.closed) {
|
|
212
|
+
console.error(`[claude-code-docker:stderr] ${line}`);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Handle process exit
|
|
217
|
+
currentProc.on("exit", (code) => {
|
|
218
|
+
console.log(`[claude-code-docker] Process exited: code=${code} session=${this.sessionId}`);
|
|
219
|
+
// Capture session ID from parser state before marking not running
|
|
220
|
+
if (this.parser.state.sessionId) {
|
|
221
|
+
this.claudeSessionId = this.parser.state.sessionId;
|
|
222
|
+
}
|
|
223
|
+
this.running = false;
|
|
224
|
+
// Only emit session_end from exit handler if the parser didn't already
|
|
225
|
+
// emit one (via a "result" message). This prevents double session_end.
|
|
226
|
+
if (!this.closed && !this.resultReceived) {
|
|
227
|
+
const endEvent = {
|
|
228
|
+
type: "session_end",
|
|
229
|
+
success: code === 0,
|
|
230
|
+
ts: ts(),
|
|
231
|
+
};
|
|
232
|
+
this.dispatchEvent(endEvent);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
handleLine(line) {
|
|
237
|
+
const trimmed = line.trim();
|
|
238
|
+
if (!trimmed)
|
|
239
|
+
return;
|
|
240
|
+
const events = this.parser.parse(trimmed);
|
|
241
|
+
for (const event of events) {
|
|
242
|
+
this.dispatchEvent(event);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
dispatchEvent(event) {
|
|
246
|
+
// Write to Durable Stream for UI
|
|
247
|
+
const msg = { source: "agent", ...event };
|
|
248
|
+
this.writer.append(JSON.stringify(msg)).catch(() => { });
|
|
249
|
+
// Track session_end from result messages to prevent duplicates
|
|
250
|
+
if (event.type === "session_end") {
|
|
251
|
+
this.resultReceived = true;
|
|
252
|
+
}
|
|
253
|
+
// Detect dev:start in Bash tool_use → emit app_ready for the UI preview
|
|
254
|
+
if (event.type === "pre_tool_use" && event.tool_name === "Bash") {
|
|
255
|
+
const cmd = event.tool_input?.command;
|
|
256
|
+
if (typeof cmd === "string" && /\bdev:start\b/.test(cmd)) {
|
|
257
|
+
const appReady = { type: "app_ready", ts: ts() };
|
|
258
|
+
const appReadyMsg = { source: "agent", ...appReady };
|
|
259
|
+
this.writer.append(JSON.stringify(appReadyMsg)).catch(() => { });
|
|
260
|
+
for (const cb of this.agentEventCallbacks) {
|
|
261
|
+
try {
|
|
262
|
+
cb(appReady);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// Swallow
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Dispatch to callbacks
|
|
271
|
+
for (const cb of this.agentEventCallbacks) {
|
|
272
|
+
try {
|
|
273
|
+
cb(event);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// Swallow callback errors
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Detect session_end
|
|
280
|
+
if (event.type === "session_end" && "success" in event) {
|
|
281
|
+
const success = event.success;
|
|
282
|
+
for (const cb of this.completeCallbacks) {
|
|
283
|
+
try {
|
|
284
|
+
cb(success);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// Swallow callback errors
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Write a user message to Claude Code's stdin in stream-json format.
|
|
294
|
+
*/
|
|
295
|
+
writeUserMessage(content) {
|
|
296
|
+
if (!this.proc?.stdin?.writable)
|
|
297
|
+
return;
|
|
298
|
+
const msg = JSON.stringify({
|
|
299
|
+
type: "user",
|
|
300
|
+
message: { role: "user", content },
|
|
301
|
+
});
|
|
302
|
+
this.proc.stdin.write(`${msg}\n`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=claude-code-docker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-docker.js","sourceRoot":"","sources":["../../src/bridge/claude-code-docker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionBridge implementation that runs Claude Code CLI inside a Sprites
|
|
3
|
+
* sandbox via the Sprites SDK session API, communicating via stream-json NDJSON.
|
|
4
|
+
*
|
|
5
|
+
* The bridge translates Claude Code's stream-json output into EngineEvents
|
|
6
|
+
* and writes them to the Durable Stream for the UI. User messages and
|
|
7
|
+
* gate responses are sent to Claude Code's stdin.
|
|
8
|
+
*
|
|
9
|
+
* Claude Code runs in one-shot mode (`-p`) and exits after completing.
|
|
10
|
+
* On iterate (follow-up message), the bridge respawns Claude Code with
|
|
11
|
+
* `--resume <sessionId>` so it picks up the previous conversation context.
|
|
12
|
+
*/
|
|
13
|
+
import type { EngineEvent } from "@electric-agent/protocol";
|
|
14
|
+
import type { Sprite } from "@fly/sprites";
|
|
15
|
+
import type { StreamConnectionInfo } from "../streams.js";
|
|
16
|
+
import type { SessionBridge } from "./types.js";
|
|
17
|
+
export interface ClaudeCodeSpritesConfig {
|
|
18
|
+
/** Initial prompt (the user's app description or task) */
|
|
19
|
+
prompt: string;
|
|
20
|
+
/** Working directory inside the sprite */
|
|
21
|
+
cwd: string;
|
|
22
|
+
/** Model to use (default: claude-sonnet-4-6) */
|
|
23
|
+
model?: string;
|
|
24
|
+
/** Allowed tools (default: all standard tools) */
|
|
25
|
+
allowedTools?: string[];
|
|
26
|
+
/** Additional CLI flags */
|
|
27
|
+
extraFlags?: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare class ClaudeCodeSpritesBridge implements SessionBridge {
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly streamUrl: string;
|
|
32
|
+
readonly streamHeaders: Record<string, string>;
|
|
33
|
+
private sprite;
|
|
34
|
+
private config;
|
|
35
|
+
private writer;
|
|
36
|
+
private parser;
|
|
37
|
+
private agentEventCallbacks;
|
|
38
|
+
private completeCallbacks;
|
|
39
|
+
private closed;
|
|
40
|
+
private cmd;
|
|
41
|
+
/** Claude Code session ID captured from stream-json system.init — used for --resume */
|
|
42
|
+
private claudeSessionId;
|
|
43
|
+
/** Whether a Claude Code process is currently running */
|
|
44
|
+
private running;
|
|
45
|
+
/** Whether the parser already emitted a session_end (from a "result" message) */
|
|
46
|
+
private resultReceived;
|
|
47
|
+
constructor(sessionId: string, connection: StreamConnectionInfo, sprite: Sprite, config: ClaudeCodeSpritesConfig);
|
|
48
|
+
emit(event: EngineEvent): Promise<void>;
|
|
49
|
+
sendCommand(cmd: Record<string, unknown>): Promise<void>;
|
|
50
|
+
sendGateResponse(gate: string, value: Record<string, unknown>): Promise<void>;
|
|
51
|
+
onAgentEvent(cb: (event: EngineEvent) => void): void;
|
|
52
|
+
onComplete(cb: (success: boolean) => void): void;
|
|
53
|
+
start(): Promise<void>;
|
|
54
|
+
close(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Spawn a new Claude Code process. Called for both the initial prompt
|
|
57
|
+
* and follow-up iterate messages (with --resume).
|
|
58
|
+
*/
|
|
59
|
+
private spawnClaude;
|
|
60
|
+
private handleLine;
|
|
61
|
+
private dispatchEvent;
|
|
62
|
+
private writeUserMessage;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=claude-code-sprites.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-sprites.d.ts","sourceRoot":"","sources":["../../src/bridge/claude-code-sprites.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAI9D,MAAM,WAAW,uBAAuB;IACvC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAA;IACX,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAcD,qBAAa,uBAAwB,YAAW,aAAa;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAE9C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,mBAAmB,CAA0C;IACrE,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,GAAG,CAA6B;IAExC,uFAAuF;IACvF,OAAO,CAAC,eAAe,CAAsB;IAC7C,yDAAyD;IACzD,OAAO,CAAC,OAAO,CAAQ;IACvB,iFAAiF;IACjF,OAAO,CAAC,cAAc,CAAQ;gBAG7B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB;IAe1B,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvC,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxD,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCnF,YAAY,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI;IAIpD,UAAU,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAI1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,KAAK,IAAI,IAAI;IAgBb;;;OAGG;IACH,OAAO,CAAC,WAAW;IAqGnB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,aAAa;IA8CrB,OAAO,CAAC,gBAAgB;CAQxB"}
|