qa360 1.0.3 → 1.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/dist/commands/history.js +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/run.d.ts +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/serve.js +1 -1
- package/dist/commands/verify.js +1 -1
- package/dist/core/adapters/gitleaks-secrets.d.ts +115 -0
- package/dist/core/adapters/gitleaks-secrets.d.ts.map +1 -0
- package/dist/core/adapters/gitleaks-secrets.js +410 -0
- package/dist/core/adapters/k6-perf.d.ts +86 -0
- package/dist/core/adapters/k6-perf.d.ts.map +1 -0
- package/dist/core/adapters/k6-perf.js +398 -0
- package/dist/core/adapters/osv-deps.d.ts +124 -0
- package/dist/core/adapters/osv-deps.d.ts.map +1 -0
- package/dist/core/adapters/osv-deps.js +372 -0
- package/dist/core/adapters/playwright-api.d.ts +82 -0
- package/dist/core/adapters/playwright-api.d.ts.map +1 -0
- package/dist/core/adapters/playwright-api.js +252 -0
- package/dist/core/adapters/playwright-ui.d.ts +115 -0
- package/dist/core/adapters/playwright-ui.d.ts.map +1 -0
- package/dist/core/adapters/playwright-ui.js +346 -0
- package/dist/core/adapters/semgrep-sast.d.ts +100 -0
- package/dist/core/adapters/semgrep-sast.d.ts.map +1 -0
- package/dist/core/adapters/semgrep-sast.js +322 -0
- package/dist/core/adapters/zap-dast.d.ts +134 -0
- package/dist/core/adapters/zap-dast.d.ts.map +1 -0
- package/dist/core/adapters/zap-dast.js +424 -0
- package/dist/core/hooks/compose.d.ts +62 -0
- package/dist/core/hooks/compose.d.ts.map +1 -0
- package/dist/core/hooks/compose.js +225 -0
- package/dist/core/hooks/runner.d.ts +69 -0
- package/dist/core/hooks/runner.d.ts.map +1 -0
- package/dist/core/hooks/runner.js +303 -0
- package/dist/core/index.d.ts +74 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +39 -0
- package/dist/core/pack/migrator.d.ts +52 -0
- package/dist/core/pack/migrator.d.ts.map +1 -0
- package/dist/core/pack/migrator.js +304 -0
- package/dist/core/pack/validator.d.ts +43 -0
- package/dist/core/pack/validator.d.ts.map +1 -0
- package/dist/core/pack/validator.js +292 -0
- package/dist/core/proof/bundle.d.ts +138 -0
- package/dist/core/proof/bundle.d.ts.map +1 -0
- package/dist/core/proof/bundle.js +160 -0
- package/dist/core/proof/canonicalize.d.ts +48 -0
- package/dist/core/proof/canonicalize.d.ts.map +1 -0
- package/dist/core/proof/canonicalize.js +105 -0
- package/dist/core/proof/index.d.ts +14 -0
- package/dist/core/proof/index.d.ts.map +1 -0
- package/dist/core/proof/index.js +18 -0
- package/dist/core/proof/schema.d.ts +218 -0
- package/dist/core/proof/schema.d.ts.map +1 -0
- package/dist/core/proof/schema.js +263 -0
- package/dist/core/proof/signer.d.ts +112 -0
- package/dist/core/proof/signer.d.ts.map +1 -0
- package/dist/core/proof/signer.js +226 -0
- package/dist/core/proof/verifier.d.ts +98 -0
- package/dist/core/proof/verifier.d.ts.map +1 -0
- package/dist/core/proof/verifier.js +302 -0
- package/dist/core/runner/phase3-runner.d.ts +102 -0
- package/dist/core/runner/phase3-runner.d.ts.map +1 -0
- package/dist/core/runner/phase3-runner.js +471 -0
- package/dist/core/secrets/crypto.d.ts +76 -0
- package/dist/core/secrets/crypto.d.ts.map +1 -0
- package/dist/core/secrets/crypto.js +225 -0
- package/dist/core/secrets/manager.d.ts +77 -0
- package/dist/core/secrets/manager.d.ts.map +1 -0
- package/dist/core/secrets/manager.js +219 -0
- package/dist/core/security/redaction-patterns-extended.d.ts +28 -0
- package/dist/core/security/redaction-patterns-extended.d.ts.map +1 -0
- package/dist/core/security/redaction-patterns-extended.js +247 -0
- package/dist/core/security/redactor.d.ts +72 -0
- package/dist/core/security/redactor.d.ts.map +1 -0
- package/dist/core/security/redactor.js +279 -0
- package/dist/core/serve/diagnostics-collector.d.ts +33 -0
- package/dist/core/serve/diagnostics-collector.d.ts.map +1 -0
- package/dist/core/serve/diagnostics-collector.js +149 -0
- package/dist/core/serve/health-checker.d.ts +45 -0
- package/dist/core/serve/health-checker.d.ts.map +1 -0
- package/dist/core/serve/health-checker.js +219 -0
- package/dist/core/serve/index.d.ts +9 -0
- package/dist/core/serve/index.d.ts.map +1 -0
- package/dist/core/serve/index.js +8 -0
- package/dist/core/serve/metrics-collector.d.ts +25 -0
- package/dist/core/serve/metrics-collector.d.ts.map +1 -0
- package/dist/core/serve/metrics-collector.js +322 -0
- package/dist/core/serve/process-manager.d.ts +37 -0
- package/dist/core/serve/process-manager.d.ts.map +1 -0
- package/dist/core/serve/process-manager.js +213 -0
- package/dist/core/serve/server.d.ts +37 -0
- package/dist/core/serve/server.d.ts.map +1 -0
- package/dist/core/serve/server.js +191 -0
- package/dist/core/types/pack-v1.d.ts +162 -0
- package/dist/core/types/pack-v1.d.ts.map +1 -0
- package/dist/core/types/pack-v1.js +5 -0
- package/dist/core/types/trust-score.d.ts +70 -0
- package/dist/core/types/trust-score.d.ts.map +1 -0
- package/dist/core/types/trust-score.js +191 -0
- package/dist/core/vault/cas.d.ts +87 -0
- package/dist/core/vault/cas.d.ts.map +1 -0
- package/dist/core/vault/cas.js +255 -0
- package/dist/core/vault/index.d.ts +205 -0
- package/dist/core/vault/index.d.ts.map +1 -0
- package/dist/core/vault/index.js +631 -0
- package/package.json +12 -6
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Diagnostics Collector
|
|
3
|
+
* Collecte des diagnostics actionnables pour /diag
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { HealthChecker } from './health-checker.js';
|
|
8
|
+
export class DiagnosticsCollector {
|
|
9
|
+
healthChecker;
|
|
10
|
+
constructor() {
|
|
11
|
+
this.healthChecker = new HealthChecker();
|
|
12
|
+
}
|
|
13
|
+
async collect() {
|
|
14
|
+
const health = await this.healthChecker.check();
|
|
15
|
+
const problems = this.analyzeProblems(health);
|
|
16
|
+
const suggestions = this.generateSuggestions(health);
|
|
17
|
+
const lastRuns = await this.getLastRuns();
|
|
18
|
+
return {
|
|
19
|
+
problems,
|
|
20
|
+
suggestions,
|
|
21
|
+
last_runs: lastRuns
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
analyzeProblems(health) {
|
|
25
|
+
const problems = [];
|
|
26
|
+
// Configuration problems
|
|
27
|
+
if (health.checks.config === 'error') {
|
|
28
|
+
problems.push({
|
|
29
|
+
code: 'DOC001',
|
|
30
|
+
title: 'QA360 directory not accessible',
|
|
31
|
+
fix: 'qa360 doctor --fix config'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// Tool problems
|
|
35
|
+
if (health.checks.tools.playwright === 'missing') {
|
|
36
|
+
problems.push({
|
|
37
|
+
code: 'DOC201',
|
|
38
|
+
title: 'Playwright not found',
|
|
39
|
+
fix: 'qa360 doctor --fix tools=playwright'
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (health.checks.tools.k6 === 'missing') {
|
|
43
|
+
problems.push({
|
|
44
|
+
code: 'DOC202',
|
|
45
|
+
title: 'k6 not found',
|
|
46
|
+
fix: 'qa360 doctor --fix tools=k6'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (health.checks.tools.zap === 'missing') {
|
|
50
|
+
problems.push({
|
|
51
|
+
code: 'DOC203',
|
|
52
|
+
title: 'ZAP not found',
|
|
53
|
+
fix: 'qa360 doctor --fix tools=zap'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Secrets problems
|
|
57
|
+
if (health.checks.secrets === 'missing') {
|
|
58
|
+
problems.push({
|
|
59
|
+
code: 'SEC101',
|
|
60
|
+
title: 'Secrets file missing',
|
|
61
|
+
fix: 'qa360 secrets add API_TOKEN'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// System problems
|
|
65
|
+
if (!health.checks.tmp_writable) {
|
|
66
|
+
problems.push({
|
|
67
|
+
code: 'SYS001',
|
|
68
|
+
title: 'Temporary directory not writable',
|
|
69
|
+
fix: 'Check permissions on ' + require('os').tmpdir()
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (health.checks.disk_free_mb < 500) {
|
|
73
|
+
problems.push({
|
|
74
|
+
code: 'SYS002',
|
|
75
|
+
title: 'Low disk space',
|
|
76
|
+
fix: 'Free up disk space (< 500MB available)'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return problems;
|
|
80
|
+
}
|
|
81
|
+
generateSuggestions(health) {
|
|
82
|
+
const suggestions = [];
|
|
83
|
+
// Performance suggestions
|
|
84
|
+
if (health.checks.tools.k6 === 'missing') {
|
|
85
|
+
suggestions.push('Enable Docker fallback: qa360 run --docker');
|
|
86
|
+
}
|
|
87
|
+
// Security suggestions
|
|
88
|
+
if (health.checks.tools.zap === 'missing') {
|
|
89
|
+
suggestions.push('Install ZAP for DAST security testing');
|
|
90
|
+
}
|
|
91
|
+
// Configuration suggestions
|
|
92
|
+
const packPath = join(process.cwd(), '.qa360', 'pack.yml');
|
|
93
|
+
if (!existsSync(packPath)) {
|
|
94
|
+
suggestions.push('Generate pack configuration: qa360 ask "Test my web app"');
|
|
95
|
+
}
|
|
96
|
+
// Budget suggestions based on common patterns
|
|
97
|
+
suggestions.push('Reduce perf budget p95_ms to 900 for demo profile');
|
|
98
|
+
suggestions.push('Enable strict mode for production: budgets.mode=strict');
|
|
99
|
+
return suggestions;
|
|
100
|
+
}
|
|
101
|
+
async getLastRuns() {
|
|
102
|
+
try {
|
|
103
|
+
const runsDir = join(process.cwd(), '.qa360', 'runs');
|
|
104
|
+
if (!existsSync(runsDir)) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const fs = require('fs');
|
|
108
|
+
const runFiles = fs.readdirSync(runsDir)
|
|
109
|
+
.filter((f) => f.endsWith('.json'))
|
|
110
|
+
.sort()
|
|
111
|
+
.slice(-5); // Last 5 runs
|
|
112
|
+
const runs = [];
|
|
113
|
+
for (const file of runFiles) {
|
|
114
|
+
try {
|
|
115
|
+
const runPath = join(runsDir, file);
|
|
116
|
+
const runData = JSON.parse(fs.readFileSync(runPath, 'utf8'));
|
|
117
|
+
runs.push({
|
|
118
|
+
runId: runData.runId || file.replace('.json', ''),
|
|
119
|
+
trust: runData.trustScore || runData.trust_score || 0,
|
|
120
|
+
ts: runData.timestamp || runData.ts || new Date().toISOString(),
|
|
121
|
+
status: this.inferRunStatus(runData)
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Skip invalid run files
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return runs.reverse(); // Most recent first
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
inferRunStatus(runData) {
|
|
135
|
+
if (runData.status) {
|
|
136
|
+
return runData.status;
|
|
137
|
+
}
|
|
138
|
+
if (runData.cancelled || runData.error?.includes('cancelled')) {
|
|
139
|
+
return 'cancelled';
|
|
140
|
+
}
|
|
141
|
+
if (runData.error || runData.errors?.length > 0) {
|
|
142
|
+
return 'failed';
|
|
143
|
+
}
|
|
144
|
+
if (runData.trustScore >= 70 || runData.trust_score >= 70) {
|
|
145
|
+
return 'success';
|
|
146
|
+
}
|
|
147
|
+
return 'failed';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Health Checker
|
|
3
|
+
* Vérifie liveness, readiness et état des outils
|
|
4
|
+
*/
|
|
5
|
+
export interface HealthStatus {
|
|
6
|
+
status: 'ok' | 'degraded' | 'error';
|
|
7
|
+
version: string;
|
|
8
|
+
commit: string;
|
|
9
|
+
env: {
|
|
10
|
+
node: string;
|
|
11
|
+
platform: string;
|
|
12
|
+
};
|
|
13
|
+
checks: {
|
|
14
|
+
config: 'ok' | 'error';
|
|
15
|
+
tools: {
|
|
16
|
+
playwright: 'ok' | 'missing';
|
|
17
|
+
k6: 'ok' | 'missing';
|
|
18
|
+
zap: 'ok' | 'missing';
|
|
19
|
+
};
|
|
20
|
+
secrets: 'ok' | 'missing';
|
|
21
|
+
disk_free_mb: number;
|
|
22
|
+
tmp_writable: boolean;
|
|
23
|
+
errors?: string[];
|
|
24
|
+
};
|
|
25
|
+
readiness: {
|
|
26
|
+
ready: boolean;
|
|
27
|
+
reason: string | null;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare class HealthChecker {
|
|
31
|
+
private packageInfo;
|
|
32
|
+
constructor();
|
|
33
|
+
check(): Promise<HealthStatus>;
|
|
34
|
+
private performChecks;
|
|
35
|
+
private checkConfigDir;
|
|
36
|
+
private checkConfig;
|
|
37
|
+
private checkTools;
|
|
38
|
+
private checkTool;
|
|
39
|
+
private checkSecrets;
|
|
40
|
+
private checkDiskSpace;
|
|
41
|
+
private checkTmpWritable;
|
|
42
|
+
private assessReadiness;
|
|
43
|
+
private getCommitHash;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=health-checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-checker.d.ts","sourceRoot":"","sources":["../../../src/core/serve/health-checker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,GAAG,UAAU,GAAG,OAAO,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;QACvB,KAAK,EAAE;YACL,UAAU,EAAE,IAAI,GAAG,SAAS,CAAC;YAC7B,EAAE,EAAE,IAAI,GAAG,SAAS,CAAC;YACrB,GAAG,EAAE,IAAI,GAAG,SAAS,CAAC;SACvB,CAAC;QACF,OAAO,EAAE,IAAI,GAAG,SAAS,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,OAAO,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,SAAS,EAAE;QACT,KAAK,EAAE,OAAO,CAAC;QACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,CAAC;CACH;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAM;;IAUnB,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC;YA8BtB,aAAa;IA8B3B,OAAO,CAAC,cAAc;YAWR,WAAW;YAeX,UAAU;YAkBV,SAAS;YAwCT,YAAY;YASZ,cAAc;IAU5B,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,aAAa;CAoBtB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Health Checker
|
|
3
|
+
* Vérifie liveness, readiness et état des outils
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, statSync, writeFileSync, unlinkSync } from 'fs';
|
|
6
|
+
import { tmpdir } from 'os';
|
|
7
|
+
import { join, resolve } from 'path';
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
export class HealthChecker {
|
|
10
|
+
packageInfo;
|
|
11
|
+
constructor() {
|
|
12
|
+
try {
|
|
13
|
+
this.packageInfo = require('../../package.json');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
this.packageInfo = { version: '0.9.0-core', name: '@qa360/core' };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async check() {
|
|
20
|
+
const checks = await this.performChecks();
|
|
21
|
+
const readiness = this.assessReadiness(checks);
|
|
22
|
+
// Determine overall status
|
|
23
|
+
let status = 'ok';
|
|
24
|
+
if (!readiness.ready) {
|
|
25
|
+
status = 'error';
|
|
26
|
+
}
|
|
27
|
+
else if (checks.tools.playwright === 'missing' ||
|
|
28
|
+
checks.tools.k6 === 'missing' ||
|
|
29
|
+
checks.secrets === 'missing') {
|
|
30
|
+
status = 'degraded';
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
status,
|
|
34
|
+
version: this.packageInfo.version,
|
|
35
|
+
commit: this.getCommitHash(),
|
|
36
|
+
env: {
|
|
37
|
+
node: process.version,
|
|
38
|
+
platform: process.platform
|
|
39
|
+
},
|
|
40
|
+
checks,
|
|
41
|
+
readiness
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async performChecks() {
|
|
45
|
+
const [configCheck, toolsCheck, secretsCheck, diskCheck, tmpCheck] = await Promise.all([
|
|
46
|
+
this.checkConfig(),
|
|
47
|
+
this.checkTools(),
|
|
48
|
+
this.checkSecrets(),
|
|
49
|
+
this.checkDiskSpace(),
|
|
50
|
+
this.checkTmpWritable()
|
|
51
|
+
]);
|
|
52
|
+
// Collect error codes
|
|
53
|
+
const errors = [];
|
|
54
|
+
if (!tmpCheck.ok)
|
|
55
|
+
errors.push(tmpCheck.code);
|
|
56
|
+
if (configCheck.code)
|
|
57
|
+
errors.push(configCheck.code);
|
|
58
|
+
return {
|
|
59
|
+
config: configCheck.ok ? 'ok' : 'error',
|
|
60
|
+
tools: toolsCheck,
|
|
61
|
+
secrets: secretsCheck,
|
|
62
|
+
disk_free_mb: diskCheck,
|
|
63
|
+
tmp_writable: tmpCheck.ok,
|
|
64
|
+
...(errors.length > 0 && { errors })
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
checkConfigDir() {
|
|
68
|
+
const dir = resolve(process.cwd(), '.qa360');
|
|
69
|
+
try {
|
|
70
|
+
const s = statSync(dir);
|
|
71
|
+
if (!s.isDirectory())
|
|
72
|
+
throw new Error('not a dir');
|
|
73
|
+
return { ok: true };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { ok: false, code: 'HLT002', reason: 'Configuration error - cannot access .qa360 directory' };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async checkConfig() {
|
|
80
|
+
try {
|
|
81
|
+
// Check if basic pack structure can be loaded
|
|
82
|
+
const packPath = join(process.cwd(), '.qa360', 'pack.yml');
|
|
83
|
+
if (existsSync(packPath)) {
|
|
84
|
+
return { ok: true };
|
|
85
|
+
}
|
|
86
|
+
// Check if we can create .qa360 directory
|
|
87
|
+
return this.checkConfigDir();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return { ok: false, code: 'HLT002', reason: 'Configuration error - cannot access .qa360 directory' };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async checkTools() {
|
|
94
|
+
const [playwright, k6, zap] = await Promise.all([
|
|
95
|
+
this.checkTool('npx playwright --version'),
|
|
96
|
+
this.checkTool('k6 version'),
|
|
97
|
+
this.checkTool('zap-baseline.py --help')
|
|
98
|
+
]);
|
|
99
|
+
return {
|
|
100
|
+
playwright: playwright === 'error' ? 'missing' : playwright,
|
|
101
|
+
k6: k6 === 'error' ? 'missing' : k6,
|
|
102
|
+
zap: zap === 'error' ? 'missing' : zap
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async checkTool(command) {
|
|
106
|
+
try {
|
|
107
|
+
const [cmd, ...args] = command.split(' ');
|
|
108
|
+
const child = spawn(cmd, args, { stdio: 'ignore' });
|
|
109
|
+
// NEW: garde-fous pour mocks qui renvoient undefined
|
|
110
|
+
if (!child || typeof child.on !== 'function') {
|
|
111
|
+
return 'missing';
|
|
112
|
+
}
|
|
113
|
+
return await new Promise(resolve => {
|
|
114
|
+
let resolved = false;
|
|
115
|
+
const timer = setTimeout(() => {
|
|
116
|
+
if (!resolved) {
|
|
117
|
+
resolved = true;
|
|
118
|
+
resolve('missing');
|
|
119
|
+
}
|
|
120
|
+
}, 3000);
|
|
121
|
+
child.on('error', () => {
|
|
122
|
+
if (!resolved) {
|
|
123
|
+
resolved = true;
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
resolve('missing');
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
child.on('exit', (code) => {
|
|
129
|
+
if (!resolved) {
|
|
130
|
+
resolved = true;
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
resolve(code === 0 ? 'ok' : 'missing');
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return 'missing';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async checkSecrets() {
|
|
142
|
+
try {
|
|
143
|
+
const secretsPath = join(process.cwd(), '.qa360', 'secrets.json');
|
|
144
|
+
return existsSync(secretsPath) ? 'ok' : 'missing';
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return 'missing';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async checkDiskSpace() {
|
|
151
|
+
try {
|
|
152
|
+
const stats = statSync(process.cwd());
|
|
153
|
+
// Approximation - in real scenario, use statvfs or similar
|
|
154
|
+
return 2048; // Default 2GB assumption
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
checkTmpWritable() {
|
|
161
|
+
try {
|
|
162
|
+
const p = join(tmpdir(), `qa360-${Date.now()}.tmp`);
|
|
163
|
+
writeFileSync(p, 'ok');
|
|
164
|
+
unlinkSync(p);
|
|
165
|
+
return { ok: true };
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return { ok: false, code: 'HLT001', reason: 'tmp not writable' };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
assessReadiness(checks) {
|
|
172
|
+
// Critical checks for readiness
|
|
173
|
+
if (!checks.tmp_writable) {
|
|
174
|
+
return {
|
|
175
|
+
ready: false,
|
|
176
|
+
reason: 'HLT001: Temporary directory not writable'
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (checks.config === 'error') {
|
|
180
|
+
return {
|
|
181
|
+
ready: false,
|
|
182
|
+
reason: 'HLT002: Configuration error - cannot access .qa360 directory'
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (checks.disk_free_mb < 100) {
|
|
186
|
+
return {
|
|
187
|
+
ready: false,
|
|
188
|
+
reason: 'HLT003: Insufficient disk space (< 100MB)'
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// All critical checks passed
|
|
192
|
+
return {
|
|
193
|
+
ready: true,
|
|
194
|
+
reason: null
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
getCommitHash() {
|
|
198
|
+
try {
|
|
199
|
+
const fs = require('fs');
|
|
200
|
+
const gitHead = join(process.cwd(), '.git', 'HEAD');
|
|
201
|
+
if (existsSync(gitHead)) {
|
|
202
|
+
const head = fs.readFileSync(gitHead, 'utf8').trim();
|
|
203
|
+
if (head.startsWith('ref: ')) {
|
|
204
|
+
const refPath = join(process.cwd(), '.git', head.substring(5));
|
|
205
|
+
if (existsSync(refPath)) {
|
|
206
|
+
return fs.readFileSync(refPath, 'utf8').trim().substring(0, 7);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return head.substring(0, 7);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return 'unknown';
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return 'unknown';
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Serve Module Exports
|
|
3
|
+
*/
|
|
4
|
+
export { QA360Server, ServeConfig, ServeResponse } from './server';
|
|
5
|
+
export { HealthChecker, HealthStatus } from './health-checker';
|
|
6
|
+
export { DiagnosticsCollector, DiagnosticsReport, DiagnosticProblem, LastRun } from './diagnostics-collector';
|
|
7
|
+
export { MetricsCollector, MetricValue } from './metrics-collector';
|
|
8
|
+
export { ProcessManager, RunProcess, CancelResult } from './process-manager';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/serve/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAC9G,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Serve Module Exports
|
|
3
|
+
*/
|
|
4
|
+
export { QA360Server } from './server';
|
|
5
|
+
export { HealthChecker } from './health-checker';
|
|
6
|
+
export { DiagnosticsCollector } from './diagnostics-collector';
|
|
7
|
+
export { MetricsCollector } from './metrics-collector';
|
|
8
|
+
export { ProcessManager } from './process-manager';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Metrics Collector
|
|
3
|
+
* Génère des métriques au format OpenMetrics/Prometheus
|
|
4
|
+
*/
|
|
5
|
+
export interface MetricValue {
|
|
6
|
+
name: string;
|
|
7
|
+
value: number;
|
|
8
|
+
labels?: Record<string, string>;
|
|
9
|
+
help?: string;
|
|
10
|
+
type?: 'counter' | 'gauge' | 'histogram' | 'summary';
|
|
11
|
+
}
|
|
12
|
+
export declare class MetricsCollector {
|
|
13
|
+
private startTime;
|
|
14
|
+
constructor();
|
|
15
|
+
collect(): Promise<string>;
|
|
16
|
+
private gatherMetrics;
|
|
17
|
+
private collectRunMetrics;
|
|
18
|
+
private collectGateMetrics;
|
|
19
|
+
private collectSecurityMetrics;
|
|
20
|
+
private collectPerformanceMetrics;
|
|
21
|
+
private getLastRunData;
|
|
22
|
+
private formatOpenMetrics;
|
|
23
|
+
private getVersion;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=metrics-collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-collector.d.ts","sourceRoot":"","sources":["../../../src/core/serve/metrics-collector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;CACtD;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAS;;IAMpB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;YAKlB,aAAa;YAwCb,iBAAiB;YA8DjB,kBAAkB;YAwClB,sBAAsB;YAwFtB,yBAAyB;YAmDzB,cAAc;IAoB5B,OAAO,CAAC,iBAAiB;IAgDzB,OAAO,CAAC,UAAU;CAQnB"}
|