arc402-cli 0.3.4 → 0.4.1

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 (145) hide show
  1. package/dist/commands/accept.d.ts.map +1 -1
  2. package/dist/commands/accept.js +17 -7
  3. package/dist/commands/accept.js.map +1 -1
  4. package/dist/commands/agent-handshake.d.ts.map +1 -1
  5. package/dist/commands/agent-handshake.js +9 -4
  6. package/dist/commands/agent-handshake.js.map +1 -1
  7. package/dist/commands/agent.d.ts.map +1 -1
  8. package/dist/commands/agent.js +14 -8
  9. package/dist/commands/agent.js.map +1 -1
  10. package/dist/commands/agreements.d.ts.map +1 -1
  11. package/dist/commands/agreements.js +51 -26
  12. package/dist/commands/agreements.js.map +1 -1
  13. package/dist/commands/arbitrator.d.ts.map +1 -1
  14. package/dist/commands/arbitrator.js +77 -20
  15. package/dist/commands/arbitrator.js.map +1 -1
  16. package/dist/commands/arena-handshake.d.ts.map +1 -1
  17. package/dist/commands/arena-handshake.js +14 -11
  18. package/dist/commands/arena-handshake.js.map +1 -1
  19. package/dist/commands/arena.d.ts.map +1 -1
  20. package/dist/commands/arena.js +4 -3
  21. package/dist/commands/arena.js.map +1 -1
  22. package/dist/commands/cancel.d.ts.map +1 -1
  23. package/dist/commands/cancel.js +20 -10
  24. package/dist/commands/cancel.js.map +1 -1
  25. package/dist/commands/channel.d.ts.map +1 -1
  26. package/dist/commands/channel.js +27 -17
  27. package/dist/commands/channel.js.map +1 -1
  28. package/dist/commands/coldstart.d.ts.map +1 -1
  29. package/dist/commands/coldstart.js +33 -22
  30. package/dist/commands/coldstart.js.map +1 -1
  31. package/dist/commands/config.d.ts.map +1 -1
  32. package/dist/commands/config.js +34 -17
  33. package/dist/commands/config.js.map +1 -1
  34. package/dist/commands/daemon.d.ts.map +1 -1
  35. package/dist/commands/daemon.js +44 -37
  36. package/dist/commands/daemon.js.map +1 -1
  37. package/dist/commands/deliver.d.ts.map +1 -1
  38. package/dist/commands/deliver.js +16 -3
  39. package/dist/commands/deliver.js.map +1 -1
  40. package/dist/commands/discover.d.ts.map +1 -1
  41. package/dist/commands/discover.js +2 -0
  42. package/dist/commands/discover.js.map +1 -1
  43. package/dist/commands/dispute.d.ts.map +1 -1
  44. package/dist/commands/dispute.js +11 -10
  45. package/dist/commands/dispute.js.map +1 -1
  46. package/dist/commands/endpoint.d.ts.map +1 -1
  47. package/dist/commands/endpoint.js +4 -3
  48. package/dist/commands/endpoint.js.map +1 -1
  49. package/dist/commands/feed.d.ts.map +1 -1
  50. package/dist/commands/feed.js.map +1 -1
  51. package/dist/commands/hire.d.ts.map +1 -1
  52. package/dist/commands/hire.js +15 -2
  53. package/dist/commands/hire.js.map +1 -1
  54. package/dist/commands/migrate.d.ts.map +1 -1
  55. package/dist/commands/migrate.js +29 -29
  56. package/dist/commands/migrate.js.map +1 -1
  57. package/dist/commands/negotiate.d.ts.map +1 -1
  58. package/dist/commands/negotiate.js +8 -7
  59. package/dist/commands/negotiate.js.map +1 -1
  60. package/dist/commands/openshell.d.ts.map +1 -1
  61. package/dist/commands/openshell.js +6 -5
  62. package/dist/commands/openshell.js.map +1 -1
  63. package/dist/commands/owner.d.ts.map +1 -1
  64. package/dist/commands/owner.js +7 -2
  65. package/dist/commands/owner.js.map +1 -1
  66. package/dist/commands/policy.d.ts.map +1 -1
  67. package/dist/commands/policy.js +22 -10
  68. package/dist/commands/policy.js.map +1 -1
  69. package/dist/commands/relay.d.ts.map +1 -1
  70. package/dist/commands/relay.js +6 -5
  71. package/dist/commands/relay.js.map +1 -1
  72. package/dist/commands/remediate.d.ts.map +1 -1
  73. package/dist/commands/remediate.js +3 -2
  74. package/dist/commands/remediate.js.map +1 -1
  75. package/dist/commands/reputation.d.ts.map +1 -1
  76. package/dist/commands/reputation.js +12 -5
  77. package/dist/commands/reputation.js.map +1 -1
  78. package/dist/commands/trust.d.ts.map +1 -1
  79. package/dist/commands/trust.js +16 -1
  80. package/dist/commands/trust.js.map +1 -1
  81. package/dist/commands/verify.d.ts.map +1 -1
  82. package/dist/commands/verify.js +6 -4
  83. package/dist/commands/verify.js.map +1 -1
  84. package/dist/commands/wallet.d.ts.map +1 -1
  85. package/dist/commands/wallet.js +217 -170
  86. package/dist/commands/wallet.js.map +1 -1
  87. package/dist/commands/watch.d.ts +3 -0
  88. package/dist/commands/watch.d.ts.map +1 -0
  89. package/dist/commands/watch.js +23 -0
  90. package/dist/commands/watch.js.map +1 -0
  91. package/dist/commands/watchtower.d.ts.map +1 -1
  92. package/dist/commands/watchtower.js +30 -13
  93. package/dist/commands/watchtower.js.map +1 -1
  94. package/dist/commands/workroom.d.ts.map +1 -1
  95. package/dist/commands/workroom.js +123 -95
  96. package/dist/commands/workroom.js.map +1 -1
  97. package/dist/config.d.ts.map +1 -1
  98. package/dist/config.js +16 -2
  99. package/dist/config.js.map +1 -1
  100. package/dist/index.js +73 -38
  101. package/dist/index.js.map +1 -1
  102. package/dist/ui/banner.d.ts.map +1 -1
  103. package/dist/ui/banner.js +4 -2
  104. package/dist/ui/banner.js.map +1 -1
  105. package/dist/ui/tree.d.ts +7 -0
  106. package/dist/ui/tree.d.ts.map +1 -0
  107. package/dist/ui/tree.js +13 -0
  108. package/dist/ui/tree.js.map +1 -0
  109. package/package.json +1 -1
  110. package/src/commands/accept.ts +19 -10
  111. package/src/commands/agent-handshake.ts +9 -4
  112. package/src/commands/agent.ts +15 -6
  113. package/src/commands/agreements.ts +51 -25
  114. package/src/commands/arbitrator.ts +71 -20
  115. package/src/commands/arena-handshake.ts +15 -8
  116. package/src/commands/arena.ts +4 -3
  117. package/src/commands/cancel.ts +19 -10
  118. package/src/commands/channel.ts +27 -17
  119. package/src/commands/coldstart.ts +29 -20
  120. package/src/commands/config.ts +34 -17
  121. package/src/commands/daemon.ts +45 -38
  122. package/src/commands/deliver.ts +16 -3
  123. package/src/commands/discover.ts +2 -0
  124. package/src/commands/dispute.ts +12 -10
  125. package/src/commands/endpoint.ts +4 -3
  126. package/src/commands/feed.ts +1 -0
  127. package/src/commands/hire.ts +17 -2
  128. package/src/commands/migrate.ts +28 -26
  129. package/src/commands/negotiate.ts +8 -7
  130. package/src/commands/openshell.ts +7 -5
  131. package/src/commands/owner.ts +7 -2
  132. package/src/commands/policy.ts +21 -10
  133. package/src/commands/relay.ts +6 -5
  134. package/src/commands/remediate.ts +4 -2
  135. package/src/commands/reputation.ts +13 -5
  136. package/src/commands/trust.ts +13 -1
  137. package/src/commands/verify.ts +7 -4
  138. package/src/commands/wallet.ts +218 -170
  139. package/src/commands/watch.ts +23 -0
  140. package/src/commands/watchtower.ts +29 -13
  141. package/src/commands/workroom.ts +121 -95
  142. package/src/config.ts +16 -2
  143. package/src/index.ts +43 -3
  144. package/src/ui/banner.ts +5 -2
  145. package/src/ui/tree.ts +16 -0
