@parallel-protocol/cli 0.2.7 → 0.2.9

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 +2 -2
  2. package/dist/index.js +109 -58
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -192,7 +192,7 @@ parallel swap redeem --collateral <symbol> --amount <n> --chain <chain> --wa
192
192
  parallel swap redeem-all --amount <n> --chain <chain> --wallet # proportional redeem
193
193
  ```
194
194
 
195
- > Command names match the MCP tools since 0.2.7: `redeem` targets ONE
195
+ > Command names match the MCP tools: `redeem` targets ONE
196
196
  > collateral (formerly `burn`), `redeem-all` redeems proportionally across the
197
197
  > whole basket (formerly `redeem`). The old `swap burn` keeps working as a
198
198
  > hidden legacy command.
@@ -438,7 +438,7 @@ Not all chains support every feature. Use `parallel protocol chains` to see the
438
438
  | `RPC_ERROR` | An HTTP provider (facilitator, history APIs) is unreachable |
439
439
  | `UNKNOWN` | Any other failure |
440
440
 
441
- Protocol-layer errors (caps, bridge limits, sunset chains, partial reads…) are surfaced with their own code in the `code` field since 0.2.7 — e.g. `EXCEEDS_CAP`, `EXCEEDS_BRIDGE_LIMIT`, `EXCEEDS_GLOBAL_LIMIT`, `CHAIN_SUNSET`, `ROUTE_UNAVAILABLE`, `PARTIAL_READ`, `PARALLELIZER_NOT_DEPLOYED`, `COLLATERAL_NOT_SUPPORTED`, `INSUFFICIENT_BALANCE`. Match on `error.code`; the message carries the human detail (remaining capacity, valid values…) without the code prefix.
441
+ Protocol-layer errors (caps, bridge limits, sunset chains, partial reads…) are surfaced with their own code in the `code` field — e.g. `EXCEEDS_CAP`, `EXCEEDS_BRIDGE_LIMIT`, `EXCEEDS_GLOBAL_LIMIT`, `CHAIN_SUNSET`, `ROUTE_UNAVAILABLE`, `PARTIAL_READ`, `PARALLELIZER_NOT_DEPLOYED`, `COLLATERAL_NOT_SUPPORTED`, `INSUFFICIENT_BALANCE`. Match on `error.code`; the message carries the human detail (remaining capacity, valid values…) without the code prefix.
442
442
 
443
443
  ---
444
444
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { AsyncLocalStorage } from 'async_hooks';
3
- import { Command, Option, CommanderError } from 'commander';
3
+ import { Command, CommanderError } from 'commander';
4
4
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
5
5
  import { join, dirname } from 'path';
6
6
  import { fileURLToPath } from 'url';
@@ -1635,7 +1635,7 @@ function suppressLogs() {
1635
1635
 
1636
1636
  // package.json
1637
1637
  var package_default = {
1638
- version: "0.2.7"};
1638
+ version: "0.2.9"};
1639
1639
  function findEnvFile(dir) {
1640
1640
  const candidate = join(dir, ".env");
1641
1641
  if (existsSync(candidate)) return candidate;
@@ -5722,7 +5722,7 @@ function toTxData(tx) {
5722
5722
  function quoteCmd() {
5723
5723
  return new Command("quote").description("Get a LayerZero bridge fee quote").option(
5724
5724
  "--token <token>",
5725
- "token to bridge: usdp|susdp|prl (default: usdp)",
5725
+ "token to bridge: usdp (the only token currently bridgeable)",
5726
5726
  "usdp"
5727
5727
  ).option("--from <chain>", "source chain").option("--to <chain>", "destination chain").option("--amount <n>", "amount to bridge").action(async function() {
5728
5728
  const opts = resolveOpts(this);
@@ -5772,7 +5772,11 @@ function quoteCmd() {
5772
5772
  });
5773
5773
  }
5774
5774
  function feesCmd() {
5775
- return new Command("fees").description("Show LayerZero bridge fees to all destinations from a chain").option("--token <token>", "token: usdp|susdp|prl (default: usdp)", "usdp").option("--from <chain>", "source chain").action(async function() {
5775
+ return new Command("fees").description("Show LayerZero bridge fees to all destinations from a chain").option(
5776
+ "--token <token>",
5777
+ "token: usdp (the only token currently bridgeable)",
5778
+ "usdp"
5779
+ ).option("--from <chain>", "source chain").action(async function() {
5776
5780
  const opts = resolveOpts(this);
5777
5781
  try {
5778
5782
  if (!opts.from)
@@ -5805,7 +5809,7 @@ function feesCmd() {
5805
5809
  function sendCmd() {
5806
5810
  return new Command("send").description("Bridge tokens cross-chain via LayerZero OFT").option(
5807
5811
  "--token <token>",
5808
- "token to bridge: usdp|susdp|prl (default: usdp)",
5812
+ "token to bridge: usdp (the only token currently bridgeable)",
5809
5813
  "usdp"
5810
5814
  ).option("--from <chain>", "source chain").option("--to <chain>", "destination chain").option("--amount <n>", "amount to bridge").option("--recipient <addr>", "override destination recipient address").option("-w, --wallet [path|env]", "keystore path or 'env'").option("--address <addr>", "sender address (for dry-run without --wallet)").option("--dry-run", "build tx without broadcasting").action(async function() {
5811
5815
  const opts = resolveOpts(this);
@@ -7153,7 +7157,11 @@ var TTL = {
7153
7157
  SUPPLY: 6e4,
7154
7158
  SAVINGS_RATE: 3e4,
7155
7159
  EXCHANGE_RATE: 3e4,
7156
- STAKING: 6e4};
7160
+ STAKING: 6e4,
7161
+ // Forum topics do not move once created — a long TTL also makes the
7162
+ // Discourse search deterministic across consecutive calls.
7163
+ FORUM_URL: 6e5
7164
+ };
7157
7165
  var lru = new I({
7158
7166
  max: 500,
7159
7167
  allowStale: false
@@ -7689,29 +7697,37 @@ async function getFeeRates(chain, collateral) {
7689
7697
  );
7690
7698
  }
7691
7699
 
7700
+ // ../shared/src/infra/http.ts
7701
+ function fetchWithTimeout(url, init = {}, timeoutMs = 1e4) {
7702
+ return fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
7703
+ }
7704
+
7692
7705
  // ../mcp-server/src/services/governance/proposals.ts
7693
7706
  var SNAPSHOT_HUB = "https://hub.snapshot.org/graphql";
7694
7707
  var SNAPSHOT_SPACE = "parallel-protocol.eth";
7695
7708
  var DISCOURSE_BASE = "https://gov.parallel.best";
7696
- async function fetchForumUrl(friendlyId) {
7697
- try {
7698
- const res = await fetch(
7699
- `${DISCOURSE_BASE}/search.json?q=${encodeURIComponent(friendlyId)}`
7700
- );
7701
- if (!res.ok) return null;
7702
- const data = await res.json();
7703
- const topic = data.topics?.[0];
7704
- if (!topic) return null;
7705
- return `${DISCOURSE_BASE}/t/${topic.slug}/${topic.id}`;
7706
- } catch {
7707
- return null;
7708
- }
7709
+ function fetchForumUrl(friendlyId) {
7710
+ return getOrSet(`gov:forum:${friendlyId}`, TTL.FORUM_URL, async () => {
7711
+ try {
7712
+ const res = await fetchWithTimeout(
7713
+ `${DISCOURSE_BASE}/search.json?q=${encodeURIComponent(friendlyId)}`,
7714
+ {},
7715
+ 3e3
7716
+ );
7717
+ if (!res.ok) return null;
7718
+ const data = await res.json();
7719
+ const topic = data.topics?.[0];
7720
+ if (!topic) return null;
7721
+ return `${DISCOURSE_BASE}/t/${topic.slug}/${topic.id}`;
7722
+ } catch {
7723
+ return null;
7724
+ }
7725
+ });
7709
7726
  }
7710
7727
  async function enrichWithForumUrls(proposals) {
7711
7728
  const urls = await Promise.all(
7712
7729
  proposals.map((p) => {
7713
- if (p.forumUrl?.startsWith(DISCOURSE_BASE))
7714
- return Promise.resolve(p.forumUrl);
7730
+ if (p.forumUrl) return Promise.resolve(p.forumUrl);
7715
7731
  if (p.id !== p.snapshotId) return fetchForumUrl(p.id);
7716
7732
  return Promise.resolve(null);
7717
7733
  })
@@ -7719,11 +7735,18 @@ async function enrichWithForumUrls(proposals) {
7719
7735
  return proposals.map((p, i) => ({ ...p, forumUrl: urls[i] ?? null }));
7720
7736
  }
7721
7737
  async function querySnapshot(query, variables) {
7722
- const res = await fetch(SNAPSHOT_HUB, {
7723
- method: "POST",
7724
- headers: { "Content-Type": "application/json", Accept: "application/json" },
7725
- body: JSON.stringify({ query, variables })
7726
- });
7738
+ const res = await fetchWithTimeout(
7739
+ SNAPSHOT_HUB,
7740
+ {
7741
+ method: "POST",
7742
+ headers: {
7743
+ "Content-Type": "application/json",
7744
+ Accept: "application/json"
7745
+ },
7746
+ body: JSON.stringify({ query, variables })
7747
+ },
7748
+ 1e4
7749
+ );
7727
7750
  if (!res.ok)
7728
7751
  throw new Error(`Snapshot API error: ${res.status} ${res.statusText}`);
7729
7752
  const json = await res.json();
@@ -7743,14 +7766,23 @@ function inferFriendlyId(title) {
7743
7766
  const match = title.match(/\b(PIP|PGP|PIR)-(\d+(?:\.\d+)?)/i);
7744
7767
  return match ? `${match[1].toUpperCase()}-${match[2]}` : null;
7745
7768
  }
7746
- function mapDetailedStatus(state, scoresTotal, quorum) {
7747
- if (state === "active") return "active";
7748
- if (state === "pending") return "pending";
7749
- if (state === "closed") {
7750
- if (scoresTotal < quorum) return "rejected";
7769
+ function isForAgainstVote(choices) {
7770
+ if (!choices || choices.length < 2) return false;
7771
+ const second = choices[1].toLowerCase();
7772
+ return second.startsWith("against") || second.startsWith("no");
7773
+ }
7774
+ function mapDetailedStatus(p) {
7775
+ if (p.state === "active") return "active";
7776
+ if (p.state === "pending") return "pending";
7777
+ if (p.state === "closed") {
7778
+ if (p.quorum > 0 && p.scores_total < p.quorum) return "rejected";
7779
+ if (isForAgainstVote(p.choices)) {
7780
+ const scores = p.scores ?? [];
7781
+ return (scores[0] ?? 0) > (scores[1] ?? 0) ? "passed" : "rejected";
7782
+ }
7751
7783
  return "passed";
7752
7784
  }
7753
- return state;
7785
+ return p.state;
7754
7786
  }
7755
7787
  function summarizeProposal(p) {
7756
7788
  const scores = p.scores ?? [];
@@ -7763,11 +7795,11 @@ function summarizeProposal(p) {
7763
7795
  title: p.title,
7764
7796
  author: p.author,
7765
7797
  type: inferProposalType(p.title),
7766
- status: mapDetailedStatus(p.state, p.scores_total, p.quorum),
7798
+ status: mapDetailedStatus(p),
7767
7799
  createdAt: new Date(p.created * 1e3).toISOString(),
7768
7800
  votingEndsAt: new Date(p.end * 1e3).toISOString(),
7769
7801
  snapshotUrl: `https://snapshot.org/#/${SNAPSHOT_SPACE}/proposal/${p.id}`,
