@steipete/oracle 0.13.0 → 0.14.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/README.md +20 -9
- package/dist/bin/oracle-cli.js +63 -63
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +1 -0
- package/dist/docs-site/RELEASING.html +410 -0
- package/dist/docs-site/agents.html +374 -0
- package/dist/docs-site/anthropic.html +368 -0
- package/dist/docs-site/bridge.html +400 -0
- package/dist/docs-site/browser-mode.html +593 -0
- package/dist/docs-site/chromium-forks.html +347 -0
- package/dist/docs-site/cli-reference.html +346 -0
- package/dist/docs-site/configuration.html +452 -0
- package/dist/docs-site/favicon.svg +14 -0
- package/dist/docs-site/followup.html +375 -0
- package/dist/docs-site/gemini.html +383 -0
- package/dist/docs-site/grok.html +325 -0
- package/dist/docs-site/index.html +360 -0
- package/dist/docs-site/install.html +335 -0
- package/dist/docs-site/linux.html +321 -0
- package/dist/docs-site/llms.txt +43 -0
- package/dist/docs-site/manual-tests.html +596 -0
- package/dist/docs-site/mcp.html +391 -0
- package/dist/docs-site/multimodel.html +364 -0
- package/dist/docs-site/mythical-pro-agents.html +360 -0
- package/dist/docs-site/notifier.html +338 -0
- package/dist/docs-site/openai-endpoints.html +387 -0
- package/dist/docs-site/openrouter.html +344 -0
- package/dist/docs-site/quickstart.html +369 -0
- package/dist/docs-site/refactor/ux.html +532 -0
- package/dist/docs-site/sessions.html +388 -0
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +79 -0
- package/dist/docs-site/spec.html +363 -0
- package/dist/docs-site/testing.html +320 -0
- package/dist/docs-site/tui-debug.html +326 -0
- package/dist/docs-site/windows-work.html +323 -0
- package/dist/docs-site/windows.html +320 -0
- package/dist/src/browser/actions/assistantResponse.js +91 -9
- package/dist/src/browser/actions/deepResearch.js +180 -52
- package/dist/src/browser/actions/modelSelection.js +13 -11
- package/dist/src/browser/actions/navigation.js +294 -65
- package/dist/src/browser/actions/promptComposer.js +50 -4
- package/dist/src/browser/actions/thinkingTime.js +308 -89
- package/dist/src/browser/artifacts.js +19 -0
- package/dist/src/browser/chatgptFiles.js +797 -0
- package/dist/src/browser/chatgptImages.js +130 -3
- package/dist/src/browser/chromeLifecycle.js +4 -0
- package/dist/src/browser/config.js +2 -0
- package/dist/src/browser/index.js +347 -39
- package/dist/src/browser/pageActions.js +1 -1
- package/dist/src/browser/policies.js +2 -6
- package/dist/src/browser/projectSourcesRunner.js +14 -2
- package/dist/src/browser/prompt.js +11 -15
- package/dist/src/browser/reattach.js +6 -2
- package/dist/src/browser/reattachability.js +22 -10
- package/dist/src/browser/recoverConversation.js +73 -0
- package/dist/src/browser/sessionRunner.js +4 -1
- package/dist/src/cli/browserConfig.js +4 -1
- package/dist/src/cli/browserTabs.js +129 -54
- package/dist/src/cli/followup.js +72 -0
- package/dist/src/cli/help.js +1 -1
- package/dist/src/cli/options.js +16 -0
- package/dist/src/cli/reattachGuidance.js +8 -0
- package/dist/src/cli/runOptions.js +2 -12
- package/dist/src/cli/sessionCommand.js +4 -0
- package/dist/src/cli/sessionDisplay.js +17 -3
- package/dist/src/cli/sessionLineage.js +7 -4
- package/dist/src/cli/sessionRunner.js +16 -1
- package/dist/src/gemini-web/client.js +5 -10
- package/dist/src/gemini-web/executor.js +1 -24
- package/dist/src/gemini-web/models.js +83 -0
- package/dist/src/mcp/server.js +2 -0
- package/dist/src/mcp/tools/chatgptImage.js +127 -0
- package/dist/src/mcp/tools/consult.js +261 -167
- package/dist/src/mcp/types.js +2 -0
- package/dist/src/mcp/utils.js +87 -1
- package/dist/src/oracle/config.js +27 -1
- package/dist/src/oracle/files.js +4 -6
- package/dist/src/oracle/geminiModels.js +2 -0
- package/dist/src/oracle/markdown.js +36 -3
- package/dist/src/oracle/modelResolver.js +21 -13
- package/dist/src/oracle/promptAssembly.js +2 -4
- package/dist/src/oracle/request.js +3 -5
- package/dist/src/oracle/tokenStats.js +5 -1
- package/dist/src/oracle.js +1 -1
- package/dist/src/sessionManager.js +1 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/package.json +19 -28
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { ASSISTANT_ROLE_SELECTOR, CONVERSATION_TURN_SELECTOR } from "./constants.js";
|
|
4
5
|
import { delay } from "./utils.js";
|
|
5
6
|
import { readAssistantSnapshot } from "./pageActions.js";
|
|
6
7
|
import { getOracleHomeDir } from "../oracleHome.js";
|
|
7
8
|
import { resolveSessionArtifactsDir } from "./artifacts.js";
|
|
9
|
+
import { saveAssistantDownloadButtonArtifacts } from "./chatgptFiles.js";
|
|
8
10
|
const GENERATED_IMAGE_WAIT_MIN_MS = 15_000;
|
|
9
11
|
const GENERATED_IMAGE_WAIT_MAX_MS = 15 * 60_000;
|
|
10
12
|
function extractFileId(url) {
|
|
@@ -150,6 +152,29 @@ function contentTypeToExtension(contentType) {
|
|
|
150
152
|
return "svg";
|
|
151
153
|
return "bin";
|
|
152
154
|
}
|
|
155
|
+
function detectImageFile(buffer) {
|
|
156
|
+
if (buffer.length >= 8 &&
|
|
157
|
+
buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
158
|
+
return { extension: "png", mimeType: "image/png" };
|
|
159
|
+
}
|
|
160
|
+
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
|
|
161
|
+
return { extension: "jpg", mimeType: "image/jpeg" };
|
|
162
|
+
}
|
|
163
|
+
if (buffer.length >= 12 &&
|
|
164
|
+
buffer.subarray(0, 4).toString("ascii") === "RIFF" &&
|
|
165
|
+
buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
166
|
+
return { extension: "webp", mimeType: "image/webp" };
|
|
167
|
+
}
|
|
168
|
+
const signature = buffer.subarray(0, 6).toString("ascii");
|
|
169
|
+
if (signature === "GIF87a" || signature === "GIF89a") {
|
|
170
|
+
return { extension: "gif", mimeType: "image/gif" };
|
|
171
|
+
}
|
|
172
|
+
const text = buffer.subarray(0, Math.min(buffer.length, 1024)).toString("utf8").trimStart();
|
|
173
|
+
if (/^(?:<\?xml[^>]*>\s*)?<svg[\s>]/i.test(text)) {
|
|
174
|
+
return { extension: "svg", mimeType: "image/svg+xml" };
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
153
178
|
function resolveSiblingImagePath(basePath, index, extension) {
|
|
154
179
|
const ext = path.extname(basePath);
|
|
155
180
|
const dir = path.dirname(basePath);
|
|
@@ -169,12 +194,13 @@ function sanitizeGeneratedImageStem(value) {
|
|
|
169
194
|
}
|
|
170
195
|
function resolveDefaultGeneratedImagePath(images, sessionId) {
|
|
171
196
|
const primary = images[0];
|
|
172
|
-
const stemSource = primary?.fileId || primary?.alt || primary?.url ||
|
|
173
|
-
const stem = sanitizeGeneratedImageStem(stemSource) ||
|
|
197
|
+
const stemSource = primary?.fileId || primary?.alt || primary?.url || "generated";
|
|
198
|
+
const stem = sanitizeGeneratedImageStem(stemSource) || "generated";
|
|
174
199
|
const baseDir = sessionId
|
|
175
200
|
? resolveSessionArtifactsDir(sessionId)
|
|
176
201
|
: path.join(getOracleHomeDir(), ".temp");
|
|
177
|
-
|
|
202
|
+
const uniqueSuffix = sessionId ? "" : `-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
|
|
203
|
+
return path.join(baseDir, `${stem}${uniqueSuffix}.png`);
|
|
178
204
|
}
|
|
179
205
|
async function buildCookieHeader(Network) {
|
|
180
206
|
const response = await Network.getCookies({ urls: ["https://chatgpt.com/"] });
|
|
@@ -245,11 +271,84 @@ export async function saveChatGptGeneratedImages(params) {
|
|
|
245
271
|
errors,
|
|
246
272
|
};
|
|
247
273
|
}
|
|
274
|
+
async function saveGeneratedImageButtonArtifacts(params) {
|
|
275
|
+
const buttonDownloads = await saveAssistantDownloadButtonArtifacts({
|
|
276
|
+
Browser: params.Browser,
|
|
277
|
+
Client: params.Client,
|
|
278
|
+
Page: params.Page,
|
|
279
|
+
Runtime: params.Runtime,
|
|
280
|
+
logger: params.logger,
|
|
281
|
+
files: [],
|
|
282
|
+
allowGenericDownloadLabels: true,
|
|
283
|
+
downloadPath: path.dirname(params.targetPath),
|
|
284
|
+
minTurnIndex: params.minTurnIndex,
|
|
285
|
+
});
|
|
286
|
+
const buttonImages = [];
|
|
287
|
+
for (const download of buttonDownloads) {
|
|
288
|
+
const contents = await fs.readFile(download.path);
|
|
289
|
+
const detected = detectImageFile(contents);
|
|
290
|
+
if (!detected) {
|
|
291
|
+
await fs.unlink(download.path).catch(() => undefined);
|
|
292
|
+
params.logger?.(`[browser] Ignored non-image assistant download: ${download.label}`);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const index = buttonImages.length;
|
|
296
|
+
const resolvedPath = resolveSiblingImagePath(params.targetPath, index, detected.extension);
|
|
297
|
+
if (path.resolve(download.path) !== resolvedPath) {
|
|
298
|
+
await fs.copyFile(download.path, resolvedPath);
|
|
299
|
+
await fs.unlink(download.path);
|
|
300
|
+
}
|
|
301
|
+
const stat = await fs.stat(resolvedPath);
|
|
302
|
+
buttonImages.push({
|
|
303
|
+
kind: "image",
|
|
304
|
+
path: resolvedPath,
|
|
305
|
+
label: index === 0 ? "Generated image" : `Generated image ${index + 1}`,
|
|
306
|
+
mimeType: detected.mimeType,
|
|
307
|
+
sizeBytes: stat.size,
|
|
308
|
+
sourceUrl: "browser-download",
|
|
309
|
+
url: "browser-download",
|
|
310
|
+
finalUrl: "browser-download",
|
|
311
|
+
alt: download.label,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (buttonImages.length > 0) {
|
|
315
|
+
params.logger?.(`[browser] Saved ${buttonImages.length} generated image download artifact(s).`);
|
|
316
|
+
}
|
|
317
|
+
return buttonImages;
|
|
318
|
+
}
|
|
319
|
+
function formatButtonImageArtifacts(buttonImages, answerText) {
|
|
320
|
+
const primaryPath = buttonImages[0]?.path ?? "";
|
|
321
|
+
return {
|
|
322
|
+
generatedImages: buttonImages.map((image) => ({
|
|
323
|
+
url: image.url,
|
|
324
|
+
alt: image.alt,
|
|
325
|
+
})),
|
|
326
|
+
savedImages: buttonImages,
|
|
327
|
+
imageCount: buttonImages.length,
|
|
328
|
+
markdownSuffix: buttonImages.length > 1
|
|
329
|
+
? `\n\n*Generated ${buttonImages.length} image(s). Saved ${buttonImages.length} file(s) starting at: ${primaryPath}*`
|
|
330
|
+
: `\n\n*Generated 1 image(s). Saved to: ${primaryPath}*`,
|
|
331
|
+
answerText,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
248
334
|
export async function collectGeneratedImageArtifacts(params) {
|
|
249
335
|
const explicitTargetPath = params.generateImagePath ?? params.outputPath;
|
|
250
336
|
let generatedImages = await readAssistantGeneratedImagesWithFallback(params.Runtime, params.minTurnIndex ?? undefined);
|
|
251
337
|
let latestAnswerText = params.answerText;
|
|
252
338
|
if (explicitTargetPath && generatedImages.length === 0) {
|
|
339
|
+
const targetPath = path.resolve(explicitTargetPath);
|
|
340
|
+
const buttonImages = await saveGeneratedImageButtonArtifacts({
|
|
341
|
+
Browser: params.Browser,
|
|
342
|
+
Client: params.Client,
|
|
343
|
+
Page: params.Page,
|
|
344
|
+
Runtime: params.Runtime,
|
|
345
|
+
logger: params.logger,
|
|
346
|
+
minTurnIndex: params.minTurnIndex,
|
|
347
|
+
targetPath,
|
|
348
|
+
});
|
|
349
|
+
if (buttonImages.length > 0) {
|
|
350
|
+
return formatButtonImageArtifacts(buttonImages, latestAnswerText);
|
|
351
|
+
}
|
|
253
352
|
const deadline = Date.now() + resolveGeneratedImageWaitTimeoutMs(params.waitTimeoutMs);
|
|
254
353
|
while (Date.now() < deadline) {
|
|
255
354
|
await delay(1500);
|
|
@@ -263,6 +362,20 @@ export async function collectGeneratedImageArtifacts(params) {
|
|
|
263
362
|
latestAnswerText = snapshotText;
|
|
264
363
|
}
|
|
265
364
|
}
|
|
365
|
+
if (generatedImages.length === 0) {
|
|
366
|
+
const delayedButtonImages = await saveGeneratedImageButtonArtifacts({
|
|
367
|
+
Browser: params.Browser,
|
|
368
|
+
Client: params.Client,
|
|
369
|
+
Page: params.Page,
|
|
370
|
+
Runtime: params.Runtime,
|
|
371
|
+
logger: params.logger,
|
|
372
|
+
minTurnIndex: params.minTurnIndex,
|
|
373
|
+
targetPath,
|
|
374
|
+
});
|
|
375
|
+
if (delayedButtonImages.length > 0) {
|
|
376
|
+
return formatButtonImageArtifacts(delayedButtonImages, latestAnswerText);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
266
379
|
}
|
|
267
380
|
const imageCount = generatedImages.length;
|
|
268
381
|
if (explicitTargetPath && imageCount === 0) {
|
|
@@ -288,6 +401,20 @@ export async function collectGeneratedImageArtifacts(params) {
|
|
|
288
401
|
logger: params.logger,
|
|
289
402
|
});
|
|
290
403
|
if (!saved.saved) {
|
|
404
|
+
if (explicitTargetPath) {
|
|
405
|
+
const buttonImages = await saveGeneratedImageButtonArtifacts({
|
|
406
|
+
Browser: params.Browser,
|
|
407
|
+
Client: params.Client,
|
|
408
|
+
Page: params.Page,
|
|
409
|
+
Runtime: params.Runtime,
|
|
410
|
+
logger: params.logger,
|
|
411
|
+
minTurnIndex: params.minTurnIndex,
|
|
412
|
+
targetPath: path.resolve(explicitTargetPath),
|
|
413
|
+
});
|
|
414
|
+
if (buttonImages.length > 0) {
|
|
415
|
+
return formatButtonImageArtifacts(buttonImages, latestAnswerText);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
291
418
|
const detail = saved.errors.length > 0 ? `\n${saved.errors.join("\n")}` : "";
|
|
292
419
|
if (explicitTargetPath) {
|
|
293
420
|
throw new Error(`No images generated. Response text:\n${latestAnswerText || "(empty response)"}${detail}`);
|
|
@@ -340,6 +340,10 @@ function createSessionBoundChromeClient(browser, sessionId) {
|
|
|
340
340
|
};
|
|
341
341
|
return {
|
|
342
342
|
...browser,
|
|
343
|
+
// Raw `send` here is the browser-level send (not session-bound), so callers
|
|
344
|
+
// that issue Target.* via `send` must pass this page session id explicitly to
|
|
345
|
+
// stay scoped to this tab (e.g. Deep Research OOPIF auto-attach).
|
|
346
|
+
oraclePageSessionId: sessionId,
|
|
343
347
|
Network: bindDomain("Network"),
|
|
344
348
|
Page: bindDomain("Page"),
|
|
345
349
|
Runtime: bindDomain("Runtime"),
|
|
@@ -55,6 +55,7 @@ export const DEFAULT_BROWSER_CONFIG = {
|
|
|
55
55
|
manualLoginCookieSync: false,
|
|
56
56
|
researchMode: "off",
|
|
57
57
|
archiveConversations: "auto",
|
|
58
|
+
resumeConversationUrl: null,
|
|
58
59
|
};
|
|
59
60
|
export function resolveBrowserConfig(config) {
|
|
60
61
|
const debugPortEnv = parseDebugPort(process.env.ORACLE_BROWSER_PORT ?? process.env.ORACLE_BROWSER_DEBUG_PORT);
|
|
@@ -112,6 +113,7 @@ export function resolveBrowserConfig(config) {
|
|
|
112
113
|
thinkingTime: config?.thinkingTime,
|
|
113
114
|
researchMode,
|
|
114
115
|
archiveConversations,
|
|
116
|
+
resumeConversationUrl: config?.resumeConversationUrl ?? DEFAULT_BROWSER_CONFIG.resumeConversationUrl,
|
|
115
117
|
manualLogin,
|
|
116
118
|
manualLoginProfileDir: manualLogin ? resolvedProfileDir : null,
|
|
117
119
|
manualLoginCookieSync: config?.manualLoginCookieSync ?? DEFAULT_BROWSER_CONFIG.manualLoginCookieSync,
|