@@ -7,6 +7,9 @@ import { getClient } from "../client";
7
7
  import { agreementStatusLabel, formatDate, printTable, truncateAddress } from "../utils/format";
8
8
  import { formatDeadline } from "../utils/time";
9
9
  import { hashString } from "../utils/hash";
10
+ import { c } from "../ui/colors";
11
+ import { renderTree } from "../ui/tree";
12
+ import { formatAddress } from "../ui/format";
10
13
 
11
14
  // ─── AgreementTree minimal ABI ────────────────────────────────────────────────
12
15
 
@@ -61,11 +64,23 @@ export function registerAgreementsCommands(program: Command): void {
61
64
  const client = new ServiceAgreementClient(config.serviceAgreementAddress, provider);
62
65
  const agreement = await client.getAgreement(BigInt(id));
63
66
  if (opts.json) return console.log(JSON.stringify(agreement, (_k, value) => typeof value === "bigint" ? value.toString() : value, 2));
64
- console.log(`agreement #${agreement.id}\nclient=${agreement.client}\nprovider=${agreement.provider}\nstatus=${agreementStatusLabel(agreement.status)}\ncreated=${formatDate(Number(agreement.createdAt))}\ndeadline=${formatDate(Number(agreement.deadline))}\nverifyWindowEnd=${Number(agreement.verifyWindowEnd) ? formatDate(Number(agreement.verifyWindowEnd)) : "n/a"}\ncommittedHash=${agreement.committedHash}`);
67
+ console.log('\n ' + c.mark + c.white(` Agreement #${agreement.id}`));
68
+ renderTree([
69
+ { label: 'Client', value: formatAddress(agreement.client) },
70
+ { label: 'Provider', value: formatAddress(agreement.provider) },
71
+ { label: 'Status', value: agreementStatusLabel(agreement.status) },
72
+ { label: 'Created', value: formatDate(Number(agreement.createdAt)) },
73
+ { label: 'Deadline', value: formatDate(Number(agreement.deadline)) },
74
+ { label: 'Verify end', value: Number(agreement.verifyWindowEnd) ? formatDate(Number(agreement.verifyWindowEnd)) : 'n/a' },
75
+ { label: 'Hash', value: String(agreement.committedHash), last: true },
76
+ ]);
65
77
  if ([AgreementStatus.REVISION_REQUESTED, AgreementStatus.REVISED, AgreementStatus.PARTIAL_SETTLEMENT, AgreementStatus.ESCALATED_TO_HUMAN, AgreementStatus.DISPUTED, AgreementStatus.ESCALATED_TO_ARBITRATION].includes(agreement.status)) {
66
78
  const remediation = await client.getRemediationCase(agreement.id);
67
79
  const dispute = await client.getDisputeCase(agreement.id);
68
- console.log(`remediationActive=${remediation.active} cycles=${remediation.cycleCount} disputeOutcome=${dispute.outcome}`);
80
+ renderTree([
81
+ { label: 'Remediation', value: `active=${remediation.active} cycles=${remediation.cycleCount}` },
82
+ { label: 'Dispute', value: `outcome=${dispute.outcome}`, last: true },
83
+ ]);
69
84
  }
70
85
  });
