chainlesschain 0.47.9 → 0.49.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/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/mcp.js +97 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +112 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol Fusion & AI Social Enhancement — CLI port of Phase 72-73
|
|
3
|
+
* (docs/design/modules/40_协议融合系统.md).
|
|
4
|
+
*
|
|
5
|
+
* Desktop uses live DID/ActivityPub/Nostr/Matrix bridges, real-time
|
|
6
|
+
* AI translation via LLM, and ML-based content quality assessment.
|
|
7
|
+
* CLI port ships:
|
|
8
|
+
*
|
|
9
|
+
* - Unified message CRUD (cross-protocol message routing simulation)
|
|
10
|
+
* - Identity mapping (DID ↔ ActivityPub ↔ Nostr ↔ Matrix)
|
|
11
|
+
* - Translation cache (simulated AI translation with caching)
|
|
12
|
+
* - Content quality scoring (heuristic quality assessment)
|
|
13
|
+
*
|
|
14
|
+
* What does NOT port: real protocol bridges, live LLM translation,
|
|
15
|
+
* ML content classifiers, real-time WebSocket feeds.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import crypto from "crypto";
|
|
19
|
+
|
|
20
|
+
/* ── Constants ─────────────────────────────────────────────── */
|
|
21
|
+
|
|
22
|
+
export const PROTOCOL = Object.freeze({
|
|
23
|
+
DID: "did",
|
|
24
|
+
ACTIVITYPUB: "activitypub",
|
|
25
|
+
NOSTR: "nostr",
|
|
26
|
+
MATRIX: "matrix",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const QUALITY_LEVEL = Object.freeze({
|
|
30
|
+
HIGH: "high",
|
|
31
|
+
MEDIUM: "medium",
|
|
32
|
+
LOW: "low",
|
|
33
|
+
HARMFUL: "harmful",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/* ── State ─────────────────────────────────────────────── */
|
|
37
|
+
|
|
38
|
+
let _messages = new Map();
|
|
39
|
+
let _identities = new Map();
|
|
40
|
+
let _qualityScores = new Map();
|
|
41
|
+
let _translationCache = new Map();
|
|
42
|
+
|
|
43
|
+
function _id() {
|
|
44
|
+
return crypto.randomUUID();
|
|
45
|
+
}
|
|
46
|
+
function _now() {
|
|
47
|
+
return Date.now();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _strip(row) {
|
|
51
|
+
if (!row) return null;
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const [k, v] of Object.entries(row)) {
|
|
54
|
+
if (k !== "_rowid_" && k !== "rowid") out[k] = v;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ── Schema ────────────────────────────────────────────── */
|
|
60
|
+
|
|
61
|
+
export function ensureProtocolFusionTables(db) {
|
|
62
|
+
db.exec(`CREATE TABLE IF NOT EXISTS unified_messages (
|
|
63
|
+
id TEXT PRIMARY KEY,
|
|
64
|
+
source_protocol TEXT NOT NULL,
|
|
65
|
+
target_protocol TEXT,
|
|
66
|
+
sender_id TEXT,
|
|
67
|
+
content TEXT,
|
|
68
|
+
unified_format TEXT,
|
|
69
|
+
converted INTEGER DEFAULT 0,
|
|
70
|
+
routed INTEGER DEFAULT 0,
|
|
71
|
+
created_at INTEGER
|
|
72
|
+
)`);
|
|
73
|
+
|
|
74
|
+
db.exec(`CREATE TABLE IF NOT EXISTS identity_mappings (
|
|
75
|
+
id TEXT PRIMARY KEY,
|
|
76
|
+
did_id TEXT,
|
|
77
|
+
activitypub_id TEXT,
|
|
78
|
+
nostr_pubkey TEXT,
|
|
79
|
+
matrix_id TEXT,
|
|
80
|
+
verified INTEGER DEFAULT 0,
|
|
81
|
+
created_at INTEGER
|
|
82
|
+
)`);
|
|
83
|
+
|
|
84
|
+
db.exec(`CREATE TABLE IF NOT EXISTS content_quality_scores (
|
|
85
|
+
id TEXT PRIMARY KEY,
|
|
86
|
+
content_id TEXT,
|
|
87
|
+
content_hash TEXT,
|
|
88
|
+
quality_level TEXT,
|
|
89
|
+
quality_score REAL,
|
|
90
|
+
harmful_detected INTEGER DEFAULT 0,
|
|
91
|
+
categories TEXT,
|
|
92
|
+
reviewer_count INTEGER DEFAULT 0,
|
|
93
|
+
consensus_reached INTEGER DEFAULT 0,
|
|
94
|
+
created_at INTEGER
|
|
95
|
+
)`);
|
|
96
|
+
|
|
97
|
+
db.exec(`CREATE TABLE IF NOT EXISTS translation_cache (
|
|
98
|
+
id TEXT PRIMARY KEY,
|
|
99
|
+
source_text TEXT,
|
|
100
|
+
source_lang TEXT,
|
|
101
|
+
target_lang TEXT,
|
|
102
|
+
translated_text TEXT,
|
|
103
|
+
model_used TEXT,
|
|
104
|
+
created_at INTEGER
|
|
105
|
+
)`);
|
|
106
|
+
|
|
107
|
+
_loadAll(db);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function _loadAll(db) {
|
|
111
|
+
_messages.clear();
|
|
112
|
+
_identities.clear();
|
|
113
|
+
_qualityScores.clear();
|
|
114
|
+
_translationCache.clear();
|
|
115
|
+
|
|
116
|
+
const tables = [
|
|
117
|
+
["unified_messages", _messages],
|
|
118
|
+
["identity_mappings", _identities],
|
|
119
|
+
["content_quality_scores", _qualityScores],
|
|
120
|
+
["translation_cache", _translationCache],
|
|
121
|
+
];
|
|
122
|
+
for (const [table, map] of tables) {
|
|
123
|
+
try {
|
|
124
|
+
for (const row of db.prepare(`SELECT * FROM ${table}`).all()) {
|
|
125
|
+
const r = _strip(row);
|
|
126
|
+
map.set(r.id, r);
|
|
127
|
+
}
|
|
128
|
+
} catch (_e) {
|
|
129
|
+
/* table may not exist */
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* ── Phase 72: Protocol Fusion Bridge ────────────────────── */
|
|
135
|
+
|
|
136
|
+
const VALID_PROTOCOLS = new Set(Object.values(PROTOCOL));
|
|
137
|
+
|
|
138
|
+
export function sendMessage(
|
|
139
|
+
db,
|
|
140
|
+
{ sourceProtocol, targetProtocol, senderId, content } = {},
|
|
141
|
+
) {
|
|
142
|
+
if (!sourceProtocol || !VALID_PROTOCOLS.has(sourceProtocol))
|
|
143
|
+
return { messageId: null, reason: "invalid_source_protocol" };
|
|
144
|
+
if (targetProtocol && !VALID_PROTOCOLS.has(targetProtocol))
|
|
145
|
+
return { messageId: null, reason: "invalid_target_protocol" };
|
|
146
|
+
if (!content) return { messageId: null, reason: "missing_content" };
|
|
147
|
+
|
|
148
|
+
const id = _id();
|
|
149
|
+
const now = _now();
|
|
150
|
+
|
|
151
|
+
// Simulated format conversion
|
|
152
|
+
const unified = JSON.stringify({
|
|
153
|
+
protocol: sourceProtocol,
|
|
154
|
+
sender: senderId,
|
|
155
|
+
body: content,
|
|
156
|
+
ts: now,
|
|
157
|
+
});
|
|
158
|
+
const converted = targetProtocol && targetProtocol !== sourceProtocol ? 1 : 0;
|
|
159
|
+
const routed = targetProtocol ? 1 : 0;
|
|
160
|
+
|
|
161
|
+
const msg = {
|
|
162
|
+
id,
|
|
163
|
+
source_protocol: sourceProtocol,
|
|
164
|
+
target_protocol: targetProtocol || null,
|
|
165
|
+
sender_id: senderId || null,
|
|
166
|
+
content,
|
|
167
|
+
unified_format: unified,
|
|
168
|
+
converted,
|
|
169
|
+
routed,
|
|
170
|
+
created_at: now,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
db.prepare(
|
|
174
|
+
`INSERT INTO unified_messages (id, source_protocol, target_protocol, sender_id, content, unified_format, converted, routed, created_at)
|
|
175
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
176
|
+
).run(
|
|
177
|
+
id,
|
|
178
|
+
msg.source_protocol,
|
|
179
|
+
msg.target_protocol,
|
|
180
|
+
msg.sender_id,
|
|
181
|
+
content,
|
|
182
|
+
unified,
|
|
183
|
+
converted,
|
|
184
|
+
routed,
|
|
185
|
+
now,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
_messages.set(id, msg);
|
|
189
|
+
return { messageId: id, converted: !!converted, routed: !!routed };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function getMessage(db, id) {
|
|
193
|
+
const m = _messages.get(id);
|
|
194
|
+
return m ? { ...m } : null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function listMessages(db, { protocol, limit = 50 } = {}) {
|
|
198
|
+
let msgs = [..._messages.values()];
|
|
199
|
+
if (protocol)
|
|
200
|
+
msgs = msgs.filter(
|
|
201
|
+
(m) => m.source_protocol === protocol || m.target_protocol === protocol,
|
|
202
|
+
);
|
|
203
|
+
return msgs
|
|
204
|
+
.sort((a, b) => b.created_at - a.created_at)
|
|
205
|
+
.slice(0, limit)
|
|
206
|
+
.map((m) => ({ ...m }));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* ── Identity Mapping ────────────────────────────────────── */
|
|
210
|
+
|
|
211
|
+
export function mapIdentity(
|
|
212
|
+
db,
|
|
213
|
+
{ didId, activitypubId, nostrPubkey, matrixId } = {},
|
|
214
|
+
) {
|
|
215
|
+
if (!didId && !activitypubId && !nostrPubkey && !matrixId)
|
|
216
|
+
return { mappingId: null, reason: "at_least_one_identity_required" };
|
|
217
|
+
|
|
218
|
+
const id = _id();
|
|
219
|
+
const now = _now();
|
|
220
|
+
|
|
221
|
+
const mapping = {
|
|
222
|
+
id,
|
|
223
|
+
did_id: didId || null,
|
|
224
|
+
activitypub_id: activitypubId || null,
|
|
225
|
+
nostr_pubkey: nostrPubkey || null,
|
|
226
|
+
matrix_id: matrixId || null,
|
|
227
|
+
verified: 0,
|
|
228
|
+
created_at: now,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
db.prepare(
|
|
232
|
+
`INSERT INTO identity_mappings (id, did_id, activitypub_id, nostr_pubkey, matrix_id, verified, created_at)
|
|
233
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
234
|
+
).run(
|
|
235
|
+
id,
|
|
236
|
+
mapping.did_id,
|
|
237
|
+
mapping.activitypub_id,
|
|
238
|
+
mapping.nostr_pubkey,
|
|
239
|
+
mapping.matrix_id,
|
|
240
|
+
0,
|
|
241
|
+
now,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
_identities.set(id, mapping);
|
|
245
|
+
return { mappingId: id };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function getIdentityMap(db, didId) {
|
|
249
|
+
for (const m of _identities.values()) {
|
|
250
|
+
if (m.did_id === didId) return { ...m };
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function getIdentityById(db, id) {
|
|
256
|
+
const m = _identities.get(id);
|
|
257
|
+
return m ? { ...m } : null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function listIdentities(db, { limit = 50 } = {}) {
|
|
261
|
+
return [..._identities.values()]
|
|
262
|
+
.sort((a, b) => b.created_at - a.created_at)
|
|
263
|
+
.slice(0, limit)
|
|
264
|
+
.map((m) => ({ ...m }));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function verifyIdentity(db, id) {
|
|
268
|
+
const m = _identities.get(id);
|
|
269
|
+
if (!m) return { verified: false, reason: "not_found" };
|
|
270
|
+
m.verified = 1;
|
|
271
|
+
db.prepare("UPDATE identity_mappings SET verified = 1 WHERE id = ?").run(id);
|
|
272
|
+
return { verified: true };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* ── Phase 73: Content Quality Assessment ────────────────── */
|
|
276
|
+
|
|
277
|
+
const HARMFUL_KEYWORDS = [
|
|
278
|
+
"spam",
|
|
279
|
+
"scam",
|
|
280
|
+
"phishing",
|
|
281
|
+
"malware",
|
|
282
|
+
"exploit",
|
|
283
|
+
"hack",
|
|
284
|
+
"injection",
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
export function assessQuality(db, contentId, content) {
|
|
288
|
+
if (!content) return { scoreId: null, reason: "missing_content" };
|
|
289
|
+
|
|
290
|
+
const id = _id();
|
|
291
|
+
const now = _now();
|
|
292
|
+
const hash = crypto.createHash("sha256").update(content).digest("hex");
|
|
293
|
+
|
|
294
|
+
// Heuristic quality scoring
|
|
295
|
+
const len = content.length;
|
|
296
|
+
let score = 0.5;
|
|
297
|
+
|
|
298
|
+
// Length factor
|
|
299
|
+
if (len > 200) score += 0.2;
|
|
300
|
+
else if (len > 50) score += 0.1;
|
|
301
|
+
else if (len < 10) score -= 0.2;
|
|
302
|
+
|
|
303
|
+
// Diversity factor (unique chars ratio)
|
|
304
|
+
const uniqueChars = new Set(content.toLowerCase()).size;
|
|
305
|
+
const diversity = uniqueChars / Math.min(len, 100);
|
|
306
|
+
score += diversity * 0.2;
|
|
307
|
+
|
|
308
|
+
// Harmful content detection
|
|
309
|
+
const lower = content.toLowerCase();
|
|
310
|
+
const harmful = HARMFUL_KEYWORDS.some((kw) => lower.includes(kw));
|
|
311
|
+
if (harmful) score = Math.min(score, 0.2);
|
|
312
|
+
|
|
313
|
+
score = Math.max(0, Math.min(1, score));
|
|
314
|
+
|
|
315
|
+
let level;
|
|
316
|
+
if (harmful) level = "harmful";
|
|
317
|
+
else if (score >= 0.7) level = "high";
|
|
318
|
+
else if (score >= 0.4) level = "medium";
|
|
319
|
+
else level = "low";
|
|
320
|
+
|
|
321
|
+
const categories = harmful ? "harmful_content" : "general";
|
|
322
|
+
|
|
323
|
+
const entry = {
|
|
324
|
+
id,
|
|
325
|
+
content_id: contentId || hash.slice(0, 16),
|
|
326
|
+
content_hash: hash,
|
|
327
|
+
quality_level: level,
|
|
328
|
+
quality_score: Math.round(score * 100) / 100,
|
|
329
|
+
harmful_detected: harmful ? 1 : 0,
|
|
330
|
+
categories,
|
|
331
|
+
reviewer_count: 1,
|
|
332
|
+
consensus_reached: 1,
|
|
333
|
+
created_at: now,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
db.prepare(
|
|
337
|
+
`INSERT INTO content_quality_scores (id, content_id, content_hash, quality_level, quality_score, harmful_detected, categories, reviewer_count, consensus_reached, created_at)
|
|
338
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
339
|
+
).run(
|
|
340
|
+
id,
|
|
341
|
+
entry.content_id,
|
|
342
|
+
entry.content_hash,
|
|
343
|
+
entry.quality_level,
|
|
344
|
+
entry.quality_score,
|
|
345
|
+
entry.harmful_detected,
|
|
346
|
+
entry.categories,
|
|
347
|
+
entry.reviewer_count,
|
|
348
|
+
entry.consensus_reached,
|
|
349
|
+
now,
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
_qualityScores.set(id, entry);
|
|
353
|
+
return {
|
|
354
|
+
scoreId: id,
|
|
355
|
+
level,
|
|
356
|
+
score: entry.quality_score,
|
|
357
|
+
harmful,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function getQualityScore(db, id) {
|
|
362
|
+
const s = _qualityScores.get(id);
|
|
363
|
+
return s ? { ...s } : null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function listQualityScores(db, { level, limit = 50 } = {}) {
|
|
367
|
+
let scores = [..._qualityScores.values()];
|
|
368
|
+
if (level) scores = scores.filter((s) => s.quality_level === level);
|
|
369
|
+
return scores
|
|
370
|
+
.sort((a, b) => b.created_at - a.created_at)
|
|
371
|
+
.slice(0, limit)
|
|
372
|
+
.map((s) => ({ ...s }));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function getQualityReport(db) {
|
|
376
|
+
const scores = [..._qualityScores.values()];
|
|
377
|
+
const total = scores.length;
|
|
378
|
+
const byLevel = {};
|
|
379
|
+
for (const l of Object.values(QUALITY_LEVEL)) {
|
|
380
|
+
byLevel[l] = scores.filter((s) => s.quality_level === l).length;
|
|
381
|
+
}
|
|
382
|
+
const avgScore =
|
|
383
|
+
total > 0
|
|
384
|
+
? Math.round(
|
|
385
|
+
(scores.reduce((s, e) => s + e.quality_score, 0) / total) * 100,
|
|
386
|
+
) / 100
|
|
387
|
+
: 0;
|
|
388
|
+
const harmful = scores.filter((s) => s.harmful_detected).length;
|
|
389
|
+
|
|
390
|
+
return { total, byLevel, avgScore, harmful };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* ── Phase 73: Translation ───────────────────────────────── */
|
|
394
|
+
|
|
395
|
+
const SUPPORTED_LANGS = [
|
|
396
|
+
"en",
|
|
397
|
+
"zh",
|
|
398
|
+
"ja",
|
|
399
|
+
"ko",
|
|
400
|
+
"es",
|
|
401
|
+
"fr",
|
|
402
|
+
"de",
|
|
403
|
+
"ru",
|
|
404
|
+
"ar",
|
|
405
|
+
"pt",
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
export function translateMessage(db, { text, sourceLang, targetLang } = {}) {
|
|
409
|
+
if (!text) return { translatedText: null, reason: "missing_text" };
|
|
410
|
+
if (!targetLang)
|
|
411
|
+
return { translatedText: null, reason: "missing_target_lang" };
|
|
412
|
+
|
|
413
|
+
const src = sourceLang || "auto";
|
|
414
|
+
|
|
415
|
+
// Check cache
|
|
416
|
+
for (const c of _translationCache.values()) {
|
|
417
|
+
if (
|
|
418
|
+
c.source_text === text &&
|
|
419
|
+
c.source_lang === src &&
|
|
420
|
+
c.target_lang === targetLang
|
|
421
|
+
) {
|
|
422
|
+
return { translatedText: c.translated_text, cached: true };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Simulated translation (prefix with target lang tag)
|
|
427
|
+
const translated = `[${targetLang}] ${text}`;
|
|
428
|
+
|
|
429
|
+
const id = _id();
|
|
430
|
+
const now = _now();
|
|
431
|
+
const entry = {
|
|
432
|
+
id,
|
|
433
|
+
source_text: text,
|
|
434
|
+
source_lang: src,
|
|
435
|
+
target_lang: targetLang,
|
|
436
|
+
translated_text: translated,
|
|
437
|
+
model_used: "simulated",
|
|
438
|
+
created_at: now,
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
db.prepare(
|
|
442
|
+
`INSERT INTO translation_cache (id, source_text, source_lang, target_lang, translated_text, model_used, created_at)
|
|
443
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
444
|
+
).run(id, text, src, targetLang, translated, "simulated", now);
|
|
445
|
+
|
|
446
|
+
_translationCache.set(id, entry);
|
|
447
|
+
return { translatedText: translated, cached: false };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export function detectLanguage(text) {
|
|
451
|
+
if (!text) return { lang: null, reason: "missing_text" };
|
|
452
|
+
|
|
453
|
+
// Simple heuristic language detection
|
|
454
|
+
const cjkPattern = /[\u4e00-\u9fff]/;
|
|
455
|
+
const jpPattern = /[\u3040-\u309f\u30a0-\u30ff]/;
|
|
456
|
+
const krPattern = /[\uac00-\ud7af]/;
|
|
457
|
+
const arPattern = /[\u0600-\u06ff]/;
|
|
458
|
+
const cyPattern = /[\u0400-\u04ff]/;
|
|
459
|
+
|
|
460
|
+
let lang = "en";
|
|
461
|
+
let confidence = 0.6;
|
|
462
|
+
|
|
463
|
+
if (jpPattern.test(text)) {
|
|
464
|
+
lang = "ja";
|
|
465
|
+
confidence = 0.85;
|
|
466
|
+
} else if (cjkPattern.test(text)) {
|
|
467
|
+
lang = "zh";
|
|
468
|
+
confidence = 0.85;
|
|
469
|
+
} else if (krPattern.test(text)) {
|
|
470
|
+
lang = "ko";
|
|
471
|
+
confidence = 0.85;
|
|
472
|
+
} else if (arPattern.test(text)) {
|
|
473
|
+
lang = "ar";
|
|
474
|
+
confidence = 0.85;
|
|
475
|
+
} else if (cyPattern.test(text)) {
|
|
476
|
+
lang = "ru";
|
|
477
|
+
confidence = 0.8;
|
|
478
|
+
} else {
|
|
479
|
+
// Default to English for Latin script
|
|
480
|
+
lang = "en";
|
|
481
|
+
confidence = 0.7;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return { lang, confidence };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function getTranslationStats(db) {
|
|
488
|
+
const cache = [..._translationCache.values()];
|
|
489
|
+
const langs = new Set();
|
|
490
|
+
for (const c of cache) {
|
|
491
|
+
langs.add(c.source_lang);
|
|
492
|
+
langs.add(c.target_lang);
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
total: cache.length,
|
|
496
|
+
languages: [...langs].sort(),
|
|
497
|
+
cacheSize: cache.length,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* ── Stats ─────────────────────────────────────────────── */
|
|
502
|
+
|
|
503
|
+
export function getProtocolFusionStats(db) {
|
|
504
|
+
const msgs = [..._messages.values()];
|
|
505
|
+
const ids = [..._identities.values()];
|
|
506
|
+
const scores = [..._qualityScores.values()];
|
|
507
|
+
const cache = [..._translationCache.values()];
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
messages: {
|
|
511
|
+
total: msgs.length,
|
|
512
|
+
converted: msgs.filter((m) => m.converted).length,
|
|
513
|
+
routed: msgs.filter((m) => m.routed).length,
|
|
514
|
+
byProtocol: msgs.reduce((acc, m) => {
|
|
515
|
+
acc[m.source_protocol] = (acc[m.source_protocol] || 0) + 1;
|
|
516
|
+
return acc;
|
|
517
|
+
}, {}),
|
|
518
|
+
},
|
|
519
|
+
identities: {
|
|
520
|
+
total: ids.length,
|
|
521
|
+
verified: ids.filter((i) => i.verified).length,
|
|
522
|
+
},
|
|
523
|
+
quality: getQualityReport(db),
|
|
524
|
+
translations: getTranslationStats(db),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* ── Reset (tests) ─────────────────────────────────────── */
|
|
529
|
+
|
|
530
|
+
export function _resetState() {
|
|
531
|
+
_messages.clear();
|
|
532
|
+
_identities.clear();
|
|
533
|
+
_qualityScores.clear();
|
|
534
|
+
_translationCache.clear();
|
|
535
|
+
}
|