@elgora/cli 7.4.1 → 7.5.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.
- package/dist/cli.js +74 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -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.0" : "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,14 @@ 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 can't contain spaces or punctuation the
|
|
7480
|
+
// wire format rejects — no display-only rendering happens in between.
|
|
7481
|
+
`name must match ${KEY_ID_RE.source} (it becomes this Guardian's private-artifact key_id)`
|
|
7482
|
+
);
|
|
7483
|
+
}
|
|
7462
7484
|
if (!guardianAccount || !isAddress4(guardianAccount)) {
|
|
7463
7485
|
throw new Error("guardian_account must be a valid EVM address");
|
|
7464
7486
|
}
|
|
@@ -7992,6 +8014,23 @@ function requirePinnedGuardianRosterHash(hubGuardianRosterHash, apiGuardianRoste
|
|
|
7992
8014
|
);
|
|
7993
8015
|
}
|
|
7994
8016
|
}
|
|
8017
|
+
function requireUsableGuardianNames(roster) {
|
|
8018
|
+
const seen = /* @__PURE__ */ new Map();
|
|
8019
|
+
for (const guardian of roster) {
|
|
8020
|
+
if (!KEY_ID_RE.test(guardian.name)) {
|
|
8021
|
+
throw new Error(
|
|
8022
|
+
`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.`
|
|
8023
|
+
);
|
|
8024
|
+
}
|
|
8025
|
+
const collidingAccount = seen.get(guardian.name);
|
|
8026
|
+
if (collidingAccount) {
|
|
8027
|
+
throw new Error(
|
|
8028
|
+
`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.`
|
|
8029
|
+
);
|
|
8030
|
+
}
|
|
8031
|
+
seen.set(guardian.name, guardian.account);
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
7995
8034
|
function requireMatchingSubmitTransaction(actual, expected) {
|
|
7996
8035
|
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
8036
|
throw new Error(
|
|
@@ -8013,9 +8052,10 @@ function requireOpenSubmittableBounty(bountyFacts, bountyId) {
|
|
|
8013
8052
|
async function buildSolverSubmissionEnvelope(input) {
|
|
8014
8053
|
const solver = input.solver.toLowerCase();
|
|
8015
8054
|
const bountyId = input.bountyId.toString();
|
|
8055
|
+
requireUsableGuardianNames(input.guardianRoster);
|
|
8016
8056
|
const guardianRecipients = input.guardianRoster.map(
|
|
8017
8057
|
(guardian) => ({
|
|
8018
|
-
key_id: guardian.
|
|
8058
|
+
key_id: guardian.name,
|
|
8019
8059
|
suite: PRIVATE_ARTIFACT_HPKE_SUITE,
|
|
8020
8060
|
public_key: guardian.encryptionPublicKey
|
|
8021
8061
|
})
|
|
@@ -8280,15 +8320,18 @@ function usageError4() {
|
|
|
8280
8320
|
}
|
|
8281
8321
|
|
|
8282
8322
|
// 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"})
|
|
8323
|
+
if (process.env.DOTENV_CONFIG_PATH) {
|
|
8324
|
+
const dotenvPath = path4.resolve(process.env.DOTENV_CONFIG_PATH);
|
|
8325
|
+
const dotenvResult = loadDotenv({ path: dotenvPath, quiet: true });
|
|
8326
|
+
process.stderr.write(
|
|
8327
|
+
`Env: ${dotenvPath} (${dotenvResult.error ? `not found: ${dotenvResult.error.message}` : "loaded"})
|
|
8290
8328
|
`
|
|
8291
|
-
);
|
|
8329
|
+
);
|
|
8330
|
+
} else {
|
|
8331
|
+
process.stderr.write(
|
|
8332
|
+
"Env: DOTENV_CONFIG_PATH not set; reading process.env only\n"
|
|
8333
|
+
);
|
|
8334
|
+
}
|
|
8292
8335
|
var COMMANDS = {
|
|
8293
8336
|
"guardian:judgeable": main5,
|
|
8294
8337
|
"guardian:open": main3,
|
|
@@ -8403,7 +8446,7 @@ var COMMAND_HELP = {
|
|
|
8403
8446
|
"roster:add-guardian": {
|
|
8404
8447
|
usage: "elgora-cli roster:add-guardian [--roster-admin-address <address>]\n <name> <guardian_account> <encryption_public_key>",
|
|
8405
8448
|
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.",
|
|
8449
|
+
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
8450
|
required: [],
|
|
8408
8451
|
defaulted: ["ELGORA_CHAIN_ID", "ELGORA_HUB_ADDRESS", "ELGORA_RPC_URL"]
|
|
8409
8452
|
},
|
|
@@ -8502,6 +8545,25 @@ async function run(rawArgv) {
|
|
|
8502
8545
|
process.exitCode = 1;
|
|
8503
8546
|
return;
|
|
8504
8547
|
}
|
|
8548
|
+
const missing = missingRequiredEnv(
|
|
8549
|
+
COMMAND_HELP[subcommand]?.required ?? [],
|
|
8550
|
+
env
|
|
8551
|
+
);
|
|
8552
|
+
if (missing.length > 0) {
|
|
8553
|
+
process.stderr.write(
|
|
8554
|
+
`${[
|
|
8555
|
+
`${subcommand} is missing required environment variable${missing.length > 1 ? "s" : ""}:`,
|
|
8556
|
+
...missing.map((envName) => ` ${envName}`),
|
|
8557
|
+
"",
|
|
8558
|
+
"This CLI reads only process.env (plus DOTENV_CONFIG_PATH if you set it)",
|
|
8559
|
+
"\u2014 it never assumes a secrets location. Provide these before invoking",
|
|
8560
|
+
`elgora-cli, or run \`elgora-cli help ${subcommand}\` for the full list.`
|
|
8561
|
+
].join("\n")}
|
|
8562
|
+
`
|
|
8563
|
+
);
|
|
8564
|
+
process.exitCode = 1;
|
|
8565
|
+
return;
|
|
8566
|
+
}
|
|
8505
8567
|
await command(env, rest);
|
|
8506
8568
|
}
|
|
8507
8569
|
function printUsage() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elgora/cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.0",
|
|
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",
|