Skip to content

Commit 8abb7ee

Browse files
feat: update executor in project.json and enhance error handling in client-server.ts
1 parent ce69e57 commit 8abb7ee

5 files changed

Lines changed: 39 additions & 12 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ yarn format:check # verify formatting
2020

2121
nx build ast-guard # build a single project
2222
nx test enclave-vm # test a single project
23-
nx serve enclave-demo # run a demo app
23+
npx nx serve enclave-demo # CLI demo: sandbox features
24+
npx nx demo streaming-demo # Web demo: 3-server streaming architecture (localhost:4100)
2425
```
2526

2627
Optional: `nx local-registry enclave` starts a local Verdaccio registry for publish testing.

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,19 +135,27 @@ See [README-ARCHITECTURE.md](./README-ARCHITECTURE.md) for detailed architecture
135135
- Reference sidecar & auto-ref
136136
- Security & encryption
137137

138-
## Demo
138+
## Demos
139139

140-
Run the streaming demo locally:
140+
### Enclave Demo (basic sandbox)
141+
142+
```bash
143+
npx nx serve enclave-demo
144+
```
145+
146+
A CLI script that demonstrates core sandbox features: arithmetic, loops, tool calls, and security blocking of disallowed operations.
147+
148+
### Streaming Demo (full architecture)
141149

142150
```bash
143151
npx nx demo streaming-demo
144152
```
145153

146-
This starts 3 servers demonstrating the secure architecture:
154+
Starts a 3-server architecture with a web UI, covering embedded, lambda, and direct execution modes:
147155

148-
- **Client** (port 4100) - Web UI
149-
- **Broker** (port 4101) - Tool execution & session management
150-
- **Runtime** (port 4102) - Sandboxed code execution
156+
- **Client** (port 4100) Web UI
157+
- **Broker** (port 4101) Tool execution & session management
158+
- **Runtime** (port 4102) Sandboxed code execution
151159

152160
**[Read the full documentation →](https://agentfront.dev/docs/guides/enclave)**
153161

apps/enclave-demo/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
}
1515
},
1616
"serve": {
17-
"executor": "@nx/js:node",
17+
"executor": "nx:run-commands",
1818
"options": {
19-
"buildTarget": "enclave-demo:build"
19+
"command": "npx tsx apps/enclave-demo/src/main.ts"
2020
}
2121
},
2222
"test": {

apps/streaming-demo/src/client-server.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ async function pipeNdjsonWithAbort(options: NdjsonPipeOptions): Promise<NdjsonPi
6464
let aborted = false;
6565
let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
6666

67-
// Handle client disconnect
67+
// Handle client disconnect (only listen on res, not req — req 'close' fires
68+
// prematurely in Node 20+ once the request body is consumed by express.json())
6869
const onClose = () => {
6970
aborted = true;
7071
controller.abort();
7172
if (reader) {
7273
void reader.cancel();
7374
}
7475
};
75-
req.on('close', onClose);
7676
res.on('close', onClose);
7777

7878
try {
@@ -136,7 +136,6 @@ async function pipeNdjsonWithAbort(options: NdjsonPipeOptions): Promise<NdjsonPi
136136

137137
return { lastEvent, aborted };
138138
} finally {
139-
req.off('close', onClose);
140139
res.off('close', onClose);
141140
if (reader) {
142141
void reader.cancel();
@@ -173,6 +172,9 @@ async function handleEmbeddedExecute(req: Request, res: Response): Promise<void>
173172

174173
if (aborted) {
175174
console.log(`\x1b[34m[Client → Embedded]\x1b[0m Aborted`);
175+
if (res.writable) {
176+
res.end();
177+
}
176178
return;
177179
}
178180

@@ -191,6 +193,9 @@ async function handleEmbeddedExecute(req: Request, res: Response): Promise<void>
191193
} catch (error) {
192194
if ((error as Error).name === 'AbortError') {
193195
console.log(`\x1b[34m[Client → Embedded]\x1b[0m Aborted`);
196+
if (res.writable) {
197+
res.end();
198+
}
194199
return;
195200
}
196201
console.error(`\x1b[31m[Client → Embedded error]\x1b[0m`, error);
@@ -232,6 +237,9 @@ app.post('/api/execute/lambda', async (req: Request, res: Response) => {
232237

233238
if (aborted) {
234239
console.log(`\x1b[34m[Client → Broker → Lambda]\x1b[0m Aborted`);
240+
if (res.writable) {
241+
res.end();
242+
}
235243
return;
236244
}
237245

@@ -250,6 +258,9 @@ app.post('/api/execute/lambda', async (req: Request, res: Response) => {
250258
} catch (error) {
251259
if ((error as Error).name === 'AbortError') {
252260
console.log(`\x1b[34m[Client → Broker → Lambda]\x1b[0m Aborted`);
261+
if (res.writable) {
262+
res.end();
263+
}
253264
return;
254265
}
255266
console.error(`\x1b[31m[Client → Broker → Lambda error]\x1b[0m`, error);

apps/streaming-demo/src/public/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ <h2>Final Result</h2>
420420
setStatus('running', `Running (${currentMode})...`);
421421

422422
let eventCount = 0;
423+
let gotResult = false;
423424

424425
try {
425426
const response = await fetch(`/api/execute/${currentMode}`, {
@@ -448,8 +449,10 @@ <h2>Final Result</h2>
448449
document.getElementById('eventCount').textContent = eventCount + ' events';
449450

450451
if (event.type === 'client_result') {
452+
gotResult = true;
451453
displayResult(event.result);
452454
} else if (event.type === 'client_error') {
455+
gotResult = true;
453456
resultDiv.innerHTML = '<span class="result-error">Error: ' + event.error + '</span>';
454457
setStatus('error', 'Error');
455458
} else {
@@ -465,6 +468,10 @@ <h2>Final Result</h2>
465468
setStatus('error', 'Error');
466469
} finally {
467470
runBtn.disabled = false;
471+
if (!gotResult) {
472+
setStatus('error', 'Stream ended without result');
473+
resultDiv.innerHTML = '<span class="result-error">Stream ended without receiving a result</span>';
474+
}
468475
}
469476
}
470477

0 commit comments

Comments
 (0)