chainlesschain 0.47.8 → 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 (86) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +10 -8
  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/activitypub.js +533 -0
  10. package/src/commands/codegen.js +303 -0
  11. package/src/commands/collab.js +482 -0
  12. package/src/commands/compliance.js +597 -6
  13. package/src/commands/crosschain.js +382 -0
  14. package/src/commands/dbevo.js +388 -0
  15. package/src/commands/dev.js +411 -0
  16. package/src/commands/federation.js +427 -0
  17. package/src/commands/fusion.js +332 -0
  18. package/src/commands/governance.js +505 -0
  19. package/src/commands/hardening.js +110 -0
  20. package/src/commands/incentive.js +373 -0
  21. package/src/commands/inference.js +304 -0
  22. package/src/commands/infra.js +361 -0
  23. package/src/commands/kg.js +371 -0
  24. package/src/commands/marketplace.js +326 -0
  25. package/src/commands/matrix.js +283 -0
  26. package/src/commands/mcp.js +441 -18
  27. package/src/commands/nlprog.js +329 -0
  28. package/src/commands/nostr.js +196 -7
  29. package/src/commands/ops.js +408 -0
  30. package/src/commands/perception.js +385 -0
  31. package/src/commands/pqc.js +34 -0
  32. package/src/commands/privacy.js +345 -0
  33. package/src/commands/quantization.js +280 -0
  34. package/src/commands/recommend.js +336 -0
  35. package/src/commands/reputation.js +349 -0
  36. package/src/commands/runtime.js +500 -0
  37. package/src/commands/sla.js +352 -0
  38. package/src/commands/social.js +265 -0
  39. package/src/commands/stress.js +252 -0
  40. package/src/commands/tech.js +268 -0
  41. package/src/commands/tenant.js +576 -0
  42. package/src/commands/trust.js +366 -0
  43. package/src/harness/mcp-client.js +330 -54
  44. package/src/index.js +114 -0
  45. package/src/lib/activitypub-bridge.js +623 -0
  46. package/src/lib/aiops.js +523 -0
  47. package/src/lib/autonomous-developer.js +524 -0
  48. package/src/lib/code-agent.js +442 -0
  49. package/src/lib/collaboration-governance.js +556 -0
  50. package/src/lib/community-governance.js +649 -0
  51. package/src/lib/compliance-framework-reporter.js +600 -0
  52. package/src/lib/content-recommendation.js +600 -0
  53. package/src/lib/cross-chain.js +669 -0
  54. package/src/lib/dbevo.js +669 -0
  55. package/src/lib/decentral-infra.js +445 -0
  56. package/src/lib/federation-hardening.js +587 -0
  57. package/src/lib/hardening-manager.js +409 -0
  58. package/src/lib/inference-network.js +407 -0
  59. package/src/lib/knowledge-graph.js +530 -0
  60. package/src/lib/matrix-bridge.js +252 -0
  61. package/src/lib/mcp-client.js +3 -0
  62. package/src/lib/mcp-registry.js +347 -0
  63. package/src/lib/mcp-scaffold.js +385 -0
  64. package/src/lib/multimodal.js +698 -0
  65. package/src/lib/nl-programming.js +595 -0
  66. package/src/lib/nostr-bridge.js +214 -38
  67. package/src/lib/perception.js +500 -0
  68. package/src/lib/pqc-manager.js +141 -9
  69. package/src/lib/privacy-computing.js +575 -0
  70. package/src/lib/protocol-fusion.js +535 -0
  71. package/src/lib/quantization.js +362 -0
  72. package/src/lib/reputation-optimizer.js +509 -0
  73. package/src/lib/skill-marketplace.js +397 -0
  74. package/src/lib/sla-manager.js +484 -0
  75. package/src/lib/social-graph.js +408 -0
  76. package/src/lib/stix-parser.js +167 -0
  77. package/src/lib/stress-tester.js +383 -0
  78. package/src/lib/tech-learning-engine.js +651 -0
  79. package/src/lib/tenant-saas.js +831 -0
  80. package/src/lib/threat-intel.js +268 -0
  81. package/src/lib/token-incentive.js +513 -0
  82. package/src/lib/topic-classifier.js +400 -0
  83. package/src/lib/trust-security.js +473 -0
  84. package/src/lib/ueba.js +403 -0
  85. package/src/lib/universal-runtime.js +771 -0
  86. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
