ilml-plugin-linkedin 1.2.0 → 1.2.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/README.md +302 -369
- package/ilml-plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,369 +1,302 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
node
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
##
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
| `NODE_EASY_APPLY_ERRORS` | ID узла для логирования ошибок |
|
|
304
|
-
|
|
305
|
-
> Токен подхватывается автоматически после `ilml login` (Шаг 5б). Прописывать в `.env` не нужно.
|
|
306
|
-
>
|
|
307
|
-
> **NODE_EASY_APPLY_MAIN обязателен** для подачи заявок (`npm run apply`). Бот использует AI для заполнения анкет.
|
|
308
|
-
>
|
|
309
|
-
> Мониторинг сообщений (`npm run sync-all`) работает без iLiveMyLife.
|
|
310
|
-
|
|
311
|
-
### Путь к данным (опционально)
|
|
312
|
-
|
|
313
|
-
| Переменная | Описание |
|
|
314
|
-
|---|---|
|
|
315
|
-
| `DATA_DIR` | Папка для хранения данных. По умолчанию — рядом с кодом |
|
|
316
|
-
|
|
317
|
-
Если хочешь хранить данные отдельно от кода (удобнее при обновлениях):
|
|
318
|
-
```ini
|
|
319
|
-
DATA_DIR=D:\MyLinkedInData
|
|
320
|
-
```
|
|
321
|
-
Бот создаст `collected-profiles/` и `market-research/` внутри этой папки.
|
|
322
|
-
|
|
323
|
-
> **С `DATA_DIR` обновление проще:** распаковал новый zip → `npm install` → готово. Данные остались на месте.
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## Где хранятся данные
|
|
328
|
-
|
|
329
|
-
Все данные в папке `collected-profiles/` (создаётся автоматически):
|
|
330
|
-
|
|
331
|
-
| Файл | Что внутри |
|
|
332
|
-
|------|-----------|
|
|
333
|
-
| `conversations.json` | Все переписки (сообщения, статусы, теги) |
|
|
334
|
-
| `people.json` | Все контакты (имена, должности, компании) |
|
|
335
|
-
| `jobs.json` | Поданные заявки |
|
|
336
|
-
| `sync-state.json` | Когда последний раз синхронизировались |
|
|
337
|
-
|
|
338
|
-
> **Бэкапы создаются автоматически** (`.backup` файлы). Если основной файл повредится — программа восстановит из бэкапа.
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
## Решение проблем
|
|
343
|
-
|
|
344
|
-
| Ошибка | Что делать |
|
|
345
|
-
|---|---|
|
|
346
|
-
| `No iLiveMyLife token found` | Запусти `ilml login` (Шаг 5б) |
|
|
347
|
-
| `NODE_EASY_APPLY_MAIN is not configured` | Создай узел с тегом `assist` и добавь ID в `.env` (Шаг 5б) |
|
|
348
|
-
| `LINKEDIN_NAME is not set in .env` | Открой `.env` и добавь `LINKEDIN_NAME=Твоё Имя` |
|
|
349
|
-
| `cookies.json not found` | Запусти `npm run login` |
|
|
350
|
-
| `cookies expired` / `Redirected to login` | Запусти `npm run login` заново |
|
|
351
|
-
| `cookies.json is corrupted` | Удали `cookies.json` и запусти `npm run login` |
|
|
352
|
-
| `Schema migration: v0 → v1` | Нормально! При обновлении данные автоматически приводятся к новому формату |
|
|
353
|
-
| `Lifebot timeout` | AI перегружен. Бот повторит 2 раза, потом пропустит вакансию. При 3 подряд — остановится, попробуй позже |
|
|
354
|
-
| `npm` не найден | Установи Node.js (Шаг 1) |
|
|
355
|
-
| Браузер не закрылся | Закрой его вручную (или нажми Ctrl+C в терминале) |
|
|
356
|
-
| `Cannot find module` | Запусти `npm install` |
|
|
357
|
-
| Данные пропали | Проверь папку `collected-profiles/` — там должны быть `.backup` файлы |
|
|
358
|
-
|
|
359
|
-
### Если ничего не помогает
|
|
360
|
-
|
|
361
|
-
1. Удали `cookies.json`
|
|
362
|
-
2. Запусти `npm run login`
|
|
363
|
-
3. Попробуй команду заново
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Дополнительные команды
|
|
368
|
-
|
|
369
|
-
Полный список — в файле `COMMANDS.md`.
|
|
1
|
+
# ilml-plugin-linkedin
|
|
2
|
+
|
|
3
|
+
LinkedIn job-search and networking automation as a plugin for the [`ilml`](https://www.npmjs.com/package/@ilivemylife/graph-sdk) CLI.
|
|
4
|
+
|
|
5
|
+
- **npm:** https://www.npmjs.com/package/ilml-plugin-linkedin
|
|
6
|
+
- **Tracker / Releases (in iLiveMyLife):** open the **Releases** node inside the **LinkedIn Automation** project in your iLiveMyLife app
|
|
7
|
+
- **License:** UNLICENSED — all rights reserved
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
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:
|
|
14
|
+
|
|
15
|
+
- **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.
|
|
16
|
+
- **Inbox sync** — scans LinkedIn messages, detects changes, and mirrors threads into your graph.
|
|
17
|
+
- **Message bot** — draft → review → send batch messaging workflow (you approve before anything is sent).
|
|
18
|
+
- **Connection funnel** — process recruiter / founder / investor profiles in batches (connect, skip, message).
|
|
19
|
+
- **Scout** — find jobs without applying; reports back which match your filters.
|
|
20
|
+
- **Profile activity** — track who viewed your profile, who you visited, etc.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# User Guide
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- **Node.js 18+** — [download](https://nodejs.org/) (the Long-Term Support version is fine).
|
|
31
|
+
- **iLiveMyLife account** — free signup at [iLiveMyLife.io](https://ilivemylife.io).
|
|
32
|
+
- **LinkedIn account** — your personal one.
|
|
33
|
+
|
|
34
|
+
That's it. There's nothing to clone or compile.
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
One-time, on your machine:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 1. Install the ilml CLI (skip if already installed)
|
|
42
|
+
npm install -g @ilivemylife/graph-sdk
|
|
43
|
+
|
|
44
|
+
# 2. Log in to your iLiveMyLife account
|
|
45
|
+
ilml login
|
|
46
|
+
|
|
47
|
+
# 3. Install this plugin
|
|
48
|
+
ilml plugin install linkedin
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
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).
|
|
52
|
+
|
|
53
|
+
Verify it's installed:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ilml plugin list
|
|
57
|
+
ilml linkedin
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Configure
|
|
61
|
+
|
|
62
|
+
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:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ilml plugin config linkedin set LINKEDIN_NAME "Your Full Name"
|
|
66
|
+
ilml plugin config linkedin set NODE_EASY_APPLY_MAIN <node-id>
|
|
67
|
+
ilml plugin config linkedin set NODE_EASY_APPLY_ERRORS <node-id>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
To inspect (secret values are masked):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
ilml plugin config linkedin
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To change a value later or remove one:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
ilml plugin config linkedin set LINKEDIN_NAME "New Name"
|
|
80
|
+
ilml plugin config linkedin unset OPTIONAL_KEY
|
|
81
|
+
ilml plugin config linkedin reset # wipe all; will re-prompt next run
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Configuration fields
|
|
85
|
+
|
|
86
|
+
| Section | Field | Required | What it is |
|
|
87
|
+
|---|---|---|---|
|
|
88
|
+
| LinkedIn account | `LINKEDIN_NAME` | yes | Your full name **exactly** as it appears on LinkedIn (used to identify your own messages in threads). |
|
|
89
|
+
| | `LINKEDIN_LOGIN` | no | LinkedIn email — only used for auto-fill during one-time login. |
|
|
90
|
+
| | `LINKEDIN_PASS` | no, secret | LinkedIn password — only used for auto-fill during one-time login. Stored locally on disk; never sent anywhere. |
|
|
91
|
+
| iLiveMyLife integration | `NODE_EASY_APPLY_MAIN` | yes | Node ID where job applications are tracked. See "Setting up your graph" below. |
|
|
92
|
+
| | `NODE_EASY_APPLY_ERRORS` | yes | Node ID where Easy Apply errors are logged. |
|
|
93
|
+
| | `NODE_RUN_REPORTS` | no | Node ID where end-of-session summaries are posted (enable notifications on it to get pings when long jobs finish). |
|
|
94
|
+
| 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. |
|
|
95
|
+
| Apply bot | `City` | no | Default city for filters. Default: `Toronto`. |
|
|
96
|
+
| | `MAX_APPLY_FOR_RUN` | no | Stop after this many applications per run. Default: `50`. |
|
|
97
|
+
| | `LINKEDIN_SEARCH_URL` | no | Default LinkedIn jobs search URL with your filters (use `f_AL=true` for Easy Apply). |
|
|
98
|
+
| Scouting | `LINKEDIN_SCOUT_URLS` | no | Comma-separated job search URLs to scan without applying. |
|
|
99
|
+
| Resume / CV | `CV_TEXT`, `RESUME_PDF_URL`, `RESUME_FILENAME` | no | CV snippet, public PDF URL, suggested filename. Used in messages to recruiters. |
|
|
100
|
+
| Funnel | `FUNNEL_USE_CUSTOM_MESSAGE` | no | Send a custom note with connection requests. Default: `false`. |
|
|
101
|
+
| | `FUNNEL_MAX_CONNECTIONS` | no | Cap connection requests per run. Default: `20`. |
|
|
102
|
+
| | `FUNNEL_MESSAGE` | no | Note text. Use `{name}` placeholder, e.g. `Hi {name}, I just applied for a role at your company`. |
|
|
103
|
+
| | `FUNNEL_EXCLUDE_LOCATIONS` | no | Comma-separated locations to skip. |
|
|
104
|
+
|
|
105
|
+
## Setting up your graph
|
|
106
|
+
|
|
107
|
+
The Easy Apply bot uses iLiveMyLife's AI assistant (**Lifebot**) to answer custom application questions in your voice. Two graph nodes are required: a **main tracking node** with Lifebot enabled, and an **errors node** for failed applications.
|
|
108
|
+
|
|
109
|
+
1. Open https://app.ilivemylife.io and create a node titled **"LinkedIn Easy Apply"** (or any name).
|
|
110
|
+
2. Open the node's **settings** (right panel) and turn on **AI assistant**. This enables Lifebot for this node — without it, the bot can't answer questions and `ilml linkedin apply` will refuse to run.
|
|
111
|
+
3. Copy the node ID from the URL: `https://app.ilivemylife.io/item/THIS-IS-THE-ID`.
|
|
112
|
+
4. Set it: `ilml plugin config linkedin set NODE_EASY_APPLY_MAIN <id>`.
|
|
113
|
+
5. In the node's **description**, write your answer rules — Lifebot reads this when filling forms. Example:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Application question rules:
|
|
117
|
+
- My full name: Ivan Petrov
|
|
118
|
+
- City: Toronto, Ontario, Canada
|
|
119
|
+
- Phone: +1-416-xxx-xxxx
|
|
120
|
+
- Email: ivan@example.com
|
|
121
|
+
- Experience: Senior software engineer, 8 years
|
|
122
|
+
- Visa status: Permanent Resident, Canada
|
|
123
|
+
- Salary expectations: $120-150K CAD
|
|
124
|
+
- Notice period: 14 days
|
|
125
|
+
- Preferred work mode: hybrid or remote
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
6. (Optional but useful) Create child nodes inside "LinkedIn Easy Apply" with topical detail: `Resume`, `Tech stack`, `Interview availability`, etc. Lifebot reads the whole subtree and uses everything as context.
|
|
129
|
+
|
|
130
|
+
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>`.
|
|
131
|
+
|
|
132
|
+
## First-time LinkedIn login
|
|
133
|
+
|
|
134
|
+
LinkedIn requires an authenticated session, so a one-time browser login is needed. Run:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
ilml linkedin login
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
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.
|
|
141
|
+
|
|
142
|
+
## Daily commands
|
|
143
|
+
|
|
144
|
+
| Command | What it does |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `ilml linkedin apply` | Auto-apply to LinkedIn Easy Apply jobs. Stops after `MAX_APPLY_FOR_RUN`. |
|
|
147
|
+
| `ilml linkedin sync` / `sync-all` | Pull the LinkedIn inbox into the local DB and the graph. |
|
|
148
|
+
| `ilml linkedin today` | Faster sync of just today's activity. |
|
|
149
|
+
| `ilml linkedin enrich` | Categorize and tag conversations. |
|
|
150
|
+
| `ilml linkedin report` | Summary of the latest session. |
|
|
151
|
+
| `ilml linkedin scout` | Scan jobs against `LINKEDIN_SCOUT_URLS` without applying. |
|
|
152
|
+
| `ilml linkedin messages` | Batch messaging — draft, review, then push. Nothing sends without your approval. |
|
|
153
|
+
| `ilml linkedin funnel` | Process a queue of recruiter / founder / investor profiles. |
|
|
154
|
+
| `ilml linkedin visit` | Track profile visits. |
|
|
155
|
+
| `ilml linkedin viewers` | List who viewed your profile. |
|
|
156
|
+
| `ilml linkedin daily` | Compound routine combining several commands. |
|
|
157
|
+
| `ilml linkedin login` | Re-save LinkedIn cookies after a session expires. |
|
|
158
|
+
|
|
159
|
+
Run `ilml linkedin` (no subcommand) to see the list anytime.
|
|
160
|
+
|
|
161
|
+
## Updating
|
|
162
|
+
|
|
163
|
+
Updates ship via npm. The CLI checks the registry and only downloads when there's a newer version:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
ilml plugin update linkedin # check this plugin
|
|
167
|
+
ilml plugin update # check all installed plugins
|
|
168
|
+
ilml update # update the SDK and all plugins together
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
You'll see one of:
|
|
172
|
+
- `linkedin: already on latest version (1.2.1)` — nothing downloaded
|
|
173
|
+
- `linkedin: 1.2.1 → 1.3.0` followed by re-install — newer version pulled
|
|
174
|
+
|
|
175
|
+
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.
|
|
176
|
+
|
|
177
|
+
## Where data lives
|
|
178
|
+
|
|
179
|
+
| Path | What's there | Survives plugin update? | Cleared by `ilml logout`? |
|
|
180
|
+
|---|---|---|---|
|
|
181
|
+
| `~/.ilivemylife/plugins/linkedin/` | Plugin code (re-extracted on update) | Replaced | No |
|
|
182
|
+
| `~/.ilivemylife/plugins-state/linkedin.json` (or per-project local) | Your configured values | Yes | Yes (with that scope) |
|
|
183
|
+
| Directory you set as `DATA_DIR` | Local DBs, cookies, scraped profiles | Yes | No (you manage it) |
|
|
184
|
+
|
|
185
|
+
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.
|
|
186
|
+
|
|
187
|
+
## Removing the plugin
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
ilml plugin remove linkedin
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This wipes the install dir. To also clear your saved configuration in the active scope:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
ilml plugin config linkedin reset
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
To clean up the data directory (cookies, DBs), delete the folder you set as `DATA_DIR` manually.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
# Developer Notes
|
|
204
|
+
|
|
205
|
+
> Single-developer project. The reference for plugin architecture and SDK contracts is the iLiveMyLife graph-sdk repo's [`PLUGINS.md`](https://github.com/iLiveMyLife/graph-sdk.iLiveMyLife.io/blob/main/PLUGINS.md). Most of what follows is just reminders for me when I come back to this in a few months.
|
|
206
|
+
|
|
207
|
+
## Layout
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
ilml-plugin.json # manifest: name, version, commands, config.sections[].fields[]
|
|
211
|
+
package.json # npm metadata; "files": ["dist", "ilml-plugin.json"] is the publish whitelist
|
|
212
|
+
.npmignore # belt-and-suspenders excludes (.env, cookies.json, sources, etc.)
|
|
213
|
+
.env / .env.example # dev only; gitignored
|
|
214
|
+
|
|
215
|
+
run.mjs / syncAll.mjs / messageBot.mjs / scout.mjs / ... # entry points (one per ilml subcommand)
|
|
216
|
+
src/
|
|
217
|
+
config.mjs # reads via SDK getPluginConfig(); merges into process.env so the rest of the codebase keeps using process.env.X
|
|
218
|
+
graphClient.mjs # SDK client wrapper
|
|
219
|
+
database.mjs # local JSON DB at DATA_DIR
|
|
220
|
+
saveLoginState.mjs # entry: ilml linkedin login
|
|
221
|
+
... domain modules ...
|
|
222
|
+
scripts/
|
|
223
|
+
build-plugin.mjs # esbuild bundler — outputs dist/*.mjs (minified, no sourcemaps)
|
|
224
|
+
# externalizes: puppeteer, dotenv, @ilivemylife/graph-sdk
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Configuration model
|
|
228
|
+
|
|
229
|
+
- The plugin reads its config **only** through `getPluginConfig()` from `@ilivemylife/graph-sdk` (called once in `src/config.mjs`).
|
|
230
|
+
- Resolved values are merged into `process.env` so existing files reading `process.env.LINKEDIN_NAME` etc. keep working unchanged. New code should read directly from the SDK return value when feasible.
|
|
231
|
+
- Auth token: `process.env.ILML_TOKEN` — injected by ilml CLI in production; resolved by the SDK in dev (local config / `.env` / global config / env var).
|
|
232
|
+
- Active scope dir: `process.env.ILML_SCOPE_DIR` — set in production only. Plugin code never reads it directly; the SDK does.
|
|
233
|
+
|
|
234
|
+
## Two run modes
|
|
235
|
+
|
|
236
|
+
| Mode | How invoked | Where config comes from |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| **Production** | `ilml linkedin <command>` | The active ilml scope's `<scope>/.ilivemylife/plugins-state/linkedin.json` |
|
|
239
|
+
| **Dev** | `node run.mjs` (etc.) | `.env` next to `ilml-plugin.json` (auto-detected by SDK) |
|
|
240
|
+
|
|
241
|
+
The plugin code is identical between the two. `ILML_SCOPE_DIR`'s presence is the only signal.
|
|
242
|
+
|
|
243
|
+
## Local dev iteration
|
|
244
|
+
|
|
245
|
+
When the plugin code itself is what's changing:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# 1. (optional) edit ilml-plugin.json or src/*
|
|
249
|
+
# 2. Build
|
|
250
|
+
npm run build:plugin
|
|
251
|
+
|
|
252
|
+
# 3a. Quick check via direct run (uses .env)
|
|
253
|
+
node run.mjs
|
|
254
|
+
|
|
255
|
+
# 3b. Full check via ilml CLI — needs the plugin re-packed and re-installed
|
|
256
|
+
npm pack
|
|
257
|
+
ilml plugin install linkedin file:///$(pwd)/ilml-plugin-linkedin-X.Y.Z.tgz
|
|
258
|
+
ilml linkedin <command>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
When the **SDK** is what's changing alongside, also rebuild and re-install the SDK from the sibling `graph-sdk.iLiveMyLife.io` repo:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# In ../graph-sdk.iLiveMyLife.io
|
|
265
|
+
npm run build && npm pack
|
|
266
|
+
|
|
267
|
+
# In this repo
|
|
268
|
+
npm install ../graph-sdk.iLiveMyLife.io/ilivemylife-graph-sdk-X.Y.Z.tgz --no-save
|
|
269
|
+
npm run build:plugin
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Release process
|
|
273
|
+
|
|
274
|
+
1. **Update `CHANGELOG.md`** — add an entry at the top describing what changed for users.
|
|
275
|
+
2. **Bump version in BOTH** `package.json#version` AND `ilml-plugin.json#version`. They must match — `ilml plugin update` compares the manifest version against npm's `dist-tags.latest`. Drift here causes false update loops or silent skips.
|
|
276
|
+
3. **Commit with explicit paths only:**
|
|
277
|
+
```bash
|
|
278
|
+
git commit -- package.json ilml-plugin.json CHANGELOG.md <other-changed-files> -m "v1.X.Y: short description"
|
|
279
|
+
```
|
|
280
|
+
4. **Push:** `git push`.
|
|
281
|
+
5. **Publish:** `npm publish --access public`. The `prepublishOnly` script runs `build:plugin` automatically — `dist/` is rebuilt from current sources before packing.
|
|
282
|
+
6. **Update the Releases node in the graph** — sync description with the new CHANGELOG entry.
|
|
283
|
+
|
|
284
|
+
## Security checklist before each `npm publish`
|
|
285
|
+
|
|
286
|
+
- `npm pack --dry-run` — verify only `dist/`, `ilml-plugin.json`, `package.json`, `README.md` end up in the tarball (~13 files). No `.env`, `cookies.json`, `*.json` data files, source `.mjs` at the repo root, `node_modules/`, internal handoff `.md`, `tmpclaude-*`, `.git/`.
|
|
287
|
+
- `grep -E "ilya|sorokin|password|token|@gmail" dist/*.mjs dist/src/*.mjs` — no real personal data should appear in bundled output. (Examples in `README.md` like `ivan@example.com` are fine — they're documentation placeholders.)
|
|
288
|
+
- `ls dist/*.map dist/**/*.map 2>/dev/null` — must return nothing (no sourcemaps).
|
|
289
|
+
- `npm whoami` — confirm correct npm account before publish.
|
|
290
|
+
|
|
291
|
+
## Useful pointers
|
|
292
|
+
|
|
293
|
+
- iLiveMyLife graph-sdk Plugin spec & 12 architecture rules: [`PLUGINS.md`](https://github.com/iLiveMyLife/graph-sdk.iLiveMyLife.io/blob/main/PLUGINS.md)
|
|
294
|
+
- SDK source: https://github.com/iLiveMyLife/graph-sdk.iLiveMyLife.io
|
|
295
|
+
- This plugin on npm: https://www.npmjs.com/package/ilml-plugin-linkedin
|
|
296
|
+
- ilml CLI on npm: https://www.npmjs.com/package/@ilivemylife/graph-sdk
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Author
|
|
301
|
+
|
|
302
|
+
Ilya Sorokin · [iLiveMyLife.io](https://ilivemylife.io) · info@ilivemylife.io
|
package/ilml-plugin.json
CHANGED