zk-agent-cli 0.1.0-beta.6 → 0.1.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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);
@@ -7758,6 +7762,18 @@ function buildTopLevelNextRecommendedCommand(requestId, paymasterMode) {
7758
7762
  function buildWalletCreateRecommendedCommand() {
7759
7763
  return "zk-agent wallet create --await-local";
7760
7764
  }
7765
+ function buildRelayInspectRecommendedCommand(relayUrl = "<url>") {
7766
+ return `zk-agent relay inspect --relay-url ${relayUrl}`;
7767
+ }
7768
+ function buildWalletCreateRemoteRecommendedCommand(relayUrl = "<url>", paymasterMode, walletName = "main", accountKind = "smart-account") {
7769
+ const command = [
7770
+ "zk-agent wallet create",
7771
+ walletName !== "main" ? `--name ${walletName}` : "",
7772
+ accountKind !== "smart-account" ? `--account-kind ${accountKind}` : "",
7773
+ `--relay-url ${relayUrl} --wait-relay --prompt-code`
7774
+ ].filter(Boolean).join(" ");
7775
+ return appendPaymasterMode(command, paymasterMode);
7776
+ }
7761
7777
  function buildWalletListRecommendedCommand() {
7762
7778
  return "zk-agent wallet list";
7763
7779
  }
@@ -7784,6 +7800,12 @@ function buildResolveTokenRecommendedCommand(chain, symbol, role, source) {
7784
7800
  const withRole = role ? `${command} --role ${role}` : command;
7785
7801
  return source ? `${withRole} --source ${source}` : withRole;
7786
7802
  }
7803
+ function buildPaymasterFeeTokensRecommendedCommand(chain) {
7804
+ return buildTokensRecommendedCommand(chain, void 0, "paymaster-fee-token");
7805
+ }
7806
+ function buildPaymasterFeeTokenResolveRecommendedCommand(chain, symbol) {
7807
+ return buildResolveTokenRecommendedCommand(chain, symbol, "paymaster-fee-token");
7808
+ }
7787
7809
  function buildDiscoveryRecommendedCommands(input) {
7788
7810
  return {
7789
7811
  inspectDefaults: buildDefaultsRecommendedCommand(),
@@ -7814,6 +7836,9 @@ function buildDiscoveryRecommendedCommands(input) {
7814
7836
  function buildWalletReapproveRecommendedCommand(walletName) {
7815
7837
  return `zk-agent wallet reapprove --name ${walletName} --await-local`;
7816
7838
  }
7839
+ function buildWalletReapproveRemoteRecommendedCommand(walletName, relayUrl = "<url>") {
7840
+ return `zk-agent wallet reapprove --name ${walletName} --relay-url ${relayUrl} --wait-relay --prompt-code`;
7841
+ }
7817
7842
  function buildWalletNextRecommendedCommand(walletName) {
7818
7843
  return `zk-agent wallet next --name ${walletName}`;
7819
7844
  }
@@ -8507,9 +8532,15 @@ function workflowFollowupLines(recommendedCommands) {
8507
8532
  if (recommendedCommands.discoverOwnedTokens) {
8508
8533
  lines.push(["discover owned tokens", recommendedCommands.discoverOwnedTokens]);
8509
8534
  }
8535
+ if (recommendedCommands.discoverPaymasterTokens) {
8536
+ lines.push(["discover paymaster tokens", recommendedCommands.discoverPaymasterTokens]);
8537
+ }
8510
8538
  if (recommendedCommands.discoverTokens) {
8511
8539
  lines.push(["discover tokens", recommendedCommands.discoverTokens]);
8512
8540
  }
8541
+ if (recommendedCommands.inspectPaymasterToken) {
8542
+ lines.push(["inspect paymaster token", recommendedCommands.inspectPaymasterToken]);
8543
+ }
8513
8544
  if (recommendedCommands.inspectToken) {
8514
8545
  lines.push(["inspect token", recommendedCommands.inspectToken]);
8515
8546
  }
@@ -9214,7 +9245,25 @@ function createBalancesCommand(deps) {
9214
9245
  }
9215
9246
  function createAssetsCommand(deps) {
9216
9247
  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) => {
9248
+ return new Command("assets").description("Fetch the preferred single-chain asset view with native balance plus registry-backed ERC-20 holdings").addHelpText(
9249
+ "after",
9250
+ [
9251
+ "",
9252
+ "Discovery asset path:",
9253
+ " Preferred single-chain asset entrypoint:",
9254
+ " zk-agent assets --wallet main",
9255
+ "",
9256
+ " Narrower owned ERC-20 registry subset:",
9257
+ " zk-agent tokens --wallet main --owned",
9258
+ "",
9259
+ " Symbol-first token lookup before a tokenized command:",
9260
+ " zk-agent tokens --chain zksync-sepolia --symbol USDC",
9261
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
9262
+ "",
9263
+ " For the machine-readable registry/default catalog:",
9264
+ " zk-agent defaults"
9265
+ ].join("\n")
9266
+ ).option("--wallet <name>", "Wallet name", "main").option("--chain <chain>", "Single chain override").action(async (options) => {
9218
9267
  const walletName = options.wallet;
9219
9268
  const wallet = await resolvedDeps.loadWallet(walletName);
9220
9269
  if (!wallet) throw new Error(`Wallet not found: ${walletName}`);
@@ -9912,11 +9961,35 @@ function createPlannedCommands() {
9912
9961
 
9913
9962
  // src/commands/setup.ts
9914
9963
  import { Command as Command2 } from "commander";
9964
+ function buildSetupHelpText() {
9965
+ return [
9966
+ "",
9967
+ "What setup does:",
9968
+ " Writes the local default chain and connector URL used by the first-run path.",
9969
+ "",
9970
+ "After setup, stay on the canonical local-first path:",
9971
+ " zk-agent next",
9972
+ " zk-agent wallet create --await-local",
9973
+ " zk-agent next",
9974
+ "",
9975
+ "If the browser is not colocated with this terminal, switch at the wallet step:",
9976
+ " zk-agent relay inspect --relay-url <url>",
9977
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
9978
+ " zk-agent next",
9979
+ "",
9980
+ "Environment note:",
9981
+ " No custom .env is required for setup, next, or wallet request creation.",
9982
+ " Add RPC env vars later, before live reads or broadcasts."
9983
+ ].join("\n");
9984
+ }
9915
9985
  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) => {
9986
+ 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
9987
  const recommendedCommands = {
9988
+ next: buildTopLevelNextRecommendedCommand(),
9918
9989
  inspectDefaults: buildDefaultsRecommendedCommand(),
9919
9990
  createWallet: buildWalletCreateRecommendedCommand(),
9991
+ relayInspect: buildRelayInspectRecommendedCommand(),
9992
+ createWalletRemote: buildWalletCreateRemoteRecommendedCommand(),
9920
9993
  afterWalletApproval: buildTopLevelNextRecommendedCommand()
9921
9994
  };
9922
9995
  const existing = await loadProjectConfig();
@@ -9926,8 +9999,11 @@ function createInitCommand() {
9926
9999
  ["status", "Config already exists. Re-run with --force to overwrite."],
9927
10000
  ["default chain", existing.defaultChain],
9928
10001
  ["connector", existing.connectorUrl],
10002
+ ["next", recommendedCommands.next],
9929
10003
  ["inspect defaults", recommendedCommands.inspectDefaults],
9930
- ["create wallet", recommendedCommands.createWallet],
10004
+ ["create wallet (local)", recommendedCommands.createWallet],
10005
+ ["relay inspect", recommendedCommands.relayInspect],
10006
+ ["create wallet (remote)", recommendedCommands.createWalletRemote],
9931
10007
  ["after approval", recommendedCommands.afterWalletApproval]
9932
10008
  ],
9933
10009
  {
@@ -9952,8 +10028,11 @@ function createInitCommand() {
9952
10028
  ["status", "Config saved"],
9953
10029
  ["default chain", config.defaultChain],
9954
10030
  ["connector", config.connectorUrl],
10031
+ ["next", recommendedCommands.next],
9955
10032
  ["inspect defaults", recommendedCommands.inspectDefaults],
9956
- ["create wallet", recommendedCommands.createWallet],
10033
+ ["create wallet (local)", recommendedCommands.createWallet],
10034
+ ["relay inspect", recommendedCommands.relayInspect],
10035
+ ["create wallet (remote)", recommendedCommands.createWalletRemote],
9957
10036
  ["after approval", recommendedCommands.afterWalletApproval]
9958
10037
  ],
9959
10038
  { ok: true, config, recommendedCommands }
@@ -10383,9 +10462,17 @@ function buildTopLevelWorkflowRecommendedCommands(input) {
10383
10462
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(input.walletName),
10384
10463
  discoverTokens: buildTokensRecommendedCommand(input.chain),
10385
10464
  inspectToken: buildResolveTokenRecommendedCommand(input.chain)
10465
+ } : {},
10466
+ ...input.paymasterMode === "approval-based" ? {
10467
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(input.chain),
10468
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(input.chain)
10386
10469
  } : {}
10387
10470
  };
10388
10471
  }
10472
+ function extractCheckpointPaymasterMode(checkpoint) {
10473
+ if (!("paymaster" in checkpoint.goal)) return void 0;
10474
+ return checkpoint.goal.paymaster?.mode;
10475
+ }
10389
10476
  function resolveNextCommandDeps(deps) {
10390
10477
  return {
10391
10478
  provider: deps?.provider ?? defaultProvider,
@@ -10409,7 +10496,15 @@ function buildNextHelpText() {
10409
10496
  return [
10410
10497
  "",
10411
10498
  "Use `next` as the product entrypoint:",
10412
- " Fresh operator routing:",
10499
+ " Fresh local-first routing:",
10500
+ " zk-agent setup",
10501
+ " zk-agent next",
10502
+ " zk-agent wallet create --await-local",
10503
+ " zk-agent next",
10504
+ "",
10505
+ " If the browser is remote, switch at the wallet step instead of waiting for a local callback:",
10506
+ " zk-agent relay inspect --relay-url <url>",
10507
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
10413
10508
  " zk-agent next",
10414
10509
  "",
10415
10510
  " Continue a stored workflow checkpoint:",
@@ -10418,6 +10513,9 @@ function buildNextHelpText() {
10418
10513
  " Stay on the wallet layer only when you need wallet-specific remediation:",
10419
10514
  " zk-agent wallet next --name main",
10420
10515
  "",
10516
+ " Switch to the hosted remote-approval path only when the browser is not colocated:",
10517
+ " zk-agent wallet --help",
10518
+ "",
10421
10519
  " Stay on the workflow layer only when you already have an explicit workflow or checkpoint:",
10422
10520
  " zk-agent workflow next --request-id <id>"
10423
10521
  ].join("\n");
@@ -10464,7 +10562,8 @@ function createNextCommand(deps) {
10464
10562
  walletName: wallet2.walletName,
10465
10563
  nextAction: nextCommand2,
10466
10564
  chain: result.plan.chain,
10467
- intent: result.intent
10565
+ intent: result.intent,
10566
+ paymasterMode: extractCheckpointPaymasterMode(updatedCheckpoint)
10468
10567
  });
10469
10568
  const workflowAgentProfile = await loadAgentIdentitySummary(wallet2.walletName);
10470
10569
  const agentFollowup2 = buildAgentFollowup(workflowAgentProfile, {
@@ -10509,6 +10608,7 @@ function createNextCommand(deps) {
10509
10608
  if (!config) {
10510
10609
  const recommendedCommands2 = {
10511
10610
  setup: buildSetupCommand(),
10611
+ afterSetup: buildTopLevelNextRecommendedCommand2(),
10512
10612
  inspectDefaults: buildDefaultsRecommendedCommand()
10513
10613
  };
10514
10614
  printResult(
@@ -10517,6 +10617,7 @@ function createNextCommand(deps) {
10517
10617
  ...agentProfileLines(agentProfile),
10518
10618
  ...agentFollowupLines(defaultAgentFollowup),
10519
10619
  ["next", recommendedCommands2.setup],
10620
+ ["after setup", recommendedCommands2.afterSetup],
10520
10621
  ["inspect defaults", recommendedCommands2.inspectDefaults]
10521
10622
  ]),
10522
10623
  {
@@ -10538,6 +10639,11 @@ function createNextCommand(deps) {
10538
10639
  buildWalletCreateRecommendedCommand(),
10539
10640
  paymasterMode
10540
10641
  ),
10642
+ relayInspect: buildRelayInspectRecommendedCommand(),
10643
+ createWalletRemote: buildWalletCreateRemoteRecommendedCommand(
10644
+ "<url>",
10645
+ paymasterMode
10646
+ ),
10541
10647
  afterApproval: appendPaymasterMode2(buildTopLevelNextRecommendedCommand2(), paymasterMode),
10542
10648
  inspectDefaults: buildDefaultsRecommendedCommand()
10543
10649
  };
@@ -10549,6 +10655,8 @@ function createNextCommand(deps) {
10549
10655
  ...agentProfileLines(agentProfile),
10550
10656
  ...agentFollowupLines(defaultAgentFollowup),
10551
10657
  ["next", recommendedCommands2.createWallet],
10658
+ ["relay inspect", recommendedCommands2.relayInspect],
10659
+ ["remote fallback", recommendedCommands2.createWalletRemote],
10552
10660
  ["after approval", recommendedCommands2.afterApproval],
10553
10661
  ["inspect defaults", recommendedCommands2.inspectDefaults]
10554
10662
  ]),
@@ -10602,6 +10710,10 @@ function createNextCommand(deps) {
10602
10710
  walletStatus: buildWalletStatusRecommendedCommand(wallet.walletName),
10603
10711
  discoverAssets: buildAssetsRecommendedCommand(wallet.walletName),
10604
10712
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(wallet.walletName),
10713
+ ...paymasterMode === "approval-based" ? {
10714
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(wallet.chain),
10715
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(wallet.chain)
10716
+ } : {},
10605
10717
  discoverTokens: buildTokensRecommendedCommand(wallet.chain),
10606
10718
  inspectToken: buildResolveTokenRecommendedCommand(wallet.chain),
10607
10719
  workflowPay,
@@ -10617,7 +10729,9 @@ function createNextCommand(deps) {
10617
10729
  ...summary.recommendedCommand ? [] : [["next", workflowPay]],
10618
10730
  ["discover assets", recommendedCommands.discoverAssets],
10619
10731
  ["discover owned tokens", recommendedCommands.discoverOwnedTokens],
10732
+ ...recommendedCommands.discoverPaymasterTokens ? [["discover paymaster tokens", recommendedCommands.discoverPaymasterTokens]] : [],
10620
10733
  ["discover tokens", recommendedCommands.discoverTokens],
10734
+ ...recommendedCommands.inspectPaymasterToken ? [["inspect paymaster token", recommendedCommands.inspectPaymasterToken]] : [],
10621
10735
  ["inspect token", recommendedCommands.inspectToken],
10622
10736
  ["inspect defaults", recommendedCommands.inspectDefaults]
10623
10737
  ]),
@@ -10734,6 +10848,21 @@ function createAgentCommand() {
10734
10848
  agent.addHelpText(
10735
10849
  "after",
10736
10850
  [
10851
+ "",
10852
+ " Agent identity path:",
10853
+ " zk-agent agent status",
10854
+ ' zk-agent agent set --name "SED Operator" --wallet main',
10855
+ " zk-agent agent show",
10856
+ "",
10857
+ " Portable local profile management:",
10858
+ " zk-agent agent export",
10859
+ " zk-agent agent import --payload @agent-profile.json --overwrite",
10860
+ "",
10861
+ " Remove the saved local profile:",
10862
+ " zk-agent agent clear",
10863
+ "",
10864
+ " This profile is optional. Wallet approval and workflow execution still work",
10865
+ " without a saved local agent profile.",
10737
10866
  "",
10738
10867
  "Examples:",
10739
10868
  " zk-agent agent status",
@@ -11380,7 +11509,24 @@ function buildDefaultsLines(input) {
11380
11509
  return lines;
11381
11510
  }
11382
11511
  function createDefaultsCommand() {
11383
- return new Command5("defaults").description("Show the machine-readable registry of supported, validated, experimental, and manually configured defaults").action(async () => {
11512
+ return new Command5("defaults").description("Show the machine-readable registry of supported, validated, experimental, and manually configured defaults").addHelpText(
11513
+ "after",
11514
+ [
11515
+ "",
11516
+ "Discovery defaults path:",
11517
+ " Use `defaults` as the machine-readable registry escape hatch for:",
11518
+ " - validated or fallback swap / bridge paths",
11519
+ " - tracked token roles and source order",
11520
+ " - paymaster defaults and supported modes",
11521
+ "",
11522
+ " For wallet-scoped asset discovery, prefer:",
11523
+ " zk-agent assets --wallet main",
11524
+ "",
11525
+ " For symbol-first token discovery, prefer:",
11526
+ " zk-agent tokens --chain zksync-sepolia",
11527
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC"
11528
+ ].join("\n")
11529
+ ).action(async () => {
11384
11530
  const defaults = loadValidatedDefaults();
11385
11531
  const localTokenRegistry = listLocalTokenRegistryEntries();
11386
11532
  const tokenRegistrySources = describeDefaultTokenRegistrySources();
@@ -11440,7 +11586,27 @@ async function resolveActiveChain(options, deps) {
11440
11586
  }
11441
11587
  function createResolveTokenCommand(deps) {
11442
11588
  const resolvedDeps = resolveResolveTokenCommandDeps(deps);
11443
- 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(
11589
+ return new Command6("resolve-token").description("Resolve a token symbol or address against the configured local-first token registry").addHelpText(
11590
+ "after",
11591
+ [
11592
+ "",
11593
+ "Resolve-token path:",
11594
+ " Symbol-first resolution on one active chain:",
11595
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
11596
+ "",
11597
+ " Use the stored wallet to infer the active chain:",
11598
+ " zk-agent resolve-token --wallet main --symbol USDC",
11599
+ "",
11600
+ " Use broader chain discovery before resolution when you still need the candidate set:",
11601
+ " zk-agent tokens --chain zksync-sepolia",
11602
+ "",
11603
+ " Use the wallet asset entrypoint when the real question is balances/holdings:",
11604
+ " zk-agent assets --wallet main",
11605
+ "",
11606
+ " Use the registry/default catalog when you need tracked roles or source order:",
11607
+ " zk-agent defaults"
11608
+ ].join("\n")
11609
+ ).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(
11444
11610
  "--role <role>",
11445
11611
  `Optional defaults-registry role filter: ${REGISTRY_TOKEN_ROLES.join(", ")}`
11446
11612
  ).option(
@@ -11643,7 +11809,29 @@ function ownedTokenSummaryLines2(summary) {
11643
11809
  }
11644
11810
  function createTokensCommand(deps) {
11645
11811
  const resolvedDeps = resolveTokensCommandDeps(deps);
11646
- 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(
11812
+ 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(
11813
+ "after",
11814
+ [
11815
+ "",
11816
+ "Discovery token path:",
11817
+ " Start with the preferred wallet asset view when you need balances plus tracked ERC-20 holdings:",
11818
+ " zk-agent assets --wallet main",
11819
+ "",
11820
+ " Use the narrower owned ERC-20 registry subset when you only want held tokens:",
11821
+ " zk-agent tokens --wallet main --owned",
11822
+ "",
11823
+ " Use chain-scoped discovery before choosing a token address:",
11824
+ " zk-agent tokens --chain zksync-sepolia",
11825
+ " zk-agent tokens --chain zksync-sepolia --symbol USDC",
11826
+ " zk-agent tokens --chain zksync-sepolia --role paymaster-fee-token",
11827
+ "",
11828
+ " For one direct token-resolution check:",
11829
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
11830
+ "",
11831
+ " For the full defaults/registry catalog:",
11832
+ " zk-agent defaults"
11833
+ ].join("\n")
11834
+ ).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(
11647
11835
  "--role <role>",
11648
11836
  `Optional defaults-registry role filter: ${REGISTRY_TOKEN_ROLES.join(", ")}`
11649
11837
  ).option(
@@ -11829,21 +12017,157 @@ import { Command as Command8 } from "commander";
11829
12017
  // src/lib/relay.ts
11830
12018
  import { createServer } from "node:http";
11831
12019
  import fs7 from "node:fs";
11832
- import path6 from "node:path";
12020
+ import path7 from "node:path";
11833
12021
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12022
+
12023
+ // src/lib/http.ts
12024
+ import { spawn } from "node:child_process";
12025
+ import { mkdtemp, readFile as readFile2, rm as rm2 } from "node:fs/promises";
12026
+ import os2 from "node:os";
12027
+ import path6 from "node:path";
12028
+ function isDnsResolutionFailure(error) {
12029
+ if (!error || typeof error !== "object") {
12030
+ return false;
12031
+ }
12032
+ const cause = "cause" in error ? error.cause : void 0;
12033
+ if (!cause || typeof cause !== "object") {
12034
+ return false;
12035
+ }
12036
+ const code = "code" in cause ? cause.code : void 0;
12037
+ return code === "ENOTFOUND" || code === "EAI_AGAIN";
12038
+ }
12039
+ function isHttpUrl(value) {
12040
+ return value.startsWith("http://") || value.startsWith("https://");
12041
+ }
12042
+ function parseCurlHeaders(rawHeaders) {
12043
+ const blocks = rawHeaders.replace(/\r\n/g, "\n").split(/\n{2,}/).map((block) => block.trim()).filter((block) => block.startsWith("HTTP/"));
12044
+ const lastBlock = blocks.at(-1);
12045
+ if (!lastBlock) {
12046
+ throw new Error("curl fallback did not emit an HTTP response header block.");
12047
+ }
12048
+ const [statusLine, ...headerLines] = lastBlock.split("\n");
12049
+ const status = Number.parseInt(statusLine.split(/\s+/)[1] || "", 10);
12050
+ if (!Number.isInteger(status)) {
12051
+ throw new Error(`curl fallback emitted an invalid status line: ${statusLine}`);
12052
+ }
12053
+ const headers = new Headers();
12054
+ for (const line of headerLines) {
12055
+ const separator = line.indexOf(":");
12056
+ if (separator <= 0) continue;
12057
+ const key = line.slice(0, separator).trim();
12058
+ const value = line.slice(separator + 1).trim();
12059
+ if (key) {
12060
+ headers.append(key, value);
12061
+ }
12062
+ }
12063
+ return { status, headers };
12064
+ }
12065
+ async function runCurlRequest(url, options) {
12066
+ const tempDir = await mkdtemp(path6.join(os2.tmpdir(), "zk-agent-http-"));
12067
+ const headersPath = path6.join(tempDir, "headers.txt");
12068
+ const bodyPath = path6.join(tempDir, "body.txt");
12069
+ try {
12070
+ const args = [
12071
+ "--silent",
12072
+ "--show-error",
12073
+ "--output",
12074
+ bodyPath,
12075
+ "--dump-header",
12076
+ headersPath,
12077
+ "--request",
12078
+ options.method || "GET"
12079
+ ];
12080
+ if (options.redirect === "follow") {
12081
+ args.push("--location");
12082
+ }
12083
+ for (const [key, value] of Object.entries(options.headers || {})) {
12084
+ args.push("--header", `${key}: ${value}`);
12085
+ }
12086
+ if (typeof options.body === "string") {
12087
+ args.push("--data-raw", options.body);
12088
+ }
12089
+ args.push(url);
12090
+ const child = spawn("curl", args, {
12091
+ env: process.env,
12092
+ stdio: ["ignore", "pipe", "pipe"]
12093
+ });
12094
+ let stdout = "";
12095
+ let stderr = "";
12096
+ child.stdout.setEncoding("utf8");
12097
+ child.stderr.setEncoding("utf8");
12098
+ child.stdout.on("data", (chunk) => {
12099
+ stdout += chunk;
12100
+ });
12101
+ child.stderr.on("data", (chunk) => {
12102
+ stderr += chunk;
12103
+ });
12104
+ const exitCode = await new Promise((resolve, reject) => {
12105
+ child.once("error", reject);
12106
+ child.once("close", resolve);
12107
+ });
12108
+ if (exitCode !== 0) {
12109
+ throw new Error(stderr.trim() || stdout.trim() || `curl exited with code ${exitCode}`);
12110
+ }
12111
+ const [rawHeaders, body] = await Promise.all([
12112
+ readFile2(headersPath, "utf8"),
12113
+ readFile2(bodyPath, "utf8")
12114
+ ]);
12115
+ const { status, headers } = parseCurlHeaders(rawHeaders);
12116
+ return {
12117
+ ok: status >= 200 && status < 300,
12118
+ status,
12119
+ headers,
12120
+ body
12121
+ };
12122
+ } finally {
12123
+ await rm2(tempDir, { recursive: true, force: true });
12124
+ }
12125
+ }
12126
+ async function fetchTextWithFallback(url, options = {}) {
12127
+ try {
12128
+ const response = await fetch(url, {
12129
+ method: options.method,
12130
+ headers: options.headers,
12131
+ body: options.body,
12132
+ redirect: options.redirect
12133
+ });
12134
+ return {
12135
+ ok: response.ok,
12136
+ status: response.status,
12137
+ headers: response.headers,
12138
+ body: await response.text()
12139
+ };
12140
+ } catch (error) {
12141
+ if (!isHttpUrl(url) || !isDnsResolutionFailure(error)) {
12142
+ throw error;
12143
+ }
12144
+ return await runCurlRequest(url, options);
12145
+ }
12146
+ }
12147
+ async function fetchJsonWithFallback(url, options = {}) {
12148
+ const response = await fetchTextWithFallback(url, options);
12149
+ return {
12150
+ ...response,
12151
+ json: JSON.parse(response.body)
12152
+ };
12153
+ }
12154
+
12155
+ // src/lib/relay.ts
11834
12156
  var RELAY_BODY_LIMIT_BYTES = 1024 * 1024;
11835
12157
  var RELAY_SERVICE = "zk-agent-relay";
11836
12158
  var RELAY_PROTOCOL = "zk-agent-session-relay";
11837
12159
  var RELAY_SCHEMA_VERSION = 1;
12160
+ var RELAY_STATE_BACKEND = "local-filesystem";
12161
+ var RELAY_DEPLOYMENT_SCOPE = "single-host";
11838
12162
  function relayDir() {
11839
- const directory = path6.join(storageDir(), "relay");
12163
+ const directory = path7.join(storageDir(), "relay");
11840
12164
  if (!fs7.existsSync(directory)) {
11841
12165
  fs7.mkdirSync(directory, { recursive: true, mode: 448 });
11842
12166
  }
11843
12167
  return directory;
11844
12168
  }
11845
12169
  function relayRecordPath(requestId) {
11846
- return path6.join(relayDir(), `${requestId}.json`);
12170
+ return path7.join(relayDir(), `${requestId}.json`);
11847
12171
  }
11848
12172
  function writeRelayRecord(record) {
11849
12173
  fs7.writeFileSync(relayRecordPath(record.request_id), JSON.stringify(record, null, 2), {
@@ -11870,11 +12194,15 @@ function sanitizeRelayRecord(record) {
11870
12194
  }
11871
12195
  function relayStatusResponse(baseUrl, record) {
11872
12196
  const sanitized = sanitizeRelayRecord(record);
12197
+ const shareUrl = relayShareUrl(baseUrl, sanitized.request_id);
12198
+ const statusUrl = relayStatusUrl(baseUrl, sanitized.request_id);
11873
12199
  return {
11874
12200
  request_id: sanitized.request_id,
11875
12201
  status: relayStatus(record),
11876
12202
  approval_ready: Boolean(record.encrypted_payload),
11877
- approval_url: `${baseUrl}/r/${sanitized.request_id}`,
12203
+ share_url: shareUrl,
12204
+ status_url: statusUrl,
12205
+ approval_url: shareUrl,
11878
12206
  expires_at: sanitized.expires_at,
11879
12207
  request: sanitized.request,
11880
12208
  approval_submitted_at: sanitized.approval_submitted_at
@@ -11925,14 +12253,14 @@ function writeBody(response, statusCode, contentType, value, extraHeaders = {})
11925
12253
  }
11926
12254
  function resolveConnectorUiDistRoot() {
11927
12255
  const currentFile = fileURLToPath3(import.meta.url);
11928
- const currentDir2 = path6.dirname(currentFile);
12256
+ const currentDir2 = path7.dirname(currentFile);
11929
12257
  const candidates = [
11930
- path6.resolve(currentDir2, "./connector-ui"),
11931
- path6.resolve(currentDir2, "../../../zk-connector-ui/dist"),
11932
- path6.resolve(currentDir2, "../../zk-connector-ui/dist")
12258
+ path7.resolve(currentDir2, "./connector-ui"),
12259
+ path7.resolve(currentDir2, "../../../zk-connector-ui/dist"),
12260
+ path7.resolve(currentDir2, "../../zk-connector-ui/dist")
11933
12261
  ];
11934
12262
  for (const candidate of candidates) {
11935
- if (fs7.existsSync(path6.join(candidate, "index.html"))) {
12263
+ if (fs7.existsSync(path7.join(candidate, "index.html"))) {
11936
12264
  return candidate;
11937
12265
  }
11938
12266
  }
@@ -11961,7 +12289,7 @@ function relayCapabilities(connectorUiAvailable) {
11961
12289
  ...connectorUiAvailable ? ["connector-ui"] : []
11962
12290
  ];
11963
12291
  }
11964
- function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable) {
12292
+ function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable, publicOriginSource) {
11965
12293
  return {
11966
12294
  ok: true,
11967
12295
  service: RELAY_SERVICE,
@@ -11970,6 +12298,10 @@ function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable) {
11970
12298
  relay_mode: "local-file",
11971
12299
  origin: normalizeRelayBaseUrl(bindBaseUrl),
11972
12300
  public_origin: normalizeRelayBaseUrl(publicBaseUrl),
12301
+ public_origin_source: publicOriginSource,
12302
+ state_backend: RELAY_STATE_BACKEND,
12303
+ deployment_scope: RELAY_DEPLOYMENT_SCOPE,
12304
+ same_host_restart_persists: true,
11973
12305
  connector_ui_available: connectorUiAvailable,
11974
12306
  capabilities: relayCapabilities(connectorUiAvailable)
11975
12307
  };
@@ -11984,31 +12316,34 @@ function relayApprovalUrl(baseUrl, requestId) {
11984
12316
  return `${relayStatusUrl(baseUrl, requestId)}/approval`;
11985
12317
  }
11986
12318
  async function publishRelayRequest(baseUrl, body) {
11987
- const response = await fetch(`${normalizeRelayBaseUrl(baseUrl)}/api/requests`, {
11988
- method: "POST",
11989
- headers: {
11990
- "Content-Type": "application/json"
11991
- },
11992
- body: JSON.stringify(body)
11993
- });
12319
+ const response = await fetchJsonWithFallback(
12320
+ `${normalizeRelayBaseUrl(baseUrl)}/api/requests`,
12321
+ {
12322
+ method: "POST",
12323
+ headers: {
12324
+ "Content-Type": "application/json"
12325
+ },
12326
+ body: JSON.stringify(body)
12327
+ }
12328
+ );
11994
12329
  if (!response.ok) {
11995
12330
  throw new Error(`Relay publish failed with status ${response.status}`);
11996
12331
  }
11997
- return await response.json();
12332
+ return response.json;
11998
12333
  }
11999
12334
  async function fetchRelayStatus(baseUrl, requestId) {
12000
- const response = await fetch(relayStatusUrl(baseUrl, requestId));
12335
+ const response = await fetchJsonWithFallback(relayStatusUrl(baseUrl, requestId));
12001
12336
  if (!response.ok) {
12002
12337
  throw new Error(`Relay status fetch failed with status ${response.status}`);
12003
12338
  }
12004
- return await response.json();
12339
+ return response.json;
12005
12340
  }
12006
12341
  async function fetchRelayHealth(baseUrl) {
12007
- const response = await fetch(`${normalizeRelayBaseUrl(baseUrl)}/health`);
12342
+ const response = await fetchJsonWithFallback(`${normalizeRelayBaseUrl(baseUrl)}/health`);
12008
12343
  if (!response.ok) {
12009
12344
  throw new Error(`Relay health fetch failed with status ${response.status}`);
12010
12345
  }
12011
- return await response.json();
12346
+ return response.json;
12012
12347
  }
12013
12348
  function sleep(ms) {
12014
12349
  return new Promise((resolve) => {
@@ -12033,15 +12368,16 @@ async function waitForRelayApprovalReady(baseUrl, requestId, options) {
12033
12368
  );
12034
12369
  }
12035
12370
  async function fetchRelayApproval(baseUrl, requestId) {
12036
- const response = await fetch(relayApprovalUrl(baseUrl, requestId));
12371
+ const response = await fetchJsonWithFallback(relayApprovalUrl(baseUrl, requestId));
12037
12372
  if (!response.ok) {
12038
12373
  throw new Error(`Relay approval fetch failed with status ${response.status}`);
12039
12374
  }
12040
- return await response.json();
12375
+ return response.json;
12041
12376
  }
12042
12377
  async function startRelayServer(options) {
12043
12378
  const uiDistRoot = resolveConnectorUiDistRoot();
12044
12379
  let bindBaseUrl = "";
12380
+ const publicOriginSource = options.publicOrigin?.trim() ? "configured" : "bind-origin-default";
12045
12381
  const server = createServer(async (request, response) => {
12046
12382
  try {
12047
12383
  const requestUrl = new URL(request.url || "/", "http://localhost");
@@ -12056,7 +12392,12 @@ async function startRelayServer(options) {
12056
12392
  writeJson2(
12057
12393
  response,
12058
12394
  200,
12059
- relayHealthResponse(bindBaseUrl, publicBaseUrl, Boolean(uiDistRoot))
12395
+ relayHealthResponse(
12396
+ bindBaseUrl,
12397
+ publicBaseUrl,
12398
+ Boolean(uiDistRoot),
12399
+ publicOriginSource
12400
+ )
12060
12401
  );
12061
12402
  return;
12062
12403
  }
@@ -12125,7 +12466,7 @@ async function startRelayServer(options) {
12125
12466
  }
12126
12467
  if (method === "GET" && uiDistRoot) {
12127
12468
  const relativePath = pathname === "/" ? "/index.html" : pathname;
12128
- const filePath = path6.resolve(uiDistRoot, `.${relativePath}`);
12469
+ const filePath = path7.resolve(uiDistRoot, `.${relativePath}`);
12129
12470
  if (!filePath.startsWith(uiDistRoot)) {
12130
12471
  writeJson2(response, 403, { error: "Forbidden path" });
12131
12472
  return;
@@ -12134,7 +12475,7 @@ async function startRelayServer(options) {
12134
12475
  writeBody(response, 200, contentTypeFor(filePath), fs7.readFileSync(filePath));
12135
12476
  return;
12136
12477
  }
12137
- const indexPath = path6.join(uiDistRoot, "index.html");
12478
+ const indexPath = path7.join(uiDistRoot, "index.html");
12138
12479
  if (fs7.existsSync(indexPath)) {
12139
12480
  writeBody(response, 200, "text/html; charset=utf-8", fs7.readFileSync(indexPath));
12140
12481
  return;
@@ -12181,8 +12522,14 @@ async function startRelayServer(options) {
12181
12522
  // src/commands/relay.ts
12182
12523
  function buildRelayServeRecommendedCommands(relayUrl) {
12183
12524
  return {
12184
- createWallet: `zk-agent wallet create --relay-url ${relayUrl}`,
12185
- reapproveWallet: `zk-agent wallet reapprove --name main --relay-url ${relayUrl}`
12525
+ createWallet: `zk-agent wallet create --relay-url ${relayUrl} --wait-relay --prompt-code`,
12526
+ reapproveWallet: `zk-agent wallet reapprove --name main --relay-url ${relayUrl} --wait-relay --prompt-code`
12527
+ };
12528
+ }
12529
+ function buildAdvertisedRelayBases(publicOrigin) {
12530
+ return {
12531
+ shareLinkBaseUrl: `${publicOrigin}/r`,
12532
+ statusApiBaseUrl: `${publicOrigin}/api/requests`
12186
12533
  };
12187
12534
  }
12188
12535
  function isRecord3(value) {
@@ -12198,6 +12545,15 @@ function isRelayCapability(value) {
12198
12545
  "connector-ui"
12199
12546
  ].includes(String(value));
12200
12547
  }
12548
+ function isRelayPublicOriginSource(value) {
12549
+ return value === "configured" || value === "bind-origin-default";
12550
+ }
12551
+ function isRelayStateBackend(value) {
12552
+ return value === "local-filesystem";
12553
+ }
12554
+ function isRelayDeploymentScope(value) {
12555
+ return value === "single-host";
12556
+ }
12201
12557
  function asRelayHealthResponse(value) {
12202
12558
  if (!isRecord3(value)) return null;
12203
12559
  if (value.ok !== true) return null;
@@ -12207,6 +12563,18 @@ function asRelayHealthResponse(value) {
12207
12563
  if (value.relay_mode !== "local-file") return null;
12208
12564
  if (typeof value.origin !== "string") return null;
12209
12565
  if (typeof value.public_origin !== "string") return null;
12566
+ if (typeof value.public_origin_source !== "undefined" && !isRelayPublicOriginSource(value.public_origin_source)) {
12567
+ return null;
12568
+ }
12569
+ if (typeof value.state_backend !== "undefined" && !isRelayStateBackend(value.state_backend)) {
12570
+ return null;
12571
+ }
12572
+ if (typeof value.deployment_scope !== "undefined" && !isRelayDeploymentScope(value.deployment_scope)) {
12573
+ return null;
12574
+ }
12575
+ if (typeof value.same_host_restart_persists !== "undefined" && typeof value.same_host_restart_persists !== "boolean") {
12576
+ return null;
12577
+ }
12210
12578
  if (typeof value.connector_ui_available !== "boolean") return null;
12211
12579
  if (!Array.isArray(value.capabilities) || !value.capabilities.every(isRelayCapability)) {
12212
12580
  return null;
@@ -12225,6 +12593,38 @@ function relayPublicOriginLooksLocal(origin) {
12225
12593
  return false;
12226
12594
  }
12227
12595
  }
12596
+ function normalizeComparableRelayUrl(value) {
12597
+ try {
12598
+ return new URL(value).toString().replace(/\/+$/, "");
12599
+ } catch {
12600
+ return null;
12601
+ }
12602
+ }
12603
+ function relayUrlMatches(left, right) {
12604
+ const normalizedLeft = normalizeComparableRelayUrl(left);
12605
+ const normalizedRight = right ? normalizeComparableRelayUrl(right) : null;
12606
+ if (normalizedLeft === null || normalizedRight === null) {
12607
+ return null;
12608
+ }
12609
+ return normalizedLeft === normalizedRight;
12610
+ }
12611
+ function inferRelayPublicOriginSource(options) {
12612
+ const normalizedOrigin = options.origin ? normalizeComparableRelayUrl(options.origin) : null;
12613
+ const normalizedPublicOrigin = normalizeComparableRelayUrl(options.publicOrigin);
12614
+ if (!normalizedOrigin || !normalizedPublicOrigin) {
12615
+ return null;
12616
+ }
12617
+ return normalizedOrigin === normalizedPublicOrigin ? "bind-origin-default" : "configured";
12618
+ }
12619
+ function inferRelayStateBackend(relayMode) {
12620
+ return relayMode === "local-file" ? "local-filesystem" : null;
12621
+ }
12622
+ function inferRelayDeploymentScope(relayMode) {
12623
+ return relayMode === "local-file" ? "single-host" : null;
12624
+ }
12625
+ function inferSameHostRestartPersists(relayMode) {
12626
+ return relayMode === "local-file" ? true : null;
12627
+ }
12228
12628
  function relayHostedReadinessNotes(options) {
12229
12629
  const notes = [];
12230
12630
  if (!options.compatible) {
@@ -12235,7 +12635,7 @@ function relayHostedReadinessNotes(options) {
12235
12635
  }
12236
12636
  if (relayPublicOriginLooksLocal(options.publicOrigin)) {
12237
12637
  notes.push(
12238
- "Relay compatibility is present, but the advertised public origin still points at a local-only address. Set --public-origin to the externally reachable URL before using this as a hosted approval path."
12638
+ options.publicOriginSource === "bind-origin-default" ? "Relay compatibility is present, but the relay is still advertising its bind origin as the public origin. Set --public-origin to the externally reachable URL before using this as a hosted approval path." : "Relay compatibility is present, but the advertised public origin still points at a local-only address. Set --public-origin to the externally reachable URL before using this as a hosted approval path."
12239
12639
  );
12240
12640
  }
12241
12641
  if (options.connectorUiAvailable === false) {
@@ -12245,19 +12645,70 @@ function relayHostedReadinessNotes(options) {
12245
12645
  }
12246
12646
  return notes;
12247
12647
  }
12648
+ function relayOperationalContractNotes(options) {
12649
+ if (!options.compatible) {
12650
+ return [];
12651
+ }
12652
+ if (options.stateBackend === "local-filesystem" && options.deploymentScope === "single-host" && options.sameHostRestartPersists === true) {
12653
+ return [
12654
+ "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."
12655
+ ];
12656
+ }
12657
+ return [];
12658
+ }
12659
+ function relayOriginRelationshipNotes(options) {
12660
+ const notes = [];
12661
+ const relayUrlMatchesOrigin = relayUrlMatches(options.relayUrl, options.origin);
12662
+ const relayUrlMatchesPublicOrigin = relayUrlMatches(options.relayUrl, options.publicOrigin);
12663
+ if (relayUrlMatchesOrigin === false) {
12664
+ notes.push(
12665
+ "The inspected relay URL differs from the bind origin reported by /health. That is expected when you inspect a relay through a reverse proxy or tunnel instead of the local bind address."
12666
+ );
12667
+ }
12668
+ if (relayUrlMatchesPublicOrigin === false) {
12669
+ notes.push(
12670
+ "The inspected relay URL differs from the advertised public origin. Share links and wallet approval commands will use the public origin, not the inspected relay URL."
12671
+ );
12672
+ }
12673
+ return notes;
12674
+ }
12248
12675
  function buildRelayInspectPayload(relayUrl, rawHealth) {
12249
12676
  const health = asRelayHealthResponse(rawHealth);
12250
12677
  const fallbackPublicOrigin = isRecord3(rawHealth) && typeof rawHealth.public_origin === "string" ? rawHealth.public_origin : relayUrl;
12251
12678
  const publicOrigin = health?.public_origin || fallbackPublicOrigin;
12679
+ const publicOriginSource = health?.public_origin_source || inferRelayPublicOriginSource({
12680
+ origin: health?.origin || null,
12681
+ publicOrigin
12682
+ });
12683
+ const stateBackend = health?.state_backend || inferRelayStateBackend(health?.relay_mode || null);
12684
+ const deploymentScope = health?.deployment_scope || inferRelayDeploymentScope(health?.relay_mode || null);
12685
+ const sameHostRestartPersists = typeof health?.same_host_restart_persists === "boolean" ? health.same_host_restart_persists : inferSameHostRestartPersists(health?.relay_mode || null);
12252
12686
  const compatible = Boolean(health && hasCoreRelayCapabilities(health.capabilities));
12253
12687
  const connectorUiAvailable = health?.connector_ui_available ?? null;
12688
+ const relayUrlMatchesOrigin = relayUrlMatches(relayUrl, health?.origin || null);
12689
+ const relayUrlMatchesPublicOrigin = relayUrlMatches(relayUrl, publicOrigin);
12254
12690
  const publicOriginLooksLocal = relayPublicOriginLooksLocal(publicOrigin);
12255
12691
  const hostedShareRedirectReady = compatible && connectorUiAvailable === true && !publicOriginLooksLocal;
12256
- const notes = relayHostedReadinessNotes({
12257
- compatible,
12258
- publicOrigin,
12259
- connectorUiAvailable
12260
- });
12692
+ const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
12693
+ const notes = [
12694
+ ...relayHostedReadinessNotes({
12695
+ compatible,
12696
+ publicOrigin,
12697
+ publicOriginSource,
12698
+ connectorUiAvailable
12699
+ }),
12700
+ ...relayOperationalContractNotes({
12701
+ compatible,
12702
+ stateBackend,
12703
+ deploymentScope,
12704
+ sameHostRestartPersists
12705
+ }),
12706
+ ...relayOriginRelationshipNotes({
12707
+ relayUrl,
12708
+ origin: health?.origin || null,
12709
+ publicOrigin
12710
+ })
12711
+ ];
12261
12712
  return {
12262
12713
  ok: true,
12263
12714
  status: "relay-inspected",
@@ -12269,6 +12720,14 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12269
12720
  relayMode: health?.relay_mode || null,
12270
12721
  origin: health?.origin || null,
12271
12722
  publicOrigin,
12723
+ publicOriginSource,
12724
+ stateBackend,
12725
+ deploymentScope,
12726
+ sameHostRestartPersists,
12727
+ shareLinkBaseUrl,
12728
+ statusApiBaseUrl,
12729
+ relayUrlMatchesOrigin,
12730
+ relayUrlMatchesPublicOrigin,
12272
12731
  publicOriginLooksLocal,
12273
12732
  connectorUiAvailable,
12274
12733
  hostedShareRedirectReady,
@@ -12279,6 +12738,23 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12279
12738
  }
12280
12739
  function createRelayCommand() {
12281
12740
  const relay = new Command8("relay").description("Run the local connector relay prototype server");
12741
+ relay.addHelpText(
12742
+ "after",
12743
+ [
12744
+ "",
12745
+ " Hosted remote-approval path:",
12746
+ " zk-agent relay serve --public-origin https://relay.example.com",
12747
+ " zk-agent relay inspect --relay-url <url>",
12748
+ " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
12749
+ " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code",
12750
+ "",
12751
+ " Keep `wallet create|reapprove --await-local` as the default baseline when",
12752
+ " the browser and terminal are colocated.",
12753
+ "",
12754
+ " Use `relay inspect` before sending operators to a hosted share link so",
12755
+ " the public origin, connector UI, and hosted-readiness contract are visible."
12756
+ ].join("\n")
12757
+ );
12282
12758
  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(
12283
12759
  "--public-origin <url>",
12284
12760
  "Public base URL to advertise in share/status links when the relay is behind a tunnel or reverse proxy"
@@ -12294,13 +12770,16 @@ function createRelayCommand() {
12294
12770
  publicOrigin: options.publicOrigin?.trim()
12295
12771
  });
12296
12772
  const publicOrigin = options.publicOrigin?.trim() || server.origin;
12773
+ const publicOriginSource = options.publicOrigin?.trim() ? "configured" : "bind-origin-default";
12297
12774
  const publicOriginLooksLocal = relayPublicOriginLooksLocal(publicOrigin);
12298
12775
  const connectorUiAvailable = server.connectorUiAvailable;
12299
12776
  const hostedShareRedirectReady = connectorUiAvailable && !publicOriginLooksLocal;
12777
+ const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
12300
12778
  const recommendedCommands = buildRelayServeRecommendedCommands(publicOrigin);
12301
12779
  const notes = relayHostedReadinessNotes({
12302
12780
  compatible: true,
12303
12781
  publicOrigin,
12782
+ publicOriginSource,
12304
12783
  connectorUiAvailable
12305
12784
  });
12306
12785
  const payload = {
@@ -12308,6 +12787,12 @@ function createRelayCommand() {
12308
12787
  status: "relay-serving",
12309
12788
  origin: server.origin,
12310
12789
  publicOrigin,
12790
+ publicOriginSource,
12791
+ stateBackend: "local-filesystem",
12792
+ deploymentScope: "single-host",
12793
+ sameHostRestartPersists: true,
12794
+ shareLinkBaseUrl,
12795
+ statusApiBaseUrl,
12311
12796
  publicOriginLooksLocal,
12312
12797
  port: server.port,
12313
12798
  healthUrl: `${server.origin}/health`,
@@ -12334,6 +12819,15 @@ function createRelayCommand() {
12334
12819
  if (publicOrigin !== server.origin) {
12335
12820
  humanLine("public origin", publicOrigin);
12336
12821
  }
12822
+ humanLine("public origin source", publicOriginSource);
12823
+ humanLine("state backend", payload.stateBackend);
12824
+ humanLine("deployment scope", payload.deploymentScope);
12825
+ humanLine(
12826
+ "same-host restart persists",
12827
+ payload.sameHostRestartPersists ? "yes" : "no"
12828
+ );
12829
+ humanLine("share-link base", shareLinkBaseUrl);
12830
+ humanLine("status api base", statusApiBaseUrl);
12337
12831
  humanLine("health", `${server.origin}/health`);
12338
12832
  humanLine("hosted ready", hostedShareRedirectReady ? "yes" : "no");
12339
12833
  if (connectorUiAvailable !== null) {
@@ -12385,6 +12879,32 @@ function createRelayCommand() {
12385
12879
  if (payload.publicOrigin) {
12386
12880
  humanLine("public origin", payload.publicOrigin);
12387
12881
  }
12882
+ if (payload.publicOriginSource) {
12883
+ humanLine("public origin source", payload.publicOriginSource);
12884
+ }
12885
+ if (payload.stateBackend) {
12886
+ humanLine("state backend", payload.stateBackend);
12887
+ }
12888
+ if (payload.deploymentScope) {
12889
+ humanLine("deployment scope", payload.deploymentScope);
12890
+ }
12891
+ if (payload.sameHostRestartPersists !== null) {
12892
+ humanLine(
12893
+ "same-host restart persists",
12894
+ payload.sameHostRestartPersists ? "yes" : "no"
12895
+ );
12896
+ }
12897
+ humanLine("share-link base", payload.shareLinkBaseUrl);
12898
+ humanLine("status api base", payload.statusApiBaseUrl);
12899
+ if (payload.relayUrlMatchesOrigin !== null) {
12900
+ humanLine("relay url matches origin", payload.relayUrlMatchesOrigin ? "yes" : "no");
12901
+ }
12902
+ if (payload.relayUrlMatchesPublicOrigin !== null) {
12903
+ humanLine(
12904
+ "relay url matches public origin",
12905
+ payload.relayUrlMatchesPublicOrigin ? "yes" : "no"
12906
+ );
12907
+ }
12388
12908
  humanLine("public origin local", payload.publicOriginLooksLocal ? "yes" : "no");
12389
12909
  if (payload.connectorUiAvailable !== null) {
12390
12910
  humanLine("connector ui", payload.connectorUiAvailable ? "available" : "missing");
@@ -12400,10 +12920,9 @@ function createRelayCommand() {
12400
12920
  if (payload.recommendedCommands.reapproveWallet) {
12401
12921
  humanLine("reapprove wallet", payload.recommendedCommands.reapproveWallet);
12402
12922
  }
12403
- } else {
12404
- for (const note of payload.notes) {
12405
- humanLine("note", note);
12406
- }
12923
+ }
12924
+ for (const note of payload.notes) {
12925
+ humanLine("note", note);
12407
12926
  }
12408
12927
  });
12409
12928
  return relay;
@@ -12416,12 +12935,12 @@ import { Command as Command9 } from "commander";
12416
12935
 
12417
12936
  // ../account-profiles/src/profiles.ts
12418
12937
  import fs8 from "node:fs";
12419
- import path7 from "node:path";
12938
+ import path8 from "node:path";
12420
12939
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12421
12940
  var PACKAGE_NAME = "@zk-agent/account-profiles";
12422
12941
  var PACKAGE_ROOT_FALLBACK = `<${PACKAGE_NAME} package root unavailable>`;
12423
12942
  function isExpectedPackageRoot(candidate) {
12424
- const manifestPath = path7.join(candidate, "package.json");
12943
+ const manifestPath = path8.join(candidate, "package.json");
12425
12944
  if (!fs8.existsSync(manifestPath)) return false;
12426
12945
  try {
12427
12946
  const manifest = JSON.parse(fs8.readFileSync(manifestPath, "utf8"));
@@ -12431,19 +12950,19 @@ function isExpectedPackageRoot(candidate) {
12431
12950
  }
12432
12951
  }
12433
12952
  function resolvePackageRoot() {
12434
- const moduleRoot = path7.resolve(path7.dirname(fileURLToPath4(import.meta.url)), "..");
12953
+ const moduleRoot = path8.resolve(path8.dirname(fileURLToPath4(import.meta.url)), "..");
12435
12954
  const cwd = process.cwd();
12436
12955
  const candidates = [
12437
12956
  process.env.ZK_AGENT_ACCOUNT_PROFILES_ROOT,
12438
- path7.join(moduleRoot, "dist", "builtin-account-profiles"),
12957
+ path8.join(moduleRoot, "dist", "builtin-account-profiles"),
12439
12958
  moduleRoot,
12440
- path7.join(cwd, "packages", "account-profiles"),
12441
- path7.join(cwd, "account-profiles"),
12959
+ path8.join(cwd, "packages", "account-profiles"),
12960
+ path8.join(cwd, "account-profiles"),
12442
12961
  cwd
12443
12962
  ].filter((value) => typeof value === "string" && value.trim().length > 0);
12444
12963
  const seen = /* @__PURE__ */ new Set();
12445
12964
  for (const candidate of candidates) {
12446
- const normalized = path7.resolve(candidate);
12965
+ const normalized = path8.resolve(candidate);
12447
12966
  if (seen.has(normalized)) continue;
12448
12967
  seen.add(normalized);
12449
12968
  if (isExpectedPackageRoot(normalized)) {
@@ -12515,11 +13034,11 @@ function missingArtifactError(profileId, artifactPath2) {
12515
13034
  }
12516
13035
  function contractPath(...segments) {
12517
13036
  const packageRoot = tryResolvePackageRoot();
12518
- return packageRoot ? path7.join(packageRoot, "contracts", ...segments) : path7.join(PACKAGE_ROOT_FALLBACK, "contracts", ...segments);
13037
+ return packageRoot ? path8.join(packageRoot, "contracts", ...segments) : path8.join(PACKAGE_ROOT_FALLBACK, "contracts", ...segments);
12519
13038
  }
12520
13039
  function artifactPath(...segments) {
12521
13040
  const packageRoot = tryResolvePackageRoot();
12522
- return packageRoot ? path7.join(packageRoot, "artifacts", ...segments) : path7.join(PACKAGE_ROOT_FALLBACK, "artifacts", ...segments);
13041
+ return packageRoot ? path8.join(packageRoot, "artifacts", ...segments) : path8.join(PACKAGE_ROOT_FALLBACK, "artifacts", ...segments);
12523
13042
  }
12524
13043
  function createDailySpendLimitProfile() {
12525
13044
  const resolvedArtifactPath = artifactPath("daily-spend-limit", "Account.json");
@@ -13005,11 +13524,16 @@ function sanitizeWalletRecord(wallet) {
13005
13524
  function relayOutputAliases(relay) {
13006
13525
  const shareUrl = relay && "share_url" in relay ? relay.share_url : void 0;
13007
13526
  const statusUrl = relay && "status_url" in relay ? relay.status_url : void 0;
13527
+ const approvalUrl = relay?.approval_url;
13528
+ const shareLinkBaseUrl = shareUrl ? shareUrl.replace(/\/[^/]+$/, "") : approvalUrl ? approvalUrl.replace(/\/[^/]+$/, "") : void 0;
13529
+ const statusApiBaseUrl = statusUrl ? statusUrl.replace(/\/[^/]+$/, "") : approvalUrl && relay?.request_id ? `${approvalUrl.replace(/\/r\/[^/]+$/, "")}/api/requests` : void 0;
13008
13530
  return {
13009
13531
  relayRequestId: relay?.request_id,
13010
13532
  relayShareUrl: shareUrl,
13011
13533
  relayStatusUrl: statusUrl,
13012
- relayApprovalUrl: relay?.approval_url
13534
+ relayApprovalUrl: approvalUrl,
13535
+ relayShareLinkBaseUrl: shareLinkBaseUrl,
13536
+ relayStatusApiBaseUrl: statusApiBaseUrl
13013
13537
  };
13014
13538
  }
13015
13539
  function sanitizeWalletRequestRecord(request) {
@@ -14279,6 +14803,165 @@ function buildPendingRequestRecommendedCommands(walletName, requestId, relayUrl,
14279
14803
  function buildPendingRequestNextAction(recommendedCommands) {
14280
14804
  return recommendedCommands.relayStatus ?? recommendedCommands.awaitLocal;
14281
14805
  }
14806
+ function buildRelayWaitGuidanceLines(options) {
14807
+ return [
14808
+ ["status", "Waiting for relay approval"],
14809
+ ["request", options.requestId],
14810
+ ["wallet", options.walletName],
14811
+ ["share url", options.relay.share_url],
14812
+ ["status url", options.relay.status_url],
14813
+ ["approval url", options.relay.approval_url],
14814
+ ["expires", options.expiresAt],
14815
+ ["browser step", "Open the share url in a browser and complete connector approval."],
14816
+ [
14817
+ "terminal step",
14818
+ 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."
14819
+ ],
14820
+ ["fallback status", options.statusCommand],
14821
+ ["fallback approve", options.approveCommand]
14822
+ ];
14823
+ }
14824
+ function printRelayWaitGuidance(options) {
14825
+ if (shouldJsonOutput()) return;
14826
+ for (const [label, value] of buildRelayWaitGuidanceLines(options)) {
14827
+ humanLine(label, value);
14828
+ }
14829
+ }
14830
+ async function buildRelayStatusFollowUp(options) {
14831
+ if (options.relay.status === "expired") {
14832
+ return await buildRelayExpiredRecoveryCommands({
14833
+ walletName: options.relay.request?.walletName,
14834
+ relayUrl: options.relayUrl,
14835
+ paymasterMode: options.relay.request?.requestedPaymasterMode,
14836
+ accountKind: options.relay.request?.requestedAccountKind
14837
+ });
14838
+ }
14839
+ if (options.relay.approval_ready) {
14840
+ const approve = buildWalletRequestRelayApproveRecommendedCommand(
14841
+ options.relay.request_id,
14842
+ options.relayUrl
14843
+ );
14844
+ return {
14845
+ nextAction: approve,
14846
+ recommendedCommands: {
14847
+ status: buildWalletRequestRelayStatusRecommendedCommand(
14848
+ options.relay.request_id,
14849
+ options.relayUrl
14850
+ ),
14851
+ approve
14852
+ }
14853
+ };
14854
+ }
14855
+ return {
14856
+ nextAction: buildWalletRequestRelayStatusRecommendedCommand(
14857
+ options.relay.request_id,
14858
+ options.relayUrl
14859
+ ),
14860
+ recommendedCommands: {
14861
+ status: buildWalletRequestRelayStatusRecommendedCommand(
14862
+ options.relay.request_id,
14863
+ options.relayUrl
14864
+ )
14865
+ }
14866
+ };
14867
+ }
14868
+ async function buildRelayExpiredRecoveryCommands(options) {
14869
+ const relayInspect = buildRelayInspectRecommendedCommand(options.relayUrl);
14870
+ const walletName = options.walletName?.trim();
14871
+ if (!walletName) {
14872
+ return {
14873
+ nextAction: relayInspect,
14874
+ recommendedCommands: {
14875
+ relayInspect
14876
+ },
14877
+ note: "Relay approval expired. Inspect the hosted relay, then reissue the wallet request again."
14878
+ };
14879
+ }
14880
+ const existingWallet = await loadWalletSession(walletName);
14881
+ const reissueRemoteApproval = existingWallet ? buildWalletReapproveRemoteRecommendedCommand(walletName, options.relayUrl) : buildWalletCreateRemoteRecommendedCommand(
14882
+ options.relayUrl,
14883
+ options.paymasterMode,
14884
+ walletName,
14885
+ options.accountKind
14886
+ );
14887
+ return {
14888
+ nextAction: reissueRemoteApproval,
14889
+ recommendedCommands: {
14890
+ relayInspect,
14891
+ reissueRemoteApproval
14892
+ },
14893
+ note: "Relay approval expired. Reissue the remote request. If the original request used scoped session flags, add those same policy flags again."
14894
+ };
14895
+ }
14896
+ function buildRelayApprovalTimeoutError(options) {
14897
+ const statusCommand = buildWalletRequestRelayStatusRecommendedCommand(
14898
+ options.requestId,
14899
+ options.relayUrl
14900
+ );
14901
+ const approveCommand = buildWalletRequestRelayApproveRecommendedCommand(
14902
+ options.requestId,
14903
+ options.relayUrl
14904
+ );
14905
+ return new AgentError(
14906
+ "RELAY_APPROVAL_TIMEOUT",
14907
+ `Timed out waiting for relay approval after ${Math.ceil(options.timeoutMs / 1e3)} seconds.`,
14908
+ {
14909
+ requestId: options.requestId,
14910
+ relayUrl: options.relayUrl,
14911
+ timeoutMs: options.timeoutMs,
14912
+ intervalMs: options.intervalMs,
14913
+ retryable: true,
14914
+ statusCommand,
14915
+ approveCommand,
14916
+ suggestedAction: "Check the current relay status, then finalize the wallet approval once approval_ready=true."
14917
+ }
14918
+ );
14919
+ }
14920
+ async function buildRelayApprovalExpiredError(options) {
14921
+ const recovery = await buildRelayExpiredRecoveryCommands({
14922
+ walletName: options.walletName,
14923
+ relayUrl: options.relayUrl,
14924
+ paymasterMode: options.paymasterMode,
14925
+ accountKind: options.accountKind
14926
+ });
14927
+ return new AgentError(
14928
+ "RELAY_APPROVAL_EXPIRED",
14929
+ `Relay approval expired before the encrypted payload was ready for request ${options.requestId}.`,
14930
+ {
14931
+ requestId: options.requestId,
14932
+ relayUrl: options.relayUrl,
14933
+ retryable: true,
14934
+ note: recovery.note,
14935
+ relayInspectCommand: recovery.recommendedCommands.relayInspect,
14936
+ reissueRemoteApprovalCommand: recovery.recommendedCommands.reissueRemoteApproval,
14937
+ suggestedAction: "Inspect the hosted relay, then reissue the remote approval request."
14938
+ }
14939
+ );
14940
+ }
14941
+ function buildRelayApprovalNotReadyError(options) {
14942
+ const statusCommand = buildWalletRequestRelayStatusRecommendedCommand(
14943
+ options.requestId,
14944
+ options.relayUrl
14945
+ );
14946
+ const approveCommand = buildWalletRequestRelayApproveRecommendedCommand(
14947
+ options.requestId,
14948
+ options.relayUrl
14949
+ );
14950
+ return new AgentError(
14951
+ "RELAY_APPROVAL_NOT_READY",
14952
+ `Relay approval is not ready yet for request ${options.requestId}.`,
14953
+ {
14954
+ requestId: options.requestId,
14955
+ relayUrl: options.relayUrl,
14956
+ status: options.status,
14957
+ approvalReady: options.approvalReady,
14958
+ retryable: true,
14959
+ statusCommand,
14960
+ approveCommand,
14961
+ suggestedAction: "Check the current relay status, then retry approval once approval_ready=true."
14962
+ }
14963
+ );
14964
+ }
14282
14965
  function buildRequestListEntryRecommendedCommands(walletName, requestId, paymasterMode) {
14283
14966
  return {
14284
14967
  show: buildWalletRequestShowRecommendedCommand(requestId),
@@ -14421,25 +15104,55 @@ async function fetchEncryptedRelayApprovalPayload(relayUrl, requestId, options)
14421
15104
  intervalMs: options.intervalMs ?? 2e3
14422
15105
  });
14423
15106
  if (!relay.approval_ready) {
14424
- throw new Error(`Relay approval expired before the encrypted payload was ready for request ${requestId}.`);
15107
+ throw await buildRelayApprovalExpiredError({
15108
+ requestId,
15109
+ relayUrl
15110
+ });
14425
15111
  }
14426
15112
  }
14427
15113
  const approval = await fetchRelayApproval(relayUrl, requestId);
14428
15114
  if (!approval.approval_ready || !approval.encrypted_payload) {
14429
- throw new Error(`Relay approval is not ready yet for request ${requestId}.`);
15115
+ throw buildRelayApprovalNotReadyError({
15116
+ requestId,
15117
+ relayUrl,
15118
+ status: approval.status,
15119
+ approvalReady: approval.approval_ready
15120
+ });
14430
15121
  }
14431
15122
  return approval.encrypted_payload;
14432
15123
  }
14433
15124
  async function finalizePublishedRelayWalletRequest(options) {
14434
- const encryptedPayload = await fetchEncryptedRelayApprovalPayload(
14435
- options.relayUrl,
14436
- options.walletRequest.requestId,
14437
- {
14438
- wait: true,
14439
- timeoutMs: options.timeoutMs,
14440
- intervalMs: options.intervalMs
15125
+ let encryptedPayload;
15126
+ try {
15127
+ encryptedPayload = await fetchEncryptedRelayApprovalPayload(
15128
+ options.relayUrl,
15129
+ options.walletRequest.requestId,
15130
+ {
15131
+ wait: true,
15132
+ timeoutMs: options.timeoutMs,
15133
+ intervalMs: options.intervalMs
15134
+ }
15135
+ );
15136
+ } catch (error) {
15137
+ if (error instanceof Error && error.message.startsWith("Timed out waiting for relay approval after ")) {
15138
+ throw buildRelayApprovalTimeoutError({
15139
+ requestId: options.walletRequest.requestId,
15140
+ relayUrl: options.relayUrl,
15141
+ timeoutMs: options.timeoutMs,
15142
+ intervalMs: options.intervalMs
15143
+ });
14441
15144
  }
14442
- );
15145
+ if (error instanceof AgentError && error.code === "RELAY_APPROVAL_EXPIRED") {
15146
+ throw await buildRelayApprovalExpiredError({
15147
+ requestId: options.walletRequest.requestId,
15148
+ walletName: options.walletRequest.walletName,
15149
+ relayUrl: options.relayUrl,
15150
+ paymasterMode: options.walletRequest.requestedPaymasterMode,
15151
+ accountKind: options.walletRequest.requestedAccountKind
15152
+ });
15153
+ }
15154
+ throw error;
15155
+ }
14443
15156
  const code = options.code || (options.promptCode ? await readApprovalCodeFromStdin() : void 0);
14444
15157
  if (!code) {
14445
15158
  throw new Error("Missing relay approval code.");
@@ -14806,7 +15519,9 @@ async function printBuiltinSmartAccountProfiles() {
14806
15519
  }
14807
15520
  function createWalletCommand(deps) {
14808
15521
  const resolvedDeps = resolveWalletCommandDeps(deps);
14809
- const wallet = new Command9("wallet").description("Manage wallet sessions");
15522
+ const wallet = new Command9("wallet").description(
15523
+ "Create, inspect, and recover local-first wallet sessions"
15524
+ );
14810
15525
  const request = new Command9("request").description("Inspect and finalize pending wallet requests");
14811
15526
  const signer = new Command9("signer").description(
14812
15527
  "Inspect and manage the stored local execution signer for a wallet"
@@ -14836,13 +15551,14 @@ function createWalletCommand(deps) {
14836
15551
  "after",
14837
15552
  [
14838
15553
  "",
14839
- "Default wallet path:",
15554
+ "Local-first wallet path:",
14840
15555
  " First bootstrap:",
14841
15556
  " zk-agent wallet create --await-local",
14842
15557
  " zk-agent next",
14843
15558
  "",
14844
15559
  " Restore approval metadata for an existing wallet:",
14845
15560
  " zk-agent wallet reapprove --name main --await-local",
15561
+ " zk-agent next",
14846
15562
  "",
14847
15563
  " Attach a local signer when approval is still present:",
14848
15564
  " zk-agent wallet signer attach --name main --private-key <hex>",
@@ -14852,10 +15568,11 @@ function createWalletCommand(deps) {
14852
15568
  " zk-agent wallet status --name main",
14853
15569
  " zk-agent wallet next --name main",
14854
15570
  "",
14855
- " Remote approval path:",
15571
+ " Hosted remote approval path:",
14856
15572
  " zk-agent relay inspect --relay-url <url>",
14857
15573
  " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
14858
- " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code"
15574
+ " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code",
15575
+ " zk-agent next"
14859
15576
  ].join("\n")
14860
15577
  );
14861
15578
  request.addHelpText(
@@ -14869,7 +15586,11 @@ function createWalletCommand(deps) {
14869
15586
  " Remote relay completion:",
14870
15587
  " zk-agent wallet request relay-publish --request-id <id> --relay-url <url>",
14871
15588
  " zk-agent wallet request relay-status --request-id <id> --relay-url <url> --wait",
14872
- " zk-agent wallet request approve --request-id <id> --relay-url <url> --code <code> --wait"
15589
+ " zk-agent wallet request approve --request-id <id> --relay-url <url> --code <code> --wait",
15590
+ "",
15591
+ " If relay-status returns status = expired:",
15592
+ " zk-agent relay inspect --relay-url <url>",
15593
+ " zk-agent wallet create|reapprove --relay-url <url> --wait-relay --prompt-code"
14873
15594
  ].join("\n")
14874
15595
  );
14875
15596
  signer.addHelpText(
@@ -14968,6 +15689,23 @@ function createWalletCommand(deps) {
14968
15689
  }
14969
15690
  const relay = options.relayUrl ? await publishWalletRequestToRelay(request2, options.relayUrl) : void 0;
14970
15691
  if (relayWaitOptions) {
15692
+ if (relay) {
15693
+ printRelayWaitGuidance({
15694
+ requestId: request2.requestId,
15695
+ walletName: request2.walletName,
15696
+ relay,
15697
+ expiresAt: request2.expiresAt,
15698
+ codeEntry: relayWaitOptions.promptCode ? "prompt" : "provided",
15699
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
15700
+ request2.requestId,
15701
+ relayWaitOptions.relayUrl
15702
+ ),
15703
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
15704
+ request2.requestId,
15705
+ relayWaitOptions.relayUrl
15706
+ )
15707
+ });
15708
+ }
14971
15709
  const { payload, walletRecord } = await finalizePublishedRelayWalletRequest({
14972
15710
  walletRequest: request2,
14973
15711
  ...relayWaitOptions
@@ -15010,7 +15748,9 @@ function createWalletCommand(deps) {
15010
15748
  ["approval url", request2.approvalUrl],
15011
15749
  ...relay ? [
15012
15750
  ["share url", relay.share_url],
15013
- ["status url", relay.status_url]
15751
+ ["status url", relay.status_url],
15752
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15753
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")]
15014
15754
  ] : [],
15015
15755
  ["expires", request2.expiresAt],
15016
15756
  ["next local", buildWalletRequestAwaitLocalRecommendedCommand(request2.requestId)],
@@ -15114,6 +15854,23 @@ function createWalletCommand(deps) {
15114
15854
  }
15115
15855
  const relay = options.relayUrl ? await publishWalletRequestToRelay(request2, options.relayUrl) : void 0;
15116
15856
  if (relayWaitOptions) {
15857
+ if (relay) {
15858
+ printRelayWaitGuidance({
15859
+ requestId: request2.requestId,
15860
+ walletName: request2.walletName,
15861
+ relay,
15862
+ expiresAt: request2.expiresAt,
15863
+ codeEntry: relayWaitOptions.promptCode ? "prompt" : "provided",
15864
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
15865
+ request2.requestId,
15866
+ relayWaitOptions.relayUrl
15867
+ ),
15868
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
15869
+ request2.requestId,
15870
+ relayWaitOptions.relayUrl
15871
+ )
15872
+ });
15873
+ }
15117
15874
  const { payload, walletRecord: approvedWallet } = await finalizePublishedRelayWalletRequest({
15118
15875
  walletRequest: request2,
15119
15876
  ...relayWaitOptions
@@ -15157,7 +15914,9 @@ function createWalletCommand(deps) {
15157
15914
  ["approval url", request2.approvalUrl],
15158
15915
  ...relay ? [
15159
15916
  ["share url", relay.share_url],
15160
- ["status url", relay.status_url]
15917
+ ["status url", relay.status_url],
15918
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15919
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")]
15161
15920
  ] : [],
15162
15921
  ["expires", request2.expiresAt],
15163
15922
  ["next local", buildWalletRequestAwaitLocalRecommendedCommand(request2.requestId)],
@@ -15511,6 +16270,8 @@ function createWalletCommand(deps) {
15511
16270
  ["request", relay.request_id],
15512
16271
  ["share url", relay.share_url],
15513
16272
  ["status url", relay.status_url],
16273
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
16274
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")],
15514
16275
  ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15515
16276
  ["next approve", buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)]
15516
16277
  ],
@@ -15529,38 +16290,53 @@ function createWalletCommand(deps) {
15529
16290
  );
15530
16291
  });
15531
16292
  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) => {
15532
- const relay = options.wait ? await waitForRelayApprovalReady(options.relayUrl, options.requestId, {
15533
- timeoutMs: parsePositiveIntegerOption(options.timeoutSeconds, "--timeout-seconds", 600) * 1e3,
15534
- intervalMs: parsePositiveIntegerOption(options.intervalMs, "--interval-ms", 2e3)
15535
- }) : await fetchRelayStatus(options.relayUrl, options.requestId);
16293
+ const timeoutMs = parsePositiveIntegerOption(options.timeoutSeconds, "--timeout-seconds", 600) * 1e3;
16294
+ const intervalMs = parsePositiveIntegerOption(options.intervalMs, "--interval-ms", 2e3);
16295
+ let relay;
16296
+ try {
16297
+ relay = options.wait ? await waitForRelayApprovalReady(options.relayUrl, options.requestId, {
16298
+ timeoutMs,
16299
+ intervalMs
16300
+ }) : await fetchRelayStatus(options.relayUrl, options.requestId);
16301
+ } catch (error) {
16302
+ if (options.wait && error instanceof Error && error.message.startsWith("Timed out waiting for relay approval after ")) {
16303
+ throw buildRelayApprovalTimeoutError({
16304
+ requestId: options.requestId,
16305
+ relayUrl: options.relayUrl,
16306
+ timeoutMs,
16307
+ intervalMs
16308
+ });
16309
+ }
16310
+ throw error;
16311
+ }
16312
+ const followUp = await buildRelayStatusFollowUp({
16313
+ relay,
16314
+ relayUrl: options.relayUrl
16315
+ });
15536
16316
  printResult(
15537
16317
  [
15538
16318
  ["status", relay.status],
15539
16319
  ["request", relay.request_id],
15540
16320
  ["approval ready", relay.approval_ready ? "yes" : "no"],
15541
- ["share url", relay.approval_url],
16321
+ ["share url", relay.share_url],
16322
+ ["status url", relay.status_url],
16323
+ ["approval url", relay.approval_url],
16324
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
16325
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")],
15542
16326
  ["expires", relay.expires_at],
15543
- ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15544
- ...relay.approval_ready ? [[
15545
- "next approve",
15546
- buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)
15547
- ]] : []
16327
+ ...followUp.note ? [["note", followUp.note]] : [],
16328
+ ...Object.entries(followUp.recommendedCommands).map(
16329
+ ([label, command]) => [label, command]
16330
+ )
15548
16331
  ],
15549
16332
  {
15550
16333
  ok: true,
15551
16334
  walletRequestId: relay.request_id,
15552
16335
  relay,
15553
16336
  ...relayOutputAliases(relay),
15554
- nextAction: relay.approval_ready ? buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl) : buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15555
- recommendedCommands: {
15556
- status: buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl),
15557
- ...relay.approval_ready ? {
15558
- approve: buildWalletRequestRelayApproveRecommendedCommand(
15559
- relay.request_id,
15560
- options.relayUrl
15561
- )
15562
- } : {}
15563
- }
16337
+ nextAction: followUp.nextAction,
16338
+ recommendedCommands: followUp.recommendedCommands,
16339
+ ...followUp.note ? { note: followUp.note } : {}
15564
16340
  }
15565
16341
  );
15566
16342
  });
@@ -15573,6 +16349,24 @@ function createWalletCommand(deps) {
15573
16349
  if (options.wait && !options.relayUrl) {
15574
16350
  throw new Error("--wait is only supported together with --relay-url.");
15575
16351
  }
16352
+ if (options.wait && options.relayUrl && !shouldJsonOutput()) {
16353
+ const relayStatus2 = await fetchRelayStatus(options.relayUrl, walletRequest.requestId);
16354
+ printRelayWaitGuidance({
16355
+ requestId: walletRequest.requestId,
16356
+ walletName: walletRequest.walletName,
16357
+ relay: relayStatus2,
16358
+ expiresAt: walletRequest.expiresAt,
16359
+ codeEntry: "provided",
16360
+ statusCommand: buildWalletRequestRelayStatusRecommendedCommand(
16361
+ walletRequest.requestId,
16362
+ options.relayUrl
16363
+ ),
16364
+ approveCommand: buildWalletRequestRelayApproveRecommendedCommand(
16365
+ walletRequest.requestId,
16366
+ options.relayUrl
16367
+ )
16368
+ });
16369
+ }
15576
16370
  const payload = options.payload ? parseJsonInput(options.payload) : decryptApprovedPayloadForWalletRequest(
15577
16371
  walletRequest,
15578
16372
  options.encryptedPayload ? parseJsonInput(options.encryptedPayload) : await fetchEncryptedRelayApprovalPayload(options.relayUrl, walletRequest.requestId, {
@@ -17818,6 +18612,7 @@ async function ensureWorkflowWalletSession(input, deps) {
17818
18612
  }
17819
18613
  function workflowWalletApprovalLines(walletApproval) {
17820
18614
  if (!walletApproval) return [];
18615
+ const relayAliases = workflowWalletApprovalRelayAliases(walletApproval.relay);
17821
18616
  const lines = [
17822
18617
  ["wallet approval", walletApproval.stage],
17823
18618
  ["wallet request", walletApproval.request.requestId]
@@ -17837,6 +18632,12 @@ function workflowWalletApprovalLines(walletApproval) {
17837
18632
  if (walletApproval.relay?.status_url) {
17838
18633
  lines.push(["status url", walletApproval.relay.status_url]);
17839
18634
  }
18635
+ if (relayAliases.walletApprovalRelayShareLinkBaseUrl) {
18636
+ lines.push(["share-link base", relayAliases.walletApprovalRelayShareLinkBaseUrl]);
18637
+ }
18638
+ if (relayAliases.walletApprovalRelayStatusApiBaseUrl) {
18639
+ lines.push(["status api base", relayAliases.walletApprovalRelayStatusApiBaseUrl]);
18640
+ }
17840
18641
  if (walletApproval.stage === "request-created" && walletApproval.recommendedCommands) {
17841
18642
  lines.push(["next local", walletApproval.recommendedCommands.awaitLocal]);
17842
18643
  lines.push(["next remote", walletApproval.recommendedCommands.approve]);
@@ -17873,7 +18674,8 @@ async function printWorkflowRunCommandResult(execution) {
17873
18674
  walletName: execution.result.walletName,
17874
18675
  nextAction: execution.result.nextCommand,
17875
18676
  chain: execution.result.plan.chain,
17876
- intent: execution.result.intent
18677
+ intent: execution.result.intent,
18678
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal)
17877
18679
  });
17878
18680
  printResult(
17879
18681
  prependWorkflowRequestId(
@@ -17896,6 +18698,7 @@ async function printWorkflowRunCommandResult(execution) {
17896
18698
  result: execution.result,
17897
18699
  walletRequestId: execution.walletApproval?.request.requestId,
17898
18700
  walletApprovalRelay: execution.walletApproval?.relay,
18701
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
17899
18702
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
17900
18703
  walletApproval: serializeWalletApproval(execution.walletApproval),
17901
18704
  recommendedCommands: recommendedCommands2
@@ -17913,7 +18716,8 @@ async function printWorkflowRunCommandResult(execution) {
17913
18716
  walletName: execution.status.walletName,
17914
18717
  nextAction: execution.status.recommendedCommand,
17915
18718
  chain: execution.status.plan.chain,
17916
- intent: execution.status.intent
18719
+ intent: execution.status.intent,
18720
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
17917
18721
  });
17918
18722
  printResult(
17919
18723
  prependWorkflowRequestId(
@@ -17937,6 +18741,7 @@ async function printWorkflowRunCommandResult(execution) {
17937
18741
  checkpoint: execution.checkpoint,
17938
18742
  walletRequestId: execution.walletApproval?.request.requestId,
17939
18743
  walletApprovalRelay: execution.walletApproval?.relay,
18744
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
17940
18745
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
17941
18746
  walletApproval: serializeWalletApproval(execution.walletApproval),
17942
18747
  recommendedCommands
@@ -18184,6 +18989,7 @@ async function executeWorkflowAutoCommand(options, deps = resolveWorkflowCommand
18184
18989
  action: result ? result.stage : walletApproval?.stage ?? status.status,
18185
18990
  requestId: context.requestId,
18186
18991
  checkpointPersisted: Boolean(checkpoint),
18992
+ goal: context.goal,
18187
18993
  checkpoint,
18188
18994
  status,
18189
18995
  result,
@@ -18202,7 +19008,8 @@ async function printWorkflowAutoCommandResult(execution) {
18202
19008
  walletName: execution.status.walletName,
18203
19009
  nextAction,
18204
19010
  chain: execution.status.plan.chain,
18205
- intent: execution.status.intent
19011
+ intent: execution.status.intent,
19012
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal) ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
18206
19013
  });
18207
19014
  const summaryLines = [
18208
19015
  ["source", execution.source],
@@ -18237,6 +19044,7 @@ async function printWorkflowAutoCommandResult(execution) {
18237
19044
  checkpoint: execution.checkpoint,
18238
19045
  walletRequestId: execution.walletApproval?.request.requestId,
18239
19046
  walletApprovalRelay: execution.walletApproval?.relay,
19047
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
18240
19048
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
18241
19049
  walletApproval: serializeWalletApproval(execution.walletApproval),
18242
19050
  recommendedCommands
@@ -18263,7 +19071,21 @@ function buildWorkflowCheckpointRecommendedCommands(checkpoint) {
18263
19071
  walletStatus: buildWalletStatusRecommendedCommand(checkpoint.walletName)
18264
19072
  };
18265
19073
  }
19074
+ function extractWorkflowGoalPaymasterMode(goal) {
19075
+ if (!goal || !("paymaster" in goal)) {
19076
+ return void 0;
19077
+ }
19078
+ return goal.paymaster?.mode;
19079
+ }
19080
+ function extractPaymasterModeFromCommand(command) {
19081
+ if (!command) {
19082
+ return void 0;
19083
+ }
19084
+ const match = command.match(/--paymaster-mode (none|sponsored|approval-based)\b/);
19085
+ return match?.[1];
19086
+ }
18266
19087
  function buildWorkflowRuntimeRecommendedCommands(input) {
19088
+ const paymasterMode = extractPaymasterModeFromCommand(input.nextAction) ?? input.paymasterMode;
18267
19089
  return {
18268
19090
  inspectDefaults: "zk-agent defaults",
18269
19091
  list: buildWorkflowListRecommendedCommand(),
@@ -18287,6 +19109,10 @@ function buildWorkflowRuntimeRecommendedCommands(input) {
18287
19109
  } : {},
18288
19110
  discoverTokens: `zk-agent tokens --chain ${input.chain}`,
18289
19111
  inspectToken: `zk-agent resolve-token --chain ${input.chain} --symbol <symbol>`
19112
+ } : {},
19113
+ ...input.chain && paymasterMode === "approval-based" ? {
19114
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(input.chain),
19115
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(input.chain)
18290
19116
  } : {}
18291
19117
  };
18292
19118
  }
@@ -18304,6 +19130,10 @@ function buildWorkflowPlanRecommendedCommands(plan) {
18304
19130
  discoverOwnedTokens: buildOwnedTokensRecommendedCommand(plan.walletName),
18305
19131
  discoverTokens: buildTokensRecommendedCommand(plan.chain),
18306
19132
  inspectToken: buildResolveTokenRecommendedCommand(plan.chain)
19133
+ } : {},
19134
+ ...plan.paymasterMode === "approval-based" ? {
19135
+ discoverPaymasterTokens: buildPaymasterFeeTokensRecommendedCommand(plan.chain),
19136
+ inspectPaymasterToken: buildPaymasterFeeTokenResolveRecommendedCommand(plan.chain)
18307
19137
  } : {}
18308
19138
  };
18309
19139
  }
@@ -18367,11 +19197,29 @@ function serializeWalletApproval(walletApproval) {
18367
19197
  if (!walletApproval) return void 0;
18368
19198
  return {
18369
19199
  ...walletApproval,
19200
+ ...workflowWalletApprovalRelayAliases(walletApproval.relay, {
19201
+ shareLinkBaseField: "relayShareLinkBaseUrl",
19202
+ statusApiBaseField: "relayStatusApiBaseUrl"
19203
+ }),
18370
19204
  walletRequestId: walletApproval.request.requestId,
18371
19205
  request: sanitizeWalletRequestRecord(walletApproval.request),
18372
19206
  wallet: walletApproval.wallet ? sanitizeWalletRecord(walletApproval.wallet) : void 0
18373
19207
  };
18374
19208
  }
19209
+ function workflowWalletApprovalRelayAliases(relay, fieldNames = {
19210
+ shareLinkBaseField: "walletApprovalRelayShareLinkBaseUrl",
19211
+ statusApiBaseField: "walletApprovalRelayStatusApiBaseUrl"
19212
+ }) {
19213
+ const shareUrl = relay?.share_url;
19214
+ const statusUrl = relay?.status_url;
19215
+ const approvalUrl = relay?.approval_url;
19216
+ const shareLinkBaseUrl = shareUrl ? shareUrl.replace(/\/[^/]+$/, "") : approvalUrl ? approvalUrl.replace(/\/[^/]+$/, "") : void 0;
19217
+ const statusApiBaseUrl = statusUrl ? statusUrl.replace(/\/[^/]+$/, "") : approvalUrl && relay?.request_id ? `${approvalUrl.replace(/\/r\/[^/]+$/, "")}/api/requests` : void 0;
19218
+ return {
19219
+ [fieldNames.shareLinkBaseField]: shareLinkBaseUrl,
19220
+ [fieldNames.statusApiBaseField]: statusApiBaseUrl
19221
+ };
19222
+ }
18375
19223
  function addWorkflowGoalOptions(command, config = {}) {
18376
19224
  if (config.includeExecutionFlags) {
18377
19225
  command.option("--broadcast", "Broadcast the underlying transaction(s) instead of returning a preview", false).option(
@@ -18533,6 +19381,7 @@ async function executeWorkflowRunCommand(options, deps = resolveWorkflowCommandD
18533
19381
  if (inspection.walletApproval?.stage === "request-created" || inspection.result.status === "blocked") {
18534
19382
  return {
18535
19383
  requestId: inspection.requestId,
19384
+ goal: context.goal,
18536
19385
  status: inspection.result,
18537
19386
  walletApproval: inspection.walletApproval,
18538
19387
  checkpoint: inspection.checkpoint
@@ -18570,6 +19419,7 @@ async function executeWorkflowRunCommand(options, deps = resolveWorkflowCommandD
18570
19419
  );
18571
19420
  return {
18572
19421
  requestId: context.requestId,
19422
+ goal: context.goal,
18573
19423
  result,
18574
19424
  walletApproval
18575
19425
  };
@@ -18646,14 +19496,13 @@ function assertWorkflowResumeReady(result) {
18646
19496
  }
18647
19497
  function buildWorkflowHelpText() {
18648
19498
  return [
18649
- "",
18650
- "Default workflow path:",
18651
- " Guided default:",
18652
- " zk-agent workflow auto --wallet main --intent <intent> [goal flags] --create-checkpoint --execute-when-ready",
18653
19499
  "",
18654
19500
  " Flagship native pay path:",
18655
19501
  " zk-agent workflow pay --wallet main --to <address> --amount <amount>",
18656
19502
  "",
19503
+ " Broader multi-intent guided path:",
19504
+ " zk-agent workflow auto --wallet main --intent <intent> [goal flags] --create-checkpoint --execute-when-ready",
19505
+ "",
18657
19506
  " Checkpointed execution:",
18658
19507
  " zk-agent workflow start --wallet main --intent <intent> [goal flags]",
18659
19508
  " zk-agent workflow status --request-id <id>",
@@ -18663,13 +19512,24 @@ function buildWorkflowHelpText() {
18663
19512
  " Funding-only step:",
18664
19513
  " zk-agent workflow fund --wallet main --amount <amount> --execute",
18665
19514
  "",
19515
+ " Token/discovery recovery path:",
19516
+ " zk-agent assets --wallet main",
19517
+ " zk-agent tokens --wallet main --owned",
19518
+ " zk-agent tokens --chain zksync-sepolia",
19519
+ " zk-agent resolve-token --chain zksync-sepolia --symbol USDC",
19520
+ "",
19521
+ " Approval-based paymaster fee-token recovery:",
19522
+ " zk-agent tokens --chain zksync-sepolia --role paymaster-fee-token",
19523
+ " zk-agent resolve-token --chain zksync-sepolia --symbol <symbol> --role paymaster-fee-token",
19524
+ " zk-agent defaults",
19525
+ "",
18666
19526
  " Lower-level one-shot escape hatch:",
18667
19527
  " zk-agent workflow run --wallet main --intent <intent> [goal flags]"
18668
19528
  ].join("\n");
18669
19529
  }
18670
19530
  var WORKFLOW_HELP_COMMAND_ORDER = [
18671
- "auto",
18672
19531
  "pay",
19532
+ "auto",
18673
19533
  "start",
18674
19534
  "status",
18675
19535
  "next",
@@ -18698,7 +19558,7 @@ function applyWorkflowHelpCommandOrder(workflow) {
18698
19558
  function createWorkflowCommand(deps) {
18699
19559
  const resolvedDeps = resolveWorkflowCommandDeps(deps);
18700
19560
  const workflow = new Command10("workflow").description(
18701
- "Build a higher-level CLI workflow for a stored wallet and a concrete action intent"
19561
+ "Plan, persist, and execute higher-level wallet workflows"
18702
19562
  );
18703
19563
  workflow.addHelpText("after", buildWorkflowHelpText());
18704
19564
  workflow.command("plan").description("Plan the prerequisite and execution steps for one concrete wallet workflow").requiredOption(
@@ -18710,15 +19570,19 @@ function createWorkflowCommand(deps) {
18710
19570
  ).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(
18711
19571
  async (options) => {
18712
19572
  const intent = parseWorkflowIntent(options.intent);
19573
+ const paymasterInput = resolveWorkflowPaymasterInput(options);
18713
19574
  const { inspection, plan } = await loadWorkflowPlanState(
18714
19575
  options.wallet,
18715
19576
  intent,
18716
19577
  parseWorkflowSwapProtocol(options.protocol),
18717
19578
  options.toChain,
18718
- resolveWorkflowPaymasterInput(options),
19579
+ paymasterInput,
18719
19580
  resolvedDeps
18720
19581
  );
18721
- const recommendedCommands = buildWorkflowPlanRecommendedCommands(plan);
19582
+ const recommendedCommands = buildWorkflowPlanRecommendedCommands({
19583
+ ...plan,
19584
+ paymasterMode: paymasterInput?.mode
19585
+ });
18722
19586
  const agentProfile = await loadWorkflowAgentProfile(plan.walletName);
18723
19587
  const agentFollowup = buildAgentFollowup(agentProfile, {
18724
19588
  walletName: plan.walletName,
@@ -18936,7 +19800,8 @@ function createWorkflowCommand(deps) {
18936
19800
  walletName: inspection.result.walletName,
18937
19801
  nextAction: inspection.result.recommendedCommand,
18938
19802
  chain: inspection.result.plan.chain,
18939
- intent: inspection.result.intent
19803
+ intent: inspection.result.intent,
19804
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal)
18940
19805
  });
18941
19806
  const agentProfile = await loadWorkflowAgentProfile(inspection.result.walletName);
18942
19807
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -18965,6 +19830,7 @@ function createWorkflowCommand(deps) {
18965
19830
  checkpoint: inspection.checkpoint,
18966
19831
  walletRequestId: inspection.walletApproval?.request.requestId,
18967
19832
  walletApprovalRelay: inspection.walletApproval?.relay,
19833
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
18968
19834
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
18969
19835
  walletApproval: serializeWalletApproval(inspection.walletApproval),
18970
19836
  recommendedCommands
@@ -18986,7 +19852,8 @@ function createWorkflowCommand(deps) {
18986
19852
  walletName: inspection.result.walletName,
18987
19853
  nextAction: nextCommand,
18988
19854
  chain: inspection.result.plan.chain,
18989
- intent: inspection.result.intent
19855
+ intent: inspection.result.intent,
19856
+ paymasterMode: inspection.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(inspection.checkpoint?.goal)
18990
19857
  });
18991
19858
  const agentProfile = await loadWorkflowAgentProfile(inspection.result.walletName);
18992
19859
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -19028,6 +19895,7 @@ function createWorkflowCommand(deps) {
19028
19895
  checkpoint: inspection.checkpoint,
19029
19896
  walletRequestId: inspection.walletApproval?.request.requestId,
19030
19897
  walletApprovalRelay: inspection.walletApproval?.relay,
19898
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19031
19899
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19032
19900
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19033
19901
  recommendedCommands
@@ -19050,7 +19918,8 @@ function createWorkflowCommand(deps) {
19050
19918
  walletName: inspection.result.walletName,
19051
19919
  nextAction: inspection.result.recommendedCommand,
19052
19920
  chain: inspection.result.plan.chain,
19053
- intent: inspection.result.intent
19921
+ intent: inspection.result.intent,
19922
+ paymasterMode: inspection.walletApproval.request.requestedPaymasterMode
19054
19923
  });
19055
19924
  const agentProfile2 = await loadWorkflowAgentProfile(inspection.result.walletName);
19056
19925
  const agentFollowup2 = buildAgentFollowup(agentProfile2, {
@@ -19079,6 +19948,7 @@ function createWorkflowCommand(deps) {
19079
19948
  checkpoint: inspection.checkpoint,
19080
19949
  walletRequestId: inspection.walletApproval.request.requestId,
19081
19950
  walletApprovalRelay: inspection.walletApproval.relay,
19951
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval.relay),
19082
19952
  walletApprovalRecommendedCommands: inspection.walletApproval.recommendedCommands,
19083
19953
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19084
19954
  recommendedCommands: recommendedCommands2
@@ -19100,7 +19970,8 @@ function createWorkflowCommand(deps) {
19100
19970
  walletName: execution.status.walletName,
19101
19971
  nextAction: execution.status.recommendedCommand,
19102
19972
  chain: execution.status.plan.chain,
19103
- intent: execution.status.intent
19973
+ intent: execution.status.intent,
19974
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.checkpoint?.goal)
19104
19975
  });
19105
19976
  const agentProfile2 = await loadWorkflowAgentProfile(execution.status.walletName);
19106
19977
  const agentFollowup2 = buildAgentFollowup(agentProfile2, {
@@ -19129,6 +20000,7 @@ function createWorkflowCommand(deps) {
19129
20000
  checkpoint: execution.checkpoint,
19130
20001
  walletRequestId: execution.walletApproval?.request.requestId,
19131
20002
  walletApprovalRelay: execution.walletApproval?.relay,
20003
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
19132
20004
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
19133
20005
  walletApproval: serializeWalletApproval(execution.walletApproval),
19134
20006
  recommendedCommands: recommendedCommands2
@@ -19141,7 +20013,8 @@ function createWorkflowCommand(deps) {
19141
20013
  walletName: execution.result.walletName,
19142
20014
  nextAction: execution.result.nextCommand,
19143
20015
  chain: execution.result.plan.chain,
19144
- intent: execution.result.intent
20016
+ intent: execution.result.intent,
20017
+ paymasterMode: execution.walletApproval?.request.requestedPaymasterMode ?? extractWorkflowGoalPaymasterMode(execution.goal)
19145
20018
  });
19146
20019
  const agentProfile = await loadWorkflowAgentProfile(execution.result.walletName);
19147
20020
  const agentFollowup = buildAgentFollowup(agentProfile, {
@@ -19170,6 +20043,7 @@ function createWorkflowCommand(deps) {
19170
20043
  result: execution.result,
19171
20044
  walletRequestId: inspection.walletApproval?.request.requestId,
19172
20045
  walletApprovalRelay: inspection.walletApproval?.relay,
20046
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19173
20047
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19174
20048
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19175
20049
  recommendedCommands
@@ -19207,15 +20081,24 @@ function createWorkflowCommand(deps) {
19207
20081
  function buildDefaultOperatorPathHelpText() {
19208
20082
  return [
19209
20083
  "",
19210
- "Default operator path:",
20084
+ "Public entrypoints:",
20085
+ " Agent harness: npx skills add https://github.com/AgiWeb3/zk-agent-cli",
20086
+ " One-shot CLI: npx zk-agent-cli --help",
20087
+ " Global CLI: npm install -g zk-agent-cli",
20088
+ "",
20089
+ "Canonical terminal path:",
19211
20090
  " zk-agent setup",
19212
20091
  " zk-agent next",
19213
20092
  " zk-agent wallet create --await-local",
19214
20093
  " zk-agent next",
19215
- ` ${buildWorkflowAutoRecommendedCommand("main")}`,
20094
+ ` ${buildWorkflowPayRecommendedCommand("main")}`,
20095
+ "",
20096
+ "No custom .env is required for setup, next, or wallet create/reapprove request generation.",
20097
+ "Add RPC env vars later, before live reads or broadcasts.",
19216
20098
  "",
19217
20099
  "Use `zk-agent next --request-id <id>` to continue a stored workflow checkpoint.",
19218
- "Use `zk-agent wallet --help` for bootstrap/reapproval details and `zk-agent workflow --help` once the intent is known."
20100
+ "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.",
20101
+ "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."
19219
20102
  ].join("\n");
19220
20103
  }
19221
20104
  var ROOT_HELP_COMMAND_ORDER = [
@@ -19256,7 +20139,9 @@ function applyRootHelpCommandOrder(program) {
19256
20139
  program.commands = sortedCommands;
19257
20140
  }
19258
20141
  function createProgram() {
19259
- 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) => {
20142
+ const program = new Command11().name("zk-agent").description(
20143
+ "Local-first zkSync Era CLI for wallet approval, workflow execution, and hosted relay recovery"
20144
+ ).showHelpAfterError().option("--json", "Force JSON output for agent harnesses", false).hook("preAction", (thisCommand) => {
19260
20145
  if (thisCommand.optsWithGlobals().json) process.env.ZK_AGENT_OUTPUT = "json";
19261
20146
  });
19262
20147
  program.addCommand(createInitCommand());