@dreamdata_io/verify-tracking 0.1.0 → 0.1.2

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Hands the generated dashboard to the operator's browser.
3
+ *
4
+ * macOS only, deliberately. `open` is the one launcher this tool's installed
5
+ * audience has, and a Linux/Windows path would be written blind — those
6
+ * platforms get the printed path instead, which is what a headless machine
7
+ * wants anyway.
8
+ *
9
+ * Never throws, and never prints: it returns what happened and the caller
10
+ * decides what to say. That is also what keeps it safe to import from a
11
+ * module graph the MCP server touches, where stdout belongs to the transport.
12
+ */
13
+ import { spawn } from "node:child_process";
14
+ import { resolve } from "node:path";
15
+ import { outputRoot } from "../config/user-config.js";
16
+ /**
17
+ * Recognisable in Finder among the report JSONs, and stable across runs so a
18
+ * closed tab is reopened by path. No spaces: the same string is a shell
19
+ * argument, a doc snippet and a file URL.
20
+ */
21
+ export const DASHBOARD_FILENAME = "dreamdata-tracking-dashboard.html";
22
+ /** Where the dashboard is written, resolved on call — never at module scope. */
23
+ export function dashboardPath() {
24
+ return resolve(outputRoot(), DASHBOARD_FILENAME);
25
+ }
26
+ /**
27
+ * Detached and unref'd, so the CLI's `process.exit` neither kills the child
28
+ * nor waits for it: on POSIX a parent exiting sends no signal, and `open` has
29
+ * already handed off to LaunchServices by then. `stdio: "ignore"` means the
30
+ * child physically cannot write to our stdout.
31
+ */
32
+ function spawnDetachedReal(command, args) {
33
+ spawn(command, args, { detached: true, stdio: "ignore" }).unref();
34
+ }
35
+ export function openDashboard(options) {
36
+ const platform = options.platform ?? process.platform;
37
+ const env = options.env ?? process.env;
38
+ const launch = options.spawnDetached ?? spawnDetachedReal;
39
+ // A build machine has no one to show it to, and popping a browser in CI is
40
+ // noise at best.
41
+ if (env.CI)
42
+ return { kind: "skipped", reason: "disabled" };
43
+ if (platform !== "darwin")
44
+ return { kind: "skipped", reason: "unsupported-platform" };
45
+ try {
46
+ launch("open", [options.path]);
47
+ return { kind: "opened" };
48
+ }
49
+ catch (err) {
50
+ return { kind: "failed", detail: err instanceof Error ? err.message : String(err) };
51
+ }
52
+ }
53
+ //# sourceMappingURL=open.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open.js","sourceRoot":"","sources":["../../src/review/open.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAEtE,gFAAgF;AAChF,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,UAAU,EAAE,EAAE,kBAAkB,CAAC,CAAC;AACnD,CAAC;AAgBD;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,OAAe,EAAE,IAAc;IACxD,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACtD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,IAAI,iBAAiB,CAAC;IAE1D,2EAA2E;IAC3E,iBAAiB;IACjB,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC3D,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;IAEtF,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACtF,CAAC;AACH,CAAC"}
package/dist/version.js CHANGED
@@ -6,5 +6,5 @@
6
6
  * breaks the moment the output layout changes. A literal that the release
7
7
  * workflow checks against the git tag is the cheaper guarantee.
8
8
  */
9
- export const VERSION = "0.1.0";
9
+ export const VERSION = "0.1.1";
10
10
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamdata_io/verify-tracking",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Scrapes a live website with Playwright and asks an AI model whether the Dreamdata Analytics Script is correctly installed.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,6 +23,7 @@
23
23
  "files": [
24
24
  "dist",
25
25
  "scripts/install.sh",
26
+ "verification/review/index.html",
26
27
  "README.md",
27
28
  "LICENSE"
28
29
  ],
@@ -38,6 +38,12 @@ NODE_DIST="${VERIFY_TRACKING_NODE_DIST:-https://nodejs.org/dist}"
38
38
  # gets the two shims that are the whole user-facing surface.
39
39
  PREFIX="${VERIFY_TRACKING_PREFIX:-$HOME/.local/share/verify-tracking}"
40
40
  BIN_DIR="${VERIFY_TRACKING_BIN_DIR:-$HOME/.local/bin}"
41
+ # Where runs save their reports and the dashboard. A globally installed CLI
42
+ # otherwise grows a verification/ tree in whatever directory it was run from,
43
+ # which makes "where is my dashboard" depend on the operator's cwd and lets a
44
+ # `cd` silently start a second, separate set.
45
+ CONFIG_FILE="$HOME/.config/verify-tracking/config.env"
46
+ REPORTS_HOME="${VERIFY_TRACKING_REPORTS_HOME:-$HOME/dreamdata-tracking}"
41
47
 
