chainlesschain 0.37.9 → 0.37.11

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 (84) hide show
  1. package/README.md +309 -19
  2. package/bin/chainlesschain.js +4 -0
  3. package/package.json +1 -1
  4. package/src/commands/a2a.js +374 -0
  5. package/src/commands/audit.js +286 -0
  6. package/src/commands/auth.js +387 -0
  7. package/src/commands/bi.js +240 -0
  8. package/src/commands/browse.js +184 -0
  9. package/src/commands/cowork.js +317 -0
  10. package/src/commands/did.js +376 -0
  11. package/src/commands/economy.js +375 -0
  12. package/src/commands/encrypt.js +233 -0
  13. package/src/commands/evolution.js +398 -0
  14. package/src/commands/export.js +125 -0
  15. package/src/commands/git.js +215 -0
  16. package/src/commands/hmemory.js +273 -0
  17. package/src/commands/hook.js +260 -0
  18. package/src/commands/import.js +259 -0
  19. package/src/commands/init.js +184 -0
  20. package/src/commands/instinct.js +202 -0
  21. package/src/commands/llm.js +155 -4
  22. package/src/commands/lowcode.js +320 -0
  23. package/src/commands/mcp.js +302 -0
  24. package/src/commands/memory.js +282 -0
  25. package/src/commands/note.js +187 -0
  26. package/src/commands/org.js +505 -0
  27. package/src/commands/p2p.js +274 -0
  28. package/src/commands/plugin.js +451 -0
  29. package/src/commands/sandbox.js +366 -0
  30. package/src/commands/search.js +237 -0
  31. package/src/commands/session.js +238 -0
  32. package/src/commands/skill.js +254 -201
  33. package/src/commands/sync.js +249 -0
  34. package/src/commands/tokens.js +214 -0
  35. package/src/commands/wallet.js +416 -0
  36. package/src/commands/workflow.js +359 -0
  37. package/src/commands/zkp.js +277 -0
  38. package/src/index.js +93 -1
  39. package/src/lib/a2a-protocol.js +371 -0
  40. package/src/lib/agent-coordinator.js +273 -0
  41. package/src/lib/agent-economy.js +369 -0
  42. package/src/lib/app-builder.js +377 -0
  43. package/src/lib/audit-logger.js +364 -0
  44. package/src/lib/bi-engine.js +299 -0
  45. package/src/lib/bm25-search.js +322 -0
  46. package/src/lib/browser-automation.js +216 -0
  47. package/src/lib/cowork/ab-comparator-cli.js +180 -0
  48. package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
  49. package/src/lib/cowork/debate-review-cli.js +144 -0
  50. package/src/lib/cowork/decision-kb-cli.js +153 -0
  51. package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
  52. package/src/lib/cowork-adapter.js +106 -0
  53. package/src/lib/crypto-manager.js +246 -0
  54. package/src/lib/did-manager.js +270 -0
  55. package/src/lib/ensure-utf8.js +59 -0
  56. package/src/lib/evolution-system.js +508 -0
  57. package/src/lib/git-integration.js +220 -0
  58. package/src/lib/hierarchical-memory.js +471 -0
  59. package/src/lib/hook-manager.js +387 -0
  60. package/src/lib/instinct-manager.js +190 -0
  61. package/src/lib/knowledge-exporter.js +302 -0
  62. package/src/lib/knowledge-importer.js +293 -0
  63. package/src/lib/llm-providers.js +325 -0
  64. package/src/lib/mcp-client.js +413 -0
  65. package/src/lib/memory-manager.js +211 -0
  66. package/src/lib/note-versioning.js +244 -0
  67. package/src/lib/org-manager.js +424 -0
  68. package/src/lib/p2p-manager.js +317 -0
  69. package/src/lib/pdf-parser.js +96 -0
  70. package/src/lib/permission-engine.js +374 -0
  71. package/src/lib/plan-mode.js +333 -0
  72. package/src/lib/plugin-manager.js +430 -0
  73. package/src/lib/project-detector.js +53 -0
  74. package/src/lib/response-cache.js +156 -0
  75. package/src/lib/sandbox-v2.js +503 -0
  76. package/src/lib/service-container.js +183 -0
  77. package/src/lib/session-manager.js +189 -0
  78. package/src/lib/skill-loader.js +274 -0
  79. package/src/lib/sync-manager.js +347 -0
  80. package/src/lib/token-tracker.js +200 -0
  81. package/src/lib/wallet-manager.js +348 -0
  82. package/src/lib/workflow-engine.js +503 -0
  83. package/src/lib/zkp-engine.js +241 -0
  84. package/src/repl/agent-repl.js +259 -124
