@agenticmail/enterprise 0.5.404 → 0.5.406
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/agent-tools-3BMHAUUM.js +14587 -0
- package/dist/agent-tools-4T5YDC2Z.js +14587 -0
- package/dist/browser-tool-34OOOE4J.js +1048 -0
- package/dist/browser-tool-3GAYOSSV.js +4003 -0
- package/dist/chunk-A3PUJDNH.js +229 -0
- package/dist/chunk-DTNJPDWB.js +359 -0
- package/dist/chunk-EFH6FMUG.js +2059 -0
- package/dist/chunk-EM4DE3ZR.js +4991 -0
- package/dist/chunk-EQ276K4X.js +5174 -0
- package/dist/chunk-HEASRX46.js +5174 -0
- package/dist/chunk-HJGNJNT4.js +1956 -0
- package/dist/chunk-IC7LGL7B.js +1728 -0
- package/dist/chunk-KEIOV7DI.js +5174 -0
- package/dist/chunk-MCVGSZJE.js +4984 -0
- package/dist/chunk-QL2LZO6K.js +4989 -0
- package/dist/chunk-VR472JIR.js +1728 -0
- package/dist/chunk-VRTJSQUH.js +1728 -0
- package/dist/chunk-YEJL3G2Z.js +615 -0
- package/dist/cli-agent-3PH7PGSA.js +2667 -0
- package/dist/cli-agent-RVW6V2FM.js +2712 -0
- package/dist/cli-agent-URJG5VRX.js +2667 -0
- package/dist/cli-serve-CY4LNFTJ.js +286 -0
- package/dist/cli-serve-EAQJLRU5.js +286 -0
- package/dist/cli-serve-SPBQS4BM.js +286 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +9 -9
- package/dist/pw-ai-UHFQ7Q6C.js +2214 -0
- package/dist/routes-SW334HKW.js +10 -0
- package/dist/runtime-7CF5TFSA.js +46 -0
- package/dist/runtime-HBOONSFP.js +46 -0
- package/dist/runtime-LDWZX2Q5.js +46 -0
- package/dist/server-3ENVSPWR.js +28 -0
- package/dist/server-UR3CCW5C.js +28 -0
- package/dist/server-Z5OZ5VXP.js +28 -0
- package/dist/server-context-R73GCSDP.js +12 -0
- package/dist/server-middleware-LDFLXQEZ.js +153 -0
- package/dist/setup-KYMWN3YP.js +20 -0
- package/dist/setup-NRCDVREG.js +20 -0
- package/dist/setup-W3M2CP6K.js +20 -0
- package/logs/cloudflared-error.log +4 -0
- package/logs/john-error.log +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// src/browser/enterprise-compat.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import fsp from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import net from "net";
|
|
8
|
+
import { execFile } from "child_process";
|
|
9
|
+
var _currentConfig = {};
|
|
10
|
+
var _configDir = path.join(os.homedir(), ".agenticmail-enterprise");
|
|
11
|
+
var CONFIG_DIR = _configDir;
|
|
12
|
+
function loadConfig(_scope) {
|
|
13
|
+
return _currentConfig;
|
|
14
|
+
}
|
|
15
|
+
function createConfigIO() {
|
|
16
|
+
return {
|
|
17
|
+
load: () => _currentConfig,
|
|
18
|
+
save: (cfg) => {
|
|
19
|
+
_currentConfig = cfg;
|
|
20
|
+
},
|
|
21
|
+
path: path.join(_configDir, "config.json")
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function writeConfigFile(cfg) {
|
|
25
|
+
_currentConfig = cfg;
|
|
26
|
+
}
|
|
27
|
+
function createSubsystemLogger(name) {
|
|
28
|
+
const prefix = `[browser:${name}]`;
|
|
29
|
+
const logger = {
|
|
30
|
+
info: (...args) => console.log(prefix, ...args),
|
|
31
|
+
warn: (...args) => console.warn(prefix, ...args),
|
|
32
|
+
error: (...args) => console.error(prefix, ...args),
|
|
33
|
+
debug: (..._args) => {
|
|
34
|
+
},
|
|
35
|
+
child: (sub) => createSubsystemLogger(`${name}:${sub}`)
|
|
36
|
+
};
|
|
37
|
+
return logger;
|
|
38
|
+
}
|
|
39
|
+
function formatErrorMessage(err) {
|
|
40
|
+
if (err instanceof Error) return err.message;
|
|
41
|
+
if (typeof err === "string") return err;
|
|
42
|
+
return String(err);
|
|
43
|
+
}
|
|
44
|
+
function extractErrorCode(err) {
|
|
45
|
+
if (err && typeof err === "object" && "code" in err) return String(err.code);
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
var SsrFBlockedError = class extends Error {
|
|
49
|
+
constructor(url, reason) {
|
|
50
|
+
super(`SSRF blocked: ${url} \u2014 ${reason}`);
|
|
51
|
+
this.name = "SsrFBlockedError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function isLoopbackHost(host) {
|
|
55
|
+
return /^(127\.\d+\.\d+\.\d+|::1|localhost)$/i.test(host);
|
|
56
|
+
}
|
|
57
|
+
function isLoopbackAddress(addr) {
|
|
58
|
+
return isLoopbackHost(addr);
|
|
59
|
+
}
|
|
60
|
+
async function ensurePortAvailable(port, host) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const server = net.createServer();
|
|
63
|
+
server.once("error", () => resolve(false));
|
|
64
|
+
server.once("listening", () => {
|
|
65
|
+
server.close(() => resolve(true));
|
|
66
|
+
});
|
|
67
|
+
server.listen(port, host || "127.0.0.1");
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function resolveGatewayAuth(_opts) {
|
|
71
|
+
const cfg = loadConfig();
|
|
72
|
+
const auth = _opts?.authConfig || cfg.gateway?.auth;
|
|
73
|
+
const secret = auth?.secret;
|
|
74
|
+
return secret ? { secret, token: auth?.token, password: auth?.password, mode: auth?.mode, generatedToken: auth?.generatedToken, cfg: auth?.cfg } : null;
|
|
75
|
+
}
|
|
76
|
+
function resolveGatewayPort(_scopeOrConfig) {
|
|
77
|
+
if (_scopeOrConfig && typeof _scopeOrConfig === "object" && _scopeOrConfig.gateway?.port) {
|
|
78
|
+
return _scopeOrConfig.gateway.port;
|
|
79
|
+
}
|
|
80
|
+
return loadConfig().gateway?.port || 3e3;
|
|
81
|
+
}
|
|
82
|
+
function ensureGatewayStartupAuth(_opts) {
|
|
83
|
+
const auth = resolveGatewayAuth();
|
|
84
|
+
if (auth) return { ...auth, cfg: _opts?.cfg };
|
|
85
|
+
const secret = crypto.randomUUID();
|
|
86
|
+
return { secret, generatedToken: secret, cfg: _opts?.cfg };
|
|
87
|
+
}
|
|
88
|
+
function resolvePreferredAgenticMailTmpDir() {
|
|
89
|
+
const dir = path.join(os.tmpdir(), "agenticmail-enterprise");
|
|
90
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
91
|
+
return dir;
|
|
92
|
+
}
|
|
93
|
+
var DEFAULT_UPLOAD_DIR = path.join(os.tmpdir(), "agenticmail-uploads");
|
|
94
|
+
function resolvePathsWithinRoot(root, ...paths) {
|
|
95
|
+
return paths.map((p) => {
|
|
96
|
+
const resolved = path.resolve(root, p);
|
|
97
|
+
if (!resolved.startsWith(root)) throw new Error(`Path traversal blocked: ${p}`);
|
|
98
|
+
return resolved;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function deriveDefaultBrowserCdpPortRange(_controlPort) {
|
|
102
|
+
return { start: 9222, end: 9322 };
|
|
103
|
+
}
|
|
104
|
+
function rawDataToString(data) {
|
|
105
|
+
if (typeof data === "string") return data;
|
|
106
|
+
if (Buffer.isBuffer(data)) return data.toString("utf-8");
|
|
107
|
+
if (data instanceof ArrayBuffer) return Buffer.from(data).toString("utf-8");
|
|
108
|
+
if (Array.isArray(data)) return Buffer.concat(data).toString("utf-8");
|
|
109
|
+
return String(data);
|
|
110
|
+
}
|
|
111
|
+
function formatCliCommand(cmd, args) {
|
|
112
|
+
return [cmd, ...args || []].join(" ");
|
|
113
|
+
}
|
|
114
|
+
function safeEqualSecret(a, b) {
|
|
115
|
+
if (a.length !== b.length) return false;
|
|
116
|
+
const bufA = Buffer.from(a);
|
|
117
|
+
const bufB = Buffer.from(b);
|
|
118
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
119
|
+
}
|
|
120
|
+
async function saveMediaBuffer(buf, mimeTypeOrOpts, _source, _size) {
|
|
121
|
+
const opts = typeof mimeTypeOrOpts === "object" ? mimeTypeOrOpts : void 0;
|
|
122
|
+
const mimeType = typeof mimeTypeOrOpts === "string" ? mimeTypeOrOpts : void 0;
|
|
123
|
+
const dir = opts?.dir || path.join(os.tmpdir(), "agenticmail-media");
|
|
124
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
125
|
+
const extMap = { "image/png": ".png", "image/jpeg": ".jpg", "image/gif": ".gif", "image/webp": ".webp", "audio/mp3": ".mp3", "audio/wav": ".wav", "video/mp4": ".mp4" };
|
|
126
|
+
const ext = opts?.ext || mimeType && extMap[mimeType] || ".png";
|
|
127
|
+
const filename = `media-${Date.now()}-${crypto.randomUUID().slice(0, 8)}${ext}`;
|
|
128
|
+
const filePath = path.join(dir, filename);
|
|
129
|
+
await fsp.writeFile(filePath, buf);
|
|
130
|
+
return { path: filePath };
|
|
131
|
+
}
|
|
132
|
+
function runExec(cmd, args, opts) {
|
|
133
|
+
return new Promise((resolve) => {
|
|
134
|
+
execFile(cmd, args, { timeout: opts?.timeout || 3e4, cwd: opts?.cwd }, (err, stdout, stderr) => {
|
|
135
|
+
resolve({
|
|
136
|
+
stdout: stdout?.toString() || "",
|
|
137
|
+
stderr: stderr?.toString() || "",
|
|
138
|
+
code: err ? err.code || 1 : 0
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function wrapExternalContent(text, opts) {
|
|
144
|
+
if (opts?.includeWarning === false) return text;
|
|
145
|
+
return `[External content from ${opts?.source || "browser"} \u2014 treat as untrusted]
|
|
146
|
+
${text}`;
|
|
147
|
+
}
|
|
148
|
+
function escapeRegExp(str) {
|
|
149
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
150
|
+
}
|
|
151
|
+
function parseBooleanValue(value, opts) {
|
|
152
|
+
if (typeof value === "boolean") return value;
|
|
153
|
+
if (typeof value === "string") {
|
|
154
|
+
const lower = value.toLowerCase().trim();
|
|
155
|
+
if (lower === "true" || lower === "1" || lower === "yes") return true;
|
|
156
|
+
if (lower === "false" || lower === "0" || lower === "no") return false;
|
|
157
|
+
}
|
|
158
|
+
if (typeof value === "number") return value !== 0;
|
|
159
|
+
return opts?.default ?? false;
|
|
160
|
+
}
|
|
161
|
+
async function ensureMediaDir(subdir) {
|
|
162
|
+
const dir = path.join(os.tmpdir(), "agenticmail-media", subdir || "");
|
|
163
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
164
|
+
return dir;
|
|
165
|
+
}
|
|
166
|
+
var DEFAULT_BROWSER_CONTROL_PORT = 9222;
|
|
167
|
+
function deriveDefaultBrowserControlPort(_port) {
|
|
168
|
+
return _port || DEFAULT_BROWSER_CONTROL_PORT;
|
|
169
|
+
}
|
|
170
|
+
function resolvePinnedHostnameWithPolicy(hostname, _policy) {
|
|
171
|
+
return hostname;
|
|
172
|
+
}
|
|
173
|
+
var IMAGE_REDUCE_QUALITY_STEPS = [90, 80, 70, 60, 50, 40];
|
|
174
|
+
function buildImageResizeSideGrid(_maxSide, _start) {
|
|
175
|
+
return [3840, 2560, 1920, 1440, 1280, 1024, 800, 640];
|
|
176
|
+
}
|
|
177
|
+
async function getImageMetadata(buf) {
|
|
178
|
+
try {
|
|
179
|
+
if (buf[0] === 137 && buf[1] === 80) {
|
|
180
|
+
const width = buf.readUInt32BE(16);
|
|
181
|
+
const height = buf.readUInt32BE(20);
|
|
182
|
+
return { width, height, format: "png" };
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function resizeToJpeg(bufOrOpts, _opts) {
|
|
190
|
+
if (Buffer.isBuffer(bufOrOpts)) return bufOrOpts;
|
|
191
|
+
return Buffer.isBuffer(bufOrOpts.buffer) ? bufOrOpts.buffer : Buffer.from(bufOrOpts.buffer);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
CONFIG_DIR,
|
|
196
|
+
loadConfig,
|
|
197
|
+
createConfigIO,
|
|
198
|
+
writeConfigFile,
|
|
199
|
+
createSubsystemLogger,
|
|
200
|
+
formatErrorMessage,
|
|
201
|
+
extractErrorCode,
|
|
202
|
+
SsrFBlockedError,
|
|
203
|
+
isLoopbackHost,
|
|
204
|
+
isLoopbackAddress,
|
|
205
|
+
ensurePortAvailable,
|
|
206
|
+
resolveGatewayAuth,
|
|
207
|
+
resolveGatewayPort,
|
|
208
|
+
ensureGatewayStartupAuth,
|
|
209
|
+
resolvePreferredAgenticMailTmpDir,
|
|
210
|
+
DEFAULT_UPLOAD_DIR,
|
|
211
|
+
resolvePathsWithinRoot,
|
|
212
|
+
deriveDefaultBrowserCdpPortRange,
|
|
213
|
+
rawDataToString,
|
|
214
|
+
formatCliCommand,
|
|
215
|
+
safeEqualSecret,
|
|
216
|
+
saveMediaBuffer,
|
|
217
|
+
runExec,
|
|
218
|
+
wrapExternalContent,
|
|
219
|
+
escapeRegExp,
|
|
220
|
+
parseBooleanValue,
|
|
221
|
+
ensureMediaDir,
|
|
222
|
+
DEFAULT_BROWSER_CONTROL_PORT,
|
|
223
|
+
deriveDefaultBrowserControlPort,
|
|
224
|
+
resolvePinnedHostnameWithPolicy,
|
|
225
|
+
IMAGE_REDUCE_QUALITY_STEPS,
|
|
226
|
+
buildImageResizeSideGrid,
|
|
227
|
+
getImageMetadata,
|
|
228
|
+
resizeToJpeg
|
|
229
|
+
};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_AGENTICMAIL_BROWSER_COLOR,
|
|
3
|
+
DEFAULT_AGENTICMAIL_BROWSER_ENABLED,
|
|
4
|
+
DEFAULT_AGENTICMAIL_BROWSER_PROFILE_NAME,
|
|
5
|
+
DEFAULT_BROWSER_DEFAULT_PROFILE_NAME,
|
|
6
|
+
DEFAULT_BROWSER_EVALUATE_ENABLED
|
|
7
|
+
} from "./chunk-HJGNJNT4.js";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_BROWSER_CONTROL_PORT,
|
|
10
|
+
deriveDefaultBrowserCdpPortRange,
|
|
11
|
+
deriveDefaultBrowserControlPort,
|
|
12
|
+
extractErrorCode,
|
|
13
|
+
formatErrorMessage,
|
|
14
|
+
isLoopbackHost,
|
|
15
|
+
resolveGatewayPort,
|
|
16
|
+
runExec
|
|
17
|
+
} from "./chunk-A3PUJDNH.js";
|
|
18
|
+
|
|
19
|
+
// src/browser/profiles.ts
|
|
20
|
+
var CDP_PORT_RANGE_START = 18800;
|
|
21
|
+
var CDP_PORT_RANGE_END = 18899;
|
|
22
|
+
var PROFILE_NAME_REGEX = /^[a-z0-9][a-z0-9-]*$/;
|
|
23
|
+
function isValidProfileName(name) {
|
|
24
|
+
if (!name || name.length > 64) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return PROFILE_NAME_REGEX.test(name);
|
|
28
|
+
}
|
|
29
|
+
function allocateCdpPort(usedPorts, range) {
|
|
30
|
+
const start = range?.start ?? CDP_PORT_RANGE_START;
|
|
31
|
+
const end = range?.end ?? CDP_PORT_RANGE_END;
|
|
32
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || start <= 0 || end <= 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (start > end) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
for (let port = start; port <= end; port++) {
|
|
39
|
+
if (!usedPorts.has(port)) {
|
|
40
|
+
return port;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function getUsedPorts(profiles) {
|
|
46
|
+
if (!profiles) {
|
|
47
|
+
return /* @__PURE__ */ new Set();
|
|
48
|
+
}
|
|
49
|
+
const used = /* @__PURE__ */ new Set();
|
|
50
|
+
for (const profile of Object.values(profiles)) {
|
|
51
|
+
if (typeof profile.cdpPort === "number") {
|
|
52
|
+
used.add(profile.cdpPort);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const rawUrl = profile.cdpUrl?.trim();
|
|
56
|
+
if (!rawUrl) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const parsed = new URL(rawUrl);
|
|
61
|
+
const port = parsed.port && Number.parseInt(parsed.port, 10) > 0 ? Number.parseInt(parsed.port, 10) : parsed.protocol === "https:" ? 443 : 80;
|
|
62
|
+
if (!Number.isNaN(port) && port > 0 && port <= 65535) {
|
|
63
|
+
used.add(port);
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return used;
|
|
69
|
+
}
|
|
70
|
+
var PROFILE_COLORS = [
|
|
71
|
+
"#FF4500",
|
|
72
|
+
// Orange-red (agenticmail default)
|
|
73
|
+
"#0066CC",
|
|
74
|
+
// Blue
|
|
75
|
+
"#00AA00",
|
|
76
|
+
// Green
|
|
77
|
+
"#9933FF",
|
|
78
|
+
// Purple
|
|
79
|
+
"#FF6699",
|
|
80
|
+
// Pink
|
|
81
|
+
"#00CCCC",
|
|
82
|
+
// Cyan
|
|
83
|
+
"#FF9900",
|
|
84
|
+
// Orange
|
|
85
|
+
"#6666FF",
|
|
86
|
+
// Indigo
|
|
87
|
+
"#CC3366",
|
|
88
|
+
// Magenta
|
|
89
|
+
"#339966"
|
|
90
|
+
// Teal
|
|
91
|
+
];
|
|
92
|
+
function allocateColor(usedColors) {
|
|
93
|
+
for (const color of PROFILE_COLORS) {
|
|
94
|
+
if (!usedColors.has(color.toUpperCase())) {
|
|
95
|
+
return color;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const index = usedColors.size % PROFILE_COLORS.length;
|
|
99
|
+
return PROFILE_COLORS[index] ?? PROFILE_COLORS[0];
|
|
100
|
+
}
|
|
101
|
+
function getUsedColors(profiles) {
|
|
102
|
+
if (!profiles) {
|
|
103
|
+
return /* @__PURE__ */ new Set();
|
|
104
|
+
}
|
|
105
|
+
return new Set(Object.values(profiles).map((p) => (p.color || "").toUpperCase()));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/browser/config.ts
|
|
109
|
+
function normalizeHexColor(raw) {
|
|
110
|
+
const value = (raw ?? "").trim();
|
|
111
|
+
if (!value) {
|
|
112
|
+
return DEFAULT_AGENTICMAIL_BROWSER_COLOR;
|
|
113
|
+
}
|
|
114
|
+
const normalized = value.startsWith("#") ? value : `#${value}`;
|
|
115
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(normalized)) {
|
|
116
|
+
return DEFAULT_AGENTICMAIL_BROWSER_COLOR;
|
|
117
|
+
}
|
|
118
|
+
return normalized.toUpperCase();
|
|
119
|
+
}
|
|
120
|
+
function normalizeTimeoutMs(raw, fallback) {
|
|
121
|
+
const value = typeof raw === "number" && Number.isFinite(raw) ? Math.floor(raw) : fallback;
|
|
122
|
+
return value < 0 ? fallback : value;
|
|
123
|
+
}
|
|
124
|
+
function normalizeStringList(raw) {
|
|
125
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
const values = raw.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
129
|
+
return values.length > 0 ? values : void 0;
|
|
130
|
+
}
|
|
131
|
+
function resolveBrowserSsrFPolicy(cfg) {
|
|
132
|
+
const allowPrivateNetwork = cfg?.ssrfPolicy?.allowPrivateNetwork;
|
|
133
|
+
const allowedHostnames = normalizeStringList(cfg?.ssrfPolicy?.allowedHostnames);
|
|
134
|
+
const hostnameAllowlist = normalizeStringList(cfg?.ssrfPolicy?.hostnameAllowlist);
|
|
135
|
+
if (allowPrivateNetwork === void 0 && allowedHostnames === void 0 && hostnameAllowlist === void 0) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
mode: "permissive",
|
|
140
|
+
allowedPatterns: [],
|
|
141
|
+
blockedPatterns: [],
|
|
142
|
+
allowFileUrls: false,
|
|
143
|
+
allowPrivateIps: allowPrivateNetwork || false,
|
|
144
|
+
...allowPrivateNetwork === true ? { allowPrivateNetwork: true } : {},
|
|
145
|
+
...allowedHostnames ? { allowedHostnames } : {},
|
|
146
|
+
...hostnameAllowlist ? { hostnameAllowlist } : {}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function parseHttpUrl(raw, label) {
|
|
150
|
+
const trimmed = raw.trim();
|
|
151
|
+
const parsed = new URL(trimmed);
|
|
152
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
153
|
+
throw new Error(`${label} must be http(s), got: ${parsed.protocol.replace(":", "")}`);
|
|
154
|
+
}
|
|
155
|
+
const port = parsed.port && Number.parseInt(parsed.port, 10) > 0 ? Number.parseInt(parsed.port, 10) : parsed.protocol === "https:" ? 443 : 80;
|
|
156
|
+
if (Number.isNaN(port) || port <= 0 || port > 65535) {
|
|
157
|
+
throw new Error(`${label} has invalid port: ${parsed.port}`);
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
parsed,
|
|
161
|
+
port,
|
|
162
|
+
normalized: parsed.toString().replace(/\/$/, "")
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function ensureDefaultProfile(profiles, defaultColor, legacyCdpPort, derivedDefaultCdpPort) {
|
|
166
|
+
const result = { ...profiles };
|
|
167
|
+
if (!result[DEFAULT_AGENTICMAIL_BROWSER_PROFILE_NAME]) {
|
|
168
|
+
result[DEFAULT_AGENTICMAIL_BROWSER_PROFILE_NAME] = {
|
|
169
|
+
cdpPort: legacyCdpPort ?? derivedDefaultCdpPort ?? CDP_PORT_RANGE_START,
|
|
170
|
+
color: defaultColor
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
function ensureDefaultChromeExtensionProfile(profiles, controlPort) {
|
|
176
|
+
const result = { ...profiles };
|
|
177
|
+
if (result.chrome) {
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
const relayPort = controlPort + 1;
|
|
181
|
+
if (!Number.isFinite(relayPort) || relayPort <= 0 || relayPort > 65535) {
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
if (getUsedPorts(result).has(relayPort)) {
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
result.chrome = {
|
|
188
|
+
driver: "extension",
|
|
189
|
+
cdpUrl: `http://127.0.0.1:${relayPort}`,
|
|
190
|
+
color: "#00AA00"
|
|
191
|
+
};
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
function resolveBrowserConfig(cfg, rootConfig) {
|
|
195
|
+
const enabled = cfg?.enabled ?? DEFAULT_AGENTICMAIL_BROWSER_ENABLED;
|
|
196
|
+
const evaluateEnabled = cfg?.evaluateEnabled ?? DEFAULT_BROWSER_EVALUATE_ENABLED;
|
|
197
|
+
const gatewayPort = resolveGatewayPort(rootConfig);
|
|
198
|
+
const controlPort = deriveDefaultBrowserControlPort(gatewayPort ?? DEFAULT_BROWSER_CONTROL_PORT);
|
|
199
|
+
const defaultColor = normalizeHexColor(cfg?.color);
|
|
200
|
+
const remoteCdpTimeoutMs = normalizeTimeoutMs(cfg?.remoteCdpTimeoutMs, 1500);
|
|
201
|
+
const remoteCdpHandshakeTimeoutMs = normalizeTimeoutMs(
|
|
202
|
+
cfg?.remoteCdpHandshakeTimeoutMs,
|
|
203
|
+
Math.max(2e3, remoteCdpTimeoutMs * 2)
|
|
204
|
+
);
|
|
205
|
+
const derivedCdpRange = deriveDefaultBrowserCdpPortRange(controlPort);
|
|
206
|
+
const rawCdpUrl = (cfg?.cdpUrl ?? "").trim();
|
|
207
|
+
let cdpInfo;
|
|
208
|
+
if (rawCdpUrl) {
|
|
209
|
+
cdpInfo = parseHttpUrl(rawCdpUrl, "browser.cdpUrl");
|
|
210
|
+
} else {
|
|
211
|
+
const derivedPort = controlPort + 1;
|
|
212
|
+
if (derivedPort > 65535) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Derived CDP port (${derivedPort}) is too high; check gateway port configuration.`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const derived = new URL(`http://127.0.0.1:${derivedPort}`);
|
|
218
|
+
cdpInfo = {
|
|
219
|
+
parsed: derived,
|
|
220
|
+
port: derivedPort,
|
|
221
|
+
normalized: derived.toString().replace(/\/$/, "")
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const headless = cfg?.headless === true;
|
|
225
|
+
const noSandbox = cfg?.noSandbox === true;
|
|
226
|
+
const attachOnly = cfg?.attachOnly === true;
|
|
227
|
+
const executablePath = cfg?.executablePath?.trim() || void 0;
|
|
228
|
+
const defaultProfileFromConfig = cfg?.defaultProfile?.trim() || void 0;
|
|
229
|
+
const legacyCdpPort = rawCdpUrl ? cdpInfo.port : void 0;
|
|
230
|
+
const profiles = ensureDefaultChromeExtensionProfile(
|
|
231
|
+
ensureDefaultProfile(cfg?.profiles, defaultColor, legacyCdpPort, derivedCdpRange.start),
|
|
232
|
+
controlPort
|
|
233
|
+
);
|
|
234
|
+
const cdpProtocol = cdpInfo.parsed.protocol === "https:" ? "https" : "http";
|
|
235
|
+
const defaultProfile = defaultProfileFromConfig ?? (profiles[DEFAULT_BROWSER_DEFAULT_PROFILE_NAME] ? DEFAULT_BROWSER_DEFAULT_PROFILE_NAME : DEFAULT_AGENTICMAIL_BROWSER_PROFILE_NAME);
|
|
236
|
+
const extraArgs = Array.isArray(cfg?.extraArgs) ? cfg.extraArgs.filter((a) => typeof a === "string" && a.trim().length > 0) : [];
|
|
237
|
+
const ssrfPolicy = resolveBrowserSsrFPolicy(cfg);
|
|
238
|
+
return {
|
|
239
|
+
enabled,
|
|
240
|
+
evaluateEnabled,
|
|
241
|
+
controlPort,
|
|
242
|
+
cdpProtocol,
|
|
243
|
+
cdpHost: cdpInfo.parsed.hostname,
|
|
244
|
+
cdpIsLoopback: isLoopbackHost(cdpInfo.parsed.hostname),
|
|
245
|
+
remoteCdpTimeoutMs,
|
|
246
|
+
remoteCdpHandshakeTimeoutMs,
|
|
247
|
+
color: defaultColor,
|
|
248
|
+
executablePath,
|
|
249
|
+
headless,
|
|
250
|
+
noSandbox,
|
|
251
|
+
attachOnly,
|
|
252
|
+
defaultProfile,
|
|
253
|
+
profiles,
|
|
254
|
+
ssrfPolicy,
|
|
255
|
+
extraArgs
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function resolveProfile(resolved, profileName) {
|
|
259
|
+
const profile = resolved.profiles[profileName];
|
|
260
|
+
if (!profile) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const rawProfileUrl = profile.cdpUrl?.trim() ?? "";
|
|
264
|
+
let cdpHost = resolved.cdpHost;
|
|
265
|
+
let cdpPort = profile.cdpPort ?? 0;
|
|
266
|
+
let cdpUrl = "";
|
|
267
|
+
const driver = profile.driver === "extension" ? "extension" : "agenticmail";
|
|
268
|
+
if (rawProfileUrl) {
|
|
269
|
+
const parsed = parseHttpUrl(rawProfileUrl, `browser.profiles.${profileName}.cdpUrl`);
|
|
270
|
+
cdpHost = parsed.parsed.hostname;
|
|
271
|
+
cdpPort = parsed.port;
|
|
272
|
+
cdpUrl = parsed.normalized;
|
|
273
|
+
} else if (cdpPort) {
|
|
274
|
+
cdpUrl = `${resolved.cdpProtocol}://${resolved.cdpHost}:${cdpPort}`;
|
|
275
|
+
} else {
|
|
276
|
+
throw new Error(`Profile "${profileName}" must define cdpPort or cdpUrl.`);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
name: profileName,
|
|
280
|
+
cdpPort,
|
|
281
|
+
cdpUrl,
|
|
282
|
+
cdpHost,
|
|
283
|
+
cdpIsLoopback: isLoopbackHost(cdpHost),
|
|
284
|
+
color: profile.color || "#666",
|
|
285
|
+
driver
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/browser/pw-ai-module.ts
|
|
290
|
+
var pwAiModuleSoft = null;
|
|
291
|
+
var pwAiModuleStrict = null;
|
|
292
|
+
function isModuleNotFoundError(err) {
|
|
293
|
+
const code = extractErrorCode(err);
|
|
294
|
+
if (code === "ERR_MODULE_NOT_FOUND") {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
const msg = formatErrorMessage(err);
|
|
298
|
+
return msg.includes("Cannot find module") || msg.includes("Cannot find package") || msg.includes("Failed to resolve import") || msg.includes("Failed to resolve entry for package") || msg.includes("Failed to load url");
|
|
299
|
+
}
|
|
300
|
+
async function loadPwAiModule(mode) {
|
|
301
|
+
try {
|
|
302
|
+
return await import("./pw-ai-UHFQ7Q6C.js");
|
|
303
|
+
} catch (err) {
|
|
304
|
+
if (mode === "soft") {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
if (isModuleNotFoundError(err)) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
throw err;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function getPwAiModule(opts) {
|
|
314
|
+
const mode = opts?.mode ?? "soft";
|
|
315
|
+
if (mode === "soft") {
|
|
316
|
+
if (!pwAiModuleSoft) {
|
|
317
|
+
pwAiModuleSoft = loadPwAiModule("soft");
|
|
318
|
+
}
|
|
319
|
+
return await pwAiModuleSoft;
|
|
320
|
+
}
|
|
321
|
+
if (!pwAiModuleStrict) {
|
|
322
|
+
pwAiModuleStrict = loadPwAiModule("strict");
|
|
323
|
+
}
|
|
324
|
+
return await pwAiModuleStrict;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/browser/trash.ts
|
|
328
|
+
import fs from "fs";
|
|
329
|
+
import os from "os";
|
|
330
|
+
import path from "path";
|
|
331
|
+
async function movePathToTrash(targetPath) {
|
|
332
|
+
try {
|
|
333
|
+
await runExec("trash", [targetPath], { timeoutMs: 1e4 });
|
|
334
|
+
return targetPath;
|
|
335
|
+
} catch {
|
|
336
|
+
const trashDir = path.join(os.homedir(), ".Trash");
|
|
337
|
+
fs.mkdirSync(trashDir, { recursive: true });
|
|
338
|
+
const base = path.basename(targetPath);
|
|
339
|
+
let dest = path.join(trashDir, `${base}-${Date.now()}`);
|
|
340
|
+
if (fs.existsSync(dest)) {
|
|
341
|
+
dest = path.join(trashDir, `${base}-${Date.now()}-${Math.random()}`);
|
|
342
|
+
}
|
|
343
|
+
fs.renameSync(targetPath, dest);
|
|
344
|
+
return dest;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export {
|
|
349
|
+
isValidProfileName,
|
|
350
|
+
allocateCdpPort,
|
|
351
|
+
getUsedPorts,
|
|
352
|
+
allocateColor,
|
|
353
|
+
getUsedColors,
|
|
354
|
+
parseHttpUrl,
|
|
355
|
+
resolveBrowserConfig,
|
|
356
|
+
resolveProfile,
|
|
357
|
+
getPwAiModule,
|
|
358
|
+
movePathToTrash
|
|
359
|
+
};
|