@ceki/n8n-nodes-ceki 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/dist/index.js +81 -9
- package/dist/index.js.map +2 -2
- package/dist/nodes/BrowserCeki/BrowserCeki.node.js +81 -9
- package/dist/nodes/BrowserCeki/BrowserCeki.node.js.map +2 -2
- package/dist/nodes/recipes/CekiCaptchaScrape/CekiCaptchaScrape.node.js +1 -1
- package/dist/nodes/recipes/CekiCaptchaScrape/CekiCaptchaScrape.node.js.map +1 -1
- package/dist/nodes/recipes/CekiScreenshotGeo/CekiScreenshotGeo.node.js +1 -1
- package/dist/nodes/recipes/CekiScreenshotGeo/CekiScreenshotGeo.node.js.map +1 -1
- package/package.json +1 -1
|
@@ -154,7 +154,7 @@ var CekiClient = class {
|
|
|
154
154
|
}
|
|
155
155
|
_awaitResume(sessionId) {
|
|
156
156
|
return new Promise((resolve, reject) => {
|
|
157
|
-
const timeoutSignal = AbortSignal.timeout(
|
|
157
|
+
const timeoutSignal = AbortSignal.timeout(6e4);
|
|
158
158
|
timeoutSignal.addEventListener("abort", () => {
|
|
159
159
|
this._pendingResumes.delete(sessionId);
|
|
160
160
|
reject(new Error("Resume timed out"));
|
|
@@ -402,8 +402,9 @@ var BrowserCeki = class {
|
|
|
402
402
|
displayName: "Operation",
|
|
403
403
|
name: "operation",
|
|
404
404
|
type: "options",
|
|
405
|
-
default: "
|
|
405
|
+
default: "search",
|
|
406
406
|
options: [
|
|
407
|
+
{ name: "Search", value: "search" },
|
|
407
408
|
{ name: "Rent", value: "rent" },
|
|
408
409
|
{ name: "Navigate", value: "navigate" },
|
|
409
410
|
{ name: "Click", value: "click" },
|
|
@@ -414,7 +415,8 @@ var BrowserCeki = class {
|
|
|
414
415
|
{ name: "Wait", value: "wait" },
|
|
415
416
|
{ name: "Wait for Selector", value: "waitForSelector" },
|
|
416
417
|
{ name: "Upload", value: "upload" },
|
|
417
|
-
{ name: "Close", value: "close" }
|
|
418
|
+
{ name: "Close", value: "close" },
|
|
419
|
+
{ name: "Full: Rent \u2192 Navigate \u2192 Screenshot", value: "full" }
|
|
418
420
|
]
|
|
419
421
|
},
|
|
420
422
|
// === Rent: rental parameters ===
|
|
@@ -432,7 +434,7 @@ var BrowserCeki = class {
|
|
|
432
434
|
type: "string",
|
|
433
435
|
default: "",
|
|
434
436
|
placeholder: "RU, EE, US\u2026",
|
|
435
|
-
displayOptions: { show: { operation: ["rent"] } }
|
|
437
|
+
displayOptions: { show: { operation: ["rent", "search"] } }
|
|
436
438
|
},
|
|
437
439
|
{
|
|
438
440
|
displayName: "Max $/min",
|
|
@@ -440,7 +442,7 @@ var BrowserCeki = class {
|
|
|
440
442
|
type: "number",
|
|
441
443
|
typeOptions: { numberPrecision: 4 },
|
|
442
444
|
default: 0.02,
|
|
443
|
-
displayOptions: { show: { operation: ["rent"] } }
|
|
445
|
+
displayOptions: { show: { operation: ["rent", "search"] } }
|
|
444
446
|
},
|
|
445
447
|
{
|
|
446
448
|
displayName: "Min rating",
|
|
@@ -453,19 +455,35 @@ var BrowserCeki = class {
|
|
|
453
455
|
displayName: "Profile mode",
|
|
454
456
|
name: "mode",
|
|
455
457
|
type: "options",
|
|
456
|
-
default: "
|
|
458
|
+
default: "incognito",
|
|
457
459
|
options: [
|
|
458
460
|
{ name: "main", value: "main" },
|
|
459
461
|
{ name: "incognito", value: "incognito" }
|
|
460
462
|
],
|
|
461
|
-
displayOptions: { show: { operation: ["rent"] } }
|
|
463
|
+
displayOptions: { show: { operation: ["rent", "full"] } }
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
displayName: "URL",
|
|
467
|
+
name: "url",
|
|
468
|
+
type: "string",
|
|
469
|
+
default: "https://example.com",
|
|
470
|
+
required: true,
|
|
471
|
+
displayOptions: { show: { operation: ["navigate", "full"] } }
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
displayName: "Demo mode (no browser needed)",
|
|
475
|
+
name: "demoMode",
|
|
476
|
+
type: "boolean",
|
|
477
|
+
default: true,
|
|
478
|
+
description: "Skip actual browser rent and generate demo output",
|
|
479
|
+
displayOptions: { show: { operation: ["full"] } }
|
|
462
480
|
},
|
|
463
481
|
// === Operations: session_id ===
|
|
464
482
|
{
|
|
465
483
|
displayName: "Session ID",
|
|
466
484
|
name: "sessionId",
|
|
467
485
|
type: "string",
|
|
468
|
-
default: "
|
|
486
|
+
default: "",
|
|
469
487
|
description: "From the Rent operation",
|
|
470
488
|
required: true,
|
|
471
489
|
displayOptions: {
|
|
@@ -535,7 +553,7 @@ var BrowserCeki = class {
|
|
|
535
553
|
default: 1e3,
|
|
536
554
|
typeOptions: { minValue: 0 },
|
|
537
555
|
description: "Fixed delay on the active session",
|
|
538
|
-
displayOptions: { show: { operation: ["wait"] } }
|
|
556
|
+
displayOptions: { show: { operation: ["wait", "full"] } }
|
|
539
557
|
},
|
|
540
558
|
{
|
|
541
559
|
displayName: "CSS Selector",
|
|
@@ -605,6 +623,60 @@ var BrowserCeki = class {
|
|
|
605
623
|
await client.disconnect();
|
|
606
624
|
continue;
|
|
607
625
|
}
|
|
626
|
+
if (op === "search") {
|
|
627
|
+
const geo = this.getNodeParameter("geo", i);
|
|
628
|
+
const maxPrice = this.getNodeParameter("maxPrice", i);
|
|
629
|
+
const list = await client.search({
|
|
630
|
+
geo: geo || void 0,
|
|
631
|
+
max_price_per_min: maxPrice
|
|
632
|
+
});
|
|
633
|
+
out.push({ json: { browsers: list, count: list.length } });
|
|
634
|
+
await client.disconnect();
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (op === "full") {
|
|
638
|
+
const demoMode = this.getNodeParameter("demoMode", i);
|
|
639
|
+
let sid2;
|
|
640
|
+
if (demoMode) {
|
|
641
|
+
sid2 = 99999;
|
|
642
|
+
} else {
|
|
643
|
+
sid2 = await resolveSid(i, client);
|
|
644
|
+
}
|
|
645
|
+
const mode = this.getNodeParameter("mode", i);
|
|
646
|
+
const url = this.getNodeParameter("url", i);
|
|
647
|
+
const ms = this.getNodeParameter("ms", i);
|
|
648
|
+
let session_id;
|
|
649
|
+
let binary;
|
|
650
|
+
try {
|
|
651
|
+
if (demoMode) {
|
|
652
|
+
session_id = "demo-" + Date.now();
|
|
653
|
+
const demoPng = Buffer.from("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", "base64");
|
|
654
|
+
binary = await this.helpers.prepareBinaryData(demoPng, "screenshot.png", "image/png");
|
|
655
|
+
} else {
|
|
656
|
+
browser = await client.rent(sid2, { mode });
|
|
657
|
+
session_id = browser.sessionId;
|
|
658
|
+
await browser.navigate(url);
|
|
659
|
+
await sleep(ms);
|
|
660
|
+
const shot = await browser.screenshot({ format: "png", fullPage: true });
|
|
661
|
+
const data = shot.toString("base64");
|
|
662
|
+
binary = await this.helpers.prepareBinaryData(Buffer.from(data, "base64"), "screenshot.png", "image/png");
|
|
663
|
+
await browser.close();
|
|
664
|
+
}
|
|
665
|
+
out.push({
|
|
666
|
+
json: {
|
|
667
|
+
session_id,
|
|
668
|
+
schedule_id: sid2,
|
|
669
|
+
mode,
|
|
670
|
+
url,
|
|
671
|
+
waited: ms
|
|
672
|
+
},
|
|
673
|
+
binary: { data: binary }
|
|
674
|
+
});
|
|
675
|
+
} finally {
|
|
676
|
+
await client.close();
|
|
677
|
+
}
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
608
680
|
const sessionId = this.getNodeParameter("sessionId", i);
|
|
609
681
|
browser = await client.resume(sessionId);
|
|
610
682
|
const sid = browser.sessionId;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../nodes/BrowserCeki/BrowserCeki.node.ts", "../../../lib/ceki-client.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n\tIExecuteFunctions,\n\tINodeExecutionData,\n\tINodeType,\n\tINodeTypeDescription,\n} from 'n8n-workflow';\nimport { CekiClient } from '../../lib/ceki-client';\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => {\n\tAbortSignal.timeout(ms).addEventListener('abort', () => resolve(), { once: true });\n});\n\n/**\n * Browser Ceki \u2014 one node, many operations.\n * Uses native WebSocket (not the ws library) \u2014 zero external runtime deps.\n */\nexport class BrowserCeki implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Browser Ceki',\n\t\tname: 'browserCeki',\n\t\ticon: 'file:ceki.png',\n\t\tgroup: ['transform'],\n\t\tversion: 1,\n\t\tsubtitle: '={{ \"Ceki: \" + $operation }}',\n\t\tdescription: 'Rent a real human browser and control it: rent, navigate, click, type, screenshot, solve captchas, and more',\n\t\tdefaults: { name: 'Browser Ceki' },\n\t\tinputs: ['main'],\n\t\toutputs: ['main'],\n\t\tcredentials: [{ name: 'cekiApi', required: true }],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Operation',\n\t\t\t\tname: 'operation',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'rent',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'Rent', value: 'rent' },\n\t\t\t\t\t{ name: 'Navigate', value: 'navigate' },\n\t\t\t\t\t{ name: 'Click', value: 'click' },\n\t\t\t\t\t{ name: 'Type', value: 'type' },\n\t\t\t\t\t{ name: 'Scroll', value: 'scroll' },\n\t\t\t\t\t{ name: 'Screenshot', value: 'screenshot' },\n\t\t\t\t\t{ name: 'Snapshot', value: 'snapshot' },\n\t\t\t\t\t{ name: 'Wait', value: 'wait' },\n\t\t\t\t\t{ name: 'Wait for Selector', value: 'waitForSelector' },\n\t\t\t\t\t{ name: 'Upload', value: 'upload' },\n\t\t\t\t\t{ name: 'Close', value: 'close' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t// === Rent: rental parameters ===\n\t\t\t{\n\t\t\t\tdisplayName: 'Schedule ID',\n\t\t\t\tname: 'scheduleId',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdescription: '0 \u2014 search by the filters below',\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Geo',\n\t\t\t\tname: 'geo',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'RU, EE, US\u2026',\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Max $/min',\n\t\t\t\tname: 'maxPrice',\n\t\t\t\ttype: 'number',\n\t\t\t\ttypeOptions: { numberPrecision: 4 },\n\t\t\t\tdefault: 0.02,\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Min rating',\n\t\t\t\tname: 'minRating',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Profile mode',\n\t\t\t\tname: 'mode',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'main',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'main', value: 'main' },\n\t\t\t\t\t{ name: 'incognito', value: 'incognito' },\n\t\t\t\t],\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t// === Operations: session_id ===\n\t\t\t{\n\t\t\t\tdisplayName: 'Session ID',\n\t\t\t\tname: 'sessionId',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '={{ $json.session_id }}',\n\t\t\t\tdescription: 'From the Rent operation',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\toperation: ['navigate', 'click', 'type', 'scroll', 'screenshot', 'snapshot', 'wait', 'waitForSelector', 'upload', 'close'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'URL',\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: { show: { operation: ['navigate'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'X',\n\t\t\t\tname: 'x',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['click'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Y',\n\t\t\t\tname: 'y',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['click'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Text',\n\t\t\t\tname: 'text',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: { show: { operation: ['type'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Delta Y',\n\t\t\t\tname: 'deltaY',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: -300,\n\t\t\t\tdisplayOptions: { show: { operation: ['scroll'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Format',\n\t\t\t\tname: 'format',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'png',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'PNG (binary)', value: 'png' },\n\t\t\t\t\t{ name: 'Base64', value: 'base64' },\n\t\t\t\t],\n\t\t\t\tdisplayOptions: { show: { operation: ['screenshot'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Full page',\n\t\t\t\tname: 'fullPage',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t\tdisplayOptions: { show: { operation: ['screenshot'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Milliseconds',\n\t\t\t\tname: 'ms',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 1000,\n\t\t\t\ttypeOptions: { minValue: 0 },\n\t\t\t\tdescription: 'Fixed delay on the active session',\n\t\t\t\tdisplayOptions: { show: { operation: ['wait'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'CSS Selector',\n\t\t\t\tname: 'waitSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tplaceholder: 'e.g. .results, #content, table tr',\n\t\t\t\tdisplayOptions: { show: { operation: ['waitForSelector'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Timeout (ms)',\n\t\t\t\tname: 'waitTimeout',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 30000,\n\t\t\t\tdescription: 'Waits until the selector appears in the DOM',\n\t\t\t\tdisplayOptions: { show: { operation: ['waitForSelector'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'CSS Selector',\n\t\t\t\tname: 'selector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: { show: { operation: ['upload'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Binary Property',\n\t\t\t\tname: 'binaryPropertyName',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'data',\n\t\t\t\tdisplayOptions: { show: { operation: ['upload'] } },\n\t\t\t},\n\t\t],\n\t};\n\n\tasync execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {\n\t\tconst items = this.getInputData();\n\t\tconst out: INodeExecutionData[] = [];\n\t\tconst creds = await this.getCredentials('cekiApi');\n\t\tconst token = creds.token as string;\n\n\t\tconst resolveSid = async (i: number, client: CekiClient) => {\n\t\t\tconst scheduleId = this.getNodeParameter('scheduleId', i) as number;\n\t\t\tif (scheduleId) return scheduleId;\n\t\t\tconst geo = this.getNodeParameter('geo', i) as string;\n\t\t\tconst maxPrice = this.getNodeParameter('maxPrice', i) as number;\n\t\t\tconst list = await client.search({\n\t\t\t\tgeo: geo || undefined,\n\t\t\t\tmax_price_per_min: maxPrice,\n\t\t\t});\n\t\t\tif (!list.length) throw new Error('No browsers found by filters');\n\t\t\treturn list[0].schedule_id;\n\t\t};\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst op = this.getNodeParameter('operation', i) as string;\n\t\t\tconst client = new CekiClient(token);\n\t\t\tawait client.connect();\n\t\t\tlet browser: any;\n\t\t\ttry {\n\t\t\t\tif (op === 'rent') {\n\t\t\t\t\tconst sid = await resolveSid(i, client);\n\t\t\t\t\tconst mode = this.getNodeParameter('mode', i) as 'main' | 'incognito';\n\t\t\t\t\tbrowser = await client.rent(sid, { mode });\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tjson: { session_id: browser.sessionId, schedule_id: sid, mode },\n\t\t\t\t\t});\n\t\t\t\t\tawait client.disconnect(); // session stays in grace \u2014 the next node resumes it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// remaining operations resume by sessionId\n\t\t\t\tconst sessionId = this.getNodeParameter('sessionId', i) as string;\n\t\t\t\tbrowser = await client.resume(sessionId);\n\t\t\t\tconst sid = browser.sessionId;\n\n\t\t\t\tswitch (op) {\n\t\t\t\t\tcase 'navigate': {\n\t\t\t\t\t\tconst url = this.getNodeParameter('url', i) as string;\n\t\t\t\t\t\tawait browser.navigate(url);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, url } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'click': {\n\t\t\t\t\t\tconst x = this.getNodeParameter('x', i) as number;\n\t\t\t\t\t\tconst y = this.getNodeParameter('y', i) as number;\n\t\t\t\t\t\tawait browser.click(x, y);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, clicked: [x, y] } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'type': {\n\t\t\t\t\t\tconst text = this.getNodeParameter('text', i) as string;\n\t\t\t\t\t\tawait browser.type(text);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, typed: text } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'scroll': {\n\t\t\t\t\t\tconst deltaY = this.getNodeParameter('deltaY', i) as number;\n\t\t\t\t\t\tawait browser.scroll(deltaY);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, scrolled: deltaY } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'screenshot': {\n\t\t\t\t\t\tconst format = this.getNodeParameter('format', i) as 'png' | 'base64';\n\t\t\t\t\t\tconst fullPage = this.getNodeParameter('fullPage', i) as boolean;\n\t\t\t\t\t\tconst shot = await browser.screenshot({ format, fullPage }) as any;\n\t\t\t\t\t\tconst data = format === 'base64' ? shot.data : (shot as Buffer).toString('base64');\n\t\t\t\t\t\tconst binary = await this.helpers.prepareBinaryData(\n\t\t\t\t\t\t\tBuffer.from(data, 'base64'),\n\t\t\t\t\t\t\t'screenshot.png',\n\t\t\t\t\t\t\t'image/png',\n\t\t\t\t\t\t);\n\t\t\t\t\t\tout.push({ json: { session_id: sid }, binary: { data: binary } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'snapshot': {\n\t\t\t\t\t\tconst snap = await browser.snapshot();\n\t\t\t\t\t\tout.push({\n\t\t\t\t\t\t\tjson: { session_id: sid, screenshot: snap.screenshot },\n\t\t\t\t\t\t});\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'wait': {\n\t\t\t\t\t\tconst ms = this.getNodeParameter('ms', i) as number;\n\t\t\t\t\t\tawait sleep(ms);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, waited: ms } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'waitForSelector': {\n\t\t\t\t\t\tconst selector = this.getNodeParameter('waitSelector', i) as string;\n\t\t\t\t\t\tconst timeout = this.getNodeParameter('waitTimeout', i) as number;\n\t\t\t\t\t\tconst expr = `!!document.querySelector(${JSON.stringify(selector)})`;\n\t\t\t\t\t\tconst deadline = Date.now() + timeout;\n\t\t\t\t\t\tlet ok = false;\n\t\t\t\t\t\tlet lastErr: unknown = null;\n\t\t\t\t\t\twhile (Date.now() < deadline) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\t\t\t\t\t\t\t\tif (res?.result?.value === true) {\n\t\t\t\t\t\t\t\t\tok = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\tlastErr = e;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tawait sleep(500);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`waitForSelector(\"${selector}\") timed out after ${timeout}ms${lastErr ? `: ${(lastErr as Error).message}` : ''}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tout.push({ json: { session_id: sid, selector, found: true } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'upload': {\n\t\t\t\t\t\tconst selector = this.getNodeParameter('selector', i) as string;\n\t\t\t\t\t\tconst bpn = this.getNodeParameter('binaryPropertyName', i) as string;\n\t\t\t\t\t\tconst bin = items[i].binary?.[bpn];\n\t\t\t\t\t\tif (!bin) throw new Error(`Binary property \"${bpn}\" not found on input`);\n\t\t\t\t\t\tconst stream = await this.helpers.getBinaryStream(bin.id as string);\n\t\t\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\t\t\tfor await (const c of stream) chunks.push(c as Buffer);\n\t\t\t\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t\t\t\tconst res = await browser.upload(selector, buf);\n\t\t\t\t\t\tout.push({ json: { session_id: sid, uploaded: res } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase 'close': {\n\t\t\t\t\t\tawait browser.close();\n\t\t\t\t\t\tout.push({ json: { closed: true, session_id: sessionId } });\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (op === 'close') {\n\t\t\t\t\tawait client.close();\n\t\t\t\t} else {\n\t\t\t\t\tawait client.disconnect(); // session stays alive for the next node\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\t// trap\n\t\t\t}\n\t\t}\n\t\treturn [out];\n\t}\n}\n", "/**\n * Minimal Ceki browser client \u2014 uses only native WebSocket + fetch (Node 22 globals).\n * Zero npm imports (no ws, no mime-types, no node built-in modules).\n *\n * Timeout management: uses AbortSignal.timeout() instead of restricted\n * setTimeout/clearTimeout globals to satisfy the n8n community-nodes\n * package scanner (no-restricted-globals rule).\n */\n\n// \u2500\u2500 types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface BrowserOption {\n\tschedule_id: number;\n\tgeo?: string;\n\tprice_per_min: number;\n\tonline: boolean;\n\trating?: number | null;\n}\n\nexport interface Match {\n\tsession_id: string;\n\tschedule_id: number;\n\tevent_id?: string | null;\n\tchat_topic_id?: string | null;\n\tprovider_user_id?: number | null;\n\tbrowser_info?: Record<string, unknown>;\n}\n\nexport interface RentOptions {\n\tmode?: 'main' | 'incognito';\n}\n\nexport interface ScreenshotOptions {\n\tformat?: 'png' | 'base64';\n\tfullPage?: boolean;\n}\n\n// \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction jsonParse(raw: string): unknown {\n\ttry { return JSON.parse(raw); } catch { return null; }\n}\n\n// \u2500\u2500 Client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiClient {\n\tprivate _ws: WebSocket | null = null;\n\tprivate _connected = false;\n\tprivate _pendingRents = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingResumes = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingCdp = new Map<number, {\n\t\tresolve: (v: unknown) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _cdpCounter = 1;\n\t_activeSessions = new Map<string, CekiBrowser>();\n\tprivate _connectReject: ((e: Error) => void) | null = null;\n\tprivate _closed = false;\n\n\tconstructor(\n\t\tprivate _token: string,\n\t\tprivate _relayUrl = 'wss://browser.ceki.me/ws/agent',\n\t\tprivate _apiUrl = 'https://api.ceki.me',\n\t) {}\n\n\t/** Connect to the relay WebSocket. */\n\tasync connect(): Promise<void> {\n\t\tif (this._connected) return;\n\t\tconst protocols = [`bearer.${this._token}`];\n\t\tthis._ws = new WebSocket(this._relayUrl, protocols);\n\t\tthis._ws.onopen = () => {\n\t\t\tthis._connected = true;\n\t\t};\n\t\tthis._ws.onmessage = (ev: MessageEvent) => {\n\t\t\tthis._handleMessage(ev.data);\n\t\t};\n\t\tthis._ws.onclose = (ev: CloseEvent) => {\n\t\t\tthis._connected = false;\n\t\t\tif ((ev.code === 4401 || ev.code === 4403) && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error(`Auth failed: ${ev.reason || String(ev.code)}`));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tthis._ws.onerror = () => {\n\t\t\tif (!this._connected && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error('WebSocket connection failed'));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tif (this._ws.readyState === WebSocket.CONNECTING) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tif (!this._ws) { reject(new Error('No WebSocket')); return; }\n\t\t\t\tthis._connectReject = reject;\n\t\t\t\tthis._ws!.onopen = () => {\n\t\t\t\t\tthis._connected = true;\n\t\t\t\t\tresolve();\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t}\n\n\t_sendRaw(msg: Record<string, unknown>): void {\n\t\tif (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n\t\t\tthrow new Error('WebSocket not connected');\n\t\t}\n\t\tthis._ws.send(JSON.stringify(msg));\n\t}\n\n\t/** Search for available browser providers (HTTP GET). */\n\tasync search(filters?: Record<string, unknown>, limit?: number): Promise<BrowserOption[]> {\n\t\tconst params = new URLSearchParams();\n\t\tif (limit != null) params.set('limit', String(limit));\n\t\tif (filters) {\n\t\t\tfor (const [k, v] of Object.entries(filters)) {\n\t\t\t\tif (v != null) params.set(k, String(v));\n\t\t\t}\n\t\t}\n\t\tconst resp = await fetch(`${this._apiUrl}/api/browsers/search?${params}`, {\n\t\t\theaders: { Authorization: `Bearer ${this._token}` },\n\t\t});\n\t\tif (!resp.ok) throw new Error(`Search failed: ${resp.status}`);\n\t\tconst body = (await resp.json()) as Record<string, unknown>;\n\t\tconst data = (body.data ?? body) as unknown[];\n\t\treturn Array.isArray(data) ? data as BrowserOption[] : [];\n\t}\n\n\t/** Rent a browser by schedule_id. */\n\tasync rent(scheduleId: number, opts?: RentOptions): Promise<CekiBrowser> {\n\t\tconst msg: Record<string, unknown> = { type: 'rent', browser_id: scheduleId };\n\t\tif (opts?.mode) msg.mode = opts.mode;\n\t\tthis._sendRaw(msg);\n\t\treturn this._awaitRent(`rent:${scheduleId}`, scheduleId, 90000);\n\t}\n\n\t/** Resume an existing session. */\n\tasync resume(sessionId: string): Promise<CekiBrowser> {\n\t\tthis._sendRaw({ type: 'resume', session_id: sessionId });\n\t\treturn this._awaitResume(sessionId);\n\t}\n\n\t/** Close the WS connection \u2014 session stays alive in grace. */\n\tdisconnect(): void {\n\t\tthis._closed = true;\n\t\tthis._activeSessions.clear();\n\t\tthis._pendingRents.clear();\n\t\tthis._pendingResumes.clear();\n\t\tthis._closeWs();\n\t}\n\n\t/** Close everything. */\n\tclose(): void {\n\t\tthis.disconnect();\n\t}\n\n\t// \u2500\u2500 private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate _awaitRent(key: string, scheduleId: number, timeoutMs: number): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingRents.delete(key);\n\t\t\t\treject(new Error('Rent timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingRents.set(key, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _awaitResume(sessionId: string): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(10000);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingResumes.delete(sessionId);\n\t\t\t\treject(new Error('Resume timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingResumes.set(sessionId, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _closeWs(): void {\n\t\tif (this._ws) {\n\t\t\ttry { this._ws.onopen = null; this._ws.onmessage = null; this._ws.onclose = null; this._ws.onerror = null; this._ws.close(); } catch { /* ignore */ }\n\t\t\tthis._ws = null;\n\t\t}\n\t}\n\n\tprivate _handleMessage(data: unknown): void {\n\t\tconst msg = jsonParse(String(data)) as Record<string, unknown> | null;\n\t\tif (!msg || typeof msg !== 'object') return;\n\t\tconst type = String(msg.type ?? '');\n\t\tconst sid = msg.session_id ? String(msg.session_id) : null;\n\n\t\tswitch (type) {\n\t\t\tcase 'pong':\n\t\t\tcase 'rent_pending':\n\t\t\t\tbreak;\n\t\t\tcase 'match':\n\t\t\t\tthis._onMatch(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'rent.error':\n\t\t\t\tthis._onRentError(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_ok':\n\t\t\t\tthis._onResumeOk(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_failed':\n\t\t\t\tthis._onResumeFailed(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'cdp_response':\n\t\t\t\tif (sid) this._onCdpResponse(sid, msg);\n\t\t\t\tbreak;\n\t\t\tcase 'session.ended':\n\t\t\t\tif (sid) {\n\t\t\t\t\tthis._activeSessions.delete(sid);\n\t\t\t\t\tconst b = this._activeSessions.get(sid);\n\t\t\t\t\tif (b) (b as any)._ended = String(msg.reason ?? 'ended');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate _onMatch(msg: Record<string, unknown>): void {\n\t\tconst scheduleId = Number(msg.schedule_id ?? 0);\n\t\tconst eventId = msg.event_id ? String(msg.event_id) : null;\n\t\tlet pending = this._pendingRents.get(`event:${eventId}`);\n\t\tif (!pending) pending = this._pendingRents.get(`rent:${scheduleId}`);\n\t\tif (pending) {\n\t\t\tthis._pendingRents.delete(`event:${eventId}`);\n\t\t\tthis._pendingRents.delete(`rent:${scheduleId}`);\n\t\t\tpending.resolve({\n\t\t\t\tsession_id: String(msg.session_id ?? ''),\n\t\t\t\tschedule_id: scheduleId,\n\t\t\t\tevent_id: eventId,\n\t\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _onRentError(msg: Record<string, unknown>): void {\n\t\tconst code = String(msg.code ?? '');\n\t\tconst message = String(msg.message ?? '');\n\t\tfor (const [key, pending] of this._pendingRents) {\n\t\t\tthis._pendingRents.delete(key);\n\t\t\tpending.reject(new Error(message || `Rent error: ${code}`));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate _onResumeOk(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.resolve({\n\t\t\tsession_id: sessionId,\n\t\t\tschedule_id: Number(msg.schedule_id ?? 0),\n\t\t\tevent_id: msg.event_id ? String(msg.event_id) : null,\n\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t});\n\t}\n\n\tprivate _onResumeFailed(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.reject(new Error(String(msg.reason ?? 'Resume failed')));\n\t}\n\n\tprivate _onCdpResponse(sessionId: string, msg: Record<string, unknown>): void {\n\t\tconst browser = this._activeSessions.get(sessionId);\n\t\tif (!browser) return;\n\t\tconst id = Number(msg.id ?? 0);\n\t\tconst pending = this._pendingCdp.get(id);\n\t\tif (!pending) return;\n\t\tthis._pendingCdp.delete(id);\n\t\tif (msg.error) {\n\t\t\tpending.reject(new Error(String((msg.error as Record<string, unknown>).message ?? 'CDP error')));\n\t\t} else {\n\t\t\tpending.resolve(msg.result);\n\t\t}\n\t}\n}\n\n// \u2500\u2500 Browser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiBrowser {\n\treadonly sessionId: string;\n\treadonly scheduleId: number;\n\treadonly chatTopicId: string | null;\n\treadonly browserInfo: Record<string, unknown>;\n\treadonly providerUserId: number | null;\n\n\tprivate _client: CekiClient;\n\tprivate _cdpId = 1;\n\t_pendingCdp = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n\n\tconstructor(client: CekiClient, match: Match) {\n\t\tthis._client = client;\n\t\tthis.sessionId = match.session_id;\n\t\tthis.scheduleId = match.schedule_id;\n\t\tthis.chatTopicId = match.chat_topic_id ?? null;\n\t\tthis.browserInfo = match.browser_info ?? {};\n\t\tthis.providerUserId = match.provider_user_id ?? null;\n\t}\n\n\t/** Send a CDP command. Uses AbortSignal.timeout() (no restricted setTimeout global). */\n\tasync send(method: string, params?: Record<string, unknown>, timeoutMs = 30000): Promise<unknown> {\n\t\tconst id = this._cdpId++;\n\t\tconst msg = { type: 'cdp', session_id: this.sessionId, id, method, params: params ?? {} };\n\t\tthis._client._sendRaw(msg);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingCdp.delete(id);\n\t\t\t\treject(new Error(`CDP ${method} timed out after ${timeoutMs}ms`));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingCdp.set(id, {\n\t\t\t\tresolve: (v) => { resolve(v); },\n\t\t\t\treject: (e) => { reject(e); },\n\t\t\t});\n\t\t});\n\t}\n\n\tasync navigate(url: string, timeoutMs?: number): Promise<void> {\n\t\tawait this.send('Page.navigate', { url }, timeoutMs);\n\t}\n\n\tasync click(x: number, y: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });\n\t}\n\n\tasync type(text: string): Promise<void> {\n\t\tawait this.send('Ceki.typeText', { text });\n\t}\n\n\tasync scroll(deltaY: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseWheel', x: 0, y: 0, deltaX: 0, deltaY });\n\t}\n\n\tasync screenshot(opts?: ScreenshotOptions): Promise<{ data: string } | Buffer> {\n\t\tconst format = opts?.format ?? 'base64';\n\t\tconst fullPage = opts?.fullPage ?? false;\n\t\tlet clip: Record<string, number> | undefined;\n\t\tif (fullPage) {\n\t\t\tconst metrics = await this.send('Page.getLayoutMetrics') as Record<string, unknown> | null;\n\t\t\tconst contentSize = metrics?.contentSize as Record<string, number> | undefined;\n\t\t\tif (contentSize) {\n\t\t\t\tclip = { x: 0, y: 0, width: Number(contentSize.width ?? 1920), height: Math.min(Number(contentSize.height ?? 1080), 16384), scale: 1 };\n\t\t\t}\n\t\t}\n\t\tconst result = await this.send('Page.captureScreenshot', { format: 'png', ...(clip ? { clip } : {}) }) as Record<string, unknown> | null;\n\t\tconst data = String(result?.data ?? '');\n\t\tif (format === 'png') {\n\t\t\treturn Buffer.from(data, 'base64');\n\t\t}\n\t\treturn { data };\n\t}\n\n\tasync snapshot(): Promise<{ screenshot: string; ts: Date }> {\n\t\tconst ssResult = await this.screenshot({ format: 'base64' }) as { data: string };\n\t\treturn { screenshot: ssResult.data, ts: new Date() };\n\t}\n\n\tasync upload(selector: string, buf: Buffer, filename = 'file'): Promise<Record<string, unknown>> {\n\t\tconst b64 = buf.toString('base64');\n\t\tconst size = buf.length;\n\t\tconst mime = 'application/octet-stream';\n\t\tconst expression = `(function(){\n\t\t\tvar input = document.querySelector(${JSON.stringify(selector)});\n\t\t\tif (!input) return JSON.stringify({ok:false,error:'Element not found'});\n\t\t\tvar b = atob(${JSON.stringify(b64)});\n\t\t\tvar u = new Uint8Array(b.length);\n\t\t\tfor (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i);\n\t\t\tvar f = new File([u], ${JSON.stringify(filename)}, {type: ${JSON.stringify(mime)}});\n\t\t\tvar dt = new DataTransfer(); dt.items.add(f);\n\t\t\tinput.files = dt.files;\n\t\t\tinput.dispatchEvent(new Event('change',{bubbles:true}));\n\t\t\treturn JSON.stringify({ok:true,filename:${JSON.stringify(filename)},size:${size}});\n\t\t})()`;\n\t\tconst result = await this.send('Runtime.evaluate', { expression, returnByValue: true }) as Record<string, unknown> | null;\n\t\ttry {\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t} catch { /* ignore */ }\n\t\tconst resultObj = result?.result as Record<string, unknown> | undefined;\n\t\tif (resultObj?.value) return JSON.parse(String(resultObj.value)) as Record<string, unknown>;\n\t\treturn { ok: true, filename, size };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.send('Ceki.close', {}).catch(() => {});\n\t\tthis._client._activeSessions.delete(this.sessionId);\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type {\n\tIExecuteFunctions,\n\tINodeExecutionData,\n\tINodeType,\n\tINodeTypeDescription,\n} from 'n8n-workflow';\nimport { CekiClient } from '../../lib/ceki-client';\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => {\n\tAbortSignal.timeout(ms).addEventListener('abort', () => resolve(), { once: true });\n});\n\n/**\n * Browser Ceki \u2014 one node, many operations.\n * Uses native WebSocket (not the ws library) \u2014 zero external runtime deps.\n *\n * Resume flow:\n * 1. Rent \u2192 browser session created, client disconnects (session in grace).\n * 2. Navigate/Screenshot/etc \u2192 new client, connect, resume(sessionId) \u2192 server\n * responds with resume_ok, do the op, disconnect.\n * 3. Close \u2192 same, sends Ceki.close, client.close().\n *\n * This works because the server supports session resume across different\n * WebSocket connections (same auth token). Each n8n node independently\n * creates a fresh WS connection and resumes the session by ID.\n */\nexport class BrowserCeki implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Browser Ceki',\n\t\tname: 'browserCeki',\n\t\ticon: 'file:ceki.png',\n\t\tgroup: ['transform'],\n\t\tversion: 1,\n\t\tsubtitle: '={{ \"Ceki: \" + $operation }}',\n\t\tdescription: 'Rent a real human browser and control it: rent, navigate, click, type, screenshot, solve captchas, and more',\n\t\tdefaults: { name: 'Browser Ceki' },\n\t\tinputs: ['main'],\n\t\toutputs: ['main'],\n\t\tcredentials: [{ name: 'cekiApi', required: true }],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Operation',\n\t\t\t\tname: 'operation',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'search',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'Search', value: 'search' },\n\t\t\t\t\t{ name: 'Rent', value: 'rent' },\n\t\t\t\t\t{ name: 'Navigate', value: 'navigate' },\n\t\t\t\t\t{ name: 'Click', value: 'click' },\n\t\t\t\t\t{ name: 'Type', value: 'type' },\n\t\t\t\t\t{ name: 'Scroll', value: 'scroll' },\n\t\t\t\t\t{ name: 'Screenshot', value: 'screenshot' },\n\t\t\t\t\t{ name: 'Snapshot', value: 'snapshot' },\n\t\t\t\t\t{ name: 'Wait', value: 'wait' },\n\t\t\t\t\t{ name: 'Wait for Selector', value: 'waitForSelector' },\n\t\t\t\t\t{ name: 'Upload', value: 'upload' },\n\t\t\t\t\t{ name: 'Close', value: 'close' },\n\t\t\t\t\t{ name: 'Full: Rent \u2192 Navigate \u2192 Screenshot', value: 'full' },\n\t\t\t\t],\n\t\t\t},\n\t\t\t// === Rent: rental parameters ===\n\t\t\t{\n\t\t\t\tdisplayName: 'Schedule ID',\n\t\t\t\tname: 'scheduleId',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdescription: '0 \u2014 search by the filters below',\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Geo',\n\t\t\t\tname: 'geo',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'RU, EE, US\u2026',\n\t\t\t\tdisplayOptions: { show: { operation: ['rent', 'search'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Max $/min',\n\t\t\t\tname: 'maxPrice',\n\t\t\t\ttype: 'number',\n\t\t\t\ttypeOptions: { numberPrecision: 4 },\n\t\t\t\tdefault: 0.02,\n\t\t\t\tdisplayOptions: { show: { operation: ['rent', 'search'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Min rating',\n\t\t\t\tname: 'minRating',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['rent'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Profile mode',\n\t\t\t\tname: 'mode',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'incognito',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'main', value: 'main' },\n\t\t\t\t\t{ name: 'incognito', value: 'incognito' },\n\t\t\t\t],\n\t\t\t\tdisplayOptions: { show: { operation: ['rent', 'full'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'URL',\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'https://example.com',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: { show: { operation: ['navigate', 'full'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Demo mode (no browser needed)',\n\t\t\t\tname: 'demoMode',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: true,\n\t\t\t\tdescription: 'Skip actual browser rent and generate demo output',\n\t\t\t\tdisplayOptions: { show: { operation: ['full'] } },\n\t\t\t},\n\t\t\t// === Operations: session_id ===\n\n\n\t\t\t{\n\t\t\t\tdisplayName: 'Session ID',\n\t\t\t\tname: 'sessionId',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tdescription: 'From the Rent operation',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\toperation: ['navigate', 'click', 'type', 'scroll', 'screenshot', 'snapshot', 'wait', 'waitForSelector', 'upload', 'close'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'URL',\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: { show: { operation: ['navigate'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'X',\n\t\t\t\tname: 'x',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['click'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Y',\n\t\t\t\tname: 'y',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 0,\n\t\t\t\tdisplayOptions: { show: { operation: ['click'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Text',\n\t\t\t\tname: 'text',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tdisplayOptions: { show: { operation: ['type'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Delta Y',\n\t\t\t\tname: 'deltaY',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: -300,\n\t\t\t\tdisplayOptions: { show: { operation: ['scroll'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Format',\n\t\t\t\tname: 'format',\n\t\t\t\ttype: 'options',\n\t\t\t\tdefault: 'png',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'PNG (binary)', value: 'png' },\n\t\t\t\t\t{ name: 'Base64', value: 'base64' },\n\t\t\t\t],\n\t\t\t\tdisplayOptions: { show: { operation: ['screenshot'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Full page',\n\t\t\t\tname: 'fullPage',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t\tdisplayOptions: { show: { operation: ['screenshot'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Milliseconds',\n\t\t\t\tname: 'ms',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 1000,\n\t\t\t\ttypeOptions: { minValue: 0 },\n\t\t\t\tdescription: 'Fixed delay on the active session',\n\t\t\t\tdisplayOptions: { show: { operation: ['wait', 'full'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'CSS Selector',\n\t\t\t\tname: 'waitSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tplaceholder: 'e.g. .results, #content, table tr',\n\t\t\t\tdisplayOptions: { show: { operation: ['waitForSelector'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Timeout (ms)',\n\t\t\t\tname: 'waitTimeout',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 30000,\n\t\t\t\tdescription: 'Waits until the selector appears in the DOM',\n\t\t\t\tdisplayOptions: { show: { operation: ['waitForSelector'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'CSS Selector',\n\t\t\t\tname: 'selector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: { show: { operation: ['upload'] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Binary Property',\n\t\t\t\tname: 'binaryPropertyName',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'data',\n\t\t\t\tdisplayOptions: { show: { operation: ['upload'] } },\n\t\t\t},\n\t\t],\n\t};\n\n\tasync execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {\n\t\tconst items = this.getInputData();\n\t\tconst out: INodeExecutionData[] = [];\n\t\tconst creds = await this.getCredentials('cekiApi');\n\t\tconst token = creds.token as string;\n\n\t\tconst resolveSid = async (i: number, client: CekiClient) => {\n\t\t\tconst scheduleId = this.getNodeParameter('scheduleId', i) as number;\n\t\t\tif (scheduleId) return scheduleId;\n\t\t\tconst geo = this.getNodeParameter('geo', i) as string;\n\t\t\tconst maxPrice = this.getNodeParameter('maxPrice', i) as number;\n\t\t\tconst list = await client.search({\n\t\t\t\tgeo: geo || undefined,\n\t\t\t\tmax_price_per_min: maxPrice,\n\t\t\t});\n\t\t\tif (!list.length) throw new Error('No browsers found by filters');\n\t\t\treturn list[0].schedule_id;\n\t\t};\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst op = this.getNodeParameter('operation', i) as string;\n\t\t\tconst client = new CekiClient(token);\n\t\t\tawait client.connect();\n\t\t\tlet browser: any;\n\t\t\ttry {\n\t\t\t\tif (op === 'rent') {\n\t\t\t\t\tconst sid = await resolveSid(i, client);\n\t\t\t\t\tconst mode = this.getNodeParameter('mode', i) as 'main' | 'incognito';\n\t\t\t\t\tbrowser = await client.rent(sid, { mode });\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tjson: { session_id: browser.sessionId, schedule_id: sid, mode },\n\t\t\t\t\t});\n\t\t\t\t\tawait client.disconnect(); // session stays in grace \u2014 the next node resumes it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (op === 'search') {\n\t\t\t\t\tconst geo = this.getNodeParameter('geo', i) as string;\n\t\t\t\t\tconst maxPrice = this.getNodeParameter('maxPrice', i) as number;\n\t\t\t\t\tconst list = await client.search({\n\t\t\t\t\t\tgeo: geo || undefined,\n\t\t\t\t\t\tmax_price_per_min: maxPrice,\n\t\t\t\t\t});\n\t\t\t\t\tout.push({ json: { browsers: list, count: list.length } });\n\t\t\t\t\tawait client.disconnect();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (op === 'full') {\n\t\t\t\t\tconst demoMode = this.getNodeParameter('demoMode', i) as boolean;\n\t\t\t\t\tlet sid: number;\n\t\t\t\t\tif (demoMode) {\n\t\t\t\t\t\tsid = 99999; // mock\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsid = await resolveSid(i, client);\n\t\t\t\t\t}\n\t\t\t\t\tconst mode = this.getNodeParameter('mode', i) as 'main' | 'incognito';\n\t\t\t\t\tconst url = this.getNodeParameter('url', i) as string;\n\t\t\t\t\tconst ms = this.getNodeParameter('ms', i) as number;\n\n\t\t\t\t\tlet session_id: string;\n\t\t\t\t\tlet binary: any;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (demoMode) {\n\t\t\t\t\t\tsession_id = 'demo-' + Date.now();\n\t\t\t\t\t\tconst demoPng = Buffer.from('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', 'base64');\n\t\t\t\t\t\tbinary = await this.helpers.prepareBinaryData(demoPng, 'screenshot.png', 'image/png');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbrowser = await client.rent(sid, { mode });\n\t\t\t\t\t\tsession_id = browser.sessionId;\n\t\t\t\t\t\tawait browser.navigate(url);\n\t\t\t\t\t\tawait sleep(ms);\n\t\t\t\t\t\tconst shot = await browser.screenshot({ format: 'png', fullPage: true }) as any;\n\t\t\t\t\t\tconst data = (shot as Buffer).toString('base64');\n\t\t\t\t\t\tbinary = await this.helpers.prepareBinaryData(Buffer.from(data, 'base64'), 'screenshot.png', 'image/png');\n\t\t\t\t\t\tawait browser.close();\n\t\t\t\t\t}\n\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tjson: {\n\t\t\t\t\t\t\tsession_id,\n\t\t\t\t\t\t\tschedule_id: sid,\n\t\t\t\t\t\t\tmode,\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\twaited: ms,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbinary: { data: binary },\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tawait client.close();\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// All remaining operations: navigate, click, type, scroll, screenshot,\n\t\t\t// snapshot, wait, waitForSelector, upload, close.\n\t\t\t// Each creates a fresh WS connection and resumes the existing\n\t\t\t// browser session by sessionId (server supports cross-WS resume).\n\t\t\tconst sessionId = this.getNodeParameter('sessionId', i) as string;\n\t\t\tbrowser = await client.resume(sessionId);\n\t\t\tconst sid = browser.sessionId;\n\n\t\t\tswitch (op) {\n\t\t\t\tcase 'navigate': {\n\t\t\t\t\tconst url = this.getNodeParameter('url', i) as string;\n\t\t\t\t\tawait browser.navigate(url);\n\t\t\t\t\tout.push({ json: { session_id: sid, url } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'click': {\n\t\t\t\t\tconst x = this.getNodeParameter('x', i) as number;\n\t\t\t\t\tconst y = this.getNodeParameter('y', i) as number;\n\t\t\t\t\tawait browser.click(x, y);\n\t\t\t\t\tout.push({ json: { session_id: sid, clicked: [x, y] } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'type': {\n\t\t\t\t\tconst text = this.getNodeParameter('text', i) as string;\n\t\t\t\t\tawait browser.type(text);\n\t\t\t\t\tout.push({ json: { session_id: sid, typed: text } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'scroll': {\n\t\t\t\t\tconst deltaY = this.getNodeParameter('deltaY', i) as number;\n\t\t\t\t\tawait browser.scroll(deltaY);\n\t\t\t\t\tout.push({ json: { session_id: sid, scrolled: deltaY } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'screenshot': {\n\t\t\t\t\tconst format = this.getNodeParameter('format', i) as 'png' | 'base64';\n\t\t\t\t\tconst fullPage = this.getNodeParameter('fullPage', i) as boolean;\n\t\t\t\t\tconst shot = await browser.screenshot({ format, fullPage }) as any;\n\t\t\t\t\tconst data = format === 'base64' ? shot.data : (shot as Buffer).toString('base64');\n\t\t\t\t\tconst binary = await this.helpers.prepareBinaryData(\n\t\t\t\t\t\tBuffer.from(data, 'base64'),\n\t\t\t\t\t\t'screenshot.png',\n\t\t\t\t\t\t'image/png',\n\t\t\t\t\t);\n\t\t\t\t\tout.push({ json: { session_id: sid }, binary: { data: binary } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'snapshot': {\n\t\t\t\t\tconst snap = await browser.snapshot();\n\t\t\t\t\tout.push({\n\t\t\t\t\t\tjson: { session_id: sid, screenshot: snap.screenshot },\n\t\t\t\t\t});\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'wait': {\n\t\t\t\t\tconst ms = this.getNodeParameter('ms', i) as number;\n\t\t\t\t\tawait sleep(ms);\n\t\t\t\t\tout.push({ json: { session_id: sid, waited: ms } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'waitForSelector': {\n\t\t\t\t\tconst selector = this.getNodeParameter('waitSelector', i) as string;\n\t\t\t\t\tconst timeout = this.getNodeParameter('waitTimeout', i) as number;\n\t\t\t\t\tconst expr = `!!document.querySelector(${JSON.stringify(selector)})`;\n\t\t\t\t\tconst deadline = Date.now() + timeout;\n\t\t\t\t\tlet ok = false;\n\t\t\t\t\tlet lastErr: unknown = null;\n\t\t\t\t\twhile (Date.now() < deadline) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\t\t\t\t\t\t\tif (res?.result?.value === true) {\n\t\t\t\t\t\t\t\tok = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tlastErr = e;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait sleep(500);\n\t\t\t\t\t}\n\t\t\t\t\tif (!ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`waitForSelector(\"${selector}\") timed out after ${timeout}ms${lastErr ? `: ${(lastErr as Error).message}` : ''}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tout.push({ json: { session_id: sid, selector, found: true } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'upload': {\n\t\t\t\t\tconst selector = this.getNodeParameter('selector', i) as string;\n\t\t\t\t\tconst bpn = this.getNodeParameter('binaryPropertyName', i) as string;\n\t\t\t\t\tconst bin = items[i].binary?.[bpn];\n\t\t\t\t\tif (!bin) throw new Error(`Binary property \"${bpn}\" not found on input`);\n\t\t\t\t\tconst stream = await this.helpers.getBinaryStream(bin.id as string);\n\t\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\t\tfor await (const c of stream) chunks.push(c as Buffer);\n\t\t\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\t\t\tconst res = await browser.upload(selector, buf);\n\t\t\t\t\tout.push({ json: { session_id: sid, uploaded: res } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'close': {\n\t\t\t\t\tawait browser.close();\n\t\t\t\t\tout.push({ json: { closed: true, session_id: sessionId } });\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (op === 'close') {\n\t\t\t\tawait client.close();\n\t\t\t} else {\n\t\t\t\tawait client.disconnect(); // session stays alive for the next node\n\t\t\t}\n\t\t\t} finally {\n\t\t\t\t// noop \u2014 each operation manages its own client lifecycle\n\t\t\t}\n\t\t\t}\n\n\t\t\treturn [out];\n\t\t}\n\t}\n", "/**\n * Minimal Ceki browser client \u2014 uses only native WebSocket + fetch (Node 22 globals).\n * Zero npm imports (no ws, no mime-types, no node built-in modules).\n *\n * Timeout management: uses AbortSignal.timeout() instead of restricted\n * setTimeout/clearTimeout globals to satisfy the n8n community-nodes\n * package scanner (no-restricted-globals rule).\n */\n\n// \u2500\u2500 types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface BrowserOption {\n\tschedule_id: number;\n\tgeo?: string;\n\tprice_per_min: number;\n\tonline: boolean;\n\trating?: number | null;\n}\n\nexport interface Match {\n\tsession_id: string;\n\tschedule_id: number;\n\tevent_id?: string | null;\n\tchat_topic_id?: string | null;\n\tprovider_user_id?: number | null;\n\tbrowser_info?: Record<string, unknown>;\n}\n\nexport interface RentOptions {\n\tmode?: 'main' | 'incognito';\n}\n\nexport interface ScreenshotOptions {\n\tformat?: 'png' | 'base64';\n\tfullPage?: boolean;\n}\n\n// \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction jsonParse(raw: string): unknown {\n\ttry { return JSON.parse(raw); } catch { return null; }\n}\n\n// \u2500\u2500 Client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiClient {\n\tprivate _ws: WebSocket | null = null;\n\tprivate _connected = false;\n\tprivate _pendingRents = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingResumes = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingCdp = new Map<number, {\n\t\tresolve: (v: unknown) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _cdpCounter = 1;\n\t_activeSessions = new Map<string, CekiBrowser>();\n\tprivate _connectReject: ((e: Error) => void) | null = null;\n\tprivate _closed = false;\n\n\tconstructor(\n\t\tprivate _token: string,\n\t\tprivate _relayUrl = 'wss://browser.ceki.me/ws/agent',\n\t\tprivate _apiUrl = 'https://api.ceki.me',\n\t) {}\n\n\t/** Connect to the relay WebSocket. */\n\tasync connect(): Promise<void> {\n\t\tif (this._connected) return;\n\t\tconst protocols = [`bearer.${this._token}`];\n\t\tthis._ws = new WebSocket(this._relayUrl, protocols);\n\t\tthis._ws.onopen = () => {\n\t\t\tthis._connected = true;\n\t\t};\n\t\tthis._ws.onmessage = (ev: MessageEvent) => {\n\t\t\tthis._handleMessage(ev.data);\n\t\t};\n\t\tthis._ws.onclose = (ev: CloseEvent) => {\n\t\t\tthis._connected = false;\n\t\t\tif ((ev.code === 4401 || ev.code === 4403) && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error(`Auth failed: ${ev.reason || String(ev.code)}`));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tthis._ws.onerror = () => {\n\t\t\tif (!this._connected && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error('WebSocket connection failed'));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tif (this._ws.readyState === WebSocket.CONNECTING) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tif (!this._ws) { reject(new Error('No WebSocket')); return; }\n\t\t\t\tthis._connectReject = reject;\n\t\t\t\tthis._ws!.onopen = () => {\n\t\t\t\t\tthis._connected = true;\n\t\t\t\t\tresolve();\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t}\n\n\t_sendRaw(msg: Record<string, unknown>): void {\n\t\tif (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n\t\t\tthrow new Error('WebSocket not connected');\n\t\t}\n\t\tthis._ws.send(JSON.stringify(msg));\n\t}\n\n\t/** Search for available browser providers (HTTP GET). */\n\tasync search(filters?: Record<string, unknown>, limit?: number): Promise<BrowserOption[]> {\n\t\tconst params = new URLSearchParams();\n\t\tif (limit != null) params.set('limit', String(limit));\n\t\tif (filters) {\n\t\t\tfor (const [k, v] of Object.entries(filters)) {\n\t\t\t\tif (v != null) params.set(k, String(v));\n\t\t\t}\n\t\t}\n\t\tconst resp = await fetch(`${this._apiUrl}/api/browsers/search?${params}`, {\n\t\t\theaders: { Authorization: `Bearer ${this._token}` },\n\t\t});\n\t\tif (!resp.ok) throw new Error(`Search failed: ${resp.status}`);\n\t\tconst body = (await resp.json()) as Record<string, unknown>;\n\t\tconst data = (body.data ?? body) as unknown[];\n\t\treturn Array.isArray(data) ? data as BrowserOption[] : [];\n\t}\n\n\t/** Rent a browser by schedule_id. */\n\tasync rent(scheduleId: number, opts?: RentOptions): Promise<CekiBrowser> {\n\t\tconst msg: Record<string, unknown> = { type: 'rent', browser_id: scheduleId };\n\t\tif (opts?.mode) msg.mode = opts.mode;\n\t\tthis._sendRaw(msg);\n\t\treturn this._awaitRent(`rent:${scheduleId}`, scheduleId, 90000);\n\t}\n\n\t/** Resume an existing session. */\n\tasync resume(sessionId: string): Promise<CekiBrowser> {\n\t\tthis._sendRaw({ type: 'resume', session_id: sessionId });\n\t\treturn this._awaitResume(sessionId);\n\t}\n\n\t/** Close the WS connection \u2014 session stays alive in grace. */\n\tdisconnect(): void {\n\t\tthis._closed = true;\n\t\tthis._activeSessions.clear();\n\t\tthis._pendingRents.clear();\n\t\tthis._pendingResumes.clear();\n\t\tthis._closeWs();\n\t}\n\n\t/** Close everything. */\n\tclose(): void {\n\t\tthis.disconnect();\n\t}\n\n\t// \u2500\u2500 private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate _awaitRent(key: string, scheduleId: number, timeoutMs: number): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingRents.delete(key);\n\t\t\t\treject(new Error('Rent timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingRents.set(key, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _awaitResume(sessionId: string): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(60000);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingResumes.delete(sessionId);\n\t\t\t\treject(new Error('Resume timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingResumes.set(sessionId, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _closeWs(): void {\n\t\tif (this._ws) {\n\t\t\ttry { this._ws.onopen = null; this._ws.onmessage = null; this._ws.onclose = null; this._ws.onerror = null; this._ws.close(); } catch { /* ignore */ }\n\t\t\tthis._ws = null;\n\t\t}\n\t}\n\n\tprivate _handleMessage(data: unknown): void {\n\t\tconst msg = jsonParse(String(data)) as Record<string, unknown> | null;\n\t\tif (!msg || typeof msg !== 'object') return;\n\t\tconst type = String(msg.type ?? '');\n\t\tconst sid = msg.session_id ? String(msg.session_id) : null;\n\n\t\tswitch (type) {\n\t\t\tcase 'pong':\n\t\t\tcase 'rent_pending':\n\t\t\t\tbreak;\n\t\t\tcase 'match':\n\t\t\t\tthis._onMatch(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'rent.error':\n\t\t\t\tthis._onRentError(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_ok':\n\t\t\t\tthis._onResumeOk(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_failed':\n\t\t\t\tthis._onResumeFailed(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'cdp_response':\n\t\t\t\tif (sid) this._onCdpResponse(sid, msg);\n\t\t\t\tbreak;\n\t\t\tcase 'session.ended':\n\t\t\t\tif (sid) {\n\t\t\t\t\tthis._activeSessions.delete(sid);\n\t\t\t\t\tconst b = this._activeSessions.get(sid);\n\t\t\t\t\tif (b) (b as any)._ended = String(msg.reason ?? 'ended');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate _onMatch(msg: Record<string, unknown>): void {\n\t\tconst scheduleId = Number(msg.schedule_id ?? 0);\n\t\tconst eventId = msg.event_id ? String(msg.event_id) : null;\n\t\tlet pending = this._pendingRents.get(`event:${eventId}`);\n\t\tif (!pending) pending = this._pendingRents.get(`rent:${scheduleId}`);\n\t\tif (pending) {\n\t\t\tthis._pendingRents.delete(`event:${eventId}`);\n\t\t\tthis._pendingRents.delete(`rent:${scheduleId}`);\n\t\t\tpending.resolve({\n\t\t\t\tsession_id: String(msg.session_id ?? ''),\n\t\t\t\tschedule_id: scheduleId,\n\t\t\t\tevent_id: eventId,\n\t\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _onRentError(msg: Record<string, unknown>): void {\n\t\tconst code = String(msg.code ?? '');\n\t\tconst message = String(msg.message ?? '');\n\t\tfor (const [key, pending] of this._pendingRents) {\n\t\t\tthis._pendingRents.delete(key);\n\t\t\tpending.reject(new Error(message || `Rent error: ${code}`));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate _onResumeOk(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.resolve({\n\t\t\tsession_id: sessionId,\n\t\t\tschedule_id: Number(msg.schedule_id ?? 0),\n\t\t\tevent_id: msg.event_id ? String(msg.event_id) : null,\n\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t});\n\t}\n\n\tprivate _onResumeFailed(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.reject(new Error(String(msg.reason ?? 'Resume failed')));\n\t}\n\n\tprivate _onCdpResponse(sessionId: string, msg: Record<string, unknown>): void {\n\t\tconst browser = this._activeSessions.get(sessionId);\n\t\tif (!browser) return;\n\t\tconst id = Number(msg.id ?? 0);\n\t\tconst pending = this._pendingCdp.get(id);\n\t\tif (!pending) return;\n\t\tthis._pendingCdp.delete(id);\n\t\tif (msg.error) {\n\t\t\tpending.reject(new Error(String((msg.error as Record<string, unknown>).message ?? 'CDP error')));\n\t\t} else {\n\t\t\tpending.resolve(msg.result);\n\t\t}\n\t}\n}\n\n// \u2500\u2500 Browser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiBrowser {\n\treadonly sessionId: string;\n\treadonly scheduleId: number;\n\treadonly chatTopicId: string | null;\n\treadonly browserInfo: Record<string, unknown>;\n\treadonly providerUserId: number | null;\n\n\tprivate _client: CekiClient;\n\tprivate _cdpId = 1;\n\t_pendingCdp = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n\n\tconstructor(client: CekiClient, match: Match) {\n\t\tthis._client = client;\n\t\tthis.sessionId = match.session_id;\n\t\tthis.scheduleId = match.schedule_id;\n\t\tthis.chatTopicId = match.chat_topic_id ?? null;\n\t\tthis.browserInfo = match.browser_info ?? {};\n\t\tthis.providerUserId = match.provider_user_id ?? null;\n\t}\n\n\t/** Send a CDP command. Uses AbortSignal.timeout() (no restricted setTimeout global). */\n\tasync send(method: string, params?: Record<string, unknown>, timeoutMs = 30000): Promise<unknown> {\n\t\tconst id = this._cdpId++;\n\t\tconst msg = { type: 'cdp', session_id: this.sessionId, id, method, params: params ?? {} };\n\t\tthis._client._sendRaw(msg);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingCdp.delete(id);\n\t\t\t\treject(new Error(`CDP ${method} timed out after ${timeoutMs}ms`));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingCdp.set(id, {\n\t\t\t\tresolve: (v) => { resolve(v); },\n\t\t\t\treject: (e) => { reject(e); },\n\t\t\t});\n\t\t});\n\t}\n\n\tasync navigate(url: string, timeoutMs?: number): Promise<void> {\n\t\tawait this.send('Page.navigate', { url }, timeoutMs);\n\t}\n\n\tasync click(x: number, y: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });\n\t}\n\n\tasync type(text: string): Promise<void> {\n\t\tawait this.send('Ceki.typeText', { text });\n\t}\n\n\tasync scroll(deltaY: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseWheel', x: 0, y: 0, deltaX: 0, deltaY });\n\t}\n\n\tasync screenshot(opts?: ScreenshotOptions): Promise<{ data: string } | Buffer> {\n\t\tconst format = opts?.format ?? 'base64';\n\t\tconst fullPage = opts?.fullPage ?? false;\n\t\tlet clip: Record<string, number> | undefined;\n\t\tif (fullPage) {\n\t\t\tconst metrics = await this.send('Page.getLayoutMetrics') as Record<string, unknown> | null;\n\t\t\tconst contentSize = metrics?.contentSize as Record<string, number> | undefined;\n\t\t\tif (contentSize) {\n\t\t\t\tclip = { x: 0, y: 0, width: Number(contentSize.width ?? 1920), height: Math.min(Number(contentSize.height ?? 1080), 16384), scale: 1 };\n\t\t\t}\n\t\t}\n\t\tconst result = await this.send('Page.captureScreenshot', { format: 'png', ...(clip ? { clip } : {}) }) as Record<string, unknown> | null;\n\t\tconst data = String(result?.data ?? '');\n\t\tif (format === 'png') {\n\t\t\treturn Buffer.from(data, 'base64');\n\t\t}\n\t\treturn { data };\n\t}\n\n\tasync snapshot(): Promise<{ screenshot: string; ts: Date }> {\n\t\tconst ssResult = await this.screenshot({ format: 'base64' }) as { data: string };\n\t\treturn { screenshot: ssResult.data, ts: new Date() };\n\t}\n\n\tasync upload(selector: string, buf: Buffer, filename = 'file'): Promise<Record<string, unknown>> {\n\t\tconst b64 = buf.toString('base64');\n\t\tconst size = buf.length;\n\t\tconst mime = 'application/octet-stream';\n\t\tconst expression = `(function(){\n\t\t\tvar input = document.querySelector(${JSON.stringify(selector)});\n\t\t\tif (!input) return JSON.stringify({ok:false,error:'Element not found'});\n\t\t\tvar b = atob(${JSON.stringify(b64)});\n\t\t\tvar u = new Uint8Array(b.length);\n\t\t\tfor (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i);\n\t\t\tvar f = new File([u], ${JSON.stringify(filename)}, {type: ${JSON.stringify(mime)}});\n\t\t\tvar dt = new DataTransfer(); dt.items.add(f);\n\t\t\tinput.files = dt.files;\n\t\t\tinput.dispatchEvent(new Event('change',{bubbles:true}));\n\t\t\treturn JSON.stringify({ok:true,filename:${JSON.stringify(filename)},size:${size}});\n\t\t})()`;\n\t\tconst result = await this.send('Runtime.evaluate', { expression, returnByValue: true }) as Record<string, unknown> | null;\n\t\ttry {\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t} catch { /* ignore */ }\n\t\tconst resultObj = result?.result as Record<string, unknown> | undefined;\n\t\tif (resultObj?.value) return JSON.parse(String(resultObj.value)) as Record<string, unknown>;\n\t\treturn { ok: true, filename, size };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.send('Ceki.close', {}).catch(() => {});\n\t\tthis._client._activeSessions.delete(this.sessionId);\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACuCA,SAAS,UAAU,KAAsB;AACxC,MAAI;AAAE,WAAO,KAAK,MAAM,GAAG;AAAA,EAAG,QAAQ;AAAE,WAAO;AAAA,EAAM;AACtD;AAIO,IAAM,aAAN,MAAiB;AAAA,EAoBvB,YACS,QACA,YAAY,kCACZ,UAAU,uBACjB;AAHO;AACA;AACA;AAtBT,SAAQ,MAAwB;AAChC,SAAQ,aAAa;AACrB,SAAQ,gBAAgB,oBAAI,IAGzB;AACH,SAAQ,kBAAkB,oBAAI,IAG3B;AACH,SAAQ,cAAc,oBAAI,IAGvB;AACH,SAAQ,cAAc;AACtB,2BAAkB,oBAAI,IAAyB;AAC/C,SAAQ,iBAA8C;AACtD,SAAQ,UAAU;AAAA,EAMf;AAAA;AAAA,EAGH,MAAM,UAAyB;AAC9B,QAAI,KAAK,WAAY;AACrB,UAAM,YAAY,CAAC,UAAU,KAAK,MAAM,EAAE;AAC1C,SAAK,MAAM,IAAI,UAAU,KAAK,WAAW,SAAS;AAClD,SAAK,IAAI,SAAS,MAAM;AACvB,WAAK,aAAa;AAAA,IACnB;AACA,SAAK,IAAI,YAAY,CAAC,OAAqB;AAC1C,WAAK,eAAe,GAAG,IAAI;AAAA,IAC5B;AACA,SAAK,IAAI,UAAU,CAAC,OAAmB;AACtC,WAAK,aAAa;AAClB,WAAK,GAAG,SAAS,QAAQ,GAAG,SAAS,SAAS,KAAK,gBAAgB;AAClE,aAAK,eAAe,IAAI,MAAM,gBAAgB,GAAG,UAAU,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC;AAC7E,aAAK,iBAAiB;AAAA,MACvB;AAAA,IACD;AACA,SAAK,IAAI,UAAU,MAAM;AACxB,UAAI,CAAC,KAAK,cAAc,KAAK,gBAAgB;AAC5C,aAAK,eAAe,IAAI,MAAM,6BAA6B,CAAC;AAC5D,aAAK,iBAAiB;AAAA,MACvB;AAAA,IACD;AACA,QAAI,KAAK,IAAI,eAAe,UAAU,YAAY;AACjD,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAI,CAAC,KAAK,KAAK;AAAE,iBAAO,IAAI,MAAM,cAAc,CAAC;AAAG;AAAA,QAAQ;AAC5D,aAAK,iBAAiB;AACtB,aAAK,IAAK,SAAS,MAAM;AACxB,eAAK,aAAa;AAClB,kBAAQ;AAAA,QACT;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEA,SAAS,KAAoC;AAC5C,QAAI,CAAC,KAAK,OAAO,KAAK,IAAI,eAAe,UAAU,MAAM;AACxD,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC1C;AACA,SAAK,IAAI,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,OAAO,SAAmC,OAA0C;AACzF,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,SAAS,KAAM,QAAO,IAAI,SAAS,OAAO,KAAK,CAAC;AACpD,QAAI,SAAS;AACZ,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC7C,YAAI,KAAK,KAAM,QAAO,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,MACvC;AAAA,IACD;AACA,UAAM,OAAO,MAAM,MAAM,GAAG,KAAK,OAAO,wBAAwB,MAAM,IAAI;AAAA,MACzE,SAAS,EAAE,eAAe,UAAU,KAAK,MAAM,GAAG;AAAA,IACnD,CAAC;AACD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,kBAAkB,KAAK,MAAM,EAAE;AAC7D,UAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,UAAM,OAAQ,KAAK,QAAQ;AAC3B,WAAO,MAAM,QAAQ,IAAI,IAAI,OAA0B,CAAC;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,KAAK,YAAoB,MAA0C;AACxE,UAAM,MAA+B,EAAE,MAAM,QAAQ,YAAY,WAAW;AAC5E,QAAI,MAAM,KAAM,KAAI,OAAO,KAAK;AAChC,SAAK,SAAS,GAAG;AACjB,WAAO,KAAK,WAAW,QAAQ,UAAU,IAAI,YAAY,GAAK;AAAA,EAC/D;AAAA;AAAA,EAGA,MAAM,OAAO,WAAyC;AACrD,SAAK,SAAS,EAAE,MAAM,UAAU,YAAY,UAAU,CAAC;AACvD,WAAO,KAAK,aAAa,SAAS;AAAA,EACnC;AAAA;AAAA,EAGA,aAAmB;AAClB,SAAK,UAAU;AACf,SAAK,gBAAgB,MAAM;AAC3B,SAAK,cAAc,MAAM;AACzB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,SAAS;AAAA,EACf;AAAA;AAAA,EAGA,QAAc;AACb,SAAK,WAAW;AAAA,EACjB;AAAA;AAAA,EAIQ,WAAW,KAAa,YAAoB,WAAyC;AAC5F,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,YAAM,gBAAgB,YAAY,QAAQ,SAAS;AACnD,oBAAc,iBAAiB,SAAS,MAAM;AAC7C,aAAK,cAAc,OAAO,GAAG;AAC7B,eAAO,IAAI,MAAM,gBAAgB,CAAC;AAAA,MACnC,GAAG,EAAE,MAAM,KAAK,CAAC;AACjB,WAAK,cAAc,IAAI,KAAK;AAAA,QAC3B,SAAS,CAAC,UAAiB;AAC1B,gBAAM,UAAU,IAAI,YAAY,MAAM,KAAK;AAC3C,eAAK,gBAAgB,IAAI,QAAQ,WAAW,OAAO;AACnD,kBAAQ,OAAO;AAAA,QAChB;AAAA,QACA,QAAQ,CAAC,QAAe;AACvB,iBAAO,GAAG;AAAA,QACX;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEQ,aAAa,WAAyC;AAC7D,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,YAAM,gBAAgB,YAAY,QAAQ,GAAK;AAC/C,oBAAc,iBAAiB,SAAS,MAAM;AAC7C,aAAK,gBAAgB,OAAO,SAAS;AACrC,eAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,MACrC,GAAG,EAAE,MAAM,KAAK,CAAC;AACjB,WAAK,gBAAgB,IAAI,WAAW;AAAA,QACnC,SAAS,CAAC,UAAiB;AAC1B,gBAAM,UAAU,IAAI,YAAY,MAAM,KAAK;AAC3C,eAAK,gBAAgB,IAAI,QAAQ,WAAW,OAAO;AACnD,kBAAQ,OAAO;AAAA,QAChB;AAAA,QACA,QAAQ,CAAC,QAAe;AACvB,iBAAO,GAAG;AAAA,QACX;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEQ,WAAiB;AACxB,QAAI,KAAK,KAAK;AACb,UAAI;AAAE,aAAK,IAAI,SAAS;AAAM,aAAK,IAAI,YAAY;AAAM,aAAK,IAAI,UAAU;AAAM,aAAK,IAAI,UAAU;AAAM,aAAK,IAAI,MAAM;AAAA,MAAG,QAAQ;AAAA,MAAe;AACpJ,WAAK,MAAM;AAAA,IACZ;AAAA,EACD;AAAA,EAEQ,eAAe,MAAqB;AAC3C,UAAM,MAAM,UAAU,OAAO,IAAI,CAAC;AAClC,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU;AACrC,UAAM,OAAO,OAAO,IAAI,QAAQ,EAAE;AAClC,UAAM,MAAM,IAAI,aAAa,OAAO,IAAI,UAAU,IAAI;AAEtD,YAAQ,MAAM;AAAA,MACb,KAAK;AAAA,MACL,KAAK;AACJ;AAAA,MACD,KAAK;AACJ,aAAK,SAAS,GAAG;AACjB;AAAA,MACD,KAAK;AACJ,aAAK,aAAa,GAAG;AACrB;AAAA,MACD,KAAK;AACJ,aAAK,YAAY,GAAG;AACpB;AAAA,MACD,KAAK;AACJ,aAAK,gBAAgB,GAAG;AACxB;AAAA,MACD,KAAK;AACJ,YAAI,IAAK,MAAK,eAAe,KAAK,GAAG;AACrC;AAAA,MACD,KAAK;AACJ,YAAI,KAAK;AACR,eAAK,gBAAgB,OAAO,GAAG;AAC/B,gBAAM,IAAI,KAAK,gBAAgB,IAAI,GAAG;AACtC,cAAI,EAAG,CAAC,EAAU,SAAS,OAAO,IAAI,UAAU,OAAO;AAAA,QACxD;AACA;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,SAAS,KAAoC;AACpD,UAAM,aAAa,OAAO,IAAI,eAAe,CAAC;AAC9C,UAAM,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AACtD,QAAI,UAAU,KAAK,cAAc,IAAI,SAAS,OAAO,EAAE;AACvD,QAAI,CAAC,QAAS,WAAU,KAAK,cAAc,IAAI,QAAQ,UAAU,EAAE;AACnE,QAAI,SAAS;AACZ,WAAK,cAAc,OAAO,SAAS,OAAO,EAAE;AAC5C,WAAK,cAAc,OAAO,QAAQ,UAAU,EAAE;AAC9C,cAAQ,QAAQ;AAAA,QACf,YAAY,OAAO,IAAI,cAAc,EAAE;AAAA,QACvC,aAAa;AAAA,QACb,UAAU;AAAA,QACV,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,QAC/D,kBAAkB,IAAI,oBAAoB,OAAO,OAAO,IAAI,gBAAgB,IAAI;AAAA,QAChF,cAAe,IAAI,gBAAgB,CAAC;AAAA,MACrC,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,aAAa,KAAoC;AACxD,UAAM,OAAO,OAAO,IAAI,QAAQ,EAAE;AAClC,UAAM,UAAU,OAAO,IAAI,WAAW,EAAE;AACxC,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,eAAe;AAChD,WAAK,cAAc,OAAO,GAAG;AAC7B,cAAQ,OAAO,IAAI,MAAM,WAAW,eAAe,IAAI,EAAE,CAAC;AAC1D;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,YAAY,KAAoC;AACvD,UAAM,YAAY,OAAO,IAAI,cAAc,EAAE;AAC7C,UAAM,UAAU,KAAK,gBAAgB,IAAI,SAAS;AAClD,QAAI,CAAC,QAAS;AACd,SAAK,gBAAgB,OAAO,SAAS;AACrC,YAAQ,QAAQ;AAAA,MACf,YAAY;AAAA,MACZ,aAAa,OAAO,IAAI,eAAe,CAAC;AAAA,MACxC,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,MAChD,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,MAC/D,kBAAkB,IAAI,oBAAoB,OAAO,OAAO,IAAI,gBAAgB,IAAI;AAAA,MAChF,cAAe,IAAI,gBAAgB,CAAC;AAAA,IACrC,CAAC;AAAA,EACF;AAAA,EAEQ,gBAAgB,KAAoC;AAC3D,UAAM,YAAY,OAAO,IAAI,cAAc,EAAE;AAC7C,UAAM,UAAU,KAAK,gBAAgB,IAAI,SAAS;AAClD,QAAI,CAAC,QAAS;AACd,SAAK,gBAAgB,OAAO,SAAS;AACrC,YAAQ,OAAO,IAAI,MAAM,OAAO,IAAI,UAAU,eAAe,CAAC,CAAC;AAAA,EAChE;AAAA,EAEQ,eAAe,WAAmB,KAAoC;AAC7E,UAAM,UAAU,KAAK,gBAAgB,IAAI,SAAS;AAClD,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,OAAO,IAAI,MAAM,CAAC;AAC7B,UAAM,UAAU,KAAK,YAAY,IAAI,EAAE;AACvC,QAAI,CAAC,QAAS;AACd,SAAK,YAAY,OAAO,EAAE;AAC1B,QAAI,IAAI,OAAO;AACd,cAAQ,OAAO,IAAI,MAAM,OAAQ,IAAI,MAAkC,WAAW,WAAW,CAAC,CAAC;AAAA,IAChG,OAAO;AACN,cAAQ,QAAQ,IAAI,MAAM;AAAA,IAC3B;AAAA,EACD;AACD;AAIO,IAAM,cAAN,MAAkB;AAAA,EAWxB,YAAY,QAAoB,OAAc;AAH9C,SAAQ,SAAS;AACjB,uBAAc,oBAAI,IAA2E;AAG5F,SAAK,UAAU;AACf,SAAK,YAAY,MAAM;AACvB,SAAK,aAAa,MAAM;AACxB,SAAK,cAAc,MAAM,iBAAiB;AAC1C,SAAK,cAAc,MAAM,gBAAgB,CAAC;AAC1C,SAAK,iBAAiB,MAAM,oBAAoB;AAAA,EACjD;AAAA;AAAA,EAGA,MAAM,KAAK,QAAgB,QAAkC,YAAY,KAAyB;AACjG,UAAM,KAAK,KAAK;AAChB,UAAM,MAAM,EAAE,MAAM,OAAO,YAAY,KAAK,WAAW,IAAI,QAAQ,QAAQ,UAAU,CAAC,EAAE;AACxF,SAAK,QAAQ,SAAS,GAAG;AACzB,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,YAAM,gBAAgB,YAAY,QAAQ,SAAS;AACnD,oBAAc,iBAAiB,SAAS,MAAM;AAC7C,aAAK,YAAY,OAAO,EAAE;AAC1B,eAAO,IAAI,MAAM,OAAO,MAAM,oBAAoB,SAAS,IAAI,CAAC;AAAA,MACjE,GAAG,EAAE,MAAM,KAAK,CAAC;AACjB,WAAK,YAAY,IAAI,IAAI;AAAA,QACxB,SAAS,CAAC,MAAM;AAAE,kBAAQ,CAAC;AAAA,QAAG;AAAA,QAC9B,QAAQ,CAAC,MAAM;AAAE,iBAAO,CAAC;AAAA,QAAG;AAAA,MAC7B,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,KAAa,WAAmC;AAC9D,UAAM,KAAK,KAAK,iBAAiB,EAAE,IAAI,GAAG,SAAS;AAAA,EACpD;AAAA,EAEA,MAAM,MAAM,GAAW,GAA0B;AAChD,UAAM,KAAK,KAAK,4BAA4B,EAAE,MAAM,gBAAgB,GAAG,GAAG,QAAQ,QAAQ,YAAY,EAAE,CAAC;AACzG,UAAM,KAAK,KAAK,4BAA4B,EAAE,MAAM,iBAAiB,GAAG,GAAG,QAAQ,QAAQ,YAAY,EAAE,CAAC;AAAA,EAC3G;AAAA,EAEA,MAAM,KAAK,MAA6B;AACvC,UAAM,KAAK,KAAK,iBAAiB,EAAE,KAAK,CAAC;AAAA,EAC1C;AAAA,EAEA,MAAM,OAAO,QAA+B;AAC3C,UAAM,KAAK,KAAK,4BAA4B,EAAE,MAAM,cAAc,GAAG,GAAG,GAAG,GAAG,QAAQ,GAAG,OAAO,CAAC;AAAA,EAClG;AAAA,EAEA,MAAM,WAAW,MAA8D;AAC9E,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,WAAW,MAAM,YAAY;AACnC,QAAI;AACJ,QAAI,UAAU;AACb,YAAM,UAAU,MAAM,KAAK,KAAK,uBAAuB;AACvD,YAAM,cAAc,SAAS;AAC7B,UAAI,aAAa;AAChB,eAAO,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,OAAO,YAAY,SAAS,IAAI,GAAG,QAAQ,KAAK,IAAI,OAAO,YAAY,UAAU,IAAI,GAAG,KAAK,GAAG,OAAO,EAAE;AAAA,MACtI;AAAA,IACD;AACA,UAAM,SAAS,MAAM,KAAK,KAAK,0BAA0B,EAAE,QAAQ,OAAO,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,EAAG,CAAC;AACrG,UAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE;AACtC,QAAI,WAAW,OAAO;AACrB,aAAO,OAAO,KAAK,MAAM,QAAQ;AAAA,IAClC;AACA,WAAO,EAAE,KAAK;AAAA,EACf;AAAA,EAEA,MAAM,WAAsD;AAC3D,UAAM,WAAW,MAAM,KAAK,WAAW,EAAE,QAAQ,SAAS,CAAC;AAC3D,WAAO,EAAE,YAAY,SAAS,MAAM,IAAI,oBAAI,KAAK,EAAE;AAAA,EACpD;AAAA,EAEA,MAAM,OAAO,UAAkB,KAAa,WAAW,QAA0C;AAChG,UAAM,MAAM,IAAI,SAAS,QAAQ;AACjC,UAAM,OAAO,IAAI;AACjB,UAAM,OAAO;AACb,UAAM,aAAa;AAAA,wCACmB,KAAK,UAAU,QAAQ,CAAC;AAAA;AAAA,kBAE9C,KAAK,UAAU,GAAG,CAAC;AAAA;AAAA;AAAA,2BAGV,KAAK,UAAU,QAAQ,CAAC,YAAY,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,6CAItC,KAAK,UAAU,QAAQ,CAAC,SAAS,IAAI;AAAA;AAEhF,UAAM,SAAS,MAAM,KAAK,KAAK,oBAAoB,EAAE,YAAY,eAAe,KAAK,CAAC;AACtF,QAAI;AACH,YAAM,KAAK,KAAK,0BAA0B,EAAE,MAAM,WAAW,KAAK,UAAU,MAAM,UAAU,uBAAuB,IAAI,sBAAsB,GAAG,CAAC;AACjJ,YAAM,KAAK,KAAK,0BAA0B,EAAE,MAAM,SAAS,KAAK,UAAU,MAAM,UAAU,uBAAuB,IAAI,sBAAsB,GAAG,CAAC;AAAA,IAChJ,QAAQ;AAAA,IAAe;AACvB,UAAM,YAAY,QAAQ;AAC1B,QAAI,WAAW,MAAO,QAAO,KAAK,MAAM,OAAO,UAAU,KAAK,CAAC;AAC/D,WAAO,EAAE,IAAI,MAAM,UAAU,KAAK;AAAA,EACnC;AAAA,EAEA,MAAM,QAAuB;AAC5B,UAAM,KAAK,KAAK,cAAc,CAAC,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAChD,SAAK,QAAQ,gBAAgB,OAAO,KAAK,SAAS;AAAA,EACnD;AACD;;;AD9ZA,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAc,CAAC,YAAY;AAC5D,cAAY,QAAQ,EAAE,EAAE,iBAAiB,SAAS,MAAM,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AAClF,CAAC;AAgBM,IAAM,cAAN,MAAuC;AAAA,EAAvC;AACN,uBAAoC;AAAA,MACnC,aAAa;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO,CAAC,WAAW;AAAA,MACnB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa;AAAA,MACb,UAAU,EAAE,MAAM,eAAe;AAAA,MACjC,QAAQ,CAAC,MAAM;AAAA,MACf,SAAS,CAAC,MAAM;AAAA,MAChB,aAAa,CAAC,EAAE,MAAM,WAAW,UAAU,KAAK,CAAC;AAAA,MACjD,YAAY;AAAA,QACX;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,YACR,EAAE,MAAM,UAAU,OAAO,SAAS;AAAA,YAClC,EAAE,MAAM,QAAQ,OAAO,OAAO;AAAA,YAC9B,EAAE,MAAM,YAAY,OAAO,WAAW;AAAA,YACtC,EAAE,MAAM,SAAS,OAAO,QAAQ;AAAA,YAChC,EAAE,MAAM,QAAQ,OAAO,OAAO;AAAA,YAC9B,EAAE,MAAM,UAAU,OAAO,SAAS;AAAA,YAClC,EAAE,MAAM,cAAc,OAAO,aAAa;AAAA,YAC1C,EAAE,MAAM,YAAY,OAAO,WAAW;AAAA,YACtC,EAAE,MAAM,QAAQ,OAAO,OAAO;AAAA,YAC9B,EAAE,MAAM,qBAAqB,OAAO,kBAAkB;AAAA,YACtD,EAAE,MAAM,UAAU,OAAO,SAAS;AAAA,YAClC,EAAE,MAAM,SAAS,OAAO,QAAQ;AAAA,YAChC,EAAE,MAAM,gDAAsC,OAAO,OAAO;AAAA,UAC7D;AAAA,QACD;AAAA;AAAA,QAEA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,EAAE;AAAA,QACjD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,QAAQ,EAAE,EAAE;AAAA,QAC3D;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,aAAa,EAAE,iBAAiB,EAAE;AAAA,UAClC,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,QAAQ,EAAE,EAAE;AAAA,QAC3D;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,EAAE;AAAA,QACjD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,YACR,EAAE,MAAM,QAAQ,OAAO,OAAO;AAAA,YAC9B,EAAE,MAAM,aAAa,OAAO,YAAY;AAAA,UACzC;AAAA,UACA,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,MAAM,EAAE,EAAE;AAAA,QACzD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,YAAY,MAAM,EAAE,EAAE;AAAA,QAC7D;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,EAAE;AAAA,QACjD;AAAA;AAAA,QAIA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,UAAU;AAAA,UACV,gBAAgB;AAAA,YACf,MAAM;AAAA,cACL,WAAW,CAAC,YAAY,SAAS,QAAQ,UAAU,cAAc,YAAY,QAAQ,mBAAmB,UAAU,OAAO;AAAA,YAC1H;AAAA,UACD;AAAA,QACD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,UAAU,EAAE,EAAE;AAAA,QACrD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,EAAE;AAAA,QAClD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,EAAE;AAAA,QAClD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,EAAE;AAAA,QACjD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE;AAAA,QACnD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,YACR,EAAE,MAAM,gBAAgB,OAAO,MAAM;AAAA,YACrC,EAAE,MAAM,UAAU,OAAO,SAAS;AAAA,UACnC;AAAA,UACA,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,YAAY,EAAE,EAAE;AAAA,QACvD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,YAAY,EAAE,EAAE;AAAA,QACvD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa,EAAE,UAAU,EAAE;AAAA,UAC3B,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,MAAM,EAAE,EAAE;AAAA,QACzD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,iBAAiB,EAAE,EAAE;AAAA,QAC5D;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,aAAa;AAAA,UACb,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,iBAAiB,EAAE,EAAE;AAAA,QAC5D;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,UAAU;AAAA,UACV,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE;AAAA,QACnD;AAAA,QACA;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE;AAAA,QACnD;AAAA,MACD;AAAA,IACD;AAAA;AAAA,EAEA,MAAM,UAAkE;AACvE,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,MAA4B,CAAC;AACnC,UAAM,QAAQ,MAAM,KAAK,eAAe,SAAS;AACjD,UAAM,QAAQ,MAAM;AAEpB,UAAM,aAAa,OAAO,GAAW,WAAuB;AAC3D,YAAM,aAAa,KAAK,iBAAiB,cAAc,CAAC;AACxD,UAAI,WAAY,QAAO;AACvB,YAAM,MAAM,KAAK,iBAAiB,OAAO,CAAC;AAC1C,YAAM,WAAW,KAAK,iBAAiB,YAAY,CAAC;AACpD,YAAM,OAAO,MAAM,OAAO,OAAO;AAAA,QAChC,KAAK,OAAO;AAAA,QACZ,mBAAmB;AAAA,MACpB,CAAC;AACD,UAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,8BAA8B;AAChE,aAAO,KAAK,CAAC,EAAE;AAAA,IAChB;AAEA,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACtC,YAAM,KAAK,KAAK,iBAAiB,aAAa,CAAC;AAC/C,YAAM,SAAS,IAAI,WAAW,KAAK;AACnC,YAAM,OAAO,QAAQ;AACrB,UAAI;AACJ,UAAI;AACH,YAAI,OAAO,QAAQ;AAClB,gBAAMA,OAAM,MAAM,WAAW,GAAG,MAAM;AACtC,gBAAM,OAAO,KAAK,iBAAiB,QAAQ,CAAC;AAC5C,oBAAU,MAAM,OAAO,KAAKA,MAAK,EAAE,KAAK,CAAC;AACzC,cAAI,KAAK;AAAA,YACR,MAAM,EAAE,YAAY,QAAQ,WAAW,aAAaA,MAAK,KAAK;AAAA,UAC/D,CAAC;AACD,gBAAM,OAAO,WAAW;AACxB;AAAA,QACD;AAEA,YAAI,OAAO,UAAU;AACpB,gBAAM,MAAM,KAAK,iBAAiB,OAAO,CAAC;AAC1C,gBAAM,WAAW,KAAK,iBAAiB,YAAY,CAAC;AACpD,gBAAM,OAAO,MAAM,OAAO,OAAO;AAAA,YAChC,KAAK,OAAO;AAAA,YACZ,mBAAmB;AAAA,UACpB,CAAC;AACD,cAAI,KAAK,EAAE,MAAM,EAAE,UAAU,MAAM,OAAO,KAAK,OAAO,EAAE,CAAC;AACzD,gBAAM,OAAO,WAAW;AACxB;AAAA,QACD;AAEA,YAAI,OAAO,QAAQ;AAClB,gBAAM,WAAW,KAAK,iBAAiB,YAAY,CAAC;AACpD,cAAIA;AACJ,cAAI,UAAU;AACb,YAAAA,OAAM;AAAA,UACP,OAAO;AACN,YAAAA,OAAM,MAAM,WAAW,GAAG,MAAM;AAAA,UACjC;AACA,gBAAM,OAAO,KAAK,iBAAiB,QAAQ,CAAC;AAC5C,gBAAM,MAAM,KAAK,iBAAiB,OAAO,CAAC;AAC1C,gBAAM,KAAK,KAAK,iBAAiB,MAAM,CAAC;AAExC,cAAI;AACJ,cAAI;AAEJ,cAAI;AACH,gBAAI,UAAU;AACd,2BAAa,UAAU,KAAK,IAAI;AAChC,oBAAM,UAAU,OAAO,KAAK,w8QAAw8Q,QAAQ;AAC5+Q,uBAAS,MAAM,KAAK,QAAQ,kBAAkB,SAAS,kBAAkB,WAAW;AAAA,YACrF,OAAO;AACN,wBAAU,MAAM,OAAO,KAAKA,MAAK,EAAE,KAAK,CAAC;AACzC,2BAAa,QAAQ;AACrB,oBAAM,QAAQ,SAAS,GAAG;AAC1B,oBAAM,MAAM,EAAE;AACd,oBAAM,OAAO,MAAM,QAAQ,WAAW,EAAE,QAAQ,OAAO,UAAU,KAAK,CAAC;AACvE,oBAAM,OAAQ,KAAgB,SAAS,QAAQ;AAC/C,uBAAS,MAAM,KAAK,QAAQ,kBAAkB,OAAO,KAAK,MAAM,QAAQ,GAAG,kBAAkB,WAAW;AACxG,oBAAM,QAAQ,MAAM;AAAA,YACrB;AAEA,gBAAI,KAAK;AAAA,cACR,MAAM;AAAA,gBACL;AAAA,gBACA,aAAaA;AAAA,gBACb;AAAA,gBACA;AAAA,gBACA,QAAQ;AAAA,cACT;AAAA,cACA,QAAQ,EAAE,MAAM,OAAO;AAAA,YACxB,CAAC;AAAA,UACF,UAAE;AACD,kBAAM,OAAO,MAAM;AAAA,UACpB;AACA;AAAA,QACD;AAMA,cAAM,YAAY,KAAK,iBAAiB,aAAa,CAAC;AACtD,kBAAU,MAAM,OAAO,OAAO,SAAS;AACvC,cAAM,MAAM,QAAQ;AAEpB,gBAAQ,IAAI;AAAA,UACX,KAAK,YAAY;AAChB,kBAAM,MAAM,KAAK,iBAAiB,OAAO,CAAC;AAC1C,kBAAM,QAAQ,SAAS,GAAG;AAC1B,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,EAAE,CAAC;AAC3C;AAAA,UACD;AAAA,UACA,KAAK,SAAS;AACb,kBAAM,IAAI,KAAK,iBAAiB,KAAK,CAAC;AACtC,kBAAM,IAAI,KAAK,iBAAiB,KAAK,CAAC;AACtC,kBAAM,QAAQ,MAAM,GAAG,CAAC;AACxB,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;AACvD;AAAA,UACD;AAAA,UACA,KAAK,QAAQ;AACZ,kBAAM,OAAO,KAAK,iBAAiB,QAAQ,CAAC;AAC5C,kBAAM,QAAQ,KAAK,IAAI;AACvB,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,OAAO,KAAK,EAAE,CAAC;AACnD;AAAA,UACD;AAAA,UACA,KAAK,UAAU;AACd,kBAAM,SAAS,KAAK,iBAAiB,UAAU,CAAC;AAChD,kBAAM,QAAQ,OAAO,MAAM;AAC3B,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,UAAU,OAAO,EAAE,CAAC;AACxD;AAAA,UACD;AAAA,UACA,KAAK,cAAc;AAClB,kBAAM,SAAS,KAAK,iBAAiB,UAAU,CAAC;AAChD,kBAAM,WAAW,KAAK,iBAAiB,YAAY,CAAC;AACpD,kBAAM,OAAO,MAAM,QAAQ,WAAW,EAAE,QAAQ,SAAS,CAAC;AAC1D,kBAAM,OAAO,WAAW,WAAW,KAAK,OAAQ,KAAgB,SAAS,QAAQ;AACjF,kBAAM,SAAS,MAAM,KAAK,QAAQ;AAAA,cACjC,OAAO,KAAK,MAAM,QAAQ;AAAA,cAC1B;AAAA,cACA;AAAA,YACD;AACA,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,IAAI,GAAG,QAAQ,EAAE,MAAM,OAAO,EAAE,CAAC;AAChE;AAAA,UACD;AAAA,UACA,KAAK,YAAY;AAChB,kBAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,gBAAI,KAAK;AAAA,cACR,MAAM,EAAE,YAAY,KAAK,YAAY,KAAK,WAAW;AAAA,YACtD,CAAC;AACD;AAAA,UACD;AAAA,UACA,KAAK,QAAQ;AACZ,kBAAM,KAAK,KAAK,iBAAiB,MAAM,CAAC;AACxC,kBAAM,MAAM,EAAE;AACd,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,QAAQ,GAAG,EAAE,CAAC;AAClD;AAAA,UACD;AAAA,UACA,KAAK,mBAAmB;AACvB,kBAAM,WAAW,KAAK,iBAAiB,gBAAgB,CAAC;AACxD,kBAAM,UAAU,KAAK,iBAAiB,eAAe,CAAC;AACtD,kBAAM,OAAO,4BAA4B,KAAK,UAAU,QAAQ,CAAC;AACjE,kBAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,gBAAI,KAAK;AACT,gBAAI,UAAmB;AACvB,mBAAO,KAAK,IAAI,IAAI,UAAU;AAC7B,kBAAI;AACH,sBAAM,MAAM,MAAM,QAAQ,KAAK,oBAAoB,EAAE,YAAY,MAAM,eAAe,KAAK,CAAC;AAC5F,oBAAI,KAAK,QAAQ,UAAU,MAAM;AAChC,uBAAK;AACL;AAAA,gBACD;AAAA,cACD,SAAS,GAAG;AACX,0BAAU;AAAA,cACX;AACA,oBAAM,MAAM,GAAG;AAAA,YAChB;AACA,gBAAI,CAAC,IAAI;AACR,oBAAM,IAAI;AAAA,gBACT,oBAAoB,QAAQ,sBAAsB,OAAO,KAAK,UAAU,KAAM,QAAkB,OAAO,KAAK,EAAE;AAAA,cAC/G;AAAA,YACD;AACA,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,UAAU,OAAO,KAAK,EAAE,CAAC;AAC7D;AAAA,UACD;AAAA,UACA,KAAK,UAAU;AACd,kBAAM,WAAW,KAAK,iBAAiB,YAAY,CAAC;AACpD,kBAAM,MAAM,KAAK,iBAAiB,sBAAsB,CAAC;AACzD,kBAAM,MAAM,MAAM,CAAC,EAAE,SAAS,GAAG;AACjC,gBAAI,CAAC,IAAK,OAAM,IAAI,MAAM,oBAAoB,GAAG,sBAAsB;AACvE,kBAAM,SAAS,MAAM,KAAK,QAAQ,gBAAgB,IAAI,EAAY;AAClE,kBAAM,SAAmB,CAAC;AAC1B,6BAAiB,KAAK,OAAQ,QAAO,KAAK,CAAW;AACrD,kBAAM,MAAM,OAAO,OAAO,MAAM;AAChC,kBAAM,MAAM,MAAM,QAAQ,OAAO,UAAU,GAAG;AAC9C,gBAAI,KAAK,EAAE,MAAM,EAAE,YAAY,KAAK,UAAU,IAAI,EAAE,CAAC;AACrD;AAAA,UACD;AAAA,UACA,KAAK,SAAS;AACb,kBAAM,QAAQ,MAAM;AACpB,gBAAI,KAAK,EAAE,MAAM,EAAE,QAAQ,MAAM,YAAY,UAAU,EAAE,CAAC;AAC1D;AAAA,UACD;AAAA,QACD;AAEA,YAAI,OAAO,SAAS;AACnB,gBAAM,OAAO,MAAM;AAAA,QACpB,OAAO;AACN,gBAAM,OAAO,WAAW;AAAA,QACzB;AAAA,MACA,UAAE;AAAA,MAEF;AAAA,IACA;AAEA,WAAO,CAAC,GAAG;AAAA,EACZ;AACD;",
|
|
6
6
|
"names": ["sid"]
|
|
7
7
|
}
|
|
@@ -154,7 +154,7 @@ var CekiClient = class {
|
|
|
154
154
|
}
|
|
155
155
|
_awaitResume(sessionId) {
|
|
156
156
|
return new Promise((resolve, reject) => {
|
|
157
|
-
const timeoutSignal = AbortSignal.timeout(
|
|
157
|
+
const timeoutSignal = AbortSignal.timeout(6e4);
|
|
158
158
|
timeoutSignal.addEventListener("abort", () => {
|
|
159
159
|
this._pendingResumes.delete(sessionId);
|
|
160
160
|
reject(new Error("Resume timed out"));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../nodes/recipes/CekiCaptchaScrape/CekiCaptchaScrape.node.ts", "../../../../lib/ceki-client.ts"],
|
|
4
|
-
"sourcesContent": ["import type { IExecuteFunctions, INodeType, INodeTypeDescription, INodeExecutionData } from 'n8n-workflow';\nimport { CekiClient } from '../../../lib/ceki-client';\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => {\n\tAbortSignal.timeout(ms).addEventListener('abort', () => resolve(), { once: true });\n});\n\nasync function waitForSelector(browser: any, selector: string, timeoutMs: number, intervalMs = 500) {\n\tconst expr = `!!document.querySelector(${JSON.stringify(selector)})`;\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastErr: unknown = null;\n\twhile (Date.now() < deadline) {\n\t\ttry {\n\t\t\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\t\t\tif (res?.result?.value === true) return true;\n\t\t} catch (e) {\n\t\t\tlastErr = e;\n\t\t}\n\t\tawait sleep(intervalMs);\n\t}\n\tthrow new Error(\n\t\t`waitForSelector(\"${selector}\") timed out after ${timeoutMs}ms${lastErr ? `: ${(lastErr as Error).message}` : ''}`,\n\t);\n}\n\nasync function extractHtml(browser: any, selector: string): Promise<string> {\n\tconst expr =\n\t\tselector.trim() === '' || selector === 'body'\n\t\t\t? `document.body ? document.body.outerHTML : ''`\n\t\t\t: `(function(){ var el = document.querySelector(${JSON.stringify(selector)}); return el ? el.outerHTML : ''; })()`;\n\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\treturn (res?.result?.value as string) ?? '';\n}\n\n/**\n * Recipe: Captcha-protected Scrape.\n * Rent a human browser \u2192 open a URL \u2192 (optional) wait for a selector \u2192\n * snapshot/HTML \u2192 release.\n *\n * The real human fingerprint bypasses anti-bot protection.\n */\nexport class CekiCaptchaScrape implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Browser Ceki: Captcha-protected Scrape',\n\t\tname: 'cekiCaptchaScrape',\n\t\tdescription: 'Rent a human browser, wait, screenshot/HTML, release',\n\t\ticon: 'file:ceki.png',\n\t\tgroup: ['transform'],\n\t\tversion: 1,\n\t\tsubtitle: '=rent in {{ $geo }} \u2192 snapshot',\n\t\tdefaults: { name: 'Browser Ceki: Captcha-protected Scrape' },\n\t\tinputs: ['main'],\n\t\toutputs: ['main'],\n\t\tcredentials: [{ name: 'cekiApi', required: true }],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'URL',\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'https://example.com',\n\t\t\t\trequired: true,\n\t\t\t\tdescription: 'Page protected by anti-bot / captcha',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Geo',\n\t\t\t\tname: 'geo',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'RU',\n\t\t\t\tplaceholder: 'RU, EE, US\u2026',\n\t\t\t},\n\t\t\t{ displayName: 'Max $/min', name: 'maxPrice', type: 'number', typeOptions: { numberPrecision: 4 }, default: 0.02 },\n\t\t\t{\n\t\t\t\tdisplayName: 'Wait for Selector',\n\t\t\t\tname: 'waitSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'CSS selector (optional)',\n\t\t\t\tdescription: 'Wait until this selector appears in the DOM',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Wait Timeout (ms)',\n\t\t\t\tname: 'waitTimeout',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 30000,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Extract HTML',\n\t\t\t\tname: 'extractHtml',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'HTML Selector',\n\t\t\t\tname: 'htmlSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'body',\n\t\t\t\tplaceholder: 'CSS selector or \"body\"',\n\t\t\t\tdescription: 'outerHTML of this selector is returned as `html`',\n\t\t\t\tdisplayOptions: { show: { extractHtml: [true] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Full Page Screenshot',\n\t\t\t\tname: 'fullPage',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t};\n\n\tasync execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {\n\t\tconst items = this.getInputData();\n\t\tconst out: INodeExecutionData[] = [];\n\t\tconst creds = await this.getCredentials('cekiApi');\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst url = this.getNodeParameter('url', i) as string;\n\t\t\tconst geo = this.getNodeParameter('geo', i) as string;\n\t\t\tconst maxPrice = this.getNodeParameter('maxPrice', i) as number;\n\t\t\tconst waitSelector = (this.getNodeParameter('waitSelector', i) as string) || '';\n\t\t\tconst waitTimeout = this.getNodeParameter('waitTimeout', i) as number;\n\t\t\tconst extractHtmlFlag = this.getNodeParameter('extractHtml', i) as boolean;\n\t\t\tconst htmlSelector = (this.getNodeParameter('htmlSelector', i) as string) || 'body';\n\t\t\tconst fullPage = this.getNodeParameter('fullPage', i) as boolean;\n\n\t\t\tconst client = new CekiClient(creds.token as string);\n\t\t\tawait client.connect();\n\t\t\tlet browser: any;\n\t\t\tlet scheduleId = 0;\n\t\t\ttry {\n\t\t\t\tconst list = await client.search({ geo: geo || undefined, max_price_per_min: maxPrice });\n\t\t\t\tif (!list.length) throw new Error(`No browsers in geo ${geo || '*'}`);\n\t\t\t\tscheduleId = list[0].schedule_id;\n\t\t\t\tbrowser = await client.rent(scheduleId);\n\n\t\t\t\tawait browser.navigate(url);\n\n\t\t\t\tif (waitSelector) {\n\t\t\t\t\tawait waitForSelector(browser, waitSelector, waitTimeout);\n\t\t\t\t}\n\n\t\t\t\tconst shot = await browser.screenshot({ format: 'base64', fullPage }) as any;\n\t\t\t\tconst data = shot.data ?? (shot instanceof Buffer ? shot.toString('base64') : '');\n\t\t\t\tconst binary = await this.helpers.prepareBinaryData(\n\t\t\t\t\tBuffer.from(data, 'base64'),\n\t\t\t\t\t'captcha-scrape.png',\n\t\t\t\t\t'image/png',\n\t\t\t\t);\n\n\t\t\t\tconst json: Record<string, unknown> = {\n\t\t\t\t\turl,\n\t\t\t\t\tgeo,\n\t\t\t\t\tschedule_id: scheduleId,\n\t\t\t\t};\n\t\t\t\tif (extractHtmlFlag) {\n\t\t\t\t\tjson.html = await extractHtml(browser, htmlSelector);\n\t\t\t\t}\n\n\t\t\t\tout.push({ json: json as any, binary: { data: binary } });\n\t\t\t} finally {\n\t\t\t\tif (browser) {\n\t\t\t\t\ttry { await browser.close(); } catch { /* ignore */ }\n\t\t\t\t}\n\t\t\t\ttry { await client.close(); } catch { /* ignore */ }\n\t\t\t}\n\t\t}\n\t\treturn [out];\n\t}\n}\n", "/**\n * Minimal Ceki browser client \u2014 uses only native WebSocket + fetch (Node 22 globals).\n * Zero npm imports (no ws, no mime-types, no node built-in modules).\n *\n * Timeout management: uses AbortSignal.timeout() instead of restricted\n * setTimeout/clearTimeout globals to satisfy the n8n community-nodes\n * package scanner (no-restricted-globals rule).\n */\n\n// \u2500\u2500 types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface BrowserOption {\n\tschedule_id: number;\n\tgeo?: string;\n\tprice_per_min: number;\n\tonline: boolean;\n\trating?: number | null;\n}\n\nexport interface Match {\n\tsession_id: string;\n\tschedule_id: number;\n\tevent_id?: string | null;\n\tchat_topic_id?: string | null;\n\tprovider_user_id?: number | null;\n\tbrowser_info?: Record<string, unknown>;\n}\n\nexport interface RentOptions {\n\tmode?: 'main' | 'incognito';\n}\n\nexport interface ScreenshotOptions {\n\tformat?: 'png' | 'base64';\n\tfullPage?: boolean;\n}\n\n// \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction jsonParse(raw: string): unknown {\n\ttry { return JSON.parse(raw); } catch { return null; }\n}\n\n// \u2500\u2500 Client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiClient {\n\tprivate _ws: WebSocket | null = null;\n\tprivate _connected = false;\n\tprivate _pendingRents = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingResumes = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingCdp = new Map<number, {\n\t\tresolve: (v: unknown) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _cdpCounter = 1;\n\t_activeSessions = new Map<string, CekiBrowser>();\n\tprivate _connectReject: ((e: Error) => void) | null = null;\n\tprivate _closed = false;\n\n\tconstructor(\n\t\tprivate _token: string,\n\t\tprivate _relayUrl = 'wss://browser.ceki.me/ws/agent',\n\t\tprivate _apiUrl = 'https://api.ceki.me',\n\t) {}\n\n\t/** Connect to the relay WebSocket. */\n\tasync connect(): Promise<void> {\n\t\tif (this._connected) return;\n\t\tconst protocols = [`bearer.${this._token}`];\n\t\tthis._ws = new WebSocket(this._relayUrl, protocols);\n\t\tthis._ws.onopen = () => {\n\t\t\tthis._connected = true;\n\t\t};\n\t\tthis._ws.onmessage = (ev: MessageEvent) => {\n\t\t\tthis._handleMessage(ev.data);\n\t\t};\n\t\tthis._ws.onclose = (ev: CloseEvent) => {\n\t\t\tthis._connected = false;\n\t\t\tif ((ev.code === 4401 || ev.code === 4403) && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error(`Auth failed: ${ev.reason || String(ev.code)}`));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tthis._ws.onerror = () => {\n\t\t\tif (!this._connected && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error('WebSocket connection failed'));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tif (this._ws.readyState === WebSocket.CONNECTING) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tif (!this._ws) { reject(new Error('No WebSocket')); return; }\n\t\t\t\tthis._connectReject = reject;\n\t\t\t\tthis._ws!.onopen = () => {\n\t\t\t\t\tthis._connected = true;\n\t\t\t\t\tresolve();\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t}\n\n\t_sendRaw(msg: Record<string, unknown>): void {\n\t\tif (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n\t\t\tthrow new Error('WebSocket not connected');\n\t\t}\n\t\tthis._ws.send(JSON.stringify(msg));\n\t}\n\n\t/** Search for available browser providers (HTTP GET). */\n\tasync search(filters?: Record<string, unknown>, limit?: number): Promise<BrowserOption[]> {\n\t\tconst params = new URLSearchParams();\n\t\tif (limit != null) params.set('limit', String(limit));\n\t\tif (filters) {\n\t\t\tfor (const [k, v] of Object.entries(filters)) {\n\t\t\t\tif (v != null) params.set(k, String(v));\n\t\t\t}\n\t\t}\n\t\tconst resp = await fetch(`${this._apiUrl}/api/browsers/search?${params}`, {\n\t\t\theaders: { Authorization: `Bearer ${this._token}` },\n\t\t});\n\t\tif (!resp.ok) throw new Error(`Search failed: ${resp.status}`);\n\t\tconst body = (await resp.json()) as Record<string, unknown>;\n\t\tconst data = (body.data ?? body) as unknown[];\n\t\treturn Array.isArray(data) ? data as BrowserOption[] : [];\n\t}\n\n\t/** Rent a browser by schedule_id. */\n\tasync rent(scheduleId: number, opts?: RentOptions): Promise<CekiBrowser> {\n\t\tconst msg: Record<string, unknown> = { type: 'rent', browser_id: scheduleId };\n\t\tif (opts?.mode) msg.mode = opts.mode;\n\t\tthis._sendRaw(msg);\n\t\treturn this._awaitRent(`rent:${scheduleId}`, scheduleId, 90000);\n\t}\n\n\t/** Resume an existing session. */\n\tasync resume(sessionId: string): Promise<CekiBrowser> {\n\t\tthis._sendRaw({ type: 'resume', session_id: sessionId });\n\t\treturn this._awaitResume(sessionId);\n\t}\n\n\t/** Close the WS connection \u2014 session stays alive in grace. */\n\tdisconnect(): void {\n\t\tthis._closed = true;\n\t\tthis._activeSessions.clear();\n\t\tthis._pendingRents.clear();\n\t\tthis._pendingResumes.clear();\n\t\tthis._closeWs();\n\t}\n\n\t/** Close everything. */\n\tclose(): void {\n\t\tthis.disconnect();\n\t}\n\n\t// \u2500\u2500 private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate _awaitRent(key: string, scheduleId: number, timeoutMs: number): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingRents.delete(key);\n\t\t\t\treject(new Error('Rent timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingRents.set(key, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _awaitResume(sessionId: string): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(10000);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingResumes.delete(sessionId);\n\t\t\t\treject(new Error('Resume timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingResumes.set(sessionId, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _closeWs(): void {\n\t\tif (this._ws) {\n\t\t\ttry { this._ws.onopen = null; this._ws.onmessage = null; this._ws.onclose = null; this._ws.onerror = null; this._ws.close(); } catch { /* ignore */ }\n\t\t\tthis._ws = null;\n\t\t}\n\t}\n\n\tprivate _handleMessage(data: unknown): void {\n\t\tconst msg = jsonParse(String(data)) as Record<string, unknown> | null;\n\t\tif (!msg || typeof msg !== 'object') return;\n\t\tconst type = String(msg.type ?? '');\n\t\tconst sid = msg.session_id ? String(msg.session_id) : null;\n\n\t\tswitch (type) {\n\t\t\tcase 'pong':\n\t\t\tcase 'rent_pending':\n\t\t\t\tbreak;\n\t\t\tcase 'match':\n\t\t\t\tthis._onMatch(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'rent.error':\n\t\t\t\tthis._onRentError(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_ok':\n\t\t\t\tthis._onResumeOk(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_failed':\n\t\t\t\tthis._onResumeFailed(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'cdp_response':\n\t\t\t\tif (sid) this._onCdpResponse(sid, msg);\n\t\t\t\tbreak;\n\t\t\tcase 'session.ended':\n\t\t\t\tif (sid) {\n\t\t\t\t\tthis._activeSessions.delete(sid);\n\t\t\t\t\tconst b = this._activeSessions.get(sid);\n\t\t\t\t\tif (b) (b as any)._ended = String(msg.reason ?? 'ended');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate _onMatch(msg: Record<string, unknown>): void {\n\t\tconst scheduleId = Number(msg.schedule_id ?? 0);\n\t\tconst eventId = msg.event_id ? String(msg.event_id) : null;\n\t\tlet pending = this._pendingRents.get(`event:${eventId}`);\n\t\tif (!pending) pending = this._pendingRents.get(`rent:${scheduleId}`);\n\t\tif (pending) {\n\t\t\tthis._pendingRents.delete(`event:${eventId}`);\n\t\t\tthis._pendingRents.delete(`rent:${scheduleId}`);\n\t\t\tpending.resolve({\n\t\t\t\tsession_id: String(msg.session_id ?? ''),\n\t\t\t\tschedule_id: scheduleId,\n\t\t\t\tevent_id: eventId,\n\t\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _onRentError(msg: Record<string, unknown>): void {\n\t\tconst code = String(msg.code ?? '');\n\t\tconst message = String(msg.message ?? '');\n\t\tfor (const [key, pending] of this._pendingRents) {\n\t\t\tthis._pendingRents.delete(key);\n\t\t\tpending.reject(new Error(message || `Rent error: ${code}`));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate _onResumeOk(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.resolve({\n\t\t\tsession_id: sessionId,\n\t\t\tschedule_id: Number(msg.schedule_id ?? 0),\n\t\t\tevent_id: msg.event_id ? String(msg.event_id) : null,\n\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t});\n\t}\n\n\tprivate _onResumeFailed(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.reject(new Error(String(msg.reason ?? 'Resume failed')));\n\t}\n\n\tprivate _onCdpResponse(sessionId: string, msg: Record<string, unknown>): void {\n\t\tconst browser = this._activeSessions.get(sessionId);\n\t\tif (!browser) return;\n\t\tconst id = Number(msg.id ?? 0);\n\t\tconst pending = this._pendingCdp.get(id);\n\t\tif (!pending) return;\n\t\tthis._pendingCdp.delete(id);\n\t\tif (msg.error) {\n\t\t\tpending.reject(new Error(String((msg.error as Record<string, unknown>).message ?? 'CDP error')));\n\t\t} else {\n\t\t\tpending.resolve(msg.result);\n\t\t}\n\t}\n}\n\n// \u2500\u2500 Browser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiBrowser {\n\treadonly sessionId: string;\n\treadonly scheduleId: number;\n\treadonly chatTopicId: string | null;\n\treadonly browserInfo: Record<string, unknown>;\n\treadonly providerUserId: number | null;\n\n\tprivate _client: CekiClient;\n\tprivate _cdpId = 1;\n\t_pendingCdp = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n\n\tconstructor(client: CekiClient, match: Match) {\n\t\tthis._client = client;\n\t\tthis.sessionId = match.session_id;\n\t\tthis.scheduleId = match.schedule_id;\n\t\tthis.chatTopicId = match.chat_topic_id ?? null;\n\t\tthis.browserInfo = match.browser_info ?? {};\n\t\tthis.providerUserId = match.provider_user_id ?? null;\n\t}\n\n\t/** Send a CDP command. Uses AbortSignal.timeout() (no restricted setTimeout global). */\n\tasync send(method: string, params?: Record<string, unknown>, timeoutMs = 30000): Promise<unknown> {\n\t\tconst id = this._cdpId++;\n\t\tconst msg = { type: 'cdp', session_id: this.sessionId, id, method, params: params ?? {} };\n\t\tthis._client._sendRaw(msg);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingCdp.delete(id);\n\t\t\t\treject(new Error(`CDP ${method} timed out after ${timeoutMs}ms`));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingCdp.set(id, {\n\t\t\t\tresolve: (v) => { resolve(v); },\n\t\t\t\treject: (e) => { reject(e); },\n\t\t\t});\n\t\t});\n\t}\n\n\tasync navigate(url: string, timeoutMs?: number): Promise<void> {\n\t\tawait this.send('Page.navigate', { url }, timeoutMs);\n\t}\n\n\tasync click(x: number, y: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });\n\t}\n\n\tasync type(text: string): Promise<void> {\n\t\tawait this.send('Ceki.typeText', { text });\n\t}\n\n\tasync scroll(deltaY: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseWheel', x: 0, y: 0, deltaX: 0, deltaY });\n\t}\n\n\tasync screenshot(opts?: ScreenshotOptions): Promise<{ data: string } | Buffer> {\n\t\tconst format = opts?.format ?? 'base64';\n\t\tconst fullPage = opts?.fullPage ?? false;\n\t\tlet clip: Record<string, number> | undefined;\n\t\tif (fullPage) {\n\t\t\tconst metrics = await this.send('Page.getLayoutMetrics') as Record<string, unknown> | null;\n\t\t\tconst contentSize = metrics?.contentSize as Record<string, number> | undefined;\n\t\t\tif (contentSize) {\n\t\t\t\tclip = { x: 0, y: 0, width: Number(contentSize.width ?? 1920), height: Math.min(Number(contentSize.height ?? 1080), 16384), scale: 1 };\n\t\t\t}\n\t\t}\n\t\tconst result = await this.send('Page.captureScreenshot', { format: 'png', ...(clip ? { clip } : {}) }) as Record<string, unknown> | null;\n\t\tconst data = String(result?.data ?? '');\n\t\tif (format === 'png') {\n\t\t\treturn Buffer.from(data, 'base64');\n\t\t}\n\t\treturn { data };\n\t}\n\n\tasync snapshot(): Promise<{ screenshot: string; ts: Date }> {\n\t\tconst ssResult = await this.screenshot({ format: 'base64' }) as { data: string };\n\t\treturn { screenshot: ssResult.data, ts: new Date() };\n\t}\n\n\tasync upload(selector: string, buf: Buffer, filename = 'file'): Promise<Record<string, unknown>> {\n\t\tconst b64 = buf.toString('base64');\n\t\tconst size = buf.length;\n\t\tconst mime = 'application/octet-stream';\n\t\tconst expression = `(function(){\n\t\t\tvar input = document.querySelector(${JSON.stringify(selector)});\n\t\t\tif (!input) return JSON.stringify({ok:false,error:'Element not found'});\n\t\t\tvar b = atob(${JSON.stringify(b64)});\n\t\t\tvar u = new Uint8Array(b.length);\n\t\t\tfor (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i);\n\t\t\tvar f = new File([u], ${JSON.stringify(filename)}, {type: ${JSON.stringify(mime)}});\n\t\t\tvar dt = new DataTransfer(); dt.items.add(f);\n\t\t\tinput.files = dt.files;\n\t\t\tinput.dispatchEvent(new Event('change',{bubbles:true}));\n\t\t\treturn JSON.stringify({ok:true,filename:${JSON.stringify(filename)},size:${size}});\n\t\t})()`;\n\t\tconst result = await this.send('Runtime.evaluate', { expression, returnByValue: true }) as Record<string, unknown> | null;\n\t\ttry {\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t} catch { /* ignore */ }\n\t\tconst resultObj = result?.result as Record<string, unknown> | undefined;\n\t\tif (resultObj?.value) return JSON.parse(String(resultObj.value)) as Record<string, unknown>;\n\t\treturn { ok: true, filename, size };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.send('Ceki.close', {}).catch(() => {});\n\t\tthis._client._activeSessions.delete(this.sessionId);\n\t}\n}\n"],
|
|
4
|
+
"sourcesContent": ["import type { IExecuteFunctions, INodeType, INodeTypeDescription, INodeExecutionData } from 'n8n-workflow';\nimport { CekiClient } from '../../../lib/ceki-client';\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => {\n\tAbortSignal.timeout(ms).addEventListener('abort', () => resolve(), { once: true });\n});\n\nasync function waitForSelector(browser: any, selector: string, timeoutMs: number, intervalMs = 500) {\n\tconst expr = `!!document.querySelector(${JSON.stringify(selector)})`;\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastErr: unknown = null;\n\twhile (Date.now() < deadline) {\n\t\ttry {\n\t\t\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\t\t\tif (res?.result?.value === true) return true;\n\t\t} catch (e) {\n\t\t\tlastErr = e;\n\t\t}\n\t\tawait sleep(intervalMs);\n\t}\n\tthrow new Error(\n\t\t`waitForSelector(\"${selector}\") timed out after ${timeoutMs}ms${lastErr ? `: ${(lastErr as Error).message}` : ''}`,\n\t);\n}\n\nasync function extractHtml(browser: any, selector: string): Promise<string> {\n\tconst expr =\n\t\tselector.trim() === '' || selector === 'body'\n\t\t\t? `document.body ? document.body.outerHTML : ''`\n\t\t\t: `(function(){ var el = document.querySelector(${JSON.stringify(selector)}); return el ? el.outerHTML : ''; })()`;\n\tconst res = await browser.send('Runtime.evaluate', { expression: expr, returnByValue: true }) as any;\n\treturn (res?.result?.value as string) ?? '';\n}\n\n/**\n * Recipe: Captcha-protected Scrape.\n * Rent a human browser \u2192 open a URL \u2192 (optional) wait for a selector \u2192\n * snapshot/HTML \u2192 release.\n *\n * The real human fingerprint bypasses anti-bot protection.\n */\nexport class CekiCaptchaScrape implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'Browser Ceki: Captcha-protected Scrape',\n\t\tname: 'cekiCaptchaScrape',\n\t\tdescription: 'Rent a human browser, wait, screenshot/HTML, release',\n\t\ticon: 'file:ceki.png',\n\t\tgroup: ['transform'],\n\t\tversion: 1,\n\t\tsubtitle: '=rent in {{ $geo }} \u2192 snapshot',\n\t\tdefaults: { name: 'Browser Ceki: Captcha-protected Scrape' },\n\t\tinputs: ['main'],\n\t\toutputs: ['main'],\n\t\tcredentials: [{ name: 'cekiApi', required: true }],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'URL',\n\t\t\t\tname: 'url',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'https://example.com',\n\t\t\t\trequired: true,\n\t\t\t\tdescription: 'Page protected by anti-bot / captcha',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Geo',\n\t\t\t\tname: 'geo',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'RU',\n\t\t\t\tplaceholder: 'RU, EE, US\u2026',\n\t\t\t},\n\t\t\t{ displayName: 'Max $/min', name: 'maxPrice', type: 'number', typeOptions: { numberPrecision: 4 }, default: 0.02 },\n\t\t\t{\n\t\t\t\tdisplayName: 'Wait for Selector',\n\t\t\t\tname: 'waitSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'CSS selector (optional)',\n\t\t\t\tdescription: 'Wait until this selector appears in the DOM',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Wait Timeout (ms)',\n\t\t\t\tname: 'waitTimeout',\n\t\t\t\ttype: 'number',\n\t\t\t\tdefault: 30000,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Extract HTML',\n\t\t\t\tname: 'extractHtml',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'HTML Selector',\n\t\t\t\tname: 'htmlSelector',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: 'body',\n\t\t\t\tplaceholder: 'CSS selector or \"body\"',\n\t\t\t\tdescription: 'outerHTML of this selector is returned as `html`',\n\t\t\t\tdisplayOptions: { show: { extractHtml: [true] } },\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Full Page Screenshot',\n\t\t\t\tname: 'fullPage',\n\t\t\t\ttype: 'boolean',\n\t\t\t\tdefault: false,\n\t\t\t},\n\t\t],\n\t};\n\n\tasync execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {\n\t\tconst items = this.getInputData();\n\t\tconst out: INodeExecutionData[] = [];\n\t\tconst creds = await this.getCredentials('cekiApi');\n\n\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\tconst url = this.getNodeParameter('url', i) as string;\n\t\t\tconst geo = this.getNodeParameter('geo', i) as string;\n\t\t\tconst maxPrice = this.getNodeParameter('maxPrice', i) as number;\n\t\t\tconst waitSelector = (this.getNodeParameter('waitSelector', i) as string) || '';\n\t\t\tconst waitTimeout = this.getNodeParameter('waitTimeout', i) as number;\n\t\t\tconst extractHtmlFlag = this.getNodeParameter('extractHtml', i) as boolean;\n\t\t\tconst htmlSelector = (this.getNodeParameter('htmlSelector', i) as string) || 'body';\n\t\t\tconst fullPage = this.getNodeParameter('fullPage', i) as boolean;\n\n\t\t\tconst client = new CekiClient(creds.token as string);\n\t\t\tawait client.connect();\n\t\t\tlet browser: any;\n\t\t\tlet scheduleId = 0;\n\t\t\ttry {\n\t\t\t\tconst list = await client.search({ geo: geo || undefined, max_price_per_min: maxPrice });\n\t\t\t\tif (!list.length) throw new Error(`No browsers in geo ${geo || '*'}`);\n\t\t\t\tscheduleId = list[0].schedule_id;\n\t\t\t\tbrowser = await client.rent(scheduleId);\n\n\t\t\t\tawait browser.navigate(url);\n\n\t\t\t\tif (waitSelector) {\n\t\t\t\t\tawait waitForSelector(browser, waitSelector, waitTimeout);\n\t\t\t\t}\n\n\t\t\t\tconst shot = await browser.screenshot({ format: 'base64', fullPage }) as any;\n\t\t\t\tconst data = shot.data ?? (shot instanceof Buffer ? shot.toString('base64') : '');\n\t\t\t\tconst binary = await this.helpers.prepareBinaryData(\n\t\t\t\t\tBuffer.from(data, 'base64'),\n\t\t\t\t\t'captcha-scrape.png',\n\t\t\t\t\t'image/png',\n\t\t\t\t);\n\n\t\t\t\tconst json: Record<string, unknown> = {\n\t\t\t\t\turl,\n\t\t\t\t\tgeo,\n\t\t\t\t\tschedule_id: scheduleId,\n\t\t\t\t};\n\t\t\t\tif (extractHtmlFlag) {\n\t\t\t\t\tjson.html = await extractHtml(browser, htmlSelector);\n\t\t\t\t}\n\n\t\t\t\tout.push({ json: json as any, binary: { data: binary } });\n\t\t\t} finally {\n\t\t\t\tif (browser) {\n\t\t\t\t\ttry { await browser.close(); } catch { /* ignore */ }\n\t\t\t\t}\n\t\t\t\ttry { await client.close(); } catch { /* ignore */ }\n\t\t\t}\n\t\t}\n\t\treturn [out];\n\t}\n}\n", "/**\n * Minimal Ceki browser client \u2014 uses only native WebSocket + fetch (Node 22 globals).\n * Zero npm imports (no ws, no mime-types, no node built-in modules).\n *\n * Timeout management: uses AbortSignal.timeout() instead of restricted\n * setTimeout/clearTimeout globals to satisfy the n8n community-nodes\n * package scanner (no-restricted-globals rule).\n */\n\n// \u2500\u2500 types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface BrowserOption {\n\tschedule_id: number;\n\tgeo?: string;\n\tprice_per_min: number;\n\tonline: boolean;\n\trating?: number | null;\n}\n\nexport interface Match {\n\tsession_id: string;\n\tschedule_id: number;\n\tevent_id?: string | null;\n\tchat_topic_id?: string | null;\n\tprovider_user_id?: number | null;\n\tbrowser_info?: Record<string, unknown>;\n}\n\nexport interface RentOptions {\n\tmode?: 'main' | 'incognito';\n}\n\nexport interface ScreenshotOptions {\n\tformat?: 'png' | 'base64';\n\tfullPage?: boolean;\n}\n\n// \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction jsonParse(raw: string): unknown {\n\ttry { return JSON.parse(raw); } catch { return null; }\n}\n\n// \u2500\u2500 Client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiClient {\n\tprivate _ws: WebSocket | null = null;\n\tprivate _connected = false;\n\tprivate _pendingRents = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingResumes = new Map<string, {\n\t\tresolve: (m: Match) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _pendingCdp = new Map<number, {\n\t\tresolve: (v: unknown) => void;\n\t\treject: (e: Error) => void;\n\t}>();\n\tprivate _cdpCounter = 1;\n\t_activeSessions = new Map<string, CekiBrowser>();\n\tprivate _connectReject: ((e: Error) => void) | null = null;\n\tprivate _closed = false;\n\n\tconstructor(\n\t\tprivate _token: string,\n\t\tprivate _relayUrl = 'wss://browser.ceki.me/ws/agent',\n\t\tprivate _apiUrl = 'https://api.ceki.me',\n\t) {}\n\n\t/** Connect to the relay WebSocket. */\n\tasync connect(): Promise<void> {\n\t\tif (this._connected) return;\n\t\tconst protocols = [`bearer.${this._token}`];\n\t\tthis._ws = new WebSocket(this._relayUrl, protocols);\n\t\tthis._ws.onopen = () => {\n\t\t\tthis._connected = true;\n\t\t};\n\t\tthis._ws.onmessage = (ev: MessageEvent) => {\n\t\t\tthis._handleMessage(ev.data);\n\t\t};\n\t\tthis._ws.onclose = (ev: CloseEvent) => {\n\t\t\tthis._connected = false;\n\t\t\tif ((ev.code === 4401 || ev.code === 4403) && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error(`Auth failed: ${ev.reason || String(ev.code)}`));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tthis._ws.onerror = () => {\n\t\t\tif (!this._connected && this._connectReject) {\n\t\t\t\tthis._connectReject(new Error('WebSocket connection failed'));\n\t\t\t\tthis._connectReject = null;\n\t\t\t}\n\t\t};\n\t\tif (this._ws.readyState === WebSocket.CONNECTING) {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tif (!this._ws) { reject(new Error('No WebSocket')); return; }\n\t\t\t\tthis._connectReject = reject;\n\t\t\t\tthis._ws!.onopen = () => {\n\t\t\t\t\tthis._connected = true;\n\t\t\t\t\tresolve();\n\t\t\t\t};\n\t\t\t});\n\t\t}\n\t}\n\n\t_sendRaw(msg: Record<string, unknown>): void {\n\t\tif (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n\t\t\tthrow new Error('WebSocket not connected');\n\t\t}\n\t\tthis._ws.send(JSON.stringify(msg));\n\t}\n\n\t/** Search for available browser providers (HTTP GET). */\n\tasync search(filters?: Record<string, unknown>, limit?: number): Promise<BrowserOption[]> {\n\t\tconst params = new URLSearchParams();\n\t\tif (limit != null) params.set('limit', String(limit));\n\t\tif (filters) {\n\t\t\tfor (const [k, v] of Object.entries(filters)) {\n\t\t\t\tif (v != null) params.set(k, String(v));\n\t\t\t}\n\t\t}\n\t\tconst resp = await fetch(`${this._apiUrl}/api/browsers/search?${params}`, {\n\t\t\theaders: { Authorization: `Bearer ${this._token}` },\n\t\t});\n\t\tif (!resp.ok) throw new Error(`Search failed: ${resp.status}`);\n\t\tconst body = (await resp.json()) as Record<string, unknown>;\n\t\tconst data = (body.data ?? body) as unknown[];\n\t\treturn Array.isArray(data) ? data as BrowserOption[] : [];\n\t}\n\n\t/** Rent a browser by schedule_id. */\n\tasync rent(scheduleId: number, opts?: RentOptions): Promise<CekiBrowser> {\n\t\tconst msg: Record<string, unknown> = { type: 'rent', browser_id: scheduleId };\n\t\tif (opts?.mode) msg.mode = opts.mode;\n\t\tthis._sendRaw(msg);\n\t\treturn this._awaitRent(`rent:${scheduleId}`, scheduleId, 90000);\n\t}\n\n\t/** Resume an existing session. */\n\tasync resume(sessionId: string): Promise<CekiBrowser> {\n\t\tthis._sendRaw({ type: 'resume', session_id: sessionId });\n\t\treturn this._awaitResume(sessionId);\n\t}\n\n\t/** Close the WS connection \u2014 session stays alive in grace. */\n\tdisconnect(): void {\n\t\tthis._closed = true;\n\t\tthis._activeSessions.clear();\n\t\tthis._pendingRents.clear();\n\t\tthis._pendingResumes.clear();\n\t\tthis._closeWs();\n\t}\n\n\t/** Close everything. */\n\tclose(): void {\n\t\tthis.disconnect();\n\t}\n\n\t// \u2500\u2500 private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate _awaitRent(key: string, scheduleId: number, timeoutMs: number): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingRents.delete(key);\n\t\t\t\treject(new Error('Rent timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingRents.set(key, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _awaitResume(sessionId: string): Promise<CekiBrowser> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(60000);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingResumes.delete(sessionId);\n\t\t\t\treject(new Error('Resume timed out'));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingResumes.set(sessionId, {\n\t\t\t\tresolve: (match: Match) => {\n\t\t\t\t\tconst browser = new CekiBrowser(this, match);\n\t\t\t\t\tthis._activeSessions.set(browser.sessionId, browser);\n\t\t\t\t\tresolve(browser);\n\t\t\t\t},\n\t\t\t\treject: (err: Error) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate _closeWs(): void {\n\t\tif (this._ws) {\n\t\t\ttry { this._ws.onopen = null; this._ws.onmessage = null; this._ws.onclose = null; this._ws.onerror = null; this._ws.close(); } catch { /* ignore */ }\n\t\t\tthis._ws = null;\n\t\t}\n\t}\n\n\tprivate _handleMessage(data: unknown): void {\n\t\tconst msg = jsonParse(String(data)) as Record<string, unknown> | null;\n\t\tif (!msg || typeof msg !== 'object') return;\n\t\tconst type = String(msg.type ?? '');\n\t\tconst sid = msg.session_id ? String(msg.session_id) : null;\n\n\t\tswitch (type) {\n\t\t\tcase 'pong':\n\t\t\tcase 'rent_pending':\n\t\t\t\tbreak;\n\t\t\tcase 'match':\n\t\t\t\tthis._onMatch(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'rent.error':\n\t\t\t\tthis._onRentError(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_ok':\n\t\t\t\tthis._onResumeOk(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'resume_failed':\n\t\t\t\tthis._onResumeFailed(msg);\n\t\t\t\tbreak;\n\t\t\tcase 'cdp_response':\n\t\t\t\tif (sid) this._onCdpResponse(sid, msg);\n\t\t\t\tbreak;\n\t\t\tcase 'session.ended':\n\t\t\t\tif (sid) {\n\t\t\t\t\tthis._activeSessions.delete(sid);\n\t\t\t\t\tconst b = this._activeSessions.get(sid);\n\t\t\t\t\tif (b) (b as any)._ended = String(msg.reason ?? 'ended');\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate _onMatch(msg: Record<string, unknown>): void {\n\t\tconst scheduleId = Number(msg.schedule_id ?? 0);\n\t\tconst eventId = msg.event_id ? String(msg.event_id) : null;\n\t\tlet pending = this._pendingRents.get(`event:${eventId}`);\n\t\tif (!pending) pending = this._pendingRents.get(`rent:${scheduleId}`);\n\t\tif (pending) {\n\t\t\tthis._pendingRents.delete(`event:${eventId}`);\n\t\t\tthis._pendingRents.delete(`rent:${scheduleId}`);\n\t\t\tpending.resolve({\n\t\t\t\tsession_id: String(msg.session_id ?? ''),\n\t\t\t\tschedule_id: scheduleId,\n\t\t\t\tevent_id: eventId,\n\t\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _onRentError(msg: Record<string, unknown>): void {\n\t\tconst code = String(msg.code ?? '');\n\t\tconst message = String(msg.message ?? '');\n\t\tfor (const [key, pending] of this._pendingRents) {\n\t\t\tthis._pendingRents.delete(key);\n\t\t\tpending.reject(new Error(message || `Rent error: ${code}`));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate _onResumeOk(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.resolve({\n\t\t\tsession_id: sessionId,\n\t\t\tschedule_id: Number(msg.schedule_id ?? 0),\n\t\t\tevent_id: msg.event_id ? String(msg.event_id) : null,\n\t\t\tchat_topic_id: msg.chat_topic_id ? String(msg.chat_topic_id) : null,\n\t\t\tprovider_user_id: msg.provider_user_id != null ? Number(msg.provider_user_id) : null,\n\t\t\tbrowser_info: (msg.browser_info ?? {}) as Record<string, unknown>,\n\t\t});\n\t}\n\n\tprivate _onResumeFailed(msg: Record<string, unknown>): void {\n\t\tconst sessionId = String(msg.session_id ?? '');\n\t\tconst pending = this._pendingResumes.get(sessionId);\n\t\tif (!pending) return;\n\t\tthis._pendingResumes.delete(sessionId);\n\t\tpending.reject(new Error(String(msg.reason ?? 'Resume failed')));\n\t}\n\n\tprivate _onCdpResponse(sessionId: string, msg: Record<string, unknown>): void {\n\t\tconst browser = this._activeSessions.get(sessionId);\n\t\tif (!browser) return;\n\t\tconst id = Number(msg.id ?? 0);\n\t\tconst pending = this._pendingCdp.get(id);\n\t\tif (!pending) return;\n\t\tthis._pendingCdp.delete(id);\n\t\tif (msg.error) {\n\t\t\tpending.reject(new Error(String((msg.error as Record<string, unknown>).message ?? 'CDP error')));\n\t\t} else {\n\t\t\tpending.resolve(msg.result);\n\t\t}\n\t}\n}\n\n// \u2500\u2500 Browser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class CekiBrowser {\n\treadonly sessionId: string;\n\treadonly scheduleId: number;\n\treadonly chatTopicId: string | null;\n\treadonly browserInfo: Record<string, unknown>;\n\treadonly providerUserId: number | null;\n\n\tprivate _client: CekiClient;\n\tprivate _cdpId = 1;\n\t_pendingCdp = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n\n\tconstructor(client: CekiClient, match: Match) {\n\t\tthis._client = client;\n\t\tthis.sessionId = match.session_id;\n\t\tthis.scheduleId = match.schedule_id;\n\t\tthis.chatTopicId = match.chat_topic_id ?? null;\n\t\tthis.browserInfo = match.browser_info ?? {};\n\t\tthis.providerUserId = match.provider_user_id ?? null;\n\t}\n\n\t/** Send a CDP command. Uses AbortSignal.timeout() (no restricted setTimeout global). */\n\tasync send(method: string, params?: Record<string, unknown>, timeoutMs = 30000): Promise<unknown> {\n\t\tconst id = this._cdpId++;\n\t\tconst msg = { type: 'cdp', session_id: this.sessionId, id, method, params: params ?? {} };\n\t\tthis._client._sendRaw(msg);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutSignal = AbortSignal.timeout(timeoutMs);\n\t\t\ttimeoutSignal.addEventListener('abort', () => {\n\t\t\t\tthis._pendingCdp.delete(id);\n\t\t\t\treject(new Error(`CDP ${method} timed out after ${timeoutMs}ms`));\n\t\t\t}, { once: true });\n\t\t\tthis._pendingCdp.set(id, {\n\t\t\t\tresolve: (v) => { resolve(v); },\n\t\t\t\treject: (e) => { reject(e); },\n\t\t\t});\n\t\t});\n\t}\n\n\tasync navigate(url: string, timeoutMs?: number): Promise<void> {\n\t\tawait this.send('Page.navigate', { url }, timeoutMs);\n\t}\n\n\tasync click(x: number, y: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button: 'left', clickCount: 1 });\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });\n\t}\n\n\tasync type(text: string): Promise<void> {\n\t\tawait this.send('Ceki.typeText', { text });\n\t}\n\n\tasync scroll(deltaY: number): Promise<void> {\n\t\tawait this.send('Input.dispatchMouseEvent', { type: 'mouseWheel', x: 0, y: 0, deltaX: 0, deltaY });\n\t}\n\n\tasync screenshot(opts?: ScreenshotOptions): Promise<{ data: string } | Buffer> {\n\t\tconst format = opts?.format ?? 'base64';\n\t\tconst fullPage = opts?.fullPage ?? false;\n\t\tlet clip: Record<string, number> | undefined;\n\t\tif (fullPage) {\n\t\t\tconst metrics = await this.send('Page.getLayoutMetrics') as Record<string, unknown> | null;\n\t\t\tconst contentSize = metrics?.contentSize as Record<string, number> | undefined;\n\t\t\tif (contentSize) {\n\t\t\t\tclip = { x: 0, y: 0, width: Number(contentSize.width ?? 1920), height: Math.min(Number(contentSize.height ?? 1080), 16384), scale: 1 };\n\t\t\t}\n\t\t}\n\t\tconst result = await this.send('Page.captureScreenshot', { format: 'png', ...(clip ? { clip } : {}) }) as Record<string, unknown> | null;\n\t\tconst data = String(result?.data ?? '');\n\t\tif (format === 'png') {\n\t\t\treturn Buffer.from(data, 'base64');\n\t\t}\n\t\treturn { data };\n\t}\n\n\tasync snapshot(): Promise<{ screenshot: string; ts: Date }> {\n\t\tconst ssResult = await this.screenshot({ format: 'base64' }) as { data: string };\n\t\treturn { screenshot: ssResult.data, ts: new Date() };\n\t}\n\n\tasync upload(selector: string, buf: Buffer, filename = 'file'): Promise<Record<string, unknown>> {\n\t\tconst b64 = buf.toString('base64');\n\t\tconst size = buf.length;\n\t\tconst mime = 'application/octet-stream';\n\t\tconst expression = `(function(){\n\t\t\tvar input = document.querySelector(${JSON.stringify(selector)});\n\t\t\tif (!input) return JSON.stringify({ok:false,error:'Element not found'});\n\t\t\tvar b = atob(${JSON.stringify(b64)});\n\t\t\tvar u = new Uint8Array(b.length);\n\t\t\tfor (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i);\n\t\t\tvar f = new File([u], ${JSON.stringify(filename)}, {type: ${JSON.stringify(mime)}});\n\t\t\tvar dt = new DataTransfer(); dt.items.add(f);\n\t\t\tinput.files = dt.files;\n\t\t\tinput.dispatchEvent(new Event('change',{bubbles:true}));\n\t\t\treturn JSON.stringify({ok:true,filename:${JSON.stringify(filename)},size:${size}});\n\t\t})()`;\n\t\tconst result = await this.send('Runtime.evaluate', { expression, returnByValue: true }) as Record<string, unknown> | null;\n\t\ttry {\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyDown', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t\tawait this.send('Input.dispatchKeyEvent', { type: 'keyUp', key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 });\n\t\t} catch { /* ignore */ }\n\t\tconst resultObj = result?.result as Record<string, unknown> | undefined;\n\t\tif (resultObj?.value) return JSON.parse(String(resultObj.value)) as Record<string, unknown>;\n\t\treturn { ok: true, filename, size };\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.send('Ceki.close', {}).catch(() => {});\n\t\tthis._client._activeSessions.delete(this.sessionId);\n\t}\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|