71
86
 
@@ -90,9 +105,12 @@ export function registerAgreementsCommands(program: Command): void {
90
105
  if (opts.json) {
91
106
  console.log(JSON.stringify({ txHash: receipt.hash, parentId: opts.parent, childId: opts.child }));
92
107
  } else {
93
- console.log(`Sub-agreement registered.`);
94
- console.log(` Parent: ${opts.parent} Child: ${opts.child}`);
95
- console.log(` tx: ${receipt.hash}`);
108
+ console.log('\n ' + c.mark + c.white(' Sub-agreement registered'));
109
+ renderTree([
110
+ { label: 'Parent', value: `#${opts.parent}` },
111
+ { label: 'Child', value: `#${opts.child}` },
112
+ { label: 'Tx', value: receipt.hash, last: true },
113
+ ]);
96
114
  }
97
115
  });
98
116
 
@@ -130,11 +148,13 @@ export function registerAgreementsCommands(program: Command): void {
130
148
  return;
131
149
  }
132
150
 
133
- console.log(`Agreement Tree — node #${agreementId}`);
134
- console.log(` Root: #${result.root}`);
135
- console.log(` Depth: ${result.depth}`);
136
- console.log(` Path: ${result.path.map((p) => "#" + p).join(" → ")}`);
137
- console.log(` Children: ${result.children.length > 0 ? result.children.map((c) => "#" + c).join(", ") : "(none)"}`);
151
+ console.log('\n ' + c.mark + c.white(` Agreement Tree — node #${agreementId}`));
152
+ renderTree([
153
+ { label: 'Root', value: `#${result.root}` },
154
+ { label: 'Depth', value: String(result.depth) },
155
+ { label: 'Path', value: result.path.map((p) => "#" + p).join(" ") },
156
+ { label: 'Children', value: result.children.length > 0 ? result.children.map((ch) => "#" + ch).join(", ") : "(none)", last: true },
157
+ ]);
138
158
  });
139
159
 
140
160
  // ── arc402 agreements-tree-status <id> ───────────────────────────────────────
@@ -168,10 +188,12 @@ export function registerAgreementsCommands(program: Command): void {
168
188
  return;
169
189
  }
170
190
 
171
- console.log(`Agreement #${agreementId} tree status:`);
172
- console.log(` Sub-agreements: ${result.childCount}`);
173
- console.log(` All settled: ${result.allChildrenSettled ? "YES" : "NO"}`);
174
- console.log(` Ready to deliver to parent: ${result.readyToDeliver ? "YES" : "NO"}`);
191
+ console.log('\n ' + c.mark + c.white(` Agreement #${agreementId} tree status`));
192
+ renderTree([
193
+ { label: 'Sub-agrmts', value: String(result.childCount) },
194
+ { label: 'All settled', value: result.allChildrenSettled ? 'YES' : 'NO' },
195
+ { label: 'Ready', value: result.readyToDeliver ? 'YES — ready to deliver to parent' : 'NO', last: true },
196
+ ]);
175
197
  });
176
198
 
177
199
  // ── arc402 agreements-create-tree ────────────────────────────────────────────
