@elgora/cli 12.0.0 → 12.1.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 +3 -1
  2. package/dist/cli.js +451 -152
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -38,6 +38,8 @@ answer.
38
38
  returns an issue list and exits non-zero on failure. It needs no wallet or
39
39
  network and does not replace hosted semantic readiness review.
40
40
  - `spec-commitment` reproduces the challenge's `spec_commitment` offline.
41
+ - `solver:solvable` lists open bounties still before their Submission
42
+ deadline, filtered by escrow amount, time left and challenge keywords.
41
43
  - `solver:check <bounty_id> <folder_or_file>` runs the Submission check that
42
44
  `solver:submit` and `guardian:open` also run, without submitting, and exits
43
45
  1 on any issue.
@@ -164,7 +166,7 @@ for that invocation without touching the environment. It takes a network name
164
166
  passed — for that run only:
165
167
 
166
168
  ```sh
167
- elgora-cli guardian:judgeable # Base mainnet, the default
169
+ elgora-cli spec-commitment bounty_challenge.md # Base mainnet, the default
168
170
  elgora-cli claim --network base-sepolia 7 # testnet, by name
169
171
  elgora-cli claim --network 84532 7 # the same, by chain id
170
172
  export ELGORA_CHAIN_ID=84532 # testnet for every run in this shell
package/dist/cli.js CHANGED
@@ -1233,11 +1233,6 @@ function extractFlagValue(argv, flag) {
1233
1233
  return { value, argv: remaining };
1234
1234
  }
1235
1235
 
1236
- // ../../packages/core/src/bounty-challenge-headings.ts
1237
- function hasLineBreak(text) {
1238
- return /[\r\u2028\u2029]/.test(text);
1239
- }
1240
-
1241
1236
  // ../../packages/core/src/bounty-challenge-markdown.ts
1242
1237
  import { keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
1243
1238
  import { z as z3 } from "zod";
@@ -1346,6 +1341,7 @@ function isKnownProtocolProfile(version) {
1346
1341
 
1347
1342
  // ../../packages/core/src/bounty-challenge-markdown.ts
1348
1343
  var FRONTMATTER_DELIMITER = "---";
1344
+ var YAML_LIST_ITEM = /^\s+-\s/;
1349
1345
  var ALLOWED_FRONTMATTER_KEYS = [
1350
1346
  ...BOUNTY_CHALLENGE_FRONTMATTER_KEYS,
1351
1347
  ...BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS
@@ -1461,6 +1457,15 @@ function splitConstraintsBlock(lines, issues) {
1461
1457
  index += 1;
1462
1458
  const parsed = frontmatterEntry(entry.trim());
1463
1459
  if (!parsed || parsed.value === "") {
1460
+ if (parsed && YAML_LIST_ITEM.test(lines[index + 1] ?? "")) {
1461
+ while (YAML_LIST_ITEM.test(lines[index + 1] ?? "")) index += 1;
1462
+ seenKeys.add(parsed.key);
1463
+ issues.push({
1464
+ path: `frontmatter.constraints.${parsed.key}`,
1465
+ message: `"${parsed.key}" must be an inline list on one line, written as ${parsed.key}: [a, b]`
1466
+ });
1467
+ continue;
1468
+ }
1464
1469
  issues.push({
1465
1470
  path: `frontmatter.constraints.line_${index + 2}`,
1466
1471
  message: "Constraint entries must use snake_case_key: value"
@@ -1500,6 +1505,9 @@ function frontmatterEntry(line) {
1500
1505
  const value = line.slice(key.length).trimStart();
1501
1506
  return hasLineBreak(value) ? null : { key: key.slice(0, -1), value };
1502
1507
  }
1508
+ function hasLineBreak(text) {
1509
+ return /[\r\u2028\u2029]/.test(text);
1510
+ }
1503
1511
  function parsePublicationTermsFrontmatter(lines, issues) {
1504
1512
  const values = /* @__PURE__ */ new Map();
1505
1513
  let foundUnsupportedKey = false;
@@ -4302,6 +4310,9 @@ function settlementAmounts(escrowAmount, treasuryFeeBps, guardianFeeBps) {
4302
4310
  };
4303
4311
  }
4304
4312
 
4313
+ // ../../packages/core/src/api/bounty-list.ts
4314
+ import { z as z6 } from "zod";
4315
+
4305
4316
  // ../../packages/core/src/api/bounty-preparation.ts
4306
4317
  import { ZodError, z as z5 } from "zod";
4307
4318
 
@@ -4349,6 +4360,12 @@ var ELGORA_HUB_OWNERSHIP_READ_ABI = [
4349
4360
  getAbiItem2({ abi: ELGORA_HUB_ABI, name: "pendingOwner" }),
4350
4361
  ...ELGORA_HUB_ERRORS
4351
4362
  ];
4363
+ var SUBMISSION_CAP_READ_ABI = [
4364
+ getAbiItem2({ abi: ELGORA_HUB_ABI, name: "submissionGuard" }),
4365
+ ...parseAbi([
4366
+ "function maximumSubmissions(uint256 bountyId) view returns (uint256)"
4367
+ ])
4368
+ ];
4352
4369
  var ELGORA_HUB_BOUNTY_CREATED_EVENT = getAbiItem2({
4353
4370
  abi: ELGORA_HUB_ABI,
4354
4371
  name: "BountyCreated"
@@ -4541,9 +4558,54 @@ var artifactUploadUrlRequestSchema = z5.object({
4541
4558
  ciphertext_byte_length: z5.number().int().positive().max(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)
4542
4559
  }).strict();
4543
4560
 
4561
+ // ../../packages/core/src/api/bounty-list.ts
4562
+ var BOUNTY_LIST_MAX_FIRST = 100;
4563
+ var integerString = z6.string().regex(/^(0|[1-9]\d*)$/, {
4564
+ message: "Must be a non-negative integer"
4565
+ });
4566
+ var bountyFilterFields = {
4567
+ min_amount: integerString.optional(),
4568
+ max_amount: integerString.optional(),
4569
+ min_time_left: integerString.optional(),
4570
+ max_time_left: integerString.optional()
4571
+ };
4572
+ var bountyListParamsSchema = z6.object({
4573
+ ...bountyFilterFields,
4574
+ status: z6.enum(BOUNTY_STATUSES).optional(),
4575
+ first: integerString.transform(Number).refine((value) => value >= 1 && value <= BOUNTY_LIST_MAX_FIRST, {
4576
+ message: `Must be from 1 to ${BOUNTY_LIST_MAX_FIRST}`
4577
+ }).optional(),
4578
+ skip: integerString.transform(Number).optional()
4579
+ }).strict();
4580
+ var hexSchema = z6.custom(
4581
+ (value) => typeof value === "string" && /^0x[0-9a-f]*$/i.test(value)
4582
+ );
4583
+ var listedBountySchema = z6.object({
4584
+ bounty_id: z6.string(),
4585
+ status: z6.enum(BOUNTY_STATUSES),
4586
+ poster: hexSchema,
4587
+ spec_commitment: hexSchema,
4588
+ escrow: z6.object({ token_address: hexSchema, amount: z6.string() }),
4589
+ submission_deadline: z6.number().int(),
4590
+ submission_count: z6.number().int(),
4591
+ winner: hexSchema.nullable(),
4592
+ created_at: z6.number().int()
4593
+ });
4594
+ var bountyListResponseSchema = z6.object({
4595
+ chain_id: z6.number().int(),
4596
+ hub_address: hexSchema,
4597
+ /** Bounties matching the filter, before `first` and `skip`. */
4598
+ total: z6.number().int(),
4599
+ bounties: z6.array(listedBountySchema)
4600
+ });
4601
+ function challengeMatchesKeywords(challenge, keywords) {
4602
+ const text = challenge.toLowerCase();
4603
+ return keywords.every((keyword) => text.includes(keyword.toLowerCase()));
4604
+ }
4605
+
4544
4606
  // ../../packages/core/src/api/config.ts
4545
4607
  import { createHash } from "crypto";
4546
- import { z as z6 } from "zod";
4608
+ import { z as z7 } from "zod";
4547
4609
  function withDeploymentEnvDefaults(env) {
4548
4610
  const merged = { ...env };
4549
4611
  const setIfBlank = (key, value) => {
@@ -4564,11 +4626,11 @@ function withDeploymentEnvDefaults(env) {
4564
4626
  setIfBlank("ELGORA_SUBGRAPH_ENDPOINT", deployment?.ELGORA_SUBGRAPH_ENDPOINT);
4565
4627
  return merged;
4566
4628
  }
4567
- var chainIdField = z6.coerce.number().int().positive().refine((chainId) => SUPPORTED_CHAINS[chainId] !== void 0, {
4629
+ var chainIdField = z7.coerce.number().int().positive().refine((chainId) => SUPPORTED_CHAINS[chainId] !== void 0, {
4568
4630
  message: `ELGORA_CHAIN_ID must be a supported chain: ${Object.keys(SUPPORTED_CHAINS).join(", ")}`
4569
4631
  });
4570
- var hubAddressField = z6.string().refine(isHexAddress, "ELGORA_HUB_ADDRESS must be a 20-byte EVM address").transform((value) => value.toLowerCase());
4571
- var publicDeploymentEnvSchema = z6.object({
4632
+ var hubAddressField = z7.string().refine(isHexAddress, "ELGORA_HUB_ADDRESS must be a 20-byte EVM address").transform((value) => value.toLowerCase());
4633
+ var publicDeploymentEnvSchema = z7.object({
4572
4634
  ELGORA_CHAIN_ID: chainIdField,
4573
4635
  ELGORA_HUB_ADDRESS: hubAddressField
4574
4636
  });
@@ -4584,30 +4646,30 @@ function loadPublicDeploymentConfig(env = process.env) {
4584
4646
  var envShape = {
4585
4647
  ELGORA_CHAIN_ID: chainIdField,
4586
4648
  ELGORA_HUB_ADDRESS: hubAddressField,
4587
- ELGORA_ESCROW_TOKEN_ADDRESS: z6.string().refine(
4649
+ ELGORA_ESCROW_TOKEN_ADDRESS: z7.string().refine(
4588
4650
  isHexAddress,
4589
4651
  "ELGORA_ESCROW_TOKEN_ADDRESS must be a 20-byte EVM address"
4590
4652
  ).transform((value) => value.toLowerCase()),
4591
- ELGORA_TIMELOCK_REVEAL_DELAY_SECONDS: z6.coerce.number().int().nonnegative().default(DEFAULT_TIMELOCK_REVEAL_DELAY_SECONDS),
4592
- ELGORA_RPC_URL: z6.string().min(1, "ELGORA_RPC_URL is required"),
4653
+ ELGORA_TIMELOCK_REVEAL_DELAY_SECONDS: z7.coerce.number().int().nonnegative().default(DEFAULT_TIMELOCK_REVEAL_DELAY_SECONDS),
4654
+ ELGORA_RPC_URL: z7.string().min(1, "ELGORA_RPC_URL is required"),
4593
4655
  /**
4594
4656
  * Only consulted for a chain with no recorded deployment — a local anvil
4595
4657
  * stack, whose addresses change on every deploy. A recorded deployment's
4596
4658
  * scheme always wins, because settlement validation compares against it.
4597
4659
  */
4598
- ELGORA_PAYOUT_SCHEME_ADDRESS: z6.string().refine(
4660
+ ELGORA_PAYOUT_SCHEME_ADDRESS: z7.string().refine(
4599
4661
  isHexAddress,
4600
4662
  "ELGORA_PAYOUT_SCHEME_ADDRESS must be a 20-byte EVM address"
4601
4663
  ).transform((value) => value.toLowerCase()).optional(),
4602
- ELGORA_DATABASE_URL: z6.string().min(1, "ELGORA_DATABASE_URL is required"),
4603
- ELGORA_SUBGRAPH_ENDPOINT: z6.string().min(1, "ELGORA_SUBGRAPH_ENDPOINT is required"),
4604
- ELGORA_ARTIFACT_STORAGE_ENDPOINT: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_ENDPOINT is required"),
4605
- ELGORA_ARTIFACT_STORAGE_REGION: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_REGION is required"),
4606
- ELGORA_ARTIFACT_STORAGE_BUCKET: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_BUCKET is required"),
4607
- ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID is required"),
4608
- ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY: z6.string().min(1, "ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY is required"),
4609
- ELGORA_DELIVERY_PUBLIC_KEY: z6.string().min(1, "ELGORA_DELIVERY_PUBLIC_KEY is required"),
4610
- ELGORA_DELIVERY_PRIVATE_KEY: z6.string().min(1, "ELGORA_DELIVERY_PRIVATE_KEY is required")
4664
+ ELGORA_DATABASE_URL: z7.string().min(1, "ELGORA_DATABASE_URL is required"),
4665
+ ELGORA_SUBGRAPH_ENDPOINT: z7.string().min(1, "ELGORA_SUBGRAPH_ENDPOINT is required"),
4666
+ ELGORA_ARTIFACT_STORAGE_ENDPOINT: z7.string().min(1, "ELGORA_ARTIFACT_STORAGE_ENDPOINT is required"),
4667
+ ELGORA_ARTIFACT_STORAGE_REGION: z7.string().min(1, "ELGORA_ARTIFACT_STORAGE_REGION is required"),
4668
+ ELGORA_ARTIFACT_STORAGE_BUCKET: z7.string().min(1, "ELGORA_ARTIFACT_STORAGE_BUCKET is required"),
4669
+ ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID: z7.string().min(1, "ELGORA_ARTIFACT_STORAGE_ACCESS_KEY_ID is required"),
4670
+ ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY: z7.string().min(1, "ELGORA_ARTIFACT_STORAGE_SECRET_ACCESS_KEY is required"),
4671
+ ELGORA_DELIVERY_PUBLIC_KEY: z7.string().min(1, "ELGORA_DELIVERY_PUBLIC_KEY is required"),
4672
+ ELGORA_DELIVERY_PRIVATE_KEY: z7.string().min(1, "ELGORA_DELIVERY_PRIVATE_KEY is required")
4611
4673
  };
4612
4674
  var publicationEnvShape = {
4613
4675
  ELGORA_CHAIN_ID: envShape.ELGORA_CHAIN_ID,
@@ -4615,89 +4677,89 @@ var publicationEnvShape = {
4615
4677
  ELGORA_ESCROW_TOKEN_ADDRESS: envShape.ELGORA_ESCROW_TOKEN_ADDRESS,
4616
4678
  ELGORA_DATABASE_URL: envShape.ELGORA_DATABASE_URL,
4617
4679
  ELGORA_SUBGRAPH_ENDPOINT: envShape.ELGORA_SUBGRAPH_ENDPOINT,
4618
- ELGORA_PUBLICATION_REVIEW_ENABLED: z6.string().trim().toLowerCase().pipe(z6.enum(["true", "false"])).default("true").transform((value) => value === "true"),
4619
- ELGORA_READINESS_TIMEOUT_MS: z6.coerce.number().int().min(1).max(READINESS_TIMEOUT_MS).default(READINESS_TIMEOUT_MS)
4680
+ ELGORA_PUBLICATION_REVIEW_ENABLED: z7.string().trim().toLowerCase().pipe(z7.enum(["true", "false"])).default("true").transform((value) => value === "true"),
4681
+ ELGORA_READINESS_TIMEOUT_MS: z7.coerce.number().int().min(1).max(READINESS_TIMEOUT_MS).default(READINESS_TIMEOUT_MS)
4620
4682
  };
4621
- var envSchema = z6.object(envShape);
4622
- var bountyPublicationEnvSchema = z6.object(publicationEnvShape);
4683
+ var envSchema = z7.object(envShape);
4684
+ var bountyPublicationEnvSchema = z7.object(publicationEnvShape);
4623
4685
 
4624
4686
  // ../../packages/core/src/api/solver-submission-responses.ts
4625
4687
  import { isHex as isHex3 } from "viem";
4626
- import { z as z7 } from "zod";
4627
- var hexAddressSchema = z7.string().refine(isHexAddress, "Must be a hex-encoded address");
4628
- var hexBytes32Schema = z7.string().refine(isHexBytes32, "Must be 32-byte hex");
4629
- var hexDataSchema = z7.string().refine((value) => isHex3(value), "Must be hex-encoded calldata");
4630
- var artifactUploadUrlResponseSchema = z7.object({
4631
- locator: z7.string().min(1),
4632
- upload_url: z7.string().min(1),
4633
- method: z7.literal("PUT"),
4634
- expires_at: z7.string().min(1)
4688
+ import { z as z8 } from "zod";
4689
+ var hexAddressSchema = z8.string().refine(isHexAddress, "Must be a hex-encoded address");
4690
+ var hexBytes32Schema = z8.string().refine(isHexBytes32, "Must be 32-byte hex");
4691
+ var hexDataSchema = z8.string().refine((value) => isHex3(value), "Must be hex-encoded calldata");
4692
+ var artifactUploadUrlResponseSchema = z8.object({
4693
+ locator: z8.string().min(1),
4694
+ upload_url: z8.string().min(1),
4695
+ method: z8.literal("PUT"),
4696
+ expires_at: z8.string().min(1)
4635
4697
  });
4636
- var solverSubmissionPrepareResponseSchema = z7.object({
4637
- content_record: z7.object({
4638
- content_kind: z7.literal(SOLVER_SUBMISSION_CONTENT_KIND),
4698
+ var solverSubmissionPrepareResponseSchema = z8.object({
4699
+ content_record: z8.object({
4700
+ content_kind: z8.literal(SOLVER_SUBMISSION_CONTENT_KIND),
4639
4701
  submission_commitment: hexBytes32Schema,
4640
- byte_length: z7.number().int().nonnegative(),
4641
- created_at: z7.string().min(1)
4702
+ byte_length: z8.number().int().nonnegative(),
4703
+ created_at: z8.string().min(1)
4642
4704
  }),
4643
- submit_transaction: z7.object({
4644
- chain_id: z7.number().int().positive(),
4705
+ submit_transaction: z8.object({
4706
+ chain_id: z8.number().int().positive(),
4645
4707
  to: hexAddressSchema,
4646
- value: z7.literal("0"),
4708
+ value: z8.literal("0"),
4647
4709
  data: hexDataSchema,
4648
- function_name: z7.literal("submit"),
4649
- args: z7.object({
4650
- bounty_id: z7.string().min(1),
4710
+ function_name: z8.literal("submit"),
4711
+ args: z8.object({
4712
+ bounty_id: z8.string().min(1),
4651
4713
  submission_commitment: hexBytes32Schema,
4652
4714
  guardian_roster_hash: hexBytes32Schema,
4653
- extra_data: z7.literal("0x")
4715
+ extra_data: z8.literal("0x")
4654
4716
  })
4655
4717
  })
4656
4718
  });
4657
- var solverSubmissionContentResponseSchema = z7.object({
4658
- content_record: z7.object({
4659
- bounty_id: z7.string().min(1),
4660
- canonical_json: z7.string().min(1)
4719
+ var solverSubmissionContentResponseSchema = z8.object({
4720
+ content_record: z8.object({
4721
+ bounty_id: z8.string().min(1),
4722
+ canonical_json: z8.string().min(1)
4661
4723
  }),
4662
- ciphertext_download_url: z7.string().min(1),
4663
- expires_at: z7.string().min(1)
4724
+ ciphertext_download_url: z8.string().min(1),
4725
+ expires_at: z8.string().min(1)
4664
4726
  });
4665
- var bountySubmissionDetailResponseSchema = z7.object({
4666
- bounty: z7.object({
4727
+ var bountySubmissionDetailResponseSchema = z8.object({
4728
+ bounty: z8.object({
4667
4729
  guardian_roster_hash: hexBytes32Schema,
4668
- guardian_roster: z7.array(
4669
- z7.object({
4670
- name: z7.string(),
4730
+ guardian_roster: z8.array(
4731
+ z8.object({
4732
+ name: z8.string(),
4671
4733
  account: hexAddressSchema,
4672
- encryption_public_key: z7.string().min(1)
4734
+ encryption_public_key: z8.string().min(1)
4673
4735
  })
4674
4736
  ).min(1)
4675
4737
  }),
4676
- challenge: z7.string().nullable()
4738
+ challenge: z8.string().nullable()
4677
4739
  });
4678
4740
 
4679
4741
  // ../../packages/core/src/api/verification-records.ts
4680
- import { z as z8 } from "zod";
4681
- var hexAddressSchema2 = z8.string().refine(isHexAddress, "Must be a hex-encoded address").transform((value) => value.toLowerCase());
4682
- var hexBytes32Schema2 = z8.string().refine(isHexBytes32, "Must be 32-byte hex").transform((value) => value.toLowerCase());
4683
- var integerStringSchema = z8.string().regex(/^(0|[1-9][0-9]*)$/);
4684
- var verdictEntrySchema = z8.object({
4685
- name: z8.string().min(1),
4686
- outcome: z8.enum(["awarded", "no_valid_submission"]).nullable(),
4742
+ import { z as z9 } from "zod";
4743
+ var hexAddressSchema2 = z9.string().refine(isHexAddress, "Must be a hex-encoded address").transform((value) => value.toLowerCase());
4744
+ var hexBytes32Schema2 = z9.string().refine(isHexBytes32, "Must be 32-byte hex").transform((value) => value.toLowerCase());
4745
+ var integerStringSchema = z9.string().regex(/^(0|[1-9][0-9]*)$/);
4746
+ var verdictEntrySchema = z9.object({
4747
+ name: z9.string().min(1),
4748
+ outcome: z9.enum(["awarded", "no_valid_submission"]).nullable(),
4687
4749
  winner: hexAddressSchema2.nullable(),
4688
4750
  report_commitment: hexBytes32Schema2.nullable(),
4689
- committed_at: z8.string().datetime({ offset: true }).nullable(),
4690
- supporting: z8.boolean()
4751
+ committed_at: z9.string().datetime({ offset: true }).nullable(),
4752
+ supporting: z9.boolean()
4691
4753
  }).strict();
4692
- var formattedVerificationRecordSchema = z8.object({
4693
- chain_id: z8.number().int().positive(),
4754
+ var formattedVerificationRecordSchema = z9.object({
4755
+ chain_id: z9.number().int().positive(),
4694
4756
  hub_address: hexAddressSchema2,
4695
4757
  bounty_id: integerStringSchema,
4696
- status: z8.enum(["awarded", "no_valid_submission", "timed_out"]),
4758
+ status: z9.enum(["awarded", "no_valid_submission", "timed_out"]),
4697
4759
  spec_commitment: hexBytes32Schema2,
4698
4760
  winner: hexAddressSchema2.nullable(),
4699
- verdicts: z8.record(z8.string(), verdictEntrySchema),
4700
- settlement: z8.object({
4761
+ verdicts: z9.record(z9.string(), verdictEntrySchema),
4762
+ settlement: z9.object({
4701
4763
  escrow_amount: integerStringSchema,
4702
4764
  settlement_recipient: hexAddressSchema2,
4703
4765
  settlement_amount: integerStringSchema,
@@ -4708,10 +4770,10 @@ var formattedVerificationRecordSchema = z8.object({
4708
4770
  settled_tx_hash: hexBytes32Schema2,
4709
4771
  settled_block_number: integerStringSchema
4710
4772
  }).strict(),
4711
- content_record: z8.object({
4712
- spec_content_byte_length: z8.number().int().positive()
4773
+ content_record: z9.object({
4774
+ spec_content_byte_length: z9.number().int().positive()
4713
4775
  }).strict(),
4714
- verified_at: z8.string().datetime({ offset: true })
4776
+ verified_at: z9.string().datetime({ offset: true })
4715
4777
  }).strict();
4716
4778
  function parseBountyVerificationRecord(input, expected) {
4717
4779
  const slice = input;
@@ -4733,66 +4795,66 @@ function parseBountyVerificationRecord(input, expected) {
4733
4795
  }
4734
4796
 
4735
4797
  // ../../packages/core/src/api/winning-solution-delivery.ts
4736
- import { z as z9 } from "zod";
4737
- var hexBytes32Schema3 = z9.string().refine(isHexBytes32, "Must be 32-byte hex").refine((value) => value !== ZERO_BYTES32, "Must not be zero").transform((value) => value.toLowerCase());
4738
- var hexAddressSchema3 = z9.string().refine((value) => /^0x[0-9a-fA-F]{40}$/.test(value), "Must be an address").transform((value) => value.toLowerCase());
4739
- var deliveryKeyIdSchema = z9.string().min(1).max(128);
4740
- var winningSolutionWrappedKeySchema = z9.strictObject({
4741
- enc: z9.string().min(1),
4742
- ciphertext: z9.string().min(1)
4798
+ import { z as z10 } from "zod";
4799
+ var hexBytes32Schema3 = z10.string().refine(isHexBytes32, "Must be 32-byte hex").refine((value) => value !== ZERO_BYTES32, "Must not be zero").transform((value) => value.toLowerCase());
4800
+ var hexAddressSchema3 = z10.string().refine((value) => /^0x[0-9a-fA-F]{40}$/.test(value), "Must be an address").transform((value) => value.toLowerCase());
4801
+ var deliveryKeyIdSchema = z10.string().min(1).max(128);
4802
+ var winningSolutionWrappedKeySchema = z10.strictObject({
4803
+ enc: z10.string().min(1),
4804
+ ciphertext: z10.string().min(1)
4743
4805
  });
4744
- var deliveryWrapRequestSchema = z9.strictObject({
4806
+ var deliveryWrapRequestSchema = z10.strictObject({
4745
4807
  solver_address: hexAddressSchema3,
4746
4808
  submission_commitment: hexBytes32Schema3,
4747
4809
  wrapped_key: winningSolutionWrappedKeySchema
4748
4810
  });
4749
- var deliveryWrapRecordSchema = z9.strictObject({
4750
- bounty_id: z9.string().min(1),
4811
+ var deliveryWrapRecordSchema = z10.strictObject({
4812
+ bounty_id: z10.string().min(1),
4751
4813
  guardian_address: hexAddressSchema3,
4752
4814
  solver_address: hexAddressSchema3,
4753
4815
  submission_commitment: hexBytes32Schema3,
4754
4816
  delivery_key_id: deliveryKeyIdSchema,
4755
- created_at: z9.string().min(1)
4817
+ created_at: z10.string().min(1)
4756
4818
  });
4757
- var deliveryWrapResponseSchema = z9.strictObject({
4758
- status: z9.literal("stored"),
4819
+ var deliveryWrapResponseSchema = z10.strictObject({
4820
+ status: z10.literal("stored"),
4759
4821
  wrap: deliveryWrapRecordSchema
4760
4822
  });
4761
- var deliveryRetrieveRequestSchema = z9.strictObject({
4823
+ var deliveryRetrieveRequestSchema = z10.strictObject({
4762
4824
  poster_ephemeral_public_key: x25519PublicKeySchema
4763
4825
  });
4764
- var deliveryRetrieveResponseSchema = z9.strictObject({
4826
+ var deliveryRetrieveResponseSchema = z10.strictObject({
4765
4827
  submission_commitment: hexBytes32Schema3,
4766
4828
  wrapped_key: winningSolutionWrappedKeySchema
4767
4829
  });
4768
- var deliveryStatusResponseSchema = z9.strictObject({
4769
- exists: z9.boolean()
4830
+ var deliveryStatusResponseSchema = z10.strictObject({
4831
+ exists: z10.boolean()
4770
4832
  });
4771
- var deliveryKeyConfigResponseSchema = z9.strictObject({
4833
+ var deliveryKeyConfigResponseSchema = z10.strictObject({
4772
4834
  delivery_key_id: deliveryKeyIdSchema,
4773
- public_key: z9.string().min(1)
4835
+ public_key: z10.string().min(1)
4774
4836
  });
4775
4837
 
4776
4838
  // ../../packages/core/src/api/written-verdicts.ts
4777
4839
  import { keccak256 as keccak2564, toBytes as toBytes3, zeroAddress } from "viem";
4778
- import { z as z10 } from "zod";
4840
+ import { z as z11 } from "zod";
4779
4841
  var WRITTEN_VERDICT_FORMAT_VERSION = 0;
4780
4842
  var FRONTMATTER_DELIMITER2 = "---";
4781
- var decimalSchema = z10.string().regex(/^[1-9][0-9]*$/, "Must be a positive integer").transform(Number).refine(Number.isSafeInteger, "Must be a safe integer");
4782
- var bountyIdSchema2 = z10.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
4783
- var addressSchema2 = z10.string().refine(isHexAddress, "Must be a hex-encoded address").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
4784
- var bytes32Schema3 = z10.string().refine(isHexBytes32, "Must be 32-byte hex").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
4843
+ var decimalSchema = z11.string().regex(/^[1-9][0-9]*$/, "Must be a positive integer").transform(Number).refine(Number.isSafeInteger, "Must be a safe integer");
4844
+ var bountyIdSchema2 = z11.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
4845
+ var addressSchema2 = z11.string().refine(isHexAddress, "Must be a hex-encoded address").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
4846
+ var bytes32Schema3 = z11.string().refine(isHexBytes32, "Must be 32-byte hex").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
4785
4847
  var commonFields = {
4786
- format_version: z10.literal(WRITTEN_VERDICT_FORMAT_VERSION.toString()).transform(() => WRITTEN_VERDICT_FORMAT_VERSION),
4848
+ format_version: z11.literal(WRITTEN_VERDICT_FORMAT_VERSION.toString()).transform(() => WRITTEN_VERDICT_FORMAT_VERSION),
4787
4849
  chain_id: decimalSchema,
4788
4850
  hub_address: addressSchema2,
4789
4851
  bounty_id: bountyIdSchema2,
4790
- verdict_body: z10.string().refine((value) => value.trim().length > 0, "Must not be empty")
4852
+ verdict_body: z11.string().refine((value) => value.trim().length > 0, "Must not be empty")
4791
4853
  };
4792
- var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
4793
- z10.object({
4854
+ var writtenVerdictSchema = z11.discriminatedUnion("outcome", [
4855
+ z11.object({
4794
4856
  ...commonFields,
4795
- outcome: z10.literal("awarded"),
4857
+ outcome: z11.literal("awarded"),
4796
4858
  winner: addressSchema2.refine(
4797
4859
  (value) => value !== zeroAddress,
4798
4860
  "Must not be the zero address"
@@ -4802,10 +4864,10 @@ var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
4802
4864
  "Must not be zero bytes"
4803
4865
  )
4804
4866
  }).strict(),
4805
- z10.object({
4867
+ z11.object({
4806
4868
  ...commonFields,
4807
- outcome: z10.literal("no_valid_submission"),
4808
- winner: z10.literal(zeroAddress)
4869
+ outcome: z11.literal("no_valid_submission"),
4870
+ winner: z11.literal(zeroAddress)
4809
4871
  }).strict()
4810
4872
  ]);
4811
4873
  function buildWrittenVerdictMarkdown(input) {
@@ -4900,17 +4962,17 @@ import { parseEventLogs } from "viem";
4900
4962
 
4901
4963
  // ../../packages/core/src/api-client-config.ts
4902
4964
  var LOCAL_DEV_API_BASE_URL = "http://127.0.0.1:3000";
4903
- function loadApiClientConfig(env, apiBaseUrlOverride) {
4904
- return { apiBaseUrl: resolveApiBaseUrl(env, apiBaseUrlOverride) };
4965
+ function loadApiClientConfig(env) {
4966
+ return { apiBaseUrl: resolveApiBaseUrl(env) };
4905
4967
  }
4906
- function loadClientDeploymentConfig(env, apiBaseUrlOverride) {
4968
+ function loadClientDeploymentConfig(env) {
4907
4969
  return {
4908
4970
  ...loadPublicDeploymentConfig(env),
4909
- ...loadApiClientConfig(env, apiBaseUrlOverride)
4971
+ ...loadApiClientConfig(env)
4910
4972
  };
4911
4973
  }
4912
- function resolveApiBaseUrl(env, override) {
4913
- const value = override?.trim() || env.ELGORA_API_BASE_URL?.trim() || defaultApiBaseUrl(env);
4974
+ function resolveApiBaseUrl(env) {
4975
+ const value = env.ELGORA_API_BASE_URL?.trim() || defaultApiBaseUrl(env);
4914
4976
  try {
4915
4977
  new URL(value);
4916
4978
  } catch {
@@ -6221,7 +6283,7 @@ function typedDataForRequest(request, block) {
6221
6283
  }
6222
6284
 
6223
6285
  // ../../packages/core/src/auth/request-approval-header.ts
6224
- import { z as z11 } from "zod";
6286
+ import { z as z12 } from "zod";
6225
6287
  async function buildAuthorizationHeader(params) {
6226
6288
  const url = new URL(params.requestUrl);
6227
6289
  const typedData = typedDataForRequest(
@@ -6248,8 +6310,8 @@ async function authorizeRequest(params) {
6248
6310
  block: await readChallengeBlock(params)
6249
6311
  });
6250
6312
  }
6251
- var challengeBlockSchema = z11.object({
6252
- error: z11.object({ block_number: z11.string(), block_hash: z11.string() })
6313
+ var challengeBlockSchema = z12.object({
6314
+ error: z12.object({ block_number: z12.string(), block_hash: z12.string() })
6253
6315
  });
6254
6316
  async function readChallengeBlock(params) {
6255
6317
  const response = await fetch(params.requestUrl, {
@@ -6623,10 +6685,10 @@ function readSubmissionArchive(archive) {
6623
6685
  assertSubmissionFiles(files);
6624
6686
  return { files, ...describeFiles(files) };
6625
6687
  }
6626
- function requireCurrentFormat(alg, bountyId, command18) {
6688
+ function requireCurrentFormat(alg, bountyId, command20) {
6627
6689
  if (alg !== SUBMISSION_ENCRYPTION_TYPE_V1) {
6628
6690
  throw new Error(
6629
- `Bounty ${bountyId} declares ${BOUNTY_CHALLENGE_PROFILE}, which this CLI does not handle. Use CLI 11 for it: npx @elgora/cli@11 ${command18}`
6691
+ `Bounty ${bountyId} declares ${BOUNTY_CHALLENGE_PROFILE}, which this CLI does not handle. Use CLI 11 for it: npx @elgora/cli@11 ${command20}`
6630
6692
  );
6631
6693
  }
6632
6694
  }
@@ -7108,6 +7170,8 @@ var BOUNTY_FIELDS = `
7108
7170
  submissionCount
