@agenticmail/enterprise 0.5.78 → 0.5.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-7RNT4O5T.js +15198 -0
- package/dist/chunk-AGFOJCSB.js +2191 -0
- package/dist/chunk-F4GSFCM3.js +898 -0
- package/dist/chunk-PZA7YOJE.js +898 -0
- package/dist/chunk-Q3V7VZFQ.js +2191 -0
- package/dist/chunk-RRFB6G6M.js +15198 -0
- package/dist/chunk-VX3VFMVB.js +409 -0
- package/dist/cli.js +1 -1
- package/dist/dashboard/pages/agent-detail.js +313 -1
- package/dist/index.js +4 -3
- package/dist/pw-ai-KPETTB25.js +2212 -0
- package/dist/routes-PDHMCIXU.js +6676 -0
- package/dist/runtime-7HW4GX5L.js +48 -0
- package/dist/runtime-XXDCZZIK.js +48 -0
- package/dist/server-FMP4BFGW.js +12 -0
- package/dist/server-JRHDUNII.js +12 -0
- package/dist/setup-O5FPRLK4.js +20 -0
- package/dist/setup-S4Z4PPIJ.js +20 -0
- package/package.json +15 -2
- package/src/agent-tools/common.ts +25 -0
- package/src/agent-tools/index.ts +3 -0
- package/src/agent-tools/schema/typebox.ts +25 -0
- package/src/agent-tools/tools/browser-tool.schema.ts +112 -0
- package/src/agent-tools/tools/browser-tool.ts +388 -0
- package/src/agent-tools/tools/gateway.ts +126 -0
- package/src/agent-tools/tools/nodes-utils.ts +80 -0
- package/src/browser/bridge-auth-registry.ts +34 -0
- package/src/browser/bridge-server.ts +93 -0
- package/src/browser/cdp.helpers.ts +180 -0
- package/src/browser/cdp.ts +466 -0
- package/src/browser/chrome.executables.ts +625 -0
- package/src/browser/chrome.profile-decoration.ts +198 -0
- package/src/browser/chrome.ts +349 -0
- package/src/browser/client-actions-core.ts +259 -0
- package/src/browser/client-actions-observe.ts +184 -0
- package/src/browser/client-actions-state.ts +284 -0
- package/src/browser/client-actions-types.ts +16 -0
- package/src/browser/client-actions-url.ts +11 -0
- package/src/browser/client-actions.ts +4 -0
- package/src/browser/client-fetch.ts +253 -0
- package/src/browser/client.ts +337 -0
- package/src/browser/config.ts +296 -0
- package/src/browser/constants.ts +8 -0
- package/src/browser/control-auth.ts +94 -0
- package/src/browser/control-service.ts +81 -0
- package/src/browser/csrf.ts +87 -0
- package/src/browser/enterprise-compat.ts +518 -0
- package/src/browser/extension-relay.ts +834 -0
- package/src/browser/http-auth.ts +63 -0
- package/src/browser/navigation-guard.ts +50 -0
- package/src/browser/paths.ts +49 -0
- package/src/browser/profiles-service.ts +187 -0
- package/src/browser/profiles.ts +113 -0
- package/src/browser/proxy-files.ts +41 -0
- package/src/browser/pw-ai-module.ts +52 -0
- package/src/browser/pw-ai-state.ts +9 -0
- package/src/browser/pw-ai.ts +65 -0
- package/src/browser/pw-role-snapshot.ts +434 -0
- package/src/browser/pw-session.ts +810 -0
- package/src/browser/pw-tools-core.activity.ts +68 -0
- package/src/browser/pw-tools-core.downloads.ts +281 -0
- package/src/browser/pw-tools-core.interactions.ts +646 -0
- package/src/browser/pw-tools-core.responses.ts +124 -0
- package/src/browser/pw-tools-core.shared.ts +70 -0
- package/src/browser/pw-tools-core.snapshot.ts +213 -0
- package/src/browser/pw-tools-core.state.ts +209 -0
- package/src/browser/pw-tools-core.storage.ts +128 -0
- package/src/browser/pw-tools-core.trace.ts +37 -0
- package/src/browser/pw-tools-core.ts +8 -0
- package/src/browser/resolved-config-refresh.ts +59 -0
- package/src/browser/routes/agent.act.shared.ts +52 -0
- package/src/browser/routes/agent.act.ts +575 -0
- package/src/browser/routes/agent.debug.ts +149 -0
- package/src/browser/routes/agent.shared.ts +143 -0
- package/src/browser/routes/agent.snapshot.ts +333 -0
- package/src/browser/routes/agent.storage.ts +451 -0
- package/src/browser/routes/agent.ts +13 -0
- package/src/browser/routes/basic.ts +202 -0
- package/src/browser/routes/dispatcher.ts +126 -0
- package/src/browser/routes/index.ts +11 -0
- package/src/browser/routes/path-output.ts +1 -0
- package/src/browser/routes/tabs.ts +217 -0
- package/src/browser/routes/types.ts +26 -0
- package/src/browser/routes/utils.ts +73 -0
- package/src/browser/screenshot.ts +54 -0
- package/src/browser/server-context.ts +688 -0
- package/src/browser/server-context.types.ts +65 -0
- package/src/browser/server-lifecycle.ts +48 -0
- package/src/browser/server-middleware.ts +37 -0
- package/src/browser/server.ts +110 -0
- package/src/browser/target-id.ts +30 -0
- package/src/browser/trash.ts +21 -0
- package/src/dashboard/pages/agent-detail.js +313 -1
- package/src/engine/agent-routes.ts +46 -0
- package/src/security/external-content.ts +299 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BrowserConsoleMessage,
|
|
3
|
+
BrowserNetworkRequest,
|
|
4
|
+
BrowserPageError,
|
|
5
|
+
} from "./pw-session.js";
|
|
6
|
+
import { ensurePageState, getPageForTargetId } from "./pw-session.js";
|
|
7
|
+
|
|
8
|
+
export async function getPageErrorsViaPlaywright(opts: {
|
|
9
|
+
cdpUrl: string;
|
|
10
|
+
targetId?: string;
|
|
11
|
+
clear?: boolean;
|
|
12
|
+
}): Promise<{ errors: BrowserPageError[] }> {
|
|
13
|
+
const page = await getPageForTargetId(opts);
|
|
14
|
+
const state = ensurePageState(page);
|
|
15
|
+
const errors = [...state.errors];
|
|
16
|
+
if (opts.clear) {
|
|
17
|
+
state.errors = [];
|
|
18
|
+
}
|
|
19
|
+
return { errors };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function getNetworkRequestsViaPlaywright(opts: {
|
|
23
|
+
cdpUrl: string;
|
|
24
|
+
targetId?: string;
|
|
25
|
+
filter?: string;
|
|
26
|
+
clear?: boolean;
|
|
27
|
+
}): Promise<{ requests: BrowserNetworkRequest[] }> {
|
|
28
|
+
const page = await getPageForTargetId(opts);
|
|
29
|
+
const state = ensurePageState(page);
|
|
30
|
+
const raw = [...state.requests];
|
|
31
|
+
const filter = typeof opts.filter === "string" ? opts.filter.trim() : "";
|
|
32
|
+
const requests = filter ? raw.filter((r) => r.url.includes(filter)) : raw;
|
|
33
|
+
if (opts.clear) {
|
|
34
|
+
state.requests = [];
|
|
35
|
+
state.requestIds = new WeakMap();
|
|
36
|
+
}
|
|
37
|
+
return { requests };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function consolePriority(level: string) {
|
|
41
|
+
switch (level) {
|
|
42
|
+
case "error":
|
|
43
|
+
return 3;
|
|
44
|
+
case "warning":
|
|
45
|
+
return 2;
|
|
46
|
+
case "info":
|
|
47
|
+
case "log":
|
|
48
|
+
return 1;
|
|
49
|
+
case "debug":
|
|
50
|
+
return 0;
|
|
51
|
+
default:
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function getConsoleMessagesViaPlaywright(opts: {
|
|
57
|
+
cdpUrl: string;
|
|
58
|
+
targetId?: string;
|
|
59
|
+
level?: string;
|
|
60
|
+
}): Promise<BrowserConsoleMessage[]> {
|
|
61
|
+
const page = await getPageForTargetId(opts);
|
|
62
|
+
const state = ensurePageState(page);
|
|
63
|
+
if (!opts.level) {
|
|
64
|
+
return [...state.console];
|
|
65
|
+
}
|
|
66
|
+
const min = consolePriority(opts.level);
|
|
67
|
+
return state.console.filter((msg) => consolePriority(msg.type) >= min);
|
|
68
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { Page } from "playwright-core";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ensurePageState,
|
|
8
|
+
getPageForTargetId,
|
|
9
|
+
refLocator,
|
|
10
|
+
restoreRoleRefsForTarget,
|
|
11
|
+
} from "./pw-session.js";
|
|
12
|
+
import { resolvePreferredOpenClawTmpDir } from "./enterprise-compat.js";
|
|
13
|
+
import {
|
|
14
|
+
bumpDialogArmId,
|
|
15
|
+
bumpDownloadArmId,
|
|
16
|
+
bumpUploadArmId,
|
|
17
|
+
normalizeTimeoutMs,
|
|
18
|
+
requireRef,
|
|
19
|
+
toAIFriendlyError,
|
|
20
|
+
} from "./pw-tools-core.shared.js";
|
|
21
|
+
|
|
22
|
+
function sanitizeDownloadFileName(fileName: string): string {
|
|
23
|
+
const trimmed = String(fileName ?? "").trim();
|
|
24
|
+
if (!trimmed) {
|
|
25
|
+
return "download.bin";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// `suggestedFilename()` is untrusted (influenced by remote servers). Force a basename so
|
|
29
|
+
// path separators/traversal can't escape the downloads dir on any platform.
|
|
30
|
+
let base = path.posix.basename(trimmed);
|
|
31
|
+
base = path.win32.basename(base);
|
|
32
|
+
let cleaned = "";
|
|
33
|
+
for (let i = 0; i < base.length; i++) {
|
|
34
|
+
const code = base.charCodeAt(i);
|
|
35
|
+
if (code < 0x20 || code === 0x7f) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
cleaned += base[i];
|
|
39
|
+
}
|
|
40
|
+
base = cleaned.trim();
|
|
41
|
+
|
|
42
|
+
if (!base || base === "." || base === "..") {
|
|
43
|
+
return "download.bin";
|
|
44
|
+
}
|
|
45
|
+
if (base.length > 200) {
|
|
46
|
+
base = base.slice(0, 200);
|
|
47
|
+
}
|
|
48
|
+
return base;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildTempDownloadPath(fileName: string): string {
|
|
52
|
+
const id = crypto.randomUUID();
|
|
53
|
+
const safeName = sanitizeDownloadFileName(fileName);
|
|
54
|
+
return path.join(resolvePreferredOpenClawTmpDir(), "downloads", `${id}-${safeName}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function createPageDownloadWaiter(page: Page, timeoutMs: number) {
|
|
58
|
+
let done = false;
|
|
59
|
+
let timer: NodeJS.Timeout | undefined;
|
|
60
|
+
let handler: ((download: unknown) => void) | undefined;
|
|
61
|
+
|
|
62
|
+
const cleanup = () => {
|
|
63
|
+
if (timer) {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
}
|
|
66
|
+
timer = undefined;
|
|
67
|
+
if (handler) {
|
|
68
|
+
page.off("download", handler as never);
|
|
69
|
+
handler = undefined;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const promise = new Promise<unknown>((resolve, reject) => {
|
|
74
|
+
handler = (download: unknown) => {
|
|
75
|
+
if (done) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
done = true;
|
|
79
|
+
cleanup();
|
|
80
|
+
resolve(download);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
page.on("download", handler as never);
|
|
84
|
+
timer = setTimeout(() => {
|
|
85
|
+
if (done) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
done = true;
|
|
89
|
+
cleanup();
|
|
90
|
+
reject(new Error("Timeout waiting for download"));
|
|
91
|
+
}, timeoutMs);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
promise,
|
|
96
|
+
cancel: () => {
|
|
97
|
+
if (done) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
done = true;
|
|
101
|
+
cleanup();
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type DownloadPayload = {
|
|
107
|
+
url?: () => string;
|
|
108
|
+
suggestedFilename?: () => string;
|
|
109
|
+
saveAs?: (outPath: string) => Promise<void>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
async function saveDownloadPayload(download: DownloadPayload, outPath: string) {
|
|
113
|
+
const suggested = download.suggestedFilename?.() || "download.bin";
|
|
114
|
+
const resolvedOutPath = outPath?.trim() || buildTempDownloadPath(suggested);
|
|
115
|
+
await fs.mkdir(path.dirname(resolvedOutPath), { recursive: true });
|
|
116
|
+
await download.saveAs?.(resolvedOutPath);
|
|
117
|
+
return {
|
|
118
|
+
url: download.url?.() || "",
|
|
119
|
+
suggestedFilename: suggested,
|
|
120
|
+
path: path.resolve(resolvedOutPath),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function awaitDownloadPayload(params: {
|
|
125
|
+
waiter: ReturnType<typeof createPageDownloadWaiter>;
|
|
126
|
+
state: ReturnType<typeof ensurePageState>;
|
|
127
|
+
armId: number;
|
|
128
|
+
outPath?: string;
|
|
129
|
+
}) {
|
|
130
|
+
try {
|
|
131
|
+
const download = (await params.waiter.promise) as DownloadPayload;
|
|
132
|
+
if (params.state.armIdDownload !== params.armId) {
|
|
133
|
+
throw new Error("Download was superseded by another waiter");
|
|
134
|
+
}
|
|
135
|
+
return await saveDownloadPayload(download, params.outPath ?? "");
|
|
136
|
+
} catch (err) {
|
|
137
|
+
params.waiter.cancel();
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function armFileUploadViaPlaywright(opts: {
|
|
143
|
+
cdpUrl: string;
|
|
144
|
+
targetId?: string;
|
|
145
|
+
paths?: string[];
|
|
146
|
+
timeoutMs?: number;
|
|
147
|
+
}): Promise<void> {
|
|
148
|
+
const page = await getPageForTargetId(opts);
|
|
149
|
+
const state = ensurePageState(page);
|
|
150
|
+
const timeout = Math.max(500, Math.min(120_000, opts.timeoutMs ?? 120_000));
|
|
151
|
+
|
|
152
|
+
state.armIdUpload = bumpUploadArmId();
|
|
153
|
+
const armId = state.armIdUpload;
|
|
154
|
+
|
|
155
|
+
void page
|
|
156
|
+
.waitForEvent("filechooser", { timeout })
|
|
157
|
+
.then(async (fileChooser) => {
|
|
158
|
+
if (state.armIdUpload !== armId) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!opts.paths?.length) {
|
|
162
|
+
// Playwright removed `FileChooser.cancel()`; best-effort close the chooser instead.
|
|
163
|
+
try {
|
|
164
|
+
await page.keyboard.press("Escape");
|
|
165
|
+
} catch {
|
|
166
|
+
// Best-effort.
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
await fileChooser.setFiles(opts.paths);
|
|
171
|
+
try {
|
|
172
|
+
const input =
|
|
173
|
+
typeof fileChooser.element === "function"
|
|
174
|
+
? await Promise.resolve(fileChooser.element())
|
|
175
|
+
: null;
|
|
176
|
+
if (input) {
|
|
177
|
+
await input.evaluate((el) => {
|
|
178
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
179
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
// Best-effort for sites that don't react to setFiles alone.
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
.catch(() => {
|
|
187
|
+
// Ignore timeouts; the chooser may never appear.
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function armDialogViaPlaywright(opts: {
|
|
192
|
+
cdpUrl: string;
|
|
193
|
+
targetId?: string;
|
|
194
|
+
accept: boolean;
|
|
195
|
+
promptText?: string;
|
|
196
|
+
timeoutMs?: number;
|
|
197
|
+
}): Promise<void> {
|
|
198
|
+
const page = await getPageForTargetId(opts);
|
|
199
|
+
const state = ensurePageState(page);
|
|
200
|
+
const timeout = normalizeTimeoutMs(opts.timeoutMs, 120_000);
|
|
201
|
+
|
|
202
|
+
state.armIdDialog = bumpDialogArmId();
|
|
203
|
+
const armId = state.armIdDialog;
|
|
204
|
+
|
|
205
|
+
void page
|
|
206
|
+
.waitForEvent("dialog", { timeout })
|
|
207
|
+
.then(async (dialog) => {
|
|
208
|
+
if (state.armIdDialog !== armId) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (opts.accept) {
|
|
212
|
+
await dialog.accept(opts.promptText);
|
|
213
|
+
} else {
|
|
214
|
+
await dialog.dismiss();
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
.catch(() => {
|
|
218
|
+
// Ignore timeouts; the dialog may never appear.
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function waitForDownloadViaPlaywright(opts: {
|
|
223
|
+
cdpUrl: string;
|
|
224
|
+
targetId?: string;
|
|
225
|
+
path?: string;
|
|
226
|
+
timeoutMs?: number;
|
|
227
|
+
}): Promise<{
|
|
228
|
+
url: string;
|
|
229
|
+
suggestedFilename: string;
|
|
230
|
+
path: string;
|
|
231
|
+
}> {
|
|
232
|
+
const page = await getPageForTargetId(opts);
|
|
233
|
+
const state = ensurePageState(page);
|
|
234
|
+
const timeout = normalizeTimeoutMs(opts.timeoutMs, 120_000);
|
|
235
|
+
|
|
236
|
+
state.armIdDownload = bumpDownloadArmId();
|
|
237
|
+
const armId = state.armIdDownload;
|
|
238
|
+
|
|
239
|
+
const waiter = createPageDownloadWaiter(page, timeout);
|
|
240
|
+
return await awaitDownloadPayload({ waiter, state, armId, outPath: opts.path });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export async function downloadViaPlaywright(opts: {
|
|
244
|
+
cdpUrl: string;
|
|
245
|
+
targetId?: string;
|
|
246
|
+
ref: string;
|
|
247
|
+
path: string;
|
|
248
|
+
timeoutMs?: number;
|
|
249
|
+
}): Promise<{
|
|
250
|
+
url: string;
|
|
251
|
+
suggestedFilename: string;
|
|
252
|
+
path: string;
|
|
253
|
+
}> {
|
|
254
|
+
const page = await getPageForTargetId(opts);
|
|
255
|
+
const state = ensurePageState(page);
|
|
256
|
+
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
257
|
+
const timeout = normalizeTimeoutMs(opts.timeoutMs, 120_000);
|
|
258
|
+
|
|
259
|
+
const ref = requireRef(opts.ref);
|
|
260
|
+
const outPath = String(opts.path ?? "").trim();
|
|
261
|
+
if (!outPath) {
|
|
262
|
+
throw new Error("path is required");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
state.armIdDownload = bumpDownloadArmId();
|
|
266
|
+
const armId = state.armIdDownload;
|
|
267
|
+
|
|
268
|
+
const waiter = createPageDownloadWaiter(page, timeout);
|
|
269
|
+
try {
|
|
270
|
+
const locator = refLocator(page, ref);
|
|
271
|
+
try {
|
|
272
|
+
await locator.click({ timeout });
|
|
273
|
+
} catch (err) {
|
|
274
|
+
throw toAIFriendlyError(err, ref);
|
|
275
|
+
}
|
|
276
|
+
return await awaitDownloadPayload({ waiter, state, armId, outPath });
|
|
277
|
+
} catch (err) {
|
|
278
|
+
waiter.cancel();
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
}
|