@@ -255,7 +277,7 @@ export function registerAgreementsCommands(program: Command): void {
255
277
  const rootDeadline = Number(opts.deadline);
256
278
  const rootHash = hashString(opts.task);
257
279
 
258
- if (!opts.json) console.log(`\nProposing root agreement to ${opts.provider}...`);
280
+ if (!opts.json) console.log('\n ' + c.mark + c.white(` Proposing root agreement to ${opts.provider}...`));
259
281
  const rootTx = await saContract.propose(
260
282
  opts.provider, opts.serviceType, opts.task,
261
283
  rootPrice, ethers.ZeroAddress, rootDeadline, rootHash,
@@ -274,11 +296,11 @@ export function registerAgreementsCommands(program: Command): void {
274
296
  }
275
297
  if (rootAgreementId === undefined) throw new Error("Could not parse root AgreementProposed event");
276
298
 
277
- if (!opts.json) console.log(` Root agreement ID: ${rootAgreementId}`);
299
+ if (!opts.json) console.log(' ' + c.success + c.white(` Root agreement ID: #${rootAgreementId}`));
278
300
 
279
301
  const childAgreementIds: bigint[] = [];
280
302
  for (const sub of subSpecs) {
281
- if (!opts.json) console.log(` Proposing sub-agreement to ${sub.provider}...`);
303
+ if (!opts.json) console.log(' ' + c.dim(` Proposing sub-agreement to ${sub.provider}...`));
282
304
  const subHash = hashString(sub.task);
283
305
  const subTx = await saContract.propose(
284
306
  sub.provider, sub.serviceType, sub.task,
@@ -294,7 +316,7 @@ export function registerAgreementsCommands(program: Command): void {
294
316
  } catch { /* skip */ }
295
317
  }
296
318
  if (childId === undefined) throw new Error(`Could not parse AgreementProposed for sub-agreement to ${sub.provider}`);
297
- if (!opts.json) console.log(` Sub-agreement ID: ${childId} — registering in tree...`);
319
+ if (!opts.json) console.log(' ' + c.success + c.white(` Sub-agreement #${childId} — registering in tree...`));
298
320
  await (await treeContract.registerSubAgreement(rootAgreementId, childId)).wait();
299
321
  childAgreementIds.push(childId);
300
322
  }
@@ -308,13 +330,17 @@ export function registerAgreementsCommands(program: Command): void {
308
330
  if (opts.json) {
309
331
  console.log(JSON.stringify(result, null, 2));
310
332
  } else {
311
- console.log(`\nAgreement tree created.`);
312
- console.log(` Root: #${result.rootAgreementId}`);
313
- if (result.childAgreementIds.length > 0) {
314
- console.log(` Children: ${result.childAgreementIds.map((id) => "#" + id).join(", ")}`);
315
- } else {
316
- console.log(` Children: (none sub-agreements can be added with agreements-sub-register)`);
317
- }
333
+ console.log('\n ' + c.mark + c.white(' Agreement tree created'));
334
+ renderTree([
335
+ { label: 'Root', value: `#${result.rootAgreementId}` },
336
+ {
337
+ label: 'Children',
338
+ value: result.childAgreementIds.length > 0
339
+ ? result.childAgreementIds.map((id) => "#" + id).join(", ")
340
+ : "(none — sub-agreements can be added with agreements-sub-register)",
341
+ last: true,
342
+ },
343
+ ]);
318
344
  }
319
345
  });
320
346
 
@@ -2,6 +2,9 @@ import { Command } from "commander";
2
2
  import { DisputeArbitrationClient } from "@arc402/sdk";
3
3
  import { loadConfig } from "../config";
4
4
  import { getClient, requireSigner } from "../client";
5
+ import { c } from "../ui/colors";
6
+ import { startSpinner } from "../ui/spinner";
7
+ import { renderTree } from "../ui/tree";
5
8
 
6
9
  export function registerArbitratorCommand(program: Command): void {
7
10
  const arbitrator = program
@@ -23,16 +26,22 @@ export function registerArbitratorCommand(program: Command): void {
23
26
  const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, provider);
24
27
 
25
28
  const eligible = await client.isEligibleArbitrator(arbitratorAddress);
26
- console.log(`Arbitrator ${arbitratorAddress} eligible: ${eligible}`);
29
+ console.log('\n ' + c.mark + c.white(' Arbitrator status'));
30
+ renderTree([
31
+ { label: 'Address', value: arbitratorAddress },
32
+ { label: 'Eligible', value: eligible ? 'yes' : 'no', last: !agreementId },
33
+ ]);
27
34
 
28
35
  if (agreementId) {
29
36
  const bondState = await client.getArbitratorBondState(arbitratorAddress, BigInt(agreementId));
30
- console.log(`Bond state for agreement ${agreementId}:`);
31
- console.log(` Amount: ${bondState.bondAmount.toString()} tokens`);
32
- console.log(` Locked: ${bondState.locked}`);
33
- console.log(` Slashed: ${bondState.slashed}`);
34
- console.log(` Returned: ${bondState.returned}`);
35
- console.log(` Locked at: ${new Date(Number(bondState.lockedAt) * 1000).toISOString()}`);
37
+ console.log('\n ' + c.mark + c.white(` Bond state agreement #${agreementId}`));
38
+ renderTree([
39
+ { label: 'Amount', value: `${bondState.bondAmount.toString()} tokens` },
40
+ { label: 'Locked', value: String(bondState.locked) },
41
+ { label: 'Slashed', value: String(bondState.slashed) },
42
+ { label: 'Returned', value: String(bondState.returned) },
43
+ { label: 'Locked at', value: new Date(Number(bondState.lockedAt) * 1000).toISOString(), last: true },
44
+ ]);
36
45
  }
37
46
  });
38
47
 
@@ -48,11 +57,18 @@ export function registerArbitratorCommand(program: Command): void {
48
57
 
49
58
  const bondAmount = BigInt(opts.bond);
50
59
  if (bondAmount === 0n) {
51
- console.warn("Warning: bond amount is 0. For ERC-20 agreements, pre-approve the DisputeArbitration contract.");
60
+ console.warn(' ' + c.warning + c.white(' Bond amount is 0. For ERC-20 agreements, pre-approve the DisputeArbitration contract.'));
52
61
  }
53
62
 
54
- await client.acceptAssignment(BigInt(agreementId), bondAmount);
55
- console.log(`accepted assignment for agreement ${agreementId}`);
63
+ const spinner = startSpinner("Submitting transaction...");
64
+ try {
65
+ await client.acceptAssignment(BigInt(agreementId), bondAmount);
66
+ spinner.succeed();
67
+ } catch (err) {
68
+ spinner.fail();
69
+ throw err;
70
+ }
71
+ console.log(' ' + c.success + c.white(` Assignment accepted — agreement #${agreementId}`));
56
72
  });
57
73
 
58
74
  bond
