@bolloon/bolloon-agent 0.4.31 → 0.4.33

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 (55) hide show
  1. package/dist/agents/chain/chain-config.js +504 -0
  2. package/dist/agents/chain/chain-index-query.js +135 -0
  3. package/dist/agents/chain/chain-indexer.js +1045 -0
  4. package/dist/agents/chain/chain-settlement.js +284 -0
  5. package/dist/agents/chain/chain-state-store.js +245 -0
  6. package/dist/agents/chain/chain-wallet.js +198 -0
  7. package/dist/agents/chain/escrow-client.js +433 -0
  8. package/dist/agents/chain/explorer.js +52 -0
  9. package/dist/agents/chain/index.js +22 -0
  10. package/dist/agents/chain/onchain-trade.js +705 -0
  11. package/dist/agents/network-pulse.js +676 -2
  12. package/dist/agents/task/task-onchain-runner.js +302 -0
  13. package/dist/agents/task/task-runner.js +27 -3
  14. package/dist/agents/task-board.js +702 -0
  15. package/dist/agents/task-group.js +567 -0
  16. package/dist/agents/x402/paid-info-store.js +75 -6
  17. package/dist/cli/commands/chain.js +926 -0
  18. package/dist/cli/commands/index.js +12 -2
  19. package/dist/cli/commands/tasks.js +627 -6
  20. package/dist/cli/mcp/server.js +4 -1
  21. package/dist/cli/mcp/tools.js +348 -5
  22. package/dist/cli/protocol-envelope.js +16 -0
  23. package/dist/cli-entry.js +6 -1
  24. package/dist/electron-build/agents/chain/chain-config.js +556 -0
  25. package/dist/electron-build/agents/chain/chain-config.js.map +1 -0
  26. package/dist/electron-build/agents/chain/chain-index-query.js +145 -0
  27. package/dist/electron-build/agents/chain/chain-index-query.js.map +1 -0
  28. package/dist/electron-build/agents/chain/chain-indexer.js +1095 -0
  29. package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -0
  30. package/dist/electron-build/agents/chain/chain-settlement.js +291 -0
  31. package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -0
  32. package/dist/electron-build/agents/chain/escrow-client.js +443 -0
  33. package/dist/electron-build/agents/chain/escrow-client.js.map +1 -0
  34. package/dist/electron-build/agents/chain/explorer.js +59 -0
  35. package/dist/electron-build/agents/chain/explorer.js.map +1 -0
  36. package/dist/electron-build/agents/local-signer.js +101 -0
  37. package/dist/electron-build/agents/local-signer.js.map +1 -0
  38. package/dist/electron-build/agents/network-pulse.js +693 -3
  39. package/dist/electron-build/agents/network-pulse.js.map +1 -1
  40. package/dist/electron-build/agents/task/task-runner.js +27 -3
  41. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  42. package/dist/electron-build/agents/task-board.js +761 -0
  43. package/dist/electron-build/agents/task-board.js.map +1 -0
  44. package/dist/electron-build/agents/task-contract.js +382 -0
  45. package/dist/electron-build/agents/task-contract.js.map +1 -0
  46. package/dist/electron-build/agents/x402/paid-info-store.js +76 -6
  47. package/dist/electron-build/agents/x402/paid-info-store.js.map +1 -1
  48. package/dist/web/chain-explorer.js +617 -0
  49. package/dist/web/explorer.html +163 -0
  50. package/dist/web/index.html +5 -0
  51. package/dist/web/routes-chain-index.js +108 -0
  52. package/dist/web/routes-onchain-trade.js +99 -0
  53. package/dist/web/server.js +13 -0
  54. package/package.json +2 -1
  55. package/scripts/build-web.ts +20 -1
@@ -244,6 +244,42 @@ function makeTracker(onEvent) {
244
244
  return async (e) => { if (onEvent)
245
245
  await onEvent(e).catch(() => null); };
246
246
  }