@@ -0,0 +1,513 @@
1
+ /**
2
+ * Token Incentive — CLI port of Phase 66 token-ledger + contribution-tracker
3
+ * (docs/design/modules/37_技能市场系统.md §2.3-2.4).
4
+ *
5
+ * The Desktop build drives token ledger + contribution tracker over Context
6
+ * Engineering and reputation-weighted reward calculation with on-chain anchoring.
7
+ * The CLI can't host the P2P agent mesh or on-chain settlement, so this port
8
+ * ships the tractable scaffolding:
9
+ *
10
+ * - TokenLedger: accounts (balance/totalEarned/totalSpent) + transfers +
11
+ * rewards, all tracked as double-entry-ish transactions.
12
+ * - ContributionTracker: contribution records with typed reward multipliers +
13
+ * leaderboard aggregation.
14
+ * - Catalogs: CONTRIBUTION_TYPES, TX_TYPES.
15
+ *
16
+ * Real reputation-weighted rewards, on-chain settlement, and P2P cross-wallet
17
+ * transfer are Desktop-only.
18
+ */
19
+
20
+ import crypto from "crypto";
21
+
22
+ /* ── Constants ─────────────────────────────────────────────── */
23
+
24
+ export const TX_TYPES = Object.freeze({
25
+ TRANSFER: "transfer",
26
+ REWARD: "reward",
27
+ MINT: "mint",
28
+ BURN: "burn",
29
+ });
30
+
31
+ const VALID_TX_TYPES = new Set(Object.values(TX_TYPES));
32
+
33
+ // Contribution types and their base reward multipliers.
34
+ // These match the Desktop defaults but are tweakable at call site via
35
+ // calculateReward(contribution, { multiplier }).
36
+ export const CONTRIBUTION_TYPES = Object.freeze({
37
+ SKILL_PUBLICATION: {
38
+ name: "skill_publication",
39
+ baseReward: 10,
40
+ description: "发布新技能服务",
41
+ },
42
+ INVOCATION_PROVIDED: {
43
+ name: "invocation_provided",
44
+ baseReward: 0.1,
45
+ description: "提供一次技能调用",
46
+ },
47
+ SKILL_REVIEW: {
48
+ name: "skill_review",
49
+ baseReward: 1,
50
+ description: "审核或评价技能",
51
+ },
52
+ BUG_REPORT: {
53
+ name: "bug_report",
54
+ baseReward: 2,
55
+ description: "提交有效 Bug 报告",
56
+ },
57
+ CODE_CONTRIBUTION: {
58
+ name: "code_contribution",
59
+ baseReward: 5,
60
+ description: "代码贡献",
61
+ },
62
+ DOCUMENTATION: {
63
+ name: "documentation",
64
+ baseReward: 3,
65
+ description: "文档贡献",
66
+ },
67
+ COMMUNITY_SUPPORT: {
68
+ name: "community_support",
69
+ baseReward: 1,
70
+ description: "社区支持(回答问题、引导新人等)",
71
+ },
72
+ });
73
+
74
+ const VALID_CONTRIBUTION_TYPES = new Set(
75
+ Object.values(CONTRIBUTION_TYPES).map((c) => c.name),
76
+ );
77
+
78
+ function _getTypeSpec(typeName) {
79
+ const name = String(typeName || "").toLowerCase();
80
+ for (const spec of Object.values(CONTRIBUTION_TYPES)) {
81
+ if (spec.name === name) return spec;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ /* ── State ─────────────────────────────────────────────────── */
87
+
88
+ const _accounts = new Map(); // accountId → account
89
+ const _transactions = new Map(); // txId → transaction
90
+ const _contributions = new Map(); // contributionId → contribution
91
+ let _seq = 0;
92
+
93
+ /* ── Schema ────────────────────────────────────────────────── */
94
+
95
+ export function ensureTokenTables(db) {
96
+ if (!db) return;
97
+ db.exec(`
98
+ CREATE TABLE IF NOT EXISTS token_accounts (
99
+ id TEXT PRIMARY KEY,
100
+ account_id TEXT NOT NULL UNIQUE,
101
+ balance REAL DEFAULT 0,
102
+ total_earned REAL DEFAULT 0,
103
+ total_spent REAL DEFAULT 0,
104
+ created_at INTEGER NOT NULL,
105
+ updated_at INTEGER NOT NULL
106
+ )
107
+ `);
108
+ db.exec(`
109
+ CREATE TABLE IF NOT EXISTS token_transactions (
110
+ id TEXT PRIMARY KEY,
111
+ from_account TEXT,
112
+ to_account TEXT,
113
+ amount REAL NOT NULL,
114
+ reason TEXT,
115
+ type TEXT DEFAULT 'transfer',
116
+ created_at INTEGER NOT NULL
117
+ )
118
+ `);
119
+ db.exec(`
120
+ CREATE TABLE IF NOT EXISTS contributions (
121
+ id TEXT PRIMARY KEY,
122
+ user_id TEXT NOT NULL,
123
+ type TEXT NOT NULL,
124
+ value REAL DEFAULT 0,
125
+ metadata TEXT,
126
+ rewarded INTEGER DEFAULT 0,
127
+ reward_amount REAL DEFAULT 0,
128
+ tx_id TEXT,
129
+ created_at INTEGER NOT NULL
130
+ )
131
+ `);
132
+ db.exec(
133
+ `CREATE INDEX IF NOT EXISTS idx_token_tx_from ON token_transactions(from_account)`,
134
+ );
135
+ db.exec(
136
+ `CREATE INDEX IF NOT EXISTS idx_token_tx_to ON token_transactions(to_account)`,
137
+ );
138
+ db.exec(
139
+ `CREATE INDEX IF NOT EXISTS idx_contributions_user ON contributions(user_id)`,
140
+ );
141
+ db.exec(
142
+ `CREATE INDEX IF NOT EXISTS idx_contributions_type ON contributions(type)`,
143
+ );
144
+ }
145
+
146
+ /* ── Catalogs ──────────────────────────────────────────────── */
147
+
148
+ export function listContributionTypes() {
149
+ return Object.values(CONTRIBUTION_TYPES).map((c) => ({ ...c }));
150
+ }
151
+
152
+ export function listTxTypes() {
153
+ return Object.values(TX_TYPES);
154
+ }
155
+
156
+ function _strip(row) {
157
+ const { _seq: _omit, ...rest } = row;
158
+ void _omit;
159
+ return rest;
160
+ }
161
+
162
+ /* ── TokenLedger ───────────────────────────────────────────── */
163
+
164
+ function _persistAccount(db, account) {
165
+ if (!db) return;
166
+ db.prepare(
167
+ `INSERT OR REPLACE INTO token_accounts (id, account_id, balance, total_earned, total_spent, created_at, updated_at)
168
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
169
+ ).run(
170
+ account.id,
171
+ account.accountId,
172
+ account.balance,
173
+ account.totalEarned,
174
+ account.totalSpent,
175
+ account.createdAt,
176
+ account.updatedAt,
177
+ );
178
+ }
179
+
180
+ function _ensureAccount(db, accountId, now) {
181
+ const id = String(accountId || "").trim();
182
+ if (!id) throw new Error("accountId is required");
183
+
184
+ let account = _accounts.get(id);
185
+ if (account) return account;
186
+
187
+ account = {
188
+ id: crypto.randomUUID(),
189
+ accountId: id,
190
+ balance: 0,
191
+ totalEarned: 0,
192
+ totalSpent: 0,
193
+ createdAt: now,
194
+ updatedAt: now,
195
+ _seq: ++_seq,
196
+ };
197
+ _accounts.set(id, account);
198
+ _persistAccount(db, account);
199
+ return account;
200
+ }
201
+
202
+ export function getBalance(accountId) {
203
+ const account = _accounts.get(String(accountId || "").trim());
204
+ return account ? _strip(account) : null;
205
+ }
206
+
207
+ export function listAccounts(opts = {}) {
208
+ let rows = [..._accounts.values()];
209
+ rows.sort((a, b) => b.balance - a.balance || b._seq - a._seq);
210
+ const limit = opts.limit || 50;
211
+ return rows.slice(0, limit).map(_strip);
212
+ }
213
+
214
+ function _recordTransaction(db, config) {
215
+ const now = Number(config.now ?? Date.now());
216
+ const id = config.id || crypto.randomUUID();
217
+ const tx = {
218
+ id,
219
+ fromAccount: config.from ?? null,
220
+ toAccount: config.to ?? null,
221
+ amount: Number(config.amount),
222
+ reason: config.reason ? String(config.reason) : null,
223
+ type: config.type || TX_TYPES.TRANSFER,
224
+ createdAt: now,
225
+ _seq: ++_seq,
226
+ };
227
+ _transactions.set(id, tx);
228
+
229
+ if (db) {
230
+ db.prepare(
231
+ `INSERT INTO token_transactions (id, from_account, to_account, amount, reason, type, created_at)
232
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
233
+ ).run(id, tx.fromAccount, tx.toAccount, tx.amount, tx.reason, tx.type, now);
234
+ }
235
+ return tx;
236
+ }
237
+
238
+ export function transfer(db, config = {}) {
239
+ const from = String(config.from || "").trim();
240
+ const to = String(config.to || "").trim();
241
+ const amount = Number(config.amount);
242
+
243
+ if (!from) throw new Error("from account is required");
244
+ if (!to) throw new Error("to account is required");
245
+ if (from === to) throw new Error("Cannot transfer to same account");
246
+ if (!Number.isFinite(amount) || amount <= 0) {
247
+ throw new Error(`Invalid amount: ${config.amount} (must be > 0)`);
248
+ }
249
+
250
+ const now = Number(config.now ?? Date.now());
251
+ const fromAccount = _ensureAccount(db, from, now);
252
+ const toAccount = _ensureAccount(db, to, now);
253
+
254
+ if (fromAccount.balance < amount) {
255
+ throw new Error(`Insufficient balance: ${fromAccount.balance} < ${amount}`);
256
+ }
257
+
258
+ fromAccount.balance -= amount;
259
+ fromAccount.totalSpent += amount;
260
+ fromAccount.updatedAt = now;
261
+ toAccount.balance += amount;
262
+ toAccount.totalEarned += amount;
263
+ toAccount.updatedAt = now;
264
+
265
+ _persistAccount(db, fromAccount);
266
+ _persistAccount(db, toAccount);
267
+
268
+ const tx = _recordTransaction(db, {
269
+ from,
270
+ to,
271
+ amount,
272
+ reason: config.reason,
273
+ type: TX_TYPES.TRANSFER,
274
+ now,
275
+ });
276
+ return _strip(tx);
277
+ }
278
+
279
+ export function mint(db, config = {}) {
280
+ const to = String(config.to || "").trim();
281
+ const amount = Number(config.amount);
282
+
283
+ if (!to) throw new Error("to account is required");
284
+ if (!Number.isFinite(amount) || amount <= 0) {
285
+ throw new Error(`Invalid amount: ${config.amount} (must be > 0)`);
286
+ }
287
+
288
+ const now = Number(config.now ?? Date.now());
289
+ const account = _ensureAccount(db, to, now);
290
+ account.balance += amount;
291
+ account.totalEarned += amount;
292
+ account.updatedAt = now;
293
+ _persistAccount(db, account);
294
+
295
+ const tx = _recordTransaction(db, {
296
+ from: null,
297
+ to,
298
+ amount,
299
+ reason: config.reason,
300
+ type: TX_TYPES.MINT,
301
+ now,
302
+ });
303
+ return _strip(tx);
304
+ }
305
+
306
+ export function getTransactionHistory(opts = {}) {
307
+ let rows = [..._transactions.values()];
308
+ if (opts.accountId) {
309
+ const id = String(opts.accountId);
310
+ rows = rows.filter((t) => t.fromAccount === id || t.toAccount === id);
311
+ }
312
+ if (opts.type) {
313
+ const t = String(opts.type).toLowerCase();
314
+ if (!VALID_TX_TYPES.has(t)) {
315
+ throw new Error(`Unknown tx type: ${opts.type}`);
316
+ }
317
+ rows = rows.filter((tx) => tx.type === t);
318
+ }
319
+ rows.sort((a, b) => b.createdAt - a.createdAt || b._seq - a._seq);
320
+ const limit = opts.limit || 50;
321
+ return rows.slice(0, limit).map(_strip);
322
+ }
323
+
324
+ /* ── ContributionTracker ───────────────────────────────────── */
325
+
326
+ export function calculateReward(contribution, opts = {}) {
327
+ const type = String(contribution?.type || "").toLowerCase();
328
+ const spec = _getTypeSpec(type);
329
+ if (!spec) {
330
+ throw new Error(`Unknown contribution type: ${contribution?.type}`);
331
+ }
332
+ const value = Number(contribution?.value ?? 1);
333
+ if (!Number.isFinite(value) || value < 0) {
334
+ throw new Error(`Invalid value: ${contribution?.value} (must be >= 0)`);
335
+ }
336
+ const multiplier = Number(opts.multiplier ?? 1);
337
+ if (!Number.isFinite(multiplier) || multiplier < 0) {
338
+ throw new Error(`Invalid multiplier: ${opts.multiplier} (must be >= 0)`);
339
+ }
340
+ const reward = spec.baseReward * value * multiplier;
341
+ return Number(reward.toFixed(4));
342
+ }
343
+
344
+ export function recordContribution(db, config = {}) {
345
+ const userId = String(config.userId || "").trim();
346
+ if (!userId) throw new Error("userId is required");
347
+
348
+ const type = String(config.type || "").toLowerCase();
349
+ if (!VALID_CONTRIBUTION_TYPES.has(type)) {
350
+ throw new Error(
351
+ `Unknown contribution type: ${config.type} (known: ${[...VALID_CONTRIBUTION_TYPES].join("/")})`,
352
+ );
353
+ }
354
+
355
+ const value = Number(config.value ?? 1);
356
+ if (!Number.isFinite(value) || value < 0) {
357
+ throw new Error(`Invalid value: ${config.value} (must be >= 0)`);
358
+ }
359
+
360
+ const metadata = config.metadata || null;
361
+ const now = Number(config.now ?? Date.now());
362
+ const id = config.id || crypto.randomUUID();
363
+
364
+ const contribution = {
365
+ id,
366
+ userId,
367
+ type,
368
+ value,
369
+ metadata,
370
+ rewarded: false,
371
+ rewardAmount: 0,
372
+ txId: null,
373
+ createdAt: now,
374
+ _seq: ++_seq,
375
+ };
376
+ _contributions.set(id, contribution);
377
+
378
+ if (db) {
379
+ db.prepare(
380
+ `INSERT INTO contributions (id, user_id, type, value, metadata, rewarded, reward_amount, tx_id, created_at)
381
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
382
+ ).run(
383
+ id,
384
+ userId,
385
+ type,
386
+ value,
387
+ metadata ? JSON.stringify(metadata) : null,
388
+ 0,
389
+ 0,
390
+ null,
391
+ now,
392
+ );
393
+ }
394
+
395
+ // Auto-reward if requested
396
+ if (config.autoReward) {
397
+ rewardContribution(db, id, { multiplier: config.multiplier });
398
+ }
399
+
400
+ return _strip(_contributions.get(id));
401
+ }
402
+
403
+ export function rewardContribution(db, contributionId, opts = {}) {
404
+ const contribution = _contributions.get(contributionId);
405
+ if (!contribution) {
406
+ throw new Error(`Contribution not found: ${contributionId}`);
407
+ }
408
+ if (contribution.rewarded) {
409
+ throw new Error(`Contribution already rewarded: ${contributionId}`);
410
+ }
411
+
412
+ const amount = calculateReward(contribution, {
413
+ multiplier: opts.multiplier,
414
+ });
415
+ if (amount <= 0) {
416
+ contribution.rewarded = true;
417
+ contribution.rewardAmount = 0;
418
+ if (db) {
419
+ db.prepare(
420
+ `UPDATE contributions SET rewarded = ?, reward_amount = ? WHERE id = ?`,
421
+ ).run(1, 0, contributionId);
422
+ }
423
+ return { contribution: _strip(contribution), tx: null };
424
+ }
425
+
426
+ const now = Number(opts.now ?? Date.now());
427
+ const account = _ensureAccount(db, contribution.userId, now);
428
+ account.balance += amount;
429
+ account.totalEarned += amount;
430
+ account.updatedAt = now;
431
+ _persistAccount(db, account);
432
+
433
+ const tx = _recordTransaction(db, {
434
+ from: null,
435
+ to: contribution.userId,
436
+ amount,
437
+ reason: `contribution:${contribution.type}:${contributionId.slice(0, 8)}`,
438
+ type: TX_TYPES.REWARD,
439
+ now,
440
+ });
441
+
442
+ contribution.rewarded = true;
443
+ contribution.rewardAmount = amount;
444
+ contribution.txId = tx.id;
445
+
446
+ if (db) {
447
+ db.prepare(
448
+ `UPDATE contributions SET rewarded = ?, reward_amount = ?, tx_id = ? WHERE id = ?`,
449
+ ).run(1, amount, tx.id, contributionId);
450
+ }
451
+
452
+ return { contribution: _strip(contribution), tx: _strip(tx) };
453
+ }
454
+
455
+ export function getContributions(opts = {}) {
456
+ let rows = [..._contributions.values()];
457
+ if (opts.userId) {
458
+ rows = rows.filter((c) => c.userId === opts.userId);
459
+ }
460
+ if (opts.type) {
461
+ const t = String(opts.type).toLowerCase();
462
+ if (!VALID_CONTRIBUTION_TYPES.has(t)) {
463
+ throw new Error(`Unknown contribution type: ${opts.type}`);
464
+ }
465
+ rows = rows.filter((c) => c.type === t);
466
+ }
467
+ if (opts.rewarded === true) {
468
+ rows = rows.filter((c) => c.rewarded === true);
469
+ } else if (opts.rewarded === false) {
470
+ rows = rows.filter((c) => c.rewarded === false);
471
+ }
472
+ rows.sort((a, b) => b.createdAt - a.createdAt || b._seq - a._seq);
473
+ const limit = opts.limit || 50;
474
+ return rows.slice(0, limit).map(_strip);
475
+ }
476
+
477
+ export function getLeaderboard(opts = {}) {
478
+ const userStats = new Map();
479
+ for (const c of _contributions.values()) {
480
+ const s = userStats.get(c.userId) || {
481
+ userId: c.userId,
482
+ contributions: 0,
483
+ totalValue: 0,
484
+ totalReward: 0,
485
+ };
486
+ s.contributions += 1;
487
+ s.totalValue += c.value;
488
+ s.totalReward += c.rewardAmount || 0;
489
+ userStats.set(c.userId, s);
490
+ }
491
+ let rows = [...userStats.values()];
492
+ rows.sort(
493
+ (a, b) =>
494
+ b.totalReward - a.totalReward ||
495
+ b.totalValue - a.totalValue ||
496
+ b.contributions - a.contributions,
497
+ );
498
+ const limit = opts.limit || 10;
499
+ return rows.slice(0, limit).map((s) => ({
500
+ ...s,
501
+ totalReward: Number(s.totalReward.toFixed(4)),
502
+ totalValue: Number(s.totalValue.toFixed(4)),
503
+ }));
504
+ }
505
+
506
+ /* ── Reset (tests) ─────────────────────────────────────────── */
507
+
508
+ export function _resetState() {
509
+ _accounts.clear();
510
+ _transactions.clear();
511
+ _contributions.clear();
512
+ _seq = 0;
513
+ }