diggylab 0.1.35 → 0.1.36

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 +4 -3
  2. package/cli.js +43 -8
  3. package/package.json +4 -1
package/README.md CHANGED
@@ -7,8 +7,8 @@ headless Chromium — the Electron app is not involved.
7
7
  ## Requirements
8
8
 
9
9
  - Node.js 20+
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: run `diggylab install-browser` once after installing the CLI.
10
+ - Linux packages: `xvfb`, `x11vnc`, `websockify`, `novnc` — run `diggylab install-display` (Debian/Ubuntu) or install them yourself. The Docker image below bundles them.
11
+ - Chromium for Playwright: downloaded automatically during `npm install`; rerun `diggylab install-browser` if that download failed.
12
12
 
13
13
  ## Install
14
14
 
@@ -61,7 +61,8 @@ 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
+ | `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. |
65
+ | `install-display` | On Debian/Ubuntu, installs `xvfb`, `x11vnc`, `websockify`, and `novnc` for the shared login screen. |
65
66
  | `unpair` | Removes device credentials, the Facebook session, and pending scans. |
66
67
 
67
68
  Useful options: `--api <origin>` (default `https://api.diggylab.com`), `--port` / `--bind` /
package/cli.js CHANGED
@@ -2351,6 +2351,12 @@ function parseCliArgs(argv) {
2351
2351
  return { command, options };
2352
2352
  }
2353
2353
 
2354
+ // src/cli/browser-install.ts
2355
+ function browserInstallCommands(platform) {
2356
+ const install = ["install", "chromium"];
2357
+ return platform === "linux" ? [["install", "--with-deps", "chromium"], install] : [install];
2358
+ }
2359
+
2354
2360
  // src/cli/file-crypto.ts
2355
2361
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2356
2362
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -2540,13 +2546,14 @@ Usage:
2540
2546
  diggylab run
2541
2547
  diggylab status
2542
2548
  diggylab install-browser
2549
+ diggylab install-display
2543
2550
  diggylab unpair
2544
2551
 
2545
2552
  Global options:
2546
2553
  --config <directory> Agent state directory (default ~/.diggylab)
2547
2554
  --api <origin> API origin (default $DIGGYLAB_API_URL or https://api.diggylab.com)`;
2548
2555
  var MISSING_BROWSER_HINT = "Chromium for Playwright is not installed yet. Run `diggylab install-browser` once.";
2549
- var MISSING_DISPLAY_HINT = "The shared login screen needs Xvfb, x11vnc, websockify, and noVNC. Install them (apt install xvfb x11vnc websockify novnc) or use the Docker image.";
2556
+ var MISSING_DISPLAY_HINT = "The shared login screen needs Xvfb, x11vnc, websockify, and noVNC. Run `diggylab install-display` (Debian/Ubuntu) or use the Docker image.";
2550
2557
  function agentVersion() {
2551
2558
  let directory = fileURLToPath(new URL(".", import.meta.url));
2552
2559
  for (let depth = 0; depth < 4; depth += 1) {
@@ -2721,10 +2728,11 @@ function playwrightCliPath() {
2721
2728
  }
2722
2729
  }
2723
2730
  async function commandInstallBrowser(io) {
2731
+ const soft = process.env.npm_lifecycle_event === "postinstall";
2724
2732
  const cli = playwrightCliPath();
2725
2733
  if (!cli) {
2726
2734
  io.error("Could not locate the bundled Playwright. Reinstall with `npm install -g diggylab@latest`.");
2727
- return 1;
2735
+ return soft ? 0 : 1;
2728
2736
  }
2729
2737
  io.log("Downloading Chromium for Playwright...");
2730
2738
  const run = (args) => new Promise((resolve, reject) => {
@@ -2732,16 +2740,42 @@ async function commandInstallBrowser(io) {
2732
2740
  child.once("error", reject);
2733
2741
  child.once("exit", (value) => resolve(value ?? 1));
2734
2742
  });
2735
- let code = process.platform === "linux" ? await run(["install", "--with-deps", "chromium"]) : 0;
2743
+ let code = 1;
2744
+ for (const [index, args] of browserInstallCommands(process.platform).entries()) {
2745
+ if (index > 0) io.log("Installing the system libraries failed; downloading the browser only...");
2746
+ code = await run(args);
2747
+ if (code === 0) break;
2748
+ }
2736
2749
  if (code !== 0) {
2737
- io.log("Installing the system libraries failed; downloading the browser only...");
2738
- code = await run(["install", "chromium"]);
2750
+ io.error(soft ? "Chromium download failed. Run `diggylab install-browser` later." : "Chromium download failed. Check the network and try again.");
2751
+ return soft ? 0 : code;
2752
+ }
2753
+ io.log("Chromium installed.");
2754
+ return 0;
2755
+ }
2756
+ async function commandInstallDisplay(io) {
2757
+ if (process.platform !== "linux") {
2758
+ io.log("The shared login screen is only needed on a Linux server; desktops open a browser window.");
2759
+ return 0;
2760
+ }
2761
+ if (!existsSync2("/usr/bin/apt-get")) {
2762
+ io.error("Automatic installation supports Debian/Ubuntu. Install xvfb, x11vnc, websockify, and noVNC with your package manager.");
2763
+ return 1;
2739
2764
  }
2765
+ const sudo = typeof process.getuid === "function" && process.getuid() !== 0 ? ["sudo"] : [];
2766
+ const run = (args) => new Promise((resolve, reject) => {
2767
+ const child = spawn2(args[0], args.slice(1), { stdio: "inherit" });
2768
+ child.once("error", reject);
2769
+ child.once("exit", (value) => resolve(value ?? 1));
2770
+ });
2771
+ io.log("Installing xvfb, x11vnc, websockify, and noVNC...");
2772
+ await run([...sudo, "apt-get", "update"]).catch(() => 1);
2773
+ const code = await run([...sudo, "apt-get", "install", "-y", "xvfb", "x11vnc", "websockify", "novnc"]);
2740
2774
  if (code !== 0) {
2741
- io.error("Chromium download failed. Check the network and try again.");
2775
+ io.error("Installation failed. Run the same command with apt manually.");
2742
2776
  return code;
2743
2777
  }
2744
- io.log("Chromium installed.");
2778
+ io.log("Login screen packages installed.");
2745
2779
  return 0;
2746
2780
  }
2747
2781
  async function commandRun(runtime, io) {
@@ -2866,12 +2900,13 @@ async function main(argv, io = console) {
2866
2900
  io.log(USAGE);
2867
2901
  return 0;
2868
2902
  }
2869
- if (!["install-browser", "login", "pair", "run", "status", "unpair"].includes(command)) {
2903
+ if (!["install-browser", "install-display", "login", "pair", "run", "status", "unpair"].includes(command)) {
2870
2904
  io.error(`Unknown command "${command}".`);
2871
2905
  io.error(USAGE);
2872
2906
  return 2;
2873
2907
  }
2874
2908
  if (command === "install-browser") return commandInstallBrowser(io);
2909
+ if (command === "install-display") return commandInstallDisplay(io);
2875
2910
  const runtime = createRuntime(parsed.options);
2876
2911
  switch (command) {
2877
2912
  case "pair":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diggylab",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
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": {
@@ -18,6 +18,9 @@
18
18
  "qrcode-terminal": "^0.12.0",
19
19
  "ws": "^8.21.3"
20
20
  },
21
+ "scripts": {
22
+ "postinstall": "node cli.js install-browser"
23
+ },
21
24
  "license": "UNLICENSED",
22
25
  "publishConfig": {
23
26
  "access": "public"