@andrewting19/oracle 0.9.9 → 0.10.0
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.
|
@@ -123,14 +123,14 @@ export async function connectToChrome(port, logger, host) {
|
|
|
123
123
|
logger("Connected to Chrome DevTools protocol");
|
|
124
124
|
return client;
|
|
125
125
|
}
|
|
126
|
-
export async function connectToRemoteChrome(host, port, logger, targetUrl) {
|
|
126
|
+
export async function connectToRemoteChrome(host, port, logger, targetUrl, rootSession) {
|
|
127
127
|
if (targetUrl) {
|
|
128
128
|
const targetConnection = await connectToNewTarget(host, port, targetUrl, logger, {
|
|
129
|
-
opened: () => `Opened dedicated remote Chrome
|
|
129
|
+
opened: (targetId) => `Opened dedicated remote Chrome ${rootSession ? "hidden " : ""}target ${targetId}`,
|
|
130
130
|
openFailed: (message) => `Failed to open dedicated remote Chrome tab (${message}); falling back to first target.`,
|
|
131
131
|
attachFailed: (targetId, message) => `Failed to attach to dedicated remote Chrome tab ${targetId} (${message}); falling back to first target.`,
|
|
132
132
|
closeFailed: (targetId, message) => `Failed to close unused remote Chrome tab ${targetId}: ${message}`,
|
|
133
|
-
});
|
|
133
|
+
}, rootSession);
|
|
134
134
|
if (targetConnection) {
|
|
135
135
|
return { client: targetConnection.client, targetId: targetConnection.targetId };
|
|
136
136
|
}
|
|
@@ -154,9 +154,35 @@ export async function closeRemoteChromeTarget(host, port, targetId, logger) {
|
|
|
154
154
|
logger(`Failed to close remote Chrome tab ${targetId}: ${message}`);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
-
async function connectToNewTarget(host, port, url, logger, messages) {
|
|
157
|
+
async function connectToNewTarget(host, port, url, logger, messages, rootSession) {
|
|
158
158
|
try {
|
|
159
|
-
|
|
159
|
+
let target;
|
|
160
|
+
if (rootSession) {
|
|
161
|
+
// Hidden target: not in tab strip, no window, no focus steal.
|
|
162
|
+
// Lifetime tied to rootSession (kept alive by the serve).
|
|
163
|
+
const result = await rootSession.Target.createTarget({
|
|
164
|
+
url,
|
|
165
|
+
hidden: true,
|
|
166
|
+
background: true,
|
|
167
|
+
});
|
|
168
|
+
target = { id: result.targetId };
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// No root session: try background + focus:false, fallback to CDP.New()
|
|
172
|
+
try {
|
|
173
|
+
const bgClient = await CDP({ host, port });
|
|
174
|
+
const result = await bgClient.Target.createTarget({
|
|
175
|
+
url,
|
|
176
|
+
background: true,
|
|
177
|
+
focus: false,
|
|
178
|
+
});
|
|
179
|
+
target = { id: result.targetId };
|
|
180
|
+
await bgClient.close();
|
|
181
|
+
}
|
|
182
|
+
catch (_bgErr) {
|
|
183
|
+
target = await CDP.New({ host, port, url });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
160
186
|
try {
|
|
161
187
|
const client = await CDP({ host, port, target: target.id });
|
|
162
188
|
if (messages.opened) {
|
|
@@ -1023,8 +1023,8 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1023
1023
|
if (!remoteChromeConfig) {
|
|
1024
1024
|
throw new Error("Remote Chrome configuration missing. Pass --remote-chrome <host:port> to use this mode.");
|
|
1025
1025
|
}
|
|
1026
|
-
const { host, port } = remoteChromeConfig;
|
|
1027
|
-
logger(`Connecting to remote Chrome at ${host}:${port}`);
|
|
1026
|
+
const { host, port, rootSession } = remoteChromeConfig;
|
|
1027
|
+
logger(`Connecting to remote Chrome at ${host}:${port}${rootSession ? " (hidden targets)" : ""}`);
|
|
1028
1028
|
let client = null;
|
|
1029
1029
|
let remoteTargetId = null;
|
|
1030
1030
|
let lastUrl;
|
|
@@ -1054,7 +1054,7 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1054
1054
|
let stopThinkingMonitor = null;
|
|
1055
1055
|
let removeDialogHandler = null;
|
|
1056
1056
|
try {
|
|
1057
|
-
const connection = await connectToRemoteChrome(host, port, logger, config.url);
|
|
1057
|
+
const connection = await connectToRemoteChrome(host, port, logger, config.url, rootSession);
|
|
1058
1058
|
client = connection.client;
|
|
1059
1059
|
remoteTargetId = connection.targetId ?? null;
|
|
1060
1060
|
await emitRuntimeHint();
|
|
@@ -1069,8 +1069,19 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1069
1069
|
}
|
|
1070
1070
|
await Promise.all(domainEnablers);
|
|
1071
1071
|
removeDialogHandler = installJavaScriptDialogAutoDismissal(Page, logger);
|
|
1072
|
-
//
|
|
1073
|
-
|
|
1072
|
+
// Sync cookies into this tab for remote Chrome (needed for headed + Cloudflare).
|
|
1073
|
+
try {
|
|
1074
|
+
const cookieCount = await syncCookies(Network, config.url, config.chromeProfile, logger, {
|
|
1075
|
+
allowErrors: true,
|
|
1076
|
+
filterNames: config.cookieNames ?? undefined,
|
|
1077
|
+
cookiePath: config.chromeCookiePath ?? undefined,
|
|
1078
|
+
waitMs: config.cookieSyncWaitMs ?? 0,
|
|
1079
|
+
});
|
|
1080
|
+
logger(`Remote tab: synced ${cookieCount} cookies`);
|
|
1081
|
+
}
|
|
1082
|
+
catch (_syncErr) {
|
|
1083
|
+
logger("Remote tab: cookie sync failed, continuing with existing session");
|
|
1084
|
+
}
|
|
1074
1085
|
await navigateToChatGPT(Page, Runtime, config.url, logger);
|
|
1075
1086
|
await ensureNotBlocked(Runtime, config.headless, logger);
|
|
1076
1087
|
await ensureLoggedIn(Runtime, logger, { remoteSession: true });
|
|
@@ -269,12 +269,15 @@ export async function serveRemote(options = {}) {
|
|
|
269
269
|
console.log(`Detected ${cookies.length} ChatGPT cookies on this host; runs will reuse this session.`);
|
|
270
270
|
}
|
|
271
271
|
// Launch a shared Chrome instance for concurrent tab-based runs.
|
|
272
|
+
// Each run gets a hidden CDP target (no tab strip entry, no window, no focus stealing).
|
|
272
273
|
let sharedChrome;
|
|
274
|
+
let sharedChromeProcess = null;
|
|
275
|
+
let sharedRootSession = null;
|
|
273
276
|
if (!preferManualLogin) {
|
|
274
277
|
try {
|
|
275
278
|
const userDataDir = await mkdtemp(path.join(os.tmpdir(), "oracle-serve-chrome-"));
|
|
276
|
-
// Launch headed Chrome with
|
|
277
|
-
//
|
|
279
|
+
// Launch headed Chrome with no initial window. Headed mode passes Cloudflare;
|
|
280
|
+
// hidden CDP targets prevent focus stealing on macOS.
|
|
278
281
|
const { launch: launchChromeRaw } = await import("chrome-launcher");
|
|
279
282
|
const chrome = await launchChromeRaw({
|
|
280
283
|
chromeFlags: [
|
|
@@ -295,16 +298,23 @@ export async function serveRemote(options = {}) {
|
|
|
295
298
|
userDataDir,
|
|
296
299
|
handleSIGINT: false,
|
|
297
300
|
});
|
|
298
|
-
|
|
299
|
-
console.log(`Shared Chrome launched (pid ${chrome.pid}, port ${chrome.port}, no-startup-window).
|
|
300
|
-
//
|
|
301
|
-
//
|
|
301
|
+
sharedChromeProcess = chrome;
|
|
302
|
+
console.log(`Shared Chrome launched (pid ${chrome.pid}, port ${chrome.port}, headed + no-startup-window). Runs will use hidden CDP targets.`);
|
|
303
|
+
// Create a persistent root CDP session. Hidden target lifetimes are tied to
|
|
304
|
+
// the session that created them, so this must stay alive for the serve lifetime.
|
|
305
|
+
const CDP = (await import("chrome-remote-interface")).default;
|
|
306
|
+
const rootSession = await CDP({ host: "127.0.0.1", port: chrome.port });
|
|
307
|
+
sharedRootSession = rootSession;
|
|
308
|
+
// Sync ChatGPT cookies into the shared Chrome via a hidden target.
|
|
302
309
|
try {
|
|
303
310
|
const { syncCookies } = await import("../browser/cookies.js");
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
311
|
+
const { targetId } = await rootSession.Target.createTarget({
|
|
312
|
+
url: "about:blank",
|
|
313
|
+
hidden: true,
|
|
314
|
+
background: true,
|
|
315
|
+
});
|
|
316
|
+
const syncClient = await CDP({ host: "127.0.0.1", port: chrome.port, target: targetId });
|
|
317
|
+
const { Network } = syncClient;
|
|
308
318
|
await Network.enable({});
|
|
309
319
|
const syncLogger = ((msg) => { if (msg)
|
|
310
320
|
console.log(`[shared-chrome] ${msg}`); });
|
|
@@ -313,13 +323,14 @@ export async function serveRemote(options = {}) {
|
|
|
313
323
|
allowErrors: true,
|
|
314
324
|
});
|
|
315
325
|
console.log(`Synced ${count} ChatGPT cookies into shared Chrome.`);
|
|
316
|
-
await
|
|
317
|
-
await
|
|
326
|
+
await syncClient.close();
|
|
327
|
+
await rootSession.Target.closeTarget({ targetId });
|
|
318
328
|
}
|
|
319
329
|
catch (cookieErr) {
|
|
320
330
|
const msg = cookieErr instanceof Error ? cookieErr.message : String(cookieErr);
|
|
321
331
|
console.log(`Warning: failed to sync cookies into shared Chrome (${msg}). Runs may need to authenticate.`);
|
|
322
332
|
}
|
|
333
|
+
sharedChrome = { host: "127.0.0.1", port: chrome.port, rootSession };
|
|
323
334
|
}
|
|
324
335
|
catch (error) {
|
|
325
336
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -335,10 +346,27 @@ export async function serveRemote(options = {}) {
|
|
|
335
346
|
await new Promise((resolve) => {
|
|
336
347
|
const shutdown = () => {
|
|
337
348
|
console.log("Shutting down remote service...");
|
|
338
|
-
server
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
.
|
|
349
|
+
// Close root CDP session and kill shared Chrome before stopping the HTTP server.
|
|
350
|
+
const cleanupPromises = [];
|
|
351
|
+
if (sharedRootSession) {
|
|
352
|
+
cleanupPromises.push(sharedRootSession.close().catch((err) => {
|
|
353
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
354
|
+
console.error(`Failed to close root CDP session: ${msg}`);
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
if (sharedChromeProcess) {
|
|
358
|
+
cleanupPromises.push(Promise.resolve(sharedChromeProcess.kill()).catch((err) => {
|
|
359
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
360
|
+
console.error(`Failed to kill shared Chrome: ${msg}`);
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
Promise.all(cleanupPromises)
|
|
364
|
+
.finally(() => {
|
|
365
|
+
server
|
|
366
|
+
.close()
|
|
367
|
+
.catch((error) => console.error("Failed to close remote server:", error))
|
|
368
|
+
.finally(() => resolve());
|
|
369
|
+
});
|
|
342
370
|
};
|
|
343
371
|
process.on("SIGINT", shutdown);
|
|
344
372
|
process.on("SIGTERM", shutdown);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrewting19/oracle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "CLI wrapper around OpenAI Responses API with GPT-5.4 Pro, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/steipete/oracle#readme",
|