argusqa-os 9.4.0 → 9.4.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.
package/README.md CHANGED
@@ -227,7 +227,7 @@ All network findings carry an `origin` field (`'first-party'` / `'third-party'`)
227
227
 
228
228
  | Severity | Bug / Issue | Detection Method |
229
229
  | --- | --- | --- |
230
- | 🔴 Critical | > 100 detached DOM nodes in V8 heap — severe leak | `take_memory_snapshot` → parse flat nodes array for "Detached Xxx" names |
230
+ | 🔴 Critical | > 100 detached DOM nodes in V8 heap — severe leak | `take_heapsnapshot` → parse flat nodes array for "Detached Xxx" names |
231
231
  | 🟡 Warning | > 10 detached DOM nodes in V8 heap — probable leak | Same snapshot parse, lower threshold |
232
232
  | 🟡 Warning | Heap grew > 2 MB after navigate-away + navigate-back — probable per-load leak | `performance.memory.usedJSHeapSize` delta across round-trip (soft — GC-dependent) |
233
233
 
@@ -328,7 +328,7 @@ Argus watches your running application and automatically surfaces issues that te
328
328
  | **Accessibility Snapshot Analysis** | Calls `take_snapshot` then `evaluate_script`; flags interactive elements missing accessible names, unlabelled form controls, duplicate landmark regions, heading level skips, and `aria-expanded` buttons with missing/broken `aria-controls` |
329
329
  | **Keyboard Focus Analysis** | Tab-walks every focusable element (up to 20 steps); detects `focus_visible_missing` (button/link with `outline:0` and no `box-shadow` fallback — keyboard users cannot see where focus is) |
330
330
  | **Chrome DevTools Issues Panel** | Queries `list_console_messages({ types: ['issue'] })` for the Issues panel namespace, which is entirely separate from `console.error`; catches CSP violations and deprecated API usage (verified) — additional Chrome-surfaced types (CORS blocks, mixed content, cookie misconfiguration, low-contrast) are classified when present |
331
- | **Mobile CPU Throttling** | Applies 4× CPU throttle (`emulate_cpu({ throttlingRate: 4 })`) during ≤768px responsive breakpoints — finds layout reflow and animation jank that only manifests under realistic mobile CPU pressure |
331
+ | **Mobile CPU Throttling** | Applies 4× CPU throttle (`emulate({ cpuThrottlingRate: 4 })`) during ≤768px responsive breakpoints — finds layout reflow and animation jank that only manifests under realistic mobile CPU pressure |
332
332
  | **Origin-Tagged Network Findings** | All network error and timing findings carry `origin: 'first-party' \| 'third-party'` so operators can triage critical first-party failures without digging through third-party CDN noise |
333
333
  | **Historical Baselines** | Saves finding keys after each run; subsequent runs only alert on *new* issues; trend summary in Slack digest |
334
334
  | **Flakiness Detection** | Crawls each route twice per run; findings in both runs are confirmed (original severity); findings in only one run are marked flaky (`severity: info`, `:zap: _flaky_` label) |
@@ -972,7 +972,7 @@ argus/
972
972
  | CSS analysis | Script injected via `evaluate_script` | Runs in page context so it sees the live computed styles, CSS Modules hashes, and React fiber properties |
973
973
  | Responsive viewport | `emulate` (not `resize_page`) | `resize_page` only resizes the browser window and does not update CSS viewport width — `emulate` is the correct API |
974
974
  | Viewport width measurement | `document.documentElement.clientWidth` | After `emulate` with mobile flag, `window.innerWidth` returns the legacy layout viewport (~952px), not the device width |
975
- | V8 heap snapshot | `take_memory_snapshot({ filePath })` → read from disk | The MCP tool writes JSON to disk (not inline); parse with `JSON.parse(fs.readFileSync(filePath))` then delete the temp file |
975
+ | V8 heap snapshot | `take_heapsnapshot({ filePath })` → read from disk | The MCP tool writes JSON to disk (not inline); parse with `JSON.parse(fs.readFileSync(filePath))` then delete the temp file |
976
976
  | Detached DOM detection | Walk flat `nodes` array for "Detached " prefix in strings table | Chrome serializes detached elements as "Detached HTMLDivElement" etc.; secondary check on `detachedness === 2` (Chrome 90+) |
