browsertrack 0.1.0 → 0.1.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.
Files changed (55) hide show
  1. package/.github/workflows/docs.yml +54 -0
  2. package/AGENTS.md +133 -0
  3. package/README.md +1 -1
  4. package/dist/{chunk-6A7FFDIB.js → chunk-6VA7GBAO.js} +29 -4
  5. package/dist/chunk-6VA7GBAO.js.map +1 -0
  6. package/dist/{chunk-ANMR5WBH.js → chunk-G2Y3CXCY.js} +2 -2
  7. package/dist/{chunk-BSKNG4V7.js → chunk-ILRYKMME.js} +30 -1
  8. package/dist/chunk-ILRYKMME.js.map +1 -0
  9. package/dist/{chunk-X7C5CHBO.js → chunk-SKCMT2DE.js} +637 -76
  10. package/dist/chunk-SKCMT2DE.js.map +1 -0
  11. package/dist/{chunk-ZLNGOOH2.js → chunk-SPCIROIU.js} +65 -3
  12. package/dist/chunk-SPCIROIU.js.map +1 -0
  13. package/dist/cli/index.js +115 -2
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/client/index.cjs +637 -75
  16. package/dist/client/index.d.ts +16 -2
  17. package/dist/client/index.js +2 -2
  18. package/dist/client.iife.js +393 -72
  19. package/dist/core/index.d.ts +12 -4
  20. package/dist/core/index.js +9 -3
  21. package/dist/daemon/index.d.ts +1 -1
  22. package/dist/daemon/index.js +3 -3
  23. package/dist/{engine-DKloFjvs.d.ts → engine-CeT9URuN.d.ts} +4 -0
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.js +13 -7
  26. package/dist/mcp/index.d.ts +2 -2
  27. package/dist/mcp/index.js +2 -2
  28. package/dist/{server-CN-se8td.d.ts → server-Dd8NX2Mk.d.ts} +1 -1
  29. package/docboot.config.js +16 -0
  30. package/docs/cli.md +51 -0
  31. package/docs/closed-loop-verification.md +57 -0
  32. package/docs/getting-started.md +122 -0
  33. package/docs/incidents-diagnostics.md +61 -0
  34. package/docs/index.md +46 -0
  35. package/docs/mcp-reference.md +128 -0
  36. package/docs/security-privacy.md +32 -0
  37. package/docs/visual-notes.md +67 -0
  38. package/package.json +6 -2
  39. package/packages/client/package.json +2 -2
  40. package/packages/client/src/notes/inspector.ts +689 -79
  41. package/packages/client/src/transport/websocket.ts +15 -0
  42. package/packages/core/package.json +3 -0
  43. package/packages/core/src/redaction.ts +40 -5
  44. package/packages/daemon/src/server/ws.ts +72 -1
  45. package/packages/daemon/src/session/manager.ts +21 -0
  46. package/packages/daemon/src/storage/db.ts +10 -1
  47. package/test/client/interceptors.test.ts +61 -0
  48. package/test/core/redaction.test.ts +12 -0
  49. package/dist/chunk-6A7FFDIB.js.map +0 -1
  50. package/dist/chunk-BSKNG4V7.js.map +0 -1
  51. package/dist/chunk-X7C5CHBO.js.map +0 -1
  52. package/dist/chunk-ZLNGOOH2.js.map +0 -1
  53. package/packages/core/dist/index.d.ts +0 -317
  54. package/packages/core/dist/index.js +0 -221
  55. /package/dist/{chunk-ANMR5WBH.js.map → chunk-G2Y3CXCY.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -505,11 +505,19 @@ var StorageDB = class {
505
505
  sql += " AND status = ?";
506
506
  params.push(options.status);
507
507
  }
508
+ if (options.route) {
509
+ sql += " AND route = ?";
510
+ params.push(options.route);
511
+ }
508
512
  sql += " ORDER BY created_at DESC LIMIT ?";
509
513
  params.push(options.limit || 50);
510
514
  const rows = this.db.prepare(sql).all(...params);
511
515
  return rows.map((r) => this.mapNoteRow(r));
512
516
  }
