@sleepinsummer/agent-browser-cli 0.2.9 → 0.3.1-beta.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/AI_INSTALL.md +40 -13
- package/CHANGELOG.md +140 -0
- package/README.md +5 -4
- package/README_EN.md +5 -4
- package/assets/tmwd_cdp_bridge/background.js +382 -3
- package/assets/tmwd_cdp_bridge/content.js +7 -3
- package/assets/tmwd_cdp_bridge/icons/bot-128.png +0 -0
- package/assets/tmwd_cdp_bridge/icons/bot-16.png +0 -0
- package/assets/tmwd_cdp_bridge/icons/bot-32.png +0 -0
- package/assets/tmwd_cdp_bridge/icons/bot-48.png +0 -0
- package/assets/tmwd_cdp_bridge/manifest.json +32 -8
- package/npm/bin/agent-browser-cli.js +2 -1
- package/package.json +7 -6
- package/skills/agent-browser-cli/SKILL.md +112 -199
- package/skills/agent-browser-cli/references/operations.md +145 -0
|
@@ -28,11 +28,23 @@ async function handleExtMessage(msg, sender) {
|
|
|
28
28
|
if (msg.cmd === 'cdp') return await handleCDP(msg, sender);
|
|
29
29
|
if (msg.cmd === 'batch') return await handleBatch(msg, sender);
|
|
30
30
|
if (msg.cmd === 'openTab') return await handleOpenTab(msg);
|
|
31
|
+
if (msg.cmd === 'closeTab') return await handleCloseTab(msg, sender);
|
|
32
|
+
if (msg.cmd === 'networkStart') return await handleNetworkStart(msg, sender);
|
|
33
|
+
if (msg.cmd === 'networkList') return await handleNetworkList(msg, sender);
|
|
34
|
+
if (msg.cmd === 'networkDetail') return await handleNetworkDetail(msg, sender);
|
|
35
|
+
if (msg.cmd === 'networkClear') return await handleNetworkClear(msg, sender);
|
|
36
|
+
if (msg.cmd === 'networkStop') return await handleNetworkStop(msg, sender);
|
|
37
|
+
if (msg.cmd === 'consoleStart') return await handleConsoleStart(msg, sender);
|
|
38
|
+
if (msg.cmd === 'consoleList') return await handleConsoleList(msg, sender);
|
|
39
|
+
if (msg.cmd === 'consoleClear') return await handleConsoleClear(msg, sender);
|
|
40
|
+
if (msg.cmd === 'consoleStop') return await handleConsoleStop(msg, sender);
|
|
41
|
+
if (msg.cmd === 'debugClearAll') return await handleDebugClearAll();
|
|
31
42
|
if (msg.cmd === 'tabs') {
|
|
32
43
|
try {
|
|
33
44
|
if (msg.method === 'switch') {
|
|
34
45
|
const tab = await chrome.tabs.update(msg.tabId, { active: true });
|
|
35
|
-
|
|
46
|
+
// 默认只切换 Chrome 内部 active tab,不抢占系统前台窗口。
|
|
47
|
+
if (msg.allowFocus === true && tab.windowId) await chrome.windows.update(tab.windowId, { focused: true });
|
|
36
48
|
return { ok: true };
|
|
37
49
|
} else {
|
|
38
50
|
const tabs = (await chrome.tabs.query({})).filter(t => isScriptable(t.url));
|
|
@@ -128,18 +140,378 @@ async function handleCookies(msg, sender) {
|
|
|
128
140
|
}
|
|
129
141
|
}
|
|
130
142
|
|
|
143
|
+
|
|
144
|
+
const debugSessions = new Map();
|
|
145
|
+
|
|
146
|
+
function resolveTabId(msg, sender) {
|
|
147
|
+
const tabId = Number(msg.tabId || sender.tab?.id);
|
|
148
|
+
if (!Number.isInteger(tabId) || tabId <= 0) throw new Error('no tabId');
|
|
149
|
+
return tabId;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function getDebugSession(tabId) {
|
|
153
|
+
let session = debugSessions.get(tabId);
|
|
154
|
+
if (!session) {
|
|
155
|
+
session = {
|
|
156
|
+
tabId,
|
|
157
|
+
attached: false,
|
|
158
|
+
network: false,
|
|
159
|
+
console: false,
|
|
160
|
+
requests: new Map(),
|
|
161
|
+
requestOrder: [],
|
|
162
|
+
logs: []
|
|
163
|
+
};
|
|
164
|
+
debugSessions.set(tabId, session);
|
|
165
|
+
}
|
|
166
|
+
return session;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function ensureDebugAttached(session) {
|
|
170
|
+
if (session.attached) return;
|
|
171
|
+
await chrome.debugger.attach({ tabId: session.tabId }, '1.3');
|
|
172
|
+
session.attached = true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function detachDebugIfIdle(session) {
|
|
176
|
+
if (!session.attached || session.network || session.console) return;
|
|
177
|
+
try { await chrome.debugger.detach({ tabId: session.tabId }); } catch (_) {}
|
|
178
|
+
session.attached = false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function handleNetworkStart(msg, sender) {
|
|
182
|
+
try {
|
|
183
|
+
const tabId = resolveTabId(msg, sender);
|
|
184
|
+
const session = getDebugSession(tabId);
|
|
185
|
+
await ensureDebugAttached(session);
|
|
186
|
+
await chrome.debugger.sendCommand({ tabId }, 'Network.enable', {});
|
|
187
|
+
session.network = true;
|
|
188
|
+
return { ok: true, status: 'started', tabId, count: session.requestOrder.length };
|
|
189
|
+
} catch (e) {
|
|
190
|
+
return { ok: false, error: e.message };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function handleNetworkList(msg, sender) {
|
|
195
|
+
try {
|
|
196
|
+
const tabId = resolveTabId(msg, sender);
|
|
197
|
+
const session = getDebugSession(tabId);
|
|
198
|
+
const filter = String(msg.filter || '').toLowerCase();
|
|
199
|
+
const limit = Math.max(1, Math.min(Number(msg.limit || 100), 1000));
|
|
200
|
+
let items = session.requestOrder.map(id => session.requests.get(id)).filter(Boolean);
|
|
201
|
+
if (filter) {
|
|
202
|
+
items = items.filter(item => [item.url, item.method, item.status, item.mimeType, item.resourceType].some(v => String(v || '').toLowerCase().includes(filter)));
|
|
203
|
+
}
|
|
204
|
+
items = items.slice(-limit).map(summarizeRequest);
|
|
205
|
+
return { ok: true, status: session.network ? 'started' : 'stopped', tabId, count: items.length, requests: items };
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return { ok: false, error: e.message };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function handleNetworkDetail(msg, sender) {
|
|
212
|
+
try {
|
|
213
|
+
const tabId = resolveTabId(msg, sender);
|
|
214
|
+
const requestId = String(msg.requestId || '');
|
|
215
|
+
const session = getDebugSession(tabId);
|
|
216
|
+
const item = session.requests.get(requestId);
|
|
217
|
+
if (!item) return { ok: false, error: 'unknown requestId: ' + requestId };
|
|
218
|
+
const detail = Object.assign({}, item);
|
|
219
|
+
if (item.completed && !item.failed && session.attached) {
|
|
220
|
+
try {
|
|
221
|
+
const body = await chrome.debugger.sendCommand({ tabId }, 'Network.getResponseBody', { requestId });
|
|
222
|
+
detail.body = truncateText(body.body || '', 20000);
|
|
223
|
+
detail.base64Encoded = !!body.base64Encoded;
|
|
224
|
+
detail.bodyTruncated = String(body.body || '').length > 20000;
|
|
225
|
+
} catch (e) {
|
|
226
|
+
detail.bodyError = e.message;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { ok: true, tabId, request: detail };
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return { ok: false, error: e.message };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function handleNetworkClear(msg, sender) {
|
|
236
|
+
try {
|
|
237
|
+
const tabId = resolveTabId(msg, sender);
|
|
238
|
+
const session = getDebugSession(tabId);
|
|
239
|
+
session.requests.clear();
|
|
240
|
+
session.requestOrder = [];
|
|
241
|
+
return { ok: true, status: 'cleared', tabId };
|
|
242
|
+
} catch (e) {
|
|
243
|
+
return { ok: false, error: e.message };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function handleNetworkStop(msg, sender) {
|
|
248
|
+
try {
|
|
249
|
+
const tabId = resolveTabId(msg, sender);
|
|
250
|
+
const session = getDebugSession(tabId);
|
|
251
|
+
if (session.attached) {
|
|
252
|
+
try { await chrome.debugger.sendCommand({ tabId }, 'Network.disable', {}); } catch (_) {}
|
|
253
|
+
}
|
|
254
|
+
session.network = false;
|
|
255
|
+
session.requests.clear();
|
|
256
|
+
session.requestOrder = [];
|
|
257
|
+
await detachDebugIfIdle(session);
|
|
258
|
+
return { ok: true, status: 'stopped', cleared: true, tabId };
|
|
259
|
+
} catch (e) {
|
|
260
|
+
return { ok: false, error: e.message };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function handleConsoleStart(msg, sender) {
|
|
265
|
+
try {
|
|
266
|
+
const tabId = resolveTabId(msg, sender);
|
|
267
|
+
const session = getDebugSession(tabId);
|
|
268
|
+
await ensureDebugAttached(session);
|
|
269
|
+
await chrome.debugger.sendCommand({ tabId }, 'Runtime.enable', {});
|
|
270
|
+
await chrome.debugger.sendCommand({ tabId }, 'Log.enable', {}).catch(() => null);
|
|
271
|
+
session.console = true;
|
|
272
|
+
return { ok: true, status: 'started', tabId, count: session.logs.length };
|
|
273
|
+
} catch (e) {
|
|
274
|
+
return { ok: false, error: e.message };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function handleConsoleList(msg, sender) {
|
|
279
|
+
try {
|
|
280
|
+
const tabId = resolveTabId(msg, sender);
|
|
281
|
+
const session = getDebugSession(tabId);
|
|
282
|
+
const level = String(msg.level || '').toLowerCase();
|
|
283
|
+
const limit = Math.max(1, Math.min(Number(msg.limit || 100), 1000));
|
|
284
|
+
let logs = session.logs;
|
|
285
|
+
if (level) logs = logs.filter(item => String(item.level || '').toLowerCase() === level);
|
|
286
|
+
return { ok: true, status: session.console ? 'started' : 'stopped', tabId, count: logs.length, logs: logs.slice(-limit) };
|
|
287
|
+
} catch (e) {
|
|
288
|
+
return { ok: false, error: e.message };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function handleConsoleClear(msg, sender) {
|
|
293
|
+
try {
|
|
294
|
+
const tabId = resolveTabId(msg, sender);
|
|
295
|
+
const session = getDebugSession(tabId);
|
|
296
|
+
session.logs = [];
|
|
297
|
+
return { ok: true, status: 'cleared', tabId };
|
|
298
|
+
} catch (e) {
|
|
299
|
+
return { ok: false, error: e.message };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function handleConsoleStop(msg, sender) {
|
|
304
|
+
try {
|
|
305
|
+
const tabId = resolveTabId(msg, sender);
|
|
306
|
+
const session = getDebugSession(tabId);
|
|
307
|
+
if (session.attached) {
|
|
308
|
+
try { await chrome.debugger.sendCommand({ tabId }, 'Runtime.disable', {}); } catch (_) {}
|
|
309
|
+
try { await chrome.debugger.sendCommand({ tabId }, 'Log.disable', {}); } catch (_) {}
|
|
310
|
+
}
|
|
311
|
+
session.console = false;
|
|
312
|
+
session.logs = [];
|
|
313
|
+
await detachDebugIfIdle(session);
|
|
314
|
+
return { ok: true, status: 'stopped', cleared: true, tabId };
|
|
315
|
+
} catch (e) {
|
|
316
|
+
return { ok: false, error: e.message };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function summarizeRequest(item) {
|
|
321
|
+
return {
|
|
322
|
+
requestId: item.requestId,
|
|
323
|
+
url: item.url,
|
|
324
|
+
method: item.method,
|
|
325
|
+
status: item.status,
|
|
326
|
+
mimeType: item.mimeType,
|
|
327
|
+
resourceType: item.resourceType,
|
|
328
|
+
completed: !!item.completed,
|
|
329
|
+
failed: !!item.failed,
|
|
330
|
+
errorText: item.errorText,
|
|
331
|
+
timestamp: item.timestamp
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function rememberRequest(session, requestId) {
|
|
336
|
+
if (!session.requests.has(requestId)) session.requestOrder.push(requestId);
|
|
337
|
+
while (session.requestOrder.length > 1000) {
|
|
338
|
+
const old = session.requestOrder.shift();
|
|
339
|
+
session.requests.delete(old);
|
|
340
|
+
}
|
|
341
|
+
let item = session.requests.get(requestId);
|
|
342
|
+
if (!item) {
|
|
343
|
+
item = { requestId };
|
|
344
|
+
session.requests.set(requestId, item);
|
|
345
|
+
}
|
|
346
|
+
return item;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function pushLog(session, item) {
|
|
350
|
+
session.logs.push(item);
|
|
351
|
+
if (session.logs.length > 1000) session.logs.splice(0, session.logs.length - 1000);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function remoteObjectText(arg) {
|
|
355
|
+
if (!arg) return '';
|
|
356
|
+
if ('value' in arg) {
|
|
357
|
+
if (typeof arg.value === 'string') return truncateText(arg.value, 2000);
|
|
358
|
+
try { return truncateText(JSON.stringify(arg.value), 2000); } catch (_) { return String(arg.value); }
|
|
359
|
+
}
|
|
360
|
+
return truncateText(arg.description || arg.type || '', 2000);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function truncateText(text, max) {
|
|
364
|
+
const value = String(text || '');
|
|
365
|
+
return value.length > max ? value.slice(0, max) : value;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function onDebuggerEvent(source, method, params) {
|
|
369
|
+
const tabId = source.tabId;
|
|
370
|
+
if (!tabId) return;
|
|
371
|
+
const session = debugSessions.get(tabId);
|
|
372
|
+
if (!session) return;
|
|
373
|
+
if (method === 'Network.requestWillBeSent') {
|
|
374
|
+
const item = rememberRequest(session, params.requestId);
|
|
375
|
+
item.url = params.request?.url || item.url;
|
|
376
|
+
item.method = params.request?.method || item.method;
|
|
377
|
+
item.resourceType = params.type || item.resourceType;
|
|
378
|
+
item.timestamp = params.wallTime || params.timestamp || item.timestamp;
|
|
379
|
+
item.requestHeaders = params.request?.headers;
|
|
380
|
+
} else if (method === 'Network.responseReceived') {
|
|
381
|
+
const item = rememberRequest(session, params.requestId);
|
|
382
|
+
item.status = params.response?.status;
|
|
383
|
+
item.statusText = params.response?.statusText;
|
|
384
|
+
item.mimeType = params.response?.mimeType;
|
|
385
|
+
item.responseHeaders = params.response?.headers;
|
|
386
|
+
item.url = params.response?.url || item.url;
|
|
387
|
+
item.resourceType = params.type || item.resourceType;
|
|
388
|
+
} else if (method === 'Network.loadingFinished') {
|
|
389
|
+
const item = rememberRequest(session, params.requestId);
|
|
390
|
+
item.completed = true;
|
|
391
|
+
item.encodedDataLength = params.encodedDataLength;
|
|
392
|
+
} else if (method === 'Network.loadingFailed') {
|
|
393
|
+
const item = rememberRequest(session, params.requestId);
|
|
394
|
+
item.completed = true;
|
|
395
|
+
item.failed = true;
|
|
396
|
+
item.errorText = params.errorText;
|
|
397
|
+
} else if (method === 'Runtime.consoleAPICalled') {
|
|
398
|
+
pushLog(session, {
|
|
399
|
+
level: params.type || 'log',
|
|
400
|
+
text: (params.args || []).map(remoteObjectText).join(' '),
|
|
401
|
+
timestamp: params.timestamp || Date.now(),
|
|
402
|
+
url: params.stackTrace?.callFrames?.[0]?.url || '',
|
|
403
|
+
line: params.stackTrace?.callFrames?.[0]?.lineNumber,
|
|
404
|
+
column: params.stackTrace?.callFrames?.[0]?.columnNumber
|
|
405
|
+
});
|
|
406
|
+
} else if (method === 'Runtime.exceptionThrown') {
|
|
407
|
+
pushLog(session, {
|
|
408
|
+
level: 'error',
|
|
409
|
+
text: params.exceptionDetails?.text || params.exceptionDetails?.exception?.description || 'exception thrown',
|
|
410
|
+
timestamp: params.timestamp || Date.now(),
|
|
411
|
+
url: params.exceptionDetails?.url || '',
|
|
412
|
+
line: params.exceptionDetails?.lineNumber,
|
|
413
|
+
column: params.exceptionDetails?.columnNumber
|
|
414
|
+
});
|
|
415
|
+
} else if (method === 'Log.entryAdded') {
|
|
416
|
+
const e = params.entry || {};
|
|
417
|
+
pushLog(session, {
|
|
418
|
+
level: e.level || 'log',
|
|
419
|
+
text: e.text || '',
|
|
420
|
+
timestamp: e.timestamp || Date.now(),
|
|
421
|
+
url: e.url || '',
|
|
422
|
+
line: e.lineNumber,
|
|
423
|
+
column: undefined
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function onDebuggerDetach(source) {
|
|
429
|
+
if (!source.tabId) return;
|
|
430
|
+
const session = debugSessions.get(source.tabId);
|
|
431
|
+
if (session) {
|
|
432
|
+
session.attached = false;
|
|
433
|
+
session.network = false;
|
|
434
|
+
session.console = false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
chrome.debugger.onEvent.addListener(onDebuggerEvent);
|
|
439
|
+
chrome.debugger.onDetach.addListener(onDebuggerDetach);
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
async function handleCloseTab(msg, sender) {
|
|
444
|
+
try {
|
|
445
|
+
const tabId = Number(msg.tabId || sender.tab?.id);
|
|
446
|
+
if (!Number.isInteger(tabId) || tabId <= 0) throw new Error('tabId is required');
|
|
447
|
+
const session = debugSessions.get(tabId);
|
|
448
|
+
if (session?.attached) {
|
|
449
|
+
try { await chrome.debugger.detach({ tabId }); } catch (_) {}
|
|
450
|
+
}
|
|
451
|
+
debugSessions.delete(tabId);
|
|
452
|
+
await chrome.tabs.remove(tabId);
|
|
453
|
+
return { ok: true, data: { status: 'success', tabId } };
|
|
454
|
+
} catch (e) {
|
|
455
|
+
return { ok: false, error: e.message };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function handleDebugClearAll() {
|
|
460
|
+
const tabIds = [...debugSessions.keys()];
|
|
461
|
+
for (const tabId of tabIds) {
|
|
462
|
+
const session = debugSessions.get(tabId);
|
|
463
|
+
if (!session) continue;
|
|
464
|
+
session.requests.clear();
|
|
465
|
+
session.requestOrder = [];
|
|
466
|
+
session.logs = [];
|
|
467
|
+
session.network = false;
|
|
468
|
+
session.console = false;
|
|
469
|
+
if (session.attached) {
|
|
470
|
+
try { await chrome.debugger.detach({ tabId }); } catch (_) {}
|
|
471
|
+
session.attached = false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
debugSessions.clear();
|
|
475
|
+
return { ok: true, status: 'cleared', tabs: tabIds.length };
|
|
476
|
+
}
|
|
477
|
+
|
|
131
478
|
async function handleOpenTab(msg) {
|
|
132
479
|
try {
|
|
133
480
|
const url = normalizeOpenUrl(msg.url);
|
|
134
481
|
const active = msg.active !== false;
|
|
135
482
|
const tab = await chrome.tabs.create({ url, active });
|
|
136
|
-
|
|
137
|
-
|
|
483
|
+
const group = await groupTabIfRequested(tab.id, msg.groupTitle);
|
|
484
|
+
// 默认创建/激活标签页但不聚焦浏览器窗口,避免打断当前工作区。
|
|
485
|
+
if (active && msg.allowFocus === true && tab.windowId) await chrome.windows.update(tab.windowId, { focused: true });
|
|
486
|
+
return { ok: true, data: { id: tab.id, url: tab.url || url, title: tab.title || '', active: tab.active, windowId: tab.windowId, group } };
|
|
138
487
|
} catch (e) {
|
|
139
488
|
return { ok: false, error: e.message };
|
|
140
489
|
}
|
|
141
490
|
}
|
|
142
491
|
|
|
492
|
+
async function groupTabIfRequested(tabId, title) {
|
|
493
|
+
const cleanTitle = String(title || '').trim();
|
|
494
|
+
if (!cleanTitle) return null;
|
|
495
|
+
if (!chrome.tabs?.group || !chrome.tabGroups?.update) {
|
|
496
|
+
return { ok: false, skipped: true, reason: 'tabGroups API unavailable' };
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
const existing = await findTabGroupByTitle(cleanTitle);
|
|
500
|
+
const groupId = await chrome.tabs.group(existing ? { tabIds: tabId, groupId: existing.id } : { tabIds: tabId });
|
|
501
|
+
await chrome.tabGroups.update(groupId, { title: cleanTitle });
|
|
502
|
+
return { ok: true, id: groupId, title: cleanTitle };
|
|
503
|
+
} catch (e) {
|
|
504
|
+
// 分组只是整理标签,不影响打开页面的主流程。
|
|
505
|
+
return { ok: false, skipped: true, reason: e.message };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async function findTabGroupByTitle(title) {
|
|
510
|
+
if (!chrome.tabGroups?.query) return null;
|
|
511
|
+
const groups = await chrome.tabGroups.query({});
|
|
512
|
+
return groups.find(group => group.title === title) || null;
|
|
513
|
+
}
|
|
514
|
+
|
|
143
515
|
function normalizeOpenUrl(url) {
|
|
144
516
|
const raw = String(url || '').trim();
|
|
145
517
|
if (!raw) throw new Error('url is required');
|
|
@@ -162,6 +534,10 @@ async function handleBatch(msg, sender) {
|
|
|
162
534
|
R.push({ ok: true, data: tabs.map(t => ({ id: t.id, url: t.url, title: t.title, active: t.active, windowId: t.windowId })) });
|
|
163
535
|
} else if (c.cmd === 'cdp') {
|
|
164
536
|
const tabId = c.tabId || msg.tabId || sender.tab?.id;
|
|
537
|
+
if (c.method === 'Page.bringToFront' && c.allowFocus !== true && msg.allowFocus !== true) {
|
|
538
|
+
R.push({ skipped: true, reason: 'Page.bringToFront requires allowFocus=true' });
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
165
541
|
if (attached !== tabId) {
|
|
166
542
|
if (attached) { await chrome.debugger.detach({ tabId: attached }); attached = null; }
|
|
167
543
|
await chrome.debugger.attach({ tabId }, '1.3');
|
|
@@ -183,6 +559,9 @@ async function handleBatch(msg, sender) {
|
|
|
183
559
|
async function handleCDP(msg, sender) {
|
|
184
560
|
const tabId = msg.tabId || sender.tab?.id;
|
|
185
561
|
if (!tabId) return { ok: false, error: 'no tabId' };
|
|
562
|
+
if (msg.method === 'Page.bringToFront' && msg.allowFocus !== true) {
|
|
563
|
+
return { ok: true, data: { skipped: true, reason: 'Page.bringToFront requires allowFocus=true' } };
|
|
564
|
+
}
|
|
186
565
|
try {
|
|
187
566
|
await chrome.debugger.attach({ tabId }, '1.3');
|
|
188
567
|
const result = await chrome.debugger.sendCommand({ tabId }, msg.method, msg.params || {});
|
|
@@ -142,13 +142,17 @@ async function handle(el) {
|
|
|
142
142
|
if (cmd === 'cookies') {
|
|
143
143
|
resp = await chrome.runtime.sendMessage({ cmd: 'cookies', url: req.url || location.href });
|
|
144
144
|
} else if (cmd === 'cdp') {
|
|
145
|
-
resp = await chrome.runtime.sendMessage({ cmd: 'cdp', method: req.method, params: req.params || {}, tabId: req.tabId });
|
|
145
|
+
resp = await chrome.runtime.sendMessage({ cmd: 'cdp', method: req.method, params: req.params || {}, tabId: req.tabId, allowFocus: req.allowFocus });
|
|
146
146
|
} else if (cmd === 'batch') {
|
|
147
147
|
resp = await chrome.runtime.sendMessage({ cmd: 'batch', commands: req.commands, tabId: req.tabId });
|
|
148
148
|
} else if (cmd === 'tabs') {
|
|
149
|
-
resp = await chrome.runtime.sendMessage({ cmd: 'tabs', method: req.method, tabId: req.tabId });
|
|
149
|
+
resp = await chrome.runtime.sendMessage({ cmd: 'tabs', method: req.method, tabId: req.tabId, allowFocus: req.allowFocus });
|
|
150
150
|
} else if (cmd === 'openTab') {
|
|
151
|
-
resp = await chrome.runtime.sendMessage({ cmd: 'openTab', url: req.url, active: req.active });
|
|
151
|
+
resp = await chrome.runtime.sendMessage({ cmd: 'openTab', url: req.url, active: req.active, allowFocus: req.allowFocus, groupTitle: req.groupTitle });
|
|
152
|
+
} else if (cmd === 'closeTab') {
|
|
153
|
+
resp = await chrome.runtime.sendMessage({ cmd: 'closeTab', tabId: req.tabId });
|
|
154
|
+
} else if (cmd === 'networkStart' || cmd === 'networkList' || cmd === 'networkDetail' || cmd === 'networkClear' || cmd === 'networkStop' || cmd === 'consoleStart' || cmd === 'consoleList' || cmd === 'consoleClear' || cmd === 'consoleStop' || cmd === 'debugClearAll') {
|
|
155
|
+
resp = await chrome.runtime.sendMessage(Object.assign({}, req, { cmd }));
|
|
152
156
|
} else {
|
|
153
157
|
resp = { ok: false, error: 'unknown cmd: ' + cmd };
|
|
154
158
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
|
-
"name": "
|
|
3
|
+
"name": "Agent Browser CLI Bridge",
|
|
4
4
|
"version": "2.0",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Browser control bridge for agent-browser-cli",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"cookies",
|
|
8
8
|
"tabs",
|
|
9
|
+
"tabGroups",
|
|
9
10
|
"activeTab",
|
|
10
11
|
"debugger",
|
|
11
12
|
"scripting",
|
|
@@ -15,27 +16,50 @@
|
|
|
15
16
|
"management",
|
|
16
17
|
"contentSettings"
|
|
17
18
|
],
|
|
18
|
-
"host_permissions": [
|
|
19
|
+
"host_permissions": [
|
|
20
|
+
"<all_urls>"
|
|
21
|
+
],
|
|
19
22
|
"background": {
|
|
20
23
|
"service_worker": "background.js"
|
|
21
24
|
},
|
|
22
25
|
"content_scripts": [
|
|
23
26
|
{
|
|
24
|
-
"matches": [
|
|
25
|
-
|
|
27
|
+
"matches": [
|
|
28
|
+
"<all_urls>"
|
|
29
|
+
],
|
|
30
|
+
"js": [
|
|
31
|
+
"disable_dialogs.js"
|
|
32
|
+
],
|
|
26
33
|
"run_at": "document_start",
|
|
27
34
|
"all_frames": true,
|
|
28
35
|
"world": "MAIN"
|
|
29
36
|
},
|
|
30
37
|
{
|
|
31
|
-
"matches": [
|
|
32
|
-
|
|
38
|
+
"matches": [
|
|
39
|
+
"<all_urls>"
|
|
40
|
+
],
|
|
41
|
+
"js": [
|
|
42
|
+
"config.js",
|
|
43
|
+
"content.js"
|
|
44
|
+
],
|
|
33
45
|
"run_at": "document_idle",
|
|
34
46
|
"all_frames": true
|
|
35
47
|
}
|
|
36
48
|
],
|
|
37
49
|
"action": {
|
|
38
50
|
"default_popup": "popup.html",
|
|
39
|
-
"default_title": "
|
|
51
|
+
"default_title": "Agent Browser CLI Bridge",
|
|
52
|
+
"default_icon": {
|
|
53
|
+
"16": "icons/bot-16.png",
|
|
54
|
+
"32": "icons/bot-32.png",
|
|
55
|
+
"48": "icons/bot-48.png",
|
|
56
|
+
"128": "icons/bot-128.png"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"icons": {
|
|
60
|
+
"16": "icons/bot-16.png",
|
|
61
|
+
"32": "icons/bot-32.png",
|
|
62
|
+
"48": "icons/bot-48.png",
|
|
63
|
+
"128": "icons/bot-128.png"
|
|
40
64
|
}
|
|
41
65
|
}
|
|
@@ -29,7 +29,8 @@ function resolveBinary() {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const bin = resolveBinary();
|
|
32
|
-
const
|
|
32
|
+
const env = { ...process.env, AGENT_BROWSER_CLI_PACKAGE_DIR: path.resolve(__dirname, "..") };
|
|
33
|
+
const result = spawnSync(bin, process.argv.slice(2), { stdio: "inherit", env });
|
|
33
34
|
if (result.error) {
|
|
34
35
|
console.error(result.error.message);
|
|
35
36
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleepinsummer/agent-browser-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1-beta.1",
|
|
4
4
|
"description": "Agent-oriented browser sensing and control CLI backed by a native Rust daemon.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"README.md",
|
|
15
15
|
"README_EN.md",
|
|
16
16
|
"AI_INSTALL.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
17
18
|
"LICENSE"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
@@ -23,11 +24,11 @@
|
|
|
23
24
|
"postinstall": "node npm/postinstall.js"
|
|
24
25
|
},
|
|
25
26
|
"optionalDependencies": {
|
|
26
|
-
"@sleepinsummer/agent-browser-cli-darwin-arm64": "0.
|
|
27
|
-
"@sleepinsummer/agent-browser-cli-darwin-x64": "0.
|
|
28
|
-
"@sleepinsummer/agent-browser-cli-linux-x64": "0.
|
|
29
|
-
"@sleepinsummer/agent-browser-cli-linux-arm64": "0.
|
|
30
|
-
"@sleepinsummer/agent-browser-cli-win32-x64": "0.
|
|
27
|
+
"@sleepinsummer/agent-browser-cli-darwin-arm64": "0.3.1-beta.1",
|
|
28
|
+
"@sleepinsummer/agent-browser-cli-darwin-x64": "0.3.1-beta.1",
|
|
29
|
+
"@sleepinsummer/agent-browser-cli-linux-x64": "0.3.1-beta.1",
|
|
30
|
+
"@sleepinsummer/agent-browser-cli-linux-arm64": "0.3.1-beta.1",
|
|
31
|
+
"@sleepinsummer/agent-browser-cli-win32-x64": "0.3.1-beta.1"
|
|
31
32
|
},
|
|
32
33
|
"engines": {
|
|
33
34
|
"node": ">=18"
|