977
977
  | Baseline finding key | `type::message[:100]::status` | Excludes timestamps and dynamic URL path IDs; message truncated to 100 chars to handle slight wording variations; `::status` suffix only added when non-null |
978
978
  | Baseline alert filter | `isNew === true` (strict) | Only findings explicitly marked new by `applyBaseline` are dispatched to Slack — prevents stale re-dispatch if baseline-manager is not called (fails silently rather than spamming) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "argusqa-os",
3
- "version": "9.4.0",
3
+ "version": "9.4.2",
4
4
  "mcpName": "io.github.ironclawdevs27/argus",
5
5
  "description": "Argus — AI-powered automated dev-testing platform using Chrome DevTools MCP and Claude Code",
6
6
  "keywords": [
@@ -27,7 +27,7 @@ export class CdpBrowserAdapter {
27
27
  evaluate(fn) { return this._mcp.evaluate_script({ function: fn }); }
28
28
  snapshot() { return this._mcp.take_snapshot(); }
29
29
  screenshot(opts = {}) { return this._mcp.take_screenshot(opts); }
30
- heapSnapshot(opts = {}) { return this._mcp.take_memory_snapshot(opts); }
30
+ heapSnapshot(opts = {}) { return this._mcp.take_heapsnapshot(opts); }
31
31
 
32
32
  // ── Interactions ────────────────────────────────────────────────────────────
33
33
  // click is intentionally NOT retried — it is not idempotent (submits forms,
@@ -46,7 +46,7 @@ export class CdpBrowserAdapter {
46
46
 
47
47
  // ── Viewport ────────────────────────────────────────────────────────────────
48
48
  emulate(viewport) { return this._mcp.emulate({ viewport }); }
49
- emulateCpu(rate) { return this._mcp.emulate_cpu({ throttlingRate: rate }); }
49
+ emulateCpu(rate) { return this._mcp.emulate({ cpuThrottlingRate: rate }); }
50
50
  resize(w, h) { return this._mcp.resize_page({ width: w, height: h }); }
51
51
 
52
52
  // ── Network & performance ───────────────────────────────────────────────────
package/src/mcp-server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Argus MCP Server (v9.4.0)
3
+ * Argus MCP Server (v9.4.2)
4
4
  *
5
5
  * Exposes Argus as an MCP server so Claude (or any MCP client) can call
6
6
  * argus_audit, argus_audit_full, argus_compare, argus_last_report, and
@@ -138,11 +138,23 @@ async function handleAudit({ url, critical = false, cache = false }) {
138
138
  return { content: [{ type: 'text', text: JSON.stringify({ ...result, _cached: true, _cachedAt: new Date(ts).toISOString() }, null, 2) }] };
139
139
  }
140
140
  return withMcp(async (mcp) => {
141
- const parsed = new URL(url);
142
- const route = { path: parsed.pathname + parsed.search + parsed.hash, name: 'audit', critical };
143
- const findings = await crawlRouteCheap(route, parsed.origin, mcp);
144
- if (cache) cacheAudit(url, findings);
145
- return { content: [{ type: 'text', text: JSON.stringify(findings, null, 2) }] };
141
+ const parsed = new URL(url);
142
+ const route = { path: parsed.pathname + parsed.search + parsed.hash, name: 'audit', critical };
143
+ const raw = await crawlRouteCheap(route, parsed.origin, mcp);
144
+ const findings = Array.isArray(raw.errors) ? raw.errors : [];
145
+ const result = {
146
+ findings,
147
+ summary: {
148
+ critical: findings.filter(f => f.severity === 'critical').length,
149
+ warning: findings.filter(f => f.severity === 'warning').length,
150
+ info: findings.filter(f => f.severity === 'info').length,
151
+ },
152
+ url: raw.url,
153
+ pageTitle: raw.pageTitle,
154
+ screenshot: raw.screenshot,
155
+ };
156
+ if (cache) cacheAudit(url, result);
157
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
146
158
  });
147
159
  }
148
160
 
@@ -271,7 +283,7 @@ async function handleLastReport() {
271
283
  // ── Server bootstrap ──────────────────────────────────────────────────────────
272
284
 
273
285
  const server = new Server(
274
- { name: 'argus', version: '9.4.0' },
286
+ { name: 'argus', version: '9.4.2' },
275
287
  { capabilities: { tools: {} } },
276
288
  );
277
289
 
@@ -251,13 +251,12 @@ export async function createMcpClient() {
251
251
  performance_start_trace: (args) => tool('performance_start_trace', args),
252
252
  performance_stop_trace: (args) => tool('performance_stop_trace', args),
253
253
  performance_analyze_insight: (args) => tool('performance_analyze_insight', args),
254
- take_memory_snapshot: (args) => tool('take_memory_snapshot', args),
254
+ take_heapsnapshot: (args) => tool('take_heapsnapshot', args),
255
255
  lighthouse_audit: (args) => tool('lighthouse_audit', args),
256
256
  handle_dialog: (args) => tool('handle_dialog', args),
257
257
  drag: (args) => tool('drag', args),
258
258
  upload_file: (args) => tool('upload_file', args),
259
259
  select_option: (args) => tool('select_option', args),
260
- emulate_cpu: (args) => tool('emulate_cpu', args),
261
260
  emulate_network: (args) => tool('emulate_network', args),
262
261
  close,
263
262
  };
@@ -2,7 +2,7 @@
2
2
  * ARGUS Memory Analyzer (v3 Phase B1)
3
3
  *
4
4
  * Two detection surfaces:
5
- * 1. Detached DOM nodes — via take_memory_snapshot (saves snapshot to disk, parsed here)
5
+ * 1. Detached DOM nodes — via take_heapsnapshot (saves snapshot to disk, parsed here)
6
6
  * Nodes removed from the live DOM but still referenced in JS are retained
7
7
  * in the V8 heap as "Detached HTMLXxx" objects, causing memory pressure.
8
8
  * 2. Heap size growth — via performance.memory across navigate-away + navigate-back
@@ -11,7 +11,7 @@
11
11
  * Called as a standalone function after crawlRoute, like analyzeResponsive.
12
12
  * The function always leaves the browser navigated to the target URL.
13
13
  *
14
- * Note: take_memory_snapshot requires a filePath argument — it writes the V8
14
+ * Note: take_heapsnapshot requires a filePath argument — it writes the V8
15
15
  * heap snapshot JSON to disk. We read and parse it, then delete the temp file.
16
16
  */
17
17
 
@@ -115,7 +115,7 @@ async function getHeapSize(browser) {
115
115
 
116
116
  /**
117
117
  * Take a V8 heap snapshot, save it to a temp file, parse it, and delete the file.
118
- * take_memory_snapshot writes the snapshot JSON to disk (filePath is required).
118
+ * take_heapsnapshot writes the snapshot JSON to disk (filePath is required).
119
119
  *
120
120
  * @param {object} browser - CdpBrowserAdapter
121
121
  * @returns {Promise<{ detachedNodeCount: number, totalNodeCount: number|null } | null>}
@@ -153,7 +153,7 @@ async function captureAndParseSnapshot(browser) {
153
153
  * Analyse a URL for memory leaks.
154
154
  *
155
155
  * Detection 1 — Detached DOM nodes (hard, deterministic):
156
- * Navigate to url → wait → take_memory_snapshot to disk → parse for "Detached Xxx" nodes.
156
+ * Navigate to url → wait → take_heapsnapshot to disk → parse for "Detached Xxx" nodes.
157
157
  * Detached nodes are DOM elements removed from the live tree but still referenced
158
158
  * in JS (e.g. stashed in a closure, array, or event handler), preventing GC.
159
159
  *