517
+ deleteNote(id) {
518
+ this.db.prepare("DELETE FROM note_verifications WHERE note_id = ?").run(id);
519
+ this.db.prepare("DELETE FROM notes WHERE id = ?").run(id);
520
+ }
513
521
  updateNoteStatus(id, status) {
514
522
  const now = (/* @__PURE__ */ new Date()).toISOString();
515
523
  const resolvedAt = status === "RESOLVED" ? now : null;
@@ -769,6 +777,27 @@ var SessionManager = class {
769
777
  getActiveCount() {
770
778
  return this.activeSockets.size;
771
779
  }
780
+ broadcastToProject(projectId, message) {
781
+ const raw = typeof message === "string" ? message : JSON.stringify(message);
782
+ for (const active of this.activeSockets.values()) {
783
+ if (active.projectId === projectId && active.ws.readyState === 1) {
784
+ try {
785
+ active.ws.send(raw);
786
+ } catch {
787
+ }
788
+ }
789
+ }
790
+ }
791
+ sendToSession(sessionId, message) {
792
+ const active = this.activeSockets.get(sessionId);
793
+ if (active && active.ws.readyState === 1) {
794
+ try {
795
+ const raw = typeof message === "string" ? message : JSON.stringify(message);
796
+ active.ws.send(raw);
797
+ } catch {
798
+ }
799
+ }
800
+ }
772
801
  };
773
802
 
774
803
  // packages/daemon/src/incidents/engine.ts
@@ -834,6 +863,8 @@ function computeFingerprint(input) {
834
863
  }
835
864
 
836
865
  // packages/core/src/redaction.ts
866
+ import { redact as visulimaRedact, standardRules } from "@visulima/redact";
867
+ var REDACTED_PLACEHOLDER = "[REDACTED]";
837
868
  var SENSITIVE_KEY_PATTERNS = [
838
869
  /^authorization$/i,
839
870
  /^cookie$/i,
@@ -863,7 +894,23 @@ var SENSITIVE_QUERY_PARAMS = [
863
894
  "code",
864
895
  "signature"
865
896
  ];
866
- var REDACTED_PLACEHOLDER = "[REDACTED]";
897
+ var BROWSER_SECURITY_RULES = [
898
+ { deep: true, key: "password", replacement: REDACTED_PLACEHOLDER },
899
+ { deep: true, key: "secret", replacement: REDACTED_PLACEHOLDER },
900
+ { deep: true, key: "token", replacement: REDACTED_PLACEHOLDER },
901
+ { deep: true, key: "authorization", replacement: REDACTED_PLACEHOLDER },
902
+ { deep: true, key: "cookie", replacement: REDACTED_PLACEHOLDER },
903
+ { deep: true, key: "set-cookie", replacement: REDACTED_PLACEHOLDER },
904
+ { deep: true, key: "apikey", replacement: REDACTED_PLACEHOLDER },
905
+ { deep: true, key: "api_key", replacement: REDACTED_PLACEHOLDER },
906
+ { deep: true, key: "creditcard", pattern: /(?:\d[ -]*?){13,16}/, replacement: REDACTED_PLACEHOLDER },
907
+ { deep: true, key: "cvv", replacement: REDACTED_PLACEHOLDER },
908
+ { deep: true, key: "ssn", pattern: /\b\d{3}-\d{2}-\d{4}\b/, replacement: REDACTED_PLACEHOLDER },
909
+ { deep: true, key: "awsid", pattern: /\bAKIA[0-9A-Z]{16}\b/, replacement: REDACTED_PLACEHOLDER },
910
+ { deep: true, key: "awskey", pattern: /\b[0-9a-zA-Z/+]{40}\b/, replacement: REDACTED_PLACEHOLDER },
911
+ { deep: true, key: "jwt", pattern: /\beyJ[0-9a-zA-Z_\-]*\.[0-9a-zA-Z_\-]*\.[0-9a-zA-Z_\-]*\b/, replacement: REDACTED_PLACEHOLDER },
912
+ { deep: true, key: "slack_token", pattern: /\bxox[baprs]-[0-9a-zA-Z]{10,48}\b/, replacement: REDACTED_PLACEHOLDER }
913
+ ];
867
914
  function isSensitiveKey(key) {
868
915
  if (!key) return false;
869
916
  const cleaned = key.replace(/[-_]/g, "");
@@ -918,7 +965,11 @@ function redactSensitiveData(data, maxDepth = 6, currentDepth = 0) {
918
965
  if (value.startsWith("http://") || value.startsWith("https://") || value.includes("?")) {
919
966
  result[key] = redactUrl(value);
920
967
  } else {
921
- result[key] = value;
968
+ try {
969
+ result[key] = visulimaRedact(value, BROWSER_SECURITY_RULES);
970
+ } catch {
971
+ result[key] = value;
972
+ }
922
973
  }
923
974
  } else {
924
975
  result[key] = value;
@@ -1697,6 +1748,13 @@ function setupWebSocketServer(wss, db, sessionManager, incidentEngine, notesEngi
1697
1748
  projectName: project.name
1698
1749
  })
1699
1750
  );
1751
+ const existingNotes = db.listNotes({ projectId: project.id, limit: 100 });
1752
+ ws.send(
1753
+ JSON.stringify({
1754
+ type: "notes_sync",
1755
+ notes: existingNotes
1756
+ })
1757
+ );
1700
1758
  if (verbose) {
1701
1759
  console.log(`[BrowserTrack] New session connected: ${sessionId} (${project.name} @ ${hello.origin})`);
1702
1760
  }
@@ -1750,6 +1808,61 @@ function setupWebSocketServer(wss, db, sessionManager, incidentEngine, notesEngi
1750
1808
  status: note.status
1751
1809
  })
1752
1810
  );