@@ -63,8 +79,15 @@ export function registerArbitratorCommand(program: Command): void {
63
79
  if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
64
80
  const { signer } = await requireSigner(config);
65
81
  const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
66
- await client.triggerFallback(BigInt(agreementId));
67
- console.log(`fallback triggered for ${agreementId}`);
82
+ const spinner = startSpinner("Submitting transaction...");
83
+ try {
84
+ await client.triggerFallback(BigInt(agreementId));
85
+ spinner.succeed();
86
+ } catch (err) {
87
+ spinner.fail();
88
+ throw err;
89
+ }
90
+ console.log(' ' + c.success + c.white(` Fallback triggered — agreement #${agreementId}`));
68
91
  });
69
92
 
70
93
  // Rate subcommands — nest under 'rate'
@@ -82,8 +105,15 @@ export function registerArbitratorCommand(program: Command): void {
82
105
  const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
83
106
 
84
107
  const rateVal = BigInt(usdPerToken);
85
- await client.setTokenUsdRate(tokenAddress, rateVal);
86
- console.log(`token rate set: ${tokenAddress} = ${rateVal.toString()} USD/token`);
108
+ const spinner = startSpinner("Submitting transaction...");
109
+ try {
110
+ await client.setTokenUsdRate(tokenAddress, rateVal);
111
+ spinner.succeed();
112
+ } catch (err) {
113
+ spinner.fail();
114
+ throw err;
115
+ }
116
+ console.log(' ' + c.success + c.white(` Token rate set: ${tokenAddress} = ${rateVal.toString()} USD/token`));
87
117
  });
88
118
 
89
119
  // Admin: slash arbitrator (manual rules violation)
@@ -95,8 +125,15 @@ export function registerArbitratorCommand(program: Command): void {
95
125
  if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
96
126
  const { signer } = await requireSigner(config);
97
127
  const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
98
- await client.slashArbitrator(BigInt(agreementId), arbitratorAddress, reason);
99
- console.log(`slashed arbitrator ${arbitratorAddress} for ${reason}`);
128
+ const spinner = startSpinner("Submitting transaction...");
129
+ try {
130
+ await client.slashArbitrator(BigInt(agreementId), arbitratorAddress, reason);
131
+ spinner.succeed();
132
+ } catch (err) {
133
+ spinner.fail();
134
+ throw err;
135
+ }
136
+ console.log(' ' + c.success + c.white(` Slashed ${arbitratorAddress}`));
100
137
  });
101
138
 
102
139
  arbitrator
@@ -107,8 +144,15 @@ export function registerArbitratorCommand(program: Command): void {
107
144
  if (!config.disputeArbitrationAddress) throw new Error("disputeArbitrationAddress missing in config");
108
145
  const { signer } = await requireSigner(config);
109
146
  const client = new DisputeArbitrationClient(config.disputeArbitrationAddress, signer);
110
- await client.reclaimExpiredBond(BigInt(agreementId));
111
- console.log(`bond reclaimed for agreement ${agreementId}`);
147
+ const spinner = startSpinner("Submitting transaction...");
148
+ try {
149
+ await client.reclaimExpiredBond(BigInt(agreementId));
150
+ spinner.succeed();
151
+ } catch (err) {
152
+ spinner.fail();
153
+ throw err;
154
+ }
155
+ console.log(' ' + c.success + c.white(` Bond reclaimed — agreement #${agreementId}`));
112
156
  });
113
157
 
114
158
  arbitrator
@@ -123,7 +167,14 @@ export function registerArbitratorCommand(program: Command): void {
123
167
  if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
124
168
  const { signer } = await requireSigner(config);
125
169
  const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
126
- await client.resolveFromArbitration(BigInt(agreementId), opts.recipient, BigInt(opts.providerAmount), BigInt(opts.clientAmount));
127
- console.log(`resolved from arbitration: agreement ${agreementId}, recipient ${opts.recipient}`);
170
+ const spinner = startSpinner("Submitting transaction...");
171
+ try {
172
+ await client.resolveFromArbitration(BigInt(agreementId), opts.recipient, BigInt(opts.providerAmount), BigInt(opts.clientAmount));
173
+ spinner.succeed();
174
+ } catch (err) {
175
+ spinner.fail();
176
+ throw err;
177
+ }
178
+ console.log(' ' + c.success + c.white(` Resolved from arbitration — agreement #${agreementId}`));
128
179
  });
129
180
  }
@@ -3,6 +3,9 @@ import { ethers } from "ethers";
3
3
  import { loadConfig, getUsdcAddress } from "../config";
4
4
  import { requireSigner } from "../client";
5
5
  import { AGENT_REGISTRY_ABI } from "../abis";
6
+ import { startSpinner } from "../ui/spinner";
7
+ import { renderTree } from "../ui/tree";
8
+ import { c } from "../ui/colors";
6
9
 
7
10
  const DEFAULT_REGISTRY_ADDRESS = "0xD5c2851B00090c92Ba7F4723FB548bb30C9B6865";
8
11
 
