diggylab 0.1.32 → 0.1.33

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/cli.js +68 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@ headless Chromium — the Electron app is not involved.
8
8
 
9
9
  - Node.js 22+
10
10
  - Linux packages: `xvfb`, `x11vnc`, `websockify`, `novnc` (Debian/Ubuntu: `apt install xvfb x11vnc websockify novnc`). The Docker image below bundles them.
11
- - Chromium for Playwright: `npx playwright install --with-deps chromium`
11
+ - Chromium for Playwright: run `diggylab install-browser` once after installing the CLI.
12
12
 
13
13
  ## Install
14
14
 
@@ -61,6 +61,7 @@ npm publish apps/desktop-agent/dist-npm --access public
61
61
  | `login` | Opens the Facebook login page on a virtual screen and prints a noVNC link + QR code. Log in from your phone; the session is stored encrypted on the server. |
62
62
  | `run` | Connects the WebSocket worker, receives assigned scans, scans headlessly, uploads results. |
63
63
  | `status` | Shows the config directory, API origin, paired device, and Facebook session state. |
64
+ | `install-browser` | Downloads the Chromium build Playwright needs (one time, ~150 MB). Run it once after installing the CLI. |
64
65
  | `unpair` | Removes device credentials, the Facebook session, and pending scans. |
65
66
 
66
67
  Useful options: `--api <origin>` (default `https://api.diggylab.com`), `--port` / `--bind` /
package/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli/cli.ts
4
4
  import { randomUUID as randomUUID3 } from "node:crypto";
5
+ import { spawn as spawn2 } from "node:child_process";
5
6
  import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "node:fs";
6
7
  import { hostname, homedir } from "node:os";
7
8
  import { dirname as dirname3, join as join4 } from "node:path";
@@ -2504,11 +2505,13 @@ Usage:
2504
2505
  diggylab login [--port 6080] [--bind 0.0.0.0] [--public-host <ip or host>]
2505
2506
  diggylab run
2506
2507
  diggylab status
2508
+ diggylab install-browser
2507
2509
  diggylab unpair
2508
2510
 
2509
2511
  Global options:
2510
2512
  --config <directory> Agent state directory (default ~/.diggylab)
2511
2513
  --api <origin> API origin (default $DIGGYLAB_API_URL or https://api.diggylab.com)`;
2514
+ var MISSING_BROWSER_HINT = "Chromium for Playwright is not installed yet. Run `diggylab install-browser` once.";
2512
2515
  function agentVersion() {
2513
2516
  let directory = fileURLToPath(new URL(".", import.meta.url));
2514
2517
  for (let depth = 0; depth < 4; depth += 1) {
@@ -2576,21 +2579,42 @@ function waitForFacebookLogin(login, timeoutMs) {
2576
2579
  });
2577
2580
  });
2578
2581
  }
2582
+ function pairErrorMessage(error, origin) {
2583
+ const code = error instanceof DesktopAgentClientError ? error.code : null;
2584
+ switch (code) {
2585
+ case "PAIRING_CODE_INVALID":
2586
+ return "The pairing code is invalid, already used, or older than 10 minutes. Create a new code and pair again.";
2587
+ case "PLAN_RESTRICTED":
2588
+ return "This account's plan does not include the Desktop Agent.";
2589
+ case "CREDENTIAL_REVOKED":
2590
+ return "This device was revoked. Create a new pairing code to pair again.";
2591
+ case "NETWORK_ERROR":
2592
+ case "SERVER_UNAVAILABLE":
2593
+ return `Could not reach ${origin}. Check the --api origin and the network.`;
2594
+ default:
2595
+ return error instanceof Error ? error.message : String(error);
2596
+ }
2597
+ }
2579
2598
  async function commandPair(runtime, options, io) {
2580
2599
  const code = options.code?.trim();
2581
2600
  if (!code) {
2582
2601
  io.error("Missing --code <pairing code>. Create one on the Desktop Agent page of the web app.");
2583
2602
  return 2;
2584
2603
  }
2585
- const access = await runtime.manager.pair({
2586
- agentVersion: agentVersion(),
2587
- code,
2588
- name: sanitizeDeviceName(options.name?.trim() || hostname()),
2589
- platform: resolvePlatform()
2590
- });
2591
- io.log(`Paired as "${access.session.device.name}".`);
2592
- io.log("Next: run `diggylab login` to connect a Facebook account.");
2593
- return 0;
2604
+ try {
2605
+ const access = await runtime.manager.pair({
2606
+ agentVersion: agentVersion(),
2607
+ code,
2608
+ name: sanitizeDeviceName(options.name?.trim() || hostname()),
2609
+ platform: resolvePlatform()
2610
+ });
2611
+ io.log(`Paired as "${access.session.device.name}".`);
2612
+ io.log("Next: run `diggylab login` to connect a Facebook account.");
2613
+ return 0;
2614
+ } catch (error) {
2615
+ io.error(pairErrorMessage(error, runtime.endpoint.httpOrigin));
2616
+ return 1;
2617
+ }
2594
2618
  }
