@andrewting19/oracle 0.11.0 → 0.11.2
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/dist/bin/oracle-cli.js +0 -0
- package/dist/bin/oracle-mcp.js +0 -0
- package/dist/src/browser/index.js +13 -0
- package/dist/src/remote/server.js +121 -17
- package/package.json +1 -1
package/dist/bin/oracle-cli.js
CHANGED
|
File without changes
|
package/dist/bin/oracle-mcp.js
CHANGED
|
File without changes
|
|
@@ -1402,6 +1402,19 @@ async function runRemoteBrowserMode(promptText, attachments, config, logger, opt
|
|
|
1402
1402
|
}
|
|
1403
1403
|
}
|
|
1404
1404
|
stopThinkingMonitor?.();
|
|
1405
|
+
// Capture the final conversation URL (chatgpt.com/c/<id>) for follow-up support.
|
|
1406
|
+
try {
|
|
1407
|
+
const { result: hrefResult } = await Runtime.evaluate({
|
|
1408
|
+
expression: "location.href",
|
|
1409
|
+
returnByValue: true,
|
|
1410
|
+
});
|
|
1411
|
+
if (typeof hrefResult?.value === "string" && hrefResult.value.includes("/c/")) {
|
|
1412
|
+
lastUrl = hrefResult.value;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
catch {
|
|
1416
|
+
// ignore — keep whatever lastUrl we have
|
|
1417
|
+
}
|
|
1405
1418
|
const durationMs = Date.now() - startedAt;
|
|
1406
1419
|
const answerChars = answerText.length;
|
|
1407
1420
|
const answerTokens = estimateTokenCount(answerMarkdown);
|
|
@@ -178,7 +178,24 @@ export async function createRemoteServer(options = {}, deps = {}) {
|
|
|
178
178
|
}
|
|
179
179
|
catch (error) {
|
|
180
180
|
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
-
|
|
181
|
+
// Detect Chrome/CDP target failures and trigger auto-recovery.
|
|
182
|
+
const lcMessage = message.toLowerCase();
|
|
183
|
+
const isChromeDown = lcMessage.includes("no inspectable targets") ||
|
|
184
|
+
lcMessage.includes("target closed") ||
|
|
185
|
+
lcMessage.includes("websocket connection closed") ||
|
|
186
|
+
lcMessage.includes("connection lost");
|
|
187
|
+
if (isChromeDown) {
|
|
188
|
+
sendEvent({
|
|
189
|
+
type: "error",
|
|
190
|
+
message: "Oracle's shared Chrome browser crashed or lost its CDP connection. " +
|
|
191
|
+
"Auto-recovery has been triggered — Chrome is being relaunched. " +
|
|
192
|
+
"Please retry this command in ~15 seconds.",
|
|
193
|
+
});
|
|
194
|
+
options.onChromeError?.();
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
sendEvent({ type: "error", message });
|
|
198
|
+
}
|
|
182
199
|
logger(`[serve] Run ${runId} failed after ${Date.now() - runStartedAt}ms: ${message}`);
|
|
183
200
|
}
|
|
184
201
|
finally {
|
|
@@ -274,6 +291,22 @@ export async function serveRemote(options = {}) {
|
|
|
274
291
|
let sharedChrome;
|
|
275
292
|
let sharedChromeProcess = null;
|
|
276
293
|
let sharedRootSession = null;
|
|
294
|
+
const SHARED_CHROME_FLAGS = [
|
|
295
|
+
"--no-startup-window",
|
|
296
|
+
"--no-first-run",
|
|
297
|
+
"--disable-background-networking",
|
|
298
|
+
"--disable-sync",
|
|
299
|
+
"--disable-translate",
|
|
300
|
+
"--disable-default-apps",
|
|
301
|
+
"--disable-hang-monitor",
|
|
302
|
+
"--disable-popup-blocking",
|
|
303
|
+
"--disable-features=TranslateUI,AutomationControlled",
|
|
304
|
+
"--remote-allow-origins=*",
|
|
305
|
+
"--mute-audio",
|
|
306
|
+
"--window-size=1280,720",
|
|
307
|
+
"--password-store=basic",
|
|
308
|
+
"--use-mock-keychain",
|
|
309
|
+
];
|
|
277
310
|
if (!preferManualLogin) {
|
|
278
311
|
try {
|
|
279
312
|
const userDataDir = await mkdtemp(path.join(os.tmpdir(), "oracle-serve-chrome-"));
|
|
@@ -281,22 +314,7 @@ export async function serveRemote(options = {}) {
|
|
|
281
314
|
// hidden CDP targets prevent focus stealing on macOS.
|
|
282
315
|
const { launch: launchChromeRaw } = await import("chrome-launcher");
|
|
283
316
|
const chrome = await launchChromeRaw({
|
|
284
|
-
chromeFlags:
|
|
285
|
-
"--no-startup-window",
|
|
286
|
-
"--no-first-run",
|
|
287
|
-
"--disable-background-networking",
|
|
288
|
-
"--disable-sync",
|
|
289
|
-
"--disable-translate",
|
|
290
|
-
"--disable-default-apps",
|
|
291
|
-
"--disable-hang-monitor",
|
|
292
|
-
"--disable-popup-blocking",
|
|
293
|
-
"--disable-features=TranslateUI,AutomationControlled",
|
|
294
|
-
"--remote-allow-origins=*",
|
|
295
|
-
"--mute-audio",
|
|
296
|
-
"--window-size=1280,720",
|
|
297
|
-
"--password-store=basic",
|
|
298
|
-
"--use-mock-keychain",
|
|
299
|
-
],
|
|
317
|
+
chromeFlags: SHARED_CHROME_FLAGS,
|
|
300
318
|
userDataDir,
|
|
301
319
|
handleSIGINT: false,
|
|
302
320
|
});
|
|
@@ -370,11 +388,97 @@ export async function serveRemote(options = {}) {
|
|
|
370
388
|
console.log(`Warning: failed to launch shared Chrome (${message}). Runs will each launch their own Chrome.`);
|
|
371
389
|
}
|
|
372
390
|
}
|
|
391
|
+
// --- Auto-recovery: relaunch shared Chrome when CDP targets become unavailable ---
|
|
392
|
+
let chromeRelaunching = false;
|
|
393
|
+
async function relaunchSharedChrome() {
|
|
394
|
+
if (chromeRelaunching)
|
|
395
|
+
return false;
|
|
396
|
+
chromeRelaunching = true;
|
|
397
|
+
try {
|
|
398
|
+
console.log("[serve] Relaunching shared Chrome...");
|
|
399
|
+
if (sharedRootSession) {
|
|
400
|
+
try {
|
|
401
|
+
await sharedRootSession.close();
|
|
402
|
+
}
|
|
403
|
+
catch { }
|
|
404
|
+
sharedRootSession = null;
|
|
405
|
+
}
|
|
406
|
+
if (sharedChromeProcess) {
|
|
407
|
+
try {
|
|
408
|
+
sharedChromeProcess.kill();
|
|
409
|
+
}
|
|
410
|
+
catch { }
|
|
411
|
+
sharedChromeProcess = null;
|
|
412
|
+
}
|
|
413
|
+
const userDataDir = await mkdtemp(path.join(os.tmpdir(), "oracle-serve-chrome-"));
|
|
414
|
+
const { launch: launchChromeRaw } = await import("chrome-launcher");
|
|
415
|
+
const chrome = await launchChromeRaw({
|
|
416
|
+
chromeFlags: SHARED_CHROME_FLAGS,
|
|
417
|
+
userDataDir,
|
|
418
|
+
handleSIGINT: false,
|
|
419
|
+
});
|
|
420
|
+
sharedChromeProcess = chrome;
|
|
421
|
+
console.log(`[serve] Chrome relaunched (pid ${chrome.pid}, port ${chrome.port}).`);
|
|
422
|
+
const CDP = (await import("chrome-remote-interface")).default;
|
|
423
|
+
const versionInfo = await CDP.Version({ host: "127.0.0.1", port: chrome.port });
|
|
424
|
+
const browserWsUrl = versionInfo.webSocketDebuggerUrl;
|
|
425
|
+
if (!browserWsUrl)
|
|
426
|
+
throw new Error("Chrome did not expose a browser WebSocket URL");
|
|
427
|
+
const rootSession = await CDP({ target: browserWsUrl });
|
|
428
|
+
sharedRootSession = rootSession;
|
|
429
|
+
// Re-sync cookies into the fresh Chrome.
|
|
430
|
+
try {
|
|
431
|
+
const { syncCookies } = await import("../browser/cookies.js");
|
|
432
|
+
let syncTargetId;
|
|
433
|
+
try {
|
|
434
|
+
const result = await rootSession.Target.createTarget({ url: "about:blank", hidden: true, background: true });
|
|
435
|
+
syncTargetId = result.targetId;
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
const result = await rootSession.Target.createTarget({ url: "about:blank", background: true, focus: false });
|
|
439
|
+
syncTargetId = result.targetId;
|
|
440
|
+
}
|
|
441
|
+
const syncClient = await CDP({ host: "127.0.0.1", port: chrome.port, target: syncTargetId });
|
|
442
|
+
await syncClient.Network.enable({});
|
|
443
|
+
const syncLogger = ((msg) => { if (msg)
|
|
444
|
+
console.log(`[shared-chrome] ${msg}`); });
|
|
445
|
+
syncLogger.verbose = false;
|
|
446
|
+
const count = await syncCookies(syncClient.Network, CHATGPT_URL, "Default", syncLogger, { allowErrors: true });
|
|
447
|
+
console.log(`[serve] Synced ${count} cookies into relaunched Chrome.`);
|
|
448
|
+
await syncClient.close();
|
|
449
|
+
await rootSession.Target.closeTarget({ targetId: syncTargetId });
|
|
450
|
+
}
|
|
451
|
+
catch (cookieErr) {
|
|
452
|
+
const msg = cookieErr instanceof Error ? cookieErr.message : String(cookieErr);
|
|
453
|
+
console.log(`[serve] Warning: cookie sync failed on relaunch (${msg}).`);
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
await hideChromeWindow(chrome, console.log);
|
|
457
|
+
}
|
|
458
|
+
catch { }
|
|
459
|
+
// Mutate the existing object so createRemoteServer's handler sees the new values.
|
|
460
|
+
if (sharedChrome) {
|
|
461
|
+
sharedChrome.port = chrome.port;
|
|
462
|
+
sharedChrome.rootSession = rootSession;
|
|
463
|
+
}
|
|
464
|
+
console.log("[serve] Shared Chrome recovery complete.");
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
469
|
+
console.log(`[serve] Failed to relaunch shared Chrome: ${msg}`);
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
chromeRelaunching = false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
373
476
|
const server = await createRemoteServer({
|
|
374
477
|
...options,
|
|
375
478
|
manualLoginDefault: preferManualLogin,
|
|
376
479
|
manualLoginProfileDir: manualProfileDir,
|
|
377
480
|
sharedChrome,
|
|
481
|
+
onChromeError: sharedChrome ? () => { void relaunchSharedChrome(); } : undefined,
|
|
378
482
|
});
|
|
379
483
|
await new Promise((resolve) => {
|
|
380
484
|
const shutdown = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrewting19/oracle",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
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",
|