@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,28 +1,39 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from
|
|
2
|
-
import path from
|
|
3
|
-
const USER_AGENT =
|
|
4
|
-
const MODEL_HEADER_NAME =
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
4
|
+
const MODEL_HEADER_NAME = "x-goog-ext-525001261-jspb";
|
|
5
5
|
const MODEL_HEADERS = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
"gemini-3-pro": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]',
|
|
7
|
+
"gemini-3-pro-deep-think": '[1,null,null,null,"e6fa609c3fa255c0",null,null,0,[4],null,null,3]',
|
|
8
|
+
"gemini-2.5-pro": '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]',
|
|
9
|
+
"gemini-2.5-flash": '[1,null,null,null,"9ec249fc9ad08861",null,null,0,[4]]',
|
|
10
|
+
};
|
|
11
|
+
const GEMINI_APP_URL = "https://gemini.google.com/app";
|
|
12
|
+
const GEMINI_STREAM_GENERATE_URL = "https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate";
|
|
13
|
+
const GEMINI_UPLOAD_URL = "https://content-push.googleapis.com/upload";
|
|
14
|
+
const GEMINI_UPLOAD_PUSH_ID = "feeds/mcudyrk2a4khkz";
|
|
15
|
+
const GEMINI_UPLOAD_MIME_TYPES = {
|
|
16
|
+
".bmp": "image/bmp",
|
|
17
|
+
".gif": "image/gif",
|
|
18
|
+
".jpeg": "image/jpeg",
|
|
19
|
+
".jpg": "image/jpeg",
|
|
20
|
+
".pdf": "application/pdf",
|
|
21
|
+
".png": "image/png",
|
|
22
|
+
".svg": "image/svg+xml",
|
|
23
|
+
".webp": "image/webp",
|
|
9
24
|
};
|
|
10
|
-
const GEMINI_APP_URL = 'https://gemini.google.com/app';
|
|
11
|
-
const GEMINI_STREAM_GENERATE_URL = 'https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate';
|
|
12
|
-
const GEMINI_UPLOAD_URL = 'https://content-push.googleapis.com/upload';
|
|
13
|
-
const GEMINI_UPLOAD_PUSH_ID = 'feeds/mcudyrk2a4khkz';
|
|
14
25
|
function getNestedValue(value, pathParts, fallback) {
|
|
15
26
|
let current = value;
|
|
16
27
|
for (const part of pathParts) {
|
|
17
28
|
if (current == null)
|
|
18
29
|
return fallback;
|
|
19
|
-
if (typeof part ===
|
|
30
|
+
if (typeof part === "number") {
|
|
20
31
|
if (!Array.isArray(current))
|
|
21
32
|
return fallback;
|
|
22
33
|
current = current[part];
|
|
23
34
|
}
|
|
24
35
|
else {
|
|
25
|
-
if (typeof current !==
|
|
36
|
+
if (typeof current !== "object")
|
|
26
37
|
return fallback;
|
|
27
38
|
current = current[part];
|
|
28
39
|
}
|
|
@@ -31,40 +42,40 @@ function getNestedValue(value, pathParts, fallback) {
|
|
|
31
42
|
}
|
|
32
43
|
function buildCookieHeader(cookieMap) {
|
|
33
44
|
return Object.entries(cookieMap)
|
|
34
|
-
.filter(([, value]) => typeof value ===
|
|
45
|
+
.filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
35
46
|
.map(([name, value]) => `${name}=${value}`)
|
|
36
|
-
.join(
|
|
47
|
+
.join("; ");
|
|
37
48
|
}
|
|
38
49
|
export async function fetchGeminiAccessToken(cookieMap, signal) {
|
|
39
50
|
const cookieHeader = buildCookieHeader(cookieMap);
|
|
40
51
|
const res = await fetch(GEMINI_APP_URL, {
|
|
41
|
-
redirect:
|
|
52
|
+
redirect: "follow",
|
|
42
53
|
signal,
|
|
43
54
|
headers: {
|
|
44
55
|
cookie: cookieHeader,
|
|
45
|
-
|
|
56
|
+
"user-agent": USER_AGENT,
|
|
46
57
|
},
|
|
47
58
|
});
|
|
48
59
|
const html = await res.text();
|
|
49
|
-
const tokens = [
|
|
60
|
+
const tokens = ["SNlM0e", "thykhd"];
|
|
50
61
|
for (const key of tokens) {
|
|
51
62
|
const match = html.match(new RegExp(`"${key}":"(.*?)"`));
|
|
52
63
|
if (match?.[1])
|
|
53
64
|
return match[1];
|
|
54
65
|
}
|
|
55
|
-
throw new Error(
|
|
66
|
+
throw new Error("Unable to locate Gemini access token on gemini.google.com/app (missing SNlM0e/thykhd).");
|
|
56
67
|
}
|
|
57
68
|
function trimGeminiJsonEnvelope(text) {
|
|
58
|
-
const start = text.indexOf(
|
|
59
|
-
const end = text.lastIndexOf(
|
|
69
|
+
const start = text.indexOf("[");
|
|
70
|
+
const end = text.lastIndexOf("]");
|
|
60
71
|
if (start === -1 || end === -1 || end <= start) {
|
|
61
|
-
throw new Error(
|
|
72
|
+
throw new Error("Gemini response did not contain a JSON payload.");
|
|
62
73
|
}
|
|
63
74
|
return text.slice(start, end + 1);
|
|
64
75
|
}
|
|
65
76
|
function extractErrorCode(responseJson) {
|
|
66
77
|
const code = getNestedValue(responseJson, [0, 5, 2, 0, 1, 0], -1);
|
|
67
|
-
return typeof code ===
|
|
78
|
+
return typeof code === "number" && code >= 0 ? code : undefined;
|
|
68
79
|
}
|
|
69
80
|
function extractGgdlUrls(rawText) {
|
|
70
81
|
const matches = rawText.match(/https:\/\/lh3\.googleusercontent\.com\/gg-dl\/[^\s"']+/g) ?? [];
|
|
@@ -79,18 +90,18 @@ function extractGgdlUrls(rawText) {
|
|
|
79
90
|
return urls;
|
|
80
91
|
}
|
|
81
92
|
function ensureFullSizeImageUrl(url) {
|
|
82
|
-
if (url.includes(
|
|
93
|
+
if (url.includes("=s2048"))
|
|
83
94
|
return url;
|
|
84
|
-
if (url.includes(
|
|
95
|
+
if (url.includes("=s"))
|
|
85
96
|
return url;
|
|
86
97
|
return `${url}=s2048`;
|
|
87
98
|
}
|
|
88
99
|
async function fetchWithCookiePreservingRedirects(url, init, signal, maxRedirects = 10) {
|
|
89
100
|
let current = url;
|
|
90
101
|
for (let i = 0; i <= maxRedirects; i += 1) {
|
|
91
|
-
const res = await fetch(current, { ...init, redirect:
|
|
102
|
+
const res = await fetch(current, { ...init, redirect: "manual", signal });
|
|
92
103
|
if (res.status >= 300 && res.status < 400) {
|
|
93
|
-
const location = res.headers.get(
|
|
104
|
+
const location = res.headers.get("location");
|
|
94
105
|
if (!location)
|
|
95
106
|
return res;
|
|
96
107
|
current = new URL(location, current).toString();
|
|
@@ -105,7 +116,7 @@ async function downloadGeminiImage(url, cookieMap, outputPath, signal) {
|
|
|
105
116
|
const res = await fetchWithCookiePreservingRedirects(ensureFullSizeImageUrl(url), {
|
|
106
117
|
headers: {
|
|
107
118
|
cookie: cookieHeader,
|
|
108
|
-
|
|
119
|
+
"user-agent": USER_AGENT,
|
|
109
120
|
},
|
|
110
121
|
}, signal);
|
|
111
122
|
if (!res.ok) {
|
|
@@ -119,15 +130,16 @@ async function uploadGeminiFile(filePath, signal) {
|
|
|
119
130
|
const absPath = path.resolve(process.cwd(), filePath);
|
|
120
131
|
const data = await readFile(absPath);
|
|
121
132
|
const fileName = path.basename(absPath);
|
|
133
|
+
const mimeType = GEMINI_UPLOAD_MIME_TYPES[path.extname(absPath).toLowerCase()] ?? "application/octet-stream";
|
|
122
134
|
const form = new FormData();
|
|
123
|
-
form.append(
|
|
135
|
+
form.append("file", new Blob([data], { type: mimeType }), fileName);
|
|
124
136
|
const res = await fetch(GEMINI_UPLOAD_URL, {
|
|
125
|
-
method:
|
|
126
|
-
redirect:
|
|
137
|
+
method: "POST",
|
|
138
|
+
redirect: "follow",
|
|
127
139
|
signal,
|
|
128
140
|
headers: {
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
"push-id": GEMINI_UPLOAD_PUSH_ID,
|
|
142
|
+
"user-agent": USER_AGENT,
|
|
131
143
|
},
|
|
132
144
|
body: form,
|
|
133
145
|
});
|
|
@@ -135,7 +147,7 @@ async function uploadGeminiFile(filePath, signal) {
|
|
|
135
147
|
if (!res.ok) {
|
|
136
148
|
throw new Error(`File upload failed: ${res.status} ${res.statusText} (${text.slice(0, 200)})`);
|
|
137
149
|
}
|
|
138
|
-
return { id: text, name: fileName };
|
|
150
|
+
return { id: text, name: fileName, mimeType };
|
|
139
151
|
}
|
|
140
152
|
function buildGeminiFReqPayload(prompt, uploaded, chatMetadata) {
|
|
141
153
|
const promptPayload = uploaded.length > 0
|
|
@@ -143,9 +155,8 @@ function buildGeminiFReqPayload(prompt, uploaded, chatMetadata) {
|
|
|
143
155
|
prompt,
|
|
144
156
|
0,
|
|
145
157
|
null,
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
uploaded.map((file) => [[file.id, 1]]),
|
|
158
|
+
// Format: [[[fileId, 1, null, "mimeType"], "filename", ...]]
|
|
159
|
+
uploaded.map((file) => [[file.id, 1, null, file.mimeType], file.name]),
|
|
149
160
|
]
|
|
150
161
|
: [prompt];
|
|
151
162
|
const innerList = [promptPayload, null, chatMetadata ?? null];
|
|
@@ -165,9 +176,15 @@ export function parseGeminiStreamGenerateResponse(rawText) {
|
|
|
165
176
|
const parsed = JSON.parse(partBody);
|
|
166
177
|
const candidateList = getNestedValue(parsed, [4], []);
|
|
167
178
|
if (Array.isArray(candidateList) && candidateList.length > 0) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
179
|
+
const candidateText = getNestedValue(candidateList[0], [1, 0], "");
|
|
180
|
+
const hasText = typeof candidateText === "string" && candidateText.length > 0;
|
|
181
|
+
if (body === null) {
|
|
182
|
+
bodyIndex = i;
|
|
183
|
+
body = parsed;
|
|
184
|
+
}
|
|
185
|
+
else if (hasText) {
|
|
186
|
+
body = parsed;
|
|
187
|
+
}
|
|
171
188
|
}
|
|
172
189
|
}
|
|
173
190
|
catch {
|
|
@@ -176,7 +193,7 @@ export function parseGeminiStreamGenerateResponse(rawText) {
|
|
|
176
193
|
}
|
|
177
194
|
const candidateList = getNestedValue(body, [4], []);
|
|
178
195
|
const firstCandidate = candidateList[0];
|
|
179
|
-
const textRaw = getNestedValue(firstCandidate, [1, 0],
|
|
196
|
+
const textRaw = getNestedValue(firstCandidate, [1, 0], "");
|
|
180
197
|
const cardContent = /^http:\/\/googleusercontent\.com\/card_content\/\d+/.test(textRaw);
|
|
181
198
|
const text = cardContent
|
|
182
199
|
? (getNestedValue(firstCandidate, [22, 0], null) ?? textRaw)
|
|
@@ -190,7 +207,7 @@ export function parseGeminiStreamGenerateResponse(rawText) {
|
|
|
190
207
|
if (!url)
|
|
191
208
|
continue;
|
|
192
209
|
images.push({
|
|
193
|
-
kind:
|
|
210
|
+
kind: "web",
|
|
194
211
|
url,
|
|
195
212
|
title: getNestedValue(webImage, [7, 0], undefined),
|
|
196
213
|
alt: getNestedValue(webImage, [0, 4], undefined),
|
|
@@ -222,10 +239,10 @@ export function parseGeminiStreamGenerateResponse(rawText) {
|
|
|
222
239
|
if (!url)
|
|
223
240
|
continue;
|
|
224
241
|
images.push({
|
|
225
|
-
kind:
|
|
242
|
+
kind: "generated",
|
|
226
243
|
url,
|
|
227
|
-
title:
|
|
228
|
-
alt:
|
|
244
|
+
title: "[Generated Image]",
|
|
245
|
+
alt: "",
|
|
229
246
|
});
|
|
230
247
|
}
|
|
231
248
|
}
|
|
@@ -240,24 +257,24 @@ export async function runGeminiWebOnce(input) {
|
|
|
240
257
|
const uploaded = [];
|
|
241
258
|
for (const file of input.files ?? []) {
|
|
242
259
|
if (input.signal?.aborted) {
|
|
243
|
-
throw new Error(
|
|
260
|
+
throw new Error("Gemini web run aborted before upload.");
|
|
244
261
|
}
|
|
245
262
|
uploaded.push(await uploadGeminiFile(file, input.signal));
|
|
246
263
|
}
|
|
247
264
|
const fReq = buildGeminiFReqPayload(input.prompt, uploaded, input.chatMetadata ?? null);
|
|
248
265
|
const params = new URLSearchParams();
|
|
249
|
-
params.set(
|
|
250
|
-
params.set(
|
|
266
|
+
params.set("at", at);
|
|
267
|
+
params.set("f.req", fReq);
|
|
251
268
|
const res = await fetch(GEMINI_STREAM_GENERATE_URL, {
|
|
252
|
-
method:
|
|
253
|
-
redirect:
|
|
269
|
+
method: "POST",
|
|
270
|
+
redirect: "follow",
|
|
254
271
|
signal: input.signal,
|
|
255
272
|
headers: {
|
|
256
|
-
|
|
257
|
-
origin:
|
|
258
|
-
referer:
|
|
259
|
-
|
|
260
|
-
|
|
273
|
+
"content-type": "application/x-www-form-urlencoded;charset=utf-8",
|
|
274
|
+
origin: "https://gemini.google.com",
|
|
275
|
+
referer: "https://gemini.google.com/",
|
|
276
|
+
"x-same-domain": "1",
|
|
277
|
+
"user-agent": USER_AGENT,
|
|
261
278
|
cookie: cookieHeader,
|
|
262
279
|
[MODEL_HEADER_NAME]: MODEL_HEADERS[input.model],
|
|
263
280
|
},
|
|
@@ -267,7 +284,7 @@ export async function runGeminiWebOnce(input) {
|
|
|
267
284
|
if (!res.ok) {
|
|
268
285
|
return {
|
|
269
286
|
rawResponseText,
|
|
270
|
-
text:
|
|
287
|
+
text: "",
|
|
271
288
|
thoughts: null,
|
|
272
289
|
metadata: input.chatMetadata ?? null,
|
|
273
290
|
images: [],
|
|
@@ -278,7 +295,7 @@ export async function runGeminiWebOnce(input) {
|
|
|
278
295
|
const parsed = parseGeminiStreamGenerateResponse(rawResponseText);
|
|
279
296
|
return {
|
|
280
297
|
rawResponseText,
|
|
281
|
-
text: parsed.text ??
|
|
298
|
+
text: parsed.text ?? "",
|
|
282
299
|
thoughts: parsed.thoughts,
|
|
283
300
|
metadata: parsed.metadata,
|
|
284
301
|
images: parsed.images,
|
|
@@ -296,25 +313,25 @@ export async function runGeminiWebOnce(input) {
|
|
|
296
313
|
const errorCode = extractErrorCode(responseJson);
|
|
297
314
|
return {
|
|
298
315
|
rawResponseText,
|
|
299
|
-
text:
|
|
316
|
+
text: "",
|
|
300
317
|
thoughts: null,
|
|
301
318
|
metadata: input.chatMetadata ?? null,
|
|
302
319
|
images: [],
|
|
303
|
-
errorCode: typeof errorCode ===
|
|
304
|
-
errorMessage: error instanceof Error ? error.message : String(error ??
|
|
320
|
+
errorCode: typeof errorCode === "number" ? errorCode : undefined,
|
|
321
|
+
errorMessage: error instanceof Error ? error.message : String(error ?? ""),
|
|
305
322
|
};
|
|
306
323
|
}
|
|
307
324
|
}
|
|
308
325
|
export async function runGeminiWebWithFallback(input) {
|
|
309
326
|
const attempt = await runGeminiWebOnce(input);
|
|
310
|
-
if (isGeminiModelUnavailable(attempt.errorCode) && input.model !==
|
|
311
|
-
const fallback = await runGeminiWebOnce({ ...input, model:
|
|
312
|
-
return { ...fallback, effectiveModel:
|
|
327
|
+
if (isGeminiModelUnavailable(attempt.errorCode) && input.model !== "gemini-2.5-flash") {
|
|
328
|
+
const fallback = await runGeminiWebOnce({ ...input, model: "gemini-2.5-flash" });
|
|
329
|
+
return { ...fallback, effectiveModel: "gemini-2.5-flash" };
|
|
313
330
|
}
|
|
314
331
|
return { ...attempt, effectiveModel: input.model };
|
|
315
332
|
}
|
|
316
333
|
export async function saveFirstGeminiImageFromOutput(output, cookieMap, outputPath, signal) {
|
|
317
|
-
const generatedOrWeb = output.images.find((img) => img.kind ===
|
|
334
|
+
const generatedOrWeb = output.images.find((img) => img.kind === "generated") ?? output.images[0];
|
|
318
335
|
if (generatedOrWeb?.url) {
|
|
319
336
|
await downloadGeminiImage(generatedOrWeb.url, cookieMap, outputPath, signal);
|
|
320
337
|
return { saved: true, imageCount: output.images.length };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function selectGeminiExecutionMode(input) {
|
|
2
|
+
const reasons = [];
|
|
3
|
+
if (input.model !== "gemini-3-pro-deep-think") {
|
|
4
|
+
return { mode: "http", reasons: ["model"] };
|
|
5
|
+
}
|
|
6
|
+
if (input.attachmentPaths.length > 0) {
|
|
7
|
+
reasons.push("attachments");
|
|
8
|
+
}
|
|
9
|
+
if (input.generateImagePath) {
|
|
10
|
+
reasons.push("image-generation");
|
|
11
|
+
}
|
|
12
|
+
if (input.editImagePath) {
|
|
13
|
+
reasons.push("image-edit");
|
|
14
|
+
}
|
|
15
|
+
return reasons.length === 0 ? { mode: "dom", reasons: [] } : { mode: "http", reasons };
|
|
16
|
+
}
|