@steipete/oracle 0.21.0 → 0.21.1
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/dist/bin/oracle-cli.js +8 -5
- package/dist/docs-site/browser-mode.html +2 -1
- package/dist/docs-site/gemini.html +3 -2
- package/dist/src/browser/executor.js +9 -4
- package/dist/src/browser/provider.js +15 -1
- package/dist/src/browser/sessionRunner.js +2 -2
- package/dist/src/gemini-web/browserSessionManager.js +15 -2
- package/dist/src/gemini-web/executor.js +1 -1
- package/dist/src/gemini-web/http.js +11 -2
- package/dist/src/remote/client.js +25 -9
- package/dist/src/remote/server.js +19 -5
- package/package.json +2 -1
package/dist/bin/oracle-cli.js
CHANGED
|
@@ -651,7 +651,7 @@ program
|
|
|
651
651
|
.option("--remote-host <host:port>", "Delegate browser runs to a remote `oracle serve` instance.")
|
|
652
652
|
.option("--remote-token <token>", "Access token for the remote `oracle serve` instance.")
|
|
653
653
|
.action(async (sessionId, _options, cmd) => {
|
|
654
|
-
const restartOptions = cmd.
|
|
654
|
+
const restartOptions = cmd.optsWithGlobals();
|
|
655
655
|
await restartSession(sessionId, restartOptions);
|
|
656
656
|
});
|
|
657
657
|
function buildRunOptions(options, overrides = {}) {
|
|
@@ -1462,9 +1462,9 @@ async function runRootCommand(options) {
|
|
|
1462
1462
|
});
|
|
1463
1463
|
let browserDeps;
|
|
1464
1464
|
if (browserConfig && remoteHost) {
|
|
1465
|
-
const {
|
|
1465
|
+
const { resolveBrowserExecutor } = await import("../src/browser/executor.js");
|
|
1466
1466
|
browserDeps = {
|
|
1467
|
-
executeBrowser:
|
|
1467
|
+
executeBrowser: await resolveBrowserExecutor({ ...resolvedOptions, model: activeModel }, { host: remoteHost, token: remoteToken }),
|
|
1468
1468
|
};
|
|
1469
1469
|
console.log(chalk.dim(`Routing browser automation to remote host ${remoteHost}`));
|
|
1470
1470
|
}
|
|
@@ -1776,9 +1776,12 @@ async function restartSession(sessionId, options) {
|
|
|
1776
1776
|
}
|
|
1777
1777
|
let browserDeps;
|
|
1778
1778
|
if (browserConfig && remoteHost) {
|
|
1779
|
-
const {
|
|
1779
|
+
const { resolveBrowserExecutor } = await import("../src/browser/executor.js");
|
|
1780
1780
|
browserDeps = {
|
|
1781
|
-
executeBrowser:
|
|
1781
|
+
executeBrowser: await resolveBrowserExecutor(runOptions, {
|
|
1782
|
+
host: remoteHost,
|
|
1783
|
+
token: remoteToken,
|
|
1784
|
+
}),
|
|
1782
1785
|
};
|
|
1783
1786
|
console.log(chalk.dim(`Routing browser automation to remote host ${remoteHost}`));
|
|
1784
1787
|
}
|
|
@@ -515,13 +515,14 @@ body:not(.home) .doc>h1:first-child{display:none}
|
|
|
515
515
|
<p>``<code>bash oracle serve </code>``</p>
|
|
516
516
|
<p>Oracle picks a free port, launches Chrome, starts an HTTP/SSE API, and prints:</p>
|
|
517
517
|
<p>``<code> Listening at 0.0.0.0:9473 Access token: c4e5f9... </code>``</p>
|
|
518
|
-
<p>Use <code>--host</code>, <code>--port</code>, or <code>--token</code> to override the defaults if needed. On first use, sign in to ChatGPT in the dedicated automation Chrome window. The service keeps that profile for later runs.</p>
|
|
518
|
+
<p>Use <code>--host</code>, <code>--port</code>, or <code>--token</code> to override the defaults if needed. On first use, sign in to ChatGPT or Gemini in the dedicated automation Chrome window, according to the models you use. The service keeps that profile for later runs.</p>
|
|
519
519
|
<ol>
|
|
520
520
|
<li><strong>Run from your laptop</strong></li>
|
|
521
521
|
</ol>
|
|
522
522
|
<p>``<code>bash oracle --engine browser \ --remote-host 192.168.64.2:9473 \ --remote-token c4e5f9... \ --prompt "Summarize the incident doc" \ --file docs/incidents/latest.md </code>``</p>
|
|
523
523
|
<ul>
|
|
524
524
|
<li><code>--remote-host</code> points the CLI at the VM.</li>
|
|
525
|
+
<li><code>--model gemini-3.5-flash</code> (or another supported Gemini browser model) selects the Gemini web executor on the host. Upgrade both endpoints for remote Gemini; see <a href="gemini.html">Gemini</a> for supported options. GPT models keep the ChatGPT browser path.</li>
|
|
525
526
|
<li><code>--remote-token</code> matches the token printed by <code>oracle serve</code> (set <code>ORACLE_REMOTE_TOKEN</code> to avoid repeating it).</li>
|
|
526
527
|
<li>You can also set defaults in <code>~/.oracle/config.json</code> (<code>browser.remoteHost</code>, <code>browser.remoteToken</code>) so you don’t need the flags; env vars still override those when present.</li>
|
|
527
528
|
<li>Cookies are <strong>not</strong> transferred from your laptop. The service reuses the dedicated automation profile on the host.</li>
|
|
@@ -289,10 +289,10 @@ body:not(.home) .doc>h1:first-child{display:none}
|
|
|
289
289
|
<li>Legacy <code>gemini-3-pro</code>, <code>gemini-2.5-pro</code>, and <code>gemini-2.5-flash</code> browser names remain accepted and map to current Gemini web models.</li>
|
|
290
290
|
<li>If your logged-in Gemini account can’t access the requested model, Oracle auto-falls back to Gemini 3.1 Flash-Lite and logs the fallback in verbose mode.</li>
|
|
291
291
|
<li>Pass <code>--no-gemini-fallback</code> to fail instead when the requested web model is unavailable.</li>
|
|
292
|
-
<li>
|
|
292
|
+
<li>Gemini web requests accept response headers up to 64 KiB automatically, including Google's large security and reporting policies; no Node options are required. These requests honor <code>HTTP_PROXY</code>, <code>HTTPS_PROXY</code>, and <code>NO_PROXY</code> (including lowercase equivalents) and Node's TLS trust configuration.</li>
|
|
293
293
|
<li>This path runs fully in Node/TypeScript (no Python/venv dependency).</li>
|
|
294
294
|
<li>Local MCP consultations and detached workers use the same Gemini executor as the CLI and preserve the session's saved Gemini options. The explicit model key takes precedence over ChatGPT picker labels.</li>
|
|
295
|
-
<li>
|
|
295
|
+
<li>Gemini browser runs also support <code>--remote-host</code>. Upgrade both endpoints, sign into Gemini on the service host, then run <code>oracle --engine browser --model gemini-3.5-flash --remote-host <host:port> --remote-token <token> --prompt "Say OK."</code>. New runs and restarts dispatch the Gemini web executor on that host; Google cookies and browser state stay there. Host manual-login profiles and <code>oracle serve --browser-attach-running</code> are supported. Text, file attachments, YouTube prompts, thoughts, and <code>--no-gemini-fallback</code> are forwarded; image generation/editing still require a local Gemini run.</li>
|
|
296
296
|
<li><code>--browser-model-strategy</code> only affects ChatGPT automation; Gemini web always uses the explicit Gemini model ID.</li>
|
|
297
297
|
<li><code>gemini-3-deep-think</code> is browser-only for now. <code>--engine api</code> rejects it instead of silently falling back to regular Gemini Pro.</li>
|
|
298
298
|
<li>Oracle intentionally does not expose generic <code>low</code> / <code>medium</code> / <code>high</code> Gemini aliases. Explicit IDs keep model choice, billing, and thinking-effort configuration distinct.</li>
|
|
@@ -313,6 +313,7 @@ body:not(.home) .doc>h1:first-child{display:none}
|
|
|
313
313
|
<ul>
|
|
314
314
|
<li><code>src/gemini-web/models.ts</code> — centralizes current private web model headers, legacy aliases, and fallback selection.</li>
|
|
315
315
|
<li><code>src/gemini-web/client.ts</code> — talks to <code>gemini.google.com</code> and downloads generated images via authenticated <code>gg-dl</code> redirects.</li>
|
|
316
|
+
<li><code>src/gemini-web/http.ts</code> — uses a reusable, Gemini-only Undici dispatcher with a 64 KiB response-header limit. It does not change the global dispatcher or replay failed requests. Embedders with custom dispatcher/proxy/TLS settings can use <code>createGeminiWebDispatcher(options)</code> and pass <code>dispatcher</code> to <code>fetchGeminiWebResource</code>; an explicitly supplied dispatcher owns its header limit. Global dispatcher customizations do not configure this dedicated transport.</li>
|
|
316
317
|
<li><code>src/gemini-web/executor.ts</code> — browser-engine executor for Gemini (loads Chrome cookies and runs the web client).</li>
|
|
317
318
|
</ul>
|
|
318
319
|
<h2 id="testing"><a class="anchor" href="#testing" aria-label="Anchor link">#</a>Testing</h2>
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { resolveBrowserProvider } from "./provider.js";
|
|
2
|
-
export async function
|
|
2
|
+
export async function resolveBrowserExecutor(options, remote) {
|
|
3
3
|
const provider = resolveBrowserProvider(options.model);
|
|
4
|
+
if (!provider) {
|
|
5
|
+
throw new Error(`Unsupported browser model: ${options.model}. Use a GPT or Gemini model.`);
|
|
6
|
+
}
|
|
7
|
+
if (remote) {
|
|
8
|
+
const { createRemoteBrowserExecutor } = await import("../remote/client.js");
|
|
9
|
+
return createRemoteBrowserExecutor({ ...remote, runOptions: options });
|
|
10
|
+
}
|
|
4
11
|
if (provider === "gemini") {
|
|
5
12
|
const { createGeminiWebExecutor } = await import("../gemini-web/index.js");
|
|
6
13
|
return createGeminiWebExecutor({
|
|
@@ -13,7 +20,5 @@ export async function resolveLocalBrowserExecutor(options) {
|
|
|
13
20
|
allowModelFallback: options.geminiAllowModelFallback,
|
|
14
21
|
});
|
|
15
22
|
}
|
|
16
|
-
|
|
17
|
-
return (await import("../browserMode.js")).runBrowserMode;
|
|
18
|
-
throw new Error(`Unsupported browser model: ${options.model}. Use a GPT or Gemini model.`);
|
|
23
|
+
return (await import("../browserMode.js")).runBrowserMode;
|
|
19
24
|
}
|
|
@@ -8,4 +8,18 @@ export function resolveBrowserProvider(model) {
|
|
|
8
8
|
return "chatgpt";
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export function resolveRemoteBrowserModel(model, desiredModel) {
|
|
12
|
+
if (model !== undefined) {
|
|
13
|
+
if (typeof model === "string" && resolveBrowserProvider(model))
|
|
14
|
+
return model;
|
|
15
|
+
throw new Error(`Unsupported browser model: ${String(model)}. Use a GPT or Gemini model.`);
|
|
16
|
+
}
|
|
17
|
+
if (typeof desiredModel === "string" && resolveBrowserProvider(desiredModel))
|
|
18
|
+
return desiredModel;
|
|
19
|
+
// Older ChatGPT clients send only a picker label, or omit the selection entirely.
|
|
20
|
+
if (desiredModel == null ||
|
|
21
|
+
(typeof desiredModel === "string" &&
|
|
22
|
+
/^(?:|latest|auto|pro|thinking|instant)(?:\s.*)?$/i.test(desiredModel.trim())))
|
|
23
|
+
return undefined;
|
|
24
|
+
throw new Error(`Unsupported browser model: ${String(desiredModel)}. Use a GPT or Gemini model.`);
|
|
25
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { formatTokenCount } from "../oracle/runUtils.js";
|
|
3
3
|
import { formatFinishLine } from "../oracle/finishLine.js";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveBrowserExecutor } from "./executor.js";
|
|
5
5
|
import { DEFAULT_BROWSER_CONFIG } from "./config.js";
|
|
6
6
|
import { redactBrowserConfigForDebugLog } from "./configLogging.js";
|
|
7
7
|
import { assembleBrowserPrompt, cleanupGeneratedBrowserBundles, materializeBrowserFallback, } from "./prompt.js";
|
|
@@ -67,7 +67,7 @@ export async function runBrowserSessionExecution({ runOptions, browserConfig, cw
|
|
|
67
67
|
if (signal?.aborted) {
|
|
68
68
|
throw new BrowserRunCancelledError();
|
|
69
69
|
}
|
|
70
|
-
const executeBrowser = deps.executeBrowser ?? (await
|
|
70
|
+
const executeBrowser = deps.executeBrowser ?? (await resolveBrowserExecutor(runOptions));
|
|
71
71
|
try {
|
|
72
72
|
promptArtifacts = await Promise.race([
|
|
73
73
|
assemblePrompt(runOptions, { cwd }).then(async (artifacts) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import { mkdir } from "node:fs/promises";
|
|
4
|
-
import { launchChrome, connectWithNewTab, closeTab } from "../browser/chromeLifecycle.js";
|
|
4
|
+
import { launchChrome, connectWithNewTab, connectToRemoteChrome, closeTab, } from "../browser/chromeLifecycle.js";
|
|
5
|
+
import { resolveAttachRunningConnection } from "../browser/attachRunning.js";
|
|
5
6
|
import { resolveBrowserConfig } from "../browser/config.js";
|
|
6
7
|
import { readDevToolsPort, writeDevToolsActivePort, writeChromePid, cleanupStaleProfileState, verifyDevToolsReachable, } from "../browser/profileState.js";
|
|
7
8
|
export async function openGeminiBrowserSession(input) {
|
|
@@ -12,8 +13,20 @@ export async function openGeminiBrowserSession(input) {
|
|
|
12
13
|
keepBrowser: browserConfig?.keepBrowser ?? keepBrowserDefault,
|
|
13
14
|
});
|
|
14
15
|
const profileDir = resolvedConfig.manualLoginProfileDir ?? path.join(os.homedir(), ".oracle", "browser-profile");
|
|
15
|
-
await mkdir(profileDir, { recursive: true });
|
|
16
16
|
const keepBrowser = Boolean(resolvedConfig.keepBrowser);
|
|
17
|
+
if (resolvedConfig.attachRunning || resolvedConfig.remoteChrome) {
|
|
18
|
+
const logger = log ?? (() => { });
|
|
19
|
+
const endpoint = await resolveAttachRunningConnection(resolvedConfig, logger);
|
|
20
|
+
const connection = await connectToRemoteChrome(endpoint.host, endpoint.port, logger, "about:blank", endpoint.browserWSEndpoint, { approvalWaitMs: resolvedConfig.approvalWaitMs, fallbackToDefault: false });
|
|
21
|
+
return {
|
|
22
|
+
profileDir: endpoint.profileRoot ?? profileDir,
|
|
23
|
+
port: endpoint.port,
|
|
24
|
+
client: connection.client,
|
|
25
|
+
targetId: connection.targetId,
|
|
26
|
+
close: () => connection.close({ preserveTarget: keepBrowser }),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
await mkdir(profileDir, { recursive: true });
|
|
17
30
|
let port = await readDevToolsPort(profileDir);
|
|
18
31
|
let launchedChrome = null;
|
|
19
32
|
let chromeWasLaunched = false;
|
|
@@ -228,7 +228,7 @@ async function loadGeminiCookies(browserConfig, log, options) {
|
|
|
228
228
|
if (hasInlineRequired) {
|
|
229
229
|
return inlineResult;
|
|
230
230
|
}
|
|
231
|
-
const manualNoKeychain = Boolean(browserConfig?.manualLogin) || Boolean(options?.preferManualNoKeychain);
|
|
231
|
+
const manualNoKeychain = Boolean(browserConfig?.manualLogin || browserConfig?.attachRunning || browserConfig?.remoteChrome) || Boolean(options?.preferManualNoKeychain);
|
|
232
232
|
if (manualNoKeychain) {
|
|
233
233
|
log?.("[gemini-web] Using manual-login cookie extraction path (no keychain cookie read).");
|
|
234
234
|
const cdpResult = await loadGeminiCookiesFromCDP(browserConfig, log);
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import { EnvHttpProxyAgent } from "undici";
|
|
2
|
+
// Gemini's /app policy/reporting headers exceed Node's default 16 KiB limit.
|
|
3
|
+
const GEMINI_MAX_HEADER_SIZE = 64 * 1024;
|
|
4
|
+
let geminiDispatcher;
|
|
5
|
+
export function createGeminiWebDispatcher(options = {}) {
|
|
6
|
+
return new EnvHttpProxyAgent({ ...options, maxHeaderSize: GEMINI_MAX_HEADER_SIZE });
|
|
7
|
+
}
|
|
1
8
|
export async function fetchGeminiWebResource(url, init = {}) {
|
|
2
9
|
try {
|
|
3
|
-
|
|
10
|
+
const dispatcher = init.dispatcher ?? (geminiDispatcher ??= createGeminiWebDispatcher());
|
|
11
|
+
const options = { ...init, dispatcher };
|
|
12
|
+
return await fetch(url, options);
|
|
4
13
|
}
|
|
5
14
|
catch (error) {
|
|
6
15
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
@@ -8,7 +17,7 @@ export async function fetchGeminiWebResource(url, init = {}) {
|
|
|
8
17
|
typeof cause === "object" &&
|
|
9
18
|
"code" in cause &&
|
|
10
19
|
cause.code === "UND_ERR_HEADERS_OVERFLOW") {
|
|
11
|
-
throw new Error("Gemini response headers exceed
|
|
20
|
+
throw new Error("Gemini response headers exceed the HTTP transport's configured limit (Oracle's default is 64 KiB).", { cause: error });
|
|
12
21
|
}
|
|
13
22
|
throw error;
|
|
14
23
|
}
|
|
@@ -11,16 +11,18 @@ import { checkRemoteHealth } from "./health.js";
|
|
|
11
11
|
import { parseHostPort } from "../bridge/connection.js";
|
|
12
12
|
import { BrowserRunCancelledError } from "../oracle/errors.js";
|
|
13
13
|
import { resolveSiblingImagePath } from "../browser/chatgptImages.js";
|
|
14
|
-
import { resolveBrowserProvider,
|
|
15
|
-
export function createRemoteBrowserExecutor({ host, token }) {
|
|
14
|
+
import { resolveBrowserProvider, resolveRemoteBrowserModel } from "../browser/provider.js";
|
|
15
|
+
export function createRemoteBrowserExecutor({ host, token, runOptions }) {
|
|
16
16
|
// Return a drop-in replacement for runBrowserMode so the browser session runner can stay unchanged.
|
|
17
17
|
return async function remoteBrowserExecutor(options) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
const model = resolveRemoteBrowserModel(options.model ?? runOptions?.model, options.config?.desiredModel);
|
|
19
|
+
const gemini = resolveBrowserProvider(model) === "gemini";
|
|
20
|
+
if (gemini &&
|
|
21
|
+
(runOptions?.editImage ||
|
|
22
|
+
runOptions?.generateImage ||
|
|
23
|
+
options.generateImagePath ||
|
|
24
|
+
options.outputPath)) {
|
|
25
|
+
throw new Error("Remote Gemini image generation and editing are not supported; run these requests locally.");
|
|
24
26
|
}
|
|
25
27
|
if (options.config?.researchMode === "search") {
|
|
26
28
|
throw new Error("Web Search is a local browser pilot; --remote-host does not negotiate this capability yet. Use local Chrome or --browser-attach-running.");
|
|
@@ -46,8 +48,22 @@ export function createRemoteBrowserExecutor({ host, token }) {
|
|
|
46
48
|
prompt: options.prompt,
|
|
47
49
|
attachments: await serializeAttachments(options.attachments ?? []),
|
|
48
50
|
fallbackSubmission: await serializeFallback(options.fallbackSubmission, { host, token }),
|
|
49
|
-
browserConfig:
|
|
51
|
+
browserConfig: {
|
|
52
|
+
...options.config,
|
|
53
|
+
// Keep old hosts fail-closed for Gemini even when the picker label is unrelated.
|
|
54
|
+
...(gemini ? { desiredModel: model } : {}),
|
|
55
|
+
inlineCookies: null,
|
|
56
|
+
inlineCookiesSource: null,
|
|
57
|
+
},
|
|
50
58
|
options: {
|
|
59
|
+
model,
|
|
60
|
+
...(gemini
|
|
61
|
+
? {
|
|
62
|
+
youtube: runOptions?.youtube,
|
|
63
|
+
geminiShowThoughts: runOptions?.geminiShowThoughts,
|
|
64
|
+
geminiAllowModelFallback: runOptions?.geminiAllowModelFallback,
|
|
65
|
+
}
|
|
66
|
+
: {}),
|
|
51
67
|
heartbeatIntervalMs: options.heartbeatIntervalMs,
|
|
52
68
|
verbose: options.verbose,
|
|
53
69
|
sessionId: options.sessionId,
|
|
@@ -9,7 +9,7 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
9
9
|
import { mkdtemp, rm, mkdir, writeFile, stat, realpath } from "node:fs/promises";
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import { materializeStagedFallbackBundle } from "../browser/prompt.js";
|
|
12
|
-
import {
|
|
12
|
+
import { resolveBrowserExecutor } from "../browser/executor.js";
|
|
13
13
|
import { resolveBrowserConfig } from "../browser/config.js";
|
|
14
14
|
import { RunSlots } from "./runSlots.js";
|
|
15
15
|
export { RunSlots } from "./runSlots.js";
|
|
@@ -19,7 +19,7 @@ import { getCookies } from "@steipete/sweet-cookie";
|
|
|
19
19
|
import { CHATGPT_URL } from "../browser/constants.js";
|
|
20
20
|
import { getCliVersion } from "../version.js";
|
|
21
21
|
import { getOracleHomeDir } from "../oracleHome.js";
|
|
22
|
-
import { resolveBrowserProvider,
|
|
22
|
+
import { resolveBrowserProvider, resolveRemoteBrowserModel } from "../browser/provider.js";
|
|
23
23
|
import { cleanupStaleProfileState, readDevToolsPort, verifyDevToolsReachable, writeChromePid, writeDevToolsActivePort, } from "../browser/profileState.js";
|
|
24
24
|
import { normalizeChatgptUrl } from "../browser/utils.js";
|
|
25
25
|
import { computeFileSha256, resolveSessionArtifactsDir, sanitizeArtifactFilename, sanitizeArtifactMimeType, validateArtifactFile, } from "../browser/artifacts.js";
|
|
@@ -60,7 +60,6 @@ function validateAdmissionOptions(options) {
|
|
|
60
60
|
throw new Error("--max-queued-runs must be a nonnegative integer.");
|
|
61
61
|
}
|
|
62
62
|
export async function createRemoteServer(options = {}, deps = {}) {
|
|
63
|
-
const runBrowser = deps.runBrowser ?? runBrowserMode;
|
|
64
63
|
const attachedBrowser = usesHostBrowserAttachment(options.browserConfig);
|
|
65
64
|
const manualLoginDefault = !attachedBrowser && options.manualLoginDefault;
|
|
66
65
|
const hostBrowserConfig = options.browserConfig
|
|
@@ -231,13 +230,20 @@ export async function createRemoteServer(options = {}, deps = {}) {
|
|
|
231
230
|
await abandon();
|
|
232
231
|
return;
|
|
233
232
|
}
|
|
234
|
-
|
|
233
|
+
let model;
|
|
234
|
+
try {
|
|
235
|
+
model = resolveRemoteBrowserModel(payload.options.model, payload.browserConfig?.desiredModel);
|
|
236
|
+
if (resolveBrowserProvider(model) === "gemini" && payload.options.imageOutputRequested) {
|
|
237
|
+
throw new Error("Remote Gemini image generation and editing are not supported; run these requests locally.");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
235
241
|
await abandon();
|
|
236
242
|
if (!res.destroyed) {
|
|
237
243
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
238
244
|
res.end(JSON.stringify({
|
|
239
245
|
error: "unsupported_browser_provider",
|
|
240
|
-
message:
|
|
246
|
+
message: error instanceof Error ? error.message : String(error),
|
|
241
247
|
}));
|
|
242
248
|
}
|
|
243
249
|
return;
|
|
@@ -362,8 +368,16 @@ export async function createRemoteServer(options = {}, deps = {}) {
|
|
|
362
368
|
logger(`[serve] Enforcing manual-login profile at ${options.manualLoginProfileDir ?? "default"} for remote run ${runId}`);
|
|
363
369
|
}
|
|
364
370
|
}
|
|
371
|
+
const runBrowser = deps.runBrowser ??
|
|
372
|
+
(await resolveBrowserExecutor({
|
|
373
|
+
model: model ?? "gpt-5.5",
|
|
374
|
+
youtube: typeof payload.options.youtube === "string" ? payload.options.youtube : undefined,
|
|
375
|
+
geminiShowThoughts: payload.options.geminiShowThoughts === true,
|
|
376
|
+
geminiAllowModelFallback: payload.options.geminiAllowModelFallback !== false,
|
|
377
|
+
}));
|
|
365
378
|
const result = await runBrowser({
|
|
366
379
|
prompt: payload.prompt,
|
|
380
|
+
model,
|
|
367
381
|
attachments,
|
|
368
382
|
fallbackSubmission,
|
|
369
383
|
config: payload.browserConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steipete/oracle",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "CLI wrapper around OpenAI Responses API with GPT-5.6 Sol, GPT-5.6, GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://askoracle.sh",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
"shiki": "^4.4.3",
|
|
80
80
|
"toasted-notifier": "^10.1.0",
|
|
81
81
|
"tokentally": "^0.1.6",
|
|
82
|
+
"undici": "^7.29.1",
|
|
82
83
|
"zod": "^4.6.2"
|
|
83
84
|
},
|
|
84
85
|
"devDependencies": {
|