2595
2619
  async function commandLogin(runtime, options, io) {
2596
2620
  const login = new FacebookLoginService({
@@ -2633,11 +2657,44 @@ ${display.url}`);
2633
2657
  await waitForFacebookLogin(login, LOGIN_TIMEOUT_MS + 6e4);
2634
2658
  io.log("Facebook session saved.");
2635
2659
  return 0;
2660
+ } catch (error) {
2661
+ const message = error instanceof Error ? error.message : String(error);
2662
+ if (/Executable doesn't exist|playwright install/i.test(message)) {
2663
+ io.error(MISSING_BROWSER_HINT);
2664
+ return 1;
2665
+ }
2666
+ throw error;
2636
2667
  } finally {
2637
2668
  await login.close();
2638
2669
  await display?.stop();
2639
2670
  }
2640
2671
  }
2672
+ function playwrightCliPath() {
2673
+ try {
2674
+ return fileURLToPath(new URL("cli.js", import.meta.resolve("playwright")));
2675
+ } catch {
2676
+ return null;
2677
+ }
2678
+ }
2679
+ async function commandInstallBrowser(io) {
2680
+ const cli = playwrightCliPath();
2681
+ if (!cli) {
2682
+ io.error("Could not locate the bundled Playwright. Reinstall with `npm install -g diggylab@latest`.");
2683
+ return 1;
2684
+ }
2685
+ io.log("Downloading Chromium for Playwright...");
2686
+ const code = await new Promise((resolve, reject) => {
2687
+ const child = spawn2(process.execPath, [cli, "install", "chromium"], { stdio: "inherit" });
2688
+ child.once("error", reject);
2689
+ child.once("exit", (value) => resolve(value ?? 1));
2690
+ });
2691
+ if (code !== 0) {
2692
+ io.error("Chromium download failed. Check the network and try again.");
2693
+ return code;
2694
+ }
2695
+ io.log("Chromium installed.");
2696
+ return 0;
2697
+ }
2641
2698
  async function commandRun(runtime, io) {
2642
2699
  const stored = await runtime.manager.load();
2643
2700
  if (!stored) {
@@ -2749,11 +2806,12 @@ async function main(argv, io = console) {
2749
2806
  io.log(USAGE);
2750
2807
  return 0;
2751
2808
  }
2752
- if (!["login", "pair", "run", "status", "unpair"].includes(command)) {
2809
+ if (!["install-browser", "login", "pair", "run", "status", "unpair"].includes(command)) {
2753
2810
  io.error(`Unknown command "${command}".`);
2754
2811
  io.error(USAGE);
2755
2812
  return 2;
2756
2813
  }
2814
+ if (command === "install-browser") return commandInstallBrowser(io);
2757
2815
  const runtime = createRuntime(parsed.options);
2758
2816
  switch (command) {
2759
2817
  case "pair":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diggylab",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "DiggyLab Desktop Agent collects posts and information from Facebook Groups on a paired computer. (headless CLI)",
5
5
  "type": "module",
6
6
  "bin": {