| 1 | #!/usr/bin/env node |
| 2 | // Small ACP peer used by buzz-runner-integration.mjs. It is intentionally |
| 3 | // disposable: it never connects to a relay and only speaks JSON-RPC on stdio. |
| 4 | let buffer = '' ; |
| 5 | let permissionID = 40; |
| 6 | let sessionID = 'qa-session' ; |
| 7 | let awaitingPermission = false; |
| 8 | if (process.env.FAKE_ACP_MODE === 'crash-once' ) { |
| 9 | const fs = await import('node:fs' ); |
| 10 | if (!fs.existsSync(process.env.FAKE_ACP_MARKER)) fs.writeFileSync(process.env.FAKE_ACP_MARKER, 'crashed' ); |
| 11 | else process.env.FAKE_ACP_MODE = '' ; |
| 12 | } |
| 13 | process.stdin.setEncoding('utf8' ); |
| 14 | process.stdin.on('data' , chunk => { |
| 15 | buffer += chunk; |
| 16 | while (buffer.includes('\n' )) { |
| 17 | const newline = buffer.indexOf('\n' ); |
| 18 | const line = buffer.slice(0, newline); |
| 19 | buffer = buffer.slice(newline + 1); |
| 20 | if (!line.trim()) continue; |
| 21 | let frame; |
| 22 | try { frame = JSON.parse(line); } catch { continue; } |
| 23 | handle(frame); |
| 24 | } |
| 25 | }); |
| 26 | function send(frame) { process.stdout.write(JSON.stringify(frame) + '\n' ); } |
| 27 | function handle(frame) { |
| 28 | if (frame.method === 'initialize' ) { |
| 29 | send({jsonrpc: '2.0' , id: frame.id, result: {protocolVersion: 1}}); |
| 30 | return; |
| 31 | } |
| 32 | if (frame.method === 'session/new' ) { |
| 33 | send({jsonrpc: '2.0' , id: frame.id, result: {sessionId: sessionID}}); |
| 34 | return; |
| 35 | } |
| 36 | if (frame.method === 'session/cancel' ) { |
| 37 | send({jsonrpc: '2.0' , id: frame.id, result: {}}); |
| 38 | return; |
| 39 | } |
| 40 | if (frame.method === 'session/prompt' ) { |
| 41 | if (process.env.FAKE_ACP_MODE === 'crash-once' ) process.exit(23); |
| 42 | send({jsonrpc: '2.0' , method: 'session/update' , params: {sessionId: sessionID, update: {sessionUpdate: 'tool_call' , title: 'publish preview' , status: 'running' }}}); |
| 43 | awaitingPermission = true; |
| 44 | send({jsonrpc: '2.0' , id: ++permissionID, method: 'session/request_permission' , params: {sessionId: sessionID, toolCall: {title: 'Publish preview' , rawInput: {path: 'public/preview.html' }}, options: [{optionId: 'allow' , name: 'Allow once' , kind: 'allow_once' }, {optionId: 'deny' , name: 'Deny once' , kind: 'reject_once' }]}}); |
| 45 | return; |
| 46 | } |
| 47 | if (frame.id === permissionID && awaitingPermission) { |
| 48 | awaitingPermission = false; |
| 49 | const outcome = frame.result?.outcome; |
| 50 | if (outcome?.outcome !== 'selected' || outcome.optionId !== 'allow' ) { |
| 51 | send({jsonrpc: '2.0' , id: 3, result: {stopReason: 'cancelled' }}); |
| 52 | return; |
| 53 | } |
| 54 | send({jsonrpc: '2.0' , method: 'session/update' , params: {sessionId: sessionID, update: {sessionUpdate: 'agent_message_chunk' , content: {type: 'text' , text: 'Preview published.' }}}}); |
| 55 | send({jsonrpc: '2.0' , id: 3, result: {stopReason: 'end_turn' }}); |
| 56 | } |
| 57 | } |