@steipete/oracle 0.8.6 → 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.
- package/LICENSE +1 -1
- package/README.md +130 -45
- package/dist/bin/oracle-cli.js +613 -379
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +314 -104
- package/dist/src/browser/actions/navigation.js +161 -136
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +452 -303
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
- package/dist/src/browser/providers/index.js +2 -0
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +65 -45
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +7 -4
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +11 -0
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +12 -8
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +145 -87
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +37 -25
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +182 -79
- package/dist/src/cli/sessionLineage.js +60 -0
- package/dist/src/cli/sessionRunner.js +118 -90
- package/dist/src/cli/sessionTable.js +28 -24
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +140 -127
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +80 -0
- package/dist/src/gemini-web/client.js +81 -64
- package/dist/src/gemini-web/executionMode.js +16 -0
- package/dist/src/gemini-web/executor.js +327 -169
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +81 -64
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +84 -46
- package/dist/src/oracle/config.js +124 -58
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +69 -45
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -30
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +23 -15
- package/dist/src/oracle/run.js +172 -140
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +81 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +69 -65
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
- /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import { CLOUDFLARE_SCRIPT_SELECTOR, CLOUDFLARE_TITLE, INPUT_SELECTORS
|
|
2
|
-
import { delay } from
|
|
3
|
-
import { logDomFailure } from
|
|
1
|
+
import { CLOUDFLARE_SCRIPT_SELECTOR, CLOUDFLARE_TITLE, INPUT_SELECTORS } from "../constants.js";
|
|
2
|
+
import { delay } from "../utils.js";
|
|
3
|
+
import { logDomFailure } from "../domDebug.js";
|
|
4
|
+
import { BrowserAutomationError } from "../../oracle/errors.js";
|
|
4
5
|
export function installJavaScriptDialogAutoDismissal(Page, logger) {
|
|
5
6
|
const pageAny = Page;
|
|
6
|
-
if (typeof pageAny.on !==
|
|
7
|
+
if (typeof pageAny.on !== "function" || typeof pageAny.handleJavaScriptDialog !== "function") {
|
|
7
8
|
return () => { };
|
|
8
9
|
}
|
|
9
10
|
const handler = async (params) => {
|
|
10
|
-
const type = typeof params?.type ===
|
|
11
|
-
const message = typeof params?.message ===
|
|
12
|
-
logger(`[nav] dismissing JS dialog (${type})${message ? `: ${message.slice(0, 140)}` :
|
|
11
|
+
const type = typeof params?.type === "string" ? params.type : "unknown";
|
|
12
|
+
const message = typeof params?.message === "string" ? params.message : "";
|
|
13
|
+
logger(`[nav] dismissing JS dialog (${type})${message ? `: ${message.slice(0, 140)}` : ""}`);
|
|
13
14
|
try {
|
|
14
|
-
await pageAny.handleJavaScriptDialog?.({ accept: true, promptText:
|
|
15
|
+
await pageAny.handleJavaScriptDialog?.({ accept: true, promptText: "" });
|
|
15
16
|
}
|
|
16
17
|
catch (error) {
|
|
17
18
|
const msg = error instanceof Error ? error.message : String(error);
|
|
18
19
|
logger(`[nav] failed to dismiss JS dialog: ${msg}`);
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
|
-
pageAny.on(
|
|
22
|
+
pageAny.on("javascriptDialogOpening", handler);
|
|
22
23
|
return () => {
|
|
23
24
|
try {
|
|
24
|
-
pageAny.off?.(
|
|
25
|
+
pageAny.off?.("javascriptDialogOpening", handler);
|
|
25
26
|
}
|
|
26
27
|
catch {
|
|
27
28
|
try {
|
|
28
|
-
pageAny.removeListener?.(
|
|
29
|
+
pageAny.removeListener?.("javascriptDialogOpening", handler);
|
|
29
30
|
}
|
|
30
31
|
catch {
|
|
31
32
|
// ignore
|
|
@@ -91,13 +92,13 @@ async function dismissBlockingUi(Runtime, logger) {
|
|
|
91
92
|
}).catch(() => null);
|
|
92
93
|
const value = outcome?.result?.value;
|
|
93
94
|
if (value?.dismissed) {
|
|
94
|
-
logger(`[nav] dismissed blocking UI (${value.action ??
|
|
95
|
+
logger(`[nav] dismissed blocking UI (${value.action ?? "unknown"})`);
|
|
95
96
|
return true;
|
|
96
97
|
}
|
|
97
98
|
return false;
|
|
98
99
|
}
|
|
99
100
|
export async function navigateToPromptReadyWithFallback(Page, Runtime, options, deps = {}) {
|
|
100
|
-
const { url, fallbackUrl, timeoutMs, fallbackTimeoutMs, headless, logger
|
|
101
|
+
const { url, fallbackUrl, timeoutMs, fallbackTimeoutMs, headless, logger } = options;
|
|
101
102
|
const navigate = deps.navigateToChatGPT ?? navigateToChatGPT;
|
|
102
103
|
const ensureBlocked = deps.ensureNotBlocked ?? ensureNotBlocked;
|
|
103
104
|
const ensureReady = deps.ensurePromptReady ?? ensurePromptReady;
|
|
@@ -114,6 +115,8 @@ export async function navigateToPromptReadyWithFallback(Page, Runtime, options,
|
|
|
114
115
|
}
|
|
115
116
|
const fallbackTimeout = fallbackTimeoutMs ?? Math.max(timeoutMs * 2, 120_000);
|
|
116
117
|
logger(`Prompt not ready after ${Math.round(timeoutMs / 1000)}s on ${url}; retrying ${fallbackUrl} with ${Math.round(fallbackTimeout / 1000)}s timeout.`);
|
|
118
|
+
await navigate(Page, Runtime, "about:blank", logger);
|
|
119
|
+
await delay(250);
|
|
117
120
|
await navigate(Page, Runtime, fallbackUrl, logger);
|
|
118
121
|
await ensureBlocked(Runtime, headless, logger);
|
|
119
122
|
await dismissBlockingUi(Runtime, logger).catch(() => false);
|
|
@@ -124,10 +127,10 @@ export async function navigateToPromptReadyWithFallback(Page, Runtime, options,
|
|
|
124
127
|
export async function ensureNotBlocked(Runtime, headless, logger) {
|
|
125
128
|
if (await isCloudflareInterstitial(Runtime)) {
|
|
126
129
|
const message = headless
|
|
127
|
-
?
|
|
128
|
-
:
|
|
129
|
-
logger(
|
|
130
|
-
throw new
|
|
130
|
+
? "Cloudflare challenge detected in headless mode. Re-run with --headful so you can solve the challenge."
|
|
131
|
+
: "Cloudflare challenge detected. Complete the “Just a moment…” check in the open browser, then rerun.";
|
|
132
|
+
logger("Cloudflare anti-bot page detected");
|
|
133
|
+
throw new BrowserAutomationError(message, { stage: "cloudflare-challenge", headless });
|
|
131
134
|
}
|
|
132
135
|
}
|
|
133
136
|
const LOGIN_CHECK_TIMEOUT_MS = 5_000;
|
|
@@ -156,98 +159,92 @@ export async function ensureLoggedIn(Runtime, logger, options = {}) {
|
|
|
156
159
|
});
|
|
157
160
|
const retryProbe = normalizeLoginProbe(retryOutcome.result?.value);
|
|
158
161
|
if (retryProbe.ok) {
|
|
159
|
-
logger(
|
|
162
|
+
logger("Login restored via Welcome back account picker");
|
|
160
163
|
return;
|
|
161
164
|
}
|
|
162
165
|
logger(`Login retry after Welcome back failed (status=${retryProbe.status}, domLoginCta=${Boolean(retryProbe.domLoginCta)})`);
|
|
163
166
|
}
|
|
164
|
-
logger(`Login probe failed (status=${probe.status}, domLoginCta=${Boolean(probe.domLoginCta)}, onAuthPage=${Boolean(probe.onAuthPage)}, url=${probe.pageUrl ??
|
|
165
|
-
const domLabel = probe.domLoginCta ?
|
|
167
|
+
logger(`Login probe failed (status=${probe.status}, domLoginCta=${Boolean(probe.domLoginCta)}, onAuthPage=${Boolean(probe.onAuthPage)}, url=${probe.pageUrl ?? "n/a"}, error=${probe.error ?? "none"})`);
|
|
168
|
+
const domLabel = probe.domLoginCta ? " Login button detected on page." : "";
|
|
166
169
|
const cookieHint = options.remoteSession
|
|
167
|
-
?
|
|
170
|
+
? "The remote Chrome session is not signed into ChatGPT. Sign in there, then rerun."
|
|
168
171
|
: (options.appliedCookies ?? 0) === 0
|
|
169
|
-
?
|
|
170
|
-
:
|
|
172
|
+
? "No ChatGPT cookies were applied; sign in to chatgpt.com in Chrome or pass inline cookies (--browser-inline-cookies[(-file)] / ORACLE_BROWSER_COOKIES_JSON)."
|
|
173
|
+
: "ChatGPT login appears missing; open chatgpt.com in Chrome to refresh the session or provide inline cookies (--browser-inline-cookies[(-file)] / ORACLE_BROWSER_COOKIES_JSON).";
|
|
171
174
|
throw new Error(`ChatGPT session not detected.${domLabel} ${cookieHint}`);
|
|
172
175
|
}
|
|
173
176
|
async function attemptWelcomeBackLogin(Runtime, logger) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const TIMEOUT_MS = 30000;
|
|
178
|
-
const getLabel = (node) =>
|
|
179
|
-
(node?.textContent || node?.getAttribute?.('aria-label') || '').trim();
|
|
180
|
-
const isAccount = (label) =>
|
|
181
|
-
Boolean(label) &&
|
|
182
|
-
label.includes('@') &&
|
|
183
|
-
!/log in|sign up|create account|another account/i.test(label);
|
|
184
|
-
const findAccount = () => {
|
|
185
|
-
const candidates = Array.from(document.querySelectorAll('[role="button"],button,a'));
|
|
186
|
-
return candidates.find((node) => isAccount(getLabel(node))) || null;
|
|
187
|
-
};
|
|
188
|
-
const clickAccount = () => {
|
|
189
|
-
const account = findAccount();
|
|
190
|
-
if (!account) return null;
|
|
177
|
+
const deadline = Date.now() + 30_000;
|
|
178
|
+
while (Date.now() < deadline) {
|
|
179
|
+
let outcome;
|
|
191
180
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
return new Promise((resolve) => {
|
|
207
|
-
const timer = setTimeout(() => {
|
|
208
|
-
observer.disconnect();
|
|
209
|
-
resolve({ clicked: false, reason: 'timeout' });
|
|
210
|
-
}, TIMEOUT_MS);
|
|
211
|
-
const observer = new MutationObserver(() => {
|
|
212
|
-
const result = clickAccount();
|
|
213
|
-
if (result) {
|
|
214
|
-
clearTimeout(timer);
|
|
215
|
-
observer.disconnect();
|
|
216
|
-
resolve(result);
|
|
181
|
+
outcome = await Runtime.evaluate({
|
|
182
|
+
expression: `(() => {
|
|
183
|
+
// Learned: "Welcome back" shows as a modal with account chips; click the email chip.
|
|
184
|
+
const getLabel = (node) =>
|
|
185
|
+
(node?.textContent || node?.getAttribute?.('aria-label') || '').trim();
|
|
186
|
+
const isAccount = (label) =>
|
|
187
|
+
Boolean(label) &&
|
|
188
|
+
label.includes('@') &&
|
|
189
|
+
!/log in|sign up|create account|another account/i.test(label);
|
|
190
|
+
const candidates = Array.from(document.querySelectorAll('[role="button"],button,a'));
|
|
191
|
+
const account = candidates.find((node) => isAccount(getLabel(node))) || null;
|
|
192
|
+
if (!account) {
|
|
193
|
+
return { clicked: false, reason: 'not-found' };
|
|
217
194
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
195
|
+
const label = getLabel(account);
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
try {
|
|
198
|
+
account.click();
|
|
199
|
+
} catch {
|
|
200
|
+
// ignore; caller will re-probe login state
|
|
201
|
+
}
|
|
202
|
+
}, 0);
|
|
203
|
+
return { clicked: true, label };
|
|
204
|
+
})()`,
|
|
205
|
+
awaitPromise: false,
|
|
206
|
+
returnByValue: true,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
211
|
+
if (/navigated or closed|context was destroyed|target closed/i.test(message)) {
|
|
212
|
+
logger("Welcome back account click triggered navigation.");
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
logger(`Welcome back auto-select probe failed: ${message}`);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (outcome.exceptionDetails) {
|
|
219
|
+
const details = outcome.exceptionDetails;
|
|
220
|
+
const description = (details.exception &&
|
|
221
|
+
typeof details.exception.description === "string" &&
|
|
222
|
+
details.exception.description) ||
|
|
223
|
+
details.text ||
|
|
224
|
+
"unknown error";
|
|
225
|
+
logger(`Welcome back auto-select probe failed: ${description}`);
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
const result = outcome.result?.value;
|
|
229
|
+
if (!result) {
|
|
230
|
+
logger("Welcome back auto-select probe returned no result.");
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
if (!("clicked" in result) && !("reason" in result)) {
|
|
234
|
+
logger("Welcome back auto-select probe returned an unexpected result.");
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
if (result.clicked) {
|
|
238
|
+
logger(`Welcome back modal detected; selected account ${result.label ?? "(unknown)"}`);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
if (result.reason && result.reason !== "not-found") {
|
|
242
|
+
logger(`Welcome back modal present but auto-select failed (${result.reason}).`);
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
await delay(500);
|
|
250
246
|
}
|
|
247
|
+
logger("Welcome back modal not detected after login probe failure.");
|
|
251
248
|
return false;
|
|
252
249
|
}
|
|
253
250
|
export async function ensurePromptReady(Runtime, timeoutMs, logger) {
|
|
@@ -256,15 +253,15 @@ export async function ensurePromptReady(Runtime, timeoutMs, logger) {
|
|
|
256
253
|
const authUrl = await currentUrl(Runtime);
|
|
257
254
|
if (authUrl && isAuthLoginUrl(authUrl)) {
|
|
258
255
|
// Learned: auth.openai.com/login can appear after cookies are copied; allow manual login window.
|
|
259
|
-
logger(
|
|
256
|
+
logger("Auth login page detected; waiting for manual login to complete...");
|
|
260
257
|
const extended = Math.min(Math.max(timeoutMs, 60_000), 20 * 60_000);
|
|
261
258
|
const loggedIn = await waitForPrompt(Runtime, extended);
|
|
262
259
|
if (loggedIn) {
|
|
263
260
|
return;
|
|
264
261
|
}
|
|
265
262
|
}
|
|
266
|
-
await logDomFailure(Runtime, logger,
|
|
267
|
-
throw new Error(
|
|
263
|
+
await logDomFailure(Runtime, logger, "prompt-textarea");
|
|
264
|
+
throw new Error("Prompt textarea did not appear before timeout");
|
|
268
265
|
}
|
|
269
266
|
}
|
|
270
267
|
async function waitForDocumentReady(Runtime, timeoutMs) {
|
|
@@ -274,24 +271,24 @@ async function waitForDocumentReady(Runtime, timeoutMs) {
|
|
|
274
271
|
expression: `document.readyState`,
|
|
275
272
|
returnByValue: true,
|
|
276
273
|
});
|
|
277
|
-
if (result?.value ===
|
|
274
|
+
if (result?.value === "complete" || result?.value === "interactive") {
|
|
278
275
|
return;
|
|
279
276
|
}
|
|
280
277
|
await delay(100);
|
|
281
278
|
}
|
|
282
|
-
throw new Error(
|
|
279
|
+
throw new Error("Page did not reach ready state in time");
|
|
283
280
|
}
|
|
284
281
|
async function currentUrl(Runtime) {
|
|
285
282
|
const { result } = await Runtime.evaluate({
|
|
286
283
|
expression: 'typeof location === "object" && location.href ? location.href : null',
|
|
287
284
|
returnByValue: true,
|
|
288
285
|
});
|
|
289
|
-
return typeof result?.value ===
|
|
286
|
+
return typeof result?.value === "string" ? result.value : null;
|
|
290
287
|
}
|
|
291
288
|
function isAuthLoginUrl(url) {
|
|
292
289
|
try {
|
|
293
290
|
const parsed = new URL(url);
|
|
294
|
-
if (parsed.hostname.includes(
|
|
291
|
+
if (parsed.hostname.includes("auth.openai.com")) {
|
|
295
292
|
return true;
|
|
296
293
|
}
|
|
297
294
|
return /^\/log-?in/i.test(parsed.pathname);
|
|
@@ -324,8 +321,11 @@ async function waitForPrompt(Runtime, timeoutMs) {
|
|
|
324
321
|
return false;
|
|
325
322
|
}
|
|
326
323
|
async function isCloudflareInterstitial(Runtime) {
|
|
327
|
-
const { result: titleResult } = await Runtime.evaluate({
|
|
328
|
-
|
|
324
|
+
const { result: titleResult } = await Runtime.evaluate({
|
|
325
|
+
expression: "document.title",
|
|
326
|
+
returnByValue: true,
|
|
327
|
+
});
|
|
328
|
+
const title = typeof titleResult.value === "string" ? titleResult.value : "";
|
|
329
329
|
const challengeTitle = CLOUDFLARE_TITLE.toLowerCase();
|
|
330
330
|
if (title.toLowerCase().includes(challengeTitle)) {
|
|
331
331
|
return true;
|
|
@@ -340,7 +340,6 @@ function buildLoginProbeExpression(timeoutMs) {
|
|
|
340
340
|
return `(async () => {
|
|
341
341
|
// Learned: /backend-api/me is the most reliable "am I logged in" signal.
|
|
342
342
|
// Some UIs render without a session; use DOM + network for a robust answer.
|
|
343
|
-
const timer = setTimeout(() => {}, ${timeoutMs});
|
|
344
343
|
const pageUrl = typeof location === 'object' && location?.href ? location.href : null;
|
|
345
344
|
const onAuthPage =
|
|
346
345
|
typeof location === 'object' &&
|
|
@@ -366,12 +365,26 @@ function buildLoginProbeExpression(timeoutMs) {
|
|
|
366
365
|
const textMatches = (text) => {
|
|
367
366
|
if (!text) return false;
|
|
368
367
|
const normalized = text.toLowerCase().trim();
|
|
369
|
-
return
|
|
370
|
-
|
|
368
|
+
return (
|
|
369
|
+
['log in', 'login', 'sign in', 'signin', 'continue with', 'sign up for free'].some(
|
|
370
|
+
(needle) => normalized.startsWith(needle),
|
|
371
|
+
) ||
|
|
372
|
+
normalized.includes('get responses tailored to you') ||
|
|
373
|
+
normalized.includes('log in to get answers')
|
|
371
374
|
);
|
|
372
375
|
};
|
|
373
376
|
for (const node of candidates) {
|
|
374
377
|
if (!(node instanceof HTMLElement)) continue;
|
|
378
|
+
const rect = node.getBoundingClientRect();
|
|
379
|
+
const style = window.getComputedStyle(node);
|
|
380
|
+
if (
|
|
381
|
+
rect.width <= 0 ||
|
|
382
|
+
rect.height <= 0 ||
|
|
383
|
+
style.display === 'none' ||
|
|
384
|
+
style.visibility === 'hidden'
|
|
385
|
+
) {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
375
388
|
const label =
|
|
376
389
|
node.textContent?.trim() ||
|
|
377
390
|
node.getAttribute('aria-label') ||
|
|
@@ -384,33 +397,45 @@ function buildLoginProbeExpression(timeoutMs) {
|
|
|
384
397
|
return false;
|
|
385
398
|
};
|
|
386
399
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
400
|
+
const readBackendStatus = async () => {
|
|
401
|
+
try {
|
|
402
|
+
if (typeof fetch === 'function') {
|
|
403
|
+
const controller = new AbortController();
|
|
404
|
+
const timeout = setTimeout(() => controller.abort(), ${timeoutMs});
|
|
405
|
+
try {
|
|
406
|
+
// Credentials included so we see a 200 only when cookies are valid.
|
|
407
|
+
const response = await fetch('/backend-api/me', {
|
|
408
|
+
cache: 'no-store',
|
|
409
|
+
credentials: 'include',
|
|
410
|
+
signal: controller.signal,
|
|
411
|
+
});
|
|
412
|
+
return { status: response.status || 0, error: null };
|
|
413
|
+
} finally {
|
|
414
|
+
clearTimeout(timeout);
|
|
415
|
+
}
|
|
403
416
|
}
|
|
417
|
+
} catch (err) {
|
|
418
|
+
return { status: 0, error: err ? String(err) : 'unknown' };
|
|
419
|
+
}
|
|
420
|
+
return { status: 0, error: null };
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
let { status, error } = await readBackendStatus();
|
|
424
|
+
let domLoginCta = hasLoginCta();
|
|
425
|
+
const settleDeadline = Date.now() + Math.min(${timeoutMs}, 2500);
|
|
426
|
+
while (!domLoginCta && Date.now() < settleDeadline) {
|
|
427
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
428
|
+
domLoginCta = hasLoginCta();
|
|
429
|
+
if (status === 0 || status === 401 || status === 403) {
|
|
430
|
+
const next = await readBackendStatus();
|
|
431
|
+
status = next.status;
|
|
432
|
+
error = next.error;
|
|
404
433
|
}
|
|
405
|
-
} catch (err) {
|
|
406
|
-
error = err ? String(err) : 'unknown';
|
|
407
434
|
}
|
|
408
435
|
|
|
409
|
-
const domLoginCta = hasLoginCta();
|
|
410
436
|
const loginSignals = domLoginCta || onAuthPage;
|
|
411
|
-
clearTimeout(timer);
|
|
412
437
|
return {
|
|
413
|
-
ok: !loginSignals &&
|
|
438
|
+
ok: !loginSignals && status === 200,
|
|
414
439
|
status,
|
|
415
440
|
redirected: false,
|
|
416
441
|
url: pageUrl,
|
|
@@ -422,23 +447,23 @@ function buildLoginProbeExpression(timeoutMs) {
|
|
|
422
447
|
})()`;
|
|
423
448
|
}
|
|
424
449
|
function normalizeLoginProbe(raw) {
|
|
425
|
-
if (!raw || typeof raw !==
|
|
450
|
+
if (!raw || typeof raw !== "object") {
|
|
426
451
|
return { ok: false, status: 0 };
|
|
427
452
|
}
|
|
428
453
|
const value = raw;
|
|
429
454
|
const statusRaw = value.status;
|
|
430
|
-
const status = typeof statusRaw ===
|
|
455
|
+
const status = typeof statusRaw === "number"
|
|
431
456
|
? statusRaw
|
|
432
|
-
: typeof statusRaw ===
|
|
457
|
+
: typeof statusRaw === "string" && !Number.isNaN(Number(statusRaw))
|
|
433
458
|
? Number(statusRaw)
|
|
434
459
|
: 0;
|
|
435
460
|
return {
|
|
436
461
|
ok: Boolean(value.ok),
|
|
437
462
|
status: Number.isFinite(status) ? status : 0,
|
|
438
|
-
url: typeof value.url ===
|
|
463
|
+
url: typeof value.url === "string" ? value.url : null,
|
|
439
464
|
redirected: Boolean(value.redirected),
|
|
440
|
-
error: typeof value.error ===
|
|
441
|
-
pageUrl: typeof value.pageUrl ===
|
|
465
|
+
error: typeof value.error === "string" ? value.error : null,
|
|
466
|
+
pageUrl: typeof value.pageUrl === "string" ? value.pageUrl : null,
|
|
442
467
|
domLoginCta: Boolean(value.domLoginCta),
|
|
443
468
|
onAuthPage: Boolean(value.onAuthPage),
|
|
444
469
|
};
|