42
48
  # Kept out of the shared ~/.cache/ms-playwright on purpose: the browser this
43
49
  # installer downloaded is then provably the browser the run launches, and an
@@ -71,6 +77,7 @@ command -v tar >/dev/null 2>&1 || die "tar is required."
71
77
  printf 'Installing verify-tracking (%s/%s)\n' "$node_os" "$node_arch"
72
78
  say "runtime, packages and browser -> $PREFIX"
73
79
  say "commands -> $BIN_DIR"
80
+ say "reports and dashboard -> $REPORTS_HOME"
74
81
 
75
82
  # ---------------------------------------------------------------------------
76
83
  # 1. A Node the tool can run on
@@ -247,7 +254,31 @@ write_shim "verify-tracking" "dist/cli.js"
247
254
  write_shim "verify-tracking-mcp" "dist/mcp/server.js"
248
255
 
249
256
  # ---------------------------------------------------------------------------
250
- # 5. Sign in to Claude — the one step a human has to finish
257
+ # 5. Where reports are saved
258
+ # ---------------------------------------------------------------------------
259
+
260
+ # The config file rather than the shim, even though the shim already exports
261
+ # PLAYWRIGHT_BROWSERS_PATH: user-config.ts only fills in variables the
262
+ # environment leaves unset, so a value exported by the shim would beat the
263
+ # operator's own config.env entry and make that documented file silently
264
+ # useless. Written here it sits at the same tier as any other setting, and can
265
+ # be edited afterwards.
266
+ #
267
+ # An existing key is never rewritten — including the `export KEY=` form, which
268
+ # the parser also accepts and resolves first-wins, so appending a second line
269
+ # would announce a directory the CLI then ignores.
270
+ step "Reports folder"
271
+ mkdir -p "$(dirname "$CONFIG_FILE")"
272
+ [ -f "$CONFIG_FILE" ] || : > "$CONFIG_FILE"
273
+ if grep -Eq '^[[:space:]]*(export[[:space:]]+)?VERIFY_TRACKING_OUTPUT_DIR=' "$CONFIG_FILE"; then
274
+ say "already set in $CONFIG_FILE — leaving it alone"
275
+ else
276
+ printf 'VERIFY_TRACKING_OUTPUT_DIR=%s\n' "$REPORTS_HOME" >> "$CONFIG_FILE"
277
+ say "reports and the dashboard will be saved in $REPORTS_HOME"
278
+ fi
279
+
280
+ # ---------------------------------------------------------------------------
281
+ # 6. Sign in to Claude — the one step a human has to finish
251
282
  # ---------------------------------------------------------------------------
252
283
 
253
284
  # `curl … | sh` leaves this script's own text on stdin, so a child that prompts
@@ -296,19 +327,99 @@ else
296
327
  fi
297
328
 
298
329
  # ---------------------------------------------------------------------------
299
- # Done
330
+ # 7. PATH
300
331
  # ---------------------------------------------------------------------------
301
332
 
302
- printf '\n==> Installed\n'
303
- "$BIN_DIR/verify-tracking" --version 2>/dev/null | sed 's/^/ /' || true
333
+ # Appended for the operator rather than printed for them to paste. The people
334
+ # this installer is for do not know what a shell profile is, and "now edit
335
+ # ~/.zshrc" is the second place a handover stops.
336
+ #
337
+ # Every profile that already exists gets the line, not just the one implied by
338
+ # $SHELL: a macOS login shell reads ~/.zprofile, an interactive one ~/.zshrc,
339
+ # and bash reads ~/.bash_profile rather than ~/.bashrc — guessing a single file
340
+ # is how the line lands somewhere that is never sourced. A file that does not
341
+ # exist is left uncreated, so this never conjures a profile for a shell the
342
+ # machine does not use.
343
+ #
344
+ # What it cannot do is take effect in the terminal that ran it: this script is
345
+ # a child process, and no child can alter its parent's environment. So the
346
+ # closing lines send the operator to a new terminal window rather than letting
347
+ # them discover that on a `command not found`.
348
+ PATH_LINE="export PATH=\"$BIN_DIR:\$PATH\""
349
+ # BIN_DIR is a path, and a path is not a regex: an unescaped `.` in `.local`
350
+ # would match any character, so a near-miss line could read as a hit. The class
351
+ # is the full ERE metacharacter set, not just the BRE one — grep runs with -E,
352
+ # and a home directory is free to contain a `+` or a `(`.
353
+ bin_dir_re="$(printf '%s' "$BIN_DIR" | sed 's/[].[^$*+?(){}|\/\\]/\\&/g')"
354
+
355
+ add_to_profile() {
356
+ profile="$1"
357
+ [ -f "$profile" ] || return 0
358
+ # Anchored, and only on an uncommented line: a commented-out or
359
+ # differently-worded mention must not read as "already configured". BIN_DIR
360
+ # has to end a PATH component too — as a bare substring, `~/.local/bin` is
361
+ # also inside `~/.local/bin2`, and a match there would skip a profile that
362
+ # never had the line.
363
+ if grep -Eq "^[[:space:]]*export[[:space:]]+PATH=.*${bin_dir_re}([:\"']|\$)" "$profile"; then
364
+ say "already in $profile"
365
+ return 0
366
+ fi
367
+ # A profile that cannot be written to is a PATH the operator has to set by
368
+ # hand, not a failed install — under `set -e` an unguarded append would take
369
+ # the whole run down over a read-only home.
370
+ if printf '\n# Added by the verify-tracking installer\n%s\n' "$PATH_LINE" >> "$profile" 2>/dev/null; then
371
+ say "added to $profile"
372
+ else
373
+ say "could not write $profile. Add this line to it yourself:"
374
+ printf '\n %s\n' "$PATH_LINE"
375
+ fi
376
+ }
304
377
 
