@weavetab/mcp 2.5.0-beta.0

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 (281) hide show
  1. package/AGENTS.md +48 -0
  2. package/ARCHITECTURE.md +590 -0
  3. package/LICENSE +631 -0
  4. package/README.md +909 -0
  5. package/SECURITY.md +196 -0
  6. package/TROUBLESHOOTING.md +365 -0
  7. package/dist/anticaptcha/detector.d.ts +49 -0
  8. package/dist/anticaptcha/detector.js +465 -0
  9. package/dist/anticaptcha/integration.d.ts +36 -0
  10. package/dist/anticaptcha/integration.js +123 -0
  11. package/dist/anticaptcha/solver.d.ts +27 -0
  12. package/dist/anticaptcha/solver.js +552 -0
  13. package/dist/audit/logger.d.ts +13 -0
  14. package/dist/audit/logger.js +177 -0
  15. package/dist/audit/telemetry.d.ts +36 -0
  16. package/dist/audit/telemetry.js +162 -0
  17. package/dist/cdp/bridge.d.ts +16 -0
  18. package/dist/cdp/bridge.js +826 -0
  19. package/dist/cdp/confirm.d.ts +26 -0
  20. package/dist/cdp/confirm.js +79 -0
  21. package/dist/cdp/connector.d.ts +37 -0
  22. package/dist/cdp/connector.js +661 -0
  23. package/dist/cdp/extension.d.ts +36 -0
  24. package/dist/cdp/extension.js +118 -0
  25. package/dist/cdp/finder.d.ts +1 -0
  26. package/dist/cdp/finder.js +2 -0
  27. package/dist/cdp/helpers.d.ts +11 -0
  28. package/dist/cdp/helpers.js +195 -0
  29. package/dist/cdp/label_cache.d.ts +12 -0
  30. package/dist/cdp/label_cache.js +51 -0
  31. package/dist/cdp/lock.d.ts +33 -0
  32. package/dist/cdp/lock.js +206 -0
  33. package/dist/cdp/omni_broker.d.ts +17 -0
  34. package/dist/cdp/omni_broker.js +119 -0
  35. package/dist/cdp/profiles.d.ts +1 -0
  36. package/dist/cdp/profiles.js +87 -0
  37. package/dist/cdp/pruner.d.ts +41 -0
  38. package/dist/cdp/pruner.js +263 -0
  39. package/dist/cdp/ref_cache.d.ts +101 -0
  40. package/dist/cdp/ref_cache.js +234 -0
  41. package/dist/cdp/retry.d.ts +5 -0
  42. package/dist/cdp/retry.js +39 -0
  43. package/dist/cdp/retry_enhanced.d.ts +44 -0
  44. package/dist/cdp/retry_enhanced.js +102 -0
  45. package/dist/cdp/security.d.ts +50 -0
  46. package/dist/cdp/security.js +237 -0
  47. package/dist/cdp/verify.d.ts +39 -0
  48. package/dist/cdp/verify.js +143 -0
  49. package/dist/cdp/walker.d.ts +91 -0
  50. package/dist/cdp/walker.js +896 -0
  51. package/dist/cdp/walker.test.d.ts +2 -0
  52. package/dist/cdp/walker.test.js +172 -0
  53. package/dist/cdp/websocket_transport.d.ts +15 -0
  54. package/dist/cdp/websocket_transport.js +118 -0
  55. package/dist/cli/plugin.d.ts +9 -0
  56. package/dist/cli/plugin.js +1134 -0
  57. package/dist/cli/updater.d.ts +1 -0
  58. package/dist/cli/updater.js +72 -0
  59. package/dist/config/loader.d.ts +120 -0
  60. package/dist/config/loader.js +704 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +1248 -0
  63. package/dist/intelligence/hints.d.ts +11 -0
  64. package/dist/intelligence/hints.js +39 -0
  65. package/dist/intelligence/intent.d.ts +33 -0
  66. package/dist/intelligence/intent.js +171 -0
  67. package/dist/intelligence/sessionMemory.d.ts +66 -0
  68. package/dist/intelligence/sessionMemory.js +284 -0
  69. package/dist/intelligence/sessionMemory.test.d.ts +2 -0
  70. package/dist/intelligence/sessionMemory.test.js +62 -0
  71. package/dist/intelligence/trail.d.ts +64 -0
  72. package/dist/intelligence/trail.js +233 -0
  73. package/dist/intelligence/trail.test.d.ts +2 -0
  74. package/dist/intelligence/trail.test.js +110 -0
  75. package/dist/intelligence/vision.d.ts +24 -0
  76. package/dist/intelligence/vision.js +151 -0
  77. package/dist/lifecycle/mission_manager.d.ts +16 -0
  78. package/dist/lifecycle/mission_manager.js +44 -0
  79. package/dist/overlay/bubble.d.ts +16 -0
  80. package/dist/overlay/bubble.js +37 -0
  81. package/dist/overlay/ghost.d.ts +35 -0
  82. package/dist/overlay/ghost.js +508 -0
  83. package/dist/overlay/injector.d.ts +22 -0
  84. package/dist/overlay/injector.js +198 -0
  85. package/dist/plugin/interface.d.ts +134 -0
  86. package/dist/plugin/interface.js +1 -0
  87. package/dist/plugin/loader.d.ts +18 -0
  88. package/dist/plugin/loader.js +155 -0
  89. package/dist/plugin/registry.d.ts +50 -0
  90. package/dist/plugin/registry.js +234 -0
  91. package/dist/security/blacklist.d.ts +24 -0
  92. package/dist/security/blacklist.js +80 -0
  93. package/dist/security/filter.d.ts +2 -0
  94. package/dist/security/filter.js +20 -0
  95. package/dist/security/guard.d.ts +8 -0
  96. package/dist/security/guard.js +35 -0
  97. package/dist/security/ratelimit.d.ts +11 -0
  98. package/dist/security/ratelimit.js +57 -0
  99. package/dist/security/secrets.d.ts +75 -0
  100. package/dist/security/secrets.js +365 -0
  101. package/dist/sensors/actionability.d.ts +81 -0
  102. package/dist/sensors/actionability.js +303 -0
  103. package/dist/sensors/guard.d.ts +25 -0
  104. package/dist/sensors/guard.js +168 -0
  105. package/dist/sensors/mutations.d.ts +23 -0
  106. package/dist/sensors/mutations.js +93 -0
  107. package/dist/sensors/network.d.ts +67 -0
  108. package/dist/sensors/network.js +193 -0
  109. package/dist/sensors/operation_monitor.d.ts +80 -0
  110. package/dist/sensors/operation_monitor.js +203 -0
  111. package/dist/sensors/pacing.d.ts +44 -0
  112. package/dist/sensors/pacing.js +111 -0
  113. package/dist/sensors/thoughts.d.ts +2 -0
  114. package/dist/sensors/thoughts.js +59 -0
  115. package/dist/sensors/wait.d.ts +12 -0
  116. package/dist/sensors/wait.js +44 -0
  117. package/dist/server.d.ts +7 -0
  118. package/dist/server.js +1687 -0
  119. package/dist/state/agent.d.ts +36 -0
  120. package/dist/state/agent.js +101 -0
  121. package/dist/state/cache.d.ts +16 -0
  122. package/dist/state/cache.js +92 -0
  123. package/dist/state/cache.test.d.ts +2 -0
  124. package/dist/state/cache.test.js +76 -0
  125. package/dist/state/delta.d.ts +71 -0
  126. package/dist/state/delta.js +234 -0
  127. package/dist/state/delta.test.d.ts +2 -0
  128. package/dist/state/delta.test.js +279 -0
  129. package/dist/state/groups.d.ts +15 -0
  130. package/dist/state/groups.js +80 -0
  131. package/dist/state/session.d.ts +30 -0
  132. package/dist/state/session.js +155 -0
  133. package/dist/tools/accessibility.d.ts +14 -0
  134. package/dist/tools/accessibility.js +32 -0
  135. package/dist/tools/ask.d.ts +14 -0
  136. package/dist/tools/ask.js +49 -0
  137. package/dist/tools/automation.d.ts +36 -0
  138. package/dist/tools/automation.js +218 -0
  139. package/dist/tools/browser_detect.d.ts +13 -0
  140. package/dist/tools/browser_detect.js +73 -0
  141. package/dist/tools/burst.d.ts +35 -0
  142. package/dist/tools/burst.js +344 -0
  143. package/dist/tools/canvas.d.ts +25 -0
  144. package/dist/tools/canvas.js +132 -0
  145. package/dist/tools/captcha.d.ts +38 -0
  146. package/dist/tools/captcha.js +209 -0
  147. package/dist/tools/click.d.ts +36 -0
  148. package/dist/tools/click.js +561 -0
  149. package/dist/tools/click_and_wait.d.ts +33 -0
  150. package/dist/tools/click_and_wait.js +149 -0
  151. package/dist/tools/clipboard.d.ts +29 -0
  152. package/dist/tools/clipboard.js +279 -0
  153. package/dist/tools/console.d.ts +21 -0
  154. package/dist/tools/console.js +107 -0
  155. package/dist/tools/cookies.d.ts +67 -0
  156. package/dist/tools/cookies.js +121 -0
  157. package/dist/tools/design_extract.d.ts +109 -0
  158. package/dist/tools/design_extract.js +641 -0
  159. package/dist/tools/dialog.d.ts +24 -0
  160. package/dist/tools/dialog.js +109 -0
  161. package/dist/tools/drag.d.ts +15 -0
  162. package/dist/tools/drag.js +29 -0
  163. package/dist/tools/emulation.d.ts +33 -0
  164. package/dist/tools/emulation.js +69 -0
  165. package/dist/tools/eval.d.ts +20 -0
  166. package/dist/tools/eval.js +92 -0
  167. package/dist/tools/fill.d.ts +41 -0
  168. package/dist/tools/fill.js +667 -0
  169. package/dist/tools/find.d.ts +30 -0
  170. package/dist/tools/find.js +60 -0
  171. package/dist/tools/geo.d.ts +19 -0
  172. package/dist/tools/geo.js +29 -0
  173. package/dist/tools/github/analyze.d.ts +4 -0
  174. package/dist/tools/github/analyze.js +113 -0
  175. package/dist/tools/github/cache.d.ts +21 -0
  176. package/dist/tools/github/cache.js +28 -0
  177. package/dist/tools/github/index.d.ts +4 -0
  178. package/dist/tools/github/index.js +4 -0
  179. package/dist/tools/github/issues.d.ts +9 -0
  180. package/dist/tools/github/issues.js +88 -0
  181. package/dist/tools/github/pr.d.ts +5 -0
  182. package/dist/tools/github/pr.js +88 -0
  183. package/dist/tools/github/rateLimiter.d.ts +4 -0
  184. package/dist/tools/github/rateLimiter.js +46 -0
  185. package/dist/tools/github/read.d.ts +34 -0
  186. package/dist/tools/github/read.js +74 -0
  187. package/dist/tools/github/repoContext.d.ts +20 -0
  188. package/dist/tools/github/repoContext.js +129 -0
  189. package/dist/tools/highlight.d.ts +20 -0
  190. package/dist/tools/highlight.js +140 -0
  191. package/dist/tools/hover.d.ts +19 -0
  192. package/dist/tools/hover.js +57 -0
  193. package/dist/tools/inspect.d.ts +17 -0
  194. package/dist/tools/inspect.js +125 -0
  195. package/dist/tools/integration.test.d.ts +2 -0
  196. package/dist/tools/integration.test.js +96 -0
  197. package/dist/tools/key.d.ts +25 -0
  198. package/dist/tools/key.js +173 -0
  199. package/dist/tools/macro_compiler.d.ts +7 -0
  200. package/dist/tools/macro_compiler.js +82 -0
  201. package/dist/tools/mouse.d.ts +21 -0
  202. package/dist/tools/mouse.js +62 -0
  203. package/dist/tools/navigate.d.ts +16 -0
  204. package/dist/tools/navigate.js +188 -0
  205. package/dist/tools/network_intercept.d.ts +17 -0
  206. package/dist/tools/network_intercept.js +151 -0
  207. package/dist/tools/office/index.d.ts +9 -0
  208. package/dist/tools/office/index.js +7 -0
  209. package/dist/tools/office/list.d.ts +15 -0
  210. package/dist/tools/office/list.js +41 -0
  211. package/dist/tools/office/parse.d.ts +20 -0
  212. package/dist/tools/office/parse.js +172 -0
  213. package/dist/tools/pdf.d.ts +22 -0
  214. package/dist/tools/pdf.js +42 -0
  215. package/dist/tools/performance.d.ts +12 -0
  216. package/dist/tools/performance.js +47 -0
  217. package/dist/tools/performance_test.d.ts +70 -0
  218. package/dist/tools/performance_test.js +128 -0
  219. package/dist/tools/plan.d.ts +39 -0
  220. package/dist/tools/plan.js +161 -0
  221. package/dist/tools/pointer.d.ts +35 -0
  222. package/dist/tools/pointer.js +522 -0
  223. package/dist/tools/read.d.ts +30 -0
  224. package/dist/tools/read.js +488 -0
  225. package/dist/tools/recording.d.ts +45 -0
  226. package/dist/tools/recording.js +124 -0
  227. package/dist/tools/response.d.ts +129 -0
  228. package/dist/tools/response.js +5 -0
  229. package/dist/tools/scrape.d.ts +24 -0
  230. package/dist/tools/scrape.js +140 -0
  231. package/dist/tools/screenshot.d.ts +40 -0
  232. package/dist/tools/screenshot.js +79 -0
  233. package/dist/tools/scroll.d.ts +23 -0
  234. package/dist/tools/scroll.js +196 -0
  235. package/dist/tools/select.d.ts +13 -0
  236. package/dist/tools/select.js +217 -0
  237. package/dist/tools/snapshot.d.ts +27 -0
  238. package/dist/tools/snapshot.js +99 -0
  239. package/dist/tools/storage.d.ts +18 -0
  240. package/dist/tools/storage.js +29 -0
  241. package/dist/tools/tabs.d.ts +77 -0
  242. package/dist/tools/tabs.js +342 -0
  243. package/dist/tools/thought.d.ts +16 -0
  244. package/dist/tools/thought.js +79 -0
  245. package/dist/tools/type.d.ts +65 -0
  246. package/dist/tools/type.js +1061 -0
  247. package/dist/tools/type_secret.d.ts +23 -0
  248. package/dist/tools/type_secret.js +229 -0
  249. package/dist/tools/upload.d.ts +19 -0
  250. package/dist/tools/upload.js +103 -0
  251. package/dist/tools/video_compiler.d.ts +24 -0
  252. package/dist/tools/video_compiler.js +199 -0
  253. package/dist/tools/viewport.d.ts +44 -0
  254. package/dist/tools/viewport.js +230 -0
  255. package/dist/tools/wait.d.ts +19 -0
  256. package/dist/tools/wait.js +202 -0
  257. package/dist/tools/wait_for.d.ts +23 -0
  258. package/dist/tools/wait_for.js +30 -0
  259. package/dist/tools/windows.d.ts +22 -0
  260. package/dist/tools/windows.js +57 -0
  261. package/dist/ui/index.d.ts +1 -0
  262. package/dist/ui/index.js +12 -0
  263. package/docs/DEV_MODE.md +68 -0
  264. package/docs/DOCUMENTATION.md +532 -0
  265. package/docs/PLUGINS.md +538 -0
  266. package/docs/TOOLS.md +104 -0
  267. package/docs/dev_plugin.md +310 -0
  268. package/ext-dist/background.js +467 -0
  269. package/ext-dist/content.js +3089 -0
  270. package/ext-dist/dashboard.html +583 -0
  271. package/ext-dist/hud/hud.js +73 -0
  272. package/ext-dist/hud/index.html +20 -0
  273. package/ext-dist/manifest.json +77 -0
  274. package/ext-dist/sidepanel/sidepanel.js +25 -0
  275. package/ext-dist/sidepanel.html +547 -0
  276. package/ext-dist/styles.css +218 -0
  277. package/icon.png +0 -0
  278. package/mcp.json +796 -0
  279. package/package.json +113 -0
  280. package/server.json +23 -0
  281. package/smithery.yaml +11 -0
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Weavetab — audit/logger.ts
3
+ * Appends structured action records to ~/.weavetab-system/logs/mission_<timestamp>.log and JSON lines to mission_<timestamp>.jsonl.
4
+ * Only writes logs when devMode is true to prevent storage spam.
5
+ * Weavetab by fy2ne
6
+ */
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { broadcastTelemetry } from "./telemetry.js";
10
+ import { getSystemDir, getCachedConfig } from "../config/loader.js";
11
+ // Mission logs always go to the system dir (~/.weavetab-system/logs)
12
+ // to avoid polluting user workspaces with large log files.
13
+ const missionId = new Date().toISOString().replace(/[:.]/g, "-");
14
+ function getLogDir() { return getSystemDir("logs"); }
15
+ function getLogPath() { return path.join(getLogDir(), `mission_${missionId}.log`); }
16
+ function getJsonLogPath() { return path.join(getLogDir(), `mission_${missionId}.jsonl`); }
17
+ // Check if logging should be enabled based on devMode
18
+ let _isCheckingConfig = false;
19
+ function isLoggingEnabled() {
20
+ if (_isCheckingConfig)
21
+ return false;
22
+ try {
23
+ _isCheckingConfig = true;
24
+ const config = getCachedConfig();
25
+ return config ? (config.devMode || false) : false;
26
+ }
27
+ catch {
28
+ return false; // Fail safe - no logging if config can't be read
29
+ }
30
+ finally {
31
+ _isCheckingConfig = false;
32
+ }
33
+ }
34
+ // Redact sensitive data patterns from logs
35
+ function redactSensitiveData(text) {
36
+ if (!text || typeof text !== 'string')
37
+ return text;
38
+ let redacted = text;
39
+ // Redact common secret patterns
40
+ const patterns = [
41
+ // API keys, tokens, secrets in URLs
42
+ /([?&](api_key|token|secret|password|credential|auth|access_token|refresh_token)=[^&\s]+)/gi,
43
+ // Bearer tokens
44
+ /(Bearer\s+[A-Za-z0-9\-._~+/]+)/gi,
45
+ // Basic auth in URLs
46
+ /(\/\/[^:]+:[^@]+@)/g,
47
+ // UUIDs that might be tokens
48
+ /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,
49
+ // Long alphanumeric strings that might be keys (30+ chars)
50
+ /\b[A-Za-z0-9+/]{30,}={0,2}\b/g,
51
+ ];
52
+ for (const pattern of patterns) {
53
+ redacted = redacted.replace(pattern, (match) => {
54
+ // Keep the parameter name but redact the value
55
+ if (match.includes('=')) {
56
+ const [key, value] = match.split('=');
57
+ return `${key}=[REDACTED]`;
58
+ }
59
+ return '[REDACTED]';
60
+ });
61
+ }
62
+ return redacted;
63
+ }
64
+ const plainBuffer = { lines: [], timer: null };
65
+ const jsonBuffer = { lines: [], timer: null };
66
+ function scheduleFlush(buffer, filePath) {
67
+ if (buffer.timer)
68
+ return; // already scheduled
69
+ buffer.timer = setTimeout(() => {
70
+ buffer.timer = null;
71
+ if (buffer.lines.length === 0)
72
+ return;
73
+ const chunk = buffer.lines.join("");
74
+ buffer.lines = [];
75
+ fs.appendFile(filePath, chunk, () => { });
76
+ }, 100);
77
+ buffer.timer.unref?.();
78
+ }
79
+ // On exit, flush remaining buffered lines synchronously so no log is lost
80
+ process.on("exit", () => {
81
+ try {
82
+ if (plainBuffer.lines.length > 0) {
83
+ if (plainBuffer.timer) {
84
+ clearTimeout(plainBuffer.timer);
85
+ plainBuffer.timer = null;
86
+ }
87
+ fs.appendFileSync(getLogPath(), plainBuffer.lines.join(""), "utf-8");
88
+ plainBuffer.lines = [];
89
+ }
90
+ if (jsonBuffer.lines.length > 0) {
91
+ if (jsonBuffer.timer) {
92
+ clearTimeout(jsonBuffer.timer);
93
+ jsonBuffer.timer = null;
94
+ }
95
+ fs.appendFileSync(getJsonLogPath(), jsonBuffer.lines.join(""), "utf-8");
96
+ jsonBuffer.lines = [];
97
+ }
98
+ }
99
+ catch { /* ignore — best-effort */ }
100
+ });
101
+ export function logAction(action, target, result) {
102
+ try {
103
+ // Redact sensitive data before logging
104
+ // Use a session-agnostic redaction for general logs (no specific session ID)
105
+ const redactedTarget = redactSensitiveData(target);
106
+ const redactedResult = redactSensitiveData(result);
107
+ // Always broadcast telemetry (separate from file logging)
108
+ broadcastTelemetry({ type: "log", payload: { action, target: redactedTarget, result: redactedResult } });
109
+ // Only write to log files if devMode is enabled
110
+ if (!isLoggingEnabled()) {
111
+ return;
112
+ }
113
+ const timestamp = new Date().toISOString();
114
+ const paddedAction = action.padEnd(10);
115
+ const line = `[${timestamp}] ${paddedAction} ${redactedTarget} → ${redactedResult}\n`;
116
+ // Ensure the log directory exists (only if we're about to write the first line)
117
+ if (plainBuffer.lines.length === 0) {
118
+ const dir = getLogDir();
119
+ if (!fs.existsSync(dir))
120
+ fs.mkdirSync(dir, { recursive: true });
121
+ }
122
+ plainBuffer.lines.push(line);
123
+ scheduleFlush(plainBuffer, getLogPath());
124
+ }
125
+ catch (err) {
126
+ const msg = err instanceof Error ? err.message : String(err);
127
+ process.stderr.write(`[!] Weave warning: Failed to write audit log: ${msg}\n`);
128
+ }
129
+ }
130
+ export function logActionStructured(entry) {
131
+ try {
132
+ // Redact sensitive data before logging
133
+ const redactedEntry = {
134
+ ...entry,
135
+ parameters: entry.parameters ? redactSensitiveDataInObject(entry.parameters) : undefined,
136
+ pageUrl: entry.pageUrl ? redactSensitiveData(entry.pageUrl) : undefined,
137
+ result: entry.result ? redactSensitiveData(entry.result) : "",
138
+ };
139
+ // Only write to log files if devMode is enabled
140
+ if (!isLoggingEnabled()) {
141
+ return;
142
+ }
143
+ const line = JSON.stringify(redactedEntry) + "\n";
144
+ if (jsonBuffer.lines.length === 0) {
145
+ const dir = getLogDir();
146
+ if (!fs.existsSync(dir))
147
+ fs.mkdirSync(dir, { recursive: true });
148
+ }
149
+ jsonBuffer.lines.push(line);
150
+ scheduleFlush(jsonBuffer, getJsonLogPath());
151
+ }
152
+ catch (err) {
153
+ const msg = err instanceof Error ? err.message : String(err);
154
+ process.stderr.write(`[!] Weave warning: Failed to write structured audit log: ${msg}\n`);
155
+ }
156
+ }
157
+ // Recursively redact sensitive data in objects
158
+ function redactSensitiveDataInObject(obj) {
159
+ const result = {};
160
+ for (const [key, value] of Object.entries(obj)) {
161
+ if (typeof value === 'string') {
162
+ result[key] = redactSensitiveData(value);
163
+ }
164
+ else if (typeof value === 'object' && value !== null) {
165
+ result[key] = redactSensitiveDataInObject(value);
166
+ }
167
+ else {
168
+ result[key] = value;
169
+ }
170
+ }
171
+ return result;
172
+ }
173
+ export function logDebug(message, ...optionalParams) {
174
+ if (!isLoggingEnabled())
175
+ return;
176
+ process.stderr.write(`${message} ${optionalParams.join(' ')}\n`);
177
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Weavetab — audit/telemetry.ts
3
+ * UDP IPC for broadcasting live telemetry to the CLI dashboard without breaking stdio MCP.
4
+ * Also tracks tool usage analytics locally (Phase 10g).
5
+ * Weavetab by fy2ne
6
+ */
7
+ export interface TelemetryEvent {
8
+ type: "log" | "state" | "browser" | "tool_start" | "tool_end" | "chat";
9
+ timestamp: number;
10
+ payload: any;
11
+ }
12
+ export interface ToolUsageEntry {
13
+ tool: string;
14
+ domain: string;
15
+ duration: number;
16
+ success: boolean;
17
+ timestamp: string;
18
+ }
19
+ export interface ToolUsageStats {
20
+ totalCalls: number;
21
+ perTool: Record<string, {
22
+ calls: number;
23
+ failures: number;
24
+ avgDurationMs: number;
25
+ }>;
26
+ perDomain: Record<string, {
27
+ calls: number;
28
+ failures: number;
29
+ }>;
30
+ recentEntries: ToolUsageEntry[];
31
+ }
32
+ export declare function recordToolUsage(entry: ToolUsageEntry): void;
33
+ export declare function getToolUsageStats(): ToolUsageStats;
34
+ export declare function clearToolUsageStats(): void;
35
+ /** Broadcast a telemetry event to any listening dashboard (wb run) */
36
+ export declare function broadcastTelemetry(event: Omit<TelemetryEvent, "timestamp">): void;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Weavetab — audit/telemetry.ts
3
+ * UDP IPC for broadcasting live telemetry to the CLI dashboard without breaking stdio MCP.
4
+ * Also tracks tool usage analytics locally (Phase 10g).
5
+ * Weavetab by fy2ne
6
+ */
7
+ import * as dgram from "node:dgram";
8
+ import * as fs from "node:fs";
9
+ import * as path from "node:path";
10
+ import * as os from "node:os";
11
+ import * as crypto from "node:crypto";
12
+ const TELEMETRY_PORT = 9223;
13
+ const STATS_FILE = path.join(os.homedir(), ".weavetab-system", "stats.json");
14
+ let udpSocket = null;
15
+ // Generate or load telemetry secret for authentication
16
+ const TELEMETRY_SECRET_FILE = path.join(os.homedir(), ".weavetab-system", "telemetry-secret");
17
+ let telemetrySecret;
18
+ function getTelemetrySecret() {
19
+ if (telemetrySecret)
20
+ return telemetrySecret;
21
+ try {
22
+ if (fs.existsSync(TELEMETRY_SECRET_FILE)) {
23
+ telemetrySecret = fs.readFileSync(TELEMETRY_SECRET_FILE, "utf-8").trim();
24
+ }
25
+ else {
26
+ telemetrySecret = crypto.randomBytes(32).toString("hex");
27
+ const dir = path.dirname(TELEMETRY_SECRET_FILE);
28
+ if (!fs.existsSync(dir))
29
+ fs.mkdirSync(dir, { recursive: true });
30
+ fs.writeFileSync(TELEMETRY_SECRET_FILE, telemetrySecret, { mode: 0o600 });
31
+ }
32
+ }
33
+ catch {
34
+ telemetrySecret = crypto.randomBytes(32).toString("hex");
35
+ }
36
+ return telemetrySecret;
37
+ }
38
+ let statsCache = null;
39
+ function loadStats() {
40
+ if (statsCache)
41
+ return statsCache;
42
+ try {
43
+ const data = fs.readFileSync(STATS_FILE, "utf-8");
44
+ statsCache = JSON.parse(data);
45
+ return statsCache;
46
+ }
47
+ catch {
48
+ statsCache = {
49
+ totalCalls: 0,
50
+ perTool: {},
51
+ perDomain: {},
52
+ recentEntries: [],
53
+ };
54
+ return statsCache;
55
+ }
56
+ }
57
+ let statsDirty = false;
58
+ let saveStatsTimer = null;
59
+ function saveStats() {
60
+ if (!statsCache)
61
+ return;
62
+ // Mark as dirty and schedule an async flush at most once per 5 seconds.
63
+ // Previously this was a synchronous writeFileSync on every tool call,
64
+ // blocking the event loop. Now we batch writes to avoid that overhead.
65
+ statsDirty = true;
66
+ if (!saveStatsTimer) {
67
+ saveStatsTimer = setTimeout(() => {
68
+ saveStatsTimer = null;
69
+ if (!statsDirty || !statsCache)
70
+ return;
71
+ statsDirty = false;
72
+ const dir = path.dirname(STATS_FILE);
73
+ try {
74
+ if (!fs.existsSync(dir))
75
+ fs.mkdirSync(dir, { recursive: true });
76
+ const data = JSON.stringify(statsCache, null, 2);
77
+ fs.writeFile(STATS_FILE, data, () => { });
78
+ }
79
+ catch { /* ignore */ }
80
+ }, 5000);
81
+ // Allow the timer to not prevent process exit
82
+ saveStatsTimer.unref?.();
83
+ }
84
+ }
85
+ // Flush remaining dirty stats synchronously on exit so no data is lost
86
+ process.on("exit", () => {
87
+ if (!statsDirty || !statsCache)
88
+ return;
89
+ try {
90
+ const dir = path.dirname(STATS_FILE);
91
+ if (!fs.existsSync(dir))
92
+ fs.mkdirSync(dir, { recursive: true });
93
+ fs.writeFileSync(STATS_FILE, JSON.stringify(statsCache, null, 2));
94
+ }
95
+ catch { /* ignore */ }
96
+ });
97
+ export function recordToolUsage(entry) {
98
+ const s = loadStats();
99
+ s.totalCalls++;
100
+ if (!s.perTool[entry.tool])
101
+ s.perTool[entry.tool] = { calls: 0, failures: 0, avgDurationMs: 0 };
102
+ const t = s.perTool[entry.tool];
103
+ t.calls++;
104
+ if (!entry.success)
105
+ t.failures++;
106
+ t.avgDurationMs = Math.round((t.avgDurationMs * (t.calls - 1) + entry.duration) / t.calls);
107
+ const domain = entry.domain || "(unknown)";
108
+ if (!s.perDomain[domain])
109
+ s.perDomain[domain] = { calls: 0, failures: 0 };
110
+ s.perDomain[domain].calls++;
111
+ if (!entry.success)
112
+ s.perDomain[domain].failures++;
113
+ s.recentEntries.unshift(entry);
114
+ if (s.recentEntries.length > 1000)
115
+ s.recentEntries.length = 1000;
116
+ saveStats();
117
+ }
118
+ export function getToolUsageStats() {
119
+ return loadStats();
120
+ }
121
+ export function clearToolUsageStats() {
122
+ statsCache = {
123
+ totalCalls: 0,
124
+ perTool: {},
125
+ perDomain: {},
126
+ recentEntries: [],
127
+ };
128
+ saveStats();
129
+ }
130
+ /** Broadcast a telemetry event to any listening dashboard (wb run) */
131
+ export function broadcastTelemetry(event) {
132
+ if (!udpSocket) {
133
+ try {
134
+ udpSocket = dgram.createSocket("udp4");
135
+ // Prevent the socket from keeping the process alive
136
+ udpSocket.unref();
137
+ }
138
+ catch {
139
+ return;
140
+ }
141
+ }
142
+ const fullEvent = {
143
+ ...event,
144
+ timestamp: Date.now()
145
+ };
146
+ try {
147
+ const secret = getTelemetrySecret();
148
+ const payload = JSON.stringify(fullEvent);
149
+ const signature = crypto.createHmac("sha256", secret).update(payload).digest("hex");
150
+ const message = {
151
+ sig: signature,
152
+ payload: payload
153
+ };
154
+ const msg = Buffer.from(JSON.stringify(message) + "\n");
155
+ udpSocket.send(msg, 0, msg.length, TELEMETRY_PORT, "127.0.0.1", (err) => {
156
+ // Ignore broadcast errors (e.g. no dashboard listening)
157
+ });
158
+ }
159
+ catch {
160
+ // Ignore stringify/send errors
161
+ }
162
+ }
@@ -0,0 +1,16 @@
1
+ import type { Config, BrowserEngine } from "../config/loader.js";
2
+ import CDP from "chrome-remote-interface";
3
+ export interface BrowserInfo {
4
+ name: string;
5
+ path: string;
6
+ }
7
+ export declare function findBrowser(preferred?: string): BrowserInfo | null;
8
+ export declare function findFirefox(): BrowserInfo | null;
9
+ export declare function probeAllBrowsers(): BrowserInfo[];
10
+ export declare function launchWithBridge(config: Config): Promise<{
11
+ path: string;
12
+ name: string;
13
+ port: number;
14
+ engine?: BrowserEngine;
15
+ }>;
16
+ export declare function injectStealthScript(session: CDP.Client): Promise<void>;