agent-browser 0.14.0 → 0.15.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 +30 -1
- package/bin/agent-browser-darwin-arm64 +0 -0
- package/bin/agent-browser-darwin-x64 +0 -0
- package/bin/agent-browser-linux-arm64 +0 -0
- package/bin/agent-browser-linux-x64 +0 -0
- package/bin/agent-browser-win32-x64.exe +0 -0
- package/dist/action-policy.d.ts +14 -0
- package/dist/action-policy.d.ts.map +1 -0
- package/dist/action-policy.js +253 -0
- package/dist/action-policy.js.map +1 -0
- package/dist/actions.d.ts +1 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +478 -303
- package/dist/actions.js.map +1 -1
- package/dist/auth-cli.d.ts +2 -0
- package/dist/auth-cli.d.ts.map +1 -0
- package/dist/auth-cli.js +97 -0
- package/dist/auth-cli.js.map +1 -0
- package/dist/auth-vault.d.ts +36 -0
- package/dist/auth-vault.d.ts.map +1 -0
- package/dist/auth-vault.js +125 -0
- package/dist/auth-vault.js.map +1 -0
- package/dist/browser.d.ts +19 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +114 -3
- package/dist/browser.js.map +1 -1
- package/dist/confirmation.d.ts +8 -0
- package/dist/confirmation.d.ts.map +1 -0
- package/dist/confirmation.js +30 -0
- package/dist/confirmation.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +6 -3
- package/dist/daemon.js.map +1 -1
- package/dist/domain-filter.d.ts +28 -0
- package/dist/domain-filter.d.ts.map +1 -0
- package/dist/domain-filter.js +149 -0
- package/dist/domain-filter.js.map +1 -0
- package/dist/encryption.d.ts +25 -2
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -10
- package/dist/encryption.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +50 -0
- package/dist/protocol.js.map +1 -1
- package/dist/types.d.ts +65 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -1
- package/skills/agent-browser/SKILL.md +72 -0
- package/skills/agent-browser/templates/authenticated-session.sh +5 -0
- package/skills/dogfood/SKILL.md +216 -0
- package/skills/dogfood/references/issue-taxonomy.md +109 -0
- package/skills/dogfood/templates/dogfood-report-template.md +53 -0
package/dist/actions.js
CHANGED
|
@@ -2,8 +2,11 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { mkdirSync } from 'node:fs';
|
|
4
4
|
import { getAppDir } from './daemon.js';
|
|
5
|
+
import { checkPolicy, describeAction, getActionCategory, loadPolicyFile, initPolicyReloader, reloadPolicyIfChanged, } from './action-policy.js';
|
|
6
|
+
import { requestConfirmation, getAndRemovePending } from './confirmation.js';
|
|
7
|
+
import { getAuthProfile, updateLastLogin } from './auth-vault.js';
|
|
5
8
|
import { getSessionsDir, readStateFile, isValidSessionName, isEncryptedPayload, listStateFiles, cleanupExpiredStates, } from './state-utils.js';
|
|
6
|
-
import { successResponse, errorResponse } from './protocol.js';
|
|
9
|
+
import { successResponse, errorResponse, parseCommand } from './protocol.js';
|
|
7
10
|
import { diffSnapshots, diffScreenshots } from './diff.js';
|
|
8
11
|
import { getEnhancedSnapshot } from './snapshot.js';
|
|
9
12
|
// Callback for screencast frames - will be set by the daemon when streaming is active
|
|
@@ -54,295 +57,353 @@ export function toAIFriendlyError(error, selector) {
|
|
|
54
57
|
// Return original error for unknown cases
|
|
55
58
|
return error instanceof Error ? error : new Error(message);
|
|
56
59
|
}
|
|
60
|
+
let actionPolicy = null;
|
|
61
|
+
let confirmCategories = new Set();
|
|
62
|
+
export function initActionPolicy() {
|
|
63
|
+
const policyPath = process.env.AGENT_BROWSER_ACTION_POLICY;
|
|
64
|
+
if (policyPath) {
|
|
65
|
+
try {
|
|
66
|
+
actionPolicy = loadPolicyFile(policyPath);
|
|
67
|
+
initPolicyReloader(policyPath, actionPolicy);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error(`[ERROR] Failed to load action policy from ${policyPath}: ${err instanceof Error ? err.message : err}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const confirmActionsEnv = process.env.AGENT_BROWSER_CONFIRM_ACTIONS;
|
|
75
|
+
if (confirmActionsEnv) {
|
|
76
|
+
confirmCategories = new Set(confirmActionsEnv
|
|
77
|
+
.split(',')
|
|
78
|
+
.map((c) => c.trim().toLowerCase())
|
|
79
|
+
.filter((c) => c.length > 0));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
57
82
|
/**
|
|
58
83
|
* Execute a command and return a response
|
|
59
84
|
*/
|
|
60
85
|
export async function executeCommand(command, browser) {
|
|
61
86
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return await handleClick(command, browser);
|
|
69
|
-
case 'type':
|
|
70
|
-
return await handleType(command, browser);
|
|
71
|
-
case 'fill':
|
|
72
|
-
return await handleFill(command, browser);
|
|
73
|
-
case 'check':
|
|
74
|
-
return await handleCheck(command, browser);
|
|
75
|
-
case 'uncheck':
|
|
76
|
-
return await handleUncheck(command, browser);
|
|
77
|
-
case 'upload':
|
|
78
|
-
return await handleUpload(command, browser);
|
|
79
|
-
case 'dblclick':
|
|
80
|
-
return await handleDoubleClick(command, browser);
|
|
81
|
-
case 'focus':
|
|
82
|
-
return await handleFocus(command, browser);
|
|
83
|
-
case 'drag':
|
|
84
|
-
return await handleDrag(command, browser);
|
|
85
|
-
case 'frame':
|
|
86
|
-
return await handleFrame(command, browser);
|
|
87
|
-
case 'mainframe':
|
|
88
|
-
return await handleMainFrame(command, browser);
|
|
89
|
-
case 'getbyrole':
|
|
90
|
-
return await handleGetByRole(command, browser);
|
|
91
|
-
case 'getbytext':
|
|
92
|
-
return await handleGetByText(command, browser);
|
|
93
|
-
case 'getbylabel':
|
|
94
|
-
return await handleGetByLabel(command, browser);
|
|
95
|
-
case 'getbyplaceholder':
|
|
96
|
-
return await handleGetByPlaceholder(command, browser);
|
|
97
|
-
case 'press':
|
|
98
|
-
return await handlePress(command, browser);
|
|
99
|
-
case 'screenshot':
|
|
100
|
-
return await handleScreenshot(command, browser);
|
|
101
|
-
case 'snapshot':
|
|
102
|
-
return await handleSnapshot(command, browser);
|
|
103
|
-
case 'evaluate':
|
|
104
|
-
return await handleEvaluate(command, browser);
|
|
105
|
-
case 'wait':
|
|
106
|
-
return await handleWait(command, browser);
|
|
107
|
-
case 'scroll':
|
|
108
|
-
return await handleScroll(command, browser);
|
|
109
|
-
case 'select':
|
|
110
|
-
return await handleSelect(command, browser);
|
|
111
|
-
case 'hover':
|
|
112
|
-
return await handleHover(command, browser);
|
|
113
|
-
case 'content':
|
|
114
|
-
return await handleContent(command, browser);
|
|
115
|
-
case 'close':
|
|
116
|
-
return await handleClose(command, browser);
|
|
117
|
-
case 'tab_new':
|
|
118
|
-
return await handleTabNew(command, browser);
|
|
119
|
-
case 'tab_list':
|
|
120
|
-
return await handleTabList(command, browser);
|
|
121
|
-
case 'tab_switch':
|
|
122
|
-
return await handleTabSwitch(command, browser);
|
|
123
|
-
case 'tab_close':
|
|
124
|
-
return await handleTabClose(command, browser);
|
|
125
|
-
case 'window_new':
|
|
126
|
-
return await handleWindowNew(command, browser);
|
|
127
|
-
case 'cookies_get':
|
|
128
|
-
return await handleCookiesGet(command, browser);
|
|
129
|
-
case 'cookies_set':
|
|
130
|
-
return await handleCookiesSet(command, browser);
|
|
131
|
-
case 'cookies_clear':
|
|
132
|
-
return await handleCookiesClear(command, browser);
|
|
133
|
-
case 'storage_get':
|
|
134
|
-
return await handleStorageGet(command, browser);
|
|
135
|
-
case 'storage_set':
|
|
136
|
-
return await handleStorageSet(command, browser);
|
|
137
|
-
case 'storage_clear':
|
|
138
|
-
return await handleStorageClear(command, browser);
|
|
139
|
-
case 'dialog':
|
|
140
|
-
return await handleDialog(command, browser);
|
|
141
|
-
case 'pdf':
|
|
142
|
-
return await handlePdf(command, browser);
|
|
143
|
-
case 'route':
|
|
144
|
-
return await handleRoute(command, browser);
|
|
145
|
-
case 'unroute':
|
|
146
|
-
return await handleUnroute(command, browser);
|
|
147
|
-
case 'requests':
|
|
148
|
-
return await handleRequests(command, browser);
|
|
149
|
-
case 'download':
|
|
150
|
-
return await handleDownload(command, browser);
|
|
151
|
-
case 'geolocation':
|
|
152
|
-
return await handleGeolocation(command, browser);
|
|
153
|
-
case 'permissions':
|
|
154
|
-
return await handlePermissions(command, browser);
|
|
155
|
-
case 'viewport':
|
|
156
|
-
return await handleViewport(command, browser);
|
|
157
|
-
case 'useragent':
|
|
158
|
-
return await handleUserAgent(command, browser);
|
|
159
|
-
case 'device':
|
|
160
|
-
return await handleDevice(command, browser);
|
|
161
|
-
case 'back':
|
|
162
|
-
return await handleBack(command, browser);
|
|
163
|
-
case 'forward':
|
|
164
|
-
return await handleForward(command, browser);
|
|
165
|
-
case 'reload':
|
|
166
|
-
return await handleReload(command, browser);
|
|
167
|
-
case 'url':
|
|
168
|
-
return await handleUrl(command, browser);
|
|
169
|
-
case 'title':
|
|
170
|
-
return await handleTitle(command, browser);
|
|
171
|
-
case 'getattribute':
|
|
172
|
-
return await handleGetAttribute(command, browser);
|
|
173
|
-
case 'gettext':
|
|
174
|
-
return await handleGetText(command, browser);
|
|
175
|
-
case 'isvisible':
|
|
176
|
-
return await handleIsVisible(command, browser);
|
|
177
|
-
case 'isenabled':
|
|
178
|
-
return await handleIsEnabled(command, browser);
|
|
179
|
-
case 'ischecked':
|
|
180
|
-
return await handleIsChecked(command, browser);
|
|
181
|
-
case 'count':
|
|
182
|
-
return await handleCount(command, browser);
|
|
183
|
-
case 'boundingbox':
|
|
184
|
-
return await handleBoundingBox(command, browser);
|
|
185
|
-
case 'styles':
|
|
186
|
-
return await handleStyles(command, browser);
|
|
187
|
-
case 'video_start':
|
|
188
|
-
return await handleVideoStart(command, browser);
|
|
189
|
-
case 'video_stop':
|
|
190
|
-
return await handleVideoStop(command, browser);
|
|
191
|
-
case 'trace_start':
|
|
192
|
-
return await handleTraceStart(command, browser);
|
|
193
|
-
case 'trace_stop':
|
|
194
|
-
return await handleTraceStop(command, browser);
|
|
195
|
-
case 'profiler_start':
|
|
196
|
-
return await handleProfilerStart(command, browser);
|
|
197
|
-
case 'profiler_stop':
|
|
198
|
-
return await handleProfilerStop(command, browser);
|
|
199
|
-
case 'har_start':
|
|
200
|
-
return await handleHarStart(command, browser);
|
|
201
|
-
case 'har_stop':
|
|
202
|
-
return await handleHarStop(command, browser);
|
|
203
|
-
case 'state_save':
|
|
204
|
-
return await handleStateSave(command, browser);
|
|
205
|
-
case 'state_load':
|
|
206
|
-
return await handleStateLoad(command, browser);
|
|
207
|
-
case 'state_list':
|
|
208
|
-
return await handleStateList(command);
|
|
209
|
-
case 'state_clear':
|
|
210
|
-
return await handleStateClear(command);
|
|
211
|
-
case 'state_show':
|
|
212
|
-
return await handleStateShow(command);
|
|
213
|
-
case 'state_clean':
|
|
214
|
-
return await handleStateClean(command);
|
|
215
|
-
case 'state_rename':
|
|
216
|
-
return await handleStateRename(command);
|
|
217
|
-
case 'console':
|
|
218
|
-
return await handleConsole(command, browser);
|
|
219
|
-
case 'errors':
|
|
220
|
-
return await handleErrors(command, browser);
|
|
221
|
-
case 'keyboard':
|
|
222
|
-
return await handleKeyboard(command, browser);
|
|
223
|
-
case 'wheel':
|
|
224
|
-
return await handleWheel(command, browser);
|
|
225
|
-
case 'tap':
|
|
226
|
-
return await handleTap(command, browser);
|
|
227
|
-
case 'clipboard':
|
|
228
|
-
return await handleClipboard(command, browser);
|
|
229
|
-
case 'highlight':
|
|
230
|
-
return await handleHighlight(command, browser);
|
|
231
|
-
case 'clear':
|
|
232
|
-
return await handleClear(command, browser);
|
|
233
|
-
case 'selectall':
|
|
234
|
-
return await handleSelectAll(command, browser);
|
|
235
|
-
case 'innertext':
|
|
236
|
-
return await handleInnerText(command, browser);
|
|
237
|
-
case 'innerhtml':
|
|
238
|
-
return await handleInnerHtml(command, browser);
|
|
239
|
-
case 'inputvalue':
|
|
240
|
-
return await handleInputValue(command, browser);
|
|
241
|
-
case 'setvalue':
|
|
242
|
-
return await handleSetValue(command, browser);
|
|
243
|
-
case 'dispatch':
|
|
244
|
-
return await handleDispatch(command, browser);
|
|
245
|
-
case 'evalhandle':
|
|
246
|
-
return await handleEvalHandle(command, browser);
|
|
247
|
-
case 'expose':
|
|
248
|
-
return await handleExpose(command, browser);
|
|
249
|
-
case 'addscript':
|
|
250
|
-
return await handleAddScript(command, browser);
|
|
251
|
-
case 'addstyle':
|
|
252
|
-
return await handleAddStyle(command, browser);
|
|
253
|
-
case 'emulatemedia':
|
|
254
|
-
return await handleEmulateMedia(command, browser);
|
|
255
|
-
case 'offline':
|
|
256
|
-
return await handleOffline(command, browser);
|
|
257
|
-
case 'headers':
|
|
258
|
-
return await handleHeaders(command, browser);
|
|
259
|
-
case 'pause':
|
|
260
|
-
return await handlePause(command, browser);
|
|
261
|
-
case 'getbyalttext':
|
|
262
|
-
return await handleGetByAltText(command, browser);
|
|
263
|
-
case 'getbytitle':
|
|
264
|
-
return await handleGetByTitle(command, browser);
|
|
265
|
-
case 'getbytestid':
|
|
266
|
-
return await handleGetByTestId(command, browser);
|
|
267
|
-
case 'nth':
|
|
268
|
-
return await handleNth(command, browser);
|
|
269
|
-
case 'waitforurl':
|
|
270
|
-
return await handleWaitForUrl(command, browser);
|
|
271
|
-
case 'waitforloadstate':
|
|
272
|
-
return await handleWaitForLoadState(command, browser);
|
|
273
|
-
case 'setcontent':
|
|
274
|
-
return await handleSetContent(command, browser);
|
|
275
|
-
case 'timezone':
|
|
276
|
-
return await handleTimezone(command, browser);
|
|
277
|
-
case 'locale':
|
|
278
|
-
return await handleLocale(command, browser);
|
|
279
|
-
case 'credentials':
|
|
280
|
-
return await handleCredentials(command, browser);
|
|
281
|
-
case 'mousemove':
|
|
282
|
-
return await handleMouseMove(command, browser);
|
|
283
|
-
case 'mousedown':
|
|
284
|
-
return await handleMouseDown(command, browser);
|
|
285
|
-
case 'mouseup':
|
|
286
|
-
return await handleMouseUp(command, browser);
|
|
287
|
-
case 'bringtofront':
|
|
288
|
-
return await handleBringToFront(command, browser);
|
|
289
|
-
case 'waitforfunction':
|
|
290
|
-
return await handleWaitForFunction(command, browser);
|
|
291
|
-
case 'scrollintoview':
|
|
292
|
-
return await handleScrollIntoView(command, browser);
|
|
293
|
-
case 'addinitscript':
|
|
294
|
-
return await handleAddInitScript(command, browser);
|
|
295
|
-
case 'keydown':
|
|
296
|
-
return await handleKeyDown(command, browser);
|
|
297
|
-
case 'keyup':
|
|
298
|
-
return await handleKeyUp(command, browser);
|
|
299
|
-
case 'inserttext':
|
|
300
|
-
return await handleInsertText(command, browser);
|
|
301
|
-
case 'multiselect':
|
|
302
|
-
return await handleMultiSelect(command, browser);
|
|
303
|
-
case 'waitfordownload':
|
|
304
|
-
return await handleWaitForDownload(command, browser);
|
|
305
|
-
case 'responsebody':
|
|
306
|
-
return await handleResponseBody(command, browser);
|
|
307
|
-
case 'screencast_start':
|
|
308
|
-
return await handleScreencastStart(command, browser);
|
|
309
|
-
case 'screencast_stop':
|
|
310
|
-
return await handleScreencastStop(command, browser);
|
|
311
|
-
case 'input_mouse':
|
|
312
|
-
return await handleInputMouse(command, browser);
|
|
313
|
-
case 'input_keyboard':
|
|
314
|
-
return await handleInputKeyboard(command, browser);
|
|
315
|
-
case 'input_touch':
|
|
316
|
-
return await handleInputTouch(command, browser);
|
|
317
|
-
case 'recording_start':
|
|
318
|
-
return await handleRecordingStart(command, browser);
|
|
319
|
-
case 'recording_stop':
|
|
320
|
-
return await handleRecordingStop(command, browser);
|
|
321
|
-
case 'recording_restart':
|
|
322
|
-
return await handleRecordingRestart(command, browser);
|
|
323
|
-
case 'diff_snapshot':
|
|
324
|
-
return await handleDiffSnapshot(command, browser);
|
|
325
|
-
case 'diff_screenshot':
|
|
326
|
-
return await handleDiffScreenshot(command, browser);
|
|
327
|
-
case 'diff_url':
|
|
328
|
-
return await handleDiffUrl(command, browser);
|
|
329
|
-
default: {
|
|
330
|
-
// TypeScript narrows to never here, but we handle it for safety
|
|
331
|
-
const unknownCommand = command;
|
|
332
|
-
return errorResponse(unknownCommand.id, `Unknown action: ${unknownCommand.action}`);
|
|
333
|
-
}
|
|
87
|
+
// Handle confirm/deny actions (bypass policy check)
|
|
88
|
+
if (command.action === 'confirm') {
|
|
89
|
+
return await handleConfirm(command, browser);
|
|
90
|
+
}
|
|
91
|
+
if (command.action === 'deny') {
|
|
92
|
+
return handleDeny(command);
|
|
334
93
|
}
|
|
94
|
+
// Hot-reload policy file if it changed on disk
|
|
95
|
+
actionPolicy = reloadPolicyIfChanged();
|
|
96
|
+
// Policy enforcement
|
|
97
|
+
const decision = checkPolicy(command.action, actionPolicy, confirmCategories);
|
|
98
|
+
if (decision === 'deny') {
|
|
99
|
+
const category = getActionCategory(command.action);
|
|
100
|
+
return errorResponse(command.id, `Action denied by policy: '${category}' is not allowed`);
|
|
101
|
+
}
|
|
102
|
+
if (decision === 'confirm') {
|
|
103
|
+
const category = getActionCategory(command.action);
|
|
104
|
+
const description = describeAction(command.action, command);
|
|
105
|
+
const { confirmationId } = requestConfirmation(command.action, category, description, command);
|
|
106
|
+
return successResponse(command.id, {
|
|
107
|
+
confirmation_required: true,
|
|
108
|
+
action: command.action,
|
|
109
|
+
category,
|
|
110
|
+
description,
|
|
111
|
+
confirmation_id: confirmationId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return await dispatchAction(command, browser);
|
|
335
115
|
}
|
|
336
116
|
catch (error) {
|
|
337
117
|
const message = error instanceof Error ? error.message : String(error);
|
|
338
118
|
return errorResponse(command.id, message);
|
|
339
119
|
}
|
|
340
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Dispatch a command to its handler after policy checks have passed.
|
|
123
|
+
*/
|
|
124
|
+
async function dispatchAction(command, browser) {
|
|
125
|
+
switch (command.action) {
|
|
126
|
+
case 'launch':
|
|
127
|
+
return await handleLaunch(command, browser);
|
|
128
|
+
case 'navigate':
|
|
129
|
+
return await handleNavigate(command, browser);
|
|
130
|
+
case 'click':
|
|
131
|
+
return await handleClick(command, browser);
|
|
132
|
+
case 'type':
|
|
133
|
+
return await handleType(command, browser);
|
|
134
|
+
case 'fill':
|
|
135
|
+
return await handleFill(command, browser);
|
|
136
|
+
case 'check':
|
|
137
|
+
return await handleCheck(command, browser);
|
|
138
|
+
case 'uncheck':
|
|
139
|
+
return await handleUncheck(command, browser);
|
|
140
|
+
case 'upload':
|
|
141
|
+
return await handleUpload(command, browser);
|
|
142
|
+
case 'dblclick':
|
|
143
|
+
return await handleDoubleClick(command, browser);
|
|
144
|
+
case 'focus':
|
|
145
|
+
return await handleFocus(command, browser);
|
|
146
|
+
case 'drag':
|
|
147
|
+
return await handleDrag(command, browser);
|
|
148
|
+
case 'frame':
|
|
149
|
+
return await handleFrame(command, browser);
|
|
150
|
+
case 'mainframe':
|
|
151
|
+
return await handleMainFrame(command, browser);
|
|
152
|
+
case 'getbyrole':
|
|
153
|
+
return await handleGetByRole(command, browser);
|
|
154
|
+
case 'getbytext':
|
|
155
|
+
return await handleGetByText(command, browser);
|
|
156
|
+
case 'getbylabel':
|
|
157
|
+
return await handleGetByLabel(command, browser);
|
|
158
|
+
case 'getbyplaceholder':
|
|
159
|
+
return await handleGetByPlaceholder(command, browser);
|
|
160
|
+
case 'press':
|
|
161
|
+
return await handlePress(command, browser);
|
|
162
|
+
case 'screenshot':
|
|
163
|
+
return await handleScreenshot(command, browser);
|
|
164
|
+
case 'snapshot':
|
|
165
|
+
return await handleSnapshot(command, browser);
|
|
166
|
+
case 'evaluate':
|
|
167
|
+
return await handleEvaluate(command, browser);
|
|
168
|
+
case 'wait':
|
|
169
|
+
return await handleWait(command, browser);
|
|
170
|
+
case 'scroll':
|
|
171
|
+
return await handleScroll(command, browser);
|
|
172
|
+
case 'select':
|
|
173
|
+
return await handleSelect(command, browser);
|
|
174
|
+
case 'hover':
|
|
175
|
+
return await handleHover(command, browser);
|
|
176
|
+
case 'content':
|
|
177
|
+
return await handleContent(command, browser);
|
|
178
|
+
case 'close':
|
|
179
|
+
return await handleClose(command, browser);
|
|
180
|
+
case 'tab_new':
|
|
181
|
+
return await handleTabNew(command, browser);
|
|
182
|
+
case 'tab_list':
|
|
183
|
+
return await handleTabList(command, browser);
|
|
184
|
+
case 'tab_switch':
|
|
185
|
+
return await handleTabSwitch(command, browser);
|
|
186
|
+
case 'tab_close':
|
|
187
|
+
return await handleTabClose(command, browser);
|
|
188
|
+
case 'window_new':
|
|
189
|
+
return await handleWindowNew(command, browser);
|
|
190
|
+
case 'cookies_get':
|
|
191
|
+
return await handleCookiesGet(command, browser);
|
|
192
|
+
case 'cookies_set':
|
|
193
|
+
return await handleCookiesSet(command, browser);
|
|
194
|
+
case 'cookies_clear':
|
|
195
|
+
return await handleCookiesClear(command, browser);
|
|
196
|
+
case 'storage_get':
|
|
197
|
+
return await handleStorageGet(command, browser);
|
|
198
|
+
case 'storage_set':
|
|
199
|
+
return await handleStorageSet(command, browser);
|
|
200
|
+
case 'storage_clear':
|
|
201
|
+
return await handleStorageClear(command, browser);
|
|
202
|
+
case 'dialog':
|
|
203
|
+
return await handleDialog(command, browser);
|
|
204
|
+
case 'pdf':
|
|
205
|
+
return await handlePdf(command, browser);
|
|
206
|
+
case 'route':
|
|
207
|
+
return await handleRoute(command, browser);
|
|
208
|
+
case 'unroute':
|
|
209
|
+
return await handleUnroute(command, browser);
|
|
210
|
+
case 'requests':
|
|
211
|
+
return await handleRequests(command, browser);
|
|
212
|
+
case 'download':
|
|
213
|
+
return await handleDownload(command, browser);
|
|
214
|
+
case 'geolocation':
|
|
215
|
+
return await handleGeolocation(command, browser);
|
|
216
|
+
case 'permissions':
|
|
217
|
+
return await handlePermissions(command, browser);
|
|
218
|
+
case 'viewport':
|
|
219
|
+
return await handleViewport(command, browser);
|
|
220
|
+
case 'useragent':
|
|
221
|
+
return await handleUserAgent(command, browser);
|
|
222
|
+
case 'device':
|
|
223
|
+
return await handleDevice(command, browser);
|
|
224
|
+
case 'back':
|
|
225
|
+
return await handleBack(command, browser);
|
|
226
|
+
case 'forward':
|
|
227
|
+
return await handleForward(command, browser);
|
|
228
|
+
case 'reload':
|
|
229
|
+
return await handleReload(command, browser);
|
|
230
|
+
case 'url':
|
|
231
|
+
return await handleUrl(command, browser);
|
|
232
|
+
case 'title':
|
|
233
|
+
return await handleTitle(command, browser);
|
|
234
|
+
case 'getattribute':
|
|
235
|
+
return await handleGetAttribute(command, browser);
|
|
236
|
+
case 'gettext':
|
|
237
|
+
return await handleGetText(command, browser);
|
|
238
|
+
case 'isvisible':
|
|
239
|
+
return await handleIsVisible(command, browser);
|
|
240
|
+
case 'isenabled':
|
|
241
|
+
return await handleIsEnabled(command, browser);
|
|
242
|
+
case 'ischecked':
|
|
243
|
+
return await handleIsChecked(command, browser);
|
|
244
|
+
case 'count':
|
|
245
|
+
return await handleCount(command, browser);
|
|
246
|
+
case 'boundingbox':
|
|
247
|
+
return await handleBoundingBox(command, browser);
|
|
248
|
+
case 'styles':
|
|
249
|
+
return await handleStyles(command, browser);
|
|
250
|
+
case 'video_start':
|
|
251
|
+
return await handleVideoStart(command, browser);
|
|
252
|
+
case 'video_stop':
|
|
253
|
+
return await handleVideoStop(command, browser);
|
|
254
|
+
case 'trace_start':
|
|
255
|
+
return await handleTraceStart(command, browser);
|
|
256
|
+
case 'trace_stop':
|
|
257
|
+
return await handleTraceStop(command, browser);
|
|
258
|
+
case 'profiler_start':
|
|
259
|
+
return await handleProfilerStart(command, browser);
|
|
260
|
+
case 'profiler_stop':
|
|
261
|
+
return await handleProfilerStop(command, browser);
|
|
262
|
+
case 'har_start':
|
|
263
|
+
return await handleHarStart(command, browser);
|
|
264
|
+
case 'har_stop':
|
|
265
|
+
return await handleHarStop(command, browser);
|
|
266
|
+
case 'state_save':
|
|
267
|
+
return await handleStateSave(command, browser);
|
|
268
|
+
case 'state_load':
|
|
269
|
+
return await handleStateLoad(command, browser);
|
|
270
|
+
case 'state_list':
|
|
271
|
+
return await handleStateList(command);
|
|
272
|
+
case 'state_clear':
|
|
273
|
+
return await handleStateClear(command);
|
|
274
|
+
case 'state_show':
|
|
275
|
+
return await handleStateShow(command);
|
|
276
|
+
case 'state_clean':
|
|
277
|
+
return await handleStateClean(command);
|
|
278
|
+
case 'state_rename':
|
|
279
|
+
return await handleStateRename(command);
|
|
280
|
+
case 'console':
|
|
281
|
+
return await handleConsole(command, browser);
|
|
282
|
+
case 'errors':
|
|
283
|
+
return await handleErrors(command, browser);
|
|
284
|
+
case 'keyboard':
|
|
285
|
+
return await handleKeyboard(command, browser);
|
|
286
|
+
case 'wheel':
|
|
287
|
+
return await handleWheel(command, browser);
|
|
288
|
+
case 'tap':
|
|
289
|
+
return await handleTap(command, browser);
|
|
290
|
+
case 'clipboard':
|
|
291
|
+
return await handleClipboard(command, browser);
|
|
292
|
+
case 'highlight':
|
|
293
|
+
return await handleHighlight(command, browser);
|
|
294
|
+
case 'clear':
|
|
295
|
+
return await handleClear(command, browser);
|
|
296
|
+
case 'selectall':
|
|
297
|
+
return await handleSelectAll(command, browser);
|
|
298
|
+
case 'innertext':
|
|
299
|
+
return await handleInnerText(command, browser);
|
|
300
|
+
case 'innerhtml':
|
|
301
|
+
return await handleInnerHtml(command, browser);
|
|
302
|
+
case 'inputvalue':
|
|
303
|
+
return await handleInputValue(command, browser);
|
|
304
|
+
case 'setvalue':
|
|
305
|
+
return await handleSetValue(command, browser);
|
|
306
|
+
case 'dispatch':
|
|
307
|
+
return await handleDispatch(command, browser);
|
|
308
|
+
case 'evalhandle':
|
|
309
|
+
return await handleEvalHandle(command, browser);
|
|
310
|
+
case 'expose':
|
|
311
|
+
return await handleExpose(command, browser);
|
|
312
|
+
case 'addscript':
|
|
313
|
+
return await handleAddScript(command, browser);
|
|
314
|
+
case 'addstyle':
|
|
315
|
+
return await handleAddStyle(command, browser);
|
|
316
|
+
case 'emulatemedia':
|
|
317
|
+
return await handleEmulateMedia(command, browser);
|
|
318
|
+
case 'offline':
|
|
319
|
+
return await handleOffline(command, browser);
|
|
320
|
+
case 'headers':
|
|
321
|
+
return await handleHeaders(command, browser);
|
|
322
|
+
case 'pause':
|
|
323
|
+
return await handlePause(command, browser);
|
|
324
|
+
case 'getbyalttext':
|
|
325
|
+
return await handleGetByAltText(command, browser);
|
|
326
|
+
case 'getbytitle':
|
|
327
|
+
return await handleGetByTitle(command, browser);
|
|
328
|
+
case 'getbytestid':
|
|
329
|
+
return await handleGetByTestId(command, browser);
|
|
330
|
+
case 'nth':
|
|
331
|
+
return await handleNth(command, browser);
|
|
332
|
+
case 'waitforurl':
|
|
333
|
+
return await handleWaitForUrl(command, browser);
|
|
334
|
+
case 'waitforloadstate':
|
|
335
|
+
return await handleWaitForLoadState(command, browser);
|
|
336
|
+
case 'setcontent':
|
|
337
|
+
return await handleSetContent(command, browser);
|
|
338
|
+
case 'timezone':
|
|
339
|
+
return await handleTimezone(command, browser);
|
|
340
|
+
case 'locale':
|
|
341
|
+
return await handleLocale(command, browser);
|
|
342
|
+
case 'credentials':
|
|
343
|
+
return await handleCredentials(command, browser);
|
|
344
|
+
case 'mousemove':
|
|
345
|
+
return await handleMouseMove(command, browser);
|
|
346
|
+
case 'mousedown':
|
|
347
|
+
return await handleMouseDown(command, browser);
|
|
348
|
+
case 'mouseup':
|
|
349
|
+
return await handleMouseUp(command, browser);
|
|
350
|
+
case 'bringtofront':
|
|
351
|
+
return await handleBringToFront(command, browser);
|
|
352
|
+
case 'waitforfunction':
|
|
353
|
+
return await handleWaitForFunction(command, browser);
|
|
354
|
+
case 'scrollintoview':
|
|
355
|
+
return await handleScrollIntoView(command, browser);
|
|
356
|
+
case 'addinitscript':
|
|
357
|
+
return await handleAddInitScript(command, browser);
|
|
358
|
+
case 'keydown':
|
|
359
|
+
return await handleKeyDown(command, browser);
|
|
360
|
+
case 'keyup':
|
|
361
|
+
return await handleKeyUp(command, browser);
|
|
362
|
+
case 'inserttext':
|
|
363
|
+
return await handleInsertText(command, browser);
|
|
364
|
+
case 'multiselect':
|
|
365
|
+
return await handleMultiSelect(command, browser);
|
|
366
|
+
case 'waitfordownload':
|
|
367
|
+
return await handleWaitForDownload(command, browser);
|
|
368
|
+
case 'responsebody':
|
|
369
|
+
return await handleResponseBody(command, browser);
|
|
370
|
+
case 'screencast_start':
|
|
371
|
+
return await handleScreencastStart(command, browser);
|
|
372
|
+
case 'screencast_stop':
|
|
373
|
+
return await handleScreencastStop(command, browser);
|
|
374
|
+
case 'input_mouse':
|
|
375
|
+
return await handleInputMouse(command, browser);
|
|
376
|
+
case 'input_keyboard':
|
|
377
|
+
return await handleInputKeyboard(command, browser);
|
|
378
|
+
case 'input_touch':
|
|
379
|
+
return await handleInputTouch(command, browser);
|
|
380
|
+
case 'recording_start':
|
|
381
|
+
return await handleRecordingStart(command, browser);
|
|
382
|
+
case 'recording_stop':
|
|
383
|
+
return await handleRecordingStop(command, browser);
|
|
384
|
+
case 'recording_restart':
|
|
385
|
+
return await handleRecordingRestart(command, browser);
|
|
386
|
+
case 'diff_snapshot':
|
|
387
|
+
return await handleDiffSnapshot(command, browser);
|
|
388
|
+
case 'diff_screenshot':
|
|
389
|
+
return await handleDiffScreenshot(command, browser);
|
|
390
|
+
case 'diff_url':
|
|
391
|
+
return await handleDiffUrl(command, browser);
|
|
392
|
+
case 'auth_login':
|
|
393
|
+
return await handleAuthLogin(command, browser);
|
|
394
|
+
default: {
|
|
395
|
+
// TypeScript narrows to never here, but we handle it for safety
|
|
396
|
+
const unknownCommand = command;
|
|
397
|
+
return errorResponse(unknownCommand.id, `Unknown action: ${unknownCommand.action}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
341
401
|
async function handleLaunch(command, browser) {
|
|
342
402
|
await browser.launch(command);
|
|
343
403
|
return successResponse(command.id, { launched: true });
|
|
344
404
|
}
|
|
345
405
|
async function handleNavigate(command, browser) {
|
|
406
|
+
browser.checkDomainAllowed(command.url);
|
|
346
407
|
const page = browser.getPage();
|
|
347
408
|
// If headers are provided, set up scoped headers for this origin
|
|
348
409
|
if (command.headers && Object.keys(command.headers).length > 0) {
|
|
@@ -612,16 +673,18 @@ async function handleSnapshot(command, browser) {
|
|
|
612
673
|
for (const [ref, data] of Object.entries(refs)) {
|
|
613
674
|
simpleRefs[ref] = { role: data.role, name: data.name };
|
|
614
675
|
}
|
|
676
|
+
const page = browser.getPage();
|
|
615
677
|
return successResponse(command.id, {
|
|
616
678
|
snapshot: tree || 'Empty page',
|
|
617
679
|
refs: Object.keys(simpleRefs).length > 0 ? simpleRefs : undefined,
|
|
680
|
+
origin: page.url(),
|
|
618
681
|
});
|
|
619
682
|
}
|
|
620
683
|
async function handleEvaluate(command, browser) {
|
|
621
684
|
const page = browser.getPage();
|
|
622
685
|
// Evaluate the script directly as a string expression
|
|
623
686
|
const result = await page.evaluate(command.script);
|
|
624
|
-
return successResponse(command.id, { result });
|
|
687
|
+
return successResponse(command.id, { result, origin: page.url() });
|
|
625
688
|
}
|
|
626
689
|
async function handleWait(command, browser) {
|
|
627
690
|
const page = browser.getPage();
|
|
@@ -642,36 +705,36 @@ async function handleWait(command, browser) {
|
|
|
642
705
|
}
|
|
643
706
|
async function handleScroll(command, browser) {
|
|
644
707
|
const page = browser.getPage();
|
|
708
|
+
let deltaX = command.x ?? 0;
|
|
709
|
+
let deltaY = command.y ?? 0;
|
|
710
|
+
const hasExplicitDelta = command.x !== undefined || command.y !== undefined;
|
|
711
|
+
if (command.direction) {
|
|
712
|
+
const amount = command.amount ?? 100;
|
|
713
|
+
switch (command.direction) {
|
|
714
|
+
case 'up':
|
|
715
|
+
deltaY = -amount;
|
|
716
|
+
break;
|
|
717
|
+
case 'down':
|
|
718
|
+
deltaY = amount;
|
|
719
|
+
break;
|
|
720
|
+
case 'left':
|
|
721
|
+
deltaX = -amount;
|
|
722
|
+
break;
|
|
723
|
+
case 'right':
|
|
724
|
+
deltaX = amount;
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
645
728
|
if (command.selector) {
|
|
646
729
|
const element = browser.getLocator(command.selector);
|
|
647
730
|
await element.scrollIntoViewIfNeeded();
|
|
648
|
-
if (
|
|
731
|
+
if (hasExplicitDelta || deltaX !== 0 || deltaY !== 0) {
|
|
649
732
|
await element.evaluate((el, { x, y }) => {
|
|
650
|
-
el.scrollBy(x
|
|
651
|
-
}, { x:
|
|
733
|
+
el.scrollBy(x, y);
|
|
734
|
+
}, { x: deltaX, y: deltaY });
|
|
652
735
|
}
|
|
653
736
|
}
|
|
654
737
|
else {
|
|
655
|
-
// Scroll the page
|
|
656
|
-
let deltaX = command.x ?? 0;
|
|
657
|
-
let deltaY = command.y ?? 0;
|
|
658
|
-
if (command.direction) {
|
|
659
|
-
const amount = command.amount ?? 100;
|
|
660
|
-
switch (command.direction) {
|
|
661
|
-
case 'up':
|
|
662
|
-
deltaY = -amount;
|
|
663
|
-
break;
|
|
664
|
-
case 'down':
|
|
665
|
-
deltaY = amount;
|
|
666
|
-
break;
|
|
667
|
-
case 'left':
|
|
668
|
-
deltaX = -amount;
|
|
669
|
-
break;
|
|
670
|
-
case 'right':
|
|
671
|
-
deltaX = amount;
|
|
672
|
-
break;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
738
|
await page.evaluate(`window.scrollBy(${deltaX}, ${deltaY})`);
|
|
676
739
|
}
|
|
677
740
|
return successResponse(command.id, { scrolled: true });
|
|
@@ -706,7 +769,7 @@ async function handleContent(command, browser) {
|
|
|
706
769
|
else {
|
|
707
770
|
html = await page.content();
|
|
708
771
|
}
|
|
709
|
-
return successResponse(command.id, { html });
|
|
772
|
+
return successResponse(command.id, { html, origin: page.url() });
|
|
710
773
|
}
|
|
711
774
|
async function handleClose(command, browser) {
|
|
712
775
|
await browser.close();
|
|
@@ -1067,14 +1130,16 @@ async function handleTitle(command, browser) {
|
|
|
1067
1130
|
return successResponse(command.id, { title });
|
|
1068
1131
|
}
|
|
1069
1132
|
async function handleGetAttribute(command, browser) {
|
|
1133
|
+
const page = browser.getPage();
|
|
1070
1134
|
const locator = browser.getLocator(command.selector);
|
|
1071
1135
|
const value = await locator.getAttribute(command.attribute);
|
|
1072
|
-
return successResponse(command.id, { attribute: command.attribute, value });
|
|
1136
|
+
return successResponse(command.id, { attribute: command.attribute, value, origin: page.url() });
|
|
1073
1137
|
}
|
|
1074
1138
|
async function handleGetText(command, browser) {
|
|
1139
|
+
const page = browser.getPage();
|
|
1075
1140
|
const locator = browser.getLocator(command.selector);
|
|
1076
1141
|
const text = await locator.textContent();
|
|
1077
|
-
return successResponse(command.id, { text });
|
|
1142
|
+
return successResponse(command.id, { text, origin: page.url() });
|
|
1078
1143
|
}
|
|
1079
1144
|
async function handleIsVisible(command, browser) {
|
|
1080
1145
|
const locator = browser.getLocator(command.selector);
|
|
@@ -1354,8 +1419,9 @@ async function handleConsole(command, browser) {
|
|
|
1354
1419
|
browser.clearConsoleMessages();
|
|
1355
1420
|
return successResponse(command.id, { cleared: true });
|
|
1356
1421
|
}
|
|
1422
|
+
const page = browser.getPage();
|
|
1357
1423
|
const messages = browser.getConsoleMessages();
|
|
1358
|
-
return successResponse(command.id, { messages });
|
|
1424
|
+
return successResponse(command.id, { messages, origin: page.url() });
|
|
1359
1425
|
}
|
|
1360
1426
|
async function handleErrors(command, browser) {
|
|
1361
1427
|
if (command.clear) {
|
|
@@ -1435,12 +1501,13 @@ async function handleInnerText(command, browser) {
|
|
|
1435
1501
|
async function handleInnerHtml(command, browser) {
|
|
1436
1502
|
const page = browser.getPage();
|
|
1437
1503
|
const html = await page.locator(command.selector).innerHTML();
|
|
1438
|
-
return successResponse(command.id, { html });
|
|
1504
|
+
return successResponse(command.id, { html, origin: page.url() });
|
|
1439
1505
|
}
|
|
1440
1506
|
async function handleInputValue(command, browser) {
|
|
1507
|
+
const page = browser.getPage();
|
|
1441
1508
|
const locator = browser.getLocator(command.selector);
|
|
1442
1509
|
const value = await locator.inputValue();
|
|
1443
|
-
return successResponse(command.id, { value });
|
|
1510
|
+
return successResponse(command.id, { value, origin: page.url() });
|
|
1444
1511
|
}
|
|
1445
1512
|
async function handleSetValue(command, browser) {
|
|
1446
1513
|
const page = browser.getPage();
|
|
@@ -1859,4 +1926,112 @@ async function handleDiffUrl(command, browser) {
|
|
|
1859
1926
|
}
|
|
1860
1927
|
return successResponse(command.id, result);
|
|
1861
1928
|
}
|
|
1929
|
+
async function handleAuthLogin(command, browser) {
|
|
1930
|
+
const profile = getAuthProfile(command.name);
|
|
1931
|
+
if (!profile) {
|
|
1932
|
+
return errorResponse(command.id, `Auth profile '${command.name}' not found`);
|
|
1933
|
+
}
|
|
1934
|
+
browser.checkDomainAllowed(profile.url);
|
|
1935
|
+
const page = browser.getPage();
|
|
1936
|
+
await page.goto(profile.url, { waitUntil: 'load' });
|
|
1937
|
+
const usingAutoDetect = !profile.usernameSelector && !profile.passwordSelector && !profile.submitSelector;
|
|
1938
|
+
if (usingAutoDetect) {
|
|
1939
|
+
console.error(`[agent-browser] Auth login '${command.name}': using auto-detected form selectors. ` +
|
|
1940
|
+
`If login fails, specify --username-selector/--password-selector/--submit-selector with auth save.`);
|
|
1941
|
+
}
|
|
1942
|
+
const passSel = profile.passwordSelector || 'input[type="password"]:visible';
|
|
1943
|
+
// Auto-detect selectors ordered from most specific to broadest.
|
|
1944
|
+
// Locale-dependent text matchers (e.g. "Sign in") are intentionally
|
|
1945
|
+
// excluded -- they break on non-English pages.
|
|
1946
|
+
const AUTO_USER_SELECTORS = [
|
|
1947
|
+
'input[autocomplete="username"]:visible',
|
|
1948
|
+
'input[type="email"]:visible',
|
|
1949
|
+
'input[name="username"]:visible',
|
|
1950
|
+
'input[name="email"]:visible',
|
|
1951
|
+
];
|
|
1952
|
+
const AUTO_SUBMIT_SELECTORS = ['button[type="submit"]:visible', 'input[type="submit"]:visible'];
|
|
1953
|
+
try {
|
|
1954
|
+
// Resolve username field: custom selector or sequential auto-detect
|
|
1955
|
+
let userLocator;
|
|
1956
|
+
if (profile.usernameSelector) {
|
|
1957
|
+
userLocator = page.locator(profile.usernameSelector).first();
|
|
1958
|
+
}
|
|
1959
|
+
else {
|
|
1960
|
+
userLocator = null;
|
|
1961
|
+
for (const sel of AUTO_USER_SELECTORS) {
|
|
1962
|
+
const loc = page.locator(sel).first();
|
|
1963
|
+
if (await loc.isVisible({ timeout: 1000 }).catch(() => false)) {
|
|
1964
|
+
userLocator = loc;
|
|
1965
|
+
break;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
if (!userLocator) {
|
|
1969
|
+
return errorResponse(command.id, `Auth login failed for '${command.name}': could not find username field. ` +
|
|
1970
|
+
`Specify --username-selector with auth save.`);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
// Resolve submit button: custom selector or sequential auto-detect
|
|
1974
|
+
let submitLocator;
|
|
1975
|
+
if (profile.submitSelector) {
|
|
1976
|
+
submitLocator = page.locator(profile.submitSelector).first();
|
|
1977
|
+
}
|
|
1978
|
+
else {
|
|
1979
|
+
submitLocator = null;
|
|
1980
|
+
for (const sel of AUTO_SUBMIT_SELECTORS) {
|
|
1981
|
+
const loc = page.locator(sel).first();
|
|
1982
|
+
if (await loc.isVisible({ timeout: 1000 }).catch(() => false)) {
|
|
1983
|
+
submitLocator = loc;
|
|
1984
|
+
break;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
if (!submitLocator) {
|
|
1988
|
+
return errorResponse(command.id, `Auth login failed for '${command.name}': could not find submit button. ` +
|
|
1989
|
+
`Specify --submit-selector with auth save.`);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
await userLocator.fill(profile.username);
|
|
1993
|
+
await page.locator(passSel).first().fill(profile.password);
|
|
1994
|
+
await submitLocator.click();
|
|
1995
|
+
await page.waitForLoadState('load');
|
|
1996
|
+
}
|
|
1997
|
+
catch (err) {
|
|
1998
|
+
return errorResponse(command.id, `Auth login failed for '${command.name}': ${err instanceof Error ? err.message : err}. ` +
|
|
1999
|
+
`Try specifying custom selectors with auth save --username-selector/--password-selector/--submit-selector`);
|
|
2000
|
+
}
|
|
2001
|
+
updateLastLogin(command.name);
|
|
2002
|
+
return successResponse(command.id, {
|
|
2003
|
+
loggedIn: true,
|
|
2004
|
+
name: command.name,
|
|
2005
|
+
url: page.url(),
|
|
2006
|
+
title: await page.title(),
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
2009
|
+
async function handleConfirm(command, browser) {
|
|
2010
|
+
const entry = getAndRemovePending(command.confirmationId);
|
|
2011
|
+
if (!entry) {
|
|
2012
|
+
return errorResponse(command.id, `No pending confirmation with id '${command.confirmationId}'`);
|
|
2013
|
+
}
|
|
2014
|
+
// Re-validate the stored command through the schema to guard against
|
|
2015
|
+
// shape drift between when the confirmation was issued and now.
|
|
2016
|
+
const parseResult = parseCommand(JSON.stringify(entry.command));
|
|
2017
|
+
if (!parseResult.success) {
|
|
2018
|
+
return errorResponse(command.id, `Stored command is no longer valid: ${parseResult.error}`);
|
|
2019
|
+
}
|
|
2020
|
+
const originalCommand = parseResult.command;
|
|
2021
|
+
// Re-check deny list in case policy was updated since the confirmation was issued
|
|
2022
|
+
actionPolicy = reloadPolicyIfChanged();
|
|
2023
|
+
const decision = checkPolicy(originalCommand.action, actionPolicy, new Set());
|
|
2024
|
+
if (decision === 'deny') {
|
|
2025
|
+
const category = getActionCategory(originalCommand.action);
|
|
2026
|
+
return errorResponse(command.id, `Action denied by policy: '${category}' is not allowed`);
|
|
2027
|
+
}
|
|
2028
|
+
return await dispatchAction(originalCommand, browser);
|
|
2029
|
+
}
|
|
2030
|
+
function handleDeny(command) {
|
|
2031
|
+
const entry = getAndRemovePending(command.confirmationId);
|
|
2032
|
+
if (!entry) {
|
|
2033
|
+
return errorResponse(command.id, `No pending confirmation with id '${command.confirmationId}'`);
|
|
2034
|
+
}
|
|
2035
|
+
return successResponse(command.id, { denied: true });
|
|
2036
|
+
}
|
|
1862
2037
|
//# sourceMappingURL=actions.js.map
|