ask-pro 0.1.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/.codex-plugin/plugin.json +30 -0
- package/LICENSE +21 -0
- package/README.md +231 -0
- package/assets/ask-pro_logo.png +0 -0
- package/dist/bin/ask-pro-cli.js +507 -0
- package/dist/scripts/run-cli.js +27 -0
- package/dist/src/ask-pro/atomicWrite.js +26 -0
- package/dist/src/ask-pro/browserRunner.js +796 -0
- package/dist/src/ask-pro/responseZip.js +349 -0
- package/dist/src/ask-pro/session.js +662 -0
- package/dist/src/ask-pro/sessionControllerLease.js +64 -0
- package/dist/src/ask-pro/toon.js +26 -0
- package/dist/src/ask-pro/zip.js +85 -0
- package/dist/src/browser/actions/assistantResponse.js +1245 -0
- package/dist/src/browser/actions/attachmentDataTransfer.js +140 -0
- package/dist/src/browser/actions/attachments.js +1720 -0
- package/dist/src/browser/actions/composerSendReadiness.js +369 -0
- package/dist/src/browser/actions/domEvents.js +31 -0
- package/dist/src/browser/actions/inputGuard.js +52 -0
- package/dist/src/browser/actions/modelPickerDom.js +68 -0
- package/dist/src/browser/actions/modelSelection.js +576 -0
- package/dist/src/browser/actions/navigation.js +510 -0
- package/dist/src/browser/actions/promptComposer.js +824 -0
- package/dist/src/browser/actions/remoteFileTransfer.js +37 -0
- package/dist/src/browser/actions/thinkingStatus.js +408 -0
- package/dist/src/browser/actions/thinkingTime.js +635 -0
- package/dist/src/browser/actions/windowState.js +47 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptModelCatalog.js +321 -0
- package/dist/src/browser/chromeLifecycle.js +807 -0
- package/dist/src/browser/config.js +110 -0
- package/dist/src/browser/constants.js +85 -0
- package/dist/src/browser/cookies.js +191 -0
- package/dist/src/browser/detect.js +337 -0
- package/dist/src/browser/domDebug.js +72 -0
- package/dist/src/browser/errors.js +20 -0
- package/dist/src/browser/format.js +16 -0
- package/dist/src/browser/index.js +2631 -0
- package/dist/src/browser/language.js +97 -0
- package/dist/src/browser/liveTabs.js +434 -0
- package/dist/src/browser/modelStrategy.js +13 -0
- package/dist/src/browser/pageActions.js +5 -0
- package/dist/src/browser/profilePaths.js +282 -0
- package/dist/src/browser/profileState.js +413 -0
- package/dist/src/browser/providerDomFlow.js +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +50 -0
- package/dist/src/browser/reattach.js +534 -0
- package/dist/src/browser/reattachHelpers.js +387 -0
- package/dist/src/browser/utils.js +122 -0
- package/dist/src/browserMode.js +1 -0
- package/dist/src/version.js +39 -0
- package/package.json +114 -0
- package/scripts/refresh-local-plugin.mjs +179 -0
- package/scripts/refresh-local-plugin.ps1 +93 -0
- package/skills/ask-pro/SKILL.md +181 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_CHATGPT_BROWSER_MODEL_LABEL } from "../browser/chatgptModelCatalog.js";
|
|
4
|
+
import { AssistantStoppedError, BrowserAutomationError } from "../browser/errors.js";
|
|
5
|
+
import { askProAgentIdForLegacyBrowserProfileDir, askProAgentIdForManagedBrowserProfileDir, askProBrowserProfileDirForAgentId, ensureAskProBrowserProfileDir, isLegacyAskProManagedBrowserProfileDir, isAskProManagedBrowserProfileDir, resolveAskProAgentId, } from "../browser/profilePaths.js";
|
|
6
|
+
import { runBrowserMode } from "../browserMode.js";
|
|
7
|
+
import { closeTab } from "../browser/chromeLifecycle.js";
|
|
8
|
+
import { resumeBrowserSession } from "../browser/reattach.js";
|
|
9
|
+
import { appendAskProLog, getAskProSessionPaths, readAskProPrompt, readAskProStatus, updateAskProResumeCommand, updateAskProStatus, writeAskProAnswer, writeAskProBrowserMetadata, } from "./session.js";
|
|
10
|
+
import { harvestLatestAssistantZip, writeResponseZipManifest } from "./responseZip.js";
|
|
11
|
+
import { withSessionControllerLease } from "./sessionControllerLease.js";
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 180 * 60 * 1000;
|
|
13
|
+
const MANUAL_LOGIN_WAIT_MS = 10 * 60 * 1000;
|
|
14
|
+
const ASK_PRO_CHATGPT_URL = "https://chatgpt.com/";
|
|
15
|
+
const ASK_PRO_TEMPORARY_CHATGPT_URL = "https://chatgpt.com/?temporary-chat=true";
|
|
16
|
+
const ASK_PRO_ACCEPT_LANGUAGE = "en-US,en";
|
|
17
|
+
export async function runAskProBrowserSession(options) {
|
|
18
|
+
const { dir } = getAskProSessionPaths(options.cwd, options.sessionId);
|
|
19
|
+
return withSessionControllerLease(dir, () => runAskProBrowserSessionWithLease(options));
|
|
20
|
+
}
|
|
21
|
+
async function runAskProBrowserSessionWithLease({ cwd, sessionId, temporary, chatgptUrl: chatgptUrlOverride, browserProfileDir, agentId: agentIdOverride, allowStartMinimized = true, verbose, }) {
|
|
22
|
+
const paths = getAskProSessionPaths(cwd, sessionId);
|
|
23
|
+
const prompt = await readAskProPrompt({ cwd, sessionId });
|
|
24
|
+
const { status: sessionStatus } = await readAskProStatus({ cwd, sessionId });
|
|
25
|
+
const artifactsRequested = sessionStatus.artifacts === true;
|
|
26
|
+
const agentId = agentIdOverride !== undefined ? agentIdOverride : resolveAskProAgentId();
|
|
27
|
+
const browserProfile = browserProfileDir ?? (await ensureAskProBrowserProfileDir(agentId));
|
|
28
|
+
const metadata = await readBrowserMetadata(paths.browser).catch(() => null);
|
|
29
|
+
const chatgptUrl = chatgptUrlOverride ??
|
|
30
|
+
(temporary === true
|
|
31
|
+
? ASK_PRO_TEMPORARY_CHATGPT_URL
|
|
32
|
+
: temporary === false
|
|
33
|
+
? ASK_PRO_CHATGPT_URL
|
|
34
|
+
: (metadata?.url ?? ASK_PRO_TEMPORARY_CHATGPT_URL));
|
|
35
|
+
await fs.mkdir(browserProfile, { recursive: true });
|
|
36
|
+
const startMinimized = allowStartMinimized;
|
|
37
|
+
let capturedFinalStatus = null;
|
|
38
|
+
await writeAskProBrowserMetadata({
|
|
39
|
+
cwd,
|
|
40
|
+
sessionId,
|
|
41
|
+
metadata: {
|
|
42
|
+
schemaVersion: 1,
|
|
43
|
+
status: "pending",
|
|
44
|
+
agentId,
|
|
45
|
+
profileDir: browserProfile,
|
|
46
|
+
temporary,
|
|
47
|
+
url: chatgptUrl,
|
|
48
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
49
|
+
chromeMode: "launching",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
await updateAskProStatus({ cwd, sessionId, status: "BROWSER_STARTING" });
|
|
53
|
+
const logger = buildAskProBrowserLogger(cwd, sessionId, verbose);
|
|
54
|
+
try {
|
|
55
|
+
await updateAskProStatus({ cwd, sessionId, status: "WAITING" });
|
|
56
|
+
const result = await runBrowserMode({
|
|
57
|
+
prompt,
|
|
58
|
+
attachments: [
|
|
59
|
+
{
|
|
60
|
+
path: paths.contextZip,
|
|
61
|
+
displayPath: "CONTEXT.zip",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
config: {
|
|
65
|
+
url: chatgptUrl,
|
|
66
|
+
manualLogin: true,
|
|
67
|
+
attachRunning: false,
|
|
68
|
+
manualLoginProfileDir: browserProfile,
|
|
69
|
+
manualLoginWaitMs: MANUAL_LOGIN_WAIT_MS,
|
|
70
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
71
|
+
inputTimeoutMs: 90_000,
|
|
72
|
+
assistantRecheckDelayMs: 30_000,
|
|
73
|
+
assistantRecheckTimeoutMs: 180_000,
|
|
74
|
+
desiredModel: DEFAULT_CHATGPT_BROWSER_MODEL_LABEL,
|
|
75
|
+
modelStrategy: "select",
|
|
76
|
+
thinkingTime: "pro",
|
|
77
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
78
|
+
startMinimized,
|
|
79
|
+
keepBrowser: true,
|
|
80
|
+
allowCookieErrors: true,
|
|
81
|
+
},
|
|
82
|
+
log: logger,
|
|
83
|
+
heartbeatIntervalMs: 30_000,
|
|
84
|
+
verbose,
|
|
85
|
+
shouldPreserveWindowStateOnError: (error) => shouldFallbackFromDefaultTemporaryChat(error, {
|
|
86
|
+
chatgptUrl,
|
|
87
|
+
chatgptUrlOverride,
|
|
88
|
+
temporary,
|
|
89
|
+
}),
|
|
90
|
+
runtimeHintCb: async (runtime) => {
|
|
91
|
+
await writeAskProBrowserMetadata({
|
|
92
|
+
cwd,
|
|
93
|
+
sessionId,
|
|
94
|
+
metadata: {
|
|
95
|
+
schemaVersion: 1,
|
|
96
|
+
status: "running",
|
|
97
|
+
agentId,
|
|
98
|
+
profileDir: browserProfile,
|
|
99
|
+
temporary,
|
|
100
|
+
url: chatgptUrl,
|
|
101
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
102
|
+
chromeMode: "launched",
|
|
103
|
+
runtime,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
afterAnswerCb: async ({ Runtime, Page, Input, answer }) => {
|
|
108
|
+
await writePostAnswerResponseZipManifest({
|
|
109
|
+
sessionDir: paths.dir,
|
|
110
|
+
artifactsRequested,
|
|
111
|
+
runtime: Runtime,
|
|
112
|
+
page: Page,
|
|
113
|
+
input: Input,
|
|
114
|
+
logger,
|
|
115
|
+
});
|
|
116
|
+
capturedFinalStatus = await persistCapturedAnswer({
|
|
117
|
+
cwd,
|
|
118
|
+
sessionId,
|
|
119
|
+
sessionDir: paths.dir,
|
|
120
|
+
answer: answer.markdown || answer.text,
|
|
121
|
+
artifactsRequested,
|
|
122
|
+
});
|
|
123
|
+
if (capturedFinalStatus.status === "INCOMPLETE_ANSWER") {
|
|
124
|
+
logger("Keeping browser open for incomplete-answer debugging.");
|
|
125
|
+
return { keepBrowserOpen: true };
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const answer = result.answerMarkdown || result.answerText;
|
|
131
|
+
const finalStatus = capturedFinalStatus ??
|
|
132
|
+
(await persistCapturedAnswer({
|
|
133
|
+
cwd,
|
|
134
|
+
sessionId,
|
|
135
|
+
sessionDir: paths.dir,
|
|
136
|
+
answer,
|
|
137
|
+
artifactsRequested,
|
|
138
|
+
}));
|
|
139
|
+
await writeAskProBrowserMetadata({
|
|
140
|
+
cwd,
|
|
141
|
+
sessionId,
|
|
142
|
+
metadata: {
|
|
143
|
+
schemaVersion: 1,
|
|
144
|
+
status: finalStatus.browserStatus,
|
|
145
|
+
agentId,
|
|
146
|
+
profileDir: browserProfile,
|
|
147
|
+
temporary,
|
|
148
|
+
url: chatgptUrl,
|
|
149
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
150
|
+
chromeMode: "launched",
|
|
151
|
+
runtime: browserResultToRuntime(result),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (capturedFinalStatus) {
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
if (shouldFallbackFromDefaultTemporaryChat(error, {
|
|
161
|
+
chatgptUrl,
|
|
162
|
+
chatgptUrlOverride,
|
|
163
|
+
temporary,
|
|
164
|
+
})) {
|
|
165
|
+
await appendAskProLog(cwd, sessionId, "Temporary Chat did not expose the Pro model; retrying in normal ChatGPT.");
|
|
166
|
+
await closeFallbackTemporaryTab(cwd, sessionId, logger);
|
|
167
|
+
await updateAskProStatus({
|
|
168
|
+
cwd,
|
|
169
|
+
sessionId,
|
|
170
|
+
status: "WAITING",
|
|
171
|
+
reason: "temporary_unavailable_fell_back_to_normal_chat",
|
|
172
|
+
temporary: false,
|
|
173
|
+
});
|
|
174
|
+
await updateAskProResumeCommand({
|
|
175
|
+
cwd,
|
|
176
|
+
sessionId,
|
|
177
|
+
resumeCommand: withNoTemporaryResumeCommand(sessionStatus.resumeCommand),
|
|
178
|
+
harvestCommand: sessionStatus.harvestCommand,
|
|
179
|
+
temporary: false,
|
|
180
|
+
});
|
|
181
|
+
const currentMetadata = await readBrowserMetadata(paths.browser).catch(() => ({}));
|
|
182
|
+
const { runtime: _closedTemporaryRuntime, ...metadataWithoutRuntime } = currentMetadata;
|
|
183
|
+
await writeAskProBrowserMetadata({
|
|
184
|
+
cwd,
|
|
185
|
+
sessionId,
|
|
186
|
+
metadata: {
|
|
187
|
+
...metadataWithoutRuntime,
|
|
188
|
+
schemaVersion: 1,
|
|
189
|
+
status: "running",
|
|
190
|
+
agentId,
|
|
191
|
+
profileDir: browserProfile,
|
|
192
|
+
temporary: false,
|
|
193
|
+
url: ASK_PRO_CHATGPT_URL,
|
|
194
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
195
|
+
chromeMode: "launched",
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
return runAskProBrowserSessionWithLease({
|
|
199
|
+
cwd,
|
|
200
|
+
sessionId,
|
|
201
|
+
temporary: false,
|
|
202
|
+
chatgptUrl: ASK_PRO_CHATGPT_URL,
|
|
203
|
+
browserProfileDir: browserProfile,
|
|
204
|
+
agentId,
|
|
205
|
+
allowStartMinimized,
|
|
206
|
+
verbose,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (isAuthGateError(error)) {
|
|
210
|
+
const currentMetadata = await readBrowserMetadata(paths.browser).catch(() => ({}));
|
|
211
|
+
await writeAskProBrowserMetadata({
|
|
212
|
+
cwd,
|
|
213
|
+
sessionId,
|
|
214
|
+
metadata: {
|
|
215
|
+
...currentMetadata,
|
|
216
|
+
schemaVersion: 1,
|
|
217
|
+
status: "needs_user_auth",
|
|
218
|
+
agentId,
|
|
219
|
+
profileDir: browserProfile,
|
|
220
|
+
temporary,
|
|
221
|
+
url: chatgptUrl,
|
|
222
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
223
|
+
chromeMode: "launched",
|
|
224
|
+
reason: classifyBrowserError(error),
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
await updateAskProStatus({
|
|
228
|
+
cwd,
|
|
229
|
+
sessionId,
|
|
230
|
+
status: "NEEDS_USER_AUTH",
|
|
231
|
+
reason: classifyBrowserError(error),
|
|
232
|
+
});
|
|
233
|
+
throw new AskProNeedsAuthError(sessionId, browserProfile, classifyBrowserError(error));
|
|
234
|
+
}
|
|
235
|
+
if (error instanceof AssistantStoppedError) {
|
|
236
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "incomplete_answer", "stopped_without_answer");
|
|
237
|
+
await updateAskProStatus({
|
|
238
|
+
cwd,
|
|
239
|
+
sessionId,
|
|
240
|
+
status: "INCOMPLETE_ANSWER",
|
|
241
|
+
reason: "stopped_without_answer",
|
|
242
|
+
});
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
if (isAssistantTimeoutError(error)) {
|
|
246
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "wait_timed_out", "assistant_timeout");
|
|
247
|
+
await updateAskProStatus({
|
|
248
|
+
cwd,
|
|
249
|
+
sessionId,
|
|
250
|
+
status: "WAIT_TIMED_OUT",
|
|
251
|
+
reason: "assistant_timeout",
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
256
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "failed", reason);
|
|
257
|
+
await updateAskProStatus({
|
|
258
|
+
cwd,
|
|
259
|
+
sessionId,
|
|
260
|
+
status: "FAILED",
|
|
261
|
+
reason,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export async function resumeAskProBrowserSession(options) {
|
|
268
|
+
const { dir } = getAskProSessionPaths(options.cwd, options.sessionId);
|
|
269
|
+
return withSessionControllerLease(dir, () => resumeAskProBrowserSessionWithLease(options));
|
|
270
|
+
}
|
|
271
|
+
async function resumeAskProBrowserSessionWithLease({ cwd, sessionId, temporary, verbose, }) {
|
|
272
|
+
const paths = getAskProSessionPaths(cwd, sessionId);
|
|
273
|
+
const prompt = await readAskProPrompt({ cwd, sessionId });
|
|
274
|
+
const { status: sessionStatus } = await readAskProStatus({ cwd, sessionId });
|
|
275
|
+
const artifactsRequested = sessionStatus.artifacts === true;
|
|
276
|
+
const logger = buildAskProBrowserLogger(cwd, sessionId, verbose);
|
|
277
|
+
const metadata = await readBrowserMetadata(paths.browser);
|
|
278
|
+
const effectiveTemporary = temporary ?? metadata.temporary;
|
|
279
|
+
const chatgptUrl = effectiveTemporary === true
|
|
280
|
+
? ASK_PRO_TEMPORARY_CHATGPT_URL
|
|
281
|
+
: effectiveTemporary === false
|
|
282
|
+
? ASK_PRO_CHATGPT_URL
|
|
283
|
+
: (metadata.url ?? ASK_PRO_TEMPORARY_CHATGPT_URL);
|
|
284
|
+
const fallbackProfile = await resolveResumeBrowserProfile(metadata);
|
|
285
|
+
let capturedFinalStatus = null;
|
|
286
|
+
if (metadata.runtime) {
|
|
287
|
+
metadata.runtime = normalizeResumeRuntime(metadata.runtime, metadata.profileDir, fallbackProfile);
|
|
288
|
+
}
|
|
289
|
+
const attachRunning = !metadata.agentId;
|
|
290
|
+
if (effectiveTemporary === false && isTemporaryAskProUrl(metadata.url ?? "")) {
|
|
291
|
+
await appendAskProLog(cwd, sessionId, "Retrying Temporary Chat session in normal ChatGPT; opening managed browser submission.");
|
|
292
|
+
await runAskProBrowserSessionWithLease({
|
|
293
|
+
cwd,
|
|
294
|
+
sessionId,
|
|
295
|
+
temporary: false,
|
|
296
|
+
chatgptUrl: ASK_PRO_CHATGPT_URL,
|
|
297
|
+
browserProfileDir: fallbackProfile,
|
|
298
|
+
agentId: metadata.agentId ?? null,
|
|
299
|
+
allowStartMinimized: false,
|
|
300
|
+
verbose,
|
|
301
|
+
});
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (!metadata.runtime) {
|
|
305
|
+
if (metadata.status !== "needs_user_auth") {
|
|
306
|
+
throw new Error(`session ${sessionId} has no saved browser runtime metadata`);
|
|
307
|
+
}
|
|
308
|
+
await appendAskProLog(cwd, sessionId, "No saved browser runtime metadata; reopening managed browser submission.");
|
|
309
|
+
const storedUrlIsDefaultTemporary = chatgptUrl === ASK_PRO_TEMPORARY_CHATGPT_URL;
|
|
310
|
+
const shouldPreserveUrl = effectiveTemporary !== undefined ||
|
|
311
|
+
(metadata.url !== undefined && !storedUrlIsDefaultTemporary);
|
|
312
|
+
await runAskProBrowserSessionWithLease({
|
|
313
|
+
cwd,
|
|
314
|
+
sessionId,
|
|
315
|
+
temporary: effectiveTemporary,
|
|
316
|
+
chatgptUrl: shouldPreserveUrl ? chatgptUrl : undefined,
|
|
317
|
+
browserProfileDir: fallbackProfile,
|
|
318
|
+
agentId: metadata.agentId ?? null,
|
|
319
|
+
allowStartMinimized: false,
|
|
320
|
+
verbose,
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
await updateAskProStatus({ cwd, sessionId, status: "WAITING" });
|
|
325
|
+
await writeAskProBrowserMetadata({
|
|
326
|
+
cwd,
|
|
327
|
+
sessionId,
|
|
328
|
+
metadata: {
|
|
329
|
+
...metadata,
|
|
330
|
+
schemaVersion: 1,
|
|
331
|
+
status: "running",
|
|
332
|
+
profileDir: fallbackProfile,
|
|
333
|
+
temporary: effectiveTemporary,
|
|
334
|
+
url: chatgptUrl,
|
|
335
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
336
|
+
chromeMode: "reattaching",
|
|
337
|
+
reason: undefined,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
try {
|
|
341
|
+
const result = await resumeBrowserSession(metadata.runtime, {
|
|
342
|
+
manualLogin: true,
|
|
343
|
+
attachRunning,
|
|
344
|
+
manualLoginProfileDir: fallbackProfile,
|
|
345
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
346
|
+
inputTimeoutMs: 90_000,
|
|
347
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
348
|
+
url: chatgptUrl,
|
|
349
|
+
desiredModel: DEFAULT_CHATGPT_BROWSER_MODEL_LABEL,
|
|
350
|
+
thinkingTime: "pro",
|
|
351
|
+
startMinimized: false,
|
|
352
|
+
}, logger, {
|
|
353
|
+
promptPreview: prompt,
|
|
354
|
+
chromeModeCb: async (chromeMode) => {
|
|
355
|
+
const current = await readBrowserMetadata(paths.browser).catch(() => metadata);
|
|
356
|
+
await writeAskProBrowserMetadata({
|
|
357
|
+
cwd,
|
|
358
|
+
sessionId,
|
|
359
|
+
metadata: {
|
|
360
|
+
...current,
|
|
361
|
+
schemaVersion: 1,
|
|
362
|
+
status: "running",
|
|
363
|
+
profileDir: fallbackProfile,
|
|
364
|
+
temporary: effectiveTemporary,
|
|
365
|
+
url: chatgptUrl,
|
|
366
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
367
|
+
chromeMode,
|
|
368
|
+
reason: undefined,
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
},
|
|
372
|
+
afterAnswerCb: async ({ Runtime, Page, Input, answer }) => {
|
|
373
|
+
await writePostAnswerResponseZipManifest({
|
|
374
|
+
sessionDir: paths.dir,
|
|
375
|
+
artifactsRequested,
|
|
376
|
+
runtime: Runtime,
|
|
377
|
+
page: Page,
|
|
378
|
+
input: Input,
|
|
379
|
+
logger,
|
|
380
|
+
});
|
|
381
|
+
capturedFinalStatus = await persistCapturedAnswer({
|
|
382
|
+
cwd,
|
|
383
|
+
sessionId,
|
|
384
|
+
sessionDir: paths.dir,
|
|
385
|
+
answer: answer.markdown || answer.text,
|
|
386
|
+
artifactsRequested,
|
|
387
|
+
});
|
|
388
|
+
if (capturedFinalStatus.status === "INCOMPLETE_ANSWER") {
|
|
389
|
+
logger("Keeping browser open for incomplete-answer debugging.");
|
|
390
|
+
return { keepBrowserOpen: true };
|
|
391
|
+
}
|
|
392
|
+
return undefined;
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
const answer = result.answerMarkdown || result.answerText;
|
|
396
|
+
const finalStatus = capturedFinalStatus ??
|
|
397
|
+
(await persistCapturedAnswer({
|
|
398
|
+
cwd,
|
|
399
|
+
sessionId,
|
|
400
|
+
sessionDir: paths.dir,
|
|
401
|
+
answer,
|
|
402
|
+
artifactsRequested,
|
|
403
|
+
}));
|
|
404
|
+
await writeAskProBrowserMetadata({
|
|
405
|
+
cwd,
|
|
406
|
+
sessionId,
|
|
407
|
+
metadata: {
|
|
408
|
+
...metadata,
|
|
409
|
+
schemaVersion: 1,
|
|
410
|
+
status: finalStatus.browserStatus,
|
|
411
|
+
profileDir: fallbackProfile,
|
|
412
|
+
temporary: effectiveTemporary,
|
|
413
|
+
url: chatgptUrl,
|
|
414
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
415
|
+
chromeMode: result.chromeMode ?? "reused_devtools",
|
|
416
|
+
reason: undefined,
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
if (capturedFinalStatus) {
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
if (isAuthGateError(error)) {
|
|
425
|
+
const currentMetadata = await readBrowserMetadata(paths.browser).catch(() => metadata);
|
|
426
|
+
await writeAskProBrowserMetadata({
|
|
427
|
+
cwd,
|
|
428
|
+
sessionId,
|
|
429
|
+
metadata: {
|
|
430
|
+
...currentMetadata,
|
|
431
|
+
schemaVersion: 1,
|
|
432
|
+
status: "needs_user_auth",
|
|
433
|
+
profileDir: fallbackProfile,
|
|
434
|
+
temporary: effectiveTemporary,
|
|
435
|
+
url: chatgptUrl,
|
|
436
|
+
acceptLanguage: ASK_PRO_ACCEPT_LANGUAGE,
|
|
437
|
+
chromeMode: authFailureChromeMode(currentMetadata.chromeMode),
|
|
438
|
+
reason: classifyBrowserError(error),
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
await updateAskProStatus({
|
|
442
|
+
cwd,
|
|
443
|
+
sessionId,
|
|
444
|
+
status: "NEEDS_USER_AUTH",
|
|
445
|
+
reason: classifyBrowserError(error),
|
|
446
|
+
});
|
|
447
|
+
throw new AskProNeedsAuthError(sessionId, fallbackProfile, classifyBrowserError(error));
|
|
448
|
+
}
|
|
449
|
+
if (error instanceof AssistantStoppedError) {
|
|
450
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "incomplete_answer", "stopped_without_answer");
|
|
451
|
+
await updateAskProStatus({
|
|
452
|
+
cwd,
|
|
453
|
+
sessionId,
|
|
454
|
+
status: "INCOMPLETE_ANSWER",
|
|
455
|
+
reason: "stopped_without_answer",
|
|
456
|
+
});
|
|
457
|
+
throw error;
|
|
458
|
+
}
|
|
459
|
+
if (isAssistantTimeoutError(error)) {
|
|
460
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "wait_timed_out", "assistant_timeout");
|
|
461
|
+
await updateAskProStatus({
|
|
462
|
+
cwd,
|
|
463
|
+
sessionId,
|
|
464
|
+
status: "WAIT_TIMED_OUT",
|
|
465
|
+
reason: "assistant_timeout",
|
|
466
|
+
});
|
|
467
|
+
throw error;
|
|
468
|
+
}
|
|
469
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
470
|
+
await writeTerminalBrowserMetadata(cwd, sessionId, "failed", reason);
|
|
471
|
+
await updateAskProStatus({
|
|
472
|
+
cwd,
|
|
473
|
+
sessionId,
|
|
474
|
+
status: "FAILED",
|
|
475
|
+
reason,
|
|
476
|
+
});
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function isTemporaryAskProUrl(url) {
|
|
481
|
+
try {
|
|
482
|
+
const parsed = new URL(url);
|
|
483
|
+
const value = (parsed.searchParams.get("temporary-chat") ?? "").trim().toLowerCase();
|
|
484
|
+
return value === "true" || value === "1" || value === "yes";
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function withNoTemporaryResumeCommand(command) {
|
|
491
|
+
const withoutStrictTemporary = command.replace(/\s--temporary(?=\s|$)/g, "");
|
|
492
|
+
if (/\s--no-temporary(?=\s|$)/.test(withoutStrictTemporary)) {
|
|
493
|
+
return withoutStrictTemporary;
|
|
494
|
+
}
|
|
495
|
+
return withoutStrictTemporary.replace(/\s--resume(?=\s|$)/, " --no-temporary --resume");
|
|
496
|
+
}
|
|
497
|
+
async function closeFallbackTemporaryTab(cwd, sessionId, logger) {
|
|
498
|
+
const paths = getAskProSessionPaths(cwd, sessionId);
|
|
499
|
+
const metadata = await readBrowserMetadata(paths.browser).catch(() => null);
|
|
500
|
+
const runtime = metadata?.runtime;
|
|
501
|
+
if (!runtime?.chromePort || !runtime.chromeTargetId) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
await closeTab(runtime.chromePort, runtime.chromeTargetId, logger, runtime.chromeHost ?? "127.0.0.1").catch(() => undefined);
|
|
505
|
+
}
|
|
506
|
+
function shouldFallbackFromDefaultTemporaryChat(error, options) {
|
|
507
|
+
return (options.temporary === undefined &&
|
|
508
|
+
options.chatgptUrlOverride === undefined &&
|
|
509
|
+
isTemporaryAskProUrl(options.chatgptUrl) &&
|
|
510
|
+
isTemporaryProUnavailableError(error));
|
|
511
|
+
}
|
|
512
|
+
function isTemporaryProUnavailableError(error) {
|
|
513
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
514
|
+
return (message.includes(`unable to find model option matching "${DEFAULT_CHATGPT_BROWSER_MODEL_LABEL.toLowerCase()}"`) ||
|
|
515
|
+
message.includes("unable to locate the chatgpt model selector button") ||
|
|
516
|
+
message.includes("unable to select pro intelligence"));
|
|
517
|
+
}
|
|
518
|
+
async function ensureResponseZipManifest(sessionDir, artifactsRequested = true) {
|
|
519
|
+
try {
|
|
520
|
+
await fs.access(path.join(sessionDir, "PRO_OUTPUT_MANIFEST.json"));
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
await writeResponseZipManifest(sessionDir, artifactsRequested ? responseZipUnavailableManifest() : responseZipNotRequestedManifest());
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async function writePostAnswerResponseZipManifest({ sessionDir, artifactsRequested, runtime, page, input, logger, }) {
|
|
527
|
+
try {
|
|
528
|
+
if (artifactsRequested) {
|
|
529
|
+
const manifest = await harvestLatestAssistantZip({
|
|
530
|
+
runtime,
|
|
531
|
+
page,
|
|
532
|
+
input,
|
|
533
|
+
sessionDir,
|
|
534
|
+
});
|
|
535
|
+
await writeResponseZipManifest(sessionDir, manifest);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
await writeResponseZipManifest(sessionDir, responseZipNotRequestedManifest());
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
543
|
+
logger(`Response zip post-processing failed after answer capture: ${message}`);
|
|
544
|
+
await writeResponseZipManifest(sessionDir, responseZipErrorManifest(message)).catch(() => undefined);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function responseZipNotRequestedManifest() {
|
|
548
|
+
return {
|
|
549
|
+
schemaVersion: 1,
|
|
550
|
+
responseZip: {
|
|
551
|
+
status: "not_requested",
|
|
552
|
+
actualFileName: null,
|
|
553
|
+
downloadPath: null,
|
|
554
|
+
extractPath: null,
|
|
555
|
+
requiredFilesPresent: false,
|
|
556
|
+
notes: ["Response zip harvesting was not requested for this inline-answer session."],
|
|
557
|
+
},
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function responseZipErrorManifest(message) {
|
|
561
|
+
return {
|
|
562
|
+
schemaVersion: 1,
|
|
563
|
+
responseZip: {
|
|
564
|
+
status: "error",
|
|
565
|
+
actualFileName: null,
|
|
566
|
+
downloadPath: null,
|
|
567
|
+
extractPath: null,
|
|
568
|
+
requiredFilesPresent: false,
|
|
569
|
+
notes: [`Response zip post-processing failed after answer capture: ${message}`],
|
|
570
|
+
},
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function responseZipUnavailableManifest() {
|
|
574
|
+
return {
|
|
575
|
+
schemaVersion: 1,
|
|
576
|
+
responseZip: {
|
|
577
|
+
status: "unavailable",
|
|
578
|
+
actualFileName: null,
|
|
579
|
+
downloadPath: null,
|
|
580
|
+
extractPath: null,
|
|
581
|
+
requiredFilesPresent: false,
|
|
582
|
+
notes: ["Generated zip was unavailable; harvested markdown answer to ANSWER.md."],
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function classifyFinalAnswer(sessionDir, answer) {
|
|
587
|
+
if (!isSuspiciousPreambleAnswer(answer) || (await hasCompleteResponseZip(sessionDir))) {
|
|
588
|
+
return { status: "COMPLETED", browserStatus: "completed" };
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
status: "INCOMPLETE_ANSWER",
|
|
592
|
+
browserStatus: "incomplete_answer",
|
|
593
|
+
reason: "preamble_without_artifacts",
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
async function persistCapturedAnswer({ cwd, sessionId, sessionDir, answer, artifactsRequested, }) {
|
|
597
|
+
await writeAskProAnswer({ cwd, sessionId, answer });
|
|
598
|
+
await ensureResponseZipManifest(sessionDir, artifactsRequested);
|
|
599
|
+
const finalStatus = await classifyFinalAnswer(sessionDir, answer);
|
|
600
|
+
await updateAskProStatus({
|
|
601
|
+
cwd,
|
|
602
|
+
sessionId,
|
|
603
|
+
status: finalStatus.status,
|
|
604
|
+
reason: finalStatus.reason,
|
|
605
|
+
});
|
|
606
|
+
return finalStatus;
|
|
607
|
+
}
|
|
608
|
+
async function hasCompleteResponseZip(sessionDir) {
|
|
609
|
+
try {
|
|
610
|
+
const raw = await fs.readFile(path.join(sessionDir, "PRO_OUTPUT_MANIFEST.json"), "utf8");
|
|
611
|
+
const manifest = JSON.parse(raw);
|
|
612
|
+
return (manifest.responseZip?.status === "downloaded" &&
|
|
613
|
+
manifest.responseZip.requiredFilesPresent === true);
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
export function isSuspiciousPreambleAnswer(answer) {
|
|
620
|
+
const normalized = answer.trim().toLowerCase().replace(/\s+/g, " ");
|
|
621
|
+
if (!normalized || normalized.length > 1000)
|
|
622
|
+
return false;
|
|
623
|
+
if (hasSubstantiveAnswerMarker(normalized))
|
|
624
|
+
return false;
|
|
625
|
+
const futureIntent = /\b(sure,?\s+)?(i['’]?ll|i will|let me|i['’]?m going to|i am going to)\b/.test(normalized);
|
|
626
|
+
const deferredWork = /\b(inspect|review|analy[sz]e|read|look at|take a look|create|prepare|generate|build|get back to you)\b/.test(normalized);
|
|
627
|
+
return futureIntent && deferredWork;
|
|
628
|
+
}
|
|
629
|
+
function hasSubstantiveAnswerMarker(answer) {
|
|
630
|
+
return /\b(recommendation|recommend|answer|verdict|use|fix|root cause|because|risk|should|shouldn['’]?t|must|do not|don['’]?t)\b/.test(answer);
|
|
631
|
+
}
|
|
632
|
+
function authFailureChromeMode(chromeMode) {
|
|
633
|
+
return chromeMode === "reattaching" ? "reused_devtools" : chromeMode;
|
|
634
|
+
}
|
|
635
|
+
async function writeTerminalBrowserMetadata(cwd, sessionId, status, reason) {
|
|
636
|
+
const paths = getAskProSessionPaths(cwd, sessionId);
|
|
637
|
+
const current = await readBrowserMetadata(paths.browser).catch(() => null);
|
|
638
|
+
if (!current)
|
|
639
|
+
return;
|
|
640
|
+
const { chromeMode, ...rest } = current;
|
|
641
|
+
const terminalChromeMode = status !== "failed" && chromeMode === "reattaching"
|
|
642
|
+
? "reused_devtools"
|
|
643
|
+
: chromeMode === "launched" || chromeMode === "reused_devtools" || chromeMode === "relaunched"
|
|
644
|
+
? chromeMode
|
|
645
|
+
: undefined;
|
|
646
|
+
await writeAskProBrowserMetadata({
|
|
647
|
+
cwd,
|
|
648
|
+
sessionId,
|
|
649
|
+
metadata: {
|
|
650
|
+
...rest,
|
|
651
|
+
schemaVersion: 1,
|
|
652
|
+
status,
|
|
653
|
+
reason,
|
|
654
|
+
...(terminalChromeMode ? { chromeMode: terminalChromeMode } : {}),
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
async function resolveResumeBrowserProfile(metadata) {
|
|
659
|
+
const agentProfile = resolveStoredAgentProfile(metadata.agentId);
|
|
660
|
+
const profileDir = metadata.profileDir;
|
|
661
|
+
const profileAgentId = profileDir ? askProAgentIdForManagedBrowserProfileDir(profileDir) : null;
|
|
662
|
+
const legacyAgentId = profileDir ? askProAgentIdForLegacyBrowserProfileDir(profileDir) : null;
|
|
663
|
+
if (profileAgentId && profileAgentId !== metadata.agentId) {
|
|
664
|
+
throw new Error("Stored ask-pro agent profile does not match stored agent id.");
|
|
665
|
+
}
|
|
666
|
+
if (legacyAgentId && legacyAgentId !== metadata.agentId) {
|
|
667
|
+
throw new Error("Stored ask-pro agent profile does not match stored agent id.");
|
|
668
|
+
}
|
|
669
|
+
if (profileAgentId && agentProfile) {
|
|
670
|
+
return profileDir;
|
|
671
|
+
}
|
|
672
|
+
if (legacyAgentId && agentProfile) {
|
|
673
|
+
return ensureAskProBrowserProfileDir(metadata.agentId);
|
|
674
|
+
}
|
|
675
|
+
if (profileDir &&
|
|
676
|
+
!profileAgentId &&
|
|
677
|
+
!legacyAgentId &&
|
|
678
|
+
!agentProfile &&
|
|
679
|
+
isAskProManagedBrowserProfileDir(profileDir)) {
|
|
680
|
+
return ensureAskProBrowserProfileDir(null);
|
|
681
|
+
}
|
|
682
|
+
if (profileDir &&
|
|
683
|
+
!profileAgentId &&
|
|
684
|
+
!legacyAgentId &&
|
|
685
|
+
!agentProfile &&
|
|
686
|
+
isLegacyAskProManagedBrowserProfileDir(profileDir)) {
|
|
687
|
+
return ensureAskProBrowserProfileDir(null);
|
|
688
|
+
}
|
|
689
|
+
if (profileDir) {
|
|
690
|
+
throw new Error("Stored ask-pro profile path is invalid.");
|
|
691
|
+
}
|
|
692
|
+
return ensureAskProBrowserProfileDir(metadata.agentId);
|
|
693
|
+
}
|
|
694
|
+
function resolveStoredAgentProfile(agentId) {
|
|
695
|
+
if (!agentId)
|
|
696
|
+
return null;
|
|
697
|
+
return askProBrowserProfileDirForAgentId(agentId);
|
|
698
|
+
}
|
|
699
|
+
function normalizeResumeRuntime(runtime, storedProfile, resolvedProfile) {
|
|
700
|
+
const allowed = [storedProfile, resolvedProfile].filter((value) => Boolean(value));
|
|
701
|
+
for (const candidate of [runtime.chromeProfileRoot, runtime.userDataDir]) {
|
|
702
|
+
if (candidate && !allowed.some((value) => samePath(candidate, value))) {
|
|
703
|
+
throw new Error("Stored ask-pro browser runtime profile path is invalid.");
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
...runtime,
|
|
708
|
+
...(runtime.chromeProfileRoot ? { chromeProfileRoot: resolvedProfile } : {}),
|
|
709
|
+
...(runtime.userDataDir ? { userDataDir: resolvedProfile } : {}),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function samePath(left, right) {
|
|
713
|
+
const normalize = (value) => {
|
|
714
|
+
const resolved = path.resolve(value);
|
|
715
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
716
|
+
};
|
|
717
|
+
return normalize(left) === normalize(right);
|
|
718
|
+
}
|
|
719
|
+
export class AskProNeedsAuthError extends Error {
|
|
720
|
+
sessionId;
|
|
721
|
+
browserProfile;
|
|
722
|
+
reason;
|
|
723
|
+
constructor(sessionId, browserProfile, reason) {
|
|
724
|
+
super("ChatGPT authentication is required.");
|
|
725
|
+
this.sessionId = sessionId;
|
|
726
|
+
this.browserProfile = browserProfile;
|
|
727
|
+
this.reason = reason;
|
|
728
|
+
this.name = "AskProNeedsAuthError";
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function buildAskProBrowserLogger(cwd, sessionId, verbose) {
|
|
732
|
+
const logger = ((message) => {
|
|
733
|
+
if (typeof message !== "string")
|
|
734
|
+
return;
|
|
735
|
+
void appendAskProLog(cwd, sessionId, message);
|
|
736
|
+
const shouldPrint = verbose || /\b(thinking|waiting|fallback|retry|stopped|url|reattach)\b/i.test(message);
|
|
737
|
+
if (shouldPrint) {
|
|
738
|
+
console.error(message);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
logger.verbose = Boolean(verbose);
|
|
742
|
+
logger.sessionLog = (message) => {
|
|
743
|
+
void appendAskProLog(cwd, sessionId, message);
|
|
744
|
+
};
|
|
745
|
+
return logger;
|
|
746
|
+
}
|
|
747
|
+
function isAuthGateError(error) {
|
|
748
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
749
|
+
const stage = error instanceof BrowserAutomationError ? String(error.details?.stage ?? "") : "";
|
|
750
|
+
return (stage.includes("cloudflare") ||
|
|
751
|
+
message.includes("login") ||
|
|
752
|
+
message.includes("auth") ||
|
|
753
|
+
message.includes("captcha") ||
|
|
754
|
+
message.includes("cloudflare") ||
|
|
755
|
+
message.includes("session expired") ||
|
|
756
|
+
message.includes("prompt textarea not available") ||
|
|
757
|
+
message.includes("no chatgpt cookies"));
|
|
758
|
+
}
|
|
759
|
+
function isAssistantTimeoutError(error) {
|
|
760
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
761
|
+
return message.includes("assistant") && message.includes("timed out");
|
|
762
|
+
}
|
|
763
|
+
function classifyBrowserError(error) {
|
|
764
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
765
|
+
if (message.includes("cloudflare") || message.includes("captcha"))
|
|
766
|
+
return "challenge_detected";
|
|
767
|
+
if (message.includes("mfa"))
|
|
768
|
+
return "mfa_detected";
|
|
769
|
+
if (message.includes("auth"))
|
|
770
|
+
return "auth_page_detected";
|
|
771
|
+
if (message.includes("login") || message.includes("no chatgpt cookies")) {
|
|
772
|
+
return "login_page_detected";
|
|
773
|
+
}
|
|
774
|
+
if (message.includes("prompt textarea not available"))
|
|
775
|
+
return "composer_not_visible";
|
|
776
|
+
return "auth_required";
|
|
777
|
+
}
|
|
778
|
+
function browserResultToRuntime(result) {
|
|
779
|
+
return {
|
|
780
|
+
browserTransport: result.browserTransport,
|
|
781
|
+
chromePid: result.chromePid,
|
|
782
|
+
chromePort: result.chromePort,
|
|
783
|
+
chromeHost: result.chromeHost,
|
|
784
|
+
chromeBrowserWSEndpoint: result.chromeBrowserWSEndpoint,
|
|
785
|
+
chromeProfileRoot: result.chromeProfileRoot,
|
|
786
|
+
userDataDir: result.userDataDir,
|
|
787
|
+
chromeTargetId: result.chromeTargetId,
|
|
788
|
+
tabUrl: result.tabUrl,
|
|
789
|
+
controllerPid: result.controllerPid,
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
async function readBrowserMetadata(filePath) {
|
|
793
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
794
|
+
const parsed = JSON.parse(raw);
|
|
795
|
+
return parsed;
|
|
796
|
+
}
|