@ia-qa/qa-discovery 0.1.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 (84) hide show
  1. package/README.md +378 -0
  2. package/ROADMAP.md +321 -0
  3. package/dist/ai/classify.d.ts +170 -0
  4. package/dist/ai/classify.js +431 -0
  5. package/dist/ai/classify.js.map +1 -0
  6. package/dist/browser/extractPage.d.ts +41 -0
  7. package/dist/browser/extractPage.js +427 -0
  8. package/dist/browser/extractPage.js.map +1 -0
  9. package/dist/browser/openables.d.ts +19 -0
  10. package/dist/browser/openables.js +127 -0
  11. package/dist/browser/openables.js.map +1 -0
  12. package/dist/capture/page.d.ts +128 -0
  13. package/dist/capture/page.js +78 -0
  14. package/dist/capture/page.js.map +1 -0
  15. package/dist/citations.d.ts +80 -0
  16. package/dist/citations.js +197 -0
  17. package/dist/citations.js.map +1 -0
  18. package/dist/classificationView.d.ts +118 -0
  19. package/dist/classificationView.js +178 -0
  20. package/dist/classificationView.js.map +1 -0
  21. package/dist/cli/args.d.ts +3 -0
  22. package/dist/cli/args.js +38 -0
  23. package/dist/cli/args.js.map +1 -0
  24. package/dist/cli/history.d.ts +1 -0
  25. package/dist/cli/history.js +35 -0
  26. package/dist/cli/history.js.map +1 -0
  27. package/dist/cli/index.d.ts +5 -0
  28. package/dist/cli/index.js +172 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/cli/login.d.ts +29 -0
  31. package/dist/cli/login.js +179 -0
  32. package/dist/cli/login.js.map +1 -0
  33. package/dist/cli/scan.d.ts +11 -0
  34. package/dist/cli/scan.js +246 -0
  35. package/dist/cli/scan.js.map +1 -0
  36. package/dist/cli-ai/index.d.ts +7 -0
  37. package/dist/cli-ai/index.js +360 -0
  38. package/dist/cli-ai/index.js.map +1 -0
  39. package/dist/config.d.ts +112 -0
  40. package/dist/config.js +178 -0
  41. package/dist/config.js.map +1 -0
  42. package/dist/diff.d.ts +12 -0
  43. package/dist/diff.js +43 -0
  44. package/dist/diff.js.map +1 -0
  45. package/dist/explore.d.ts +59 -0
  46. package/dist/explore.js +119 -0
  47. package/dist/explore.js.map +1 -0
  48. package/dist/healingLink.d.ts +30 -0
  49. package/dist/healingLink.js +127 -0
  50. package/dist/healingLink.js.map +1 -0
  51. package/dist/history.d.ts +38 -0
  52. package/dist/history.js +120 -0
  53. package/dist/history.js.map +1 -0
  54. package/dist/htmlReport.d.ts +2 -0
  55. package/dist/htmlReport.js +371 -0
  56. package/dist/htmlReport.js.map +1 -0
  57. package/dist/index.d.ts +24 -0
  58. package/dist/index.js +65 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/mcp/sandbox.d.ts +9 -0
  61. package/dist/mcp/sandbox.js +82 -0
  62. package/dist/mcp/sandbox.js.map +1 -0
  63. package/dist/mcp/server.d.ts +120 -0
  64. package/dist/mcp/server.js +315 -0
  65. package/dist/mcp/server.js.map +1 -0
  66. package/dist/network.d.ts +60 -0
  67. package/dist/network.js +99 -0
  68. package/dist/network.js.map +1 -0
  69. package/dist/overview.d.ts +40 -0
  70. package/dist/overview.js +370 -0
  71. package/dist/overview.js.map +1 -0
  72. package/dist/overviewFile.d.ts +31 -0
  73. package/dist/overviewFile.js +101 -0
  74. package/dist/overviewFile.js.map +1 -0
  75. package/dist/scan.d.ts +159 -0
  76. package/dist/scan.js +402 -0
  77. package/dist/scan.js.map +1 -0
  78. package/dist/sharedCalls.d.ts +28 -0
  79. package/dist/sharedCalls.js +61 -0
  80. package/dist/sharedCalls.js.map +1 -0
  81. package/dist/taxonomy.d.ts +77 -0
  82. package/dist/taxonomy.js +153 -0
  83. package/dist/taxonomy.js.map +1 -0
  84. package/package.json +61 -0
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.STALE_CAVEAT = exports.CLASSIFICATION_CAVEAT = void 0;
37
+ exports.classificationPath = classificationPath;
38
+ exports.loadClassification = loadClassification;
39
+ exports.buildClassificationView = buildClassificationView;
40
+ exports.classificationHeadline = classificationHeadline;
41
+ exports.classificationFor = classificationFor;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const config_1 = require("./config");
45
+ const classify_1 = require("./ai/classify");
46
+ /** Where `ia-qa-discover-ai` writes. Never `capture/`: that directory is pages. */
47
+ function classificationPath(cwd = (0, config_1.baseDir)()) {
48
+ return path.join(cwd, config_1.CONFIG_DIR, classify_1.CLASSIFICATION_FILENAME);
49
+ }
50
+ /**
51
+ * Read the classification, or null when there is none.
52
+ *
53
+ * A file whose `schema` is not the one this build understands is treated as
54
+ * absent rather than parsed optimistically: a report that renders fields it
55
+ * guessed at is worse than a report with no classification section.
56
+ */
57
+ function loadClassification(file = classificationPath()) {
58
+ if (!fs.existsSync(file))
59
+ return null;
60
+ try {
61
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
62
+ if (parsed?.schema !== classify_1.CLASSIFICATION_SCHEMA || typeof parsed.pages !== 'object' || !parsed.pages)
63
+ return null;
64
+ return parsed;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ /** True when the page was captured after it was classified. */
71
+ function isStale(page, classifiedAt) {
72
+ if (!page?.capturedAt)
73
+ return false;
74
+ const captured = Date.parse(page.capturedAt);
75
+ const classified = Date.parse(classifiedAt);
76
+ if (Number.isNaN(captured) || Number.isNaN(classified))
77
+ return false;
78
+ return captured > classified;
79
+ }
80
+ /**
81
+ * Pair a classification document with the capture the reports are rendering.
82
+ *
83
+ * `pages` is the current capture, so every count below is relative to what is on
84
+ * disk NOW — a classification naming a page that no longer exists is dropped
85
+ * from the view entirely rather than shown against nothing.
86
+ */
87
+ function buildClassificationView(doc, pages) {
88
+ // `entryPointsOf` is the one definition of "this page has something to
89
+ // classify" — the AI CLI decides what to send with it, so the report must
90
+ // count what was NOT classified with the same function or the two disagree.
91
+ const hasEntryPoints = (p) => (0, classify_1.entryPointsOf)(p).length > 0;
92
+ const byName = new Map(pages.map((p) => [p.page, p]));
93
+ const out = [];
94
+ for (const [name, result] of Object.entries(doc.pages)) {
95
+ if (!byName.has(name))
96
+ continue;
97
+ out.push({
98
+ page: name,
99
+ kept: result.kept ?? [],
100
+ refused: result.refused ?? [],
101
+ dropped: (result.dropped ?? []).length,
102
+ failed: result.failed?.reason,
103
+ stale: isStale(byName.get(name), doc.classifiedAt),
104
+ });
105
+ }
106
+ out.sort((a, b) => a.page.localeCompare(b.page));
107
+ const candidates = pages.filter(hasEntryPoints);
108
+ const seen = new Set(out.filter((p) => !p.failed).map((p) => p.page));
109
+ const unclassified = candidates.filter((p) => !seen.has(p.page)).map((p) => p.page).sort();
110
+ const tools = new Map();
111
+ for (const p of out) {
112
+ for (const k of p.kept) {
113
+ for (const t of k.securityTools ?? []) {
114
+ if (!tools.has(t.tool))
115
+ tools.set(t.tool, new Set());
116
+ tools.get(t.tool).add(p.page);
117
+ }
118
+ }
119
+ }
120
+ return {
121
+ model: doc.model,
122
+ classifiedAt: doc.classifiedAt,
123
+ taxonomyVersion: doc.taxonomyVersion,
124
+ pages: out,
125
+ unclassified,
126
+ noEntryPoints: pages.length - candidates.length,
127
+ staleCount: out.filter((p) => p.stale).length,
128
+ keptTotal: out.reduce((n, p) => n + p.kept.length, 0),
129
+ refusedTotal: out.reduce((n, p) => n + p.refused.length, 0),
130
+ droppedTotal: out.reduce((n, p) => n + p.dropped, 0),
131
+ securityTools: [...tools.entries()].map(([tool, ps]) => ({ tool, pages: [...ps].sort() })).sort((a, b) => a.tool.localeCompare(b.tool)),
132
+ };
133
+ }
134
+ /**
135
+ * The one-line summary both renderers put at the top of the section.
136
+ *
137
+ * It leads with what was kept and immediately qualifies it, because a bare
138
+ * "12 classified" is precisely the confident number this package spends its
139
+ * design on refusing to print.
140
+ */
141
+ function classificationHeadline(v) {
142
+ const bits = [`${v.keptTotal} classified`];
143
+ if (v.refusedTotal > 0)
144
+ bits.push(`${v.refusedTotal} refused`);
145
+ if (v.droppedTotal > 0)
146
+ bits.push(`${v.droppedTotal} dropped`);
147
+ if (v.unclassified.length > 0)
148
+ bits.push(`${v.unclassified.length} page(s) not classified`);
149
+ if (v.staleCount > 0)
150
+ bits.push(`${v.staleCount} stale`);
151
+ return bits.join(' · ');
152
+ }
153
+ /**
154
+ * The reminder, deliberately softer than the deterministic one.
155
+ *
156
+ * The tool already refuses a claim whose citation does not resolve, so telling
157
+ * the reader to check everything again would be alarm fatigue — a warning that
158
+ * fires on the good rows too is a warning people learn to skip, and then it is
159
+ * not there when it matters. So it names the exact residual risk instead: the
160
+ * check proved the citation, never the reading built on it.
161
+ */
162
+ exports.CLASSIFICATION_CAVEAT = 'Every line below cites something this tool resolved in the capture and checked — a claim whose ' +
163
+ 'citation did not exist, or did not say what it was claimed to say, was dropped before you saw it. ' +
164
+ 'That proves the premise, not the conclusion: a model can cite a real password field and still be ' +
165
+ 'wrong about what the page is for. Read the `why` and the confidence, not the label alone.';
166
+ /** Why a stale row is not simply out of date. */
167
+ exports.STALE_CAVEAT = 'These pages were re-captured after they were classified, so the reading below describes a version ' +
168
+ 'of the page that is no longer on disk. Re-run `ia-qa-discover-ai classify` before relying on them.';
169
+ /**
170
+ * The whole read-and-pair step, for a caller that has pages and wants the
171
+ * section. Returns null when there is no classification — the normal case, and
172
+ * every renderer must be complete without one.
173
+ */
174
+ function classificationFor(pages, file = classificationPath()) {
175
+ const doc = loadClassification(file);
176
+ return doc ? buildClassificationView(doc, pages) : null;
177
+ }
178
+ //# sourceMappingURL=classificationView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classificationView.js","sourceRoot":"","sources":["../src/classificationView.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** Flag validation for the hand-rolled CLI parser — mirrors `@ia-qa/self-healing`'s `cli/args.ts`. */
2
+ export declare const KNOWN_FLAGS: Record<string, readonly string[]>;
3
+ export declare function rejectUnknownFlags(command: string, args: string[]): void;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KNOWN_FLAGS = void 0;
4
+ exports.rejectUnknownFlags = rejectUnknownFlags;
5
+ /** Flag validation for the hand-rolled CLI parser — mirrors `@ia-qa/self-healing`'s `cli/args.ts`. */
6
+ exports.KNOWN_FLAGS = {
7
+ scan: [
8
+ '--depth',
9
+ '--max',
10
+ '--no-reveal',
11
+ '--strict-host',
12
+ '--session',
13
+ '--no-network',
14
+ '--no-sitemap',
15
+ '--deep',
16
+ '--deep-budget',
17
+ '--network-threshold',
18
+ '--save',
19
+ '--json',
20
+ '--report',
21
+ '--open',
22
+ ],
23
+ login: ['--url', '--session'],
24
+ history: ['--json', '--limit'],
25
+ };
26
+ function rejectUnknownFlags(command, args) {
27
+ const known = exports.KNOWN_FLAGS[command];
28
+ if (known === undefined)
29
+ return;
30
+ for (const a of args) {
31
+ if (!a.startsWith('-'))
32
+ continue;
33
+ if (known.includes(a))
34
+ continue;
35
+ throw new Error(`Unknown flag "${a}" for "ia-qa-discover ${command}". Run \`ia-qa-discover help\` to see its flags.`);
36
+ }
37
+ }
38
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":";;;AAsBA,gDAQC;AA9BD,sGAAsG;AACzF,QAAA,WAAW,GAAsC;IAC5D,IAAI,EAAE;QACJ,SAAS;QACT,OAAO;QACP,aAAa;QACb,eAAe;QACf,WAAW;QACX,cAAc;QACd,cAAc;QACd,QAAQ;QACR,eAAe;QACf,qBAAqB;QACrB,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,QAAQ;KACT;IACD,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC;IAC7B,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC/B,CAAC;AAEF,SAAgB,kBAAkB,CAAC,OAAe,EAAE,IAAc;IAChE,MAAM,KAAK,GAAG,mBAAW,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QAChC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,yBAAyB,OAAO,kDAAkD,CAAC,CAAC;IACxH,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function runHistory(args: string[]): Promise<void>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runHistory = runHistory;
4
+ const history_1 = require("../history");
5
+ function intArg(args, flag, fallback, max) {
6
+ const i = args.indexOf(flag);
7
+ if (i === -1)
8
+ return fallback;
9
+ const raw = args[i + 1];
10
+ const n = Number(raw);
11
+ if (!raw || !Number.isInteger(n) || n < 1 || n > max) {
12
+ throw new Error(`${flag} needs a whole number between 1 and ${max}.`);
13
+ }
14
+ return n;
15
+ }
16
+ async function runHistory(args) {
17
+ const limit = intArg(args, '--limit', history_1.HISTORY_LIMIT, history_1.HISTORY_LIMIT);
18
+ const entries = (0, history_1.readHistory)(limit);
19
+ if (args.includes('--json')) {
20
+ console.log(JSON.stringify(entries, null, 2));
21
+ return;
22
+ }
23
+ if (entries.length === 0) {
24
+ console.log('No scan history yet — run `ia-qa-discover scan <url>` first.');
25
+ return;
26
+ }
27
+ for (const e of entries) {
28
+ const churn = e.added.length + e.removed.length + e.changed.length;
29
+ console.log(`${e.ts} ${e.pagesCaptured}/${e.pagesFound} pages ` +
30
+ `+${e.added.length} -${e.removed.length} ~${e.changed.length}` +
31
+ (churn === 0 ? ' (no change)' : '') +
32
+ (e.commit ? ` ${e.commit}${e.branch ? ` (${e.branch})` : ''}` : ''));
33
+ }
34
+ }
35
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/cli/history.ts"],"names":[],"mappings":";;AAaA,gCAuBC;AApCD,wCAAwD;AAExD,SAAS,MAAM,CAAC,IAAc,EAAE,IAAY,EAAE,QAAgB,EAAE,GAAW;IACzE,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,uCAAuC,GAAG,GAAG,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAEM,KAAK,UAAU,UAAU,CAAC,IAAc;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,uBAAa,EAAE,uBAAa,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,IAAA,qBAAW,EAAC,KAAK,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACnE,OAAO,CAAC,GAAG,CACT,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,UAAU,UAAU;YACnD,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE;YAC9D,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;YACpC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACvE,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ declare const HELP = "ia-qa-discover \u2014 deterministic surface reconnaissance for a web app\n\nF1 of QA Discovery: crawl an app's same-origin pages and capture what each one\nis structurally about \u2014 title, headings, forms, and observed API calls. No\nLLM, no verdict: this is recon, not a gate. Runs entirely on your machine;\nnothing is uploaded anywhere.\n\n[FOR AI AGENTS] You are driving this for a human. Run `scan` to capture the\napp's surface, then read .ia-qa-discovery/capture/surface.json (the index) and\nthe per-page files it points at \u2014 that is the evidence for anything you\nreport about this app. Never assert a page's purpose or risk from the URL\nalone; ground it in the captured meta/headings/forms/apiCalls. Never read a\npage count as coverage: if scan warns that pages still show a login form, you\nmeasured the wall, not the app. You CANNOT run `login` \u2014 it waits for a human\nat a browser; ask them to run it, then scan again.\n\nUsage:\n ia-qa-discover login Open a visible browser, log in by hand, keep the\n session (.ia-qa-discovery/session.json) for scan\n to reuse. The general answer to a login wall: it\n models nothing, so SSO, MFA, a consent screen or\n a magic link all work \u2014 you do them.\n Needs a terminal, refuses under CI. The file holds\n live cookies: gitignored, stays on this machine.\n --url <path> page to open (default: your baseUrl)\n --session <f> where to write it\n Already have a storageState (a Playwright globalSetup\n writes one)? Skip this verb: point scan at it with\n --session <file> or \"session\" in config.json.\n ia-qa-discover scan [url] Crawl same-origin pages from <url> (or the saved\n baseUrl) and capture each page's structure.\n Writes .ia-qa-discovery/capture/, diffs against\n the previous run, appends history.jsonl.\n --depth <n> crawl link-depth (default 2)\n --max <n> page cap (default 60)\n --deep also open menus, tabs and dialogs one level\n down and capture the fields they reveal. Every\n non-GET request is blocked for the duration,\n so a click cannot mutate anything server-side.\n --deep-budget <n> max controls clicked per page (default 60)\n --no-sitemap do not read sitemap.xml / robots.txt\n --no-reveal do not open menus/dropdowns for hidden links\n --strict-host treat www./apex as different hosts\n --session <file> reuse an existing storageState (login) file\n --no-network skip API/network-call capture this run\n --network-threshold <n> shared-call promotion threshold 0-1 (default 0.6)\n --save persist this baseUrl to config.json\n --json machine-readable summary on stdout\n --report [file.html] branded ia-qa.com HTML dossier of the\n app (default ia-qa-discover-report.html)\n --open open that report in the browser\n ia-qa-discover history Recorded scan runs \u2014 pages/forms/API-surface trend.\n --json --limit <n> (default 500)\n ia-qa-discover version Print the installed version (also --version, -v)\n ia-qa-discover help Show this help\n\nGlobal:\n --config <dir> Resolve .ia-qa-discovery/ under <dir> instead of the\n current directory. Also IAQA_DISCOVERY_CONFIG_DIR=<dir>.\n";
3
+ declare function verbHelp(command: string | undefined): string | null;
4
+ declare function main(): Promise<void>;
5
+ export { HELP, verbHelp, main };
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.HELP = void 0;
5
+ exports.verbHelp = verbHelp;
6
+ exports.main = main;
7
+ const config_1 = require("../config");
8
+ const args_1 = require("./args");
9
+ const scan_1 = require("./scan");
10
+ const login_1 = require("./login");
11
+ const history_1 = require("./history");
12
+ // dist/cli/ → package root; npm ships package.json in every tarball.
13
+ const VERSION = require('../../package.json').version;
14
+ const HELP = `ia-qa-discover — deterministic surface reconnaissance for a web app
15
+
16
+ F1 of QA Discovery: crawl an app's same-origin pages and capture what each one
17
+ is structurally about — title, headings, forms, and observed API calls. No
18
+ LLM, no verdict: this is recon, not a gate. Runs entirely on your machine;
19
+ nothing is uploaded anywhere.
20
+
21
+ [FOR AI AGENTS] You are driving this for a human. Run \`scan\` to capture the
22
+ app's surface, then read .ia-qa-discovery/capture/surface.json (the index) and
23
+ the per-page files it points at — that is the evidence for anything you
24
+ report about this app. Never assert a page's purpose or risk from the URL
25
+ alone; ground it in the captured meta/headings/forms/apiCalls. Never read a
26
+ page count as coverage: if scan warns that pages still show a login form, you
27
+ measured the wall, not the app. You CANNOT run \`login\` — it waits for a human
28
+ at a browser; ask them to run it, then scan again.
29
+
30
+ Usage:
31
+ ia-qa-discover login Open a visible browser, log in by hand, keep the
32
+ session (.ia-qa-discovery/session.json) for scan
33
+ to reuse. The general answer to a login wall: it
34
+ models nothing, so SSO, MFA, a consent screen or
35
+ a magic link all work — you do them.
36
+ Needs a terminal, refuses under CI. The file holds
37
+ live cookies: gitignored, stays on this machine.
38
+ --url <path> page to open (default: your baseUrl)
39
+ --session <f> where to write it
40
+ Already have a storageState (a Playwright globalSetup
41
+ writes one)? Skip this verb: point scan at it with
42
+ --session <file> or "session" in config.json.
43
+ ia-qa-discover scan [url] Crawl same-origin pages from <url> (or the saved
44
+ baseUrl) and capture each page's structure.
45
+ Writes .ia-qa-discovery/capture/, diffs against
46
+ the previous run, appends history.jsonl.
47
+ --depth <n> crawl link-depth (default 2)
48
+ --max <n> page cap (default 60)
49
+ --deep also open menus, tabs and dialogs one level
50
+ down and capture the fields they reveal. Every
51
+ non-GET request is blocked for the duration,
52
+ so a click cannot mutate anything server-side.
53
+ --deep-budget <n> max controls clicked per page (default 60)
54
+ --no-sitemap do not read sitemap.xml / robots.txt
55
+ --no-reveal do not open menus/dropdowns for hidden links
56
+ --strict-host treat www./apex as different hosts
57
+ --session <file> reuse an existing storageState (login) file
58
+ --no-network skip API/network-call capture this run
59
+ --network-threshold <n> shared-call promotion threshold 0-1 (default 0.6)
60
+ --save persist this baseUrl to config.json
61
+ --json machine-readable summary on stdout
62
+ --report [file.html] branded ia-qa.com HTML dossier of the
63
+ app (default ia-qa-discover-report.html)
64
+ --open open that report in the browser
65
+ ia-qa-discover history Recorded scan runs — pages/forms/API-surface trend.
66
+ --json --limit <n> (default 500)
67
+ ia-qa-discover version Print the installed version (also --version, -v)
68
+ ia-qa-discover help Show this help
69
+
70
+ Global:
71
+ --config <dir> Resolve .ia-qa-discovery/ under <dir> instead of the
72
+ current directory. Also IAQA_DISCOVERY_CONFIG_DIR=<dir>.
73
+ `;
74
+ exports.HELP = HELP;
75
+ function extractConfigDir(argv) {
76
+ const rest = [];
77
+ let dir;
78
+ for (let i = 0; i < argv.length; i++) {
79
+ const a = argv[i];
80
+ if (a === '--config') {
81
+ const next = argv[i + 1];
82
+ if (next === undefined || next.startsWith('-')) {
83
+ throw new Error('--config needs a directory argument, e.g. --config apps/xsp.');
84
+ }
85
+ dir = next;
86
+ i++;
87
+ continue;
88
+ }
89
+ if (a.startsWith('--config=')) {
90
+ dir = a.slice('--config='.length);
91
+ continue;
92
+ }
93
+ rest.push(a);
94
+ }
95
+ return { dir, rest };
96
+ }
97
+ function verbHelp(command) {
98
+ if (!command || command.startsWith('-'))
99
+ return null;
100
+ const lines = HELP.split('\n');
101
+ const start = lines.findIndex((l) => l.startsWith(` ia-qa-discover ${command} `) || l === ` ia-qa-discover ${command}`);
102
+ if (start === -1)
103
+ return null;
104
+ const block = [lines[start]];
105
+ for (let i = start + 1; i < lines.length; i++) {
106
+ const l = lines[i];
107
+ if (/^\s{4,}\S/.test(l))
108
+ block.push(l);
109
+ else
110
+ break;
111
+ }
112
+ const flags = args_1.KNOWN_FLAGS[command];
113
+ const tail = flags?.length ? `\n\nFlags: ${flags.join(' ')}` : '\n\n(no flags)';
114
+ return `${block.join('\n')}${tail}\n\nFull help: ia-qa-discover help`;
115
+ }
116
+ async function main() {
117
+ const { dir: configDir, rest: argv } = extractConfigDir(process.argv.slice(2));
118
+ if (configDir !== undefined) {
119
+ if (!configDir)
120
+ throw new Error('--config needs a directory argument.');
121
+ (0, config_1.setBaseDir)(configDir);
122
+ }
123
+ const [command, ...rest] = argv;
124
+ if (rest.includes('--help') || rest.includes('-h')) {
125
+ console.log(verbHelp(command) ?? HELP);
126
+ return;
127
+ }
128
+ if (command !== undefined)
129
+ (0, args_1.rejectUnknownFlags)(command, rest);
130
+ switch (command) {
131
+ case 'scan':
132
+ await (0, scan_1.runScan)(rest);
133
+ break;
134
+ case 'login':
135
+ await (0, login_1.runLogin)(rest);
136
+ break;
137
+ case 'history':
138
+ await (0, history_1.runHistory)(rest);
139
+ break;
140
+ case 'version':
141
+ case '--version':
142
+ case '-v':
143
+ console.log(VERSION);
144
+ break;
145
+ case 'help':
146
+ case '--help':
147
+ case '-h':
148
+ case undefined:
149
+ console.log(HELP);
150
+ break;
151
+ default:
152
+ console.error(`Unknown command "${command}".\n\n${HELP}`);
153
+ process.exit(1);
154
+ }
155
+ }
156
+ /**
157
+ * Only when this file IS the process.
158
+ *
159
+ * `main()` reads argv and can `process.exit`, so an unconditional call made
160
+ * this module impossible to import and therefore impossible to test — and the
161
+ * verb dispatch is exactly where a CLI/MCP divergence would hide. Same guard,
162
+ * and same reason, as `@ia-qa/self-healing`'s MCP server.
163
+ */
164
+ if (require.main === module)
165
+ run();
166
+ function run() {
167
+ main().catch((err) => {
168
+ console.error(`\n❌ ${err instanceof Error ? err.message : String(err)}`);
169
+ process.exit(1);
170
+ });
171
+ }
172
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `ia-qa-discover login` — open a browser, let a human log in, keep the session.
3
+ *
4
+ * Same reasoning as `ia-qa-heal login`, and for the same reason: any
5
+ * declarative `auth` block models ONE shape of login (a username field, a
6
+ * password field, a submit button), so federated SSO, MFA, a consent screen, a
7
+ * magic link or a device check are each out of reach of every selector list.
8
+ * This verb models nothing at all — the human does whatever the app asks — so
9
+ * it is the general answer to a login wall.
10
+ *
11
+ * Measured need: on a real target, an anonymous scan reached 3 of 11 routes;
12
+ * everything worth discovering was behind the wall, and the app's catch-all
13
+ * route served the login page at every unknown URL with HTTP 200 — so the scan
14
+ * reported "3 pages captured" and looked like a success.
15
+ *
16
+ * Deliberately not automatic, and never part of a pipeline:
17
+ * - The only caller here that opens a VISIBLE browser. Every capture path
18
+ * stays headless: a window in CI is a hung build.
19
+ * - Refuses without a TTY and under CI — it waits for a person twice over (at
20
+ * the browser, then at the keyboard), so a pipeline that runs it just hangs.
21
+ * That refusal is aimed squarely at an agent driving this CLI: it cannot
22
+ * perform this step, and must ask the human to.
23
+ * - What it writes is a SECRET (live cookies). The path and that fact are
24
+ * announced BEFORE the browser opens, and `.ia-qa-discovery/.gitignore` is
25
+ * written on the way out. Nothing here uploads it.
26
+ *
27
+ * Not an MCP tool, on purpose. See `mcp/server.ts`.
28
+ */
29
+ export declare function runLogin(args: string[]): Promise<void>;