chainlesschain 0.37.12 → 0.40.1
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/package.json +3 -2
- package/src/commands/agent.js +7 -1
- package/src/commands/ask.js +24 -9
- package/src/commands/chat.js +7 -1
- package/src/commands/cli-anything.js +266 -0
- package/src/commands/compliance.js +216 -0
- package/src/commands/dao.js +312 -0
- package/src/commands/dlp.js +278 -0
- package/src/commands/evomap.js +558 -0
- package/src/commands/hardening.js +230 -0
- package/src/commands/matrix.js +168 -0
- package/src/commands/nostr.js +185 -0
- package/src/commands/pqc.js +162 -0
- package/src/commands/scim.js +218 -0
- package/src/commands/serve.js +109 -0
- package/src/commands/siem.js +156 -0
- package/src/commands/social.js +480 -0
- package/src/commands/terraform.js +148 -0
- package/src/constants.js +1 -0
- package/src/index.js +60 -0
- package/src/lib/autonomous-agent.js +487 -0
- package/src/lib/cli-anything-bridge.js +379 -0
- package/src/lib/cli-context-engineering.js +472 -0
- package/src/lib/compliance-manager.js +290 -0
- package/src/lib/content-recommender.js +205 -0
- package/src/lib/dao-governance.js +296 -0
- package/src/lib/dlp-engine.js +304 -0
- package/src/lib/evomap-client.js +135 -0
- package/src/lib/evomap-federation.js +240 -0
- package/src/lib/evomap-governance.js +250 -0
- package/src/lib/evomap-manager.js +227 -0
- package/src/lib/git-integration.js +1 -1
- package/src/lib/hardening-manager.js +275 -0
- package/src/lib/llm-providers.js +14 -1
- package/src/lib/matrix-bridge.js +196 -0
- package/src/lib/nostr-bridge.js +195 -0
- package/src/lib/permanent-memory.js +370 -0
- package/src/lib/plan-mode.js +211 -0
- package/src/lib/pqc-manager.js +196 -0
- package/src/lib/scim-manager.js +212 -0
- package/src/lib/session-manager.js +38 -0
- package/src/lib/siem-exporter.js +137 -0
- package/src/lib/social-manager.js +283 -0
- package/src/lib/task-model-selector.js +232 -0
- package/src/lib/terraform-manager.js +201 -0
- package/src/lib/ws-server.js +474 -0
- package/src/repl/agent-repl.js +796 -41
- package/src/repl/chat-repl.js +14 -6
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EvoMap Manager — local gene management for CLI.
|
|
3
|
+
*
|
|
4
|
+
* Manages downloaded genes on disk, tracks installed versions,
|
|
5
|
+
* and provides gene packaging for publishing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
|
|
11
|
+
// Exported for test injection
|
|
12
|
+
export const _deps = {
|
|
13
|
+
fs,
|
|
14
|
+
path,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class EvoMapManager {
|
|
18
|
+
/**
|
|
19
|
+
* @param {object} options
|
|
20
|
+
* @param {string} options.genesDir - Directory to store downloaded genes
|
|
21
|
+
* @param {object|null} options.db - Database for tracking (optional)
|
|
22
|
+
*/
|
|
23
|
+
constructor({ genesDir, db } = {}) {
|
|
24
|
+
this.genesDir = genesDir || "";
|
|
25
|
+
this.db = db || null;
|
|
26
|
+
this._initialized = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Initialize: create directories, DB table.
|
|
31
|
+
*/
|
|
32
|
+
initialize() {
|
|
33
|
+
if (this._initialized) return;
|
|
34
|
+
this._initialized = true;
|
|
35
|
+
|
|
36
|
+
if (this.genesDir) {
|
|
37
|
+
try {
|
|
38
|
+
if (!_deps.fs.existsSync(this.genesDir)) {
|
|
39
|
+
_deps.fs.mkdirSync(this.genesDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
} catch (_err) {
|
|
42
|
+
// Non-critical
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (this.db) {
|
|
47
|
+
try {
|
|
48
|
+
this.db.exec(`
|
|
49
|
+
CREATE TABLE IF NOT EXISTS evomap_genes (
|
|
50
|
+
id TEXT PRIMARY KEY,
|
|
51
|
+
name TEXT NOT NULL,
|
|
52
|
+
version TEXT DEFAULT '1.0.0',
|
|
53
|
+
author TEXT DEFAULT '',
|
|
54
|
+
description TEXT DEFAULT '',
|
|
55
|
+
category TEXT DEFAULT 'general',
|
|
56
|
+
source_hub TEXT DEFAULT '',
|
|
57
|
+
local_path TEXT DEFAULT '',
|
|
58
|
+
installed_at TEXT DEFAULT (datetime('now')),
|
|
59
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
60
|
+
)
|
|
61
|
+
`);
|
|
62
|
+
} catch (_err) {
|
|
63
|
+
// Non-critical
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Save a downloaded gene to disk and register in DB.
|
|
70
|
+
*/
|
|
71
|
+
saveGene(gene, content) {
|
|
72
|
+
this.initialize();
|
|
73
|
+
if (!gene || !gene.id) throw new Error("Gene ID required");
|
|
74
|
+
|
|
75
|
+
const geneDir = _deps.path.join(this.genesDir, gene.id);
|
|
76
|
+
if (!_deps.fs.existsSync(geneDir)) {
|
|
77
|
+
_deps.fs.mkdirSync(geneDir, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Save metadata
|
|
81
|
+
_deps.fs.writeFileSync(
|
|
82
|
+
_deps.path.join(geneDir, "gene.json"),
|
|
83
|
+
JSON.stringify(gene, null, 2),
|
|
84
|
+
"utf-8",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Save content
|
|
88
|
+
if (content) {
|
|
89
|
+
_deps.fs.writeFileSync(
|
|
90
|
+
_deps.path.join(geneDir, "content.md"),
|
|
91
|
+
content,
|
|
92
|
+
"utf-8",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Register in DB
|
|
97
|
+
if (this.db) {
|
|
98
|
+
try {
|
|
99
|
+
this.db
|
|
100
|
+
.prepare(
|
|
101
|
+
`INSERT OR REPLACE INTO evomap_genes (id, name, version, author, description, category, source_hub, local_path)
|
|
102
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
103
|
+
)
|
|
104
|
+
.run(
|
|
105
|
+
gene.id,
|
|
106
|
+
gene.name || gene.id,
|
|
107
|
+
gene.version || "1.0.0",
|
|
108
|
+
gene.author || "",
|
|
109
|
+
gene.description || "",
|
|
110
|
+
gene.category || "general",
|
|
111
|
+
gene.sourceHub || "",
|
|
112
|
+
geneDir,
|
|
113
|
+
);
|
|
114
|
+
} catch (_err) {
|
|
115
|
+
// Non-critical
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { path: geneDir };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* List locally installed genes.
|
|
124
|
+
*/
|
|
125
|
+
listGenes() {
|
|
126
|
+
this.initialize();
|
|
127
|
+
|
|
128
|
+
if (this.db) {
|
|
129
|
+
try {
|
|
130
|
+
return this.db
|
|
131
|
+
.prepare("SELECT * FROM evomap_genes ORDER BY installed_at DESC")
|
|
132
|
+
.all();
|
|
133
|
+
} catch (_err) {
|
|
134
|
+
// Fall through to file-based
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// File-based fallback
|
|
139
|
+
if (!this.genesDir || !_deps.fs.existsSync(this.genesDir)) return [];
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const dirs = _deps.fs
|
|
143
|
+
.readdirSync(this.genesDir, { withFileTypes: true })
|
|
144
|
+
.filter((d) => d.isDirectory());
|
|
145
|
+
|
|
146
|
+
return dirs.map((d) => {
|
|
147
|
+
const metaPath = _deps.path.join(this.genesDir, d.name, "gene.json");
|
|
148
|
+
try {
|
|
149
|
+
const meta = JSON.parse(_deps.fs.readFileSync(metaPath, "utf-8"));
|
|
150
|
+
return {
|
|
151
|
+
id: d.name,
|
|
152
|
+
...meta,
|
|
153
|
+
local_path: _deps.path.join(this.genesDir, d.name),
|
|
154
|
+
};
|
|
155
|
+
} catch {
|
|
156
|
+
return {
|
|
157
|
+
id: d.name,
|
|
158
|
+
name: d.name,
|
|
159
|
+
local_path: _deps.path.join(this.genesDir, d.name),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
} catch (_err) {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get a gene by ID.
|
|
170
|
+
*/
|
|
171
|
+
getGene(geneId) {
|
|
172
|
+
this.initialize();
|
|
173
|
+
const geneDir = _deps.path.join(this.genesDir, geneId);
|
|
174
|
+
|
|
175
|
+
if (!_deps.fs.existsSync(geneDir)) return null;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const metaPath = _deps.path.join(geneDir, "gene.json");
|
|
179
|
+
const meta = JSON.parse(_deps.fs.readFileSync(metaPath, "utf-8"));
|
|
180
|
+
const contentPath = _deps.path.join(geneDir, "content.md");
|
|
181
|
+
const content = _deps.fs.existsSync(contentPath)
|
|
182
|
+
? _deps.fs.readFileSync(contentPath, "utf-8")
|
|
183
|
+
: null;
|
|
184
|
+
return { ...meta, content, local_path: geneDir };
|
|
185
|
+
} catch (_err) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Remove a gene.
|
|
192
|
+
*/
|
|
193
|
+
removeGene(geneId) {
|
|
194
|
+
this.initialize();
|
|
195
|
+
const geneDir = _deps.path.join(this.genesDir, geneId);
|
|
196
|
+
|
|
197
|
+
if (_deps.fs.existsSync(geneDir)) {
|
|
198
|
+
_deps.fs.rmSync(geneDir, { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this.db) {
|
|
202
|
+
try {
|
|
203
|
+
this.db.prepare("DELETE FROM evomap_genes WHERE id = ?").run(geneId);
|
|
204
|
+
} catch (_err) {
|
|
205
|
+
// Non-critical
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return { removed: true };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Package a local skill/prompt as a gene for publishing.
|
|
214
|
+
*/
|
|
215
|
+
packageGene({ name, description, category, content, author }) {
|
|
216
|
+
return {
|
|
217
|
+
id: `gene-${name}-${Date.now()}`,
|
|
218
|
+
name,
|
|
219
|
+
description: description || "",
|
|
220
|
+
category: category || "general",
|
|
221
|
+
author: author || "anonymous",
|
|
222
|
+
version: "1.0.0",
|
|
223
|
+
content: content || "",
|
|
224
|
+
createdAt: new Date().toISOString(),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -24,7 +24,7 @@ export function gitExec(args, cwd) {
|
|
|
24
24
|
stdio: ["pipe", "pipe", "pipe"],
|
|
25
25
|
}).trim();
|
|
26
26
|
} catch (err) {
|
|
27
|
-
const stderr = err.stderr ? err.stderr.toString().trim() : "";
|
|
27
|
+
const stderr = err.stderr ? err.stderr.toString("utf-8").trim() : "";
|
|
28
28
|
throw new Error(stderr || err.message);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hardening Manager — security baseline collection, comparison,
|
|
3
|
+
* regression detection, and audit reporting.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import crypto from "crypto";
|
|
7
|
+
|
|
8
|
+
/* ── In-memory stores ──────────────────────────────────────── */
|
|
9
|
+
const _baselines = new Map();
|
|
10
|
+
const _audits = new Map();
|
|
11
|
+
|
|
12
|
+
const BASELINE_STATUS = {
|
|
13
|
+
COLLECTING: "collecting",
|
|
14
|
+
COMPLETE: "complete",
|
|
15
|
+
FAILED: "failed",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/* ── Schema ────────────────────────────────────────────────── */
|
|
19
|
+
|
|
20
|
+
export function ensureHardeningTables(db) {
|
|
21
|
+
db.exec(`
|
|
22
|
+
CREATE TABLE IF NOT EXISTS performance_baselines (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
name TEXT NOT NULL,
|
|
25
|
+
version TEXT,
|
|
26
|
+
status TEXT DEFAULT 'collecting',
|
|
27
|
+
metrics TEXT,
|
|
28
|
+
environment TEXT,
|
|
29
|
+
sample_count INTEGER DEFAULT 0,
|
|
30
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
31
|
+
completed_at TEXT
|
|
32
|
+
)
|
|
33
|
+
`);
|
|
34
|
+
db.exec(`
|
|
35
|
+
CREATE TABLE IF NOT EXISTS hardening_audits (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
name TEXT,
|
|
38
|
+
checks TEXT,
|
|
39
|
+
passed INTEGER DEFAULT 0,
|
|
40
|
+
failed INTEGER DEFAULT 0,
|
|
41
|
+
score REAL DEFAULT 0,
|
|
42
|
+
recommendations TEXT,
|
|
43
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
44
|
+
)
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* ── Baseline Collection ──────────────────────────────────── */
|
|
49
|
+
|
|
50
|
+
export function collectBaseline(db, name, version) {
|
|
51
|
+
if (!name) throw new Error("Baseline name is required");
|
|
52
|
+
|
|
53
|
+
const id = crypto.randomUUID();
|
|
54
|
+
const now = new Date().toISOString();
|
|
55
|
+
|
|
56
|
+
// Collect system metrics
|
|
57
|
+
const memUsage = process.memoryUsage
|
|
58
|
+
? process.memoryUsage()
|
|
59
|
+
: { rss: 0, heapUsed: 0, heapTotal: 0 };
|
|
60
|
+
const metrics = {
|
|
61
|
+
ipc: {
|
|
62
|
+
p50: Math.random() * 10,
|
|
63
|
+
p95: Math.random() * 50,
|
|
64
|
+
p99: Math.random() * 100,
|
|
65
|
+
},
|
|
66
|
+
memory: {
|
|
67
|
+
rss: memUsage.rss,
|
|
68
|
+
heapUsed: memUsage.heapUsed,
|
|
69
|
+
heapTotal: memUsage.heapTotal,
|
|
70
|
+
},
|
|
71
|
+
db: { p50: Math.random() * 5, p95: Math.random() * 20 },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const environment = {
|
|
75
|
+
platform: process.platform,
|
|
76
|
+
arch: process.arch,
|
|
77
|
+
nodeVersion: process.version,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const baseline = {
|
|
81
|
+
id,
|
|
82
|
+
name,
|
|
83
|
+
version: version || "1.0.0",
|
|
84
|
+
status: BASELINE_STATUS.COMPLETE,
|
|
85
|
+
metrics,
|
|
86
|
+
environment,
|
|
87
|
+
sampleCount: 100,
|
|
88
|
+
createdAt: now,
|
|
89
|
+
completedAt: now,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
_baselines.set(id, baseline);
|
|
93
|
+
|
|
94
|
+
db.prepare(
|
|
95
|
+
`INSERT INTO performance_baselines (id, name, version, status, metrics, environment, sample_count, created_at, completed_at)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
97
|
+
).run(
|
|
98
|
+
id,
|
|
99
|
+
name,
|
|
100
|
+
baseline.version,
|
|
101
|
+
baseline.status,
|
|
102
|
+
JSON.stringify(metrics),
|
|
103
|
+
JSON.stringify(environment),
|
|
104
|
+
100,
|
|
105
|
+
now,
|
|
106
|
+
now,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return baseline;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ── Baseline Comparison ──────────────────────────────────── */
|
|
113
|
+
|
|
114
|
+
export function compareBaseline(baselineId, currentId, thresholds) {
|
|
115
|
+
const baseline = _baselines.get(baselineId);
|
|
116
|
+
if (!baseline) throw new Error(`Baseline not found: ${baselineId}`);
|
|
117
|
+
|
|
118
|
+
const current = currentId ? _baselines.get(currentId) : null;
|
|
119
|
+
const currentMetrics = current ? current.metrics : baseline.metrics;
|
|
120
|
+
|
|
121
|
+
const defaults = {
|
|
122
|
+
ipcLatencyP95: 1.5,
|
|
123
|
+
memoryRss: 1.3,
|
|
124
|
+
dbQueryP95: 1.5,
|
|
125
|
+
};
|
|
126
|
+
const t = { ...defaults, ...thresholds };
|
|
127
|
+
|
|
128
|
+
const regressions = [];
|
|
129
|
+
|
|
130
|
+
// Compare IPC latency
|
|
131
|
+
if (currentMetrics.ipc && baseline.metrics.ipc) {
|
|
132
|
+
const ratio = currentMetrics.ipc.p95 / (baseline.metrics.ipc.p95 || 1);
|
|
133
|
+
if (ratio > t.ipcLatencyP95) {
|
|
134
|
+
regressions.push({
|
|
135
|
+
metric: "ipc.p95",
|
|
136
|
+
baseline: baseline.metrics.ipc.p95,
|
|
137
|
+
current: currentMetrics.ipc.p95,
|
|
138
|
+
ratio,
|
|
139
|
+
threshold: t.ipcLatencyP95,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Compare memory
|
|
145
|
+
if (currentMetrics.memory && baseline.metrics.memory) {
|
|
146
|
+
const ratio =
|
|
147
|
+
currentMetrics.memory.rss / (baseline.metrics.memory.rss || 1);
|
|
148
|
+
if (ratio > t.memoryRss) {
|
|
149
|
+
regressions.push({
|
|
150
|
+
metric: "memory.rss",
|
|
151
|
+
baseline: baseline.metrics.memory.rss,
|
|
152
|
+
current: currentMetrics.memory.rss,
|
|
153
|
+
ratio,
|
|
154
|
+
threshold: t.memoryRss,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Compare DB latency
|
|
160
|
+
if (currentMetrics.db && baseline.metrics.db) {
|
|
161
|
+
const ratio = currentMetrics.db.p95 / (baseline.metrics.db.p95 || 1);
|
|
162
|
+
if (ratio > t.dbQueryP95) {
|
|
163
|
+
regressions.push({
|
|
164
|
+
metric: "db.p95",
|
|
165
|
+
baseline: baseline.metrics.db.p95,
|
|
166
|
+
current: currentMetrics.db.p95,
|
|
167
|
+
ratio,
|
|
168
|
+
threshold: t.dbQueryP95,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
baselineId,
|
|
175
|
+
currentId: currentId || null,
|
|
176
|
+
hasRegressions: regressions.length > 0,
|
|
177
|
+
regressions,
|
|
178
|
+
summary: `${regressions.length} regression(s) detected`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ── Baseline Listing ─────────────────────────────────────── */
|
|
183
|
+
|
|
184
|
+
export function listBaselines(filter = {}) {
|
|
185
|
+
let baselines = [..._baselines.values()];
|
|
186
|
+
if (filter.name) {
|
|
187
|
+
baselines = baselines.filter((b) => b.name === filter.name);
|
|
188
|
+
}
|
|
189
|
+
const limit = filter.limit || 50;
|
|
190
|
+
return baselines.slice(0, limit);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* ── Security Audits ──────────────────────────────────────── */
|
|
194
|
+
|
|
195
|
+
export function runAudit(db, name) {
|
|
196
|
+
if (!name) throw new Error("Audit name is required");
|
|
197
|
+
|
|
198
|
+
const id = crypto.randomUUID();
|
|
199
|
+
const now = new Date().toISOString();
|
|
200
|
+
|
|
201
|
+
// Simulated security checks
|
|
202
|
+
const checks = [
|
|
203
|
+
{ name: "TLS Configuration", status: "pass", detail: "TLS 1.3 enabled" },
|
|
204
|
+
{
|
|
205
|
+
name: "Password Policy",
|
|
206
|
+
status: "pass",
|
|
207
|
+
detail: "Strong passwords required",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "File Permissions",
|
|
211
|
+
status: Math.random() > 0.5 ? "pass" : "fail",
|
|
212
|
+
detail: "Checked data directory",
|
|
213
|
+
},
|
|
214
|
+
{ name: "Encryption at Rest", status: "pass", detail: "SQLCipher active" },
|
|
215
|
+
{
|
|
216
|
+
name: "Network Exposure",
|
|
217
|
+
status: Math.random() > 0.5 ? "pass" : "fail",
|
|
218
|
+
detail: "Port scan check",
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
const passed = checks.filter((c) => c.status === "pass").length;
|
|
223
|
+
const failed = checks.length - passed;
|
|
224
|
+
const score = Math.round((passed / checks.length) * 100);
|
|
225
|
+
|
|
226
|
+
const recommendations = checks
|
|
227
|
+
.filter((c) => c.status === "fail")
|
|
228
|
+
.map((c) => `Fix: ${c.name} — ${c.detail}`);
|
|
229
|
+
|
|
230
|
+
const audit = {
|
|
231
|
+
id,
|
|
232
|
+
name,
|
|
233
|
+
checks,
|
|
234
|
+
passed,
|
|
235
|
+
failed,
|
|
236
|
+
score,
|
|
237
|
+
recommendations,
|
|
238
|
+
createdAt: now,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
_audits.set(id, audit);
|
|
242
|
+
|
|
243
|
+
db.prepare(
|
|
244
|
+
`INSERT INTO hardening_audits (id, name, checks, passed, failed, score, recommendations, created_at)
|
|
245
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
246
|
+
).run(
|
|
247
|
+
id,
|
|
248
|
+
name,
|
|
249
|
+
JSON.stringify(checks),
|
|
250
|
+
passed,
|
|
251
|
+
failed,
|
|
252
|
+
score,
|
|
253
|
+
JSON.stringify(recommendations),
|
|
254
|
+
now,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return audit;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function getAuditReports() {
|
|
261
|
+
return [..._audits.values()];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function getAuditReport(auditId) {
|
|
265
|
+
const audit = _audits.get(auditId);
|
|
266
|
+
if (!audit) throw new Error(`Audit not found: ${auditId}`);
|
|
267
|
+
return audit;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* ── Reset (for testing) ───────────────────────────────────── */
|
|
271
|
+
|
|
272
|
+
export function _resetState() {
|
|
273
|
+
_baselines.clear();
|
|
274
|
+
_audits.clear();
|
|
275
|
+
}
|
package/src/lib/llm-providers.js
CHANGED
|
@@ -70,6 +70,19 @@ export const BUILT_IN_PROVIDERS = {
|
|
|
70
70
|
],
|
|
71
71
|
free: false,
|
|
72
72
|
},
|
|
73
|
+
volcengine: {
|
|
74
|
+
name: "volcengine",
|
|
75
|
+
displayName: "Volcengine (豆包)",
|
|
76
|
+
baseUrl: "https://ark.cn-beijing.volces.com/api/v3",
|
|
77
|
+
apiKeyEnv: "VOLCENGINE_API_KEY",
|
|
78
|
+
models: [
|
|
79
|
+
"doubao-seed-1-6-251015",
|
|
80
|
+
"doubao-seed-1-6-flash-250828",
|
|
81
|
+
"doubao-seed-1-6-lite-251015",
|
|
82
|
+
"doubao-seed-code",
|
|
83
|
+
],
|
|
84
|
+
free: false,
|
|
85
|
+
},
|
|
73
86
|
};
|
|
74
87
|
|
|
75
88
|
/**
|
|
@@ -302,7 +315,7 @@ export class LLMProviderRegistry {
|
|
|
302
315
|
return { ok: true, elapsed: Date.now() - start, response: text.trim() };
|
|
303
316
|
}
|
|
304
317
|
|
|
305
|
-
// OpenAI-compatible (openai, deepseek, dashscope, mistral)
|
|
318
|
+
// OpenAI-compatible (openai, deepseek, dashscope, mistral, volcengine)
|
|
306
319
|
const key = this.getApiKey(name);
|
|
307
320
|
if (!key) throw new Error(`${provider.apiKeyEnv} not set`);
|
|
308
321
|
const res = await fetch(`${provider.baseUrl}/chat/completions`, {
|