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.
- package/.env.example +12 -0
- package/.github/workflows/ci.yml +66 -0
- package/.github/workflows/publish.yml +48 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +4 -0
- package/.prettierrc +10 -0
- package/AGENTS.md +294 -0
- package/CHANGELOG.md +40 -0
- package/GIT_RELEASE.md +167 -0
- package/LICENSE +21 -0
- package/Makefile +30 -0
- package/NPM_PUBLISHING.md +230 -0
- package/PYEOF +0 -0
- package/README.md +295 -0
- package/TESTING-GUIDE.md +151 -0
- package/cmd/linkedin/main.go +9 -0
- package/dist/agent/action-executor.d.ts +81 -0
- package/dist/agent/action-executor.d.ts.map +1 -0
- package/dist/agent/action-executor.js +170 -0
- package/dist/agent/action-executor.js.map +1 -0
- package/dist/agent/action-executor.test.d.ts +2 -0
- package/dist/agent/action-executor.test.d.ts.map +1 -0
- package/dist/agent/action-executor.test.js +366 -0
- package/dist/agent/action-executor.test.js.map +1 -0
- package/dist/agent/claude-client.d.ts +74 -0
- package/dist/agent/claude-client.d.ts.map +1 -0
- package/dist/agent/claude-client.js +314 -0
- package/dist/agent/claude-client.js.map +1 -0
- package/dist/agent/claude-client.test.d.ts +2 -0
- package/dist/agent/claude-client.test.d.ts.map +1 -0
- package/dist/agent/claude-client.test.js +590 -0
- package/dist/agent/claude-client.test.js.map +1 -0
- package/dist/agent/dom-extractor.d.ts +50 -0
- package/dist/agent/dom-extractor.d.ts.map +1 -0
- package/dist/agent/dom-extractor.js +374 -0
- package/dist/agent/dom-extractor.js.map +1 -0
- package/dist/agent/dom-extractor.test.d.ts +7 -0
- package/dist/agent/dom-extractor.test.d.ts.map +1 -0
- package/dist/agent/dom-extractor.test.js +504 -0
- package/dist/agent/dom-extractor.test.js.map +1 -0
- package/dist/agent/extension-client.d.ts +75 -0
- package/dist/agent/extension-client.d.ts.map +1 -0
- package/dist/agent/extension-client.js +245 -0
- package/dist/agent/extension-client.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +16 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/page-agent.d.ts +76 -0
- package/dist/agent/page-agent.d.ts.map +1 -0
- package/dist/agent/page-agent.js +236 -0
- package/dist/agent/page-agent.js.map +1 -0
- package/dist/agent/types.d.ts +236 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +37 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/agent-commands.d.ts +3 -0
- package/dist/cli/agent-commands.d.ts.map +1 -0
- package/dist/cli/agent-commands.js +250 -0
- package/dist/cli/agent-commands.js.map +1 -0
- package/dist/cli/auth.d.ts +3 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +288 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/company.d.ts +3 -0
- package/dist/cli/company.d.ts.map +1 -0
- package/dist/cli/company.js +55 -0
- package/dist/cli/company.js.map +1 -0
- package/dist/cli/connection.d.ts +3 -0
- package/dist/cli/connection.d.ts.map +1 -0
- package/dist/cli/connection.js +79 -0
- package/dist/cli/connection.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/messages.d.ts +3 -0
- package/dist/cli/messages.d.ts.map +1 -0
- package/dist/cli/messages.js +268 -0
- package/dist/cli/messages.js.map +1 -0
- package/dist/cli/profile.d.ts +3 -0
- package/dist/cli/profile.d.ts.map +1 -0
- package/dist/cli/profile.js +81 -0
- package/dist/cli/profile.js.map +1 -0
- package/dist/cli/profile.test.d.ts +2 -0
- package/dist/cli/profile.test.d.ts.map +1 -0
- package/dist/cli/profile.test.js +15 -0
- package/dist/cli/profile.test.js.map +1 -0
- package/dist/cli/reply.d.ts +3 -0
- package/dist/cli/reply.d.ts.map +1 -0
- package/dist/cli/reply.js +129 -0
- package/dist/cli/reply.js.map +1 -0
- package/dist/core/audit.d.ts +17 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +121 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/audit.test.d.ts +2 -0
- package/dist/core/audit.test.d.ts.map +1 -0
- package/dist/core/audit.test.js +142 -0
- package/dist/core/audit.test.js.map +1 -0
- package/dist/core/browser-cookies.d.ts +19 -0
- package/dist/core/browser-cookies.d.ts.map +1 -0
- package/dist/core/browser-cookies.js +181 -0
- package/dist/core/browser-cookies.js.map +1 -0
- package/dist/core/browser.d.ts +50 -0
- package/dist/core/browser.d.ts.map +1 -0
- package/dist/core/browser.js +318 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +103 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/config.test.d.ts +2 -0
- package/dist/core/config.test.d.ts.map +1 -0
- package/dist/core/config.test.js +111 -0
- package/dist/core/config.test.js.map +1 -0
- package/dist/core/storage.d.ts +19 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +124 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/storage.test.d.ts +2 -0
- package/dist/core/storage.test.d.ts.map +1 -0
- package/dist/core/storage.test.js +142 -0
- package/dist/core/storage.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/linkedin/auth.d.ts +22 -0
- package/dist/linkedin/auth.d.ts.map +1 -0
- package/dist/linkedin/auth.js +167 -0
- package/dist/linkedin/auth.js.map +1 -0
- package/dist/linkedin/company-extractor.d.ts +36 -0
- package/dist/linkedin/company-extractor.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.js +211 -0
- package/dist/linkedin/company-extractor.js.map +1 -0
- package/dist/linkedin/company-extractor.test.d.ts +2 -0
- package/dist/linkedin/company-extractor.test.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.test.js +52 -0
- package/dist/linkedin/company-extractor.test.js.map +1 -0
- package/dist/linkedin/connector.d.ts +45 -0
- package/dist/linkedin/connector.d.ts.map +1 -0
- package/dist/linkedin/connector.js +245 -0
- package/dist/linkedin/connector.js.map +1 -0
- package/dist/linkedin/message-sender.d.ts +32 -0
- package/dist/linkedin/message-sender.d.ts.map +1 -0
- package/dist/linkedin/message-sender.js +112 -0
- package/dist/linkedin/message-sender.js.map +1 -0
- package/dist/linkedin/messages.d.ts +78 -0
- package/dist/linkedin/messages.d.ts.map +1 -0
- package/dist/linkedin/messages.js +745 -0
- package/dist/linkedin/messages.js.map +1 -0
- package/dist/linkedin/profile.d.ts +37 -0
- package/dist/linkedin/profile.d.ts.map +1 -0
- package/dist/linkedin/profile.js +268 -0
- package/dist/linkedin/profile.js.map +1 -0
- package/dist/linkedin/profile.test.d.ts +2 -0
- package/dist/linkedin/profile.test.d.ts.map +1 -0
- package/dist/linkedin/profile.test.js +68 -0
- package/dist/linkedin/profile.test.js.map +1 -0
- package/dist/linkedin/reply.d.ts +21 -0
- package/dist/linkedin/reply.d.ts.map +1 -0
- package/dist/linkedin/reply.js +76 -0
- package/dist/linkedin/reply.js.map +1 -0
- package/dist/linkedin/selector-engine.d.ts +69 -0
- package/dist/linkedin/selector-engine.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.js +339 -0
- package/dist/linkedin/selector-engine.js.map +1 -0
- package/dist/linkedin/selector-engine.test.d.ts +2 -0
- package/dist/linkedin/selector-engine.test.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.test.js +135 -0
- package/dist/linkedin/selector-engine.test.js.map +1 -0
- package/dist/linkedin/selectors.d.ts +65 -0
- package/dist/linkedin/selectors.d.ts.map +1 -0
- package/dist/linkedin/selectors.js +261 -0
- package/dist/linkedin/selectors.js.map +1 -0
- package/dist/templates/engine.d.ts +37 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/engine.js +215 -0
- package/dist/templates/engine.js.map +1 -0
- package/dist/templates/engine.test.d.ts +2 -0
- package/dist/templates/engine.test.d.ts.map +1 -0
- package/dist/templates/engine.test.js +212 -0
- package/dist/templates/engine.test.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +7 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +113 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.test.d.ts +2 -0
- package/dist/types/index.test.d.ts.map +1 -0
- package/dist/types/index.test.js +90 -0
- package/dist/types/index.test.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +68 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +22 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +57 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry.d.ts +18 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +49 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/connection-command.md +52 -0
- package/docs/plans/2025-03-03-linkedin-cli-design.md +280 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation-plan.md +2087 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation.md +2420 -0
- package/docs/plans/2026-02-19-linkedin-connection-feature.md +596 -0
- package/docs/plans/2026-02-28-messages-send-feature.md +480 -0
- package/docs/plans/2026-02-28-messages-show-design.md +243 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-design.md +394 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-plan.md +1592 -0
- package/docs/superpowers/plans/2026-03-13-linkedin-automation-resilience-migration.md +425 -0
- package/docs/superpowers/plans/2026-03-13-playwright-fara-migration.md +1112 -0
- package/docs/superpowers/plans/2026-03-14-page-agent-plan.md +1598 -0
- package/docs/superpowers/plans/2026-03-15-company-profile-extraction.md +591 -0
- package/docs/superpowers/plans/2026-03-15-profile-extraction-plan.md +943 -0
- package/docs/superpowers/specs/2026-03-14-company-profile-extraction-design.md +371 -0
- package/docs/superpowers/specs/2026-03-14-page-agent-design.md +385 -0
- package/docs/superpowers/specs/2026-03-15-profile-extraction-design.md +409 -0
- package/eslint.config.mjs +58 -0
- package/go.mod +9 -0
- package/go.sum +10 -0
- package/import-cookies.js +376 -0
- package/internal/cmd/actions.go +123 -0
- package/internal/cmd/auth.go +108 -0
- package/internal/cmd/connect.go +42 -0
- package/internal/cmd/message.go +44 -0
- package/internal/cmd/people.go +454 -0
- package/internal/cmd/profiles.go +121 -0
- package/internal/cmd/root.go +89 -0
- package/internal/cmd/sequence.go +192 -0
- package/internal/config/config.go +187 -0
- package/internal/config/config_test.go +121 -0
- package/internal/config/profile.go +65 -0
- package/internal/linkedin/navigator.go +195 -0
- package/internal/linkedin/selectors.go +39 -0
- package/internal/linkedin/validator.go +69 -0
- package/internal/pinchtab/client.go +183 -0
- package/internal/pinchtab/client_test.go +67 -0
- package/internal/pinchtab/types.go +50 -0
- package/internal/ratelimit/limiter.go +115 -0
- package/internal/ratelimit/limits.go +32 -0
- package/package.json +67 -0
- package/release.sh +66 -0
- package/scripts/debug-linkedin.js +156 -0
- package/scripts/debug-login.js +193 -0
- package/scripts/extract-from-edge.js +96 -0
- package/scripts/import-cookies.js +101 -0
- package/scripts/poc-show-data.js +205 -0
- package/scripts/proof-of-access.js +87 -0
- package/scripts/prove-connection.js +110 -0
- package/scripts/show-linkedin-data.js +173 -0
- package/src/agent/action-executor.test.ts +464 -0
- package/src/agent/action-executor.ts +203 -0
- package/src/agent/claude-client.test.ts +707 -0
- package/src/agent/claude-client.ts +422 -0
- package/src/agent/dom-extractor.test.ts +574 -0
- package/src/agent/dom-extractor.ts +437 -0
- package/src/agent/extension-client.ts +306 -0
- package/src/agent/index.ts +28 -0
- package/src/agent/page-agent.ts +292 -0
- package/src/agent/types.ts +288 -0
- package/src/cli/agent-commands.ts +274 -0
- package/src/cli/auth.ts +343 -0
- package/src/cli/company.ts +66 -0
- package/src/cli/connection.ts +89 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/messages.ts +338 -0
- package/src/cli/profile.test.ts +14 -0
- package/src/cli/profile.ts +95 -0
- package/src/cli/reply.ts +110 -0
- package/src/core/audit.test.ts +134 -0
- package/src/core/audit.ts +98 -0
- package/src/core/browser-cookies.ts +203 -0
- package/src/core/browser.ts +304 -0
- package/src/core/config.test.ts +90 -0
- package/src/core/config.ts +81 -0
- package/src/core/storage.test.ts +129 -0
- package/src/core/storage.ts +100 -0
- package/src/index.ts +70 -0
- package/src/linkedin/auth.ts +218 -0
- package/src/linkedin/company-extractor.test.ts +58 -0
- package/src/linkedin/company-extractor.ts +222 -0
- package/src/linkedin/connector.ts +336 -0
- package/src/linkedin/message-sender.ts +141 -0
- package/src/linkedin/messages.ts +894 -0
- package/src/linkedin/profile.test.ts +79 -0
- package/src/linkedin/profile.ts +314 -0
- package/src/linkedin/reply.ts +96 -0
- package/src/linkedin/selector-engine.test.ts +167 -0
- package/src/linkedin/selector-engine.ts +393 -0
- package/src/linkedin/selectors.ts +268 -0
- package/src/templates/defaults/followup.txt +14 -0
- package/src/templates/defaults/meeting.txt +16 -0
- package/src/templates/defaults/welcome.txt +14 -0
- package/src/templates/engine.test.ts +228 -0
- package/src/templates/engine.ts +208 -0
- package/src/templates/index.ts +1 -0
- package/src/types/index.test.ts +94 -0
- package/src/types/index.ts +143 -0
- package/src/types/sql.js.d.ts +23 -0
- package/src/utils/paths.ts +33 -0
- package/src/utils/rate-limiter.ts +75 -0
- package/src/utils/retry.ts +78 -0
- package/test-cli.sh +85 -0
- package/test-real-data.sh +97 -0
- package/tsconfig.json +23 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# Page Agent Integration Design
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-03-14
|
|
4
|
+
**Status:** Draft
|
|
5
|
+
**Scope:** Replace selector-based automation with DOM-text + LLM agent approach
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Replace the current CSS selector-based LinkedIn automation with a DOM-text extraction and LLM-powered agent system, inspired by Alibaba's Page Agent. This approach extracts semantic text representations of the page and uses Claude API (via Dashscope) to make decisions about actions.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Current Architecture (v0.3.0)
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
User Command → CLI → SelectorEngine → Playwright → CSS Selectors → LinkedIn
|
|
19
|
+
↓
|
|
20
|
+
(Multi-layer: a11y → text → pattern → legacy)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Problems:**
|
|
24
|
+
- Brittle CSS selectors break when LinkedIn updates UI
|
|
25
|
+
- Selector learning system adds complexity
|
|
26
|
+
- Hard-coded selector chains are maintenance burden
|
|
27
|
+
- No natural language understanding
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## New Architecture
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
User Command → CLI → PageAgent → DOM Extractor → Claude API → Action Plan → Playwright → LinkedIn
|
|
35
|
+
↓ ↓
|
|
36
|
+
(Natural language) (Structured actions)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Components
|
|
42
|
+
|
|
43
|
+
### 1. DOM Extractor (`src/agent/dom-extractor.ts`)
|
|
44
|
+
|
|
45
|
+
Extracts semantic text representation of the LinkedIn page using Playwright.
|
|
46
|
+
|
|
47
|
+
**Output format:**
|
|
48
|
+
```typescript
|
|
49
|
+
interface DOMRepresentation {
|
|
50
|
+
url: string;
|
|
51
|
+
title: string;
|
|
52
|
+
elements: DOMElement[];
|
|
53
|
+
textContent: string;
|
|
54
|
+
interactiveElements: InteractiveElement[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface DOMElement {
|
|
58
|
+
id: string;
|
|
59
|
+
tag: string;
|
|
60
|
+
role?: string;
|
|
61
|
+
ariaLabel?: string;
|
|
62
|
+
text: string;
|
|
63
|
+
bbox?: { x: number; y: number; width: number; height: number };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface InteractiveElement {
|
|
67
|
+
id: string;
|
|
68
|
+
type: 'button' | 'link' | 'input' | 'textarea' | 'select';
|
|
69
|
+
text: string;
|
|
70
|
+
purpose: string; // inferred from context
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Extraction strategy:**
|
|
75
|
+
- Use `page.evaluate()` to extract clean DOM structure
|
|
76
|
+
- Focus on interactive elements (buttons, links, inputs)
|
|
77
|
+
- Include ARIA labels, roles, and visible text
|
|
78
|
+
- Remove script/style content, keep semantic structure
|
|
79
|
+
|
|
80
|
+
### 2. Claude API Client (`src/agent/claude-client.ts`)
|
|
81
|
+
|
|
82
|
+
Client for Dashscope Claude API endpoint.
|
|
83
|
+
|
|
84
|
+
**Configuration:**
|
|
85
|
+
- Base URL: `https://coding.dashscope.aliyuncs.com/apps/anthropic/v1`
|
|
86
|
+
- Model: `qwen3.5-plus`
|
|
87
|
+
- API Key: From `.env` file (`DASHSCOPE_API_KEY`)
|
|
88
|
+
|
|
89
|
+
**Interface:**
|
|
90
|
+
```typescript
|
|
91
|
+
interface ClaudeClient {
|
|
92
|
+
generateActionPlan(
|
|
93
|
+
dom: DOMRepresentation,
|
|
94
|
+
goal: string,
|
|
95
|
+
context?: ActionContext
|
|
96
|
+
): Promise<ActionPlan>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ActionPlan {
|
|
100
|
+
actions: Action[];
|
|
101
|
+
reasoning: string;
|
|
102
|
+
expectedOutcome: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type Action =
|
|
106
|
+
| { type: 'click'; elementId: string; description: string }
|
|
107
|
+
| { type: 'type'; elementId: string; text: string; description: string }
|
|
108
|
+
| { type: 'wait'; durationMs: number; description: string }
|
|
109
|
+
| { type: 'navigate'; url: string; description: string }
|
|
110
|
+
| { type: 'verify'; condition: string; description: string };
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 3. Action Executor (`src/agent/action-executor.ts`)
|
|
114
|
+
|
|
115
|
+
Executes action plans using Playwright.
|
|
116
|
+
|
|
117
|
+
**Responsibilities:**
|
|
118
|
+
- Map element IDs to actual DOM elements
|
|
119
|
+
- Execute clicks, typing, navigation
|
|
120
|
+
- Handle errors and retries
|
|
121
|
+
- Verify outcomes
|
|
122
|
+
|
|
123
|
+
### 4. Page Agent (`src/agent/page-agent.ts`)
|
|
124
|
+
|
|
125
|
+
Main orchestrator combining all components.
|
|
126
|
+
|
|
127
|
+
**Interface:**
|
|
128
|
+
```typescript
|
|
129
|
+
interface PageAgent {
|
|
130
|
+
// High-level task execution
|
|
131
|
+
execute(task: Task): Promise<TaskResult>;
|
|
132
|
+
|
|
133
|
+
// Specific tasks
|
|
134
|
+
connect(profileUrl: string, note?: string): Promise<ConnectionResult>;
|
|
135
|
+
sendMessage(profileUrl: string, message: string): Promise<MessageResult>;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Prompt Design
|
|
142
|
+
|
|
143
|
+
### System Prompt
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
You are a web automation agent that controls LinkedIn through a browser.
|
|
147
|
+
You receive a DOM representation of the current page and must decide the next action(s) to achieve the user's goal.
|
|
148
|
+
|
|
149
|
+
Rules:
|
|
150
|
+
1. Respond ONLY with a JSON action plan
|
|
151
|
+
2. Each action must reference an element by its ID from the DOM
|
|
152
|
+
3. Include brief reasoning for your decision
|
|
153
|
+
4. If the goal is already achieved, return empty actions with success status
|
|
154
|
+
5. If stuck or error, explain why and suggest recovery
|
|
155
|
+
|
|
156
|
+
Available actions:
|
|
157
|
+
- click: Click an element
|
|
158
|
+
- type: Type text into an input
|
|
159
|
+
- wait: Wait for page to stabilize
|
|
160
|
+
- navigate: Go to a URL
|
|
161
|
+
- verify: Check if a condition is met
|
|
162
|
+
|
|
163
|
+
Response format:
|
|
164
|
+
{
|
|
165
|
+
"reasoning": "string",
|
|
166
|
+
"expectedOutcome": "string",
|
|
167
|
+
"actions": [
|
|
168
|
+
{ "type": "click", "elementId": "elem-123", "description": "Click Connect button" }
|
|
169
|
+
],
|
|
170
|
+
"status": "in_progress" | "completed" | "error"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### User Prompt Template
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
Goal: {{userGoal}}
|
|
178
|
+
|
|
179
|
+
Current page DOM:
|
|
180
|
+
```json
|
|
181
|
+
{{domRepresentation}}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
{{#if previousActions}}
|
|
185
|
+
Previous actions taken:
|
|
186
|
+
{{#each previousActions}}
|
|
187
|
+
- {{type}}: {{description}} ({{status}})
|
|
188
|
+
{{/each}}
|
|
189
|
+
{{/if}}
|
|
190
|
+
|
|
191
|
+
What action(s) should be taken next?
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## DOM Extraction Strategy
|
|
197
|
+
|
|
198
|
+
### Approach: Accessibility Tree + Interactive Elements
|
|
199
|
+
|
|
200
|
+
Extract elements in priority order:
|
|
201
|
+
|
|
202
|
+
1. **Buttons** - `button` elements, `[role="button"]`, clickables
|
|
203
|
+
2. **Links** - `a[href]` elements
|
|
204
|
+
3. **Form inputs** - `input`, `textarea`, `select`
|
|
205
|
+
4. **Text content** - Headings, labels, status messages
|
|
206
|
+
|
|
207
|
+
### Element ID Assignment
|
|
208
|
+
|
|
209
|
+
Assign stable IDs based on element characteristics:
|
|
210
|
+
- Use `data-testid` if available
|
|
211
|
+
- Fallback to `[tag]-[aria-label]-[index]` hash
|
|
212
|
+
- Include bounding box for click coordinates
|
|
213
|
+
|
|
214
|
+
### Example Extracted DOM
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"url": "https://www.linkedin.com/in/alice",
|
|
219
|
+
"title": "Alice Smith | LinkedIn",
|
|
220
|
+
"elements": [
|
|
221
|
+
{
|
|
222
|
+
"id": "btn-connect-1",
|
|
223
|
+
"tag": "button",
|
|
224
|
+
"role": "button",
|
|
225
|
+
"ariaLabel": "Connect",
|
|
226
|
+
"text": "Connect",
|
|
227
|
+
"bbox": { "x": 800, "y": 200, "width": 100, "height": 40 }
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"id": "btn-message-1",
|
|
231
|
+
"tag": "button",
|
|
232
|
+
"role": "button",
|
|
233
|
+
"ariaLabel": "Message",
|
|
234
|
+
"text": "Message",
|
|
235
|
+
"bbox": { "x": 920, "y": 200, "width": 100, "height": 40 }
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"id": "input-note-1",
|
|
239
|
+
"tag": "textarea",
|
|
240
|
+
"role": "textbox",
|
|
241
|
+
"ariaLabel": "Add a note",
|
|
242
|
+
"text": "",
|
|
243
|
+
"bbox": { "x": 500, "y": 400, "width": 400, "height": 100 }
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
"status": {
|
|
247
|
+
"connectionState": "not_connected",
|
|
248
|
+
"profileName": "Alice Smith",
|
|
249
|
+
"profileTitle": "Software Engineer at TechCorp"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Action Execution Flow
|
|
257
|
+
|
|
258
|
+
### Connect Flow
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
1. Navigate to profile URL
|
|
262
|
+
2. Extract DOM
|
|
263
|
+
3. Send to Claude: "Send connection request to this profile"
|
|
264
|
+
4. Receive action plan:
|
|
265
|
+
- Click Connect button
|
|
266
|
+
- Wait for modal
|
|
267
|
+
- Type note (if provided)
|
|
268
|
+
- Click Send
|
|
269
|
+
5. Execute actions one by one
|
|
270
|
+
6. Verify outcome (check for Pending status)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Message Flow
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
1. Navigate to profile URL
|
|
277
|
+
2. Extract DOM
|
|
278
|
+
3. Send to Claude: "Send message to this person"
|
|
279
|
+
4. Receive action plan:
|
|
280
|
+
- Click Message button
|
|
281
|
+
- Wait for composer
|
|
282
|
+
- Type message
|
|
283
|
+
- Click Send
|
|
284
|
+
5. Execute actions
|
|
285
|
+
6. Verify outcome
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Error Handling
|
|
291
|
+
|
|
292
|
+
### Recovery Strategies
|
|
293
|
+
|
|
294
|
+
1. **Element not found** - Re-extract DOM, retry with updated IDs
|
|
295
|
+
2. **Action timeout** - Wait longer, check if page changed
|
|
296
|
+
3. **Unexpected state** - Re-plan with new DOM
|
|
297
|
+
4. **LLM error** - Retry with exponential backoff
|
|
298
|
+
|
|
299
|
+
### Safety Checks
|
|
300
|
+
|
|
301
|
+
- Validate URLs are LinkedIn profiles before navigation
|
|
302
|
+
- Rate limiting via existing limiter
|
|
303
|
+
- Confirmation prompts for destructive actions
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## File Structure
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
src/
|
|
311
|
+
├── agent/
|
|
312
|
+
│ ├── index.ts # Public API exports
|
|
313
|
+
│ ├── page-agent.ts # Main orchestrator
|
|
314
|
+
│ ├── dom-extractor.ts # DOM to text extraction
|
|
315
|
+
│ ├── claude-client.ts # Claude API client
|
|
316
|
+
│ ├── action-executor.ts # Playwright action execution
|
|
317
|
+
│ ├── prompts.ts # LLM prompts
|
|
318
|
+
│ └── types.ts # Agent types
|
|
319
|
+
├── cli/
|
|
320
|
+
│ └── agent-command.ts # New CLI command: `linkedin agent`
|
|
321
|
+
└── linkedin/
|
|
322
|
+
└── (existing files - keep for fallback)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Migration Plan
|
|
328
|
+
|
|
329
|
+
### Phase 1: New Agent Commands (This PR)
|
|
330
|
+
- Create `src/agent/` module
|
|
331
|
+
- Implement DOM extractor
|
|
332
|
+
- Implement Claude client
|
|
333
|
+
- Add `linkedin agent connect <url>` command
|
|
334
|
+
- Add `linkedin agent message <url> <text>` command
|
|
335
|
+
- Keep existing commands unchanged
|
|
336
|
+
|
|
337
|
+
### Phase 2: Replace Existing Commands (Future)
|
|
338
|
+
- Migrate `linkedin connect` to use agent
|
|
339
|
+
- Migrate `linkedin message` to use agent
|
|
340
|
+
- Deprecate selector engine
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Environment Configuration
|
|
345
|
+
|
|
346
|
+
Add to `.env`:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Dashscope Claude API
|
|
350
|
+
DASHSCOPE_API_KEY=your_key_here
|
|
351
|
+
DASHSCOPE_BASE_URL=https://coding.dashscope.aliyuncs.com/apps/anthropic/v1
|
|
352
|
+
DASHSCOPE_MODEL=qwen3.5-plus
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Testing Strategy
|
|
358
|
+
|
|
359
|
+
1. **Unit tests** - DOM extractor with mock pages
|
|
360
|
+
2. **Integration tests** - Agent with mock Claude responses
|
|
361
|
+
3. **E2E tests** - Full flow with test LinkedIn accounts
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Trade-offs
|
|
366
|
+
|
|
367
|
+
### Advantages
|
|
368
|
+
- Resilient to UI changes (text-based, not selectors)
|
|
369
|
+
- Natural language understanding
|
|
370
|
+
- Easier maintenance
|
|
371
|
+
- Self-documenting actions (LLM reasoning)
|
|
372
|
+
|
|
373
|
+
### Disadvantages
|
|
374
|
+
- Requires API calls (latency, cost)
|
|
375
|
+
- Depends on LLM availability
|
|
376
|
+
- More complex debugging
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Success Criteria
|
|
381
|
+
|
|
382
|
+
1. Connect and message commands work reliably on standard LinkedIn profiles
|
|
383
|
+
2. No hard-coded selectors in new agent code
|
|
384
|
+
3. Clear action logging for debugging
|
|
385
|
+
4. Graceful fallback if LLM is unavailable
|