247
+ /**
248
+ * ★ F5 修复点。
249
+ * 老代码: `chainSettled = !!txHash` —— 拿到 txHash 就宣称链上已验证 (从不读 receipt/事件/确认数)。
250
+ * 新逻辑:
251
+ * · 没有 txHash → `not_attempted`, chainSettled=false (没发过链上交易)
252
+ * · 读不到 RPC / receipt → `unknown`, chainSettled=false (不确定 ≠ 没付)
253
+ * · receipt.status == 0 → `reverted`, chainSettled=false
254
+ * · 确认数 < 门槛 → `pending`, chainSettled=false
255
+ * · 事件(taskKey/resultHash)对不上 → `event_mismatch`, chainSettled=false
256
+ * · 全过 → `confirmed`/`finalized`, chainSettled=true
257
+ * 验证器本身抛错也**不会**炸付款路径: 一律降级成 `unknown` + chainSettled=false (fail-closed)。
258
+ */
259
+ export async function verifyPaymentOnChain(args) {
260
+ const gate = args.chainSettlement?.gate || 'confirmed';
261
+ const txHash = String(args.txHash || '');
262
+ if (!txHash) {
263
+ return {
264
+ chainSettled: false, status: 'not_attempted',
265
+ reason: 'facilitator 说成功但没给 txHash: 没有链上交易可查, 不能认定链上结算完成',
266
+ confirmationsRequired: 0, requiredGate: gate, rpcAvailable: true, checkedAt: Date.now(), evidence: {},
267
+ };
268
+ }
269
+ try {
270
+ const verifier = args.chainSettlement?.verifier
271
+ ?? (await import('../chain/chain-settlement.js')).createDefaultChainSettlementVerifier({ gate });
272
+ return await verifier({ txHash, expect: args.chainSettlement?.expect, gate, recorded: args.chainSettlement?.recorded });
273
+ }
274
+ catch (e) {
275
+ return {
276
+ chainSettled: false, status: 'unknown',
277
+ reason: `链上验证器抛错 → 不能判已结算: ${String(e?.message || e).slice(0, 200)}`,
278
+ txHash, confirmationsRequired: 0, requiredGate: gate, rpcAvailable: false,
279
+ checkedAt: Date.now(), evidence: { verifierError: String(e?.message || e).slice(0, 200) },
280
+ };
281
+ }
282
+ }
247
283
  export async function buyInfo(params) {
248
284
  const { verifyEnvelope } = await import('./paid-info-protocol.js');
249
285
  const doFetch = params.fetchImpl ?? fetch;
@@ -299,26 +335,59 @@ export async function buyInfo(params) {
299
335
  const text = await retry.text();
300
336
  const parsed = safeJson(text);
301
337
  const receipt = retry.headers.get('x-payment-response') || parsed?.payment?.receipt || '';
338
+ // ★ 链上事实需要 txHash: facilitator 说成功但没有 txHash → 不能标 chainSettled (真跑抓到过这类"假结算")
339
+ const txHash = parsed?.payment?.txHash || retry.headers.get('x-payment-txhash') || parsed?.txHash || '';
302
340
  if (retry.status < 200 || retry.status >= 300) {
303
341
  // 付款这一步已经发出去了 (可能已上链), 只是拿资源失败 → 绝不许当"没付过钱"
342
+ // ★ F5: 有 txHash 就去链上真查一遍, 而不是"有 txHash 就算结算"
343
+ const v = await verifyPaymentOnChain({ txHash, chainSettlement: params.chainSettlement });
304
344
  return {
305
345
  ok: false, status: retry.status,
306
- payment: { mode: 'facilitator', receipt: receipt || undefined, attempted: true, settled: true, settlementUncertain: !receipt },
346
+ payment: {
347
+ mode: 'facilitator', receipt: receipt || undefined, attempted: true, settled: true,
348
+ settlementUncertain: !receipt && !v.chainSettled,
349
+ chainSettled: v.chainSettled, chainSettlementStatus: v.status, chainSettlementReason: v.reason,
350
+ confirmations: v.confirmations, confirmationsRequired: v.confirmationsRequired, rpcAvailable: v.rpcAvailable,
351
+ ...(txHash ? { txHash } : {}),
352
+ },
307
353
  error: `付款后重试失败 ${retry.status}: ${text.slice(0, 200)}`,
308
354
  };
309
355
  }
310
356
  mode = 'facilitator';
311
- // ★ 链上事实需要 txHash: facilitator 说成功但没有 txHash → 不能标 chainSettled (真跑抓到过这类"假结算")
312
- const txHash = parsed?.payment?.txHash || retry.headers.get('x-payment-txhash') || parsed?.txHash || '';
357
+ // ★ F5 修复: 拿到 txHash ≠ 链上已验证。真验证 = receipt(status==1) + 确认数 + 事件(taskKey/resultHash) + 合约 escrow 状态。
358
+ const verdict = await verifyPaymentOnChain({ txHash, chainSettlement: params.chainSettlement });
313
359
  await trackEvent({
314
360
  kind: 'settled',
315
- detail: `mode=facilitator receipt=${receipt.slice(0, 24)}… txHash=${txHash ? `${String(txHash).slice(0, 16)}…` : '(缺失: 不能认定链上结算完成)'}`,
316
- patch: { paymentMode: 'facilitator', paymentReceipt: receipt, chainSettled: !!txHash, ...(txHash ? { txHash: String(txHash) } : {}) },
361
+ detail: `mode=facilitator receipt=${receipt.slice(0, 24)}… txHash=${txHash ? `${String(txHash).slice(0, 16)}…` : '(缺失: 不能认定链上结算完成)'} 链上判定=${verdict.status}` +
362
+ (verdict.confirmations != null ? ` 确认数=${verdict.confirmations}/${verdict.confirmationsRequired}` : ''),
363
+ patch: {
364
+ paymentMode: 'facilitator', paymentReceipt: receipt,
365
+ chainSettled: verdict.chainSettled,
366
+ chainSettlementStatus: verdict.status,
367
+ chainSettlementReason: verdict.reason,
368
+ ...(verdict.confirmations != null ? { chainConfirmations: verdict.confirmations } : {}),
369
+ ...(verdict.rpcAvailable ? {} : { chainRpcAvailable: false }),
370
+ ...(txHash ? { txHash: String(txHash) } : {}),
371
+ },
317
372
  });
318
373
  const report = parsed?.proof ? await verifyEnvelope(parsed, { resolveDid: params.resolveDid, expectItemId: params.expectItemId }) : undefined;
319
374
  if (report)
320
375
  await trackEvent({ kind: 'delivered', detail: `trust=${report.trust}`, patch: { verificationTrust: report.trust, contentHash: parsed?.contentHash, protocolVerified: report.trust === 'verified' } });
321
- return { ok: true, status: retry.status, envelope: parsed, verify: report, payment: { mode, receipt }, metadata, raw: text };
376
+ return {
377
+ ok: true, status: retry.status, envelope: parsed, verify: report, metadata, raw: text,
378
+ payment: {
379
+ mode, receipt,
380
+ ...(txHash ? { txHash } : {}),
381
+ attempted: true, settled: true,
382
+ settlementUncertain: verdict.status === 'unknown' || verdict.status === 'config_unavailable' || !verdict.rpcAvailable,
383
+ chainSettled: verdict.chainSettled,
384
+ chainSettlementStatus: verdict.status,
385
+ chainSettlementReason: verdict.reason,
386
+ confirmations: verdict.confirmations,
387
+ confirmationsRequired: verdict.confirmationsRequired,
388
+ rpcAvailable: verdict.rpcAvailable,
389
+ },
390
+ };
322
391
  }
323
392
  if (!params.allowLocalDev) {
324
393
  return { ok: false, status: 402, error: '需要钱包私钥才能支付 (未提供 privateKey; 本机联调请显式 allowLocalDev)' };