1811
+ const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });
1812
+ sessionManager.broadcastToProject(note.projectId, {
1813
+ type: "notes_sync",
1814
+ notes: allNotes
1815
+ });
1816
+ return;
1817
+ }
1818
+ if (data.type === "resolve_note" && data.noteId) {
1819
+ const note = db.getNote(data.noteId);
1820
+ if (note) {
1821
+ db.updateNoteStatus(data.noteId, "RESOLVED");
1822
+ const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });
1823
+ sessionManager.broadcastToProject(note.projectId, {
1824
+ type: "notes_sync",
1825
+ notes: allNotes
1826
+ });
1827
+ }
1828
+ return;
1829
+ }
1830
+ if (data.type === "reopen_note" && data.noteId) {
1831
+ const note = db.getNote(data.noteId);
1832
+ if (note) {
1833
+ db.updateNoteStatus(data.noteId, "OPEN");
1834
+ const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });
1835
+ sessionManager.broadcastToProject(note.projectId, {
1836
+ type: "notes_sync",
1837
+ notes: allNotes
1838
+ });
1839
+ }
1840
+ return;
1841
+ }
1842
+ if (data.type === "delete_note" && data.noteId) {
1843
+ const note = db.getNote(data.noteId);
1844
+ if (note) {
1845
+ db.deleteNote(data.noteId);
1846
+ const allNotes = db.listNotes({ projectId: note.projectId, limit: 100 });
1847
+ sessionManager.broadcastToProject(note.projectId, {
1848
+ type: "notes_sync",
1849
+ notes: allNotes
1850
+ });
1851
+ }
1852
+ return;
1853
+ }
1854
+ if (data.type === "get_notes") {
1855
+ const session = currentSessionId ? db.getSession(currentSessionId) : null;
1856
+ const projectId = data.projectId || session?.projectId;
1857
+ if (projectId) {
1858
+ const allNotes = db.listNotes({ projectId, limit: 100 });
1859
+ ws.send(
1860
+ JSON.stringify({
1861
+ type: "notes_sync",
1862
+ notes: allNotes
1863
+ })
1864
+ );
1865
+ }
1753
1866
  return;
1754
1867
  }
1755
1868
  if (data.type === "command_response") {