lhremote 0.1.0 → 0.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 ADDED
@@ -0,0 +1,564 @@
1
+ # lhremote: LinkedHelper Automation Toolkit
2
+
3
+ [![CI](https://github.com/alexey-pelykh/lhremote/actions/workflows/ci.yml/badge.svg)](https://github.com/alexey-pelykh/lhremote/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/alexey-pelykh/lhremote/graph/badge.svg)](https://codecov.io/gh/alexey-pelykh/lhremote)
5
+ [![npm version](https://img.shields.io/npm/v/lhremote?logo=npm)](https://www.npmjs.com/package/lhremote)
6
+ [![npm downloads](https://img.shields.io/npm/dm/lhremote?logo=npm)](https://www.npmjs.com/package/lhremote)
7
+ [![GitHub Repo stars](https://img.shields.io/github/stars/alexey-pelykh/lhremote?style=flat&logo=github)](https://github.com/alexey-pelykh/lhremote)
8
+ [![License](https://img.shields.io/github/license/alexey-pelykh/lhremote)](LICENSE)
9
+
10
+ CLI and MCP server for [LinkedHelper](https://linkedhelper.com) automation.
11
+
12
+ This project is brought to you by [Alexey Pelykh](https://github.com/alexey-pelykh).
13
+
14
+ ## What It Does
15
+
16
+ lhremote lets AI assistants (Claude, etc.) control LinkedHelper through the [Model Context Protocol](https://modelcontextprotocol.io). It can:
17
+
18
+ - **App management** — detect, launch, and quit LinkedHelper instances
19
+ - **Account & instance control** — list accounts, start/stop instances, check status
20
+ - **Campaign automation** — create, configure, start, stop, and monitor campaigns with full action-chain management
21
+ - **People import** — import LinkedIn profile URLs into campaign target lists
22
+ - **Profile queries** — look up and search cached LinkedIn profiles from the local database
23
+ - **Messaging** — query messaging history, check for new replies, scrape conversations from LinkedIn
24
+ - **Action discovery** — list available LinkedHelper action types with configuration schemas
25
+
26
+ **New to lhremote?** Check out the [Getting Started guide](docs/getting-started.md) for a step-by-step walkthrough.
27
+
28
+ ## Prerequisites
29
+
30
+ - **Node.js** >= 24
31
+ - **LinkedHelper** desktop application (requires a paid subscription)
32
+
33
+ ## Installation
34
+
35
+ ```sh
36
+ npm install -g lhremote
37
+ ```
38
+
39
+ Or run directly with npx:
40
+
41
+ ```sh
42
+ npx lhremote --help
43
+ ```
44
+
45
+ ## Usage with Claude Desktop
46
+
47
+ Add to your Claude Desktop configuration (`claude_desktop_config.json`):
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "lhremote": {
53
+ "command": "npx",
54
+ "args": ["lhremote", "mcp"]
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ Once configured, Claude can use all 32 tools directly. A typical workflow:
61
+
62
+ 1. **`find-app`** — Detect a running LinkedHelper instance (or **`launch-app`** to start one)
63
+ 2. **`list-accounts`** — See available LinkedIn accounts
64
+ 3. **`start-instance`** — Start an instance for an account
65
+ 4. **`describe-actions`** — Explore available action types
66
+ 5. **`campaign-create`** — Create a campaign from YAML/JSON configuration
67
+ 6. **`import-people-from-urls`** — Import target LinkedIn profiles into the campaign
68
+ 7. **`campaign-start`** — Run the campaign
69
+ 8. **`campaign-status`** / **`campaign-statistics`** — Monitor progress
70
+ 9. **`query-messages`** / **`check-replies`** — Review messaging results
71
+
72
+ ## CLI Usage
73
+
74
+ The `lhremote` command provides the same functionality as the MCP server. Every MCP tool has a corresponding CLI command.
75
+
76
+ ### App Management
77
+
78
+ ```sh
79
+ lhremote find-app [--json]
80
+ lhremote launch-app [--cdp-port <port>]
81
+ lhremote quit-app [--cdp-port <port>]
82
+ ```
83
+
84
+ ### Account & Instance
85
+
86
+ ```sh
87
+ lhremote list-accounts [--cdp-port <port>] [--json]
88
+ lhremote start-instance <accountId> [--cdp-port <port>]
89
+ lhremote stop-instance <accountId> [--cdp-port <port>]
90
+ lhremote check-status [--cdp-port <port>] [--json]
91
+ ```
92
+
93
+ ### Campaigns
94
+
95
+ ```sh
96
+ lhremote campaign-list [--include-archived] [--json]
97
+ lhremote campaign-create --file <path> | --yaml <config> | --json-input <config> [--cdp-port <port>] [--json]
98
+ lhremote campaign-get <campaignId> [--cdp-port <port>] [--json]
99
+ lhremote campaign-export <campaignId> [--format yaml|json] [--output <path>] [--cdp-port <port>]
100
+ lhremote campaign-update <campaignId> [--name <name>] [--description <text>] [--clear-description] [--cdp-port <port>] [--json]
101
+ lhremote campaign-delete <campaignId> [--cdp-port <port>] [--json]
102
+ lhremote campaign-start <campaignId> [--person-ids <ids>] [--person-ids-file <path>] [--cdp-port <port>] [--json]
103
+ lhremote campaign-stop <campaignId> [--cdp-port <port>] [--json]
104
+ lhremote campaign-status <campaignId> [--include-results] [--limit <n>] [--cdp-port <port>] [--json]
105
+ lhremote campaign-statistics <campaignId> [--action-id <id>] [--max-errors <n>] [--cdp-port <port>] [--json]
106
+ lhremote campaign-retry <campaignId> [--person-ids <ids>] [--person-ids-file <path>] [--cdp-port <port>] [--json]
107
+ ```
108
+
109
+ ### Campaign Actions
110
+
111
+ ```sh
112
+ lhremote campaign-add-action <campaignId> --name <name> --action-type <type> [--description <text>] [--cool-down <ms>] [--max-results <n>] [--action-settings <json>] [--cdp-port <port>] [--json]
113
+ lhremote campaign-remove-action <campaignId> <actionId> [--cdp-port <port>] [--json]
114
+ lhremote campaign-reorder-actions <campaignId> --action-ids <ids> [--cdp-port <port>] [--json]
115
+ lhremote campaign-move-next <campaignId> <actionId> [--person-ids <ids>] [--person-ids-file <path>] [--cdp-port <port>] [--json]
116
+ ```
117
+
118
+ ### Campaign Targeting
119
+
120
+ ```sh
121
+ lhremote campaign-exclude-list <campaignId> [--action-id <id>] [--cdp-port <port>] [--json]
122
+ lhremote campaign-exclude-add <campaignId> --person-ids <ids> | --person-ids-file <path> [--action-id <id>] [--cdp-port <port>] [--json]
123
+ lhremote campaign-exclude-remove <campaignId> --person-ids <ids> | --person-ids-file <path> [--action-id <id>] [--cdp-port <port>] [--json]
124
+ lhremote import-people-from-urls <campaignId> --urls <urls> | --urls-file <path> [--cdp-port <port>] [--json]
125
+ ```
126
+
127
+ ### Profiles & Messaging
128
+
129
+ ```sh
130
+ lhremote query-profile --person-id <id> | --public-id <slug> [--json]
131
+ lhremote query-profiles [--query <text>] [--company <name>] [--limit <n>] [--offset <n>] [--json]
132
+ lhremote query-messages [--person-id <id>] [--chat-id <id>] [--search <text>] [--limit <n>] [--offset <n>] [--json]
133
+ lhremote check-replies [--since <timestamp>] [--cdp-port <port>] [--json]
134
+ lhremote scrape-messaging-history [--cdp-port <port>] [--json]
135
+ ```
136
+
137
+ ### Utilities
138
+
139
+ ```sh
140
+ lhremote describe-actions [--category <category>] [--type <type>] [--json]
141
+ ```
142
+
143
+ ## MCP Tools
144
+
145
+ ### Common Parameters
146
+
147
+ Most tools and CLI commands connect to LinkedHelper via the Chrome DevTools Protocol (CDP). In addition to the tool-specific parameters listed below, all CDP-connected tools accept:
148
+
149
+ | Parameter | CLI Flag | Type | Default | Description |
150
+ |-----------|----------|------|---------|-------------|
151
+ | `cdpPort` | `--cdp-port` | number | 9222 | CDP debugging port |
152
+ | `cdpHost` | `--cdp-host` | string | `127.0.0.1` | CDP host address |
153
+ | `allowRemote` | `--allow-remote` | boolean | false | Allow connections to non-loopback addresses |
154
+
155
+ > **Security warning:** Enabling `allowRemote` permits CDP connections to remote hosts. CDP is an unsandboxed protocol that grants full control over the target browser — equivalent to remote code execution. Only enable this when the network path between your machine and the target host is fully secured (e.g., SSH tunnel, VPN, or trusted LAN).
156
+
157
+ ### App Management
158
+
159
+ #### `find-app`
160
+
161
+ Detect running LinkedHelper application instances and their CDP connection details.
162
+
163
+ *No parameters.*
164
+
165
+ #### `launch-app`
166
+
167
+ Launch the LinkedHelper application with remote debugging enabled.
168
+
169
+ | Parameter | Type | Required | Default | Description |
170
+ |-----------|------|----------|---------|-------------|
171
+ | `cdpPort` | number | No | auto-select | CDP port to use |
172
+
173
+ #### `quit-app`
174
+
175
+ Quit the LinkedHelper application.
176
+
177
+ | Parameter | Type | Required | Default | Description |
178
+ |-----------|------|----------|---------|-------------|
179
+ | `cdpPort` | number | No | 9222 | CDP port |
180
+
181
+ ### Account & Instance
182
+
183
+ #### `list-accounts`
184
+
185
+ List available LinkedHelper accounts. Returns account ID, LinkedIn ID, name, and email for each account.
186
+
187
+ | Parameter | Type | Required | Default | Description |
188
+ |-----------|------|----------|---------|-------------|
189
+ | `cdpPort` | number | No | 9222 | CDP port |
190
+
191
+ #### `start-instance`
192
+
193
+ Start a LinkedHelper instance for a LinkedIn account.
194
+
195
+ | Parameter | Type | Required | Default | Description |
196
+ |-----------|------|----------|---------|-------------|
197
+ | `accountId` | number | No | auto-select if single account | Account ID |
198
+ | `cdpPort` | number | No | 9222 | CDP port |
199
+
200
+ #### `stop-instance`
201
+
202
+ Stop a running LinkedHelper instance.
203
+
204
+ | Parameter | Type | Required | Default | Description |
205
+ |-----------|------|----------|---------|-------------|
206
+ | `accountId` | number | No | auto-select if single account | Account ID |
207
+ | `cdpPort` | number | No | 9222 | CDP port |
208
+
209
+ #### `check-status`
210
+
211
+ Check LinkedHelper connection status, running instances, and database health.
212
+
213
+ | Parameter | Type | Required | Default | Description |
214
+ |-----------|------|----------|---------|-------------|
215
+ | `cdpPort` | number | No | 9222 | CDP port |
216
+
217
+ ### Campaigns
218
+
219
+ #### `campaign-list`
220
+
221
+ List existing campaigns with summary statistics.
222
+
223
+ | Parameter | Type | Required | Default | Description |
224
+ |-----------|------|----------|---------|-------------|
225
+ | `includeArchived` | boolean | No | false | Include archived campaigns |
226
+ | `cdpPort` | number | No | 9222 | CDP port |
227
+
228
+ #### `campaign-create`
229
+
230
+ Create a new campaign from YAML or JSON configuration. Provide exactly one of `yamlConfig` or `jsonConfig`.
231
+
232
+ | Parameter | Type | Required | Default | Description |
233
+ |-----------|------|----------|---------|-------------|
234
+ | `yamlConfig` | string | No | — | YAML campaign configuration |
235
+ | `jsonConfig` | string | No | — | JSON campaign configuration |
236
+ | `cdpPort` | number | No | 9222 | CDP port |
237
+
238
+ #### `campaign-get`
239
+
240
+ Get detailed campaign information including action chain.
241
+
242
+ | Parameter | Type | Required | Default | Description |
243
+ |-----------|------|----------|---------|-------------|
244
+ | `campaignId` | number | Yes | — | Campaign ID |
245
+ | `cdpPort` | number | No | 9222 | CDP port |
246
+
247
+ #### `campaign-export`
248
+
249
+ Export campaign configuration as YAML or JSON.
250
+
251
+ | Parameter | Type | Required | Default | Description |
252
+ |-----------|------|----------|---------|-------------|
253
+ | `campaignId` | number | Yes | — | Campaign ID |
254
+ | `format` | string | No | yaml | Export format (`yaml` or `json`) |
255
+ | `cdpPort` | number | No | 9222 | CDP port |
256
+
257
+ #### `campaign-update`
258
+
259
+ Update a campaign's name and/or description.
260
+
261
+ | Parameter | Type | Required | Default | Description |
262
+ |-----------|------|----------|---------|-------------|
263
+ | `campaignId` | number | Yes | — | Campaign ID |
264
+ | `name` | string | No | — | New campaign name |
265
+ | `description` | string | No | — | New description (empty string to clear) |
266
+ | `cdpPort` | number | No | 9222 | CDP port |
267
+
268
+ #### `campaign-delete`
269
+
270
+ Delete (archive) a campaign.
271
+
272
+ | Parameter | Type | Required | Default | Description |
273
+ |-----------|------|----------|---------|-------------|
274
+ | `campaignId` | number | Yes | — | Campaign ID |
275
+ | `cdpPort` | number | No | 9222 | CDP port |
276
+
277
+ #### `campaign-start`
278
+
279
+ Start a campaign with specified target persons.
280
+
281
+ | Parameter | Type | Required | Default | Description |
282
+ |-----------|------|----------|---------|-------------|
283
+ | `campaignId` | number | Yes | — | Campaign ID |
284
+ | `personIds` | number[] | No | — | Person IDs to target |
285
+ | `cdpPort` | number | No | 9222 | CDP port |
286
+
287
+ #### `campaign-stop`
288
+
289
+ Stop a running campaign.
290
+
291
+ | Parameter | Type | Required | Default | Description |
292
+ |-----------|------|----------|---------|-------------|
293
+ | `campaignId` | number | Yes | — | Campaign ID |
294
+ | `cdpPort` | number | No | 9222 | CDP port |
295
+
296
+ #### `campaign-status`
297
+
298
+ Check campaign execution status and results.
299
+
300
+ | Parameter | Type | Required | Default | Description |
301
+ |-----------|------|----------|---------|-------------|
302
+ | `campaignId` | number | Yes | — | Campaign ID |
303
+ | `includeResults` | boolean | No | false | Include execution results |
304
+ | `limit` | number | No | 20 | Max results to return |
305
+ | `cdpPort` | number | No | 9222 | CDP port |
306
+
307
+ #### `campaign-statistics`
308
+
309
+ Get per-action statistics for a campaign.
310
+
311
+ | Parameter | Type | Required | Default | Description |
312
+ |-----------|------|----------|---------|-------------|
313
+ | `campaignId` | number | Yes | — | Campaign ID |
314
+ | `actionId` | number | No | — | Filter to a specific action |
315
+ | `maxErrors` | number | No | 5 | Max top errors per action |
316
+ | `cdpPort` | number | No | 9222 | CDP port |
317
+
318
+ #### `campaign-retry`
319
+
320
+ Reset specified people for re-run in a campaign.
321
+
322
+ | Parameter | Type | Required | Default | Description |
323
+ |-----------|------|----------|---------|-------------|
324
+ | `campaignId` | number | Yes | — | Campaign ID |
325
+ | `personIds` | number[] | No | — | Person IDs to retry |
326
+ | `cdpPort` | number | No | 9222 | CDP port |
327
+
328
+ ### Campaign Actions
329
+
330
+ #### `campaign-add-action`
331
+
332
+ Add a new action to a campaign's action chain. Use `describe-actions` to explore available action types.
333
+
334
+ | Parameter | Type | Required | Default | Description |
335
+ |-----------|------|----------|---------|-------------|
336
+ | `campaignId` | number | Yes | — | Campaign ID |
337
+ | `name` | string | Yes | — | Display name for the action |
338
+ | `actionType` | string | Yes | — | Action type (e.g., `VisitAndExtract`, `MessageToPerson`) |
339
+ | `description` | string | No | — | Action description |
340
+ | `coolDown` | number | No | — | Milliseconds between executions |
341
+ | `maxResults` | number | No | — | Max results per iteration (-1 for unlimited) |
342
+ | `actionSettings` | object | No | — | Action-specific settings |
343
+ | `cdpPort` | number | No | 9222 | CDP port |
344
+
345
+ #### `campaign-remove-action`
346
+
347
+ Remove an action from a campaign's action chain.
348
+
349
+ | Parameter | Type | Required | Default | Description |
350
+ |-----------|------|----------|---------|-------------|
351
+ | `campaignId` | number | Yes | — | Campaign ID |
352
+ | `actionId` | number | Yes | — | Action ID to remove |
353
+ | `cdpPort` | number | No | 9222 | CDP port |
354
+
355
+ #### `campaign-reorder-actions`
356
+
357
+ Reorder actions in a campaign's action chain.
358
+
359
+ | Parameter | Type | Required | Default | Description |
360
+ |-----------|------|----------|---------|-------------|
361
+ | `campaignId` | number | Yes | — | Campaign ID |
362
+ | `actionIds` | number[] | Yes | — | Action IDs in desired order |
363
+ | `cdpPort` | number | No | 9222 | CDP port |
364
+
365
+ #### `campaign-move-next`
366
+
367
+ Move people from one action to the next in a campaign.
368
+
369
+ | Parameter | Type | Required | Default | Description |
370
+ |-----------|------|----------|---------|-------------|
371
+ | `campaignId` | number | Yes | — | Campaign ID |
372
+ | `actionId` | number | Yes | — | Action ID to move people from |
373
+ | `personIds` | number[] | No | — | Person IDs to move |
374
+ | `cdpPort` | number | No | 9222 | CDP port |
375
+
376
+ ### Campaign Targeting
377
+
378
+ #### `campaign-exclude-list`
379
+
380
+ View the exclude list for a campaign or action.
381
+
382
+ | Parameter | Type | Required | Default | Description |
383
+ |-----------|------|----------|---------|-------------|
384
+ | `campaignId` | number | Yes | — | Campaign ID |
385
+ | `actionId` | number | No | — | Action ID (for action-level list) |
386
+ | `cdpPort` | number | No | 9222 | CDP port |
387
+
388
+ #### `campaign-exclude-add`
389
+
390
+ Add people to a campaign or action exclude list.
391
+
392
+ | Parameter | Type | Required | Default | Description |
393
+ |-----------|------|----------|---------|-------------|
394
+ | `campaignId` | number | Yes | — | Campaign ID |
395
+ | `personIds` | number[] | Yes | — | Person IDs to exclude |
396
+ | `actionId` | number | No | — | Action ID (for action-level list) |
397
+ | `cdpPort` | number | No | 9222 | CDP port |
398
+
399
+ #### `campaign-exclude-remove`
400
+
401
+ Remove people from a campaign or action exclude list.
402
+
403
+ | Parameter | Type | Required | Default | Description |
404
+ |-----------|------|----------|---------|-------------|
405
+ | `campaignId` | number | Yes | — | Campaign ID |
406
+ | `personIds` | number[] | Yes | — | Person IDs to remove from exclude list |
407
+ | `actionId` | number | No | — | Action ID (for action-level list) |
408
+ | `cdpPort` | number | No | 9222 | CDP port |
409
+
410
+ #### `import-people-from-urls`
411
+
412
+ Import LinkedIn profile URLs into a campaign action target list. Idempotent — previously imported URLs are skipped.
413
+
414
+ | Parameter | Type | Required | Default | Description |
415
+ |-----------|------|----------|---------|-------------|
416
+ | `campaignId` | number | Yes | — | Campaign ID |
417
+ | `urls` | string[] | Yes | — | LinkedIn profile URLs |
418
+ | `cdpPort` | number | No | 9222 | CDP port |
419
+
420
+ ### Profiles & Messaging
421
+
422
+ #### `query-profile`
423
+
424
+ Look up a cached LinkedIn profile from the local database by person ID or public ID.
425
+
426
+ | Parameter | Type | Required | Default | Description |
427
+ |-----------|------|----------|---------|-------------|
428
+ | `personId` | number | No | — | Internal person ID |
429
+ | `publicId` | string | No | — | LinkedIn public ID (URL slug) |
430
+
431
+ #### `query-profiles`
432
+
433
+ Search for profiles in the local database with name, headline, or company filters.
434
+
435
+ | Parameter | Type | Required | Default | Description |
436
+ |-----------|------|----------|---------|-------------|
437
+ | `query` | string | No | — | Search name or headline |
438
+ | `company` | string | No | — | Filter by company |
439
+ | `limit` | number | No | 20 | Max results |
440
+ | `offset` | number | No | 0 | Pagination offset |
441
+
442
+ #### `query-messages`
443
+
444
+ Query messaging history from the local database.
445
+
446
+ | Parameter | Type | Required | Default | Description |
447
+ |-----------|------|----------|---------|-------------|
448
+ | `personId` | number | No | — | Filter by person ID |
449
+ | `chatId` | number | No | — | Show specific conversation thread |
450
+ | `search` | string | No | — | Search message text |
451
+ | `limit` | number | No | 20 | Max results |
452
+ | `offset` | number | No | 0 | Pagination offset |
453
+
454
+ #### `check-replies`
455
+
456
+ Check for new message replies from LinkedIn.
457
+
458
+ | Parameter | Type | Required | Default | Description |
459
+ |-----------|------|----------|---------|-------------|
460
+ | `since` | string | No | — | Only show replies after this ISO timestamp |
461
+ | `cdpPort` | number | No | 9222 | CDP port |
462
+
463
+ #### `scrape-messaging-history`
464
+
465
+ Scrape all messaging history from LinkedIn into the local database. This is a long-running operation that navigates LinkedIn's messaging interface.
466
+
467
+ | Parameter | Type | Required | Default | Description |
468
+ |-----------|------|----------|---------|-------------|
469
+ | `cdpPort` | number | No | 9222 | CDP port |
470
+
471
+ ### Utilities
472
+
473
+ #### `describe-actions`
474
+
475
+ List available LinkedHelper action types with descriptions and configuration schemas.
476
+
477
+ | Parameter | Type | Required | Default | Description |
478
+ |-----------|------|----------|---------|-------------|
479
+ | `category` | string | No | — | Filter by category (`people`, `messaging`, `engagement`, `crm`, `workflow`) |
480
+ | `actionType` | string | No | — | Get details for a specific action type |
481
+
482
+ ## Known Limitations
483
+
484
+ - **Platform support**: LinkedHelper runs on macOS, Windows, and Linux. Binary paths are detected automatically but can be overridden with the `LINKEDHELPER_PATH` environment variable.
485
+ - **Instance startup time**: Starting an instance loads LinkedIn, which may take up to 45 seconds.
486
+ - **Profile data is cached**: `query-profile` and `query-profiles` search the local LinkedHelper database. Profiles must have been visited or imported by LinkedHelper to appear in results.
487
+ - **Messaging scrape is slow**: `scrape-messaging-history` navigates LinkedIn's messaging UI and can take several minutes depending on conversation volume.
488
+ - **Same-machine requirement**: lhremote must run on the same machine as LinkedHelper. CDP connections are localhost-only by default (for security), and database access requires direct file system access to the LinkedHelper SQLite database.
489
+
490
+ ## Troubleshooting
491
+
492
+ ### LinkedHelper is not running
493
+
494
+ **Error**: `LinkedHelper is not running (no CDP endpoint at port 9222)`
495
+
496
+ **Solution**: Use `launch-app` to start LinkedHelper, or start it manually. lhremote communicates with LinkedHelper via the Chrome DevTools Protocol (CDP), which requires the application to be running.
497
+
498
+ ### Application binary not found
499
+
500
+ **Error**: `LinkedHelper application binary not found. Set LINKEDHELPER_PATH to override.`
501
+
502
+ **Solution**: Install LinkedHelper from [linkedhelper.com](https://linkedhelper.com). If installed in a non-standard location, set the `LINKEDHELPER_PATH` environment variable to the binary path.
503
+
504
+ ### No accounts found
505
+
506
+ **Error**: `No accounts found.`
507
+
508
+ **Solution**: Open LinkedHelper and configure at least one LinkedIn account before using lhremote.
509
+
510
+ ### Multiple accounts found
511
+
512
+ **Error**: `Multiple accounts found. Specify accountId. Use list-accounts to see available accounts.`
513
+
514
+ **Solution**: Use `list-accounts` to see available accounts, then pass the desired `accountId` to `start-instance`, `stop-instance`, or other tools.
515
+
516
+ ### No instance running
517
+
518
+ **Error**: `No LinkedHelper instance is running. Use start-instance first.`
519
+
520
+ **Solution**: Run `start-instance` before using campaign or messaging tools. An instance must be running to interact with LinkedIn.
521
+
522
+ ### Instance initialization timeout
523
+
524
+ **Error**: `Instance started but failed to initialize within timeout.`
525
+
526
+ **Solution**: The instance was started but took too long to finish loading. This can happen on slow connections. Try again; the instance may still be starting in the background. Use `check-status` to verify.
527
+
528
+ ### Database not found
529
+
530
+ **Error**: `No database found for account`
531
+
532
+ **Solution**: The LinkedHelper database file is missing for the specified account. Ensure the account has been used at least once in LinkedHelper so that a local database has been created.
533
+
534
+ ## Disclaimer
535
+
536
+ `lhremote` is an **independent project** not affiliated with, endorsed by, or officially connected to:
537
+
538
+ - **LinkedIn** or LinkedIn Corporation
539
+ - **LinkedHelper** or its parent company
540
+
541
+ LinkedIn is a trademark of LinkedIn Corporation. LinkedHelper is a trademark of its respective owner.
542
+
543
+ ## Purpose
544
+
545
+ This project enables **interoperability** between automation tools and LinkedHelper, as permitted under DMCA § 1201(f). Implementation is based on publicly observable behavior (Chrome DevTools Protocol) without access to protected source code.
546
+
547
+ ## What This Project Does NOT Do
548
+
549
+ - Circumvent copy protection or licensing
550
+ - Bypass LinkedHelper authentication
551
+ - Enable use without a valid LinkedHelper subscription
552
+ - Provide access to LinkedIn without LinkedHelper
553
+
554
+ ## User Responsibility
555
+
556
+ Use of `lhremote` requires a valid LinkedHelper subscription and is subject to LinkedHelper's and LinkedIn's terms of service. Users accept all responsibility for compliance.
557
+
558
+ ## Ethical Use
559
+
560
+ This tool is for **legitimate productivity**. Do NOT use for spam, scraping at scale, or harassment.
561
+
562
+ ## License
563
+
564
+ [AGPL-3.0-only](LICENSE) — For commercial licensing, contact the maintainer.
package/dist/cli.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+ // Copyright (C) 2026 Oleksii PELYKH
2
4
  import { createProgram } from "@lhremote/cli";
3
5
  import { runStdioServer } from "@lhremote/mcp/stdio";
4
6
  const program = createProgram();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAEhC,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,8DAA8D,CAAC;KAC3E,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;AAEhC,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,8DAA8D,CAAC;KAC3E,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/dist/cli.test.js CHANGED
@@ -1,3 +1,5 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
1
3
  import { describe, expect, it } from "vitest";
2
4
  import { createProgram } from "@lhremote/cli";
3
5
  describe("lhremote meta-package CLI", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../src/cli.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAE7B,iDAAiD;QACjD,OAAO;aACJ,OAAO,CAAC,KAAK,CAAC;aACd,WAAW,CACV,8DAA8D,CAC/D,CAAC;QAEJ,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAExD,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../src/cli.test.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAE7B,iDAAiD;QACjD,OAAO;aACJ,OAAO,CAAC,KAAK,CAAC;aACd,WAAW,CACV,8DAA8D,CAC/D,CAAC;QAEJ,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAExD,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,20 +1,27 @@
1
1
  {
2
2
  "name": "lhremote",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "LinkedHelper automation toolkit — CLI & MCP server",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=22"
7
+ "node": ">=24"
8
8
  },
9
9
  "license": "AGPL-3.0-only",
10
10
  "author": "Alexey Pelykh (https://github.com/alexey-pelykh)",
11
11
  "homepage": "https://github.com/alexey-pelykh/lhremote",
12
12
  "bugs": "https://github.com/alexey-pelykh/lhremote/issues",
13
+ "funding": "https://github.com/sponsors/alexey-pelykh",
13
14
  "repository": {
14
15
  "type": "git",
15
16
  "url": "https://github.com/alexey-pelykh/lhremote.git",
16
17
  "directory": "packages/lhremote"
17
18
  },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/cli.d.ts",
22
+ "import": "./dist/cli.js"
23
+ }
24
+ },
18
25
  "bin": {
19
26
  "lhremote": "./dist/cli.js"
20
27
  },
@@ -29,13 +36,13 @@
29
36
  "dist"
30
37
  ],
31
38
  "dependencies": {
32
- "@lhremote/cli": "^0.1.0",
33
- "@lhremote/mcp": "^0.1.0"
39
+ "@lhremote/cli": "^0.2.1",
40
+ "@lhremote/mcp": "^0.2.1"
34
41
  },
35
42
  "devDependencies": {
36
- "@types/node": "^22",
43
+ "@types/node": "^25",
37
44
  "eslint": "^9.39.2",
38
- "typescript": "^5.7.3",
45
+ "typescript": "^5.9.3",
39
46
  "vitest": "^4.0.18"
40
47
  },
41
48
  "scripts": {