@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.
File without changes
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
- sendEvent({ type: "error", message });
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.0",
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",