@steipete/oracle 0.9.0 → 0.11.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 +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- 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/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- 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 +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- 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 +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- 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 +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- 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 +32 -28
- package/dist/src/cli/sessionCommand.js +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- 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 +16 -13
- package/dist/src/oracle/run.js +160 -135
- 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/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- 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 +78 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +67 -62
- 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 → src/projectSources}/types.js +0 -0
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { rm } from
|
|
2
|
-
import { readFileSync } from
|
|
3
|
-
import os from
|
|
4
|
-
import net from
|
|
5
|
-
import { execFile } from
|
|
6
|
-
import { promisify } from
|
|
7
|
-
import CDP from
|
|
8
|
-
import { launch, Launcher } from
|
|
9
|
-
import { cleanupStaleProfileState } from
|
|
10
|
-
import { delay } from
|
|
1
|
+
import { rm } from "node:fs/promises";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import CDP from "chrome-remote-interface";
|
|
8
|
+
import { launch, Launcher } from "chrome-launcher";
|
|
9
|
+
import { cleanupStaleProfileState } from "./profileState.js";
|
|
10
|
+
import { delay } from "./utils.js";
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
export async function launchChrome(config, userDataDir, logger) {
|
|
13
13
|
const connectHost = resolveRemoteDebugHost();
|
|
14
|
-
const debugBindAddress = connectHost && connectHost !==
|
|
14
|
+
const debugBindAddress = connectHost && connectHost !== "127.0.0.1" ? "0.0.0.0" : connectHost;
|
|
15
15
|
const debugPort = config.debugPort ?? parseDebugPortEnv();
|
|
16
16
|
const chromeFlags = buildChromeFlags(config.headless ?? false, debugBindAddress);
|
|
17
|
-
const usePatchedLauncher = Boolean(connectHost && connectHost !==
|
|
17
|
+
const usePatchedLauncher = Boolean(connectHost && connectHost !== "127.0.0.1");
|
|
18
18
|
const launcher = usePatchedLauncher
|
|
19
19
|
? await launchWithCustomHost({
|
|
20
20
|
chromeFlags,
|
|
21
21
|
chromePath: config.chromePath ?? undefined,
|
|
22
22
|
userDataDir,
|
|
23
|
-
host: connectHost ??
|
|
23
|
+
host: connectHost ?? "127.0.0.1",
|
|
24
24
|
requestedPort: debugPort ?? undefined,
|
|
25
25
|
})
|
|
26
26
|
: await launch({
|
|
@@ -30,13 +30,13 @@ export async function launchChrome(config, userDataDir, logger) {
|
|
|
30
30
|
handleSIGINT: false,
|
|
31
31
|
port: debugPort ?? undefined,
|
|
32
32
|
});
|
|
33
|
-
const pidLabel = typeof launcher.pid ===
|
|
34
|
-
const hostLabel = connectHost ? ` on ${connectHost}` :
|
|
33
|
+
const pidLabel = typeof launcher.pid === "number" ? ` (pid ${launcher.pid})` : "";
|
|
34
|
+
const hostLabel = connectHost ? ` on ${connectHost}` : "";
|
|
35
35
|
logger(`Launched Chrome${pidLabel} on port ${launcher.port}${hostLabel}`);
|
|
36
|
-
return Object.assign(launcher, { host: connectHost ??
|
|
36
|
+
return Object.assign(launcher, { host: connectHost ?? "127.0.0.1" });
|
|
37
37
|
}
|
|
38
38
|
export function registerTerminationHooks(chrome, userDataDir, keepBrowser, logger, opts) {
|
|
39
|
-
const signals = [
|
|
39
|
+
const signals = ["SIGINT", "SIGTERM", "SIGQUIT"];
|
|
40
40
|
let handling;
|
|
41
41
|
const handleSignal = (signal) => {
|
|
42
42
|
if (handling) {
|
|
@@ -46,7 +46,7 @@ export function registerTerminationHooks(chrome, userDataDir, keepBrowser, logge
|
|
|
46
46
|
const inFlight = opts?.isInFlight?.() ?? false;
|
|
47
47
|
const leaveRunning = keepBrowser || inFlight;
|
|
48
48
|
if (leaveRunning) {
|
|
49
|
-
logger(`Received ${signal}; leaving Chrome running${inFlight ?
|
|
49
|
+
logger(`Received ${signal}; leaving Chrome running${inFlight ? " (assistant response pending)" : ""}`);
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
52
|
logger(`Received ${signal}; terminating Chrome process`);
|
|
@@ -69,18 +69,18 @@ export function registerTerminationHooks(chrome, userDataDir, keepBrowser, logge
|
|
|
69
69
|
if (opts?.preserveUserDataDir) {
|
|
70
70
|
// Preserve the profile directory (manual login), but clear reattach hints so we don't
|
|
71
71
|
// try to reuse a dead DevTools port on the next run.
|
|
72
|
-
await cleanupStaleProfileState(userDataDir, logger, { lockRemovalMode:
|
|
72
|
+
await cleanupStaleProfileState(userDataDir, logger, { lockRemovalMode: "never" }).catch(() => undefined);
|
|
73
73
|
}
|
|
74
74
|
else {
|
|
75
75
|
await rm(userDataDir, { recursive: true, force: true }).catch(() => undefined);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
})().finally(() => {
|
|
79
|
-
const exitCode = signal ===
|
|
79
|
+
const exitCode = signal === "SIGINT" ? 130 : 1;
|
|
80
80
|
// Vitest treats any `process.exit()` call as an unhandled failure, even if mocked.
|
|
81
81
|
// Keep production behavior (hard-exit on signals) while letting tests observe state changes.
|
|
82
82
|
process.exitCode = exitCode;
|
|
83
|
-
const isTestRun = process.env.VITEST ===
|
|
83
|
+
const isTestRun = process.env.VITEST === "1" || process.env.NODE_ENV === "test";
|
|
84
84
|
if (!isTestRun) {
|
|
85
85
|
process.exit(exitCode);
|
|
86
86
|
}
|
|
@@ -96,12 +96,12 @@ export function registerTerminationHooks(chrome, userDataDir, keepBrowser, logge
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
export async function hideChromeWindow(chrome, logger) {
|
|
99
|
-
if (process.platform !==
|
|
100
|
-
logger(
|
|
99
|
+
if (process.platform !== "darwin") {
|
|
100
|
+
logger("Window hiding is only supported on macOS");
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
if (!chrome.pid) {
|
|
104
|
-
logger(
|
|
104
|
+
logger("Unable to hide window: missing Chrome PID");
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
const script = `tell application "System Events"
|
|
@@ -110,8 +110,8 @@ export async function hideChromeWindow(chrome, logger) {
|
|
|
110
110
|
end try
|
|
111
111
|
end tell`;
|
|
112
112
|
try {
|
|
113
|
-
await execFileAsync(
|
|
114
|
-
logger(
|
|
113
|
+
await execFileAsync("osascript", ["-e", script]);
|
|
114
|
+
logger("Chrome window hidden (Cmd-H)");
|
|
115
115
|
}
|
|
116
116
|
catch (error) {
|
|
117
117
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -120,10 +120,18 @@ export async function hideChromeWindow(chrome, logger) {
|
|
|
120
120
|
}
|
|
121
121
|
export async function connectToChrome(port, logger, host) {
|
|
122
122
|
const client = await CDP({ port, host });
|
|
123
|
-
logger(
|
|
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, browserWSEndpoint, options) {
|
|
127
|
+
if (browserWSEndpoint) {
|
|
128
|
+
return await connectToRemoteChromeTarget(host, port, logger, {
|
|
129
|
+
browserWSEndpoint,
|
|
130
|
+
targetUrl: targetUrl ?? "about:blank",
|
|
131
|
+
closeTargetOnDispose: true,
|
|
132
|
+
approvalWaitMs: options?.approvalWaitMs,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
127
135
|
if (targetUrl) {
|
|
128
136
|
const targetConnection = await connectToNewTarget(host, port, targetUrl, logger, {
|
|
129
137
|
opened: () => `Opened dedicated remote Chrome tab targeting ${targetUrl}`,
|
|
@@ -132,12 +140,24 @@ export async function connectToRemoteChrome(host, port, logger, targetUrl) {
|
|
|
132
140
|
closeFailed: (targetId, message) => `Failed to close unused remote Chrome tab ${targetId}: ${message}`,
|
|
133
141
|
});
|
|
134
142
|
if (targetConnection) {
|
|
135
|
-
return {
|
|
143
|
+
return {
|
|
144
|
+
client: targetConnection.client,
|
|
145
|
+
targetId: targetConnection.targetId,
|
|
146
|
+
close: async () => {
|
|
147
|
+
await targetConnection.client.close().catch(() => undefined);
|
|
148
|
+
await closeRemoteChromeTarget(host, port, targetConnection.targetId, logger);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
136
151
|
}
|
|
137
152
|
}
|
|
138
153
|
const fallbackClient = await CDP({ host, port });
|
|
139
154
|
logger(`Connected to remote Chrome DevTools protocol at ${host}:${port}`);
|
|
140
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
client: fallbackClient,
|
|
157
|
+
close: async () => {
|
|
158
|
+
await fallbackClient.close().catch(() => undefined);
|
|
159
|
+
},
|
|
160
|
+
};
|
|
141
161
|
}
|
|
142
162
|
export async function closeRemoteChromeTarget(host, port, targetId, logger) {
|
|
143
163
|
if (!targetId) {
|
|
@@ -154,6 +174,111 @@ export async function closeRemoteChromeTarget(host, port, targetId, logger) {
|
|
|
154
174
|
logger(`Failed to close remote Chrome tab ${targetId}: ${message}`);
|
|
155
175
|
}
|
|
156
176
|
}
|
|
177
|
+
export async function listRemoteChromeTargets(options) {
|
|
178
|
+
if (!options.browserWSEndpoint) {
|
|
179
|
+
const targets = await CDP.List({ host: options.host, port: options.port });
|
|
180
|
+
return targets;
|
|
181
|
+
}
|
|
182
|
+
const browser = await CDP({ target: options.browserWSEndpoint, local: true });
|
|
183
|
+
try {
|
|
184
|
+
const result = await browser.Target.getTargets();
|
|
185
|
+
return (result.targetInfos ?? []).map((target) => ({
|
|
186
|
+
targetId: target.targetId,
|
|
187
|
+
type: target.type,
|
|
188
|
+
url: target.url,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
await browser.close().catch(() => undefined);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export async function connectToRemoteChromeTarget(host, port, logger, options) {
|
|
196
|
+
if (!options.browserWSEndpoint) {
|
|
197
|
+
const client = await CDP({ host, port, target: options.targetId });
|
|
198
|
+
return {
|
|
199
|
+
client,
|
|
200
|
+
targetId: options.targetId,
|
|
201
|
+
close: async () => {
|
|
202
|
+
await client.close().catch(() => undefined);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const browser = await connectToBrowserWebSocket(host, port, options.browserWSEndpoint, logger, options.approvalWaitMs);
|
|
207
|
+
let targetId = options.targetId;
|
|
208
|
+
try {
|
|
209
|
+
if (!targetId) {
|
|
210
|
+
const created = await browser.Target.createTarget({
|
|
211
|
+
url: options.targetUrl ?? "about:blank",
|
|
212
|
+
});
|
|
213
|
+
targetId = created.targetId;
|
|
214
|
+
logger(`Opened dedicated remote Chrome tab targeting ${options.targetUrl ?? "about:blank"}`);
|
|
215
|
+
}
|
|
216
|
+
const attached = await browser.Target.attachToTarget({ targetId, flatten: true });
|
|
217
|
+
const client = createSessionBoundChromeClient(browser, attached.sessionId);
|
|
218
|
+
return {
|
|
219
|
+
client,
|
|
220
|
+
targetId,
|
|
221
|
+
browserWSEndpoint: options.browserWSEndpoint,
|
|
222
|
+
close: async () => {
|
|
223
|
+
await browser.Target.detachFromTarget({ sessionId: attached.sessionId }).catch(() => undefined);
|
|
224
|
+
if (options.closeTargetOnDispose && targetId) {
|
|
225
|
+
await browser.Target.closeTarget({ targetId }).catch(() => undefined);
|
|
226
|
+
}
|
|
227
|
+
await browser.close().catch(() => undefined);
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
await browser.close().catch(() => undefined);
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
async function connectToBrowserWebSocket(host, port, browserWSEndpoint, logger, approvalWaitMs) {
|
|
237
|
+
if (!approvalWaitMs || approvalWaitMs <= 0) {
|
|
238
|
+
return (await CDP({ target: browserWSEndpoint, local: true }));
|
|
239
|
+
}
|
|
240
|
+
logger(`Waiting for Chrome remote debugging approval for ${host}:${port}...`);
|
|
241
|
+
const deadline = Date.now() + approvalWaitMs;
|
|
242
|
+
let lastApprovalError;
|
|
243
|
+
while (Date.now() < deadline) {
|
|
244
|
+
const remainingMs = Math.max(1, deadline - Date.now());
|
|
245
|
+
try {
|
|
246
|
+
return await Promise.race([
|
|
247
|
+
CDP({ target: browserWSEndpoint, local: true }),
|
|
248
|
+
new Promise((_, reject) => {
|
|
249
|
+
setTimeout(() => {
|
|
250
|
+
reject(new Error("__oracle_remote_debugging_approval_timeout__"));
|
|
251
|
+
}, remainingMs);
|
|
252
|
+
}),
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (error instanceof Error &&
|
|
257
|
+
error.message === "__oracle_remote_debugging_approval_timeout__") {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
if (!isRemoteDebuggingApprovalError(error)) {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
lastApprovalError = error;
|
|
264
|
+
await delay(Math.min(500, Math.max(0, deadline - Date.now())));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const suffix = lastApprovalError instanceof Error && lastApprovalError.message
|
|
268
|
+
? ` Last Chrome response: ${lastApprovalError.message}`
|
|
269
|
+
: "";
|
|
270
|
+
throw new Error(`Oracle waited ${formatApprovalWait(approvalWaitMs)} for Chrome remote debugging approval at ${host}:${port}. Allow the Chrome prompt or retry after toggling remote debugging.${suffix}`);
|
|
271
|
+
}
|
|
272
|
+
function isRemoteDebuggingApprovalError(error) {
|
|
273
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
274
|
+
return /unexpected server response:\s*403|remote debugging|forbidden/i.test(message);
|
|
275
|
+
}
|
|
276
|
+
function formatApprovalWait(waitMs) {
|
|
277
|
+
if (waitMs % 1000 === 0) {
|
|
278
|
+
return `${waitMs / 1000}s`;
|
|
279
|
+
}
|
|
280
|
+
return `${waitMs}ms`;
|
|
281
|
+
}
|
|
157
282
|
async function connectToNewTarget(host, port, url, logger, messages) {
|
|
158
283
|
try {
|
|
159
284
|
const target = await CDP.New({ host, port, url });
|
|
@@ -182,13 +307,63 @@ async function connectToNewTarget(host, port, url, logger, messages) {
|
|
|
182
307
|
}
|
|
183
308
|
return null;
|
|
184
309
|
}
|
|
310
|
+
function createSessionBoundChromeClient(browser, sessionId) {
|
|
311
|
+
const browserWithEvents = browser;
|
|
312
|
+
const bindDomain = (domainName) => {
|
|
313
|
+
const domain = browser[domainName];
|
|
314
|
+
const eventName = (name) => `${domainName}.${name}.${sessionId}`;
|
|
315
|
+
return new Proxy((domain ?? {}), {
|
|
316
|
+
get(target, prop, receiver) {
|
|
317
|
+
if (prop === "on") {
|
|
318
|
+
return (name, listener) => {
|
|
319
|
+
const domainEvent = target[name];
|
|
320
|
+
if (typeof domainEvent === "function") {
|
|
321
|
+
return domainEvent(sessionId, listener);
|
|
322
|
+
}
|
|
323
|
+
browserWithEvents.on(eventName(name), listener);
|
|
324
|
+
return () => browserWithEvents.removeListener(eventName(name), listener);
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (prop === "off" || prop === "removeListener") {
|
|
328
|
+
return (name, listener) => {
|
|
329
|
+
const off = browserWithEvents.off ?? browserWithEvents.removeListener.bind(browserWithEvents);
|
|
330
|
+
off(eventName(name), listener);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const value = Reflect.get(target, prop, receiver);
|
|
334
|
+
if (typeof value !== "function") {
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
return (...args) => value(...args, sessionId);
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
return {
|
|
342
|
+
...browser,
|
|
343
|
+
Network: bindDomain("Network"),
|
|
344
|
+
Page: bindDomain("Page"),
|
|
345
|
+
Runtime: bindDomain("Runtime"),
|
|
346
|
+
Input: bindDomain("Input"),
|
|
347
|
+
DOM: bindDomain("DOM"),
|
|
348
|
+
on: browserWithEvents.on.bind(browserWithEvents),
|
|
349
|
+
once: browserWithEvents.once.bind(browserWithEvents),
|
|
350
|
+
off: browserWithEvents.off?.bind(browserWithEvents) ??
|
|
351
|
+
browserWithEvents.removeListener.bind(browserWithEvents),
|
|
352
|
+
removeListener: browserWithEvents.removeListener.bind(browserWithEvents),
|
|
353
|
+
close: async () => {
|
|
354
|
+
await browser.Target.detachFromTarget({ sessionId }).catch(() => undefined);
|
|
355
|
+
},
|
|
356
|
+
};
|
|
357
|
+
}
|
|
185
358
|
export async function connectWithNewTab(port, logger, initialUrl, host, options) {
|
|
186
|
-
const effectiveHost = host ??
|
|
187
|
-
const url = initialUrl ??
|
|
359
|
+
const effectiveHost = host ?? "127.0.0.1";
|
|
360
|
+
const url = initialUrl ?? "about:blank";
|
|
188
361
|
const fallbackToDefault = options?.fallbackToDefault ?? true;
|
|
189
362
|
const retries = Math.max(0, options?.retries ?? 0);
|
|
190
363
|
const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 250);
|
|
191
|
-
const fallbackLabel = fallbackToDefault
|
|
364
|
+
const fallbackLabel = fallbackToDefault
|
|
365
|
+
? "falling back to default target."
|
|
366
|
+
: "strict mode: not falling back.";
|
|
192
367
|
let attempt = 0;
|
|
193
368
|
while (attempt <= retries) {
|
|
194
369
|
const targetConnection = await connectToNewTarget(effectiveHost, port, url, logger, {
|
|
@@ -207,13 +382,13 @@ export async function connectWithNewTab(port, logger, initialUrl, host, options)
|
|
|
207
382
|
await delay(retryDelayMs * attempt);
|
|
208
383
|
}
|
|
209
384
|
if (!fallbackToDefault) {
|
|
210
|
-
throw new Error(
|
|
385
|
+
throw new Error("Failed to open isolated browser tab; refusing to attach to default target.");
|
|
211
386
|
}
|
|
212
387
|
const client = await connectToChrome(port, logger, effectiveHost);
|
|
213
388
|
return { client };
|
|
214
389
|
}
|
|
215
390
|
export async function closeTab(port, targetId, logger, host) {
|
|
216
|
-
const effectiveHost = host ??
|
|
391
|
+
const effectiveHost = host ?? "127.0.0.1";
|
|
217
392
|
try {
|
|
218
393
|
await CDP.Close({ host: effectiveHost, port, id: targetId });
|
|
219
394
|
logger(`Closed isolated browser tab (target=${targetId})`);
|
|
@@ -223,35 +398,73 @@ export async function closeTab(port, targetId, logger, host) {
|
|
|
223
398
|
logger(`Failed to close browser tab ${targetId}: ${message}`);
|
|
224
399
|
}
|
|
225
400
|
}
|
|
401
|
+
export async function closeBlankChromeTabs(port, logger, host, options) {
|
|
402
|
+
const effectiveHost = host ?? "127.0.0.1";
|
|
403
|
+
const excluded = new Set([...(options?.excludeTargetIds ?? [])].filter((targetId) => typeof targetId === "string" && targetId.length > 0));
|
|
404
|
+
let targets;
|
|
405
|
+
try {
|
|
406
|
+
targets = (await CDP.List({ host: effectiveHost, port }));
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
410
|
+
logger(`Failed to inspect blank Chrome tabs: ${message}`);
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
let closed = 0;
|
|
414
|
+
for (const target of targets) {
|
|
415
|
+
const targetId = target.targetId ?? target.id;
|
|
416
|
+
if (!targetId || excluded.has(targetId) || !isBlankPageTarget(target)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
await CDP.Close({ host: effectiveHost, port, id: targetId });
|
|
421
|
+
closed += 1;
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
425
|
+
logger(`Failed to close blank Chrome tab ${targetId}: ${message}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (closed > 0) {
|
|
429
|
+
logger(`Closed ${closed} blank Chrome tab${closed === 1 ? "" : "s"}.`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function isBlankPageTarget(target) {
|
|
433
|
+
if (target.type && target.type !== "page") {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
const url = (target.url ?? "").trim().toLowerCase();
|
|
437
|
+
return url === "about:blank" || url === "chrome://newtab/" || url === "chrome://new-tab-page/";
|
|
438
|
+
}
|
|
226
439
|
function buildChromeFlags(headless, debugBindAddress) {
|
|
227
440
|
const flags = [
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
441
|
+
"--disable-background-networking",
|
|
442
|
+
"--disable-background-timer-throttling",
|
|
443
|
+
"--disable-breakpad",
|
|
444
|
+
"--disable-client-side-phishing-detection",
|
|
445
|
+
"--disable-default-apps",
|
|
446
|
+
"--disable-hang-monitor",
|
|
447
|
+
"--disable-popup-blocking",
|
|
448
|
+
"--disable-prompt-on-repost",
|
|
449
|
+
"--disable-sync",
|
|
450
|
+
"--disable-translate",
|
|
451
|
+
"--metrics-recording-only",
|
|
452
|
+
"--no-first-run",
|
|
453
|
+
"--safebrowsing-disable-auto-update",
|
|
454
|
+
"--disable-features=TranslateUI,AutomationControlled",
|
|
455
|
+
"--mute-audio",
|
|
456
|
+
"--window-size=1280,720",
|
|
457
|
+
"--lang=en-US",
|
|
458
|
+
"--accept-lang=en-US,en",
|
|
246
459
|
];
|
|
247
|
-
if (process.platform !==
|
|
248
|
-
flags.push(
|
|
460
|
+
if (process.platform !== "win32" && !isWsl()) {
|
|
461
|
+
flags.push("--password-store=basic", "--use-mock-keychain");
|
|
249
462
|
}
|
|
250
463
|
if (debugBindAddress) {
|
|
251
464
|
flags.push(`--remote-debugging-address=${debugBindAddress}`);
|
|
252
465
|
}
|
|
253
466
|
if (headless) {
|
|
254
|
-
flags.push(
|
|
467
|
+
flags.push("--headless=new");
|
|
255
468
|
}
|
|
256
469
|
return flags;
|
|
257
470
|
}
|
|
@@ -274,8 +487,8 @@ function resolveRemoteDebugHost() {
|
|
|
274
487
|
return null;
|
|
275
488
|
}
|
|
276
489
|
try {
|
|
277
|
-
const resolv = readFileSync(
|
|
278
|
-
for (const line of resolv.split(
|
|
490
|
+
const resolv = readFileSync("/etc/resolv.conf", "utf8");
|
|
491
|
+
for (const line of resolv.split("\n")) {
|
|
279
492
|
const match = line.match(/^nameserver\s+([0-9.]+)/);
|
|
280
493
|
if (match?.[1]) {
|
|
281
494
|
return match[1];
|
|
@@ -288,14 +501,14 @@ function resolveRemoteDebugHost() {
|
|
|
288
501
|
return null;
|
|
289
502
|
}
|
|
290
503
|
function isWsl() {
|
|
291
|
-
if (process.platform !==
|
|
504
|
+
if (process.platform !== "linux") {
|
|
292
505
|
return false;
|
|
293
506
|
}
|
|
294
507
|
if (process.env.WSL_DISTRO_NAME) {
|
|
295
508
|
return true;
|
|
296
509
|
}
|
|
297
510
|
const release = os.release();
|
|
298
|
-
return release.toLowerCase().includes(
|
|
511
|
+
return release.toLowerCase().includes("microsoft");
|
|
299
512
|
}
|
|
300
513
|
async function launchWithCustomHost({ chromeFlags, chromePath, userDataDir, host, requestedPort, }) {
|
|
301
514
|
const launcher = new Launcher({
|
|
@@ -310,7 +523,7 @@ async function launchWithCustomHost({ chromeFlags, chromePath, userDataDir, host
|
|
|
310
523
|
patched.isDebuggerReady = function patchedIsDebuggerReady() {
|
|
311
524
|
const debugPort = this.port ?? 0;
|
|
312
525
|
if (!debugPort) {
|
|
313
|
-
return Promise.reject(new Error(
|
|
526
|
+
return Promise.reject(new Error("Missing Chrome debug port"));
|
|
314
527
|
}
|
|
315
528
|
return new Promise((resolve, reject) => {
|
|
316
529
|
const client = net.createConnection({ port: debugPort, host });
|
|
@@ -320,11 +533,11 @@ async function launchWithCustomHost({ chromeFlags, chromePath, userDataDir, host
|
|
|
320
533
|
client.destroy();
|
|
321
534
|
client.unref();
|
|
322
535
|
};
|
|
323
|
-
client.once(
|
|
536
|
+
client.once("error", (err) => {
|
|
324
537
|
cleanup();
|
|
325
538
|
reject(err);
|
|
326
539
|
});
|
|
327
|
-
client.once(
|
|
540
|
+
client.once("connect", () => {
|
|
328
541
|
cleanup();
|
|
329
542
|
resolve();
|
|
330
543
|
});
|
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET } from
|
|
2
|
-
import { normalizeBrowserModelStrategy } from
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
import { CHATGPT_URL, DEEP_RESEARCH_DEFAULT_TIMEOUT_MS, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET, } from "./constants.js";
|
|
2
|
+
import { normalizeBrowserModelStrategy } from "./modelStrategy.js";
|
|
3
|
+
import { DEFAULT_MAX_CONCURRENT_CHATGPT_TABS, normalizeMaxConcurrentTabs, } from "./tabLeaseRegistry.js";
|
|
4
|
+
import { isTemporaryChatUrl, normalizeChatgptUrl } from "./utils.js";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
export const DEFAULT_CHATGPT_COOKIE_NAMES = [
|
|
8
|
+
"__Secure-next-auth.session-token",
|
|
9
|
+
"__Secure-next-auth.session-token.0",
|
|
10
|
+
"__Secure-next-auth.session-token.1",
|
|
11
|
+
"_account",
|
|
12
|
+
"cf_clearance",
|
|
13
|
+
"__cf_bm",
|
|
14
|
+
"_cfuvid",
|
|
15
|
+
"CF_Authorization",
|
|
16
|
+
"__cflb",
|
|
17
|
+
];
|
|
6
18
|
export const DEFAULT_BROWSER_CONFIG = {
|
|
7
19
|
chromeProfile: null,
|
|
8
20
|
chromePath: null,
|
|
9
21
|
chromeCookiePath: null,
|
|
22
|
+
attachRunning: false,
|
|
23
|
+
browserTabRef: null,
|
|
10
24
|
url: CHATGPT_URL,
|
|
11
25
|
chatgptUrl: CHATGPT_URL,
|
|
12
26
|
timeoutMs: 1_200_000,
|
|
@@ -16,11 +30,12 @@ export const DEFAULT_BROWSER_CONFIG = {
|
|
|
16
30
|
assistantRecheckTimeoutMs: 120_000,
|
|
17
31
|
reuseChromeWaitMs: 10_000,
|
|
18
32
|
profileLockTimeoutMs: 300_000,
|
|
33
|
+
maxConcurrentTabs: DEFAULT_MAX_CONCURRENT_CHATGPT_TABS,
|
|
19
34
|
autoReattachDelayMs: 0,
|
|
20
35
|
autoReattachIntervalMs: 0,
|
|
21
36
|
autoReattachTimeoutMs: 120_000,
|
|
22
37
|
cookieSync: true,
|
|
23
|
-
cookieNames:
|
|
38
|
+
cookieNames: DEFAULT_CHATGPT_COOKIE_NAMES,
|
|
24
39
|
cookieSyncWaitMs: 0,
|
|
25
40
|
inlineCookies: null,
|
|
26
41
|
inlineCookiesSource: null,
|
|
@@ -32,42 +47,50 @@ export const DEFAULT_BROWSER_CONFIG = {
|
|
|
32
47
|
debug: false,
|
|
33
48
|
allowCookieErrors: false,
|
|
34
49
|
remoteChrome: null,
|
|
50
|
+
remoteChromeBrowserWSEndpoint: null,
|
|
51
|
+
remoteChromeProfileRoot: null,
|
|
35
52
|
manualLogin: false,
|
|
36
53
|
manualLoginProfileDir: null,
|
|
37
54
|
manualLoginCookieSync: false,
|
|
55
|
+
researchMode: "off",
|
|
56
|
+
archiveConversations: "auto",
|
|
38
57
|
};
|
|
39
58
|
export function resolveBrowserConfig(config) {
|
|
40
59
|
const debugPortEnv = parseDebugPort(process.env.ORACLE_BROWSER_PORT ?? process.env.ORACLE_BROWSER_DEBUG_PORT);
|
|
41
|
-
const envAllowCookieErrors = (process.env.ORACLE_BROWSER_ALLOW_COOKIE_ERRORS ??
|
|
42
|
-
(process.env.ORACLE_BROWSER_ALLOW_COOKIE_ERRORS ??
|
|
60
|
+
const envAllowCookieErrors = (process.env.ORACLE_BROWSER_ALLOW_COOKIE_ERRORS ?? "").trim().toLowerCase() === "true" ||
|
|
61
|
+
(process.env.ORACLE_BROWSER_ALLOW_COOKIE_ERRORS ?? "").trim() === "1";
|
|
43
62
|
const rawUrl = config?.chatgptUrl ?? config?.url ?? DEFAULT_BROWSER_CONFIG.url;
|
|
44
63
|
const normalizedUrl = normalizeChatgptUrl(rawUrl ?? DEFAULT_BROWSER_CONFIG.url, DEFAULT_BROWSER_CONFIG.url);
|
|
45
64
|
const desiredModel = config?.desiredModel ?? DEFAULT_BROWSER_CONFIG.desiredModel ?? DEFAULT_MODEL_TARGET;
|
|
46
65
|
const modelStrategy = normalizeBrowserModelStrategy(config?.modelStrategy) ??
|
|
47
66
|
DEFAULT_BROWSER_CONFIG.modelStrategy ??
|
|
48
67
|
DEFAULT_MODEL_STRATEGY;
|
|
49
|
-
if (modelStrategy ===
|
|
50
|
-
|
|
68
|
+
if (modelStrategy === "select" &&
|
|
69
|
+
isTemporaryChatUrl(normalizedUrl) &&
|
|
70
|
+
/\bpro\b/i.test(desiredModel)) {
|
|
71
|
+
throw new Error("Temporary Chat mode does not expose Pro models in the ChatGPT model picker. " +
|
|
51
72
|
'Remove "temporary-chat=true" from your browser URL, or use a non-Pro model label (e.g. "GPT-5.2").');
|
|
52
73
|
}
|
|
53
|
-
const isWindows = process.platform ===
|
|
74
|
+
const isWindows = process.platform === "win32";
|
|
54
75
|
const manualLogin = config?.manualLogin ?? (isWindows ? true : DEFAULT_BROWSER_CONFIG.manualLogin);
|
|
55
76
|
const cookieSyncDefault = isWindows ? false : DEFAULT_BROWSER_CONFIG.cookieSync;
|
|
56
|
-
const resolvedProfileDir = config?.manualLoginProfileDir
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
const resolvedProfileDir = resolveManualLoginProfileDir(config?.manualLoginProfileDir, process.env.ORACLE_BROWSER_PROFILE_DIR);
|
|
78
|
+
const researchMode = normalizeResearchMode(config?.researchMode);
|
|
79
|
+
const archiveConversations = normalizeArchiveMode(config?.archiveConversations);
|
|
80
|
+
const defaultTimeoutMs = researchMode === "deep" ? DEEP_RESEARCH_DEFAULT_TIMEOUT_MS : DEFAULT_BROWSER_CONFIG.timeoutMs;
|
|
59
81
|
return {
|
|
60
82
|
...DEFAULT_BROWSER_CONFIG,
|
|
61
|
-
...
|
|
83
|
+
...config,
|
|
62
84
|
url: normalizedUrl,
|
|
63
85
|
chatgptUrl: normalizedUrl,
|
|
64
|
-
timeoutMs: config?.timeoutMs ??
|
|
86
|
+
timeoutMs: config?.timeoutMs ?? defaultTimeoutMs,
|
|
65
87
|
debugPort: config?.debugPort ?? debugPortEnv ?? DEFAULT_BROWSER_CONFIG.debugPort,
|
|
66
88
|
inputTimeoutMs: config?.inputTimeoutMs ?? DEFAULT_BROWSER_CONFIG.inputTimeoutMs,
|
|
67
89
|
assistantRecheckDelayMs: config?.assistantRecheckDelayMs ?? DEFAULT_BROWSER_CONFIG.assistantRecheckDelayMs,
|
|
68
90
|
assistantRecheckTimeoutMs: config?.assistantRecheckTimeoutMs ?? DEFAULT_BROWSER_CONFIG.assistantRecheckTimeoutMs,
|
|
69
91
|
reuseChromeWaitMs: config?.reuseChromeWaitMs ?? DEFAULT_BROWSER_CONFIG.reuseChromeWaitMs,
|
|
70
92
|
profileLockTimeoutMs: config?.profileLockTimeoutMs ?? DEFAULT_BROWSER_CONFIG.profileLockTimeoutMs,
|
|
93
|
+
maxConcurrentTabs: normalizeMaxConcurrentTabs(config?.maxConcurrentTabs ?? DEFAULT_BROWSER_CONFIG.maxConcurrentTabs),
|
|
71
94
|
autoReattachDelayMs: config?.autoReattachDelayMs ?? DEFAULT_BROWSER_CONFIG.autoReattachDelayMs,
|
|
72
95
|
autoReattachIntervalMs: config?.autoReattachIntervalMs ?? DEFAULT_BROWSER_CONFIG.autoReattachIntervalMs,
|
|
73
96
|
autoReattachTimeoutMs: config?.autoReattachTimeoutMs ?? DEFAULT_BROWSER_CONFIG.autoReattachTimeoutMs,
|
|
@@ -84,14 +107,26 @@ export function resolveBrowserConfig(config) {
|
|
|
84
107
|
chromeProfile: config?.chromeProfile ?? DEFAULT_BROWSER_CONFIG.chromeProfile,
|
|
85
108
|
chromePath: config?.chromePath ?? DEFAULT_BROWSER_CONFIG.chromePath,
|
|
86
109
|
chromeCookiePath: config?.chromeCookiePath ?? DEFAULT_BROWSER_CONFIG.chromeCookiePath,
|
|
110
|
+
attachRunning: config?.attachRunning ?? DEFAULT_BROWSER_CONFIG.attachRunning,
|
|
111
|
+
browserTabRef: config?.browserTabRef ?? DEFAULT_BROWSER_CONFIG.browserTabRef,
|
|
87
112
|
debug: config?.debug ?? DEFAULT_BROWSER_CONFIG.debug,
|
|
88
113
|
allowCookieErrors: config?.allowCookieErrors ?? envAllowCookieErrors ?? DEFAULT_BROWSER_CONFIG.allowCookieErrors,
|
|
114
|
+
remoteChromeBrowserWSEndpoint: config?.remoteChromeBrowserWSEndpoint ?? DEFAULT_BROWSER_CONFIG.remoteChromeBrowserWSEndpoint,
|
|
115
|
+
remoteChromeProfileRoot: config?.remoteChromeProfileRoot ?? DEFAULT_BROWSER_CONFIG.remoteChromeProfileRoot,
|
|
89
116
|
thinkingTime: config?.thinkingTime,
|
|
117
|
+
researchMode,
|
|
118
|
+
archiveConversations,
|
|
90
119
|
manualLogin,
|
|
91
120
|
manualLoginProfileDir: manualLogin ? resolvedProfileDir : null,
|
|
92
121
|
manualLoginCookieSync: config?.manualLoginCookieSync ?? DEFAULT_BROWSER_CONFIG.manualLoginCookieSync,
|
|
93
122
|
};
|
|
94
123
|
}
|
|
124
|
+
function normalizeResearchMode(value) {
|
|
125
|
+
return value === "deep" ? "deep" : "off";
|
|
126
|
+
}
|
|
127
|
+
function normalizeArchiveMode(value) {
|
|
128
|
+
return value === "always" || value === "never" ? value : "auto";
|
|
129
|
+
}
|
|
95
130
|
function parseDebugPort(raw) {
|
|
96
131
|
if (!raw)
|
|
97
132
|
return null;
|
|
@@ -101,3 +136,11 @@ function parseDebugPort(raw) {
|
|
|
101
136
|
}
|
|
102
137
|
return value;
|
|
103
138
|
}
|
|
139
|
+
function resolveManualLoginProfileDir(...candidates) {
|
|
140
|
+
for (const candidate of candidates) {
|
|
141
|
+
const profileDir = candidate?.trim();
|
|
142
|
+
if (profileDir)
|
|
143
|
+
return profileDir;
|
|
144
|
+
}
|
|
145
|
+
return path.join(os.homedir(), ".oracle", "browser-profile");
|
|
146
|
+
}
|