@lawrenceliang-btc/atel-sdk 1.1.18 → 1.1.20

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 (2) hide show
  1. package/bin/atel.mjs +184 -107
  2. package/package.json +1 -1
package/bin/atel.mjs CHANGED
@@ -5271,108 +5271,68 @@ async function cmdResult(taskId, resultJson) {
5271
5271
 
5272
5272
  async function cmdCheck(targetDid, riskLevel, options) {
5273
5273
  const risk = riskLevel || 'low';
5274
- const chainMode = options?.chain || !!process.env.ATEL_SOLANA_RPC_URL;
5275
5274
  const policy = loadPolicy();
5276
5275
  const tp = policy.trustPolicy || DEFAULT_POLICY.trustPolicy;
5276
+ const RISK_THRESHOLDS = { low: 0, medium: 30, high: 50, critical: 75 };
5277
5277
 
5278
- console.log(JSON.stringify({ event: 'checking_trust', did: targetDid, risk, mode: chainMode ? 'chain-verified' : 'local-only' }));
5278
+ console.log(JSON.stringify({ event: 'checking_trust', did: targetDid, risk }));
5279
5279
 
5280
- // 1. Get Registry info (reference only, includes wallets)
5281
- let registryScore = null;
5282
- let agentName = null;
5283
- let peerWallets = null;
5280
+ // 1. Fetch agent profile from Platform (primary source of truth)
5281
+ let platformAgent = null;
5282
+ let platformScore = null;
5283
+ let platformReachable = true;
5284
5284
  try {
5285
- const r = await fetch(`${REGISTRY_URL}/registry/v1/agent/${encodeURIComponent(targetDid)}`, { signal: AbortSignal.timeout(5000) });
5285
+ const r = await fetch(`${ATEL_PLATFORM}/registry/v1/agent/${encodeURIComponent(targetDid)}`, { signal: AbortSignal.timeout(5000) });
5286
+ if (r.ok) {
5287
+ platformAgent = await r.json();
5288
+ platformScore = platformAgent.trustScore;
5289
+ }
5290
+ } catch {
5291
+ platformReachable = false;
5292
+ }
5293
+
5294
+ // 2. Fetch trust history from Platform
5295
+ let trustEvents = [];
5296
+ try {
5297
+ const r = await fetch(`${ATEL_PLATFORM}/registry/v1/agent/${encodeURIComponent(targetDid)}/trust-history`, { signal: AbortSignal.timeout(5000) });
5286
5298
  if (r.ok) {
5287
5299
  const d = await r.json();
5288
- registryScore = d.trustScore;
5289
- agentName = d.name;
5290
- if (d.wallets) peerWallets = d.wallets;
5300
+ trustEvents = d.events || [];
5291
5301
  }
5292
5302
  } catch {}
5293
5303
 
5294
- // 2. Local interaction history
5304
+ // 3. Local interaction history (secondary/fallback)
5295
5305
  const localHistoryFile = resolve(ATEL_DIR, 'trust-history.json');
5296
5306
  let history = {};
5297
5307
  try { history = JSON.parse(readFileSync(localHistoryFile, 'utf-8')); } catch {}
5298
5308
  const agentHistory = history[targetDid] || { tasks: 0, successes: 0, failures: 0, lastSeen: null, proofs: [] };
5309
+ const localScore = computeTrustScore(agentHistory);
5299
5310
 
5300
- // 3. Chain-verified mode: query all three chains by wallet address
5301
- let chainVerification = null;
5302
- if (chainMode) {
5303
- const chainResults = { solana: null, base: null, bsc: null, totalRecords: 0, matchingDid: 0 };
5304
-
5305
- // 3a. Verify unverified local proofs on-chain
5306
- const unverifiedProofs = agentHistory.proofs.filter(p => !p.verified && p.anchor_tx);
5307
- if (unverifiedProofs.length > 0) {
5308
- console.log(JSON.stringify({ event: 'verifying_local_proofs', count: unverifiedProofs.length }));
5309
- const result = await verifyAnchorTxList(unverifiedProofs, targetDid);
5310
- for (const vp of result.proofs) {
5311
- const existing = agentHistory.proofs.find(p => p.anchor_tx === vp.anchor_tx);
5312
- if (existing) existing.verified = true;
5313
- }
5314
- history[targetDid] = agentHistory;
5315
- try { writeFileSync(localHistoryFile, JSON.stringify(history, null, 2)); } catch {}
5316
- }
5317
-
5318
- // 3b. Query peer's wallet addresses on all three chains
5319
- if (peerWallets) {
5320
- console.log(JSON.stringify({ event: 'querying_chain_history', wallets: peerWallets }));
5321
-
5322
- // Solana
5323
- if (peerWallets.solana) {
5324
- try {
5325
- const rpcUrl = process.env.ATEL_SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com';
5326
- const provider = new SolanaAnchorProvider({ rpcUrl });
5327
- const records = await provider.queryByWallet(peerWallets.solana, { limit: 100, filterDid: targetDid });
5328
- chainResults.solana = { wallet: peerWallets.solana, records: records.length, asExecutor: records.filter(r => r.executorDid === targetDid).length, asRequester: records.filter(r => r.requesterDid === targetDid).length };
5329
- chainResults.totalRecords += records.length;
5330
- chainResults.matchingDid += records.length;
5331
- } catch (e) { chainResults.solana = { error: e.message }; }
5332
- }
5333
-
5334
- // Base
5335
- if (peerWallets.base) {
5336
- try {
5337
- const { BaseAnchorProvider } = await import('@lawrenceliang-btc/atel-sdk');
5338
- const baseRpc = process.env.ATEL_BASE_RPC_URL || 'https://mainnet.base.org';
5339
- const provider = new BaseAnchorProvider({ rpcUrl: baseRpc });
5340
- const explorerApi = process.env.ATEL_BASE_EXPLORER_API || 'https://api.basescan.org/api';
5341
- const apiKey = process.env.ATEL_BASE_EXPLORER_KEY;
5342
- const records = await provider.queryByWallet(peerWallets.base, explorerApi, apiKey, { limit: 100, filterDid: targetDid });
5343
- chainResults.base = { wallet: peerWallets.base, records: records.length, asExecutor: records.filter(r => r.executorDid === targetDid).length, asRequester: records.filter(r => r.requesterDid === targetDid).length };
5344
- chainResults.totalRecords += records.length;
5345
- chainResults.matchingDid += records.length;
5346
- } catch (e) { chainResults.base = { error: e.message }; }
5347
- }
5348
-
5349
- // BSC
5350
- if (peerWallets.bsc) {
5351
- try {
5352
- const { BSCAnchorProvider } = await import('@lawrenceliang-btc/atel-sdk');
5353
- const bscRpc = process.env.ATEL_BSC_RPC_URL || 'https://bsc-dataseed.binance.org';
5354
- const provider = new BSCAnchorProvider({ rpcUrl: bscRpc });
5355
- const explorerApi = process.env.ATEL_BSC_EXPLORER_API || 'https://api.bscscan.com/api';
5356
- const apiKey = process.env.ATEL_BSC_EXPLORER_KEY;
5357
- const records = await provider.queryByWallet(peerWallets.bsc, explorerApi, apiKey, { limit: 100, filterDid: targetDid });
5358
- chainResults.bsc = { wallet: peerWallets.bsc, records: records.length, asExecutor: records.filter(r => r.executorDid === targetDid).length, asRequester: records.filter(r => r.requesterDid === targetDid).length };
5359
- chainResults.totalRecords += records.length;
5360
- chainResults.matchingDid += records.length;
5361
- } catch (e) { chainResults.bsc = { error: e.message }; }
5311
+ // 4. Local TrustGraph info (supplementary)
5312
+ let graphInfo = null;
5313
+ try {
5314
+ const saved = JSON.parse(readFileSync(resolve(ATEL_DIR, 'trust-graph.json'), 'utf-8'));
5315
+ if (saved.interactions) {
5316
+ const tg = new TrustGraph();
5317
+ for (const i of saved.interactions) tg.recordInteraction(i);
5318
+ const directConns = saved.interactions.filter(
5319
+ i => i.from === targetDid || i.to === targetDid
5320
+ );
5321
+ if (directConns.length > 0) {
5322
+ const stats = tg.getStats();
5323
+ graphInfo = { directConnections: directConns.length, stats };
5362
5324
  }
5363
5325
  }
5326
+ } catch {}
5364
5327
 
5365
- chainVerification = chainResults;
5366
- }
5367
-
5368
- // 4. Compute unified trust score and level
5369
- const computedScore = computeTrustScore(agentHistory);
5370
- const trustLevel = computeTrustLevel(computedScore);
5328
+ // 5. Determine effective score: Platform is primary, local is fallback
5329
+ const effectiveScore = platformScore !== null ? platformScore : localScore;
5330
+ const scoreSource = platformScore !== null ? 'platform' : 'local';
5331
+ const trustLevel = computeTrustLevel(effectiveScore);
5371
5332
 
5372
- // 5. Apply trust policy
5373
- const threshold = tp.riskThresholds?.[risk] ?? 0;
5374
- const effectiveScore = computedScore > 0 ? computedScore : (registryScore || 0);
5375
- const isNewAgent = agentHistory.tasks === 0;
5333
+ // 6. Apply trust policy (allow/deny decision)
5334
+ const threshold = RISK_THRESHOLDS[risk] ?? (tp.riskThresholds?.[risk] ?? 0);
5335
+ const isNewAgent = platformAgent === null && agentHistory.tasks === 0;
5376
5336
  let decision = 'allow';
5377
5337
  let reason = '';
5378
5338
 
@@ -5391,28 +5351,82 @@ async function cmdCheck(targetDid, riskLevel, options) {
5391
5351
  reason = `Score ${effectiveScore} meets threshold ${threshold} for ${risk} risk`;
5392
5352
  }
5393
5353
 
5354
+ // 7. Build human-readable output
5355
+ const lines = [];
5356
+ lines.push(`Trust Check: ${targetDid}`);
5357
+ if (platformAgent?.name) lines.push(` Name: ${platformAgent.name}`);
5358
+ lines.push(` Platform Score: ${effectiveScore} / 100${scoreSource === 'local' ? ' (fallback: local)' : ''}`);
5359
+ lines.push(` Decision: ${decision} (threshold: ${threshold} for ${risk} risk)`);
5360
+ if (reason) lines.push(` Reason: ${reason}`);
5361
+
5362
+ // Recent events from Platform
5363
+ if (trustEvents.length > 0) {
5364
+ lines.push(` Recent Events:`);
5365
+ for (const ev of trustEvents.slice(0, 10)) {
5366
+ const delta = ev.scoreDelta >= 0 ? `+${ev.scoreDelta}` : `${ev.scoreDelta}`;
5367
+ const date = ev.createdAt ? ev.createdAt.split('T')[0] : '';
5368
+ const ref = ev.referenceId || '';
5369
+ lines.push(` ${delta.padEnd(8)} ${ev.eventType.padEnd(22)} ${ref.padEnd(16)} ${date}`);
5370
+ }
5371
+ if (trustEvents.length > 10) lines.push(` ... and ${trustEvents.length - 10} more`);
5372
+ }
5373
+
5374
+ // Stats from Platform
5375
+ if (platformAgent?.stats) {
5376
+ const s = platformAgent.stats;
5377
+ const successPct = s.successRate != null ? `${Math.round(s.successRate * 100)}%` : 'n/a';
5378
+ lines.push(` Stats: ${s.totalTasks} tasks, ${successPct} success${s.avgRating ? `, avg rating: ${s.avgRating.toFixed(1)}` : ''}`);
5379
+ } else if (agentHistory.tasks > 0) {
5380
+ const successPct = agentHistory.tasks > 0 ? `${Math.round((agentHistory.successes / agentHistory.tasks) * 100)}%` : 'n/a';
5381
+ lines.push(` Stats: ${agentHistory.tasks} tasks, ${successPct} success (local)`);
5382
+ }
5383
+
5384
+ // Verified & Certification from Platform
5385
+ if (platformAgent) {
5386
+ lines.push(` Verified: ${platformAgent.verified || false}`);
5387
+ if (platformAgent.certification) {
5388
+ lines.push(` Certification: ${platformAgent.certification.level} (since ${platformAgent.certification.since})`);
5389
+ }
5390
+ if (platformAgent.boost?.active) {
5391
+ lines.push(` Boost: ${platformAgent.boost.tier} (active)`);
5392
+ }
5393
+ }
5394
+
5395
+ // Local TrustGraph supplementary info
5396
+ if (graphInfo) {
5397
+ lines.push(` Local Graph: ${graphInfo.directConnections} direct connection${graphInfo.directConnections !== 1 ? 's' : ''}`);
5398
+ }
5399
+
5400
+ // Fallback warning
5401
+ if (!platformReachable) {
5402
+ lines.push(` Warning: Platform unreachable, using local score as fallback`);
5403
+ }
5404
+
5405
+ // Print human-readable output
5406
+ console.log(lines.join('\n'));
5407
+
5408
+ // Also emit structured JSON for machine consumption
5394
5409
  const output = {
5395
5410
  did: targetDid,
5396
- name: agentName,
5397
- mode: chainMode ? 'chain-verified' : 'local-only',
5398
- trust: {
5399
- computed_score: computedScore,
5400
- registry_score: registryScore,
5401
- effective_score: effectiveScore,
5402
- level: trustLevel.level,
5403
- level_name: trustLevel.name,
5404
- max_risk: trustLevel.maxRisk,
5405
- total_tasks: agentHistory.tasks,
5406
- successes: agentHistory.successes,
5407
- failures: agentHistory.failures,
5408
- verified_proofs: agentHistory.proofs.filter(p => p.verified).length,
5409
- total_proofs: agentHistory.proofs.length,
5410
- },
5411
- policy: { risk, threshold, decision, reason },
5411
+ name: platformAgent?.name || null,
5412
+ score: effectiveScore,
5413
+ scoreSource,
5414
+ level: trustLevel.level,
5415
+ levelName: trustLevel.name,
5416
+ decision,
5417
+ risk,
5418
+ threshold,
5419
+ reason,
5420
+ verified: platformAgent?.verified || false,
5421
+ certification: platformAgent?.certification || null,
5422
+ stats: platformAgent?.stats || null,
5423
+ recentEvents: trustEvents.slice(0, 10),
5424
+ localGraph: graphInfo,
5425
+ platformReachable,
5412
5426
  };
5413
- if (chainVerification) output.chain_verification = chainVerification;
5414
- if (!chainMode) output.note = 'Local-only mode: score based on direct interaction history only. Set ATEL_SOLANA_RPC_URL or use --chain for on-chain verification.';
5415
-
5427
+ if (localScore > 0 && scoreSource === 'platform') {
5428
+ output.localScore = localScore;
5429
+ }
5416
5430
  console.log(JSON.stringify(output, null, 2));
5417
5431
  }
5418
5432
 
@@ -6345,8 +6359,8 @@ async function cmdMilestoneFeedback(orderId) {
6345
6359
 
6346
6360
  async function cmdMilestoneSubmit(orderId, indexStr) {
6347
6361
  if (!orderId || indexStr === undefined) {
6348
- console.error('Usage: atel milestone-submit <orderId> <index> --result "结果描述"');
6349
- console.error(' atel milestone-submit <orderId> <index> --result ./file.pdf [--hash 0x...]');
6362
+ console.error('Usage: atel milestone-submit <orderId> <index> --result "结果描述" [--file <path>]');
6363
+ console.error(' atel milestone-submit <orderId> <index> --result ./file.pdf [--hash 0x...] [--file <path>]');
6350
6364
  process.exit(1);
6351
6365
  }
6352
6366
  const resultIdx = rawArgs.findIndex(a => a === '--result');
@@ -6380,11 +6394,35 @@ async function cmdMilestoneSubmit(orderId, indexStr) {
6380
6394
  resultHash = ethers.keccak256(ethers.toUtf8Bytes(result));
6381
6395
  }
6382
6396
 
6383
- console.log(`Submitting M${indexStr}: "${result.slice(0, 60)}${result.length > 60 ? '...' : ''}"`);
6397
+ // --file support: upload attachment before submitting
6398
+ let resultText = result;
6399
+ const filePath = rawArgs.find((a, i) => rawArgs[i - 1] === '--file');
6400
+ if (filePath) {
6401
+ const { execFileSync } = await import('child_process');
6402
+ const path = await import('path');
6403
+ try {
6404
+ const id = requireIdentity();
6405
+ const uploadResult = execFileSync('curl', ['-s', '-X', 'POST',
6406
+ `${PLATFORM_URL}/attachment/v1/upload`,
6407
+ '-F', `file=@${filePath}`,
6408
+ '-F', 'kind=file',
6409
+ '-F', `uploadedBy=${id.did}`
6410
+ ], { encoding: 'utf8' });
6411
+ const uploadData = JSON.parse(uploadResult);
6412
+ if (uploadData.attachmentId) {
6413
+ resultText += `\n[Attachment: ${uploadData.attachmentId} (${path.basename(filePath)})]`;
6414
+ console.error(`File uploaded: ${uploadData.attachmentId}`);
6415
+ }
6416
+ } catch (e) {
6417
+ console.error(`File upload failed: ${e.message}`);
6418
+ }
6419
+ }
6420
+
6421
+ console.log(`Submitting M${indexStr}: "${resultText.slice(0, 60)}${resultText.length > 60 ? '...' : ''}"`);
6384
6422
  console.log(`Hash: ${resultHash}`);
6385
6423
 
6386
6424
  const data = await signedFetch('POST', `/trade/v1/order/${orderId}/milestone/${indexStr}/submit`, {
6387
- resultSummary: result,
6425
+ resultSummary: resultText,
6388
6426
  resultHash,
6389
6427
  });
6390
6428
  console.log(JSON.stringify(data, null, 2));
@@ -6475,10 +6513,34 @@ async function cmdDispute(orderId, reason, description) {
6475
6513
  }
6476
6514
 
6477
6515
  async function cmdEvidence(disputeId, evidenceJson) {
6478
- if (!disputeId || !evidenceJson) { console.error('Usage: atel evidence <disputeId> <json>'); process.exit(1); }
6516
+ if (!disputeId || !evidenceJson) { console.error('Usage: atel evidence <disputeId> <json> [--file <path>]'); process.exit(1); }
6479
6517
  let evidence;
6480
6518
  try { evidence = JSON.parse(evidenceJson); } catch { console.error('Invalid JSON'); process.exit(1); }
6481
6519
 
6520
+ // --file support: upload attachment and attach to evidence
6521
+ const filePath = rawArgs.find((a, i) => rawArgs[i - 1] === '--file');
6522
+ if (filePath) {
6523
+ const { execFileSync } = await import('child_process');
6524
+ const path = await import('path');
6525
+ try {
6526
+ const id = requireIdentity();
6527
+ const uploadResult = execFileSync('curl', ['-s', '-X', 'POST',
6528
+ `${PLATFORM_URL}/attachment/v1/upload`,
6529
+ '-F', `file=@${filePath}`,
6530
+ '-F', 'kind=file',
6531
+ '-F', `uploadedBy=${id.did}`
6532
+ ], { encoding: 'utf8' });
6533
+ const uploadData = JSON.parse(uploadResult);
6534
+ if (uploadData.attachmentId) {
6535
+ evidence.attachmentId = uploadData.attachmentId;
6536
+ evidence.attachmentName = path.basename(filePath);
6537
+ console.error(`File uploaded: ${uploadData.attachmentId}`);
6538
+ }
6539
+ } catch (e) {
6540
+ console.error(`File upload failed: ${e.message}`);
6541
+ }
6542
+ }
6543
+
6482
6544
  // Submit to Platform API
6483
6545
  const data = await signedFetch('POST', `/dispute/v1/${disputeId}/evidence`, { evidence });
6484
6546
 
@@ -6534,6 +6596,21 @@ async function cmdCertStatus(did) {
6534
6596
  const text = await res.text(); console.error("DEBUG Response:", text); const data = JSON.parse(text);
6535
6597
  if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
6536
6598
  console.log(JSON.stringify(data, null, 2));
6599
+
6600
+ // Fetch and display certification requirements
6601
+ try {
6602
+ const reqResp = await fetch(`${PLATFORM_URL}/cert/v1/requirements`, { signal: AbortSignal.timeout(5000) });
6603
+ if (reqResp.ok) {
6604
+ const reqs = await reqResp.json();
6605
+ console.log('\nCertification Requirements:');
6606
+ if (reqs.certified) {
6607
+ console.log(` Certified: trust_score >= ${reqs.certified.minTrustScore}, cost: $${reqs.certified.cost}`);
6608
+ }
6609
+ if (reqs.enterprise) {
6610
+ console.log(` Enterprise: trust_score >= ${reqs.enterprise.minTrustScore}, cost: $${reqs.enterprise.cost}`);
6611
+ }
6612
+ }
6613
+ } catch {}
6537
6614
  }
6538
6615
 
6539
6616
  async function cmdCertRenew(level) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawrenceliang-btc/atel-sdk",
3
- "version": "1.1.18",
3
+ "version": "1.1.20",
4
4
  "description": "ATEL Protocol SDK - Agent Trust & Exchange Layer",
5
5
  "repository": {
6
6
  "type": "git",