@@ -122,6 +125,7 @@ export function registerArenaHandshakeCommands(program: Command): void {
122
125
 
123
126
  const handshake = new ethers.Contract(config.handshakeAddress, HANDSHAKE_ABI, signer);
124
127
 
128
+ const hsSpinner = startSpinner(`Sending ${opts.type} handshake...`);
125
129
  let tx;
126
130
  if (opts.usdc) {
127
131
  // USDC handshake
@@ -133,6 +137,7 @@ export function registerArenaHandshakeCommands(program: Command): void {
133
137
  const value = opts.tip ? ethers.parseEther(opts.tip) : 0n;
134
138
  tx = await handshake.sendHandshake(agentAddress, hsType, opts.note, { value });
135
139
  }
140
+ hsSpinner.succeed("Handshake sent");
136
141
 
137
142
  // Notify recipient's HTTP endpoint (non-blocking)
138
143
  const registryAddress = config.agentRegistryV2Address ?? config.agentRegistryAddress ?? DEFAULT_REGISTRY_ADDRESS;
@@ -150,14 +155,16 @@ export function registerArenaHandshakeCommands(program: Command): void {
150
155
  if (opts.json) {
151
156
  console.log(JSON.stringify({ tx: tx.hash, from: myAddress, to: agentAddress, type: opts.type, note: opts.note }));
152
157
  } else {
153
- console.log(`✓ Handshake sent`);
154
- console.log(` From: ${myAddress}`);
155
- console.log(` To: ${agentAddress}`);
156
- console.log(` Type: ${opts.type}`);
157
- if (opts.note) console.log(` Note: ${opts.note}`);
158
- if (opts.tip) console.log(` Tip: ${opts.tip} ETH`);
159
- if (opts.usdc) console.log(` Tip: ${opts.usdc} USDC`);
160
- console.log(` tx: ${tx.hash}`);
158
+ const treeItems = [
159
+ { label: "From", value: myAddress },
160
+ { label: "To", value: agentAddress },
161
+ { label: "Type", value: opts.type },
162
+ ...(opts.note ? [{ label: "Note", value: opts.note as string }] : []),
163
+ ...(opts.tip ? [{ label: "Tip", value: `${opts.tip as string} ETH` }] : []),
164
+ ...(opts.usdc ? [{ label: "Tip", value: `${opts.usdc as string} USDC` }] : []),
165
+ { label: "Tx", value: tx.hash as string, last: true },
166
+ ];
167
+ renderTree(treeItems);
161
168
  }
162
169
  });
163
170
 
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import { runFeed, FeedOptions } from "./feed";
4
+ import { c } from "../ui/colors";
4
5
 
5
6
  const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/1744310/arc-402/v0.2.0";
6
7
 
@@ -45,7 +46,7 @@ export function registerArenaCommands(program: Command): void {
45
46
  if (opts.json) {
46
47
  console.log(JSON.stringify({ error: msg }));
47
48
  } else {
48
- console.error(chalk.red(msg));
49
+ console.error(' ' + c.warning + c.white(` ${msg}`));
49
50
  }
50
51
  process.exit(1);
51
52
  }
@@ -90,7 +91,7 @@ export function registerArenaCommands(program: Command): void {
90
91
  if (opts.json) {
91
92
  console.log(JSON.stringify({ error: "Subgraph unavailable", details: msg }));
92
93
  } else {
93
- console.error(chalk.red(`Subgraph unavailable: ${msg}`));
94
+ console.error(' ' + c.failure + c.white(` Subgraph unavailable: ${msg}`));
94
95
  }
95
96
  process.exit(1);
96
97
  }
@@ -113,7 +114,7 @@ export function registerArenaCommands(program: Command): void {
113
114
  if ((opts as FeedOptions).json) {
114
115
  console.log(JSON.stringify({ error: "Subgraph unavailable", details: msg }));
115
116
  } else {
116
- console.error(chalk.red(`Subgraph unavailable: ${msg}`));
117
+ console.error(' ' + c.failure + c.white(` Subgraph unavailable: ${msg}`));
117
118
  }
118
119
  process.exit(1);
119
120
  }
@@ -4,6 +4,8 @@ import { loadConfig } from "../config";
4
4
  import { requireSigner } from "../client";
5
5
  import { printSenderInfo, executeContractWriteViaWallet } from "../wallet-router";
6
6
  import { SERVICE_AGREEMENT_ABI } from "../abis";
7
+ import { c } from "../ui/colors";
8
+ import { startSpinner } from "../ui/spinner";
7
9
 
8
10
  export function registerCancelCommand(program: Command): void {
9
11
  program.command("cancel <id>").description("Cancel a proposed agreement; use --expired for post-deadline recovery paths").option("--expired", "Call expiredCancel()", false).action(async (id, opts) => {
@@ -11,16 +13,23 @@ export function registerCancelCommand(program: Command): void {
11
13
  if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
12
14
  const { signer } = await requireSigner(config);
13
15
  printSenderInfo(config);
14
- if (config.walletContractAddress) {
15
- const fn = opts.expired ? "expiredCancel" : "cancel";
16
- await executeContractWriteViaWallet(
17
- config.walletContractAddress, signer, config.serviceAgreementAddress,
18
- SERVICE_AGREEMENT_ABI, fn, [BigInt(id)],
19
- );
20
- } else {
21
- const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
22
- if (opts.expired) await client.expiredCancel(BigInt(id)); else await client.cancel(BigInt(id));
16
+ const spinner = startSpinner("Submitting transaction...");
17
+ try {
18
+ if (config.walletContractAddress) {
19
+ const fn = opts.expired ? "expiredCancel" : "cancel";
20
+ await executeContractWriteViaWallet(
21
+ config.walletContractAddress, signer, config.serviceAgreementAddress,
22
+ SERVICE_AGREEMENT_ABI, fn, [BigInt(id)],
23
+ );
24
+ } else {
25
+ const client = new ServiceAgreementClient(config.serviceAgreementAddress, signer);
26
+ if (opts.expired) await client.expiredCancel(BigInt(id)); else await client.cancel(BigInt(id));
27
+ }
28
+ spinner.succeed();
29
+ } catch (err) {
30
+ spinner.fail();
31
+ throw err;
23
32
  }
24
- console.log(`cancelled ${id}`);
33
+ console.log(' ' + c.success + c.white(` Cancelled — agreement #${id}`));
25
34
  });
26
35
  }
@@ -6,6 +6,10 @@ import type { ChannelState } from "@arc402/sdk";
6
6
  import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import * as os from "os";
9
+ import { c } from '../ui/colors';
10
+ import { startSpinner } from '../ui/spinner';
11
+ import { renderTree } from '../ui/tree';
12
+ import { formatAddress } from '../ui/format';
9
13
 
10
14
  const CHANNEL_STATES_DIR = path.join(os.homedir(), ".arc402", "channel-states");
11
15
 
@@ -33,6 +37,7 @@ export function registerChannelCommands(program: Command): void {
33
37
  if (!config.serviceAgreementAddress) throw new Error("serviceAgreementAddress missing in config");
34
38
  const { signer } = await requireSigner(config);
35
39
  const client = new ChannelClient(config.serviceAgreementAddress, signer);
40
+ const spinner = startSpinner('Opening session channel…');
36
41
  const result = await client.openSessionChannel(
37
42
  provider,
38
43
  opts.token,
@@ -41,10 +46,14 @@ export function registerChannelCommands(program: Command): void {
41
46
  Number(opts.deadline)
42
47
  );
43
48
  if (opts.json || program.opts().json) {
49
+ spinner.stop();
44
50
  console.log(JSON.stringify(result, null, 2));
45
51
  } else {
46
- console.log(`channel opened: ${result.channelId}`);
47
- console.log(`tx: ${result.txHash}`);
52
+ spinner.succeed(' Opened — channel ' + result.channelId);
53
+ renderTree([
54
+ { label: 'Channel', value: result.channelId },
55
+ { label: 'Tx', value: result.txHash, last: true },
56
+ ]);
48
57
  }
49
58
  });
50
59
 
@@ -60,14 +69,15 @@ export function registerChannelCommands(program: Command): void {
60
69
  if (opts.json || program.opts().json) {
61
70
  console.log(JSON.stringify(ch, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2));
62
71
  } else {
63
- console.log(`channel: ${channelId}`);
64
- console.log(` status: ${ch.status}`);
65
- console.log(` client: ${ch.client}`);
66
- console.log(` provider: ${ch.provider}`);
67
- console.log(` deposit: ${ch.depositAmount}`);
68
- console.log(` settled: ${ch.settledAmount}`);
69
- console.log(` seq: ${ch.lastSequenceNumber}`);
70
- console.log(` deadline: ${ch.deadline}`);
72
+ console.log('\n ' + c.mark + c.white(` Channel ${channelId}`));
73
+ renderTree([
74
+ { label: 'Status', value: ch.status },
75
+ { label: 'Client', value: formatAddress(ch.client) },
76
+ { label: 'Provider', value: formatAddress(ch.provider) },
77
+ { label: 'Deposit', value: ch.depositAmount.toString() },
78
+ { label: 'Settled', value: ch.settledAmount.toString() },
79
+ { label: 'Seq', value: ch.lastSequenceNumber.toString(), last: true },
80
+ ]);
71
81
  }
72
82
  });
73
83
 
@@ -107,8 +117,8 @@ export function registerChannelCommands(program: Command): void {
107
117
  if (opts.json || program.opts().json) {
108
118
  console.log(JSON.stringify(result, null, 2));
109
119
  } else {
110
- console.log(`close submitted: ${result.txHash}`);
111
- console.log("challenge window open (24h)");
120
+ console.log(' ' + c.success + c.white(` Close submitted ${result.txHash}`));
121
+ console.log(' ' + c.dim('Challenge window open (24h)'));
112
122
  }
113
123
  });
114
124
 
@@ -126,7 +136,7 @@ export function registerChannelCommands(program: Command): void {
126
136
  if (opts.json || program.opts().json) {
127
137
  console.log(JSON.stringify(result, null, 2));
128
138
  } else {
129
- console.log(`challenge submitted: ${result.txHash}`);
139
+ console.log(' ' + c.success + c.white(` Challenge submitted ${result.txHash}`));
130
140
  }
131
141
  });