378
+ step "PATH"
305
379
  case ":${PATH}:" in
306
- *":$BIN_DIR:"*) ;;
380
+ *":$BIN_DIR:"*)
381
+ say "$BIN_DIR is already on your PATH"
382
+ ;;
307
383
  *)
308
- printf '\n %s is not on your PATH. Add it, then reopen your terminal:\n\n' "$BIN_DIR"
309
- printf " echo 'export PATH=\"%s:\$PATH\"' >> ~/.zshrc\n" "$BIN_DIR"
384
+ for profile in "$HOME/.zshrc" "$HOME/.zprofile" "$HOME/.bashrc" "$HOME/.bash_profile"; do
385
+ add_to_profile "$profile"
386
+ done
387
+ # Nothing to append to means nothing was appended — print the line rather
388
+ # than claim a PATH change that did not happen.
389
+ if [ -f "$HOME/.zshrc" ] || [ -f "$HOME/.zprofile" ] \
390
+ || [ -f "$HOME/.bashrc" ] || [ -f "$HOME/.bash_profile" ]; then
391
+ :
392
+ else
393
+ say "no shell profile found. Add this line to yours:"
394
+ printf '\n %s\n' "$PATH_LINE"
395
+ fi
310
396
  ;;
311
397
  esac
312
398
 
313
- printf '\n Try it:\n\n verify-tracking --url https://example.com --quick --no-save-report\n'
314
- printf '\n Uninstall:\n\n rm -rf %s %s/verify-tracking %s/verify-tracking-mcp\n\n' "$PREFIX" "$BIN_DIR" "$BIN_DIR"
399
+ # ---------------------------------------------------------------------------
400
+ # Done
401
+ # ---------------------------------------------------------------------------
402
+
403
+ printf '\n==> Installed\n'
404
+ "$BIN_DIR/verify-tracking" --version 2>/dev/null | sed 's/^/ /' || true
405
+
406
+ printf '\n Reports and dashboard:\n\n %s\n' "$REPORTS_HOME"
407
+ printf '\n Try it, from a new terminal window:\n\n verify-tracking --slug dreamdata_io --url https://dreamdata.io/\n'
408
+
409
+ # Claimed only where a browser can actually appear: --dashboard skips opening
410
+ # one on a machine with nobody in front of it, and an installer that announces
411
+ # a window that never arrives sends the operator looking for it.
412
+ if [ -r /dev/tty ]; then
413
+ printf '\n A browser window is opening with your dashboard. Type an account slug\n'
414
+ printf ' and a URL there to build your command, then open a new terminal window\n'
415
+ printf ' and run it.\n'
416
+ fi
417
+ # The reports folder is the operator's data and is deliberately left out: an
418
+ # `rm -rf` line someone pastes without reading must not take their work with it.
419
+ printf '\n Uninstall (your reports folder is left in place):\n\n rm -rf %s %s/verify-tracking %s/verify-tracking-mcp\n\n' "$PREFIX" "$BIN_DIR" "$BIN_DIR"
420
+
421
+ # Opening the dashboard is the last thing, and never a reason to fail: it also
422
+ # builds the page, so an operator who is looking at the empty state is reading
423
+ # the instructions for their first check. It prints its own path, and skips the
424
+ # browser on a machine with nobody in front of it.
425
+ "$BIN_DIR/verify-tracking" --dashboard >/dev/null 2>&1 || true