@ceki/n8n-nodes-ceki 0.2.29 → 0.2.31
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 +3 -3
- package/dist/credentials/CekiApi.credentials.js +1 -0
- package/dist/credentials/CekiApi.credentials.js.map +2 -2
- package/dist/index.js +967 -956
- package/dist/index.js.map +4 -4
- package/dist/nodes/BrowserCeki/BrowserCeki.node.js +928 -305
- package/dist/nodes/BrowserCeki/BrowserCeki.node.js.map +4 -4
- package/dist/nodes/BrowserCeki/ceki-dark.svg +9 -0
- package/dist/nodes/BrowserCeki/ceki-light.svg +9 -0
- package/dist/nodes/BrowserCeki/ceki.png +0 -0
- package/package.json +9 -8
- package/dist/nodes/CekiContract/CekiContract.node.js +0 -415
- package/dist/nodes/CekiContract/CekiContract.node.js.map +0 -7
- package/dist/nodes/CekiContract/ceki.png +0 -0
- package/dist/nodes/recipes/CekiCaptchaScrape/CekiCaptchaScrape.node.js +0 -595
- package/dist/nodes/recipes/CekiCaptchaScrape/CekiCaptchaScrape.node.js.map +0 -7
- package/dist/nodes/recipes/CekiCaptchaScrape/ceki.png +0 -0
- package/dist/nodes/recipes/CekiScreenshotGeo/CekiScreenshotGeo.node.js +0 -514
- package/dist/nodes/recipes/CekiScreenshotGeo/CekiScreenshotGeo.node.js.map +0 -7
- package/dist/nodes/recipes/CekiScreenshotGeo/ceki.png +0 -0
- package/dist/nodes/recipes/ceki.png +0 -0
|
@@ -23,6 +23,7 @@ __export(BrowserCeki_node_exports, {
|
|
|
23
23
|
BrowserCeki: () => BrowserCeki
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(BrowserCeki_node_exports);
|
|
26
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
26
27
|
|
|
27
28
|
// lib/ceki-client.ts
|
|
28
29
|
function jsonParse(raw) {
|
|
@@ -147,18 +148,18 @@ var CekiClient = class {
|
|
|
147
148
|
return;
|
|
148
149
|
}
|
|
149
150
|
await new Promise((r) => {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
const onAbort = () => r();
|
|
152
|
+
const abortTimer = AbortSignal.timeout(5e3);
|
|
153
|
+
abortTimer.addEventListener("abort", onAbort, { once: true });
|
|
153
154
|
ws.onclose = () => {
|
|
154
155
|
this._connected = false;
|
|
155
|
-
|
|
156
|
+
abortTimer.removeEventListener("abort", onAbort);
|
|
156
157
|
r();
|
|
157
158
|
};
|
|
158
159
|
try {
|
|
159
160
|
ws.close();
|
|
160
161
|
} catch {
|
|
161
|
-
|
|
162
|
+
abortTimer.removeEventListener("abort", onAbort);
|
|
162
163
|
r();
|
|
163
164
|
}
|
|
164
165
|
});
|
|
@@ -370,9 +371,9 @@ var CekiBrowser = class {
|
|
|
370
371
|
async navigate(url, timeoutMs) {
|
|
371
372
|
await this.send("Page.navigate", { url }, timeoutMs);
|
|
372
373
|
}
|
|
373
|
-
async click(
|
|
374
|
-
await this.send("Input.dispatchMouseEvent", { type: "mousePressed", x
|
|
375
|
-
await this.send("Input.dispatchMouseEvent", { type: "mouseReleased", x
|
|
374
|
+
async click(x, y) {
|
|
375
|
+
await this.send("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 });
|
|
376
|
+
await this.send("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 });
|
|
376
377
|
}
|
|
377
378
|
async type(text) {
|
|
378
379
|
await this.send("Ceki.typeText", { text });
|
|
@@ -435,7 +436,181 @@ var CekiBrowser = class {
|
|
|
435
436
|
}
|
|
436
437
|
};
|
|
437
438
|
|
|
439
|
+
// lib/contract-client.ts
|
|
440
|
+
function cleanArgs(o) {
|
|
441
|
+
const out = { ...o };
|
|
442
|
+
for (const k of Object.keys(out)) {
|
|
443
|
+
if (out[k] === void 0 || out[k] === null) delete out[k];
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
function parseBenefitable(value) {
|
|
448
|
+
if (!value) return null;
|
|
449
|
+
const m = /^(agent|user):(\d+)$/.exec(value);
|
|
450
|
+
if (!m) return null;
|
|
451
|
+
return { type: m[1], value: Number(m[2]) };
|
|
452
|
+
}
|
|
453
|
+
function parseParticipant(value, roleId) {
|
|
454
|
+
const b = parseBenefitable(value);
|
|
455
|
+
if (!b) return null;
|
|
456
|
+
return { participable_id: b.value, type: b.type, role_id: roleId };
|
|
457
|
+
}
|
|
458
|
+
function deriveLabel(desc) {
|
|
459
|
+
if (!desc) return "";
|
|
460
|
+
const line = desc.split("\n")[0].trim();
|
|
461
|
+
return line.length > 60 ? line.slice(0, 57) + "..." : line;
|
|
462
|
+
}
|
|
463
|
+
var ROLE_REVIEWER = 5;
|
|
464
|
+
var ROLE_QA = 6;
|
|
465
|
+
var ContractClient = class {
|
|
466
|
+
constructor(token, endpoint, apiBase) {
|
|
467
|
+
this._endpoint = (endpoint ?? "https://api.ceki.me/mcp").replace(/\/+$/, "");
|
|
468
|
+
this._apiBase = (apiBase ?? "https://api.ceki.me").replace(/\/+$/, "");
|
|
469
|
+
this._token = token;
|
|
470
|
+
}
|
|
471
|
+
_headers() {
|
|
472
|
+
return {
|
|
473
|
+
"Content-Type": "application/json",
|
|
474
|
+
Accept: "application/json",
|
|
475
|
+
Authorization: `Bearer ${this._token}`
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
async _rpc(method, params) {
|
|
479
|
+
const body = JSON.stringify({ jsonrpc: "2.0", id: Date.now(), method, params });
|
|
480
|
+
const resp = await fetch(this._endpoint, {
|
|
481
|
+
method: "POST",
|
|
482
|
+
headers: this._headers(),
|
|
483
|
+
body
|
|
484
|
+
});
|
|
485
|
+
if (!resp.ok) {
|
|
486
|
+
const text = await resp.text().catch(() => "");
|
|
487
|
+
throw new Error(`HTTP ${resp.status}: ${text.slice(0, 400)}`);
|
|
488
|
+
}
|
|
489
|
+
return resp.json();
|
|
490
|
+
}
|
|
491
|
+
async _call(tool, args) {
|
|
492
|
+
const body = await this._rpc("tools/call", { name: tool, arguments: args ?? {} });
|
|
493
|
+
if (body.error) throw new Error(`${tool} \u2192 ${JSON.stringify(body.error).slice(0, 400)}`);
|
|
494
|
+
const result = body.result ?? {};
|
|
495
|
+
const content = result.content;
|
|
496
|
+
if (Array.isArray(content)) {
|
|
497
|
+
const texts = content.filter((c) => c.type === "text").map((c) => String(c.text ?? ""));
|
|
498
|
+
const joined = texts.join("\n");
|
|
499
|
+
try {
|
|
500
|
+
return JSON.parse(joined);
|
|
501
|
+
} catch {
|
|
502
|
+
return joined;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (result.structuredContent !== void 0) return result.structuredContent;
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
// ── domain methods ─────────────────────────────────────────
|
|
509
|
+
async listContracts() {
|
|
510
|
+
return this._call("list-contracts");
|
|
511
|
+
}
|
|
512
|
+
async members(contractId) {
|
|
513
|
+
return this._call("contract-members", { contract_id: contractId });
|
|
514
|
+
}
|
|
515
|
+
async tasks(contractId) {
|
|
516
|
+
return this._call("contract-tasks", { contract_id: contractId });
|
|
517
|
+
}
|
|
518
|
+
async myEvents() {
|
|
519
|
+
return this._call("get-my-events");
|
|
520
|
+
}
|
|
521
|
+
async task(eventId) {
|
|
522
|
+
return this._call("get-event", { event_id: eventId });
|
|
523
|
+
}
|
|
524
|
+
async create(contractId, opts) {
|
|
525
|
+
const args = cleanArgs({
|
|
526
|
+
contract_id: contractId,
|
|
527
|
+
label: opts.label,
|
|
528
|
+
type_id: opts.type,
|
|
529
|
+
status_id: opts.status,
|
|
530
|
+
description: opts.description,
|
|
531
|
+
benefitable: opts.benefitable ? parseBenefitable(opts.benefitable) : void 0
|
|
532
|
+
});
|
|
533
|
+
const users = [];
|
|
534
|
+
const rev = parseParticipant(opts.reviewer, ROLE_REVIEWER);
|
|
535
|
+
if (rev) users.push(rev);
|
|
536
|
+
const qa = parseParticipant(opts.qa, ROLE_QA);
|
|
537
|
+
if (qa) users.push(qa);
|
|
538
|
+
if (users.length) args.users = users;
|
|
539
|
+
return this._call("create-contract-event", args);
|
|
540
|
+
}
|
|
541
|
+
async propose(eventId, opts) {
|
|
542
|
+
return this._call("propose-correction", cleanArgs({
|
|
543
|
+
event_id: eventId,
|
|
544
|
+
status_id: opts.status,
|
|
545
|
+
label: opts.label,
|
|
546
|
+
description: opts.description,
|
|
547
|
+
benefitable: opts.benefitable ? parseBenefitable(opts.benefitable) : void 0
|
|
548
|
+
}));
|
|
549
|
+
}
|
|
550
|
+
async comment(eventId, opts) {
|
|
551
|
+
return this._call("comment", cleanArgs({
|
|
552
|
+
event_id: eventId,
|
|
553
|
+
label: opts?.label,
|
|
554
|
+
description: opts?.description
|
|
555
|
+
}));
|
|
556
|
+
}
|
|
557
|
+
async progress(eventId, opts) {
|
|
558
|
+
let statusResult = null;
|
|
559
|
+
if (opts.status != null) {
|
|
560
|
+
statusResult = await this.propose(eventId, { status: opts.status });
|
|
561
|
+
}
|
|
562
|
+
const commentResult = await this.comment(eventId, { label: deriveLabel(opts.desc), description: opts.desc });
|
|
563
|
+
return { status_correction: statusResult, comment: commentResult };
|
|
564
|
+
}
|
|
565
|
+
async callHuman(eventId, kind, desc) {
|
|
566
|
+
if (!["input", "review", "stuck"].includes(kind)) throw new Error(`kind must be input|review|stuck, got ${kind}`);
|
|
567
|
+
return this._call("call-human", { event_id: eventId, kind, desc });
|
|
568
|
+
}
|
|
569
|
+
/** GET /agent/polling. Returns [] on 429. */
|
|
570
|
+
async poll() {
|
|
571
|
+
const resp = await fetch(`${this._apiBase}/agent/polling`, {
|
|
572
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${this._token}` }
|
|
573
|
+
});
|
|
574
|
+
if (resp.status === 429) return [];
|
|
575
|
+
if (!resp.ok) {
|
|
576
|
+
const text = await resp.text().catch(() => "");
|
|
577
|
+
throw new Error(`poll HTTP ${resp.status}: ${text.slice(0, 300)}`);
|
|
578
|
+
}
|
|
579
|
+
const body = await resp.json();
|
|
580
|
+
if (Array.isArray(body)) return body;
|
|
581
|
+
if (body && typeof body === "object") {
|
|
582
|
+
for (const k of ["notifications", "data", "items"]) {
|
|
583
|
+
if (Array.isArray(body[k])) return body[k];
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return [];
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
438
590
|
// nodes/BrowserCeki/BrowserCeki.node.ts
|
|
591
|
+
async function waitForSelector(node, browser, selector, timeoutMs, intervalMs = 500) {
|
|
592
|
+
const expr = `!!document.querySelector(${JSON.stringify(selector)})`;
|
|
593
|
+
const deadline = Date.now() + timeoutMs;
|
|
594
|
+
let lastErr = null;
|
|
595
|
+
while (Date.now() < deadline) {
|
|
596
|
+
try {
|
|
597
|
+
const res = await browser.send("Runtime.evaluate", { expression: expr, returnByValue: true });
|
|
598
|
+
if (res?.result?.value === true) return true;
|
|
599
|
+
} catch (e) {
|
|
600
|
+
lastErr = e;
|
|
601
|
+
}
|
|
602
|
+
await sleep(intervalMs);
|
|
603
|
+
}
|
|
604
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
605
|
+
node,
|
|
606
|
+
`waitForSelector("${selector}") timed out after ${timeoutMs}ms${lastErr ? ": " + lastErr.message : ""}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
async function extractHtml(browser, selector) {
|
|
610
|
+
const expr = selector.trim() === "" || selector === "body" ? "document.body ? document.body.outerHTML : ''" : "(function(){ var el = document.querySelector(" + JSON.stringify(selector) + "); return el ? el.outerHTML : ''; })()";
|
|
611
|
+
const res = await browser.send("Runtime.evaluate", { expression: expr, returnByValue: true });
|
|
612
|
+
return res?.result?.value ?? "";
|
|
613
|
+
}
|
|
439
614
|
var sleep = (ms) => new Promise((resolve) => {
|
|
440
615
|
AbortSignal.timeout(ms).addEventListener("abort", () => resolve(), { once: true });
|
|
441
616
|
});
|
|
@@ -444,14 +619,18 @@ var BrowserCeki = class {
|
|
|
444
619
|
this.description = {
|
|
445
620
|
displayName: "Browser Ceki",
|
|
446
621
|
name: "browserCeki",
|
|
447
|
-
icon:
|
|
622
|
+
icon: {
|
|
623
|
+
light: "file:ceki-light.svg",
|
|
624
|
+
dark: "file:ceki-dark.svg"
|
|
625
|
+
},
|
|
448
626
|
group: ["transform"],
|
|
449
627
|
version: 1,
|
|
628
|
+
usableAsTool: true,
|
|
450
629
|
subtitle: '={{ "Ceki: " + $parameter.operation }}',
|
|
451
630
|
description: "Rent a real human browser and control it: rent, navigate, click, type, screenshot, solve captchas, and more",
|
|
452
631
|
defaults: { name: "Browser Ceki" },
|
|
453
|
-
inputs: [
|
|
454
|
-
outputs: [
|
|
632
|
+
inputs: [import_n8n_workflow.NodeConnectionTypes.Main],
|
|
633
|
+
outputs: [import_n8n_workflow.NodeConnectionTypes.Main],
|
|
455
634
|
credentials: [{ name: "cekiApi", required: true }],
|
|
456
635
|
properties: [
|
|
457
636
|
{
|
|
@@ -459,22 +638,38 @@ var BrowserCeki = class {
|
|
|
459
638
|
name: "operation",
|
|
460
639
|
type: "options",
|
|
461
640
|
default: "search",
|
|
641
|
+
noDataExpression: true,
|
|
462
642
|
options: [
|
|
463
|
-
|
|
464
|
-
{ name: "
|
|
465
|
-
{ name: "Navigate", value: "navigate" },
|
|
643
|
+
// Browser operations
|
|
644
|
+
{ name: "Captcha-Protected Scrape", value: "captchaScrape" },
|
|
466
645
|
{ name: "Click", value: "click" },
|
|
467
|
-
{ name: "Type", value: "type" },
|
|
468
|
-
{ name: "Scroll", value: "scroll" },
|
|
469
|
-
{ name: "Screenshot", value: "screenshot" },
|
|
470
|
-
{ name: "Snapshot", value: "snapshot" },
|
|
471
|
-
{ name: "Wait", value: "wait" },
|
|
472
|
-
{ name: "Wait for Selector", value: "waitForSelector" },
|
|
473
|
-
{ name: "Upload", value: "upload" },
|
|
474
646
|
{ name: "Close", value: "close" },
|
|
647
|
+
// Contract operations
|
|
648
|
+
{ name: "Contract: Assign Executor", value: "contract_assign" },
|
|
649
|
+
{ name: "Contract: Call Human", value: "contract_callHuman" },
|
|
650
|
+
{ name: "Contract: Comment", value: "contract_comment" },
|
|
651
|
+
{ name: "Contract: Create Task", value: "contract_create" },
|
|
652
|
+
{ name: "Contract: Get Task", value: "contract_get" },
|
|
653
|
+
{ name: "Contract: List My Contracts", value: "contract_list" },
|
|
654
|
+
{ name: "Contract: List Tasks in Contract", value: "contract_tasks" },
|
|
655
|
+
{ name: "Contract: My Assigned Events", value: "contract_myEvents" },
|
|
656
|
+
{ name: "Contract: Poll Notifications", value: "contract_poll" },
|
|
657
|
+
{ name: "Contract: Progress Report", value: "contract_progress" },
|
|
658
|
+
{ name: "Contract: Update Status", value: "contract_setStatus" },
|
|
475
659
|
{ name: "Full: Rent \u2192 Navigate \u2192 Screenshot", value: "full" },
|
|
476
660
|
{ name: "My Sessions", value: "my_sessions" },
|
|
477
|
-
{ name: "
|
|
661
|
+
{ name: "Navigate", value: "navigate" },
|
|
662
|
+
{ name: "Rent", value: "rent" },
|
|
663
|
+
{ name: "Screenshot", value: "screenshot" },
|
|
664
|
+
{ name: "Screenshot in Geo", value: "screenshotGeo" },
|
|
665
|
+
{ name: "Scroll", value: "scroll" },
|
|
666
|
+
{ name: "Search", value: "search" },
|
|
667
|
+
{ name: "Snapshot", value: "snapshot" },
|
|
668
|
+
{ name: "Stop Session", value: "stop_session" },
|
|
669
|
+
{ name: "Type", value: "type" },
|
|
670
|
+
{ name: "Upload", value: "upload" },
|
|
671
|
+
{ name: "Wait", value: "wait" },
|
|
672
|
+
{ name: "Wait for Selector", value: "waitForSelector" }
|
|
478
673
|
]
|
|
479
674
|
},
|
|
480
675
|
// === Rent: rental parameters ===
|
|
@@ -492,31 +687,31 @@ var BrowserCeki = class {
|
|
|
492
687
|
type: "string",
|
|
493
688
|
default: "",
|
|
494
689
|
placeholder: "RU, EE, US\u2026",
|
|
495
|
-
displayOptions: { show: { operation: ["search"] } }
|
|
690
|
+
displayOptions: { show: { operation: ["search", "captchaScrape", "screenshotGeo"] } }
|
|
496
691
|
},
|
|
497
692
|
{
|
|
498
|
-
displayName: "Max $/
|
|
693
|
+
displayName: "Max $/Min",
|
|
499
694
|
name: "maxPrice",
|
|
500
695
|
type: "number",
|
|
501
696
|
typeOptions: { numberPrecision: 4 },
|
|
502
697
|
default: 0.02,
|
|
503
|
-
displayOptions: { show: { operation: ["search"] } }
|
|
698
|
+
displayOptions: { show: { operation: ["search", "captchaScrape", "screenshotGeo"] } }
|
|
504
699
|
},
|
|
505
700
|
{
|
|
506
|
-
displayName: "Min
|
|
701
|
+
displayName: "Min Rating",
|
|
507
702
|
name: "minRating",
|
|
508
703
|
type: "number",
|
|
509
704
|
default: 0,
|
|
510
|
-
displayOptions: { show: { operation: ["search"] } }
|
|
705
|
+
displayOptions: { show: { operation: ["search", "captchaScrape", "screenshotGeo"] } }
|
|
511
706
|
},
|
|
512
707
|
{
|
|
513
|
-
displayName: "Profile
|
|
708
|
+
displayName: "Profile Mode",
|
|
514
709
|
name: "mode",
|
|
515
710
|
type: "options",
|
|
516
711
|
default: "incognito",
|
|
517
712
|
options: [
|
|
518
|
-
{ name: "
|
|
519
|
-
{ name: "
|
|
713
|
+
{ name: "Incognito", value: "incognito" },
|
|
714
|
+
{ name: "Main", value: "main" }
|
|
520
715
|
],
|
|
521
716
|
displayOptions: { show: { operation: ["rent", "full"] } }
|
|
522
717
|
},
|
|
@@ -529,13 +724,211 @@ var BrowserCeki = class {
|
|
|
529
724
|
displayOptions: { show: { operation: ["navigate", "full"] } }
|
|
530
725
|
},
|
|
531
726
|
{
|
|
532
|
-
displayName: "Demo
|
|
727
|
+
displayName: "Demo Mode (No Browser Needed)",
|
|
533
728
|
name: "demoMode",
|
|
534
729
|
type: "boolean",
|
|
535
730
|
default: true,
|
|
536
|
-
description: "
|
|
731
|
+
description: "Whether to skip actual browser rent and generate demo output",
|
|
537
732
|
displayOptions: { show: { operation: ["full"] } }
|
|
538
733
|
},
|
|
734
|
+
// === Captcha-protected Scrape params ===
|
|
735
|
+
{
|
|
736
|
+
displayName: "Captcha URL",
|
|
737
|
+
name: "url",
|
|
738
|
+
type: "string",
|
|
739
|
+
default: "https://example.com",
|
|
740
|
+
required: true,
|
|
741
|
+
description: "Page protected by anti-bot / captcha",
|
|
742
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
displayName: "Geo",
|
|
746
|
+
name: "geo",
|
|
747
|
+
type: "string",
|
|
748
|
+
default: "RU",
|
|
749
|
+
placeholder: "RU, EE, US\u2026",
|
|
750
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
displayName: "Max $/Min",
|
|
754
|
+
name: "maxPrice",
|
|
755
|
+
type: "number",
|
|
756
|
+
typeOptions: { numberPrecision: 4 },
|
|
757
|
+
default: 0.02,
|
|
758
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
displayName: "Wait for Selector",
|
|
762
|
+
name: "waitSelector",
|
|
763
|
+
type: "string",
|
|
764
|
+
default: "",
|
|
765
|
+
placeholder: "CSS selector (optional)",
|
|
766
|
+
description: "Wait until this selector appears in the DOM",
|
|
767
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
displayName: "Wait Timeout (Ms)",
|
|
771
|
+
name: "waitTimeout",
|
|
772
|
+
type: "number",
|
|
773
|
+
default: 3e4,
|
|
774
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
displayName: "Extract HTML",
|
|
778
|
+
name: "extractHtml",
|
|
779
|
+
type: "boolean",
|
|
780
|
+
default: true,
|
|
781
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
displayName: "HTML Selector",
|
|
785
|
+
name: "htmlSelector",
|
|
786
|
+
type: "string",
|
|
787
|
+
default: "body",
|
|
788
|
+
placeholder: 'CSS selector or "body"',
|
|
789
|
+
description: "OuterHTML of this selector is returned as html",
|
|
790
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
displayName: "Full Page Screenshot",
|
|
794
|
+
name: "fullPage",
|
|
795
|
+
type: "boolean",
|
|
796
|
+
default: false,
|
|
797
|
+
displayOptions: { show: { operation: ["captchaScrape"] } }
|
|
798
|
+
},
|
|
799
|
+
// === Screenshot in Geo params ===
|
|
800
|
+
{
|
|
801
|
+
displayName: "Geo URL",
|
|
802
|
+
name: "url",
|
|
803
|
+
type: "string",
|
|
804
|
+
default: "https://ifconfig.me",
|
|
805
|
+
required: true,
|
|
806
|
+
displayOptions: { show: { operation: ["screenshotGeo"] } }
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
displayName: "Geo",
|
|
810
|
+
name: "geo",
|
|
811
|
+
type: "string",
|
|
812
|
+
default: "RU",
|
|
813
|
+
placeholder: "RU, EE, US\u2026",
|
|
814
|
+
displayOptions: { show: { operation: ["screenshotGeo"] } }
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
displayName: "Full Page Screenshot",
|
|
818
|
+
name: "fullPage",
|
|
819
|
+
type: "boolean",
|
|
820
|
+
default: false,
|
|
821
|
+
displayOptions: { show: { operation: ["screenshotGeo"] } }
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
displayName: "Max $/Min",
|
|
825
|
+
name: "maxPrice",
|
|
826
|
+
type: "number",
|
|
827
|
+
default: 0.02,
|
|
828
|
+
displayOptions: { show: { operation: ["screenshotGeo"] } }
|
|
829
|
+
},
|
|
830
|
+
// === Contract params ===
|
|
831
|
+
{
|
|
832
|
+
displayName: "Contract ID",
|
|
833
|
+
name: "contractId",
|
|
834
|
+
type: "number",
|
|
835
|
+
default: 0,
|
|
836
|
+
description: "Ceki contract ID",
|
|
837
|
+
displayOptions: { show: { operation: ["contract_tasks", "contract_create"] } }
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
displayName: "Event ID",
|
|
841
|
+
name: "eventId",
|
|
842
|
+
type: "number",
|
|
843
|
+
default: 0,
|
|
844
|
+
description: "Task / event ID (KalEvent)",
|
|
845
|
+
displayOptions: {
|
|
846
|
+
show: { operation: ["contract_get", "contract_assign", "contract_setStatus", "contract_comment", "contract_progress", "contract_callHuman"] }
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
displayName: "Label",
|
|
851
|
+
name: "label",
|
|
852
|
+
type: "string",
|
|
853
|
+
default: "",
|
|
854
|
+
required: true,
|
|
855
|
+
displayOptions: { show: { operation: ["contract_create"] } }
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
displayName: "Description",
|
|
859
|
+
name: "description",
|
|
860
|
+
type: "string",
|
|
861
|
+
typeOptions: { rows: 4 },
|
|
862
|
+
default: "",
|
|
863
|
+
displayOptions: { show: { operation: ["contract_create", "contract_comment"] } }
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
displayName: "Executor Type",
|
|
867
|
+
name: "benefitableType",
|
|
868
|
+
type: "options",
|
|
869
|
+
default: "agent",
|
|
870
|
+
options: [
|
|
871
|
+
{ name: "Agent", value: "agent" },
|
|
872
|
+
{ name: "User (Human)", value: "user" }
|
|
873
|
+
],
|
|
874
|
+
displayOptions: { show: { operation: ["contract_create", "contract_assign"] } }
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
displayName: "Executor ID",
|
|
878
|
+
name: "benefitableValue",
|
|
879
|
+
type: "number",
|
|
880
|
+
default: 0,
|
|
881
|
+
description: "Agent ID or user ID of the executor",
|
|
882
|
+
displayOptions: { show: { operation: ["contract_create", "contract_assign"] } }
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
displayName: "Status",
|
|
886
|
+
name: "status",
|
|
887
|
+
type: "options",
|
|
888
|
+
options: [
|
|
889
|
+
{ name: "100 \xB7 Backlog", value: 100 },
|
|
890
|
+
{ name: "200 \xB7 Hand (Assigned)", value: 200 },
|
|
891
|
+
{ name: "222 \xB7 Hand Done", value: 222 },
|
|
892
|
+
{ name: "300 \xB7 QA", value: 300 },
|
|
893
|
+
{ name: "350 \xB7 QA Done", value: 350 },
|
|
894
|
+
{ name: "499 \xB7 Reviewer", value: 499 }
|
|
895
|
+
],
|
|
896
|
+
default: 200,
|
|
897
|
+
displayOptions: { show: { operation: ["contract_create", "contract_setStatus", "contract_progress"] } }
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
displayName: "Progress Description",
|
|
901
|
+
name: "progressDesc",
|
|
902
|
+
type: "string",
|
|
903
|
+
typeOptions: { rows: 4 },
|
|
904
|
+
default: "",
|
|
905
|
+
required: true,
|
|
906
|
+
description: "Body of the progress comment (does not overwrite the task spec)",
|
|
907
|
+
displayOptions: { show: { operation: ["contract_progress"] } }
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
displayName: "Call Kind",
|
|
911
|
+
name: "callKind",
|
|
912
|
+
type: "options",
|
|
913
|
+
default: "review",
|
|
914
|
+
options: [
|
|
915
|
+
{ name: "Input (Need Clarification)", value: "input" },
|
|
916
|
+
{ name: "Review (Done, Take a Look)", value: "review" },
|
|
917
|
+
{ name: "Stuck (Blocked)", value: "stuck" }
|
|
918
|
+
],
|
|
919
|
+
description: "Type of escalation to a human (the call-human action)",
|
|
920
|
+
displayOptions: { show: { operation: ["contract_callHuman"] } }
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
displayName: "Message",
|
|
924
|
+
name: "callDesc",
|
|
925
|
+
type: "string",
|
|
926
|
+
typeOptions: { rows: 4 },
|
|
927
|
+
default: "",
|
|
928
|
+
required: true,
|
|
929
|
+
description: "What to tell the human \u2014 context, question, or what was done",
|
|
930
|
+
displayOptions: { show: { operation: ["contract_callHuman"] } }
|
|
931
|
+
},
|
|
539
932
|
// === Operations: session_id ===
|
|
540
933
|
{
|
|
541
934
|
displayName: "Session ID",
|
|
@@ -592,13 +985,13 @@ var BrowserCeki = class {
|
|
|
592
985
|
type: "options",
|
|
593
986
|
default: "png",
|
|
594
987
|
options: [
|
|
595
|
-
{ name: "
|
|
596
|
-
{ name: "
|
|
988
|
+
{ name: "Base64", value: "base64" },
|
|
989
|
+
{ name: "PNG (Binary)", value: "png" }
|
|
597
990
|
],
|
|
598
991
|
displayOptions: { show: { operation: ["screenshot"] } }
|
|
599
992
|
},
|
|
600
993
|
{
|
|
601
|
-
displayName: "Full
|
|
994
|
+
displayName: "Full Page",
|
|
602
995
|
name: "fullPage",
|
|
603
996
|
type: "boolean",
|
|
604
997
|
default: false,
|
|
@@ -623,7 +1016,7 @@ var BrowserCeki = class {
|
|
|
623
1016
|
displayOptions: { show: { operation: ["waitForSelector"] } }
|
|
624
1017
|
},
|
|
625
1018
|
{
|
|
626
|
-
displayName: "Timeout (
|
|
1019
|
+
displayName: "Timeout (Ms)",
|
|
627
1020
|
name: "waitTimeout",
|
|
628
1021
|
type: "number",
|
|
629
1022
|
default: 3e4,
|
|
@@ -653,6 +1046,40 @@ var BrowserCeki = class {
|
|
|
653
1046
|
const out = [];
|
|
654
1047
|
const creds = await this.getCredentials("cekiApi");
|
|
655
1048
|
const token = creds.token;
|
|
1049
|
+
const node = this.getNode();
|
|
1050
|
+
const touchedSessions = /* @__PURE__ */ new Set();
|
|
1051
|
+
const _stopSession = async (sid) => {
|
|
1052
|
+
await new Promise((resolve) => {
|
|
1053
|
+
const stopWs = new WebSocket("wss://browser.ceki.me/ws/agent", ["bearer." + token]);
|
|
1054
|
+
const _t = AbortSignal.timeout(1e4);
|
|
1055
|
+
const _done = () => {
|
|
1056
|
+
try {
|
|
1057
|
+
stopWs.close();
|
|
1058
|
+
} catch {
|
|
1059
|
+
}
|
|
1060
|
+
resolve();
|
|
1061
|
+
};
|
|
1062
|
+
_t.addEventListener("abort", _done, { once: true });
|
|
1063
|
+
stopWs.onopen = () => {
|
|
1064
|
+
try {
|
|
1065
|
+
stopWs.send(JSON.stringify({ type: "stop", session_id: sid, reason: "n8n error" }));
|
|
1066
|
+
} catch {
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
stopWs.onmessage = (ev) => {
|
|
1070
|
+
try {
|
|
1071
|
+
const m = JSON.parse(ev.data);
|
|
1072
|
+
if (m.type === "session_ended") {
|
|
1073
|
+
_t.removeEventListener("abort", _done);
|
|
1074
|
+
_done();
|
|
1075
|
+
}
|
|
1076
|
+
} catch {
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
stopWs.onerror = _done;
|
|
1080
|
+
stopWs.onclose = _done;
|
|
1081
|
+
});
|
|
1082
|
+
};
|
|
656
1083
|
const resolveSid = async (i, client) => {
|
|
657
1084
|
const scheduleId = this.getNodeParameter("scheduleId", i);
|
|
658
1085
|
if (scheduleId) return scheduleId;
|
|
@@ -662,331 +1089,527 @@ var BrowserCeki = class {
|
|
|
662
1089
|
geo: geo || void 0,
|
|
663
1090
|
max_price_per_min: maxPrice
|
|
664
1091
|
});
|
|
665
|
-
if (!list.length) throw new
|
|
1092
|
+
if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers found by filters");
|
|
666
1093
|
return list[0].schedule_id;
|
|
667
1094
|
};
|
|
668
1095
|
for (let i = 0; i < items.length; i++) {
|
|
669
|
-
const op = this.getNodeParameter("operation", i);
|
|
670
|
-
const client = new CekiClient(token);
|
|
671
|
-
let browser;
|
|
672
|
-
let needFullClose = false;
|
|
673
1096
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
1097
|
+
const op = this.getNodeParameter("operation", i);
|
|
1098
|
+
const client = new CekiClient(token);
|
|
1099
|
+
let browser;
|
|
1100
|
+
let needFullClose = false;
|
|
1101
|
+
try {
|
|
1102
|
+
await client.connect();
|
|
1103
|
+
if (op === "rent") {
|
|
1104
|
+
try {
|
|
1105
|
+
const sid2 = await resolveSid(i, client);
|
|
1106
|
+
const mode = this.getNodeParameter("mode", i);
|
|
1107
|
+
browser = await client.rent(sid2, { mode });
|
|
1108
|
+
out.push({
|
|
1109
|
+
json: { session_id: browser.sessionId, schedule_id: sid2, mode },
|
|
1110
|
+
pairedItem: { item: i }
|
|
1111
|
+
});
|
|
1112
|
+
} catch (e) {
|
|
1113
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Rent failed" });
|
|
1114
|
+
} finally {
|
|
1115
|
+
await client.disconnect();
|
|
1116
|
+
}
|
|
1117
|
+
continue;
|
|
687
1118
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
})
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
1119
|
+
if (op === "search") {
|
|
1120
|
+
try {
|
|
1121
|
+
const geo = this.getNodeParameter("geo", i);
|
|
1122
|
+
const maxPrice = this.getNodeParameter("maxPrice", i);
|
|
1123
|
+
const list = await client.search({
|
|
1124
|
+
geo: geo || void 0,
|
|
1125
|
+
max_price_per_min: maxPrice
|
|
1126
|
+
});
|
|
1127
|
+
out.push({ json: { browsers: list, count: list.length }, pairedItem: { item: i } });
|
|
1128
|
+
} catch (e) {
|
|
1129
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Search failed" });
|
|
1130
|
+
} finally {
|
|
1131
|
+
await client.disconnect();
|
|
1132
|
+
}
|
|
1133
|
+
continue;
|
|
703
1134
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1135
|
+
if (op === "my_sessions") {
|
|
1136
|
+
try {
|
|
1137
|
+
const resp = await fetch("https://api.ceki.me/api/agent/sessions", {
|
|
1138
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1139
|
+
});
|
|
1140
|
+
if (!resp.ok) throw new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: `HTTP ${resp.status}`, httpCode: `${resp.status}` });
|
|
1141
|
+
const body = await resp.json();
|
|
1142
|
+
const sessions = body.data ?? [];
|
|
1143
|
+
out.push({ json: { sessions, count: sessions.length }, pairedItem: { item: i } });
|
|
1144
|
+
} catch (e) {
|
|
1145
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "My sessions failed" });
|
|
1146
|
+
}
|
|
1147
|
+
continue;
|
|
717
1148
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const t = AbortSignal.timeout(15e3);
|
|
726
|
-
t.addEventListener("abort", () => {
|
|
727
|
-
try {
|
|
728
|
-
ws.close();
|
|
729
|
-
} catch {
|
|
730
|
-
}
|
|
731
|
-
reject(new Error("Stop session timed out"));
|
|
732
|
-
}, { once: true });
|
|
733
|
-
ws.onopen = () => {
|
|
734
|
-
ws.send(JSON.stringify({ type: "stop", session_id: sessionId2, reason: "n8n stop_session" }));
|
|
735
|
-
};
|
|
736
|
-
ws.onmessage = (ev) => {
|
|
737
|
-
const msg = JSON.parse(ev.data);
|
|
738
|
-
if (msg.type === "session_ended" || msg.type === "pong") {
|
|
1149
|
+
if (op === "stop_session") {
|
|
1150
|
+
const sessionId2 = this.getNodeParameter("sessionId", i);
|
|
1151
|
+
try {
|
|
1152
|
+
await new Promise((resolve, reject) => {
|
|
1153
|
+
const ws = new WebSocket("wss://browser.ceki.me/ws/agent", [`bearer.${token}`]);
|
|
1154
|
+
const t = AbortSignal.timeout(15e3);
|
|
1155
|
+
t.addEventListener("abort", () => {
|
|
739
1156
|
try {
|
|
740
1157
|
ws.close();
|
|
741
1158
|
} catch {
|
|
742
1159
|
}
|
|
743
|
-
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
sid2 = await resolveSid(i, client);
|
|
1160
|
+
reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "Stop session timed out" }));
|
|
1161
|
+
}, { once: true });
|
|
1162
|
+
ws.onopen = () => {
|
|
1163
|
+
ws.send(JSON.stringify({ type: "stop", session_id: sessionId2, reason: "n8n stop_session" }));
|
|
1164
|
+
};
|
|
1165
|
+
ws.onmessage = (ev) => {
|
|
1166
|
+
const msg = JSON.parse(ev.data);
|
|
1167
|
+
if (msg.type === "session_ended" || msg.type === "pong") {
|
|
1168
|
+
try {
|
|
1169
|
+
ws.close();
|
|
1170
|
+
} catch {
|
|
1171
|
+
}
|
|
1172
|
+
resolve();
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
ws.onerror = () => {
|
|
1176
|
+
reject(new import_n8n_workflow.NodeApiError(node, void 0, { itemIndex: i, message: "WebSocket connection failed" }));
|
|
1177
|
+
};
|
|
1178
|
+
});
|
|
1179
|
+
out.push({ json: { stopped: true, session_id: sessionId2 }, pairedItem: { item: i } });
|
|
764
1180
|
} catch (e) {
|
|
765
|
-
throw new
|
|
1181
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Stop session failed" });
|
|
766
1182
|
}
|
|
1183
|
+
continue;
|
|
767
1184
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
let session_id;
|
|
772
|
-
let binary;
|
|
773
|
-
try {
|
|
1185
|
+
if (op === "full") {
|
|
1186
|
+
const demoMode = this.getNodeParameter("demoMode", i);
|
|
1187
|
+
let sid2;
|
|
774
1188
|
if (demoMode) {
|
|
775
|
-
|
|
776
|
-
const demoPng = Buffer.from("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", "base64");
|
|
777
|
-
binary = await this.helpers.prepareBinaryData(demoPng, "screenshot.png", "image/png");
|
|
1189
|
+
sid2 = 99999;
|
|
778
1190
|
} else {
|
|
779
1191
|
try {
|
|
780
|
-
|
|
781
|
-
session_id = browser.sessionId;
|
|
782
|
-
await browser.navigate(url);
|
|
783
|
-
await sleep(ms);
|
|
784
|
-
const shot = await browser.screenshot({ format: "png", fullPage: true });
|
|
785
|
-
const data = shot.toString("base64");
|
|
786
|
-
binary = await this.helpers.prepareBinaryData(Buffer.from(data, "base64"), "screenshot.png", "image/png");
|
|
787
|
-
await browser.close();
|
|
1192
|
+
sid2 = await resolveSid(i, client);
|
|
788
1193
|
} catch (e) {
|
|
789
|
-
throw new
|
|
1194
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full: resolve schedule failed" });
|
|
790
1195
|
}
|
|
791
1196
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
url,
|
|
798
|
-
waited: ms
|
|
799
|
-
},
|
|
800
|
-
binary: { data: binary }
|
|
801
|
-
});
|
|
802
|
-
} finally {
|
|
803
|
-
await client.close().catch(() => {
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
const sessionId = this.getNodeParameter("sessionId", i);
|
|
809
|
-
try {
|
|
810
|
-
browser = await client.resume(sessionId);
|
|
811
|
-
} catch (e) {
|
|
812
|
-
throw new Error(`Resume session "${sessionId}" failed: ${e.message}. Session may have expired or still be in grace.`);
|
|
813
|
-
}
|
|
814
|
-
const sid = browser.sessionId;
|
|
815
|
-
switch (op) {
|
|
816
|
-
case "navigate": {
|
|
817
|
-
try {
|
|
818
|
-
const url = this.getNodeParameter("url", i);
|
|
819
|
-
await browser.navigate(url);
|
|
820
|
-
out.push({ json: { session_id: sid, url } });
|
|
821
|
-
} catch (e) {
|
|
822
|
-
throw new Error(`Navigate failed: ${e.message}`);
|
|
823
|
-
}
|
|
824
|
-
break;
|
|
825
|
-
}
|
|
826
|
-
case "click": {
|
|
827
|
-
try {
|
|
828
|
-
const x2 = this.getNodeParameter("x", i);
|
|
829
|
-
const y2 = this.getNodeParameter("y", i);
|
|
830
|
-
await browser.click(x2, y2);
|
|
831
|
-
out.push({ json: { session_id: sid, clicked: [x2, y2] } });
|
|
832
|
-
} catch (e) {
|
|
833
|
-
throw new Error(`Click at (${x},${y}) failed: ${e.message}`);
|
|
834
|
-
}
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
case "type": {
|
|
1197
|
+
const mode = this.getNodeParameter("mode", i);
|
|
1198
|
+
const url = this.getNodeParameter("url", i);
|
|
1199
|
+
const ms = this.getNodeParameter("ms", i);
|
|
1200
|
+
let session_id;
|
|
1201
|
+
let binary;
|
|
838
1202
|
try {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1203
|
+
if (demoMode) {
|
|
1204
|
+
session_id = "demo-" + Date.now();
|
|
1205
|
+
const demoPng = Buffer.from("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", "base64");
|
|
1206
|
+
binary = await this.helpers.prepareBinaryData(demoPng, "screenshot.png", "image/png");
|
|
1207
|
+
} else {
|
|
1208
|
+
try {
|
|
1209
|
+
browser = await client.rent(sid2, { mode });
|
|
1210
|
+
session_id = browser.sessionId;
|
|
1211
|
+
await browser.navigate(url);
|
|
1212
|
+
await sleep(ms);
|
|
1213
|
+
const shot = await browser.screenshot({ format: "png", fullPage: true });
|
|
1214
|
+
const data = shot.toString("base64");
|
|
1215
|
+
binary = await this.helpers.prepareBinaryData(Buffer.from(data, "base64"), "screenshot.png", "image/png");
|
|
1216
|
+
await browser.close();
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Full op failed" });
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
out.push({
|
|
1222
|
+
json: {
|
|
1223
|
+
session_id,
|
|
1224
|
+
schedule_id: sid2,
|
|
1225
|
+
mode,
|
|
1226
|
+
url,
|
|
1227
|
+
waited: ms
|
|
1228
|
+
},
|
|
1229
|
+
binary: { data: binary },
|
|
1230
|
+
pairedItem: { item: i }
|
|
1231
|
+
});
|
|
1232
|
+
} finally {
|
|
1233
|
+
await client.close().catch(() => {
|
|
1234
|
+
});
|
|
844
1235
|
}
|
|
845
|
-
|
|
1236
|
+
continue;
|
|
846
1237
|
}
|
|
847
|
-
|
|
1238
|
+
if (op === "captchaScrape") {
|
|
1239
|
+
const url = this.getNodeParameter("url", i);
|
|
1240
|
+
const geo = this.getNodeParameter("geo", i);
|
|
1241
|
+
const maxPrice = this.getNodeParameter("maxPrice", i);
|
|
1242
|
+
const waitSelector = this.getNodeParameter("waitSelector", i) || "";
|
|
1243
|
+
const waitTimeout = this.getNodeParameter("waitTimeout", i);
|
|
1244
|
+
const extractHtmlFlag = this.getNodeParameter("extractHtml", i);
|
|
1245
|
+
const htmlSelector = this.getNodeParameter("htmlSelector", i) || "body";
|
|
1246
|
+
const fullPage = this.getNodeParameter("fullPage", i);
|
|
1247
|
+
let scheduleId = 0;
|
|
848
1248
|
try {
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
1249
|
+
const list = await client.search({ geo: geo || void 0, max_price_per_min: maxPrice });
|
|
1250
|
+
if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + (geo || "*"), { itemIndex: i });
|
|
1251
|
+
scheduleId = list[0].schedule_id;
|
|
1252
|
+
browser = await client.rent(scheduleId);
|
|
1253
|
+
touchedSessions.add(browser.sessionId);
|
|
1254
|
+
await browser.navigate(url);
|
|
1255
|
+
if (waitSelector) {
|
|
1256
|
+
await waitForSelector(node, browser, waitSelector, waitTimeout);
|
|
1257
|
+
}
|
|
1258
|
+
const shot = await browser.screenshot({ format: "base64", fullPage });
|
|
1259
|
+
const data = shot.data ?? (shot instanceof Buffer ? shot.toString("base64") : "");
|
|
1260
|
+
const binary = await this.helpers.prepareBinaryData(
|
|
1261
|
+
Buffer.from(data, "base64"),
|
|
1262
|
+
"captcha-scrape.png",
|
|
1263
|
+
"image/png"
|
|
1264
|
+
);
|
|
1265
|
+
const json = {
|
|
1266
|
+
url,
|
|
1267
|
+
geo,
|
|
1268
|
+
schedule_id: scheduleId
|
|
1269
|
+
};
|
|
1270
|
+
if (extractHtmlFlag) {
|
|
1271
|
+
json.html = await extractHtml(browser, htmlSelector);
|
|
1272
|
+
}
|
|
1273
|
+
out.push({ json, binary: { data: binary }, pairedItem: { item: i } });
|
|
852
1274
|
} catch (e) {
|
|
853
|
-
throw new
|
|
1275
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Captcha-protected scrape failed" });
|
|
1276
|
+
} finally {
|
|
1277
|
+
if (browser) {
|
|
1278
|
+
try {
|
|
1279
|
+
await browser.close();
|
|
1280
|
+
} catch {
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
try {
|
|
1284
|
+
await client.close();
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
854
1287
|
}
|
|
855
|
-
|
|
1288
|
+
continue;
|
|
856
1289
|
}
|
|
857
|
-
|
|
1290
|
+
if (op === "screenshotGeo") {
|
|
1291
|
+
const url = this.getNodeParameter("url", i);
|
|
1292
|
+
const geo = this.getNodeParameter("geo", i);
|
|
1293
|
+
const fullPage = this.getNodeParameter("fullPage", i);
|
|
1294
|
+
const maxPrice = this.getNodeParameter("maxPrice", i);
|
|
858
1295
|
try {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1296
|
+
const list = await client.search({ geo, max_price_per_min: maxPrice });
|
|
1297
|
+
if (!list.length) throw new import_n8n_workflow.NodeOperationError(node, "No browsers in geo " + geo, { itemIndex: i });
|
|
1298
|
+
browser = await client.rent(list[0].schedule_id);
|
|
1299
|
+
touchedSessions.add(browser.sessionId);
|
|
1300
|
+
await browser.navigate(url);
|
|
1301
|
+
const shot = await browser.screenshot({ format: "base64", fullPage });
|
|
863
1302
|
const binary = await this.helpers.prepareBinaryData(
|
|
864
|
-
Buffer.from(data, "base64"),
|
|
1303
|
+
Buffer.from(shot.data, "base64"),
|
|
865
1304
|
"screenshot.png",
|
|
866
1305
|
"image/png"
|
|
867
1306
|
);
|
|
868
|
-
out.push({
|
|
1307
|
+
out.push({
|
|
1308
|
+
json: { url, geo, schedule_id: list[0].schedule_id },
|
|
1309
|
+
binary: { data: binary },
|
|
1310
|
+
pairedItem: { item: i }
|
|
1311
|
+
});
|
|
869
1312
|
} catch (e) {
|
|
870
|
-
throw new
|
|
1313
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot in geo " + geo + " failed" });
|
|
1314
|
+
} finally {
|
|
1315
|
+
if (browser) {
|
|
1316
|
+
try {
|
|
1317
|
+
await browser.close();
|
|
1318
|
+
} catch {
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
try {
|
|
1322
|
+
await client.close();
|
|
1323
|
+
} catch {
|
|
1324
|
+
}
|
|
871
1325
|
}
|
|
872
|
-
|
|
1326
|
+
continue;
|
|
873
1327
|
}
|
|
874
|
-
|
|
1328
|
+
if (op.startsWith("contract_")) {
|
|
1329
|
+
const contractClient = new ContractClient(token);
|
|
1330
|
+
const pop = op.replace("contract_", "");
|
|
1331
|
+
let result;
|
|
875
1332
|
try {
|
|
876
|
-
|
|
1333
|
+
switch (pop) {
|
|
1334
|
+
case "list":
|
|
1335
|
+
result = await contractClient.listContracts();
|
|
1336
|
+
break;
|
|
1337
|
+
case "tasks": {
|
|
1338
|
+
const contractId = this.getNodeParameter("contractId", i);
|
|
1339
|
+
result = await contractClient.tasks(contractId);
|
|
1340
|
+
break;
|
|
1341
|
+
}
|
|
1342
|
+
case "get": {
|
|
1343
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1344
|
+
result = await contractClient.task(eventId);
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
case "myEvents":
|
|
1348
|
+
result = await contractClient.myEvents();
|
|
1349
|
+
break;
|
|
1350
|
+
case "create": {
|
|
1351
|
+
const contractId = this.getNodeParameter("contractId", i);
|
|
1352
|
+
const label = this.getNodeParameter("label", i);
|
|
1353
|
+
const description = this.getNodeParameter("description", i) || "";
|
|
1354
|
+
const status = this.getNodeParameter("status", i);
|
|
1355
|
+
const bType = this.getNodeParameter("benefitableType", i);
|
|
1356
|
+
const bValue = this.getNodeParameter("benefitableValue", i);
|
|
1357
|
+
result = await contractClient.create(contractId, {
|
|
1358
|
+
label,
|
|
1359
|
+
description: description || void 0,
|
|
1360
|
+
status,
|
|
1361
|
+
benefitable: bValue ? bType + ":" + bValue : void 0
|
|
1362
|
+
});
|
|
1363
|
+
break;
|
|
1364
|
+
}
|
|
1365
|
+
case "assign": {
|
|
1366
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1367
|
+
const bType = this.getNodeParameter("benefitableType", i);
|
|
1368
|
+
const bValue = this.getNodeParameter("benefitableValue", i);
|
|
1369
|
+
result = await contractClient.assign(eventId, bType + ":" + bValue);
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
case "setStatus": {
|
|
1373
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1374
|
+
const status = this.getNodeParameter("status", i);
|
|
1375
|
+
result = await contractClient.setStatus(eventId, status);
|
|
1376
|
+
break;
|
|
1377
|
+
}
|
|
1378
|
+
case "comment": {
|
|
1379
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1380
|
+
const text = this.getNodeParameter("description", i) || "";
|
|
1381
|
+
result = await contractClient.comment(eventId, text);
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
case "progress": {
|
|
1385
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1386
|
+
const status = this.getNodeParameter("status", i);
|
|
1387
|
+
const desc = this.getNodeParameter("progressDesc", i);
|
|
1388
|
+
result = await contractClient.progress(eventId, status, desc);
|
|
1389
|
+
break;
|
|
1390
|
+
}
|
|
1391
|
+
case "callHuman": {
|
|
1392
|
+
const eventId = this.getNodeParameter("eventId", i);
|
|
1393
|
+
const kind = this.getNodeParameter("callKind", i);
|
|
1394
|
+
const msg = this.getNodeParameter("callDesc", i);
|
|
1395
|
+
result = await contractClient.callHuman(eventId, kind, msg);
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
case "poll":
|
|
1399
|
+
result = await contractClient.poll();
|
|
1400
|
+
break;
|
|
1401
|
+
default:
|
|
1402
|
+
throw new import_n8n_workflow.NodeOperationError(node, "Unknown contract operation: " + pop, { itemIndex: i });
|
|
1403
|
+
}
|
|
877
1404
|
out.push({
|
|
878
|
-
json:
|
|
1405
|
+
json: result,
|
|
1406
|
+
pairedItem: { item: i }
|
|
879
1407
|
});
|
|
880
1408
|
} catch (e) {
|
|
881
|
-
throw new
|
|
1409
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Contract " + pop + " failed" });
|
|
882
1410
|
}
|
|
883
|
-
|
|
1411
|
+
continue;
|
|
884
1412
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
await
|
|
888
|
-
|
|
889
|
-
|
|
1413
|
+
const sessionId = this.getNodeParameter("sessionId", i);
|
|
1414
|
+
try {
|
|
1415
|
+
browser = await client.resume(sessionId);
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: `Resume session "${sessionId}" failed. Session may have expired or still be in grace.` });
|
|
890
1418
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
const expr = `!!document.querySelector(${JSON.stringify(selector)})`;
|
|
895
|
-
const deadline = Date.now() + timeout;
|
|
896
|
-
let ok = false;
|
|
897
|
-
let lastErr = null;
|
|
898
|
-
while (Date.now() < deadline) {
|
|
1419
|
+
const sid = browser.sessionId;
|
|
1420
|
+
switch (op) {
|
|
1421
|
+
case "navigate": {
|
|
899
1422
|
try {
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
break;
|
|
904
|
-
}
|
|
1423
|
+
const url = this.getNodeParameter("url", i);
|
|
1424
|
+
await browser.navigate(url);
|
|
1425
|
+
out.push({ json: { session_id: sid, url }, pairedItem: { item: i } });
|
|
905
1426
|
} catch (e) {
|
|
906
|
-
|
|
1427
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Navigate failed" });
|
|
907
1428
|
}
|
|
908
|
-
|
|
1429
|
+
break;
|
|
909
1430
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1431
|
+
case "click": {
|
|
1432
|
+
try {
|
|
1433
|
+
const x = this.getNodeParameter("x", i);
|
|
1434
|
+
const y = this.getNodeParameter("y", i);
|
|
1435
|
+
await browser.click(x, y);
|
|
1436
|
+
out.push({ json: { session_id: sid, clicked: [x, y] }, pairedItem: { item: i } });
|
|
1437
|
+
} catch (e) {
|
|
1438
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Click failed" });
|
|
1439
|
+
}
|
|
1440
|
+
break;
|
|
914
1441
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
const stream = await this.helpers.getBinaryStream(bin.id);
|
|
925
|
-
const chunks = [];
|
|
926
|
-
for await (const c of stream) chunks.push(c);
|
|
927
|
-
const buf = Buffer.concat(chunks);
|
|
928
|
-
const res = await browser.upload(selector, buf);
|
|
929
|
-
out.push({ json: { session_id: sid, uploaded: res } });
|
|
930
|
-
} catch (e) {
|
|
931
|
-
throw new Error(`Upload failed: ${e.message}`);
|
|
1442
|
+
case "type": {
|
|
1443
|
+
try {
|
|
1444
|
+
const text = this.getNodeParameter("text", i);
|
|
1445
|
+
await browser.type(text);
|
|
1446
|
+
out.push({ json: { session_id: sid, typed: text }, pairedItem: { item: i } });
|
|
1447
|
+
} catch (e) {
|
|
1448
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Type failed" });
|
|
1449
|
+
}
|
|
1450
|
+
break;
|
|
932
1451
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
})
|
|
939
|
-
|
|
1452
|
+
case "scroll": {
|
|
1453
|
+
try {
|
|
1454
|
+
const deltaY = this.getNodeParameter("deltaY", i);
|
|
1455
|
+
await browser.scroll(deltaY);
|
|
1456
|
+
out.push({ json: { session_id: sid, scrolled: deltaY }, pairedItem: { item: i } });
|
|
1457
|
+
} catch (e) {
|
|
1458
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Scroll failed" });
|
|
1459
|
+
}
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
case "screenshot": {
|
|
1463
|
+
try {
|
|
1464
|
+
const format = this.getNodeParameter("format", i);
|
|
1465
|
+
const fullPage = this.getNodeParameter("fullPage", i);
|
|
1466
|
+
const shot = await browser.screenshot({ format, fullPage });
|
|
1467
|
+
const data = format === "base64" ? shot.data : shot.toString("base64");
|
|
1468
|
+
const binary = await this.helpers.prepareBinaryData(
|
|
1469
|
+
Buffer.from(data, "base64"),
|
|
1470
|
+
"screenshot.png",
|
|
1471
|
+
"image/png"
|
|
1472
|
+
);
|
|
1473
|
+
out.push({ json: { session_id: sid }, binary: { data: binary }, pairedItem: { item: i } });
|
|
1474
|
+
} catch (e) {
|
|
1475
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Screenshot failed" });
|
|
1476
|
+
}
|
|
1477
|
+
break;
|
|
940
1478
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1479
|
+
case "snapshot": {
|
|
1480
|
+
try {
|
|
1481
|
+
const snap = await browser.snapshot();
|
|
1482
|
+
out.push({
|
|
1483
|
+
json: { session_id: sid, screenshot: snap.screenshot },
|
|
1484
|
+
pairedItem: { item: i }
|
|
1485
|
+
});
|
|
1486
|
+
} catch (e) {
|
|
1487
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Snapshot failed" });
|
|
1488
|
+
}
|
|
1489
|
+
break;
|
|
1490
|
+
}
|
|
1491
|
+
case "wait": {
|
|
1492
|
+
const ms = this.getNodeParameter("ms", i);
|
|
1493
|
+
await sleep(ms);
|
|
1494
|
+
out.push({ json: { session_id: sid, waited: ms }, pairedItem: { item: i } });
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
case "waitForSelector": {
|
|
1498
|
+
const selector = this.getNodeParameter("waitSelector", i);
|
|
1499
|
+
const timeout = this.getNodeParameter("waitTimeout", i);
|
|
1500
|
+
const expr = `!!document.querySelector(${JSON.stringify(selector)})`;
|
|
1501
|
+
const deadline = Date.now() + timeout;
|
|
1502
|
+
let ok = false;
|
|
1503
|
+
let lastErr = null;
|
|
1504
|
+
while (Date.now() < deadline) {
|
|
945
1505
|
try {
|
|
946
|
-
|
|
947
|
-
|
|
1506
|
+
const res = await browser.send("Runtime.evaluate", { expression: expr, returnByValue: true });
|
|
1507
|
+
if (res?.result?.value === true) {
|
|
1508
|
+
ok = true;
|
|
1509
|
+
break;
|
|
1510
|
+
}
|
|
1511
|
+
} catch (e) {
|
|
1512
|
+
lastErr = e;
|
|
948
1513
|
}
|
|
949
|
-
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1514
|
+
await sleep(500);
|
|
1515
|
+
}
|
|
1516
|
+
if (!ok) {
|
|
1517
|
+
throw new import_n8n_workflow.NodeOperationError(
|
|
1518
|
+
node,
|
|
1519
|
+
`waitForSelector("${selector}") timed out after ${timeout}ms${lastErr ? `: ${lastErr.message}` : ""}`,
|
|
1520
|
+
{ itemIndex: i }
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
out.push({ json: { session_id: sid, selector, found: true }, pairedItem: { item: i } });
|
|
1524
|
+
break;
|
|
1525
|
+
}
|
|
1526
|
+
case "upload": {
|
|
1527
|
+
try {
|
|
1528
|
+
const selector = this.getNodeParameter("selector", i);
|
|
1529
|
+
const bpn = this.getNodeParameter("binaryPropertyName", i);
|
|
1530
|
+
const bin = items[i].binary?.[bpn];
|
|
1531
|
+
if (!bin) throw new import_n8n_workflow.NodeOperationError(node, `Binary property "${bpn}" not found on input`, { itemIndex: i });
|
|
1532
|
+
const stream = await this.helpers.getBinaryStream(bin.id);
|
|
1533
|
+
const chunks = [];
|
|
1534
|
+
for await (const c of stream) chunks.push(c);
|
|
1535
|
+
const buf = Buffer.concat(chunks);
|
|
1536
|
+
const res = await browser.upload(selector, buf);
|
|
1537
|
+
out.push({ json: { session_id: sid, uploaded: res }, pairedItem: { item: i } });
|
|
1538
|
+
} catch (e) {
|
|
1539
|
+
throw new import_n8n_workflow.NodeApiError(node, e, { itemIndex: i, message: "Upload failed" });
|
|
1540
|
+
}
|
|
1541
|
+
break;
|
|
1542
|
+
}
|
|
1543
|
+
case "close": {
|
|
1544
|
+
try {
|
|
1545
|
+
await browser.close().catch(() => {
|
|
1546
|
+
});
|
|
1547
|
+
} catch {
|
|
1548
|
+
}
|
|
1549
|
+
await new Promise((resolve) => {
|
|
1550
|
+
const stopWs = new WebSocket("wss://browser.ceki.me/ws/agent", [`bearer.${token}`]);
|
|
1551
|
+
const abortTimer = AbortSignal.timeout(1e4);
|
|
1552
|
+
const onTimer = () => {
|
|
1553
|
+
try {
|
|
1554
|
+
stopWs.close();
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
resolve();
|
|
1558
|
+
};
|
|
1559
|
+
abortTimer.addEventListener("abort", onTimer, { once: true });
|
|
1560
|
+
stopWs.onopen = () => {
|
|
1561
|
+
stopWs.send(JSON.stringify({ type: "stop", session_id: sessionId, reason: "n8n close" }));
|
|
1562
|
+
};
|
|
1563
|
+
stopWs.onmessage = (ev) => {
|
|
1564
|
+
try {
|
|
1565
|
+
const msg = JSON.parse(ev.data);
|
|
1566
|
+
if (msg.type === "session_ended") {
|
|
1567
|
+
abortTimer.removeEventListener("abort", onTimer);
|
|
1568
|
+
try {
|
|
1569
|
+
stopWs.close();
|
|
1570
|
+
} catch {
|
|
1571
|
+
}
|
|
1572
|
+
resolve();
|
|
963
1573
|
}
|
|
964
|
-
|
|
1574
|
+
} catch {
|
|
965
1575
|
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1576
|
+
};
|
|
1577
|
+
stopWs.onerror = () => {
|
|
1578
|
+
abortTimer.removeEventListener("abort", onTimer);
|
|
1579
|
+
resolve();
|
|
1580
|
+
};
|
|
1581
|
+
stopWs.onclose = () => {
|
|
1582
|
+
abortTimer.removeEventListener("abort", onTimer);
|
|
1583
|
+
resolve();
|
|
1584
|
+
};
|
|
1585
|
+
});
|
|
1586
|
+
out.push({ json: { closed: true, session_id: sessionId }, pairedItem: { item: i } });
|
|
1587
|
+
break;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
needFullClose = op === "close";
|
|
1591
|
+
} finally {
|
|
1592
|
+
try {
|
|
1593
|
+
if (needFullClose) {
|
|
1594
|
+
await client.close();
|
|
1595
|
+
} else {
|
|
1596
|
+
await client.disconnect();
|
|
1597
|
+
}
|
|
1598
|
+
} catch {
|
|
980
1599
|
}
|
|
981
1600
|
}
|
|
982
|
-
|
|
983
|
-
|
|
1601
|
+
} catch (error) {
|
|
1602
|
+
if (this.continueOnFail()) {
|
|
1603
|
+
out.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1606
|
+
throw new import_n8n_workflow.NodeApiError(this.getNode(), error, { itemIndex: i });
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
if (touchedSessions.size) {
|
|
1610
|
+
for (const sid of touchedSessions) {
|
|
984
1611
|
try {
|
|
985
|
-
|
|
986
|
-
await client.close();
|
|
987
|
-
} else {
|
|
988
|
-
await client.disconnect();
|
|
989
|
-
}
|
|
1612
|
+
await _stopSession(sid);
|
|
990
1613
|
} catch {
|
|
991
1614
|
}
|
|
992
1615
|
}
|