132
142
 
@@ -142,7 +152,7 @@ export function registerChannelCommands(program: Command): void {
142
152
  if (opts.json || program.opts().json) {
143
153
  console.log(JSON.stringify(result, null, 2));
144
154
  } else {
145
- console.log(`finalised: ${result.txHash}`);
155
+ console.log(' ' + c.success + c.white(` Finalised — ${result.txHash}`));
146
156
  }
147
157
  });
148
158
 
@@ -158,7 +168,7 @@ export function registerChannelCommands(program: Command): void {
158
168
  if (opts.json || program.opts().json) {
159
169
  console.log(JSON.stringify(result, null, 2));
160
170
  } else {
161
- console.log(`reclaimed: ${result.txHash}`);
171
+ console.log(' ' + c.success + c.white(` Reclaimed — ${result.txHash}`));
162
172
  }
163
173
  });
164
174
 
@@ -201,8 +211,8 @@ export function registerChannelCommands(program: Command): void {
201
211
  if (opts.json || program.opts().json) {
202
212
  console.log(JSON.stringify({ stored: true, channelId, path: dest }));
203
213
  } else {
204
- console.log(`State stored: ${dest}`);
205
- console.log(` seq: ${state.sequenceNumber}`);
214
+ console.log(' ' + c.success + c.white(` State stored ${dest}`));
215
+ console.log(' ' + c.dim(`seq: ${state.sequenceNumber}`));
206
216
  }
207
217
  });
