linkedin-automation-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/.env.example +12 -0
  2. package/.github/workflows/ci.yml +66 -0
  3. package/.github/workflows/publish.yml +48 -0
  4. package/.husky/pre-commit +6 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +10 -0
  7. package/AGENTS.md +294 -0
  8. package/CHANGELOG.md +40 -0
  9. package/GIT_RELEASE.md +167 -0
  10. package/LICENSE +21 -0
  11. package/Makefile +30 -0
  12. package/NPM_PUBLISHING.md +230 -0
  13. package/PYEOF +0 -0
  14. package/README.md +295 -0
  15. package/TESTING-GUIDE.md +151 -0
  16. package/cmd/linkedin/main.go +9 -0
  17. package/dist/agent/action-executor.d.ts +81 -0
  18. package/dist/agent/action-executor.d.ts.map +1 -0
  19. package/dist/agent/action-executor.js +170 -0
  20. package/dist/agent/action-executor.js.map +1 -0
  21. package/dist/agent/action-executor.test.d.ts +2 -0
  22. package/dist/agent/action-executor.test.d.ts.map +1 -0
  23. package/dist/agent/action-executor.test.js +366 -0
  24. package/dist/agent/action-executor.test.js.map +1 -0
  25. package/dist/agent/claude-client.d.ts +74 -0
  26. package/dist/agent/claude-client.d.ts.map +1 -0
  27. package/dist/agent/claude-client.js +314 -0
  28. package/dist/agent/claude-client.js.map +1 -0
  29. package/dist/agent/claude-client.test.d.ts +2 -0
  30. package/dist/agent/claude-client.test.d.ts.map +1 -0
  31. package/dist/agent/claude-client.test.js +590 -0
  32. package/dist/agent/claude-client.test.js.map +1 -0
  33. package/dist/agent/dom-extractor.d.ts +50 -0
  34. package/dist/agent/dom-extractor.d.ts.map +1 -0
  35. package/dist/agent/dom-extractor.js +374 -0
  36. package/dist/agent/dom-extractor.js.map +1 -0
  37. package/dist/agent/dom-extractor.test.d.ts +7 -0
  38. package/dist/agent/dom-extractor.test.d.ts.map +1 -0
  39. package/dist/agent/dom-extractor.test.js +504 -0
  40. package/dist/agent/dom-extractor.test.js.map +1 -0
  41. package/dist/agent/extension-client.d.ts +75 -0
  42. package/dist/agent/extension-client.d.ts.map +1 -0
  43. package/dist/agent/extension-client.js +245 -0
  44. package/dist/agent/extension-client.js.map +1 -0
  45. package/dist/agent/index.d.ts +8 -0
  46. package/dist/agent/index.d.ts.map +1 -0
  47. package/dist/agent/index.js +16 -0
  48. package/dist/agent/index.js.map +1 -0
  49. package/dist/agent/page-agent.d.ts +76 -0
  50. package/dist/agent/page-agent.d.ts.map +1 -0
  51. package/dist/agent/page-agent.js +236 -0
  52. package/dist/agent/page-agent.js.map +1 -0
  53. package/dist/agent/types.d.ts +236 -0
  54. package/dist/agent/types.d.ts.map +1 -0
  55. package/dist/agent/types.js +37 -0
  56. package/dist/agent/types.js.map +1 -0
  57. package/dist/cli/agent-commands.d.ts +3 -0
  58. package/dist/cli/agent-commands.d.ts.map +1 -0
  59. package/dist/cli/agent-commands.js +250 -0
  60. package/dist/cli/agent-commands.js.map +1 -0
  61. package/dist/cli/auth.d.ts +3 -0
  62. package/dist/cli/auth.d.ts.map +1 -0
  63. package/dist/cli/auth.js +288 -0
  64. package/dist/cli/auth.js.map +1 -0
  65. package/dist/cli/company.d.ts +3 -0
  66. package/dist/cli/company.d.ts.map +1 -0
  67. package/dist/cli/company.js +55 -0
  68. package/dist/cli/company.js.map +1 -0
  69. package/dist/cli/connection.d.ts +3 -0
  70. package/dist/cli/connection.d.ts.map +1 -0
  71. package/dist/cli/connection.js +79 -0
  72. package/dist/cli/connection.js.map +1 -0
  73. package/dist/cli/index.d.ts +7 -0
  74. package/dist/cli/index.d.ts.map +1 -0
  75. package/dist/cli/index.js +17 -0
  76. package/dist/cli/index.js.map +1 -0
  77. package/dist/cli/messages.d.ts +3 -0
  78. package/dist/cli/messages.d.ts.map +1 -0
  79. package/dist/cli/messages.js +268 -0
  80. package/dist/cli/messages.js.map +1 -0
  81. package/dist/cli/profile.d.ts +3 -0
  82. package/dist/cli/profile.d.ts.map +1 -0
  83. package/dist/cli/profile.js +81 -0
  84. package/dist/cli/profile.js.map +1 -0
  85. package/dist/cli/profile.test.d.ts +2 -0
  86. package/dist/cli/profile.test.d.ts.map +1 -0
  87. package/dist/cli/profile.test.js +15 -0
  88. package/dist/cli/profile.test.js.map +1 -0
  89. package/dist/cli/reply.d.ts +3 -0
  90. package/dist/cli/reply.d.ts.map +1 -0
  91. package/dist/cli/reply.js +129 -0
  92. package/dist/cli/reply.js.map +1 -0
  93. package/dist/core/audit.d.ts +17 -0
  94. package/dist/core/audit.d.ts.map +1 -0
  95. package/dist/core/audit.js +121 -0
  96. package/dist/core/audit.js.map +1 -0
  97. package/dist/core/audit.test.d.ts +2 -0
  98. package/dist/core/audit.test.d.ts.map +1 -0
  99. package/dist/core/audit.test.js +142 -0
  100. package/dist/core/audit.test.js.map +1 -0
  101. package/dist/core/browser-cookies.d.ts +19 -0
  102. package/dist/core/browser-cookies.d.ts.map +1 -0
  103. package/dist/core/browser-cookies.js +181 -0
  104. package/dist/core/browser-cookies.js.map +1 -0
  105. package/dist/core/browser.d.ts +50 -0
  106. package/dist/core/browser.d.ts.map +1 -0
  107. package/dist/core/browser.js +318 -0
  108. package/dist/core/browser.js.map +1 -0
  109. package/dist/core/config.d.ts +20 -0
  110. package/dist/core/config.d.ts.map +1 -0
  111. package/dist/core/config.js +103 -0
  112. package/dist/core/config.js.map +1 -0
  113. package/dist/core/config.test.d.ts +2 -0
  114. package/dist/core/config.test.d.ts.map +1 -0
  115. package/dist/core/config.test.js +111 -0
  116. package/dist/core/config.test.js.map +1 -0
  117. package/dist/core/storage.d.ts +19 -0
  118. package/dist/core/storage.d.ts.map +1 -0
  119. package/dist/core/storage.js +124 -0
  120. package/dist/core/storage.js.map +1 -0
  121. package/dist/core/storage.test.d.ts +2 -0
  122. package/dist/core/storage.test.d.ts.map +1 -0
  123. package/dist/core/storage.test.js +142 -0
  124. package/dist/core/storage.test.js.map +1 -0
  125. package/dist/index.d.ts +3 -0
  126. package/dist/index.d.ts.map +1 -0
  127. package/dist/index.js +63 -0
  128. package/dist/index.js.map +1 -0
  129. package/dist/linkedin/auth.d.ts +22 -0
  130. package/dist/linkedin/auth.d.ts.map +1 -0
  131. package/dist/linkedin/auth.js +167 -0
  132. package/dist/linkedin/auth.js.map +1 -0
  133. package/dist/linkedin/company-extractor.d.ts +36 -0
  134. package/dist/linkedin/company-extractor.d.ts.map +1 -0
  135. package/dist/linkedin/company-extractor.js +211 -0
  136. package/dist/linkedin/company-extractor.js.map +1 -0
  137. package/dist/linkedin/company-extractor.test.d.ts +2 -0
  138. package/dist/linkedin/company-extractor.test.d.ts.map +1 -0
  139. package/dist/linkedin/company-extractor.test.js +52 -0
  140. package/dist/linkedin/company-extractor.test.js.map +1 -0
  141. package/dist/linkedin/connector.d.ts +45 -0
  142. package/dist/linkedin/connector.d.ts.map +1 -0
  143. package/dist/linkedin/connector.js +245 -0
  144. package/dist/linkedin/connector.js.map +1 -0
  145. package/dist/linkedin/message-sender.d.ts +32 -0
  146. package/dist/linkedin/message-sender.d.ts.map +1 -0
  147. package/dist/linkedin/message-sender.js +112 -0
  148. package/dist/linkedin/message-sender.js.map +1 -0
  149. package/dist/linkedin/messages.d.ts +78 -0
  150. package/dist/linkedin/messages.d.ts.map +1 -0
  151. package/dist/linkedin/messages.js +745 -0
  152. package/dist/linkedin/messages.js.map +1 -0
  153. package/dist/linkedin/profile.d.ts +37 -0
  154. package/dist/linkedin/profile.d.ts.map +1 -0
  155. package/dist/linkedin/profile.js +268 -0
  156. package/dist/linkedin/profile.js.map +1 -0
  157. package/dist/linkedin/profile.test.d.ts +2 -0
  158. package/dist/linkedin/profile.test.d.ts.map +1 -0
  159. package/dist/linkedin/profile.test.js +68 -0
  160. package/dist/linkedin/profile.test.js.map +1 -0
  161. package/dist/linkedin/reply.d.ts +21 -0
  162. package/dist/linkedin/reply.d.ts.map +1 -0
  163. package/dist/linkedin/reply.js +76 -0
  164. package/dist/linkedin/reply.js.map +1 -0
  165. package/dist/linkedin/selector-engine.d.ts +69 -0
  166. package/dist/linkedin/selector-engine.d.ts.map +1 -0
  167. package/dist/linkedin/selector-engine.js +339 -0
  168. package/dist/linkedin/selector-engine.js.map +1 -0
  169. package/dist/linkedin/selector-engine.test.d.ts +2 -0
  170. package/dist/linkedin/selector-engine.test.d.ts.map +1 -0
  171. package/dist/linkedin/selector-engine.test.js +135 -0
  172. package/dist/linkedin/selector-engine.test.js.map +1 -0
  173. package/dist/linkedin/selectors.d.ts +65 -0
  174. package/dist/linkedin/selectors.d.ts.map +1 -0
  175. package/dist/linkedin/selectors.js +261 -0
  176. package/dist/linkedin/selectors.js.map +1 -0
  177. package/dist/templates/engine.d.ts +37 -0
  178. package/dist/templates/engine.d.ts.map +1 -0
  179. package/dist/templates/engine.js +215 -0
  180. package/dist/templates/engine.js.map +1 -0
  181. package/dist/templates/engine.test.d.ts +2 -0
  182. package/dist/templates/engine.test.d.ts.map +1 -0
  183. package/dist/templates/engine.test.js +212 -0
  184. package/dist/templates/engine.test.js.map +1 -0
  185. package/dist/templates/index.d.ts +2 -0
  186. package/dist/templates/index.d.ts.map +1 -0
  187. package/dist/templates/index.js +7 -0
  188. package/dist/templates/index.js.map +1 -0
  189. package/dist/types/index.d.ts +113 -0
  190. package/dist/types/index.d.ts.map +1 -0
  191. package/dist/types/index.js +3 -0
  192. package/dist/types/index.js.map +1 -0
  193. package/dist/types/index.test.d.ts +2 -0
  194. package/dist/types/index.test.d.ts.map +1 -0
  195. package/dist/types/index.test.js +90 -0
  196. package/dist/types/index.test.js.map +1 -0
  197. package/dist/utils/paths.d.ts +8 -0
  198. package/dist/utils/paths.d.ts.map +1 -0
  199. package/dist/utils/paths.js +68 -0
  200. package/dist/utils/paths.js.map +1 -0
  201. package/dist/utils/rate-limiter.d.ts +22 -0
  202. package/dist/utils/rate-limiter.d.ts.map +1 -0
  203. package/dist/utils/rate-limiter.js +57 -0
  204. package/dist/utils/rate-limiter.js.map +1 -0
  205. package/dist/utils/retry.d.ts +18 -0
  206. package/dist/utils/retry.d.ts.map +1 -0
  207. package/dist/utils/retry.js +49 -0
  208. package/dist/utils/retry.js.map +1 -0
  209. package/docs/connection-command.md +52 -0
  210. package/docs/plans/2025-03-03-linkedin-cli-design.md +280 -0
  211. package/docs/plans/2025-03-03-linkedin-cli-implementation-plan.md +2087 -0
  212. package/docs/plans/2025-03-03-linkedin-cli-implementation.md +2420 -0
  213. package/docs/plans/2026-02-19-linkedin-connection-feature.md +596 -0
  214. package/docs/plans/2026-02-28-messages-send-feature.md +480 -0
  215. package/docs/plans/2026-02-28-messages-show-design.md +243 -0
  216. package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-design.md +394 -0
  217. package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-plan.md +1592 -0
  218. package/docs/superpowers/plans/2026-03-13-linkedin-automation-resilience-migration.md +425 -0
  219. package/docs/superpowers/plans/2026-03-13-playwright-fara-migration.md +1112 -0
  220. package/docs/superpowers/plans/2026-03-14-page-agent-plan.md +1598 -0
  221. package/docs/superpowers/plans/2026-03-15-company-profile-extraction.md +591 -0
  222. package/docs/superpowers/plans/2026-03-15-profile-extraction-plan.md +943 -0
  223. package/docs/superpowers/specs/2026-03-14-company-profile-extraction-design.md +371 -0
  224. package/docs/superpowers/specs/2026-03-14-page-agent-design.md +385 -0
  225. package/docs/superpowers/specs/2026-03-15-profile-extraction-design.md +409 -0
  226. package/eslint.config.mjs +58 -0
  227. package/go.mod +9 -0
  228. package/go.sum +10 -0
  229. package/import-cookies.js +376 -0
  230. package/internal/cmd/actions.go +123 -0
  231. package/internal/cmd/auth.go +108 -0
  232. package/internal/cmd/connect.go +42 -0
  233. package/internal/cmd/message.go +44 -0
  234. package/internal/cmd/people.go +454 -0
  235. package/internal/cmd/profiles.go +121 -0
  236. package/internal/cmd/root.go +89 -0
  237. package/internal/cmd/sequence.go +192 -0
  238. package/internal/config/config.go +187 -0
  239. package/internal/config/config_test.go +121 -0
  240. package/internal/config/profile.go +65 -0
  241. package/internal/linkedin/navigator.go +195 -0
  242. package/internal/linkedin/selectors.go +39 -0
  243. package/internal/linkedin/validator.go +69 -0
  244. package/internal/pinchtab/client.go +183 -0
  245. package/internal/pinchtab/client_test.go +67 -0
  246. package/internal/pinchtab/types.go +50 -0
  247. package/internal/ratelimit/limiter.go +115 -0
  248. package/internal/ratelimit/limits.go +32 -0
  249. package/package.json +67 -0
  250. package/release.sh +66 -0
  251. package/scripts/debug-linkedin.js +156 -0
  252. package/scripts/debug-login.js +193 -0
  253. package/scripts/extract-from-edge.js +96 -0
  254. package/scripts/import-cookies.js +101 -0
  255. package/scripts/poc-show-data.js +205 -0
  256. package/scripts/proof-of-access.js +87 -0
  257. package/scripts/prove-connection.js +110 -0
  258. package/scripts/show-linkedin-data.js +173 -0
  259. package/src/agent/action-executor.test.ts +464 -0
  260. package/src/agent/action-executor.ts +203 -0
  261. package/src/agent/claude-client.test.ts +707 -0
  262. package/src/agent/claude-client.ts +422 -0
  263. package/src/agent/dom-extractor.test.ts +574 -0
  264. package/src/agent/dom-extractor.ts +437 -0
  265. package/src/agent/extension-client.ts +306 -0
  266. package/src/agent/index.ts +28 -0
  267. package/src/agent/page-agent.ts +292 -0
  268. package/src/agent/types.ts +288 -0
  269. package/src/cli/agent-commands.ts +274 -0
  270. package/src/cli/auth.ts +343 -0
  271. package/src/cli/company.ts +66 -0
  272. package/src/cli/connection.ts +89 -0
  273. package/src/cli/index.ts +7 -0
  274. package/src/cli/messages.ts +338 -0
  275. package/src/cli/profile.test.ts +14 -0
  276. package/src/cli/profile.ts +95 -0
  277. package/src/cli/reply.ts +110 -0
  278. package/src/core/audit.test.ts +134 -0
  279. package/src/core/audit.ts +98 -0
  280. package/src/core/browser-cookies.ts +203 -0
  281. package/src/core/browser.ts +304 -0
  282. package/src/core/config.test.ts +90 -0
  283. package/src/core/config.ts +81 -0
  284. package/src/core/storage.test.ts +129 -0
  285. package/src/core/storage.ts +100 -0
  286. package/src/index.ts +70 -0
  287. package/src/linkedin/auth.ts +218 -0
  288. package/src/linkedin/company-extractor.test.ts +58 -0
  289. package/src/linkedin/company-extractor.ts +222 -0
  290. package/src/linkedin/connector.ts +336 -0
  291. package/src/linkedin/message-sender.ts +141 -0
  292. package/src/linkedin/messages.ts +894 -0
  293. package/src/linkedin/profile.test.ts +79 -0
  294. package/src/linkedin/profile.ts +314 -0
  295. package/src/linkedin/reply.ts +96 -0
  296. package/src/linkedin/selector-engine.test.ts +167 -0
  297. package/src/linkedin/selector-engine.ts +393 -0
  298. package/src/linkedin/selectors.ts +268 -0
  299. package/src/templates/defaults/followup.txt +14 -0
  300. package/src/templates/defaults/meeting.txt +16 -0
  301. package/src/templates/defaults/welcome.txt +14 -0
  302. package/src/templates/engine.test.ts +228 -0
  303. package/src/templates/engine.ts +208 -0
  304. package/src/templates/index.ts +1 -0
  305. package/src/types/index.test.ts +94 -0
  306. package/src/types/index.ts +143 -0
  307. package/src/types/sql.js.d.ts +23 -0
  308. package/src/utils/paths.ts +33 -0
  309. package/src/utils/rate-limiter.ts +75 -0
  310. package/src/utils/retry.ts +78 -0
  311. package/test-cli.sh +85 -0
  312. package/test-real-data.sh +97 -0
  313. package/tsconfig.json +23 -0
  314. package/vitest.config.ts +35 -0
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * Company Profile Extractor
4
+ *
5
+ * Extracts structured data from LinkedIn company profile pages.
6
+ * No authentication required - company pages are public.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CompanyExtractor = void 0;
10
+ exports.isValidCompanyUrl = isValidCompanyUrl;
11
+ exports.parseFollowerCount = parseFollowerCount;
12
+ exports.parseSpecialties = parseSpecialties;
13
+ const selectors_1 = require("./selectors");
14
+ /** URL pattern for valid LinkedIn company pages */
15
+ const COMPANY_URL_PATTERN = /^https:\/\/www\.linkedin\.com\/company\/[^\/]+\/?$/;
16
+ /**
17
+ * Validate if a URL is a valid LinkedIn company URL
18
+ */
19
+ function isValidCompanyUrl(url) {
20
+ return COMPANY_URL_PATTERN.test(url);
21
+ }
22
+ /**
23
+ * Parse follower count string to number
24
+ * Handles formats like "2.5M followers", "12K", "1,234"
25
+ */
26
+ function parseFollowerCount(text) {
27
+ if (!text)
28
+ return null;
29
+ // Extract numeric part with optional K/M suffix
30
+ const match = text.match(/([\d,.]+)\s*([KMkm]?)/);
31
+ if (!match)
32
+ return null;
33
+ let num = parseFloat(match[1].replace(/,/g, ''));
34
+ const suffix = match[2].toUpperCase();
35
+ if (suffix === 'K')
36
+ num *= 1000;
37
+ else if (suffix === 'M')
38
+ num *= 1000000;
39
+ return Math.round(num);
40
+ }
41
+ /**
42
+ * Parse specialties string to array
43
+ * Splits by comma and trims whitespace
44
+ */
45
+ function parseSpecialties(text) {
46
+ if (!text)
47
+ return null;
48
+ const specialties = text
49
+ .split(',')
50
+ .map((s) => s.trim())
51
+ .filter((s) => s.length > 0);
52
+ return specialties.length > 0 ? specialties : null;
53
+ }
54
+ /**
55
+ * Extract definition list data by matching dt text to dd value
56
+ */
57
+ async function extractDefinitionList(page) {
58
+ const result = new Map();
59
+ try {
60
+ const dts = await page.locator('dt').all();
61
+ const dds = await page.locator('dd').all();
62
+ for (let i = 0; i < Math.min(dts.length, dds.length); i++) {
63
+ const dtText = (await dts[i].textContent())?.trim() || '';
64
+ const ddText = (await dds[i].textContent())?.trim() || '';
65
+ if (dtText && ddText) {
66
+ result.set(dtText.toLowerCase(), ddText);
67
+ }
68
+ }
69
+ }
70
+ catch {
71
+ // Ignore errors
72
+ }
73
+ return result;
74
+ }
75
+ /**
76
+ * Extract href from a link element using selector fallbacks
77
+ */
78
+ async function extractHref(page, selectors) {
79
+ for (const selector of selectors) {
80
+ try {
81
+ const element = page.locator(selector).first();
82
+ const href = await element.getAttribute('href', { timeout: 2000 });
83
+ if (href?.trim()) {
84
+ return href.trim();
85
+ }
86
+ }
87
+ catch {
88
+ // Try next selector
89
+ }
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Dismiss authwall popup if present
95
+ */
96
+ async function dismissAuthwall(page) {
97
+ const selectors = selectors_1.SELECTORS.company.dismissAuthwall;
98
+ for (const selector of selectors) {
99
+ try {
100
+ const btn = page.locator(selector).first();
101
+ if (await btn.isVisible({ timeout: 1000 })) {
102
+ await btn.click();
103
+ await page.waitForTimeout(500);
104
+ return;
105
+ }
106
+ }
107
+ catch {
108
+ // Try next selector
109
+ }
110
+ }
111
+ }
112
+ /**
113
+ * Company Profile Extractor
114
+ *
115
+ * Extracts structured data from LinkedIn company profile pages.
116
+ */
117
+ class CompanyExtractor {
118
+ page;
119
+ constructor(page) {
120
+ this.page = page;
121
+ }
122
+ /**
123
+ * Extract company profile data from a LinkedIn company URL
124
+ */
125
+ async extract(url) {
126
+ // Navigate to company page with forced reload
127
+ await this.page.goto(url, {
128
+ waitUntil: 'domcontentloaded',
129
+ timeout: 30000,
130
+ });
131
+ // Force reload to ensure fresh content (helps with CDP connections)
132
+ await this.page.reload({ waitUntil: 'domcontentloaded', timeout: 30000 });
133
+ // Wait for page to load
134
+ await this.page.waitForTimeout(5000);
135
+ // Dismiss authwall popup if present
136
+ await dismissAuthwall(this.page);
137
+ // Wait additional time for dynamic content
138
+ await this.page.waitForTimeout(2000);
139
+ // Wait for about section to load (dt/dd elements)
140
+ try {
141
+ await this.page.waitForSelector('dt', { timeout: 10000 });
142
+ }
143
+ catch {
144
+ // Continue anyway
145
+ }
146
+ // Extract definition list data (dt/dd pairs)
147
+ const definitions = await extractDefinitionList(this.page);
148
+ // Extract name from top card (clean up whitespace)
149
+ const rawName = await this.page.locator('h1').first().textContent({ timeout: 5000 });
150
+ const name = rawName?.trim().replace(/\s+/g, ' ') || '';
151
+ // Extract website from definition list (first link in dd)
152
+ let website = null;
153
+ try {
154
+ const websiteLink = this.page.locator('dt:has-text("Website") + dd a').first();
155
+ website = await websiteLink.getAttribute('href', { timeout: 2000 });
156
+ }
157
+ catch {
158
+ // Try alternative selector
159
+ website = await extractHref(this.page, selectors_1.SELECTORS.company.website);
160
+ }
161
+ // Extract from definition list
162
+ const industry = definitions.get('industry') || null;
163
+ const company_size = definitions.get('company size') || definitions.get('employees') || null;
164
+ const headquarters = definitions.get('headquarters') || null;
165
+ const founded = definitions.get('founded') || null;
166
+ const specialtiesRaw = definitions.get('specialties') || null;
167
+ const type = definitions.get('company type') || definitions.get('type') || null;
168
+ // Extract follower count from top card
169
+ let followerRaw = null;
170
+ try {
171
+ // Look for text containing "followers"
172
+ const followerText = await this.page
173
+ .locator('.org-top-card-summary__followers, span.org-top-card-summary__followers')
174
+ .first()
175
+ .textContent({ timeout: 3000 });
176
+ followerRaw = followerText;
177
+ }
178
+ catch {
179
+ // Fallback: look for any span with "followers" text
180
+ try {
181
+ const spans = await this.page.locator('span').all();
182
+ for (const span of spans) {
183
+ const text = await span.textContent();
184
+ if (text && text.toLowerCase().includes('followers')) {
185
+ followerRaw = text;
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ catch {
191
+ // Ignore
192
+ }
193
+ }
194
+ // Build profile object
195
+ const profile = {
196
+ name: name || '',
197
+ linkedin_url: url,
198
+ website,
199
+ industry,
200
+ company_size,
201
+ headquarters,
202
+ founded,
203
+ specialties: parseSpecialties(specialtiesRaw),
204
+ type,
205
+ follower_count: parseFollowerCount(followerRaw),
206
+ };
207
+ return profile;
208
+ }
209
+ }
210
+ exports.CompanyExtractor = CompanyExtractor;
211
+ //# sourceMappingURL=company-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company-extractor.js","sourceRoot":"","sources":["../../src/linkedin/company-extractor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=company-extractor.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company-extractor.test.d.ts","sourceRoot":"","sources":["../../src/linkedin/company-extractor.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const company_extractor_1 = require("./company-extractor");
5
+ (0, vitest_1.describe)('isValidCompanyUrl', () => {
6
+ (0, vitest_1.it)('should accept valid HTTPS company URLs', () => {
7
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('https://www.linkedin.com/company/openai/')).toBe(true);
8
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('https://www.linkedin.com/company/microsoft')).toBe(true);
9
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('https://www.linkedin.com/company/123company/')).toBe(true);
10
+ });
11
+ (0, vitest_1.it)('should reject invalid URLs', () => {
12
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('http://www.linkedin.com/company/openai/')).toBe(false);
13
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('https://linkedin.com/company/openai/')).toBe(false);
14
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('https://www.linkedin.com/in/openai/')).toBe(false);
15
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('not-a-url')).toBe(false);
16
+ (0, vitest_1.expect)((0, company_extractor_1.isValidCompanyUrl)('')).toBe(false);
17
+ });
18
+ });
19
+ (0, vitest_1.describe)('parseFollowerCount', () => {
20
+ (0, vitest_1.it)('should parse K suffix', () => {
21
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('12K followers')).toBe(12000);
22
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('5k')).toBe(5000);
23
+ });
24
+ (0, vitest_1.it)('should parse M suffix', () => {
25
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('2.5M followers')).toBe(2500000);
26
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('1.2m')).toBe(1200000);
27
+ });
28
+ (0, vitest_1.it)('should parse plain numbers with commas', () => {
29
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('1,234 followers')).toBe(1234);
30
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('500+ followers')).toBe(500);
31
+ });
32
+ (0, vitest_1.it)('should return null for invalid input', () => {
33
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)(null)).toBe(null);
34
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('')).toBe(null);
35
+ (0, vitest_1.expect)((0, company_extractor_1.parseFollowerCount)('no numbers')).toBe(null);
36
+ });
37
+ });
38
+ (0, vitest_1.describe)('parseSpecialties', () => {
39
+ (0, vitest_1.it)('should split by comma and trim', () => {
40
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)('AI, ML, Research')).toEqual(['AI', 'ML', 'Research']);
41
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)('Artificial Intelligence')).toEqual(['Artificial Intelligence']);
42
+ });
43
+ (0, vitest_1.it)('should handle whitespace', () => {
44
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)(' AI , ML ')).toEqual(['AI', 'ML']);
45
+ });
46
+ (0, vitest_1.it)('should return null for empty input', () => {
47
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)(null)).toBe(null);
48
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)('')).toBe(null);
49
+ (0, vitest_1.expect)((0, company_extractor_1.parseSpecialties)(' ')).toBe(null);
50
+ });
51
+ });
52
+ //# sourceMappingURL=company-extractor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company-extractor.test.js","sourceRoot":"","sources":["../../src/linkedin/company-extractor.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { BrowserController } from '../core/browser';
2
+ export interface ConnectionResult {
3
+ success: boolean;
4
+ error?: string;
5
+ sent?: boolean;
6
+ pending?: boolean;
7
+ }
8
+ export interface ConnectionOptions {
9
+ profileUrl: string;
10
+ note?: string;
11
+ skipNote?: boolean;
12
+ }
13
+ /**
14
+ * LinkedInConnector handles sending connection requests to LinkedIn profiles.
15
+ * It supports both direct Connect buttons and Connect options within the More menu.
16
+ */
17
+ export declare class LinkedInConnector {
18
+ private browser;
19
+ constructor(browser: BrowserController);
20
+ /**
21
+ * Main method to send a connection request to a LinkedIn profile.
22
+ * Handles navigation, finding the connect button, adding notes, and sending.
23
+ */
24
+ connect(options: ConnectionOptions): Promise<ConnectionResult>;
25
+ /**
26
+ * Finds the Connect button on a profile page.
27
+ * Tries direct Connect button first, then looks in the More actions menu.
28
+ * Returns the element and which selector was used, or null if not found.
29
+ */
30
+ findConnectButton(): Promise<{
31
+ element: import('playwright').ElementHandle;
32
+ selectorUsed: string;
33
+ } | null>;
34
+ /**
35
+ * Checks the current connection status with a profile.
36
+ * Returns: 'connected', 'pending', 'none', or 'unknown'
37
+ */
38
+ checkConnectionStatus(): Promise<'connected' | 'pending' | 'none' | 'unknown'>;
39
+ /**
40
+ * Helper method to try multiple selectors and return the first matching element.
41
+ * Logs which selector was used for debugging purposes.
42
+ */
43
+ private findElementWithFallbacks;
44
+ }
45
+ //# sourceMappingURL=connector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../../src/linkedin/connector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAoB;gBAEvB,OAAO,EAAE,iBAAiB;IAItC;;;OAGG;IACG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2IpE;;;;OAIG;IACG,iBAAiB,IAAI,OAAO,CAAC;QACjC,OAAO,EAAE,OAAO,YAAY,EAAE,aAAa,CAAC;QAC5C,YAAY,EAAE,MAAM,CAAC;KACtB,GAAG,IAAI,CAAC;IAqDT;;;OAGG;IACG,qBAAqB,IAAI,OAAO,CAAC,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IAsEpF;;;OAGG;YACW,wBAAwB;CA0BvC"}
@@ -0,0 +1,245 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LinkedInConnector = void 0;
4
+ const selectors_1 = require("./selectors");
5
+ /**
6
+ * LinkedInConnector handles sending connection requests to LinkedIn profiles.
7
+ * It supports both direct Connect buttons and Connect options within the More menu.
8
+ */
9
+ class LinkedInConnector {
10
+ browser;
11
+ constructor(browser) {
12
+ this.browser = browser;
13
+ }
14
+ /**
15
+ * Main method to send a connection request to a LinkedIn profile.
16
+ * Handles navigation, finding the connect button, adding notes, and sending.
17
+ */
18
+ async connect(options) {
19
+ const page = this.browser.getPage();
20
+ if (!page) {
21
+ return { success: false, error: 'Browser not initialized' };
22
+ }
23
+ try {
24
+ // Navigate to profile
25
+ console.log(`Navigating to profile: ${options.profileUrl}`);
26
+ await page.goto(options.profileUrl, { waitUntil: 'domcontentloaded', timeout: 60000 });
27
+ await page.waitForTimeout(5000);
28
+ // Check current connection status
29
+ const status = await this.checkConnectionStatus();
30
+ console.log(`Connection status: ${status}`);
31
+ if (status === 'connected') {
32
+ return { success: true, sent: false, pending: false, error: 'Already connected' };
33
+ }
34
+ if (status === 'pending') {
35
+ return {
36
+ success: true,
37
+ sent: false,
38
+ pending: true,
39
+ error: 'Connection request already pending',
40
+ };
41
+ }
42
+ // Find and click the connect button
43
+ const connectButton = await this.findConnectButton();
44
+ if (!connectButton) {
45
+ return { success: false, error: 'Could not find Connect button' };
46
+ }
47
+ console.log(`Found connect button using selector: ${connectButton.selectorUsed}`);
48
+ // Use force click to avoid interception by other elements
49
+ await connectButton.element.click({ force: true });
50
+ console.log('Clicked Connect button, waiting for modal...');
51
+ await page.waitForTimeout(3000);
52
+ // Handle "Add a note" modal if note is provided
53
+ if (options.note && !options.skipNote) {
54
+ const addNoteResult = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.addNoteButton, 'Add a note button');
55
+ if (addNoteResult) {
56
+ console.log('Clicking "Add a note" button');
57
+ await addNoteResult.click();
58
+ await page.waitForTimeout(500);
59
+ // Find and fill the note textarea
60
+ const noteTextareaResult = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.noteTextarea, 'Note textarea');
61
+ if (noteTextareaResult) {
62
+ console.log('Adding note to connection request');
63
+ await noteTextareaResult.fill(options.note);
64
+ await page.waitForTimeout(500);
65
+ }
66
+ }
67
+ }
68
+ // Click the Send button
69
+ console.log('Looking for Send button in modal...');
70
+ // Wait a moment for the modal to fully render
71
+ await page.waitForTimeout(2000);
72
+ // Try to find Send button with multiple strategies
73
+ let sendButton = null;
74
+ // Strategy 1: Try primary button in modal
75
+ const modalButtons = await page.$$('.artdeco-modal button, [role="dialog"] button');
76
+ console.log(`Found ${modalButtons.length} buttons in modal/dialog`);
77
+ for (const button of modalButtons) {
78
+ const text = await button.textContent();
79
+ const ariaLabel = await button.getAttribute('aria-label');
80
+ const isPrimary = await button.evaluate((el) => el.classList.contains('artdeco-button--primary') || el.getAttribute('type') === 'submit');
81
+ console.log(` Button: text="${text?.trim()}", aria="${ariaLabel}", primary=${isPrimary}`);
82
+ // Look for Send, Connect, or primary action button
83
+ if (text?.match(/send|connect/i) ||
84
+ ariaLabel?.match(/send|connect/i) ||
85
+ (isPrimary && text?.length && text.length < 20)) {
86
+ sendButton = button;
87
+ console.log(` -> Selected as send button`);
88
+ break;
89
+ }
90
+ }
91
+ // Strategy 2: Fallback to original selector-based approach
92
+ if (!sendButton) {
93
+ console.log('Falling back to selector-based approach...');
94
+ const sendButtonResult = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.sendButton, 'Send button');
95
+ if (sendButtonResult) {
96
+ sendButton = sendButtonResult;
97
+ }
98
+ }
99
+ if (!sendButton) {
100
+ return { success: false, error: 'Could not find Send button' };
101
+ }
102
+ console.log('Clicking Send button');
103
+ await sendButton.click();
104
+ await page.waitForTimeout(2000);
105
+ // Verify the request was sent (check for success indicators or absence of error)
106
+ const errorMessage = await page.$('[role="alert"], .artdeco-inline-feedback--error, [data-testid="error-message"]');
107
+ if (errorMessage) {
108
+ const errorText = await errorMessage.textContent();
109
+ return { success: false, error: errorText || 'Unknown error occurred' };
110
+ }
111
+ return { success: true, sent: true, pending: true };
112
+ }
113
+ catch (error) {
114
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
115
+ console.error('Connection request failed:', errorMessage);
116
+ return { success: false, error: errorMessage };
117
+ }
118
+ }
119
+ /**
120
+ * Finds the Connect button on a profile page.
121
+ * Tries direct Connect button first, then looks in the More actions menu.
122
+ * Returns the element and which selector was used, or null if not found.
123
+ */
124
+ async findConnectButton() {
125
+ const page = this.browser.getPage();
126
+ if (!page)
127
+ return null;
128
+ // First, try direct Connect button
129
+ console.log('Looking for direct Connect button...');
130
+ const directConnect = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.connectButton, 'Connect button');
131
+ if (directConnect) {
132
+ console.log('Found direct Connect button');
133
+ return { element: directConnect, selectorUsed: 'direct-connect' };
134
+ }
135
+ // If no direct button, look for "More actions" menu
136
+ console.log('No direct Connect button found, looking for More actions menu...');
137
+ const moreActions = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.moreActionsButton, 'More actions button');
138
+ if (!moreActions) {
139
+ console.log('Could not find More actions button');
140
+ return null;
141
+ }
142
+ // Click More actions to open dropdown
143
+ console.log('Clicking More actions button');
144
+ await moreActions.click();
145
+ await page.waitForTimeout(1000);
146
+ // Look for Connect option in the dropdown
147
+ console.log('Looking for Connect option in dropdown...');
148
+ const connectOption = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.connectOptionInMenu, 'Connect option in menu');
149
+ if (connectOption) {
150
+ console.log('Found Connect option in dropdown');
151
+ return { element: connectOption, selectorUsed: 'more-menu' };
152
+ }
153
+ // Close the dropdown by pressing Escape
154
+ await page.keyboard.press('Escape');
155
+ await page.waitForTimeout(500);
156
+ console.log('Could not find Connect option in dropdown');
157
+ return null;
158
+ }
159
+ /**
160
+ * Checks the current connection status with a profile.
161
+ * Returns: 'connected', 'pending', 'none', or 'unknown'
162
+ */
163
+ async checkConnectionStatus() {
164
+ const page = this.browser.getPage();
165
+ if (!page)
166
+ return 'unknown';
167
+ try {
168
+ // IMPORTANT: Check for Pending and Message FIRST, before looking for Connect buttons
169
+ // This avoids finding "Connect" buttons on other people's profiles in the sidebar
170
+ // when the actual profile status is "Pending" or "Connected"
171
+ // Check for "Pending" button/text (request already sent)
172
+ // LinkedIn shows "Pending" as a button when connection is pending
173
+ // IMPORTANT: Use a more specific selector that only matches in the main profile header
174
+ const pendingButton = await page.$('section.artdeco-card button:has-text("Pending")');
175
+ if (pendingButton) {
176
+ const isVisible = await pendingButton.isVisible().catch(() => false);
177
+ if (isVisible) {
178
+ return 'pending';
179
+ }
180
+ }
181
+ // Check for "Message" button (already connected)
182
+ // Only check in the main profile header, not dropdown menus
183
+ const messageButton = await page.$('section.artdeco-card button:has-text("Message")');
184
+ if (messageButton) {
185
+ const isVisible = await messageButton.isVisible().catch(() => false);
186
+ if (isVisible) {
187
+ return 'connected';
188
+ }
189
+ }
190
+ // Now check if there's a Connect button (meaning NOT connected)
191
+ const connectButton = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.connectButton, 'Connect button for status check');
192
+ if (connectButton) {
193
+ return 'none'; // Can connect - not connected yet
194
+ }
195
+ // Check for "More" menu with Connect inside (also means NOT connected)
196
+ const moreActions = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.moreActionsButton, 'More actions button for status check');
197
+ if (moreActions) {
198
+ // Check if there's a Connect option in the dropdown
199
+ await moreActions.click();
200
+ await page.waitForTimeout(500);
201
+ const connectOption = await this.findElementWithFallbacks(selectors_1.SELECTORS.connection.connectOptionInMenu, 'Connect option in More menu');
202
+ // Close the dropdown
203
+ await page.keyboard.press('Escape');
204
+ await page.waitForTimeout(300);
205
+ if (connectOption) {
206
+ return 'none'; // Can connect via More menu
207
+ }
208
+ }
209
+ return 'unknown';
210
+ }
211
+ catch (error) {
212
+ console.error('Error checking connection status:', error);
213
+ return 'unknown';
214
+ }
215
+ }
216
+ /**
217
+ * Helper method to try multiple selectors and return the first matching element.
218
+ * Logs which selector was used for debugging purposes.
219
+ */
220
+ async findElementWithFallbacks(selectors, elementName) {
221
+ const page = this.browser.getPage();
222
+ if (!page)
223
+ return null;
224
+ for (const selector of selectors) {
225
+ try {
226
+ const element = await page.$(selector);
227
+ if (element) {
228
+ const isVisible = await element.isVisible().catch(() => false);
229
+ if (isVisible) {
230
+ console.log(`Found ${elementName} using selector: ${selector}`);
231
+ return element;
232
+ }
233
+ await element.dispose();
234
+ }
235
+ }
236
+ catch {
237
+ // Continue to next selector
238
+ }
239
+ }
240
+ console.log(`Could not find ${elementName} with any selector`);
241
+ return null;
242
+ }
243
+ }
244
+ exports.LinkedInConnector = LinkedInConnector;
245
+ //# sourceMappingURL=connector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../../src/linkedin/connector.ts"],"names":[],"mappings":";;;AACA,2CAAwC;AAexC;;;GAGG;AACH,MAAa,iBAAiB;IACpB,OAAO,CAAoB;IAEnC,YAAY,OAA0B;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,OAA0B;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;QAC9D,CAAC;QAED,IAAI,CAAC;YACH,sBAAsB;YACtB,OAAO,CAAC,GAAG,CAAC,0BAA0B,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;YAC5D,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YACvF,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAEhC,kCAAkC;YAClC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,EAAE,CAAC,CAAC;YAE5C,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;YACpF,CAAC;YAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,IAAI,EAAE,KAAK;oBACX,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,oCAAoC;iBAC5C,CAAC;YACJ,CAAC;YAED,oCAAoC;YACpC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACrD,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC;YACpE,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,wCAAwC,aAAa,CAAC,YAAY,EAAE,CAAC,CAAC;YAClF,0DAA0D;YAC1D,MAAM,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;YAC5D,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAEhC,gDAAgD;YAChD,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACtC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACvD,qBAAS,CAAC,UAAU,CAAC,aAAa,EAClC,mBAAmB,CACpB,CAAC;gBAEF,IAAI,aAAa,EAAE,CAAC;oBAClB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;oBAC5C,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC;oBAC5B,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;oBAE/B,kCAAkC;oBAClC,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAC5D,qBAAS,CAAC,UAAU,CAAC,YAAY,EACjC,eAAe,CAChB,CAAC;oBAEF,IAAI,kBAAkB,EAAE,CAAC;wBACvB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;wBACjD,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;wBAC5C,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;oBACjC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,wBAAwB;YACxB,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;YAEnD,8CAA8C;YAC9C,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAEhC,mDAAmD;YACnD,IAAI,UAAU,GAAG,IAAI,CAAC;YAEtB,0CAA0C;YAC1C,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,+CAA+C,CAAC,CAAC;YACpF,OAAO,CAAC,GAAG,CAAC,SAAS,YAAY,CAAC,MAAM,0BAA0B,CAAC,CAAC;YAEpE,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;gBACxC,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;gBAC1D,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,QAAQ,CACrC,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,yBAAyB,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,QAAQ,CAC3F,CAAC;gBAEF,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,IAAI,EAAE,YAAY,SAAS,cAAc,SAAS,EAAE,CAAC,CAAC;gBAE3F,mDAAmD;gBACnD,IACE,IAAI,EAAE,KAAK,CAAC,eAAe,CAAC;oBAC5B,SAAS,EAAE,KAAK,CAAC,eAAe,CAAC;oBACjC,CAAC,SAAS,IAAI,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,EAC/C,CAAC;oBACD,UAAU,GAAG,MAAM,CAAC;oBACpB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;oBAC5C,MAAM;gBACR,CAAC;YACH,CAAC;YAED,2DAA2D;YAC3D,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;gBAC1D,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAC1D,qBAAS,CAAC,UAAU,CAAC,UAAU,EAC/B,aAAa,CACd,CAAC;gBACF,IAAI,gBAAgB,EAAE,CAAC;oBACrB,UAAU,GAAG,gBAAgB,CAAC;gBAChC,CAAC;YACH,CAAC;YAED,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC;YACjE,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACpC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAEhC,iFAAiF;YACjF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,CAAC,CAC/B,gFAAgF,CACjF,CAAC;YACF,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,CAAC;gBACnD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,IAAI,wBAAwB,EAAE,CAAC;YAC1E,CAAC;YAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YAC9E,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAC;YAC1D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,iBAAiB;QAIrB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEvB,mCAAmC;QACnC,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACvD,qBAAS,CAAC,UAAU,CAAC,aAAa,EAClC,gBAAgB,CACjB,CAAC;QAEF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;YAC3C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC;QACpE,CAAC;QAED,oDAAoD;QACpD,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;QAChF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACrD,qBAAS,CAAC,UAAU,CAAC,iBAAiB,EACtC,qBAAqB,CACtB,CAAC;QAEF,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAEhC,0CAA0C;QAC1C,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACvD,qBAAS,CAAC,UAAU,CAAC,mBAAmB,EACxC,wBAAwB,CACzB,CAAC;QAEF,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;YAChD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;QAC/D,CAAC;QAED,wCAAwC;QACxC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAE/B,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,qBAAqB;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAE5B,IAAI,CAAC;YACH,qFAAqF;YACrF,kFAAkF;YAClF,6DAA6D;YAE7D,yDAAyD;YACzD,kEAAkE;YAClE,uFAAuF;YACvF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC;YACtF,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;gBACrE,IAAI,SAAS,EAAE,CAAC;oBACd,OAAO,SAAS,CAAC;gBACnB,CAAC;YACH,CAAC;YAED,iDAAiD;YACjD,4DAA4D;YAC5D,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC;YACtF,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;gBACrE,IAAI,SAAS,EAAE,CAAC;oBACd,OAAO,WAAW,CAAC;gBACrB,CAAC;YACH,CAAC;YAED,gEAAgE;YAChE,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACvD,qBAAS,CAAC,UAAU,CAAC,aAAa,EAClC,iCAAiC,CAClC,CAAC;YACF,IAAI,aAAa,EAAE,CAAC;gBAClB,OAAO,MAAM,CAAC,CAAC,kCAAkC;YACnD,CAAC;YAED,uEAAuE;YACvE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACrD,qBAAS,CAAC,UAAU,CAAC,iBAAiB,EACtC,sCAAsC,CACvC,CAAC;YACF,IAAI,WAAW,EAAE,CAAC;gBAChB,oDAAoD;gBACpD,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;gBAC1B,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAE/B,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACvD,qBAAS,CAAC,UAAU,CAAC,mBAAmB,EACxC,6BAA6B,CAC9B,CAAC;gBAEF,qBAAqB;gBACrB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACpC,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;gBAE/B,IAAI,aAAa,EAAE,CAAC;oBAClB,OAAO,MAAM,CAAC,CAAC,4BAA4B;gBAC7C,CAAC;YACH,CAAC;YAED,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,wBAAwB,CACpC,SAA4B,EAC5B,WAAmB;QAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAEvB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBACvC,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;oBAC/D,IAAI,SAAS,EAAE,CAAC;wBACd,OAAO,CAAC,GAAG,CAAC,SAAS,WAAW,oBAAoB,QAAQ,EAAE,CAAC,CAAC;wBAChE,OAAO,OAAO,CAAC;oBACjB,CAAC;oBACD,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,4BAA4B;YAC9B,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,WAAW,oBAAoB,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AA3TD,8CA2TC"}
@@ -0,0 +1,32 @@
1
+ import type { Page } from 'playwright';
2
+ export interface SendMessageOptions {
3
+ profileUrl: string;
4
+ text: string;
5
+ dryRun?: boolean;
6
+ }
7
+ export interface SendMessageResult {
8
+ success: boolean;
9
+ messageId?: string;
10
+ threadId?: string;
11
+ error?: string;
12
+ }
13
+ export declare class LinkedInMessageSender {
14
+ private page;
15
+ private selectorEngine;
16
+ private linkedInDomain;
17
+ constructor(page: Page);
18
+ /**
19
+ * Send a message to a LinkedIn profile
20
+ */
21
+ sendMessage(options: SendMessageOptions): Promise<SendMessageResult>;
22
+ /**
23
+ * Extract profile ID from LinkedIn profile URL
24
+ * e.g., https://www.linkedin.com/in/lily-q-7145971b9/ -> lily-q-7145971b9
25
+ */
26
+ private extractProfileId;
27
+ /**
28
+ * Extract thread ID from current URL
29
+ */
30
+ private extractThreadId;
31
+ }
32
+ //# sourceMappingURL=message-sender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-sender.d.ts","sourceRoot":"","sources":["../../src/linkedin/message-sender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,cAAc,CAAsC;gBAEhD,IAAI,EAAE,IAAI;IAOtB;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4F1E;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;OAEG;IACH,OAAO,CAAC,eAAe;CAKxB"}