@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.
- package/README.md +378 -0
- package/ROADMAP.md +321 -0
- package/dist/ai/classify.d.ts +170 -0
- package/dist/ai/classify.js +431 -0
- package/dist/ai/classify.js.map +1 -0
- package/dist/browser/extractPage.d.ts +41 -0
- package/dist/browser/extractPage.js +427 -0
- package/dist/browser/extractPage.js.map +1 -0
- package/dist/browser/openables.d.ts +19 -0
- package/dist/browser/openables.js +127 -0
- package/dist/browser/openables.js.map +1 -0
- package/dist/capture/page.d.ts +128 -0
- package/dist/capture/page.js +78 -0
- package/dist/capture/page.js.map +1 -0
- package/dist/citations.d.ts +80 -0
- package/dist/citations.js +197 -0
- package/dist/citations.js.map +1 -0
- package/dist/classificationView.d.ts +118 -0
- package/dist/classificationView.js +178 -0
- package/dist/classificationView.js.map +1 -0
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.js +38 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/history.d.ts +1 -0
- package/dist/cli/history.js +35 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +172 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/login.d.ts +29 -0
- package/dist/cli/login.js +179 -0
- package/dist/cli/login.js.map +1 -0
- package/dist/cli/scan.d.ts +11 -0
- package/dist/cli/scan.js +246 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli-ai/index.d.ts +7 -0
- package/dist/cli-ai/index.js +360 -0
- package/dist/cli-ai/index.js.map +1 -0
- package/dist/config.d.ts +112 -0
- package/dist/config.js +178 -0
- package/dist/config.js.map +1 -0
- package/dist/diff.d.ts +12 -0
- package/dist/diff.js +43 -0
- package/dist/diff.js.map +1 -0
- package/dist/explore.d.ts +59 -0
- package/dist/explore.js +119 -0
- package/dist/explore.js.map +1 -0
- package/dist/healingLink.d.ts +30 -0
- package/dist/healingLink.js +127 -0
- package/dist/healingLink.js.map +1 -0
- package/dist/history.d.ts +38 -0
- package/dist/history.js +120 -0
- package/dist/history.js.map +1 -0
- package/dist/htmlReport.d.ts +2 -0
- package/dist/htmlReport.js +371 -0
- package/dist/htmlReport.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/sandbox.d.ts +9 -0
- package/dist/mcp/sandbox.js +82 -0
- package/dist/mcp/sandbox.js.map +1 -0
- package/dist/mcp/server.d.ts +120 -0
- package/dist/mcp/server.js +315 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/network.d.ts +60 -0
- package/dist/network.js +99 -0
- package/dist/network.js.map +1 -0
- package/dist/overview.d.ts +40 -0
- package/dist/overview.js +370 -0
- package/dist/overview.js.map +1 -0
- package/dist/overviewFile.d.ts +31 -0
- package/dist/overviewFile.js +101 -0
- package/dist/overviewFile.js.map +1 -0
- package/dist/scan.d.ts +159 -0
- package/dist/scan.js +402 -0
- package/dist/scan.js.map +1 -0
- package/dist/sharedCalls.d.ts +28 -0
- package/dist/sharedCalls.js +61 -0
- package/dist/sharedCalls.js.map +1 -0
- package/dist/taxonomy.d.ts +77 -0
- package/dist/taxonomy.js +153 -0
- package/dist/taxonomy.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.HELP = exports.CLASSIFICATION_FILENAME = exports.CLASSIFICATION_SCHEMA = void 0;
|
|
38
|
+
exports.main = main;
|
|
39
|
+
exports.runClassify = runClassify;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const self_healing_1 = require("@ia-qa/self-healing");
|
|
43
|
+
const config_1 = require("../config");
|
|
44
|
+
const citations_1 = require("../citations");
|
|
45
|
+
const classify_1 = require("../ai/classify");
|
|
46
|
+
Object.defineProperty(exports, "CLASSIFICATION_FILENAME", { enumerable: true, get: function () { return classify_1.CLASSIFICATION_FILENAME; } });
|
|
47
|
+
Object.defineProperty(exports, "CLASSIFICATION_SCHEMA", { enumerable: true, get: function () { return classify_1.CLASSIFICATION_SCHEMA; } });
|
|
48
|
+
const taxonomy_1 = require("../taxonomy");
|
|
49
|
+
const overviewFile_1 = require("../overviewFile");
|
|
50
|
+
/**
|
|
51
|
+
* `ia-qa-discover-ai` — the optional, BYOK classification layer.
|
|
52
|
+
*
|
|
53
|
+
* A separate binary for the same reason `ia-qa-heal-ai` is one: the
|
|
54
|
+
* deterministic verbs must behave identically whether or not anyone has a key,
|
|
55
|
+
* and a flag on `scan` would blur that. `scan` never calls a model; this reads
|
|
56
|
+
* what `scan` already wrote, offline, and adds a layer a human reviews.
|
|
57
|
+
*
|
|
58
|
+
* It produces no verdict and gates nothing. Every claim it keeps cites a path
|
|
59
|
+
* into the capture that was resolved and checked; every claim it drops says why.
|
|
60
|
+
*/
|
|
61
|
+
const VERSION = require('../../package.json').version;
|
|
62
|
+
const HELP = `ia-qa-discover-ai — optional BYOK classification for a discovery capture
|
|
63
|
+
|
|
64
|
+
What each entry point your app exposes is FOR, and how sensitive what it handles
|
|
65
|
+
is. Reads .ia-qa-discovery/capture/ offline — it never opens a browser and never
|
|
66
|
+
touches your application. Your key, your model, your machine.
|
|
67
|
+
|
|
68
|
+
Every classification carries a sentence you read and a path into the capture that
|
|
69
|
+
this tool resolves and checks before keeping it. A claim that cites nothing, cites
|
|
70
|
+
something that does not exist, or misdescribes what it cites, is DROPPED and the
|
|
71
|
+
drop is reported. That check proves the premise, never the conclusion: only you
|
|
72
|
+
can say whether the reading follows.
|
|
73
|
+
|
|
74
|
+
Nothing here is a gate. \`ia-qa-discover\` works fully without a key.
|
|
75
|
+
|
|
76
|
+
Usage:
|
|
77
|
+
ia-qa-discover-ai classify Classify every entry point in the capture.
|
|
78
|
+
--page <name> just this page
|
|
79
|
+
--json machine-readable output
|
|
80
|
+
--dry-run show what would be sent, call nothing
|
|
81
|
+
ia-qa-discover-ai models List the providers and models you can configure
|
|
82
|
+
ia-qa-discover-ai version Print the installed version
|
|
83
|
+
ia-qa-discover-ai help Show this help
|
|
84
|
+
|
|
85
|
+
Configure it in ${config_1.CONFIG_DIR}/config.json:
|
|
86
|
+
|
|
87
|
+
"ai": {
|
|
88
|
+
"provider": "anthropic",
|
|
89
|
+
"model": "claude-haiku-4-5",
|
|
90
|
+
"apiKey": { "source": "env", "key": "ANTHROPIC_API_KEY" }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
The key itself never goes in that file — only the name of the variable holding it.
|
|
94
|
+
|
|
95
|
+
Global:
|
|
96
|
+
--config <dir> Resolve ${config_1.CONFIG_DIR}/ under <dir> instead of the current directory
|
|
97
|
+
`;
|
|
98
|
+
exports.HELP = HELP;
|
|
99
|
+
/**
|
|
100
|
+
* Read the `ai` block and resolve its key. Returns null — having already
|
|
101
|
+
* explained itself and set exit code 2 — rather than throwing, because every
|
|
102
|
+
* failure here is a configuration problem the user can act on.
|
|
103
|
+
*/
|
|
104
|
+
async function resolveAi() {
|
|
105
|
+
if (!(0, config_1.configExists)()) {
|
|
106
|
+
console.error(`❌ No ${config_1.CONFIG_DIR}/config.json here.\n` +
|
|
107
|
+
` Run \`ia-qa-discover scan <url> --save\` first: this tool classifies a capture,\n` +
|
|
108
|
+
` it does not make one.`);
|
|
109
|
+
process.exitCode = 2;
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const ai = (0, config_1.loadConfig)().ai;
|
|
113
|
+
if (!ai) {
|
|
114
|
+
console.error(`❌ No "ai" block in ${config_1.CONFIG_DIR}/config.json, so there is no model to call.\n\n` +
|
|
115
|
+
` Add one:\n` +
|
|
116
|
+
` "ai": { "provider": "anthropic", "model": "claude-haiku-4-5",\n` +
|
|
117
|
+
` "apiKey": { "source": "env", "key": "ANTHROPIC_API_KEY" } }\n\n` +
|
|
118
|
+
` \`ia-qa-discover-ai models\` lists what you can put there.\n` +
|
|
119
|
+
` The deterministic \`ia-qa-discover\` works fully without any of this.`);
|
|
120
|
+
process.exitCode = 2;
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const provider = (0, self_healing_1.findProvider)(ai.provider ?? '');
|
|
124
|
+
if (!provider) {
|
|
125
|
+
console.error(`❌ Unknown ai.provider "${ai.provider}". Use one of: ${self_healing_1.AI_PROVIDERS.map((p) => p.id).join(', ')}.\n` +
|
|
126
|
+
` Run \`ia-qa-discover-ai models\` to see them with their models.`);
|
|
127
|
+
process.exitCode = 2;
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
if (!ai.model) {
|
|
131
|
+
console.error(`❌ ai.model is required — pin one, so two runs are comparable. See \`ia-qa-discover-ai models\`.`);
|
|
132
|
+
process.exitCode = 2;
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (!ai.apiKey) {
|
|
136
|
+
console.error(`❌ ai.apiKey is required. It is a REFERENCE, never the key itself:\n` +
|
|
137
|
+
` "apiKey": { "source": "env", "key": "${provider.keyEnv}" }`);
|
|
138
|
+
process.exitCode = 2;
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
// The common case — an env var — is handled here rather than delegated,
|
|
142
|
+
// because `resolveSecret`'s own message is written for the other package: it
|
|
143
|
+
// names `ia-qa-heal`, its CLI path and `.ia-qa/config.json`, and sending
|
|
144
|
+
// someone here to a different tool's config is worse than no help at all.
|
|
145
|
+
// Everything else (AWS SSM) still delegates: one secret path, not two.
|
|
146
|
+
if (ai.apiKey.source === 'env' && !process.env[ai.apiKey.key]) {
|
|
147
|
+
console.error(`❌ Environment variable "${ai.apiKey.key}" is not set, so there is no key to call ${provider.label} with.\n\n` +
|
|
148
|
+
` This CLI reads process.env only — it never loads .env files, so a key that lives\n` +
|
|
149
|
+
` only in .env will not be seen. Put it in the environment first:\n` +
|
|
150
|
+
` npx dotenv-cli -- ia-qa-discover-ai classify\n\n` +
|
|
151
|
+
` The value never belongs in ${config_1.CONFIG_DIR}/config.json, which holds references only.`);
|
|
152
|
+
process.exitCode = 2;
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const apiKey = await (0, self_healing_1.resolveSecret)(ai.apiKey);
|
|
157
|
+
return { provider: provider.id, model: ai.model, apiKey };
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
console.error(`❌ ${e instanceof Error ? e.message : String(e)}`);
|
|
161
|
+
process.exitCode = 2;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function flag(args, name) {
|
|
166
|
+
const i = args.indexOf(name);
|
|
167
|
+
if (i === -1)
|
|
168
|
+
return undefined;
|
|
169
|
+
const next = args[i + 1];
|
|
170
|
+
if (!next || next.startsWith('-'))
|
|
171
|
+
throw new Error(`${name} needs a value.`);
|
|
172
|
+
return next;
|
|
173
|
+
}
|
|
174
|
+
async function runClassify(args) {
|
|
175
|
+
const json = args.includes('--json');
|
|
176
|
+
const dryRun = args.includes('--dry-run');
|
|
177
|
+
const only = flag(args, '--page');
|
|
178
|
+
const index = (0, citations_1.loadCaptureIndex)();
|
|
179
|
+
if (index.pages.size === 0) {
|
|
180
|
+
console.error(`❌ No capture in ${path.relative(process.cwd(), (0, config_1.captureDir)()) || (0, config_1.captureDir)()}.\n` +
|
|
181
|
+
` Run \`ia-qa-discover scan\` first.`);
|
|
182
|
+
process.exitCode = 2;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const pages = [...index.pages.values()].filter((p) => !only || p.page === only);
|
|
186
|
+
if (pages.length === 0) {
|
|
187
|
+
console.error(`❌ No page "${only}" in the capture. Have: ${[...index.pages.keys()].join(', ')}`);
|
|
188
|
+
process.exitCode = 2;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const withEntryPoints = pages.filter((p) => (0, classify_1.entryPointsOf)(p).length > 0);
|
|
192
|
+
if (dryRun) {
|
|
193
|
+
// What would be sent, and to whom — before any key is even resolved.
|
|
194
|
+
console.log(`Would classify ${withEntryPoints.length} page(s) with entry points, of ${pages.length} captured:`);
|
|
195
|
+
for (const p of withEntryPoints)
|
|
196
|
+
console.log(` ${p.page} — ${(0, classify_1.entryPointsOf)(p).length} entry point(s)`);
|
|
197
|
+
const skipped = pages.length - withEntryPoints.length;
|
|
198
|
+
if (skipped > 0)
|
|
199
|
+
console.log(` (${skipped} page(s) take no input and would not be sent at all)`);
|
|
200
|
+
// Naming the destination is the whole point of a dry run: someone runs it to
|
|
201
|
+
// decide whether these strings may go, and "to whom" is half that decision.
|
|
202
|
+
const declared = ((0, config_1.configExists)() ? (0, config_1.loadConfig)().ai : undefined) ?? {};
|
|
203
|
+
const provider = (0, self_healing_1.findProvider)(declared.provider ?? '');
|
|
204
|
+
if (provider)
|
|
205
|
+
console.log((0, classify_1.egressNotice)(provider.id, declared.model ?? '(no model set)', withEntryPoints.length, true));
|
|
206
|
+
console.log(`Nothing was called. Drop --dry-run to run it.`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const ai = await resolveAi();
|
|
210
|
+
if (!ai)
|
|
211
|
+
return;
|
|
212
|
+
if (!json) {
|
|
213
|
+
console.log(`🤖 ${ai.provider}/${ai.model} · ${withEntryPoints.length} page(s) with entry points · ${taxonomy_1.TAXONOMY_VERSION}`);
|
|
214
|
+
console.log(` Reading the capture offline. Your app is not touched.`);
|
|
215
|
+
}
|
|
216
|
+
// "Offline" above means no browser, and on its own it reads as "nothing leaves".
|
|
217
|
+
// stderr in every mode, `--json` included — a security announcement, not decoration.
|
|
218
|
+
console.error((0, classify_1.egressNotice)(ai.provider, ai.model, withEntryPoints.length));
|
|
219
|
+
const perPage = {};
|
|
220
|
+
for (const page of withEntryPoints) {
|
|
221
|
+
const result = await (0, classify_1.classifyPage)(page, index, { provider: ai.provider, model: ai.model, apiKey: ai.apiKey });
|
|
222
|
+
perPage[page.page] = result;
|
|
223
|
+
if (!json) {
|
|
224
|
+
if (result.failed) {
|
|
225
|
+
console.log(` ${page.page.padEnd(22)} ✘ ${result.failed.reason}`);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const bits = [`${result.kept.length} classified`];
|
|
229
|
+
if (result.refused.length > 0)
|
|
230
|
+
bits.push(`${result.refused.length} refused`);
|
|
231
|
+
if (result.dropped.length > 0)
|
|
232
|
+
bits.push(`${result.dropped.length} dropped`);
|
|
233
|
+
console.log(` ${page.page.padEnd(22)} ${bits.join(' · ')}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// A call that did not happen is not a page with nothing on it. Reported before
|
|
238
|
+
// any total, and it sets a non-zero exit: with an invalid key this used to
|
|
239
|
+
// print "0 classified · 0 refused · 0 dropped", exit 0, and write a file —
|
|
240
|
+
// indistinguishable from an app that genuinely has nothing to classify.
|
|
241
|
+
const failures = Object.entries(perPage).filter(([, r]) => r.failed);
|
|
242
|
+
if (failures.length > 0) {
|
|
243
|
+
console.error(`\n❌ ${failures.length} of ${withEntryPoints.length} page(s) could not be classified at all:`);
|
|
244
|
+
for (const [name, r] of failures.slice(0, 5))
|
|
245
|
+
console.error(` ${name}: ${r.failed.reason}`);
|
|
246
|
+
if (failures.length > 5)
|
|
247
|
+
console.error(` …and ${failures.length - 5} more.`);
|
|
248
|
+
console.error(` Nothing is wrong with your app or your capture — the model call did not succeed.`);
|
|
249
|
+
process.exitCode = 2;
|
|
250
|
+
if (failures.length === withEntryPoints.length) {
|
|
251
|
+
// Nothing to WRITE — a document of failures would overwrite a previous good
|
|
252
|
+
// classification with the record of a bad key. But not writing is not a
|
|
253
|
+
// reason to say nothing: `--json` promised stdout a document, and an empty
|
|
254
|
+
// stdout with exit 2 is indistinguishable from a crashed process. So the
|
|
255
|
+
// failures go out on stdout, and only the file is skipped.
|
|
256
|
+
if (json) {
|
|
257
|
+
console.log(JSON.stringify({ schema: classify_1.CLASSIFICATION_SCHEMA, taxonomyVersion: taxonomy_1.TAXONOMY_VERSION, classifiedAt: new Date().toISOString(), model: `${ai.provider}/${ai.model}`, totals: { kept: 0, refused: 0, dropped: 0 }, pages: perPage, written: false }, null, 2));
|
|
258
|
+
}
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const kept = Object.values(perPage).flatMap((r) => r.kept);
|
|
263
|
+
const refused = Object.values(perPage).flatMap((r) => r.refused);
|
|
264
|
+
const dropped = Object.values(perPage).flatMap((r) => r.dropped);
|
|
265
|
+
const document = {
|
|
266
|
+
schema: classify_1.CLASSIFICATION_SCHEMA,
|
|
267
|
+
taxonomyVersion: taxonomy_1.TAXONOMY_VERSION,
|
|
268
|
+
classifiedAt: new Date().toISOString(),
|
|
269
|
+
model: `${ai.provider}/${ai.model}`,
|
|
270
|
+
totals: { kept: kept.length, refused: refused.length, dropped: dropped.length },
|
|
271
|
+
pages: perPage,
|
|
272
|
+
};
|
|
273
|
+
const file = path.join((0, config_1.baseDir)(), config_1.CONFIG_DIR, classify_1.CLASSIFICATION_FILENAME);
|
|
274
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
275
|
+
fs.writeFileSync(file, JSON.stringify(document, null, 2) + '\n', 'utf8');
|
|
276
|
+
// `classification.json` is not where anyone looks. `_overview.md` is, and
|
|
277
|
+
// `scan` wrote it before this classification existed — so it is rewritten
|
|
278
|
+
// here, which is also the only moment the classification is NOT stale.
|
|
279
|
+
// A refusal is reported, never swallowed: an overview silently left at its
|
|
280
|
+
// previous content is a report that disagrees with the file beside it.
|
|
281
|
+
const refreshed = (0, overviewFile_1.refreshOverview)();
|
|
282
|
+
if (!refreshed.ok)
|
|
283
|
+
console.error(`\n⚠ _overview.md was not updated: ${refreshed.reason}`);
|
|
284
|
+
if (json) {
|
|
285
|
+
console.log(JSON.stringify({ ...document, overview: refreshed.ok ? refreshed.file : null }, null, 2));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
console.log(`\n${kept.length} classified · ${refused.length} refused · ${dropped.length} dropped`);
|
|
289
|
+
// The security work this unlocks, deterministically — the point of classifying
|
|
290
|
+
// at all. Deduplicated across entry points: one tool per reason, not per field.
|
|
291
|
+
const tools = new Map();
|
|
292
|
+
for (const k of kept) {
|
|
293
|
+
for (const t of k.securityTools ?? []) {
|
|
294
|
+
if (!tools.has(t.tool))
|
|
295
|
+
tools.set(t.tool, new Set());
|
|
296
|
+
tools.get(t.tool).add(k.entryPoint.split('#')[0].replace(/^pages\//, '').replace(/\.json$/, ''));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (tools.size > 0) {
|
|
300
|
+
console.log(`\n🔐 Security checks this selects (they already exist on the ia-qa.com MCP):`);
|
|
301
|
+
for (const [tool, pagesFor] of tools) {
|
|
302
|
+
console.log(` ${tool.padEnd(24)} for ${[...pagesFor].join(', ')}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (refused.length > 0) {
|
|
306
|
+
console.log(`\n${refused.length} entry point(s) the model would not label. That is an answer, not a gap —` +
|
|
307
|
+
`\nthe evidence did not support any category. They need a person, not a retry.`);
|
|
308
|
+
}
|
|
309
|
+
if (dropped.length > 0) {
|
|
310
|
+
console.log(`\n${dropped.length} classification(s) dropped because their evidence did not check out:`);
|
|
311
|
+
for (const d of dropped.slice(0, 5))
|
|
312
|
+
console.log(` ${(d.classification.entryPoint ?? '?')} — ${d.reason}`);
|
|
313
|
+
if (dropped.length > 5)
|
|
314
|
+
console.log(` …and ${dropped.length - 5} more (see the JSON).`);
|
|
315
|
+
}
|
|
316
|
+
console.log(`\nWritten to ${file}`);
|
|
317
|
+
console.log(`Every kept claim cites the capture. Read the "why" and the evidence before acting on it.`);
|
|
318
|
+
}
|
|
319
|
+
async function main() {
|
|
320
|
+
const argv = process.argv.slice(2);
|
|
321
|
+
const ci = argv.indexOf('--config');
|
|
322
|
+
if (ci !== -1) {
|
|
323
|
+
const dir = argv[ci + 1];
|
|
324
|
+
if (!dir || dir.startsWith('-'))
|
|
325
|
+
throw new Error('--config needs a directory argument.');
|
|
326
|
+
(0, config_1.setBaseDir)(dir);
|
|
327
|
+
argv.splice(ci, 2);
|
|
328
|
+
}
|
|
329
|
+
const [command, ...rest] = argv;
|
|
330
|
+
switch (command) {
|
|
331
|
+
case 'classify':
|
|
332
|
+
await runClassify(rest);
|
|
333
|
+
break;
|
|
334
|
+
case 'models':
|
|
335
|
+
console.log((0, self_healing_1.renderModelList)());
|
|
336
|
+
break;
|
|
337
|
+
case 'version':
|
|
338
|
+
case '--version':
|
|
339
|
+
case '-v':
|
|
340
|
+
console.log(VERSION);
|
|
341
|
+
break;
|
|
342
|
+
case 'help':
|
|
343
|
+
case '--help':
|
|
344
|
+
case '-h':
|
|
345
|
+
case undefined:
|
|
346
|
+
console.log(HELP);
|
|
347
|
+
break;
|
|
348
|
+
default:
|
|
349
|
+
console.error(`Unknown command "${command}".\n\n${HELP}`);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/** Only when this file IS the process — so the dispatch stays importable, and testable. */
|
|
354
|
+
if (require.main === module) {
|
|
355
|
+
main().catch((err) => {
|
|
356
|
+
console.error(`\n❌ ${err instanceof Error ? err.message : String(err)}`);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli-ai/index.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { SecretRef } from '@ia-qa/self-healing';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for `@ia-qa/qa-discovery` — `.ia-qa-discovery/config.json`.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately a SIBLING of `@ia-qa/self-healing`'s `.ia-qa/`, never nested
|
|
6
|
+
* inside it: the two tools' local state never overlaps, so a project can run
|
|
7
|
+
* both without either one reading the other's files by accident.
|
|
8
|
+
*/
|
|
9
|
+
export interface CaptureConfig {
|
|
10
|
+
/** Observe API/network calls per page. Default true. */
|
|
11
|
+
network?: boolean;
|
|
12
|
+
/** Fraction of pages (0-1) a call must appear on to be promoted as shared. Default 0.6. */
|
|
13
|
+
networkThreshold?: number;
|
|
14
|
+
/** Click one level of disclosures per page and capture what they reveal. Default false. */
|
|
15
|
+
deep?: boolean;
|
|
16
|
+
/** Query param names whose VALUE is kept when normalizing a captured API call. Everything else keeps only the key name. */
|
|
17
|
+
safeQueryParams?: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A page the user declares, because a crawl cannot find it.
|
|
21
|
+
*
|
|
22
|
+
* Discovery follows `<a href>`. An app that navigates programmatically (React
|
|
23
|
+
* Router's `useNavigate()`, a button with an onClick handler) exposes no href,
|
|
24
|
+
* so a crawl reaches exactly the entry URL — measured on a real app: 11 routes,
|
|
25
|
+
* zero `<Link>`, one page found. Declaring the routes is the deterministic way
|
|
26
|
+
* out, and they are usually sitting in the router file in plain sight.
|
|
27
|
+
*
|
|
28
|
+
* Declared pages are scanned IN ADDITION to whatever the crawl finds, deduped
|
|
29
|
+
* by path — so declaring some does not turn the crawl off.
|
|
30
|
+
*/
|
|
31
|
+
export interface DeclaredPage {
|
|
32
|
+
/** Filename for the capture. Defaults to a slug of the URL path. */
|
|
33
|
+
name?: string;
|
|
34
|
+
/** Path or absolute URL, resolved against `baseUrl`. */
|
|
35
|
+
url: string;
|
|
36
|
+
}
|
|
37
|
+
export interface DiscoveryConfig {
|
|
38
|
+
baseUrl: string;
|
|
39
|
+
/** Routes a crawl cannot discover on its own. See `DeclaredPage`. */
|
|
40
|
+
pages?: DeclaredPage[];
|
|
41
|
+
/** Path to an existing Playwright storageState file (e.g. from self-healing's `login`, or a project's own globalSetup). Never written by this package. */
|
|
42
|
+
session?: string;
|
|
43
|
+
locale?: string;
|
|
44
|
+
crawl?: {
|
|
45
|
+
maxDepth?: number;
|
|
46
|
+
maxPages?: number;
|
|
47
|
+
reveal?: boolean;
|
|
48
|
+
strictHost?: boolean;
|
|
49
|
+
/** Read sitemap.xml (robots.txt fallback) as a page source. Default true. */
|
|
50
|
+
sitemap?: boolean;
|
|
51
|
+
};
|
|
52
|
+
capture?: CaptureConfig;
|
|
53
|
+
/**
|
|
54
|
+
* Optional BYOK config for . Its absence means the AI layer
|
|
55
|
+
* is inert and every deterministic verb behaves exactly as it does today.
|
|
56
|
+
* is a REFERENCE (env var name / SSM parameter), never the value:
|
|
57
|
+
* config.json is meant to be committed.
|
|
58
|
+
*/
|
|
59
|
+
ai?: {
|
|
60
|
+
provider: string;
|
|
61
|
+
/** Pinned, so two runs are comparable. */
|
|
62
|
+
model: string;
|
|
63
|
+
apiKey: SecretRef;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export declare const CONFIG_DIR = ".ia-qa-discovery";
|
|
67
|
+
export declare const CAPTURE_DIRNAME = "capture";
|
|
68
|
+
export declare const HISTORY_FILENAME = "history.jsonl";
|
|
69
|
+
/**
|
|
70
|
+
* A browser session saved by `ia-qa-discover login`, in Playwright
|
|
71
|
+
* `storageState` shape. It holds live cookies: whoever has this file is logged
|
|
72
|
+
* in as that user. It is the one artefact in `.ia-qa-discovery/` that is a
|
|
73
|
+
* secret — never uploaded anywhere, and `login` writes
|
|
74
|
+
* `.ia-qa-discovery/.gitignore` so a commit cannot carry it.
|
|
75
|
+
*/
|
|
76
|
+
export declare const SESSION_FILENAME = "session.json";
|
|
77
|
+
/** Set the base dir (the `--config <dir>` flag). `null` clears it back to the default. */
|
|
78
|
+
export declare function setBaseDir(dir: string | null): void;
|
|
79
|
+
export declare function baseDir(): string;
|
|
80
|
+
export declare function configPath(cwd?: string): string;
|
|
81
|
+
export declare function captureDir(cwd?: string): string;
|
|
82
|
+
export declare function historyPath(cwd?: string): string;
|
|
83
|
+
export declare function loadConfig(cwd?: string): DiscoveryConfig;
|
|
84
|
+
export declare function saveConfig(config: DiscoveryConfig, cwd?: string): string;
|
|
85
|
+
export declare function configExists(cwd?: string): boolean;
|
|
86
|
+
export declare function sessionPath(cwd?: string): string;
|
|
87
|
+
export type SessionSource = 'flag' | 'env' | 'config' | 'login';
|
|
88
|
+
export interface SessionChoice {
|
|
89
|
+
file: string;
|
|
90
|
+
source: SessionSource;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Which session file to *write* — `login`'s target. Same precedence as
|
|
94
|
+
* reading, minus the existence check: pointing `session` at a path is also a
|
|
95
|
+
* way of saying where you want it kept, so `login` refreshes THAT file rather
|
|
96
|
+
* than leaving a second one beside it for `scan` to have to choose between.
|
|
97
|
+
*/
|
|
98
|
+
export declare function sessionTarget(config: Pick<DiscoveryConfig, 'session'> | undefined, explicit?: string, cwd?: string): SessionChoice;
|
|
99
|
+
/**
|
|
100
|
+
* The session to *read*, or null when there is none to read.
|
|
101
|
+
*
|
|
102
|
+
* A session the user NAMED — a flag, an env var, a config field — that cannot
|
|
103
|
+
* be used is a hard error, never a silent fall-through. Ignoring it would
|
|
104
|
+
* capture the login page under the names of their real pages and exit 0, which
|
|
105
|
+
* is the false green this whole package must avoid. The default location is
|
|
106
|
+
* different: absent simply means nobody has run `login` yet.
|
|
107
|
+
*
|
|
108
|
+
* The shape check is deliberately shallow — `cookies` is enough to tell a
|
|
109
|
+
* storageState from a config file pasted by mistake. The file is a secret: it
|
|
110
|
+
* is never read for anything else, never logged, never sent anywhere.
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveSession(config: Pick<DiscoveryConfig, 'session'> | undefined, explicit?: string, cwd?: string): SessionChoice | null;
|