7109
7171
  createdTxHash
7110
7172
  createdAt
7173
+ createdBlockNumber
7174
+ createdLogIndex
7111
7175
  consensusReachedAt
7112
7176
  consensusOutcome
7113
7177
  consensusWinner
@@ -7115,6 +7179,7 @@ var BOUNTY_FIELDS = `
7115
7179
  settledTxHash
7116
7180
  settledAt
7117
7181
  settledBlockNumber
7182
+ settledLogIndex
7118
7183
  updatedAt
7119
7184
  `;
7120
7185
  var LIST_BOUNTIES_QUERY = `
@@ -7141,6 +7206,7 @@ var SUBMISSION_FIELDS = `
7141
7206
  submissionCommitment
7142
7207
  recordedAt
7143
7208
  recordedBlockNumber
7209
+ recordedLogIndex
7144
7210
  recordedTxHash
7145
7211
  submissionCallCount
7146
7212
  `;
@@ -7153,8 +7219,18 @@ var VERDICT_FIELDS = `
7153
7219
  payoutData
7154
7220
  committedTxHash
7155
7221
  committedAt
7222
+ committedBlockNumber
7223
+ committedLogIndex
7156
7224
  updatedAt
7157
7225
  `;
7226
+ var CLAIM_FIELDS = `
7227
+ claimant
7228
+ amount
7229
+ claimedAt
7230
+ claimedBlockNumber
7231
+ claimedLogIndex
7232
+ claimedTxHash
7233
+ `;
7158
7234
  var GUARDIAN_ROSTER_SNAPSHOT_FIELDS = `
