diggylab 0.1.50 → 0.1.52
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 +228 -225
- package/cli.js +921 -134
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,225 +1,228 @@
|
|
|
1
|
-
# Desktop Agent CLI
|
|
2
|
-
|
|
3
|
-
The Desktop Agent also runs headless as a CLI worker, for customers who scan from a Linux server
|
|
4
|
-
instead of the desktop app. It pairs like any device, receives `DESKTOP` scan jobs, and scans with
|
|
5
|
-
headless Chromium — the Electron app is not involved.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
npm
|
|
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
|
-
- `socket closed:
|
|
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
|
-
docker
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
1
|
+
# Desktop Agent CLI
|
|
2
|
+
|
|
3
|
+
The Desktop Agent also runs headless as a CLI worker, for customers who scan from a Linux server
|
|
4
|
+
instead of the desktop app. It pairs like any device, receives `DESKTOP` scan jobs, and scans with
|
|
5
|
+
headless Chromium — the Electron app is not involved.
|
|
6
|
+
|
|
7
|
+
For the platform-run scan worker (admin-managed Proxmox/Linux nodes with one Facebook account and an
|
|
8
|
+
optional proxy), see [worker-agent.md](worker-agent.md).
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- Node.js 20+
|
|
13
|
+
- Chromium for Playwright: downloaded automatically during `npm install`; rerun `diggylab install-browser` if that download failed.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
From npm (customers):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g diggylab
|
|
21
|
+
diggylab --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
From the repository (development):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm install
|
|
28
|
+
pnpm --filter jobfinding-desktop-agent cli:build
|
|
29
|
+
node apps/desktop-agent/dist-cli/cli/cli.js --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
State lives in `~/.diggylab` (`--config <directory>` or `DIGGYLAB_AGENT_DIR` overrides it).
|
|
33
|
+
|
|
34
|
+
## Update
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
diggylab update
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
It asks npm for the newest release and upgrades the global install in place; when you are already
|
|
41
|
+
current it says so and changes nothing. `diggylab run` checks once a day in the background and
|
|
42
|
+
prints `A newer version is available: ...` when there is one, and every other command repeats the
|
|
43
|
+
last known notice for free — so an old worker does not go unnoticed. A systemd service needs no
|
|
44
|
+
reinstall — the unit points at npm's symlink, so restart it to switch versions (`systemctl restart
|
|
45
|
+
diggylab`).
|
|
46
|
+
|
|
47
|
+
## Publishing the npm package
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm --filter jobfinding-desktop-agent cli:pack
|
|
51
|
+
npm publish apps/desktop-agent/dist-npm --access public
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`cli:pack` bundles the workspace code into a single `cli.js`, so the published package only depends
|
|
55
|
+
on `playwright` and `ws`. The packed manifest copies the version from
|
|
56
|
+
`apps/desktop-agent/package.json`; use the override below when the app release has not caught up yet.
|
|
57
|
+
|
|
58
|
+
### Publishing ahead of an app release
|
|
59
|
+
|
|
60
|
+
To ship a CLI fix before the next app release — without running the Version Release workflow — pack a
|
|
61
|
+
specific version. The repo version stays where the release workflow expects it, and that workflow
|
|
62
|
+
later lands on the same number:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pnpm --filter jobfinding-desktop-agent cli:pack -- --version 0.1.31
|
|
66
|
+
npm publish apps/desktop-agent/dist-npm --access public
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`CLI_PACKAGE_VERSION=0.1.31` works as well.
|
|
70
|
+
|
|
71
|
+
## Local development
|
|
72
|
+
|
|
73
|
+
Run the CLI from source against the local API without touching a real installation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pnpm dev:cli # prints status against http://localhost:4000 and ~/.diggylab-dev
|
|
77
|
+
pnpm dev:cli pair # forwards any command to the CLI
|
|
78
|
+
pnpm dev:cli --build run # rebuilds dist-cli first, then runs the worker
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The wrapper points `DIGGYLAB_API_URL` at `http://localhost:4000` and `DIGGYLAB_AGENT_DIR` at
|
|
82
|
+
`~/.diggylab-dev`; set either variable to override the defaults.
|
|
83
|
+
|
|
84
|
+
## Commands
|
|
85
|
+
|
|
86
|
+
| Command | What it does |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `pair [--code <code>] [--name <device name>]` | Pairs the server as a device. Create the code on `/settings/desktop-agent` in the web app; leave `--code` out and the command asks for it. |
|
|
89
|
+
| `login` | Opens Facebook's real login page in a browser window when the machine has a screen. On a server without one it asks for `facebook.com` cookies pasted from your own browser, and the session is stored encrypted. |
|
|
90
|
+
| `logout` | Removes the stored Facebook session from this server (the device pairing stays). |
|
|
91
|
+
| `rename [--name <new name>]` | Renames the device shown in the web app; leave `--name` out and the command asks for it. |
|
|
92
|
+
| `run` | Connects the WebSocket worker, receives assigned scans, scans headlessly, uploads results. |
|
|
93
|
+
| `status` | Shows the config directory, API origin, paired device, and Facebook session state. |
|
|
94
|
+
| `install-browser` | Downloads the Chromium build Playwright needs (~150 MB). It also runs during `npm install`, so you only need it if that download failed. |
|
|
95
|
+
| `install-service` | On a Linux server with systemd, writes the unit for `run`, enables it, and starts it now and on every boot. |
|
|
96
|
+
| `uninstall-service` | Stops that service and removes the unit. |
|
|
97
|
+
| `unpair` | Removes device credentials, the Facebook session, and pending scans. |
|
|
98
|
+
|
|
99
|
+
Useful options: `--api <origin>` (default `https://api.diggylab.com`), `--cookies <file>` to import
|
|
100
|
+
cookies directly.
|
|
101
|
+
|
|
102
|
+
`pair` and `login` finish by printing what is still missing, so you do not need to run `status`
|
|
103
|
+
between steps:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Paired: "DiggyLab" (device-1)
|
|
107
|
+
Facebook: missing
|
|
108
|
+
Next: run `diggylab login` — on a server without a screen it asks you to paste cookies.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## How `login` works
|
|
112
|
+
|
|
113
|
+
Two paths, depending on the machine:
|
|
114
|
+
|
|
115
|
+
| Machine | What `login` does |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| Desktop, or Linux with a display (`$DISPLAY` set) | Opens Chromium on that display with Facebook's real login page; you log in normally. |
|
|
118
|
+
| Server without a screen | Prints a short prompt and asks you to paste `facebook.com` cookies exported from your own browser. |
|
|
119
|
+
|
|
120
|
+
Both paths save the session encrypted with `~/.diggylab/key`, and `diggylab run` then uses it for scans.
|
|
121
|
+
|
|
122
|
+
### Logging in with cookies from your own browser
|
|
123
|
+
|
|
124
|
+
On a machine without a screen this is the only path, and you can also pick it on a desktop with
|
|
125
|
+
`diggylab login cookies`:
|
|
126
|
+
|
|
127
|
+
1. Get the `facebook.com` cookies, in whichever way is easiest:
|
|
128
|
+
- **DevTools → Network** (fastest): open facebook.com, click any request, copy the whole
|
|
129
|
+
`cookie:` value from *Request Headers*.
|
|
130
|
+
- **DevTools → Application → Cookies → `https://www.facebook.com`**: select the table and copy the
|
|
131
|
+
rows.
|
|
132
|
+
- **Cookie-Editor** (Chrome, Edge, Firefox): *Export → JSON*.
|
|
133
|
+
2. On the server, run the friendly form and paste when asked (Enter finishes the paste):
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
diggylab login cookies
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The equivalents: `diggylab login --cookies 'name=value; ...'` for a quoted one-liner, or
|
|
140
|
+
`diggylab login --cookies cookies.json` for a saved file.
|
|
141
|
+
|
|
142
|
+
Only `name` and `value` are required; the agent fills in the rest (`.facebook.com`, `/`, session
|
|
143
|
+
expiry). Every `facebook.com` cookie is kept. The export must contain `c_user` (the agent rejects it
|
|
144
|
+
otherwise) and `xs`, which carries the session — without `xs` Facebook refuses the session during the
|
|
145
|
+
check, and `datr` is worth keeping too.
|
|
146
|
+
|
|
147
|
+
If you paste cookies, prefer the file form or `--cookies -` over `--cookies '...'`: a value on the
|
|
148
|
+
command line stays in your shell history, and pasted text stays in the terminal scrollback.
|
|
149
|
+
|
|
150
|
+
Do not use `document.cookie` from the Console: Facebook's `c_user` and `xs` are HttpOnly, so they
|
|
151
|
+
never appear there. The DevTools cookie table and the extension do show them.
|
|
152
|
+
|
|
153
|
+
The agent saves the cookies encrypted and opens Facebook once to confirm the session before it
|
|
154
|
+
accepts it. Cookies exported from a different account, or expired ones, are rejected with a message
|
|
155
|
+
instead of failing during a scan.
|
|
156
|
+
|
|
157
|
+
Note: Facebook sometimes asks for a security check when a session moves to a server's IP. If that
|
|
158
|
+
happens the command says so — approve the login from your phone and import again.
|
|
159
|
+
|
|
160
|
+
## Troubleshooting
|
|
161
|
+
|
|
162
|
+
- `socket closed: 1005` followed by `session: ONLINE` about every 14 minutes is **normal**: the access
|
|
163
|
+
token lives 15 minutes, so the worker refreshes it one minute early and reconnects with the new one
|
|
164
|
+
(the disconnect lasts a second or two, and a running scan keeps going).
|
|
165
|
+
- `socket closed: 4009` right after `session: ONLINE` means **two workers use the same device** — for
|
|
166
|
+
example the systemd service plus a manual `diggylab run`. The worker that connected last keeps the
|
|
167
|
+
device and the older one stops with a message. Check `pgrep -af "diggylab run"` and
|
|
168
|
+
`systemctl status diggylab --no-pager`, then keep only one.
|
|
169
|
+
- `socket error:` plus `socket closed: 1006` and `RECONNECTING (NETWORK_ERROR)` is a dropped
|
|
170
|
+
connection; the worker retries with backoff. If it never settles, check `curl -sS -o /dev/null -w
|
|
171
|
+
'%{http_code}\n' https://api.diggylab.com/health` and that the machine clock is right (`date -u`),
|
|
172
|
+
because a clock off by more than five minutes makes the server's messages look stale.
|
|
173
|
+
- `Facebook: missing` after a login means the session did not survive; run `diggylab login` again.
|
|
174
|
+
|
|
175
|
+
## Security notes
|
|
176
|
+
|
|
177
|
+
- `~/.diggylab/key` (mode `600`) encrypts device credentials, the Facebook session, and the
|
|
178
|
+
scan spool. Treat it like a password: back it up privately, never share it.
|
|
179
|
+
- The Facebook session belongs to the customer's account; run `login` again when `status` reports
|
|
180
|
+
it missing (sessions expire, and a sudden IP change can trigger a Facebook checkpoint).
|
|
181
|
+
- Unmatched or expired payments are unrelated to this worker — the agent only scans.
|
|
182
|
+
|
|
183
|
+
## Run as a service
|
|
184
|
+
|
|
185
|
+
On a Linux server with systemd, let the CLI write the unit for you:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
diggylab install-service # writes /etc/systemd/system/diggylab.service, enables + starts it
|
|
189
|
+
diggylab uninstall-service # stops it and removes the unit
|
|
190
|
+
journalctl -u diggylab -f # follow the worker log
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The unit keeps the same state directory and API origin you are using, restarts the worker if it
|
|
194
|
+
crashes, and starts it on boot. It points at npm's `diggylab` symlink, so upgrading the package is
|
|
195
|
+
enough — no need to reinstall the service (`diggylab update`, then `systemctl restart diggylab`). Run it as root (or with `sudo`) to write the unit.
|
|
196
|
+
|
|
197
|
+
<details>
|
|
198
|
+
<summary>Equivalent unit, if you prefer to write it yourself</summary>
|
|
199
|
+
|
|
200
|
+
```ini
|
|
201
|
+
[Unit]
|
|
202
|
+
Description=DiggyLab Desktop Agent
|
|
203
|
+
After=network-online.target
|
|
204
|
+
Wants=network-online.target
|
|
205
|
+
|
|
206
|
+
[Service]
|
|
207
|
+
ExecStart=/usr/local/bin/diggylab run
|
|
208
|
+
Environment="DIGGYLAB_AGENT_DIR=/root/.diggylab"
|
|
209
|
+
Environment="DIGGYLAB_API_URL=https://api.diggylab.com"
|
|
210
|
+
Restart=on-failure
|
|
211
|
+
RestartSec=10
|
|
212
|
+
|
|
213
|
+
[Install]
|
|
214
|
+
WantedBy=multi-user.target
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
</details>
|
|
218
|
+
|
|
219
|
+
## Docker
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
docker build -f apps/desktop-agent/Dockerfile.cli -t diggylab .
|
|
223
|
+
docker run -it --rm -v diggylab:/data diggylab pair --code ABC123
|
|
224
|
+
docker run -it --rm -v diggylab:/data diggylab login cookies
|
|
225
|
+
docker run -d --restart unless-stopped -v diggylab:/data diggylab run
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The image sets `DIGGYLAB_AGENT_DIR=/data`, so the volume keeps the key, credentials, and spool.
|