@aluvia/sdk 1.4.1 → 2.0.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/CHANGELOG.md +188 -0
- package/README.md +162 -477
- package/dist/cjs/api/apiUtils.js +4 -1
- package/dist/cjs/client/AluviaClient.js +30 -32
- package/dist/cjs/client/BlockDetection.js +69 -87
- package/dist/cjs/client/rules.js +12 -2
- package/dist/cjs/connect.js +2 -2
- package/dist/cjs/index.js +12 -1
- package/dist/cjs/session/lock.js +40 -4
- package/dist/esm/api/apiUtils.js +4 -1
- package/dist/esm/client/AluviaClient.js +38 -40
- package/dist/esm/client/BlockDetection.js +69 -87
- package/dist/esm/client/rules.js +12 -2
- package/dist/esm/connect.js +2 -2
- package/dist/esm/index.js +6 -4
- package/dist/esm/session/lock.js +40 -4
- package/dist/types/client/AluviaClient.d.ts +2 -2
- package/dist/types/client/BlockDetection.d.ts +4 -4
- package/dist/types/client/types.d.ts +11 -11
- package/dist/types/index.d.ts +9 -7
- package/package.json +15 -23
- package/dist/cjs/bin/account.js +0 -31
- package/dist/cjs/bin/api-helpers.js +0 -58
- package/dist/cjs/bin/cli-adapter.js +0 -16
- package/dist/cjs/bin/cli.js +0 -245
- package/dist/cjs/bin/close.js +0 -120
- package/dist/cjs/bin/geos.js +0 -10
- package/dist/cjs/bin/mcp-helpers.js +0 -57
- package/dist/cjs/bin/open.js +0 -317
- package/dist/cjs/bin/session.js +0 -259
- package/dist/esm/bin/account.js +0 -28
- package/dist/esm/bin/api-helpers.js +0 -53
- package/dist/esm/bin/cli-adapter.js +0 -8
- package/dist/esm/bin/cli.js +0 -242
- package/dist/esm/bin/close.js +0 -117
- package/dist/esm/bin/geos.js +0 -7
- package/dist/esm/bin/mcp-helpers.js +0 -51
- package/dist/esm/bin/open.js +0 -280
- package/dist/esm/bin/session.js +0 -252
- package/dist/types/bin/account.d.ts +0 -1
- package/dist/types/bin/api-helpers.d.ts +0 -20
- package/dist/types/bin/cli-adapter.d.ts +0 -8
- package/dist/types/bin/cli.d.ts +0 -2
- package/dist/types/bin/close.d.ts +0 -1
- package/dist/types/bin/geos.d.ts +0 -1
- package/dist/types/bin/mcp-helpers.d.ts +0 -28
- package/dist/types/bin/open.d.ts +0 -21
- package/dist/types/bin/session.d.ts +0 -11
package/dist/cjs/bin/close.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleClose = handleClose;
|
|
4
|
-
const lock_js_1 = require("../session/lock.js");
|
|
5
|
-
const cli_js_1 = require("./cli.js");
|
|
6
|
-
async function handleClose(sessionName, closeAll) {
|
|
7
|
-
if (closeAll) {
|
|
8
|
-
const sessions = (0, lock_js_1.listSessions)();
|
|
9
|
-
if (sessions.length === 0) {
|
|
10
|
-
return (0, cli_js_1.output)({ error: 'No running browser sessions found.', closed: [], count: 0 }, 1);
|
|
11
|
-
}
|
|
12
|
-
// Send SIGTERM to all sessions
|
|
13
|
-
for (const s of sessions) {
|
|
14
|
-
try {
|
|
15
|
-
process.kill(s.pid, 'SIGTERM');
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
// ignore
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
// Wait up to 10 seconds for all processes to exit
|
|
22
|
-
const maxWait = 40;
|
|
23
|
-
const alive = new Set(sessions.map((s) => s.pid));
|
|
24
|
-
for (let i = 0; i < maxWait && alive.size > 0; i++) {
|
|
25
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
26
|
-
for (const pid of alive) {
|
|
27
|
-
if (!(0, lock_js_1.isProcessAlive)(pid)) {
|
|
28
|
-
alive.delete(pid);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// Force-kill any survivors
|
|
33
|
-
for (const pid of alive) {
|
|
34
|
-
try {
|
|
35
|
-
process.kill(pid, 'SIGKILL');
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// ignore
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Now remove all locks
|
|
42
|
-
const closed = [];
|
|
43
|
-
for (const s of sessions) {
|
|
44
|
-
(0, lock_js_1.removeLock)(s.session);
|
|
45
|
-
closed.push(s.session);
|
|
46
|
-
}
|
|
47
|
-
return (0, cli_js_1.output)({ message: 'All browser sessions closed.', closed, count: closed.length });
|
|
48
|
-
}
|
|
49
|
-
// If no session name specified, figure out what to close
|
|
50
|
-
if (!sessionName) {
|
|
51
|
-
const sessions = (0, lock_js_1.listSessions)();
|
|
52
|
-
if (sessions.length === 0) {
|
|
53
|
-
return (0, cli_js_1.output)({ error: 'No running browser sessions found.' }, 1);
|
|
54
|
-
}
|
|
55
|
-
if (sessions.length > 1) {
|
|
56
|
-
return (0, cli_js_1.output)({
|
|
57
|
-
error: 'Multiple sessions running. Specify --browser-session <name> or --all.',
|
|
58
|
-
browserSessions: sessions.map((s) => s.session),
|
|
59
|
-
}, 1);
|
|
60
|
-
}
|
|
61
|
-
// Single session — use its data directly instead of re-reading the lock file
|
|
62
|
-
const session = sessions[0];
|
|
63
|
-
sessionName = session.session;
|
|
64
|
-
return closeSession(sessionName, (0, lock_js_1.toLockData)(session));
|
|
65
|
-
}
|
|
66
|
-
// Session name provided — need to verify it's alive
|
|
67
|
-
const sessions = (0, lock_js_1.listSessions)();
|
|
68
|
-
const match = sessions.find((s) => s.session === sessionName);
|
|
69
|
-
if (!match) {
|
|
70
|
-
return (0, cli_js_1.output)({ browserSession: sessionName, error: 'No running browser session found.' }, 1);
|
|
71
|
-
}
|
|
72
|
-
return closeSession(sessionName, (0, lock_js_1.toLockData)(match));
|
|
73
|
-
}
|
|
74
|
-
async function closeSession(sessionName, lock) {
|
|
75
|
-
if (!(0, lock_js_1.isProcessAlive)(lock.pid)) {
|
|
76
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
77
|
-
return (0, cli_js_1.output)({
|
|
78
|
-
browserSession: sessionName,
|
|
79
|
-
message: 'Browser process was not running. Lock file cleaned up.',
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
process.kill(lock.pid, 'SIGTERM');
|
|
84
|
-
}
|
|
85
|
-
catch (err) {
|
|
86
|
-
return (0, cli_js_1.output)({ browserSession: sessionName, error: `Failed to stop process: ${err.message}` }, 1);
|
|
87
|
-
}
|
|
88
|
-
// Wait for the process to exit (up to 10 seconds)
|
|
89
|
-
const maxWait = 40;
|
|
90
|
-
for (let i = 0; i < maxWait; i++) {
|
|
91
|
-
await new Promise((r) => setTimeout(r, 250));
|
|
92
|
-
if (!(0, lock_js_1.isProcessAlive)(lock.pid)) {
|
|
93
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
94
|
-
return (0, cli_js_1.output)({
|
|
95
|
-
browserSession: sessionName,
|
|
96
|
-
pid: lock.pid,
|
|
97
|
-
message: 'Browser session closed.',
|
|
98
|
-
startUrl: lock.url ?? null,
|
|
99
|
-
cdpUrl: lock.cdpUrl ?? null,
|
|
100
|
-
connectionId: lock.connectionId ?? null,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// Force kill if still alive
|
|
105
|
-
try {
|
|
106
|
-
process.kill(lock.pid, 'SIGKILL');
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
// ignore
|
|
110
|
-
}
|
|
111
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
112
|
-
return (0, cli_js_1.output)({
|
|
113
|
-
browserSession: sessionName,
|
|
114
|
-
pid: lock.pid,
|
|
115
|
-
message: 'Browser session force-killed.',
|
|
116
|
-
startUrl: lock.url ?? null,
|
|
117
|
-
cdpUrl: lock.cdpUrl ?? null,
|
|
118
|
-
connectionId: lock.connectionId ?? null,
|
|
119
|
-
});
|
|
120
|
-
}
|
package/dist/cjs/bin/geos.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleGeos = handleGeos;
|
|
4
|
-
const api_helpers_js_1 = require("./api-helpers.js");
|
|
5
|
-
const cli_js_1 = require("./cli.js");
|
|
6
|
-
async function handleGeos() {
|
|
7
|
-
const api = (0, api_helpers_js_1.requireApi)();
|
|
8
|
-
const geos = await api.geos.list();
|
|
9
|
-
return (0, cli_js_1.output)({ geos, count: geos.length });
|
|
10
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* MCP output capture helpers.
|
|
4
|
-
*
|
|
5
|
-
* The CLI handlers call `output()` which normally does `console.log` + `process.exit()`.
|
|
6
|
-
* In MCP mode, we switch `output()` to throw an MCPOutputCapture instead,
|
|
7
|
-
* allowing us to catch and return the data without exiting the process.
|
|
8
|
-
*
|
|
9
|
-
* Uses AsyncLocalStorage so concurrent MCP tool calls don't interfere.
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.MCPOutputCapture = void 0;
|
|
13
|
-
exports.isCapturing = isCapturing;
|
|
14
|
-
exports.captureOutput = captureOutput;
|
|
15
|
-
const node_async_hooks_1 = require("node:async_hooks");
|
|
16
|
-
/**
|
|
17
|
-
* Thrown by output() when in capture mode.
|
|
18
|
-
* Contains the JSON data and exit code that would have been written to stdout.
|
|
19
|
-
*/
|
|
20
|
-
class MCPOutputCapture {
|
|
21
|
-
constructor(data, exitCode) {
|
|
22
|
-
this.data = data;
|
|
23
|
-
this.exitCode = exitCode;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
exports.MCPOutputCapture = MCPOutputCapture;
|
|
27
|
-
const captureContext = new node_async_hooks_1.AsyncLocalStorage();
|
|
28
|
-
function isCapturing() {
|
|
29
|
-
return captureContext.getStore() ?? false;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Run a CLI handler function in capture mode.
|
|
33
|
-
* Returns the data that output() would have written to stdout.
|
|
34
|
-
* Safe for concurrent use — each call gets its own async context.
|
|
35
|
-
*/
|
|
36
|
-
async function captureOutput(fn) {
|
|
37
|
-
return captureContext.run(true, async () => {
|
|
38
|
-
try {
|
|
39
|
-
await fn();
|
|
40
|
-
// Handler completed without calling output() — shouldn't happen for CLI handlers
|
|
41
|
-
return { data: { error: "Handler did not produce output" }, isError: true };
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
if (err instanceof MCPOutputCapture) {
|
|
45
|
-
return {
|
|
46
|
-
data: err.data,
|
|
47
|
-
isError: err.exitCode !== 0,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
// Unexpected error (not from output())
|
|
51
|
-
return {
|
|
52
|
-
data: { error: err instanceof Error ? err.message : String(err) },
|
|
53
|
-
isError: true,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
}
|
package/dist/cjs/bin/open.js
DELETED
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.handleOpen = handleOpen;
|
|
37
|
-
exports.handleOpenDaemon = handleOpenDaemon;
|
|
38
|
-
const AluviaClient_js_1 = require("../client/AluviaClient.js");
|
|
39
|
-
const lock_js_1 = require("../session/lock.js");
|
|
40
|
-
const cli_js_1 = require("./cli.js");
|
|
41
|
-
const node_child_process_1 = require("node:child_process");
|
|
42
|
-
const fs = __importStar(require("node:fs"));
|
|
43
|
-
const path = __importStar(require("node:path"));
|
|
44
|
-
const node_url_1 = require("node:url");
|
|
45
|
-
// Determine the directory of this module at load time
|
|
46
|
-
// @ts-ignore - import.meta.url exists at runtime in ESM
|
|
47
|
-
const thisModuleDir = path.dirname((0, node_url_1.fileURLToPath)(import.meta.url));
|
|
48
|
-
/**
|
|
49
|
-
* Get the path to cli.js for spawning daemon processes.
|
|
50
|
-
* Looks in the same directory as this module (works for both dev and installed).
|
|
51
|
-
*/
|
|
52
|
-
function getCliScriptPath() {
|
|
53
|
-
// cli.js should be in the same directory as open.js
|
|
54
|
-
const cliPath = path.join(thisModuleDir, 'cli.js');
|
|
55
|
-
if (fs.existsSync(cliPath)) {
|
|
56
|
-
return cliPath;
|
|
57
|
-
}
|
|
58
|
-
throw new Error(`Could not find cli.js at ${cliPath}`);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Called from cli.ts when running `session start <url>`.
|
|
62
|
-
* Spawns the actual browser in a detached child and polls until ready.
|
|
63
|
-
* Returns a Promise that resolves via process.exit() (never returns normally).
|
|
64
|
-
*/
|
|
65
|
-
function handleOpen({ url, connectionId, headless, sessionName, autoUnblock, disableBlockDetection, run }) {
|
|
66
|
-
// Generate session name if not provided
|
|
67
|
-
const session = sessionName ?? (0, lock_js_1.generateSessionName)();
|
|
68
|
-
// Validate session name early (before spawning daemon)
|
|
69
|
-
if (sessionName && !(0, lock_js_1.validateSessionName)(sessionName)) {
|
|
70
|
-
(0, cli_js_1.output)({ error: 'Invalid session name. Use only letters, numbers, hyphens, and underscores.' }, 1);
|
|
71
|
-
}
|
|
72
|
-
// Check for existing instance with this session name
|
|
73
|
-
const existing = (0, lock_js_1.readLock)(session);
|
|
74
|
-
if (existing !== null && (0, lock_js_1.isProcessAlive)(existing.pid)) {
|
|
75
|
-
(0, cli_js_1.output)({
|
|
76
|
-
error: `A browser session named '${session}' is already running.`,
|
|
77
|
-
browserSession: session,
|
|
78
|
-
startUrl: existing.url ?? null,
|
|
79
|
-
cdpUrl: existing.cdpUrl ?? null,
|
|
80
|
-
connectionId: existing.connectionId ?? null,
|
|
81
|
-
pid: existing.pid,
|
|
82
|
-
}, 1);
|
|
83
|
-
}
|
|
84
|
-
// Clean up stale lock if process is dead
|
|
85
|
-
if (existing !== null) {
|
|
86
|
-
(0, lock_js_1.removeLock)(session);
|
|
87
|
-
}
|
|
88
|
-
// Require API key
|
|
89
|
-
const apiKey = process.env.ALUVIA_API_KEY;
|
|
90
|
-
if (!apiKey) {
|
|
91
|
-
(0, cli_js_1.output)({ error: 'ALUVIA_API_KEY environment variable is required.' }, 1);
|
|
92
|
-
}
|
|
93
|
-
// Spawn a detached child process that runs the daemon
|
|
94
|
-
const logFile = (0, lock_js_1.getLogFilePath)(session);
|
|
95
|
-
const out = fs.openSync(logFile, 'a');
|
|
96
|
-
const args = ['--daemon', url, '--browser-session', session];
|
|
97
|
-
if (connectionId != null) {
|
|
98
|
-
args.push('--connection-id', String(connectionId));
|
|
99
|
-
}
|
|
100
|
-
if (!headless) {
|
|
101
|
-
args.push('--headful');
|
|
102
|
-
}
|
|
103
|
-
if (autoUnblock) {
|
|
104
|
-
args.push('--auto-unblock');
|
|
105
|
-
}
|
|
106
|
-
if (disableBlockDetection) {
|
|
107
|
-
args.push('--disable-block-detection');
|
|
108
|
-
}
|
|
109
|
-
if (run) {
|
|
110
|
-
args.push('--run', run);
|
|
111
|
-
}
|
|
112
|
-
let child;
|
|
113
|
-
try {
|
|
114
|
-
// Get the path to cli.js in the same directory as this module
|
|
115
|
-
const cliPath = getCliScriptPath();
|
|
116
|
-
child = (0, node_child_process_1.spawn)(process.execPath, [cliPath, ...args], {
|
|
117
|
-
detached: true,
|
|
118
|
-
stdio: ['ignore', out, out],
|
|
119
|
-
env: { ...process.env, ALUVIA_API_KEY: apiKey },
|
|
120
|
-
});
|
|
121
|
-
child.unref();
|
|
122
|
-
}
|
|
123
|
-
catch (err) {
|
|
124
|
-
fs.closeSync(out);
|
|
125
|
-
return (0, cli_js_1.output)({ browserSession: session, error: `Failed to spawn browser process: ${err.message}` }, 1);
|
|
126
|
-
}
|
|
127
|
-
fs.closeSync(out);
|
|
128
|
-
// Wait for the daemon to be fully ready (lock file with ready: true)
|
|
129
|
-
return new Promise((resolve, reject) => {
|
|
130
|
-
let attempts = 0;
|
|
131
|
-
const maxAttempts = 240; // 60 seconds max
|
|
132
|
-
const poll = setInterval(() => {
|
|
133
|
-
attempts++;
|
|
134
|
-
try {
|
|
135
|
-
// Early exit if daemon process died
|
|
136
|
-
if (child.pid && !(0, lock_js_1.isProcessAlive)(child.pid)) {
|
|
137
|
-
clearInterval(poll);
|
|
138
|
-
(0, lock_js_1.removeLock)(session);
|
|
139
|
-
(0, cli_js_1.output)({
|
|
140
|
-
browserSession: session,
|
|
141
|
-
error: 'Browser process exited unexpectedly.',
|
|
142
|
-
logFile,
|
|
143
|
-
}, 1);
|
|
144
|
-
}
|
|
145
|
-
const lock = (0, lock_js_1.readLock)(session);
|
|
146
|
-
if (lock && lock.ready) {
|
|
147
|
-
clearInterval(poll);
|
|
148
|
-
(0, cli_js_1.output)({
|
|
149
|
-
browserSession: session,
|
|
150
|
-
pid: lock.pid,
|
|
151
|
-
startUrl: lock.url ?? null,
|
|
152
|
-
cdpUrl: lock.cdpUrl ?? null,
|
|
153
|
-
connectionId: lock.connectionId ?? null,
|
|
154
|
-
blockDetection: lock.blockDetection ?? false,
|
|
155
|
-
autoUnblock: lock.autoUnblock ?? false,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
if (attempts >= maxAttempts) {
|
|
159
|
-
clearInterval(poll);
|
|
160
|
-
const alive = child.pid ? (0, lock_js_1.isProcessAlive)(child.pid) : false;
|
|
161
|
-
(0, cli_js_1.output)({
|
|
162
|
-
browserSession: session,
|
|
163
|
-
error: alive ? 'Browser is still initializing (timeout).' : 'Browser process exited unexpectedly.',
|
|
164
|
-
logFile,
|
|
165
|
-
}, 1);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
catch (err) {
|
|
169
|
-
// In MCP capture mode, output() throws MCPOutputCapture which we need to propagate
|
|
170
|
-
clearInterval(poll);
|
|
171
|
-
reject(err);
|
|
172
|
-
}
|
|
173
|
-
}, 250);
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Daemon entry point — runs in the detached child process.
|
|
178
|
-
* Starts the proxy + browser, writes lock, and stays alive.
|
|
179
|
-
* Logs go to the daemon log file (stdout is redirected), not to the user.
|
|
180
|
-
*/
|
|
181
|
-
async function handleOpenDaemon({ url, connectionId, headless, sessionName, autoUnblock, disableBlockDetection, run }) {
|
|
182
|
-
const apiKey = process.env.ALUVIA_API_KEY;
|
|
183
|
-
const blockDetectionEnabled = !disableBlockDetection;
|
|
184
|
-
const updateLockWithDetection = (result) => {
|
|
185
|
-
const lock = (0, lock_js_1.readLock)(sessionName);
|
|
186
|
-
if (!lock)
|
|
187
|
-
return;
|
|
188
|
-
const lastDetection = {
|
|
189
|
-
hostname: result.hostname,
|
|
190
|
-
lastUrl: result.url,
|
|
191
|
-
blockStatus: result.blockStatus,
|
|
192
|
-
score: result.score,
|
|
193
|
-
signals: result.signals.map((s) => s.name),
|
|
194
|
-
pass: result.pass,
|
|
195
|
-
persistentBlock: result.persistentBlock,
|
|
196
|
-
timestamp: Date.now(),
|
|
197
|
-
};
|
|
198
|
-
(0, lock_js_1.writeLock)({ ...lock, lastDetection }, sessionName);
|
|
199
|
-
};
|
|
200
|
-
const client = new AluviaClient_js_1.AluviaClient({
|
|
201
|
-
apiKey,
|
|
202
|
-
startPlaywright: true,
|
|
203
|
-
...(connectionId != null ? { connectionId } : {}),
|
|
204
|
-
headless: headless ?? true,
|
|
205
|
-
blockDetection: blockDetectionEnabled
|
|
206
|
-
? autoUnblock
|
|
207
|
-
? { enabled: true, autoUnblock: true, onDetection: updateLockWithDetection }
|
|
208
|
-
: { enabled: true, onDetection: updateLockWithDetection }
|
|
209
|
-
: { enabled: false },
|
|
210
|
-
});
|
|
211
|
-
const connection = await client.start();
|
|
212
|
-
// Write early lock so parent knows daemon is alive
|
|
213
|
-
(0, lock_js_1.writeLock)({ pid: process.pid, session: sessionName, url, proxyUrl: connection.url, blockDetection: blockDetectionEnabled, autoUnblock: blockDetectionEnabled && !!autoUnblock }, sessionName);
|
|
214
|
-
if (autoUnblock)
|
|
215
|
-
console.log('[daemon] Auto-unblock enabled');
|
|
216
|
-
console.log(`[daemon] Browser initialized — proxy: ${connection.url}`);
|
|
217
|
-
if (connection.cdpUrl)
|
|
218
|
-
console.log(`[daemon] CDP URL: ${connection.cdpUrl}`);
|
|
219
|
-
if (connectionId != null)
|
|
220
|
-
console.log(`[daemon] Connection ID: ${connectionId}`);
|
|
221
|
-
if (sessionName)
|
|
222
|
-
console.log(`[daemon] Session: ${sessionName}`);
|
|
223
|
-
console.log(`[daemon] Opening ${url}`);
|
|
224
|
-
// Navigate to URL in the browser
|
|
225
|
-
const page = await connection.browserContext.newPage();
|
|
226
|
-
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
227
|
-
// Gather session info
|
|
228
|
-
const pageTitle = await page.title().catch(() => '');
|
|
229
|
-
const cdpUrl = connection.cdpUrl ?? '';
|
|
230
|
-
// Get connection ID: use the one passed in, or read from ConfigManager
|
|
231
|
-
const connId = connectionId ?? client.connectionId;
|
|
232
|
-
// Write lock file with full session metadata (marks session as ready)
|
|
233
|
-
// Read existing lock first to preserve lastDetection written by the onDetection callback
|
|
234
|
-
const existingLock = (0, lock_js_1.readLock)(sessionName);
|
|
235
|
-
(0, lock_js_1.writeLock)({
|
|
236
|
-
pid: process.pid,
|
|
237
|
-
session: sessionName,
|
|
238
|
-
connectionId: connId,
|
|
239
|
-
cdpUrl,
|
|
240
|
-
proxyUrl: connection.url,
|
|
241
|
-
url,
|
|
242
|
-
ready: true,
|
|
243
|
-
blockDetection: blockDetectionEnabled,
|
|
244
|
-
autoUnblock: blockDetectionEnabled && !!autoUnblock,
|
|
245
|
-
lastDetection: existingLock?.lastDetection,
|
|
246
|
-
}, sessionName);
|
|
247
|
-
console.log(`[daemon] Session ready — session: ${sessionName ?? 'default'}, url: ${url}, cdpUrl: ${cdpUrl}, connectionId: ${connId ?? 'unknown'}, pid: ${process.pid}`);
|
|
248
|
-
if (pageTitle)
|
|
249
|
-
console.log(`[daemon] Page title: ${pageTitle}`);
|
|
250
|
-
// If --run was provided, execute the script and then shut down
|
|
251
|
-
if (run) {
|
|
252
|
-
const scriptPath = path.resolve(run);
|
|
253
|
-
if (!fs.existsSync(scriptPath)) {
|
|
254
|
-
console.error(`[daemon] Script not found: ${scriptPath}`);
|
|
255
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
256
|
-
await connection.close();
|
|
257
|
-
process.exit(1);
|
|
258
|
-
}
|
|
259
|
-
console.log(`[daemon] Running script: ${scriptPath}`);
|
|
260
|
-
// Inject page, browser, context as globals so the script can use them directly
|
|
261
|
-
const browser = connection.browser;
|
|
262
|
-
const context = connection.browserContext;
|
|
263
|
-
globalThis.page = page;
|
|
264
|
-
globalThis.browser = browser;
|
|
265
|
-
globalThis.context = context;
|
|
266
|
-
let exitCode = 0;
|
|
267
|
-
try {
|
|
268
|
-
await Promise.resolve(`${(0, node_url_1.pathToFileURL)(scriptPath).href}`).then(s => __importStar(require(s)));
|
|
269
|
-
}
|
|
270
|
-
catch (err) {
|
|
271
|
-
console.error(`[daemon] Script error: ${err.message}`);
|
|
272
|
-
if (err.stack)
|
|
273
|
-
console.error(err.stack);
|
|
274
|
-
exitCode = 1;
|
|
275
|
-
}
|
|
276
|
-
// Clean up globals
|
|
277
|
-
delete globalThis.page;
|
|
278
|
-
delete globalThis.browser;
|
|
279
|
-
delete globalThis.context;
|
|
280
|
-
console.log(`[daemon] Script finished.`);
|
|
281
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
282
|
-
await connection.close();
|
|
283
|
-
process.exit(exitCode);
|
|
284
|
-
}
|
|
285
|
-
// Graceful shutdown handler
|
|
286
|
-
let stopping = false;
|
|
287
|
-
const shutdown = async () => {
|
|
288
|
-
if (stopping)
|
|
289
|
-
return;
|
|
290
|
-
stopping = true;
|
|
291
|
-
console.log('[daemon] Shutting down...');
|
|
292
|
-
try {
|
|
293
|
-
await connection.close();
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
// ignore
|
|
297
|
-
}
|
|
298
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
299
|
-
console.log('[daemon] Stopped.');
|
|
300
|
-
process.exit(0);
|
|
301
|
-
};
|
|
302
|
-
process.on('SIGINT', shutdown);
|
|
303
|
-
process.on('SIGTERM', shutdown);
|
|
304
|
-
// Detect when the browser is closed by the user
|
|
305
|
-
if (connection.browser) {
|
|
306
|
-
connection.browser.on('disconnected', () => {
|
|
307
|
-
if (!stopping) {
|
|
308
|
-
console.log('[daemon] Browser closed by user.');
|
|
309
|
-
(0, lock_js_1.removeLock)(sessionName);
|
|
310
|
-
client
|
|
311
|
-
.stop()
|
|
312
|
-
.catch(() => { })
|
|
313
|
-
.finally(() => process.exit(0));
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|