7159
7235
  id
7160
7236
  memberCount
@@ -7181,7 +7257,6 @@ var BOUNTY_DETAIL_QUERY = `
7181
7257
  reportCommitments
7182
7258
  awardedPayoutData
7183
7259
  timedOutRefundAmount
7184
- settledLogIndex
7185
7260
  claimsQueued(first: 1, orderBy: queuedLogIndex, orderDirection: desc) {
7186
7261
  recipient
7187
7262
  amount
@@ -7197,6 +7272,9 @@ var BOUNTY_DETAIL_QUERY = `
7197
7272
  verdicts {
7198
7273
  ${VERDICT_FIELDS}
7199
7274
  }
7275
+ claims(orderBy: claimedAt, orderDirection: asc) {
7276
+ ${CLAIM_FIELDS}
7277
+ }
7200
7278
  }
7201
7279
  }
7202
7280
  `;
@@ -7208,6 +7286,7 @@ var ANALYTICS_SNAPSHOT_QUERY = `
7208
7286
  $rosterSkip: Int!
7209
7287
  $submissionSkip: Int!
7210
7288
  $verdictSkip: Int!
7289
+ $claimSkip: Int!
7211
7290
  $block: Block_height
7212
7291
  ) {
7213
7292
  _meta(block: $block) {
@@ -7260,6 +7339,16 @@ var ANALYTICS_SNAPSHOT_QUERY = `
7260
7339
  bountyId
7261
7340
  ${VERDICT_FIELDS}
7262
7341
  }
7342
+ claims(
7343
+ first: $first
7344
+ skip: $claimSkip
7345
+ orderBy: id
7346
+ orderDirection: asc
7347
+ block: $block
7348
+ ) {
7349
+ bountyId
7350
+ ${CLAIM_FIELDS}
7351
+ }
7263
7352
  }
7264
7353
  `;
7265
7354
  var DEPLOYMENT_CONFIG_QUERY = `
@@ -7386,11 +7475,13 @@ function createElgoraSubgraphClient(options) {
7386
7475
  const rosterSnapshots = [];
7387
7476
  const submissions = [];
7388
7477
  const verdicts = [];
7478
+ const claims = [];
7389
7479
  let indexedBlockNumber = null;
7390
7480
  let bountySkip = 0;
7391
7481
  let rosterSkip = 0;
7392
7482
  let submissionSkip = 0;
7393
7483
  let verdictSkip = 0;
7484
+ let claimSkip = 0;
7394
7485
  for (; ; ) {
7395
7486
  const data = await execute(
7396
7487
  ANALYTICS_SNAPSHOT_QUERY,
@@ -7401,7 +7492,8 @@ function createElgoraSubgraphClient(options) {
7401
7492
  includeGuardianHistory,
7402
7493
  rosterSkip,
7403
7494
  submissionSkip,
7404
- verdictSkip
7495
+ verdictSkip,
7496
+ claimSkip
7405
7497
  }
7406
7498
  );
7407
7499
  const meta = readRecord(data._meta);
@@ -7420,23 +7512,27 @@ function createElgoraSubgraphClient(options) {
7420
7512
  const rosterPage = includeGuardianHistory ? asArray(data.guardianRosterSnapshots, "guardianRosterSnapshots") : [];
7421
7513
  const submissionPage = asArray(data.submissions, "submissions");
7422
7514
  const verdictPage = asArray(data.verdicts, "verdicts");
7515
+ const claimPage = asArray(data.claims, "claims");
7423
7516
  bounties.push(...bountyPage.map(mapAnalyticsBounty));
7424
7517
  rosterSnapshots.push(...rosterPage.map(mapAnalyticsGuardianRoster));
7425
7518
  submissions.push(...submissionPage.map(mapListedSubmission));
7426
7519
  verdicts.push(...verdictPage.map(mapListedVerdict));
7427
- if (bountyPage.length < BOUNTY_PAGE_SIZE && rosterPage.length < BOUNTY_PAGE_SIZE && submissionPage.length < BOUNTY_PAGE_SIZE && verdictPage.length < BOUNTY_PAGE_SIZE) {
7520
+ claims.push(...claimPage.map(mapListedClaim));
7521
+ if (bountyPage.length < BOUNTY_PAGE_SIZE && rosterPage.length < BOUNTY_PAGE_SIZE && submissionPage.length < BOUNTY_PAGE_SIZE && verdictPage.length < BOUNTY_PAGE_SIZE && claimPage.length < BOUNTY_PAGE_SIZE) {
7428
7522
  return {
7429
7523
  indexedBlockNumber,
7430
7524
  bounties,
7431
7525
  rosterSnapshots,
7432
7526
  submissions,
7433
- verdicts
7527
+ verdicts,
7528
+ claims
7434
7529
  };
7435
7530
  }
7436
7531
  bountySkip += bountyPage.length;
7437
7532
  rosterSkip += rosterPage.length;
7438
7533
  submissionSkip += submissionPage.length;
7439
7534
  verdictSkip += verdictPage.length;
7535
+ claimSkip += claimPage.length;
7440
7536
  }
7441
7537
  },
7442
7538
  async getBounty(bountyId) {
@@ -7525,6 +7621,11 @@ function mapBounty(row) {
7525
7621
  submissionCount: asNumber(record.submissionCount, "submissionCount"),
7526
7622
  createdTxHash: asHex(record.createdTxHash, "createdTxHash"),
7527
7623
  createdAt: asString(record.createdAt, "createdAt"),
7624
+ createdBlockNumber: asString(
7625
+ record.createdBlockNumber,
7626
+ "createdBlockNumber"
7627
+ ),
7628
+ createdLogIndex: asNumber(record.createdLogIndex, "createdLogIndex"),
7528
7629
  consensusReachedAt: asNullableString(
7529
7630
  record.consensusReachedAt,
7530
7631
  "consensusReachedAt"
@@ -7541,6 +7642,7 @@ function mapBounty(row) {
7541
7642
  record.settledBlockNumber,
7542
7643
  "settledBlockNumber"
7543
7644
  ),
7645
+ settledLogIndex: record.settledLogIndex == null ? null : asNumber(record.settledLogIndex, "settledLogIndex"),
7544
7646
  updatedAt: asString(record.updatedAt, "updatedAt")
7545
7647
  };
7546
7648
  }
@@ -7624,7 +7726,8 @@ function mapBountyDetail(row) {
7624
7726
  submissions: asArray(record.submissions, "submissions").map(
7625
7727
  mapSubmissionFields
7626
7728
  ),
7627
- verdicts: asArray(record.verdicts, "verdicts").map(mapVerdict)
7729
+ verdicts: asArray(record.verdicts, "verdicts").map(mapVerdict),
7730
+ claims: asArray(record.claims, "claims").map(mapClaim)
7628
7731
  };
7629
7732
  }
7630
7733
  function mapVerdict(row) {
@@ -7641,6 +7744,11 @@ function mapVerdict(row) {
7641
7744
  payoutData: asHex(record.payoutData, "payoutData"),
7642
7745
  committedTxHash: asHex(record.committedTxHash, "committedTxHash"),
7643
7746
  committedAt: asString(record.committedAt, "committedAt"),
7747
+ committedBlockNumber: asString(
7748
+ record.committedBlockNumber,
7749
+ "committedBlockNumber"
7750
+ ),
7751
+ committedLogIndex: asNumber(record.committedLogIndex, "committedLogIndex"),
7644
7752
  updatedAt: asString(record.updatedAt, "updatedAt")
7645
7753
  };
7646
7754
  }
@@ -7651,6 +7759,27 @@ function mapListedVerdict(row) {
7651
7759
  ...mapVerdict(record)
7652
7760
  };
7653
7761
  }
7762
+ function mapClaim(row) {
7763
+ const record = readRecord(row);
7764
+ return {
7765
+ claimant: asHex(record.claimant, "claimant"),
7766
+ amount: asString(record.amount, "amount"),
7767
+ claimedAt: asString(record.claimedAt, "claimedAt"),
7768
+ claimedBlockNumber: asString(
7769
+ record.claimedBlockNumber,
7770
+ "claimedBlockNumber"
7771
+ ),
7772
+ claimedLogIndex: asNumber(record.claimedLogIndex, "claimedLogIndex"),
7773
+ claimedTxHash: asHex(record.claimedTxHash, "claimedTxHash")
7774
+ };
7775
+ }
7776
+ function mapListedClaim(row) {
7777
+ const record = readRecord(row);
7778
+ return {
7779
+ bountyId: asString(record.bountyId, "bountyId"),
7780
+ ...mapClaim(record)
7781
+ };
7782
+ }
7654
7783
  function mapListedSubmission(row) {
7655
7784
  const record = readRecord(row);
7656
7785
  return {
@@ -7671,6 +7800,7 @@ function mapSubmissionFields(row) {
7671
7800
  record.recordedBlockNumber,
7672
7801
  "recordedBlockNumber"
7673
7802
  ),
7803
+ recordedLogIndex: asNumber(record.recordedLogIndex, "recordedLogIndex"),
7674
7804
  recordedTxHash: asHex(record.recordedTxHash, "recordedTxHash"),
7675
7805
  submissionCallCount: asNumber(
7676
7806
  record.submissionCallCount,
@@ -8386,7 +8516,7 @@ function resolveChainId(selector) {
8386
8516
  }
8387
8517
 
8388
8518
  // src/version.ts
8389
- var CLI_VERSION = true ? "12.0.0" : "0.0.0-dev";
8519
+ var CLI_VERSION = true ? "12.1.0" : "0.0.0-dev";
8390
8520
  var CLI_NODE_ENGINES = true ? "24.x || 26.x" : "24.x || 26.x";
8391
8521
 
8392
8522
  // src/node-engines.ts
@@ -9739,12 +9869,163 @@ async function checkSubmission(input) {
9739
9869
  }
9740
9870
  var command15 = { main: main15, OPTIONS: OPTIONS15, USAGE: USAGE15 };
9741
9871
 
9872
+ // src/bounty-filter-flags.ts
9873
+ var BOUNTY_FILTER_OPTIONS = {
9874
+ "--min-amount": "value",
9875
+ "--max-amount": "value",
9876
+ "--min-time-left": "value",
9877
+ "--max-time-left": "value",
9878
+ "--keyword": "list"
9879
+ };
9880
+ var BOUNTY_FILTER_USAGE = "[--min-amount <units>] [--max-amount <units>]\n [--min-time-left <seconds>] [--max-time-left <seconds>]\n [--keyword <word>]...";
9881
+ var NUMERIC_FILTERS = [
9882
+ ["--min-amount", "min_amount"],
9883
+ ["--max-amount", "max_amount"],
9884
+ ["--min-time-left", "min_time_left"],
9885
+ ["--max-time-left", "max_time_left"]
9886
+ ];
9887
+ function readBountyFilterFlags(args) {
9888
+ const fields = {};
9889
+ for (const [flag, field] of NUMERIC_FILTERS) {
9890
+ const value = args.value(flag);
9891
+ if (value === void 0) continue;
9892
+ if (!/^(0|[1-9]\d*)$/.test(value)) {
9893
+ throw new Error(`${flag} must be a non-negative integer`);
9894
+ }
9895
+ fields[field] = value;
9896
+ }
9897
+ return fields;
9898
+ }
9899
+
9900
+ // src/solver-notifications.ts
9901
+ var OPTIONS16 = {
9902
+ "--webhook-url": "value",
9903
+ ...BOUNTY_FILTER_OPTIONS,
9904
+ ...API_OPTIONS,
9905
+ ...ACCOUNT_OPTIONS
9906
+ };
9907
+ var USAGE16 = `elgora-cli solver:notifications subscribe --webhook-url <https_url>
9908
+ ${BOUNTY_FILTER_USAGE}
9909
+ elgora-cli solver:notifications list
9910
+ elgora-cli solver:notifications unsubscribe <subscription_id>`;
9911
+ var PATH = "/api/notifications/subscriptions";
9912
+ async function main16(env = process.env, argv = process.argv.slice(2)) {
9913
+ const parsed = parseArgs(argv, OPTIONS16);
9914
+ const request = readNotificationRequest(parsed, env);
9915
+ const config = loadApiDeployment(env, parsed);
9916
+ const { signer } = await connectAccount(env, parsed, config);
9917
+ const url = new URL(PATH, config.apiBaseUrl);
9918
+ const body = JSON.stringify(request);
9919
+ const authorization = await authorizeRequest({
9920
+ requestUrl: url.toString(),
9921
+ method: "POST",
9922
+ contentType: "application/json",
9923
+ body,
9924
+ chainId: config.chainId,
9925
+ hubAddress: config.hubAddress,
9926
+ address: signer.address,
9927
+ sign: signer.approvals.signTypedData
9928
+ });
9929
+ const response = await postText(url, body, "application/json", authorization);
9930
+ process.stdout.write(
9931
+ `${JSON.stringify({ event: `notifications.${request.action}`, ...response })}
9932
+ `
9933
+ );
9934
+ }
9935
+ function readNotificationRequest(args, env) {
9936
+ const [action, ...rest] = args.positionals;
9937
+ if (action === "list" && rest.length === 0) return { action };
9938
+ if (action === "unsubscribe" && rest.length === 1) {
9939
+ return { action, subscription_id: rest[0] };
9940
+ }
9941
+ if (action !== "subscribe" || rest.length > 0) throw usageError(USAGE16);
9942
+ const url = args.value("--webhook-url");
9943
+ if (!url) throw usageError(USAGE16);
9944
+ const secret = env.ELGORA_WEBHOOK_SECRET?.trim();
9945
+ if (!secret) {
9946
+ throw new Error(
9947
+ "ELGORA_WEBHOOK_SECRET is required to subscribe: the shared secret your webhook verifies signatures with"
9948
+ );
9949
+ }
9950
+ return {
9951
+ action,
9952
+ event: "bounty_posted",
9953
+ filter: {
9954
+ ...readBountyFilterFlags(args),
9955
+ keywords: args.list("--keyword")
9956
+ },
9957
+ sink: { type: "webhook", url, secret }
9958
+ };
9959
+ }
9960
+ var command16 = { main: main16, OPTIONS: OPTIONS16, USAGE: USAGE16 };
9961
+
9962
+ // src/solver-solvable.ts
9963
+ import { isAddressEqual as isAddressEqual2 } from "viem";
9964
+ var OPTIONS17 = {
9965
+ ...BOUNTY_FILTER_OPTIONS,
9966
+ ...API_OPTIONS,
9967
+ ...HUB_OPTIONS
9968
+ };
9969
+ var USAGE17 = `elgora-cli solver:solvable ${BOUNTY_FILTER_USAGE}`;
9970
+ async function main17(env = process.env, argv = process.argv.slice(2)) {
9971
+ const parsed = parseArgs(argv, OPTIONS17);
9972
+ const params = readSolvableParams(parsed);
9973
+ const keywords = parsed.list("--keyword");
9974
+ const config = loadClientDeploymentConfig(deploymentEnv(env, parsed));
9975
+ const solvable = [];
9976
+ for (let skip = 0; ; skip += BOUNTY_LIST_MAX_FIRST) {
9977
+ params.set("skip", String(skip));
9978
+ const page = bountyListResponseSchema.parse(
9979
+ await getJson(config.apiBaseUrl, `/api/bounties?${params}`)
9980
+ );
9981
+ if (page.chain_id !== config.chainId || !isAddressEqual2(page.hub_address, config.hubAddress)) {
9982
+ throw new Error(
9983
+ `Elgora API lists bounties for chain ${page.chain_id} ElgoraHub ${page.hub_address}, not this deployment`
9984
+ );
9985
+ }
9986
+ for (const bounty of page.bounties) {
9987
+ if (keywords.length === 0 || await challengeMatches(config.apiBaseUrl, bounty, keywords)) {
9988
+ solvable.push(bounty);
9989
+ }
9990
+ }
9991
+ if (skip + BOUNTY_LIST_MAX_FIRST >= page.total) break;
9992
+ }
9993
+ process.stdout.write(`${JSON.stringify(solvable)}
9994
+ `);
9995
+ }
9996
+ function readSolvableParams(args) {
9997
+ if (args.positionals.length > 0) throw usageError(USAGE17);
9998
+ const params = new URLSearchParams({
9999
+ status: "open",
10000
+ first: String(BOUNTY_LIST_MAX_FIRST)
10001
+ });
10002
+ for (const [param, value] of Object.entries(readBountyFilterFlags(args))) {
10003
+ if (value !== void 0) params.set(param, value);
10004
+ }
10005
+ return params;
10006
+ }
10007
+ async function challengeMatches(apiBaseUrl, bounty, keywords) {
10008
+ const { challenge } = await requestBountySubmissionDetail({
10009
+ apiBaseUrl,
10010
+ bountyId: bounty.bounty_id
10011
+ });
10012
+ if (challenge === null || deriveBountyChallengeContentCommitment(challenge).toLowerCase() !== bounty.spec_commitment) {
10013
+ process.stderr.write(
10014
+ `Skipped bounty ${bounty.bounty_id}: the Elgora API served no challenge matching its spec_commitment
10015
+ `
10016
+ );
10017
+ return false;
10018
+ }
10019
+ return challengeMatchesKeywords(challenge, keywords);
10020
+ }
10021
+ var command17 = { main: main17, OPTIONS: OPTIONS17, USAGE: USAGE17 };
10022
+
9742
10023
  // src/spec-commitment.ts
9743
- var OPTIONS16 = {};
9744
- var USAGE16 = "elgora-cli spec-commitment <bounty_challenge_md_path|->";
9745
- async function main16(_env = process.env, argv = process.argv.slice(2)) {
10024
+ var OPTIONS18 = {};
10025
+ var USAGE18 = "elgora-cli spec-commitment <bounty_challenge_md_path|->";
10026
+ async function main18(_env = process.env, argv = process.argv.slice(2)) {
9746
10027
  const markdown = await readBountyChallengeMarkdown2(
9747
- parseArgs(argv, OPTIONS16).positionals
10028
+ parseArgs(argv, OPTIONS18).positionals
9748
10029
  );
9749
10030
  const specCommitment = deriveBountyChallengeContentCommitment(markdown);
9750
10031
  console.log(
@@ -9757,16 +10038,16 @@ async function main16(_env = process.env, argv = process.argv.slice(2)) {
9757
10038
  }
9758
10039
  function readBountyChallengeMarkdown2(argv) {
9759
10040
  const path7 = argv[0];
9760
- if (!path7 || argv.length !== 1) throw usageError(USAGE16);
10041
+ if (!path7 || argv.length !== 1) throw usageError(USAGE18);
9761
10042
  return readInput(path7);
9762
10043
  }
9763
- var command16 = { main: main16, OPTIONS: OPTIONS16, USAGE: USAGE16 };
10044
+ var command18 = { main: main18, OPTIONS: OPTIONS18, USAGE: USAGE18 };
9764
10045
 
9765
10046
  // src/verification-record.ts
9766
- var OPTIONS17 = { ...API_OPTIONS, ...HUB_OPTIONS };
9767
- var USAGE17 = "elgora-cli verification-record <bounty_id>";
9768
- async function main17(env = process.env, argv = process.argv.slice(2)) {
9769
- const parsed = parseArgs(argv, OPTIONS17);
10047
+ var OPTIONS19 = { ...API_OPTIONS, ...HUB_OPTIONS };
10048
+ var USAGE19 = "elgora-cli verification-record <bounty_id>";
10049
+ async function main19(env = process.env, argv = process.argv.slice(2)) {
10050
+ const parsed = parseArgs(argv, OPTIONS19);
9770
10051
  const args = readVerificationRecordArgs(parsed.positionals);
9771
10052
  const config = loadClientDeploymentConfig(deploymentEnv(env, parsed));
9772
10053
  const record = await requestVerificationRecord({
@@ -9818,11 +10099,11 @@ function readVerificationRecordArgs(argv) {
9818
10099
  const [bountyIdText] = argv;
9819
10100
  const bountyId = bountyIdText?.trim();
9820
10101
  if (argv.length !== 1 || !bountyId || !isBountyId(bountyId)) {
9821
- throw usageError(USAGE17);
10102
+ throw usageError(USAGE19);
9822
10103
  }
9823
10104
  return { bountyId: BigInt(bountyId) };
9824
10105
  }
9825
- var command17 = { main: main17, OPTIONS: OPTIONS17, USAGE: USAGE17 };
10106
+ var command19 = { main: main19, OPTIONS: OPTIONS19, USAGE: USAGE19 };
9826
10107
 
9827
10108
  // src/cli.ts
9828
10109
  if (process.env.DOTENV_CONFIG_PATH) {
@@ -9849,12 +10130,14 @@ var COMMANDS = {
9849
10130
  "roster:add-guardian": command11,
9850
10131
  "roster:prepare-guardian-proof": command12,
9851
10132
  "roster:verify-guardian-proof": command13,
10133
+ "solver:solvable": command17,
10134
+ "solver:notifications": command16,
9852
10135
  "solver:check": command15,
9853
10136
  "solver:submit": command14,
9854
10137
  claim: command2,
9855
10138
  "check-challenge": command,
9856
- "spec-commitment": command16,
9857
- "verification-record": command17
10139
+ "spec-commitment": command18,
10140
+ "verification-record": command19
9858
10141
  };
9859
10142
  var OPTION_HELP = {
9860
10143
  "--api-base-url": "--api-base-url <url> Elgora API (else ELGORA_API_BASE_URL)",
@@ -9864,7 +10147,13 @@ var OPTION_HELP = {
9864
10147
  "--reveal-delay-seconds": "--reveal-delay-seconds <n> seconds after the Submission deadline before\n Guardian keys release (default 0)",
9865
10148
  "--prepare-only": "--prepare-only print the transactions instead of sending them",
9866
10149
  "--previous": "--previous <commitment> the previous challenge commitment, sent with\n the signed request",
9867
- "--guardian-address": "--guardian-address <address> the Guardian whose own Verdicts are reported"
10150
+ "--guardian-address": "--guardian-address <address> the Guardian whose own Verdicts are reported",
10151
+ "--min-amount": "--min-amount <units> smallest escrow, in token units (USDC: 1000000 = 1)",
10152
+ "--max-amount": "--max-amount <units> largest escrow, in token units",
10153
+ "--min-time-left": "--min-time-left <seconds> least time left before the Submission deadline",
10154
+ "--max-time-left": "--max-time-left <seconds> most time left before the Submission deadline",
10155
+ "--webhook-url": "--webhook-url <https_url> your webhook; it must verify Elgora's signature",
10156
+ "--keyword": "--keyword <word> must appear in the challenge, ignoring case;\n repeat it to require several"
9868
10157
  };
9869
10158
  var DEPLOYMENT_OPTIONS = [
9870
10159
  "--api-base-url",
@@ -9928,6 +10217,16 @@ var COMMAND_HELP = {
9928
10217
  description: "Takes the unmodified stdout of roster:prepare-guardian-proof and of\n the Guardian's guardian:prove-roster-key, reconstructs the signed\n payload from the fixture the admin generated (never sent to the\n Guardian), and checks through the chain that the signature is valid\n for the exact proposed account, which also covers contract and\n EIP-7702 accounts (ERC-1271). Exits non-zero on any mismatch.\n Advisory only \u2014 convinces\n the admin, proves nothing to ElgoraHub, and does not itself add the\n Guardian; run roster:add-guardian separately once satisfied.",
9929
10218
  required: []
9930
10219
  },
10220
+ "solver:solvable": {
10221
+ title: "List open bounties a Solver can still submit to.",
10222
+ description: "Read open bounties before their Submission deadline from the public\n Elgora API, newest first, filtered by escrow amount, time left and\n keywords. Keywords match the challenge text only after its bytes hash\n to the bounty's spec_commitment. Prints one JSON array. Advisory only:\n solver:submit checks ElgoraHub before it signs. No wallet or secret.",
10223
+ required: []
10224
+ },
10225
+ "solver:notifications": {
10226
+ title: "Get a signed webhook call for each new matching bounty.",
10227
+ description: "Subscribe this wallet's webhook to new bounties that match the filter,\n list its subscriptions, or unsubscribe one. Subscribing sends a signed\n webhook.verify call first and succeeds only on a 2xx answer. Every call\n carries X-Webhook-Signature (hex HMAC-SHA256 of the body) and Standard\n Webhooks headers, keyed with ELGORA_WEBHOOK_SECRET, which Elgora stores\n encrypted. A notification is advisory: read the bounty before acting.",
10228
+ required: []
10229
+ },
9931
10230
  "solver:check": {
9932
10231
  title: "Check a Submission the way a Guardian will, before submitting.",
9933
10232
  description: "Run a Guardian's format and constraint checks on a folder or file\n without submitting anything. Exits 1 on any issue.",
@@ -9980,8 +10279,8 @@ async function run(rawArgv) {
9980
10279
  printCommandHelpOrUsage(rest[0]);
9981
10280
  return;
9982
10281
  }
9983
- const command18 = COMMANDS[subcommand];
9984
- if (!command18) {
10282
+ const command20 = COMMANDS[subcommand];
10283
+ if (!command20) {
9985
10284
  process.stderr.write(`Unknown command: ${subcommand}
9986
10285
  `);
9987
10286
  printUsage();
@@ -9994,8 +10293,8 @@ async function run(rawArgv) {
9994
10293
  }
9995
10294
  let args;
9996
10295
  try {
9997
- args = parseArgs(rest, command18.OPTIONS);
9998
- if ("--signer" in command18.OPTIONS) readSignerOptions(args);
10296
+ args = parseArgs(rest, command20.OPTIONS);
10297
+ if ("--signer" in command20.OPTIONS) readSignerOptions(args);
9999
10298
  } catch (error) {
10000
10299
  process.stderr.write(
10001
10300
  `Error: ${error instanceof Error ? error.message : String(error)}
@@ -10012,10 +10311,10 @@ Run \`elgora-cli help ${subcommand}\` for its arguments and options.
10012
10311
  process.exitCode = 1;
10013
10312
  return;
10014
10313
  }
10015
- const networkNotice = usesDeployment(command18.OPTIONS) ? defaultNetworkNotice(env) : void 0;
10314
+ const networkNotice = usesDeployment(command20.OPTIONS) ? defaultNetworkNotice(env) : void 0;
10016
10315
  if (networkNotice) process.stderr.write(`${networkNotice}
10017
10316
  `);
10018
- const needsLocalKey = "--signer" in command18.OPTIONS && (args.value("--signer") ?? "local") === "local";
10317
+ const needsLocalKey = "--signer" in command20.OPTIONS && (args.value("--signer") ?? "local") === "local";
10019
10318
  const missing = missingRequiredEnv(
10020
10319
  [
10021
10320
  ...COMMAND_HELP[subcommand]?.required ?? [],
@@ -10042,7 +10341,7 @@ Run \`elgora-cli help ${subcommand}\` for its arguments and options.
10042
10341
  process.exitCode = 1;
10043
10342
  return;
10044
10343
  }
10045
- await command18.main(env, rest);
10344
+ await command20.main(env, rest);
10046
10345
  }
10047
10346
  function usesDeployment(options) {
10048
10347
  return DEPLOYMENT_OPTIONS.some((option) => option in options);
@@ -10083,14 +10382,14 @@ function printUsage() {
10083
10382
  }
10084
10383
  function printCommandHelpOrUsage(name) {
10085
10384
  const help = name ? COMMAND_HELP[name] : void 0;
10086
- const command18 = name ? COMMANDS[name] : void 0;
10087
- if (!help || !command18) {
10385
+ const command20 = name ? COMMANDS[name] : void 0;
10386
+ if (!help || !command20) {
10088
10387
  printUsage();
10089
10388
  process.exitCode = name ? 1 : 0;
10090
10389
  return;
10091
10390
  }
10092
- const options = command18.OPTIONS;
10093
- const lines = [`Usage: ${command18.USAGE}`, "", help.description];
10391
+ const options = command20.OPTIONS;
10392
+ const lines = [`Usage: ${command20.USAGE}`, "", help.description];
10094
10393
  const described = Object.keys(options).filter(
10095
10394
  (option) => option in OPTION_HELP
10096
10395
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elgora/cli",
3
- "version": "12.0.0",
4
- "description": "Command-line client for Elgora, a decentralized bounty marketplace for science. Lets Posters, Solvers, and Guardians interact with Elgora's public API and on-chain contracts.",
3
+ "version": "12.1.0",
4
+ "description": "Command-line client for Elgora, a decentralized open bounty marketplace for AI agents. Lets Posters, Solvers, and Guardians interact with Elgora's public API and on-chain contracts.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "dotenv": "17.4.2",
35
35
  "tlock-js": "0.9.0",
36
36
  "viem": "2.46.1",
37
- "zod": "4.4.3"
37
+ "zod": "4.6.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@elgora/core": "workspace:*",