|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | (() => { |
| 10 | + const ALLOWED_BRIDGE_ACTIONS = new Set(['cloud_run', 'cloud_status', 'cloud_abort']); |
10 | 11 | let socket = null; |
11 | 12 | let bridgeUrl = null; |
12 | 13 | let enabled = false; |
|
34 | 35 | }; |
35 | 36 | } |
36 | 37 |
|
37 | | - function sendJson(obj) { |
38 | | - if (!socket || socket.readyState !== WebSocket.OPEN) return; |
| 38 | + function sendJson(obj, target = socket) { |
| 39 | + if (!target || target.readyState !== WebSocket.OPEN) return; |
39 | 40 | try { |
40 | | - socket.send(JSON.stringify(obj)); |
| 41 | + target.send(JSON.stringify(obj)); |
41 | 42 | } catch (e) { |
42 | 43 | lastError = e.message || String(e); |
43 | 44 | } |
|
56 | 57 | if (!enabled || !bridgeUrl) return; |
57 | 58 | if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) return; |
58 | 59 | try { |
59 | | - socket = new WebSocket(bridgeUrl); |
60 | | - socket.addEventListener('open', () => { |
| 60 | + const nextSocket = new WebSocket(bridgeUrl); |
| 61 | + socket = nextSocket; |
| 62 | + nextSocket.addEventListener('open', () => { |
| 63 | + if (socket !== nextSocket) return; |
61 | 64 | reconnectAttempt = 0; |
62 | 65 | lastError = ''; |
63 | | - sendJson({ type: 'hello', client: 'webbrain-extension', status: status() }); |
| 66 | + sendJson({ type: 'hello', client: 'webbrain-extension', status: status() }, nextSocket); |
64 | 67 | }); |
65 | | - socket.addEventListener('message', async (event) => { |
| 68 | + nextSocket.addEventListener('message', async (event) => { |
| 69 | + if (socket !== nextSocket) return; |
66 | 70 | let msg; |
67 | 71 | try { |
68 | 72 | msg = JSON.parse(event.data); |
69 | 73 | } catch (e) { |
70 | | - sendJson({ ok: false, error: `Invalid JSON message: ${e.message}` }); |
| 74 | + sendJson({ ok: false, error: `Invalid JSON message: ${e.message}` }, nextSocket); |
71 | 75 | return; |
72 | 76 | } |
73 | 77 |
|
74 | 78 | const id = msg.id || null; |
75 | 79 | const action = msg.action || msg.command; |
76 | 80 | const payload = msg.payload || msg; |
77 | 81 | if (!action) { |
78 | | - sendJson({ id, ok: false, error: 'Missing action' }); |
| 82 | + sendJson({ id, ok: false, error: 'Missing action' }, nextSocket); |
| 83 | + return; |
| 84 | + } |
| 85 | + if (!ALLOWED_BRIDGE_ACTIONS.has(action)) { |
| 86 | + sendJson({ id, ok: false, error: `Unsupported cloud bridge action: ${action}` }, nextSocket); |
79 | 87 | return; |
80 | 88 | } |
81 | 89 |
|
|
85 | 93 | target: 'background', |
86 | 94 | action, |
87 | 95 | }); |
88 | | - if (response && response.error) { |
89 | | - sendJson({ id, ok: false, error: response.error }); |
| 96 | + const isRunSnapshot = !!response |
| 97 | + && (response.runId != null || response.run_id != null) |
| 98 | + && typeof response.status === 'string'; |
| 99 | + if (response?.error && !isRunSnapshot) { |
| 100 | + sendJson({ id, ok: false, error: response.error }, nextSocket); |
90 | 101 | } else { |
91 | | - sendJson({ id, ok: true, result: response }); |
| 102 | + sendJson({ id, ok: true, result: response }, nextSocket); |
92 | 103 | } |
93 | 104 | } catch (e) { |
94 | | - sendJson({ id, ok: false, error: e.message || String(e) }); |
| 105 | + sendJson({ id, ok: false, error: e.message || String(e) }, nextSocket); |
95 | 106 | } |
96 | 107 | }); |
97 | | - socket.addEventListener('close', () => { |
| 108 | + nextSocket.addEventListener('close', () => { |
| 109 | + if (socket !== nextSocket) return; |
98 | 110 | socket = null; |
99 | 111 | scheduleReconnect(); |
100 | 112 | }); |
101 | | - socket.addEventListener('error', () => { |
| 113 | + nextSocket.addEventListener('error', () => { |
| 114 | + if (socket !== nextSocket) return; |
102 | 115 | lastError = 'WebSocket error'; |
103 | 116 | }); |
104 | 117 | } catch (e) { |
|
122 | 135 | enabled = true; |
123 | 136 | bridgeUrl = nextUrl; |
124 | 137 | if (changed && socket) { |
125 | | - try { socket.close(); } catch {} |
| 138 | + const previousSocket = socket; |
126 | 139 | socket = null; |
| 140 | + try { previousSocket.close(); } catch {} |
127 | 141 | } |
128 | 142 | connect(); |
129 | 143 | sendResponse(status()); |
|
135 | 149 | reconnectTimer = null; |
136 | 150 | reconnectAttempt = 0; |
137 | 151 | if (socket) { |
138 | | - try { socket.close(); } catch {} |
| 152 | + const previousSocket = socket; |
| 153 | + socket = null; |
| 154 | + try { previousSocket.close(); } catch {} |
139 | 155 | } |
140 | | - socket = null; |
141 | 156 | sendResponse(status()); |
142 | 157 | return false; |
143 | 158 | } |
|
0 commit comments