@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,27 +1,32 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import { getCookies } from
|
|
3
|
-
import {
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { getCookies } from "@steipete/sweet-cookie";
|
|
3
|
+
import { runProviderDomFlow } from "../browser/providerDomFlow.js";
|
|
4
|
+
import { delay } from "../browser/utils.js";
|
|
5
|
+
import { runGeminiWebWithFallback, saveFirstGeminiImageFromOutput } from "./client.js";
|
|
6
|
+
import { geminiDeepThinkDomProvider } from "../browser/providers/index.js";
|
|
7
|
+
import { openGeminiBrowserSession } from "./browserSessionManager.js";
|
|
8
|
+
import { selectGeminiExecutionMode } from "./executionMode.js";
|
|
4
9
|
const GEMINI_COOKIE_NAMES = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
10
|
+
"__Secure-1PSID",
|
|
11
|
+
"__Secure-1PSIDTS",
|
|
12
|
+
"__Secure-1PSIDCC",
|
|
13
|
+
"__Secure-1PAPISID",
|
|
14
|
+
"NID",
|
|
15
|
+
"AEC",
|
|
16
|
+
"SOCS",
|
|
17
|
+
"__Secure-BUCKET",
|
|
18
|
+
"__Secure-ENID",
|
|
19
|
+
"SID",
|
|
20
|
+
"HSID",
|
|
21
|
+
"SSID",
|
|
22
|
+
"APISID",
|
|
23
|
+
"SAPISID",
|
|
24
|
+
"__Secure-3PSID",
|
|
25
|
+
"__Secure-3PSIDTS",
|
|
26
|
+
"__Secure-3PAPISID",
|
|
27
|
+
"SIDCC",
|
|
23
28
|
];
|
|
24
|
-
const GEMINI_REQUIRED_COOKIES = [
|
|
29
|
+
const GEMINI_REQUIRED_COOKIES = ["__Secure-1PSID", "__Secure-1PSIDTS"];
|
|
25
30
|
function estimateTokenCount(text) {
|
|
26
31
|
return Math.ceil(text.length / 4);
|
|
27
32
|
}
|
|
@@ -34,28 +39,33 @@ function resolveInvocationPath(value) {
|
|
|
34
39
|
return path.isAbsolute(trimmed) ? trimmed : path.resolve(process.cwd(), trimmed);
|
|
35
40
|
}
|
|
36
41
|
function resolveGeminiWebModel(desiredModel, log) {
|
|
37
|
-
const desired = typeof desiredModel ===
|
|
42
|
+
const desired = typeof desiredModel === "string" ? desiredModel.trim() : "";
|
|
38
43
|
if (!desired)
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
case
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
case
|
|
47
|
-
|
|
44
|
+
return "gemini-3-pro";
|
|
45
|
+
const normalized = desired.toLowerCase().replace(/[_\s]+/g, "-");
|
|
46
|
+
switch (normalized) {
|
|
47
|
+
case "gemini-3-pro":
|
|
48
|
+
case "gemini-3.0-pro":
|
|
49
|
+
return "gemini-3-pro";
|
|
50
|
+
case "gemini-3-deep-think":
|
|
51
|
+
case "gemini-3-pro-deep-think":
|
|
52
|
+
case "gemini-3-pro-deepthink":
|
|
53
|
+
return "gemini-3-pro-deep-think";
|
|
54
|
+
case "gemini-2.5-pro":
|
|
55
|
+
return "gemini-2.5-pro";
|
|
56
|
+
case "gemini-2.5-flash":
|
|
57
|
+
return "gemini-2.5-flash";
|
|
48
58
|
default:
|
|
49
|
-
if (
|
|
59
|
+
if (normalized.startsWith("gemini-") || normalized.includes("gemini")) {
|
|
50
60
|
log?.(`[gemini-web] Unsupported Gemini web model "${desired}". Falling back to gemini-3-pro.`);
|
|
51
61
|
}
|
|
52
|
-
return
|
|
62
|
+
return "gemini-3-pro";
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
65
|
function resolveCookieDomain(cookie) {
|
|
56
66
|
const rawDomain = cookie.domain?.trim();
|
|
57
67
|
if (rawDomain) {
|
|
58
|
-
return rawDomain.startsWith(
|
|
68
|
+
return rawDomain.startsWith(".") ? rawDomain.slice(1) : rawDomain;
|
|
59
69
|
}
|
|
60
70
|
const rawUrl = cookie.url?.trim();
|
|
61
71
|
if (rawUrl) {
|
|
@@ -69,14 +79,14 @@ function resolveCookieDomain(cookie) {
|
|
|
69
79
|
return null;
|
|
70
80
|
}
|
|
71
81
|
function pickCookieValue(cookies, name) {
|
|
72
|
-
const matches = cookies.filter((cookie) => cookie.name === name && typeof cookie.value ===
|
|
82
|
+
const matches = cookies.filter((cookie) => cookie.name === name && typeof cookie.value === "string");
|
|
73
83
|
if (matches.length === 0)
|
|
74
84
|
return undefined;
|
|
75
85
|
const preferredDomain = matches.find((cookie) => {
|
|
76
86
|
const domain = resolveCookieDomain(cookie);
|
|
77
|
-
return domain ===
|
|
87
|
+
return domain === "google.com" && (cookie.path ?? "/") === "/";
|
|
78
88
|
});
|
|
79
|
-
const googleDomain = matches.find((cookie) => (resolveCookieDomain(cookie) ??
|
|
89
|
+
const googleDomain = matches.find((cookie) => (resolveCookieDomain(cookie) ?? "").endsWith("google.com"));
|
|
80
90
|
return (preferredDomain ?? googleDomain ?? matches[0])?.value;
|
|
81
91
|
}
|
|
82
92
|
function buildGeminiCookieMap(cookies) {
|
|
@@ -91,87 +101,181 @@ function buildGeminiCookieMap(cookies) {
|
|
|
91
101
|
function hasRequiredGeminiCookies(cookieMap) {
|
|
92
102
|
return GEMINI_REQUIRED_COOKIES.every((name) => Boolean(cookieMap[name]));
|
|
93
103
|
}
|
|
104
|
+
const GEMINI_CDP_COOKIE_URLS = [
|
|
105
|
+
"https://gemini.google.com",
|
|
106
|
+
"https://accounts.google.com",
|
|
107
|
+
"https://www.google.com",
|
|
108
|
+
];
|
|
109
|
+
async function loadGeminiCookiesFromCDP(browserConfig, log) {
|
|
110
|
+
const session = await openGeminiBrowserSession({
|
|
111
|
+
browserConfig,
|
|
112
|
+
keepBrowserDefault: false,
|
|
113
|
+
purpose: "Gemini manual-login cookie extraction (no keychain)",
|
|
114
|
+
log,
|
|
115
|
+
});
|
|
116
|
+
try {
|
|
117
|
+
const client = session.client;
|
|
118
|
+
const { Network, Page } = client;
|
|
119
|
+
await Network.enable({});
|
|
120
|
+
await Page.enable();
|
|
121
|
+
log?.("[gemini-web] Navigating to gemini.google.com for sign-in/cookie capture...");
|
|
122
|
+
await Page.navigate({ url: "https://gemini.google.com" });
|
|
123
|
+
await delay(2_000);
|
|
124
|
+
const pollTimeoutMs = 5 * 60_000;
|
|
125
|
+
const pollIntervalMs = 2_000;
|
|
126
|
+
const deadline = Date.now() + pollTimeoutMs;
|
|
127
|
+
let lastNotice = 0;
|
|
128
|
+
let cookieMap = {};
|
|
129
|
+
while (Date.now() < deadline) {
|
|
130
|
+
const { cookies } = await Network.getCookies({ urls: GEMINI_CDP_COOKIE_URLS });
|
|
131
|
+
cookieMap = buildGeminiCookieMap(cookies);
|
|
132
|
+
if (hasRequiredGeminiCookies(cookieMap)) {
|
|
133
|
+
log?.(`[gemini-web] Extracted ${Object.keys(cookieMap).length} Gemini cookie(s) via CDP.`);
|
|
134
|
+
return { cookieMap, warnings: [] };
|
|
135
|
+
}
|
|
136
|
+
const now = Date.now();
|
|
137
|
+
if (now - lastNotice > 10_000) {
|
|
138
|
+
log?.("[gemini-web] Waiting for Google sign-in... please sign in in the opened Chrome window.");
|
|
139
|
+
lastNotice = now;
|
|
140
|
+
}
|
|
141
|
+
await delay(pollIntervalMs);
|
|
142
|
+
}
|
|
143
|
+
throw new Error("Timed out waiting for Google sign-in (5 minutes). Please sign in and retry.");
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
await session.close();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function runGeminiDeepThinkViaBrowser(prompt, browserConfig, log) {
|
|
150
|
+
const session = await openGeminiBrowserSession({
|
|
151
|
+
browserConfig,
|
|
152
|
+
keepBrowserDefault: true,
|
|
153
|
+
purpose: "Gemini Deep Think",
|
|
154
|
+
log,
|
|
155
|
+
});
|
|
156
|
+
try {
|
|
157
|
+
const client = session.client;
|
|
158
|
+
const { Runtime, Page } = client;
|
|
159
|
+
if (!Runtime ||
|
|
160
|
+
typeof Runtime.enable !== "function" ||
|
|
161
|
+
typeof Runtime.evaluate !== "function") {
|
|
162
|
+
throw new Error("Chrome Runtime domain unavailable for Gemini Deep Think DOM automation.");
|
|
163
|
+
}
|
|
164
|
+
if (!Page || typeof Page.enable !== "function" || typeof Page.navigate !== "function") {
|
|
165
|
+
throw new Error("Chrome Page domain unavailable for Gemini Deep Think DOM automation.");
|
|
166
|
+
}
|
|
167
|
+
await Runtime.enable();
|
|
168
|
+
await Page.enable();
|
|
169
|
+
const evaluate = async (expression) => {
|
|
170
|
+
const { result } = await Runtime.evaluate({ expression, returnByValue: true });
|
|
171
|
+
return result?.value;
|
|
172
|
+
};
|
|
173
|
+
log?.("[gemini-web] Navigating to gemini.google.com...");
|
|
174
|
+
await Page.navigate({ url: "https://gemini.google.com/app" });
|
|
175
|
+
await delay(3_000);
|
|
176
|
+
const domResult = await runProviderDomFlow(geminiDeepThinkDomProvider, {
|
|
177
|
+
prompt,
|
|
178
|
+
evaluate,
|
|
179
|
+
delay,
|
|
180
|
+
log,
|
|
181
|
+
state: {
|
|
182
|
+
inputTimeoutMs: browserConfig?.inputTimeoutMs,
|
|
183
|
+
timeoutMs: browserConfig?.timeoutMs,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
log?.(`[gemini-web] Deep Think response received (${domResult.text.length} chars).`);
|
|
187
|
+
return domResult;
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
await session.close();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
94
193
|
async function loadGeminiCookiesFromInline(browserConfig, log) {
|
|
95
194
|
const inline = browserConfig?.inlineCookies;
|
|
96
195
|
if (!inline || inline.length === 0)
|
|
97
|
-
return {};
|
|
98
|
-
const cookieMap = buildGeminiCookieMap(inline.filter((cookie) => Boolean(cookie?.name && typeof cookie.value ===
|
|
196
|
+
return { cookieMap: {}, warnings: [] };
|
|
197
|
+
const cookieMap = buildGeminiCookieMap(inline.filter((cookie) => Boolean(cookie?.name && typeof cookie.value === "string")));
|
|
99
198
|
if (Object.keys(cookieMap).length > 0) {
|
|
100
|
-
const source = browserConfig?.inlineCookiesSource ??
|
|
199
|
+
const source = browserConfig?.inlineCookiesSource ?? "inline";
|
|
101
200
|
log?.(`[gemini-web] Loaded Gemini cookies from inline payload (${source}): ${Object.keys(cookieMap).length} cookie(s).`);
|
|
102
201
|
}
|
|
103
202
|
else {
|
|
104
|
-
log?.(
|
|
203
|
+
log?.("[gemini-web] Inline cookie payload provided but no Gemini cookies matched.");
|
|
105
204
|
}
|
|
106
|
-
return cookieMap;
|
|
205
|
+
return { cookieMap, warnings: [] };
|
|
107
206
|
}
|
|
108
207
|
async function loadGeminiCookiesFromChrome(browserConfig, log) {
|
|
109
208
|
try {
|
|
110
209
|
// Learned: Gemini web relies on Google auth cookies in the *browser* profile, not API keys.
|
|
111
210
|
const profileCandidate = browserConfig?.chromeCookiePath ?? browserConfig?.chromeProfile ?? undefined;
|
|
112
|
-
const profile = typeof profileCandidate ===
|
|
211
|
+
const profile = typeof profileCandidate === "string" && profileCandidate.trim().length > 0
|
|
113
212
|
? profileCandidate.trim()
|
|
114
213
|
: undefined;
|
|
115
214
|
const sources = [
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
215
|
+
"https://gemini.google.com",
|
|
216
|
+
"https://accounts.google.com",
|
|
217
|
+
"https://www.google.com",
|
|
119
218
|
];
|
|
120
219
|
const { cookies, warnings } = await getCookies({
|
|
121
220
|
url: sources[0],
|
|
122
221
|
origins: sources,
|
|
123
222
|
names: [...GEMINI_COOKIE_NAMES],
|
|
124
|
-
browsers: [
|
|
125
|
-
mode:
|
|
223
|
+
browsers: ["chrome"],
|
|
224
|
+
mode: "merge",
|
|
126
225
|
chromeProfile: profile,
|
|
127
226
|
timeoutMs: 5_000,
|
|
128
227
|
});
|
|
129
228
|
if (warnings.length && log?.verbose) {
|
|
130
|
-
log(`[gemini-web] Cookie warnings:\n- ${warnings.join(
|
|
229
|
+
log(`[gemini-web] Cookie warnings:\n- ${warnings.join("\n- ")}`);
|
|
131
230
|
}
|
|
132
231
|
const cookieMap = buildGeminiCookieMap(cookies);
|
|
133
232
|
log?.(`[gemini-web] Loaded Gemini cookies from Chrome (node): ${Object.keys(cookieMap).length} cookie(s).`);
|
|
134
|
-
return cookieMap;
|
|
233
|
+
return { cookieMap, warnings };
|
|
135
234
|
}
|
|
136
235
|
catch (error) {
|
|
137
|
-
log?.(`[gemini-web] Failed to load Chrome cookies via node: ${error instanceof Error ? error.message : String(error ??
|
|
138
|
-
return {};
|
|
236
|
+
log?.(`[gemini-web] Failed to load Chrome cookies via node: ${error instanceof Error ? error.message : String(error ?? "")}`);
|
|
237
|
+
return { cookieMap: {}, warnings: [] };
|
|
139
238
|
}
|
|
140
239
|
}
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
if (
|
|
145
|
-
return
|
|
240
|
+
function formatGeminiCookieError(warnings) {
|
|
241
|
+
const base = "Gemini browser mode requires Chrome cookies for google.com (missing __Secure-1PSID/__Secure-1PSIDTS).";
|
|
242
|
+
const guidance = "Try --browser-manual-login or --browser-inline-cookies-file if local cookie extraction is unavailable.";
|
|
243
|
+
if (warnings.length === 0) {
|
|
244
|
+
return `${base} ${guidance}`;
|
|
245
|
+
}
|
|
246
|
+
return `${base}\nCookie read warnings:\n- ${warnings.join("\n- ")}\n${guidance}`;
|
|
247
|
+
}
|
|
248
|
+
async function loadGeminiCookies(browserConfig, log, options) {
|
|
249
|
+
const inlineResult = await loadGeminiCookiesFromInline(browserConfig, log);
|
|
250
|
+
const hasInlineRequired = hasRequiredGeminiCookies(inlineResult.cookieMap);
|
|
251
|
+
if (hasInlineRequired) {
|
|
252
|
+
return inlineResult;
|
|
253
|
+
}
|
|
254
|
+
const manualNoKeychain = Boolean(browserConfig?.manualLogin) || Boolean(options?.preferManualNoKeychain);
|
|
255
|
+
if (manualNoKeychain) {
|
|
256
|
+
log?.("[gemini-web] Using manual-login cookie extraction path (no keychain cookie read).");
|
|
257
|
+
const cdpResult = await loadGeminiCookiesFromCDP(browserConfig, log);
|
|
258
|
+
return {
|
|
259
|
+
cookieMap: { ...cdpResult.cookieMap, ...inlineResult.cookieMap },
|
|
260
|
+
warnings: [...inlineResult.warnings, ...cdpResult.warnings],
|
|
261
|
+
};
|
|
146
262
|
}
|
|
147
263
|
if (browserConfig?.cookieSync === false && !hasInlineRequired) {
|
|
148
|
-
log?.(
|
|
149
|
-
return
|
|
264
|
+
log?.("[gemini-web] Cookie sync disabled and inline cookies missing Gemini auth tokens.");
|
|
265
|
+
return inlineResult;
|
|
150
266
|
}
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
267
|
+
const chromeResult = await loadGeminiCookiesFromChrome(browserConfig, log);
|
|
268
|
+
return {
|
|
269
|
+
cookieMap: { ...chromeResult.cookieMap, ...inlineResult.cookieMap },
|
|
270
|
+
warnings: [...inlineResult.warnings, ...chromeResult.warnings],
|
|
271
|
+
};
|
|
154
272
|
}
|
|
155
273
|
export function createGeminiWebExecutor(geminiOptions) {
|
|
156
274
|
return async (runOptions) => {
|
|
157
275
|
const startTime = Date.now();
|
|
158
276
|
const log = runOptions.log;
|
|
159
|
-
log?.(
|
|
160
|
-
const
|
|
161
|
-
if (!hasRequiredGeminiCookies(cookieMap)) {
|
|
162
|
-
throw new Error('Gemini browser mode requires Chrome cookies for google.com (missing __Secure-1PSID/__Secure-1PSIDTS).');
|
|
163
|
-
}
|
|
164
|
-
const configTimeout = typeof runOptions.config?.timeoutMs === 'number' && Number.isFinite(runOptions.config.timeoutMs)
|
|
165
|
-
? Math.max(1_000, runOptions.config.timeoutMs)
|
|
166
|
-
: null;
|
|
167
|
-
const defaultTimeoutMs = geminiOptions.youtube
|
|
168
|
-
? 240_000
|
|
169
|
-
: geminiOptions.generateImage || geminiOptions.editImage
|
|
170
|
-
? 300_000
|
|
171
|
-
: 120_000;
|
|
172
|
-
const timeoutMs = Math.min(configTimeout ?? defaultTimeoutMs, 600_000);
|
|
173
|
-
const controller = new AbortController();
|
|
174
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
277
|
+
log?.("[gemini-web] Starting Gemini web executor (TypeScript)");
|
|
278
|
+
const model = resolveGeminiWebModel(runOptions.config?.desiredModel, log);
|
|
175
279
|
const generateImagePath = resolveInvocationPath(geminiOptions.generateImage);
|
|
176
280
|
const editImagePath = resolveInvocationPath(geminiOptions.editImage);
|
|
177
281
|
const outputPath = resolveInvocationPath(geminiOptions.outputPath);
|
|
@@ -186,100 +290,154 @@ export function createGeminiWebExecutor(geminiOptions) {
|
|
|
186
290
|
if (generateImagePath && !editImagePath) {
|
|
187
291
|
prompt = `Generate an image: ${prompt}`;
|
|
188
292
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
files: attachmentPaths,
|
|
205
|
-
model,
|
|
206
|
-
cookieMap,
|
|
207
|
-
chatMetadata: intro.metadata,
|
|
208
|
-
signal: controller.signal,
|
|
209
|
-
});
|
|
210
|
-
response = {
|
|
211
|
-
text: out.text ?? null,
|
|
212
|
-
thoughts: geminiOptions.showThoughts ? out.thoughts : null,
|
|
213
|
-
has_images: false,
|
|
214
|
-
image_count: 0,
|
|
215
|
-
};
|
|
216
|
-
const resolvedOutputPath = outputPath ?? generateImagePath ?? 'generated.png';
|
|
217
|
-
const imageSave = await saveFirstGeminiImageFromOutput(out, cookieMap, resolvedOutputPath, controller.signal);
|
|
218
|
-
response.has_images = imageSave.saved;
|
|
219
|
-
response.image_count = imageSave.imageCount;
|
|
220
|
-
if (!imageSave.saved) {
|
|
221
|
-
throw new Error(`No images generated. Response text:\n${out.text || '(empty response)'}`);
|
|
293
|
+
const modeSelection = selectGeminiExecutionMode({
|
|
294
|
+
model,
|
|
295
|
+
attachmentPaths,
|
|
296
|
+
generateImagePath,
|
|
297
|
+
editImagePath,
|
|
298
|
+
});
|
|
299
|
+
const domClient = {
|
|
300
|
+
mode: "dom",
|
|
301
|
+
execute: async () => {
|
|
302
|
+
log?.("[gemini-web] Using browser DOM automation for Deep Think.");
|
|
303
|
+
const browserResult = await runGeminiDeepThinkViaBrowser(prompt, runOptions.config, log);
|
|
304
|
+
const tookMs = Date.now() - startTime;
|
|
305
|
+
let answerMarkdown = browserResult.text;
|
|
306
|
+
if (geminiOptions.showThoughts && browserResult.thoughts) {
|
|
307
|
+
answerMarkdown = `## Thinking\n\n${browserResult.thoughts}\n\n## Response\n\n${browserResult.text}`;
|
|
222
308
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
chatMetadata: null,
|
|
231
|
-
signal: controller.signal,
|
|
232
|
-
});
|
|
233
|
-
response = {
|
|
234
|
-
text: out.text ?? null,
|
|
235
|
-
thoughts: geminiOptions.showThoughts ? out.thoughts : null,
|
|
236
|
-
has_images: false,
|
|
237
|
-
image_count: 0,
|
|
309
|
+
log?.(`[gemini-web] Completed in ${tookMs}ms`);
|
|
310
|
+
return {
|
|
311
|
+
answerText: browserResult.text,
|
|
312
|
+
answerMarkdown,
|
|
313
|
+
tookMs,
|
|
314
|
+
answerTokens: estimateTokenCount(browserResult.text),
|
|
315
|
+
answerChars: browserResult.text.length,
|
|
238
316
|
};
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const out = await runGeminiWebWithFallback({
|
|
248
|
-
prompt,
|
|
249
|
-
files: attachmentPaths,
|
|
250
|
-
model,
|
|
251
|
-
cookieMap,
|
|
252
|
-
chatMetadata: null,
|
|
253
|
-
signal: controller.signal,
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
const httpClient = {
|
|
320
|
+
mode: "http",
|
|
321
|
+
execute: async () => {
|
|
322
|
+
const useNoKeychainPath = Boolean(runOptions.config?.manualLogin);
|
|
323
|
+
const cookieResult = await loadGeminiCookies(runOptions.config, log, {
|
|
324
|
+
preferManualNoKeychain: useNoKeychainPath,
|
|
254
325
|
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
326
|
+
if (!hasRequiredGeminiCookies(cookieResult.cookieMap)) {
|
|
327
|
+
throw new Error(formatGeminiCookieError(cookieResult.warnings));
|
|
328
|
+
}
|
|
329
|
+
const configTimeout = typeof runOptions.config?.timeoutMs === "number" &&
|
|
330
|
+
Number.isFinite(runOptions.config.timeoutMs)
|
|
331
|
+
? Math.max(1_000, runOptions.config.timeoutMs)
|
|
332
|
+
: null;
|
|
333
|
+
const defaultTimeoutMs = geminiOptions.youtube
|
|
334
|
+
? 240_000
|
|
335
|
+
: geminiOptions.generateImage || geminiOptions.editImage
|
|
336
|
+
? 300_000
|
|
337
|
+
: 120_000;
|
|
338
|
+
const timeoutMs = Math.min(configTimeout ?? defaultTimeoutMs, 600_000);
|
|
339
|
+
const controller = new AbortController();
|
|
340
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
341
|
+
let response;
|
|
342
|
+
try {
|
|
343
|
+
if (editImagePath) {
|
|
344
|
+
const intro = await runGeminiWebWithFallback({
|
|
345
|
+
prompt: "Here is an image to edit",
|
|
346
|
+
files: [editImagePath],
|
|
347
|
+
model,
|
|
348
|
+
cookieMap: cookieResult.cookieMap,
|
|
349
|
+
chatMetadata: null,
|
|
350
|
+
signal: controller.signal,
|
|
351
|
+
});
|
|
352
|
+
const editPrompt = `Use image generation tool to ${prompt}`;
|
|
353
|
+
const out = await runGeminiWebWithFallback({
|
|
354
|
+
prompt: editPrompt,
|
|
355
|
+
files: attachmentPaths,
|
|
356
|
+
model,
|
|
357
|
+
cookieMap: cookieResult.cookieMap,
|
|
358
|
+
chatMetadata: intro.metadata,
|
|
359
|
+
signal: controller.signal,
|
|
360
|
+
});
|
|
361
|
+
response = {
|
|
362
|
+
text: out.text ?? null,
|
|
363
|
+
thoughts: geminiOptions.showThoughts ? out.thoughts : null,
|
|
364
|
+
has_images: false,
|
|
365
|
+
image_count: 0,
|
|
366
|
+
};
|
|
367
|
+
const resolvedOutputPath = outputPath ?? generateImagePath ?? "generated.png";
|
|
368
|
+
const imageSave = await saveFirstGeminiImageFromOutput(out, cookieResult.cookieMap, resolvedOutputPath, controller.signal);
|
|
369
|
+
response.has_images = imageSave.saved;
|
|
370
|
+
response.image_count = imageSave.imageCount;
|
|
371
|
+
if (!imageSave.saved) {
|
|
372
|
+
throw new Error(`No images generated. Response text:\n${out.text || "(empty response)"}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
else if (generateImagePath) {
|
|
376
|
+
const out = await runGeminiWebWithFallback({
|
|
377
|
+
prompt,
|
|
378
|
+
files: attachmentPaths,
|
|
379
|
+
model,
|
|
380
|
+
cookieMap: cookieResult.cookieMap,
|
|
381
|
+
chatMetadata: null,
|
|
382
|
+
signal: controller.signal,
|
|
383
|
+
});
|
|
384
|
+
response = {
|
|
385
|
+
text: out.text ?? null,
|
|
386
|
+
thoughts: geminiOptions.showThoughts ? out.thoughts : null,
|
|
387
|
+
has_images: false,
|
|
388
|
+
image_count: 0,
|
|
389
|
+
};
|
|
390
|
+
const imageSave = await saveFirstGeminiImageFromOutput(out, cookieResult.cookieMap, generateImagePath, controller.signal);
|
|
391
|
+
response.has_images = imageSave.saved;
|
|
392
|
+
response.image_count = imageSave.imageCount;
|
|
393
|
+
if (!imageSave.saved) {
|
|
394
|
+
throw new Error(`No images generated. Response text:\n${out.text || "(empty response)"}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const out = await runGeminiWebWithFallback({
|
|
399
|
+
prompt,
|
|
400
|
+
files: attachmentPaths,
|
|
401
|
+
model,
|
|
402
|
+
cookieMap: cookieResult.cookieMap,
|
|
403
|
+
chatMetadata: null,
|
|
404
|
+
signal: controller.signal,
|
|
405
|
+
});
|
|
406
|
+
response = {
|
|
407
|
+
text: out.text ?? null,
|
|
408
|
+
thoughts: geminiOptions.showThoughts ? out.thoughts : null,
|
|
409
|
+
has_images: out.images.length > 0,
|
|
410
|
+
image_count: out.images.length,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
clearTimeout(timeout);
|
|
416
|
+
}
|
|
417
|
+
const answerText = response.text ?? "";
|
|
418
|
+
let answerMarkdown = answerText;
|
|
419
|
+
if (geminiOptions.showThoughts && response.thoughts) {
|
|
420
|
+
answerMarkdown = `## Thinking\n\n${response.thoughts}\n\n## Response\n\n${answerText}`;
|
|
421
|
+
}
|
|
422
|
+
if (response.has_images && response.image_count > 0) {
|
|
423
|
+
const imagePath = generateImagePath || outputPath || "generated.png";
|
|
424
|
+
answerMarkdown += `\n\n*Generated ${response.image_count} image(s). Saved to: ${imagePath}*`;
|
|
425
|
+
}
|
|
426
|
+
const tookMs = Date.now() - startTime;
|
|
427
|
+
log?.(`[gemini-web] Completed in ${tookMs}ms`);
|
|
428
|
+
return {
|
|
429
|
+
answerText,
|
|
430
|
+
answerMarkdown,
|
|
431
|
+
tookMs,
|
|
432
|
+
answerTokens: estimateTokenCount(answerText),
|
|
433
|
+
answerChars: answerText.length,
|
|
260
434
|
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
finally {
|
|
264
|
-
clearTimeout(timeout);
|
|
265
|
-
}
|
|
266
|
-
const answerText = response.text ?? '';
|
|
267
|
-
let answerMarkdown = answerText;
|
|
268
|
-
if (geminiOptions.showThoughts && response.thoughts) {
|
|
269
|
-
answerMarkdown = `## Thinking\n\n${response.thoughts}\n\n## Response\n\n${answerText}`;
|
|
270
|
-
}
|
|
271
|
-
if (response.has_images && response.image_count > 0) {
|
|
272
|
-
const imagePath = generateImagePath || outputPath || 'generated.png';
|
|
273
|
-
answerMarkdown += `\n\n*Generated ${response.image_count} image(s). Saved to: ${imagePath}*`;
|
|
274
|
-
}
|
|
275
|
-
const tookMs = Date.now() - startTime;
|
|
276
|
-
log?.(`[gemini-web] Completed in ${tookMs}ms`);
|
|
277
|
-
return {
|
|
278
|
-
answerText,
|
|
279
|
-
answerMarkdown,
|
|
280
|
-
tookMs,
|
|
281
|
-
answerTokens: estimateTokenCount(answerText),
|
|
282
|
-
answerChars: answerText.length,
|
|
435
|
+
},
|
|
283
436
|
};
|
|
437
|
+
if (model === "gemini-3-pro-deep-think" && modeSelection.mode === "http") {
|
|
438
|
+
log?.(`[gemini-web] Deep Think DOM path skipped (${modeSelection.reasons.join(", ")} requested); using HTTP/header fallback path.`);
|
|
439
|
+
}
|
|
440
|
+
const executionClient = modeSelection.mode === "dom" ? domClient : httpClient;
|
|
441
|
+
return executionClient.execute();
|
|
284
442
|
};
|
|
285
443
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createGeminiWebExecutor } from
|
|
1
|
+
export { createGeminiWebExecutor } from "./executor.js";
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import process from
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { getCliVersion } from "../version.js";
|
|
8
|
+
import { registerConsultTool } from "./tools/consult.js";
|
|
9
|
+
import { registerSessionsTool } from "./tools/sessions.js";
|
|
10
|
+
import { registerSessionResources } from "./tools/sessionResources.js";
|
|
10
11
|
export async function startMcpServer() {
|
|
11
12
|
const server = new McpServer({
|
|
12
|
-
name:
|
|
13
|
+
name: "oracle-mcp",
|
|
13
14
|
version: getCliVersion(),
|
|
14
15
|
}, {
|
|
15
16
|
capabilities: {
|
|
@@ -21,7 +22,7 @@ export async function startMcpServer() {
|
|
|
21
22
|
registerSessionResources(server);
|
|
22
23
|
const transport = new StdioServerTransport();
|
|
23
24
|
transport.onerror = (error) => {
|
|
24
|
-
console.error(
|
|
25
|
+
console.error("MCP transport error:", error);
|
|
25
26
|
};
|
|
26
27
|
const closed = new Promise((resolve) => {
|
|
27
28
|
transport.onclose = () => {
|
|
@@ -32,9 +33,12 @@ export async function startMcpServer() {
|
|
|
32
33
|
await server.connect(transport);
|
|
33
34
|
await closed;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
export function shouldStartMcpServerFromModule(moduleUrl = import.meta.url, argv1 = process.argv[1]) {
|
|
37
|
+
return argv1 ? moduleUrl === pathToFileURL(argv1).href : false;
|
|
38
|
+
}
|
|
39
|
+
if (shouldStartMcpServerFromModule()) {
|
|
36
40
|
startMcpServer().catch((error) => {
|
|
37
|
-
console.error(
|
|
41
|
+
console.error("Failed to start oracle-mcp:", error);
|
|
38
42
|
process.exitCode = 1;
|
|
39
43
|
});
|
|
40
44
|
}
|