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.
- package/.github/workflows/docs.yml +54 -0
- package/AGENTS.md +133 -0
- package/README.md +1 -1
- package/dist/{chunk-6A7FFDIB.js → chunk-6VA7GBAO.js} +29 -4
- package/dist/chunk-6VA7GBAO.js.map +1 -0
- package/dist/{chunk-ANMR5WBH.js → chunk-G2Y3CXCY.js} +2 -2
- package/dist/{chunk-BSKNG4V7.js → chunk-ILRYKMME.js} +30 -1
- package/dist/chunk-ILRYKMME.js.map +1 -0
- package/dist/{chunk-X7C5CHBO.js → chunk-SKCMT2DE.js} +637 -76
- package/dist/chunk-SKCMT2DE.js.map +1 -0
- package/dist/{chunk-ZLNGOOH2.js → chunk-SPCIROIU.js} +65 -3
- package/dist/chunk-SPCIROIU.js.map +1 -0
- package/dist/cli/index.js +115 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +637 -75
- package/dist/client/index.d.ts +16 -2
- package/dist/client/index.js +2 -2
- package/dist/client.iife.js +393 -72
- package/dist/core/index.d.ts +12 -4
- package/dist/core/index.js +9 -3
- package/dist/daemon/index.d.ts +1 -1
- package/dist/daemon/index.js +3 -3
- package/dist/{engine-DKloFjvs.d.ts → engine-CeT9URuN.d.ts} +4 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +13 -7
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +2 -2
- package/dist/{server-CN-se8td.d.ts → server-Dd8NX2Mk.d.ts} +1 -1
- package/docboot.config.js +16 -0
- package/docs/cli.md +51 -0
- package/docs/closed-loop-verification.md +57 -0
- package/docs/getting-started.md +122 -0
- package/docs/incidents-diagnostics.md +61 -0
- package/docs/index.md +46 -0
- package/docs/mcp-reference.md +128 -0
- package/docs/security-privacy.md +32 -0
- package/docs/visual-notes.md +67 -0
- package/package.json +6 -2
- package/packages/client/package.json +2 -2
- package/packages/client/src/notes/inspector.ts +689 -79
- package/packages/client/src/transport/websocket.ts +15 -0
- package/packages/core/package.json +3 -0
- package/packages/core/src/redaction.ts +40 -5
- package/packages/daemon/src/server/ws.ts +72 -1
- package/packages/daemon/src/session/manager.ts +21 -0
- package/packages/daemon/src/storage/db.ts +10 -1
- package/test/client/interceptors.test.ts +61 -0
- package/test/core/redaction.test.ts +12 -0
- package/dist/chunk-6A7FFDIB.js.map +0 -1
- package/dist/chunk-BSKNG4V7.js.map +0 -1
- package/dist/chunk-X7C5CHBO.js.map +0 -1
- package/dist/chunk-ZLNGOOH2.js.map +0 -1
- package/packages/core/dist/index.d.ts +0 -317
- package/packages/core/dist/index.js +0 -221
- /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
|
|
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
|
-
|
|
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") {
|