@elgora/cli 7.2.3 → 7.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/cli.js +1001 -162
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5,6 +5,10 @@ import path4 from "path";
5
5
  import { config as loadDotenv } from "dotenv";
6
6
  import { ZodError as ZodError2 } from "zod";
7
7
 
8
+ // src/check-page.ts
9
+ import { readFile } from "fs/promises";
10
+ import { text as readStdinText } from "stream/consumers";
11
+
8
12
  // ../../packages/core/src/argv-flags.ts
9
13
  function extractFlagValue(argv, flag) {
10
14
  const remaining = [];
@@ -63,10 +67,13 @@ function safeDecodeUtf8(bytes) {
63
67
  }
64
68
 
65
69
  // ../../packages/core/src/constants.ts
70
+ var BOUNTY_CHALLENGE_PROFILE = "elgora_markdown_bounty_challenge_v0";
71
+ var WINNER_TAKE_ALL_PAYOUT_POLICY = "winner_take_all";
66
72
  var SUBMISSION_ENCRYPTION_TYPE = "guardian_timelock_hpke_aes_256_gcm_v0";
67
73
  var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
68
74
 
69
75
  // ../../packages/core/src/bounty-challenge-markdown.ts
76
+ var FRONTMATTER_DELIMITER = "---";
70
77
  var MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS = 1e6;
71
78
  var BOUNTY_CHALLENGE_FRONTMATTER_KEYS = [
72
79
  "profile",
@@ -74,15 +81,307 @@ var BOUNTY_CHALLENGE_FRONTMATTER_KEYS = [
74
81
  "submission_deadline",
75
82
  "payout_policy"
76
83
  ];
77
- var ALLOWED_FRONTMATTER_KEYS = BOUNTY_CHALLENGE_FRONTMATTER_KEYS.join(", ");
84
+ var BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS = [
85
+ "constraints"
86
+ ];
87
+ var ALLOWED_FRONTMATTER_KEYS = [
88
+ ...BOUNTY_CHALLENGE_FRONTMATTER_KEYS,
89
+ ...BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS
90
+ ].join(", ");
78
91
  var ALLOWED_FRONTMATTER_KEYS_MESSAGE = `Use only the v0 template frontmatter keys: ${ALLOWED_FRONTMATTER_KEYS}`;
92
+ var DEPLOYMENT_OWNED_FRONTMATTER_KEYS = /* @__PURE__ */ new Set([
93
+ "chain_id",
94
+ "token_address",
95
+ "hub_address",
96
+ "guardian_recipients_commitment",
97
+ "guardian_settlement_signer",
98
+ "treasury_recipient",
99
+ "treasury_fee_bps",
100
+ "guardian_fee_recipient",
101
+ "guardian_fee_bps",
102
+ "timelock_reveal_delay_seconds",
103
+ "timelock_network",
104
+ "timelock_chain_hash",
105
+ "storage_provider",
106
+ "artifact_storage_bucket"
107
+ ]);
108
+ var BountyChallengeSpecValidationError = class extends Error {
109
+ issues;
110
+ constructor(issues) {
111
+ super("Invalid BountyChallengeSpec Markdown");
112
+ this.name = "BountyChallengeSpecValidationError";
113
+ this.issues = issues;
114
+ }
115
+ };
79
116
  var markdownSchema = z.string().min(1, "Markdown body must not be empty").max(
80
117
  MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS,
81
118
  `Markdown body must be at most ${MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS} characters`
82
119
  );
120
+ function parseBountyChallengeMarkdown(input) {
121
+ const markdown = parseMarkdownInput(input);
122
+ const lines = normalizedLines(markdown);
123
+ const issues = [];
124
+ const { frontmatterLines, bodyLines } = splitFrontmatter(lines, issues);
125
+ const { scalarLines, constraints } = splitConstraintsBlock(
126
+ frontmatterLines,
127
+ issues
128
+ );
129
+ const publicationTerms = parsePublicationTermsFrontmatter(
130
+ scalarLines,
131
+ issues
132
+ );
133
+ validateChallengeBody(bodyLines, issues);
134
+ if (issues.length > 0 || publicationTerms === null) {
135
+ throw new BountyChallengeSpecValidationError(issues);
136
+ }
137
+ return { markdown, publicationTerms, constraints };
138
+ }
83
139
  function deriveBountyChallengeContentCommitment(content) {
84
140
  return keccak256(toBytes(content));
85
141
  }
142
+ function parseMarkdownInput(input) {
143
+ const parsed = markdownSchema.safeParse(input);
144
+ if (!parsed.success) {
145
+ throw new BountyChallengeSpecValidationError(
146
+ parsed.error.issues.map((issue) => ({
147
+ path: issue.path.join("."),
148
+ message: issue.message
149
+ }))
150
+ );
151
+ }
152
+ return parsed.data;
153
+ }
154
+ function splitFrontmatter(lines, issues) {
155
+ if (lines[0] !== FRONTMATTER_DELIMITER) {
156
+ issues.push({
157
+ path: "frontmatter",
158
+ message: "Document must start with v0 frontmatter delimited by ---"
159
+ });
160
+ return { frontmatterLines: [], bodyLines: lines };
161
+ }
162
+ const closingIndex = lines.findIndex(
163
+ (line, index) => index > 0 && line === FRONTMATTER_DELIMITER
164
+ );
165
+ if (closingIndex === -1) {
166
+ issues.push({
167
+ path: "frontmatter",
168
+ message: "Frontmatter must close with --- before the H1 title"
169
+ });
170
+ return { frontmatterLines: lines.slice(1), bodyLines: [] };
171
+ }
172
+ return {
173
+ frontmatterLines: lines.slice(1, closingIndex),
174
+ bodyLines: lines.slice(closingIndex + 1)
175
+ };
176
+ }
177
+ function splitConstraintsBlock(lines, issues) {
178
+ const scalarLines = [];
179
+ const constraints = [];
180
+ let seenConstraintsBlock = false;
181
+ for (let index = 0; index < lines.length; index += 1) {
182
+ const line = lines[index] ?? "";
183
+ if (line.trim() !== "constraints:") {
184
+ scalarLines.push({ text: line, index });
185
+ continue;
186
+ }
187
+ if (seenConstraintsBlock) {
188
+ issues.push({
189
+ path: "frontmatter.constraints",
190
+ message: 'Frontmatter key "constraints" must appear exactly once'
191
+ });
192
+ }
193
+ seenConstraintsBlock = true;
194
+ const seenKeys = /* @__PURE__ */ new Set();
195
+ while (index + 1 < lines.length) {
196
+ const entry = lines[index + 1] ?? "";
197
+ if (entry.trim().length === 0) break;
198
+ if (!/^\s+\S/.test(entry)) break;
199
+ index += 1;
200
+ const match = entry.trim().match(/^([a-z][a-z0-9_]*):\s*(.+)$/);
201
+ const key = match?.[1];
202
+ const rawValue = match?.[2];
203
+ if (key === void 0 || rawValue === void 0) {
204
+ issues.push({
205
+ path: `frontmatter.constraints.line_${index + 2}`,
206
+ message: "Constraint entries must use snake_case_key: value"
207
+ });
208
+ continue;
209
+ }
210
+ if (seenKeys.has(key)) {
211
+ issues.push({
212
+ path: `frontmatter.constraints.${key}`,
213
+ message: `Constraint "${key}" must appear exactly once`
214
+ });
215
+ continue;
216
+ }
217
+ seenKeys.add(key);
218
+ const value = normalizeFrontmatterScalar(
219
+ rawValue,
220
+ `frontmatter.constraints.${key}`,
221
+ issues
222
+ );
223
+ if (value !== null) {
224
+ constraints.push({ key, value, line: index + 2 });
225
+ }
226
+ }
227
+ if (constraints.length === 0 && seenKeys.size === 0) {
228
+ issues.push({
229
+ path: "frontmatter.constraints",
230
+ message: "Remove the empty constraints block, or add at least one indented constraint entry"
231
+ });
232
+ }
233
+ }
234
+ return { scalarLines, constraints };
235
+ }
236
+ function parsePublicationTermsFrontmatter(lines, issues) {
237
+ const values = /* @__PURE__ */ new Map();
238
+ let foundUnsupportedKey = false;
239
+ for (const { text: line, index } of lines) {
240
+ if (line.trim().length === 0) continue;
241
+ const match = line.match(/^([a-z][a-z0-9_]*):\s*(.*)$/);
242
+ if (!match) {
243
+ issues.push({
244
+ path: `frontmatter.line_${index + 2}`,
245
+ message: "Frontmatter entries must use snake_case_key: value"
246
+ });
247
+ continue;
248
+ }
249
+ const rawKey = match[1];
250
+ const rawValue = match[2];
251
+ if (rawKey === void 0 || rawValue === void 0) {
252
+ issues.push({
253
+ path: `frontmatter.line_${index + 2}`,
254
+ message: "Frontmatter entries must use snake_case_key: value"
255
+ });
256
+ continue;
257
+ }
258
+ if (!isBountyChallengeFrontmatterKey(rawKey)) {
259
+ if (DEPLOYMENT_OWNED_FRONTMATTER_KEYS.has(rawKey)) {
260
+ if (!foundUnsupportedKey) {
261
+ issues.push({
262
+ path: "frontmatter",
263
+ message: ALLOWED_FRONTMATTER_KEYS_MESSAGE
264
+ });
265
+ foundUnsupportedKey = true;
266
+ }
267
+ } else {
268
+ issues.push({
269
+ path: `frontmatter.${rawKey}`,
270
+ message: `Remove frontmatter key "${rawKey}"; use only the v0 template frontmatter keys: ${ALLOWED_FRONTMATTER_KEYS}`
271
+ });
272
+ }
273
+ continue;
274
+ }
275
+ if (values.has(rawKey)) {
276
+ issues.push({
277
+ path: `frontmatter.${rawKey}`,
278
+ message: `Frontmatter key "${rawKey}" must appear exactly once`
279
+ });
280
+ continue;
281
+ }
282
+ const value = normalizeFrontmatterScalar(
283
+ rawValue,
284
+ `frontmatter.${rawKey}`,
285
+ issues
286
+ );
287
+ if (value !== null) values.set(rawKey, value);
288
+ }
289
+ for (const key of BOUNTY_CHALLENGE_FRONTMATTER_KEYS) {
290
+ if (!values.has(key)) {
291
+ issues.push({
292
+ path: `frontmatter.${key}`,
293
+ message: `Add required frontmatter key "${key}"`
294
+ });
295
+ }
296
+ }
297
+ const profile = values.get("profile");
298
+ if (profile !== void 0 && profile !== BOUNTY_CHALLENGE_PROFILE) {
299
+ issues.push({
300
+ path: "frontmatter.profile",
301
+ message: `Must equal ${BOUNTY_CHALLENGE_PROFILE}`
302
+ });
303
+ }
304
+ const payoutPolicy = values.get("payout_policy");
305
+ if (payoutPolicy !== void 0 && payoutPolicy !== WINNER_TAKE_ALL_PAYOUT_POLICY) {
306
+ issues.push({
307
+ path: "frontmatter.payout_policy",
308
+ message: `Must equal ${WINNER_TAKE_ALL_PAYOUT_POLICY}`
309
+ });
310
+ }
311
+ const submissionDeadline = parsePositiveSafeIntegerFrontmatter(
312
+ values.get("submission_deadline"),
313
+ "frontmatter.submission_deadline",
314
+ issues
315
+ );
316
+ const escrowAmount = parsePositiveIntegerStringFrontmatter(
317
+ values.get("escrow_amount"),
318
+ "frontmatter.escrow_amount",
319
+ issues
320
+ );
321
+ if (profile !== BOUNTY_CHALLENGE_PROFILE || payoutPolicy !== WINNER_TAKE_ALL_PAYOUT_POLICY || escrowAmount === null || submissionDeadline === null) {
322
+ return null;
323
+ }
324
+ return {
325
+ profile,
326
+ escrowAmount,
327
+ submissionDeadline,
328
+ payoutPolicy
329
+ };
330
+ }
331
+ function normalizeFrontmatterScalar(rawValue, path5, issues) {
332
+ const value = rawValue.trim();
333
+ if (value.length === 0) {
334
+ issues.push({ path: path5, message: "Frontmatter value must not be empty" });
335
+ return null;
336
+ }
337
+ const quote = value[0];
338
+ if (quote !== '"' && quote !== "'") return value;
339
+ if (value.length < 2 || value.at(-1) !== quote) {
340
+ issues.push({
341
+ path: path5,
342
+ message: "Quoted frontmatter values must close with the same quote"
343
+ });
344
+ return null;
345
+ }
346
+ return value.slice(1, -1);
347
+ }
348
+ function parsePositiveSafeIntegerFrontmatter(value, path5, issues) {
349
+ if (value === void 0) return null;
350
+ if (!isPositiveInteger(value)) {
351
+ issues.push({ path: path5, message: "Must be a positive integer" });
352
+ return null;
353
+ }
354
+ const parsed = Number(value);
355
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
356
+ issues.push({ path: path5, message: "Must be a positive safe integer" });
357
+ return null;
358
+ }
359
+ return parsed;
360
+ }
361
+ function parsePositiveIntegerStringFrontmatter(value, path5, issues) {
362
+ if (value === void 0) return null;
363
+ if (!isPositiveInteger(value)) {
364
+ issues.push({ path: path5, message: "Must be positive token smallest units" });
365
+ return null;
366
+ }
367
+ return value;
368
+ }
369
+ function validateChallengeBody(lines, issues) {
370
+ if (!lines.some((line) => line.trim().length > 0)) {
371
+ issues.push({
372
+ path: "body",
373
+ message: "Add the Markdown challenge after frontmatter"
374
+ });
375
+ }
376
+ }
377
+ function normalizedLines(markdown) {
378
+ return markdown.split("\n").map((line) => line.replace(/\r$/, ""));
379
+ }
380
+ function isBountyChallengeFrontmatterKey(value) {
381
+ return BOUNTY_CHALLENGE_FRONTMATTER_KEYS.includes(
382
+ value
383
+ );
384
+ }
86
385
 
87
386
  // ../../packages/core/src/bounty-publication-response.ts
88
387
  import { isHex as isHex2 } from "viem";
@@ -144,22 +443,42 @@ function resolveSupportedChain(chainId) {
144
443
  }
145
444
  var FOUNDRY_CHAIN_ID = foundry.id;
146
445
 
147
- // ../../packages/core/src/deployments.ts
148
- import { baseSepolia as baseSepolia2 } from "viem/chains";
149
- var DEPLOYMENTS = {
150
- [baseSepolia2.id]: {
151
- ELGORA_HUB_ADDRESS: "0x2f97b5f616495c2e923f39a46648eb783c053ad7",
152
- ELGORA_ESCROW_TOKEN_ADDRESS: "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
153
- payoutSchemeAddress: "0x752d4305b8567b777d479dfa9847dc4f5ffb5750",
154
- ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmqz9ta0fx8bu01xt0om3fo75/subgraphs/elgora-elgorahub/testnet-production/gn",
155
- deployBlock: 46353511,
156
- deployTransaction: "0xed3d3c8af3c69f9f8d4ec47da7179fe4b89bd4a4cd8d8232dbbd3d3f3d38e74b",
157
- sourceCommit: "1377da5c38dc5c90d8608ffa3cb15ad6f8567a01"
446
+ // ../../packages/core/src/submission-encryption.ts
447
+ import { Aes256Gcm, CipherSuite, HkdfSha256 } from "@hpke/core";
448
+ import { DhkemX25519HkdfSha256 } from "@hpke/dhkem-x25519";
449
+ import { concatBytes, keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
450
+ import { z as z3 } from "zod";
451
+
452
+ // ../../packages/core/src/base64.ts
453
+ var BASE64_ENCODE_CHUNK_BYTES = 3 * 16384;
454
+ var FROM_CHAR_CODE_MAX_ARGUMENTS = 4096;
455
+ function bytesToBase64Url(bytes) {
456
+ const chunks = [];
457
+ for (let offset = 0; offset < bytes.length; offset += BASE64_ENCODE_CHUNK_BYTES) {
458
+ const chunk = bytes.subarray(offset, offset + BASE64_ENCODE_CHUNK_BYTES);
459
+ let binary = "";
460
+ for (let index = 0; index < chunk.length; index += FROM_CHAR_CODE_MAX_ARGUMENTS) {
461
+ binary += String.fromCharCode(
462
+ ...chunk.subarray(index, index + FROM_CHAR_CODE_MAX_ARGUMENTS)
463
+ );
464
+ }
465
+ chunks.push(
466
+ btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "")
467
+ );
158
468
  }
159
- };
160
- var DEFAULT_CHAIN_ID = baseSepolia2.id;
161
- function getDeployment(chainId) {
162
- return DEPLOYMENTS[chainId];
469
+ return chunks.join("");
470
+ }
471
+ function base64UrlToBytes(value) {
472
+ const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
473
+ return base64ToBytes(padded);
474
+ }
475
+ function base64ToBytes(value) {
476
+ const binary = atob(value);
477
+ const bytes = new Uint8Array(binary.length);
478
+ for (let index = 0; index < binary.length; index += 1) {
479
+ bytes[index] = binary.charCodeAt(index);
480
+ }
481
+ return bytes;
163
482
  }
164
483
 
165
484
  // ../../packages/core/src/json.ts
@@ -193,33 +512,6 @@ function canonicalizeJson(value) {
193
512
  throw new Error("Unsupported JSON value");
194
513
  }
195
514
 
196
- // ../../packages/core/src/submission-encryption.ts
197
- import { Aes256Gcm, CipherSuite, HkdfSha256 } from "@hpke/core";
198
- import { DhkemX25519HkdfSha256 } from "@hpke/dhkem-x25519";
199
- import { concatBytes, keccak256 as keccak2562, toBytes as toBytes2 } from "viem";
200
- import { z as z3 } from "zod";
201
-
202
- // ../../packages/core/src/base64.ts
203
- function bytesToBase64Url(bytes) {
204
- let binary = "";
205
- for (const byte of bytes) {
206
- binary += String.fromCharCode(byte);
207
- }
208
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
209
- }
210
- function base64UrlToBytes(value) {
211
- const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
212
- return base64ToBytes(padded);
213
- }
214
- function base64ToBytes(value) {
215
- const binary = atob(value);
216
- const bytes = new Uint8Array(binary.length);
217
- for (let index = 0; index < binary.length; index += 1) {
218
- bytes[index] = binary.charCodeAt(index);
219
- }
220
- return bytes;
221
- }
222
-
223
515
  // ../../packages/core/src/submission-encryption.ts
224
516
  var PRIVATE_ARTIFACT_ENVELOPE_KIND = "elgora_private_artifact_envelope_v0";
225
517
  var PRIVATE_ARTIFACT_PUBLIC_MANIFEST_KIND = "elgora_private_artifact_public_manifest_v0";
@@ -228,13 +520,17 @@ var PRIVATE_ARTIFACT_WRAPPED_KEY_BAG_KIND = "elgora_private_artifact_wrapped_key
228
520
  var PRIVATE_ARTIFACT_REVEAL_POLICY_KIND = "drand_timelock_reveal_policy_v0";
229
521
  var PRIVATE_ARTIFACT_HPKE_SUITE = "hpke-dhkem-x25519-hkdf-sha256+hkdf-sha256+aes-256-gcm@1";
230
522
  var MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES = 50 * 1024 * 1024;
523
+ var MAX_SOLVER_SUBMISSION_FILES = 500;
524
+ var MAX_SOLVER_SUBMISSION_NAME_CHARACTERS = 255;
525
+ var MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES = 37 * 1024 * 1024;
526
+ var MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES = 250 * 1e3 * 1e3;
231
527
  var PRIVATE_ARTIFACT_HPKE_INFO_PREFIX = "elgora/private-artifact-envelope/hpke-aes-key/v0";
232
528
  var AES_256_KEY_BYTES = 32;
233
529
  var AES_GCM_IV_BYTES = 12;
234
530
  var PRIVATE_ARTIFACT_NONCE_BYTES = 16;
235
531
  var AES_GCM_TAG_BITS = 128;
236
532
  var BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
237
- var SAFE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
533
+ var SOLVER_SUBMISSION_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
238
534
  var MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;
239
535
  var KEY_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
240
536
  var base64UrlSchema = z3.string().regex(BASE64URL_RE);
@@ -242,7 +538,10 @@ var lowerAddressSchema = z3.string().refine(isLowercaseHexAddress);
242
538
  var bytes32Schema2 = z3.string().refine(isLowercaseBytes32);
243
539
  var sha256HexSchema = z3.string().refine(isSha256Hex);
244
540
  var keyIdSchema = z3.string().min(1).max(128).regex(KEY_ID_RE);
245
- var artifactNameSchema = z3.string().regex(SAFE_NAME_RE);
541
+ var artifactNameSchema = z3.string().max(
542
+ MAX_SOLVER_SUBMISSION_NAME_CHARACTERS,
543
+ `Artifact filename exceeds ${MAX_SOLVER_SUBMISSION_NAME_CHARACTERS} ASCII characters (including extension). Solvers may shorten it only if the published challenge permits, before the deadline.`
544
+ ).regex(SOLVER_SUBMISSION_NAME_RE);
246
545
  var mediaTypeSchema = z3.string().regex(MEDIA_TYPE_RE);
247
546
  var bountyIdSchema = z3.string().refine(isBountyId);
248
547
  var chainIdSchema = z3.number().int().positive().lte(Number.MAX_SAFE_INTEGER);
@@ -299,7 +598,10 @@ var encryptedBundleArtifactSchema = artifactManifestEntrySchema.extend({
299
598
  var privateArtifactEncryptedBundleSchema = z3.strictObject({
300
599
  kind: z3.literal(PRIVATE_ARTIFACT_ENCRYPTED_BUNDLE_KIND),
301
600
  context: privateArtifactContextSchema,
302
- artifacts: z3.array(encryptedBundleArtifactSchema).min(1).refine(
601
+ artifacts: z3.array(encryptedBundleArtifactSchema).min(1).max(
602
+ MAX_SOLVER_SUBMISSION_FILES,
603
+ `Submission exceeds the maximum of ${MAX_SOLVER_SUBMISSION_FILES} files.`
604
+ ).refine(
303
605
  (artifacts) => new Set(artifacts.map((artifact) => artifact.name.toLowerCase())).size === artifacts.length,
304
606
  "artifact names must be unique case-insensitively"
305
607
  )
@@ -849,8 +1151,8 @@ async function openAesKeyForRecipient(input) {
849
1151
  });
850
1152
  }
851
1153
  async function encryptAes256Gcm(input) {
852
- const crypto = getWebCrypto();
853
- const key = await crypto.subtle.importKey(
1154
+ const crypto2 = getWebCrypto();
1155
+ const key = await crypto2.subtle.importKey(
854
1156
  "raw",
855
1157
  toArrayBufferView(input.rawAesKey),
856
1158
  "AES-GCM",
@@ -858,7 +1160,7 @@ async function encryptAes256Gcm(input) {
858
1160
  ["encrypt"]
859
1161
  );
860
1162
  return toUint8Array(
861
- await crypto.subtle.encrypt(
1163
+ await crypto2.subtle.encrypt(
862
1164
  {
863
1165
  name: "AES-GCM",
864
1166
  iv: toArrayBufferView(input.iv),
@@ -871,8 +1173,8 @@ async function encryptAes256Gcm(input) {
871
1173
  );
872
1174
  }
873
1175
  async function decryptAes256Gcm(input) {
874
- const crypto = getWebCrypto();
875
- const key = await crypto.subtle.importKey(
1176
+ const crypto2 = getWebCrypto();
1177
+ const key = await crypto2.subtle.importKey(
876
1178
  "raw",
877
1179
  toArrayBufferView(input.rawAesKey),
878
1180
  "AES-GCM",
@@ -881,7 +1183,7 @@ async function decryptAes256Gcm(input) {
881
1183
  );
882
1184
  try {
883
1185
  return toUint8Array(
884
- await crypto.subtle.decrypt(
1186
+ await crypto2.subtle.decrypt(
885
1187
  {
886
1188
  name: "AES-GCM",
887
1189
  iv: toArrayBufferView(input.iv),
@@ -959,6 +1261,277 @@ function compareCanonicalStrings(left, right) {
959
1261
  return 0;
960
1262
  }
961
1263
 
1264
+ // ../../packages/core/src/protocol-profile.ts
1265
+ var PROTOCOL_PROFILE_V0 = Object.freeze({
1266
+ profile: BOUNTY_CHALLENGE_PROFILE,
1267
+ submission: Object.freeze({
1268
+ maxCiphertextBytes: MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES,
1269
+ maxExtractedBytes: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES,
1270
+ maxFiles: MAX_SOLVER_SUBMISSION_FILES,
1271
+ filename: Object.freeze({
1272
+ maxCharacters: MAX_SOLVER_SUBMISSION_NAME_CHARACTERS,
1273
+ firstCharacter: "letter_or_digit",
1274
+ allowedCharacters: "letters_digits_dot_underscore_hyphen",
1275
+ caseInsensitivelyUnique: true
1276
+ }),
1277
+ layout: Object.freeze({
1278
+ flat: true,
1279
+ regularFilesOnly: true,
1280
+ allowsSubdirectories: false,
1281
+ allowsSymlinks: false,
1282
+ allowsEmpty: false,
1283
+ allowsArchivedDirectoryEntries: false,
1284
+ allowsArchivedPathTraversal: false
1285
+ }),
1286
+ externalReferences: false
1287
+ }),
1288
+ challenge: Object.freeze({
1289
+ maxMarkdownCharacters: MAX_BOUNTY_CHALLENGE_MARKDOWN_CHARACTERS,
1290
+ requiredFrontmatterKeys: Object.freeze([
1291
+ ...BOUNTY_CHALLENGE_FRONTMATTER_KEYS
1292
+ ]),
1293
+ optionalFrontmatterKeys: Object.freeze([
1294
+ ...BOUNTY_CHALLENGE_OPTIONAL_FRONTMATTER_KEYS
1295
+ ]),
1296
+ payoutPolicies: Object.freeze([WINNER_TAKE_ALL_PAYOUT_POLICY])
1297
+ }),
1298
+ encryption: Object.freeze({
1299
+ type: SUBMISSION_ENCRYPTION_TYPE
1300
+ }),
1301
+ tightenable: Object.freeze({
1302
+ max_extracted_bytes: Object.freeze({
1303
+ comparison: "at_most",
1304
+ profileValue: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES
1305
+ }),
1306
+ max_files: Object.freeze({
1307
+ comparison: "at_most",
1308
+ profileValue: MAX_SOLVER_SUBMISSION_FILES
1309
+ }),
1310
+ allowed_extensions: Object.freeze({ comparison: "subset" }),
1311
+ required_files: Object.freeze({ comparison: "filename_list" })
1312
+ })
1313
+ });
1314
+ var PROTOCOL_PROFILES = /* @__PURE__ */ new Map([
1315
+ [PROTOCOL_PROFILE_V0.profile, PROTOCOL_PROFILE_V0]
1316
+ ]);
1317
+ var CURRENT_PROTOCOL_PROFILE = PROTOCOL_PROFILE_V0;
1318
+ var PROTOCOL_PROFILE_VERSIONS = Object.freeze([
1319
+ ...PROTOCOL_PROFILES.keys()
1320
+ ]);
1321
+ function getProtocolProfile(version) {
1322
+ return PROTOCOL_PROFILES.get(version) ?? null;
1323
+ }
1324
+
1325
+ // ../../packages/core/src/challenge-conformance.ts
1326
+ function checkChallengeConformance(input, profile = CURRENT_PROTOCOL_PROFILE) {
1327
+ const issues = [];
1328
+ const parsed = typeof input === "string" ? readFrontmatter(input, issues) : input;
1329
+ const pinned = parsed === null ? null : resolveProfile(parsed.publicationTerms.profile, profile, issues);
1330
+ if (parsed !== null && pinned !== null) {
1331
+ checkTightenings(parsed.constraints, pinned, issues);
1332
+ }
1333
+ return { issues, profile: pinned };
1334
+ }
1335
+ function readFrontmatter(markdown, issues) {
1336
+ try {
1337
+ return parseBountyChallengeMarkdown(markdown);
1338
+ } catch (error) {
1339
+ if (!(error instanceof BountyChallengeSpecValidationError)) throw error;
1340
+ for (const issue of error.issues) {
1341
+ issues.push({
1342
+ code: "frontmatter_invalid",
1343
+ anchor: issue.path,
1344
+ path: issue.path,
1345
+ message: issue.message
1346
+ });
1347
+ }
1348
+ return null;
1349
+ }
1350
+ }
1351
+ function resolveProfile(declared, fallback, issues) {
1352
+ const pinned = getProtocolProfile(declared) ?? (declared === fallback.profile ? fallback : null);
1353
+ if (pinned === null) {
1354
+ issues.push({
1355
+ code: "profile_unknown",
1356
+ anchor: "frontmatter.profile",
1357
+ path: "frontmatter.profile",
1358
+ message: `Unknown protocol profile "${declared}". A bounty can only pin a published profile version.`
1359
+ });
1360
+ }
1361
+ return pinned;
1362
+ }
1363
+ function checkTightenings(constraints, profile, issues) {
1364
+ for (const constraint of constraints) {
1365
+ const anchor = `frontmatter.constraints.${constraint.key}`;
1366
+ const rule = profile.tightenable[constraint.key];
1367
+ if (rule === void 0) {
1368
+ issues.push({
1369
+ code: "tightening_unknown_key",
1370
+ anchor,
1371
+ path: anchor,
1372
+ message: `"${constraint.key}" is not a constraint a challenge may narrow. A Poster sets bounty parameters; protocol invariants and agent procedure are not among them. Tightenable: ${Object.keys(profile.tightenable).join(", ")}.`
1373
+ });
1374
+ continue;
1375
+ }
1376
+ if (rule.comparison === "at_most") {
1377
+ checkNumericTightening(constraint, rule, anchor, issues);
1378
+ continue;
1379
+ }
1380
+ checkListTightening(constraint, rule, profile, anchor, issues);
1381
+ }
1382
+ }
1383
+ function checkNumericTightening(constraint, rule, anchor, issues) {
1384
+ const ceiling = rule.profileValue;
1385
+ if (ceiling === void 0) return;
1386
+ if (!/^\d+$/.test(constraint.value)) {
1387
+ issues.push({
1388
+ code: "tightening_malformed",
1389
+ anchor,
1390
+ path: anchor,
1391
+ message: `"${constraint.key}" must be a positive integer, written in full without units or separators.`
1392
+ });
1393
+ return;
1394
+ }
1395
+ const value = Number(constraint.value);
1396
+ if (value <= 0 || !Number.isSafeInteger(value)) {
1397
+ issues.push({
1398
+ code: "tightening_malformed",
1399
+ anchor,
1400
+ path: anchor,
1401
+ message: `"${constraint.key}" must be a positive integer below ${ceiling}.`
1402
+ });
1403
+ return;
1404
+ }
1405
+ if (value >= ceiling) {
1406
+ issues.push({
1407
+ code: "tightening_exceeds_profile",
1408
+ anchor,
1409
+ path: anchor,
1410
+ message: `"${constraint.key}" is ${value}, which does not narrow the protocol value of ${ceiling}. A challenge may lower a limit and may never raise or restate one.`
1411
+ });
1412
+ }
1413
+ }
1414
+ function checkListTightening(constraint, rule, profile, anchor, issues) {
1415
+ const members = parseInlineList(constraint.value);
1416
+ if (members === null || members.length === 0) {
1417
+ issues.push({
1418
+ code: "tightening_malformed",
1419
+ anchor,
1420
+ path: anchor,
1421
+ message: `"${constraint.key}" must be a non-empty inline list, written as [a, b].`
1422
+ });
1423
+ return;
1424
+ }
1425
+ if (rule.comparison === "subset") {
1426
+ const malformed2 = members.filter(
1427
+ (member) => !/^\.[A-Za-z0-9][A-Za-z0-9.]*$/.test(member)
1428
+ );
1429
+ if (malformed2.length > 0) {
1430
+ issues.push({
1431
+ code: "tightening_malformed",
1432
+ anchor,
1433
+ path: anchor,
1434
+ message: `"${constraint.key}" entries must be filename extensions including the dot: ${malformed2.join(", ")}.`
1435
+ });
1436
+ }
1437
+ return;
1438
+ }
1439
+ const { filename, maxFiles } = profile.submission;
1440
+ const malformed = members.filter(
1441
+ (member) => member.length > filename.maxCharacters || !SOLVER_SUBMISSION_NAME_RE.test(member)
1442
+ );
1443
+ if (malformed.length > 0) {
1444
+ issues.push({
1445
+ code: "tightening_malformed",
1446
+ anchor,
1447
+ path: anchor,
1448
+ message: `"${constraint.key}" names files that do not satisfy the profile's filename rule: ${malformed.join(", ")}.`
1449
+ });
1450
+ return;
1451
+ }
1452
+ if (filename.caseInsensitivelyUnique) {
1453
+ const seen = /* @__PURE__ */ new Set();
1454
+ const collisions = /* @__PURE__ */ new Set();
1455
+ for (const member of members) {
1456
+ const folded = member.toLowerCase();
1457
+ if (seen.has(folded)) collisions.add(folded);
1458
+ seen.add(folded);
1459
+ }
1460
+ if (collisions.size > 0) {
1461
+ issues.push({
1462
+ code: "tightening_malformed",
1463
+ anchor,
1464
+ path: anchor,
1465
+ message: `"${constraint.key}" names files that differ only in case: ${[...collisions].join(", ")}. A Submission's filenames are unique case-insensitively, so no Solver could satisfy this set.`
1466
+ });
1467
+ return;
1468
+ }
1469
+ }
1470
+ if (members.length > maxFiles) {
1471
+ issues.push({
1472
+ code: "tightening_exceeds_profile",
1473
+ anchor,
1474
+ path: anchor,
1475
+ message: `"${constraint.key}" requires ${members.length} files, above the profile's ceiling of ${maxFiles}.`
1476
+ });
1477
+ }
1478
+ }
1479
+ function parseInlineList(value) {
1480
+ if (!value.startsWith("[") || !value.endsWith("]")) return null;
1481
+ return value.slice(1, -1).split(",").map((member) => member.trim().replace(/^["']|["']$/g, "")).filter((member) => member.length > 0);
1482
+ }
1483
+
1484
+ // ../../packages/core/src/deployments.ts
1485
+ import { baseSepolia as baseSepolia2 } from "viem/chains";
1486
+ var DEPLOYMENTS = {
1487
+ [baseSepolia2.id]: {
1488
+ ELGORA_HUB_ADDRESS: "0x2f97b5f616495c2e923f39a46648eb783c053ad7",
1489
+ ELGORA_ESCROW_TOKEN_ADDRESS: "0x036cbd53842c5426634e7929541ec2318f3dcf7e",
1490
+ payoutSchemeAddress: "0x752d4305b8567b777d479dfa9847dc4f5ffb5750",
1491
+ ELGORA_SUBGRAPH_ENDPOINT: "https://api.goldsky.com/api/public/project_cmtu7wslg04s501ovc2je3beh/subgraphs/elgora-elgorahub/testnet-production/gn",
1492
+ deployBlock: 46353511,
1493
+ deployTransaction: "0xed3d3c8af3c69f9f8d4ec47da7179fe4b89bd4a4cd8d8232dbbd3d3f3d38e74b",
1494
+ sourceCommit: "1377da5c38dc5c90d8608ffa3cb15ad6f8567a01"
1495
+ }
1496
+ };
1497
+ var DEFAULT_CHAIN_ID = baseSepolia2.id;
1498
+ function getDeployment(chainId) {
1499
+ return DEPLOYMENTS[chainId];
1500
+ }
1501
+
1502
+ // src/check-page.ts
1503
+ var CHECK_PAGE_USAGE = "Usage: elgora-cli check-page <bounty_challenge_md_path>\n or: elgora-cli check-page -";
1504
+ async function main(_env = process.env, argv = process.argv.slice(2)) {
1505
+ const markdown = await readBountyChallengeMarkdown(argv);
1506
+ const { issues, profile } = checkChallengeConformance(markdown);
1507
+ console.log(
1508
+ JSON.stringify({
1509
+ event: issues.length === 0 ? "check_page.structure_conforming" : "check_page.issues",
1510
+ profile: profile?.profile ?? null,
1511
+ // Named so a caller cannot read a clean result as "this page is safe".
1512
+ checked: "structure",
1513
+ issues: issues.map((issue) => ({
1514
+ code: issue.code,
1515
+ anchor: issue.anchor,
1516
+ message: issue.message
1517
+ }))
1518
+ })
1519
+ );
1520
+ if (issues.length > 0) {
1521
+ process.exitCode = 1;
1522
+ }
1523
+ }
1524
+ async function readBountyChallengeMarkdown(argv) {
1525
+ const path5 = argv[0];
1526
+ if (!path5) {
1527
+ throw new Error(CHECK_PAGE_USAGE);
1528
+ }
1529
+ if (path5 === "-") {
1530
+ return readStdinText(process.stdin);
1531
+ }
1532
+ return readFile(path5, "utf8");
1533
+ }
1534
+
962
1535
  // ../../packages/core/src/api/artifact-storage.ts
963
1536
  var ArtifactTooLargeError = class extends Error {
964
1537
  code = "artifact_too_large";
@@ -3481,12 +4054,24 @@ async function classifyBeaconFetch(operation) {
3481
4054
  }
3482
4055
 
3483
4056
  // ../../packages/core/src/api/elgorahub.ts
3484
- import { encodeFunctionData, getAbiItem as getAbiItem2 } from "viem";
4057
+ import {
4058
+ BaseError,
4059
+ bytesToHex,
4060
+ ContractFunctionRevertedError,
4061
+ ContractFunctionZeroDataError,
4062
+ domainSeparator,
4063
+ encodeFunctionData,
4064
+ getAbiItem as getAbiItem2,
4065
+ parseAbi,
4066
+ parseSignature,
4067
+ recoverTypedDataAddress
4068
+ } from "viem";
3485
4069
  var ELGORA_HUB_ERRORS = ELGORA_HUB_ABI.filter(
3486
4070
  (item) => item.type === "error"
3487
4071
  );
3488
4072
  var ELGORA_HUB_CREATE_BOUNTY_ABI = [
3489
4073
  getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBounty" }),
4074
+ getAbiItem2({ abi: ELGORA_HUB_ABI, name: "createBountyWithAuthorization" }),
3490
4075
  ...ELGORA_HUB_ERRORS
3491
4076
  ];
3492
4077
  var ELGORA_HUB_SUBMIT_ABI = [
@@ -3546,6 +4131,117 @@ var ELGORA_HUB_ADD_GUARDIAN_ABI = [
3546
4131
  getAbiItem2({ abi: ELGORA_HUB_ABI, name: "addGuardian" }),
3547
4132
  ...ELGORA_HUB_ERRORS
3548
4133
  ];
4134
+ var RECEIVE_AUTHORIZATION_TYPES = {
4135
+ ReceiveWithAuthorization: [
4136
+ { name: "from", type: "address" },
4137
+ { name: "to", type: "address" },
4138
+ { name: "value", type: "uint256" },
4139
+ { name: "validAfter", type: "uint256" },
4140
+ { name: "validBefore", type: "uint256" },
4141
+ { name: "nonce", type: "bytes32" }
4142
+ ]
4143
+ };
4144
+ var USDC_DOMAIN_ABI = parseAbi([
4145
+ "function name() view returns (string)",
4146
+ "function version() view returns (string)",
4147
+ "function DOMAIN_SEPARATOR() view returns (bytes32)"
4148
+ ]);
4149
+ async function prepareAuthorizedCreateBountyTransaction(input) {
4150
+ const { transaction, publicClient, poster, tokenAddress } = input;
4151
+ if (await publicClient.getChainId() !== transaction.chain_id) {
4152
+ throw new Error("Funding RPC is on the wrong chain.");
4153
+ }
4154
+ const code = await publicClient.getCode({ address: poster });
4155
+ if (code && code !== "0x") return null;
4156
+ let name;
4157
+ let version;
4158
+ let tokenDomain;
4159
+ try {
4160
+ [name, version, tokenDomain] = await Promise.all([
4161
+ publicClient.readContract({
4162
+ address: tokenAddress,
4163
+ abi: USDC_DOMAIN_ABI,
4164
+ functionName: "name"
4165
+ }),
4166
+ publicClient.readContract({
4167
+ address: tokenAddress,
4168
+ abi: USDC_DOMAIN_ABI,
4169
+ functionName: "version"
4170
+ }),
4171
+ publicClient.readContract({
4172
+ address: tokenAddress,
4173
+ abi: USDC_DOMAIN_ABI,
4174
+ functionName: "DOMAIN_SEPARATOR"
4175
+ })
4176
+ ]);
4177
+ } catch (error) {
4178
+ if (error instanceof BaseError && error.walk(
4179
+ (cause) => cause instanceof ContractFunctionRevertedError || cause instanceof ContractFunctionZeroDataError
4180
+ ))
4181
+ return null;
4182
+ throw error;
4183
+ }
4184
+ const block = await publicClient.getBlock();
4185
+ const authorization = {
4186
+ domain: {
4187
+ name,
4188
+ version,
4189
+ chainId: transaction.chain_id,
4190
+ verifyingContract: tokenAddress
4191
+ },
4192
+ types: RECEIVE_AUTHORIZATION_TYPES,
4193
+ primaryType: "ReceiveWithAuthorization",
4194
+ message: {
4195
+ from: poster,
4196
+ to: transaction.to,
4197
+ value: BigInt(transaction.args.escrow_amount),
4198
+ validAfter: 0n,
4199
+ validBefore: block.timestamp + 600n,
4200
+ nonce: bytesToHex(crypto.getRandomValues(new Uint8Array(32)))
4201
+ }
4202
+ };
4203
+ if (domainSeparator({ domain: authorization.domain }).toLowerCase() !== tokenDomain.toLowerCase()) {
4204
+ throw new Error("USDC authorization domain does not match the token.");
4205
+ }
4206
+ let signature;
4207
+ try {
4208
+ signature = await input.sign(authorization);
4209
+ } catch (error) {
4210
+ const seen = /* @__PURE__ */ new Set();
4211
+ let current = error;
4212
+ let unsupported = false;
4213
+ while (current && typeof current === "object" && !seen.has(current)) {
4214
+ seen.add(current);
4215
+ const cause = current;
4216
+ if (cause.code === 4001 || cause.name === "UserRejectedRequestError")
4217
+ throw error;
4218
+ unsupported ||= cause.code === 4200 || cause.code === -32601 || cause.code === -32004;
4219
+ current = cause.cause;
4220
+ }
4221
+ if (unsupported) return null;
4222
+ throw error;
4223
+ }
4224
+ if ((await recoverTypedDataAddress({ ...authorization, signature })).toLowerCase() !== poster.toLowerCase()) {
4225
+ throw new Error("USDC authorization was not signed by the funding Poster.");
4226
+ }
4227
+ const { r, s, yParity } = parseSignature(signature);
4228
+ const { validAfter, validBefore, nonce } = authorization.message;
4229
+ return {
4230
+ chain_id: transaction.chain_id,
4231
+ to: transaction.to,
4232
+ value: "0",
4233
+ data: encodeFunctionData({
4234
+ abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
4235
+ functionName: "createBountyWithAuthorization",
4236
+ args: [
4237
+ transaction.args.spec_commitment,
4238
+ authorization.message.value,
4239
+ BigInt(transaction.args.submission_deadline),
4240
+ { validAfter, validBefore, nonce, v: yParity + 27, r, s }
4241
+ ]
4242
+ })
4243
+ };
4244
+ }
3549
4245
  function encodeAddGuardianTransaction(input) {
3550
4246
  return {
3551
4247
  chain_id: input.chainId,
@@ -3699,15 +4395,34 @@ var ELGORA_HUB_FEE_POLICY_ABI = [
3699
4395
  // ../../packages/core/src/api/orchestrator-readiness.ts
3700
4396
  import { z as z6 } from "zod";
3701
4397
  var MAX_ORCHESTRATOR_ISSUES = 20;
3702
- var boundedText = (maxLength) => z6.string().trim().min(1).max(maxLength);
3703
4398
  var readinessIssueSchema = z6.object({
3704
- path: boundedText(120),
3705
- message: boundedText(500)
4399
+ path: z6.string().trim().min(1).max(120),
4400
+ message: z6.string().trim().min(1).max(1500),
4401
+ code: z6.enum([
4402
+ "acceptance_undecidable",
4403
+ "winner_unresolved",
4404
+ "source_unidentified",
4405
+ "domain_underspecified",
4406
+ "platform_conflict",
4407
+ "operative_instruction",
4408
+ "placeholder_unreplaced",
4409
+ "unsafe_content"
4410
+ ]),
4411
+ anchor: z6.string().trim().min(1).max(120),
4412
+ hazard: z6.boolean()
3706
4413
  }).strict();
3707
4414
  var readinessResponseSchema = z6.object({
3708
- judgeable: z6.boolean(),
3709
- issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES)
4415
+ issues: z6.array(readinessIssueSchema).max(MAX_ORCHESTRATOR_ISSUES),
4416
+ judgeable: z6.boolean()
3710
4417
  }).strict();
4418
+ var READINESS_OUTPUT_JSON_SCHEMA = z6.toJSONSchema(readinessResponseSchema, {
4419
+ override: ({ jsonSchema }) => {
4420
+ delete jsonSchema.minLength;
4421
+ delete jsonSchema.maxLength;
4422
+ delete jsonSchema.maxItems;
4423
+ }
4424
+ });
4425
+ delete READINESS_OUTPUT_JSON_SCHEMA.$schema;
3711
4426
  var providerMessageSchema = z6.object({
3712
4427
  id: z6.string().optional(),
3713
4428
  choices: z6.array(
@@ -3875,7 +4590,7 @@ var deliveryKeyConfigResponseSchema = z9.strictObject({
3875
4590
  import { keccak256 as keccak2564, toBytes as toBytes3, zeroAddress as zeroAddress2 } from "viem";
3876
4591
  import { z as z10 } from "zod";
3877
4592
  var WRITTEN_VERDICT_FORMAT_VERSION = 0;
3878
- var FRONTMATTER_DELIMITER = "---";
4593
+ var FRONTMATTER_DELIMITER2 = "---";
3879
4594
  var decimalSchema = z10.string().regex(/^[1-9][0-9]*$/, "Must be a positive integer").transform(Number).refine(Number.isSafeInteger, "Must be a safe integer");
3880
4595
  var bountyIdSchema2 = z10.string().regex(/^(0|[1-9][0-9]*)$/, "Must be a non-negative integer");
3881
4596
  var addressSchema2 = z10.string().refine(isHexAddress, "Must be a hex-encoded address").refine((value) => value === value.toLowerCase(), "Must use lowercase hex").transform((value) => value);
@@ -3907,7 +4622,7 @@ var writtenVerdictSchema = z10.discriminatedUnion("outcome", [
3907
4622
  }).strict()
3908
4623
  ]);
3909
4624
  function buildWrittenVerdictMarkdown(input) {
3910
- const common = `${FRONTMATTER_DELIMITER}
4625
+ const common = `${FRONTMATTER_DELIMITER2}
3911
4626
  format_version: ${WRITTEN_VERDICT_FORMAT_VERSION}
3912
4627
  chain_id: ${input.config.chainId}
3913
4628
  hub_address: ${input.config.hubAddress.toLowerCase()}
@@ -3917,7 +4632,7 @@ outcome: ${input.outcome}`;
3917
4632
  awarded_submission_commitment: ${input.awardedSubmissionCommitment.toLowerCase()}` : `winner: ${zeroAddress2}`;
3918
4633
  const markdown = `${common}
3919
4634
  ${position}
3920
- ${FRONTMATTER_DELIMITER}
4635
+ ${FRONTMATTER_DELIMITER2}
3921
4636
  ${input.verdictBody}`;
3922
4637
  parseWrittenVerdictMarkdown(markdown);
3923
4638
  return markdown;
@@ -3928,11 +4643,11 @@ function deriveReportCommitment(markdown) {
3928
4643
  function parseWrittenVerdictMarkdown(markdown) {
3929
4644
  const closingDelimiter = markdown.indexOf(
3930
4645
  `
3931
- ${FRONTMATTER_DELIMITER}
4646
+ ${FRONTMATTER_DELIMITER2}
3932
4647
  `,
3933
- FRONTMATTER_DELIMITER.length + 1
4648
+ FRONTMATTER_DELIMITER2.length + 1
3934
4649
  );
3935
- if (!markdown.startsWith(`${FRONTMATTER_DELIMITER}
4650
+ if (!markdown.startsWith(`${FRONTMATTER_DELIMITER2}
3936
4651
  `) || closingDelimiter < 0) {
3937
4652
  throw invalidWrittenVerdict(
3938
4653
  "frontmatter",
@@ -3940,12 +4655,12 @@ ${FRONTMATTER_DELIMITER}
3940
4655
  );
3941
4656
  }
3942
4657
  const frontmatter = markdown.slice(
3943
- FRONTMATTER_DELIMITER.length + 1,
4658
+ FRONTMATTER_DELIMITER2.length + 1,
3944
4659
  closingDelimiter
3945
4660
  );
3946
4661
  const rawVerdict = {
3947
4662
  verdict_body: markdown.slice(
3948
- closingDelimiter + FRONTMATTER_DELIMITER.length + 2
4663
+ closingDelimiter + FRONTMATTER_DELIMITER2.length + 2
3949
4664
  )
3950
4665
  };
3951
4666
  for (const [index, line] of frontmatter.split("\n").entries()) {
@@ -4018,14 +4733,12 @@ function assertChainId(actual, expected) {
4018
4733
  throw new Error(`RPC returned chain_id ${actual}, expected ${expected}`);
4019
4734
  }
4020
4735
  }
4736
+ var SIGNING_BLOCK_OFFSET = 3n;
4021
4737
  async function fetchSignableBlock(client) {
4022
- const block = await client.getBlock();
4023
- if (block.hash === null) {
4024
- throw new Error(
4025
- "The latest block has no hash yet \u2014 retry signing in a moment."
4026
- );
4027
- }
4028
- return { number: block.number, hash: block.hash };
4738
+ const head = await client.getBlockNumber();
4739
+ const anchorNumber = head > SIGNING_BLOCK_OFFSET ? head - SIGNING_BLOCK_OFFSET : 0n;
4740
+ const anchor = await client.getBlock({ blockNumber: anchorNumber });
4741
+ return { number: anchor.number, hash: anchor.hash };
4029
4742
  }
4030
4743
  async function assertHubDeployment(client, chainId, hubAddress) {
4031
4744
  const [actualChainId, hubCode] = await Promise.all([
@@ -4068,7 +4781,7 @@ function withDeploymentDefaults(env) {
4068
4781
 
4069
4782
  // src/claim.ts
4070
4783
  var CLAIMANT_ADDRESS_FLAG = "--claimant-address";
4071
- async function main(env = process.env, argv = process.argv.slice(2)) {
4784
+ async function main2(env = process.env, argv = process.argv.slice(2)) {
4072
4785
  const args = readClaimArgs(argv);
4073
4786
  const config = loadClaimConfig(env, args.claimantAddress);
4074
4787
  const account = config.claimantPrivateKey ? privateKeyToAccount(config.claimantPrivateKey) : null;
@@ -4470,12 +5183,16 @@ function formatApiError(body) {
4470
5183
  const apiError = body.error;
4471
5184
  const parts = [String(apiError.message)];
4472
5185
  if (Array.isArray(apiError.issues) && apiError.issues.length > 0) {
4473
- parts.push(`issues: ${JSON.stringify(apiError.issues)}`);
5186
+ for (const issue of apiError.issues) {
5187
+ if (!issue || typeof issue !== "object" || typeof issue.message !== "string")
5188
+ continue;
5189
+ parts.push(`${issue.path || "request"}: ${issue.message}`);
5190
+ }
4474
5191
  }
4475
5192
  if (apiError.next_action !== void 0 && apiError.next_action !== null) {
4476
5193
  parts.push(`next action: ${String(apiError.next_action)}`);
4477
5194
  }
4478
- return parts.join(" \u2014 ");
5195
+ return parts.join("\n");
4479
5196
  }
4480
5197
  return JSON.stringify(body);
4481
5198
  }
@@ -4579,7 +5296,7 @@ import {
4579
5296
  // ../../packages/core/src/auth/signature.ts
4580
5297
  import {
4581
5298
  isAddressEqual,
4582
- recoverTypedDataAddress
5299
+ recoverTypedDataAddress as recoverTypedDataAddress2
4583
5300
  } from "viem";
4584
5301
 
4585
5302
  // ../../packages/core/src/auth/request-approval-header.ts
@@ -4700,7 +5417,7 @@ import { mkdir, writeFile } from "fs/promises";
4700
5417
  import path from "path";
4701
5418
  import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
4702
5419
  var DEFAULT_OUTPUT_ROOT = "./guardian-open";
4703
- async function main2(env = process.env, argv = process.argv.slice(2)) {
5420
+ async function main3(env = process.env, argv = process.argv.slice(2)) {
4704
5421
  const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
4705
5422
  const envWithDefaults = withDeploymentDefaults(env);
4706
5423
  const config = loadClientDeploymentConfig(
@@ -4791,7 +5508,11 @@ async function main2(env = process.env, argv = process.argv.slice(2)) {
4791
5508
  bounty_id: args.bountyId,
4792
5509
  solver: args.solver,
4793
5510
  submission_commitment: args.submissionCommitment,
4794
- artifact_count: opened.artifacts.length
5511
+ artifact_count: opened.artifacts.length,
5512
+ // The budget the Guardian has to vet these artifacts against before
5513
+ // extracting any archive among them. Reported here so it comes from the
5514
+ // running client rather than a possibly older copy of the skill.
5515
+ max_extracted_bytes: MAX_SOLVER_SUBMISSION_EXTRACTED_BYTES
4795
5516
  })
4796
5517
  );
4797
5518
  }
@@ -4857,7 +5578,7 @@ function readGuardianPrivateKeys(env) {
4857
5578
 
4858
5579
  // src/guardian-deliver-key.ts
4859
5580
  var USAGE = "Usage: elgora-cli guardian:deliver-key <bounty_id> <solver> <submission_commitment>";
4860
- async function main3(env = process.env, argv = process.argv.slice(2)) {
5581
+ async function main4(env = process.env, argv = process.argv.slice(2)) {
4861
5582
  const { apiBaseUrl, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
4862
5583
  const { bountyId, solver, submissionCommitment } = readArgs(positionalArgs);
4863
5584
  const envWithDefaults = withDeploymentDefaults(env);
@@ -4999,6 +5720,7 @@ var VERDICT_FIELDS = `
4999
5720
  awardedSubmissionCommitment
5000
5721
  reportCommitment
5001
5722
  payoutData
5723
+ committedTxHash
5002
5724
  committedAt
5003
5725
  updatedAt
5004
5726
  `;
@@ -5458,6 +6180,7 @@ function mapVerdict(row) {
5458
6180
  ),
5459
6181
  reportCommitment: asHex(record.reportCommitment, "reportCommitment"),
5460
6182
  payoutData: asHex(record.payoutData, "payoutData"),
6183
+ committedTxHash: asHex(record.committedTxHash, "committedTxHash"),
5461
6184
  committedAt: asString(record.committedAt, "committedAt"),
5462
6185
  updatedAt: asString(record.updatedAt, "updatedAt")
5463
6186
  };
@@ -5686,7 +6409,7 @@ function asNullableOutcome(value, field) {
5686
6409
 
5687
6410
  // src/guardian-judgeable.ts
5688
6411
  import { zeroAddress as zeroAddress3 } from "viem";
5689
- async function main4(env = process.env, argv = process.argv.slice(2)) {
6412
+ async function main5(env = process.env, argv = process.argv.slice(2)) {
5690
6413
  const guardian = readGuardianAddress(argv);
5691
6414
  const config = readConfig(withDeploymentDefaults(env));
5692
6415
  const snapshot = await createElgoraSubgraphClient({
@@ -5780,10 +6503,10 @@ function requiredUrl(env, name) {
5780
6503
  }
5781
6504
 
5782
6505
  // src/guardian-prove-roster-key.ts
5783
- import { readFile } from "fs/promises";
5784
- import { text as readStdinText } from "stream/consumers";
6506
+ import { readFile as readFile2 } from "fs/promises";
6507
+ import { text as readStdinText2 } from "stream/consumers";
5785
6508
  import { privateKeyToAccount as privateKeyToAccount5 } from "viem/accounts";
5786
- async function main5(env = process.env, argv = process.argv.slice(2)) {
6509
+ async function main6(env = process.env, argv = process.argv.slice(2)) {
5787
6510
  const [path5] = argv;
5788
6511
  if (!path5) {
5789
6512
  throw new Error(
@@ -5791,7 +6514,7 @@ async function main5(env = process.env, argv = process.argv.slice(2)) {
5791
6514
  );
5792
6515
  }
5793
6516
  const challenge = parseChallenge(
5794
- path5 === "-" ? await readStdinText(process.stdin) : await readFile(path5, "utf8")
6517
+ path5 === "-" ? await readStdinText2(process.stdin) : await readFile2(path5, "utf8")
5795
6518
  );
5796
6519
  const envWithDefaults = withDeploymentDefaults(env);
5797
6520
  const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
@@ -5858,7 +6581,7 @@ function parseChallenge(text) {
5858
6581
  }
5859
6582
 
5860
6583
  // src/guardian-verdict.ts
5861
- import { readFile as readFile2 } from "fs/promises";
6584
+ import { readFile as readFile3 } from "fs/promises";
5862
6585
  import { zeroAddress as zeroAddress4 } from "viem";
5863
6586
  import { privateKeyToAccount as privateKeyToAccount6 } from "viem/accounts";
5864
6587
  var OUTCOME = {
@@ -5895,7 +6618,7 @@ function parseVerdictArgs(argv) {
5895
6618
  }
5896
6619
  throw usageError();
5897
6620
  }
5898
- async function main6(env = process.env, argv = process.argv.slice(2)) {
6621
+ async function main7(env = process.env, argv = process.argv.slice(2)) {
5899
6622
  const { apiBaseUrl: apiBaseUrlOverride, argv: positionalArgs } = extractApiBaseUrlFlag(argv);
5900
6623
  const args = parseVerdictArgs(positionalArgs);
5901
6624
  const envWithDefaults = withDeploymentDefaults(env);
@@ -5915,7 +6638,7 @@ async function main6(env = process.env, argv = process.argv.slice(2)) {
5915
6638
  const publicClient = createChainPublicClient(config.chainId, rpcUrl);
5916
6639
  const walletClient = createChainWalletClient(config.chainId, rpcUrl, account);
5917
6640
  await assertHubDeployment(publicClient, config.chainId, config.hubAddress);
5918
- const verdictBody = await readFile2(args.verdictBodyPath, "utf8");
6641
+ const verdictBody = await readFile3(args.verdictBodyPath, "utf8");
5919
6642
  const markdown = args.outcome === "awarded" ? buildWrittenVerdictMarkdown({
5920
6643
  config,
5921
6644
  bountyId: args.bountyId,
@@ -6179,12 +6902,76 @@ function resolveChainId(selector) {
6179
6902
  );
6180
6903
  }
6181
6904
 
6905
+ // src/version.ts
6906
+ var CLI_VERSION = true ? "7.4.1" : "0.0.0-dev";
6907
+ var CLI_NODE_ENGINES = true ? ">=24 <25" : ">=24 <25";
6908
+
6909
+ // src/node-engines.ts
6910
+ function unsupportedNodeMessage(running = process.versions.node, range = CLI_NODE_ENGINES) {
6911
+ if (satisfiesRange(running, range)) return null;
6912
+ return `elgora-cli needs Node ${range}; this is Node ${running}. Install a Node inside that range and run the command again. --version and help still answer.`;
6913
+ }
6914
+ function satisfiesRange(version, range) {
6915
+ const running = parseVersion(version);
6916
+ if (!running) return false;
6917
+ return range.split("||").some(
6918
+ (alternative) => alternative.trim().split(/\s+/).filter(Boolean).every((comparator) => satisfiesComparator(running, comparator))
6919
+ );
6920
+ }
6921
+ function satisfiesComparator(running, comparator) {
6922
+ const match = /^(>=|<=|>|<|=|\^|~)?v?(\d+)(?:\.(\d+|x|\*))?(?:\.(\d+|x|\*))?$/.exec(
6923
+ comparator
6924
+ );
6925
+ if (!match) return false;
6926
+ const operator = match[1] ?? "=";
6927
+ const major = Number(match[2]);
6928
+ const minorRaw = match[3];
6929
+ const patchRaw = match[4];
6930
+ const wildcard = minorRaw === void 0 || minorRaw === "x" || minorRaw === "*" || patchRaw === void 0 || patchRaw === "x" || patchRaw === "*";
6931
+ const bound = [
6932
+ major,
6933
+ minorRaw && !["x", "*"].includes(minorRaw) ? Number(minorRaw) : 0,
6934
+ patchRaw && !["x", "*"].includes(patchRaw) ? Number(patchRaw) : 0
6935
+ ];
6936
+ const order = compareVersions(running, bound);
6937
+ switch (operator) {
6938
+ case ">":
6939
+ return order > 0;
6940
+ case ">=":
6941
+ return order >= 0;
6942
+ case "<":
6943
+ return order < 0;
6944
+ case "<=":
6945
+ return order <= 0;
6946
+ case "^":
6947
+ return order >= 0 && running[0] === major;
6948
+ case "~": {
6949
+ const anyMinor = minorRaw === void 0 || minorRaw === "x" || minorRaw === "*";
6950
+ return order >= 0 && running[0] === major && (anyMinor || running[1] === bound[1]);
6951
+ }
6952
+ default:
6953
+ return wildcard ? running[0] === major && (minorRaw === void 0 || ["x", "*"].includes(minorRaw) || running[1] === bound[1]) : order === 0;
6954
+ }
6955
+ }
6956
+ function parseVersion(version) {
6957
+ const match = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
6958
+ if (!match) return null;
6959
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
6960
+ }
6961
+ function compareVersions(left, right) {
6962
+ for (let index = 0; index < 3; index += 1) {
6963
+ const difference = (left[index] ?? 0) - (right[index] ?? 0);
6964
+ if (difference !== 0) return difference;
6965
+ }
6966
+ return 0;
6967
+ }
6968
+
6182
6969
  // src/poster-open-winning-submission.ts
6183
6970
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
6184
6971
  import path2 from "path";
6185
6972
  import { privateKeyToAccount as privateKeyToAccount7 } from "viem/accounts";
6186
6973
  var DEFAULT_OUTPUT_ROOT2 = "./winning-submission";
6187
- async function main7(env = process.env, argv = process.argv.slice(2)) {
6974
+ async function main8(env = process.env, argv = process.argv.slice(2)) {
6188
6975
  const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
6189
6976
  const args = readPosterOpenWinningSubmissionArgs(remainingArgv);
6190
6977
  const envWithDefaults = withDeploymentDefaults(env);
@@ -6323,25 +7110,26 @@ function required3(env, name) {
6323
7110
  }
6324
7111
 
6325
7112
  // src/publish-fund.ts
6326
- import { readFile as readFile3 } from "fs/promises";
6327
- import { text as readStdinText2 } from "stream/consumers";
7113
+ import { readFile as readFile4 } from "fs/promises";
7114
+ import { text as readStdinText3 } from "stream/consumers";
6328
7115
  import {
6329
- parseAbi,
7116
+ decodeFunctionData,
7117
+ parseAbi as parseAbi2,
6330
7118
  parseEventLogs
6331
7119
  } from "viem";
6332
7120
  import { privateKeyToAccount as privateKeyToAccount8 } from "viem/accounts";
6333
7121
  var PUBLISH_FUND_USAGE = "Usage: elgora-cli poster:publish-fund <bounty_challenge_md_path>\n or: elgora-cli poster:publish-fund -";
6334
- var usdcAbi = parseAbi([
7122
+ var usdcAbi = parseAbi2([
6335
7123
  "function approve(address spender,uint256 value) returns (bool)",
6336
7124
  "function balanceOf(address account) view returns (uint256)"
6337
7125
  ]);
6338
- async function main8(env = process.env, argv = process.argv.slice(2)) {
7126
+ async function main9(env = process.env, argv = process.argv.slice(2)) {
6339
7127
  const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
6340
7128
  const config = loadPublishFundConfig(
6341
7129
  withDeploymentDefaults(env),
6342
7130
  apiBaseUrlOverride
6343
7131
  );
6344
- const bountyChallengeMarkdown = await readBountyChallengeMarkdown(remainingArgv);
7132
+ const bountyChallengeMarkdown = await readBountyChallengeMarkdown2(remainingArgv);
6345
7133
  const account = privateKeyToAccount8(config.posterPrivateKey);
6346
7134
  const publicClient = createChainPublicClient(config.chainId, config.rpcUrl);
6347
7135
  const walletClient = createChainWalletClient(
@@ -6415,35 +7203,58 @@ async function main8(env = process.env, argv = process.argv.slice(2)) {
6415
7203
  `Poster has insufficient escrow token balance on chain ${config.chainId}: required=${escrowAmount.toString()} available=${usdcBalance.toString()}`
6416
7204
  );
6417
7205
  }
6418
- const approveHash = await walletClient.writeContract({
6419
- address: config.escrowTokenAddress,
6420
- abi: usdcAbi,
6421
- functionName: "approve",
6422
- args: [config.hubAddress, escrowAmount]
6423
- });
6424
- const approveReceipt = await publicClient.waitForTransactionReceipt({
6425
- hash: approveHash
7206
+ const authorized = await prepareAuthorizedCreateBountyTransaction({
7207
+ transaction: prepared.create_bounty_transaction,
7208
+ tokenAddress: config.escrowTokenAddress,
7209
+ poster: account.address,
7210
+ publicClient,
7211
+ sign: (typedData) => account.signTypedData(typedData)
6426
7212
  });
6427
- if (approveReceipt.status !== "success") {
6428
- throw new Error(`approve transaction reverted: ${approveHash}`);
7213
+ let createBountyHash;
7214
+ if (authorized) {
7215
+ const call = decodeFunctionData({
7216
+ abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
7217
+ data: authorized.data
7218
+ });
7219
+ if (call.functionName !== "createBountyWithAuthorization") {
7220
+ throw new Error("Expected an authorization funding call.");
7221
+ }
7222
+ createBountyHash = await walletClient.writeContract({
7223
+ address: authorized.to,
7224
+ abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
7225
+ ...call
7226
+ });
7227
+ } else {
7228
+ const approveHash = await walletClient.writeContract({
7229
+ address: config.escrowTokenAddress,
7230
+ abi: usdcAbi,
7231
+ functionName: "approve",
7232
+ args: [config.hubAddress, escrowAmount]
7233
+ });
7234
+ const approveReceipt = await publicClient.waitForTransactionReceipt({
7235
+ hash: approveHash
7236
+ });
7237
+ if (approveReceipt.status !== "success") {
7238
+ throw new Error(`approve transaction reverted: ${approveHash}`);
7239
+ }
7240
+ console.log(
7241
+ JSON.stringify({
7242
+ event: "publish_fund.usdc_approved",
7243
+ tx_hash: approveHash,
7244
+ block_number: approveReceipt.blockNumber.toString()
7245
+ })
7246
+ );
7247
+ createBountyHash = await walletClient.writeContract({
7248
+ address: config.hubAddress,
7249
+ abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
7250
+ functionName: "createBounty",
7251
+ args: [
7252
+ localSpecCommitment,
7253
+ escrowAmount,
7254
+ BigInt(prepared.create_bounty_transaction.args.submission_deadline)
7255
+ ]
7256
+ });
6429
7257
  }
6430
- console.log(
6431
- JSON.stringify({
6432
- event: "publish_fund.usdc_approved",
6433
- tx_hash: approveHash,
6434
- block_number: approveReceipt.blockNumber.toString()
6435
- })
6436
- );
6437
- const createBountyHash = await walletClient.writeContract({
6438
- address: config.hubAddress,
6439
- abi: ELGORA_HUB_CREATE_BOUNTY_ABI,
6440
- functionName: "createBounty",
6441
- args: [
6442
- localSpecCommitment,
6443
- escrowAmount,
6444
- BigInt(prepared.create_bounty_transaction.args.submission_deadline)
6445
- ]
6446
- });
6447
7258
  const createBountyReceipt = await publicClient.waitForTransactionReceipt({
6448
7259
  hash: createBountyHash
6449
7260
  });
@@ -6493,15 +7304,15 @@ async function requestPreparePublication(input) {
6493
7304
  );
6494
7305
  return bountyPublicationResponseSchema.parse(body);
6495
7306
  }
6496
- async function readBountyChallengeMarkdown(argv) {
7307
+ async function readBountyChallengeMarkdown2(argv) {
6497
7308
  const path5 = argv[0];
6498
7309
  if (!path5) {
6499
7310
  throw new Error(PUBLISH_FUND_USAGE);
6500
7311
  }
6501
7312
  if (path5 === "-") {
6502
- return readStdinText2(process.stdin);
7313
+ return readStdinText3(process.stdin);
6503
7314
  }
6504
- return readFile3(path5, "utf8");
7315
+ return readFile4(path5, "utf8");
6505
7316
  }
6506
7317
  function loadPublishFundConfig(env, apiBaseUrlOverride) {
6507
7318
  const clientConfig = loadClientDeploymentConfig(env, apiBaseUrlOverride);
@@ -6534,7 +7345,7 @@ import { isAddress as isAddress4 } from "viem";
6534
7345
  import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
6535
7346
  var ROSTER_ADMIN_ADDRESS_FLAG = "--roster-admin-address";
6536
7347
  var ENCRYPTION_PUBLIC_KEY_RE = /^[A-Za-z0-9_-]{43}$/;
6537
- async function main9(env = process.env, argv = process.argv.slice(2)) {
7348
+ async function main10(env = process.env, argv = process.argv.slice(2)) {
6538
7349
  const args = parseRosterAddGuardianArgs(argv);
6539
7350
  const config = loadRosterAddGuardianConfig(env, args.rosterAdminAddress);
6540
7351
  const account = config.rosterAdminPrivateKey ? privateKeyToAccount9(config.rosterAdminPrivateKey) : null;
@@ -6691,7 +7502,7 @@ function usageError2() {
6691
7502
  // src/roster-prepare-guardian-proof.ts
6692
7503
  import { isAddress as isAddress5 } from "viem";
6693
7504
  var ENCRYPTION_PUBLIC_KEY_RE2 = /^[A-Za-z0-9_-]{43}$/;
6694
- async function main10(env = process.env, argv = process.argv.slice(2)) {
7505
+ async function main11(env = process.env, argv = process.argv.slice(2)) {
6695
7506
  const args = parseArgs(argv);
6696
7507
  const envWithDefaults = withDeploymentDefaults(env);
6697
7508
  const { chainId, hubAddress } = loadPublicDeploymentConfig(envWithDefaults);
@@ -6738,17 +7549,17 @@ function usageError3() {
6738
7549
  }
6739
7550
 
6740
7551
  // src/roster-verify-guardian-proof.ts
6741
- import { readFile as readFile4 } from "fs/promises";
7552
+ import { readFile as readFile5 } from "fs/promises";
6742
7553
  import { isAddress as isAddress6, isHex as isHex5, recoverMessageAddress } from "viem";
6743
- async function main11(_env = process.env, argv = process.argv.slice(2)) {
7554
+ async function main12(_env = process.env, argv = process.argv.slice(2)) {
6744
7555
  const [preparedPath, responsePath] = argv;
6745
7556
  if (!preparedPath || !responsePath) {
6746
7557
  throw new Error(
6747
7558
  "Usage: elgora-cli roster:verify-guardian-proof <prepared_json_path> <response_json_path>"
6748
7559
  );
6749
7560
  }
6750
- const prepared = parsePrepared(await readFile4(preparedPath, "utf8"));
6751
- const response = parseResponse(await readFile4(responsePath, "utf8"));
7561
+ const prepared = parsePrepared(await readFile5(preparedPath, "utf8"));
7562
+ const response = parseResponse(await readFile5(responsePath, "utf8"));
6752
7563
  const fixture = base64UrlToBytes(prepared.fixture);
6753
7564
  const signedPayload = buildGuardianRosterKeyProofSignedPayload(
6754
7565
  prepared.message,
@@ -6830,11 +7641,11 @@ function get(value, key) {
6830
7641
  }
6831
7642
 
6832
7643
  // src/spec-commitment.ts
6833
- import { readFile as readFile5 } from "fs/promises";
6834
- import { text as readStdinText3 } from "stream/consumers";
7644
+ import { readFile as readFile6 } from "fs/promises";
7645
+ import { text as readStdinText4 } from "stream/consumers";
6835
7646
  var SPEC_COMMITMENT_USAGE = "Usage: elgora-cli spec-commitment <bounty_challenge_md_path>\n or: elgora-cli spec-commitment -";
6836
- async function main12(_env = process.env, argv = process.argv.slice(2)) {
6837
- const markdown = await readBountyChallengeMarkdown2(argv);
7647
+ async function main13(_env = process.env, argv = process.argv.slice(2)) {
7648
+ const markdown = await readBountyChallengeMarkdown3(argv);
6838
7649
  const specCommitment = deriveBountyChallengeContentCommitment(markdown);
6839
7650
  console.log(
6840
7651
  JSON.stringify({
@@ -6844,19 +7655,19 @@ async function main12(_env = process.env, argv = process.argv.slice(2)) {
6844
7655
  })
6845
7656
  );
6846
7657
  }
6847
- async function readBountyChallengeMarkdown2(argv) {
7658
+ async function readBountyChallengeMarkdown3(argv) {
6848
7659
  const path5 = argv[0];
6849
7660
  if (!path5) {
6850
7661
  throw new Error(SPEC_COMMITMENT_USAGE);
6851
7662
  }
6852
7663
  if (path5 === "-") {
6853
- return readStdinText3(process.stdin);
7664
+ return readStdinText4(process.stdin);
6854
7665
  }
6855
- return readFile5(path5, "utf8");
7666
+ return readFile6(path5, "utf8");
6856
7667
  }
6857
7668
 
6858
7669
  // src/submit.ts
6859
- import { readdir, readFile as readFile6, stat } from "fs/promises";
7670
+ import { readdir, readFile as readFile7, stat } from "fs/promises";
6860
7671
  import path3 from "path";
6861
7672
  import { createInterface } from "readline";
6862
7673
  import { isAddress as isAddress7 } from "viem";
@@ -6910,7 +7721,7 @@ var MEDIA_TYPE_BY_EXTENSION = {
6910
7721
  diff: "text/x-diff"
6911
7722
  };
6912
7723
  var DEFAULT_MEDIA_TYPE = "application/octet-stream";
6913
- async function main13(env = process.env, argv = process.argv.slice(2)) {
7724
+ async function main14(env = process.env, argv = process.argv.slice(2)) {
6914
7725
  const { solverAddress, argv: argvWithoutSolverAddress } = extractSolverAddressFlag(argv);
6915
7726
  const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argvWithoutSolverAddress);
6916
7727
  const config = loadSubmitConfig(env, apiBaseUrlOverride, solverAddress);
@@ -6987,7 +7798,7 @@ async function main13(env = process.env, argv = process.argv.slice(2)) {
6987
7798
  });
6988
7799
  if (ciphertext.byteLength > MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES) {
6989
7800
  throw new Error(
6990
- `Encrypted submission is ${ciphertext.byteLength} bytes and exceeds Elgora's 50 MiB encrypted submission limit. Reduce the final artifact package before submitting.`
7801
+ `Encrypted submission is ${ciphertext.byteLength} bytes and exceeds Elgora's ${formatMebibytes(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)} encrypted submission limit. Reduce the final artifact package before submitting.`
6991
7802
  );
6992
7803
  }
6993
7804
  const prepared = await (async () => {
@@ -7274,18 +8085,23 @@ async function readArtifactDirectory(artifactDir) {
7274
8085
  if (entries.length === 0) {
7275
8086
  throw new Error(`No files found in artifact directory: ${artifactDir}`);
7276
8087
  }
8088
+ if (entries.length > MAX_SOLVER_SUBMISSION_FILES) {
8089
+ throw new Error(
8090
+ `Artifact directory holds ${entries.length} files and exceeds Elgora's maximum of ${MAX_SOLVER_SUBMISSION_FILES} files per Submission. Combine them into fewer files, if the challenge allows it, before submitting.`
8091
+ );
8092
+ }
7277
8093
  let plaintextByteLength = 0;
7278
8094
  for (const entry of entries) {
7279
8095
  plaintextByteLength += (await stat(path3.join(artifactDir, entry.name))).size;
7280
- if (plaintextByteLength > MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES) {
8096
+ if (plaintextByteLength > MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES) {
7281
8097
  throw new Error(
7282
- `Artifact directory exceeds Elgora's 50 MiB encrypted submission limit before packaging. Reduce the final artifact package before submitting.`
8098
+ `Artifact directory exceeds Elgora's ${formatMebibytes(MAX_SOLVER_SUBMISSION_ARTIFACT_BYTES)} of files per Submission, which is what fits the ${formatMebibytes(MAX_SOLVER_SUBMISSION_CIPHERTEXT_BYTES)} encrypted submission limit once packaged. Reduce the final artifact package before submitting.`
7283
8099
  );
7284
8100
  }
7285
8101
  }
7286
8102
  return Promise.all(
7287
8103
  entries.map(async (entry) => {
7288
- const bytes = await readFile6(path3.join(artifactDir, entry.name));
8104
+ const bytes = await readFile7(path3.join(artifactDir, entry.name));
7289
8105
  return {
7290
8106
  name: entry.name,
7291
8107
  media_type: guessMediaType(entry.name),
@@ -7294,6 +8110,9 @@ async function readArtifactDirectory(artifactDir) {
7294
8110
  })
7295
8111
  );
7296
8112
  }
8113
+ function formatMebibytes(byteLength) {
8114
+ return `${byteLength / 1024 / 1024} MiB`;
8115
+ }
7297
8116
  function guessMediaType(fileName) {
7298
8117
  const extension = path3.extname(fileName).slice(1).toLowerCase();
7299
8118
  return MEDIA_TYPE_BY_EXTENSION[extension] ?? DEFAULT_MEDIA_TYPE;
@@ -7395,7 +8214,7 @@ function readTimelockRevealDelaySeconds(env) {
7395
8214
 
7396
8215
  // src/verification-record.ts
7397
8216
  var VERIFICATION_RECORD_USAGE = "Usage: elgora-cli verification-record <bounty_id>";
7398
- async function main14(env = process.env, argv = process.argv.slice(2)) {
8217
+ async function main15(env = process.env, argv = process.argv.slice(2)) {
7399
8218
  const { apiBaseUrl: apiBaseUrlOverride, argv: remainingArgv } = extractApiBaseUrlFlag(argv);
7400
8219
  const args = readVerificationRecordArgs(remainingArgv);
7401
8220
  const envWithDefaults = withDeploymentDefaults(env);
@@ -7471,20 +8290,21 @@ process.stderr.write(
7471
8290
  `
7472
8291
  );
7473
8292
  var COMMANDS = {
7474
- "guardian:judgeable": main4,
7475
- "guardian:open": main2,
7476
- "guardian:prove-roster-key": main5,
7477
- "guardian:verdict": main6,
7478
- "guardian:deliver-key": main3,
7479
- "poster:open-winning-submission": main7,
7480
- "poster:publish-fund": main8,
7481
- "roster:add-guardian": main9,
7482
- "roster:prepare-guardian-proof": main10,
7483
- "roster:verify-guardian-proof": main11,
7484
- "solver:submit": main13,
7485
- claim: main,
7486
- "spec-commitment": main12,
7487
- "verification-record": main14
8293
+ "guardian:judgeable": main5,
8294
+ "guardian:open": main3,
8295
+ "guardian:prove-roster-key": main6,
8296
+ "guardian:verdict": main7,
8297
+ "guardian:deliver-key": main4,
8298
+ "poster:open-winning-submission": main8,
8299
+ "poster:publish-fund": main9,
8300
+ "roster:add-guardian": main10,
8301
+ "roster:prepare-guardian-proof": main11,
8302
+ "roster:verify-guardian-proof": main12,
8303
+ "solver:submit": main14,
8304
+ claim: main2,
8305
+ "check-page": main,
8306
+ "spec-commitment": main13,
8307
+ "verification-record": main15
7488
8308
  };
7489
8309
  var COMMAND_HELP = {
7490
8310
  "guardian:judgeable": {
@@ -7556,7 +8376,7 @@ var COMMAND_HELP = {
7556
8376
  "poster:publish-fund": {
7557
8377
  usage: "elgora-cli poster:publish-fund <bounty_challenge_md_path|->",
7558
8378
  title: "Publish an approved challenge and fund its bounty.",
7559
- description: "Read the exact approved bounty_challenge.md bytes, publish them through\n Elgora's API, approve the escrow token, and create the bounty on\n ElgoraHub. Pass '-' to read the challenge from stdin.",
8379
+ description: "Read the exact approved bounty_challenge.md bytes and publish them through\n Elgora's API. Eligible wallets sign a USDC authorization, then send\n one funding transaction and pay gas. No new allowance is required;\n this authorization path does not revoke existing allowances.\n Unsupported accounts use approval then creation. Pass '-' to read stdin.",
7560
8380
  required: ["ELGORA_POSTER_PRIVATE_KEY"],
7561
8381
  defaulted: [
7562
8382
  "ELGORA_CHAIN_ID",
@@ -7621,6 +8441,12 @@ var COMMAND_HELP = {
7621
8441
  required: [],
7622
8442
  defaulted: ["ELGORA_CHAIN_ID", "ELGORA_HUB_ADDRESS", "ELGORA_RPC_URL"]
7623
8443
  },
8444
+ "check-page": {
8445
+ usage: "elgora-cli check-page <bounty_challenge_md_path|->",
8446
+ title: "Check a challenge page's structure against Elgora's rules.",
8447
+ description: "Run the same structural module the API runs before its readiness\n review: frontmatter, a known profile, and every declared tightening.\n Reports each issue with a stable code and the field that fixes it,\n and exits non-zero when any is found. A Guardian runs this against a\n bounty's committed bytes before judging, because a page can reach\n chain without passing publication. It checks structure only: whether\n the page tries to instruct you, or carries unsafe content, is a\n reading you make yourself. Pass '-' to read from stdin. Makes no\n network call and needs no configuration or secret.",
8448
+ required: []
8449
+ },
7624
8450
  "spec-commitment": {
7625
8451
  usage: "elgora-cli spec-commitment <bounty_challenge_md_path|->",
7626
8452
  title: "Reproduce a challenge's spec_commitment offline.",
@@ -7648,6 +8474,11 @@ async function run(rawArgv) {
7648
8474
  printUsage();
7649
8475
  return;
7650
8476
  }
8477
+ if (subcommand === "--version" || subcommand === "-v" || subcommand === "version") {
8478
+ process.stdout.write(`${CLI_VERSION}
8479
+ `);
8480
+ return;
8481
+ }
7651
8482
  if (subcommand === "help") {
7652
8483
  printCommandHelpOrUsage(rest[0]);
7653
8484
  return;
@@ -7664,6 +8495,13 @@ async function run(rawArgv) {
7664
8495
  printCommandHelpOrUsage(subcommand);
7665
8496
  return;
7666
8497
  }
8498
+ const nodeProblem = unsupportedNodeMessage();
8499
+ if (nodeProblem) {
8500
+ process.stderr.write(`${nodeProblem}
8501
+ `);
8502
+ process.exitCode = 1;
8503
+ return;
8504
+ }
7667
8505
  await command(env, rest);
7668
8506
  }
7669
8507
  function printUsage() {
@@ -7680,7 +8518,8 @@ function printUsage() {
7680
8518
  `Every command accepts ${NETWORK_FLAG} <name|chain id> to select the`,
7681
8519
  `deployment for that invocation. Supported: ${NETWORK_NAMES_HINT}.`,
7682
8520
  "",
7683
- "Run `elgora-cli help <command>` for exact arguments and configuration."
8521
+ "Run `elgora-cli help <command>` for exact arguments and configuration.",
8522
+ "Run `elgora-cli --version` to print this release's version."
7684
8523
  ].join("\n")}
7685
8524
  `
7686
8525
  );
@@ -7733,7 +8572,7 @@ run(process.argv.slice(2)).catch((error) => {
7733
8572
  function formatCliError(error) {
7734
8573
  if (error instanceof ZodError2) {
7735
8574
  return renderError(
7736
- "invalid environment configuration",
8575
+ "invalid input",
7737
8576
  error.issues.map((issue) => ({
7738
8577
  path: issue.path.join("."),
7739
8578
  message: issue.message