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.
Files changed (70) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
  5. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
  6. package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
  7. package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
  8. package/src/assets/web-panel/index.html +2 -2
  9. package/src/commands/codegen.js +303 -0
  10. package/src/commands/collab.js +482 -0
  11. package/src/commands/crosschain.js +382 -0
  12. package/src/commands/dbevo.js +388 -0
  13. package/src/commands/dev.js +411 -0
  14. package/src/commands/federation.js +427 -0
  15. package/src/commands/fusion.js +332 -0
  16. package/src/commands/governance.js +505 -0
  17. package/src/commands/hardening.js +110 -0
  18. package/src/commands/incentive.js +373 -0
  19. package/src/commands/inference.js +304 -0
  20. package/src/commands/infra.js +361 -0
  21. package/src/commands/kg.js +371 -0
  22. package/src/commands/marketplace.js +326 -0
  23. package/src/commands/mcp.js +97 -18
  24. package/src/commands/nlprog.js +329 -0
  25. package/src/commands/ops.js +408 -0
  26. package/src/commands/perception.js +385 -0
  27. package/src/commands/pqc.js +34 -0
  28. package/src/commands/privacy.js +345 -0
  29. package/src/commands/quantization.js +280 -0
  30. package/src/commands/recommend.js +336 -0
  31. package/src/commands/reputation.js +349 -0
  32. package/src/commands/runtime.js +500 -0
  33. package/src/commands/sla.js +352 -0
  34. package/src/commands/stress.js +252 -0
  35. package/src/commands/tech.js +268 -0
  36. package/src/commands/tenant.js +576 -0
  37. package/src/commands/trust.js +366 -0
  38. package/src/harness/mcp-client.js +330 -54
  39. package/src/index.js +112 -0
  40. package/src/lib/aiops.js +523 -0
  41. package/src/lib/autonomous-developer.js +524 -0
  42. package/src/lib/code-agent.js +442 -0
  43. package/src/lib/collaboration-governance.js +556 -0
  44. package/src/lib/community-governance.js +649 -0
  45. package/src/lib/content-recommendation.js +600 -0
  46. package/src/lib/cross-chain.js +669 -0
  47. package/src/lib/dbevo.js +669 -0
  48. package/src/lib/decentral-infra.js +445 -0
  49. package/src/lib/federation-hardening.js +587 -0
  50. package/src/lib/hardening-manager.js +409 -0
  51. package/src/lib/inference-network.js +407 -0
  52. package/src/lib/knowledge-graph.js +530 -0
  53. package/src/lib/mcp-client.js +3 -0
  54. package/src/lib/multimodal.js +698 -0
  55. package/src/lib/nl-programming.js +595 -0
  56. package/src/lib/perception.js +500 -0
  57. package/src/lib/pqc-manager.js +141 -9
  58. package/src/lib/privacy-computing.js +575 -0
  59. package/src/lib/protocol-fusion.js +535 -0
  60. package/src/lib/quantization.js +362 -0
  61. package/src/lib/reputation-optimizer.js +509 -0
  62. package/src/lib/skill-marketplace.js +397 -0
  63. package/src/lib/sla-manager.js +484 -0
  64. package/src/lib/stress-tester.js +383 -0
  65. package/src/lib/tech-learning-engine.js +651 -0
  66. package/src/lib/tenant-saas.js +831 -0
  67. package/src/lib/token-incentive.js +513 -0
  68. package/src/lib/trust-security.js +473 -0
  69. package/src/lib/universal-runtime.js +771 -0
  70. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