@@ -0,0 +1,317 @@
1
+ /**
2
+ * P2P Manager — Peer-to-peer messaging and device pairing for CLI.
3
+ * Uses a bridge protocol to connect to desktop app or runs standalone
4
+ * with a lightweight signaling mechanism.
5
+ */
6
+
7
+ import crypto from "crypto";
8
+ import { EventEmitter } from "events";
9
+
10
+ const DEFAULT_SIGNALING_PORT = 9001;
11
+ const BRIDGE_TIMEOUT = 5000;
12
+
13
+ /**
14
+ * Ensure P2P tables exist.
15
+ */
16
+ export function ensureP2PTables(db) {
17
+ db.exec(`
18
+ CREATE TABLE IF NOT EXISTS p2p_peers (
19
+ peer_id TEXT PRIMARY KEY,
20
+ display_name TEXT,
21
+ did TEXT,
22
+ public_key TEXT,
23
+ last_seen TEXT,
24
+ status TEXT DEFAULT 'offline',
25
+ device_type TEXT DEFAULT 'unknown',
26
+ created_at TEXT DEFAULT (datetime('now'))
27
+ )
28
+ `);
29
+ db.exec(`
30
+ CREATE TABLE IF NOT EXISTS p2p_messages (
31
+ id TEXT PRIMARY KEY,
32
+ from_peer TEXT NOT NULL,
33
+ to_peer TEXT NOT NULL,
34
+ content TEXT NOT NULL,
35
+ encrypted INTEGER DEFAULT 0,
36
+ read INTEGER DEFAULT 0,
37
+ created_at TEXT DEFAULT (datetime('now'))
38
+ )
39
+ `);
40
+ db.exec(`
41
+ CREATE TABLE IF NOT EXISTS p2p_paired_devices (
42
+ device_id TEXT PRIMARY KEY,
43
+ device_name TEXT,
44
+ device_type TEXT,
45
+ pairing_code TEXT,
46
+ paired_at TEXT DEFAULT (datetime('now')),
47
+ last_sync TEXT,
48
+ status TEXT DEFAULT 'active'
49
+ )
50
+ `);
51
+ }
52
+
53
+ /**
54
+ * Generate a unique peer ID.
55
+ */
56
+ export function generatePeerId() {
57
+ return `peer-${crypto.randomBytes(16).toString("hex")}`;
58
+ }
59
+
60
+ /**
61
+ * Generate a pairing code (6-digit).
62
+ */
63
+ export function generatePairingCode() {
64
+ return String(crypto.randomInt(100000, 999999));
65
+ }
66
+
67
+ /**
68
+ * Register a peer in the database.
69
+ */
70
+ export function registerPeer(
71
+ db,
72
+ peerId,
73
+ displayName,
74
+ did,
75
+ publicKey,
76
+ deviceType,
77
+ ) {
78
+ ensureP2PTables(db);
79
+ db.prepare(
80
+ `INSERT OR REPLACE INTO p2p_peers (peer_id, display_name, did, public_key, last_seen, status, device_type)
81
+ VALUES (?, ?, ?, ?, datetime('now'), ?, ?)`,
82
+ ).run(
83
+ peerId,
84
+ displayName || null,
85
+ did || null,
86
+ publicKey || null,
87
+ "online",
88
+ deviceType || "cli",
89
+ );
90
+ return {
91
+ peerId,
92
+ displayName,
93
+ did,
94
+ deviceType: deviceType || "cli",
95
+ status: "online",
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Get a peer by ID.
101
+ */
102
+ export function getPeer(db, peerId) {
103
+ ensureP2PTables(db);
104
+ return db.prepare("SELECT * FROM p2p_peers WHERE peer_id = ?").get(peerId);
105
+ }
106
+
107
+ /**
108
+ * Get all peers.
109
+ */
110
+ export function getAllPeers(db) {
111
+ ensureP2PTables(db);
112
+ return db.prepare("SELECT * FROM p2p_peers ORDER BY last_seen DESC").all();
113
+ }
114
+
115
+ /**
116
+ * Get online peers.
117
+ */
118
+ export function getOnlinePeers(db) {
119
+ ensureP2PTables(db);
120
+ return db.prepare("SELECT * FROM p2p_peers WHERE status = ?").get("online")
121
+ ? db.prepare("SELECT * FROM p2p_peers WHERE status = ?").all("online")
122
+ : [];
123
+ }
124
+
125
+ /**
126
+ * Update peer status.
127
+ */
128
+ export function updatePeerStatus(db, peerId, status) {
129
+ ensureP2PTables(db);
130
+ const result = db
131
+ .prepare(
132
+ "UPDATE p2p_peers SET status = ?, last_seen = datetime('now') WHERE peer_id = ?",
133
+ )
134
+ .run(status, peerId);
135
+ return result.changes > 0;
136
+ }
137
+
138
+ /**
139
+ * Send a message to a peer (store in DB).
140
+ */
141
+ export function sendMessage(db, fromPeer, toPeer, content, encrypted = false) {
142
+ ensureP2PTables(db);
143
+ const id = `msg-${crypto.randomBytes(8).toString("hex")}`;
144
+
145
+ // Verify recipient exists
146
+ const recipient = getPeer(db, toPeer);
147
+ if (!recipient) {
148
+ throw new Error(`Peer not found: ${toPeer}`);
149
+ }
150
+
151
+ db.prepare(
152
+ `INSERT INTO p2p_messages (id, from_peer, to_peer, content, encrypted)
153
+ VALUES (?, ?, ?, ?, ?)`,
154
+ ).run(id, fromPeer, toPeer, content, encrypted ? 1 : 0);
155
+
156
+ return {
157
+ id,
158
+ fromPeer,
159
+ toPeer,
160
+ content,
161
+ encrypted,
162
+ createdAt: new Date().toISOString(),
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Get inbox messages for a peer.
168
+ */
169
+ export function getInbox(db, peerId, options = {}) {
170
+ ensureP2PTables(db);
171
+ const { unreadOnly = false, limit = 50 } = options;
172
+
173
+ let sql = "SELECT * FROM p2p_messages WHERE to_peer = ?";
174
+ const params = [peerId];
175
+
176
+ if (unreadOnly) {
177
+ sql += " AND read = 0";
178
+ }
179
+
180
+ sql += " ORDER BY created_at DESC LIMIT ?";
181
+ params.push(limit);
182
+
183
+ return db.prepare(sql).all(...params);
184
+ }
185
+
186
+ /**
187
+ * Mark a message as read.
188
+ */
189
+ export function markMessageRead(db, messageId) {
190
+ ensureP2PTables(db);
191
+ const result = db
192
+ .prepare("UPDATE p2p_messages SET read = 1 WHERE id = ?")
193
+ .run(messageId);
194
+ return result.changes > 0;
195
+ }
196
+
197
+ /**
198
+ * Get message count for a peer.
199
+ */
200
+ export function getMessageCount(db, peerId) {
201
+ ensureP2PTables(db);
202
+ const allMsgs = db
203
+ .prepare("SELECT * FROM p2p_messages WHERE to_peer = ?")
204
+ .all(peerId);
205
+ const total = allMsgs.length;
206
+ const unread = allMsgs.filter((m) => !m.read || m.read === 0).length;
207
+ return { total, unread };
208
+ }
209
+
210
+ /**
211
+ * Pair a device.
212
+ */
213
+ export function pairDevice(db, deviceName, deviceType) {
214
+ ensureP2PTables(db);
215
+ const deviceId = `device-${crypto.randomBytes(8).toString("hex")}`;
216
+ const pairingCode = generatePairingCode();
217
+
218
+ db.prepare(
219
+ `INSERT INTO p2p_paired_devices (device_id, device_name, device_type, pairing_code, status)
220
+ VALUES (?, ?, ?, ?, ?)`,
221
+ ).run(deviceId, deviceName, deviceType || "unknown", pairingCode, "pending");
222
+
223
+ return { deviceId, deviceName, deviceType, pairingCode, status: "pending" };
224
+ }
225
+
226
+ /**
227
+ * Confirm device pairing with code.
228
+ */
229
+ export function confirmPairing(db, deviceId, code) {
230
+ ensureP2PTables(db);
231
+ const device = db
232
+ .prepare("SELECT * FROM p2p_paired_devices WHERE device_id = ?")
233
+ .get(deviceId);
234
+ if (!device) return { success: false, error: "Device not found" };
235
+ if (device.pairing_code !== code)
236
+ return { success: false, error: "Invalid pairing code" };
237
+
238
+ db.prepare(
239
+ "UPDATE p2p_paired_devices SET status = ? WHERE device_id = ?",
240
+ ).run("active", deviceId);
241
+ return { success: true, deviceId, deviceName: device.device_name };
242
+ }
243
+
244
+ /**
245
+ * Get all paired devices.
246
+ */
247
+ export function getPairedDevices(db) {
248
+ ensureP2PTables(db);
249
+ return db
250
+ .prepare("SELECT * FROM p2p_paired_devices ORDER BY paired_at DESC")
251
+ .all();
252
+ }
253
+
254
+ /**
255
+ * Unpair a device.
256
+ */
257
+ export function unpairDevice(db, deviceId) {
258
+ ensureP2PTables(db);
259
+ const result = db
260
+ .prepare("DELETE FROM p2p_paired_devices WHERE device_id = ?")
261
+ .run(deviceId);
262
+ return result.changes > 0;
263
+ }
264
+
265
+ /**
266
+ * Delete a peer.
267
+ */
268
+ export function deletePeer(db, peerId) {
269
+ ensureP2PTables(db);
270
+ const result = db
271
+ .prepare("DELETE FROM p2p_peers WHERE peer_id = ?")
272
+ .run(peerId);
273
+ return result.changes > 0;
274
+ }
275
+
276
+ /**
277
+ * P2P Bridge — connects CLI to desktop app via HTTP/WebSocket.
278
+ */
279
+ export class P2PBridge extends EventEmitter {
280
+ constructor(options = {}) {
281
+ super();
282
+ this.host = options.host || "localhost";
283
+ this.port = options.port || DEFAULT_SIGNALING_PORT;
284
+ this.timeout = options.timeout || BRIDGE_TIMEOUT;
285
+ this.connected = false;
286
+ }
287
+
288
+ /**
289
+ * Check if desktop bridge is available.
290
+ */
291
+ async checkBridge() {
292
+ try {
293
+ const controller = new AbortController();
294
+ const timer = setTimeout(() => controller.abort(), this.timeout);
295
+ const response = await fetch(`http://${this.host}:${this.port}/health`, {
296
+ signal: controller.signal,
297
+ });
298
+ clearTimeout(timer);
299
+ return response.ok;
300
+ } catch (_err) {
301
+ // Bridge not available
302
+ return false;
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Get bridge status info.
308
+ */
309
+ getStatus() {
310
+ return {
311
+ host: this.host,
312
+ port: this.port,
313
+ connected: this.connected,
314
+ endpoint: `http://${this.host}:${this.port}`,
315
+ };
316
+ }
317
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Lightweight PDF text extractor.
3
+ * Uses a simple built-in parser for basic PDF text extraction.
4
+ * Falls back gracefully if text cannot be extracted.
5
+ */
6
+
7
+ import { readFileSync } from "fs";
8
+ import { basename, extname } from "path";
9
+
10
+ /**
11
+ * Extract text content from a PDF file.
12
+ * Uses a basic approach: scan PDF stream for text operators.
13
+ * This handles simple PDFs without requiring heavy external dependencies.
14
+ *
15
+ * @param {string} filePath - Path to the PDF file
16
+ * @returns {{ title: string, content: string, pages: number|null }}
17
+ */
18
+ export async function parsePdfText(filePath) {
19
+ const buffer = readFileSync(filePath);
20
+ const title = basename(filePath, extname(filePath));
21
+
22
+ // Try to extract text from PDF binary
23
+ const text = extractTextFromPdf(buffer);
24
+
25
+ // Count pages by looking for /Type /Page objects
26
+ const pdfStr = buffer.toString("latin1");
27
+ const pageMatches = pdfStr.match(/\/Type\s*\/Page[^s]/g);
28
+ const pages = pageMatches ? pageMatches.length : null;
29
+
30
+ return {
31
+ title,
32
+ content: text,
33
+ pages,
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Basic PDF text extraction.
39
+ * Scans for text between BT/ET operators and decodes common encodings.
40
+ */
41
+ function extractTextFromPdf(buffer) {
42
+ const pdfStr = buffer.toString("latin1");
43
+ const textParts = [];
44
+
45
+ // Find all stream sections and decode them
46
+ const streamRegex = /stream\r?\n([\s\S]*?)\r?\nendstream/g;
47
+ let match;
48
+
49
+ while ((match = streamRegex.exec(pdfStr)) !== null) {
50
+ const streamContent = match[1];
51
+
52
+ // Look for text showing operators: Tj, TJ, ', "
53
+ const tjRegex = /\(([^)]*)\)\s*Tj/g;
54
+ let tjMatch;
55
+ while ((tjMatch = tjRegex.exec(streamContent)) !== null) {
56
+ const decoded = decodePdfString(tjMatch[1]);
57
+ if (decoded.trim()) textParts.push(decoded);
58
+ }
59
+
60
+ // TJ operator: array of strings and numbers
61
+ const tjArrayRegex = /\[((?:\([^)]*\)|[^[\]])*)\]\s*TJ/gi;
62
+ let tjArrMatch;
63
+ while ((tjArrMatch = tjArrayRegex.exec(streamContent)) !== null) {
64
+ const inner = tjArrMatch[1];
65
+ const strRegex = /\(([^)]*)\)/g;
66
+ let strMatch;
67
+ const parts = [];
68
+ while ((strMatch = strRegex.exec(inner)) !== null) {
69
+ parts.push(decodePdfString(strMatch[1]));
70
+ }
71
+ if (parts.length > 0) textParts.push(parts.join(""));
72
+ }
73
+ }
74
+
75
+ // Clean up and join
76
+ const text = textParts
77
+ .join("\n")
78
+ .replace(/\r\n/g, "\n")
79
+ .replace(/\n{3,}/g, "\n\n")
80
+ .trim();
81
+
82
+ return text;
83
+ }
84
+
85
+ /**
86
+ * Decode a PDF string (handle basic escape sequences).
87
+ */
88
+ function decodePdfString(str) {
89
+ return str
90
+ .replace(/\\n/g, "\n")
91
+ .replace(/\\r/g, "\r")
92
+ .replace(/\\t/g, "\t")
93
+ .replace(/\\\(/g, "(")
94
+ .replace(/\\\)/g, ")")
95
+ .replace(/\\\\/g, "\\");
96
+ }