208
218
  }
@@ -2,6 +2,9 @@ import { Command } from "commander";
2
2
  import { ethers } from "ethers";
3
3
  import { loadConfig } from "../config";
4
4
  import { getClient, requireSigner } from "../client";
5
+ import { c } from '../ui/colors';
6
+ import { startSpinner } from '../ui/spinner';
7
+ import { renderTree } from '../ui/tree';
5
8
 
6
9
  const VOUCHING_REGISTRY_ABI = [
7
10
  "function vouch(address newAgent, uint256 stakeAmount) external payable",
@@ -42,6 +45,7 @@ export function registerColdStartCommands(program: Command): void {
42
45
  const stakeAmount = opts.stake ? BigInt(opts.stake) : 0n;
43
46
  const contract = new ethers.Contract(config.vouchingRegistryAddress, VOUCHING_REGISTRY_ABI, signer);
44
47
 
48
+ const spinner = startSpinner(`Vouching for ${address}…`);
45
49
  const tx = await contract.vouch(address, stakeAmount);
46
50
  const receipt = await tx.wait();
47
51
 
@@ -53,11 +57,14 @@ export function registerColdStartCommands(program: Command): void {
53
57
  boostGranted: boost.toString(),
54
58
  txHash: receipt.hash,
55
59
  };
56
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
57
- console.log(`vouched for ${address}`);
58
- if (stakeAmount > 0n) console.log(` stake: ${ethers.formatEther(stakeAmount)} ETH`);
59
- console.log(` boost granted: +${boost} trust points`);
60
- console.log(` tx: ${receipt.hash}`);
60
+ if (opts.json) { spinner.stop(); return console.log(JSON.stringify(payload, null, 2)); }
61
+ spinner.succeed(` Vouched ${address}`);
62
+ renderTree([
63
+ { label: 'New Agent', value: address },
64
+ { label: 'Stake', value: stakeAmount > 0n ? `${ethers.formatEther(stakeAmount)} ETH` : '0' },
65
+ { label: 'Boost', value: `+${boost} trust points` },
66
+ { label: 'Tx', value: receipt.hash, last: true },
67
+ ]);
61
68
  });
62
69
 
63
70
  // ─── bond ──────────────────────────────────────────────────────────────────
@@ -88,14 +95,18 @@ export function registerColdStartCommands(program: Command): void {
88
95
  }
89
96
 
90
97
  const amount = opts.amount ? BigInt(opts.amount) : BOND_DEFAULT_AMOUNT;
98
+ const bondSpinner = startSpinner('Posting bond…');
91
99
  const tx = await contract.postBond({ value: amount });
92
100
  const receipt = await tx.wait();
93
101
 
94
102
  const payload = { bonded: true, amount: amount.toString(), txHash: receipt.hash };
95
- if (opts.json) return console.log(JSON.stringify(payload, null, 2));
96
- console.log(`bond posted: ${ethers.formatEther(amount)} ETH`);
97
- console.log(` claimable after 90 days of clean operation`);
98
- console.log(` tx: ${receipt.hash}`);
103
+ if (opts.json) { bondSpinner.stop(); return console.log(JSON.stringify(payload, null, 2)); }
104
+ bondSpinner.succeed(` Bond posted ${ethers.formatEther(amount)} ETH`);
105
+ renderTree([
106
+ { label: 'Amount', value: `${ethers.formatEther(amount)} ETH` },
107
+ { label: 'Tx', value: receipt.hash },
108
+ { label: 'Note', value: 'Claimable after 90 days of clean operation', last: true },
109
+ ]);
99
110
  });
100
111
 
101
112
  // arc402 bond status <address>
@@ -121,8 +132,8 @@ export function registerColdStartCommands(program: Command): void {
121
132
  } catch {
122
133
  const payload = { address, bonded: false };
123
134
  if (opts.json) return console.log(JSON.stringify(payload, null, 2));
124
- console.log(`address=${address}`);
125
- console.log(` no active bond`);
135
+ console.log('\n ' + c.mark + c.white(` Bond Status — ${address}`));
136
+ renderTree([{ label: 'Status', value: 'No active bond', last: true }]);
126
137
  return;
127
138
  }
128
139
 
@@ -140,17 +151,15 @@ export function registerColdStartCommands(program: Command): void {
140
151
  daysRemaining,
141
152
  };
142
153
  if (opts.json) return console.log(JSON.stringify(payload, null, 2));
143
- console.log(`address=${address}`);
154
+ console.log('\n ' + c.mark + c.white(` Bond Status — ${address}`));
144
155
  if (!active) {
145
- console.log(` no active bond`);
156
+ renderTree([{ label: 'Status', value: 'No active bond', last: true }]);
146
157
  return;
147
158
  }
148
- console.log(` amount: ${ethers.formatEther(amount)} ETH`);
149
- console.log(` posted: ${new Date(Number(postedAt) * 1000).toISOString()}`);
150
- if (daysRemaining > 0) {
151
- console.log(` claimable in: ${daysRemaining} days`);
152
- } else {
153
- console.log(` claimable: now`);
154
- }
159
+ renderTree([
160
+ { label: 'Amount', value: `${ethers.formatEther(amount)} ETH` },
161
+ { label: 'Posted', value: new Date(Number(postedAt) * 1000).toISOString() },
162
+ { label: 'Claimable', value: daysRemaining > 0 ? `in ${daysRemaining} days` : 'now', last: true },
163
+ ]);
155
164
  });
156
165
  }