@elgora/cli 7.4.1 → 7.5.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.
- package/dist/cli.js +76 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -532,7 +532,7 @@ var AES_GCM_TAG_BITS = 128;
|
|
|
532
532
|
var BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
533
533
|
var SOLVER_SUBMISSION_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
534
534
|
var MEDIA_TYPE_RE = /^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*$/;
|
|
535
|
-
var KEY_ID_RE = /^[A-Za-z0-9][
|
|
535
|
+
var KEY_ID_RE = /^[A-Za-z0-9][ -~]*$/;
|
|
536
536
|
var base64UrlSchema = z3.string().regex(BASE64URL_RE);
|
|
537
537
|
var lowerAddressSchema = z3.string().refine(isLowercaseHexAddress);
|
|
538
538
|
var bytes32Schema2 = z3.string().refine(isLowercaseBytes32);
|
|
@@ -1142,8 +1142,12 @@ async function openAesKeyForRecipient(input) {
|
|
|
1142
1142
|
}
|
|
1143
1143
|
}
|
|
1144
1144
|
if (!matched) {
|
|
1145
|
+
const wanted = [...new Set(wrappedKeys.map((key) => key.key_id))].sort();
|
|
1146
|
+
const configured = [
|
|
1147
|
+
...new Set(privateKeys.map((key) => key.key_id))
|
|
1148
|
+
].sort();
|
|
1145
1149
|
throw new Error(
|
|
1146
|
-
|
|
1150
|
+
`no matching Guardian private key for private artifact envelope: this envelope was sealed for key_id ${JSON.stringify(wanted)}, but ELGORA_GUARDIAN_PRIVATE_KEYS_JSON configures key_id ${JSON.stringify(configured)}. key_id must be this Guardian's own registered roster name (the name passed to roster:add-guardian), not its account address.`
|
|
1147
1151
|
);
|
|
1148
1152
|
}
|
|
1149
1153
|
throw new Error("matching Guardian private key could not open wrapped key", {
|
|
@@ -6903,7 +6907,7 @@ function resolveChainId(selector) {
|
|
|
6903
6907
|
}
|
|
6904
6908
|
|
|
6905
6909
|
// src/version.ts
|
|
6906
|
-
var CLI_VERSION = true ? "7.
|
|
6910
|
+
var CLI_VERSION = true ? "7.5.1" : "0.0.0-dev";
|
|
6907
6911
|
var CLI_NODE_ENGINES = true ? ">=24 <25" : ">=24 <25";
|
|
6908
6912
|
|
|
6909
6913
|
// src/node-engines.ts
|
|
@@ -7340,6 +7344,11 @@ function loadPublishFundConfig(env, apiBaseUrlOverride) {
|
|
|
7340
7344
|
};
|
|
7341
7345
|
}
|
|
7342
7346
|
|
|
7347
|
+
// src/required-env.ts
|
|
7348
|
+
function missingRequiredEnv(required4, env) {
|
|
7349
|
+
return required4.filter((envName) => !env[envName]?.trim());
|
|
7350
|
+
}
|
|
7351
|
+
|
|
7343
7352
|
// src/roster-add-guardian.ts
|
|
7344
7353
|
import { isAddress as isAddress4 } from "viem";
|
|
7345
7354
|
import { privateKeyToAccount as privateKeyToAccount9 } from "viem/accounts";
|
|
@@ -7376,6 +7385,11 @@ async function main10(env = process.env, argv = process.argv.slice(2)) {
|
|
|
7376
7385
|
`${args.guardianAccount} is already on the Guardian roster`
|
|
7377
7386
|
);
|
|
7378
7387
|
}
|
|
7388
|
+
if (roster.some((guardian) => guardian.name === args.name)) {
|
|
7389
|
+
throw new Error(
|
|
7390
|
+
`"${args.name}" is already the name of a Guardian on the roster; names must be unique`
|
|
7391
|
+
);
|
|
7392
|
+
}
|
|
7379
7393
|
const contractArgs = [
|
|
7380
7394
|
args.name,
|
|
7381
7395
|
args.guardianAccount,
|
|
@@ -7459,6 +7473,15 @@ function parseRosterAddGuardianArgs(argv) {
|
|
|
7459
7473
|
const guardianAccount = positional[1]?.trim();
|
|
7460
7474
|
const encryptionPublicKey = positional[2]?.trim();
|
|
7461
7475
|
if (!name) throw usageError2();
|
|
7476
|
+
if (!KEY_ID_RE.test(name)) {
|
|
7477
|
+
throw new Error(
|
|
7478
|
+
// A Solver's CLI seals private artifacts against this exact string as
|
|
7479
|
+
// the Guardian's key_id, so it must be non-empty, start with an
|
|
7480
|
+
// alphanumeric, and stay within printable ASCII (no control/formatting
|
|
7481
|
+
// characters, no leading whitespace).
|
|
7482
|
+
`name must match ${KEY_ID_RE.source} (it becomes this Guardian's private-artifact key_id)`
|
|
7483
|
+
);
|
|
7484
|
+
}
|
|
7462
7485
|
if (!guardianAccount || !isAddress4(guardianAccount)) {
|
|
7463
7486
|
throw new Error("guardian_account must be a valid EVM address");
|
|
7464
7487
|
}
|
|
@@ -7992,6 +8015,23 @@ function requirePinnedGuardianRosterHash(hubGuardianRosterHash, apiGuardianRoste
|
|
|
7992
8015
|
);
|
|
7993
8016
|
}
|
|
7994
8017
|
}
|
|
8018
|
+
function requireUsableGuardianNames(roster) {
|
|
8019
|
+
const seen = /* @__PURE__ */ new Map();
|
|
8020
|
+
for (const guardian of roster) {
|
|
8021
|
+
if (!KEY_ID_RE.test(guardian.name)) {
|
|
8022
|
+
throw new Error(
|
|
8023
|
+
`Guardian ${guardian.account} has roster name ${JSON.stringify(guardian.name)}, which cannot be used as a private-artifact key_id (must match ${KEY_ID_RE.source}). This roster cannot receive private submissions until its admin corrects the name.`
|
|
8024
|
+
);
|
|
8025
|
+
}
|
|
8026
|
+
const collidingAccount = seen.get(guardian.name);
|
|
8027
|
+
if (collidingAccount) {
|
|
8028
|
+
throw new Error(
|
|
8029
|
+
`Guardians ${collidingAccount} and ${guardian.account} share the roster name ${JSON.stringify(guardian.name)}. Names must be unique on the pinned roster so each Guardian's key_id resolves to exactly one recipient.`
|
|
8030
|
+
);
|
|
8031
|
+
}
|
|
8032
|
+
seen.set(guardian.name, guardian.account);
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
7995
8035
|
function requireMatchingSubmitTransaction(actual, expected) {
|
|
7996
8036
|
if (actual.chain_id !== expected.chain_id || actual.to.toLowerCase() !== expected.to.toLowerCase() || actual.value !== expected.value || actual.data.toLowerCase() !== expected.data.toLowerCase() || actual.function_name !== expected.function_name || actual.args.bounty_id !== expected.args.bounty_id || actual.args.submission_commitment.toLowerCase() !== expected.args.submission_commitment.toLowerCase() || actual.args.guardian_roster_hash.toLowerCase() !== expected.args.guardian_roster_hash.toLowerCase() || actual.args.extra_data !== expected.args.extra_data) {
|
|
7997
8037
|
throw new Error(
|
|
@@ -8013,9 +8053,10 @@ function requireOpenSubmittableBounty(bountyFacts, bountyId) {
|
|
|
8013
8053
|
async function buildSolverSubmissionEnvelope(input) {
|
|
8014
8054
|
const solver = input.solver.toLowerCase();
|
|
8015
8055
|
const bountyId = input.bountyId.toString();
|
|
8056
|
+
requireUsableGuardianNames(input.guardianRoster);
|
|
8016
8057
|
const guardianRecipients = input.guardianRoster.map(
|
|
8017
8058
|
(guardian) => ({
|
|
8018
|
-
key_id: guardian.
|
|
8059
|
+
key_id: guardian.name,
|
|
8019
8060
|
suite: PRIVATE_ARTIFACT_HPKE_SUITE,
|
|
8020
8061
|
public_key: guardian.encryptionPublicKey
|
|
8021
8062
|
})
|
|
@@ -8280,15 +8321,18 @@ function usageError4() {
|
|
|
8280
8321
|
}
|
|
8281
8322
|
|
|
8282
8323
|
// src/cli.ts
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
quiet: true
|
|
8286
|
-
|
|
8287
|
-
})
|
|
8288
|
-
process.stderr.write(
|
|
8289
|
-
`Env: ${dotenvPath} (${dotenvResult.error ? "not found, using process env only" : "loaded"})
|
|
8324
|
+
if (process.env.DOTENV_CONFIG_PATH) {
|
|
8325
|
+
const dotenvPath = path4.resolve(process.env.DOTENV_CONFIG_PATH);
|
|
8326
|
+
const dotenvResult = loadDotenv({ path: dotenvPath, quiet: true });
|
|
8327
|
+
process.stderr.write(
|
|
8328
|
+
`Env: ${dotenvPath} (${dotenvResult.error ? `not found: ${dotenvResult.error.message}` : "loaded"})
|
|
8290
8329
|
`
|
|
8291
|
-
);
|
|
8330
|
+
);
|
|
8331
|
+
} else {
|
|
8332
|
+
process.stderr.write(
|
|
8333
|
+
"Env: DOTENV_CONFIG_PATH not set; reading process.env only\n"
|
|
8334
|
+
);
|
|
8335
|
+
}
|
|
8292
8336
|
var COMMANDS = {
|
|
8293
8337
|
"guardian:judgeable": main5,
|
|
8294
8338
|
"guardian:open": main3,
|
|
@@ -8403,7 +8447,7 @@ var COMMAND_HELP = {
|
|
|
8403
8447
|
"roster:add-guardian": {
|
|
8404
8448
|
usage: "elgora-cli roster:add-guardian [--roster-admin-address <address>]\n <name> <guardian_account> <encryption_public_key>",
|
|
8405
8449
|
title: "Append one Guardian to the live roster.",
|
|
8406
|
-
description: "Call ElgoraHub's guardianRosterAdmin-only addGuardian directly. Only\n future bounties see the new roster; open bounties keep judging\n against the hash pinned when they were created. With\n --roster-admin-address, the transaction is simulated and printed\n unsigned (to, data, value) for an external signer such as a Safe,\n instead of being sent. Run roster:prepare-guardian-proof,\n guardian:prove-roster-key, and roster:verify-guardian-proof against\n the proposed Guardian before ever calling this.",
|
|
8450
|
+
description: "Call ElgoraHub's guardianRosterAdmin-only addGuardian directly. Only\n future bounties see the new roster; open bounties keep judging\n against the hash pinned when they were created. With\n --roster-admin-address, the transaction is simulated and printed\n unsigned (to, data, value) for an external signer such as a Safe,\n instead of being sent. Run roster:prepare-guardian-proof,\n guardian:prove-roster-key, and roster:verify-guardian-proof against\n the proposed Guardian before ever calling this. name becomes this\n Guardian's key_id for every private submission a Solver seals\n afterward \u2014 it must be unique on the roster and is what this\n Guardian's own ELGORA_GUARDIAN_PRIVATE_KEYS_JSON entries must use as\n their key_id, not the account address.",
|
|
8407
8451
|
required: [],
|
|
8408
8452
|
defaulted: ["ELGORA_CHAIN_ID", "ELGORA_HUB_ADDRESS", "ELGORA_RPC_URL"]
|
|
8409
8453
|
},
|
|
@@ -8502,6 +8546,25 @@ async function run(rawArgv) {
|
|
|
8502
8546
|
process.exitCode = 1;
|
|
8503
8547
|
return;
|
|
8504
8548
|
}
|
|
8549
|
+
const missing = missingRequiredEnv(
|
|
8550
|
+
COMMAND_HELP[subcommand]?.required ?? [],
|
|
8551
|
+
env
|
|
8552
|
+
);
|
|
8553
|
+
if (missing.length > 0) {
|
|
8554
|
+
process.stderr.write(
|
|
8555
|
+
`${[
|
|
8556
|
+
`${subcommand} is missing required environment variable${missing.length > 1 ? "s" : ""}:`,
|
|
8557
|
+
...missing.map((envName) => ` ${envName}`),
|
|
8558
|
+
"",
|
|
8559
|
+
"This CLI reads only process.env (plus DOTENV_CONFIG_PATH if you set it)",
|
|
8560
|
+
"\u2014 it never assumes a secrets location. Provide these before invoking",
|
|
8561
|
+
`elgora-cli, or run \`elgora-cli help ${subcommand}\` for the full list.`
|
|
8562
|
+
].join("\n")}
|
|
8563
|
+
`
|
|
8564
|
+
);
|
|
8565
|
+
process.exitCode = 1;
|
|
8566
|
+
return;
|
|
8567
|
+
}
|
|
8505
8568
|
await command(env, rest);
|
|
8506
8569
|
}
|
|
8507
8570
|
function printUsage() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elgora/cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.1",
|
|
4
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|