ilml-plugin-linkedin 1.2.2 → 1.3.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.1
4
+ - **`ilml linkedin warm-scan` now actually works** — `warmScan.mjs` was previously only reachable through `npm run warm-scan` in the dev repo. Wired through to the published plugin: added `warm-scan` to `ilml-plugin.json` commands, `warmScan.mjs` to the build entry points, and `target-companies.json` to the npm tarball. The script now finds `target-companies.json` in three locations, in order: `<DATA_DIR>/target-companies.json` (user override), `<package-root>/target-companies.json` (shipped default for prod), source dir (dev mode)
5
+ - README polish — leads with the killer differentiators that were buried before: AI drafts that go through user review with re-check on push, auto-triaged inbox classification, `--skip-unread` sync that preserves LinkedIn unread badges. Daily-commands table descriptions tightened (especially `today`, `enrich`, `daily`, which were vague or wrong); added `apply-queue` and `warm-scan` rows; new "Common routines" section with morning-triage / active-search / passive-pipeline / targeted-networking patterns
6
+ - `package.json` — new npm `description` (one line that says what the plugin does, not just a list of subcommands), expanded `keywords` for npm discovery (`linkedin-bot`, `linkedin-automation`, `easy-apply`, `job-search`, `inbox-sync`, `recruiter-outreach`, `ai-messaging`)
7
+
8
+ ## 1.3.0
9
+ - **Behavior change — LinkedIn session cookies now live in the active ilml scope** instead of in the plugin install directory. New path: `<scope>/.ilivemylife/plugins-state/linkedin/cookies.json` (production) / `./cookies.json` next to `ilml-plugin.json` (dev mode, unchanged). This means:
10
+ - `ilml plugin update linkedin` no longer wipes your LinkedIn session — sessions survive plugin updates
11
+ - `ilml logout` correctly clears the LinkedIn session in that scope (alongside other user-tied data)
12
+ - Different ilml login scopes (e.g. global vs `--local` per project) hold separate LinkedIn sessions — useful for multiple LinkedIn accounts
13
+ - **One-time:** after upgrading to 1.3.0, run `ilml linkedin login` once. The old session was inside the install dir which got wiped by the upgrade
14
+ - README rewritten with marketing-first framing — leads with what users *get* (your LinkedIn data finally yours, AI-assisted messaging, terminal-first automation, Easy Apply that actually thinks) before diving into install/configure
15
+ - New section: **Save AI tokens — tune the node settings** — explains how to use `ilml editSettings` to set `intelligence: disabled` (cheaper model) and `rootAccess: disabled` (skip whole-graph context lookup) on the Easy Apply node to control token spend
16
+ - Improved **Where things live** table — clearer per-row "wiped on plugin update?" / "wiped on `ilml logout`?" columns; explicitly highlights that `DATA_DIR` survives both
17
+ - Internal: new `src/cookiesStore.mjs` is the single source of truth for cookie path resolution. All cookie readers/writers in published entries (`src/browser.mjs`, `src/saveLoginState.mjs`, `run.mjs`, `funnelRunner.mjs`) go through it. SDK and CLI APIs unchanged
18
+
3
19
  ## 1.2.2
4
20
  - README polish based on real-user feedback:
5
21
  - Cleaner top — removed the metadata bullet list (npm/tracker/license) that was the very first thing visitors saw on the npm page; now opens straight into "What it does"
package/README.md CHANGED
@@ -1,27 +1,28 @@
1
- # ilml-plugin-linkedin
1
+ # Your LinkedIn — finally yours
2
2
 
