@anybrowser/cli 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +78 -2
- package/dist/allowedSites.js +164 -0
- package/dist/allowedSites.js.map +1 -0
- package/dist/blockLedger.js +111 -0
- package/dist/blockLedger.js.map +1 -0
- package/dist/browser.js +1096 -0
- package/dist/browser.js.map +1 -0
- package/dist/browserDetect.js +53 -0
- package/dist/browserDetect.js.map +1 -0
- package/dist/cli.js +366 -0
- package/dist/cli.js.map +1 -0
- package/dist/connect.js +260 -0
- package/dist/connect.js.map +1 -0
- package/dist/daemon.js +487 -0
- package/dist/daemon.js.map +1 -0
- package/dist/hello.js +62 -0
- package/dist/hello.js.map +1 -0
- package/dist/idle.js +84 -0
- package/dist/idle.js.map +1 -0
- package/dist/install.js +63 -0
- package/dist/install.js.map +1 -0
- package/dist/paths.js +36 -0
- package/dist/paths.js.map +1 -0
- package/dist/profile.js +273 -0
- package/dist/profile.js.map +1 -0
- package/dist/proxy.js +41 -0
- package/dist/proxy.js.map +1 -0
- package/dist/siteProxy.js +354 -0
- package/dist/siteProxy.js.map +1 -0
- package/dist/transport.js +85 -0
- package/dist/transport.js.map +1 -0
- package/dist/version.js +18 -0
- package/dist/version.js.map +1 -0
- package/package.json +50 -4
- package/skills/anybrowser/SKILL.md +42 -0
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { createConnection, createServer } from "node:net";
|
|
3
|
+
import { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { assertSocketPathFits } from "./paths.js";
|
|
6
|
+
import { formatErrorLine, formatStatusLine, formatStopAckLine, parseHelloLine, } from "./hello.js";
|
|
7
|
+
import { readVersion } from "./version.js";
|
|
8
|
+
import { SocketTransport } from "./transport.js";
|
|
9
|
+
import { BrowserContextRouter, BrowserSession, ProfileHeadlessRunningError, ProfileUrlBlockedError, } from "./browser.js";
|
|
10
|
+
import { addProfileWithDetection, allowedOriginsProblem, listProfiles, listProfilesWithBadList, profileExists, readProfile, } from "./profile.js";
|
|
11
|
+
import { resolveIdleMs } from "./idle.js";
|
|
12
|
+
const DAEMON_STATUS_TOOL = {
|
|
13
|
+
name: "daemon_status",
|
|
14
|
+
description: "Report this daemon's version, pid, uptime, and connected MCP session count.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {},
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const PROFILE_LIST_TOOL = {
|
|
22
|
+
name: "profile_list",
|
|
23
|
+
description: "List every anybrowser profile: id, browser, headless, allowedOrigins (the sites the profile is restricted to, or null for no restriction), and whether it is currently running.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {},
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const PROFILE_CREATE_TOOL = {
|
|
31
|
+
name: "profile_create",
|
|
32
|
+
description: "Create a new anybrowser profile. `browser` is one of chrome, chromium, brave, edge, arc, vivaldi, opera. Returns the new profile's id.",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
name: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "A lowercase slug, e.g. 'work'. Composed into the id as '<name>-in-<browser>'.",
|
|
39
|
+
},
|
|
40
|
+
browser: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "chrome | chromium | brave | edge | arc | vivaldi | opera",
|
|
43
|
+
},
|
|
44
|
+
headless: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
description: "Defaults to false.",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ["name", "browser"],
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
const PROFILE_STATUS_TOOL = {
|
|
54
|
+
name: "profile_status",
|
|
55
|
+
description: "Report a profile's running state, tab count (when running), headless flag, browser, and allowedOrigins (the sites the profile is restricted to, or null for no restriction).",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
profile: { type: "string", description: "The profile id." },
|
|
60
|
+
},
|
|
61
|
+
required: ["profile"],
|
|
62
|
+
additionalProperties: false,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const PROFILE_LOGIN_TOOL = {
|
|
66
|
+
name: "profile_login",
|
|
67
|
+
description: "Open `url` in a new, foregrounded tab of `profile`'s browser (launching it headed if not already running), so a human can log in. Returns immediately; ask the user to log in, then verify with a snapshot.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
profile: { type: "string", description: "The profile id." },
|
|
72
|
+
url: { type: "string", description: "The URL to open for login." },
|
|
73
|
+
},
|
|
74
|
+
required: ["profile", "url"],
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const DAEMON_TOOLS = [
|
|
79
|
+
DAEMON_STATUS_TOOL,
|
|
80
|
+
PROFILE_LIST_TOOL,
|
|
81
|
+
PROFILE_CREATE_TOOL,
|
|
82
|
+
PROFILE_STATUS_TOOL,
|
|
83
|
+
PROFILE_LOGIN_TOOL,
|
|
84
|
+
];
|
|
85
|
+
/** Bytes we'll buffer looking for the hello line's newline before giving up on a connection. */
|
|
86
|
+
const MAX_HELLO_LINE_BYTES = 4096;
|
|
87
|
+
function log(event) {
|
|
88
|
+
process.stderr.write(`[anyb daemon] ${new Date().toISOString()} ${event}\n`);
|
|
89
|
+
}
|
|
90
|
+
function buildStatus(state) {
|
|
91
|
+
return {
|
|
92
|
+
version: state.version,
|
|
93
|
+
pid: process.pid,
|
|
94
|
+
uptimeSeconds: (Date.now() - state.startedAt) / 1000,
|
|
95
|
+
sessions: state.sessions.size,
|
|
96
|
+
profiles: state.browserRouter.runningProfileIds(),
|
|
97
|
+
idleMs: state.idleMs,
|
|
98
|
+
retiring: state.retiring,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Clears any pending idle-exit timer, e.g. because a session just connected
|
|
103
|
+
* or the daemon is shutting down for another reason.
|
|
104
|
+
*/
|
|
105
|
+
function clearIdleExitTimer(state) {
|
|
106
|
+
if (state.idleExitTimer !== undefined) {
|
|
107
|
+
clearTimeout(state.idleExitTimer);
|
|
108
|
+
state.idleExitTimer = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Re-arms the "exit for idleness" timer whenever sessions.size is (or just
|
|
113
|
+
* became) zero. A session connecting cancels it (see startMcpSession); this
|
|
114
|
+
* is called again on every session close so the countdown restarts from that
|
|
115
|
+
* moment, not from whenever the daemon happened to start.
|
|
116
|
+
*
|
|
117
|
+
* Idling is disabled entirely when `idleMs` is null (ANYBROWSER_IDLE_MS=off).
|
|
118
|
+
*/
|
|
119
|
+
function armIdleExitTimer(state) {
|
|
120
|
+
clearIdleExitTimer(state);
|
|
121
|
+
if (state.idleMs === null || state.sessions.size > 0 || state.shuttingDown) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
state.idleExitTimer = setTimeout(() => {
|
|
125
|
+
// Re-check at fire time: a session may have connected in the interim.
|
|
126
|
+
if (state.sessions.size === 0 && !state.shuttingDown) {
|
|
127
|
+
log(`idle exit: no sessions connected for ${state.idleMs}ms`);
|
|
128
|
+
void shutdown(state, 0);
|
|
129
|
+
}
|
|
130
|
+
}, state.idleMs);
|
|
131
|
+
}
|
|
132
|
+
/** Connects to `socketPath` just to see whether anything answers. Never throws. */
|
|
133
|
+
function probeSocket(socketPath) {
|
|
134
|
+
return new Promise((resolve) => {
|
|
135
|
+
const socket = createConnection(socketPath);
|
|
136
|
+
const finish = (alive) => {
|
|
137
|
+
socket.removeAllListeners();
|
|
138
|
+
socket.destroy();
|
|
139
|
+
resolve(alive);
|
|
140
|
+
};
|
|
141
|
+
socket.once("connect", () => finish(true));
|
|
142
|
+
socket.once("error", () => finish(false));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* If daemon.sock exists and something is listening on it, another daemon
|
|
147
|
+
* already owns this state dir: exit quietly. If it exists but nothing
|
|
148
|
+
* answers, it's a stale leftover: remove it and let the caller bind fresh.
|
|
149
|
+
*/
|
|
150
|
+
async function claimSocketOrExit(paths) {
|
|
151
|
+
if (!existsSync(paths.socket)) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const alive = await probeSocket(paths.socket);
|
|
155
|
+
if (alive) {
|
|
156
|
+
log("another daemon is already live on this socket; exiting");
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
unlinkSync(paths.socket);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error.code !== "ENOENT") {
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function startMcpSession(socket, initialData, state, clientVersion) {
|
|
169
|
+
state.sessions.add(socket);
|
|
170
|
+
clearIdleExitTimer(state);
|
|
171
|
+
if (clientVersion !== state.version && !state.retiring) {
|
|
172
|
+
state.retiring = true;
|
|
173
|
+
state.retiredFor = clientVersion;
|
|
174
|
+
log(`retiring: client version ${clientVersion} differs from daemon version ${state.version}; exiting once the last session ends`);
|
|
175
|
+
}
|
|
176
|
+
log(`session open (client version ${clientVersion}, sessions=${state.sessions.size})`);
|
|
177
|
+
const browserSession = new BrowserSession(state.browserRouter);
|
|
178
|
+
socket.on("close", () => {
|
|
179
|
+
state.sessions.delete(socket);
|
|
180
|
+
log(`session close (sessions=${state.sessions.size})`);
|
|
181
|
+
if (state.retiring && state.sessions.size === 0 && !state.shuttingDown) {
|
|
182
|
+
log(`retiring: exiting, no sessions left (daemon version ${state.version}, client version ${state.retiredFor})`);
|
|
183
|
+
void shutdown(state, 0);
|
|
184
|
+
}
|
|
185
|
+
armIdleExitTimer(state);
|
|
186
|
+
// Its tabs go with it; the browser stays (the daemon owns its lifetime).
|
|
187
|
+
browserSession.dispose().catch((error) => {
|
|
188
|
+
log(`session tab cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
// No 'error' listener here: attachHelloHandler already installed the one
|
|
192
|
+
// permanent listener for this socket's whole life (see its comment), so
|
|
193
|
+
// adding a second one here would log every socket error twice.
|
|
194
|
+
try {
|
|
195
|
+
const transport = new SocketTransport(socket, initialData);
|
|
196
|
+
const server = new McpServer({ name: "anybrowser", version: state.version }, { capabilities: { tools: {} } });
|
|
197
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
198
|
+
const browserTools = await browserSession.listTools();
|
|
199
|
+
return { tools: [...DAEMON_TOOLS, ...browserTools] };
|
|
200
|
+
});
|
|
201
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
202
|
+
if (request.params.name === "daemon_status") {
|
|
203
|
+
return {
|
|
204
|
+
content: [{ type: "text", text: JSON.stringify(buildStatus(state)) }],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (request.params.name === "profile_list") {
|
|
208
|
+
const profiles = listProfiles(state.paths).map((profile) => ({
|
|
209
|
+
id: profile.id,
|
|
210
|
+
browser: profile.browser,
|
|
211
|
+
headless: profile.headless,
|
|
212
|
+
allowedOrigins: profile.allowedOrigins,
|
|
213
|
+
running: state.browserRouter.runningProfileIds().includes(profile.id),
|
|
214
|
+
}));
|
|
215
|
+
const broken = listProfilesWithBadList(state.paths).map(({ id, problem }) => ({ id, error: problem }));
|
|
216
|
+
return {
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: JSON.stringify([...profiles, ...broken]),
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (request.params.name === "profile_create") {
|
|
226
|
+
const args = (request.params.arguments ?? {});
|
|
227
|
+
if (args.allowedOrigins !== undefined) {
|
|
228
|
+
// The allowed-sites list is a human guard rail, like `headless`
|
|
229
|
+
// (decision 7). Failing loudly beats ignoring it: an agent that
|
|
230
|
+
// thought it had pinned a profile would act as if it were safe.
|
|
231
|
+
return {
|
|
232
|
+
isError: true,
|
|
233
|
+
content: [
|
|
234
|
+
{
|
|
235
|
+
type: "text",
|
|
236
|
+
text: "profile_create cannot set an allowed-sites list; only the user can, with 'anyb profile add <name> <browser> --allow <list>' or 'anyb profile set <id> allowedOrigins=<list>'",
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (typeof args.name !== "string" || typeof args.browser !== "string") {
|
|
242
|
+
return {
|
|
243
|
+
isError: true,
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: "profile_create requires string 'name' and 'browser'",
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const outcome = addProfileWithDetection(state.paths, {
|
|
253
|
+
name: args.name,
|
|
254
|
+
browser: args.browser,
|
|
255
|
+
headless: args.headless === true,
|
|
256
|
+
});
|
|
257
|
+
if (!outcome.ok) {
|
|
258
|
+
return {
|
|
259
|
+
isError: true,
|
|
260
|
+
content: [{ type: "text", text: outcome.message }],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return { content: [{ type: "text", text: outcome.id }] };
|
|
264
|
+
}
|
|
265
|
+
if (request.params.name === "profile_status") {
|
|
266
|
+
const args = (request.params.arguments ?? {});
|
|
267
|
+
if (typeof args.profile !== "string" || args.profile.length === 0) {
|
|
268
|
+
throw new McpError(ErrorCode.InvalidParams, "profile_status requires a string 'profile' argument");
|
|
269
|
+
}
|
|
270
|
+
if (!profileExists(state.paths, args.profile)) {
|
|
271
|
+
throw new McpError(ErrorCode.InvalidParams, `unknown profile '${args.profile}'`);
|
|
272
|
+
}
|
|
273
|
+
const problem = allowedOriginsProblem(state.paths, args.profile);
|
|
274
|
+
if (problem) {
|
|
275
|
+
return {
|
|
276
|
+
isError: true,
|
|
277
|
+
content: [
|
|
278
|
+
{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: `Profile '${args.profile}': ${problem}. The last list read stays in force while the daemon runs. Ask the user to fix profile.json outside the agent.`,
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
const stored = readProfile(state.paths, args.profile);
|
|
286
|
+
const runningContext = state.browserRouter.runningContext(args.profile);
|
|
287
|
+
const idleForMs = state.browserRouter.idleForMs(args.profile);
|
|
288
|
+
const status = {
|
|
289
|
+
running: runningContext !== undefined,
|
|
290
|
+
headless: stored.headless,
|
|
291
|
+
browser: stored.browser,
|
|
292
|
+
allowedOrigins: stored.allowedOrigins,
|
|
293
|
+
...(runningContext
|
|
294
|
+
? {
|
|
295
|
+
tabCount: runningContext.pages().length,
|
|
296
|
+
...(idleForMs !== undefined ? { idleForMs } : {}),
|
|
297
|
+
}
|
|
298
|
+
: {}),
|
|
299
|
+
};
|
|
300
|
+
return { content: [{ type: "text", text: JSON.stringify(status) }] };
|
|
301
|
+
}
|
|
302
|
+
if (request.params.name === "profile_login") {
|
|
303
|
+
const args = (request.params.arguments ?? {});
|
|
304
|
+
if (typeof args.profile !== "string" ||
|
|
305
|
+
args.profile.length === 0 ||
|
|
306
|
+
typeof args.url !== "string" ||
|
|
307
|
+
args.url.length === 0) {
|
|
308
|
+
throw new McpError(ErrorCode.InvalidParams, "profile_login requires string 'profile' and 'url' arguments");
|
|
309
|
+
}
|
|
310
|
+
if (!profileExists(state.paths, args.profile)) {
|
|
311
|
+
throw new McpError(ErrorCode.InvalidParams, `unknown profile '${args.profile}'`);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
await state.browserRouter.loginLaunch(args.profile, args.url);
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
if (error instanceof ProfileHeadlessRunningError ||
|
|
318
|
+
error instanceof ProfileUrlBlockedError) {
|
|
319
|
+
return {
|
|
320
|
+
isError: true,
|
|
321
|
+
content: [{ type: "text", text: error.message }],
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: `Opened ${args.url} in a new tab for profile '${args.profile}'. Ask the user to log in, then verify with a snapshot.`,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return browserSession.callTool(request.params.name, (request.params.arguments ?? {}));
|
|
336
|
+
});
|
|
337
|
+
server.connect(transport).catch((error) => {
|
|
338
|
+
log(`session error: ${error instanceof Error ? error.message : String(error)}`);
|
|
339
|
+
socket.destroy();
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
log(`session error: ${error instanceof Error ? error.message : String(error)}`);
|
|
344
|
+
socket.destroy();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function handleHello(socket, line, rest, state) {
|
|
348
|
+
const parsed = parseHelloLine(line);
|
|
349
|
+
if (!parsed.ok) {
|
|
350
|
+
log(`bad hello: ${parsed.error}`);
|
|
351
|
+
socket.end(formatErrorLine(parsed.error));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
switch (parsed.role) {
|
|
355
|
+
case "status":
|
|
356
|
+
socket.end(formatStatusLine(buildStatus(state)));
|
|
357
|
+
return;
|
|
358
|
+
case "stop":
|
|
359
|
+
socket.write(formatStopAckLine(), () => {
|
|
360
|
+
socket.end(() => {
|
|
361
|
+
void shutdown(state, 0);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
return;
|
|
365
|
+
case "mcp":
|
|
366
|
+
startMcpSession(socket, rest, state, parsed.version);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function attachHelloHandler(socket, state) {
|
|
371
|
+
let buffered = Buffer.alloc(0);
|
|
372
|
+
// Every accepted socket must have an 'error' listener for its whole life:
|
|
373
|
+
// Node treats an unhandled 'error' event as fatal to the process. A reply
|
|
374
|
+
// write can fail (e.g. EPIPE) after the hello-parsing phase is done, once
|
|
375
|
+
// control has moved on to handleHello/startMcpSession, so this listener is
|
|
376
|
+
// never removed - unlike the hello-parsing 'data'/'close' listeners below,
|
|
377
|
+
// which are only needed until the hello line is read.
|
|
378
|
+
socket.on("error", (error) => {
|
|
379
|
+
log(`session error: ${error.message}`);
|
|
380
|
+
});
|
|
381
|
+
const cleanup = () => {
|
|
382
|
+
socket.off("data", onData);
|
|
383
|
+
socket.off("close", onClose);
|
|
384
|
+
};
|
|
385
|
+
const onClose = () => cleanup();
|
|
386
|
+
const onData = (chunk) => {
|
|
387
|
+
buffered = buffered.length === 0 ? chunk : Buffer.concat([buffered, chunk]);
|
|
388
|
+
const newlineIndex = buffered.indexOf(0x0a);
|
|
389
|
+
if (newlineIndex === -1) {
|
|
390
|
+
if (buffered.length > MAX_HELLO_LINE_BYTES) {
|
|
391
|
+
log("bad hello: first line too long");
|
|
392
|
+
socket.destroy();
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
cleanup();
|
|
397
|
+
const line = buffered
|
|
398
|
+
.subarray(0, newlineIndex)
|
|
399
|
+
.toString("utf8")
|
|
400
|
+
.replace(/\r$/, "");
|
|
401
|
+
const rest = buffered.subarray(newlineIndex + 1);
|
|
402
|
+
handleHello(socket, line, rest, state);
|
|
403
|
+
};
|
|
404
|
+
socket.on("data", onData);
|
|
405
|
+
socket.on("close", onClose);
|
|
406
|
+
}
|
|
407
|
+
async function shutdown(state, exitCode) {
|
|
408
|
+
if (state.shuttingDown) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
state.shuttingDown = true;
|
|
412
|
+
clearIdleExitTimer(state);
|
|
413
|
+
log("stop: shutting down");
|
|
414
|
+
state.server.close();
|
|
415
|
+
for (const session of state.sessions) {
|
|
416
|
+
session.destroy();
|
|
417
|
+
}
|
|
418
|
+
state.sessions.clear();
|
|
419
|
+
// Close every open browser context (flushing its cookie store etc. to
|
|
420
|
+
// disk) BEFORE removing the socket file: a caller of `anyb stop` waits for
|
|
421
|
+
// the socket to disappear as its signal that shutdown is complete (see
|
|
422
|
+
// cli.ts's runStopCommand), so the socket must not vanish until a script
|
|
423
|
+
// that immediately inspects the profile directory afterwards sees a
|
|
424
|
+
// flushed cookie store.
|
|
425
|
+
await state.browserRouter.closeAll();
|
|
426
|
+
try {
|
|
427
|
+
unlinkSync(state.paths.socket);
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
if (error.code !== "ENOENT") {
|
|
431
|
+
log(`stop: failed to remove socket: ${error.message}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
log("stop: done");
|
|
435
|
+
process.exit(exitCode);
|
|
436
|
+
}
|
|
437
|
+
/** Runs the daemon in the foreground. Resolves once it is listening; keeps running until shutdown. */
|
|
438
|
+
export async function runDaemon(paths) {
|
|
439
|
+
// Playwright's own switch for NOT forcing loopback through a browser's proxy.
|
|
440
|
+
// The allowed-sites proxy must see local servers too, and the check upstream
|
|
441
|
+
// is plain truthiness (even "0" disables forcing), so only deleting it is safe.
|
|
442
|
+
delete process.env["PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK"];
|
|
443
|
+
assertSocketPathFits(paths.socket);
|
|
444
|
+
log(`start pid=${process.pid} version=${readVersion()}`);
|
|
445
|
+
mkdirSync(paths.home, { recursive: true, mode: 0o700 });
|
|
446
|
+
chmodSync(paths.home, 0o700);
|
|
447
|
+
await claimSocketOrExit(paths);
|
|
448
|
+
const version = readVersion();
|
|
449
|
+
const server = createServer();
|
|
450
|
+
const idleMs = resolveIdleMs(paths);
|
|
451
|
+
const state = {
|
|
452
|
+
paths,
|
|
453
|
+
version,
|
|
454
|
+
startedAt: Date.now(),
|
|
455
|
+
sessions: new Set(),
|
|
456
|
+
server,
|
|
457
|
+
shuttingDown: false,
|
|
458
|
+
retiring: false,
|
|
459
|
+
retiredFor: undefined,
|
|
460
|
+
browserRouter: new BrowserContextRouter(paths, log, idleMs),
|
|
461
|
+
idleMs,
|
|
462
|
+
idleExitTimer: undefined,
|
|
463
|
+
};
|
|
464
|
+
log(`idle: idleMs=${idleMs === null ? "off" : idleMs}`);
|
|
465
|
+
// No session has connected yet, so the countdown to an idle exit starts
|
|
466
|
+
// from daemon startup itself (see armIdleExitTimer's doc comment).
|
|
467
|
+
armIdleExitTimer(state);
|
|
468
|
+
server.on("connection", (socket) => {
|
|
469
|
+
attachHelloHandler(socket, state);
|
|
470
|
+
});
|
|
471
|
+
server.on("error", (error) => {
|
|
472
|
+
log(`server error: ${error.message}`);
|
|
473
|
+
});
|
|
474
|
+
await new Promise((resolve, reject) => {
|
|
475
|
+
const onError = (error) => reject(error);
|
|
476
|
+
server.once("error", onError);
|
|
477
|
+
server.listen(paths.socket, () => {
|
|
478
|
+
server.off("error", onError);
|
|
479
|
+
resolve();
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
chmodSync(paths.socket, 0o600);
|
|
483
|
+
log(`listening on ${paths.socket}`);
|
|
484
|
+
process.on("SIGTERM", () => void shutdown(state, 0));
|
|
485
|
+
process.on("SIGINT", () => void shutdown(state, 0));
|
|
486
|
+
}
|
|
487
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/hello.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The hello line protocol: the first line written by every client on a
|
|
3
|
+
* fresh connection to the daemon's Unix socket, a single JSON object
|
|
4
|
+
* followed by `\n`:
|
|
5
|
+
*
|
|
6
|
+
* {"anybrowser":{"version":"<package version>","role":"mcp"}}
|
|
7
|
+
*
|
|
8
|
+
* `role` selects what the rest of the connection carries. See daemon.ts for
|
|
9
|
+
* how each role is handled.
|
|
10
|
+
*/
|
|
11
|
+
const HELLO_ROLES = ["mcp", "status", "stop"];
|
|
12
|
+
export function isHelloRole(value) {
|
|
13
|
+
return (typeof value === "string" &&
|
|
14
|
+
HELLO_ROLES.includes(value));
|
|
15
|
+
}
|
|
16
|
+
/** Formats a hello line, including its trailing newline. */
|
|
17
|
+
export function formatHelloLine(role, version) {
|
|
18
|
+
const hello = { anybrowser: { version, role } };
|
|
19
|
+
return JSON.stringify(hello) + "\n";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Parses a hello line (without its trailing newline). Never throws: any
|
|
23
|
+
* problem is reported through the `ok: false` branch so the caller can
|
|
24
|
+
* reply with an error line instead of crashing.
|
|
25
|
+
*/
|
|
26
|
+
export function parseHelloLine(line) {
|
|
27
|
+
let data;
|
|
28
|
+
try {
|
|
29
|
+
data = JSON.parse(line);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { ok: false, error: "first line is not valid JSON" };
|
|
33
|
+
}
|
|
34
|
+
if (typeof data !== "object" || data === null || !("anybrowser" in data)) {
|
|
35
|
+
return { ok: false, error: "missing 'anybrowser' key" };
|
|
36
|
+
}
|
|
37
|
+
const envelope = data.anybrowser;
|
|
38
|
+
if (typeof envelope !== "object" || envelope === null) {
|
|
39
|
+
return { ok: false, error: "'anybrowser' must be an object" };
|
|
40
|
+
}
|
|
41
|
+
const { version, role } = envelope;
|
|
42
|
+
if (typeof version !== "string") {
|
|
43
|
+
return { ok: false, error: "missing 'anybrowser.version'" };
|
|
44
|
+
}
|
|
45
|
+
if (!isHelloRole(role)) {
|
|
46
|
+
return { ok: false, error: `unknown role '${String(role)}'` };
|
|
47
|
+
}
|
|
48
|
+
return { ok: true, version, role };
|
|
49
|
+
}
|
|
50
|
+
/** One JSON line the daemon sends back when a hello line is invalid. */
|
|
51
|
+
export function formatErrorLine(reason) {
|
|
52
|
+
return JSON.stringify({ error: reason }) + "\n";
|
|
53
|
+
}
|
|
54
|
+
/** One JSON line the daemon sends back to acknowledge a `stop` hello. */
|
|
55
|
+
export function formatStopAckLine() {
|
|
56
|
+
return JSON.stringify({ ok: true }) + "\n";
|
|
57
|
+
}
|
|
58
|
+
/** One JSON line the daemon sends back to a `status` hello. */
|
|
59
|
+
export function formatStatusLine(status) {
|
|
60
|
+
return JSON.stringify(status) + "\n";
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=hello.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hello.js","sourceRoot":"","sources":["../src/hello.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,GAAyB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAsBpE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACxB,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,CACnD,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,eAAe,CAAC,IAAe,EAAE,OAAe;IAC9D,MAAM,KAAK,GAAc,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;AACtC,CAAC;AAKD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,EAAE,CAAC;IAC9D,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,EAAE,CAAC;QACzE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,QAAQ,GAAI,IAAgC,CAAC,UAAU,CAAC;IAC9D,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC;IAChE,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,QAAiD,CAAC;IAC5E,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IAChE,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;AAClD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;AAC7C,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;AACvC,CAAC"}
|
package/dist/idle.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Default idle timeout (docs/DESIGN.md decision 5), used when neither
|
|
5
|
+
* ANYBROWSER_IDLE_MS nor config.json's `idleMinutes` gives a usable value.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_IDLE_MINUTES = 10;
|
|
8
|
+
export const DEFAULT_IDLE_MS = DEFAULT_IDLE_MINUTES * 60_000;
|
|
9
|
+
function fromEnv(env) {
|
|
10
|
+
const raw = env["ANYBROWSER_IDLE_MS"];
|
|
11
|
+
if (raw === undefined) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const trimmed = raw.trim();
|
|
15
|
+
if (trimmed === "") {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (trimmed.toLowerCase() === "off") {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const parsed = Number(trimmed);
|
|
22
|
+
// 0 or negative means "use the next source", not "never" - only the
|
|
23
|
+
// literal "off" above disables the timers.
|
|
24
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function fromConfigFile(paths) {
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = readFileSync(join(paths.home, "config.json"), "utf8");
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined; // absent or unreadable: fall through silently
|
|
36
|
+
}
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined; // malformed JSON: fall through silently
|
|
43
|
+
}
|
|
44
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const idleMinutes = parsed.idleMinutes;
|
|
48
|
+
if (typeof idleMinutes !== "number" ||
|
|
49
|
+
!Number.isFinite(idleMinutes) ||
|
|
50
|
+
idleMinutes <= 0) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return idleMinutes * 60_000;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolves the idle timeout shared by the daemon-exit timer and each
|
|
57
|
+
* profile's browser-close timer (docs/DESIGN.md decision 5).
|
|
58
|
+
*
|
|
59
|
+
* Precedence, checked in order, each falling through silently to the next
|
|
60
|
+
* on an absent/invalid value:
|
|
61
|
+
*
|
|
62
|
+
* 1. `ANYBROWSER_IDLE_MS` - an env var in milliseconds, for tests and
|
|
63
|
+
* power users. The literal value "off" (case-insensitive) disables
|
|
64
|
+
* BOTH timers entirely (returns null). Any other non-positive or
|
|
65
|
+
* unparseable value is treated as absent, not as "never".
|
|
66
|
+
* 2. `idleMinutes` in `<home>/config.json` (a number of minutes). The
|
|
67
|
+
* file may be absent, unreadable, malformed JSON, or missing the key.
|
|
68
|
+
* A non-numeric or non-positive `idleMinutes` also falls through.
|
|
69
|
+
* 3. The default, `DEFAULT_IDLE_MS` (10 minutes).
|
|
70
|
+
*
|
|
71
|
+
* Returns the resolved timeout in milliseconds, or null when disabled.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveIdleMs(paths, env = process.env) {
|
|
74
|
+
const envValue = fromEnv(env);
|
|
75
|
+
if (envValue !== undefined) {
|
|
76
|
+
return envValue;
|
|
77
|
+
}
|
|
78
|
+
const configValue = fromConfigFile(paths);
|
|
79
|
+
if (configValue !== undefined) {
|
|
80
|
+
return configValue;
|
|
81
|
+
}
|
|
82
|
+
return DEFAULT_IDLE_MS;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=idle.js.map
|