7770
- forumUrl: p.link ?? null,
7802
+ forumUrl: p.discussion?.trim() ? p.discussion : null,
7771
7803
  summary: (p.body ?? "").slice(0, 200) + ((p.body?.length ?? 0) > 200 ? "\u2026" : ""),
7772
7804
  forVotes,
7773
7805
  againstVotes,
@@ -7795,7 +7827,7 @@ var PROPOSALS_QUERY = `
7795
7827
  scores
7796
7828
  choices
7797
7829
  quorum
7798
- link
7830
+ discussion
7799
7831
  }
7800
7832
  }
7801
7833
  `;
@@ -7838,7 +7870,7 @@ var PROPOSAL_DETAILS_QUERY = `
7838
7870
  scores
7839
7871
  choices
7840
7872
  quorum
7841
- link
7873
+ discussion
7842
7874
  }
7843
7875
  }
7844
7876
  `;
@@ -7846,23 +7878,28 @@ async function buildProposalDetails(p) {
7846
7878
  const [enriched] = await enrichWithForumUrls([summarizeProposal(p)]);
7847
7879
  const summary = enriched;
7848
7880
  const scores = p.scores ?? [];
7881
+ const hasQuorum = p.quorum > 0;
7882
+ const quorumReached = hasQuorum ? p.scores_total >= p.quorum : null;
7849
7883
  return {
7850
7884
  ...summary,
7851
7885
  description: p.body ?? "",
7852
7886
  choices: p.choices ?? [],
7853
7887
  scores,
7854
7888
  quorum: p.quorum,
7855
- quorumReached: p.scores_total >= p.quorum,
7889
+ quorumReached,
7856
7890
  voteResults: {
7857
7891
  for: scores[0] ?? 0,
7858
7892
  against: scores[1] ?? 0,
7859
7893
  abstain: scores[2] ?? 0,
7860
7894
  quorum: p.quorum,
7861
- quorumReached: p.scores_total >= p.quorum
7895
+ quorumReached
7862
7896
  },
7863
7897
  parametersChanged: null,
7864
7898
  executionTx: null,
7865
7899
  unavailableFields: {
7900
+ ...hasQuorum ? {} : {
7901
+ quorumReached: "No quorum is configured for this space on Snapshot (proposal quorum is 0) \u2014 pass/fail is derived from the For/Against split alone."
7902
+ },
7866
7903
  parametersChanged: "Snapshot does not expose structured on-chain parameter changes \u2014 this information is in the proposal body text.",
7867
7904
  executionTx: "Snapshot does not record on-chain execution transactions \u2014 check the DAO's AccessManager or TimelockController on Etherscan."
7868
7905
  }
@@ -8161,7 +8198,7 @@ function proposalsCmd() {
8161
8198
  `--type must be PIP, PGP or PIR, got "${opts.type}"`
8162
8199
  );
8163
8200
  }
8164
- const STATUSES = ["active", "passed", "rejected", "executed"];
8201
+ const STATUSES = ["active", "pending", "passed", "rejected"];
8165
8202
  if (opts.status && !STATUSES.includes(opts.status)) {
8166
8203
  throw new CliError(
8167
8204
  ErrorCode.INVALID_OPTION,
@@ -8208,7 +8245,11 @@ function proposalCmd() {
8208
8245
  ["Created", fmtDate(result.createdAt)],
8209
8246
  ["Voting Ends", fmtDate(result.votingEndsAt)],
8210
8247
  ["Quorum", fmtVotes(result.quorum)],
8211
- ["Quorum Reached", result.quorumReached ? "Yes \u2713" : "No \u2717"],
8248
+ [
8249
+ "Quorum Reached",
8250
+ // null = no quorum configured — "No ✗" would be misleading.
8251
+ result.quorumReached === null ? "\u2014" : result.quorumReached ? "Yes \u2713" : "No \u2717"
8252
+ ],
8212
8253
  ["Snapshot URL", result.snapshotUrl],
8213
8254
  ["Forum URL", result.forumUrl ?? "\u2014"]
8214
8255
  ]);
@@ -8383,9 +8424,9 @@ async function getPaymentCapabilities(chain, {
8383
8424
  const hasUSDp = !!getUSDpAddress(chain);
8384
8425
  const hasSUSDp = !!CHAINS[chain].contracts.susdp;
8385
8426
  const hasUSDC = !!getUSDCAddress(chain);
8386
- const sunset = CHAINS[chain].status === "sunset";
8387
- const parallelizerActive = hasParallelizer && !sunset;
8388
- const susdpVaultActive = hasSUSDp && !sunset;
8427
+ const chainActive = CHAINS[chain].status === "active";
8428
+ const parallelizerActive = hasParallelizer && chainActive;
8429
+ const susdpVaultActive = hasSUSDp && chainActive;
8389
8430
  const facilitatorSupported = parallelizerActive;
8390
8431
  const gasSponsored = parallelizerActive && chain !== "ethereum";
8391
8432
  let hasUsdcCollateral = false;
@@ -8409,7 +8450,7 @@ async function getPaymentCapabilities(chain, {
8409
8450
  }
8410
8451
  }
8411
8452
  const capabilities = [];
8412
- if (hasUSDp) {
8453
+ if (hasUSDp && facilitatorSupported) {
8413
8454
  capabilities.push({
8414
8455
  route: "A",
8415
8456
  method: "transferWithAuthorization",
@@ -8449,7 +8490,7 @@ async function getPaymentCapabilities(chain, {
8449
8490
  gasSponsored
8450
8491
  });
8451
8492
  }
8452
- if (hasUSDp && susdpVaultActive) {
8493
+ if (hasUSDp && susdpVaultActive && facilitatorSupported) {
8453
8494
  capabilities.push({
8454
8495
  route: "C",
8455
8496
  method: "depositWithAuthorization",
@@ -8469,7 +8510,7 @@ async function getPaymentCapabilities(chain, {
8469
8510
  gasSponsored
8470
8511
  });
8471
8512
  }
8472
- if (hasUSDC) {
8513
+ if (hasUSDC && facilitatorSupported) {
8473
8514
  capabilities.push({
8474
8515
  route: "F",
8475
8516
  method: "transferWithAuthorization",
@@ -11143,7 +11184,7 @@ async function renderSupplyHistory(token, opts) {
11143
11184
  try {
11144
11185
  if (token.toLowerCase() !== "usdp")
11145
11186
  throw new CliError(
11146
- ErrorCode.UNKNOWN,
11187
+ ErrorCode.INVALID_OPTION,
11147
11188
  `supply --history is only available for usdp (got "${token}")`,
11148
11189
  "Only USDp has a public historical supply source"
11149
11190
  );
@@ -12761,8 +12802,8 @@ async function getStakingOverview() {
12761
12802
  aprNote: "Base APR estimated from previous epoch rewards and token prices. Actual APR varies per user based on ParaBoost score. Same formula as app."
12762
12803
  } : {
12763
12804
  unavailableFields: {
12764
- sprl1APR: "APR unavailable \u2014 STAKE_API_URL not configured.",
12765
- sprl2APR: "APR unavailable \u2014 STAKE_API_URL not configured."
12805
+ sprl1APR: "APR unavailable \u2014 the staking API returned no validated epoch data (unreachable, unauthorized, or no epoch settled yet).",
12806
+ sprl2APR: "APR unavailable \u2014 the staking API returned no validated epoch data (unreachable, unauthorized, or no epoch settled yet)."
12766
12807
  }
12767
12808
  }
12768
12809
  };
@@ -12956,17 +12997,17 @@ async function getStakingInfo(userAddress) {
12956
12997
  estimatedAPR: userAPR,
12957
12998
  unavailableFields: {
12958
12999
  ...pendingRewards.sprl1Pending === null && {
12959
- "sprl1.pendingRewards": "sPRL1 rewards are distributed via an off-chain Merkle distributor \u2014 configure STAKE_API_URL and GOLDSKY_SUBGRAPH_URL to enable."
13000
+ "sprl1.pendingRewards": "sPRL1 rewards are distributed via an off-chain Merkle distributor \u2014 the staking API returned no data for this field."
12960
13001
  },
12961
13002
  ...pendingRewards.currentEpoch === null && {
12962
- "sprl1.cooldown.epoch": "Current epoch unavailable \u2014 configure STAKE_API_URL to enable."
13003
+ "sprl1.cooldown.epoch": "Current epoch unavailable \u2014 the staking API returned no epoch data."
12963
13004
  },
12964
13005
  "sprl2.auraRewards": "BAL and AURA rewards from Aura Finance are automatically forwarded to the DAO treasury, not distributed to individual stakers.",
12965
13006
  ...pendingRewards.sprl2Pending === null && {
12966
- "sprl2.protocolRewards": "Protocol rewards for sPRL2 are distributed via the same off-chain epoch-based Merkle distributor \u2014 configure STAKE_API_URL and GOLDSKY_SUBGRAPH_URL to enable."
13007
+ "sprl2.protocolRewards": "Protocol rewards for sPRL2 are distributed via the same off-chain epoch-based Merkle distributor \u2014 the staking API returned no data for this field."
12967
13008
  },
12968
13009
  ...pendingRewards.paraboost === null && {
12969
- "sprl2.paraboost": "Paraboost multiplier unavailable \u2014 configure STAKE_API_URL to enable. Base multiplier is 2.5x; paraboost mechanics increase it over time."
13010
+ "sprl2.paraboost": "Paraboost multiplier unavailable \u2014 the staking API returned no data. Base multiplier is 2.5x; paraboost mechanics increase it over time."
12970
13011
  },
12971
13012
  ...sprl2DelegationNotActive && {
12972
13013
  "sprl2.votingPower": `Voting power is 0 despite having BPT staked. This address has not self-delegated. Call delegate(yourAddress) on the sPRL2 contract (${SPRL2_ADDRESS}) to activate on-chain voting power.`
@@ -13055,7 +13096,7 @@ async function getCooldownStatus(userAddress) {
13055
13096
  address: userAddress,
13056
13097
  unavailableFields: {
13057
13098
  ...epochMeta === null && {
13058
- currentEpoch: "Current epoch unavailable \u2014 configure STAKE_API_URL to enable."
13099
+ currentEpoch: "Current epoch unavailable \u2014 the staking API returned no epoch data."
13059
13100
  }
13060
13101
  }
13061
13102
  };
@@ -13257,7 +13298,7 @@ async function buildStakeSprl2Tx(amount, sender, wethAmount) {
13257
13298
  const estimatedVotingPower = prlPer1Bpt !== null ? (parseFloat(amount) * prlPer1Bpt * userParaboost).toFixed(6) : null;
13258
13299
  const unavailableFields = {};
13259
13300
  if (estimatedAPR === null) {
13260
- unavailableFields.estimatedAPR = "Failed to reach APR API \u2014 configure STAKE_API_URL to enable.";
13301
+ unavailableFields.estimatedAPR = "Failed to reach the staking API \u2014 APR unavailable.";
13261
13302
  }
13262
13303
  const { simulation: simResult, warnings: simWarnings } = toSimFields(
13263
13304
  simulation,
@@ -15319,7 +15360,7 @@ function swapCommand() {
15319
15360
 
15320
15361
  // ../mcp-server/package.json
15321
15362
  var package_default2 = {
15322
- version: "1.0.0"};
15363
+ version: "1.1.1"};
15323
15364
 
15324
15365
  // src/commands/version.ts
15325
15366
  function versionCommand() {
@@ -15357,7 +15398,7 @@ var program = new Command();
15357
15398
  program.name("parallel").description("Parallel Protocol CLI").version(package_default.version).option("-c, --chain <chain>", "target chain (e.g. base, ethereum)").option("-j, --json", "force JSON output").option("--dry-run", "build transaction without broadcasting").option(
15358
15399
  "-w, --wallet [path|env]",
15359
15400
  "keystore path, 'env', or alone to auto-resolve from config/env"
15360
- ).option("-v, --verbose", "verbose logs").option("--no-cache", "bypass TTL cache, force fresh RPC call").addOption(new Option("--rpc <url>", "(not implemented)").hideHelp()).addOption(new Option("--gas-limit <n>", "(not implemented)").hideHelp()).hook("preAction", (thisCommand) => {
15401
+ ).option("-v, --verbose", "verbose logs").option("--no-cache", "bypass TTL cache, force fresh RPC call").hook("preAction", (thisCommand) => {
15361
15402
  const opts = thisCommand.opts();
15362
15403
  if (opts.json) process.env._PARALLEL_JSON_FLAG = "1";
15363
15404
  });
@@ -15403,7 +15444,17 @@ try {
15403
15444
  process.exitCode = 0;
15404
15445
  } else {
15405
15446
  if (isJsonMode()) {
15406
- const invoked = process.argv.slice(2).filter((a) => !a.startsWith("-"));
15447
+ const invoked = [];
15448
+ let node = program;
15449
+ for (const token of process.argv.slice(2)) {
15450
+ if (token.startsWith("-")) break;
15451
+ const child = node.commands.find(
15452
+ (c) => c.name() === token || c.aliases().includes(token)
15453
+ );
15454
+ if (!child) break;
15455
+ invoked.push(token);
15456
+ node = child;
15457
+ }
15407
15458
  const message = err.code === "commander.help" ? `Missing subcommand for 'parallel ${invoked.join(" ")}'` : err.message.replace(/^error:\s*/, "");
15408
15459
  console.error(
15409
15460
  JSON.stringify(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parallel-protocol/cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "files": [