3
- LinkedIn job-search and networking automation as a plugin for the [`ilml`](https://www.npmjs.com/package/@ilivemylife/graph-sdk) CLI.
3
+ LinkedIn keeps your **connections, conversations, and the notes you write about people** locked inside their interface. You can't export message threads. You can't query an AI on your own contact history. You can't run batch operations from a script. You can't keep a personal database of who said what when.
4
4
 
5
- ## What it does
5
+ This plugin pulls all of that onto your machine and into your [iLiveMyLife](https://ilivemylife.io) knowledge graph — where it's searchable, syncable, AI-queryable, and yours to keep forever. Even if you uninstall the plugin or switch ilml accounts, **your accumulated data stays where you put it**.
6
6
 
7
- A toolkit that drives LinkedIn from your machine via a real browser (Puppeteer + stealth), with results synced into your [iLiveMyLife](https://ilivemylife.io) knowledge graph:
7
+ It also automates LinkedIn from the terminal applying to jobs, drafting outreach, processing recruiter funnels so you can script what LinkedIn's web UI makes you do one click at a time.
8
8
 
9
- - **Easy Apply** — auto-fills and submits LinkedIn job applications. The graph's AI assistant (**Lifebot**) reads your profile node to answer custom application questions in your voice.
10
- - **Inbox sync** — scans LinkedIn messages, detects changes, and mirrors threads into your graph.
11
- - **Message bot** — draft → review → send batch messaging workflow (you approve before anything is sent).
12
- - **Connection funnel** — process recruiter / founder / investor profiles in batches (connect, skip, message).
13
- - **Scout** — find jobs without applying; reports back which match your filters.
14
- - **Profile activity** — track who viewed your profile, who you visited, etc.
9
+ ---
15
10
 
16
- All persistent data (job DB, profile DB, conversation history, cookies) is stored locally in a directory you choose — never in a cloud you don't control.
11
+ ## What you get
17
12
 
18
- ---
13
+ - **A full local mirror of your LinkedIn graph.** Connections, conversation threads, message history, profile metadata, your private notes about people — synced into your iLiveMyLife graph and a local database. Nothing stays trapped in linkedin.com.
14
+ - **AI-assisted messaging — drafts go through you.** [Lifebot](https://ilivemylife.io) reads the full context of each thread (history, your notes, tags) and drafts replies in your voice. You review the batch in one pass and push approved drafts as a group. On push, the bot re-scans every thread first — if a new incoming message arrived since you reviewed, it pauses that draft and flags it for re-review instead of sending blind. Never autopilot.
15
+ - **An auto-triaged inbox.** Every conversation gets classified (recruiter / hiring manager / founder / investor / spam / event), tagged, prioritized, and stamped with a suggested next action. `ilml linkedin today` prints a no-browser daily plan: who to reply to, what to do, in priority order — before you've even opened LinkedIn.
16
+ - **Inbox sync that doesn't break your workflow.** A `--skip-unread` mode pulls the conversation list and metadata without "opening" unread threads — LinkedIn keeps showing the unread badges until *you* read them in the UI. Use the bot as an analyst without losing your own attention markers.
17
+ - **Terminal-first automation.** Batch operations — apply to dozens of jobs at once, draft 50 outreach messages then review them all in one pass, run a connection campaign on a schedule. Scriptable. Re-runnable. Logged.
18
+ - **Easy Apply that actually thinks.** Auto-fills LinkedIn job applications, with Lifebot answering custom questions ("Why are you interested in this role?") in your voice using context from your graph — not boilerplate.
19
+ - **Your data outlives the plugin.** The connection database, conversation history, and your notes live in a directory **you** choose (`DATA_DIR`). Plugin updates don't wipe them. Logging out of ilml doesn't wipe them. Uninstalling the plugin doesn't wipe them. Only **you** decide when they go.
19
20
 
20
- # User Guide
21
+ ---
21
22
 
22
23
  ## Prerequisites
23
24
 
24
- - **Node.js 18+** — [download](https://nodejs.org/) (the Long-Term Support version is fine).
25
+ - **Node.js 18+** — [download](https://nodejs.org/) (the LTS version is fine).
25
26
  - **iLiveMyLife account** — free signup at [iLiveMyLife.io](https://ilivemylife.io).
26
27
  - **LinkedIn account** — your personal one.
27
28
 
@@ -42,9 +43,9 @@ ilml login
42
43
  ilml plugin install linkedin
43
44
  ```
44
45
 
45
- That's the whole install. The bare-name `linkedin` resolves to `npm:ilml-plugin-linkedin` via the ilml plugin convention. The CLI downloads the latest version from npm, extracts it under `~/.ilivemylife/plugins/linkedin/`, and runs `npm install` for its runtime dependencies (mainly Puppeteer's bundled Chromium — first install takes a couple of minutes).
46
+ The bare-name `linkedin` resolves to `npm:ilml-plugin-linkedin` via the ilml plugin convention. The CLI downloads the latest version from npm, extracts it under `~/.ilivemylife/plugins/linkedin/`, and runs `npm install` for runtime dependencies (mainly Puppeteer's bundled Chromium — first install takes a couple of minutes).
46
47
 
47
- Verify it's installed:
48
+ Verify:
48
49
 
49
50
  ```bash
50
51
  ilml plugin list
@@ -53,7 +54,7 @@ ilml linkedin
53
54
 
54
55
  ## Configure
55
56
 
56
- The plugin needs a few things to know who you are and where to log activity. The first time you run any `ilml linkedin <command>`, the CLI will prompt you for the **required** fields. You can also pre-configure:
57
+ The first time you run `ilml linkedin <command>`, the CLI will prompt for required fields. You can also pre-configure:
57
58
 
58
59
  ```bash
59
60
  ilml plugin config linkedin set LINKEDIN_NAME "Your Full Name"
@@ -61,18 +62,16 @@ ilml plugin config linkedin set NODE_EASY_APPLY_MAIN <node-id>
61
62
  ilml plugin config linkedin set NODE_EASY_APPLY_ERRORS <node-id>
62
63
  ```
63
64
 
64
- To inspect (secret values are masked):
65
-
65
+ Inspect (secrets masked):
66
66
  ```bash
67
67
  ilml plugin config linkedin
68
68
  ```
69
69
 
70
- To change a value later or remove one:
71
-
70
+ Change later or wipe:
72
71
  ```bash
73
72
  ilml plugin config linkedin set LINKEDIN_NAME "New Name"
74
73
  ilml plugin config linkedin unset OPTIONAL_KEY
75
- ilml plugin config linkedin reset # wipe all; will re-prompt next run
74
+ ilml plugin config linkedin reset # re-prompt on next run
76
75
  ```
77
76
 
78
77
  ### Configuration fields
@@ -80,15 +79,15 @@ ilml plugin config linkedin reset # wipe all; will re-promp
80
79
  | Section | Field | Required | What it is |
81
80
  |---|---|---|---|
82
81
  | LinkedIn account | `LINKEDIN_NAME` | yes | Your full name **exactly** as it appears on LinkedIn (used to identify your own messages in threads). |
83
- | | `LINKEDIN_LOGIN` | no | LinkedIn email — only used for auto-fill during one-time login. |
84
- | | `LINKEDIN_PASS` | no, secret | LinkedIn password — only used for auto-fill during one-time login. Stored locally on disk; never sent anywhere. |
82
+ | | `LINKEDIN_LOGIN` | no | LinkedIn email — only used for auto-fill during one-time browser login. |
83
+ | | `LINKEDIN_PASS` | no, secret | LinkedIn password — auto-fill only. Stored locally on disk; never sent anywhere. |
85
84
  | iLiveMyLife integration | `NODE_EASY_APPLY_MAIN` | yes | Node ID where job applications are tracked. See "Setting up your graph" below. |
86
85
  | | `NODE_EASY_APPLY_ERRORS` | yes | Node ID where Easy Apply errors are logged. |
87
86
  | | `NODE_RUN_REPORTS` | no | Node ID where end-of-session summaries are posted (enable notifications on it to get pings when long jobs finish). |
88
- | Data storage | `DATA_DIR` | no | Absolute path to a directory for the local DB / cookies. **Recommended** — keeps data outside the plugin install folder so it survives plugin updates. |
87
+ | Data storage | `DATA_DIR` | no | Absolute path to a directory for your local databases (connections, conversations, profile cache). **Strongly recommended** — keeps your accumulated data outside the plugin install folder so it survives plugin updates, ilml logout, and reinstalls. |
89
88
  | Apply bot | `City` | no | Default city for filters. Default: `Toronto`. |
90
89
  | | `MAX_APPLY_FOR_RUN` | no | Stop after this many applications per run. Default: `50`. |
91
- | | `LINKEDIN_SEARCH_URL` | no | Default LinkedIn jobs search URL open LinkedIn → Jobs, set your filters, then make sure **Easy Apply** is on. Copy the resulting URL from the address bar and paste it here. |
90
+ | | `LINKEDIN_SEARCH_URL` | no | Default LinkedIn jobs search URL. Open LinkedIn → Jobs, set your filters, make sure Easy Apply is on, copy the resulting URL from the address bar. |
92
91
  | Scouting | `LINKEDIN_SCOUT_URLS` | no | Comma-separated job search URLs to scan without applying. |
93
92
  | Resume / CV | `CV_TEXT`, `RESUME_PDF_URL`, `RESUME_FILENAME` | no | CV snippet, public PDF URL, suggested filename. Used in messages to recruiters. |
94
93
  | Funnel | `FUNNEL_USE_CUSTOM_MESSAGE` | no | Send a custom note with connection requests. Default: `false`. |
@@ -123,9 +122,27 @@ The Easy Apply bot uses iLiveMyLife's AI assistant (**Lifebot**) to answer custo
123
122
 
124
123
  7. Create a sibling node titled **"Easy Apply Errors"** (no AI needed). Copy its ID and set it: `ilml plugin config linkedin set NODE_EASY_APPLY_ERRORS <id>`.
125
124
 
125
+ ### Save AI tokens — tune the node settings
126
+
127
+ Lifebot uses AI tokens for every form question it answers. Two settings on the Easy Apply node let you tune cost vs. quality without touching the plugin:
128
+
129
+ ```bash
130
+ # Use a cheaper, faster AI model (good enough for filling forms):
131
+ ilml editSettings <NODE_EASY_APPLY_MAIN> --intelligence disabled
132
+
133
+ # Skip whole-graph context lookup — just use this node's description:
134
+ ilml editSettings <NODE_EASY_APPLY_MAIN> --rootAccess disabled
135
+ ```
136
+
137
+ `intelligence: disabled` switches Lifebot to a cheaper model (think GPT-3.5 / Claude Haiku tier instead of GPT-4 / Claude Opus). For application form filling, the cheap model is usually fine — questions are mechanical.
138
+
139
+ `rootAccess: disabled` stops Lifebot from searching your entire knowledge graph for context every time it answers. The Easy Apply node's description and child nodes already have everything Lifebot needs — extra context costs tokens for no benefit.
140
+
141
+ To switch back: pass `--intelligence enabled` or `--rootAccess enabled`. To reset to your account default: pass `null`.
142
+
126
143
  ## First-time LinkedIn login
127
144
 
128
- LinkedIn requires an authenticated session, so a one-time browser login is needed. Run:
145
+ LinkedIn requires an authenticated browser session, so a one-time login is needed:
129
146
 
130
147
  ```bash
131
148
  ilml linkedin login
@@ -133,28 +150,69 @@ ilml linkedin login
133
150
 
134
151
  A real browser window opens. Sign in to LinkedIn (manually solve any captcha or 2FA). When you see your feed, close the window — your session cookies are saved locally for future runs.
135
152
 
153
+ Sessions live in your active ilml scope (`<scope>/.ilivemylife/plugins-state/linkedin/cookies.json`), so:
154
+
155
+ - Different ilml scopes (e.g. `ilml login` globally vs `ilml login --local` in a project folder) keep **separate** LinkedIn sessions — useful if you have multiple LinkedIn accounts.
156
+ - Plugin updates do **not** wipe your session — you don't need to re-login after `ilml plugin update linkedin`.
157
+ - `ilml logout` does wipe the session in that scope (alongside other user-tied data) — clean handoff between users on a shared machine.
158
+
136
159
  ## Daily commands
137
160
 
138
161
  | Command | What it does |
139
162
  |---|---|
140
- | `ilml linkedin apply` | Auto-apply to LinkedIn Easy Apply jobs. Stops after `MAX_APPLY_FOR_RUN`. |
141
- | `ilml linkedin sync` / `sync-all` | Pull the LinkedIn inbox into the local DB and the graph. |
142
- | `ilml linkedin today` | Faster sync of just today's activity. |
143
- | `ilml linkedin enrich` | Categorize and tag conversations. |
144
- | `ilml linkedin report` | Summary of the latest session. |
145
- | `ilml linkedin scout` | Scan jobs against `LINKEDIN_SCOUT_URLS` without applying. |
146
- | `ilml linkedin messages` | Batch messaging draft, review, then push. Nothing sends without your approval. |
147
- | `ilml linkedin funnel` | Process a queue of recruiter / founder / investor profiles. |
148
- | `ilml linkedin visit` | Track profile visits. |
149
- | `ilml linkedin viewers` | List who viewed your profile. |
150
- | `ilml linkedin daily` | Compound routine combining several commands. |
163
+ | `ilml linkedin apply` | Auto-apply to LinkedIn Easy Apply jobs from `LINKEDIN_SEARCH_URL`. Stops after `MAX_APPLY_FOR_RUN`. |
164
+ | `ilml linkedin sync-all` | Pull the LinkedIn inbox into the local DB and the graph. Add `--skip-unread` to scan without opening unread threads (preserves LinkedIn's unread badges). `--full` for a full re-scan, `--report` for stats only with no browser. |
165
+ | `ilml linkedin today` | No-browser daily plan: who to reply to, what to do, in priority order. Reads already-synced data — doesn't open LinkedIn. |
166
+ | `ilml linkedin enrich` | Re-classify every conversation (recruiter / hiring manager / founder / investor / spam / event) and rebuild priorities, summaries, and suggested actions. Offline, no browser. |
167
+ | `ilml linkedin report` | Print the summary of the latest session. |
168
+ | `ilml linkedin scout` | Scan jobs from `LINKEDIN_SCOUT_URLS` and score them — without applying. Builds a queue for `apply-queue`. |
169
+ | `ilml linkedin apply-queue` | Apply to the top-scored jobs from the latest `scout` run. |
170
+ | `ilml linkedin messages` | Batch messaging draft, review, then push. Nothing sends without your approval. Common forms: `messages --review-drafts` (review pending drafts) and `messages --push-drafts` (push approved). |
171
+ | `ilml linkedin funnel` | Run the recruiter / founder / investor connection-request queue. |
172
+ | `ilml linkedin visit` | Visit recruiter / target profiles so they see you in "who viewed your profile". |
173
+ | `ilml linkedin viewers` | Pull the "who viewed your profile" list. |
174
+ | `ilml linkedin warm-scan` | Find 1st-degree connections at target companies (offline, no browser). Reads your already-synced people / conversations and groups them by company so you know who you can message directly versus where you need cold outreach. Override the company list via `--companies="Anthropic,OpenAI"` or pick a named group with `--group=topPaying`. |
175
+ | `ilml linkedin daily` | Full pipeline in one command: `sync-all` → `apply` → `scout` → `funnel` → `visit` → `viewers` → run report. |
151
176
  | `ilml linkedin login` | Re-save LinkedIn cookies after a session expires. |
152
177
 
153
- Run `ilml linkedin` (no subcommand) to see the list anytime.
178
+ Run `ilml linkedin` (no subcommand) any time to see the list.
179
+
180
+ ## Common routines
181
+
182
+ A few patterns that string the daily commands together. Outgoing messages always require your explicit approval (via `messages --push-drafts`); commands like `apply` and `funnel` do submit Easy Apply forms and connection requests on their own, scoped by `MAX_APPLY_FOR_RUN` and `FUNNEL_MAX_CONNECTIONS`.
183
+
184
+ **Morning triage** — see what needs attention before opening LinkedIn:
185
+ ```bash
186
+ ilml linkedin sync-all --skip-unread # pull new messages without losing unread badges
187
+ ilml linkedin today # print prioritized day plan
188
+ ```
189
+
190
+ **Active job search** — apply, scout, network on autopilot; you stay in the loop on outgoing messages:
191
+ ```bash
192
+ ilml linkedin daily # apply + scout + funnel + visit + viewers + report
193
+ ilml linkedin messages --review-drafts # later: review the AI's drafted replies
194
+ ilml linkedin messages --push-drafts # push the ones you approved
195
+ ```
196
+
197
+ **Passive pipeline** — keep the inbox synced and triaged without touching outgoing channels:
198
+ ```bash
199
+ ilml linkedin sync-all --skip-unread
200
+ ilml linkedin enrich
201
+ ilml linkedin today
202
+ ```
203
+
204
+ **Targeted networking** — figure out which target companies you can reach via existing connections before sending any cold outreach:
205
+ ```bash
206
+ ilml linkedin warm-scan # default group
207
+ ilml linkedin warm-scan --group=topPaying # broader (FAANG + tier-1 + high-comp)
208
+ ilml linkedin warm-scan --companies="Anthropic,OpenAI" # one-off override
209
+ ```
210
+
211
+ Compose your own from the commands above — each one is a standalone script.
154
212
 
155
213
  ## Updating
156
214
 
157
- Updates ship via npm. The CLI checks the registry and only downloads when there's a newer version:
215
+ Updates ship via npm. The CLI checks the registry and only downloads when a newer version is actually available:
158
216
 
159
217
  ```bash
160
218
  ilml plugin update linkedin # check this plugin
@@ -163,20 +221,27 @@ ilml update # update the SDK and all plugins together
163
221
  ```
164
222
 
165
223
  You'll see one of:
166
- - `linkedin: already on latest version (1.2.1)` — nothing downloaded
167
- - `linkedin: 1.2.1 → 1.3.0` followed by re-install — newer version pulled
224
+ - `linkedin: already on latest version (1.3.0)` — nothing downloaded
225
+ - `linkedin: 1.3.0 → 1.3.1` then re-install — newer version pulled
168
226
 
169
- Your saved configuration is preserved across updates (it lives outside the install dir). If a new version requires a new field, the next run will prompt you for just the new field.
227
+ **Your config and your data both survive updates.** Only the plugin code dir gets refreshed; everything user-tied lives elsewhere.
170
228
 
171
- ## Where data lives
229
+ ## Where things live
172
230
 
173
- | Path | What's there | Survives plugin update? | Cleared by `ilml logout`? |
231
+ | Path | What's there | Wiped on plugin update? | Wiped on `ilml logout`? |
174
232
  |---|---|---|---|
175
- | `~/.ilivemylife/plugins/linkedin/` | Plugin code (re-extracted on update) | Replaced | No |
176
- | `~/.ilivemylife/plugins-state/linkedin.json` (or per-project local) | Your configured values | Yes | Yes (with that scope) |
177
- | Directory you set as `DATA_DIR` | Local DBs, cookies, scraped profiles | Yes | No (you manage it) |
233
+ | `~/.ilivemylife/plugins/linkedin/` | Plugin code | yes (re-extracted) | no |
234
+ | `<scope>/.ilivemylife/plugins-state/linkedin.json` | Your configured values (LINKEDIN_NAME, node IDs, etc.) | no | yes (with that scope) |
235
+ | `<scope>/.ilivemylife/plugins-state/linkedin/cookies.json` | LinkedIn session cookies | no | yes (with that scope) |
236
+ | Directory you set as `DATA_DIR` | Local DBs, scraped profiles, message history, your notes | **no** | **no** |
237
+
238
+ If you didn't set `DATA_DIR`, runtime data files default to inside the plugin's install dir — meaning a plugin reinstall **wipes them**. Setting `DATA_DIR` to a sibling folder like `~/linkedin-plugin-data/` is **strongly recommended**:
178
239
 
179
- If you didn't set `DATA_DIR`, data files default to inside the plugin's install dir — meaning a plugin reinstall wipes them. Setting `DATA_DIR` to a sibling folder like `~/linkedin-plugin-data/` is strongly recommended.
240
+ ```bash
241
+ ilml plugin config linkedin set DATA_DIR /absolute/path/to/your/linkedin-data
242
+ ```
243
+
244
+ Once set, you can sync `DATA_DIR` to a backup drive, a cloud folder, or another machine — the data is plain SQLite-style JSON, fully portable.
180
245
 
181
246
  ## Removing the plugin
182
247
 
@@ -184,13 +249,14 @@ If you didn't set `DATA_DIR`, data files default to inside the plugin's install
184
249
  ilml plugin remove linkedin
185
250
  ```
186
251
 
187
- This wipes the install dir. To also clear your saved configuration in the active scope:
188
-
252
+ This wipes the install dir. Your config in the active scope:
189
253
  ```bash
190
254
  ilml plugin config linkedin reset
191
255
  ```
192
256
 
193
- To clean up the data directory (cookies, DBs), delete the folder you set as `DATA_DIR` manually.
257
+ The data in `DATA_DIR` is yours delete the folder manually if you want it gone, or keep it.
258
+
259
+ ---
194
260
 
195
261
  ## Feedback & support
196
262