@spectratools/assembly-cli 0.1.0 → 0.2.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 (3) hide show
  1. package/README.md +41 -199
  2. package/dist/cli.js +730 -89
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -29,6 +29,19 @@ function relTime(unixSeconds) {
29
29
  function asNum(value) {
30
30
  return Number(value);
31
31
  }
32
+ function jsonSafe(value) {
33
+ if (typeof value === "bigint") return value.toString();
34
+ if (Array.isArray(value)) return value.map((item) => jsonSafe(item));
35
+ if (value && typeof value === "object") {
36
+ return Object.fromEntries(
37
+ Object.entries(value).map(([key, entry]) => [
38
+ key,
39
+ jsonSafe(entry)
40
+ ])
41
+ );
42
+ }
43
+ return value;
44
+ }
32
45
 
33
46
  // src/commands/council.ts
34
47
  import { Cli, z } from "incur";
@@ -4293,12 +4306,35 @@ function createAssemblyPublicClient(rpcUrl) {
4293
4306
  }
4294
4307
 
4295
4308
  // src/commands/council.ts
4296
- var env = z.object({ ABSTRACT_RPC_URL: z.string().optional() });
4309
+ var env = z.object({
4310
+ ABSTRACT_RPC_URL: z.string().optional().describe("Abstract RPC URL override")
4311
+ });
4312
+ function decodeSeat(value) {
4313
+ const [owner, startAt, endAt, forfeited] = value;
4314
+ return { owner, startAt, endAt, forfeited };
4315
+ }
4316
+ function decodeAuction(value) {
4317
+ const [highestBidder, highestBid, settled] = value;
4318
+ return { highestBidder, highestBid, settled };
4319
+ }
4297
4320
  var council = Cli.create("council", {
4298
- description: "Read council seat and auction state."
4321
+ description: "Inspect council seats, members, auctions, and seat parameters."
4299
4322
  });
4300
4323
  council.command("seats", {
4324
+ description: "List all council seats and their occupancy windows.",
4301
4325
  env,
4326
+ output: z.array(
4327
+ z.object({
4328
+ id: z.number(),
4329
+ owner: z.string(),
4330
+ startAt: z.number(),
4331
+ startAtRelative: z.string(),
4332
+ endAt: z.number(),
4333
+ endAtRelative: z.string(),
4334
+ forfeited: z.boolean()
4335
+ })
4336
+ ),
4337
+ examples: [{ description: "List all council seats" }],
4302
4338
  async run(c) {
4303
4339
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4304
4340
  const count = await client.readContract({
@@ -4307,7 +4343,7 @@ council.command("seats", {
4307
4343
  functionName: "seatCount"
4308
4344
  });
4309
4345
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i));
4310
- const seats = ids.length ? await client.multicall({
4346
+ const seatTuples = ids.length ? await client.multicall({
4311
4347
  allowFailure: false,
4312
4348
  contracts: ids.map((id) => ({
4313
4349
  abi: councilSeatsAbi,
@@ -4316,13 +4352,14 @@ council.command("seats", {
4316
4352
  args: [id]
4317
4353
  }))
4318
4354
  }) : [];
4355
+ const seats = seatTuples.map(decodeSeat);
4319
4356
  return c.ok(
4320
4357
  seats.map((seat, idx) => ({
4321
4358
  id: idx,
4322
4359
  owner: toChecksum(seat.owner),
4323
- startAt: Number(seat.startAt),
4360
+ startAt: asNum(seat.startAt),
4324
4361
  startAtRelative: relTime(seat.startAt),
4325
- endAt: Number(seat.endAt),
4362
+ endAt: asNum(seat.endAt),
4326
4363
  endAtRelative: relTime(seat.endAt),
4327
4364
  forfeited: seat.forfeited
4328
4365
  }))
@@ -4330,26 +4367,49 @@ council.command("seats", {
4330
4367
  }
4331
4368
  });
4332
4369
  council.command("seat", {
4333
- args: z.object({ id: z.coerce.number().int().nonnegative() }),
4370
+ description: "Get detailed seat information for a specific seat id.",
4371
+ args: z.object({
4372
+ id: z.coerce.number().int().nonnegative().describe("Seat id (0-indexed)")
4373
+ }),
4334
4374
  env,
4375
+ output: z.object({
4376
+ id: z.number(),
4377
+ owner: z.string(),
4378
+ startAt: z.number(),
4379
+ endAt: z.number(),
4380
+ forfeited: z.boolean(),
4381
+ endAtRelative: z.string()
4382
+ }),
4383
+ examples: [{ args: { id: 0 }, description: "Inspect seat #0" }],
4335
4384
  async run(c) {
4336
4385
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4337
- const seat = await client.readContract({
4386
+ const seatTuple = await client.readContract({
4338
4387
  abi: councilSeatsAbi,
4339
4388
  address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
4340
4389
  functionName: "seats",
4341
4390
  args: [BigInt(c.args.id)]
4342
4391
  });
4392
+ const seat = decodeSeat(seatTuple);
4343
4393
  return c.ok({
4344
4394
  id: c.args.id,
4345
- ...seat,
4346
4395
  owner: toChecksum(seat.owner),
4396
+ startAt: asNum(seat.startAt),
4397
+ endAt: asNum(seat.endAt),
4398
+ forfeited: seat.forfeited,
4347
4399
  endAtRelative: relTime(seat.endAt)
4348
4400
  });
4349
4401
  }
4350
4402
  });
4351
4403
  council.command("members", {
4404
+ description: "List currently active council members and voting power.",
4352
4405
  env,
4406
+ output: z.array(
4407
+ z.object({
4408
+ address: z.string(),
4409
+ votingPower: z.number()
4410
+ })
4411
+ ),
4412
+ examples: [{ description: "List active council members" }],
4353
4413
  async run(c) {
4354
4414
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4355
4415
  const count = await client.readContract({
@@ -4358,7 +4418,7 @@ council.command("members", {
4358
4418
  functionName: "seatCount"
4359
4419
  });
4360
4420
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i));
4361
- const seats = ids.length ? await client.multicall({
4421
+ const seatTuples = ids.length ? await client.multicall({
4362
4422
  allowFailure: false,
4363
4423
  contracts: ids.map((id) => ({
4364
4424
  abi: councilSeatsAbi,
@@ -4367,9 +4427,10 @@ council.command("members", {
4367
4427
  args: [id]
4368
4428
  }))
4369
4429
  }) : [];
4430
+ const seats = seatTuples.map(decodeSeat);
4370
4431
  const activeOwners = [
4371
4432
  ...new Set(
4372
- seats.filter((x) => !x.forfeited && Number(x.endAt) > Math.floor(Date.now() / 1e3)).map((x) => x.owner)
4433
+ seats.filter((x) => !x.forfeited && asNum(x.endAt) > Math.floor(Date.now() / 1e3)).map((x) => x.owner)
4373
4434
  )
4374
4435
  ];
4375
4436
  const powers = activeOwners.length ? await client.multicall({
@@ -4390,8 +4451,21 @@ council.command("members", {
4390
4451
  }
4391
4452
  });
4392
4453
  council.command("is-member", {
4393
- args: z.object({ address: z.string() }),
4454
+ description: "Check whether an address is currently a council member.",
4455
+ args: z.object({
4456
+ address: z.string().describe("Address to check")
4457
+ }),
4394
4458
  env,
4459
+ output: z.object({
4460
+ address: z.string(),
4461
+ isMember: z.boolean()
4462
+ }),
4463
+ examples: [
4464
+ {
4465
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
4466
+ description: "Check council status for one address"
4467
+ }
4468
+ ],
4395
4469
  async run(c) {
4396
4470
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4397
4471
  const isMember = await client.readContract({
@@ -4404,8 +4478,21 @@ council.command("is-member", {
4404
4478
  }
4405
4479
  });
4406
4480
  council.command("voting-power", {
4407
- args: z.object({ address: z.string() }),
4481
+ description: "Get the current voting power for an address.",
4482
+ args: z.object({
4483
+ address: z.string().describe("Address to inspect")
4484
+ }),
4408
4485
  env,
4486
+ output: z.object({
4487
+ address: z.string(),
4488
+ votingPower: z.number()
4489
+ }),
4490
+ examples: [
4491
+ {
4492
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
4493
+ description: "Get voting power for one address"
4494
+ }
4495
+ ],
4409
4496
  async run(c) {
4410
4497
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4411
4498
  const votingPower = await client.readContract({
@@ -4418,7 +4505,22 @@ council.command("voting-power", {
4418
4505
  }
4419
4506
  });
4420
4507
  council.command("auctions", {
4508
+ description: "List recent and current council auction slots and leading bids.",
4421
4509
  env,
4510
+ output: z.object({
4511
+ currentDay: z.number(),
4512
+ currentSlot: z.number(),
4513
+ auctions: z.array(
4514
+ z.object({
4515
+ day: z.number(),
4516
+ slot: z.number(),
4517
+ highestBidder: z.string(),
4518
+ highestBid: z.string(),
4519
+ settled: z.boolean()
4520
+ })
4521
+ )
4522
+ }),
4523
+ examples: [{ description: "Inspect current and recent auction slots" }],
4422
4524
  async run(c) {
4423
4525
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4424
4526
  const [day, slot, slotsPerDay] = await Promise.all([
@@ -4443,7 +4545,7 @@ council.command("auctions", {
4443
4545
  if (d < 0) continue;
4444
4546
  for (let s = 0; s < Number(slotsPerDay); s++) recent.push({ day: BigInt(d), slot: s });
4445
4547
  }
4446
- const auctions = recent.length ? await client.multicall({
4548
+ const auctionTuples = recent.length ? await client.multicall({
4447
4549
  allowFailure: false,
4448
4550
  contracts: recent.map((x) => ({
4449
4551
  abi: councilSeatsAbi,
@@ -4452,12 +4554,14 @@ council.command("auctions", {
4452
4554
  args: [x.day, x.slot]
4453
4555
  }))
4454
4556
  }) : [];
4557
+ const auctions = auctionTuples.map(decodeAuction);
4455
4558
  return c.ok(
4456
4559
  {
4457
4560
  currentDay: asNum(day),
4458
4561
  currentSlot: asNum(slot),
4459
4562
  auctions: recent.map((x, i) => ({
4460
- ...x,
4563
+ day: Number(x.day),
4564
+ slot: x.slot,
4461
4565
  highestBidder: toChecksum(auctions[i].highestBidder),
4462
4566
  highestBid: eth(auctions[i].highestBid),
4463
4567
  settled: auctions[i].settled
@@ -4476,19 +4580,29 @@ council.command("auctions", {
4476
4580
  }
4477
4581
  });
4478
4582
  council.command("auction", {
4583
+ description: "Get one auction slot by day + slot.",
4479
4584
  args: z.object({
4480
- day: z.coerce.number().int().nonnegative(),
4481
- slot: z.coerce.number().int().nonnegative()
4585
+ day: z.coerce.number().int().nonnegative().describe("Auction day index"),
4586
+ slot: z.coerce.number().int().nonnegative().describe("Slot index within day")
4482
4587
  }),
4483
4588
  env,
4589
+ output: z.object({
4590
+ day: z.number(),
4591
+ slot: z.number(),
4592
+ highestBidder: z.string(),
4593
+ highestBid: z.string(),
4594
+ settled: z.boolean()
4595
+ }),
4596
+ examples: [{ args: { day: 0, slot: 0 }, description: "Inspect day 0, slot 0 auction" }],
4484
4597
  async run(c) {
4485
4598
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4486
- const auction = await client.readContract({
4599
+ const auctionTuple = await client.readContract({
4487
4600
  abi: councilSeatsAbi,
4488
4601
  address: ABSTRACT_MAINNET_ADDRESSES.councilSeats,
4489
4602
  functionName: "auctions",
4490
4603
  args: [BigInt(c.args.day), c.args.slot]
4491
4604
  });
4605
+ const auction = decodeAuction(auctionTuple);
4492
4606
  return c.ok({
4493
4607
  day: c.args.day,
4494
4608
  slot: c.args.slot,
@@ -4499,8 +4613,22 @@ council.command("auction", {
4499
4613
  }
4500
4614
  });
4501
4615
  council.command("pending-refund", {
4502
- args: z.object({ address: z.string() }),
4616
+ description: "Get pending refundable bid amount for an address.",
4617
+ args: z.object({
4618
+ address: z.string().describe("Bidder address")
4619
+ }),
4503
4620
  env,
4621
+ output: z.object({
4622
+ address: z.string(),
4623
+ pendingRefund: z.string(),
4624
+ pendingRefundWei: z.string()
4625
+ }),
4626
+ examples: [
4627
+ {
4628
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
4629
+ description: "Check pending refund for an address"
4630
+ }
4631
+ ],
4504
4632
  async run(c) {
4505
4633
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4506
4634
  const amount = await client.readContract({
@@ -4517,7 +4645,17 @@ council.command("pending-refund", {
4517
4645
  }
4518
4646
  });
4519
4647
  council.command("params", {
4648
+ description: "Read council seat term and auction scheduling parameters.",
4520
4649
  env,
4650
+ output: z.object({
4651
+ SEAT_TERM: z.number(),
4652
+ AUCTION_SLOT_DURATION: z.number(),
4653
+ AUCTION_SLOTS_PER_DAY: z.number(),
4654
+ auctionEpochStart: z.number(),
4655
+ auctionWindowStart: z.number(),
4656
+ auctionWindowEnd: z.number()
4657
+ }),
4658
+ examples: [{ description: "Inspect council seat + auction timing constants" }],
4521
4659
  async run(c) {
4522
4660
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4523
4661
  const [SEAT_TERM, AUCTION_SLOT_DURATION, AUCTION_SLOTS_PER_DAY, auctionEpochStart] = await Promise.all([
@@ -4569,10 +4707,78 @@ council.command("params", {
4569
4707
 
4570
4708
  // src/commands/forum.ts
4571
4709
  import { Cli as Cli2, z as z2 } from "incur";
4572
- var env2 = z2.object({ ABSTRACT_RPC_URL: z2.string().optional() });
4573
- var forum = Cli2.create("forum", { description: "Read forum threads/comments/petitions." });
4710
+ var env2 = z2.object({
4711
+ ABSTRACT_RPC_URL: z2.string().optional().describe("Abstract RPC URL override")
4712
+ });
4713
+ function decodeThread(value) {
4714
+ const [id, kind, author, createdAt, category, title, body, proposalId, petitionId] = value;
4715
+ return {
4716
+ id: asNum(id),
4717
+ kind: asNum(kind),
4718
+ author: toChecksum(author),
4719
+ createdAt: asNum(createdAt),
4720
+ category,
4721
+ title,
4722
+ body,
4723
+ proposalId: asNum(proposalId),
4724
+ petitionId: asNum(petitionId)
4725
+ };
4726
+ }
4727
+ function decodeComment(value) {
4728
+ const [id, threadId, parentId, author, createdAt, body] = value;
4729
+ return {
4730
+ id: asNum(id),
4731
+ threadId: asNum(threadId),
4732
+ parentId: asNum(parentId),
4733
+ author: toChecksum(author),
4734
+ createdAt: asNum(createdAt),
4735
+ body
4736
+ };
4737
+ }
4738
+ function decodePetition(value) {
4739
+ const [
4740
+ id,
4741
+ proposer,
4742
+ createdAt,
4743
+ category,
4744
+ title,
4745
+ body,
4746
+ signatures,
4747
+ promoted,
4748
+ threadId,
4749
+ proposalInput
4750
+ ] = value;
4751
+ return {
4752
+ id: asNum(id),
4753
+ proposer: toChecksum(proposer),
4754
+ createdAt: asNum(createdAt),
4755
+ category,
4756
+ title,
4757
+ body,
4758
+ signatures: asNum(signatures),
4759
+ promoted,
4760
+ threadId: asNum(threadId),
4761
+ proposalInput: jsonSafe(proposalInput)
4762
+ };
4763
+ }
4764
+ var forum = Cli2.create("forum", {
4765
+ description: "Browse Assembly forum threads, comments, and petitions."
4766
+ });
4574
4767
  forum.command("threads", {
4768
+ description: "List forum threads with author and creation metadata.",
4575
4769
  env: env2,
4770
+ output: z2.array(
4771
+ z2.object({
4772
+ id: z2.number(),
4773
+ kind: z2.number(),
4774
+ author: z2.string(),
4775
+ createdAt: z2.number(),
4776
+ createdAtRelative: z2.string(),
4777
+ category: z2.string().nullable().optional(),
4778
+ title: z2.string().nullable().optional()
4779
+ })
4780
+ ),
4781
+ examples: [{ description: "List all forum threads" }],
4576
4782
  async run(c) {
4577
4783
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4578
4784
  const count = await client.readContract({
@@ -4581,7 +4787,7 @@ forum.command("threads", {
4581
4787
  functionName: "threadCount"
4582
4788
  });
4583
4789
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
4584
- const items = ids.length ? await client.multicall({
4790
+ const threadTuples = ids.length ? await client.multicall({
4585
4791
  allowFailure: false,
4586
4792
  contracts: ids.map((id) => ({
4587
4793
  abi: forumAbi,
@@ -4590,15 +4796,16 @@ forum.command("threads", {
4590
4796
  args: [id]
4591
4797
  }))
4592
4798
  }) : [];
4799
+ const items = threadTuples.map(decodeThread);
4593
4800
  return c.ok(
4594
4801
  items.map((x) => ({
4595
- id: asNum(x.id),
4596
- kind: asNum(x.kind),
4597
- author: toChecksum(x.author),
4598
- createdAt: asNum(x.createdAt),
4802
+ id: x.id,
4803
+ kind: x.kind,
4804
+ author: x.author,
4805
+ createdAt: x.createdAt,
4599
4806
  createdAtRelative: relTime(x.createdAt),
4600
- category: x.category,
4601
- title: x.title
4807
+ category: x.category ?? null,
4808
+ title: x.title ?? null
4602
4809
  })),
4603
4810
  {
4604
4811
  cta: {
@@ -4613,11 +4820,19 @@ forum.command("threads", {
4613
4820
  }
4614
4821
  });
4615
4822
  forum.command("thread", {
4616
- args: z2.object({ id: z2.coerce.number().int().positive() }),
4823
+ description: "Get one thread and all comments associated with it.",
4824
+ args: z2.object({
4825
+ id: z2.coerce.number().int().positive().describe("Thread id (1-indexed)")
4826
+ }),
4617
4827
  env: env2,
4828
+ output: z2.object({
4829
+ thread: z2.record(z2.string(), z2.unknown()),
4830
+ comments: z2.array(z2.record(z2.string(), z2.unknown()))
4831
+ }),
4832
+ examples: [{ args: { id: 1 }, description: "Fetch thread #1 and its comments" }],
4618
4833
  async run(c) {
4619
4834
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4620
- const [thread, commentCount] = await Promise.all([
4835
+ const [threadTuple, commentCount] = await Promise.all([
4621
4836
  client.readContract({
4622
4837
  abi: forumAbi,
4623
4838
  address: ABSTRACT_MAINNET_ADDRESSES.forum,
@@ -4630,8 +4845,9 @@ forum.command("thread", {
4630
4845
  functionName: "commentCount"
4631
4846
  })
4632
4847
  ]);
4848
+ const thread = decodeThread(threadTuple);
4633
4849
  const ids = Array.from({ length: Number(commentCount) }, (_, i) => BigInt(i + 1));
4634
- const comments = ids.length ? await client.multicall({
4850
+ const commentTuples = ids.length ? await client.multicall({
4635
4851
  allowFailure: false,
4636
4852
  contracts: ids.map((id) => ({
4637
4853
  abi: forumAbi,
@@ -4640,15 +4856,21 @@ forum.command("thread", {
4640
4856
  args: [id]
4641
4857
  }))
4642
4858
  }) : [];
4859
+ const comments = commentTuples.map(decodeComment);
4643
4860
  return c.ok({
4644
- thread,
4645
- comments: comments.filter((x) => Number(x.threadId) === c.args.id)
4861
+ thread: jsonSafe(thread),
4862
+ comments: comments.filter((x) => x.threadId === c.args.id).map((comment) => jsonSafe(comment))
4646
4863
  });
4647
4864
  }
4648
4865
  });
4649
4866
  forum.command("comments", {
4650
- args: z2.object({ threadId: z2.coerce.number().int().positive() }),
4867
+ description: "List comments for a thread id.",
4868
+ args: z2.object({
4869
+ threadId: z2.coerce.number().int().positive().describe("Thread id to filter comments by")
4870
+ }),
4651
4871
  env: env2,
4872
+ output: z2.array(z2.record(z2.string(), z2.unknown())),
4873
+ examples: [{ args: { threadId: 1 }, description: "List comments for thread #1" }],
4652
4874
  async run(c) {
4653
4875
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4654
4876
  const count = await client.readContract({
@@ -4657,7 +4879,7 @@ forum.command("comments", {
4657
4879
  functionName: "commentCount"
4658
4880
  });
4659
4881
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
4660
- const comments = ids.length ? await client.multicall({
4882
+ const commentTuples = ids.length ? await client.multicall({
4661
4883
  allowFailure: false,
4662
4884
  contracts: ids.map((id) => ({
4663
4885
  abi: forumAbi,
@@ -4666,25 +4888,36 @@ forum.command("comments", {
4666
4888
  args: [id]
4667
4889
  }))
4668
4890
  }) : [];
4669
- return c.ok(comments.filter((x) => Number(x.threadId) === c.args.threadId));
4891
+ const comments = commentTuples.map(decodeComment);
4892
+ return c.ok(
4893
+ comments.filter((x) => x.threadId === c.args.threadId).map((comment) => jsonSafe(comment))
4894
+ );
4670
4895
  }
4671
4896
  });
4672
4897
  forum.command("comment", {
4673
- args: z2.object({ id: z2.coerce.number().int().positive() }),
4898
+ description: "Get one comment by comment id.",
4899
+ args: z2.object({
4900
+ id: z2.coerce.number().int().positive().describe("Comment id (1-indexed)")
4901
+ }),
4674
4902
  env: env2,
4903
+ output: z2.record(z2.string(), z2.unknown()),
4904
+ examples: [{ args: { id: 1 }, description: "Fetch comment #1" }],
4675
4905
  async run(c) {
4676
4906
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4677
- const comment = await client.readContract({
4907
+ const commentTuple = await client.readContract({
4678
4908
  abi: forumAbi,
4679
4909
  address: ABSTRACT_MAINNET_ADDRESSES.forum,
4680
4910
  functionName: "comments",
4681
4911
  args: [BigInt(c.args.id)]
4682
4912
  });
4683
- return c.ok(comment);
4913
+ return c.ok(jsonSafe(decodeComment(commentTuple)));
4684
4914
  }
4685
4915
  });
4686
4916
  forum.command("petitions", {
4917
+ description: "List petitions submitted in the forum contract.",
4687
4918
  env: env2,
4919
+ output: z2.array(z2.record(z2.string(), z2.unknown())),
4920
+ examples: [{ description: "List all petitions" }],
4688
4921
  async run(c) {
4689
4922
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4690
4923
  const count = await client.readContract({
@@ -4693,7 +4926,7 @@ forum.command("petitions", {
4693
4926
  functionName: "petitionCount"
4694
4927
  });
4695
4928
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
4696
- const petitions = ids.length ? await client.multicall({
4929
+ const petitionTuples = ids.length ? await client.multicall({
4697
4930
  allowFailure: false,
4698
4931
  contracts: ids.map((id) => ({
4699
4932
  abi: forumAbi,
@@ -4702,32 +4935,58 @@ forum.command("petitions", {
4702
4935
  args: [id]
4703
4936
  }))
4704
4937
  }) : [];
4705
- return c.ok(petitions);
4938
+ const petitions = petitionTuples.map(decodePetition);
4939
+ return c.ok(petitions.map((petition) => jsonSafe(petition)));
4706
4940
  }
4707
4941
  });
4708
4942
  forum.command("petition", {
4709
- args: z2.object({ id: z2.coerce.number().int().positive() }),
4943
+ description: "Get one petition plus whether proposer already signed it.",
4944
+ args: z2.object({
4945
+ id: z2.coerce.number().int().positive().describe("Petition id (1-indexed)")
4946
+ }),
4710
4947
  env: env2,
4948
+ output: z2.object({ proposerSigned: z2.boolean() }).passthrough(),
4949
+ examples: [{ args: { id: 1 }, description: "Fetch petition #1" }],
4711
4950
  async run(c) {
4712
4951
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4713
- const petition = await client.readContract({
4714
- abi: forumAbi,
4715
- address: ABSTRACT_MAINNET_ADDRESSES.forum,
4716
- functionName: "petitions",
4717
- args: [BigInt(c.args.id)]
4718
- });
4952
+ const petition = decodePetition(
4953
+ await client.readContract({
4954
+ abi: forumAbi,
4955
+ address: ABSTRACT_MAINNET_ADDRESSES.forum,
4956
+ functionName: "petitions",
4957
+ args: [BigInt(c.args.id)]
4958
+ })
4959
+ );
4719
4960
  const proposerSigned = await client.readContract({
4720
4961
  abi: forumAbi,
4721
4962
  address: ABSTRACT_MAINNET_ADDRESSES.forum,
4722
4963
  functionName: "hasSignedPetition",
4723
4964
  args: [BigInt(c.args.id), petition.proposer]
4724
4965
  });
4725
- return c.ok({ ...petition, proposerSigned });
4966
+ return c.ok({ ...jsonSafe(petition), proposerSigned });
4726
4967
  }
4727
4968
  });
4728
4969
  forum.command("has-signed", {
4729
- args: z2.object({ petitionId: z2.coerce.number().int().positive(), address: z2.string() }),
4970
+ description: "Check whether an address signed a petition.",
4971
+ args: z2.object({
4972
+ petitionId: z2.coerce.number().int().positive().describe("Petition id (1-indexed)"),
4973
+ address: z2.string().describe("Signer address to check")
4974
+ }),
4730
4975
  env: env2,
4976
+ output: z2.object({
4977
+ petitionId: z2.number(),
4978
+ address: z2.string(),
4979
+ hasSigned: z2.boolean()
4980
+ }),
4981
+ examples: [
4982
+ {
4983
+ args: {
4984
+ petitionId: 1,
4985
+ address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
4986
+ },
4987
+ description: "Check if an address signed petition #1"
4988
+ }
4989
+ ],
4731
4990
  async run(c) {
4732
4991
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4733
4992
  const hasSigned = await client.readContract({
@@ -4740,7 +4999,15 @@ forum.command("has-signed", {
4740
4999
  }
4741
5000
  });
4742
5001
  forum.command("stats", {
5002
+ description: "Read top-level forum counters and petition threshold.",
4743
5003
  env: env2,
5004
+ output: z2.object({
5005
+ threadCount: z2.number(),
5006
+ commentCount: z2.number(),
5007
+ petitionCount: z2.number(),
5008
+ petitionThresholdBps: z2.number()
5009
+ }),
5010
+ examples: [{ description: "Get forum counts and petition threshold" }],
4744
5011
  async run(c) {
4745
5012
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4746
5013
  const [threadCount, commentCount, petitionCount, petitionThresholdBps] = await Promise.all([
@@ -4776,12 +5043,105 @@ forum.command("stats", {
4776
5043
 
4777
5044
  // src/commands/governance.ts
4778
5045
  import { Cli as Cli3, z as z3 } from "incur";
4779
- var env3 = z3.object({ ABSTRACT_RPC_URL: z3.string().optional() });
5046
+ var env3 = z3.object({
5047
+ ABSTRACT_RPC_URL: z3.string().optional().describe("Abstract RPC URL override")
5048
+ });
5049
+ function decodeProposal(value) {
5050
+ const [
5051
+ kind,
5052
+ configRiskTier,
5053
+ origin,
5054
+ status,
5055
+ proposer,
5056
+ threadId,
5057
+ petitionId,
5058
+ createdAt,
5059
+ deliberationEndsAt,
5060
+ voteStartAt,
5061
+ voteEndAt,
5062
+ timelockEndsAt,
5063
+ activeSeatsSnapshot,
5064
+ forVotes,
5065
+ againstVotes,
5066
+ abstainVotes,
5067
+ amount,
5068
+ snapshotAssetBalance,
5069
+ transferIntent,
5070
+ intentDeadline,
5071
+ intentMaxRiskTier,
5072
+ title,
5073
+ description
5074
+ ] = value;
5075
+ return {
5076
+ kind,
5077
+ configRiskTier,
5078
+ origin,
5079
+ status,
5080
+ proposer: toChecksum(proposer),
5081
+ threadId,
5082
+ petitionId,
5083
+ createdAt,
5084
+ deliberationEndsAt,
5085
+ voteStartAt,
5086
+ voteEndAt,
5087
+ timelockEndsAt,
5088
+ activeSeatsSnapshot,
5089
+ forVotes,
5090
+ againstVotes,
5091
+ abstainVotes,
5092
+ amount,
5093
+ snapshotAssetBalance,
5094
+ transferIntent,
5095
+ intentDeadline,
5096
+ intentMaxRiskTier,
5097
+ title,
5098
+ description
5099
+ };
5100
+ }
5101
+ function serializeProposal(proposal) {
5102
+ return {
5103
+ kind: asNum(proposal.kind),
5104
+ configRiskTier: asNum(proposal.configRiskTier),
5105
+ origin: asNum(proposal.origin),
5106
+ status: asNum(proposal.status),
5107
+ proposer: proposal.proposer,
5108
+ threadId: asNum(proposal.threadId),
5109
+ petitionId: asNum(proposal.petitionId),
5110
+ createdAt: asNum(proposal.createdAt),
5111
+ deliberationEndsAt: asNum(proposal.deliberationEndsAt),
5112
+ voteStartAt: asNum(proposal.voteStartAt),
5113
+ voteEndAt: asNum(proposal.voteEndAt),
5114
+ timelockEndsAt: asNum(proposal.timelockEndsAt),
5115
+ activeSeatsSnapshot: asNum(proposal.activeSeatsSnapshot),
5116
+ forVotes: proposal.forVotes.toString(),
5117
+ againstVotes: proposal.againstVotes.toString(),
5118
+ abstainVotes: proposal.abstainVotes.toString(),
5119
+ amount: proposal.amount.toString(),
5120
+ snapshotAssetBalance: proposal.snapshotAssetBalance.toString(),
5121
+ transferIntent: proposal.transferIntent,
5122
+ intentDeadline: asNum(proposal.intentDeadline),
5123
+ intentMaxRiskTier: asNum(proposal.intentMaxRiskTier),
5124
+ title: proposal.title,
5125
+ description: proposal.description
5126
+ };
5127
+ }
4780
5128
  var governance = Cli3.create("governance", {
4781
- description: "Read governance proposals and params."
5129
+ description: "Inspect Assembly governance proposals, votes, and parameters."
4782
5130
  });
4783
5131
  governance.command("proposals", {
5132
+ description: "List governance proposals with status and vote end time.",
4784
5133
  env: env3,
5134
+ output: z3.array(
5135
+ z3.object({
5136
+ id: z3.number(),
5137
+ kind: z3.number(),
5138
+ status: z3.number(),
5139
+ title: z3.string().nullable().optional(),
5140
+ voteEndAt: z3.number(),
5141
+ voteEndRelative: z3.string()
5142
+ })
5143
+ ),
5144
+ examples: [{ description: "List all proposals" }],
4785
5145
  async run(c) {
4786
5146
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4787
5147
  const count = await client.readContract({
@@ -4790,7 +5150,7 @@ governance.command("proposals", {
4790
5150
  functionName: "proposalCount"
4791
5151
  });
4792
5152
  const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
4793
- const proposals = ids.length ? await client.multicall({
5153
+ const proposalTuples = ids.length ? await client.multicall({
4794
5154
  allowFailure: false,
4795
5155
  contracts: ids.map((id) => ({
4796
5156
  abi: governanceAbi,
@@ -4799,12 +5159,13 @@ governance.command("proposals", {
4799
5159
  args: [id]
4800
5160
  }))
4801
5161
  }) : [];
5162
+ const proposals = proposalTuples.map(decodeProposal);
4802
5163
  return c.ok(
4803
5164
  proposals.map((p, i) => ({
4804
5165
  id: i + 1,
4805
5166
  kind: asNum(p.kind),
4806
5167
  status: asNum(p.status),
4807
- title: p.title,
5168
+ title: p.title ?? null,
4808
5169
  voteEndAt: asNum(p.voteEndAt),
4809
5170
  voteEndRelative: relTime(p.voteEndAt)
4810
5171
  })),
@@ -4821,22 +5182,47 @@ governance.command("proposals", {
4821
5182
  }
4822
5183
  });
4823
5184
  governance.command("proposal", {
4824
- args: z3.object({ id: z3.coerce.number().int().positive() }),
5185
+ description: "Get full raw proposal details by proposal id.",
5186
+ args: z3.object({
5187
+ id: z3.coerce.number().int().positive().describe("Proposal id (1-indexed)")
5188
+ }),
4825
5189
  env: env3,
5190
+ output: z3.record(z3.string(), z3.unknown()),
5191
+ examples: [{ args: { id: 1 }, description: "Fetch proposal #1" }],
4826
5192
  async run(c) {
4827
5193
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4828
- const proposal = await client.readContract({
4829
- abi: governanceAbi,
4830
- address: ABSTRACT_MAINNET_ADDRESSES.governance,
4831
- functionName: "proposals",
4832
- args: [BigInt(c.args.id)]
4833
- });
4834
- return c.ok(proposal);
5194
+ const proposal = decodeProposal(
5195
+ await client.readContract({
5196
+ abi: governanceAbi,
5197
+ address: ABSTRACT_MAINNET_ADDRESSES.governance,
5198
+ functionName: "proposals",
5199
+ args: [BigInt(c.args.id)]
5200
+ })
5201
+ );
5202
+ return c.ok(serializeProposal(proposal));
4835
5203
  }
4836
5204
  });
4837
5205
  governance.command("has-voted", {
4838
- args: z3.object({ proposalId: z3.coerce.number().int().positive(), address: z3.string() }),
5206
+ description: "Check if an address has voted on a proposal.",
5207
+ args: z3.object({
5208
+ proposalId: z3.coerce.number().int().positive().describe("Proposal id (1-indexed)"),
5209
+ address: z3.string().describe("Voter address")
5210
+ }),
4839
5211
  env: env3,
5212
+ output: z3.object({
5213
+ proposalId: z3.number(),
5214
+ address: z3.string(),
5215
+ hasVoted: z3.boolean()
5216
+ }),
5217
+ examples: [
5218
+ {
5219
+ args: {
5220
+ proposalId: 1,
5221
+ address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
5222
+ },
5223
+ description: "Check whether an address already voted"
5224
+ }
5225
+ ],
4840
5226
  async run(c) {
4841
5227
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4842
5228
  const hasVoted = await client.readContract({
@@ -4845,11 +5231,31 @@ governance.command("has-voted", {
4845
5231
  functionName: "hasVoted",
4846
5232
  args: [BigInt(c.args.proposalId), c.args.address]
4847
5233
  });
4848
- return c.ok({ proposalId: c.args.proposalId, address: c.args.address, hasVoted });
5234
+ return c.ok({
5235
+ proposalId: c.args.proposalId,
5236
+ address: toChecksum(c.args.address),
5237
+ hasVoted
5238
+ });
4849
5239
  }
4850
5240
  });
4851
5241
  governance.command("params", {
5242
+ description: "Read governance threshold and timing parameters.",
4852
5243
  env: env3,
5244
+ output: z3.object({
5245
+ deliberationPeriod: z3.number(),
5246
+ votePeriod: z3.number(),
5247
+ quorumBps: z3.number(),
5248
+ constitutionalDeliberationPeriod: z3.number(),
5249
+ constitutionalVotePeriod: z3.number(),
5250
+ constitutionalPassBps: z3.number(),
5251
+ majorPassBps: z3.number(),
5252
+ parameterPassBps: z3.number(),
5253
+ significantPassBps: z3.number(),
5254
+ significantThresholdBps: z3.number(),
5255
+ routineThresholdBps: z3.number(),
5256
+ timelockPeriod: z3.number()
5257
+ }),
5258
+ examples: [{ description: "Inspect governance timing and pass thresholds" }],
4853
5259
  async run(c) {
4854
5260
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4855
5261
  const getters = [
@@ -4874,33 +5280,171 @@ governance.command("params", {
4874
5280
  functionName: name
4875
5281
  }))
4876
5282
  });
4877
- return c.ok(Object.fromEntries(getters.map((k, i) => [k, asNum(values[i])])));
5283
+ return c.ok({
5284
+ deliberationPeriod: asNum(values[0]),
5285
+ votePeriod: asNum(values[1]),
5286
+ quorumBps: asNum(values[2]),
5287
+ constitutionalDeliberationPeriod: asNum(values[3]),
5288
+ constitutionalVotePeriod: asNum(values[4]),
5289
+ constitutionalPassBps: asNum(values[5]),
5290
+ majorPassBps: asNum(values[6]),
5291
+ parameterPassBps: asNum(values[7]),
5292
+ significantPassBps: asNum(values[8]),
5293
+ significantThresholdBps: asNum(values[9]),
5294
+ routineThresholdBps: asNum(values[10]),
5295
+ timelockPeriod: asNum(values[11])
5296
+ });
4878
5297
  }
4879
5298
  });
4880
5299
 
4881
5300
  // src/commands/members.ts
4882
5301
  import { Cli as Cli4, z as z4 } from "incur";
5302
+ var DEFAULT_MEMBER_SNAPSHOT_URL = "https://www.theaiassembly.org/api/indexer/members";
5303
+ var REGISTERED_EVENT_SCAN_STEP = 100000n;
4883
5304
  var env4 = z4.object({
4884
- ABSTRACT_RPC_URL: z4.string().optional(),
4885
- ASSEMBLY_INDEXER_URL: z4.string().optional()
5305
+ ABSTRACT_RPC_URL: z4.string().optional().describe("Abstract RPC URL override"),
5306
+ ASSEMBLY_INDEXER_URL: z4.string().optional().describe("Optional members snapshot endpoint (default: theaiassembly.org indexer)")
4886
5307
  });
5308
+ var memberSnapshotSchema = z4.array(z4.string());
5309
+ var AssemblyApiValidationError = class extends Error {
5310
+ constructor(details) {
5311
+ super("Assembly API response validation failed");
5312
+ this.details = details;
5313
+ this.name = "AssemblyApiValidationError";
5314
+ }
5315
+ };
5316
+ var AssemblyIndexerUnavailableError = class extends Error {
5317
+ constructor(details) {
5318
+ super("Assembly indexer unavailable");
5319
+ this.details = details;
5320
+ this.name = "AssemblyIndexerUnavailableError";
5321
+ }
5322
+ };
4887
5323
  async function memberSnapshot(url) {
4888
- const res = await fetch(url);
4889
- if (!res.ok) return [];
5324
+ let res;
5325
+ try {
5326
+ res = await fetch(url);
5327
+ } catch (error) {
5328
+ throw new AssemblyIndexerUnavailableError({
5329
+ code: "ASSEMBLY_INDEXER_UNAVAILABLE",
5330
+ url,
5331
+ reason: error instanceof Error ? error.message : String(error)
5332
+ });
5333
+ }
5334
+ if (!res.ok) {
5335
+ throw new AssemblyIndexerUnavailableError({
5336
+ code: "ASSEMBLY_INDEXER_UNAVAILABLE",
5337
+ url,
5338
+ status: res.status,
5339
+ statusText: res.statusText
5340
+ });
5341
+ }
4890
5342
  const json = await res.json();
4891
- if (!Array.isArray(json)) return [];
4892
- return json.filter((x) => typeof x === "string");
5343
+ const parsed = memberSnapshotSchema.safeParse(json);
5344
+ if (parsed.success) {
5345
+ return parsed.data;
5346
+ }
5347
+ throw new AssemblyApiValidationError({
5348
+ code: "INVALID_ASSEMBLY_API_RESPONSE",
5349
+ url,
5350
+ issues: parsed.error.issues,
5351
+ response: json
5352
+ });
5353
+ }
5354
+ async function membersFromRegisteredEvents(client) {
5355
+ const latestBlock = await client.getBlockNumber();
5356
+ const addresses = /* @__PURE__ */ new Set();
5357
+ for (let fromBlock = 0n; fromBlock <= latestBlock; fromBlock += REGISTERED_EVENT_SCAN_STEP) {
5358
+ const toBlock = fromBlock + REGISTERED_EVENT_SCAN_STEP - 1n > latestBlock ? latestBlock : fromBlock + REGISTERED_EVENT_SCAN_STEP - 1n;
5359
+ const events = await client.getContractEvents({
5360
+ abi: registryAbi,
5361
+ address: ABSTRACT_MAINNET_ADDRESSES.registry,
5362
+ eventName: "Registered",
5363
+ fromBlock,
5364
+ toBlock,
5365
+ strict: true
5366
+ });
5367
+ for (const event of events) {
5368
+ const member = event.args.member;
5369
+ if (typeof member === "string") {
5370
+ addresses.add(member);
5371
+ }
5372
+ }
5373
+ }
5374
+ return [...addresses];
5375
+ }
5376
+ function indexerIssue(details) {
5377
+ if (typeof details.status === "number") {
5378
+ return `${details.status}${details.statusText ? ` ${details.statusText}` : ""}`;
5379
+ }
5380
+ if (details.reason) return details.reason;
5381
+ return "unknown error";
5382
+ }
5383
+ function emitIndexerFallbackWarning(details) {
5384
+ process.stderr.write(
5385
+ `${JSON.stringify({
5386
+ level: "warn",
5387
+ code: details.code,
5388
+ message: "Member snapshot indexer is unavailable. Falling back to on-chain Registered events.",
5389
+ url: details.url,
5390
+ issue: indexerIssue(details)
5391
+ })}
5392
+ `
5393
+ );
4893
5394
  }
4894
5395
  var members = Cli4.create("members", {
4895
- description: "Read Assembly membership state from Registry."
5396
+ description: "Inspect Assembly membership and registry fee state."
4896
5397
  });
4897
5398
  members.command("list", {
4898
- description: "List members from indexer snapshot + onchain status.",
5399
+ description: "List members from an indexer snapshot (or Registered event fallback) plus on-chain active state.",
4899
5400
  env: env4,
5401
+ output: z4.array(
5402
+ z4.object({
5403
+ address: z4.string(),
5404
+ active: z4.boolean(),
5405
+ registered: z4.boolean(),
5406
+ activeUntil: z4.number(),
5407
+ activeUntilRelative: z4.string(),
5408
+ lastHeartbeatAt: z4.number(),
5409
+ lastHeartbeatRelative: z4.string()
5410
+ })
5411
+ ),
5412
+ examples: [
5413
+ { description: "List members using default indexer snapshot" },
5414
+ { description: "Override ASSEMBLY_INDEXER_URL to use a custom snapshot source" }
5415
+ ],
4900
5416
  async run(c) {
4901
5417
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4902
- const snapshotUrl = c.env.ASSEMBLY_INDEXER_URL ?? "https://www.theaiassembly.org/api/indexer/members";
4903
- const addresses = await memberSnapshot(snapshotUrl);
5418
+ const snapshotUrl = c.env.ASSEMBLY_INDEXER_URL ?? DEFAULT_MEMBER_SNAPSHOT_URL;
5419
+ let addresses;
5420
+ let fallbackReason;
5421
+ try {
5422
+ addresses = await memberSnapshot(snapshotUrl);
5423
+ } catch (error) {
5424
+ if (error instanceof AssemblyApiValidationError) {
5425
+ return c.error({
5426
+ code: error.details.code,
5427
+ message: `Member snapshot response failed validation. url=${error.details.url}; issues=${JSON.stringify(error.details.issues)}; response=${JSON.stringify(error.details.response)}`,
5428
+ retryable: false
5429
+ });
5430
+ }
5431
+ if (!(error instanceof AssemblyIndexerUnavailableError)) {
5432
+ throw error;
5433
+ }
5434
+ fallbackReason = error.details;
5435
+ try {
5436
+ addresses = await membersFromRegisteredEvents(client);
5437
+ } catch (fallbackError) {
5438
+ return c.error({
5439
+ code: "MEMBER_LIST_SOURCE_UNAVAILABLE",
5440
+ message: `Member indexer unavailable (${indexerIssue(error.details)} at ${error.details.url}) and on-chain Registered event fallback failed: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}`,
5441
+ retryable: true
5442
+ });
5443
+ }
5444
+ }
5445
+ if (fallbackReason) {
5446
+ emitIndexerFallbackWarning(fallbackReason);
5447
+ }
4904
5448
  const calls = addresses.flatMap((address) => [
4905
5449
  {
4906
5450
  abi: registryAbi,
@@ -4931,16 +5475,32 @@ members.command("list", {
4931
5475
  });
4932
5476
  return c.ok(rows, {
4933
5477
  cta: {
4934
- description: "Inspect one member:",
5478
+ description: fallbackReason ? `Indexer unavailable (${indexerIssue(fallbackReason)}); using on-chain Registered event fallback. Inspect one member:` : "Inspect one member:",
4935
5479
  commands: [{ command: "members info", args: { address: "<addr>" } }]
4936
5480
  }
4937
5481
  });
4938
5482
  }
4939
5483
  });
4940
5484
  members.command("info", {
4941
- description: "Read member info for an address.",
4942
- args: z4.object({ address: z4.string() }),
5485
+ description: "Get registry record and active status for a member address.",
5486
+ args: z4.object({
5487
+ address: z4.string().describe("Member wallet address")
5488
+ }),
4943
5489
  env: env4,
5490
+ output: z4.object({
5491
+ address: z4.string(),
5492
+ active: z4.boolean(),
5493
+ activeUntil: z4.number(),
5494
+ lastHeartbeatAt: z4.number(),
5495
+ activeUntilRelative: z4.string(),
5496
+ lastHeartbeatRelative: z4.string()
5497
+ }),
5498
+ examples: [
5499
+ {
5500
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
5501
+ description: "Inspect one member address"
5502
+ }
5503
+ ],
4944
5504
  async run(c) {
4945
5505
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4946
5506
  const [member, active] = await Promise.all([
@@ -4960,15 +5520,21 @@ members.command("info", {
4960
5520
  return c.ok({
4961
5521
  address: toChecksum(c.args.address),
4962
5522
  active,
4963
- ...member,
5523
+ activeUntil: Number(member.activeUntil),
5524
+ lastHeartbeatAt: Number(member.lastHeartbeatAt),
4964
5525
  activeUntilRelative: relTime(member.activeUntil),
4965
5526
  lastHeartbeatRelative: relTime(member.lastHeartbeatAt)
4966
5527
  });
4967
5528
  }
4968
5529
  });
4969
5530
  members.command("count", {
4970
- description: "Read active + known member counts.",
5531
+ description: "Get active and total-known member counts from Registry.",
4971
5532
  env: env4,
5533
+ output: z4.object({
5534
+ active: z4.number(),
5535
+ total: z4.number()
5536
+ }),
5537
+ examples: [{ description: "Count active and known members" }],
4972
5538
  async run(c) {
4973
5539
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4974
5540
  const [active, total] = await Promise.all([
@@ -4987,8 +5553,16 @@ members.command("count", {
4987
5553
  }
4988
5554
  });
4989
5555
  members.command("fees", {
4990
- description: "Read registry fee config.",
5556
+ description: "Get registration and heartbeat fee settings.",
4991
5557
  env: env4,
5558
+ output: z4.object({
5559
+ registrationFeeWei: z4.string(),
5560
+ registrationFee: z4.string(),
5561
+ heartbeatFeeWei: z4.string(),
5562
+ heartbeatFee: z4.string(),
5563
+ heartbeatGracePeriodSeconds: z4.number()
5564
+ }),
5565
+ examples: [{ description: "Inspect current registry fee configuration" }],
4992
5566
  async run(c) {
4993
5567
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
4994
5568
  const [registrationFee, heartbeatFee, heartbeatGracePeriod] = await Promise.all([
@@ -5020,10 +5594,21 @@ members.command("fees", {
5020
5594
 
5021
5595
  // src/commands/treasury.ts
5022
5596
  import { Cli as Cli5, z as z5 } from "incur";
5023
- var env5 = z5.object({ ABSTRACT_RPC_URL: z5.string().optional() });
5024
- var treasury = Cli5.create("treasury", { description: "Read treasury state." });
5597
+ var env5 = z5.object({
5598
+ ABSTRACT_RPC_URL: z5.string().optional().describe("Abstract RPC URL override")
5599
+ });
5600
+ var treasury = Cli5.create("treasury", {
5601
+ description: "Inspect treasury balances, execution status, and spend controls."
5602
+ });
5025
5603
  treasury.command("balance", {
5604
+ description: "Get current native token balance for the treasury contract.",
5026
5605
  env: env5,
5606
+ output: z5.object({
5607
+ address: z5.string(),
5608
+ balanceWei: z5.string(),
5609
+ balance: z5.string()
5610
+ }),
5611
+ examples: [{ description: "Check treasury balance" }],
5027
5612
  async run(c) {
5028
5613
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5029
5614
  const balance = await client.getBalance({ address: ABSTRACT_MAINNET_ADDRESSES.treasury });
@@ -5035,8 +5620,21 @@ treasury.command("balance", {
5035
5620
  }
5036
5621
  });
5037
5622
  treasury.command("whitelist", {
5038
- args: z5.object({ asset: z5.string() }),
5623
+ description: "Check whether an asset address is treasury-whitelisted.",
5624
+ args: z5.object({
5625
+ asset: z5.string().describe("Token/asset contract address")
5626
+ }),
5039
5627
  env: env5,
5628
+ output: z5.object({
5629
+ asset: z5.string(),
5630
+ whitelisted: z5.boolean()
5631
+ }),
5632
+ examples: [
5633
+ {
5634
+ args: { asset: "0x0000000000000000000000000000000000000000" },
5635
+ description: "Check whitelist status for one asset"
5636
+ }
5637
+ ],
5040
5638
  async run(c) {
5041
5639
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5042
5640
  const whitelisted = await client.readContract({
@@ -5049,7 +5647,15 @@ treasury.command("whitelist", {
5049
5647
  }
5050
5648
  });
5051
5649
  treasury.command("major-spend-status", {
5650
+ description: "Read major-spend cooldown status for the treasury contract.",
5052
5651
  env: env5,
5652
+ output: z5.object({
5653
+ majorSpendCooldownSeconds: z5.number(),
5654
+ lastMajorSpendAt: z5.number(),
5655
+ lastMajorSpendRelative: z5.string(),
5656
+ isMajorSpendAllowed: z5.boolean()
5657
+ }),
5658
+ examples: [{ description: "Inspect treasury major-spend guardrails" }],
5053
5659
  async run(c) {
5054
5660
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5055
5661
  const [cooldown, lastMajorSpendAt, allowed] = await Promise.all([
@@ -5080,8 +5686,16 @@ treasury.command("major-spend-status", {
5080
5686
  }
5081
5687
  });
5082
5688
  treasury.command("executed", {
5083
- args: z5.object({ proposalId: z5.coerce.number().int().positive() }),
5689
+ description: "Check whether a treasury action for a proposal has executed.",
5690
+ args: z5.object({
5691
+ proposalId: z5.coerce.number().int().positive().describe("Governance proposal id")
5692
+ }),
5084
5693
  env: env5,
5694
+ output: z5.object({
5695
+ proposalId: z5.number(),
5696
+ executed: z5.boolean()
5697
+ }),
5698
+ examples: [{ args: { proposalId: 1 }, description: "Check execution status for proposal #1" }],
5085
5699
  async run(c) {
5086
5700
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5087
5701
  const executed = await client.readContract({
@@ -5103,10 +5717,21 @@ cli.command(council);
5103
5717
  cli.command(forum);
5104
5718
  cli.command(governance);
5105
5719
  cli.command(treasury);
5106
- var rootEnv = z6.object({ ABSTRACT_RPC_URL: z6.string().optional() });
5720
+ var rootEnv = z6.object({
5721
+ ABSTRACT_RPC_URL: z6.string().optional().describe("Abstract RPC URL override")
5722
+ });
5107
5723
  cli.command("status", {
5108
- description: "Cross-contract status snapshot.",
5724
+ description: "Get a cross-contract Assembly snapshot (members, council, governance, treasury).",
5109
5725
  env: rootEnv,
5726
+ output: z6.object({
5727
+ activeMemberCount: z6.number(),
5728
+ seatCount: z6.number(),
5729
+ proposalCount: z6.number(),
5730
+ currentAuctionDay: z6.number(),
5731
+ currentAuctionSlot: z6.number(),
5732
+ treasuryBalance: z6.string()
5733
+ }),
5734
+ examples: [{ description: "Fetch the current Assembly system status" }],
5110
5735
  async run(c) {
5111
5736
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5112
5737
  const [
@@ -5155,9 +5780,25 @@ cli.command("status", {
5155
5780
  }
5156
5781
  });
5157
5782
  cli.command("health", {
5158
- description: "Cross-contract health for one address.",
5159
- args: z6.object({ address: z6.string() }),
5783
+ description: "Check cross-contract health for one address (membership, council, refunds, power).",
5784
+ args: z6.object({
5785
+ address: z6.string().describe("Member or wallet address to inspect")
5786
+ }),
5160
5787
  env: rootEnv,
5788
+ output: z6.object({
5789
+ address: z6.string(),
5790
+ isActive: z6.boolean(),
5791
+ activeUntil: z6.number(),
5792
+ isCouncilMember: z6.boolean(),
5793
+ pendingReturnsWei: z6.string(),
5794
+ votingPower: z6.number()
5795
+ }),
5796
+ examples: [
5797
+ {
5798
+ args: { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" },
5799
+ description: "Inspect one address across Assembly contracts"
5800
+ }
5801
+ ],
5161
5802
  async run(c) {
5162
5803
  const client = createAssemblyPublicClient(c.env.ABSTRACT_RPC_URL);
5163
5804
  const [isActive, member, isCouncilMember, pendingReturns, votingPower] = await Promise.all([
@@ -5193,7 +5834,7 @@ cli.command("health", {
5193
5834
  })
5194
5835
  ]);
5195
5836
  return c.ok({
5196
- address: c.args.address,
5837
+ address: toChecksum(c.args.address),
5197
5838
  isActive,
5198
5839
  activeUntil: Number(member.activeUntil),
5199
5840
  isCouncilMember,