grasp-sdk 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.

Potentially problematic release.


This version of grasp-sdk might be problematic. Click here for more details.

@@ -1,395 +0,0 @@
1
- import { chromium } from 'playwright-extra';
2
- import StealthPlugin from 'puppeteer-extra-plugin-stealth';
3
- import httpProxy from 'http-proxy';
4
- import http from 'http';
5
-
6
- import { Logtail } from '@logtail/node';
7
- import * as Sentry from "@sentry/node";
8
-
9
- const logtail = new Logtail(process.env.BS_SOURCE_TOKEN, {
10
- endpoint: `https://${process.env.BS_INGESTING_HOST}`,
11
- });
12
-
13
- Sentry.init({
14
- dsn: process.env.SENTRY_DSN,
15
-
16
- // Setting this option to true will send default PII data to Sentry.
17
- // For example, automatic IP address collection on events
18
- sendDefaultPii: true,
19
- _experiments: {
20
- enableLogs: true, // 启用日志功能
21
- },
22
- });
23
-
24
- const logger = {
25
- info: async (message, context) => {
26
- Sentry.logger.info(message, context);
27
- return logtail.info(message, context);
28
- },
29
- warn: async (message, context) => {
30
- Sentry.logger.warn(message, context);
31
- return logtail.warn(message, context);
32
- },
33
- error: async (message, context) => {
34
- Sentry.logger.error(message, context);
35
- return logtail.error(message, context);
36
- },
37
- }
38
-
39
- function parseWebSocketFrame(buffer) {
40
- if (buffer.length < 2) {
41
- throw new Error('Incomplete WebSocket frame.');
42
- }
43
-
44
- const firstByte = buffer.readUInt8(0);
45
- const fin = (firstByte & 0x80) !== 0;
46
- const opcode = firstByte & 0x0f;
47
-
48
- // 仅处理文本帧(opcode 为 0x1)
49
- if (opcode !== 0x1) {
50
- throw new Error(`Unsupported opcode: ${opcode}`);
51
- }
52
-
53
- const secondByte = buffer.readUInt8(1);
54
- const isMasked = (secondByte & 0x80) !== 0;
55
- let payloadLength = secondByte & 0x7f;
56
- let offset = 2;
57
-
58
- if (payloadLength === 126) {
59
- if (buffer.length < offset + 2) {
60
- throw new Error('Incomplete extended payload length.');
61
- }
62
- payloadLength = buffer.readUInt16BE(offset);
63
- offset += 2;
64
- } else if (payloadLength === 127) {
65
- if (buffer.length < offset + 8) {
66
- throw new Error('Incomplete extended payload length.');
67
- }
68
- // 注意:JavaScript 无法精确表示超过 2^53 的整数
69
- const highBits = buffer.readUInt32BE(offset);
70
- const lowBits = buffer.readUInt32BE(offset + 4);
71
- payloadLength = highBits * 2 ** 32 + lowBits;
72
- offset += 8;
73
- }
74
-
75
- let maskingKey;
76
- if (isMasked) {
77
- if (buffer.length < offset + 4) {
78
- throw new Error('Incomplete masking key.');
79
- }
80
- maskingKey = buffer.slice(offset, offset + 4);
81
- offset += 4;
82
- }
83
-
84
- if (buffer.length < offset + payloadLength) {
85
- throw new Error('Incomplete payload data.');
86
- }
87
-
88
- const payloadData = buffer.slice(offset, offset + payloadLength);
89
-
90
- if (isMasked) {
91
- for (let i = 0; i < payloadLength; i++) {
92
- payloadData[i] ^= maskingKey[i % 4];
93
- }
94
- }
95
-
96
- return payloadData.toString('utf8');
97
- }
98
-
99
- const sandboxId = process.env.SANDBOX_ID;
100
- const cdpPort = Number(process.env.CDP_PORT);
101
- const headless = process.env.HEADLESS !== 'false';
102
- const enableAdblock = process.env.ADBLOCK !== 'false';
103
- const timeoutMS = process.env.SANBOX_TIMEOUT;
104
- const workspace = process.env.WORKSPACE;
105
- const keepAliveMS = Number(process.env.KEEP_ALIVE_MS) || 0;
106
- const args = [];
107
-
108
- try {
109
- console.log('🚀 Starting Chromium browser with CDP...');
110
-
111
- chromium.use(StealthPlugin());
112
-
113
- const adblockPlugin = '/home/user/.config/google-chrome/Default/Extensions/adblock';
114
-
115
- args.push(
116
- `--remote-debugging-port=${cdpPort}`,
117
- '--no-sandbox',
118
- '--disable-setuid-sandbox',
119
- '--disable-dev-shm-usage',
120
- '--disable-gpu',
121
- '--no-first-run',
122
- '--no-default-browser-check',
123
- '--disable-background-timer-throttling',
124
- '--disable-backgrounding-occluded-windows',
125
- '--disable-renderer-backgrounding',
126
- "--disable-software-rasterizer",
127
- ...JSON.parse(process.env.BROWSER_ARGS),
128
- );
129
-
130
- if (headless) {
131
- args.push('--headless=new');
132
- }
133
-
134
- if(enableAdblock) {
135
- args.push(...[
136
- `--disable-extensions-except=${adblockPlugin}`,
137
- `--load-extension=${adblockPlugin}`,
138
- ])
139
- }
140
-
141
- const browser = await chromium.launch({
142
- headless,
143
- args,
144
- timeout: Number(process.env.LAUNCH_TIMEOUT),
145
- // @ts-ignore
146
- userDataDir: '/home/user/.browser-context',
147
- });
148
-
149
- console.log('🎉 Chromium browser launched successfully');
150
- console.log(`✨ CDP server available at: http://localhost:${cdpPort}`);
151
- console.log(`✨ WebSocket endpoint: ws://localhost:${cdpPort}`);
152
-
153
- // Keep the process alive
154
- process.on('SIGTERM', async () => {
155
- console.log('Received SIGTERM, closing browser...');
156
- await logger.warn('Received SIGTERM signal', { sandboxId });
157
- Sentry.addBreadcrumb({
158
- category: 'process',
159
- message: 'Received SIGTERM signal',
160
- level: 'info',
161
- data: { sandboxId }
162
- });
163
- await browser.close();
164
- process.exit(0);
165
- });
166
-
167
- process.on('SIGINT', async () => {
168
- console.log('Received SIGINT, closing browser...');
169
- await logger.warn('Received SIGINT signal', { sandboxId });
170
- Sentry.addBreadcrumb({
171
- category: 'process',
172
- message: 'Received SIGINT signal',
173
- level: 'info',
174
- data: { sandboxId }
175
- });
176
- await browser.close();
177
- process.exit(0);
178
- });
179
-
180
- // 创建代理服务:从 ${this.config.cdpPort! + 1} 转发到 127.0.0.1:${this.config.cdpPort!}
181
- const proxy = httpProxy.createProxyServer({
182
- target: `http://127.0.0.1:${cdpPort}`,
183
- ws: true, // 支持 WebSocket
184
- changeOrigin: true
185
- });
186
-
187
- const clients = new Set();
188
-
189
- // 监听 WebSocket 事件
190
- proxy.on('open', () => {
191
- console.log('🔌 CDP WebSocket connection established');
192
- const wsId = Date.now();
193
- logger.info('CDP WebSocket connection established', { sandboxId, wsId });
194
- Sentry.addBreadcrumb({
195
- category: 'websocket',
196
- message: 'CDP connection established',
197
- level: 'info',
198
- data: { sandboxId }
199
- });
200
-
201
- clients.add(wsId);
202
- proxy.once('close', async (req, socket, head) => {
203
- console.log('🔒 CDP WebSocket connection closed');
204
- await logger.info('CDP WebSocket connection closed', { sandboxId });
205
- Sentry.addBreadcrumb({
206
- category: 'websocket',
207
- message: 'CDP connection closed',
208
- level: 'info',
209
- data: { sandboxId }
210
- });
211
- clients.delete(wsId);
212
-
213
- if(clients.size <= 0) {
214
- setTimeout(() => {
215
- if(clients.size <= 0) {
216
- console.log('❌ Force closed...', wsId);
217
- process.exit(0);
218
- }
219
- }, keepAliveMS);
220
- }
221
- });
222
- });
223
-
224
- proxy.on('proxyReqWs', (proxyReq, req, socket, options, head) => {
225
- console.log('📡 New CDP WebSocket connection request:', req.url);
226
- logger.info('New CDP WebSocket connection request', { url: req.url, sandboxId });
227
- Sentry.addBreadcrumb({
228
- category: 'websocket',
229
- message: 'New CDP connection request',
230
- level: 'info',
231
- data: { url: req.url, sandboxId }
232
- });
233
- });
234
-
235
- proxy.on('error', (err, req, res) => {
236
- console.error('❌ CDP WebSocket proxy error:', err);
237
- logger.error('CDP WebSocket proxy error', { error: err.message, url: req?.url, sandboxId });
238
- Sentry.captureException(err, {
239
- tags: { type: 'websocket_proxy_error', sandboxId },
240
- extra: { url: req?.url }
241
- });
242
- });
243
-
244
- const server = http.createServer(async (req, res) => {
245
- if (req.url === '/health') {
246
- res.end('ok');
247
- return;
248
- }
249
- if (req.url === '/json/version' || req.url === '/json/version/') {
250
- try {
251
- // 向本地 CDP 发请求,获取原始 JSON
252
- const jsonRes = await fetch(`http://127.0.0.1:${cdpPort}/json/version`);
253
- const data = await jsonRes.json();
254
- // 替换掉本地的 WebSocket 地址为代理暴露地址
255
- data.webSocketDebuggerUrl = data.webSocketDebuggerUrl.replace(
256
- `ws://127.0.0.1:${cdpPort}`,
257
- `wss://${req.headers.host}`
258
- );
259
- await logger.info('CDP version info requested', { url: req.url, response: data, sandboxId });
260
- Sentry.addBreadcrumb({
261
- category: 'http',
262
- message: 'CDP version info requested',
263
- level: 'info',
264
- data: { url: req.url, response: data, sandboxId }
265
- });
266
- res.writeHead(200, { 'Content-Type': 'application/json' });
267
- res.end(JSON.stringify(data));
268
- } catch(ex) {
269
- console.error('Failed to fetch CDP version:', ex.message);
270
- await logger.error('Failed to fetch CDP version', { error: ex.message, sandboxId });
271
- Sentry.captureException(ex, {
272
- tags: { type: 'cdp_version_error', sandboxId }
273
- });
274
- res.writeHead(500);
275
- res.end('Internal Server Error');
276
- }
277
- } else {
278
- proxy.web(req, res, {}, async (err) => {
279
- console.error('Proxy error:', err);
280
- await logger.error('HTTP proxy error', { error: err.message, url: req.url, sandboxId });
281
- Sentry.captureException(err, {
282
- tags: { type: 'proxy_error', sandboxId },
283
- extra: { url: req.url }
284
- });
285
- res.writeHead(502);
286
- res.end('Bad gateway');
287
- });
288
- }
289
- });
290
-
291
- server.on('upgrade', (req, socket, head) => {
292
- // 监听 WebSocket 数据
293
- let _buffers = [];
294
- socket.on('data', (data) => {
295
- let message = '';
296
- try {
297
- _buffers.push(data);
298
- // console.log(`💬 ${_buffers.length}`);
299
- message = parseWebSocketFrame(Buffer.concat(_buffers)); // 复制data不能破坏原始数据
300
- _buffers.length = 0;
301
- if (message.startsWith('{')){ // 只解析 JSON 消息
302
- const parsed = JSON.parse(message);
303
- console.log('📨 CDP WebSocket message:', parsed);
304
- logger.info('CDP WebSocket message received', {
305
- data: parsed,
306
- sandboxId: process.env.SANDBOX_ID,
307
- });
308
- Sentry.addBreadcrumb({
309
- category: 'websocket',
310
- message: 'CDP message received',
311
- level: 'debug',
312
- data: { ...parsed, sandboxId }
313
- });
314
- }
315
- } catch (err) {
316
- const msg = err.message;
317
- if(!msg.includes('Incomplete')) {
318
- // 记录解析错误
319
- console.warn('⚠️ Failed to parse CDP WebSocket message:', err.message, _buffers.length);
320
- _buffers.length = 0;
321
- Sentry.captureException(err, {
322
- tags: { type: 'websocket_error', sandboxId }
323
- });
324
- logger.warn('Failed to parse CDP WebSocket message', {
325
- error: err.message,
326
- data: message,
327
- sandboxId: process.env.SANDBOX_ID
328
- });
329
- }
330
- }
331
- });
332
-
333
- socket.on('error', (err) => {
334
- console.error('❌ CDP WebSocket error:', err);
335
- logger.error('CDP WebSocket error', { error: err.message, sandboxId });
336
- Sentry.captureException(err, {
337
- tags: { type: 'websocket_error', sandboxId }
338
- });
339
- });
340
-
341
- proxy.ws(req, socket, head);
342
- });
343
-
344
- server.listen(cdpPort + 1, '0.0.0.0', () => {
345
- console.log(`🎯 Proxy server listening on http://0.0.0.0:${cdpPort + 1} → http://127.0.0.1:${cdpPort}`);
346
- logger.info('Proxy server started', {
347
- port: cdpPort + 1,
348
- target: cdpPort,
349
- sandboxId,
350
- settings: {
351
- type: 'chromium',
352
- args,
353
- headless,
354
- enableAdblock,
355
- timeoutMS,
356
- workspace,
357
- sandboxId
358
- },
359
- });
360
- Sentry.addBreadcrumb({
361
- category: 'server',
362
- message: 'Proxy server started',
363
- level: 'info',
364
- data: {
365
- port: cdpPort + 1,
366
- target: cdpPort,
367
- sandboxId,
368
- settings: {
369
- type: 'chromium',
370
- args,
371
- headless,
372
- enableAdblock,
373
- timeoutMS,
374
- workspace,
375
- sandboxId
376
- },
377
- }
378
- });
379
- });
380
- } catch (ex) {
381
- console.error('Failed to launch Chromium:', ex);
382
- logger.error('Failed to launch Chrome', {
383
- error: ex.message,
384
- args,
385
- headless,
386
- cdpPort,
387
- enableAdblock,
388
- sandboxId,
389
- });
390
- Sentry.captureException(ex, {
391
- tags: { type: 'launch_error', sandboxId },
392
- extra: { args, headless, cdpPort, enableAdblock }
393
- });
394
- process.exit(1);
395
- }
@@ -1,324 +0,0 @@
1
- import httpProxy from 'http-proxy';
2
- import http from 'http';
3
- import fs from 'fs';
4
-
5
- import { Logtail } from '@logtail/node';
6
- import * as Sentry from "@sentry/node";
7
-
8
- const logtail = new Logtail(process.env.BS_SOURCE_TOKEN, {
9
- endpoint: `https://${process.env.BS_INGESTING_HOST}`,
10
- });
11
-
12
- Sentry.init({
13
- dsn: process.env.SENTRY_DSN,
14
-
15
- // Setting this option to true will send default PII data to Sentry.
16
- // For example, automatic IP address collection on events
17
- sendDefaultPii: true,
18
- _experiments: {
19
- enableLogs: true, // 启用日志功能
20
- },
21
- });
22
-
23
- const logger = {
24
- info: async (message, context) => {
25
- Sentry.logger.info(message, context);
26
- return logtail.info(message, context);
27
- },
28
- warn: async (message, context) => {
29
- Sentry.logger.warn(message, context);
30
- return logtail.warn(message, context);
31
- },
32
- error: async (message, context) => {
33
- Sentry.logger.error(message, context);
34
- return logtail.error(message, context);
35
- },
36
- }
37
-
38
- function parseWebSocketFrame(buffer) {
39
- if (buffer.length < 2) {
40
- throw new Error('Incomplete WebSocket frame.');
41
- }
42
-
43
- const firstByte = buffer.readUInt8(0);
44
- const fin = (firstByte & 0x80) !== 0;
45
- const opcode = firstByte & 0x0f;
46
-
47
- // 仅处理文本帧(opcode 为 0x1)
48
- if (opcode !== 0x1) {
49
- throw new Error(`Unsupported opcode: ${opcode}`);
50
- }
51
-
52
- const secondByte = buffer.readUInt8(1);
53
- const isMasked = (secondByte & 0x80) !== 0;
54
- let payloadLength = secondByte & 0x7f;
55
- let offset = 2;
56
-
57
- if (payloadLength === 126) {
58
- if (buffer.length < offset + 2) {
59
- throw new Error('Incomplete extended payload length.');
60
- }
61
- payloadLength = buffer.readUInt16BE(offset);
62
- offset += 2;
63
- } else if (payloadLength === 127) {
64
- if (buffer.length < offset + 8) {
65
- throw new Error('Incomplete extended payload length.');
66
- }
67
- // 注意:JavaScript 无法精确表示超过 2^53 的整数
68
- const highBits = buffer.readUInt32BE(offset);
69
- const lowBits = buffer.readUInt32BE(offset + 4);
70
- payloadLength = highBits * 2 ** 32 + lowBits;
71
- offset += 8;
72
- }
73
-
74
- let maskingKey;
75
- if (isMasked) {
76
- if (buffer.length < offset + 4) {
77
- throw new Error('Incomplete masking key.');
78
- }
79
- maskingKey = buffer.slice(offset, offset + 4);
80
- offset += 4;
81
- }
82
-
83
- if (buffer.length < offset + payloadLength) {
84
- throw new Error('Incomplete payload data.');
85
- }
86
-
87
- const payloadData = buffer.slice(offset, offset + payloadLength);
88
-
89
- if (isMasked) {
90
- for (let i = 0; i < payloadLength; i++) {
91
- payloadData[i] ^= maskingKey[i % 4];
92
- }
93
- }
94
-
95
- return payloadData.toString('utf8');
96
- }
97
-
98
- let sandboxId = '';
99
- const cdpPort = Number(process.env.CDP_PORT);
100
- const headless = process.env.HEADLESS !== 'false';
101
- const enableAdblock = process.env.ADBLOCK !== 'false';
102
- const timeoutMS = process.env.SANBOX_TIMEOUT;
103
- const workspace = process.env.WORKSPACE;
104
- const keepAliveMS = Number(process.env.KEEP_ALIVE_MS) || 0;
105
- const args = [];
106
-
107
- try {
108
- // 创建代理服务:从 ${this.config.cdpPort! + 1} 转发到 127.0.0.1:${this.config.cdpPort!}
109
- const proxy = httpProxy.createProxyServer({
110
- target: `http://127.0.0.1:${cdpPort}`,
111
- ws: true, // 支持 WebSocket
112
- changeOrigin: true
113
- });
114
-
115
- const clients = new Set();
116
-
117
- // 监听 WebSocket 事件
118
- proxy.on('open', () => {
119
- sandboxId = fs.readFileSync('/home/user/.sandbox_id', 'utf-8');
120
- console.log('🔌 CDP WebSocket connection established', sandboxId);
121
- const wsId = Date.now();
122
- logger.info('CDP WebSocket connection established', { sandboxId, wsId });
123
- Sentry.addBreadcrumb({
124
- category: 'websocket',
125
- message: 'CDP connection established',
126
- level: 'info',
127
- data: { sandboxId }
128
- });
129
-
130
- clients.add(wsId);
131
- proxy.once('close', async (req, socket, head) => {
132
- console.log('🔒 CDP WebSocket connection closed');
133
- await logger.info('CDP WebSocket connection closed', { sandboxId });
134
- Sentry.addBreadcrumb({
135
- category: 'websocket',
136
- message: 'CDP connection closed',
137
- level: 'info',
138
- data: { sandboxId }
139
- });
140
- clients.delete(wsId);
141
-
142
- if(clients.size <= 0) {
143
- setTimeout(() => {
144
- if(clients.size <= 0) {
145
- console.log('❌ Force closed...', wsId);
146
- process.exit(0);
147
- }
148
- }, keepAliveMS);
149
- }
150
- });
151
- });
152
-
153
- proxy.on('proxyReqWs', (proxyReq, req, socket, options, head) => {
154
- console.log('📡 New CDP WebSocket connection request:', req.url);
155
- logger.info('New CDP WebSocket connection request', { url: req.url, sandboxId });
156
- Sentry.addBreadcrumb({
157
- category: 'websocket',
158
- message: 'New CDP connection request',
159
- level: 'info',
160
- data: { url: req.url, sandboxId }
161
- });
162
- });
163
-
164
- proxy.on('error', (err, req, res) => {
165
- console.error('❌ CDP WebSocket proxy error:', err);
166
- logger.error('CDP WebSocket proxy error', { error: err.message, url: req?.url, sandboxId });
167
- Sentry.captureException(err, {
168
- tags: { type: 'websocket_proxy_error', sandboxId },
169
- extra: { url: req?.url }
170
- });
171
- });
172
-
173
- const server = http.createServer(async (req, res) => {
174
- if (req.url === '/health') {
175
- res.end('ok');
176
- return;
177
- }
178
- if (req.url === '/json/version' || req.url === '/json/version/') {
179
- try {
180
- // 向本地 CDP 发请求,获取原始 JSON
181
- const jsonRes = await fetch(`http://127.0.0.1:${cdpPort}/json/version`);
182
- const data = await jsonRes.json();
183
- // 替换掉本地的 WebSocket 地址为代理暴露地址
184
- data.webSocketDebuggerUrl = data.webSocketDebuggerUrl.replace(
185
- `ws://127.0.0.1:${cdpPort}`,
186
- `wss://${req.headers.host}`
187
- );
188
- await logger.info('CDP version info requested', { url: req.url, response: data, sandboxId });
189
- Sentry.addBreadcrumb({
190
- category: 'http',
191
- message: 'CDP version info requested',
192
- level: 'info',
193
- data: { url: req.url, response: data, sandboxId }
194
- });
195
- res.writeHead(200, { 'Content-Type': 'application/json' });
196
- res.end(JSON.stringify(data));
197
- } catch(ex) {
198
- console.error('Failed to fetch CDP version:', ex.message);
199
- await logger.error('Failed to fetch CDP version', { error: ex.message, sandboxId });
200
- Sentry.captureException(ex, {
201
- tags: { type: 'cdp_version_error', sandboxId }
202
- });
203
- res.writeHead(500);
204
- res.end('Internal Server Error');
205
- }
206
- } else {
207
- proxy.web(req, res, {}, async (err) => {
208
- console.error('Proxy error:', err);
209
- await logger.error('HTTP proxy error', { error: err.message, url: req.url, sandboxId });
210
- Sentry.captureException(err, {
211
- tags: { type: 'proxy_error', sandboxId },
212
- extra: { url: req.url }
213
- });
214
- res.writeHead(502);
215
- res.end('Bad gateway');
216
- });
217
- }
218
- });
219
-
220
- server.on('upgrade', (req, socket, head) => {
221
- // 监听 WebSocket 数据
222
- let _buffers = [];
223
- socket.on('data', (data) => {
224
- let message = '';
225
- try {
226
- _buffers.push(data);
227
- // console.log(`💬 ${_buffers.length}`);
228
- message = parseWebSocketFrame(Buffer.concat(_buffers)); // 复制data不能破坏原始数据
229
- _buffers.length = 0;
230
- if (message.startsWith('{')){ // 只解析 JSON 消息
231
- const parsed = JSON.parse(message);
232
- console.log('📨 CDP WebSocket message:', parsed);
233
- logger.info('CDP WebSocket message received', {
234
- data: parsed,
235
- sandboxId,
236
- });
237
- Sentry.addBreadcrumb({
238
- category: 'websocket',
239
- message: 'CDP message received',
240
- level: 'debug',
241
- data: { ...parsed, sandboxId }
242
- });
243
- }
244
- } catch (err) {
245
- const msg = err.message;
246
- if(!msg.includes('Incomplete')) {
247
- // 记录解析错误
248
- console.warn('⚠️ Failed to parse CDP WebSocket message:', err.message, _buffers.length);
249
- _buffers.length = 0;
250
- Sentry.captureException(err, {
251
- tags: { type: 'websocket_error', sandboxId }
252
- });
253
- logger.warn('Failed to parse CDP WebSocket message', {
254
- error: err.message,
255
- data: message,
256
- sandboxId,
257
- });
258
- }
259
- }
260
- });
261
-
262
- socket.on('error', (err) => {
263
- console.error('❌ CDP WebSocket error:', err);
264
- logger.error('CDP WebSocket error', { error: err.message, sandboxId });
265
- Sentry.captureException(err, {
266
- tags: { type: 'websocket_error', sandboxId }
267
- });
268
- });
269
-
270
- proxy.ws(req, socket, head);
271
- });
272
-
273
- server.listen(cdpPort + 1, '0.0.0.0', () => {
274
- console.log(`🎯 Proxy server listening on http://0.0.0.0:${cdpPort + 1} → http://127.0.0.1:${cdpPort}`);
275
- logger.info('Proxy server started', {
276
- port: cdpPort + 1,
277
- target: cdpPort,
278
- sandboxId,
279
- settings: {
280
- type: 'chromium',
281
- args,
282
- headless,
283
- enableAdblock,
284
- timeoutMS,
285
- workspace,
286
- sandboxId
287
- },
288
- });
289
- Sentry.addBreadcrumb({
290
- category: 'server',
291
- message: 'Proxy server started',
292
- level: 'info',
293
- data: {
294
- port: cdpPort + 1,
295
- target: cdpPort,
296
- sandboxId,
297
- settings: {
298
- type: 'chromium',
299
- args,
300
- headless,
301
- enableAdblock,
302
- timeoutMS,
303
- workspace,
304
- sandboxId
305
- },
306
- }
307
- });
308
- });
309
- } catch (ex) {
310
- console.error('Failed to launch Chromium:', ex);
311
- logger.error('Failed to launch Chrome', {
312
- error: ex.message,
313
- args,
314
- headless,
315
- cdpPort,
316
- enableAdblock,
317
- sandboxId,
318
- });
319
- Sentry.captureException(ex, {
320
- tags: { type: 'launch_error', sandboxId },
321
- extra: { args, headless, cdpPort, enableAdblock }
322
- });
323
- process.exit(1);
324
- }