zk-agent-cli 0.1.0-beta.7 → 0.1.0-beta.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 +82 -2
  2. package/dist/index.js +1291 -80
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7722,6 +7722,10 @@ function formatHumanErrorMessage(error) {
7722
7722
  pushDetailLine(lines, "reason", details.reason);
7723
7723
  pushDetailLine(lines, "note", details.note);
7724
7724
  pushDetailLine(lines, "suggested action", details.suggestedAction);
7725
+ pushDetailLine(lines, "status command", details.statusCommand);
7726
+ pushDetailLine(lines, "approve command", details.approveCommand);
7727
+ pushDetailLine(lines, "relay inspect command", details.relayInspectCommand);
7728
+ pushDetailLine(lines, "reissue remote approval command", details.reissueRemoteApprovalCommand);
7725
7729
  pushDetailLine(lines, "validation domain", details.validationDomain);
7726
7730
  pushDetailLine(lines, "validation stage", details.validationStage);
7727
7731
  const validation = asRecord2(details.validation);
@@ -7741,6 +7745,71 @@ function formatHumanErrorMessage(error) {
7741
7745
  return lines.join("\n");
7742
7746
  }
7743
7747
 
7748
+ // src/lib/discovery-summary.ts
7749
+ function createEmptyDiscoverySourceCounts() {
7750
+ return {
7751
+ localDeployments: 0,
7752
+ tokenDirectory: 0,
7753
+ unknown: 0
7754
+ };
7755
+ }
7756
+ function createEmptyDiscoveryRoleCounts() {
7757
+ return Object.fromEntries(
7758
+ REGISTRY_TOKEN_ROLES.map((role) => [role, 0])
7759
+ );
7760
+ }
7761
+ function summarizeDiscoveryEntrySources(entries) {
7762
+ const counts = createEmptyDiscoverySourceCounts();
7763
+ for (const entry of entries) {
7764
+ if (entry.source === "local-deployments") {
7765
+ counts.localDeployments += 1;
7766
+ continue;
7767
+ }
7768
+ if (entry.source === "token-directory") {
7769
+ counts.tokenDirectory += 1;
7770
+ continue;
7771
+ }
7772
+ counts.unknown += 1;
7773
+ }
7774
+ return counts;
7775
+ }
7776
+ function summarizeDiscoveryRoleMatches(entries) {
7777
+ const counts = createEmptyDiscoveryRoleCounts();
7778
+ for (const entry of entries) {
7779
+ for (const match of entry.defaultsRegistryMatches || []) {
7780
+ counts[match.role] += 1;
7781
+ }
7782
+ }
7783
+ return counts;
7784
+ }
7785
+ function countCurrentValidatedDefaultEntries(entries) {
7786
+ return entries.filter(
7787
+ (entry) => (entry.defaultsRegistryMatches || []).some((match) => match.isCurrentValidatedDefault)
7788
+ ).length;
7789
+ }
7790
+ function listUniqueDiscoveryChains(entries) {
7791
+ return [...new Set(
7792
+ entries.map((entry) => entry.chainKey?.trim()).filter((value) => Boolean(value))
7793
+ )];
7794
+ }
7795
+ function firstDiscoverySymbol(entries) {
7796
+ return entries.find((entry) => entry.symbol?.trim())?.symbol?.trim() || null;
7797
+ }
7798
+ function firstDiscoverySource(entries) {
7799
+ if (entries.length === 0) return null;
7800
+ return entries[0]?.source || "unknown";
7801
+ }
7802
+ function summarizeTokenRegistrySources(sources) {
7803
+ return sources.map((source) => ({
7804
+ id: source.id,
7805
+ enabled: source.enabled,
7806
+ exists: source.exists
7807
+ }));
7808
+ }
7809
+ function findNativeBalance(balances) {
7810
+ return balances.find((balance) => balance.type === "native") || null;
7811
+ }
7812
+
7744
7813
  // src/lib/recommended-commands.ts
7745
7814
  function appendPaymasterMode(command, paymasterMode) {
7746
7815
  if (!paymasterMode || paymasterMode === "none") {
@@ -7758,6 +7827,18 @@ function buildTopLevelNextRecommendedCommand(requestId, paymasterMode) {
7758
7827
  function buildWalletCreateRecommendedCommand() {
7759
7828
  return "zk-agent wallet create --await-local";
7760
7829
  }
7830
+ function buildRelayInspectRecommendedCommand(relayUrl = "<url>") {
7831
+ return `zk-agent relay inspect --relay-url ${relayUrl}`;
7832
+ }
7833
+ function buildWalletCreateRemoteRecommendedCommand(relayUrl = "<url>", paymasterMode, walletName = "main", accountKind = "smart-account") {
7834
+ const command = [
7835
+ "zk-agent wallet create",
7836
+ walletName !== "main" ? `--name ${walletName}` : "",
7837
+ accountKind !== "smart-account" ? `--account-kind ${accountKind}` : "",
7838
+ `--relay-url ${relayUrl} --wait-relay --prompt-code`
7839
+ ].filter(Boolean).join(" ");
7840
+ return appendPaymasterMode(command, paymasterMode);
7841
+ }
7761
7842
  function buildWalletListRecommendedCommand() {
7762
7843
  return "zk-agent wallet list";
7763
7844
  }
@@ -7784,6 +7865,12 @@ function buildResolveTokenRecommendedCommand(chain, symbol, role, source) {
7784
7865
  const withRole = role ? `${command} --role ${role}` : command;
7785
7866
  return source ? `${withRole} --source ${source}` : withRole;
7786
7867
  }
7868
+ function buildPaymasterFeeTokensRecommendedCommand(chain) {
7869
+ return buildTokensRecommendedCommand(chain, void 0, "paymaster-fee-token");
7870
+ }
7871
+ function buildPaymasterFeeTokenResolveRecommendedCommand(chain, symbol) {
7872
+ return buildResolveTokenRecommendedCommand(chain, symbol, "paymaster-fee-token");
7873
+ }
7787
7874
  function buildDiscoveryRecommendedCommands(input) {
7788
7875
  return {
7789
7876
  inspectDefaults: buildDefaultsRecommendedCommand(),
@@ -7814,6 +7901,9 @@ function buildDiscoveryRecommendedCommands(input) {
7814
7901
  function buildWalletReapproveRecommendedCommand(walletName) {
7815
7902
  return `zk-agent wallet reapprove --name ${walletName} --await-local`;
7816
7903
  }
7904
+ function buildWalletReapproveRemoteRecommendedCommand(walletName, relayUrl = "<url>") {
7905
+ return `zk-agent wallet reapprove --name ${walletName} --relay-url ${relayUrl} --wait-relay --prompt-code`;
7906
+ }
7817
7907
  function buildWalletNextRecommendedCommand(walletName) {
7818
7908
  return `zk-agent wallet next --name ${walletName}`;
7819
7909
  }
@@ -8507,9 +8597,15 @@ function workflowFollowupLines(recommendedCommands) {
8507
8597
  if (recommendedCommands.discoverOwnedTokens) {
8508
8598
  lines.push(["discover owned tokens", recommendedCommands.discoverOwnedTokens]);
8509
8599
  }
8600
+ if (recommendedCommands.discoverPaymasterTokens) {
8601
+ lines.push(["discover paymaster tokens", recommendedCommands.discoverPaymasterTokens]);
8602
+ }
8510
8603
  if (recommendedCommands.discoverTokens) {
8511
8604
  lines.push(["discover tokens", recommendedCommands.discoverTokens]);
8512
8605
  }
8606
+ if (recommendedCommands.inspectPaymasterToken) {
8607
+ lines.push(["inspect paymaster token", recommendedCommands.inspectPaymasterToken]);
8608
+ }
8513
8609
  if (recommendedCommands.inspectToken) {
8514
8610
  lines.push(["inspect token", recommendedCommands.inspectToken]);
8515
8611
  }
@@ -9150,6 +9246,27 @@ function linesForMultiBalances(result) {
9150
9246
  }
9151
9247
  return lines;
9152
9248
  }
9249
+ function buildAssetsDiscoverySummary(result) {
9250
+ const nativeBalance = findNativeBalance(result.balances);
9251
+ const ownedTokenSymbols = (result.ownedTokenRegistry?.entries || []).map((entry) => entry.symbol?.trim()).filter((value) => Boolean(value));
9252
+ return {
9253
+ walletName: result.walletName,
9254
+ chain: result.chain,
9255
+ chainId: result.chainId,
9256
+ assetCount: result.balances.length,
9257
+ nativeAssetSymbol: nativeBalance?.symbol || null,
9258
+ nativeAssetBalance: nativeBalance?.balance || null,
9259
+ ownedTokenCount: result.ownedTokenRegistry?.entryCount || 0,
9260
+ primaryOwnedTokenSymbol: firstDiscoverySymbol(result.ownedTokenRegistry?.entries || []),
9261
+ ownedTokenSymbols,
9262
+ ownedTokenSourceCounts: result.ownedTokenRegistry?.summary.sourceCounts || null,
9263
+ ownedBridgeMappingCounts: result.ownedTokenRegistry?.summary.bridgeMappingCounts || null,
9264
+ ownedRegistryRoleCounts: result.ownedTokenRegistry?.summary.registryRoleCounts || null
9265
+ };
9266
+ }
9267
+ function buildBalancesDiscoverySummary(result) {
9268
+ return buildAssetsDiscoverySummary(result);
9269
+ }
9153
9270
  function withPaymasterOptions(command) {
9154
9271
  return command.option("--paymaster-mode <mode>", "none, sponsored, or approval-based").option("--paymaster-address <address>", "Explicit paymaster contract address override").option("--paymaster-token <address>", "ERC-20 token address for approval-based paymaster mode");
9155
9272
  }
@@ -9209,12 +9326,46 @@ function createBalancesCommand(deps) {
9209
9326
  provider: resolvedDeps.provider,
9210
9327
  ownedTokens: options.ownedTokens
9211
9328
  });
9212
- printResult(linesForSingleBalances(payload), { ok: true, ...payload });
9329
+ const discoverySummary = options.ownedTokens && "ownedTokenRegistry" in payload ? buildBalancesDiscoverySummary(payload) : void 0;
9330
+ const recommendedCommands = options.ownedTokens ? buildDiscoveryRecommendedCommands({
9331
+ walletName,
9332
+ chain: payload.chain
9333
+ }) : void 0;
9334
+ printResult(
9335
+ [
9336
+ ...linesForSingleBalances(payload),
9337
+ ...recommendedCommands ? workflowFollowupLines(recommendedCommands) : []
9338
+ ],
9339
+ {
9340
+ ok: true,
9341
+ ...discoverySummary ? { discoverySummary } : {},
9342
+ ...recommendedCommands ? { recommendedCommands } : {},
9343
+ ...payload
9344
+ }
9345
+ );
9213
9346
  });
9214
9347
  }
9215
9348
  function createAssetsCommand(deps) {
9216
9349
  const resolvedDeps = resolveBalancesCommandDeps(deps);
9217
- return new Command("assets").description("Fetch the preferred single-chain asset view with native balance plus registry-backed ERC-20 holdings").option("--wallet <name>", "Wallet name", "main").option("--chain <chain>", "Single chain override").action(async (options) => {
9350
+ return new Command("assets").description("Fetch the preferred single-chain asset view with native balance plus registry-backed ERC-20 holdings").addHelpText(
9351
+ "after",
9352
+ [
9353
+ "",
9354
+ "Discovery asset path:",
9355
+ " Preferred single-chain asset entrypoint:",
9356
+ " zk-agent assets --wallet main",
9357
+ "",
9358
+ " Narrower owned ERC-20 registry subset:",
9359
+ " zk-agent tokens --wallet main --owned",
9360
+ "",
9361
+ " Symbol-first token lookup before a tokenized command:",
9362
+ " zk-agent tokens --chain zksync-sepolia --symbol USDC",
9363
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
9364
+ "",
9365
+ " For the machine-readable registry/default catalog:",
9366
+ " zk-agent defaults"
9367
+ ].join("\n")
9368
+ ).option("--wallet <name>", "Wallet name", "main").option("--chain <chain>", "Single chain override").action(async (options) => {
9218
9369
  const walletName = options.wallet;
9219
9370
  const wallet = await resolvedDeps.loadWallet(walletName);
9220
9371
  if (!wallet) throw new Error(`Wallet not found: ${walletName}`);
@@ -9230,9 +9381,12 @@ function createAssetsCommand(deps) {
9230
9381
  chain: payload.chain,
9231
9382
  includeAssets: false
9232
9383
  });
9384
+ const discoverySummary = buildAssetsDiscoverySummary(
9385
+ payload
9386
+ );
9233
9387
  printResult(
9234
9388
  [...linesForSingleBalances(payload), ...workflowFollowupLines(recommendedCommands)],
9235
- { ok: true, recommendedCommands, ...payload }
9389
+ { ok: true, discoverySummary, recommendedCommands, ...payload }
9236
9390
  );
9237
9391
  });
9238
9392
  }
@@ -9912,11 +10066,35 @@ function createPlannedCommands() {
9912
10066
 
9913
10067
  // src/commands/setup.ts
9914
10068
  import { Command as Command2 } from "commander";
10069
+ function buildSetupHelpText() {
10070
+ return [
10071
+ "",
10072
+ "What setup does:",
10073
+ " Writes the local default chain and connector URL used by the first-run path.",
10074
+ "",
10075
+ "After setup, stay on the canonical local-first path:",
10076
+ " zk-agent next",
10077
+ " zk-agent wallet create --await-local",
10078
+ " zk-agent next",
10079
+ "",
10080
+ "If the browser is not colocated with this terminal, switch at the wallet step:",
10081
+ " zk-agent relay inspect --relay-url <url>",
10082
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
10083
+ " zk-agent next",
10084
+ "",
10085
+ "Environment note:",
10086
+ " No custom .env is required for setup, next, or wallet request creation.",
10087
+ " Add RPC env vars later, before live reads or broadcasts."
10088
+ ].join("\n");
10089
+ }
9915
10090
  function createInitCommand() {
9916
- return new Command2("init").alias("setup").description("Initialize local zk-agent configuration").option("--default-chain <chain>", "Default chain key", "zksync-era").option("--connector-url <url>", "Connector UI base URL", "http://localhost:4444").option("--force", "Overwrite an existing config", false).action(async (options) => {
10091
+ return new Command2("init").alias("setup").description("Initialize local zk-agent configuration for the default operator path").addHelpText("after", buildSetupHelpText()).option("--default-chain <chain>", "Default chain key", "zksync-era").option("--connector-url <url>", "Connector UI base URL", "http://localhost:4444").option("--force", "Overwrite an existing config", false).action(async (options) => {
9917
10092
  const recommendedCommands = {
10093
+ next: buildTopLevelNextRecommendedCommand(),
9918
10094
  inspectDefaults: buildDefaultsRecommendedCommand(),
9919
10095
  createWallet: buildWalletCreateRecommendedCommand(),
10096
+ relayInspect: buildRelayInspectRecommendedCommand(),
10097
+ createWalletRemote: buildWalletCreateRemoteRecommendedCommand(),
9920
10098
  afterWalletApproval: buildTopLevelNextRecommendedCommand()
9921
10099
  };
9922
10100
  const existing = await loadProjectConfig();
@@ -9926,8 +10104,11 @@ function createInitCommand() {
9926
10104
  ["status", "Config already exists. Re-run with --force to overwrite."],
9927
10105
  ["default chain", existing.defaultChain],
9928
10106
  ["connector", existing.connectorUrl],
10107
+ ["next", recommendedCommands.next],
9929
10108
  ["inspect defaults", recommendedCommands.inspectDefaults],
9930
- ["create wallet", recommendedCommands.createWallet],
10109
+ ["create wallet (local)", recommendedCommands.createWallet],
10110
+ ["relay inspect", recommendedCommands.relayInspect],
10111
+ ["create wallet (remote)", recommendedCommands.createWalletRemote],
9931
10112
  ["after approval", recommendedCommands.afterWalletApproval]
9932
10113
  ],
9933
10114
  {
@@ -9952,8 +10133,11 @@ function createInitCommand() {
9952
10133
  ["status", "Config saved"],
9953
10134
  ["default chain", config.defaultChain],
9954
10135
  ["connector", config.connectorUrl],
10136
+ ["next", recommendedCommands.next],
9955
10137
  ["inspect defaults", recommendedCommands.inspectDefaults],
9956
- ["create wallet", recommendedCommands.createWallet],
10138
+ ["create wallet (local)", recommendedCommands.createWallet],
10139
+ ["relay inspect", recommendedCommands.relayInspect],
10140
+ ["create wallet (remote)", recommendedCommands.createWalletRemote],
9957
10141
  ["after approval", recommendedCommands.afterWalletApproval]
9958
10142
  ],
9959
10143
  { ok: true, config, recommendedCommands }
@@ -10330,6 +10514,36 @@ function buildWalletNextRecommendedCommands(walletName, summary) {
10330
10514
  ...summary.recommendedCommand ? { nextAction: summary.recommendedCommand } : {}
10331
10515
  };
10332
10516
  }
10517
+ function workflowIntentSupportsTokenDiscovery(intent) {
10518
+ return intent === "send-token" || intent === "swap" || intent === "bridge" || intent === "deposit" || intent === "withdraw";
10519
+ }
10520
+ function buildWalletTokenDiscoverySummary(input) {
10521
+ if (!input.recommendedCommands) {
10522
+ return void 0;
10523
+ }
10524
+ const hasTokenDiscovery = Boolean(input.recommendedCommands.discoverAssets) || Boolean(input.recommendedCommands.discoverOwnedTokens) || Boolean(input.recommendedCommands.discoverTokens) || Boolean(input.recommendedCommands.inspectToken) || Boolean(input.recommendedCommands.discoverPaymasterTokens) || Boolean(input.recommendedCommands.inspectPaymasterToken);
10525
+ if (!hasTokenDiscovery) {
10526
+ return void 0;
10527
+ }
10528
+ return {
10529
+ walletName: input.walletName,
10530
+ chain: input.chain,
10531
+ intent: input.intent || null,
10532
+ nextAction: input.nextAction || null,
10533
+ paymasterMode: input.paymasterMode || null,
10534
+ tokenizedIntent: workflowIntentSupportsTokenDiscovery(input.intent),
10535
+ includesAssetDiscovery: Boolean(input.recommendedCommands.discoverAssets),
10536
+ includesOwnedTokenDiscovery: Boolean(input.recommendedCommands.discoverOwnedTokens),
10537
+ includesChainTokenDiscovery: Boolean(input.recommendedCommands.discoverTokens),
10538
+ includesDirectTokenInspection: Boolean(input.recommendedCommands.inspectToken),
10539
+ includesPaymasterTokenDiscovery: Boolean(
10540
+ input.recommendedCommands.discoverPaymasterTokens
10541
+ ),
10542
+ includesPaymasterTokenInspection: Boolean(
10543
+ input.recommendedCommands.inspectPaymasterToken
10544
+ )
10545
+ };
10546
+ }
10333
10547
  function walletNextLines(summary) {
10334
10548
  const lines = [
10335
10549
  ["wallet", summary.walletName],
@@ -10364,7 +10578,7 @@ var defaultProvider = new ZkSyncWalletProvider();
10364
10578
  var defaultDefiProvider = new ZkSyncDefiProvider({
10365
10579
  walletWriter: defaultProvider
10366
10580
  });
10367
- function workflowIntentSupportsTokenDiscovery(intent) {
10581
+ function workflowIntentSupportsTokenDiscovery2(intent) {
10368
10582
  return intent === "send-token" || intent === "swap" || intent === "bridge" || intent === "deposit" || intent === "withdraw";
10369
10583
  }
10370
10584
  function buildTopLevelWorkflowRecommendedCommands(input) {
@@ -10378,14 +10592,22 @@ function buildTopLevelWorkflowRecommendedCommands(input) {
10378
10592
  delete: buildWorkflowDeleteRecommendedCommand(input.requestId),
10379
10593
  walletStatus: buildWalletStatusRecommendedCommand(input.walletName),
10380
10594
  ...input.nextAction ? { nextAction: input.nextAction } : {},
10381
- ...workflowIntentSupportsTokenDiscovery(input.intent) ? {
10595
+ ...workflowIntentSupportsTokenDiscovery2(input.intent) ? {
10382
10596
  discoverAssets: buildAssetsRecommendedCommand(input.walletName),
10383
10597
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(input.walletName),
10384
10598
  discoverTokens: buildTokensRecommendedCommand(input.chain),
10385
10599
  inspectToken: buildResolveTokenRecommendedCommand(input.chain)
10600
+ } : {},
10601
+ ...input.paymasterMode === "approval-based" ? {
10602
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(input.chain),
10603
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(input.chain)
10386
10604
  } : {}
10387
10605
  };
10388
10606
  }
10607
+ function extractCheckpointPaymasterMode(checkpoint) {
10608
+ if (!("paymaster" in checkpoint.goal)) return void 0;
10609
+ return checkpoint.goal.paymaster?.mode;
10610
+ }
10389
10611
  function resolveNextCommandDeps(deps) {
10390
10612
  return {
10391
10613
  provider: deps?.provider ?? defaultProvider,
@@ -10415,6 +10637,11 @@ function buildNextHelpText() {
10415
10637
  " zk-agent wallet create --await-local",
10416
10638
  " zk-agent next",
10417
10639
  "",
10640
+ " If the browser is remote, switch at the wallet step instead of waiting for a local callback:",
10641
+ " zk-agent relay inspect --relay-url <url>",
10642
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
10643
+ " zk-agent next",
10644
+ "",
10418
10645
  " Continue a stored workflow checkpoint:",
10419
10646
  " zk-agent next --request-id <id>",
10420
10647
  "",
@@ -10470,7 +10697,16 @@ function createNextCommand(deps) {
10470
10697
  walletName: wallet2.walletName,
10471
10698
  nextAction: nextCommand2,
10472
10699
  chain: result.plan.chain,
10473
- intent: result.intent
10700
+ intent: result.intent,
10701
+ paymasterMode: extractCheckpointPaymasterMode(updatedCheckpoint)
10702
+ });
10703
+ const tokenDiscoverySummary2 = buildWalletTokenDiscoverySummary({
10704
+ walletName: wallet2.walletName,
10705
+ chain: result.plan.chain,
10706
+ intent: result.intent,
10707
+ nextAction: nextCommand2,
10708
+ paymasterMode: extractCheckpointPaymasterMode(updatedCheckpoint),
10709
+ recommendedCommands: recommendedCommands2
10474
10710
  });
10475
10711
  const workflowAgentProfile = await loadAgentIdentitySummary(wallet2.walletName);
10476
10712
  const agentFollowup2 = buildAgentFollowup(workflowAgentProfile, {
@@ -10506,6 +10742,7 @@ function createNextCommand(deps) {
10506
10742
  agentFollowup: agentFollowup2,
10507
10743
  result,
10508
10744
  checkpoint: updatedCheckpoint,
10745
+ tokenDiscoverySummary: tokenDiscoverySummary2,
10509
10746
  recommendedCommands: recommendedCommands2
10510
10747
  }
10511
10748
  );
@@ -10515,6 +10752,7 @@ function createNextCommand(deps) {
10515
10752
  if (!config) {
10516
10753
  const recommendedCommands2 = {
10517
10754
  setup: buildSetupCommand(),
10755
+ afterSetup: buildTopLevelNextRecommendedCommand2(),
10518
10756
  inspectDefaults: buildDefaultsRecommendedCommand()
10519
10757
  };
10520
10758
  printResult(
@@ -10523,6 +10761,7 @@ function createNextCommand(deps) {
10523
10761
  ...agentProfileLines(agentProfile),
10524
10762
  ...agentFollowupLines(defaultAgentFollowup),
10525
10763
  ["next", recommendedCommands2.setup],
10764
+ ["after setup", recommendedCommands2.afterSetup],
10526
10765
  ["inspect defaults", recommendedCommands2.inspectDefaults]
10527
10766
  ]),
10528
10767
  {
@@ -10544,6 +10783,11 @@ function createNextCommand(deps) {
10544
10783
  buildWalletCreateRecommendedCommand(),
10545
10784
  paymasterMode
10546
10785
  ),
10786
+ relayInspect: buildRelayInspectRecommendedCommand(),
10787
+ createWalletRemote: buildWalletCreateRemoteRecommendedCommand(
10788
+ "<url>",
10789
+ paymasterMode
10790
+ ),
10547
10791
  afterApproval: appendPaymasterMode2(buildTopLevelNextRecommendedCommand2(), paymasterMode),
10548
10792
  inspectDefaults: buildDefaultsRecommendedCommand()
10549
10793
  };
@@ -10555,6 +10799,8 @@ function createNextCommand(deps) {
10555
10799
  ...agentProfileLines(agentProfile),
10556
10800
  ...agentFollowupLines(defaultAgentFollowup),
10557
10801
  ["next", recommendedCommands2.createWallet],
10802
+ ["relay inspect", recommendedCommands2.relayInspect],
10803
+ ["remote fallback", recommendedCommands2.createWalletRemote],
10558
10804
  ["after approval", recommendedCommands2.afterApproval],
10559
10805
  ["inspect defaults", recommendedCommands2.inspectDefaults]
10560
10806
  ]),
@@ -10608,6 +10854,10 @@ function createNextCommand(deps) {
10608
10854
  walletStatus: buildWalletStatusRecommendedCommand(wallet.walletName),
10609
10855
  discoverAssets: buildAssetsRecommendedCommand(wallet.walletName),
10610
10856
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(wallet.walletName),
10857
+ ...paymasterMode === "approval-based" ? {
10858
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(wallet.chain),
10859
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(wallet.chain)
10860
+ } : {},
10611
10861
  discoverTokens: buildTokensRecommendedCommand(wallet.chain),
10612
10862
  inspectToken: buildResolveTokenRecommendedCommand(wallet.chain),
10613
10863
  workflowPay,
@@ -10615,6 +10865,13 @@ function createNextCommand(deps) {
10615
10865
  nextAction: nextCommand,
10616
10866
  inspectDefaults: buildDefaultsRecommendedCommand()
10617
10867
  };
10868
+ const tokenDiscoverySummary = buildWalletTokenDiscoverySummary({
10869
+ walletName: wallet.walletName,
10870
+ chain: wallet.chain,
10871
+ nextAction: nextCommand,
10872
+ paymasterMode,
10873
+ recommendedCommands
10874
+ });
10618
10875
  printResult(
10619
10876
  topLevelNextLines("wallet", [
10620
10877
  ...walletNextLines(summary),
@@ -10623,7 +10880,9 @@ function createNextCommand(deps) {
10623
10880
  ...summary.recommendedCommand ? [] : [["next", workflowPay]],
10624
10881
  ["discover assets", recommendedCommands.discoverAssets],
10625
10882
  ["discover owned tokens", recommendedCommands.discoverOwnedTokens],
10883
+ ...recommendedCommands.discoverPaymasterTokens ? [["discover paymaster tokens", recommendedCommands.discoverPaymasterTokens]] : [],
10626
10884
  ["discover tokens", recommendedCommands.discoverTokens],
10885
+ ...recommendedCommands.inspectPaymasterToken ? [["inspect paymaster token", recommendedCommands.inspectPaymasterToken]] : [],
10627
10886
  ["inspect token", recommendedCommands.inspectToken],
10628
10887
  ["inspect defaults", recommendedCommands.inspectDefaults]
10629
10888
  ]),
@@ -10636,6 +10895,7 @@ function createNextCommand(deps) {
10636
10895
  inspection,
10637
10896
  summary,
10638
10897
  nextCommand,
10898
+ tokenDiscoverySummary,
10639
10899
  recommendedCommands
10640
10900
  }
10641
10901
  );
@@ -10740,6 +11000,21 @@ function createAgentCommand() {
10740
11000
  agent.addHelpText(
10741
11001
  "after",
10742
11002
  [
11003
+ "",
11004
+ " Agent identity path:",
11005
+ " zk-agent agent status",
11006
+ ' zk-agent agent set --name "SED Operator" --wallet main',
11007
+ " zk-agent agent show",
11008
+ "",
11009
+ " Portable local profile management:",
11010
+ " zk-agent agent export",
11011
+ " zk-agent agent import --payload @agent-profile.json --overwrite",
11012
+ "",
11013
+ " Remove the saved local profile:",
11014
+ " zk-agent agent clear",
11015
+ "",
11016
+ " This profile is optional. Wallet approval and workflow execution still work",
11017
+ " without a saved local agent profile.",
10743
11018
  "",
10744
11019
  "Examples:",
10745
11020
  " zk-agent agent status",
@@ -10913,6 +11188,80 @@ function createAgentCommand() {
10913
11188
 
10914
11189
  // src/commands/defaults.ts
10915
11190
  import { Command as Command5 } from "commander";
11191
+ function inferPrimaryDiscoveryChain(defaults) {
11192
+ return defaults.defaultSelections.paymaster.validatedDefault?.chain || defaults.defaultSelections.swap.validatedDefault?.chain || defaults.defaultSelections.bridge.validatedWithdraw?.fromChain || defaults.defaultSelections.bridge.validatedDeposit?.toChain || defaults.validated.feeTokenEraVm?.chain || defaults.builtinChains.find((chain) => chain.key === "zksync-sepolia")?.key || defaults.builtinChains[0]?.key || "zksync-sepolia";
11193
+ }
11194
+ function inferExampleTokenSymbol(defaults) {
11195
+ return defaults.defaultSelections.swap.validatedDefault?.trackedTokenA.symbol || defaults.defaultSelections.paymaster.validatedApprovalBased?.feeTokenSymbol || defaults.validated.feeTokenEraVm?.symbol || defaults.registry.tokens.find((entry) => entry.symbol?.trim())?.symbol || "USDC";
11196
+ }
11197
+ function inferPaymasterFeeTokenSymbol(defaults) {
11198
+ return defaults.defaultSelections.paymaster.validatedApprovalBased?.feeTokenSymbol || defaults.validated.feeTokenEraVm?.symbol || null;
11199
+ }
11200
+ function buildDefaultsRecommendedCommands(defaults) {
11201
+ const primaryChain = inferPrimaryDiscoveryChain(defaults);
11202
+ const exampleSymbol = inferExampleTokenSymbol(defaults);
11203
+ const paymasterFeeTokenSymbol = inferPaymasterFeeTokenSymbol(defaults) || exampleSymbol;
11204
+ return {
11205
+ inspectDefaults: buildDefaultsRecommendedCommand(),
11206
+ discoverTokens: buildTokensRecommendedCommand(primaryChain),
11207
+ inspectToken: buildResolveTokenRecommendedCommand(primaryChain, exampleSymbol),
11208
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(primaryChain),
11209
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(
11210
+ primaryChain,
11211
+ paymasterFeeTokenSymbol
11212
+ )
11213
+ };
11214
+ }
11215
+ function buildDefaultsSummary(input) {
11216
+ const { defaults, localTokenRegistry, tokenRegistrySources, tokenDirectoryChains } = input;
11217
+ const primaryChain = inferPrimaryDiscoveryChain(defaults);
11218
+ const exampleTokenSymbol = inferExampleTokenSymbol(defaults);
11219
+ const paymasterFeeTokenSymbol = inferPaymasterFeeTokenSymbol(defaults);
11220
+ return {
11221
+ primaryDiscoveryChain: primaryChain,
11222
+ exampleTokenSymbol,
11223
+ paymasterFeeTokenSymbol,
11224
+ localTokenCount: localTokenRegistry.length,
11225
+ tokenDirectoryChainCount: tokenDirectoryChains.length,
11226
+ tokenRegistrySources: tokenRegistrySources.map((source) => ({
11227
+ id: source.id,
11228
+ enabled: source.enabled,
11229
+ exists: source.exists
11230
+ })),
11231
+ resolvedDefaults: {
11232
+ swap: defaults.defaultSelections.swap.validatedDefault ? {
11233
+ entryId: defaults.defaultSelections.swap.validatedDefault.entryId,
11234
+ chain: defaults.defaultSelections.swap.validatedDefault.chain,
11235
+ protocol: defaults.defaultSelections.swap.validatedDefault.protocol,
11236
+ status: defaults.defaultSelections.swap.validatedDefault.status
11237
+ } : null,
11238
+ bridgeDeposit: defaults.defaultSelections.bridge.validatedDeposit ? {
11239
+ entryId: defaults.defaultSelections.bridge.validatedDeposit.entryId,
11240
+ fromChain: defaults.defaultSelections.bridge.validatedDeposit.fromChain,
11241
+ toChain: defaults.defaultSelections.bridge.validatedDeposit.toChain,
11242
+ status: defaults.defaultSelections.bridge.validatedDeposit.status
11243
+ } : null,
11244
+ bridgeWithdraw: defaults.defaultSelections.bridge.validatedWithdraw ? {
11245
+ entryId: defaults.defaultSelections.bridge.validatedWithdraw.entryId,
11246
+ fromChain: defaults.defaultSelections.bridge.validatedWithdraw.fromChain,
11247
+ toChain: defaults.defaultSelections.bridge.validatedWithdraw.toChain,
11248
+ status: defaults.defaultSelections.bridge.validatedWithdraw.status,
11249
+ requiresFinalize: defaults.defaultSelections.bridge.validatedWithdraw.requiresFinalize
11250
+ } : null,
11251
+ paymasterDefault: defaults.defaultSelections.paymaster.validatedDefault ? {
11252
+ entryId: defaults.defaultSelections.paymaster.validatedDefault.entryId,
11253
+ chain: defaults.defaultSelections.paymaster.validatedDefault.chain,
11254
+ mode: defaults.defaultSelections.paymaster.validatedDefault.mode,
11255
+ status: defaults.defaultSelections.paymaster.validatedDefault.status
11256
+ } : null,
11257
+ paymasterByMode: {
11258
+ none: defaults.defaultSelections.paymaster.validatedNone?.entryId || null,
11259
+ sponsored: defaults.defaultSelections.paymaster.validatedSponsored?.entryId || null,
11260
+ approvalBased: defaults.defaultSelections.paymaster.validatedApprovalBased?.entryId || null
11261
+ }
11262
+ }
11263
+ };
11264
+ }
10916
11265
  function formatTrackedTokenSummary(token) {
10917
11266
  if (!token.address) return null;
10918
11267
  const label = token.symbol || token.address;
@@ -11386,19 +11735,56 @@ function buildDefaultsLines(input) {
11386
11735
  return lines;
11387
11736
  }
11388
11737
  function createDefaultsCommand() {
11389
- return new Command5("defaults").description("Show the machine-readable registry of supported, validated, experimental, and manually configured defaults").action(async () => {
11738
+ return new Command5("defaults").description("Show the machine-readable registry of supported, validated, experimental, and manually configured defaults").addHelpText(
11739
+ "after",
11740
+ [
11741
+ "",
11742
+ "Discovery defaults path:",
11743
+ " Use `defaults` as the machine-readable registry escape hatch for:",
11744
+ " - validated or fallback swap / bridge paths",
11745
+ " - tracked token roles and source order",
11746
+ " - paymaster defaults and supported modes",
11747
+ "",
11748
+ " For wallet-scoped asset discovery, prefer:",
11749
+ " zk-agent assets --wallet main",
11750
+ "",
11751
+ " For symbol-first token discovery, prefer:",
11752
+ " zk-agent tokens --chain zksync-sepolia",
11753
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC"
11754
+ ].join("\n")
11755
+ ).action(async () => {
11390
11756
  const defaults = loadValidatedDefaults();
11391
11757
  const localTokenRegistry = listLocalTokenRegistryEntries();
11392
11758
  const tokenRegistrySources = describeDefaultTokenRegistrySources();
11393
11759
  const tokenDirectoryChains = await listTokenDirectoryIndexedChains();
11760
+ const recommendedCommands = buildDefaultsRecommendedCommands(defaults);
11761
+ const summary = buildDefaultsSummary({
11762
+ defaults,
11763
+ localTokenRegistry,
11764
+ tokenRegistrySources,
11765
+ tokenDirectoryChains
11766
+ });
11394
11767
  const lines = buildDefaultsLines({
11395
11768
  defaults,
11396
11769
  localTokenRegistry,
11397
11770
  tokenRegistrySources,
11398
11771
  tokenDirectoryChains
11399
11772
  });
11773
+ lines.push([
11774
+ "next discovery chain",
11775
+ summary.primaryDiscoveryChain
11776
+ ]);
11777
+ lines.push([
11778
+ "example token",
11779
+ summary.exampleTokenSymbol
11780
+ ]);
11781
+ if (summary.paymasterFeeTokenSymbol) {
11782
+ lines.push(["paymaster fee token symbol", summary.paymasterFeeTokenSymbol]);
11783
+ }
11400
11784
  printResult(lines, {
11401
11785
  ok: true,
11786
+ summary,
11787
+ recommendedCommands,
11402
11788
  defaults,
11403
11789
  localTokenRegistry,
11404
11790
  tokenRegistrySources,
@@ -11444,9 +11830,49 @@ async function resolveActiveChain(options, deps) {
11444
11830
  }
11445
11831
  );
11446
11832
  }
11833
+ function buildResolveTokenDiscoverySummary(result) {
11834
+ return {
11835
+ chain: result.chainKey,
11836
+ chainId: result.chainId,
11837
+ queryType: result.queryType,
11838
+ query: result.queryType === "symbol" ? result.symbol || null : result.address || null,
11839
+ roleFilter: result.role || null,
11840
+ sourceFilter: result.source || null,
11841
+ matchCount: result.matchCount,
11842
+ ambiguous: result.ambiguous,
11843
+ primarySymbol: result.primaryMatch?.symbol || null,
11844
+ primaryAddress: result.primaryMatch?.address || null,
11845
+ primaryDecimals: result.primaryMatch?.decimals ?? null,
11846
+ primarySource: result.primaryMatch?.source || (result.primaryMatch ? "unknown" : null),
11847
+ sourceCounts: summarizeDiscoveryEntrySources(result.matches),
11848
+ roleMatchCounts: summarizeDiscoveryRoleMatches(result.matches),
11849
+ currentDefaultEntryCount: countCurrentValidatedDefaultEntries(result.matches),
11850
+ tokenRegistrySources: summarizeTokenRegistrySources(result.tokenRegistrySources)
11851
+ };
11852
+ }
11447
11853
  function createResolveTokenCommand(deps) {
11448
11854
  const resolvedDeps = resolveResolveTokenCommandDeps(deps);
11449
- return new Command6("resolve-token").description("Resolve a token symbol or address against the configured local-first token registry").option("--wallet <name>", "Optional stored wallet name to infer the active chain").option("--chain <chain>", "Chain key or chain id override").option("--symbol <symbol>", "Token symbol to resolve on the active chain").option("--address <address>", "Token address to inspect on the active chain").option(
11855
+ return new Command6("resolve-token").description("Resolve a token symbol or address against the configured local-first token registry").addHelpText(
11856
+ "after",
11857
+ [
11858
+ "",
11859
+ "Resolve-token path:",
11860
+ " Symbol-first resolution on one active chain:",
11861
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
11862
+ "",
11863
+ " Use the stored wallet to infer the active chain:",
11864
+ " zk-agent resolve-token --wallet main --symbol USDC",
11865
+ "",
11866
+ " Use broader chain discovery before resolution when you still need the candidate set:",
11867
+ " zk-agent tokens --chain zksync-sepolia",
11868
+ "",
11869
+ " Use the wallet asset entrypoint when the real question is balances/holdings:",
11870
+ " zk-agent assets --wallet main",
11871
+ "",
11872
+ " Use the registry/default catalog when you need tracked roles or source order:",
11873
+ " zk-agent defaults"
11874
+ ].join("\n")
11875
+ ).option("--wallet <name>", "Optional stored wallet name to infer the active chain").option("--chain <chain>", "Chain key or chain id override").option("--symbol <symbol>", "Token symbol to resolve on the active chain").option("--address <address>", "Token address to inspect on the active chain").option(
11450
11876
  "--role <role>",
11451
11877
  `Optional defaults-registry role filter: ${REGISTRY_TOKEN_ROLES.join(", ")}`
11452
11878
  ).option(
@@ -11528,6 +11954,7 @@ function createResolveTokenCommand(deps) {
11528
11954
  lines.push(...workflowFollowupLines(recommendedCommands));
11529
11955
  printResult(lines, {
11530
11956
  ok: true,
11957
+ discoverySummary: buildResolveTokenDiscoverySummary(result),
11531
11958
  recommendedCommands,
11532
11959
  ...result
11533
11960
  });
@@ -11647,9 +12074,71 @@ function ownedTokenSummaryLines2(summary) {
11647
12074
  }
11648
12075
  return lines;
11649
12076
  }
12077
+ function buildOwnedTokensDiscoverySummary(result) {
12078
+ return {
12079
+ mode: "owned-registry-erc20",
12080
+ walletName: result.walletName,
12081
+ chainScope: result.chainFilter.chainKey,
12082
+ chainCount: 1,
12083
+ entryCount: result.entryCount,
12084
+ symbolFilter: result.symbol || null,
12085
+ roleFilter: result.role || null,
12086
+ sourceFilter: result.source || null,
12087
+ primarySymbol: firstDiscoverySymbol(result.entries),
12088
+ primarySource: firstDiscoverySource(result.entries),
12089
+ sourceCounts: result.summary.sourceCounts,
12090
+ roleMatchCounts: result.summary.registryRoleCounts,
12091
+ currentDefaultEntryCount: countCurrentValidatedDefaultEntries(result.entries),
12092
+ probeFailureCount: result.probeFailureCount,
12093
+ bridgeMappingCounts: result.summary.bridgeMappingCounts,
12094
+ tokenRegistrySources: summarizeTokenRegistrySources(result.tokenRegistrySources)
12095
+ };
12096
+ }
12097
+ function buildTokenDiscoverySummary(result) {
12098
+ return {
12099
+ mode: "discoverable",
12100
+ walletName: null,
12101
+ chainScope: result.chainFilter?.chainKey || "all-built-in-chains",
12102
+ chainCount: result.chainFilter ? 1 : listUniqueDiscoveryChains(result.entries).length,
12103
+ entryCount: result.entryCount,
12104
+ symbolFilter: result.symbol || null,
12105
+ roleFilter: result.role || null,
12106
+ sourceFilter: result.source || null,
12107
+ primarySymbol: firstDiscoverySymbol(result.entries),
12108
+ primarySource: firstDiscoverySource(result.entries),
12109
+ sourceCounts: summarizeDiscoveryEntrySources(result.entries),
12110
+ roleMatchCounts: summarizeDiscoveryRoleMatches(result.entries),
12111
+ currentDefaultEntryCount: countCurrentValidatedDefaultEntries(result.entries),
12112
+ probeFailureCount: null,
12113
+ bridgeMappingCounts: null,
12114
+ tokenRegistrySources: summarizeTokenRegistrySources(result.tokenRegistrySources)
12115
+ };
12116
+ }
11650
12117
  function createTokensCommand(deps) {
11651
12118
  const resolvedDeps = resolveTokensCommandDeps(deps);
11652
- return new Command7("tokens").description("List discoverable tokens from the configured local-first token registry, or inspect the owned ERC-20 registry subset for one wallet").option("--wallet <name>", "Optional stored wallet name to infer the active chain").option("--chain <chain>", "Chain key or chain id override").option("--symbol <symbol>", "Optional exact symbol filter").option(
12119
+ return new Command7("tokens").description("List discoverable tokens from the configured local-first token registry, or inspect the owned ERC-20 registry subset for one wallet").addHelpText(
12120
+ "after",
12121
+ [
12122
+ "",
12123
+ "Discovery token path:",
12124
+ " Start with the preferred wallet asset view when you need balances plus tracked ERC-20 holdings:",
12125
+ " zk-agent assets --wallet main",
12126
+ "",
12127
+ " Use the narrower owned ERC-20 registry subset when you only want held tokens:",
12128
+ " zk-agent tokens --wallet main --owned",
12129
+ "",
12130
+ " Use chain-scoped discovery before choosing a token address:",
12131
+ " zk-agent tokens --chain zksync-sepolia",
12132
+ " zk-agent tokens --chain zksync-sepolia --symbol USDC",
12133
+ " zk-agent tokens --chain zksync-sepolia --role paymaster-fee-token",
12134
+ "",
12135
+ " For one direct token-resolution check:",
12136
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
12137
+ "",
12138
+ " For the full defaults/registry catalog:",
12139
+ " zk-agent defaults"
12140
+ ].join("\n")
12141
+ ).option("--wallet <name>", "Optional stored wallet name to infer the active chain").option("--chain <chain>", "Chain key or chain id override").option("--symbol <symbol>", "Optional exact symbol filter").option(
11653
12142
  "--role <role>",
11654
12143
  `Optional defaults-registry role filter: ${REGISTRY_TOKEN_ROLES.join(", ")}`
11655
12144
  ).option(
@@ -11758,6 +12247,7 @@ function createTokensCommand(deps) {
11758
12247
  lines2.push(...workflowFollowupLines(recommendedCommands2));
11759
12248
  printResult(lines2, {
11760
12249
  ok: true,
12250
+ discoverySummary: buildOwnedTokensDiscoverySummary(result2),
11761
12251
  recommendedCommands: recommendedCommands2,
11762
12252
  ...result2
11763
12253
  });
@@ -11813,6 +12303,7 @@ function createTokensCommand(deps) {
11813
12303
  lines.push(...workflowFollowupLines(recommendedCommands));
11814
12304
  printResult(lines, {
11815
12305
  ok: true,
12306
+ discoverySummary: buildTokenDiscoverySummary(result),
11816
12307
  recommendedCommands,
11817
12308
  ...result
11818
12309
  });
@@ -11975,6 +12466,8 @@ var RELAY_BODY_LIMIT_BYTES = 1024 * 1024;
11975
12466
  var RELAY_SERVICE = "zk-agent-relay";
11976
12467
  var RELAY_PROTOCOL = "zk-agent-session-relay";
11977
12468
  var RELAY_SCHEMA_VERSION = 1;
12469
+ var RELAY_STATE_BACKEND = "local-filesystem";
12470
+ var RELAY_DEPLOYMENT_SCOPE = "single-host";
11978
12471
  function relayDir() {
11979
12472
  const directory = path7.join(storageDir(), "relay");
11980
12473
  if (!fs7.existsSync(directory)) {
@@ -12010,11 +12503,15 @@ function sanitizeRelayRecord(record) {
12010
12503
  }
12011
12504
  function relayStatusResponse(baseUrl, record) {
12012
12505
  const sanitized = sanitizeRelayRecord(record);
12506
+ const shareUrl = relayShareUrl(baseUrl, sanitized.request_id);
12507
+ const statusUrl = relayStatusUrl(baseUrl, sanitized.request_id);
12013
12508
  return {
12014
12509
  request_id: sanitized.request_id,
12015
12510
  status: relayStatus(record),
12016
12511
  approval_ready: Boolean(record.encrypted_payload),
12017
- approval_url: `${baseUrl}/r/${sanitized.request_id}`,
12512
+ share_url: shareUrl,
12513
+ status_url: statusUrl,
12514
+ approval_url: shareUrl,
12018
12515
  expires_at: sanitized.expires_at,
12019
12516
  request: sanitized.request,
12020
12517
  approval_submitted_at: sanitized.approval_submitted_at
@@ -12111,6 +12608,9 @@ function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable, p
12111
12608
  origin: normalizeRelayBaseUrl(bindBaseUrl),
12112
12609
  public_origin: normalizeRelayBaseUrl(publicBaseUrl),
12113
12610
  public_origin_source: publicOriginSource,
12611
+ state_backend: RELAY_STATE_BACKEND,
12612
+ deployment_scope: RELAY_DEPLOYMENT_SCOPE,
12613
+ same_host_restart_persists: true,
12114
12614
  connector_ui_available: connectorUiAvailable,
12115
12615
  capabilities: relayCapabilities(connectorUiAvailable)
12116
12616
  };
@@ -12357,6 +12857,12 @@ function isRelayCapability(value) {
12357
12857
  function isRelayPublicOriginSource(value) {
12358
12858
  return value === "configured" || value === "bind-origin-default";
12359
12859
  }
12860
+ function isRelayStateBackend(value) {
12861
+ return value === "local-filesystem";
12862
+ }
12863
+ function isRelayDeploymentScope(value) {
12864
+ return value === "single-host";
12865
+ }
12360
12866
  function asRelayHealthResponse(value) {
12361
12867
  if (!isRecord3(value)) return null;
12362
12868
  if (value.ok !== true) return null;
@@ -12369,6 +12875,15 @@ function asRelayHealthResponse(value) {
12369
12875
  if (typeof value.public_origin_source !== "undefined" && !isRelayPublicOriginSource(value.public_origin_source)) {
12370
12876
  return null;
12371
12877
  }
12878
+ if (typeof value.state_backend !== "undefined" && !isRelayStateBackend(value.state_backend)) {
12879
+ return null;
12880
+ }
12881
+ if (typeof value.deployment_scope !== "undefined" && !isRelayDeploymentScope(value.deployment_scope)) {
12882
+ return null;
12883
+ }
12884
+ if (typeof value.same_host_restart_persists !== "undefined" && typeof value.same_host_restart_persists !== "boolean") {
12885
+ return null;
12886
+ }
12372
12887
  if (typeof value.connector_ui_available !== "boolean") return null;
12373
12888
  if (!Array.isArray(value.capabilities) || !value.capabilities.every(isRelayCapability)) {
12374
12889
  return null;
@@ -12410,6 +12925,15 @@ function inferRelayPublicOriginSource(options) {
12410
12925
  }
12411
12926
  return normalizedOrigin === normalizedPublicOrigin ? "bind-origin-default" : "configured";
12412
12927
  }
12928
+ function inferRelayStateBackend(relayMode) {
12929
+ return relayMode === "local-file" ? "local-filesystem" : null;
12930
+ }
12931
+ function inferRelayDeploymentScope(relayMode) {
12932
+ return relayMode === "local-file" ? "single-host" : null;
12933
+ }
12934
+ function inferSameHostRestartPersists(relayMode) {
12935
+ return relayMode === "local-file" ? true : null;
12936
+ }
12413
12937
  function relayHostedReadinessNotes(options) {
12414
12938
  const notes = [];
12415
12939
  if (!options.compatible) {
@@ -12430,6 +12954,17 @@ function relayHostedReadinessNotes(options) {
12430
12954
  }
12431
12955
  return notes;
12432
12956
  }
12957
+ function relayOperationalContractNotes(options) {
12958
+ if (!options.compatible) {
12959
+ return [];
12960
+ }
12961
+ if (options.stateBackend === "local-filesystem" && options.deploymentScope === "single-host" && options.sameHostRestartPersists === true) {
12962
+ return [
12963
+ "Relay state is stored on the relay host local filesystem. Restarts on the same host keep pending approval state, but multi-instance or load-balanced deployments do not share relay state."
12964
+ ];
12965
+ }
12966
+ return [];
12967
+ }
12433
12968
  function relayOriginRelationshipNotes(options) {
12434
12969
  const notes = [];
12435
12970
  const relayUrlMatchesOrigin = relayUrlMatches(options.relayUrl, options.origin);
@@ -12446,6 +12981,20 @@ function relayOriginRelationshipNotes(options) {
12446
12981
  }
12447
12982
  return notes;
12448
12983
  }
12984
+ function buildRelayDeploymentSummary(options) {
12985
+ return {
12986
+ origin: options.origin,
12987
+ publicOrigin: options.publicOrigin,
12988
+ publicOriginSource: options.publicOriginSource,
12989
+ shareLinkBaseUrl: options.shareLinkBaseUrl,
12990
+ statusApiBaseUrl: options.statusApiBaseUrl,
12991
+ publicOriginConfigured: options.publicOriginSource === "configured",
12992
+ publicOriginLooksLocal: options.publicOriginLooksLocal,
12993
+ connectorUiAvailable: options.connectorUiAvailable,
12994
+ hostedShareRedirectReady: options.hostedShareRedirectReady,
12995
+ singleHostFileState: options.stateBackend === "local-filesystem" && options.deploymentScope === "single-host" && options.sameHostRestartPersists === true
12996
+ };
12997
+ }
12449
12998
  function buildRelayInspectPayload(relayUrl, rawHealth) {
12450
12999
  const health = asRelayHealthResponse(rawHealth);
12451
13000
  const fallbackPublicOrigin = isRecord3(rawHealth) && typeof rawHealth.public_origin === "string" ? rawHealth.public_origin : relayUrl;
@@ -12454,6 +13003,9 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12454
13003
  origin: health?.origin || null,
12455
13004
  publicOrigin
12456
13005
  });
13006
+ const stateBackend = health?.state_backend || inferRelayStateBackend(health?.relay_mode || null);
13007
+ const deploymentScope = health?.deployment_scope || inferRelayDeploymentScope(health?.relay_mode || null);
13008
+ const sameHostRestartPersists = typeof health?.same_host_restart_persists === "boolean" ? health.same_host_restart_persists : inferSameHostRestartPersists(health?.relay_mode || null);
12457
13009
  const compatible = Boolean(health && hasCoreRelayCapabilities(health.capabilities));
12458
13010
  const connectorUiAvailable = health?.connector_ui_available ?? null;
12459
13011
  const relayUrlMatchesOrigin = relayUrlMatches(relayUrl, health?.origin || null);
@@ -12461,6 +13013,19 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12461
13013
  const publicOriginLooksLocal = relayPublicOriginLooksLocal(publicOrigin);
12462
13014
  const hostedShareRedirectReady = compatible && connectorUiAvailable === true && !publicOriginLooksLocal;
12463
13015
  const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
13016
+ const deploymentSummary = buildRelayDeploymentSummary({
13017
+ origin: health?.origin || null,
13018
+ publicOrigin,
13019
+ publicOriginSource,
13020
+ shareLinkBaseUrl,
13021
+ statusApiBaseUrl,
13022
+ publicOriginLooksLocal,
13023
+ connectorUiAvailable,
13024
+ hostedShareRedirectReady,
13025
+ stateBackend,
13026
+ deploymentScope,
13027
+ sameHostRestartPersists
13028
+ });
12464
13029
  const notes = [
12465
13030
  ...relayHostedReadinessNotes({
12466
13031
  compatible,
@@ -12468,6 +13033,12 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12468
13033
  publicOriginSource,
12469
13034
  connectorUiAvailable
12470
13035
  }),
13036
+ ...relayOperationalContractNotes({
13037
+ compatible,
13038
+ stateBackend,
13039
+ deploymentScope,
13040
+ sameHostRestartPersists
13041
+ }),
12471
13042
  ...relayOriginRelationshipNotes({
12472
13043
  relayUrl,
12473
13044
  origin: health?.origin || null,
@@ -12486,6 +13057,9 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12486
13057
  origin: health?.origin || null,
12487
13058
  publicOrigin,
12488
13059
  publicOriginSource,
13060
+ stateBackend,
13061
+ deploymentScope,
13062
+ sameHostRestartPersists,
12489
13063
  shareLinkBaseUrl,
12490
13064
  statusApiBaseUrl,
12491
13065
  relayUrlMatchesOrigin,
@@ -12493,6 +13067,7 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12493
13067
  publicOriginLooksLocal,
12494
13068
  connectorUiAvailable,
12495
13069
  hostedShareRedirectReady,
13070
+ deploymentSummary,
12496
13071
  capabilities: health?.capabilities || [],
12497
13072
  recommendedCommands: compatible ? buildRelayServeRecommendedCommands(publicOrigin) : {},
12498
13073
  notes
@@ -12500,6 +13075,23 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12500
13075
  }
12501
13076
  function createRelayCommand() {
12502
13077
  const relay = new Command8("relay").description("Run the local connector relay prototype server");
13078
+ relay.addHelpText(
13079
+ "after",
13080
+ [
13081
+ "",
13082
+ " Hosted remote-approval path:",
13083
+ " zk-agent relay serve --public-origin https://relay.example.com",
13084
+ " zk-agent relay inspect --relay-url <url>",
13085
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
13086
+ " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code",
13087
+ "",
13088
+ " Keep `wallet create|reapprove --await-local` as the default baseline when",
13089
+ " the browser and terminal are colocated.",
13090
+ "",
13091
+ " Use `relay inspect` before sending operators to a hosted share link so",
13092
+ " the public origin, connector UI, and hosted-readiness contract are visible."
13093
+ ].join("\n")
13094
+ );
12503
13095
  relay.command("serve").description("Serve the local relay API and, when available, the built connector UI").option("--host <host>", "Host to bind", "127.0.0.1").option("--port <port>", "Port to bind (0 = choose a free port)", "4445").option(
12504
13096
  "--public-origin <url>",
12505
13097
  "Public base URL to advertise in share/status links when the relay is behind a tunnel or reverse proxy"
@@ -12521,6 +13113,19 @@ function createRelayCommand() {
12521
13113
  const hostedShareRedirectReady = connectorUiAvailable && !publicOriginLooksLocal;
12522
13114
  const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
12523
13115
  const recommendedCommands = buildRelayServeRecommendedCommands(publicOrigin);
13116
+ const deploymentSummary = buildRelayDeploymentSummary({
13117
+ origin: server.origin,
13118
+ publicOrigin,
13119
+ publicOriginSource,
13120
+ shareLinkBaseUrl,
13121
+ statusApiBaseUrl,
13122
+ publicOriginLooksLocal,
13123
+ connectorUiAvailable,
13124
+ hostedShareRedirectReady,
13125
+ stateBackend: "local-filesystem",
13126
+ deploymentScope: "single-host",
13127
+ sameHostRestartPersists: true
13128
+ });
12524
13129
  const notes = relayHostedReadinessNotes({
12525
13130
  compatible: true,
12526
13131
  publicOrigin,
@@ -12533,9 +13138,13 @@ function createRelayCommand() {
12533
13138
  origin: server.origin,
12534
13139
  publicOrigin,
12535
13140
  publicOriginSource,
13141
+ stateBackend: "local-filesystem",
13142
+ deploymentScope: "single-host",
13143
+ sameHostRestartPersists: true,
12536
13144
  shareLinkBaseUrl,
12537
13145
  statusApiBaseUrl,
12538
13146
  publicOriginLooksLocal,
13147
+ deploymentSummary,
12539
13148
  port: server.port,
12540
13149
  healthUrl: `${server.origin}/health`,
12541
13150
  publicHealthUrl: `${publicOrigin}/health`,
@@ -12562,6 +13171,12 @@ function createRelayCommand() {
12562
13171
  humanLine("public origin", publicOrigin);
12563
13172
  }
12564
13173
  humanLine("public origin source", publicOriginSource);
13174
+ humanLine("state backend", payload.stateBackend);
13175
+ humanLine("deployment scope", payload.deploymentScope);
13176
+ humanLine(
13177
+ "same-host restart persists",
13178
+ payload.sameHostRestartPersists ? "yes" : "no"
13179
+ );
12565
13180
  humanLine("share-link base", shareLinkBaseUrl);
12566
13181
  humanLine("status api base", statusApiBaseUrl);
12567
13182
  humanLine("health", `${server.origin}/health`);
@@ -12618,6 +13233,18 @@ function createRelayCommand() {
12618
13233
  if (payload.publicOriginSource) {
12619
13234
  humanLine("public origin source", payload.publicOriginSource);
12620
13235
  }
13236
+ if (payload.stateBackend) {
13237
+ humanLine("state backend", payload.stateBackend);
13238
+ }
13239
+ if (payload.deploymentScope) {
13240
+ humanLine("deployment scope", payload.deploymentScope);
13241
+ }
13242
+ if (payload.sameHostRestartPersists !== null) {
13243
+ humanLine(
13244
+ "same-host restart persists",
13245
+ payload.sameHostRestartPersists ? "yes" : "no"
13246
+ );
13247
+ }
12621
13248
  humanLine("share-link base", payload.shareLinkBaseUrl);
12622
13249
  humanLine("status api base", payload.statusApiBaseUrl);
12623
13250
  if (payload.relayUrlMatchesOrigin !== null) {
@@ -13260,6 +13887,39 @@ function relayOutputAliases(relay) {
13260
13887
  relayStatusApiBaseUrl: statusApiBaseUrl
13261
13888
  };
13262
13889
  }
13890
+ function determineRelayRecoveryMode(options) {
13891
+ if (options.recommendedCommands.reissueRemoteApproval && options.nextAction === options.recommendedCommands.reissueRemoteApproval) {
13892
+ return "reissue-remote-approval";
13893
+ }
13894
+ if (options.recommendedCommands.relayInspect && options.nextAction === options.recommendedCommands.relayInspect) {
13895
+ return "inspect-relay";
13896
+ }
13897
+ if (options.recommendedCommands.approve && options.nextAction === options.recommendedCommands.approve) {
13898
+ return "approve";
13899
+ }
13900
+ return "status-poll";
13901
+ }
13902
+ function buildRelayRecoverySummary(options) {
13903
+ const aliases = relayOutputAliases(options.relay);
13904
+ return {
13905
+ requestId: options.requestId,
13906
+ walletName: options.walletName || null,
13907
+ relayUrl: options.relayUrl,
13908
+ relayStatus: options.relayStatus ?? options.relay?.status ?? null,
13909
+ approvalReady: typeof options.approvalReady === "boolean" ? options.approvalReady : options.relay && "approval_ready" in options.relay ? options.relay.approval_ready : null,
13910
+ nextAction: options.nextAction,
13911
+ shareLinkBaseUrl: aliases.relayShareLinkBaseUrl || null,
13912
+ statusApiBaseUrl: aliases.relayStatusApiBaseUrl || null,
13913
+ recoveryMode: determineRelayRecoveryMode({
13914
+ nextAction: options.nextAction,
13915
+ recommendedCommands: options.recommendedCommands
13916
+ }),
13917
+ includesStatusPoll: Boolean(options.recommendedCommands.status),
13918
+ includesApprove: Boolean(options.recommendedCommands.approve),
13919
+ includesRelayInspect: Boolean(options.recommendedCommands.relayInspect),
13920
+ includesRemoteReissue: Boolean(options.recommendedCommands.reissueRemoteApproval)
13921
+ };
13922
+ }
13263
13923
  function sanitizeWalletRequestRecord(request) {
13264
13924
  const { sessionSecretKey: _sessionSecretKey, ...rest } = request;
13265
13925
  return rest;
@@ -14527,6 +15187,197 @@ function buildPendingRequestRecommendedCommands(walletName, requestId, relayUrl,
14527
15187
  function buildPendingRequestNextAction(recommendedCommands) {
14528
15188
  return recommendedCommands.relayStatus ?? recommendedCommands.awaitLocal;
14529
15189
  }
15190
+ function buildRelayWaitGuidanceLines(options) {
15191
+ return [
15192
+ ["status", "Waiting for relay approval"],
15193
+ ["request", options.requestId],
15194
+ ["wallet", options.walletName],
15195
+ ["share url", options.relay.share_url],
15196
+ ["status url", options.relay.status_url],
15197
+ ["approval url", options.relay.approval_url],
15198
+ ["expires", options.expiresAt],
15199
+ ["browser step", "Open the share url in a browser and complete connector approval."],
15200
+ [
15201
+ "terminal step",
15202
+ options.codeEntry === "prompt" ? "After the relay is ready, enter the 6-digit approval code in this terminal." : "The CLI will finalize automatically with the provided 6-digit approval code once the relay is ready."
15203
+ ],
15204
+ ["fallback status", options.statusCommand],
15205
+ ["fallback approve", options.approveCommand]
15206
+ ];
15207
+ }
15208
+ function printRelayWaitGuidance(options) {
15209
+ if (shouldJsonOutput()) return;
15210
+ for (const [label, value] of buildRelayWaitGuidanceLines(options)) {
15211
+ humanLine(label, value);
15212
+ }
15213
+ }
15214
+ async function buildRelayStatusFollowUp(options) {
15215
+ if (options.relay.status === "expired") {
15216
+ return await buildRelayExpiredRecoveryCommands({
15217
+ walletName: options.relay.request?.walletName,
15218
+ relayUrl: options.relayUrl,
15219
+ paymasterMode: options.relay.request?.requestedPaymasterMode,
15220
+ accountKind: options.relay.request?.requestedAccountKind
15221
+ });
15222
+ }
15223
+ if (options.relay.approval_ready) {
15224
+ const approve = buildWalletRequestRelayApproveRecommendedCommand(
15225
+ options.relay.request_id,
15226
+ options.relayUrl
15227
+ );
15228
+ return {
15229
+ nextAction: approve,
15230
+ recommendedCommands: {
15231
+ status: buildWalletRequestRelayStatusRecommendedCommand(
15232
+ options.relay.request_id,
15233
+ options.relayUrl
15234
+ ),
15235
+ approve
15236
+ }
15237
+ };
15238
+ }
15239
+ return {
15240
+ nextAction: buildWalletRequestRelayStatusRecommendedCommand(
15241
+ options.relay.request_id,
15242
+ options.relayUrl
15243
+ ),
15244
+ recommendedCommands: {
15245
+ status: buildWalletRequestRelayStatusRecommendedCommand(
15246
+ options.relay.request_id,
15247
+ options.relayUrl
15248
+ )
15249
+ }
15250
+ };
15251
+ }
15252
+ async function buildRelayExpiredRecoveryCommands(options) {
15253
+ const relayInspect = buildRelayInspectRecommendedCommand(options.relayUrl);
15254
+ const walletName = options.walletName?.trim();
15255
+ if (!walletName) {
15256
+ return {
15257
+ nextAction: relayInspect,
15258
+ recommendedCommands: {
15259
+ relayInspect
15260
+ },
15261
+ note: "Relay approval expired. Inspect the hosted relay, then reissue the wallet request again."
15262
+ };
15263
+ }
15264
+ const existingWallet = await loadWalletSession(walletName);
15265
+ const reissueRemoteApproval = existingWallet ? buildWalletReapproveRemoteRecommendedCommand(walletName, options.relayUrl) : buildWalletCreateRemoteRecommendedCommand(
15266
+ options.relayUrl,
15267
+ options.paymasterMode,
15268
+ walletName,
15269
+ options.accountKind
15270
+ );
15271
+ return {
15272
+ nextAction: reissueRemoteApproval,
15273
+ recommendedCommands: {
15274
+ relayInspect,
15275
+ reissueRemoteApproval
15276
+ },
15277
+ note: "Relay approval expired. Reissue the remote request. If the original request used scoped session flags, add those same policy flags again."
15278
+ };
15279
+ }
15280
+ function buildRelayApprovalTimeoutError(options) {
15281
+ const statusCommand = buildWalletRequestRelayStatusRecommendedCommand(
15282
+ options.requestId,
15283
+ options.relayUrl
15284
+ );
15285
+ const approveCommand = buildWalletRequestRelayApproveRecommendedCommand(
15286
+ options.requestId,
15287
+ options.relayUrl
15288
+ );
15289
+ return new AgentError(
15290
+ "RELAY_APPROVAL_TIMEOUT",
15291
+ `Timed out waiting for relay approval after ${Math.ceil(options.timeoutMs / 1e3)} seconds.`,
15292
+ {
15293
+ requestId: options.requestId,
15294
+ relayUrl: options.relayUrl,
15295
+ timeoutMs: options.timeoutMs,
15296
+ intervalMs: options.intervalMs,
15297
+ retryable: true,
15298
+ statusCommand,
15299
+ approveCommand,
15300
+ relayRecoverySummary: buildRelayRecoverySummary({
15301
+ requestId: options.requestId,
15302
+ walletName: options.walletName,
15303
+ relayUrl: options.relayUrl,
15304
+ relayStatus: null,
15305
+ approvalReady: null,
15306
+ nextAction: statusCommand,
15307
+ recommendedCommands: {
15308
+ status: statusCommand,
15309
+ approve: approveCommand
15310
+ }
15311
+ }),
15312
+ suggestedAction: "Check the current relay status, then finalize the wallet approval once approval_ready=true."
15313
+ }
15314
+ );
15315
+ }
15316
+ async function buildRelayApprovalExpiredError(options) {
15317
+ const recovery = await buildRelayExpiredRecoveryCommands({
15318
+ walletName: options.walletName,
15319
+ relayUrl: options.relayUrl,
15320
+ paymasterMode: options.paymasterMode,
15321
+ accountKind: options.accountKind
15322
+ });
15323
+ return new AgentError(
15324
+ "RELAY_APPROVAL_EXPIRED",
15325
+ `Relay approval expired before the encrypted payload was ready for request ${options.requestId}.`,
15326
+ {
15327
+ requestId: options.requestId,
15328
+ relayUrl: options.relayUrl,
15329
+ retryable: true,
15330
+ note: recovery.note,
15331
+ relayInspectCommand: recovery.recommendedCommands.relayInspect,
15332
+ reissueRemoteApprovalCommand: recovery.recommendedCommands.reissueRemoteApproval,
15333
+ relayRecoverySummary: buildRelayRecoverySummary({
15334
+ requestId: options.requestId,
15335
+ walletName: options.walletName,
15336
+ relayUrl: options.relayUrl,
15337
+ relayStatus: "expired",
15338
+ approvalReady: false,
15339
+ nextAction: recovery.nextAction,
15340
+ recommendedCommands: recovery.recommendedCommands
15341
+ }),
15342
+ suggestedAction: "Inspect the hosted relay, then reissue the remote approval request."
15343
+ }
15344
+ );
15345
+ }
15346
+ function buildRelayApprovalNotReadyError(options) {
15347
+ const statusCommand = buildWalletRequestRelayStatusRecommendedCommand(
15348
+ options.requestId,
15349
+ options.relayUrl
15350
+ );
15351
+ const approveCommand = buildWalletRequestRelayApproveRecommendedCommand(
15352
+ options.requestId,
15353
+ options.relayUrl
15354
+ );
15355
+ return new AgentError(
15356
+ "RELAY_APPROVAL_NOT_READY",
15357
+ `Relay approval is not ready yet for request ${options.requestId}.`,
15358
+ {
15359
+ requestId: options.requestId,
15360
+ relayUrl: options.relayUrl,
15361
+ status: options.status,
15362
+ approvalReady: options.approvalReady,
15363
+ retryable: true,
15364
+ statusCommand,
15365
+ approveCommand,
15366
+ relayRecoverySummary: buildRelayRecoverySummary({
15367
+ requestId: options.requestId,
15368
+ relayUrl: options.relayUrl,
15369
+ relayStatus: options.status,
15370
+ approvalReady: options.approvalReady,
15371
+ nextAction: statusCommand,
15372
+ recommendedCommands: {
15373
+ status: statusCommand,
15374
+ approve: approveCommand
15375
+ }
15376
+ }),
15377
+ suggestedAction: "Check the current relay status, then retry approval once approval_ready=true."
15378
+ }
15379
+ );
15380
+ }
14530
15381
  function buildRequestListEntryRecommendedCommands(walletName, requestId, paymasterMode) {
14531
15382
  return {
14532
15383
  show: buildWalletRequestShowRecommendedCommand(requestId),
@@ -14669,25 +15520,56 @@ async function fetchEncryptedRelayApprovalPayload(relayUrl, requestId, options)
14669
15520
  intervalMs: options.intervalMs ?? 2e3
14670
15521
  });
14671
15522
  if (!relay.approval_ready) {
14672
- throw new Error(`Relay approval expired before the encrypted payload was ready for request ${requestId}.`);
15523
+ throw await buildRelayApprovalExpiredError({
15524
+ requestId,
15525
+ relayUrl
15526
+ });
14673
15527
  }
14674
15528
  }
14675
15529
  const approval = await fetchRelayApproval(relayUrl, requestId);
14676
15530
  if (!approval.approval_ready || !approval.encrypted_payload) {
14677
- throw new Error(`Relay approval is not ready yet for request ${requestId}.`);
15531
+ throw buildRelayApprovalNotReadyError({
15532
+ requestId,
15533
+ relayUrl,
15534
+ status: approval.status,
15535
+ approvalReady: approval.approval_ready
15536
+ });
14678
15537
  }
14679
15538
  return approval.encrypted_payload;
14680
15539
  }
14681
15540
  async function finalizePublishedRelayWalletRequest(options) {
14682
- const encryptedPayload = await fetchEncryptedRelayApprovalPayload(
14683
- options.relayUrl,
14684
- options.walletRequest.requestId,
14685
- {
14686
- wait: true,
14687
- timeoutMs: options.timeoutMs,
14688
- intervalMs: options.intervalMs
15541
+ let encryptedPayload;
15542
+ try {
15543
+ encryptedPayload = await fetchEncryptedRelayApprovalPayload(
15544
+ options.relayUrl,
15545
+ options.walletRequest.requestId,
15546
+ {
15547
+ wait: true,
15548
+ timeoutMs: options.timeoutMs,
15549
+ intervalMs: options.intervalMs
15550
+ }
15551
+ );
15552
+ } catch (error) {
15553
+ if (error instanceof Error && error.message.startsWith("Timed out waiting for relay approval after ")) {
15554
+ throw buildRelayApprovalTimeoutError({
15555
+ requestId: options.walletRequest.requestId,
15556
+ walletName: options.walletRequest.walletName,
15557
+ relayUrl: options.relayUrl,
15558
+ timeoutMs: options.timeoutMs,
15559
+ intervalMs: options.intervalMs
15560
+ });
14689
15561
  }
14690
- );
15562
+ if (error instanceof AgentError && error.code === "RELAY_APPROVAL_EXPIRED") {
15563
+ throw await buildRelayApprovalExpiredError({
15564
+ requestId: options.walletRequest.requestId,
15565
+ walletName: options.walletRequest.walletName,
15566
+ relayUrl: options.relayUrl,
15567
+ paymasterMode: options.walletRequest.requestedPaymasterMode,
15568
+ accountKind: options.walletRequest.requestedAccountKind
15569
+ });
15570
+ }
15571
+ throw error;
15572
+ }
14691
15573
  const code = options.code || (options.promptCode ? await readApprovalCodeFromStdin() : void 0);
14692
15574
  if (!code) {
14693
15575
  throw new Error("Missing relay approval code.");
@@ -15054,7 +15936,9 @@ async function printBuiltinSmartAccountProfiles() {
15054
15936
  }
15055
15937
  function createWalletCommand(deps) {
15056
15938
  const resolvedDeps = resolveWalletCommandDeps(deps);
15057
- const wallet = new Command9("wallet").description("Manage wallet sessions");
15939
+ const wallet = new Command9("wallet").description(
15940
+ "Create, inspect, and recover local-first wallet sessions"
15941
+ );
15058
15942
  const request = new Command9("request").description("Inspect and finalize pending wallet requests");
15059
15943
  const signer = new Command9("signer").description(
15060
15944
  "Inspect and manage the stored local execution signer for a wallet"
@@ -15104,7 +15988,8 @@ function createWalletCommand(deps) {
15104
15988
  " Hosted remote approval path:",
15105
15989
  " zk-agent relay inspect --relay-url <url>",
15106
15990
  " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
15107
- " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code"
15991
+ " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code",
15992
+ " zk-agent next"
15108
15993
  ].join("\n")
15109
15994
  );
15110
15995
  request.addHelpText(
@@ -15118,7 +16003,11 @@ function createWalletCommand(deps) {
15118
16003
  " Remote relay completion:",
15119
16004
  " zk-agent wallet request relay-publish --request-id <id> --relay-url <url>",
15120
16005
  " zk-agent wallet request relay-status --request-id <id> --relay-url <url> --wait",
15121
- " zk-agent wallet request approve --request-id <id> --relay-url <url> --code <code> --wait"
16006
+ " zk-agent wallet request approve --request-id <id> --relay-url <url> --code <code> --wait",
16007
+ "",
16008
+ " If relay-status returns status = expired:",
16009
+ " zk-agent relay inspect --relay-url <url>",
16010
+ " zk-agent wallet create|reapprove --relay-url <url> --wait-relay --prompt-code"
15122
16011
  ].join("\n")
15123
16012
  );
15124
16013
  signer.addHelpText(
@@ -15217,6 +16106,23 @@ function createWalletCommand(deps) {
15217
16106
  }
15218
16107
  const relay = options.relayUrl ? await publishWalletRequestToRelay(request2, options.relayUrl) : void 0;
15219
16108
  if (relayWaitOptions) {
16109
+ if (relay) {
16110
+ printRelayWaitGuidance({
16111
+ requestId: request2.requestId,
16112
+ walletName: request2.walletName,
16113
+ relay,
16114
+ expiresAt: request2.expiresAt,
16115
+ codeEntry: relayWaitOptions.promptCode ? "prompt" : "provided",
16116
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
16117
+ request2.requestId,
16118
+ relayWaitOptions.relayUrl
16119
+ ),
16120
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
16121
+ request2.requestId,
16122
+ relayWaitOptions.relayUrl
16123
+ )
16124
+ });
16125
+ }
15220
16126
  const { payload, walletRecord } = await finalizePublishedRelayWalletRequest({
15221
16127
  walletRequest: request2,
15222
16128
  ...relayWaitOptions
@@ -15248,6 +16154,20 @@ function createWalletCommand(deps) {
15248
16154
  options.relayUrl,
15249
16155
  request2.requestedPaymasterMode
15250
16156
  );
16157
+ const nextAction = buildPendingRequestNextAction(recommendedCommands);
16158
+ const relayRecoverySummary = relay && recommendedCommands.relayStatus && recommendedCommands.relayApprove ? buildRelayRecoverySummary({
16159
+ requestId: request2.requestId,
16160
+ walletName: request2.walletName,
16161
+ relayUrl: options.relayUrl,
16162
+ relayStatus: relay.status,
16163
+ approvalReady: null,
16164
+ nextAction,
16165
+ recommendedCommands: {
16166
+ status: recommendedCommands.relayStatus,
16167
+ approve: recommendedCommands.relayApprove
16168
+ },
16169
+ relay
16170
+ }) : void 0;
15251
16171
  printResult(
15252
16172
  [
15253
16173
  ["wallet", request2.walletName],
@@ -15290,6 +16210,7 @@ function createWalletCommand(deps) {
15290
16210
  approvalUrl: request2.approvalUrl,
15291
16211
  relay,
15292
16212
  ...relayOutputAliases(relay),
16213
+ relayRecoverySummary,
15293
16214
  expiresAt: request2.expiresAt,
15294
16215
  chain: request2.chain,
15295
16216
  chainId: request2.chainId,
@@ -15297,7 +16218,7 @@ function createWalletCommand(deps) {
15297
16218
  paymasterMode: request2.requestedPaymasterMode,
15298
16219
  capabilities: request2.requestedCapabilities,
15299
16220
  sessionScope: request2.requestedSessionScope,
15300
- nextAction: buildPendingRequestNextAction(recommendedCommands),
16221
+ nextAction,
15301
16222
  recommendedCommands
15302
16223
  }
15303
16224
  );
@@ -15365,6 +16286,23 @@ function createWalletCommand(deps) {
15365
16286
  }
15366
16287
  const relay = options.relayUrl ? await publishWalletRequestToRelay(request2, options.relayUrl) : void 0;
15367
16288
  if (relayWaitOptions) {
16289
+ if (relay) {
16290
+ printRelayWaitGuidance({
16291
+ requestId: request2.requestId,
16292
+ walletName: request2.walletName,
16293
+ relay,
16294
+ expiresAt: request2.expiresAt,
16295
+ codeEntry: relayWaitOptions.promptCode ? "prompt" : "provided",
16296
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
16297
+ request2.requestId,
16298
+ relayWaitOptions.relayUrl
16299
+ ),
16300
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
16301
+ request2.requestId,
16302
+ relayWaitOptions.relayUrl
16303
+ )
16304
+ });
16305
+ }
15368
16306
  const { payload, walletRecord: approvedWallet } = await finalizePublishedRelayWalletRequest({
15369
16307
  walletRequest: request2,
15370
16308
  ...relayWaitOptions
@@ -15396,6 +16334,20 @@ function createWalletCommand(deps) {
15396
16334
  options.relayUrl,
15397
16335
  request2.requestedPaymasterMode
15398
16336
  );
16337
+ const nextAction = buildPendingRequestNextAction(recommendedCommands);
16338
+ const relayRecoverySummary = relay && recommendedCommands.relayStatus && recommendedCommands.relayApprove ? buildRelayRecoverySummary({
16339
+ requestId: request2.requestId,
16340
+ walletName: request2.walletName,
16341
+ relayUrl: options.relayUrl,
16342
+ relayStatus: relay.status,
16343
+ approvalReady: null,
16344
+ nextAction,
16345
+ recommendedCommands: {
16346
+ status: recommendedCommands.relayStatus,
16347
+ approve: recommendedCommands.relayApprove
16348
+ },
16349
+ relay
16350
+ }) : void 0;
15399
16351
  printResult(
15400
16352
  [
15401
16353
  ["wallet", request2.walletName],
@@ -15438,7 +16390,8 @@ function createWalletCommand(deps) {
15438
16390
  request: sanitizeWalletRequestRecord(request2),
15439
16391
  relay,
15440
16392
  ...relayOutputAliases(relay),
15441
- nextAction: buildPendingRequestNextAction(recommendedCommands),
16393
+ relayRecoverySummary,
16394
+ nextAction,
15442
16395
  recommendedCommands
15443
16396
  }
15444
16397
  );
@@ -15582,6 +16535,13 @@ function createWalletCommand(deps) {
15582
16535
  walletRecord.walletName,
15583
16536
  summary
15584
16537
  );
16538
+ const tokenDiscoverySummary = buildWalletTokenDiscoverySummary({
16539
+ walletName: walletRecord.walletName,
16540
+ chain: summary.chain,
16541
+ nextAction: summary.recommendedCommand,
16542
+ paymasterMode: resolveEffectivePaymasterSelection(walletRecord)?.mode,
16543
+ recommendedCommands
16544
+ });
15585
16545
  printResult(
15586
16546
  [
15587
16547
  ...walletNextLines(summary),
@@ -15595,6 +16555,7 @@ function createWalletCommand(deps) {
15595
16555
  ok: true,
15596
16556
  inspection,
15597
16557
  summary,
16558
+ tokenDiscoverySummary,
15598
16559
  recommendedCommands
15599
16560
  }
15600
16561
  );
@@ -15758,6 +16719,30 @@ function createWalletCommand(deps) {
15758
16719
  request.command("relay-publish").description("Publish a stored wallet request to a relay server and get a shareable approval URL").requiredOption("--request-id <id>", "Wallet request id").requiredOption("--relay-url <url>", "Relay server base URL").action(async (options) => {
15759
16720
  const walletRequest = await requireActiveWalletRequest(options.requestId);
15760
16721
  const relay = await publishWalletRequestToRelay(walletRequest, options.relayUrl);
16722
+ const nextAction = buildWalletRequestRelayStatusRecommendedCommand(
16723
+ relay.request_id,
16724
+ options.relayUrl
16725
+ );
16726
+ const recommendedCommands = {
16727
+ status: buildWalletRequestRelayStatusRecommendedCommand(
16728
+ relay.request_id,
16729
+ options.relayUrl
16730
+ ),
16731
+ approve: buildWalletRequestRelayApproveRecommendedCommand(
16732
+ relay.request_id,
16733
+ options.relayUrl
16734
+ )
16735
+ };
16736
+ const relayRecoverySummary = buildRelayRecoverySummary({
16737
+ requestId: relay.request_id,
16738
+ walletName: walletRequest.walletName,
16739
+ relayUrl: options.relayUrl,
16740
+ relayStatus: relay.status,
16741
+ approvalReady: null,
16742
+ nextAction,
16743
+ recommendedCommands,
16744
+ relay
16745
+ });
15761
16746
  printResult(
15762
16747
  [
15763
16748
  ["status", relay.status],
@@ -15766,58 +16751,82 @@ function createWalletCommand(deps) {
15766
16751
  ["status url", relay.status_url],
15767
16752
  ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15768
16753
  ["status api base", relay.status_url.replace(/\/[^/]+$/, "")],
15769
- ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15770
- ["next approve", buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)]
16754
+ ["next status", recommendedCommands.status],
16755
+ ["next approve", recommendedCommands.approve]
15771
16756
  ],
15772
16757
  {
15773
16758
  ok: true,
15774
16759
  walletRequestId: walletRequest.requestId,
15775
16760
  relay,
15776
16761
  ...relayOutputAliases(relay),
16762
+ relayRecoverySummary,
15777
16763
  request: sanitizeWalletRequestRecord(walletRequest),
15778
- nextAction: buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15779
- recommendedCommands: {
15780
- status: buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15781
- approve: buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)
15782
- }
16764
+ nextAction,
16765
+ recommendedCommands
15783
16766
  }
15784
16767
  );
15785
16768
  });
15786
16769
  request.command("relay-status").description("Inspect the relay status of a published wallet request, optionally waiting until approval is ready").requiredOption("--request-id <id>", "Wallet request id").requiredOption("--relay-url <url>", "Relay server base URL").option("--wait", "Poll until the encrypted relay approval is ready or the request expires").option("--timeout-seconds <seconds>", "How long to wait while polling relay status", "600").option("--interval-ms <milliseconds>", "How often to poll relay status while waiting", "2000").action(async (options) => {
15787
- const relay = options.wait ? await waitForRelayApprovalReady(options.relayUrl, options.requestId, {
15788
- timeoutMs: parsePositiveIntegerOption(options.timeoutSeconds, "--timeout-seconds", 600) * 1e3,
15789
- intervalMs: parsePositiveIntegerOption(options.intervalMs, "--interval-ms", 2e3)
15790
- }) : await fetchRelayStatus(options.relayUrl, options.requestId);
16770
+ const timeoutMs = parsePositiveIntegerOption(options.timeoutSeconds, "--timeout-seconds", 600) * 1e3;
16771
+ const intervalMs = parsePositiveIntegerOption(options.intervalMs, "--interval-ms", 2e3);
16772
+ const walletRequest = await loadWalletRequest(options.requestId);
16773
+ let relay;
16774
+ try {
16775
+ relay = options.wait ? await waitForRelayApprovalReady(options.relayUrl, options.requestId, {
16776
+ timeoutMs,
16777
+ intervalMs
16778
+ }) : await fetchRelayStatus(options.relayUrl, options.requestId);
16779
+ } catch (error) {
16780
+ if (options.wait && error instanceof Error && error.message.startsWith("Timed out waiting for relay approval after ")) {
16781
+ throw buildRelayApprovalTimeoutError({
16782
+ requestId: options.requestId,
16783
+ walletName: walletRequest?.walletName,
16784
+ relayUrl: options.relayUrl,
16785
+ timeoutMs,
16786
+ intervalMs
16787
+ });
16788
+ }
16789
+ throw error;
16790
+ }
16791
+ const followUp = await buildRelayStatusFollowUp({
16792
+ relay,
16793
+ relayUrl: options.relayUrl
16794
+ });
16795
+ const relayRecoverySummary = buildRelayRecoverySummary({
16796
+ requestId: relay.request_id,
16797
+ walletName: relay.request?.walletName || walletRequest?.walletName,
16798
+ relayUrl: options.relayUrl,
16799
+ relayStatus: relay.status,
16800
+ approvalReady: relay.approval_ready,
16801
+ nextAction: followUp.nextAction,
16802
+ recommendedCommands: followUp.recommendedCommands,
16803
+ relay
16804
+ });
15791
16805
  printResult(
15792
16806
  [
15793
16807
  ["status", relay.status],
15794
16808
  ["request", relay.request_id],
15795
16809
  ["approval ready", relay.approval_ready ? "yes" : "no"],
15796
- ["share url", relay.approval_url],
15797
- ["share-link base", relay.approval_url.replace(/\/[^/]+$/, "")],
15798
- ["status api base", `${relay.approval_url.replace(/\/r\/[^/]+$/, "")}/api/requests`],
16810
+ ["share url", relay.share_url],
16811
+ ["status url", relay.status_url],
16812
+ ["approval url", relay.approval_url],
16813
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
16814
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")],
15799
16815
  ["expires", relay.expires_at],
15800
- ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15801
- ...relay.approval_ready ? [[
15802
- "next approve",
15803
- buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)
15804
- ]] : []
16816
+ ...followUp.note ? [["note", followUp.note]] : [],
16817
+ ...Object.entries(followUp.recommendedCommands).map(
16818
+ ([label, command]) => [label, command]
16819
+ )
15805
16820
  ],
15806
16821
  {
15807
16822
  ok: true,
15808
16823
  walletRequestId: relay.request_id,
15809
16824
  relay,
15810
16825
  ...relayOutputAliases(relay),
15811
- nextAction: relay.approval_ready ? buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl) : buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15812
- recommendedCommands: {
15813
- status: buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15814
- ...relay.approval_ready ? {
15815
- approve: buildWalletRequestRelayApproveRecommendedCommand(
15816
- relay.request_id,
15817
- options.relayUrl
15818
- )
15819
- } : {}
15820
- }
16826
+ relayRecoverySummary,
16827
+ nextAction: followUp.nextAction,
16828
+ recommendedCommands: followUp.recommendedCommands,
16829
+ ...followUp.note ? { note: followUp.note } : {}
15821
16830
  }
15822
16831
  );
15823
16832
  });
@@ -15830,6 +16839,24 @@ function createWalletCommand(deps) {
15830
16839
  if (options.wait && !options.relayUrl) {
15831
16840
  throw new Error("--wait is only supported together with --relay-url.");
15832
16841
  }
16842
+ if (options.wait && options.relayUrl && !shouldJsonOutput()) {
16843
+ const relayStatus2 = await fetchRelayStatus(options.relayUrl, walletRequest.requestId);
16844
+ printRelayWaitGuidance({
16845
+ requestId: walletRequest.requestId,
16846
+ walletName: walletRequest.walletName,
16847
+ relay: relayStatus2,
16848
+ expiresAt: walletRequest.expiresAt,
16849
+ codeEntry: "provided",
16850
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
16851
+ walletRequest.requestId,
16852
+ options.relayUrl
16853
+ ),
16854
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
16855
+ walletRequest.requestId,
16856
+ options.relayUrl
16857
+ )
16858
+ });
16859
+ }
15833
16860
  const payload = options.payload ? parseJsonInput(options.payload) : decryptApprovedPayloadForWalletRequest(
15834
16861
  walletRequest,
15835
16862
  options.encryptedPayload ? parseJsonInput(options.encryptedPayload) : await fetchEncryptedRelayApprovalPayload(options.relayUrl, walletRequest.requestId, {
@@ -18137,7 +19164,16 @@ async function printWorkflowRunCommandResult(execution) {
18137
19164
  walletName: execution.result.walletName,
18138
19165
  nextAction: execution.result.nextCommand,
18139
19166
  chain: execution.result.plan.chain,
18140
- intent: execution.result.intent
19167
+ intent: execution.result.intent,
19168
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal)
19169
+ });
19170
+ const tokenDiscoverySummary2 = buildWorkflowTokenDiscoverySummary({
19171
+ walletName: execution.result.walletName,
19172
+ chain: execution.result.plan.chain,
19173
+ intent: execution.result.intent,
19174
+ nextAction: execution.result.nextCommand,
19175
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal),
19176
+ recommendedCommands: recommendedCommands2
18141
19177
  });
18142
19178
  printResult(
18143
19179
  prependWorkflowRequestId(
@@ -18163,6 +19199,7 @@ async function printWorkflowRunCommandResult(execution) {
18163
19199
  ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
18164
19200
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
18165
19201
  walletApproval: serializeWalletApproval(execution.walletApproval),
19202
+ tokenDiscoverySummary: tokenDiscoverySummary2,
18166
19203
  recommendedCommands: recommendedCommands2
18167
19204
  }
18168
19205
  );
@@ -18178,7 +19215,16 @@ async function printWorkflowRunCommandResult(execution) {
18178
19215
  walletName: execution.status.walletName,
18179
19216
  nextAction: execution.status.recommendedCommand,
18180
19217
  chain: execution.status.plan.chain,
18181
- intent: execution.status.intent
19218
+ intent: execution.status.intent,
19219
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
19220
+ });
19221
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
19222
+ walletName: execution.status.walletName,
19223
+ chain: execution.status.plan.chain,
19224
+ intent: execution.status.intent,
19225
+ nextAction: execution.status.recommendedCommand,
19226
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal),
19227
+ recommendedCommands
18182
19228
  });
18183
19229
  printResult(
18184
19230
  prependWorkflowRequestId(
@@ -18205,6 +19251,7 @@ async function printWorkflowRunCommandResult(execution) {
18205
19251
  ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
18206
19252
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
18207
19253
  walletApproval: serializeWalletApproval(execution.walletApproval),
19254
+ tokenDiscoverySummary,
18208
19255
  recommendedCommands
18209
19256
  }
18210
19257
  );
@@ -18450,6 +19497,7 @@ async function executeWorkflowAutoCommand(options, deps = resolveWorkflowCommand
18450
19497
  action: result ? result.stage : walletApproval?.stage ?? status.status,
18451
19498
  requestId: context.requestId,
18452
19499
  checkpointPersisted: Boolean(checkpoint),
19500
+ goal: context.goal,
18453
19501
  checkpoint,
18454
19502
  status,
18455
19503
  result,
@@ -18468,7 +19516,16 @@ async function printWorkflowAutoCommandResult(execution) {
18468
19516
  walletName: execution.status.walletName,
18469
19517
  nextAction,
18470
19518
  chain: execution.status.plan.chain,
18471
- intent: execution.status.intent
19519
+ intent: execution.status.intent,
19520
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal) ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
19521
+ });
19522
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
19523
+ walletName: execution.status.walletName,
19524
+ chain: execution.status.plan.chain,
19525
+ intent: execution.status.intent,
19526
+ nextAction,
19527
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal) ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal),
19528
+ recommendedCommands
18472
19529
  });
18473
19530
  const summaryLines = [
18474
19531
  ["source", execution.source],
@@ -18506,6 +19563,7 @@ async function printWorkflowAutoCommandResult(execution) {
18506
19563
  ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
18507
19564
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
18508
19565
  walletApproval: serializeWalletApproval(execution.walletApproval),
19566
+ tokenDiscoverySummary,
18509
19567
  recommendedCommands
18510
19568
  }
18511
19569
  );
@@ -18530,7 +19588,21 @@ function buildWorkflowCheckpointRecommendedCommands(checkpoint) {
18530
19588
  walletStatus: buildWalletStatusRecommendedCommand(checkpoint.walletName)
18531
19589
  };
18532
19590
  }
19591
+ function extractWorkflowGoalPaymasterMode(goal) {
19592
+ if (!goal || !("paymaster" in goal)) {
19593
+ return void 0;
19594
+ }
19595
+ return goal.paymaster?.mode;
19596
+ }
19597
+ function extractPaymasterModeFromCommand(command) {
19598
+ if (!command) {
19599
+ return void 0;
19600
+ }
19601
+ const match = command.match(/--paymaster-mode (none|sponsored|approval-based)\b/);
19602
+ return match?.[1];
19603
+ }
18533
19604
  function buildWorkflowRuntimeRecommendedCommands(input) {
19605
+ const paymasterMode = extractPaymasterModeFromCommand(input.nextAction) ?? input.paymasterMode;
18534
19606
  return {
18535
19607
  inspectDefaults: "zk-agent defaults",
18536
19608
  list: buildWorkflowListRecommendedCommand(),
@@ -18547,30 +19619,76 @@ function buildWorkflowRuntimeRecommendedCommands(input) {
18547
19619
  ...input.nextAction ? {
18548
19620
  nextAction: input.nextAction
18549
19621
  } : {},
18550
- ...input.chain && input.intent && workflowIntentSupportsTokenDiscovery2(input.intent) ? {
19622
+ ...input.chain && input.intent && workflowIntentSupportsTokenDiscovery3(input.intent) ? {
18551
19623
  ...input.walletName ? {
18552
19624
  discoverAssets: `zk-agent assets --wallet ${input.walletName}`,
18553
19625
  discoverOwnedTokens: `zk-agent tokens --wallet ${input.walletName} --owned`
18554
19626
  } : {},
18555
19627
  discoverTokens: `zk-agent tokens --chain ${input.chain}`,
18556
19628
  inspectToken: `zk-agent resolve-token --chain ${input.chain} --symbol <symbol>`
19629
+ } : {},
19630
+ ...input.chain && paymasterMode === "approval-based" ? {
19631
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(input.chain),
19632
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(input.chain)
18557
19633
  } : {}
18558
19634
  };
18559
19635
  }
18560
- function workflowIntentSupportsTokenDiscovery2(intent) {
19636
+ function workflowIntentSupportsTokenDiscovery3(intent) {
18561
19637
  return intent === "send-token" || intent === "swap" || intent === "bridge" || intent === "deposit" || intent === "withdraw";
18562
19638
  }
19639
+ function buildWorkflowTokenDiscoverySummary(input) {
19640
+ if (!input.recommendedCommands) {
19641
+ return void 0;
19642
+ }
19643
+ const hasTokenDiscovery = Boolean(input.recommendedCommands.discoverAssets) || Boolean(input.recommendedCommands.discoverOwnedTokens) || Boolean(input.recommendedCommands.discoverTokens) || Boolean(input.recommendedCommands.inspectToken) || Boolean(input.recommendedCommands.discoverPaymasterTokens) || Boolean(input.recommendedCommands.inspectPaymasterToken);
19644
+ if (!hasTokenDiscovery) {
19645
+ return void 0;
19646
+ }
19647
+ return {
19648
+ walletName: input.walletName || null,
19649
+ chain: input.chain || null,
19650
+ intent: input.intent || null,
19651
+ nextAction: input.nextAction || null,
19652
+ paymasterMode: input.paymasterMode || null,
19653
+ tokenizedIntent: input.intent ? workflowIntentSupportsTokenDiscovery3(input.intent) : false,
19654
+ includesAssetDiscovery: Boolean(input.recommendedCommands.discoverAssets),
19655
+ includesOwnedTokenDiscovery: Boolean(input.recommendedCommands.discoverOwnedTokens),
19656
+ includesChainTokenDiscovery: Boolean(input.recommendedCommands.discoverTokens),
19657
+ includesDirectTokenInspection: Boolean(input.recommendedCommands.inspectToken),
19658
+ includesPaymasterTokenDiscovery: Boolean(
19659
+ input.recommendedCommands.discoverPaymasterTokens
19660
+ ),
19661
+ includesPaymasterTokenInspection: Boolean(
19662
+ input.recommendedCommands.inspectPaymasterToken
19663
+ )
19664
+ };
19665
+ }
19666
+ function buildWorkflowTokenInputErrorSummary(input) {
19667
+ return {
19668
+ chain: input.chain || null,
19669
+ queryType: input.tokenAddress ? "address" : input.symbol ? "symbol" : null,
19670
+ query: input.tokenAddress || input.symbol || null,
19671
+ roleFilter: input.role || null,
19672
+ includesChainTokenDiscovery: Boolean(input.recommendedCommands.discoverTokens),
19673
+ includesDirectTokenInspection: Boolean(input.recommendedCommands.inspectToken),
19674
+ workflowHelp: input.recommendedCommands.workflowHelp
19675
+ };
19676
+ }
18563
19677
  function buildWorkflowPlanRecommendedCommands(plan) {
18564
19678
  return {
18565
19679
  inspectDefaults: "zk-agent defaults",
18566
19680
  next: plan.recommendedCommand,
18567
19681
  goal: plan.goalCommand,
18568
19682
  workflowHelp: "zk-agent workflow --help",
18569
- ...workflowIntentSupportsTokenDiscovery2(plan.intent) ? {
19683
+ ...workflowIntentSupportsTokenDiscovery3(plan.intent) ? {
18570
19684
  discoverAssets: buildAssetsRecommendedCommand(plan.walletName),
18571
19685
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(plan.walletName),
18572
19686
  discoverTokens: buildTokensRecommendedCommand(plan.chain),
18573
19687
  inspectToken: buildResolveTokenRecommendedCommand(plan.chain)
19688
+ } : {},
19689
+ ...plan.paymasterMode === "approval-based" ? {
19690
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(plan.chain),
19691
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(plan.chain)
18574
19692
  } : {}
18575
19693
  };
18576
19694
  }
@@ -18606,6 +19724,13 @@ function buildWorkflowTokenErrorRecommendedCommands(error) {
18606
19724
  }
18607
19725
  function printWorkflowTokenInputError(error) {
18608
19726
  const recommendedCommands = buildWorkflowTokenErrorRecommendedCommands(error);
19727
+ const tokenDiscoverySummary = buildWorkflowTokenInputErrorSummary({
19728
+ chain: typeof error.details?.chain === "string" ? error.details.chain : void 0,
19729
+ symbol: typeof error.details?.symbol === "string" ? error.details.symbol : void 0,
19730
+ tokenAddress: typeof error.details?.tokenAddress === "string" ? error.details.tokenAddress : void 0,
19731
+ role: typeof error.details?.role === "string" ? error.details.role : void 0,
19732
+ recommendedCommands
19733
+ });
18609
19734
  const lines = [["error", error.message], ["code", error.code]];
18610
19735
  if (typeof error.details?.suggestedAction === "string" && error.details.suggestedAction.length > 0) {
18611
19736
  lines.push(["suggested action", error.details.suggestedAction]);
@@ -18613,6 +19738,7 @@ function printWorkflowTokenInputError(error) {
18613
19738
  lines.push(...workflowFollowupLines(recommendedCommands));
18614
19739
  printResult(lines, {
18615
19740
  ...formatErrorPayload(error),
19741
+ tokenDiscoverySummary,
18616
19742
  recommendedCommands
18617
19743
  });
18618
19744
  process.exitCode = 1;
@@ -18818,6 +19944,7 @@ async function executeWorkflowRunCommand(options, deps = resolveWorkflowCommandD
18818
19944
  if (inspection.walletApproval?.stage === "request-created" || inspection.result.status === "blocked") {
18819
19945
  return {
18820
19946
  requestId: inspection.requestId,
19947
+ goal: context.goal,
18821
19948
  status: inspection.result,
18822
19949
  walletApproval: inspection.walletApproval,
18823
19950
  checkpoint: inspection.checkpoint
@@ -18855,6 +19982,7 @@ async function executeWorkflowRunCommand(options, deps = resolveWorkflowCommandD
18855
19982
  );
18856
19983
  return {
18857
19984
  requestId: context.requestId,
19985
+ goal: context.goal,
18858
19986
  result,
18859
19987
  walletApproval
18860
19988
  };
@@ -18931,14 +20059,13 @@ function assertWorkflowResumeReady(result) {
18931
20059
  }
18932
20060
  function buildWorkflowHelpText() {
18933
20061
  return [
18934
- "",
18935
- "Default workflow path:",
18936
- " Guided default:",
18937
- " zk-agent workflow auto --wallet main --intent <intent> [goal flags] --create-checkpoint --execute-when-ready",
18938
20062
  "",
18939
20063
  " Flagship native pay path:",
18940
20064
  " zk-agent workflow pay --wallet main --to <address> --amount <amount>",
18941
20065
  "",
20066
+ " Broader multi-intent guided path:",
20067
+ " zk-agent workflow auto --wallet main --intent <intent> [goal flags] --create-checkpoint --execute-when-ready",
20068
+ "",
18942
20069
  " Checkpointed execution:",
18943
20070
  " zk-agent workflow start --wallet main --intent <intent> [goal flags]",
18944
20071
  " zk-agent workflow status --request-id <id>",
@@ -18948,13 +20075,24 @@ function buildWorkflowHelpText() {
18948
20075
  " Funding-only step:",
18949
20076
  " zk-agent workflow fund --wallet main --amount <amount> --execute",
18950
20077
  "",
20078
+ " Token/discovery recovery path:",
20079
+ " zk-agent assets --wallet main",
20080
+ " zk-agent tokens --wallet main --owned",
20081
+ " zk-agent tokens --chain zksync-sepolia",
20082
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
20083
+ "",
20084
+ " Approval-based paymaster fee-token recovery:",
20085
+ " zk-agent tokens --chain zksync-sepolia --role paymaster-fee-token",
20086
+ " zk-agent resolve-token --chain zksync-sepolia --symbol <symbol> --role paymaster-fee-token",
20087
+ " zk-agent defaults",
20088
+ "",
18951
20089
  " Lower-level one-shot escape hatch:",
18952
20090
  " zk-agent workflow run --wallet main --intent <intent> [goal flags]"
18953
20091
  ].join("\n");
18954
20092
  }
18955
20093
  var WORKFLOW_HELP_COMMAND_ORDER = [
18956
- "auto",
18957
20094
  "pay",
20095
+ "auto",
18958
20096
  "start",
18959
20097
  "status",
18960
20098
  "next",
@@ -18983,7 +20121,7 @@ function applyWorkflowHelpCommandOrder(workflow) {
18983
20121
  function createWorkflowCommand(deps) {
18984
20122
  const resolvedDeps = resolveWorkflowCommandDeps(deps);
18985
20123
  const workflow = new Command10("workflow").description(
18986
- "Build a higher-level CLI workflow for a stored wallet and a concrete action intent"
20124
+ "Plan, persist, and execute higher-level wallet workflows"
18987
20125
  );
18988
20126
  workflow.addHelpText("after", buildWorkflowHelpText());
18989
20127
  workflow.command("plan").description("Plan the prerequisite and execution steps for one concrete wallet workflow").requiredOption(
@@ -18995,15 +20133,27 @@ function createWorkflowCommand(deps) {
18995
20133
  ).option("--to-chain <chain>", "Optional destination chain override for bridge workflows").option("--paymaster-mode <mode>", "none, sponsored, or approval-based").option("--paymaster-address <address>", "Explicit paymaster contract address override").option("--paymaster-token <address>", "ERC-20 token address for approval-based paymaster mode").action(
18996
20134
  async (options) => {
18997
20135
  const intent = parseWorkflowIntent(options.intent);
20136
+ const paymasterInput = resolveWorkflowPaymasterInput(options);
18998
20137
  const { inspection, plan } = await loadWorkflowPlanState(
18999
20138
  options.wallet,
19000
20139
  intent,
19001
20140
  parseWorkflowSwapProtocol(options.protocol),
19002
20141
  options.toChain,
19003
- resolveWorkflowPaymasterInput(options),
20142
+ paymasterInput,
19004
20143
  resolvedDeps
19005
20144
  );
19006
- const recommendedCommands = buildWorkflowPlanRecommendedCommands(plan);
20145
+ const recommendedCommands = buildWorkflowPlanRecommendedCommands({
20146
+ ...plan,
20147
+ paymasterMode: paymasterInput?.mode
20148
+ });
20149
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
20150
+ walletName: plan.walletName,
20151
+ chain: plan.chain,
20152
+ intent: plan.intent,
20153
+ paymasterMode: paymasterInput?.mode,
20154
+ nextAction: plan.recommendedCommand,
20155
+ recommendedCommands
20156
+ });
19007
20157
  const agentProfile = await loadWorkflowAgentProfile(plan.walletName);
19008
20158
  const agentFollowup = buildAgentFollowup(agentProfile, {
19009
20159
  walletName: plan.walletName,
@@ -19021,6 +20171,7 @@ function createWorkflowCommand(deps) {
19021
20171
  agentFollowup,
19022
20172
  inspection,
19023
20173
  plan,
20174
+ tokenDiscoverySummary,
19024
20175
  recommendedCommands
19025
20176
  }
19026
20177
  );
@@ -19221,7 +20372,16 @@ function createWorkflowCommand(deps) {
19221
20372
  walletName: inspection.result.walletName,
19222
20373
  nextAction: inspection.result.recommendedCommand,
19223
20374
  chain: inspection.result.plan.chain,
19224
- intent: inspection.result.intent
20375
+ intent: inspection.result.intent,
20376
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal)
20377
+ });
20378
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
20379
+ walletName: inspection.result.walletName,
20380
+ chain: inspection.result.plan.chain,
20381
+ intent: inspection.result.intent,
20382
+ nextAction: inspection.result.recommendedCommand,
20383
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal),
20384
+ recommendedCommands
19225
20385
  });
19226
20386
  const agentProfile = await loadWorkflowAgentProfile(inspection.result.walletName);
19227
20387
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -19253,6 +20413,7 @@ function createWorkflowCommand(deps) {
19253
20413
  ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19254
20414
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19255
20415
  walletApproval: serializeWalletApproval(inspection.walletApproval),
20416
+ tokenDiscoverySummary,
19256
20417
  recommendedCommands
19257
20418
  }
19258
20419
  );
@@ -19272,7 +20433,16 @@ function createWorkflowCommand(deps) {
19272
20433
  walletName: inspection.result.walletName,
19273
20434
  nextAction: nextCommand,
19274
20435
  chain: inspection.result.plan.chain,
19275
- intent: inspection.result.intent
20436
+ intent: inspection.result.intent,
20437
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal)
20438
+ });
20439
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
20440
+ walletName: inspection.result.walletName,
20441
+ chain: inspection.result.plan.chain,
20442
+ intent: inspection.result.intent,
20443
+ nextAction: nextCommand,
20444
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal),
20445
+ recommendedCommands
19276
20446
  });
19277
20447
  const agentProfile = await loadWorkflowAgentProfile(inspection.result.walletName);
19278
20448
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -19317,6 +20487,7 @@ function createWorkflowCommand(deps) {
19317
20487
  ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19318
20488
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19319
20489
  walletApproval: serializeWalletApproval(inspection.walletApproval),
20490
+ tokenDiscoverySummary,
19320
20491
  recommendedCommands
19321
20492
  }
19322
20493
  );
@@ -19337,7 +20508,16 @@ function createWorkflowCommand(deps) {
19337
20508
  walletName: inspection.result.walletName,
19338
20509
  nextAction: inspection.result.recommendedCommand,
19339
20510
  chain: inspection.result.plan.chain,
19340
- intent: inspection.result.intent
20511
+ intent: inspection.result.intent,
20512
+ paymasterMode: inspection.walletApproval.request.requestedPaymasterMode
20513
+ });
20514
+ const tokenDiscoverySummary2 = buildWorkflowTokenDiscoverySummary({
20515
+ walletName: inspection.result.walletName,
20516
+ chain: inspection.result.plan.chain,
20517
+ intent: inspection.result.intent,
20518
+ nextAction: inspection.result.recommendedCommand,
20519
+ paymasterMode: inspection.walletApproval.request.requestedPaymasterMode,
20520
+ recommendedCommands: recommendedCommands2
19341
20521
  });
19342
20522
  const agentProfile2 = await loadWorkflowAgentProfile(inspection.result.walletName);
19343
20523
  const agentFollowup2 = buildAgentFollowup(agentProfile2, {
@@ -19369,6 +20549,7 @@ function createWorkflowCommand(deps) {
19369
20549
  ...workflowWalletApprovalRelayAliases(inspection.walletApproval.relay),
19370
20550
  walletApprovalRecommendedCommands: inspection.walletApproval.recommendedCommands,
19371
20551
  walletApproval: serializeWalletApproval(inspection.walletApproval),
20552
+ tokenDiscoverySummary: tokenDiscoverySummary2,
19372
20553
  recommendedCommands: recommendedCommands2
19373
20554
  }
19374
20555
  );
@@ -19388,7 +20569,16 @@ function createWorkflowCommand(deps) {
19388
20569
  walletName: execution.status.walletName,
19389
20570
  nextAction: execution.status.recommendedCommand,
19390
20571
  chain: execution.status.plan.chain,
19391
- intent: execution.status.intent
20572
+ intent: execution.status.intent,
20573
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
20574
+ });
20575
+ const tokenDiscoverySummary2 = buildWorkflowTokenDiscoverySummary({
20576
+ walletName: execution.status.walletName,
20577
+ chain: execution.status.plan.chain,
20578
+ intent: execution.status.intent,
20579
+ nextAction: execution.status.recommendedCommand,
20580
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal),
20581
+ recommendedCommands: recommendedCommands2
19392
20582
  });
19393
20583
  const agentProfile2 = await loadWorkflowAgentProfile(execution.status.walletName);
19394
20584
  const agentFollowup2 = buildAgentFollowup(agentProfile2, {
@@ -19420,6 +20610,7 @@ function createWorkflowCommand(deps) {
19420
20610
  ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
19421
20611
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
19422
20612
  walletApproval: serializeWalletApproval(execution.walletApproval),
20613
+ tokenDiscoverySummary: tokenDiscoverySummary2,
19423
20614
  recommendedCommands: recommendedCommands2
19424
20615
  }
19425
20616
  );
@@ -19430,7 +20621,16 @@ function createWorkflowCommand(deps) {
19430
20621
  walletName: execution.result.walletName,
19431
20622
  nextAction: execution.result.nextCommand,
19432
20623
  chain: execution.result.plan.chain,
19433
- intent: execution.result.intent
20624
+ intent: execution.result.intent,
20625
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal)
20626
+ });
20627
+ const tokenDiscoverySummary = buildWorkflowTokenDiscoverySummary({
20628
+ walletName: execution.result.walletName,
20629
+ chain: execution.result.plan.chain,
20630
+ intent: execution.result.intent,
20631
+ nextAction: execution.result.nextCommand,
20632
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal),
20633
+ recommendedCommands
19434
20634
  });
19435
20635
  const agentProfile = await loadWorkflowAgentProfile(execution.result.walletName);
19436
20636
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -19462,6 +20662,7 @@ function createWorkflowCommand(deps) {
19462
20662
  ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19463
20663
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19464
20664
  walletApproval: serializeWalletApproval(inspection.walletApproval),
20665
+ tokenDiscoverySummary,
19465
20666
  recommendedCommands
19466
20667
  }
19467
20668
  );
@@ -19497,16 +20698,24 @@ function createWorkflowCommand(deps) {
19497
20698
  function buildDefaultOperatorPathHelpText() {
19498
20699
  return [
19499
20700
  "",
19500
- "Default local-first operator path:",
20701
+ "Public entrypoints:",
20702
+ " Agent harness: npx skills add https://github.com/AgiWeb3/zk-agent-cli",
20703
+ " One-shot CLI: npx zk-agent-cli --help",
20704
+ " Global CLI: npm install -g zk-agent-cli",
20705
+ "",
20706
+ "Canonical terminal path:",
19501
20707
  " zk-agent setup",
19502
20708
  " zk-agent next",
19503
20709
  " zk-agent wallet create --await-local",
19504
20710
  " zk-agent next",
19505
20711
  ` ${buildWorkflowPayRecommendedCommand("main")}`,
19506
20712
  "",
20713
+ "No custom .env is required for setup, next, or wallet create/reapprove request generation.",
20714
+ "Add RPC env vars later, before live reads or broadcasts.",
20715
+ "",
19507
20716
  "Use `zk-agent next --request-id <id>` to continue a stored workflow checkpoint.",
19508
- "Use `zk-agent relay inspect --relay-url <url>` and `zk-agent wallet --help` for the hosted remote-approval path.",
19509
- "Use `zk-agent wallet --help` for bootstrap/reapproval details and `zk-agent workflow --help` once the intent is known."
20717
+ "Use `zk-agent relay inspect --relay-url <url>` plus `zk-agent wallet create|reapprove --relay-url <url> --wait-relay --prompt-code` when the browser is not colocated.",
20718
+ "Use `zk-agent wallet --help` for wallet recovery details and `zk-agent workflow --help` when the intent is broader than the flagship native-send path."
19510
20719
  ].join("\n");
19511
20720
  }
19512
20721
  var ROOT_HELP_COMMAND_ORDER = [
@@ -19547,7 +20756,9 @@ function applyRootHelpCommandOrder(program) {
19547
20756
  program.commands = sortedCommands;
19548
20757
  }
19549
20758
  function createProgram() {
19550
- const program = new Command11().name("zk-agent").description("zkSync and ZK Stack CLI scaffold for agent workflows").showHelpAfterError().option("--json", "Force JSON output for agent harnesses", false).hook("preAction", (thisCommand) => {
20759
+ const program = new Command11().name("zk-agent").description(
20760
+ "Local-first zkSync Era CLI for wallet approval, workflow execution, and hosted relay recovery"
20761
+ ).showHelpAfterError().option("--json", "Force JSON output for agent harnesses", false).hook("preAction", (thisCommand) => {
19551
20762
  if (thisCommand.optsWithGlobals().json) process.env.ZK_AGENT_OUTPUT = "json";
19552
20763
  });
19553
20764
  program.addCommand(createInitCommand());