argusqa-os 9.4.1 → 9.4.3

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.1",
3
+ "version": "9.4.3",
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": [
@@ -54,15 +54,13 @@
54
54
  "@modelcontextprotocol/sdk": "^1.29.0",
55
55
  "@opentelemetry/api": "^1.9.1",
56
56
  "@opentelemetry/sdk-node": "^0.218.0",
57
- "@slack/web-api": "^7.3.4",
58
- "chrome": "^0.1.0",
57
+ "@slack/web-api": "^7.16.0",
59
58
  "dotenv": "^16.4.5",
60
- "express": "^4.19.2",
59
+ "express": "^5.2.1",
61
60
  "pino": "^10.3.1",
62
61
  "pino-pretty": "^13.1.3",
63
- "pixelmatch": "^5.3.0",
62
+ "pixelmatch": "^7.2.0",
64
63
  "pngjs": "^7.0.0",
65
- "sharp": "^0.33.4",
66
64
  "zod": "^4.4.3"
67
65
  },
68
66
  "devDependencies": {
@@ -1,86 +1,86 @@
1
- /**
2
- * CdpBrowserAdapter — facade over chrome-devtools-mcp.
3
- *
4
- * All analyzer modules call browser.* methods instead of mcp.* directly.
5
- * This single file is the only place that knows the chrome-devtools-mcp
6
- * API shape, so any future API change (parameter renames, new tool versions)
7
- * requires editing exactly one file.
8
- *
9
- * listConsole() and listNetwork() parse the markdown-text format that
10
- * chrome-devtools-mcp@latest returns, so callers always receive structured
11
- * arrays rather than raw text.
12
- *
13
- * listConsoleRaw(args) passes arbitrary args through unparsed — used by
14
- * issues-analyzer.js which calls list_console_messages({ types: ['issue'] }).
15
- */
16
-
17
- import { parseConsoleMsgResponse, parseNetworkReqResponse } from '../utils/mcp-parsers.js';
18
- import { withRetry } from '../utils/retry.js';
19
-
20
- export class CdpBrowserAdapter {
21
- constructor(mcp) { this._mcp = mcp; }
22
-
23
- // ── Navigation ──────────────────────────────────────────────────────────────
24
- navigate(url) { return withRetry(() => this._mcp.navigate_page({ url }), { label: `navigate(${url})` }); }
25
-
26
- // ── Evaluation & snapshots ──────────────────────────────────────────────────
27
- evaluate(fn) { return this._mcp.evaluate_script({ function: fn }); }
28
- snapshot() { return this._mcp.take_snapshot(); }
29
- screenshot(opts = {}) { return this._mcp.take_screenshot(opts); }
30
- heapSnapshot(opts = {}) { return this._mcp.take_memory_snapshot(opts); }
31
-
32
- // ── Interactions ────────────────────────────────────────────────────────────
33
- // click is intentionally NOT retried — it is not idempotent (submits forms,
34
- // toggles state, triggers deletions). A retry after an ambiguous MCP timeout
35
- // cannot distinguish "Chrome never received the click" from "Chrome processed
36
- // it but the pipe dropped the response" — firing twice causes duplicate actions.
37
- click(uid) { return this._mcp.click({ uid }); }
38
- fill(uid, value) { return withRetry(() => this._mcp.fill({ uid, value }), { label: `fill(${uid})` }); }
39
- type(text) { return this._mcp.type_text({ text }); }
40
- pressKey(key) { return this._mcp.press_key({ key }); }
41
- hover(uid) { return this._mcp.hover({ uid }); }
42
- drag(src, tgt) { return this._mcp.drag({ from_uid: src, to_uid: tgt }); }
43
- uploadFile(uid, filePath) { return this._mcp.upload_file({ uid, filePath }); }
44
- handleDialog(accept, promptText = '') { return this._mcp.handle_dialog({ accept, promptText }); }
45
- waitFor(opts) { return this._mcp.wait_for(opts); }
46
-
47
- // ── Viewport ────────────────────────────────────────────────────────────────
48
- emulate(viewport) { return this._mcp.emulate({ viewport }); }
49
- emulateCpu(rate) { return this._mcp.emulate_cpu({ throttlingRate: rate }); }
50
- resize(w, h) { return this._mcp.resize_page({ width: w, height: h }); }
51
-
52
- // ── Network & performance ───────────────────────────────────────────────────
53
- getNetworkRequest(reqId) { return this._mcp.get_network_request({ requestId: reqId }); }
54
- lighthouse(url, opts = {}) { return this._mcp.lighthouse_audit({ url, ...opts }); }
55
- startTrace() { return this._mcp.performance_start_trace({}); }
56
- stopTrace() { return this._mcp.performance_stop_trace({}); }
57
- analyzeInsight(opts) { return this._mcp.performance_analyze_insight(opts); }
58
-
59
- // ── Tab management ─────────────────────────────────────────────────────────
60
- listPages() { return this._mcp.list_pages({}); }
61
- selectPage(tabId) { return this._mcp.select_page({ pageId: tabId }); }
62
-
63
- // ── Lifecycle ───────────────────────────────────────────────────────────────
64
- close() { return this._mcp.close(); }
65
-
66
- // ── Console & network lists (text-parsed) ───────────────────────────────────
67
-
68
- /** Returns structured array from list_console_messages (parses markdown text). */
69
- async listConsole() {
70
- const raw = await this._mcp.list_console_messages({});
71
- return parseConsoleMsgResponse(raw);
72
- }
73
-
74
- /** Returns structured array from list_network_requests (parses markdown text). */
75
- async listNetwork() {
76
- const raw = await this._mcp.list_network_requests({});
77
- return parseNetworkReqResponse(raw);
78
- }
79
-
80
- /**
81
- * Raw pass-through to list_console_messages with custom args.
82
- * Used by issues-analyzer.js for the DevTools Issues panel
83
- * (types: ['issue']) which returns structured data, not text.
84
- */
85
- listConsoleRaw(args = {}) { return this._mcp.list_console_messages(args); }
86
- }
1
+ /**
2
+ * CdpBrowserAdapter — facade over chrome-devtools-mcp.
3
+ *
4
+ * All analyzer modules call browser.* methods instead of mcp.* directly.
5
+ * This single file is the only place that knows the chrome-devtools-mcp
6
+ * API shape, so any future API change (parameter renames, new tool versions)
7
+ * requires editing exactly one file.
8
+ *
9
+ * listConsole() and listNetwork() parse the markdown-text format that
10
+ * chrome-devtools-mcp@latest returns, so callers always receive structured
11
+ * arrays rather than raw text.
12
+ *
13
+ * listConsoleRaw(args) passes arbitrary args through unparsed — used by
14
+ * issues-analyzer.js which calls list_console_messages({ types: ['issue'] }).
15
+ */
16
+
17
+ import { parseConsoleMsgResponse, parseNetworkReqResponse } from '../utils/mcp-parsers.js';
18
+ import { withRetry } from '../utils/retry.js';
19
+
20
+ export class CdpBrowserAdapter {
21
+ constructor(mcp) { this._mcp = mcp; }
22
+
23
+ // ── Navigation ──────────────────────────────────────────────────────────────
24
+ navigate(url) { return withRetry(() => this._mcp.navigate_page({ url }), { label: `navigate(${url})` }); }
25
+
26
+ // ── Evaluation & snapshots ──────────────────────────────────────────────────
27
+ evaluate(fn) { return this._mcp.evaluate_script({ function: fn }); }
28
+ snapshot() { return this._mcp.take_snapshot(); }
29
+ screenshot(opts = {}) { return this._mcp.take_screenshot(opts); }
30
+ heapSnapshot(opts = {}) { return this._mcp.take_heapsnapshot(opts); }
31
+
32
+ // ── Interactions ────────────────────────────────────────────────────────────
33
+ // click is intentionally NOT retried — it is not idempotent (submits forms,
34
+ // toggles state, triggers deletions). A retry after an ambiguous MCP timeout
35
+ // cannot distinguish "Chrome never received the click" from "Chrome processed
36
+ // it but the pipe dropped the response" — firing twice causes duplicate actions.
37
+ click(uid) { return this._mcp.click({ uid }); }
38
+ fill(uid, value) { return withRetry(() => this._mcp.fill({ uid, value }), { label: `fill(${uid})` }); }
39
+ type(text) { return this._mcp.type_text({ text }); }
40
+ pressKey(key) { return this._mcp.press_key({ key }); }
41
+ hover(uid) { return this._mcp.hover({ uid }); }
42
+ drag(src, tgt) { return this._mcp.drag({ from_uid: src, to_uid: tgt }); }
43
+ uploadFile(uid, filePath) { return this._mcp.upload_file({ uid, filePath }); }
44
+ handleDialog(accept, promptText = '') { return this._mcp.handle_dialog({ accept, promptText }); }
45
+ waitFor(opts) { return this._mcp.wait_for(opts); }
46
+
47
+ // ── Viewport ────────────────────────────────────────────────────────────────
48
+ emulate(viewport) { return this._mcp.emulate({ viewport }); }
49
+ emulateCpu(rate) { return this._mcp.emulate({ cpuThrottlingRate: rate }); }
50
+ resize(w, h) { return this._mcp.resize_page({ width: w, height: h }); }
51
+
52
+ // ── Network & performance ───────────────────────────────────────────────────
53
+ getNetworkRequest(reqId) { return this._mcp.get_network_request({ requestId: reqId }); }
54
+ lighthouse(url, opts = {}) { return this._mcp.lighthouse_audit({ url, ...opts }); }
55
+ startTrace() { return this._mcp.performance_start_trace({}); }
56
+ stopTrace() { return this._mcp.performance_stop_trace({}); }
57
+ analyzeInsight(opts) { return this._mcp.performance_analyze_insight(opts); }
58
+
59
+ // ── Tab management ─────────────────────────────────────────────────────────
60
+ listPages() { return this._mcp.list_pages({}); }
61
+ selectPage(tabId) { return this._mcp.select_page({ pageId: tabId }); }
62
+
63
+ // ── Lifecycle ───────────────────────────────────────────────────────────────
64
+ close() { return this._mcp.close(); }
65
+
66
+ // ── Console & network lists (text-parsed) ───────────────────────────────────
67
+
68
+ /** Returns structured array from list_console_messages (parses markdown text). */
69
+ async listConsole() {
70
+ const raw = await this._mcp.list_console_messages({});
71
+ return parseConsoleMsgResponse(raw);
72
+ }
73
+
74
+ /** Returns structured array from list_network_requests (parses markdown text). */
75
+ async listNetwork() {
76
+ const raw = await this._mcp.list_network_requests({});
77
+ return parseNetworkReqResponse(raw);
78
+ }
79
+
80
+ /**
81
+ * Raw pass-through to list_console_messages with custom args.
82
+ * Used by issues-analyzer.js for the DevTools Issues panel
83
+ * (types: ['issue']) which returns structured data, not text.
84
+ */
85
+ listConsoleRaw(args = {}) { return this._mcp.list_console_messages(args); }
86
+ }