@steipete/oracle 0.17.3 → 0.19.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 +1 -1
- package/dist/bin/oracle-cli.js +75 -64
- package/dist/bin/oracle-mcp.js +0 -0
- package/dist/src/browser/actions/archiveConversation.js +31 -7
- package/dist/src/browser/actions/assistantResponse.js +43 -1
- package/dist/src/browser/actions/attachmentContext.js +239 -0
- package/dist/src/browser/actions/attachmentDataTransfer.js +19 -1
- package/dist/src/browser/actions/attachmentEvidence.js +78 -0
- package/dist/src/browser/actions/attachmentProgress.js +41 -0
- package/dist/src/browser/actions/attachmentPrompt.js +130 -0
- package/dist/src/browser/actions/attachments.js +306 -355
- package/dist/src/browser/actions/deepResearch.js +2 -1
- package/dist/src/browser/actions/modelSelection.js +13 -3
- package/dist/src/browser/actions/navigation.js +1 -1
- package/dist/src/browser/actions/promptComposer.js +361 -186
- package/dist/src/browser/actions/remoteFileTransfer.js +4 -2
- package/dist/src/browser/actions/thinkingStatus.js +2 -5
- package/dist/src/browser/actions/thinkingTime.js +267 -15
- package/dist/src/browser/attachRunning.js +46 -2
- package/dist/src/browser/attachmentValidation.js +53 -0
- package/dist/src/browser/cancellation.js +105 -0
- package/dist/src/browser/chatgptImages.js +2 -0
- package/dist/src/browser/chatgptThrottle.js +104 -0
- package/dist/src/browser/chromeLifecycle.js +267 -43
- package/dist/src/browser/config.js +15 -3
- package/dist/src/browser/detect.js +1 -1
- package/dist/src/browser/index.js +447 -263
- package/dist/src/browser/modelDisplay.js +9 -0
- package/dist/src/browser/pageActions.js +2 -2
- package/dist/src/browser/policies.js +6 -2
- package/dist/src/browser/profileState.js +114 -9
- package/dist/src/browser/projectSourcesRunner.js +88 -17
- package/dist/src/browser/prompt.js +272 -113
- package/dist/src/browser/providers/chatgptDomProvider.js +2 -0
- package/dist/src/browser/reattach.js +14 -4
- package/dist/src/browser/sessionRunner.js +71 -11
- package/dist/src/browser/tabLeaseRegistry.js +414 -38
- package/dist/src/browser/utils.js +20 -2
- package/dist/src/cli/browserConfig.js +40 -15
- package/dist/src/cli/browserDefaults.js +13 -0
- package/dist/src/cli/browserTabs.js +12 -29
- package/dist/src/cli/detachedSession.js +69 -0
- package/dist/src/cli/dryRun.js +58 -47
- package/dist/src/cli/harvestIntegrity.js +140 -0
- package/dist/src/cli/projectSources.js +5 -3
- package/dist/src/cli/serveBrowserConfig.js +24 -0
- package/dist/src/cli/sessionDisplay.js +9 -2
- package/dist/src/cli/sessionRunner.js +91 -7
- package/dist/src/config.js +1 -0
- package/dist/src/gemini-web/client.js +67 -21
- package/dist/src/gemini-web/executor.js +4 -0
- package/dist/src/gemini-web/http.js +15 -0
- package/dist/src/gemini-web/models.js +15 -11
- package/dist/src/mcp/server.js +11 -14
- package/dist/src/mcp/tools/chatgptImage.js +4 -4
- package/dist/src/mcp/tools/consult.js +97 -26
- package/dist/src/mcp/tools/projectSources.js +4 -6
- package/dist/src/mcp/tools/sessionResources.js +1 -1
- package/dist/src/mcp/tools/sessions.js +2 -2
- package/dist/src/mcp/tools/wait.js +182 -0
- package/dist/src/mcp/types.js +7 -0
- package/dist/src/oracle/errors.js +11 -0
- package/dist/src/oracle/providerRoutePlan.js +3 -2
- package/dist/src/remote/client.js +70 -6
- package/dist/src/remote/health.js +6 -2
- package/dist/src/remote/runSlots.js +83 -0
- package/dist/src/remote/server.js +351 -72
- package/dist/src/sessionManager.js +36 -1
- 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 +25 -23
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/bin/oracle.js +0 -683
- package/dist/docs-site/.nojekyll +0 -0
- package/dist/docs-site/CNAME +0 -1
- package/dist/docs-site/RELEASING.html +0 -410
- package/dist/docs-site/agents.html +0 -374
- package/dist/docs-site/anthropic.html +0 -368
- package/dist/docs-site/bridge.html +0 -400
- package/dist/docs-site/browser-mode.html +0 -588
- package/dist/docs-site/chromium-forks.html +0 -347
- package/dist/docs-site/cli-reference.html +0 -346
- package/dist/docs-site/configuration.html +0 -436
- package/dist/docs-site/favicon.svg +0 -14
- package/dist/docs-site/followup.html +0 -368
- package/dist/docs-site/gemini.html +0 -379
- package/dist/docs-site/grok.html +0 -325
- package/dist/docs-site/index.html +0 -360
- package/dist/docs-site/install.html +0 -335
- package/dist/docs-site/linux.html +0 -321
- package/dist/docs-site/llms.txt +0 -43
- package/dist/docs-site/manual-tests.html +0 -595
- package/dist/docs-site/mcp.html +0 -368
- package/dist/docs-site/multimodel.html +0 -364
- package/dist/docs-site/mythical-pro-agents.html +0 -360
- package/dist/docs-site/notifier.html +0 -338
- package/dist/docs-site/openai-endpoints.html +0 -387
- package/dist/docs-site/openrouter.html +0 -344
- package/dist/docs-site/quickstart.html +0 -369
- package/dist/docs-site/refactor/ux.html +0 -532
- package/dist/docs-site/sessions.html +0 -388
- package/dist/docs-site/social-card.png +0 -0
- package/dist/docs-site/social-card.svg +0 -79
- package/dist/docs-site/spec.html +0 -363
- package/dist/docs-site/testing.html +0 -320
- package/dist/docs-site/tui-debug.html +0 -326
- package/dist/docs-site/windows-work.html +0 -323
- package/dist/docs-site/windows.html +0 -320
- 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/types.js +0 -1
- 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/chromeCookies.js +0 -312
- package/dist/src/browser/keytarShim.js +0 -56
- package/dist/src/browser/profileSync.js +0 -141
- package/dist/src/browser/windowsCookies.js +0 -219
|
@@ -5,6 +5,7 @@ import { normalizeThinkingTimeLevel } from "../oracle/thinkingTime.js";
|
|
|
5
5
|
import { CHATGPT_URL, DEFAULT_MODEL_STRATEGY, DEFAULT_MODEL_TARGET } from "../browser/constants.js";
|
|
6
6
|
import { normalizeChatgptUrl } from "../browser/utils.js";
|
|
7
7
|
import { parseDuration } from "../duration.js";
|
|
8
|
+
import { resolveBrowserApprovalWait } from "../browser/config.js";
|
|
8
9
|
import { normalizeBrowserModelStrategy } from "../browser/modelStrategy.js";
|
|
9
10
|
import { getOracleHomeDir } from "../oracleHome.js";
|
|
10
11
|
const DEFAULT_BROWSER_TIMEOUT_MS = 1_200_000;
|
|
@@ -13,6 +14,12 @@ const DEFAULT_BROWSER_ATTACHMENT_TIMEOUT_MS = 45_000;
|
|
|
13
14
|
const DEFAULT_BROWSER_RECHECK_TIMEOUT_MS = 120_000;
|
|
14
15
|
const DEFAULT_BROWSER_AUTO_REATTACH_TIMEOUT_MS = 120_000;
|
|
15
16
|
const DEFAULT_CHROME_PROFILE = "Default";
|
|
17
|
+
const CURRENT_CHATGPT_PRO_ALIASES = new Set([
|
|
18
|
+
"gpt-5-pro",
|
|
19
|
+
"gpt-5.1-pro",
|
|
20
|
+
"gpt-5.2-pro",
|
|
21
|
+
"gpt-5.4-pro",
|
|
22
|
+
]);
|
|
16
23
|
// Ordered array: most specific models first to ensure correct selection.
|
|
17
24
|
// The browser label is passed to the model picker which fuzzy-matches against ChatGPT's UI.
|
|
18
25
|
const BROWSER_MODEL_LABELS = [
|
|
@@ -52,11 +59,8 @@ export function normalizeChatGptModelForBrowser(model) {
|
|
|
52
59
|
return normalized;
|
|
53
60
|
}
|
|
54
61
|
// Pro variants: resolve to the latest Pro model in ChatGPT.
|
|
55
|
-
if (normalized
|
|
56
|
-
|
|
57
|
-
normalized === "gpt-5.2-pro" ||
|
|
58
|
-
normalized === "gpt-5.4-pro") {
|
|
59
|
-
return "gpt-5.5-pro";
|
|
62
|
+
if (isCurrentChatGptProAlias(normalized)) {
|
|
63
|
+
return "gpt-5.6-sol";
|
|
60
64
|
}
|
|
61
65
|
// Explicit model variants: keep as-is (they have their own browser labels)
|
|
62
66
|
if (normalized === "gpt-5.2-thinking" || normalized === "gpt-5.2-instant") {
|
|
@@ -68,6 +72,18 @@ export function normalizeChatGptModelForBrowser(model) {
|
|
|
68
72
|
}
|
|
69
73
|
return model;
|
|
70
74
|
}
|
|
75
|
+
export function isCurrentChatGptProAlias(model) {
|
|
76
|
+
return CURRENT_CHATGPT_PRO_ALIASES.has(model?.trim().toLowerCase() ?? "");
|
|
77
|
+
}
|
|
78
|
+
export function resolveDefaultBrowserThinkingTime({ model, requestedModel, modelStrategy, }) {
|
|
79
|
+
const strategy = normalizeBrowserModelStrategy(modelStrategy) ?? DEFAULT_MODEL_STRATEGY;
|
|
80
|
+
if (strategy !== "select")
|
|
81
|
+
return undefined;
|
|
82
|
+
const normalizedModel = normalizeChatGptModelForBrowser(model);
|
|
83
|
+
return isCurrentChatGptProAlias(requestedModel ?? model) || normalizedModel === "gpt-5.5-pro"
|
|
84
|
+
? "pro"
|
|
85
|
+
: undefined;
|
|
86
|
+
}
|
|
71
87
|
export async function buildBrowserConfig(options) {
|
|
72
88
|
if (options.copyProfile && options.browserKeepBrowser) {
|
|
73
89
|
throw new Error("--copy-profile cannot be combined with --browser-keep-browser: the copied profile is a throwaway that is deleted after the run, so it must not be retained.");
|
|
@@ -85,11 +101,14 @@ export async function buildBrowserConfig(options) {
|
|
|
85
101
|
const normalizedOverride = desiredModelOverride?.toLowerCase() ?? "";
|
|
86
102
|
const baseModel = options.model.toLowerCase();
|
|
87
103
|
const isChatGptModel = baseModel.startsWith("gpt-") && !baseModel.includes("codex");
|
|
88
|
-
const normalizedBrowserModel = normalizeChatGptModelForBrowser(options.model);
|
|
89
104
|
const shouldUseOverride = !isChatGptModel && normalizedOverride.length > 0 && normalizedOverride !== baseModel;
|
|
90
105
|
const modelStrategy = normalizeBrowserModelStrategy(options.browserModelStrategy) ?? DEFAULT_MODEL_STRATEGY;
|
|
91
106
|
const thinkingTime = normalizeThinkingTimeLevel(options.browserThinkingTime) ??
|
|
92
|
-
(
|
|
107
|
+
resolveDefaultBrowserThinkingTime({
|
|
108
|
+
model: options.model,
|
|
109
|
+
requestedModel: options.browserRequestedModel,
|
|
110
|
+
modelStrategy,
|
|
111
|
+
});
|
|
93
112
|
assertBrowserModelAvailable(options.model, modelStrategy);
|
|
94
113
|
const cookieNames = parseCookieNames(options.browserCookieNames ?? process.env.ORACLE_BROWSER_COOKIE_NAMES);
|
|
95
114
|
let inline = await resolveInlineCookies({
|
|
@@ -99,7 +118,9 @@ export async function buildBrowserConfig(options) {
|
|
|
99
118
|
envFile: process.env.ORACLE_BROWSER_COOKIES_FILE,
|
|
100
119
|
cwd: process.cwd(),
|
|
101
120
|
});
|
|
102
|
-
|
|
121
|
+
const chromeCookieSyncRequested = options.browserNoCookieSync !== true &&
|
|
122
|
+
(options.browserCookieSync === true || options.browserManualLoginCookieSync === true);
|
|
123
|
+
if (inline?.source?.startsWith("home:") && chromeCookieSyncRequested) {
|
|
103
124
|
inline = undefined;
|
|
104
125
|
}
|
|
105
126
|
let remoteChrome;
|
|
@@ -133,6 +154,7 @@ export async function buildBrowserConfig(options) {
|
|
|
133
154
|
inputTimeoutMs: options.browserInputTimeout
|
|
134
155
|
? parseBrowserDuration(options.browserInputTimeout, "--browser-input-timeout", DEFAULT_BROWSER_INPUT_TIMEOUT_MS)
|
|
135
156
|
: undefined,
|
|
157
|
+
approvalWaitMs: resolveBrowserApprovalWait(options.browserApprovalWait),
|
|
136
158
|
attachmentTimeoutMs: options.browserAttachmentTimeout
|
|
137
159
|
? parseBrowserDuration(options.browserAttachmentTimeout, "--browser-attachment-timeout", DEFAULT_BROWSER_ATTACHMENT_TIMEOUT_MS)
|
|
138
160
|
: undefined,
|
|
@@ -161,11 +183,13 @@ export async function buildBrowserConfig(options) {
|
|
|
161
183
|
cookieSyncWaitMs: options.browserCookieWait
|
|
162
184
|
? parseBrowserDuration(options.browserCookieWait, "--browser-cookie-wait", 0)
|
|
163
185
|
: undefined,
|
|
164
|
-
cookieSync:
|
|
165
|
-
?
|
|
166
|
-
: options.
|
|
167
|
-
?
|
|
168
|
-
:
|
|
186
|
+
cookieSync: inline?.cookies?.length
|
|
187
|
+
? true
|
|
188
|
+
: options.browserNoCookieSync
|
|
189
|
+
? false
|
|
190
|
+
: options.browserCookieSync === true || options.browserManualLoginCookieSync === true
|
|
191
|
+
? true
|
|
192
|
+
: undefined,
|
|
169
193
|
cookieNames,
|
|
170
194
|
inlineCookies: inline?.cookies,
|
|
171
195
|
inlineCookiesSource: inline?.source ?? null,
|
|
@@ -173,7 +197,7 @@ export async function buildBrowserConfig(options) {
|
|
|
173
197
|
keepBrowser: options.browserKeepBrowser ? true : undefined,
|
|
174
198
|
manualLogin: options.browserManualLogin === undefined ? undefined : options.browserManualLogin,
|
|
175
199
|
manualLoginProfileDir: options.browserManualLoginProfileDir ?? undefined,
|
|
176
|
-
manualLoginCookieSync: options.browserManualLoginCookieSync,
|
|
200
|
+
manualLoginCookieSync: inline?.cookies?.length ? true : options.browserManualLoginCookieSync,
|
|
177
201
|
copyProfileSource: options.copyProfile ?? undefined,
|
|
178
202
|
hideWindow: options.browserHideWindow ? true : undefined,
|
|
179
203
|
desiredModel,
|
|
@@ -206,6 +230,7 @@ function validateAttachRunningOptions(options, { attachRunning, hasInlineCookies
|
|
|
206
230
|
const conflicts = [
|
|
207
231
|
options.browserChromeProfile ? "--browser-chrome-profile" : null,
|
|
208
232
|
options.browserCookiePath ? "--browser-cookie-path" : null,
|
|
233
|
+
options.browserCookieSync ? "--browser-cookie-sync" : null,
|
|
209
234
|
options.browserNoCookieSync ? "--browser-no-cookie-sync" : null,
|
|
210
235
|
options.browserHeadless ? "--browser-headless" : null,
|
|
211
236
|
options.browserHideWindow ? "--browser-hide-window" : null,
|
|
@@ -268,7 +293,7 @@ export function resolveBrowserModelLabel(input, model) {
|
|
|
268
293
|
}
|
|
269
294
|
return trimmed;
|
|
270
295
|
}
|
|
271
|
-
function parseRemoteChromeTarget(raw) {
|
|
296
|
+
export function parseRemoteChromeTarget(raw) {
|
|
272
297
|
const target = raw.trim();
|
|
273
298
|
if (!target) {
|
|
274
299
|
throw new Error("Invalid remote-chrome value: expected host:port but received an empty string.");
|
|
@@ -12,6 +12,13 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
12
12
|
const attachRunningRequested = options.browserAttachRunning === true ||
|
|
13
13
|
(isUnset("browserAttachRunning") && browser.attachRunning === true);
|
|
14
14
|
const currentModelRequestedByCli = options.browserModelStrategy === "current" && getSource("browserModelStrategy") === "cli";
|
|
15
|
+
if (!options.copyProfile &&
|
|
16
|
+
isUnset("remoteChrome") &&
|
|
17
|
+
options.remoteChrome === undefined &&
|
|
18
|
+
browser.remoteChrome) {
|
|
19
|
+
const { host, port } = browser.remoteChrome;
|
|
20
|
+
options.remoteChrome = `${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${port}`;
|
|
21
|
+
}
|
|
15
22
|
const configuredChatgptUrl = browser.chatgptUrl ?? browser.url;
|
|
16
23
|
const cliChatgptSet = options.chatgptUrl !== undefined || options.browserUrl !== undefined;
|
|
17
24
|
if (isUnset("chatgptUrl") && !cliChatgptSet && configuredChatgptUrl !== undefined) {
|
|
@@ -45,6 +52,9 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
45
52
|
if (isUnset("browserInputTimeout") && typeof browser.inputTimeoutMs === "number") {
|
|
46
53
|
options.browserInputTimeout = String(browser.inputTimeoutMs);
|
|
47
54
|
}
|
|
55
|
+
if (isUnset("browserApprovalWait") && typeof browser.approvalWaitMs === "number") {
|
|
56
|
+
options.browserApprovalWait = String(browser.approvalWaitMs);
|
|
57
|
+
}
|
|
48
58
|
if (isUnset("browserAttachmentTimeout") && typeof browser.attachmentTimeoutMs === "number") {
|
|
49
59
|
options.browserAttachmentTimeout = String(browser.attachmentTimeoutMs);
|
|
50
60
|
}
|
|
@@ -76,6 +86,9 @@ export function applyBrowserDefaultsFromConfig(options, config, getSource) {
|
|
|
76
86
|
if (isUnset("browserCookieWait") && typeof browser.cookieSyncWaitMs === "number") {
|
|
77
87
|
options.browserCookieWait = String(browser.cookieSyncWaitMs);
|
|
78
88
|
}
|
|
89
|
+
if (!attachRunningRequested && isUnset("browserCookieSync") && browser.cookieSync !== undefined) {
|
|
90
|
+
options.browserCookieSync = browser.cookieSync;
|
|
91
|
+
}
|
|
79
92
|
if (!attachRunningRequested && isUnset("browserHeadless") && browser.headless !== undefined) {
|
|
80
93
|
options.browserHeadless = browser.headless;
|
|
81
94
|
}
|
|
@@ -2,9 +2,10 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { sessionStore } from "../sessionStore.js";
|
|
5
|
-
import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT,
|
|
5
|
+
import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT, formatBrowserTabState, harvestChatGptTab, sessionMatchesTab, } from "../browser/liveTabs.js";
|
|
6
6
|
import { isRecoveredConversationHarvestReady, recoverConversationTab, } from "../browser/recoverConversation.js";
|
|
7
7
|
import { resolveOutputPath } from "./writeOutputPath.js";
|
|
8
|
+
import { persistBrowserHarvest } from "./harvestIntegrity.js";
|
|
8
9
|
const LIVE_POLL_MS = 2000;
|
|
9
10
|
const DEFAULT_STALL_THRESHOLD_MS = 60_000;
|
|
10
11
|
function isRecoverableMissingTabError(message) {
|
|
@@ -85,35 +86,17 @@ function resolveSessionTabRef(meta) {
|
|
|
85
86
|
export function resolveSessionTabRefForTest(meta) {
|
|
86
87
|
return resolveSessionTabRef(meta);
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
const hash = createHash("sha1")
|
|
90
|
-
.update(harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "")
|
|
91
|
-
.digest("hex");
|
|
92
|
-
const browser = {
|
|
93
|
-
...(meta.browser ?? {}),
|
|
94
|
-
harvest: {
|
|
95
|
-
targetId: harvested.targetId,
|
|
96
|
-
url: harvested.url,
|
|
97
|
-
conversationId: harvested.conversationId ?? extractConversationIdFromUrl(harvested.url),
|
|
98
|
-
harvestedAt: new Date().toISOString(),
|
|
99
|
-
assistantHash: hash,
|
|
100
|
-
state: harvested.state,
|
|
101
|
-
stopExists: harvested.stopExists,
|
|
102
|
-
sendExists: harvested.sendExists,
|
|
103
|
-
assistantCount: harvested.assistantCount,
|
|
104
|
-
currentModelLabel: harvested.currentModelLabel,
|
|
105
|
-
lastAssistantSnippet: harvested.lastAssistantSnippet,
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
await sessionStore.updateSession(sessionId, { browser });
|
|
109
|
-
}
|
|
110
|
-
function printHarvestSummary(sessionId, harvested) {
|
|
89
|
+
function printHarvestSummary(sessionId, harvested, integrity) {
|
|
111
90
|
console.log(chalk.bold(`Session: ${sessionId}`));
|
|
112
91
|
console.log(`Target: ${harvested.targetId}`);
|
|
113
92
|
console.log(`State: ${formatBrowserTabState(harvested)}`);
|
|
114
93
|
console.log(`Model: ${harvested.currentModelLabel || "(unknown)"}`);
|
|
115
94
|
console.log(`URL: ${harvested.url}`);
|
|
116
95
|
console.log(`Assistant turns: ${harvested.assistantCount}`);
|
|
96
|
+
console.log(`Capture identity: ${integrity.status}${integrity.explicitTarget ? " (explicit target)" : ""}`);
|
|
97
|
+
if (integrity.status === "mismatch") {
|
|
98
|
+
console.log(chalk.yellow("Explicit harvest target differs from the saved capture; original artifacts are unchanged."));
|
|
99
|
+
}
|
|
117
100
|
console.log(`Signals: stop=${harvested.stopExists ? "yes" : "no"} send=${harvested.sendExists ? "yes" : "no"}`);
|
|
118
101
|
if (harvested.lastUserSnippet) {
|
|
119
102
|
console.log(`Last user: ${harvested.lastUserSnippet}`);
|
|
@@ -207,8 +190,8 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
|
|
|
207
190
|
stallWindowMs: options.stallWindowMs,
|
|
208
191
|
});
|
|
209
192
|
}
|
|
210
|
-
await
|
|
211
|
-
printHarvestSummary(sessionId, harvested);
|
|
193
|
+
const integrity = await persistBrowserHarvest(sessionId, harvested, Boolean(options.browserTabRef));
|
|
194
|
+
printHarvestSummary(sessionId, harvested, integrity);
|
|
212
195
|
const output = harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "";
|
|
213
196
|
if (options.writeOutputPath) {
|
|
214
197
|
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
@@ -287,8 +270,8 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
|
287
270
|
const statusLine = `[${new Date().toISOString()}] state=${harvested.state} stop=${harvested.stopExists ? "yes" : "no"} ` +
|
|
288
271
|
`send=${harvested.sendExists ? "yes" : "no"} model=${harvested.currentModelLabel || "(unknown)"} ` +
|
|
289
272
|
`snippet=${snippet(harvested.lastAssistantSnippet || fullText, 160)}`;
|
|
273
|
+
await persistBrowserHarvest(sessionId, harvested, Boolean(options.browserTabRef));
|
|
290
274
|
console.log(statusLine);
|
|
291
|
-
await persistHarvest(sessionId, meta, harvested);
|
|
292
275
|
}
|
|
293
276
|
const derivedState = harvested.stopExists
|
|
294
277
|
? Date.now() - unchangedSince >= stallThresholdMs
|
|
@@ -304,8 +287,8 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
|
|
|
304
287
|
...harvested,
|
|
305
288
|
state: derivedState,
|
|
306
289
|
};
|
|
307
|
-
await
|
|
308
|
-
printHarvestSummary(sessionId, finalHarvest);
|
|
290
|
+
const integrity = await persistBrowserHarvest(sessionId, finalHarvest, Boolean(options.browserTabRef));
|
|
291
|
+
printHarvestSummary(sessionId, finalHarvest, integrity);
|
|
309
292
|
const output = finalHarvest.lastAssistantMarkdown ?? finalHarvest.lastAssistantText ?? "";
|
|
310
293
|
if (options.writeOutputPath) {
|
|
311
294
|
await maybeWriteHarvestOutput(options.writeOutputPath, meta.cwd ?? process.cwd(), output);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
export function resolveOracleCliEntrypoint(moduleUrl = import.meta.url) {
|
|
4
|
+
const extension = fileURLToPath(moduleUrl).endsWith(".ts") ? "ts" : "js";
|
|
5
|
+
return fileURLToPath(new URL(`../../bin/oracle-cli.${extension}`, moduleUrl));
|
|
6
|
+
}
|
|
7
|
+
export function buildDetachedSessionSpawnSpec({ sessionId, cliEntrypoint = resolveOracleCliEntrypoint(), env = process.env, nodeExecutable = process.execPath, }) {
|
|
8
|
+
return {
|
|
9
|
+
command: nodeExecutable,
|
|
10
|
+
args: ["--", cliEntrypoint, "--exec-session", sessionId],
|
|
11
|
+
options: {
|
|
12
|
+
detached: true,
|
|
13
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
14
|
+
env: {
|
|
15
|
+
...env,
|
|
16
|
+
ORACLE_DETACHED_START_GATE: "1",
|
|
17
|
+
},
|
|
18
|
+
windowsHide: true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function launchDetachedSession({ sessionId, cliEntrypoint, env, nodeExecutable, prepare, spawnProcess = spawn, }) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
let child;
|
|
25
|
+
try {
|
|
26
|
+
const spec = buildDetachedSessionSpawnSpec({
|
|
27
|
+
sessionId,
|
|
28
|
+
cliEntrypoint,
|
|
29
|
+
env,
|
|
30
|
+
nodeExecutable,
|
|
31
|
+
});
|
|
32
|
+
child = spawnProcess(spec.command, spec.args, spec.options);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
reject(error);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
child.once("error", reject);
|
|
39
|
+
child.once("spawn", async () => {
|
|
40
|
+
if (child.pid === undefined) {
|
|
41
|
+
child.kill();
|
|
42
|
+
reject(new Error("Detached session worker started without a process ID."));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
await prepare(child.pid);
|
|
47
|
+
if (!child.stdin) {
|
|
48
|
+
throw new Error("Detached session worker started without a writable start gate.");
|
|
49
|
+
}
|
|
50
|
+
const startGate = child.stdin;
|
|
51
|
+
await new Promise((resolveGate, rejectGate) => {
|
|
52
|
+
startGate.once("error", rejectGate);
|
|
53
|
+
startGate.end("ready\n", (error) => {
|
|
54
|
+
if (error)
|
|
55
|
+
rejectGate(error);
|
|
56
|
+
else
|
|
57
|
+
resolveGate();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
child.unref();
|
|
61
|
+
resolve(child.pid);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
child.kill();
|
|
65
|
+
reject(error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
package/dist/src/cli/dryRun.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { MODEL_CONFIGS, TOKENIZER_OPTIONS, DEFAULT_SYSTEM_PROMPT, buildPrompt, readFiles, getFileTokenStats, printFileTokenStats, } from "../oracle.js";
|
|
3
3
|
import { isKnownModel } from "../oracle/modelResolver.js";
|
|
4
|
-
import { assembleBrowserPrompt } from "../browser/prompt.js";
|
|
4
|
+
import { assembleBrowserPrompt, cleanupGeneratedBrowserBundles, } from "../browser/prompt.js";
|
|
5
5
|
import { buildTokenEstimateSuffix, formatAttachmentLabel } from "../browser/promptSummary.js";
|
|
6
6
|
import { buildCookiePlan } from "../browser/policies.js";
|
|
7
7
|
import { describeBrowserControlPlan, formatBrowserControlPlan } from "../browser/controlPlan.js";
|
|
@@ -45,19 +45,24 @@ async function runBrowserDryRun({ runOptions, cwd, version, log, browserConfig,
|
|
|
45
45
|
validateBrowserFollowUps(runOptions, browserConfig);
|
|
46
46
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
47
47
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
try {
|
|
49
|
+
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
50
|
+
const displayModel = formatBrowserModelTarget({
|
|
51
|
+
model: runOptions.model,
|
|
52
|
+
desiredModel: browserConfig?.desiredModel,
|
|
53
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
54
|
+
});
|
|
55
|
+
const headerLine = `[dry-run] Oracle (${version}) would launch browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
56
|
+
log(chalk.cyan(headerLine));
|
|
57
|
+
logBrowserControlPlan(browserConfig, log, "dry-run");
|
|
58
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "dry-run");
|
|
59
|
+
logBrowserCookieStrategy(browserConfig, log, "dry-run");
|
|
60
|
+
logBrowserArchivePolicy(browserConfig, log, "dry-run");
|
|
61
|
+
logBrowserFileSummary(artifacts, log, "dry-run");
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await cleanupGeneratedBrowserBundles(artifacts);
|
|
65
|
+
}
|
|
61
66
|
}
|
|
62
67
|
function logBrowserControlPlan(browserConfig, log, label) {
|
|
63
68
|
const plan = describeBrowserControlPlan(browserConfig);
|
|
@@ -100,41 +105,47 @@ export async function runBrowserPreview({ runOptions, cwd, version, previewMode,
|
|
|
100
105
|
validateBrowserFollowUps(runOptions, browserConfig);
|
|
101
106
|
const assemblePromptImpl = deps.assembleBrowserPromptImpl ?? assembleBrowserPrompt;
|
|
102
107
|
const artifacts = await assemblePromptImpl(runOptions, { cwd });
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
desiredModel: browserConfig?.desiredModel,
|
|
107
|
-
modelStrategy: browserConfig?.modelStrategy,
|
|
108
|
-
});
|
|
109
|
-
const headerLine = `[preview] Oracle (${version}) browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
110
|
-
log(chalk.cyan(headerLine));
|
|
111
|
-
logBrowserControlPlan(browserConfig, log, "preview");
|
|
112
|
-
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
|
113
|
-
logBrowserFileSummary(artifacts, log, "preview");
|
|
114
|
-
if (previewMode === "json" || previewMode === "full") {
|
|
115
|
-
const attachmentSummary = artifacts.attachments.map((attachment) => ({
|
|
116
|
-
path: attachment.path,
|
|
117
|
-
displayPath: attachment.displayPath,
|
|
118
|
-
sizeBytes: attachment.sizeBytes,
|
|
119
|
-
}));
|
|
120
|
-
const previewPayload = {
|
|
108
|
+
try {
|
|
109
|
+
const suffix = buildTokenEstimateSuffix(artifacts);
|
|
110
|
+
const displayModel = formatBrowserModelTarget({
|
|
121
111
|
model: runOptions.model,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
log
|
|
131
|
-
|
|
132
|
-
|
|
112
|
+
desiredModel: browserConfig?.desiredModel,
|
|
113
|
+
modelStrategy: browserConfig?.modelStrategy,
|
|
114
|
+
});
|
|
115
|
+
const headerLine = `[preview] Oracle (${version}) browser mode (${displayModel}) with ~${artifacts.estimatedInputTokens.toLocaleString()} tokens${suffix}.`;
|
|
116
|
+
log(chalk.cyan(headerLine));
|
|
117
|
+
logBrowserControlPlan(browserConfig, log, "preview");
|
|
118
|
+
logBrowserFollowUpSummary(runOptions.browserFollowUps, log, "preview");
|
|
119
|
+
logBrowserCookieStrategy(browserConfig, log, "preview");
|
|
120
|
+
logBrowserFileSummary(artifacts, log, "preview");
|
|
121
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
122
|
+
const attachmentSummary = artifacts.attachments.map((attachment) => ({
|
|
123
|
+
path: attachment.path,
|
|
124
|
+
displayPath: attachment.displayPath,
|
|
125
|
+
sizeBytes: attachment.sizeBytes,
|
|
126
|
+
}));
|
|
127
|
+
const previewPayload = {
|
|
128
|
+
model: runOptions.model,
|
|
129
|
+
engine: "browser",
|
|
130
|
+
composerText: artifacts.composerText,
|
|
131
|
+
attachments: attachmentSummary,
|
|
132
|
+
inlineFileCount: artifacts.inlineFileCount,
|
|
133
|
+
bundled: artifacts.bundled,
|
|
134
|
+
tokenEstimate: artifacts.estimatedInputTokens,
|
|
135
|
+
browserFollowUps: runOptions.browserFollowUps ?? [],
|
|
136
|
+
};
|
|
137
|
+
log("");
|
|
138
|
+
log(chalk.bold("Preview JSON"));
|
|
139
|
+
log(JSON.stringify(previewPayload, null, 2));
|
|
140
|
+
}
|
|
141
|
+
if (previewMode === "full") {
|
|
142
|
+
log("");
|
|
143
|
+
log(chalk.bold("Composer Text"));
|
|
144
|
+
log(artifacts.composerText || chalk.dim("(empty prompt)"));
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
log(chalk.bold("Composer Text"));
|
|
137
|
-
log(artifacts.composerText || chalk.dim("(empty prompt)"));
|
|
147
|
+
finally {
|
|
148
|
+
await cleanupGeneratedBrowserBundles(artifacts);
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
151
|
function logBrowserFollowUpSummary(followUps, log, label) {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { sessionStore } from "../sessionStore.js";
|
|
6
|
+
import { extractConversationIdFromUrl } from "../browser/liveTabs.js";
|
|
7
|
+
import { BrowserAutomationError } from "../oracle/errors.js";
|
|
8
|
+
const INTEGRITY_WARNING = "browser-harvest-integrity";
|
|
9
|
+
const INTEGRITY_MESSAGE = "Harvest or saved conversation identities conflict. Original artifacts were preserved; inspect browser.harvest.integrity before using the answer.";
|
|
10
|
+
async function readTranscriptConversation(filename) {
|
|
11
|
+
let file;
|
|
12
|
+
try {
|
|
13
|
+
const stat = await fs.lstat(filename);
|
|
14
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
15
|
+
return undefined;
|
|
16
|
+
file = await fs.open(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
17
|
+
if (!(await file.stat()).isFile())
|
|
18
|
+
return undefined;
|
|
19
|
+
const buffer = Buffer.alloc(2_048);
|
|
20
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, 0);
|
|
21
|
+
const match = buffer
|
|
22
|
+
.subarray(0, bytesRead)
|
|
23
|
+
.toString("utf8")
|
|
24
|
+
.match(/^# Oracle Browser Transcript\r?\n\r?\nConversation: ([^\r\n]+)\r?\n/);
|
|
25
|
+
return match ? extractConversationIdFromUrl(match[1]) : undefined;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
await file?.close();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function persistBrowserHarvest(sessionId, harvested, explicitTarget = false) {
|
|
35
|
+
// Harvesting can take time; compare against the latest saved capture, not the initial read.
|
|
36
|
+
const meta = await sessionStore.readSession(sessionId);
|
|
37
|
+
if (!meta)
|
|
38
|
+
throw new Error(`No session found with ID ${sessionId}.`);
|
|
39
|
+
const captured = [];
|
|
40
|
+
const unverifiedSources = [];
|
|
41
|
+
const add = (source, id) => {
|
|
42
|
+
if (id)
|
|
43
|
+
captured.push({ source, conversationId: id });
|
|
44
|
+
};
|
|
45
|
+
const addUrl = (source, url, expectsConversation = true) => {
|
|
46
|
+
if (!url)
|
|
47
|
+
return;
|
|
48
|
+
const id = extractConversationIdFromUrl(url);
|
|
49
|
+
add(source, id);
|
|
50
|
+
if (!id && expectsConversation)
|
|
51
|
+
unverifiedSources.push(source);
|
|
52
|
+
};
|
|
53
|
+
add("runtime", meta.browser?.runtime?.conversationId);
|
|
54
|
+
addUrl("runtime-url", meta.browser?.runtime?.tabUrl);
|
|
55
|
+
addUrl("archive", meta.browser?.archive?.conversationUrl);
|
|
56
|
+
for (const [index, artifact] of (meta.artifacts ?? []).entries()) {
|
|
57
|
+
addUrl(`artifact:${index}`, artifact.sourceUrl, artifact.kind === "transcript" || artifact.kind === "deep-research-report");
|
|
58
|
+
}
|
|
59
|
+
const artifactDir = path.join((await sessionStore.getPaths(sessionId)).dir, "artifacts");
|
|
60
|
+
const directoryStat = await fs.lstat(artifactDir).catch(() => null);
|
|
61
|
+
if (directoryStat?.isDirectory() && !directoryStat.isSymbolicLink()) {
|
|
62
|
+
const transcripts = new Map([[path.join(artifactDir, "transcript.md"), false]]);
|
|
63
|
+
for (const artifact of meta.artifacts ?? []) {
|
|
64
|
+
if (artifact.kind !== "transcript")
|
|
65
|
+
continue;
|
|
66
|
+
const candidate = path.isAbsolute(artifact.path)
|
|
67
|
+
? artifact.path
|
|
68
|
+
: path.resolve(artifactDir, "..", artifact.path);
|
|
69
|
+
// Stored paths must remain direct children of this session's artifact directory.
|
|
70
|
+
if (path.dirname(candidate) === artifactDir)
|
|
71
|
+
transcripts.set(candidate, true);
|
|
72
|
+
else
|
|
73
|
+
unverifiedSources.push("external-transcript-path");
|
|
74
|
+
}
|
|
75
|
+
for (const [filename, expected] of transcripts) {
|
|
76
|
+
const source = `transcript:${path.basename(filename)}`;
|
|
77
|
+
const id = await readTranscriptConversation(filename);
|
|
78
|
+
add(source, id);
|
|
79
|
+
if (!id && (expected || (await fs.lstat(filename).catch(() => null)))) {
|
|
80
|
+
unverifiedSources.push(source);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (directoryStat || meta.artifacts?.some((artifact) => artifact.kind === "transcript")) {
|
|
85
|
+
unverifiedSources.push("artifact-directory");
|
|
86
|
+
}
|
|
87
|
+
const observedConversationId = harvested.conversationId ?? extractConversationIdFromUrl(harvested.url);
|
|
88
|
+
const capturedIds = new Set(captured.map((entry) => entry.conversationId));
|
|
89
|
+
const previousIntegrity = meta.browser?.harvest?.integrity;
|
|
90
|
+
const mismatch = capturedIds.size > 1 ||
|
|
91
|
+
(observedConversationId
|
|
92
|
+
? captured.some((entry) => entry.conversationId !== observedConversationId)
|
|
93
|
+
: previousIntegrity?.status === "mismatch");
|
|
94
|
+
const integrity = {
|
|
95
|
+
status: mismatch
|
|
96
|
+
? "mismatch"
|
|
97
|
+
: observedConversationId && captured.length && unverifiedSources.length === 0
|
|
98
|
+
? "matched"
|
|
99
|
+
: "unverified",
|
|
100
|
+
observedConversationId,
|
|
101
|
+
captured,
|
|
102
|
+
unverifiedSources,
|
|
103
|
+
explicitTarget,
|
|
104
|
+
previousHarvestConversationId: meta.browser?.harvest?.conversationId ?? previousIntegrity?.previousHarvestConversationId,
|
|
105
|
+
};
|
|
106
|
+
const warnings = (meta.browser?.warnings ?? []).filter((warning) => warning.code !== INTEGRITY_WARNING);
|
|
107
|
+
if (mismatch) {
|
|
108
|
+
warnings.push({ code: INTEGRITY_WARNING, severity: "warning", message: INTEGRITY_MESSAGE });
|
|
109
|
+
}
|
|
110
|
+
await sessionStore.updateSession(sessionId, {
|
|
111
|
+
browser: {
|
|
112
|
+
...meta.browser,
|
|
113
|
+
warnings,
|
|
114
|
+
harvest: {
|
|
115
|
+
targetId: harvested.targetId,
|
|
116
|
+
url: harvested.url,
|
|
117
|
+
conversationId: observedConversationId,
|
|
118
|
+
harvestedAt: new Date().toISOString(),
|
|
119
|
+
assistantHash: createHash("sha1")
|
|
120
|
+
.update(harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "")
|
|
121
|
+
.digest("hex"),
|
|
122
|
+
state: harvested.state,
|
|
123
|
+
stopExists: harvested.stopExists,
|
|
124
|
+
sendExists: harvested.sendExists,
|
|
125
|
+
assistantCount: harvested.assistantCount,
|
|
126
|
+
currentModelLabel: harvested.currentModelLabel,
|
|
127
|
+
lastAssistantSnippet: harvested.lastAssistantSnippet,
|
|
128
|
+
integrity,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
if (mismatch && !explicitTarget) {
|
|
133
|
+
throw new BrowserAutomationError(INTEGRITY_MESSAGE, {
|
|
134
|
+
stage: "harvest-integrity",
|
|
135
|
+
code: "conversation-identity-mismatch",
|
|
136
|
+
integrity,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return integrity;
|
|
140
|
+
}
|
|
@@ -74,9 +74,11 @@ export async function buildProjectSourcesBrowserConfig({ options, projectUrl, co
|
|
|
74
74
|
const manualLoginCookieSync = flagConfig.manualLoginCookieSync ?? configuredBrowser.manualLoginCookieSync;
|
|
75
75
|
const cookieSync = flagConfig.cookieSync === false
|
|
76
76
|
? false
|
|
77
|
-
:
|
|
78
|
-
?
|
|
79
|
-
:
|
|
77
|
+
: flagConfig.cookieSync === true
|
|
78
|
+
? true
|
|
79
|
+
: manualLogin
|
|
80
|
+
? manualLoginCookieSync === true
|
|
81
|
+
: configuredBrowser.cookieSync === true;
|
|
80
82
|
return {
|
|
81
83
|
...configuredBrowser,
|
|
82
84
|
...flagConfig,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolveBrowserApprovalWait } from "../browser/config.js";
|
|
2
|
+
import { parseRemoteChromeTarget } from "./browserConfig.js";
|
|
3
|
+
import { applyBrowserDefaultsFromConfig } from "./browserDefaults.js";
|
|
4
|
+
export function buildServeBrowserConfig(options, config) {
|
|
5
|
+
const flags = {
|
|
6
|
+
browserAttachRunning: options.browserAttachRunning,
|
|
7
|
+
remoteChrome: options.remoteChrome,
|
|
8
|
+
browserApprovalWait: options.browserApprovalWait ??
|
|
9
|
+
(process.env.ORACLE_BROWSER_APPROVAL_WAIT?.trim() || undefined),
|
|
10
|
+
};
|
|
11
|
+
const browser = config.browser;
|
|
12
|
+
applyBrowserDefaultsFromConfig(flags, {
|
|
13
|
+
browser: {
|
|
14
|
+
attachRunning: browser?.attachRunning,
|
|
15
|
+
remoteChrome: browser?.remoteChrome,
|
|
16
|
+
approvalWaitMs: browser?.approvalWaitMs,
|
|
17
|
+
},
|
|
18
|
+
}, (key) => (flags[key] === undefined ? undefined : "cli"));
|
|
19
|
+
return {
|
|
20
|
+
attachRunning: flags.browserAttachRunning ?? false,
|
|
21
|
+
remoteChrome: flags.remoteChrome ? parseRemoteChromeTarget(flags.remoteChrome) : null,
|
|
22
|
+
approvalWaitMs: resolveBrowserApprovalWait(flags.browserApprovalWait),
|
|
23
|
+
};
|
|
24
|
+
}
|