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
package/.env.example ADDED
@@ -0,0 +1,12 @@
1
+ # Dashscope Claude API Configuration
2
+ # Get your API key from: https://dashscope.aliyun.com/
3
+ DASHSCOPE_API_KEY=your_key_here
4
+ DASHSCOPE_BASE_URL=https://coding.dashscope.aliyuncs.com/apps/anthropic/v1
5
+ DASHSCOPE_MODEL=qwen3.5-plus
6
+
7
+ # Page Agent Extension Configuration (for fast mode)
8
+ # Get auth token from extension side panel
9
+ PAGE_AGENT_AUTH_TOKEN=your_extension_auth_token
10
+
11
+ # Page Agent CDP Port (default: 9222)
12
+ PAGE_AGENT_CDP_PORT=9222
@@ -0,0 +1,66 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ node-version: [18.x, 20.x, 22.x]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Use Node.js ${{ matrix.node-version }}
22
+ uses: actions/setup-node@v4
23
+ with:
24
+ node-version: ${{ matrix.node-version }}
25
+ cache: 'npm'
26
+
27
+ - name: Install dependencies
28
+ run: npm ci
29
+
30
+ - name: Type check
31
+ run: npm run typecheck
32
+
33
+ - name: Lint
34
+ run: npm run lint
35
+ continue-on-error: true
36
+
37
+ - name: Format check
38
+ run: npm run format:check
39
+
40
+ - name: Run tests with coverage
41
+ run: npm run test:coverage
42
+
43
+ build:
44
+ runs-on: ubuntu-latest
45
+ needs: test
46
+
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+
50
+ - name: Use Node.js 20.x
51
+ uses: actions/setup-node@v4
52
+ with:
53
+ node-version: 20.x
54
+ cache: 'npm'
55
+
56
+ - name: Install dependencies
57
+ run: npm ci
58
+
59
+ - name: Build
60
+ run: npm run build
61
+
62
+ - name: Verify dist output
63
+ run: |
64
+ test -d dist/
65
+ test -f dist/index.js
66
+ echo "Build verified successfully"
@@ -0,0 +1,48 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*' # Trigger on version tags like v1.0.0
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Use Node.js 20.x
16
+ uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 20.x
19
+ cache: 'npm'
20
+ cache-dependency-path: package-lock.json
21
+ registry-url: 'https://registry.npmjs.org'
22
+
23
+ - name: Install dependencies
24
+ run: npm ci
25
+
26
+ - name: Type check
27
+ run: npm run typecheck
28
+
29
+ - name: Lint
30
+ run: npm run lint
31
+
32
+ - name: Run tests with coverage
33
+ run: npm run test:coverage
34
+
35
+ - name: Build
36
+ run: npm run build
37
+
38
+ - name: Publish to npm
39
+ run: npm publish --access public
40
+ env:
41
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
42
+
43
+ - name: Create GitHub Release
44
+ uses: softprops/action-gh-release@v1
45
+ with:
46
+ generate_release_notes: true
47
+ env:
48
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ # Run lint and format checks before commit
5
+ npm run lint
6
+ npm run format:check
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
4
+ *.log
package/.prettierrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 100,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "bracketSpacing": true,
9
+ "arrowParens": "always"
10
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,294 @@
1
+ # AGENTS.md - LinkedIn CLI Development Guide
2
+
3
+ This document provides essential information for AI coding agents working on the LinkedIn CLI codebase.
4
+
5
+ ## Project Overview
6
+
7
+ LinkedIn CLI is a Go-based command-line tool for LinkedIn automation using PinchTab (browser automation HTTP server). It sends connection requests and direct messages with built-in rate limiting for safety.
8
+
9
+ **Tech Stack:** Go 1.21+, Cobra CLI, PinchTab HTTP client
10
+
11
+ ## Build, Test, and Lint Commands
12
+
13
+ ### Build
14
+ ```bash
15
+ make build # Build binary → linkedin-cli
16
+ go build -o linkedin-cli ./cmd/linkedin
17
+ ```
18
+
19
+ ### Test
20
+ ```bash
21
+ make test # Run all tests
22
+ go test -v ./... # Run all tests with verbose output
23
+
24
+ # Run single test
25
+ go test -v -run TestFunctionName ./path/to/package
26
+ go test -v -run TestProfileCRUD ./internal/config
27
+
28
+ # Run tests in specific package
29
+ go test -v ./internal/config
30
+ go test -v ./internal/pinchtab
31
+ ```
32
+
33
+ ### Lint and Format
34
+ ```bash
35
+ make lint # Run golangci-lint
36
+ make fmt # Format code with go fmt
37
+ golangci-lint run # Run linter directly
38
+ go fmt ./... # Format all files
39
+ ```
40
+
41
+ ### Clean
42
+ ```bash
43
+ make clean # Remove binary and clean build cache
44
+ ```
45
+
46
+ ## Project Structure
47
+
48
+ ```
49
+ linkedin-cli/
50
+ ├── cmd/linkedin/main.go # Entry point
51
+ ├── internal/ # Private packages
52
+ │ ├── cmd/ # CLI commands (Cobra)
53
+ │ │ ├── root.go # Base command, flags, helpers
54
+ │ │ ├── auth.go # Authentication flow
55
+ │ │ ├── connect.go # Send connection requests
56
+ │ │ ├── message.go # Send direct messages
57
+ │ │ └── profiles.go # Profile management
58
+ │ ├── config/ # Configuration persistence
59
+ │ │ ├── config.go # Manager for profile/rate limits
60
+ │ │ └── profile.go # Profile and RateLimits types
61
+ │ ├── linkedin/ # LinkedIn page interactions
62
+ │ │ ├── navigator.go # Page interaction logic
63
+ │ │ ├── selectors.go # CSS selectors (fragile!)
64
+ │ │ └── validator.go # URL validation
65
+ │ ├── pinchtab/ # PinchTab HTTP client
66
+ │ │ ├── client.go # API wrapper
67
+ │ │ └── types.go # Instance, Tab, Snapshot types
68
+ │ └── ratelimit/ # Rate limiting logic
69
+ │ ├── limiter.go # Check/Record methods
70
+ │ └── limits.go # Default/Conservative limits
71
+ ├── go.mod
72
+ ├── Makefile
73
+ └── README.md
74
+ ```
75
+
76
+ ## Code Style Guidelines
77
+
78
+ ### Imports
79
+ Organize imports in three groups, separated by blank lines:
80
+ 1. Standard library
81
+ 2. External packages
82
+ 3. Internal packages
83
+
84
+ ```go
85
+ import (
86
+ "fmt"
87
+ "os"
88
+ "time"
89
+
90
+ "github.com/spf13/cobra"
91
+
92
+ "github.com/thaddeus-git/linkedin-cli/internal/config"
93
+ "github.com/thaddeus-git/linkedin-cli/internal/pinchtab"
94
+ )
95
+ ```
96
+
97
+ ### Naming Conventions
98
+ - **Packages:** lowercase, single word if possible (e.g., `config`, `cmd`, `pinchtab`)
99
+ - **Types:** PascalCase for exported, camelCase for private
100
+ - **Functions/Methods:** PascalCase for exported, camelCase for private
101
+ - **Variables:** camelCase, descriptive but concise
102
+ - **Constants:** PascalCase or UPPER_SNAKE_CASE
103
+ - **Interfaces:** typically end with `-er` (e.g., `Navigator`, `Sleeper`)
104
+
105
+ ### Comments
106
+ - Start with the name of the thing being commented
107
+ - Use complete sentences
108
+ - No inline comments explaining obvious code
109
+
110
+ ```go
111
+ // Manager handles configuration and state
112
+ type Manager struct {
113
+ configDir string
114
+ }
115
+
116
+ // NewManager creates a new config manager
117
+ func NewManager() (*Manager, error) {
118
+ // Implementation
119
+ }
120
+ ```
121
+
122
+ ### Error Handling
123
+ - Wrap errors with context using `fmt.Errorf` and `%w`
124
+ - Provide actionable error messages
125
+ - Use `exitWithError()` in CLI commands for user-facing errors
126
+
127
+ ```go
128
+ // Good: wrap with context
129
+ if err := client.Navigate(url); err != nil {
130
+ return fmt.Errorf("failed to navigate: %w", err)
131
+ }
132
+
133
+ // Good: user-facing error in CLI
134
+ if profileName == "" {
135
+ exitWithError("Profile name is required (--profile or LINKEDIN_PROFILE)")
136
+ }
137
+
138
+ // Avoid: bare error returns without context
139
+ return err
140
+ ```
141
+
142
+ ### Types and Structures
143
+ - Use structs for data, interfaces for behavior
144
+ - Prefer composition over inheritance
145
+ - Keep structs small and focused
146
+
147
+ ```go
148
+ // Good: focused struct with clear purpose
149
+ type Profile struct {
150
+ Name string `json:"name"`
151
+ CreatedAt time.Time `json:"created_at"`
152
+ LastUsed time.Time `json:"last_used"`
153
+ }
154
+ ```
155
+
156
+ ## Testing Guidelines
157
+
158
+ ### Test Structure
159
+ - Place tests in `*_test.go` files alongside source code
160
+ - Use `t.TempDir()` for test isolation (auto-cleanup)
161
+ - Follow AAA pattern: Arrange, Act, Assert
162
+
163
+ ```go
164
+ func TestProfileCRUD(t *testing.T) {
165
+ // Arrange
166
+ tempDir := t.TempDir()
167
+ mgr := &Manager{configDir: tempDir}
168
+ profile := &Profile{Name: "test"}
169
+
170
+ // Act
171
+ err := mgr.SaveProfile(profile)
172
+
173
+ // Assert
174
+ if err != nil {
175
+ t.Fatalf("failed to save: %v", err)
176
+ }
177
+ if !mgr.ProfileExists("test") {
178
+ t.Error("profile should exist")
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### Table-Driven Tests
184
+ Use for testing multiple scenarios:
185
+
186
+ ```go
187
+ func TestValidateProfileURL(t *testing.T) {
188
+ tests := []struct {
189
+ name string
190
+ input string
191
+ want string
192
+ wantErr bool
193
+ }{
194
+ {"full URL", "https://linkedin.com/in/john", "https://linkedin.com/in/john", false},
195
+ {"username only", "john", "https://linkedin.com/in/john", false},
196
+ }
197
+
198
+ for _, tt := range tests {
199
+ t.Run(tt.name, func(t *testing.T) {
200
+ got, err := ValidateProfileURL(tt.input)
201
+ if (err != nil) != tt.wantErr {
202
+ t.Errorf("ValidateProfileURL() error = %v", err)
203
+ }
204
+ if got != tt.want {
205
+ t.Errorf("ValidateProfileURL() = %v, want %v", got, tt.want)
206
+ }
207
+ })
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## Architecture Patterns
213
+
214
+ ### CLI Commands
215
+ - Use Cobra framework for all commands
216
+ - Define commands in `init()` function
217
+ - Extract logic to separate `run*` functions
218
+ - Use `exitWithError()` for errors, not `log.Fatal()`
219
+
220
+ ```go
221
+ func init() {
222
+ rootCmd.AddCommand(connectCmd)
223
+ connectCmd.Flags().StringVarP(&connectURL, "url", "u", "", "LinkedIn profile URL")
224
+ }
225
+
226
+ var connectCmd = &cobra.Command{
227
+ Use: "connect",
228
+ Short: "Send a connection request",
229
+ Run: runConnect,
230
+ }
231
+
232
+ func runConnect(cmd *cobra.Command, args []string) {
233
+ // Validate, execute, handle errors
234
+ }
235
+ ```
236
+
237
+ ### HTTP Client Pattern
238
+ - Wrap external APIs in dedicated client packages
239
+ - Use struct with methods for API calls
240
+ - Return errors with context
241
+
242
+ ```go
243
+ type Client struct {
244
+ baseURL string
245
+ client *http.Client
246
+ }
247
+
248
+ func (c *Client) Navigate(url string) error {
249
+ return c.post("/navigate", map[string]string{"url": url}, nil)
250
+ }
251
+ ```
252
+
253
+ ### Configuration Management
254
+ - Store in `~/.linkedin-cli/` directory
255
+ - JSON format for persistence
256
+ - Separate files for profiles and rate limits
257
+
258
+ ## Important Notes
259
+
260
+ ### LinkedIn Selectors
261
+ - Selectors in `internal/linkedin/selectors.go` are **fragile**
262
+ - LinkedIn changes their UI frequently
263
+ - These may need updates when LinkedIn updates
264
+
265
+ ### Rate Limiting
266
+ - Default: 20 connections/day, 100/week, 50 messages/day
267
+ - Delays: 3-8 seconds between actions
268
+ - Stored per profile in `~/.linkedin-cli/ratelimit.json`
269
+
270
+ ### PinchTab Integration
271
+ - Requires PinchTab running on `http://localhost:9867`
272
+ - Install: `curl -fsSL https://pinchtab.com/install.sh | bash`
273
+ - No built-in session persistence - user must re-login manually
274
+
275
+ ### No External Dependencies
276
+ - Only use `github.com/spf13/cobra` for CLI
277
+ - Use standard library for everything else
278
+ - Avoid adding unnecessary dependencies
279
+
280
+ ## Development Workflow
281
+
282
+ 1. **Make changes** → Edit relevant files in `internal/`
283
+ 2. **Format code** → `make fmt` or `go fmt ./...`
284
+ 3. **Run tests** → `make test` or `go test -v ./...`
285
+ 4. **Lint check** → `make lint`
286
+ 5. **Build** → `make build`
287
+ 6. **Test manually** → `./linkedin-cli --help`
288
+
289
+ ## Debugging
290
+
291
+ - Use `--verbose` flag for detailed output
292
+ - Use `--dry-run` flag to preview actions without executing
293
+ - Check PinchTab logs if browser automation fails
294
+ - Use `curl http://localhost:9867/health` to verify PinchTab is running
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.0.0] - 2025-03-14
6
+
7
+ ### Breaking Changes
8
+
9
+ - **`agent ext:connect` removed** - Merged into `agent connect`
10
+ - Old: `linkedin agent ext:connect <url>`
11
+ - New: `linkedin agent connect <url>` (auto-detects extension)
12
+
13
+ ### Changed
14
+
15
+ - **`agent connect` now auto-detects extension** - Uses fast extension mode when CDP browser available
16
+ - Falls back to Playwright when no CDP browser found
17
+ - Add `--playwright` flag to force Playwright mode
18
+
19
+ ### Added
20
+
21
+ - **`agent playwright` command** - Explicit Playwright mode (old `connect` behavior)
22
+ - **`PAGE_AGENT_AUTH_TOKEN` env var** - Configure extension auth token
23
+ - **CDP auto-detection** - Automatically uses fastest available method
24
+
25
+ ### Migration Guide
26
+
27
+ ```bash
28
+ # Before (v0.3.0)
29
+ linkedin agent ext:connect https://linkedin.com/in/user/
30
+
31
+ # After (v1.0.0)
32
+ linkedin agent connect https://linkedin.com/in/user/
33
+ ```
34
+
35
+ ### Prerequisites for Extension Mode
36
+
37
+ 1. Install [Page Agent Extension](https://chromewebstore.google.com/detail/page-agent-ext/akldabonmimlicnjlflnapfeklbfemhj)
38
+ 2. Start Chrome with CDP: `chrome --remote-debugging-port=9222`
39
+ 3. Log into LinkedIn in that browser
40
+ 4. Set `DASHSCOPE_API_KEY` env var
package/GIT_RELEASE.md ADDED
@@ -0,0 +1,167 @@
1
+ # Git Release Guide
2
+
3
+ This guide walks you through committing and pushing all changes to GitHub.
4
+
5
+ ## Quick Release
6
+
7
+ ```bash
8
+ # 1. Navigate to worktree
9
+ cd /Users/thaddeus/projects/linkedin-automation/.worktrees/linkedin-automation-cli
10
+
11
+ # 2. Install new dependencies
12
+ npm install
13
+
14
+ # 3. Run all quality checks
15
+ npm run typecheck
16
+ npm run lint
17
+ npm run format
18
+ npm run test:coverage
19
+
20
+ # 4. Build the project
21
+ npm run build
22
+
23
+ # 5. Stage all changes
24
+ git add -A
25
+
26
+ # 6. Commit
27
+ git commit -m "feat: add startup best practices (tests, CI/CD, linting)
28
+
29
+ - Added Vitest for unit testing with 80% coverage threshold
30
+ - Added ESLint + Prettier for code quality
31
+ - Added GitHub Actions CI/CD workflows
32
+ - Added Husky pre-commit hooks
33
+ - Created test files for core modules:
34
+ - storage.test.ts (encryption, secure storage)
35
+ - config.test.ts (configuration management)
36
+ - audit.test.ts (audit logging)
37
+ - selector-engine.test.ts (multi-layer selectors)
38
+ - engine.test.ts (template rendering)
39
+ - Fixed duplicate code in browser.ts and cli/auth.ts
40
+ - Updated package.json with new scripts and dependencies
41
+ - Added npm publishing guide
42
+
43
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
44
+
45
+ # 7. Push to main branch
46
+ git push origin main
47
+
48
+ # 8. Create and push version tag (triggers auto-publish)
49
+ npm version minor # or patch/major as needed
50
+ git push origin --tags
51
+ ```
52
+
53
+ ## Step-by-Step Verification
54
+
55
+ ### 1. Check Git Status
56
+
57
+ ```bash
58
+ git status
59
+ ```
60
+
61
+ You should see all the new files staged for commit.
62
+
63
+ ### 2. Verify Tests Pass
64
+
65
+ ```bash
66
+ npm run test:coverage
67
+ ```
68
+
69
+ Expected output:
70
+ - All tests pass
71
+ - Coverage meets 80% threshold
72
+
73
+ ### 3. Verify Build
74
+
75
+ ```bash
76
+ npm run build
77
+ node dist/index.js --help
78
+ ```
79
+
80
+ Should display CLI help text.
81
+
82
+ ### 4. Verify CI Configuration
83
+
84
+ Check that `.github/workflows/ci.yml` exists and contains:
85
+ - Test job (runs on push/PR)
86
+ - Build job (runs after tests pass)
87
+
88
+ ### 5. Commit and Push
89
+
90
+ ```bash
91
+ git add -A
92
+ git commit -m "feat: add startup best practices"
93
+ git push origin main
94
+ ```
95
+
96
+ ### 6. Create Release Tag
97
+
98
+ ```bash
99
+ # This bumps version, creates commit, and creates tag
100
+ npm version minor
101
+
102
+ # Push the tag (triggers GitHub Actions publish workflow)
103
+ git push && git push --tags
104
+ ```
105
+
106
+ ## Verify on GitHub
107
+
108
+ 1. Go to: https://github.com/thaddeus-git/linkedin-cli
109
+ 2. Check that all files are visible
110
+ 3. Check Actions tab for CI run
111
+ 4. After tag push, check Releases for auto-created release
112
+
113
+ ## Manual npm Publish (if needed)
114
+
115
+ If GitHub Actions doesn't auto-publish:
116
+
117
+ ```bash
118
+ # Login (one-time)
119
+ npm login
120
+
121
+ # Dry run
122
+ npm publish --dry-run
123
+
124
+ # Publish
125
+ npm publish --access public
126
+ ```
127
+
128
+ ## Files Added Summary
129
+
130
+ ### Configuration Files
131
+ - `vitest.config.ts` - Vitest test configuration
132
+ - `eslint.config.mjs` - ESLint configuration
133
+ - `.prettierrc` - Prettier formatting rules
134
+ - `.prettierignore` - Files to ignore for formatting
135
+ - `.husky/pre-commit` - Pre-commit hook script
136
+
137
+ ### GitHub Actions
138
+ - `.github/workflows/ci.yml` - CI pipeline
139
+ - `.github/workflows/publish.yml` - npm publish pipeline
140
+
141
+ ### Test Files
142
+ - `src/core/storage.test.ts`
143
+ - `src/core/config.test.ts`
144
+ - `src/core/audit.test.ts`
145
+ - `src/linkedin/selector-engine.test.ts`
146
+ - `src/templates/engine.test.ts`
147
+
148
+ ### Documentation
149
+ - `NPM_PUBLISHING.md` - npm publishing guide
150
+ - `GIT_RELEASE.md` - This file
151
+ - Updated `README.md` with development workflow
152
+
153
+ ### Code Fixes
154
+ - Fixed duplicate code in `src/core/browser.ts`
155
+ - Fixed duplicate code in `src/cli/auth.ts`
156
+
157
+ ---
158
+
159
+ **Troubleshooting:**
160
+
161
+ | Issue | Solution |
162
+ |-------|----------|
163
+ | Tests fail | Run `npm run test:watch` to debug |
164
+ | Lint errors | Run `npm run lint:fix` to auto-fix |
165
+ | Format errors | Run `npm run format` |
166
+ | Coverage too low | Add more tests for uncovered code |
167
+ | Git push rejected | Run `git pull --rebase` first |
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thaddeus Liu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Makefile ADDED
@@ -0,0 +1,30 @@
1
+ .PHONY: build test clean install lint
2
+
3
+ BINARY_NAME=linkedin-cli
4
+ MAIN_PACKAGE=./cmd/linkedin
5
+
6
+ build:
7
+ go build -o $(BINARY_NAME) $(MAIN_PACKAGE)
8
+
9
+ test:
10
+ go test -v ./...
11
+
12
+ clean:
13
+ rm -f $(BINARY_NAME)
14
+ go clean
15
+
16
+ install: build
17
+ mv $(BINARY_NAME) $(GOPATH)/bin/linkedin
18
+
19
+ dev:
20
+ go run $(MAIN_PACKAGE)
21
+
22
+ lint:
23
+ golangci-lint run
24
+
25
+ fmt:
26
+ go fmt ./...
27
+
28
+ deps:
29
+ go mod download
30
+ go mod tidy