chrome-ai-bridge 1.0.16 → 1.0.18
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/build/src/browser.js +30 -1
- package/build/src/cli.js +5 -0
- package/build/src/main.js +1 -0
- package/build/src/tools/gemini-web.js +14 -19
- package/package.json +1 -1
package/build/src/browser.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
6
7
|
import fs from 'node:fs';
|
|
7
8
|
import os from 'node:os';
|
|
8
9
|
import path from 'node:path';
|
|
@@ -421,7 +422,7 @@ export function getDevelopmentExtensionPaths() {
|
|
|
421
422
|
return developmentExtensionPaths;
|
|
422
423
|
}
|
|
423
424
|
export async function launch(options) {
|
|
424
|
-
const { channel, executablePath, customDevTools, headless, isolated, loadExtension, loadExtensionsDir, loadSystemExtensions, chromeProfile, } = options;
|
|
425
|
+
const { channel, executablePath, customDevTools, headless, isolated, loadExtension, loadExtensionsDir, loadSystemExtensions, chromeProfile, focus = false, } = options;
|
|
425
426
|
// Reset development extension paths
|
|
426
427
|
developmentExtensionPaths = [];
|
|
427
428
|
// Resolve user data directory using new profile resolver (v0.15.0+)
|
|
@@ -562,6 +563,22 @@ export async function launch(options) {
|
|
|
562
563
|
}
|
|
563
564
|
let browser;
|
|
564
565
|
let finalUserDataDir = userDataDir;
|
|
566
|
+
// Remember current foreground app before Chrome launch (for background mode)
|
|
567
|
+
let previousApp = null;
|
|
568
|
+
if (!focus && !effectiveHeadless && os.platform() === 'darwin') {
|
|
569
|
+
try {
|
|
570
|
+
previousApp = execSync(`osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'`, { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
571
|
+
console.error(`📋 Current foreground app: ${previousApp}`);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
console.warn(`⚠️ Could not detect foreground app: ${error instanceof Error ? error.message : String(error)}`);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Windows/Linux: Add --start-minimized for background mode
|
|
578
|
+
if (!focus && !effectiveHeadless && os.platform() !== 'darwin') {
|
|
579
|
+
args.push('--start-minimized');
|
|
580
|
+
console.error('📋 Added --start-minimized for background mode');
|
|
581
|
+
}
|
|
565
582
|
try {
|
|
566
583
|
browser = await puppeteer.launch({
|
|
567
584
|
...connectOptions,
|
|
@@ -660,6 +677,18 @@ export async function launch(options) {
|
|
|
660
677
|
});
|
|
661
678
|
console.error('Applied navigator.webdriver bypass to existing page');
|
|
662
679
|
}
|
|
680
|
+
// Restore focus to previous app (background mode on macOS)
|
|
681
|
+
if (!focus && !effectiveHeadless && previousApp && os.platform() === 'darwin') {
|
|
682
|
+
try {
|
|
683
|
+
// Small delay to ensure Chrome window is fully rendered
|
|
684
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
685
|
+
execSync(`osascript -e 'tell application "${previousApp}" to activate'`, { timeout: 5000 });
|
|
686
|
+
console.error(`✅ Restored focus to: ${previousApp}`);
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
console.warn(`⚠️ Could not restore focus: ${error instanceof Error ? error.message : String(error)}`);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
663
692
|
return browser;
|
|
664
693
|
}
|
|
665
694
|
catch (error) {
|
package/build/src/cli.js
CHANGED
|
@@ -79,6 +79,11 @@ export const cliOptions = {
|
|
|
79
79
|
description: 'Explicitly specify the project root directory for profile isolation. Overrides MCP roots/list. Useful when roots/list is not available.',
|
|
80
80
|
conflicts: 'browserUrl',
|
|
81
81
|
},
|
|
82
|
+
focus: {
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
description: 'Bring Chrome window to foreground on launch. By default, Chrome launches in the background to avoid interrupting your work.',
|
|
85
|
+
default: false,
|
|
86
|
+
},
|
|
82
87
|
};
|
|
83
88
|
export function parseArguments(version, argv = process.argv) {
|
|
84
89
|
const yargsInstance = yargs(hideBin(argv))
|
package/build/src/main.js
CHANGED
|
@@ -140,6 +140,7 @@ async function getContext() {
|
|
|
140
140
|
userDataDir: args.userDataDir,
|
|
141
141
|
logFile,
|
|
142
142
|
rootsInfo: cachedRootsInfo, // Pass roots info to browser
|
|
143
|
+
focus: args.focus, // v1.0.18: Background mode control
|
|
143
144
|
};
|
|
144
145
|
const browser = await resolveBrowser(browserOptions);
|
|
145
146
|
// Announce browser PID for graceful shutdown
|
|
@@ -267,32 +267,27 @@ export const askGeminiWeb = defineTool({
|
|
|
267
267
|
}
|
|
268
268
|
response.appendResponseLine('✅ ログイン確認完了');
|
|
269
269
|
response.appendResponseLine('質問を送信中...');
|
|
270
|
-
// Input text using
|
|
271
|
-
//
|
|
270
|
+
// Input text using innerText + event dispatch for proper Angular state updates
|
|
271
|
+
// This is more reliable than keyboard.type() which can cause sync issues with Angular
|
|
272
272
|
const textboxSelector = '[role="textbox"]';
|
|
273
273
|
const textbox = await page.$(textboxSelector);
|
|
274
274
|
if (!textbox) {
|
|
275
275
|
response.appendResponseLine('❌ 入力欄が見つかりません');
|
|
276
276
|
return;
|
|
277
277
|
}
|
|
278
|
-
// Click to focus
|
|
279
|
-
await textbox.click(
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
await page.keyboard.press('Enter');
|
|
290
|
-
await page.keyboard.up('Shift');
|
|
291
|
-
}
|
|
292
|
-
await page.keyboard.type(lines[i], { delay: 2 });
|
|
293
|
-
}
|
|
278
|
+
// Click to focus
|
|
279
|
+
await textbox.click();
|
|
280
|
+
// Use innerText + input event dispatch for proper Angular state updates
|
|
281
|
+
// This approach is more reliable than keyboard.type() which can cause sync issues
|
|
282
|
+
await textbox.evaluate((el, text) => {
|
|
283
|
+
// Clear and set content
|
|
284
|
+
el.innerText = text;
|
|
285
|
+
// Dispatch input event to notify Angular of the change
|
|
286
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
287
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
288
|
+
}, sanitizedQuestion);
|
|
294
289
|
// Wait for Angular to process the input and show send button
|
|
295
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
290
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
296
291
|
// 質問送信前に model-response 要素数を記録(ChatGPTと同じカウント方式)
|
|
297
292
|
const initialModelResponseCount = await page.evaluate(() => {
|
|
298
293
|
return document.querySelectorAll('model-response').length;
|
package/package.json
CHANGED