@avi770/testteam 1.2.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/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/agents/01-analyst.ts +100 -0
- package/agents/02-seed-architect.ts +59 -0
- package/agents/03-test-generator.ts +191 -0
- package/agents/04-unit-runner.ts +160 -0
- package/agents/05-browser-crawler.ts +790 -0
- package/agents/06-api-exerciser.ts +311 -0
- package/agents/07-security-scout.ts +188 -0
- package/agents/08-a11y-guardian.ts +212 -0
- package/agents/09-healer.ts +228 -0
- package/agents/10-reporter.ts +266 -0
- package/agents/11-fixer.ts +253 -0
- package/agents/12-ux-inspector.ts +444 -0
- package/agents/13-performance-profiler.ts +271 -0
- package/agents/14-data-integrity-auditor.ts +417 -0
- package/agents/15-regression-sentinel.ts +307 -0
- package/agents/16-chaos-agent.ts +228 -0
- package/agents/17-documentation-validator.ts +266 -0
- package/agents/18-integration-watchdog.ts +178 -0
- package/agents/19-tenant-isolation-auditor.ts +199 -0
- package/agents/20-workflow-completion-tester.ts +203 -0
- package/agents/21-state-session-tester.ts +262 -0
- package/agents/22-email-notification-verifier.ts +244 -0
- package/agents/23-migration-tester.ts +80 -0
- package/agents/__tests__/01-analyst.test.ts +188 -0
- package/agents/__tests__/02-seed-architect.test.ts +152 -0
- package/agents/__tests__/03-test-generator-full.test.ts +321 -0
- package/agents/__tests__/03-test-generator.test.ts +318 -0
- package/agents/__tests__/04-unit-runner.test.ts +320 -0
- package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
- package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
- package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
- package/agents/__tests__/05-browser-crawler.test.ts +518 -0
- package/agents/__tests__/06-api-exerciser.test.ts +619 -0
- package/agents/__tests__/07-security-scout.test.ts +382 -0
- package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
- package/agents/__tests__/09-healer.test.ts +384 -0
- package/agents/__tests__/10-reporter.test.ts +366 -0
- package/agents/__tests__/11-fixer.test.ts +406 -0
- package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
- package/agents/__tests__/12-ux-inspector.test.ts +443 -0
- package/agents/__tests__/13-performance-profiler.test.ts +411 -0
- package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
- package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
- package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
- package/agents/__tests__/16-chaos-agent.test.ts +427 -0
- package/agents/__tests__/17-documentation-validator.test.ts +402 -0
- package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
- package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
- package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
- package/agents/__tests__/21-state-session-tester.test.ts +374 -0
- package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
- package/agents/__tests__/23-migration-tester.test.ts +145 -0
- package/agents/__tests__/base-agent.test.ts +188 -0
- package/agents/__tests__/registry.test.ts +218 -0
- package/agents/base-agent.ts +77 -0
- package/agents/registry.ts +136 -0
- package/baselines/api-schemas/.gitkeep +0 -0
- package/baselines/performance/.gitkeep +0 -0
- package/baselines/screenshots/.gitkeep +0 -0
- package/bin/testteam.js +10 -0
- package/core/__tests__/ci-output.test.ts +430 -0
- package/core/__tests__/cli.test.ts +387 -0
- package/core/__tests__/config.test.ts +78 -0
- package/core/__tests__/cost-tracker.test.ts +158 -0
- package/core/__tests__/evidence.test.ts +265 -0
- package/core/__tests__/fix-loop.test.ts +210 -0
- package/core/__tests__/health-check.test.ts +44 -0
- package/core/__tests__/init.test.ts +609 -0
- package/core/__tests__/integration.test.ts +204 -0
- package/core/__tests__/license-gen.test.ts +227 -0
- package/core/__tests__/license.test.ts +326 -0
- package/core/__tests__/multi-browser.test.ts +278 -0
- package/core/__tests__/orchestrator.test.ts +519 -0
- package/core/__tests__/phase-gate.test.ts +43 -0
- package/core/__tests__/report-html.test.ts +398 -0
- package/core/__tests__/report-upload.test.ts +325 -0
- package/core/__tests__/run-counter.test.ts +234 -0
- package/core/ci-output.ts +240 -0
- package/core/cli.ts +232 -0
- package/core/config.ts +178 -0
- package/core/cost-tracker.ts +59 -0
- package/core/evidence.ts +132 -0
- package/core/fix-loop.ts +85 -0
- package/core/health-check.ts +54 -0
- package/core/init.ts +546 -0
- package/core/license-gen.ts +212 -0
- package/core/license.ts +211 -0
- package/core/messages.ts +67 -0
- package/core/multi-browser.ts +136 -0
- package/core/orchestrator.ts +354 -0
- package/core/phase-gate.ts +55 -0
- package/core/report-html.ts +657 -0
- package/core/report-upload.ts +188 -0
- package/core/run-counter.ts +175 -0
- package/core/types.ts +57 -0
- package/dist/agents/01-analyst.d.ts +11 -0
- package/dist/agents/01-analyst.d.ts.map +1 -0
- package/dist/agents/01-analyst.js +75 -0
- package/dist/agents/01-analyst.js.map +1 -0
- package/dist/agents/02-seed-architect.d.ts +11 -0
- package/dist/agents/02-seed-architect.d.ts.map +1 -0
- package/dist/agents/02-seed-architect.js +51 -0
- package/dist/agents/02-seed-architect.js.map +1 -0
- package/dist/agents/03-test-generator.d.ts +9 -0
- package/dist/agents/03-test-generator.d.ts.map +1 -0
- package/dist/agents/03-test-generator.js +167 -0
- package/dist/agents/03-test-generator.js.map +1 -0
- package/dist/agents/04-unit-runner.d.ts +9 -0
- package/dist/agents/04-unit-runner.d.ts.map +1 -0
- package/dist/agents/04-unit-runner.js +113 -0
- package/dist/agents/04-unit-runner.js.map +1 -0
- package/dist/agents/05-browser-crawler.d.ts +30 -0
- package/dist/agents/05-browser-crawler.d.ts.map +1 -0
- package/dist/agents/05-browser-crawler.js +685 -0
- package/dist/agents/05-browser-crawler.js.map +1 -0
- package/dist/agents/06-api-exerciser.d.ts +23 -0
- package/dist/agents/06-api-exerciser.d.ts.map +1 -0
- package/dist/agents/06-api-exerciser.js +253 -0
- package/dist/agents/06-api-exerciser.js.map +1 -0
- package/dist/agents/07-security-scout.d.ts +11 -0
- package/dist/agents/07-security-scout.d.ts.map +1 -0
- package/dist/agents/07-security-scout.js +142 -0
- package/dist/agents/07-security-scout.js.map +1 -0
- package/dist/agents/08-a11y-guardian.d.ts +13 -0
- package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
- package/dist/agents/08-a11y-guardian.js +176 -0
- package/dist/agents/08-a11y-guardian.js.map +1 -0
- package/dist/agents/09-healer.d.ts +33 -0
- package/dist/agents/09-healer.d.ts.map +1 -0
- package/dist/agents/09-healer.js +167 -0
- package/dist/agents/09-healer.js.map +1 -0
- package/dist/agents/10-reporter.d.ts +26 -0
- package/dist/agents/10-reporter.d.ts.map +1 -0
- package/dist/agents/10-reporter.js +215 -0
- package/dist/agents/10-reporter.js.map +1 -0
- package/dist/agents/11-fixer.d.ts +26 -0
- package/dist/agents/11-fixer.d.ts.map +1 -0
- package/dist/agents/11-fixer.js +195 -0
- package/dist/agents/11-fixer.js.map +1 -0
- package/dist/agents/12-ux-inspector.d.ts +15 -0
- package/dist/agents/12-ux-inspector.d.ts.map +1 -0
- package/dist/agents/12-ux-inspector.js +364 -0
- package/dist/agents/12-ux-inspector.js.map +1 -0
- package/dist/agents/13-performance-profiler.d.ts +13 -0
- package/dist/agents/13-performance-profiler.d.ts.map +1 -0
- package/dist/agents/13-performance-profiler.js +216 -0
- package/dist/agents/13-performance-profiler.js.map +1 -0
- package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
- package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
- package/dist/agents/14-data-integrity-auditor.js +356 -0
- package/dist/agents/14-data-integrity-auditor.js.map +1 -0
- package/dist/agents/15-regression-sentinel.d.ts +25 -0
- package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
- package/dist/agents/15-regression-sentinel.js +251 -0
- package/dist/agents/15-regression-sentinel.js.map +1 -0
- package/dist/agents/16-chaos-agent.d.ts +9 -0
- package/dist/agents/16-chaos-agent.d.ts.map +1 -0
- package/dist/agents/16-chaos-agent.js +207 -0
- package/dist/agents/16-chaos-agent.js.map +1 -0
- package/dist/agents/17-documentation-validator.d.ts +31 -0
- package/dist/agents/17-documentation-validator.d.ts.map +1 -0
- package/dist/agents/17-documentation-validator.js +246 -0
- package/dist/agents/17-documentation-validator.js.map +1 -0
- package/dist/agents/18-integration-watchdog.d.ts +10 -0
- package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
- package/dist/agents/18-integration-watchdog.js +138 -0
- package/dist/agents/18-integration-watchdog.js.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.js +166 -0
- package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
- package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
- package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
- package/dist/agents/20-workflow-completion-tester.js +159 -0
- package/dist/agents/20-workflow-completion-tester.js.map +1 -0
- package/dist/agents/21-state-session-tester.d.ts +10 -0
- package/dist/agents/21-state-session-tester.d.ts.map +1 -0
- package/dist/agents/21-state-session-tester.js +233 -0
- package/dist/agents/21-state-session-tester.js.map +1 -0
- package/dist/agents/22-email-notification-verifier.d.ts +11 -0
- package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
- package/dist/agents/22-email-notification-verifier.js +199 -0
- package/dist/agents/22-email-notification-verifier.js.map +1 -0
- package/dist/agents/23-migration-tester.d.ts +10 -0
- package/dist/agents/23-migration-tester.d.ts.map +1 -0
- package/dist/agents/23-migration-tester.js +74 -0
- package/dist/agents/23-migration-tester.js.map +1 -0
- package/dist/agents/base-agent.d.ts +19 -0
- package/dist/agents/base-agent.d.ts.map +1 -0
- package/dist/agents/base-agent.js +67 -0
- package/dist/agents/base-agent.js.map +1 -0
- package/dist/agents/registry.d.ts +29 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +117 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/core/ci-output.d.ts +35 -0
- package/dist/core/ci-output.d.ts.map +1 -0
- package/dist/core/ci-output.js +193 -0
- package/dist/core/ci-output.js.map +1 -0
- package/dist/core/cli.d.ts +11 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +197 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/config.d.ts +111 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/cost-tracker.d.ts +22 -0
- package/dist/core/cost-tracker.d.ts.map +1 -0
- package/dist/core/cost-tracker.js +41 -0
- package/dist/core/cost-tracker.js.map +1 -0
- package/dist/core/evidence.d.ts +28 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +95 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/fix-loop.d.ts +29 -0
- package/dist/core/fix-loop.d.ts.map +1 -0
- package/dist/core/fix-loop.js +70 -0
- package/dist/core/fix-loop.js.map +1 -0
- package/dist/core/health-check.d.ts +21 -0
- package/dist/core/health-check.d.ts.map +1 -0
- package/dist/core/health-check.js +26 -0
- package/dist/core/health-check.js.map +1 -0
- package/dist/core/init.d.ts +2 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +435 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/license-gen.d.ts +12 -0
- package/dist/core/license-gen.d.ts.map +1 -0
- package/dist/core/license-gen.js +169 -0
- package/dist/core/license-gen.js.map +1 -0
- package/dist/core/license.d.ts +33 -0
- package/dist/core/license.d.ts.map +1 -0
- package/dist/core/license.js +170 -0
- package/dist/core/license.js.map +1 -0
- package/dist/core/messages.d.ts +10 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +47 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/multi-browser.d.ts +36 -0
- package/dist/core/multi-browser.d.ts.map +1 -0
- package/dist/core/multi-browser.js +88 -0
- package/dist/core/multi-browser.js.map +1 -0
- package/dist/core/orchestrator.d.ts +48 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +291 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/phase-gate.d.ts +4 -0
- package/dist/core/phase-gate.d.ts.map +1 -0
- package/dist/core/phase-gate.js +39 -0
- package/dist/core/phase-gate.js.map +1 -0
- package/dist/core/report-html.d.ts +9 -0
- package/dist/core/report-html.d.ts.map +1 -0
- package/dist/core/report-html.js +617 -0
- package/dist/core/report-html.js.map +1 -0
- package/dist/core/report-upload.d.ts +16 -0
- package/dist/core/report-upload.d.ts.map +1 -0
- package/dist/core/report-upload.js +124 -0
- package/dist/core/report-upload.js.map +1 -0
- package/dist/core/run-counter.d.ts +40 -0
- package/dist/core/run-counter.d.ts.map +1 -0
- package/dist/core/run-counter.js +120 -0
- package/dist/core/run-counter.js.map +1 -0
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/helpers/api-client.d.ts +30 -0
- package/dist/helpers/api-client.d.ts.map +1 -0
- package/dist/helpers/api-client.js +77 -0
- package/dist/helpers/api-client.js.map +1 -0
- package/dist/helpers/element-discovery.d.ts +18 -0
- package/dist/helpers/element-discovery.d.ts.map +1 -0
- package/dist/helpers/element-discovery.js +82 -0
- package/dist/helpers/element-discovery.js.map +1 -0
- package/dist/helpers/env-resolver.d.ts +29 -0
- package/dist/helpers/env-resolver.d.ts.map +1 -0
- package/dist/helpers/env-resolver.js +51 -0
- package/dist/helpers/env-resolver.js.map +1 -0
- package/dist/helpers/form-filler.d.ts +13 -0
- package/dist/helpers/form-filler.d.ts.map +1 -0
- package/dist/helpers/form-filler.js +98 -0
- package/dist/helpers/form-filler.js.map +1 -0
- package/dist/helpers/modal-handler.d.ts +16 -0
- package/dist/helpers/modal-handler.d.ts.map +1 -0
- package/dist/helpers/modal-handler.js +95 -0
- package/dist/helpers/modal-handler.js.map +1 -0
- package/dist/helpers/navigation.d.ts +37 -0
- package/dist/helpers/navigation.d.ts.map +1 -0
- package/dist/helpers/navigation.js +83 -0
- package/dist/helpers/navigation.js.map +1 -0
- package/dist/helpers/quality-gate.d.ts +17 -0
- package/dist/helpers/quality-gate.d.ts.map +1 -0
- package/dist/helpers/quality-gate.js +144 -0
- package/dist/helpers/quality-gate.js.map +1 -0
- package/dist/helpers/screenshot.d.ts +24 -0
- package/dist/helpers/screenshot.d.ts.map +1 -0
- package/dist/helpers/screenshot.js +76 -0
- package/dist/helpers/screenshot.js.map +1 -0
- package/dist/helpers/seed-validator.d.ts +15 -0
- package/dist/helpers/seed-validator.d.ts.map +1 -0
- package/dist/helpers/seed-validator.js +53 -0
- package/dist/helpers/seed-validator.js.map +1 -0
- package/helpers/__tests__/api-client.test.ts +199 -0
- package/helpers/__tests__/element-discovery.test.ts +202 -0
- package/helpers/__tests__/form-filler-extended.test.ts +212 -0
- package/helpers/__tests__/form-filler.test.ts +99 -0
- package/helpers/__tests__/modal-handler.test.ts +152 -0
- package/helpers/__tests__/navigation.test.ts +214 -0
- package/helpers/__tests__/quality-gate.test.ts +117 -0
- package/helpers/__tests__/screenshot.test.ts +139 -0
- package/helpers/__tests__/seed-validator.test.ts +114 -0
- package/helpers/api-client.ts +111 -0
- package/helpers/element-discovery.ts +105 -0
- package/helpers/env-resolver.ts +69 -0
- package/helpers/form-filler.ts +126 -0
- package/helpers/modal-handler.ts +108 -0
- package/helpers/navigation.ts +100 -0
- package/helpers/quality-gate.ts +180 -0
- package/helpers/screenshot.ts +111 -0
- package/helpers/seed-validator.ts +70 -0
- package/package.json +88 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as readline from 'node:readline';
|
|
3
|
+
// ── Base64url helpers ────────────────────────────────────────────────
|
|
4
|
+
function base64urlEncode(data) {
|
|
5
|
+
return data.toString('base64url');
|
|
6
|
+
}
|
|
7
|
+
// ── Key generation ───────────────────────────────────────────────────
|
|
8
|
+
/** Generate an ES256 (ECDSA P-256) keypair in PEM format. */
|
|
9
|
+
export function generateKeyPair() {
|
|
10
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
|
|
11
|
+
namedCurve: 'P-256',
|
|
12
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
13
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
|
|
14
|
+
});
|
|
15
|
+
return { publicKey, privateKey };
|
|
16
|
+
}
|
|
17
|
+
// ── License key generation ───────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Generate a signed JWT license key using ES256.
|
|
20
|
+
* Uses only Node.js built-in `crypto` — no external JWT libraries.
|
|
21
|
+
*/
|
|
22
|
+
export function generateLicenseKey(payload, privateKey) {
|
|
23
|
+
const header = { alg: 'ES256', typ: 'JWT' };
|
|
24
|
+
const fullPayload = {
|
|
25
|
+
...payload,
|
|
26
|
+
iat: Math.floor(Date.now() / 1000),
|
|
27
|
+
};
|
|
28
|
+
const headerB64 = base64urlEncode(Buffer.from(JSON.stringify(header)));
|
|
29
|
+
const payloadB64 = base64urlEncode(Buffer.from(JSON.stringify(fullPayload)));
|
|
30
|
+
const signedContent = `${headerB64}.${payloadB64}`;
|
|
31
|
+
// Sign with ECDSA P-256 / SHA-256
|
|
32
|
+
const signer = crypto.createSign('SHA256');
|
|
33
|
+
signer.update(signedContent);
|
|
34
|
+
const derSignature = signer.sign({ key: privateKey, dsaEncoding: 'der' });
|
|
35
|
+
// Convert DER to raw (r || s) for compact JWT representation
|
|
36
|
+
const rawSignature = derToRaw(derSignature);
|
|
37
|
+
const signatureB64 = base64urlEncode(rawSignature);
|
|
38
|
+
return `${signedContent}.${signatureB64}`;
|
|
39
|
+
}
|
|
40
|
+
const TIER_DEFAULTS = {
|
|
41
|
+
free: { maxProjects: 1, maxRunsPerMonth: 10 },
|
|
42
|
+
pro: { maxProjects: 3, maxRunsPerMonth: 100 },
|
|
43
|
+
enterprise: { maxProjects: 999, maxRunsPerMonth: 999999 },
|
|
44
|
+
};
|
|
45
|
+
// ── Interactive CLI ──────────────────────────────────────────────────
|
|
46
|
+
async function prompt(rl, question) {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
rl.question(question, (answer) => resolve(answer.trim()));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function main() {
|
|
52
|
+
const rl = readline.createInterface({
|
|
53
|
+
input: process.stdin,
|
|
54
|
+
output: process.stdout,
|
|
55
|
+
});
|
|
56
|
+
try {
|
|
57
|
+
console.log('\n=== TestTeam License Generator ===\n');
|
|
58
|
+
// Tier
|
|
59
|
+
const tierInput = await prompt(rl, 'Tier (free / pro / enterprise): ');
|
|
60
|
+
const tier = tierInput;
|
|
61
|
+
if (!['free', 'pro', 'enterprise'].includes(tier)) {
|
|
62
|
+
console.error(`Invalid tier: ${tierInput}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// Email / Customer ID
|
|
66
|
+
const sub = await prompt(rl, 'Customer email or ID: ');
|
|
67
|
+
if (!sub) {
|
|
68
|
+
console.error('Customer identifier is required.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
// Expiry
|
|
72
|
+
const daysInput = await prompt(rl, 'License duration in days (default: 365): ');
|
|
73
|
+
const days = daysInput ? parseInt(daysInput, 10) : 365;
|
|
74
|
+
if (isNaN(days) || days <= 0) {
|
|
75
|
+
console.error('Invalid duration.');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
// Features
|
|
79
|
+
const featuresInput = await prompt(rl, 'Feature flags (comma-separated, or empty): ');
|
|
80
|
+
const features = featuresInput
|
|
81
|
+
? featuresInput.split(',').map((f) => f.trim()).filter(Boolean)
|
|
82
|
+
: [];
|
|
83
|
+
// Generate keypair
|
|
84
|
+
const { publicKey, privateKey } = generateKeyPair();
|
|
85
|
+
const defaults = TIER_DEFAULTS[tier];
|
|
86
|
+
const exp = Math.floor(Date.now() / 1000) + days * 86400;
|
|
87
|
+
const licenseKey = generateLicenseKey({
|
|
88
|
+
tier,
|
|
89
|
+
exp,
|
|
90
|
+
sub,
|
|
91
|
+
maxProjects: defaults.maxProjects,
|
|
92
|
+
maxRunsPerMonth: defaults.maxRunsPerMonth,
|
|
93
|
+
features,
|
|
94
|
+
}, privateKey);
|
|
95
|
+
console.log('\n--- LICENSE KEY ---');
|
|
96
|
+
console.log(licenseKey);
|
|
97
|
+
console.log('\n--- PUBLIC KEY (embed in .testteam-license or set TF_LICENSE_PUBLIC_KEY) ---');
|
|
98
|
+
console.log(publicKey);
|
|
99
|
+
console.log('--- PRIVATE KEY (keep secret — for generating new keys only) ---');
|
|
100
|
+
console.log(privateKey);
|
|
101
|
+
console.log('--- .testteam-license file content ---');
|
|
102
|
+
console.log(`${licenseKey}\n\n${publicKey}`);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
rl.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ── DER to raw conversion ────────────────────────────────────────────
|
|
109
|
+
/**
|
|
110
|
+
* Convert a DER-encoded ECDSA signature to raw (r || s) format (64 bytes for P-256).
|
|
111
|
+
*/
|
|
112
|
+
function derToRaw(der) {
|
|
113
|
+
// DER: 0x30 <len> 0x02 <rLen> <r> 0x02 <sLen> <s>
|
|
114
|
+
let offset = 2; // skip 0x30 and total length
|
|
115
|
+
// Read r
|
|
116
|
+
if (der[offset] !== 0x02)
|
|
117
|
+
throw new Error('Invalid DER: expected 0x02 for r');
|
|
118
|
+
offset++;
|
|
119
|
+
const rLen = der[offset];
|
|
120
|
+
offset++;
|
|
121
|
+
let r = der.subarray(offset, offset + rLen);
|
|
122
|
+
offset += rLen;
|
|
123
|
+
// Read s
|
|
124
|
+
if (der[offset] !== 0x02)
|
|
125
|
+
throw new Error('Invalid DER: expected 0x02 for s');
|
|
126
|
+
offset++;
|
|
127
|
+
const sLen = der[offset];
|
|
128
|
+
offset++;
|
|
129
|
+
let s = der.subarray(offset, offset + sLen);
|
|
130
|
+
// Normalize to exactly 32 bytes each (strip leading zero padding, or left-pad)
|
|
131
|
+
r = normalizeInteger(r, 32);
|
|
132
|
+
s = normalizeInteger(s, 32);
|
|
133
|
+
return Buffer.concat([r, s]);
|
|
134
|
+
}
|
|
135
|
+
function normalizeInteger(buf, targetLen) {
|
|
136
|
+
if (buf.length === targetLen)
|
|
137
|
+
return buf;
|
|
138
|
+
if (buf.length > targetLen) {
|
|
139
|
+
// Strip leading zeros
|
|
140
|
+
return buf.subarray(buf.length - targetLen);
|
|
141
|
+
}
|
|
142
|
+
// Left-pad with zeros
|
|
143
|
+
const padded = Buffer.alloc(targetLen, 0);
|
|
144
|
+
buf.copy(padded, targetLen - buf.length);
|
|
145
|
+
return padded;
|
|
146
|
+
}
|
|
147
|
+
// ── ESM entry point detection ────────────────────────────────────────
|
|
148
|
+
const isDirectRun = (() => {
|
|
149
|
+
try {
|
|
150
|
+
// In ESM, check if this module is the entry point
|
|
151
|
+
const moduleUrl = import.meta.url;
|
|
152
|
+
const argv1 = process.argv[1];
|
|
153
|
+
if (!argv1)
|
|
154
|
+
return false;
|
|
155
|
+
// Normalize for comparison
|
|
156
|
+
const scriptUrl = new URL(`file:///${argv1.replace(/\\/g, '/')}`).href;
|
|
157
|
+
return moduleUrl === scriptUrl || moduleUrl.endsWith(argv1.replace(/\\/g, '/'));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
})();
|
|
163
|
+
if (isDirectRun) {
|
|
164
|
+
main().catch((err) => {
|
|
165
|
+
console.error('License generation failed:', err);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=license-gen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license-gen.js","sourceRoot":"","sources":["../../core/license-gen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** License tiers supported by TestTeam. */
|
|
2
|
+
export type LicenseTier = 'free' | 'pro' | 'enterprise';
|
|
3
|
+
/** JWT payload embedded in a TestTeam license key. */
|
|
4
|
+
export interface LicensePayload {
|
|
5
|
+
tier: LicenseTier;
|
|
6
|
+
exp: number;
|
|
7
|
+
iat: number;
|
|
8
|
+
sub: string;
|
|
9
|
+
maxProjects: number;
|
|
10
|
+
maxRunsPerMonth: number;
|
|
11
|
+
features: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Decode and verify a JWT signed with ES256.
|
|
15
|
+
* Returns the payload on success or `null` on any failure
|
|
16
|
+
* (bad format, invalid signature, expired, malformed JSON).
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateLicense(key: string, publicKey: string): LicensePayload | null;
|
|
19
|
+
/**
|
|
20
|
+
* Load a license from the `.testteam-license` file in the given directory.
|
|
21
|
+
* The file must contain the JWT on the first non-empty line.
|
|
22
|
+
* A second non-empty line, if present, is treated as the PEM public key
|
|
23
|
+
* (or the public key can be embedded as `TF_LICENSE_PUBLIC_KEY` env var).
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadLicense(configDir: string): LicensePayload | null;
|
|
26
|
+
/**
|
|
27
|
+
* Get the license tier for the current project.
|
|
28
|
+
* Falls back to `'free'` when no valid license is found.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getLicenseTier(configDir?: string): LicenseTier;
|
|
31
|
+
/** Reset the in-memory license cache (useful for testing). */
|
|
32
|
+
export declare function resetLicenseCache(): void;
|
|
33
|
+
//# sourceMappingURL=license.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../../core/license.ts"],"names":[],"mappings":"AAIA,2CAA2C;AAC3C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAC;AAExD,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAmBD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB,cAAc,GAAG,IAAI,CAiDvB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAuCpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAI9D;AAED,8DAA8D;AAC9D,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
/** In-memory cache so we only validate once per process. */
|
|
5
|
+
let cachedLicense;
|
|
6
|
+
const LICENSE_FILENAME = '.testteam-license';
|
|
7
|
+
// ── Base64url helpers ────────────────────────────────────────────────
|
|
8
|
+
function base64urlEncode(data) {
|
|
9
|
+
return data.toString('base64url');
|
|
10
|
+
}
|
|
11
|
+
function base64urlDecode(str) {
|
|
12
|
+
return Buffer.from(str, 'base64url');
|
|
13
|
+
}
|
|
14
|
+
// ── JWT helpers (ES256 / ECDSA P-256 with SHA-256) ───────────────────
|
|
15
|
+
/**
|
|
16
|
+
* Decode and verify a JWT signed with ES256.
|
|
17
|
+
* Returns the payload on success or `null` on any failure
|
|
18
|
+
* (bad format, invalid signature, expired, malformed JSON).
|
|
19
|
+
*/
|
|
20
|
+
export function validateLicense(key, publicKey) {
|
|
21
|
+
try {
|
|
22
|
+
const parts = key.trim().split('.');
|
|
23
|
+
if (parts.length !== 3)
|
|
24
|
+
return null;
|
|
25
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
26
|
+
// Verify header declares ES256
|
|
27
|
+
const header = JSON.parse(base64urlDecode(headerB64).toString('utf8'));
|
|
28
|
+
if (typeof header !== 'object' ||
|
|
29
|
+
header === null ||
|
|
30
|
+
header['alg'] !== 'ES256') {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// Verify signature
|
|
34
|
+
const signedContent = `${headerB64}.${payloadB64}`;
|
|
35
|
+
const signature = base64urlDecode(signatureB64);
|
|
36
|
+
// ES256 produces a 64-byte raw (r || s) signature.
|
|
37
|
+
// Node.js crypto.verify expects DER encoding, so convert if raw.
|
|
38
|
+
const derSignature = rawToDer(signature);
|
|
39
|
+
const verifier = crypto.createVerify('SHA256');
|
|
40
|
+
verifier.update(signedContent);
|
|
41
|
+
const valid = verifier.verify({ key: publicKey, dsaEncoding: 'der' }, derSignature);
|
|
42
|
+
if (!valid)
|
|
43
|
+
return null;
|
|
44
|
+
// Decode payload
|
|
45
|
+
const payloadJson = base64urlDecode(payloadB64).toString('utf8');
|
|
46
|
+
const payload = JSON.parse(payloadJson);
|
|
47
|
+
if (!isLicensePayload(payload))
|
|
48
|
+
return null;
|
|
49
|
+
// Check expiry
|
|
50
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
51
|
+
if (payload.exp <= nowSec)
|
|
52
|
+
return null;
|
|
53
|
+
return payload;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Load a license from the `.testteam-license` file in the given directory.
|
|
61
|
+
* The file must contain the JWT on the first non-empty line.
|
|
62
|
+
* A second non-empty line, if present, is treated as the PEM public key
|
|
63
|
+
* (or the public key can be embedded as `TF_LICENSE_PUBLIC_KEY` env var).
|
|
64
|
+
*/
|
|
65
|
+
export function loadLicense(configDir) {
|
|
66
|
+
// Return cached result if available
|
|
67
|
+
if (cachedLicense !== undefined)
|
|
68
|
+
return cachedLicense;
|
|
69
|
+
try {
|
|
70
|
+
const filePath = path.join(configDir, LICENSE_FILENAME);
|
|
71
|
+
if (!fs.existsSync(filePath)) {
|
|
72
|
+
cachedLicense = null;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const content = fs.readFileSync(filePath, 'utf8').trim();
|
|
76
|
+
if (!content) {
|
|
77
|
+
cachedLicense = null;
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
// Split into sections separated by blank lines
|
|
81
|
+
const sections = content.split(/\n\s*\n/);
|
|
82
|
+
const licenseKey = sections[0].trim();
|
|
83
|
+
// Public key: either embedded after the JWT or from env
|
|
84
|
+
let publicKey = process.env['TF_LICENSE_PUBLIC_KEY'] ?? '';
|
|
85
|
+
if (sections.length > 1) {
|
|
86
|
+
publicKey = sections.slice(1).join('\n\n').trim();
|
|
87
|
+
}
|
|
88
|
+
if (!publicKey) {
|
|
89
|
+
cachedLicense = null;
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const result = validateLicense(licenseKey, publicKey);
|
|
93
|
+
cachedLicense = result;
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
cachedLicense = null;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get the license tier for the current project.
|
|
103
|
+
* Falls back to `'free'` when no valid license is found.
|
|
104
|
+
*/
|
|
105
|
+
export function getLicenseTier(configDir) {
|
|
106
|
+
const dir = configDir ?? process.cwd();
|
|
107
|
+
const license = loadLicense(dir);
|
|
108
|
+
return license?.tier ?? 'free';
|
|
109
|
+
}
|
|
110
|
+
/** Reset the in-memory license cache (useful for testing). */
|
|
111
|
+
export function resetLicenseCache() {
|
|
112
|
+
cachedLicense = undefined;
|
|
113
|
+
}
|
|
114
|
+
// ── Internal helpers ─────────────────────────────────────────────────
|
|
115
|
+
/** Type guard for LicensePayload. */
|
|
116
|
+
function isLicensePayload(value) {
|
|
117
|
+
if (typeof value !== 'object' || value === null)
|
|
118
|
+
return false;
|
|
119
|
+
const obj = value;
|
|
120
|
+
if (!['free', 'pro', 'enterprise'].includes(obj['tier']))
|
|
121
|
+
return false;
|
|
122
|
+
if (typeof obj['exp'] !== 'number')
|
|
123
|
+
return false;
|
|
124
|
+
if (typeof obj['iat'] !== 'number')
|
|
125
|
+
return false;
|
|
126
|
+
if (typeof obj['sub'] !== 'string')
|
|
127
|
+
return false;
|
|
128
|
+
if (typeof obj['maxProjects'] !== 'number')
|
|
129
|
+
return false;
|
|
130
|
+
if (typeof obj['maxRunsPerMonth'] !== 'number')
|
|
131
|
+
return false;
|
|
132
|
+
if (!Array.isArray(obj['features']))
|
|
133
|
+
return false;
|
|
134
|
+
if (!obj['features'].every((f) => typeof f === 'string'))
|
|
135
|
+
return false;
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Convert a raw ECDSA signature (r || s, 64 bytes for P-256) to DER encoding.
|
|
140
|
+
* If the input is already DER-encoded (starts with 0x30), return as-is.
|
|
141
|
+
*/
|
|
142
|
+
function rawToDer(raw) {
|
|
143
|
+
// If already DER-encoded, return as-is
|
|
144
|
+
if (raw.length > 0 && raw[0] === 0x30)
|
|
145
|
+
return raw;
|
|
146
|
+
if (raw.length !== 64)
|
|
147
|
+
return raw; // Not a valid raw P-256 signature
|
|
148
|
+
const r = raw.subarray(0, 32);
|
|
149
|
+
const s = raw.subarray(32, 64);
|
|
150
|
+
const encodeInteger = (int) => {
|
|
151
|
+
// Strip leading zeros but keep at least one byte
|
|
152
|
+
let start = 0;
|
|
153
|
+
while (start < int.length - 1 && int[start] === 0)
|
|
154
|
+
start++;
|
|
155
|
+
let trimmed = int.subarray(start);
|
|
156
|
+
// If high bit is set, prepend a 0x00 byte
|
|
157
|
+
if (trimmed[0] & 0x80) {
|
|
158
|
+
trimmed = Buffer.concat([Buffer.from([0x00]), trimmed]);
|
|
159
|
+
}
|
|
160
|
+
return Buffer.concat([
|
|
161
|
+
Buffer.from([0x02, trimmed.length]),
|
|
162
|
+
trimmed,
|
|
163
|
+
]);
|
|
164
|
+
};
|
|
165
|
+
const rDer = encodeInteger(r);
|
|
166
|
+
const sDer = encodeInteger(s);
|
|
167
|
+
const body = Buffer.concat([rDer, sDer]);
|
|
168
|
+
return Buffer.concat([Buffer.from([0x30, body.length]), body]);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=license.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.js","sourceRoot":"","sources":["../../core/license.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LicenseTier } from './license';
|
|
2
|
+
/** Look up the display name for an agent number. */
|
|
3
|
+
export declare function getAgentDisplayName(agentNumber: number): string;
|
|
4
|
+
/** Message shown when an agent is gated by license tier. */
|
|
5
|
+
export declare function agentGatedMessage(agentNumber: number, agentName: string, requiredTier: LicenseTier, currentTier: LicenseTier): string;
|
|
6
|
+
/** Message shown when a phase is gated by license tier. */
|
|
7
|
+
export declare function phaseGatedMessage(phase: string, requiredTier: LicenseTier, currentTier: LicenseTier): string;
|
|
8
|
+
/** Message shown when the run limit has been reached. */
|
|
9
|
+
export declare function runLimitMessage(used: number, limit: number, tier: LicenseTier): string;
|
|
10
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../core/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AA+B7C,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,WAAW,EACzB,WAAW,EAAE,WAAW,GACvB,MAAM,CAER;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,WAAW,EACzB,WAAW,EAAE,WAAW,GACvB,MAAM,CAER;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,GAChB,MAAM,CAER"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const PRICING_URL = 'https://testteam.dev/pricing';
|
|
2
|
+
/** Agent names keyed by agent number. */
|
|
3
|
+
const AGENT_NAMES = {
|
|
4
|
+
1: 'Analyst',
|
|
5
|
+
2: 'Seed Architect',
|
|
6
|
+
3: 'Test Generator',
|
|
7
|
+
4: 'Unit Runner',
|
|
8
|
+
5: 'Browser Crawler',
|
|
9
|
+
6: 'API Exerciser',
|
|
10
|
+
7: 'Security Scout',
|
|
11
|
+
8: 'A11y Guardian',
|
|
12
|
+
9: 'Healer',
|
|
13
|
+
10: 'Reporter',
|
|
14
|
+
11: 'Fixer',
|
|
15
|
+
12: 'UX Inspector',
|
|
16
|
+
13: 'Performance Profiler',
|
|
17
|
+
14: 'Data Integrity Auditor',
|
|
18
|
+
15: 'Regression Sentinel',
|
|
19
|
+
16: 'Chaos Agent',
|
|
20
|
+
17: 'Documentation Validator',
|
|
21
|
+
18: 'Integration Watchdog',
|
|
22
|
+
19: 'Tenant Isolation Auditor',
|
|
23
|
+
20: 'Workflow Completion Tester',
|
|
24
|
+
21: 'State & Session Tester',
|
|
25
|
+
22: 'Email Notification Verifier',
|
|
26
|
+
23: 'Migration Tester',
|
|
27
|
+
};
|
|
28
|
+
/** Look up the display name for an agent number. */
|
|
29
|
+
export function getAgentDisplayName(agentNumber) {
|
|
30
|
+
return AGENT_NAMES[agentNumber] ?? `Agent ${agentNumber}`;
|
|
31
|
+
}
|
|
32
|
+
/** Message shown when an agent is gated by license tier. */
|
|
33
|
+
export function agentGatedMessage(agentNumber, agentName, requiredTier, currentTier) {
|
|
34
|
+
return `[Agent ${agentNumber}: ${agentName}] requires ${capitalise(requiredTier)} tier — skipped (current: ${currentTier})`;
|
|
35
|
+
}
|
|
36
|
+
/** Message shown when a phase is gated by license tier. */
|
|
37
|
+
export function phaseGatedMessage(phase, requiredTier, currentTier) {
|
|
38
|
+
return `${capitalise(phase)} phase requires ${capitalise(requiredTier)} tier — upgrade at ${PRICING_URL} (current: ${currentTier})`;
|
|
39
|
+
}
|
|
40
|
+
/** Message shown when the run limit has been reached. */
|
|
41
|
+
export function runLimitMessage(used, limit, tier) {
|
|
42
|
+
return `Run limit reached: ${used}/${limit} runs used on ${capitalise(tier)} tier — upgrade at ${PRICING_URL}`;
|
|
43
|
+
}
|
|
44
|
+
function capitalise(str) {
|
|
45
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../core/messages.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,8BAA8B,CAAC;AAEnD,yCAAyC;AACzC,MAAM,WAAW,GAA2B;IAC1C,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,gBAAgB;IACnB,CAAC,EAAE,gBAAgB;IACnB,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,iBAAiB;IACpB,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,gBAAgB;IACnB,CAAC,EAAE,eAAe;IAClB,CAAC,EAAE,QAAQ;IACX,EAAE,EAAE,UAAU;IACd,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,cAAc;IAClB,EAAE,EAAE,sBAAsB;IAC1B,EAAE,EAAE,wBAAwB;IAC5B,EAAE,EAAE,qBAAqB;IACzB,EAAE,EAAE,aAAa;IACjB,EAAE,EAAE,yBAAyB;IAC7B,EAAE,EAAE,sBAAsB;IAC1B,EAAE,EAAE,0BAA0B;IAC9B,EAAE,EAAE,4BAA4B;IAChC,EAAE,EAAE,wBAAwB;IAC5B,EAAE,EAAE,6BAA6B;IACjC,EAAE,EAAE,kBAAkB;CACvB,CAAC;AAEF,oDAAoD;AACpD,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,OAAO,WAAW,CAAC,WAAW,CAAC,IAAI,SAAS,WAAW,EAAE,CAAC;AAC5D,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,SAAiB,EACjB,YAAyB,EACzB,WAAwB;IAExB,OAAO,UAAU,WAAW,KAAK,SAAS,cAAc,UAAU,CAAC,YAAY,CAAC,6BAA6B,WAAW,GAAG,CAAC;AAC9H,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,YAAyB,EACzB,WAAwB;IAExB,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,mBAAmB,UAAU,CAAC,YAAY,CAAC,sBAAsB,WAAW,cAAc,WAAW,GAAG,CAAC;AACtI,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,KAAa,EACb,IAAiB;IAEjB,OAAO,sBAAsB,IAAI,IAAI,KAAK,iBAAiB,UAAU,CAAC,IAAI,CAAC,sBAAsB,WAAW,EAAE,CAAC;AACjH,CAAC;AAED,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AgentResult, Finding, Phase } from './types';
|
|
2
|
+
import type { TestTeamConfig } from './config';
|
|
3
|
+
import type { BaseAgent } from '../agents/base-agent';
|
|
4
|
+
export type BrowserEngine = 'chromium' | 'firefox' | 'webkit';
|
|
5
|
+
export interface MultiBrowserConfig {
|
|
6
|
+
engines: BrowserEngine[];
|
|
7
|
+
parallel: boolean;
|
|
8
|
+
compareResults: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MultiBrowserResult {
|
|
11
|
+
engine: BrowserEngine;
|
|
12
|
+
result: AgentResult;
|
|
13
|
+
duration: number;
|
|
14
|
+
}
|
|
15
|
+
export interface CrossBrowserDiff {
|
|
16
|
+
engine1: BrowserEngine;
|
|
17
|
+
engine2: BrowserEngine;
|
|
18
|
+
differences: Finding[];
|
|
19
|
+
}
|
|
20
|
+
/** Default configuration for multi-browser execution. */
|
|
21
|
+
export declare const DEFAULT_MULTI_BROWSER_CONFIG: MultiBrowserConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Run an agent across multiple browser engines, optionally in parallel,
|
|
24
|
+
* and compare findings across browsers.
|
|
25
|
+
*/
|
|
26
|
+
export declare function runMultiBrowser(agent: BaseAgent, _config: TestTeamConfig, _phase: Phase, browserConfig: MultiBrowserConfig): Promise<{
|
|
27
|
+
results: MultiBrowserResult[];
|
|
28
|
+
diffs: CrossBrowserDiff[];
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Compare findings across browser results.
|
|
32
|
+
* A finding is considered unique to a browser if no finding in the other
|
|
33
|
+
* browser shares the same base description (stripping the engine prefix tag).
|
|
34
|
+
*/
|
|
35
|
+
export declare function compareBrowserResults(results: MultiBrowserResult[]): CrossBrowserDiff[];
|
|
36
|
+
//# sourceMappingURL=multi-browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-browser.d.ts","sourceRoot":"","sources":["../../core/multi-browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB;AAED,yDAAyD;AACzD,eAAO,MAAM,4BAA4B,EAAE,kBAI1C,CAAC;AAEF;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,KAAK,EACb,aAAa,EAAE,kBAAkB,GAChC,OAAO,CAAC;IAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAAC,KAAK,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAAC,CAqCvE;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,EAAE,GAC5B,gBAAgB,EAAE,CA4CpB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Default configuration for multi-browser execution. */
|
|
2
|
+
export const DEFAULT_MULTI_BROWSER_CONFIG = {
|
|
3
|
+
engines: ['chromium'],
|
|
4
|
+
parallel: true,
|
|
5
|
+
compareResults: true,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Run an agent across multiple browser engines, optionally in parallel,
|
|
9
|
+
* and compare findings across browsers.
|
|
10
|
+
*/
|
|
11
|
+
export async function runMultiBrowser(agent, _config, _phase, browserConfig) {
|
|
12
|
+
const runSingleEngine = async (engine) => {
|
|
13
|
+
const start = Date.now();
|
|
14
|
+
const result = await agent.run();
|
|
15
|
+
const duration = Date.now() - start;
|
|
16
|
+
// Tag each finding with the browser engine for traceability
|
|
17
|
+
const taggedFindings = result.findings.map(f => ({
|
|
18
|
+
...f,
|
|
19
|
+
description: `[${engine}] ${f.description}`,
|
|
20
|
+
}));
|
|
21
|
+
return {
|
|
22
|
+
engine,
|
|
23
|
+
result: { ...result, findings: taggedFindings },
|
|
24
|
+
duration,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
let results;
|
|
28
|
+
if (browserConfig.parallel) {
|
|
29
|
+
results = await Promise.all(browserConfig.engines.map(engine => runSingleEngine(engine)));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
results = [];
|
|
33
|
+
for (const engine of browserConfig.engines) {
|
|
34
|
+
results.push(await runSingleEngine(engine));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const diffs = browserConfig.compareResults
|
|
38
|
+
? compareBrowserResults(results)
|
|
39
|
+
: [];
|
|
40
|
+
return { results, diffs };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compare findings across browser results.
|
|
44
|
+
* A finding is considered unique to a browser if no finding in the other
|
|
45
|
+
* browser shares the same base description (stripping the engine prefix tag).
|
|
46
|
+
*/
|
|
47
|
+
export function compareBrowserResults(results) {
|
|
48
|
+
if (results.length < 2) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const diffs = [];
|
|
52
|
+
for (let i = 0; i < results.length; i++) {
|
|
53
|
+
for (let j = i + 1; j < results.length; j++) {
|
|
54
|
+
const r1 = results[i];
|
|
55
|
+
const r2 = results[j];
|
|
56
|
+
const descriptions1 = new Set(r1.result.findings.map(f => stripEngineTag(f.description)));
|
|
57
|
+
const descriptions2 = new Set(r2.result.findings.map(f => stripEngineTag(f.description)));
|
|
58
|
+
const differences = [];
|
|
59
|
+
// Findings in engine1 but not in engine2
|
|
60
|
+
for (const finding of r1.result.findings) {
|
|
61
|
+
const baseDesc = stripEngineTag(finding.description);
|
|
62
|
+
if (!descriptions2.has(baseDesc)) {
|
|
63
|
+
differences.push(finding);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Findings in engine2 but not in engine1
|
|
67
|
+
for (const finding of r2.result.findings) {
|
|
68
|
+
const baseDesc = stripEngineTag(finding.description);
|
|
69
|
+
if (!descriptions1.has(baseDesc)) {
|
|
70
|
+
differences.push(finding);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (differences.length > 0) {
|
|
74
|
+
diffs.push({
|
|
75
|
+
engine1: r1.engine,
|
|
76
|
+
engine2: r2.engine,
|
|
77
|
+
differences,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return diffs;
|
|
83
|
+
}
|
|
84
|
+
/** Strip the "[engine] " prefix tag from a finding description. */
|
|
85
|
+
function stripEngineTag(description) {
|
|
86
|
+
return description.replace(/^\[(chromium|firefox|webkit)\]\s*/, '');
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=multi-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-browser.js","sourceRoot":"","sources":["../../core/multi-browser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Phase, AgentResult, PhaseGateResult } from './types';
|
|
2
|
+
import type { ValidatedConfig } from './config';
|
|
3
|
+
import type { BaseAgent } from '../agents/base-agent';
|
|
4
|
+
import type { HealthCheck } from './health-check';
|
|
5
|
+
import { CostTracker } from './cost-tracker';
|
|
6
|
+
import type { LicenseTier } from './license';
|
|
7
|
+
export interface DispatchPlanEntry {
|
|
8
|
+
agentId: number;
|
|
9
|
+
status: 'scheduled' | 'disabled' | 'unregistered';
|
|
10
|
+
}
|
|
11
|
+
export interface PhaseRunResult {
|
|
12
|
+
gate: PhaseGateResult;
|
|
13
|
+
agentResults: Record<number, AgentResult>;
|
|
14
|
+
skippedAgents: number[];
|
|
15
|
+
costTracker: CostTracker;
|
|
16
|
+
}
|
|
17
|
+
export declare class Orchestrator {
|
|
18
|
+
private readonly config;
|
|
19
|
+
private readonly agents;
|
|
20
|
+
private readonly healthChecks;
|
|
21
|
+
private runDir;
|
|
22
|
+
/**
|
|
23
|
+
* License tier resolver — injectable for testing.
|
|
24
|
+
* Defaults to `getLicenseTier()` from the license module.
|
|
25
|
+
*/
|
|
26
|
+
_getLicenseTier: () => LicenseTier;
|
|
27
|
+
constructor(config: ValidatedConfig, runDir?: string);
|
|
28
|
+
/** Register an agent instance for a given agent ID. */
|
|
29
|
+
registerAgent(agentId: number, agent: BaseAgent): void;
|
|
30
|
+
/** Register health checks to run before agent dispatch. */
|
|
31
|
+
registerHealthChecks(checks: HealthCheck[]): void;
|
|
32
|
+
/** Return the dispatch plan for a phase without executing anything. */
|
|
33
|
+
dryRun(phase: Phase): DispatchPlanEntry[];
|
|
34
|
+
/** Execute all agents for the given phase, evaluate the gate, and return results. */
|
|
35
|
+
runPhase(phase: Phase): Promise<PhaseRunResult>;
|
|
36
|
+
/**
|
|
37
|
+
* After all phase agents complete, classify findings and dispatch cross-phase
|
|
38
|
+
* agents on demand:
|
|
39
|
+
* - Agent 9 (Healer) for test-bug findings
|
|
40
|
+
* - Agent 11 (Fixer) for code-bug findings
|
|
41
|
+
*
|
|
42
|
+
* Iterates up to MAX_FIX_LOOP_ITERATIONS times to prevent infinite heal/fix loops.
|
|
43
|
+
*/
|
|
44
|
+
private runFixLoop;
|
|
45
|
+
/** Get the run directory path. */
|
|
46
|
+
getRunDir(): string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|