@bubblelab/bubble-core 0.1.150 → 0.1.151
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bubble-bundle.d.ts +76 -76
- package/dist/bubbles/service-bubble/agi-inc.d.ts +20 -20
- package/dist/bubbles/service-bubble/ai-agent.d.ts +74 -74
- package/dist/bubbles/service-bubble/airtable.d.ts +76 -76
- package/dist/bubbles/service-bubble/apify/actors/instagram-hashtag-scraper.d.ts +2 -2
- package/dist/bubbles/service-bubble/apify/actors/instagram-scraper.d.ts +8 -8
- package/dist/bubbles/service-bubble/apify/actors/linkedin-jobs-scraper.d.ts +2 -2
- package/dist/bubbles/service-bubble/apify/actors/linkedin-posts-search.d.ts +22 -22
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-detail.d.ts +38 -38
- package/dist/bubbles/service-bubble/apify/actors/linkedin-profile-posts.d.ts +52 -52
- package/dist/bubbles/service-bubble/apify/actors/tiktok-scraper.d.ts +6 -6
- package/dist/bubbles/service-bubble/apify/actors/twitter-scraper.d.ts +12 -12
- package/dist/bubbles/service-bubble/apify/actors/youtube-scraper.d.ts +12 -12
- package/dist/bubbles/service-bubble/apify/apify-scraper.schema.d.ts +126 -126
- package/dist/bubbles/service-bubble/apify/apify.d.ts +18 -18
- package/dist/bubbles/service-bubble/ashby/ashby.d.ts +2 -2
- package/dist/bubbles/service-bubble/ashby/ashby.schema.d.ts +2 -2
- package/dist/bubbles/service-bubble/browserbase/browserbase.d.ts +38 -1
- package/dist/bubbles/service-bubble/browserbase/browserbase.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/browserbase/browserbase.js +80 -0
- package/dist/bubbles/service-bubble/browserbase/browserbase.js.map +1 -1
- package/dist/bubbles/service-bubble/browserbase/browserbase.schema.d.ts +34 -1
- package/dist/bubbles/service-bubble/browserbase/browserbase.schema.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/browserbase/browserbase.schema.js +30 -0
- package/dist/bubbles/service-bubble/browserbase/browserbase.schema.js.map +1 -1
- package/dist/bubbles/service-bubble/firecrawl.d.ts +334 -334
- package/dist/bubbles/service-bubble/followupboss.d.ts +4 -4
- package/dist/bubbles/service-bubble/github.d.ts +60 -60
- package/dist/bubbles/service-bubble/gmail.d.ts +164 -164
- package/dist/bubbles/service-bubble/google-calendar.d.ts +6 -6
- package/dist/bubbles/service-bubble/google-drive.d.ts +9 -9
- package/dist/bubbles/service-bubble/google-drive.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/google-drive.js +37 -1
- package/dist/bubbles/service-bubble/google-drive.js.map +1 -1
- package/dist/bubbles/service-bubble/http.d.ts +2 -2
- package/dist/bubbles/service-bubble/insforge-db.d.ts +8 -8
- package/dist/bubbles/service-bubble/jira/jira.d.ts +30 -30
- package/dist/bubbles/service-bubble/jira/jira.schema.d.ts +36 -36
- package/dist/bubbles/service-bubble/notion/notion.d.ts +164 -164
- package/dist/bubbles/service-bubble/notion/property-schemas.d.ts +8 -8
- package/dist/bubbles/service-bubble/postgresql.d.ts +8 -8
- package/dist/bubbles/service-bubble/resend.d.ts +4 -4
- package/dist/bubbles/service-bubble/salesforce/index.d.ts +4 -0
- package/dist/bubbles/service-bubble/salesforce/index.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/index.js +4 -0
- package/dist/bubbles/service-bubble/salesforce/index.js.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.d.ts +1331 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.js +618 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.js.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.schema.d.ts +1445 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.schema.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.schema.js +609 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.schema.js.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.utils.d.ts +87 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.utils.d.ts.map +1 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.utils.js +181 -0
- package/dist/bubbles/service-bubble/salesforce/salesforce.utils.js.map +1 -0
- package/dist/bubbles/service-bubble/slack/slack.d.ts +328 -328
- package/dist/bubbles/service-bubble/slack/slack.utils.d.ts.map +1 -1
- package/dist/bubbles/service-bubble/slack/slack.utils.js +17 -2
- package/dist/bubbles/service-bubble/slack/slack.utils.js.map +1 -1
- package/dist/bubbles/service-bubble/stripe/stripe.d.ts +24 -24
- package/dist/bubbles/service-bubble/stripe/stripe.schema.d.ts +28 -28
- package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.d.ts +6 -6
- package/dist/bubbles/tool-bubble/amazon-shopping-tool/amazon-shopping-tool.schema.d.ts +8 -8
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.d.ts +66 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.js +484 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.js.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.types.d.ts +85 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.types.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.types.js +2 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-browser-agent.types.js.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-fallback-step.d.ts +27 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-fallback-step.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-fallback-step.js +77 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/ai-fallback-step.js.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/index.d.ts +4 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/index.d.ts.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/index.js +3 -0
- package/dist/bubbles/tool-bubble/browser-tools/_shared/ai/index.js.map +1 -0
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-accept-invitations/tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-accept-invitations/tool.js +254 -195
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-accept-invitations/tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-connection/tool.d.ts +2 -0
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-connection/tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-connection/tool.js +346 -229
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-connection/tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-received-invitations/tool.d.ts +34 -33
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-received-invitations/tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-received-invitations/tool.js +212 -151
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-received-invitations/tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-sent-invitations/tool.d.ts +33 -32
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-sent-invitations/tool.d.ts.map +1 -1
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-sent-invitations/tool.js +188 -127
- package/dist/bubbles/tool-bubble/browser-tools/linkedin-sent-invitations/tool.js.map +1 -1
- package/dist/bubbles/tool-bubble/bubbleflow-validation-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/company-enrichment-tool.d.ts +20 -20
- package/dist/bubbles/tool-bubble/linkedin-connection-tool/linkedin-connection-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/linkedin-tool.d.ts +380 -380
- package/dist/bubbles/tool-bubble/people-search-tool.d.ts +44 -44
- package/dist/bubbles/tool-bubble/reddit-scrape-tool.d.ts +10 -10
- package/dist/bubbles/tool-bubble/research-agent-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/sql-query-tool.d.ts +4 -4
- package/dist/bubbles/tool-bubble/tiktok-tool.d.ts +60 -60
- package/dist/bubbles/tool-bubble/twitter-tool.d.ts +134 -134
- package/dist/bubbles/tool-bubble/yc-scraper-tool.d.ts +8 -8
- package/dist/bubbles/tool-bubble/youtube-tool.d.ts +20 -20
- package/dist/bubbles/workflow-bubble/generate-document.workflow.d.ts +12 -12
- package/dist/bubbles/workflow-bubble/pdf-form-operations.workflow.d.ts +8 -8
- package/dist/bubbles/workflow-bubble/slack-data-assistant.workflow.d.ts +2 -2
- package/dist/bubbles/workflow-bubble/slack-formatter-agent.d.ts +18 -18
- package/dist/bubbles.json +73 -2
- package/package.json +1 -1
|
@@ -1,8 +1,43 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
1
35
|
import { ToolBubble } from '../../../../types/tool-bubble-class.js';
|
|
36
|
+
import { AIFallbackStep } from '../_shared/ai/ai-fallback-step.js';
|
|
2
37
|
import { BrowserBaseBubble, } from '../../../service-bubble/browserbase/index.js';
|
|
3
38
|
import { CredentialType } from '@bubblelab/shared-schemas';
|
|
4
39
|
import { parseBrowserSessionData, buildProxyConfig } from '../_shared/utils.js';
|
|
5
|
-
import { LinkedInConnectionToolParamsSchema, LinkedInConnectionToolResultSchema, } from './schema.js';
|
|
40
|
+
import { LinkedInConnectionToolParamsSchema, LinkedInConnectionToolResultSchema, ProfileInfoSchema, } from './schema.js';
|
|
6
41
|
/**
|
|
7
42
|
* Recordable LinkedIn Connection Tool
|
|
8
43
|
*
|
|
@@ -10,106 +45,167 @@ import { LinkedInConnectionToolParamsSchema, LinkedInConnectionToolResultSchema,
|
|
|
10
45
|
* Each major action is decorated with @RecordableStep to capture before/after
|
|
11
46
|
* screenshots, URLs, and timing information.
|
|
12
47
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
48
|
+
let LinkedInConnectionTool = (() => {
|
|
49
|
+
let _classSuper = ToolBubble;
|
|
50
|
+
let _instanceExtraInitializers = [];
|
|
51
|
+
let _stepNavigateToProfile_decorators;
|
|
52
|
+
let _stepWaitForProfilePage_decorators;
|
|
53
|
+
let _stepExtractProfileInfo_decorators;
|
|
54
|
+
let _stepClickConnect_decorators;
|
|
55
|
+
let _stepWaitForModal_decorators;
|
|
56
|
+
let _stepAddNote_decorators;
|
|
57
|
+
let _stepSendRequest_decorators;
|
|
58
|
+
return class LinkedInConnectionTool extends _classSuper {
|
|
59
|
+
static {
|
|
60
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
61
|
+
_stepNavigateToProfile_decorators = [AIFallbackStep('Navigate to profile', {
|
|
62
|
+
taskDescription: 'Navigate to the LinkedIn profile URL and wait for page to load',
|
|
63
|
+
})];
|
|
64
|
+
_stepWaitForProfilePage_decorators = [AIFallbackStep('Wait for profile page', {
|
|
65
|
+
taskDescription: 'Wait for LinkedIn profile page to fully load with action buttons (Connect, Message, Follow)',
|
|
66
|
+
})];
|
|
67
|
+
_stepExtractProfileInfo_decorators = [AIFallbackStep('Extract profile info', {
|
|
68
|
+
taskDescription: 'Extract the LinkedIn profile name, headline, and location from the profile page',
|
|
69
|
+
extractionSchema: ProfileInfoSchema,
|
|
70
|
+
})];
|
|
71
|
+
_stepClickConnect_decorators = [AIFallbackStep('Click Connect button', {
|
|
72
|
+
taskDescription: 'Find and click the Connect button to send a connection request. If there is no visible "Connect" button, first click the "More" button to open the dropdown menu, then click "Connect" inside the dropdown. The goal is to open the connection request modal.',
|
|
73
|
+
})];
|
|
74
|
+
_stepWaitForModal_decorators = [AIFallbackStep('Wait for connection modal', {
|
|
75
|
+
taskDescription: 'Wait for the connection modal to appear with "Add a note" or "Send without a note" buttons',
|
|
76
|
+
})];
|
|
77
|
+
_stepAddNote_decorators = [AIFallbackStep('Add note to connection', {
|
|
78
|
+
taskDescription: 'Click "Add a note" button and type the personalized message into the textarea',
|
|
79
|
+
})];
|
|
80
|
+
_stepSendRequest_decorators = [AIFallbackStep('Send connection request', {
|
|
81
|
+
taskDescription: 'Click the Send button to submit the connection request. Look for a blue "Send" button or "Send without a note" button in the connection modal.',
|
|
82
|
+
})];
|
|
83
|
+
__esDecorate(this, null, _stepNavigateToProfile_decorators, { kind: "method", name: "stepNavigateToProfile", static: false, private: false, access: { has: obj => "stepNavigateToProfile" in obj, get: obj => obj.stepNavigateToProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
84
|
+
__esDecorate(this, null, _stepWaitForProfilePage_decorators, { kind: "method", name: "stepWaitForProfilePage", static: false, private: false, access: { has: obj => "stepWaitForProfilePage" in obj, get: obj => obj.stepWaitForProfilePage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
85
|
+
__esDecorate(this, null, _stepExtractProfileInfo_decorators, { kind: "method", name: "stepExtractProfileInfo", static: false, private: false, access: { has: obj => "stepExtractProfileInfo" in obj, get: obj => obj.stepExtractProfileInfo }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
86
|
+
__esDecorate(this, null, _stepClickConnect_decorators, { kind: "method", name: "stepClickConnect", static: false, private: false, access: { has: obj => "stepClickConnect" in obj, get: obj => obj.stepClickConnect }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
87
|
+
__esDecorate(this, null, _stepWaitForModal_decorators, { kind: "method", name: "stepWaitForModal", static: false, private: false, access: { has: obj => "stepWaitForModal" in obj, get: obj => obj.stepWaitForModal }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
88
|
+
__esDecorate(this, null, _stepAddNote_decorators, { kind: "method", name: "stepAddNote", static: false, private: false, access: { has: obj => "stepAddNote" in obj, get: obj => obj.stepAddNote }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
89
|
+
__esDecorate(this, null, _stepSendRequest_decorators, { kind: "method", name: "stepSendRequest", static: false, private: false, access: { has: obj => "stepSendRequest" in obj, get: obj => obj.stepSendRequest }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
90
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
91
|
+
}
|
|
92
|
+
static bubbleName = 'linkedin-connection-tool';
|
|
93
|
+
static schema = LinkedInConnectionToolParamsSchema;
|
|
94
|
+
static resultSchema = LinkedInConnectionToolResultSchema;
|
|
95
|
+
static shortDescription = 'LinkedIn connection automation with step recording';
|
|
96
|
+
static longDescription = `
|
|
19
97
|
Recordable LinkedIn Connection Tool for automating connection requests.
|
|
20
98
|
Records each step with screenshots and timing information for debugging.
|
|
21
99
|
`;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
100
|
+
static alias = 'linkedin-recordable';
|
|
101
|
+
static type = 'tool';
|
|
102
|
+
/** JS helper to query elements across main document, iframes, and shadow DOM */
|
|
103
|
+
static CROSS_DOM_QUERY = `
|
|
104
|
+
function queryAllDOMs(selector) {
|
|
105
|
+
const results = [...document.querySelectorAll(selector)];
|
|
106
|
+
for (const iframe of document.querySelectorAll('iframe')) {
|
|
107
|
+
try {
|
|
108
|
+
if (iframe.contentDocument) {
|
|
109
|
+
results.push(...iframe.contentDocument.querySelectorAll(selector));
|
|
110
|
+
}
|
|
111
|
+
} catch(e) {}
|
|
112
|
+
}
|
|
113
|
+
const shadowHost = document.querySelector('[data-testid="interop-shadowdom"]');
|
|
114
|
+
if (shadowHost && shadowHost.shadowRoot) {
|
|
115
|
+
results.push(...shadowHost.shadowRoot.querySelectorAll(selector));
|
|
116
|
+
}
|
|
117
|
+
return results;
|
|
40
118
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.contextId = sessionData.contextId;
|
|
48
|
-
this.cookies = sessionData.cookies;
|
|
119
|
+
`;
|
|
120
|
+
sessionId = (__runInitializers(this, _instanceExtraInitializers), null);
|
|
121
|
+
contextId = null;
|
|
122
|
+
cookies = null;
|
|
123
|
+
constructor(params = { operation: 'send_connection', profile_url: '' }, context) {
|
|
124
|
+
super(params, context);
|
|
49
125
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
...proxyConfig,
|
|
58
|
-
}, this.context, 'startsession');
|
|
59
|
-
const result = await browserbase.action();
|
|
60
|
-
if (!result.data.success || !result.data.session_id) {
|
|
61
|
-
throw new Error(result.data.error || 'Failed to start browser session');
|
|
126
|
+
/** Required by RecordableToolBubble - returns the active browser session ID */
|
|
127
|
+
chooseCredential() {
|
|
128
|
+
const { credentials } = this.params;
|
|
129
|
+
if (!credentials || typeof credentials !== 'object') {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return credentials[CredentialType.LINKEDIN_CRED];
|
|
62
133
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.contextId = result.data.context_id;
|
|
134
|
+
parseBrowserSessionData() {
|
|
135
|
+
return parseBrowserSessionData(this.chooseCredential());
|
|
66
136
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
137
|
+
// ==================== RECORDABLE STEPS ====================
|
|
138
|
+
async stepStartBrowserSession() {
|
|
139
|
+
if (this.sessionId)
|
|
140
|
+
return;
|
|
141
|
+
const sessionData = this.parseBrowserSessionData();
|
|
142
|
+
if (sessionData) {
|
|
143
|
+
this.contextId = sessionData.contextId;
|
|
144
|
+
this.cookies = sessionData.cookies;
|
|
145
|
+
}
|
|
146
|
+
const proxyConfig = buildProxyConfig(this.params.proxy);
|
|
147
|
+
const browserbase = new BrowserBaseBubble({
|
|
148
|
+
operation: 'start_session',
|
|
149
|
+
context_id: this.contextId || undefined,
|
|
150
|
+
cookies: this.cookies || undefined,
|
|
151
|
+
credentials: this.params.credentials,
|
|
152
|
+
stealth: { solveCaptchas: true },
|
|
153
|
+
...proxyConfig,
|
|
154
|
+
}, this.context, 'startsession');
|
|
155
|
+
const result = await browserbase.action();
|
|
156
|
+
if (!result.data.success || !result.data.session_id) {
|
|
157
|
+
throw new Error(result.data.error || 'Failed to start browser session');
|
|
158
|
+
}
|
|
159
|
+
this.sessionId = result.data.session_id;
|
|
160
|
+
if (result.data.context_id) {
|
|
161
|
+
this.contextId = result.data.context_id;
|
|
162
|
+
}
|
|
163
|
+
console.log(`[RecordableLinkedIn] Session started: ${this.sessionId}`);
|
|
164
|
+
const ipAddress = await this.detectIPAddress();
|
|
165
|
+
if (ipAddress) {
|
|
166
|
+
console.log(`[RecordableLinkedIn] Browser IP: ${ipAddress}`);
|
|
167
|
+
}
|
|
71
168
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
169
|
+
async stepNavigateToProfile() {
|
|
170
|
+
if (!this.sessionId)
|
|
171
|
+
throw new Error('No active session');
|
|
172
|
+
const browserbase = new BrowserBaseBubble({
|
|
173
|
+
operation: 'navigate',
|
|
174
|
+
session_id: this.sessionId,
|
|
175
|
+
url: this.params.profile_url,
|
|
176
|
+
wait_until: 'domcontentloaded',
|
|
177
|
+
timeout: 30000,
|
|
178
|
+
}, this.context, 'navigate');
|
|
179
|
+
const result = await browserbase.action();
|
|
180
|
+
if (!result.data.success) {
|
|
181
|
+
throw new Error(result.data.error || 'Navigation failed');
|
|
182
|
+
}
|
|
86
183
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const checkScript = `
|
|
184
|
+
async stepWaitForProfilePage() {
|
|
185
|
+
const checkScript = `
|
|
90
186
|
(() => {
|
|
91
|
-
const
|
|
92
|
-
for (const
|
|
93
|
-
const ariaLabel = (
|
|
94
|
-
const text = (
|
|
187
|
+
const elements = document.querySelectorAll('button, a, [role="button"]');
|
|
188
|
+
for (const el of elements) {
|
|
189
|
+
const ariaLabel = (el.getAttribute('aria-label') || '').toLowerCase();
|
|
190
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
95
191
|
if (ariaLabel.includes('connect') || text === 'connect') return true;
|
|
96
|
-
if (ariaLabel
|
|
192
|
+
if (ariaLabel.includes('more actions')) return true;
|
|
97
193
|
if (text === 'message' || ariaLabel.includes('message')) return true;
|
|
98
194
|
if (text === 'follow' || ariaLabel.includes('follow')) return true;
|
|
99
195
|
}
|
|
100
196
|
return false;
|
|
101
197
|
})()
|
|
102
198
|
`;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
199
|
+
for (let attempt = 1; attempt <= 30; attempt++) {
|
|
200
|
+
const found = await this.evaluate(checkScript);
|
|
201
|
+
if (found)
|
|
202
|
+
return true;
|
|
203
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
108
206
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
async stepExtractProfileInfo() {
|
|
112
|
-
const info = (await this.evaluate(`
|
|
207
|
+
async stepExtractProfileInfo() {
|
|
208
|
+
const info = (await this.evaluate(`
|
|
113
209
|
(() => {
|
|
114
210
|
let name = '';
|
|
115
211
|
const h1El = document.querySelector('h1');
|
|
@@ -134,54 +230,64 @@ export class LinkedInConnectionTool extends ToolBubble {
|
|
|
134
230
|
return { name, headline, location, profile_url: window.location.href };
|
|
135
231
|
})()
|
|
136
232
|
`));
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
233
|
+
return info.name ? info : null;
|
|
234
|
+
}
|
|
235
|
+
async stepClickConnect() {
|
|
236
|
+
// Primary: find <a> with custom-invite href (new LinkedIn design)
|
|
237
|
+
const directResult = (await this.evaluate(`
|
|
141
238
|
(() => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
239
|
+
// New LinkedIn design: Connect is an <a> with /custom-invite/ href
|
|
240
|
+
const connectLink = document.querySelector('a[href*="/custom-invite/"]');
|
|
241
|
+
if (connectLink) {
|
|
242
|
+
connectLink.click();
|
|
243
|
+
return { clicked: true, element: 'A - ' + (connectLink.getAttribute('aria-label') || 'custom-invite') };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Fallback: search all clickable elements by aria-label or text
|
|
247
|
+
const elements = document.querySelectorAll('button, a, [role="button"]');
|
|
248
|
+
for (const el of elements) {
|
|
249
|
+
const ariaLabel = (el.getAttribute('aria-label') || '').toLowerCase();
|
|
250
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
251
|
+
if ((ariaLabel.includes('invite') && ariaLabel.includes('connect')) ||
|
|
252
|
+
text === 'connect') {
|
|
253
|
+
const rect = el.getBoundingClientRect();
|
|
254
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
255
|
+
el.click();
|
|
256
|
+
return { clicked: true, element: el.tagName + ' - ' + (ariaLabel || text) };
|
|
153
257
|
}
|
|
154
258
|
}
|
|
155
259
|
}
|
|
156
260
|
return { clicked: false };
|
|
157
261
|
})()
|
|
158
262
|
`));
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
263
|
+
if (directResult.clicked)
|
|
264
|
+
return true;
|
|
265
|
+
// More dropdown fallback
|
|
266
|
+
const moreResult = (await this.evaluate(`
|
|
162
267
|
(() => {
|
|
163
|
-
const
|
|
164
|
-
for (const
|
|
165
|
-
const ariaLabel = (
|
|
166
|
-
const text = (
|
|
167
|
-
if ((
|
|
168
|
-
|
|
169
|
-
btn.click();
|
|
268
|
+
const elements = document.querySelectorAll('button, a, [role="button"]');
|
|
269
|
+
for (const el of elements) {
|
|
270
|
+
const ariaLabel = (el.getAttribute('aria-label') || '').toLowerCase();
|
|
271
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
272
|
+
if (ariaLabel.includes('more actions') || text === 'more') {
|
|
273
|
+
el.click();
|
|
170
274
|
return { clicked: true, element: ariaLabel || text };
|
|
171
275
|
}
|
|
172
276
|
}
|
|
173
277
|
return { clicked: false };
|
|
174
278
|
})()
|
|
175
279
|
`));
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
280
|
+
if (moreResult.clicked) {
|
|
281
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
282
|
+
const dropdownResult = (await this.evaluate(`
|
|
179
283
|
(() => {
|
|
180
|
-
|
|
284
|
+
// Search dropdown items, menu items, and list items for Connect
|
|
285
|
+
const items = document.querySelectorAll('[role="button"], [role="menuitem"], [role="option"], li a, li button');
|
|
181
286
|
for (const item of items) {
|
|
182
287
|
const ariaLabel = (item.getAttribute('aria-label') || '').toLowerCase();
|
|
183
288
|
const text = (item.innerText || item.textContent || '').trim().toLowerCase();
|
|
184
|
-
if (ariaLabel.includes('
|
|
289
|
+
if ((ariaLabel.includes('invite') && ariaLabel.includes('connect')) ||
|
|
290
|
+
ariaLabel.includes('connect') || text === 'connect') {
|
|
185
291
|
item.click();
|
|
186
292
|
return { clicked: true, element: ariaLabel || text };
|
|
187
293
|
}
|
|
@@ -189,49 +295,56 @@ export class LinkedInConnectionTool extends ToolBubble {
|
|
|
189
295
|
return { clicked: false, itemCount: items.length };
|
|
190
296
|
})()
|
|
191
297
|
`));
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
298
|
+
if (dropdownResult.clicked)
|
|
299
|
+
return true;
|
|
300
|
+
throw new Error(`Could not find Connect option in More dropdown (found ${dropdownResult.itemCount} items)`);
|
|
301
|
+
}
|
|
302
|
+
throw new Error('Could not find Connect button or More dropdown');
|
|
195
303
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
async stepWaitForModal() {
|
|
199
|
-
const checkScript = `
|
|
304
|
+
async stepWaitForModal() {
|
|
305
|
+
const checkScript = `
|
|
200
306
|
(() => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
307
|
+
${LinkedInConnectionTool.CROSS_DOM_QUERY}
|
|
308
|
+
const elements = queryAllDOMs('button, a, [role="button"]');
|
|
309
|
+
for (const el of elements) {
|
|
310
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
204
311
|
if (text.includes('add a note') || text.includes('send without')) return true;
|
|
205
312
|
}
|
|
206
313
|
return false;
|
|
207
314
|
})()
|
|
208
315
|
`;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
316
|
+
for (let attempt = 1; attempt <= 8; attempt++) {
|
|
317
|
+
const found = await this.evaluate(checkScript);
|
|
318
|
+
if (found)
|
|
319
|
+
return true;
|
|
320
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
321
|
+
}
|
|
322
|
+
throw new Error('Connection modal did not appear within 8 seconds');
|
|
214
323
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
async stepAddNote(message) {
|
|
218
|
-
await this.evaluate(`
|
|
324
|
+
async stepAddNote(message) {
|
|
325
|
+
await this.evaluate(`
|
|
219
326
|
(() => {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
327
|
+
${LinkedInConnectionTool.CROSS_DOM_QUERY}
|
|
328
|
+
const elements = queryAllDOMs('button, a, [role="button"]');
|
|
329
|
+
for (const el of elements) {
|
|
330
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
223
331
|
if (text.includes('add a note')) {
|
|
224
|
-
|
|
332
|
+
el.click();
|
|
225
333
|
return true;
|
|
226
334
|
}
|
|
227
335
|
}
|
|
228
336
|
return false;
|
|
229
337
|
})()
|
|
230
338
|
`);
|
|
231
|
-
|
|
232
|
-
|
|
339
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
340
|
+
await this.evaluate(`
|
|
233
341
|
(() => {
|
|
234
|
-
|
|
342
|
+
${LinkedInConnectionTool.CROSS_DOM_QUERY}
|
|
343
|
+
const textareas = queryAllDOMs('textarea');
|
|
344
|
+
const textarea = textareas.find(t => {
|
|
345
|
+
const rect = t.getBoundingClientRect();
|
|
346
|
+
return rect.width > 0 && rect.height > 0;
|
|
347
|
+
}) || textareas[0];
|
|
235
348
|
if (textarea) {
|
|
236
349
|
textarea.value = ${JSON.stringify(message)};
|
|
237
350
|
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
@@ -240,75 +353,77 @@ export class LinkedInConnectionTool extends ToolBubble {
|
|
|
240
353
|
return false;
|
|
241
354
|
})()
|
|
242
355
|
`);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
356
|
+
}
|
|
357
|
+
async stepSendRequest(withNote) {
|
|
358
|
+
if (withNote) {
|
|
359
|
+
const result = (await this.evaluate(`
|
|
247
360
|
(() => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
361
|
+
${LinkedInConnectionTool.CROSS_DOM_QUERY}
|
|
362
|
+
const elements = queryAllDOMs('button, a, [role="button"]');
|
|
363
|
+
for (const el of elements) {
|
|
364
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
365
|
+
if (text === 'send') {
|
|
366
|
+
el.click();
|
|
253
367
|
return { clicked: true };
|
|
254
368
|
}
|
|
255
369
|
}
|
|
256
370
|
return { clicked: false };
|
|
257
371
|
})()
|
|
258
372
|
`));
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
373
|
+
if (!result.clicked)
|
|
374
|
+
throw new Error('Could not find Send button in modal');
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const result = (await this.evaluate(`
|
|
265
379
|
(() => {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
380
|
+
${LinkedInConnectionTool.CROSS_DOM_QUERY}
|
|
381
|
+
const elements = queryAllDOMs('button, a, [role="button"]');
|
|
382
|
+
for (const el of elements) {
|
|
383
|
+
const text = (el.innerText || el.textContent || '').trim().toLowerCase();
|
|
269
384
|
if (text.includes('send without')) {
|
|
270
|
-
|
|
385
|
+
el.click();
|
|
271
386
|
return { clicked: true };
|
|
272
387
|
}
|
|
273
388
|
}
|
|
274
389
|
return { clicked: false };
|
|
275
390
|
})()
|
|
276
391
|
`));
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
392
|
+
if (!result.clicked)
|
|
393
|
+
throw new Error('Could not find "Send without a note" button in modal');
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
280
396
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
this.sessionId = null;
|
|
292
|
-
}
|
|
293
|
-
async evaluate(script) {
|
|
294
|
-
if (!this.sessionId)
|
|
295
|
-
throw new Error('No active session');
|
|
296
|
-
const browserbase = new BrowserBaseBubble({
|
|
297
|
-
operation: 'evaluate',
|
|
298
|
-
session_id: this.sessionId,
|
|
299
|
-
script,
|
|
300
|
-
}, this.context, 'evaluate');
|
|
301
|
-
const result = await browserbase.action();
|
|
302
|
-
if (!result.data.success) {
|
|
303
|
-
throw new Error(result.data.error || 'Evaluation failed');
|
|
397
|
+
async stepEndBrowserSession() {
|
|
398
|
+
if (!this.sessionId)
|
|
399
|
+
return;
|
|
400
|
+
const browserbase = new BrowserBaseBubble({
|
|
401
|
+
operation: 'end_session',
|
|
402
|
+
session_id: this.sessionId,
|
|
403
|
+
}, this.context, 'endsession');
|
|
404
|
+
await browserbase.action();
|
|
405
|
+
console.log(`[RecordableLinkedIn] Session ended: ${this.sessionId}`);
|
|
406
|
+
this.sessionId = null;
|
|
304
407
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
408
|
+
async evaluate(script) {
|
|
409
|
+
if (!this.sessionId)
|
|
410
|
+
throw new Error('No active session');
|
|
411
|
+
const browserbase = new BrowserBaseBubble({
|
|
412
|
+
operation: 'evaluate',
|
|
413
|
+
session_id: this.sessionId,
|
|
414
|
+
script,
|
|
415
|
+
}, this.context, 'evaluate');
|
|
416
|
+
const result = await browserbase.action();
|
|
417
|
+
if (!result.data.success) {
|
|
418
|
+
throw new Error(result.data.error || 'Evaluation failed');
|
|
419
|
+
}
|
|
420
|
+
return result.data.result;
|
|
421
|
+
}
|
|
422
|
+
async detectIPAddress() {
|
|
423
|
+
if (!this.sessionId)
|
|
424
|
+
return null;
|
|
425
|
+
try {
|
|
426
|
+
const result = await this.evaluate(`
|
|
312
427
|
(async () => {
|
|
313
428
|
try {
|
|
314
429
|
const response = await fetch('https://api.ipify.org?format=json');
|
|
@@ -319,46 +434,48 @@ export class LinkedInConnectionTool extends ToolBubble {
|
|
|
319
434
|
}
|
|
320
435
|
})()
|
|
321
436
|
`);
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
catch {
|
|
325
|
-
return null;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
async performAction() {
|
|
329
|
-
try {
|
|
330
|
-
await this.stepStartBrowserSession();
|
|
331
|
-
await this.stepNavigateToProfile();
|
|
332
|
-
const pageReady = await this.stepWaitForProfilePage();
|
|
333
|
-
if (!pageReady) {
|
|
334
|
-
console.log('[RecordableLinkedIn] Profile page slow to load, continuing anyway');
|
|
437
|
+
return result;
|
|
335
438
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
await this.stepWaitForModal();
|
|
339
|
-
const { message } = this.params;
|
|
340
|
-
if (message) {
|
|
341
|
-
await this.stepAddNote(message);
|
|
439
|
+
catch {
|
|
440
|
+
return null;
|
|
342
441
|
}
|
|
343
|
-
await this.stepSendRequest(!!message);
|
|
344
|
-
return {
|
|
345
|
-
operation: 'send_connection',
|
|
346
|
-
success: true,
|
|
347
|
-
message: `Connection request sent to ${profileInfo?.name || 'profile'}`,
|
|
348
|
-
profile: profileInfo || undefined,
|
|
349
|
-
error: '',
|
|
350
|
-
};
|
|
351
442
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
443
|
+
async performAction() {
|
|
444
|
+
try {
|
|
445
|
+
await this.stepStartBrowserSession();
|
|
446
|
+
await this.stepNavigateToProfile();
|
|
447
|
+
const pageReady = await this.stepWaitForProfilePage();
|
|
448
|
+
if (!pageReady) {
|
|
449
|
+
console.log('[RecordableLinkedIn] Profile page slow to load, continuing anyway');
|
|
450
|
+
}
|
|
451
|
+
const profileInfo = await this.stepExtractProfileInfo();
|
|
452
|
+
await this.stepClickConnect();
|
|
453
|
+
await this.stepWaitForModal();
|
|
454
|
+
const { message } = this.params;
|
|
455
|
+
if (message) {
|
|
456
|
+
await this.stepAddNote(message);
|
|
457
|
+
}
|
|
458
|
+
await this.stepSendRequest(!!message);
|
|
459
|
+
return {
|
|
460
|
+
operation: 'send_connection',
|
|
461
|
+
success: true,
|
|
462
|
+
message: `Connection request sent to ${profileInfo?.name || 'profile'}`,
|
|
463
|
+
profile: profileInfo || undefined,
|
|
464
|
+
error: '',
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
return {
|
|
469
|
+
operation: 'send_connection',
|
|
470
|
+
success: false,
|
|
471
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
await this.stepEndBrowserSession();
|
|
476
|
+
}
|
|
361
477
|
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
478
|
+
};
|
|
479
|
+
})();
|
|
480
|
+
export { LinkedInConnectionTool };
|
|
364
481
|
//# sourceMappingURL=tool.js.map
|