@@ -0,0 +1,530 @@
1
+ /**
2
+ * Knowledge Graph — CLI port of Phase 94 企业知识图谱
3
+ * (docs/design/modules/59_企业知识图谱.md).
4
+ *
5
+ * The Desktop build drives knowledge graph with GraphRAG fusion (entity
6
+ * embeddings + vector retrieval + LLM augmentation) and force-directed
7
+ * visualization. The CLI can't host Qdrant / force-directed layout, so this
8
+ * port ships the tractable scaffolding:
9
+ *
10
+ * - EntityStore: add/list/show/query with type + name-substring filters.
11
+ * - RelationStore: add/list/filter with source/target/type/weight.
12
+ * - Graph traversal: BFS multi-hop reasoning with depth limit and path
13
+ * reconstruction (no vector rerank).
14
+ * - Stats: entity/relationship counts, type distribution, avg degree.
15
+ * - Import/export: JSON serialization.
16
+ * - Catalogs: ENTITY_TYPES (7 standard types).
17
+ *
18
+ * GraphRAG fusion search, vector embeddings, force-directed visualization
19
+ * layout, and real-time graph editing UI are Desktop-only.
20
+ */
21
+
22
+ import crypto from "crypto";
23
+
24
+ /* ── Constants ─────────────────────────────────────────────── */
25
+
26
+ // 7 standard entity types — match Desktop defaults (37_技能市场系统 §2.2).
27
+ // `type` field on entities is a free-form string; these are the recommended
28
+ // classifiers. CLI does NOT enforce — a user can create entities with
29
+ // custom types for their domain.
30
+ export const ENTITY_TYPES = Object.freeze({
31
+ PERSON: { name: "Person", description: "人员(员工、客户、合作伙伴)" },
32
+ ORGANIZATION: {
33
+ name: "Organization",
34
+ description: "组织(部门、公司、团队)",
35
+ },
36
+ PROJECT: { name: "Project", description: "项目(产品、工程、研究课题)" },
37
+ TECHNOLOGY: {
38
+ name: "Technology",
39
+ description: "技术(编程语言、框架、工具)",
40
+ },
41
+ DOCUMENT: { name: "Document", description: "文档(设计稿、报告、规范)" },
42
+ CONCEPT: { name: "Concept", description: "概念(领域知识、术语、理论)" },
43
+ EVENT: { name: "Event", description: "事件(会议、里程碑、发布)" },
44
+ });
45
+
46
+ /* ── State ─────────────────────────────────────────────────── */
47
+
48
+ const _entities = new Map(); // id → entity
49
+ const _relations = new Map(); // id → relation
50
+ // Adjacency indexes for fast traversal (rebuilt on add/remove)
51
+ const _outEdges = new Map(); // sourceId → Set<relationId>
52
+ const _inEdges = new Map(); // targetId → Set<relationId>
53
+ let _seq = 0;
54
+
55
+ /* ── Schema ────────────────────────────────────────────────── */
56
+
57
+ export function ensureKnowledgeGraphTables(db) {
58
+ if (!db) return;
59
+ db.exec(`
60
+ CREATE TABLE IF NOT EXISTS kg_entities (
61
+ id TEXT PRIMARY KEY,
62
+ name TEXT NOT NULL,
63
+ type TEXT NOT NULL,
64
+ properties TEXT,
65
+ tags TEXT,
66
+ created_at INTEGER NOT NULL,
67
+ updated_at INTEGER NOT NULL
68
+ )
69
+ `);
70
+ db.exec(`
71
+ CREATE TABLE IF NOT EXISTS kg_relationships (
72
+ id TEXT PRIMARY KEY,
73
+ source_id TEXT NOT NULL,
74
+ target_id TEXT NOT NULL,
75
+ relation_type TEXT NOT NULL,
76
+ weight REAL DEFAULT 1.0,
77
+ properties TEXT,
78
+ created_at INTEGER NOT NULL
79
+ )
80
+ `);
81
+ db.exec(
82
+ `CREATE INDEX IF NOT EXISTS idx_kg_entities_type ON kg_entities(type)`,
83
+ );
84
+ db.exec(
85
+ `CREATE INDEX IF NOT EXISTS idx_kg_entities_name ON kg_entities(name)`,
86
+ );
87
+ db.exec(
88
+ `CREATE INDEX IF NOT EXISTS idx_kg_rel_source ON kg_relationships(source_id)`,
89
+ );
90
+ db.exec(
91
+ `CREATE INDEX IF NOT EXISTS idx_kg_rel_target ON kg_relationships(target_id)`,
92
+ );
93
+ db.exec(
94
+ `CREATE INDEX IF NOT EXISTS idx_kg_rel_type ON kg_relationships(relation_type)`,
95
+ );
96
+ }
97
+
98
+ /* ── Catalogs ──────────────────────────────────────────────── */
99
+
100
+ export function listEntityTypes() {
101
+ return Object.values(ENTITY_TYPES).map((t) => ({ ...t }));
102
+ }
103
+
104
+ function _strip(row) {
105
+ const { _seq: _omit, ...rest } = row;
106
+ void _omit;
107
+ return rest;
108
+ }
109
+
110
+ /* ── Entities ──────────────────────────────────────────────── */
111
+
112
+ function _persistEntity(db, entity) {
113
+ if (!db) return;
114
+ db.prepare(
115
+ `INSERT OR REPLACE INTO kg_entities
116
+ (id, name, type, properties, tags, created_at, updated_at)
117
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
118
+ ).run(
119
+ entity.id,
120
+ entity.name,
121
+ entity.type,
122
+ entity.properties ? JSON.stringify(entity.properties) : null,
123
+ entity.tags ? JSON.stringify(entity.tags) : null,
124
+ entity.createdAt,
125
+ entity.updatedAt,
126
+ );
127
+ }
128
+
129
+ export function addEntity(db, config = {}) {
130
+ const name = String(config.name || "").trim();
131
+ if (!name) throw new Error("entity name is required");
132
+
133
+ const type = String(config.type || "").trim();
134
+ if (!type) throw new Error("entity type is required");
135
+
136
+ const now = Number(config.now ?? Date.now());
137
+ const id = config.id || crypto.randomUUID();
138
+
139
+ if (_entities.has(id)) {
140
+ throw new Error(`Entity already exists: ${id}`);
141
+ }
142
+
143
+ const entity = {
144
+ id,
145
+ name,
146
+ type,
147
+ properties: config.properties || null,
148
+ tags: Array.isArray(config.tags) ? [...config.tags] : null,
149
+ createdAt: now,
150
+ updatedAt: now,
151
+ _seq: ++_seq,
152
+ };
153
+ _entities.set(id, entity);
154
+ _persistEntity(db, entity);
155
+ return _strip(entity);
156
+ }
157
+
158
+ export function getEntity(id) {
159
+ const e = _entities.get(String(id || ""));
160
+ return e ? _strip(e) : null;
161
+ }
162
+
163
+ export function listEntities(opts = {}) {
164
+ let rows = [..._entities.values()];
165
+ if (opts.type) {
166
+ const t = String(opts.type);
167
+ rows = rows.filter((e) => e.type === t);
168
+ }
169
+ if (opts.name) {
170
+ const q = String(opts.name).toLowerCase();
171
+ rows = rows.filter((e) => e.name.toLowerCase().includes(q));
172
+ }
173
+ if (opts.tag) {
174
+ const tag = String(opts.tag);
175
+ rows = rows.filter((e) => Array.isArray(e.tags) && e.tags.includes(tag));
176
+ }
177
+ rows.sort((a, b) => b.createdAt - a.createdAt || b._seq - a._seq);
178
+ const limit = opts.limit || 50;
179
+ return rows.slice(0, limit).map(_strip);
180
+ }
181
+
182
+ export function removeEntity(db, id) {
183
+ const entity = _entities.get(id);
184
+ if (!entity) return false;
185
+
186
+ // Cascade: remove all relations touching this entity
187
+ const touching = [];
188
+ for (const r of _relations.values()) {
189
+ if (r.sourceId === id || r.targetId === id) touching.push(r.id);
190
+ }
191
+ for (const rid of touching) removeRelation(db, rid);
192
+
193
+ _entities.delete(id);
194
+ if (db) {
195
+ db.prepare(`DELETE FROM kg_entities WHERE id = ?`).run(id);
196
+ }
197
+ return true;
198
+ }
199
+
200
+ /* ── Relations ─────────────────────────────────────────────── */
201
+
202
+ function _persistRelation(db, relation) {
203
+ if (!db) return;
204
+ db.prepare(
205
+ `INSERT OR REPLACE INTO kg_relationships
206
+ (id, source_id, target_id, relation_type, weight, properties, created_at)
207
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
208
+ ).run(
209
+ relation.id,
210
+ relation.sourceId,
211
+ relation.targetId,
212
+ relation.relationType,
213
+ relation.weight,
214
+ relation.properties ? JSON.stringify(relation.properties) : null,
215
+ relation.createdAt,
216
+ );
217
+ }
218
+
219
+ function _indexRelation(relation) {
220
+ let outSet = _outEdges.get(relation.sourceId);
221
+ if (!outSet) {
222
+ outSet = new Set();
223
+ _outEdges.set(relation.sourceId, outSet);
224
+ }
225
+ outSet.add(relation.id);
226
+ let inSet = _inEdges.get(relation.targetId);
227
+ if (!inSet) {
228
+ inSet = new Set();
229
+ _inEdges.set(relation.targetId, inSet);
230
+ }
231
+ inSet.add(relation.id);
232
+ }
233
+
234
+ function _unindexRelation(relation) {
235
+ const outSet = _outEdges.get(relation.sourceId);
236
+ if (outSet) {
237
+ outSet.delete(relation.id);
238
+ if (outSet.size === 0) _outEdges.delete(relation.sourceId);
239
+ }
240
+ const inSet = _inEdges.get(relation.targetId);
241
+ if (inSet) {
242
+ inSet.delete(relation.id);
243
+ if (inSet.size === 0) _inEdges.delete(relation.targetId);
244
+ }
245
+ }
246
+
247
+ export function addRelation(db, config = {}) {
248
+ const sourceId = String(config.sourceId || "").trim();
249
+ const targetId = String(config.targetId || "").trim();
250
+ const relationType = String(config.relationType || "").trim();
251
+
252
+ if (!sourceId) throw new Error("sourceId is required");
253
+ if (!targetId) throw new Error("targetId is required");
254
+ if (!relationType) throw new Error("relationType is required");
255
+ if (sourceId === targetId) {
256
+ throw new Error("Cannot create self-referencing relation");
257
+ }
258
+
259
+ if (!_entities.has(sourceId)) {
260
+ throw new Error(`Source entity not found: ${sourceId}`);
261
+ }
262
+ if (!_entities.has(targetId)) {
263
+ throw new Error(`Target entity not found: ${targetId}`);
264
+ }
265
+
266
+ const weight = Number(config.weight ?? 1.0);
267
+ if (!Number.isFinite(weight) || weight < 0) {
268
+ throw new Error(`Invalid weight: ${config.weight} (must be >= 0)`);
269
+ }
270
+
271
+ const now = Number(config.now ?? Date.now());
272
+ const id = config.id || crypto.randomUUID();
273
+
274
+ const relation = {
275
+ id,
276
+ sourceId,
277
+ targetId,
278
+ relationType,
279
+ weight,
280
+ properties: config.properties || null,
281
+ createdAt: now,
282
+ _seq: ++_seq,
283
+ };
284
+ _relations.set(id, relation);
285
+ _indexRelation(relation);
286
+ _persistRelation(db, relation);
287
+ return _strip(relation);
288
+ }
289
+
290
+ export function getRelation(id) {
291
+ const r = _relations.get(String(id || ""));
292
+ return r ? _strip(r) : null;
293
+ }
294
+
295
+ export function listRelations(opts = {}) {
296
+ let rows = [..._relations.values()];
297
+ if (opts.sourceId) {
298
+ const s = String(opts.sourceId);
299
+ rows = rows.filter((r) => r.sourceId === s);
300
+ }
301
+ if (opts.targetId) {
302
+ const t = String(opts.targetId);
303
+ rows = rows.filter((r) => r.targetId === t);
304
+ }
305
+ if (opts.relationType) {
306
+ const t = String(opts.relationType);
307
+ rows = rows.filter((r) => r.relationType === t);
308
+ }
309
+ rows.sort((a, b) => b.createdAt - a.createdAt || b._seq - a._seq);
310
+ const limit = opts.limit || 50;
311
+ return rows.slice(0, limit).map(_strip);
312
+ }
313
+
314
+ export function removeRelation(db, id) {
315
+ const relation = _relations.get(id);
316
+ if (!relation) return false;
317
+ _unindexRelation(relation);
318
+ _relations.delete(id);
319
+ if (db) {
320
+ db.prepare(`DELETE FROM kg_relationships WHERE id = ?`).run(id);
321
+ }
322
+ return true;
323
+ }
324
+
325
+ /* ── Traversal / Reasoning ─────────────────────────────────── */
326
+
327
+ /**
328
+ * BFS multi-hop traversal from a start entity.
329
+ * Returns entities reachable within `maxDepth` hops, along with the path
330
+ * (array of relation ids) that reaches each.
331
+ *
332
+ * opts:
333
+ * - maxDepth: hop limit (default 3)
334
+ * - direction: "out" | "in" | "both" (default "out")
335
+ * - relationType: filter edges by relation_type
336
+ * - includeStart: include start entity in result (default false)
337
+ */
338
+ export function reason(startEntityId, opts = {}) {
339
+ const start = String(startEntityId || "").trim();
340
+ if (!_entities.has(start)) {
341
+ throw new Error(`Start entity not found: ${start}`);
342
+ }
343
+
344
+ const maxDepth = Number.isFinite(opts.maxDepth) ? opts.maxDepth : 3;
345
+ if (maxDepth < 0) throw new Error(`Invalid maxDepth: ${opts.maxDepth}`);
346
+
347
+ const direction = opts.direction || "out";
348
+ if (!["out", "in", "both"].includes(direction)) {
349
+ throw new Error(`Invalid direction: ${opts.direction}`);
350
+ }
351
+
352
+ const relTypeFilter = opts.relationType || null;
353
+
354
+ const visited = new Map(); // entityId → { depth, pathRelationIds[] }
355
+ visited.set(start, { depth: 0, path: [] });
356
+
357
+ const queue = [start];
358
+ while (queue.length > 0) {
359
+ const currentId = queue.shift();
360
+ const currentNode = visited.get(currentId);
361
+ if (currentNode.depth >= maxDepth) continue;
362
+
363
+ const edgeIds = new Set();
364
+ if (direction === "out" || direction === "both") {
365
+ const out = _outEdges.get(currentId);
366
+ if (out) for (const id of out) edgeIds.add(id);
367
+ }
368
+ if (direction === "in" || direction === "both") {
369
+ const inn = _inEdges.get(currentId);
370
+ if (inn) for (const id of inn) edgeIds.add(id);
371
+ }
372
+
373
+ for (const relId of edgeIds) {
374
+ const rel = _relations.get(relId);
375
+ if (!rel) continue;
376
+ if (relTypeFilter && rel.relationType !== relTypeFilter) continue;
377
+
378
+ const nextId = rel.sourceId === currentId ? rel.targetId : rel.sourceId;
379
+ if (visited.has(nextId)) continue;
380
+
381
+ visited.set(nextId, {
382
+ depth: currentNode.depth + 1,
383
+ path: [...currentNode.path, relId],
384
+ });
385
+ queue.push(nextId);
386
+ }
387
+ }
388
+
389
+ const results = [];
390
+ for (const [entityId, node] of visited.entries()) {
391
+ if (!opts.includeStart && entityId === start) continue;
392
+ const entity = _entities.get(entityId);
393
+ results.push({
394
+ entity: _strip(entity),
395
+ depth: node.depth,
396
+ path: [...node.path],
397
+ });
398
+ }
399
+ results.sort(
400
+ (a, b) => a.depth - b.depth || a.entity.createdAt - b.entity.createdAt,
401
+ );
402
+ return results;
403
+ }
404
+
405
+ /**
406
+ * Simple name/type match query — returns entities that pass all active
407
+ * filters. Lightweight alternative to `listEntities` for programmatic
408
+ * callers (callers don't need to care about default limits).
409
+ */
410
+ export function query(opts = {}) {
411
+ return listEntities({ ...opts, limit: opts.limit || 100 });
412
+ }
413
+
414
+ /* ── Stats ─────────────────────────────────────────────────── */
415
+
416
+ export function getStats() {
417
+ const entityCount = _entities.size;
418
+ const relationCount = _relations.size;
419
+
420
+ const typeDistribution = {};
421
+ for (const e of _entities.values()) {
422
+ typeDistribution[e.type] = (typeDistribution[e.type] || 0) + 1;
423
+ }
424
+
425
+ const relationTypeDistribution = {};
426
+ for (const r of _relations.values()) {
427
+ relationTypeDistribution[r.relationType] =
428
+ (relationTypeDistribution[r.relationType] || 0) + 1;
429
+ }
430
+
431
+ // Average degree = 2 × edges / nodes (undirected interpretation:
432
+ // each edge contributes to 2 endpoints). 0 when no entities.
433
+ const avgDegree =
434
+ entityCount > 0
435
+ ? Number(((2 * relationCount) / entityCount).toFixed(4))
436
+ : 0;
437
+
438
+ // Density = edges / (nodes × (nodes-1)) for directed graph; 0 when <2.
439
+ const density =
440
+ entityCount > 1
441
+ ? Number((relationCount / (entityCount * (entityCount - 1))).toFixed(6))
442
+ : 0;
443
+
444
+ return {
445
+ entityCount,
446
+ relationCount,
447
+ typeDistribution,
448
+ relationTypeDistribution,
449
+ avgDegree,
450
+ density,
451
+ };
452
+ }
453
+
454
+ /* ── Import / Export ───────────────────────────────────────── */
455
+
456
+ export function exportGraph() {
457
+ return {
458
+ entities: [..._entities.values()].map(_strip),
459
+ relations: [..._relations.values()].map(_strip),
460
+ exportedAt: Date.now(),
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Batch import. Input shape: `{ entities[], relations[] }`.
466
+ * - Entities without `id` get generated ones
467
+ * - Relations referencing unknown sourceId/targetId are skipped
468
+ * - Returns `{ importedEntities, importedRelations, skippedRelations }`
469
+ */
470
+ export function importGraph(db, data = {}) {
471
+ const entities = Array.isArray(data.entities) ? data.entities : [];
472
+ const relations = Array.isArray(data.relations) ? data.relations : [];
473
+
474
+ let importedEntities = 0;
475
+ const idMap = new Map(); // original-id → final-id (for relation resolution)
476
+ for (const raw of entities) {
477
+ const originalId = raw.id || null;
478
+ try {
479
+ const entity = addEntity(db, {
480
+ id: raw.id,
481
+ name: raw.name,
482
+ type: raw.type,
483
+ properties: raw.properties,
484
+ tags: raw.tags,
485
+ now: raw.createdAt,
486
+ });
487
+ if (originalId) idMap.set(originalId, entity.id);
488
+ importedEntities += 1;
489
+ } catch {
490
+ // Skip malformed entity
491
+ }
492
+ }
493
+
494
+ let importedRelations = 0;
495
+ let skippedRelations = 0;
496
+ for (const raw of relations) {
497
+ const sourceId = idMap.get(raw.sourceId) || raw.sourceId;
498
+ const targetId = idMap.get(raw.targetId) || raw.targetId;
499
+ if (!_entities.has(sourceId) || !_entities.has(targetId)) {
500
+ skippedRelations += 1;
501
+ continue;
502
+ }
503
+ try {
504
+ addRelation(db, {
505
+ id: raw.id,
506
+ sourceId,
507
+ targetId,
508
+ relationType: raw.relationType,
509
+ weight: raw.weight,
510
+ properties: raw.properties,
511
+ now: raw.createdAt,
512
+ });
513
+ importedRelations += 1;
514
+ } catch {
515
+ skippedRelations += 1;
516
+ }
517
+ }
518
+
519
+ return { importedEntities, importedRelations, skippedRelations };
520
+ }
521
+
522
+ /* ── Reset (tests) ─────────────────────────────────────────── */
523
+
524
+ export function _resetState() {
525
+ _entities.clear();
526
+ _relations.clear();
527
+ _outEdges.clear();
528
+ _inEdges.clear();
529
+ _seq = 0;
530
+ }
@@ -12,4 +12,7 @@ export {
12
12
  ServerState,
13
13
  MCPClient,
14
14
  MCPServerConfig,
15
+ inferTransport,
16
+ isHttpTransport,
17
+ _deps,
15
18
  } from "../harness/mcp-client.js";