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

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
@@ -10409,7 +10409,10 @@ function buildNextHelpText() {
10409
10409
  return [
10410
10410
  "",
10411
10411
  "Use `next` as the product entrypoint:",
10412
- " Fresh operator routing:",
10412
+ " Fresh local-first routing:",
10413
+ " zk-agent setup",
10414
+ " zk-agent next",
10415
+ " zk-agent wallet create --await-local",
10413
10416
  " zk-agent next",
10414
10417
  "",
10415
10418
  " Continue a stored workflow checkpoint:",
@@ -10418,6 +10421,9 @@ function buildNextHelpText() {
10418
10421
  " Stay on the wallet layer only when you need wallet-specific remediation:",
10419
10422
  " zk-agent wallet next --name main",
10420
10423
  "",
10424
+ " Switch to the hosted remote-approval path only when the browser is not colocated:",
10425
+ " zk-agent wallet --help",
10426
+ "",
10421
10427
  " Stay on the workflow layer only when you already have an explicit workflow or checkpoint:",
10422
10428
  " zk-agent workflow next --request-id <id>"
10423
10429
  ].join("\n");
@@ -11829,21 +11835,155 @@ import { Command as Command8 } from "commander";
11829
11835
  // src/lib/relay.ts
11830
11836
  import { createServer } from "node:http";
11831
11837
  import fs7 from "node:fs";
11832
- import path6 from "node:path";
11838
+ import path7 from "node:path";
11833
11839
  import { fileURLToPath as fileURLToPath3 } from "node:url";
11840
+
11841
+ // src/lib/http.ts
11842
+ import { spawn } from "node:child_process";
11843
+ import { mkdtemp, readFile as readFile2, rm as rm2 } from "node:fs/promises";
11844
+ import os2 from "node:os";
11845
+ import path6 from "node:path";
11846
+ function isDnsResolutionFailure(error) {
11847
+ if (!error || typeof error !== "object") {
11848
+ return false;
11849
+ }
11850
+ const cause = "cause" in error ? error.cause : void 0;
11851
+ if (!cause || typeof cause !== "object") {
11852
+ return false;
11853
+ }
11854
+ const code = "code" in cause ? cause.code : void 0;
11855
+ return code === "ENOTFOUND" || code === "EAI_AGAIN";
11856
+ }
11857
+ function isHttpUrl(value) {
11858
+ return value.startsWith("http://") || value.startsWith("https://");
11859
+ }
11860
+ function parseCurlHeaders(rawHeaders) {
11861
+ const blocks = rawHeaders.replace(/\r\n/g, "\n").split(/\n{2,}/).map((block) => block.trim()).filter((block) => block.startsWith("HTTP/"));
11862
+ const lastBlock = blocks.at(-1);
11863
+ if (!lastBlock) {
11864
+ throw new Error("curl fallback did not emit an HTTP response header block.");
11865
+ }
11866
+ const [statusLine, ...headerLines] = lastBlock.split("\n");
11867
+ const status = Number.parseInt(statusLine.split(/\s+/)[1] || "", 10);
11868
+ if (!Number.isInteger(status)) {
11869
+ throw new Error(`curl fallback emitted an invalid status line: ${statusLine}`);
11870
+ }
11871
+ const headers = new Headers();
11872
+ for (const line of headerLines) {
11873
+ const separator = line.indexOf(":");
11874
+ if (separator <= 0) continue;
11875
+ const key = line.slice(0, separator).trim();
11876
+ const value = line.slice(separator + 1).trim();
11877
+ if (key) {
11878
+ headers.append(key, value);
11879
+ }
11880
+ }
11881
+ return { status, headers };
11882
+ }
11883
+ async function runCurlRequest(url, options) {
11884
+ const tempDir = await mkdtemp(path6.join(os2.tmpdir(), "zk-agent-http-"));
11885
+ const headersPath = path6.join(tempDir, "headers.txt");
11886
+ const bodyPath = path6.join(tempDir, "body.txt");
11887
+ try {
11888
+ const args = [
11889
+ "--silent",
11890
+ "--show-error",
11891
+ "--output",
11892
+ bodyPath,
11893
+ "--dump-header",
11894
+ headersPath,
11895
+ "--request",
11896
+ options.method || "GET"
11897
+ ];
11898
+ if (options.redirect === "follow") {
11899
+ args.push("--location");
11900
+ }
11901
+ for (const [key, value] of Object.entries(options.headers || {})) {
11902
+ args.push("--header", `${key}: ${value}`);
11903
+ }
11904
+ if (typeof options.body === "string") {
11905
+ args.push("--data-raw", options.body);
11906
+ }
11907
+ args.push(url);
11908
+ const child = spawn("curl", args, {
11909
+ env: process.env,
11910
+ stdio: ["ignore", "pipe", "pipe"]
11911
+ });
11912
+ let stdout = "";
11913
+ let stderr = "";
11914
+ child.stdout.setEncoding("utf8");
11915
+ child.stderr.setEncoding("utf8");
11916
+ child.stdout.on("data", (chunk) => {
11917
+ stdout += chunk;
11918
+ });
11919
+ child.stderr.on("data", (chunk) => {
11920
+ stderr += chunk;
11921
+ });
11922
+ const exitCode = await new Promise((resolve, reject) => {
11923
+ child.once("error", reject);
11924
+ child.once("close", resolve);
11925
+ });
11926
+ if (exitCode !== 0) {
11927
+ throw new Error(stderr.trim() || stdout.trim() || `curl exited with code ${exitCode}`);
11928
+ }
11929
+ const [rawHeaders, body] = await Promise.all([
11930
+ readFile2(headersPath, "utf8"),
11931
+ readFile2(bodyPath, "utf8")
11932
+ ]);
11933
+ const { status, headers } = parseCurlHeaders(rawHeaders);
11934
+ return {
11935
+ ok: status >= 200 && status < 300,
11936
+ status,
11937
+ headers,
11938
+ body
11939
+ };
11940
+ } finally {
11941
+ await rm2(tempDir, { recursive: true, force: true });
11942
+ }
11943
+ }
11944
+ async function fetchTextWithFallback(url, options = {}) {
11945
+ try {
11946
+ const response = await fetch(url, {
11947
+ method: options.method,
11948
+ headers: options.headers,
11949
+ body: options.body,
11950
+ redirect: options.redirect
11951
+ });
11952
+ return {
11953
+ ok: response.ok,
11954
+ status: response.status,
11955
+ headers: response.headers,
11956
+ body: await response.text()
11957
+ };
11958
+ } catch (error) {
11959
+ if (!isHttpUrl(url) || !isDnsResolutionFailure(error)) {
11960
+ throw error;
11961
+ }
11962
+ return await runCurlRequest(url, options);
11963
+ }
11964
+ }
11965
+ async function fetchJsonWithFallback(url, options = {}) {
11966
+ const response = await fetchTextWithFallback(url, options);
11967
+ return {
11968
+ ...response,
11969
+ json: JSON.parse(response.body)
11970
+ };
11971
+ }
11972
+
11973
+ // src/lib/relay.ts
11834
11974
  var RELAY_BODY_LIMIT_BYTES = 1024 * 1024;
11835
11975
  var RELAY_SERVICE = "zk-agent-relay";
11836
11976
  var RELAY_PROTOCOL = "zk-agent-session-relay";
11837
11977
  var RELAY_SCHEMA_VERSION = 1;
11838
11978
  function relayDir() {
11839
- const directory = path6.join(storageDir(), "relay");
11979
+ const directory = path7.join(storageDir(), "relay");
11840
11980
  if (!fs7.existsSync(directory)) {
11841
11981
  fs7.mkdirSync(directory, { recursive: true, mode: 448 });
11842
11982
  }
11843
11983
  return directory;
11844
11984
  }
11845
11985
  function relayRecordPath(requestId) {
11846
- return path6.join(relayDir(), `${requestId}.json`);
11986
+ return path7.join(relayDir(), `${requestId}.json`);
11847
11987
  }
11848
11988
  function writeRelayRecord(record) {
11849
11989
  fs7.writeFileSync(relayRecordPath(record.request_id), JSON.stringify(record, null, 2), {
@@ -11925,14 +12065,14 @@ function writeBody(response, statusCode, contentType, value, extraHeaders = {})
11925
12065
  }
11926
12066
  function resolveConnectorUiDistRoot() {
11927
12067
  const currentFile = fileURLToPath3(import.meta.url);
11928
- const currentDir2 = path6.dirname(currentFile);
12068
+ const currentDir2 = path7.dirname(currentFile);
11929
12069
  const candidates = [
11930
- path6.resolve(currentDir2, "./connector-ui"),
11931
- path6.resolve(currentDir2, "../../../zk-connector-ui/dist"),
11932
- path6.resolve(currentDir2, "../../zk-connector-ui/dist")
12070
+ path7.resolve(currentDir2, "./connector-ui"),
12071
+ path7.resolve(currentDir2, "../../../zk-connector-ui/dist"),
12072
+ path7.resolve(currentDir2, "../../zk-connector-ui/dist")
11933
12073
  ];
11934
12074
  for (const candidate of candidates) {
11935
- if (fs7.existsSync(path6.join(candidate, "index.html"))) {
12075
+ if (fs7.existsSync(path7.join(candidate, "index.html"))) {
11936
12076
  return candidate;
11937
12077
  }
11938
12078
  }
@@ -11961,7 +12101,7 @@ function relayCapabilities(connectorUiAvailable) {
11961
12101
  ...connectorUiAvailable ? ["connector-ui"] : []
11962
12102
  ];
11963
12103
  }
11964
- function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable) {
12104
+ function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable, publicOriginSource) {
11965
12105
  return {
11966
12106
  ok: true,
11967
12107
  service: RELAY_SERVICE,
@@ -11970,6 +12110,7 @@ function relayHealthResponse(bindBaseUrl, publicBaseUrl, connectorUiAvailable) {
11970
12110
  relay_mode: "local-file",
11971
12111
  origin: normalizeRelayBaseUrl(bindBaseUrl),
11972
12112
  public_origin: normalizeRelayBaseUrl(publicBaseUrl),
12113
+ public_origin_source: publicOriginSource,
11973
12114
  connector_ui_available: connectorUiAvailable,
11974
12115
  capabilities: relayCapabilities(connectorUiAvailable)
11975
12116
  };
@@ -11984,31 +12125,34 @@ function relayApprovalUrl(baseUrl, requestId) {
11984
12125
  return `${relayStatusUrl(baseUrl, requestId)}/approval`;
11985
12126
  }
11986
12127
  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
- });
12128
+ const response = await fetchJsonWithFallback(
12129
+ `${normalizeRelayBaseUrl(baseUrl)}/api/requests`,
12130
+ {
12131
+ method: "POST",
12132
+ headers: {
12133
+ "Content-Type": "application/json"
12134
+ },
12135
+ body: JSON.stringify(body)
12136
+ }
12137
+ );
11994
12138
  if (!response.ok) {
11995
12139
  throw new Error(`Relay publish failed with status ${response.status}`);
11996
12140
  }
11997
- return await response.json();
12141
+ return response.json;
11998
12142
  }
11999
12143
  async function fetchRelayStatus(baseUrl, requestId) {
12000
- const response = await fetch(relayStatusUrl(baseUrl, requestId));
12144
+ const response = await fetchJsonWithFallback(relayStatusUrl(baseUrl, requestId));
12001
12145
  if (!response.ok) {
12002
12146
  throw new Error(`Relay status fetch failed with status ${response.status}`);
12003
12147
  }
12004
- return await response.json();
12148
+ return response.json;
12005
12149
  }
12006
12150
  async function fetchRelayHealth(baseUrl) {
12007
- const response = await fetch(`${normalizeRelayBaseUrl(baseUrl)}/health`);
12151
+ const response = await fetchJsonWithFallback(`${normalizeRelayBaseUrl(baseUrl)}/health`);
12008
12152
  if (!response.ok) {
12009
12153
  throw new Error(`Relay health fetch failed with status ${response.status}`);
12010
12154
  }
12011
- return await response.json();
12155
+ return response.json;
12012
12156
  }
12013
12157
  function sleep(ms) {
12014
12158
  return new Promise((resolve) => {
@@ -12033,15 +12177,16 @@ async function waitForRelayApprovalReady(baseUrl, requestId, options) {
12033
12177
  );
12034
12178
  }
12035
12179
  async function fetchRelayApproval(baseUrl, requestId) {
12036
- const response = await fetch(relayApprovalUrl(baseUrl, requestId));
12180
+ const response = await fetchJsonWithFallback(relayApprovalUrl(baseUrl, requestId));
12037
12181
  if (!response.ok) {
12038
12182
  throw new Error(`Relay approval fetch failed with status ${response.status}`);
12039
12183
  }
12040
- return await response.json();
12184
+ return response.json;
12041
12185
  }
12042
12186
  async function startRelayServer(options) {
12043
12187
  const uiDistRoot = resolveConnectorUiDistRoot();
12044
12188
  let bindBaseUrl = "";
12189
+ const publicOriginSource = options.publicOrigin?.trim() ? "configured" : "bind-origin-default";
12045
12190
  const server = createServer(async (request, response) => {
12046
12191
  try {
12047
12192
  const requestUrl = new URL(request.url || "/", "http://localhost");
@@ -12056,7 +12201,12 @@ async function startRelayServer(options) {
12056
12201
  writeJson2(
12057
12202
  response,
12058
12203
  200,
12059
- relayHealthResponse(bindBaseUrl, publicBaseUrl, Boolean(uiDistRoot))
12204
+ relayHealthResponse(
12205
+ bindBaseUrl,
12206
+ publicBaseUrl,
12207
+ Boolean(uiDistRoot),
12208
+ publicOriginSource
12209
+ )
12060
12210
  );
12061
12211
  return;
12062
12212
  }
@@ -12125,7 +12275,7 @@ async function startRelayServer(options) {
12125
12275
  }
12126
12276
  if (method === "GET" && uiDistRoot) {
12127
12277
  const relativePath = pathname === "/" ? "/index.html" : pathname;
12128
- const filePath = path6.resolve(uiDistRoot, `.${relativePath}`);
12278
+ const filePath = path7.resolve(uiDistRoot, `.${relativePath}`);
12129
12279
  if (!filePath.startsWith(uiDistRoot)) {
12130
12280
  writeJson2(response, 403, { error: "Forbidden path" });
12131
12281
  return;
@@ -12134,7 +12284,7 @@ async function startRelayServer(options) {
12134
12284
  writeBody(response, 200, contentTypeFor(filePath), fs7.readFileSync(filePath));
12135
12285
  return;
12136
12286
  }
12137
- const indexPath = path6.join(uiDistRoot, "index.html");
12287
+ const indexPath = path7.join(uiDistRoot, "index.html");
12138
12288
  if (fs7.existsSync(indexPath)) {
12139
12289
  writeBody(response, 200, "text/html; charset=utf-8", fs7.readFileSync(indexPath));
12140
12290
  return;
@@ -12181,8 +12331,14 @@ async function startRelayServer(options) {
12181
12331
  // src/commands/relay.ts
12182
12332
  function buildRelayServeRecommendedCommands(relayUrl) {
12183
12333
  return {
12184
- createWallet: `zk-agent wallet create --relay-url ${relayUrl}`,
12185
- reapproveWallet: `zk-agent wallet reapprove --name main --relay-url ${relayUrl}`
12334
+ createWallet: `zk-agent wallet create --relay-url ${relayUrl} --wait-relay --prompt-code`,
12335
+ reapproveWallet: `zk-agent wallet reapprove --name main --relay-url ${relayUrl} --wait-relay --prompt-code`
12336
+ };
12337
+ }
12338
+ function buildAdvertisedRelayBases(publicOrigin) {
12339
+ return {
12340
+ shareLinkBaseUrl: `${publicOrigin}/r`,
12341
+ statusApiBaseUrl: `${publicOrigin}/api/requests`
12186
12342
  };
12187
12343
  }
12188
12344
  function isRecord3(value) {
@@ -12198,6 +12354,9 @@ function isRelayCapability(value) {
12198
12354
  "connector-ui"
12199
12355
  ].includes(String(value));
12200
12356
  }
12357
+ function isRelayPublicOriginSource(value) {
12358
+ return value === "configured" || value === "bind-origin-default";
12359
+ }
12201
12360
  function asRelayHealthResponse(value) {
12202
12361
  if (!isRecord3(value)) return null;
12203
12362
  if (value.ok !== true) return null;
@@ -12207,6 +12366,9 @@ function asRelayHealthResponse(value) {
12207
12366
  if (value.relay_mode !== "local-file") return null;
12208
12367
  if (typeof value.origin !== "string") return null;
12209
12368
  if (typeof value.public_origin !== "string") return null;
12369
+ if (typeof value.public_origin_source !== "undefined" && !isRelayPublicOriginSource(value.public_origin_source)) {
12370
+ return null;
12371
+ }
12210
12372
  if (typeof value.connector_ui_available !== "boolean") return null;
12211
12373
  if (!Array.isArray(value.capabilities) || !value.capabilities.every(isRelayCapability)) {
12212
12374
  return null;
@@ -12225,6 +12387,29 @@ function relayPublicOriginLooksLocal(origin) {
12225
12387
  return false;
12226
12388
  }
12227
12389
  }
12390
+ function normalizeComparableRelayUrl(value) {
12391
+ try {
12392
+ return new URL(value).toString().replace(/\/+$/, "");
12393
+ } catch {
12394
+ return null;
12395
+ }
12396
+ }
12397
+ function relayUrlMatches(left, right) {
12398
+ const normalizedLeft = normalizeComparableRelayUrl(left);
12399
+ const normalizedRight = right ? normalizeComparableRelayUrl(right) : null;
12400
+ if (normalizedLeft === null || normalizedRight === null) {
12401
+ return null;
12402
+ }
12403
+ return normalizedLeft === normalizedRight;
12404
+ }
12405
+ function inferRelayPublicOriginSource(options) {
12406
+ const normalizedOrigin = options.origin ? normalizeComparableRelayUrl(options.origin) : null;
12407
+ const normalizedPublicOrigin = normalizeComparableRelayUrl(options.publicOrigin);
12408
+ if (!normalizedOrigin || !normalizedPublicOrigin) {
12409
+ return null;
12410
+ }
12411
+ return normalizedOrigin === normalizedPublicOrigin ? "bind-origin-default" : "configured";
12412
+ }
12228
12413
  function relayHostedReadinessNotes(options) {
12229
12414
  const notes = [];
12230
12415
  if (!options.compatible) {
@@ -12235,7 +12420,7 @@ function relayHostedReadinessNotes(options) {
12235
12420
  }
12236
12421
  if (relayPublicOriginLooksLocal(options.publicOrigin)) {
12237
12422
  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."
12423
+ 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
12424
  );
12240
12425
  }
12241
12426
  if (options.connectorUiAvailable === false) {
@@ -12245,19 +12430,50 @@ function relayHostedReadinessNotes(options) {
12245
12430
  }
12246
12431
  return notes;
12247
12432
  }
12433
+ function relayOriginRelationshipNotes(options) {
12434
+ const notes = [];
12435
+ const relayUrlMatchesOrigin = relayUrlMatches(options.relayUrl, options.origin);
12436
+ const relayUrlMatchesPublicOrigin = relayUrlMatches(options.relayUrl, options.publicOrigin);
12437
+ if (relayUrlMatchesOrigin === false) {
12438
+ notes.push(
12439
+ "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."
12440
+ );
12441
+ }
12442
+ if (relayUrlMatchesPublicOrigin === false) {
12443
+ notes.push(
12444
+ "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."
12445
+ );
12446
+ }
12447
+ return notes;
12448
+ }
12248
12449
  function buildRelayInspectPayload(relayUrl, rawHealth) {
12249
12450
  const health = asRelayHealthResponse(rawHealth);
12250
12451
  const fallbackPublicOrigin = isRecord3(rawHealth) && typeof rawHealth.public_origin === "string" ? rawHealth.public_origin : relayUrl;
12251
12452
  const publicOrigin = health?.public_origin || fallbackPublicOrigin;
12453
+ const publicOriginSource = health?.public_origin_source || inferRelayPublicOriginSource({
12454
+ origin: health?.origin || null,
12455
+ publicOrigin
12456
+ });
12252
12457
  const compatible = Boolean(health && hasCoreRelayCapabilities(health.capabilities));
12253
12458
  const connectorUiAvailable = health?.connector_ui_available ?? null;
12459
+ const relayUrlMatchesOrigin = relayUrlMatches(relayUrl, health?.origin || null);
12460
+ const relayUrlMatchesPublicOrigin = relayUrlMatches(relayUrl, publicOrigin);
12254
12461
  const publicOriginLooksLocal = relayPublicOriginLooksLocal(publicOrigin);
12255
12462
  const hostedShareRedirectReady = compatible && connectorUiAvailable === true && !publicOriginLooksLocal;
12256
- const notes = relayHostedReadinessNotes({
12257
- compatible,
12258
- publicOrigin,
12259
- connectorUiAvailable
12260
- });
12463
+ const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
12464
+ const notes = [
12465
+ ...relayHostedReadinessNotes({
12466
+ compatible,
12467
+ publicOrigin,
12468
+ publicOriginSource,
12469
+ connectorUiAvailable
12470
+ }),
12471
+ ...relayOriginRelationshipNotes({
12472
+ relayUrl,
12473
+ origin: health?.origin || null,
12474
+ publicOrigin
12475
+ })
12476
+ ];
12261
12477
  return {
12262
12478
  ok: true,
12263
12479
  status: "relay-inspected",
@@ -12269,6 +12485,11 @@ function buildRelayInspectPayload(relayUrl, rawHealth) {
12269
12485
  relayMode: health?.relay_mode || null,
12270
12486
  origin: health?.origin || null,
12271
12487
  publicOrigin,
12488
+ publicOriginSource,
12489
+ shareLinkBaseUrl,
12490
+ statusApiBaseUrl,
12491
+ relayUrlMatchesOrigin,
12492
+ relayUrlMatchesPublicOrigin,
12272
12493
  publicOriginLooksLocal,
12273
12494
  connectorUiAvailable,
12274
12495
  hostedShareRedirectReady,
@@ -12294,13 +12515,16 @@ function createRelayCommand() {
12294
12515
  publicOrigin: options.publicOrigin?.trim()
12295
12516
  });
12296
12517
  const publicOrigin = options.publicOrigin?.trim() || server.origin;
12518
+ const publicOriginSource = options.publicOrigin?.trim() ? "configured" : "bind-origin-default";
12297
12519
  const publicOriginLooksLocal = relayPublicOriginLooksLocal(publicOrigin);
12298
12520
  const connectorUiAvailable = server.connectorUiAvailable;
12299
12521
  const hostedShareRedirectReady = connectorUiAvailable && !publicOriginLooksLocal;
12522
+ const { shareLinkBaseUrl, statusApiBaseUrl } = buildAdvertisedRelayBases(publicOrigin);
12300
12523
  const recommendedCommands = buildRelayServeRecommendedCommands(publicOrigin);
12301
12524
  const notes = relayHostedReadinessNotes({
12302
12525
  compatible: true,
12303
12526
  publicOrigin,
12527
+ publicOriginSource,
12304
12528
  connectorUiAvailable
12305
12529
  });
12306
12530
  const payload = {
@@ -12308,6 +12532,9 @@ function createRelayCommand() {
12308
12532
  status: "relay-serving",
12309
12533
  origin: server.origin,
12310
12534
  publicOrigin,
12535
+ publicOriginSource,
12536
+ shareLinkBaseUrl,
12537
+ statusApiBaseUrl,
12311
12538
  publicOriginLooksLocal,
12312
12539
  port: server.port,
12313
12540
  healthUrl: `${server.origin}/health`,
@@ -12334,6 +12561,9 @@ function createRelayCommand() {
12334
12561
  if (publicOrigin !== server.origin) {
12335
12562
  humanLine("public origin", publicOrigin);
12336
12563
  }
12564
+ humanLine("public origin source", publicOriginSource);
12565
+ humanLine("share-link base", shareLinkBaseUrl);
12566
+ humanLine("status api base", statusApiBaseUrl);
12337
12567
  humanLine("health", `${server.origin}/health`);
12338
12568
  humanLine("hosted ready", hostedShareRedirectReady ? "yes" : "no");
12339
12569
  if (connectorUiAvailable !== null) {
@@ -12385,6 +12615,20 @@ function createRelayCommand() {
12385
12615
  if (payload.publicOrigin) {
12386
12616
  humanLine("public origin", payload.publicOrigin);
12387
12617
  }
12618
+ if (payload.publicOriginSource) {
12619
+ humanLine("public origin source", payload.publicOriginSource);
12620
+ }
12621
+ humanLine("share-link base", payload.shareLinkBaseUrl);
12622
+ humanLine("status api base", payload.statusApiBaseUrl);
12623
+ if (payload.relayUrlMatchesOrigin !== null) {
12624
+ humanLine("relay url matches origin", payload.relayUrlMatchesOrigin ? "yes" : "no");
12625
+ }
12626
+ if (payload.relayUrlMatchesPublicOrigin !== null) {
12627
+ humanLine(
12628
+ "relay url matches public origin",
12629
+ payload.relayUrlMatchesPublicOrigin ? "yes" : "no"
12630
+ );
12631
+ }
12388
12632
  humanLine("public origin local", payload.publicOriginLooksLocal ? "yes" : "no");
12389
12633
  if (payload.connectorUiAvailable !== null) {
12390
12634
  humanLine("connector ui", payload.connectorUiAvailable ? "available" : "missing");
@@ -12400,10 +12644,9 @@ function createRelayCommand() {
12400
12644
  if (payload.recommendedCommands.reapproveWallet) {
12401
12645
  humanLine("reapprove wallet", payload.recommendedCommands.reapproveWallet);
12402
12646
  }
12403
- } else {
12404
- for (const note of payload.notes) {
12405
- humanLine("note", note);
12406
- }
12647
+ }
12648
+ for (const note of payload.notes) {
12649
+ humanLine("note", note);
12407
12650
  }
12408
12651
  });
12409
12652
  return relay;
@@ -12416,12 +12659,12 @@ import { Command as Command9 } from "commander";
12416
12659
 
12417
12660
  // ../account-profiles/src/profiles.ts
12418
12661
  import fs8 from "node:fs";
12419
- import path7 from "node:path";
12662
+ import path8 from "node:path";
12420
12663
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12421
12664
  var PACKAGE_NAME = "@zk-agent/account-profiles";
12422
12665
  var PACKAGE_ROOT_FALLBACK = `<${PACKAGE_NAME} package root unavailable>`;
12423
12666
  function isExpectedPackageRoot(candidate) {
12424
- const manifestPath = path7.join(candidate, "package.json");
12667
+ const manifestPath = path8.join(candidate, "package.json");
12425
12668
  if (!fs8.existsSync(manifestPath)) return false;
12426
12669
  try {
12427
12670
  const manifest = JSON.parse(fs8.readFileSync(manifestPath, "utf8"));
@@ -12431,19 +12674,19 @@ function isExpectedPackageRoot(candidate) {
12431
12674
  }
12432
12675
  }
12433
12676
  function resolvePackageRoot() {
12434
- const moduleRoot = path7.resolve(path7.dirname(fileURLToPath4(import.meta.url)), "..");
12677
+ const moduleRoot = path8.resolve(path8.dirname(fileURLToPath4(import.meta.url)), "..");
12435
12678
  const cwd = process.cwd();
12436
12679
  const candidates = [
12437
12680
  process.env.ZK_AGENT_ACCOUNT_PROFILES_ROOT,
12438
- path7.join(moduleRoot, "dist", "builtin-account-profiles"),
12681
+ path8.join(moduleRoot, "dist", "builtin-account-profiles"),
12439
12682
  moduleRoot,
12440
- path7.join(cwd, "packages", "account-profiles"),
12441
- path7.join(cwd, "account-profiles"),
12683
+ path8.join(cwd, "packages", "account-profiles"),
12684
+ path8.join(cwd, "account-profiles"),
12442
12685
  cwd
12443
12686
  ].filter((value) => typeof value === "string" && value.trim().length > 0);
12444
12687
  const seen = /* @__PURE__ */ new Set();
12445
12688
  for (const candidate of candidates) {
12446
- const normalized = path7.resolve(candidate);
12689
+ const normalized = path8.resolve(candidate);
12447
12690
  if (seen.has(normalized)) continue;
12448
12691
  seen.add(normalized);
12449
12692
  if (isExpectedPackageRoot(normalized)) {
@@ -12515,11 +12758,11 @@ function missingArtifactError(profileId, artifactPath2) {
12515
12758
  }
12516
12759
  function contractPath(...segments) {
12517
12760
  const packageRoot = tryResolvePackageRoot();
12518
- return packageRoot ? path7.join(packageRoot, "contracts", ...segments) : path7.join(PACKAGE_ROOT_FALLBACK, "contracts", ...segments);
12761
+ return packageRoot ? path8.join(packageRoot, "contracts", ...segments) : path8.join(PACKAGE_ROOT_FALLBACK, "contracts", ...segments);
12519
12762
  }
12520
12763
  function artifactPath(...segments) {
12521
12764
  const packageRoot = tryResolvePackageRoot();
12522
- return packageRoot ? path7.join(packageRoot, "artifacts", ...segments) : path7.join(PACKAGE_ROOT_FALLBACK, "artifacts", ...segments);
12765
+ return packageRoot ? path8.join(packageRoot, "artifacts", ...segments) : path8.join(PACKAGE_ROOT_FALLBACK, "artifacts", ...segments);
12523
12766
  }
12524
12767
  function createDailySpendLimitProfile() {
12525
12768
  const resolvedArtifactPath = artifactPath("daily-spend-limit", "Account.json");
@@ -13005,11 +13248,16 @@ function sanitizeWalletRecord(wallet) {
13005
13248
  function relayOutputAliases(relay) {
13006
13249
  const shareUrl = relay && "share_url" in relay ? relay.share_url : void 0;
13007
13250
  const statusUrl = relay && "status_url" in relay ? relay.status_url : void 0;
13251
+ const approvalUrl = relay?.approval_url;
13252
+ const shareLinkBaseUrl = shareUrl ? shareUrl.replace(/\/[^/]+$/, "") : approvalUrl ? approvalUrl.replace(/\/[^/]+$/, "") : void 0;
13253
+ const statusApiBaseUrl = statusUrl ? statusUrl.replace(/\/[^/]+$/, "") : approvalUrl && relay?.request_id ? `${approvalUrl.replace(/\/r\/[^/]+$/, "")}/api/requests` : void 0;
13008
13254
  return {
13009
13255
  relayRequestId: relay?.request_id,
13010
13256
  relayShareUrl: shareUrl,
13011
13257
  relayStatusUrl: statusUrl,
13012
- relayApprovalUrl: relay?.approval_url
13258
+ relayApprovalUrl: approvalUrl,
13259
+ relayShareLinkBaseUrl: shareLinkBaseUrl,
13260
+ relayStatusApiBaseUrl: statusApiBaseUrl
13013
13261
  };
13014
13262
  }
13015
13263
  function sanitizeWalletRequestRecord(request) {
@@ -14836,13 +15084,14 @@ function createWalletCommand(deps) {
14836
15084
  "after",
14837
15085
  [
14838
15086
  "",
14839
- "Default wallet path:",
15087
+ "Local-first wallet path:",
14840
15088
  " First bootstrap:",
14841
15089
  " zk-agent wallet create --await-local",
14842
15090
  " zk-agent next",
14843
15091
  "",
14844
15092
  " Restore approval metadata for an existing wallet:",
14845
15093
  " zk-agent wallet reapprove --name main --await-local",
15094
+ " zk-agent next",
14846
15095
  "",
14847
15096
  " Attach a local signer when approval is still present:",
14848
15097
  " zk-agent wallet signer attach --name main --private-key <hex>",
@@ -14852,7 +15101,7 @@ function createWalletCommand(deps) {
14852
15101
  " zk-agent wallet status --name main",
14853
15102
  " zk-agent wallet next --name main",
14854
15103
  "",
14855
- " Remote approval path:",
15104
+ " Hosted remote approval path:",
14856
15105
  " zk-agent relay inspect --relay-url <url>",
14857
15106
  " zk-agent wallet create --relay-url <url> --wait-relay --prompt-code",
14858
15107
  " zk-agent wallet reapprove --name main --relay-url <url> --wait-relay --prompt-code"
@@ -15010,7 +15259,9 @@ function createWalletCommand(deps) {
15010
15259
  ["approval url", request2.approvalUrl],
15011
15260
  ...relay ? [
15012
15261
  ["share url", relay.share_url],
15013
- ["status url", relay.status_url]
15262
+ ["status url", relay.status_url],
15263
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15264
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")]
15014
15265
  ] : [],
15015
15266
  ["expires", request2.expiresAt],
15016
15267
  ["next local", buildWalletRequestAwaitLocalRecommendedCommand(request2.requestId)],
@@ -15157,7 +15408,9 @@ function createWalletCommand(deps) {
15157
15408
  ["approval url", request2.approvalUrl],
15158
15409
  ...relay ? [
15159
15410
  ["share url", relay.share_url],
15160
- ["status url", relay.status_url]
15411
+ ["status url", relay.status_url],
15412
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15413
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")]
15161
15414
  ] : [],
15162
15415
  ["expires", request2.expiresAt],
15163
15416
  ["next local", buildWalletRequestAwaitLocalRecommendedCommand(request2.requestId)],
@@ -15511,6 +15764,8 @@ function createWalletCommand(deps) {
15511
15764
  ["request", relay.request_id],
15512
15765
  ["share url", relay.share_url],
15513
15766
  ["status url", relay.status_url],
15767
+ ["share-link base", relay.share_url.replace(/\/[^/]+$/, "")],
15768
+ ["status api base", relay.status_url.replace(/\/[^/]+$/, "")],
15514
15769
  ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15515
15770
  ["next approve", buildWalletRequestRelayApproveRecommendedCommand(relay.request_id, options.relayUrl)]
15516
15771
  ],
@@ -15539,6 +15794,8 @@ function createWalletCommand(deps) {
15539
15794
  ["request", relay.request_id],
15540
15795
  ["approval ready", relay.approval_ready ? "yes" : "no"],
15541
15796
  ["share url", relay.approval_url],
15797
+ ["share-link base", relay.approval_url.replace(/\/[^/]+$/, "")],
15798
+ ["status api base", `${relay.approval_url.replace(/\/r\/[^/]+$/, "")}/api/requests`],
15542
15799
  ["expires", relay.expires_at],
15543
15800
  ["next status", buildWalletRequestRelayStatusRecommendedCommand(relay.request_id, options.relayUrl)],
15544
15801
  ...relay.approval_ready ? [[
@@ -17818,6 +18075,7 @@ async function ensureWorkflowWalletSession(input, deps) {
17818
18075
  }
17819
18076
  function workflowWalletApprovalLines(walletApproval) {
17820
18077
  if (!walletApproval) return [];
18078
+ const relayAliases = workflowWalletApprovalRelayAliases(walletApproval.relay);
17821
18079
  const lines = [
17822
18080
  ["wallet approval", walletApproval.stage],
17823
18081
  ["wallet request", walletApproval.request.requestId]
@@ -17837,6 +18095,12 @@ function workflowWalletApprovalLines(walletApproval) {
17837
18095
  if (walletApproval.relay?.status_url) {
17838
18096
  lines.push(["status url", walletApproval.relay.status_url]);
17839
18097
  }
18098
+ if (relayAliases.walletApprovalRelayShareLinkBaseUrl) {
18099
+ lines.push(["share-link base", relayAliases.walletApprovalRelayShareLinkBaseUrl]);
18100
+ }
18101
+ if (relayAliases.walletApprovalRelayStatusApiBaseUrl) {
18102
+ lines.push(["status api base", relayAliases.walletApprovalRelayStatusApiBaseUrl]);
18103
+ }
17840
18104
  if (walletApproval.stage === "request-created" && walletApproval.recommendedCommands) {
17841
18105
  lines.push(["next local", walletApproval.recommendedCommands.awaitLocal]);
17842
18106
  lines.push(["next remote", walletApproval.recommendedCommands.approve]);
@@ -17896,6 +18160,7 @@ async function printWorkflowRunCommandResult(execution) {
17896
18160
  result: execution.result,
17897
18161
  walletRequestId: execution.walletApproval?.request.requestId,
17898
18162
  walletApprovalRelay: execution.walletApproval?.relay,
18163
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
17899
18164
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
17900
18165
  walletApproval: serializeWalletApproval(execution.walletApproval),
17901
18166
  recommendedCommands: recommendedCommands2
@@ -17937,6 +18202,7 @@ async function printWorkflowRunCommandResult(execution) {
17937
18202
  checkpoint: execution.checkpoint,
17938
18203
  walletRequestId: execution.walletApproval?.request.requestId,
17939
18204
  walletApprovalRelay: execution.walletApproval?.relay,
18205
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
17940
18206
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
17941
18207
  walletApproval: serializeWalletApproval(execution.walletApproval),
17942
18208
  recommendedCommands
@@ -18237,6 +18503,7 @@ async function printWorkflowAutoCommandResult(execution) {
18237
18503
  checkpoint: execution.checkpoint,
18238
18504
  walletRequestId: execution.walletApproval?.request.requestId,
18239
18505
  walletApprovalRelay: execution.walletApproval?.relay,
18506
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
18240
18507
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
18241
18508
  walletApproval: serializeWalletApproval(execution.walletApproval),
18242
18509
  recommendedCommands
@@ -18367,11 +18634,29 @@ function serializeWalletApproval(walletApproval) {
18367
18634
  if (!walletApproval) return void 0;
18368
18635
  return {
18369
18636
  ...walletApproval,
18637
+ ...workflowWalletApprovalRelayAliases(walletApproval.relay, {
18638
+ shareLinkBaseField: "relayShareLinkBaseUrl",
18639
+ statusApiBaseField: "relayStatusApiBaseUrl"
18640
+ }),
18370
18641
  walletRequestId: walletApproval.request.requestId,
18371
18642
  request: sanitizeWalletRequestRecord(walletApproval.request),
18372
18643
  wallet: walletApproval.wallet ? sanitizeWalletRecord(walletApproval.wallet) : void 0
18373
18644
  };
18374
18645
  }
18646
+ function workflowWalletApprovalRelayAliases(relay, fieldNames = {
18647
+ shareLinkBaseField: "walletApprovalRelayShareLinkBaseUrl",
18648
+ statusApiBaseField: "walletApprovalRelayStatusApiBaseUrl"
18649
+ }) {
18650
+ const shareUrl = relay?.share_url;
18651
+ const statusUrl = relay?.status_url;
18652
+ const approvalUrl = relay?.approval_url;
18653
+ const shareLinkBaseUrl = shareUrl ? shareUrl.replace(/\/[^/]+$/, "") : approvalUrl ? approvalUrl.replace(/\/[^/]+$/, "") : void 0;
18654
+ const statusApiBaseUrl = statusUrl ? statusUrl.replace(/\/[^/]+$/, "") : approvalUrl && relay?.request_id ? `${approvalUrl.replace(/\/r\/[^/]+$/, "")}/api/requests` : void 0;
18655
+ return {
18656
+ [fieldNames.shareLinkBaseField]: shareLinkBaseUrl,
18657
+ [fieldNames.statusApiBaseField]: statusApiBaseUrl
18658
+ };
18659
+ }
18375
18660
  function addWorkflowGoalOptions(command, config = {}) {
18376
18661
  if (config.includeExecutionFlags) {
18377
18662
  command.option("--broadcast", "Broadcast the underlying transaction(s) instead of returning a preview", false).option(
@@ -18965,6 +19250,7 @@ function createWorkflowCommand(deps) {
18965
19250
  checkpoint: inspection.checkpoint,
18966
19251
  walletRequestId: inspection.walletApproval?.request.requestId,
18967
19252
  walletApprovalRelay: inspection.walletApproval?.relay,
19253
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
18968
19254
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
18969
19255
  walletApproval: serializeWalletApproval(inspection.walletApproval),
18970
19256
  recommendedCommands
@@ -19028,6 +19314,7 @@ function createWorkflowCommand(deps) {
19028
19314
  checkpoint: inspection.checkpoint,
19029
19315
  walletRequestId: inspection.walletApproval?.request.requestId,
19030
19316
  walletApprovalRelay: inspection.walletApproval?.relay,
19317
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19031
19318
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19032
19319
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19033
19320
  recommendedCommands
@@ -19079,6 +19366,7 @@ function createWorkflowCommand(deps) {
19079
19366
  checkpoint: inspection.checkpoint,
19080
19367
  walletRequestId: inspection.walletApproval.request.requestId,
19081
19368
  walletApprovalRelay: inspection.walletApproval.relay,
19369
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval.relay),
19082
19370
  walletApprovalRecommendedCommands: inspection.walletApproval.recommendedCommands,
19083
19371
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19084
19372
  recommendedCommands: recommendedCommands2
@@ -19129,6 +19417,7 @@ function createWorkflowCommand(deps) {
19129
19417
  checkpoint: execution.checkpoint,
19130
19418
  walletRequestId: execution.walletApproval?.request.requestId,
19131
19419
  walletApprovalRelay: execution.walletApproval?.relay,
19420
+ ...workflowWalletApprovalRelayAliases(execution.walletApproval?.relay),
19132
19421
  walletApprovalRecommendedCommands: execution.walletApproval?.recommendedCommands,
19133
19422
  walletApproval: serializeWalletApproval(execution.walletApproval),
19134
19423
  recommendedCommands: recommendedCommands2
@@ -19170,6 +19459,7 @@ function createWorkflowCommand(deps) {
19170
19459
  result: execution.result,
19171
19460
  walletRequestId: inspection.walletApproval?.request.requestId,
19172
19461
  walletApprovalRelay: inspection.walletApproval?.relay,
19462
+ ...workflowWalletApprovalRelayAliases(inspection.walletApproval?.relay),
19173
19463
  walletApprovalRecommendedCommands: inspection.walletApproval?.recommendedCommands,
19174
19464
  walletApproval: serializeWalletApproval(inspection.walletApproval),
19175
19465
  recommendedCommands
@@ -19207,14 +19497,15 @@ function createWorkflowCommand(deps) {
19207
19497
  function buildDefaultOperatorPathHelpText() {
19208
19498
  return [
19209
19499
  "",
19210
- "Default operator path:",
19500
+ "Default local-first operator path:",
19211
19501
  " zk-agent setup",
19212
19502
  " zk-agent next",
19213
19503
  " zk-agent wallet create --await-local",
19214
19504
  " zk-agent next",
19215
- ` ${buildWorkflowAutoRecommendedCommand("main")}`,
19505
+ ` ${buildWorkflowPayRecommendedCommand("main")}`,
19216
19506
  "",
19217
19507
  "Use `zk-agent next --request-id <id>` to continue a stored workflow checkpoint.",
19508
+ "Use `zk-agent relay inspect --relay-url <url>` and `zk-agent wallet --help` for the hosted remote-approval path.",
19218
19509
  "Use `zk-agent wallet --help` for bootstrap/reapproval details and `zk-agent workflow --help` once the intent is known."
19219
19510
  ].join("\n");
19220
19511
  }