@dev.sail.money/sailor 1.3.0-206 → 1.3.0-208

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/package.json +1 -1
  2. package/packages/cli/dist/index.cjs +402 -324
  3. package/packages/cli/dist/server.cjs +7 -8
  4. package/packages/sdk/dist/intelligence.d.ts +1 -1
  5. package/packages/sdk/dist/intelligence.js +1 -1
  6. package/packages/sdk/dist/keyring.d.ts +0 -6
  7. package/packages/sdk/dist/keyring.d.ts.map +1 -1
  8. package/packages/sdk/dist/keyring.js +1 -8
  9. package/packages/sdk/dist/keyring.js.map +1 -1
  10. package/packages/ui/dist/assets/{add-Dj4_n773.js → add-CfTRVkh-.js} +1 -1
  11. package/packages/ui/dist/assets/{all-wallets-BaFysN1G.js → all-wallets-VZChJwJf.js} +1 -1
  12. package/packages/ui/dist/assets/{app-store-DOc31zzY.js → app-store-CuFK1xBJ.js} +1 -1
  13. package/packages/ui/dist/assets/{apple-BvrvOkPH.js → apple-DETqVk_I.js} +1 -1
  14. package/packages/ui/dist/assets/{arrow-bottom-E1AfjXOT.js → arrow-bottom-CnNYsZfo.js} +1 -1
  15. package/packages/ui/dist/assets/{arrow-bottom-circle-sou7Vo1V.js → arrow-bottom-circle-DZwJF3UO.js} +1 -1
  16. package/packages/ui/dist/assets/{arrow-left-wk3G9Ywf.js → arrow-left-CBnisTAa.js} +1 -1
  17. package/packages/ui/dist/assets/{arrow-right-_a703_Pw.js → arrow-right-DA3Y1Yzf.js} +1 -1
  18. package/packages/ui/dist/assets/{arrow-top-txyg3R3e.js → arrow-top-e8-FE_fo.js} +1 -1
  19. package/packages/ui/dist/assets/{bank-BB1kd0NJ.js → bank-DdwromQn.js} +1 -1
  20. package/packages/ui/dist/assets/{basic-D0dy7nIZ.js → basic-DLYER2un.js} +1 -1
  21. package/packages/ui/dist/assets/{browser-BwHLhsTn.js → browser-BJNrv4Zj.js} +1 -1
  22. package/packages/ui/dist/assets/{card-DRC6HmPF.js → card-DB6j4DSk.js} +1 -1
  23. package/packages/ui/dist/assets/{ccip-Dq6KeJQl.js → ccip-Dy_s323Y.js} +1 -1
  24. package/packages/ui/dist/assets/{checkmark-TbAAQGjd.js → checkmark-Db9oicko.js} +1 -1
  25. package/packages/ui/dist/assets/{checkmark-bold-4723vJ0-.js → checkmark-bold-D9MgGHxv.js} +1 -1
  26. package/packages/ui/dist/assets/{chevron-bottom-DtLoBRe9.js → chevron-bottom-IGUDJRKN.js} +1 -1
  27. package/packages/ui/dist/assets/{chevron-left-BtydsF9j.js → chevron-left-CWpfP-rw.js} +1 -1
  28. package/packages/ui/dist/assets/{chevron-right-BbkU_Kf8.js → chevron-right-BejY0vtE.js} +1 -1
  29. package/packages/ui/dist/assets/{chevron-top-BWf-sN-F.js → chevron-top-BaUN0LGF.js} +1 -1
  30. package/packages/ui/dist/assets/{chrome-store-CVFxc5H2.js → chrome-store-fox1tIqs.js} +1 -1
  31. package/packages/ui/dist/assets/{clock-B4hJww1X.js → clock-DlqB4Ehi.js} +1 -1
  32. package/packages/ui/dist/assets/{close-Ci93jqGe.js → close-03e52feP.js} +1 -1
  33. package/packages/ui/dist/assets/{coinPlaceholder-cRv_xDgC.js → coinPlaceholder-BO3nv4hx.js} +1 -1
  34. package/packages/ui/dist/assets/{compass-_sZZPtm6.js → compass-CIqPRE0k.js} +1 -1
  35. package/packages/ui/dist/assets/{copy-DLaiLK0n.js → copy-D5cJBwZq.js} +1 -1
  36. package/packages/ui/dist/assets/{core-YssZmpJa.js → core-COelOg3-.js} +3 -3
  37. package/packages/ui/dist/assets/cursor-CRb7R3u1.js +3 -0
  38. package/packages/ui/dist/assets/{cursor-transparent-CzXQbB2p.js → cursor-transparent-Cvbiq9Br.js} +1 -1
  39. package/packages/ui/dist/assets/{desktop-BiqQI1Ss.js → desktop-Dp2wtF3n.js} +1 -1
  40. package/packages/ui/dist/assets/{disconnect-DYu7YCzu.js → disconnect-Dx_IGM73.js} +1 -1
  41. package/packages/ui/dist/assets/{discord-C-ife-UZ.js → discord-Cxryn14a.js} +1 -1
  42. package/packages/ui/dist/assets/{etherscan-Dq7ac-p9.js → etherscan-D8aNXWCx.js} +1 -1
  43. package/packages/ui/dist/assets/{events-BhTGLk8n.js → events-DR6OOHz2.js} +1 -1
  44. package/packages/ui/dist/assets/{exclamation-triangle-BcOdRdbp.js → exclamation-triangle-BBCaPLj1.js} +1 -1
  45. package/packages/ui/dist/assets/{extension-DmJLIidM.js → extension-bACRNBl5.js} +1 -1
  46. package/packages/ui/dist/assets/{external-link-HNjp7-Po.js → external-link-BZRqHo3q.js} +1 -1
  47. package/packages/ui/dist/assets/{facebook-BrSvHNyw.js → facebook-TAdUitg4.js} +1 -1
  48. package/packages/ui/dist/assets/{fallback-Ubz80uXP.js → fallback-DGiwrboN.js} +1 -1
  49. package/packages/ui/dist/assets/{farcaster-BsjltM6k.js → farcaster-YzdobBfR.js} +1 -1
  50. package/packages/ui/dist/assets/{filters-B4yQbYZP.js → filters-CT-xLEoI.js} +1 -1
  51. package/packages/ui/dist/assets/{github-CFq82Dds.js → github-DxPBbmEL.js} +1 -1
  52. package/packages/ui/dist/assets/{google-DwZutf4a.js → google-B0TMgFhK.js} +1 -1
  53. package/packages/ui/dist/assets/{help-circle-CyA_8KQg.js → help-circle-Be6sRPTw.js} +1 -1
  54. package/packages/ui/dist/assets/{id-jyZYn4LJ.js → id-Dem_akbE.js} +1 -1
  55. package/packages/ui/dist/assets/{image-Zv0yoBcH.js → image-CszBzFVG.js} +1 -1
  56. package/packages/ui/dist/assets/{index-BxRaYDiX.js → index-B0FDHAiK.js} +1 -1
  57. package/packages/ui/dist/assets/{index-67NncC_u.js → index-C2n9XvGG.js} +3 -3
  58. package/packages/ui/dist/assets/{index-_DkPx8RN.js → index-Cb5eBfrN.js} +77 -77
  59. package/packages/ui/dist/assets/{index-BY8A6EK8.js → index-DAawBKd2.js} +1 -1
  60. package/packages/ui/dist/assets/{index-CeGppB9s.js → index-DXqj6oYp.js} +1 -1
  61. package/packages/ui/dist/assets/{index-BdG43zCl.js → index-homh6mWC.js} +1 -1
  62. package/packages/ui/dist/assets/{index.es-C8EklrJY.js → index.es-DJgl78dj.js} +4 -4
  63. package/packages/ui/dist/assets/{info-BwGEOdmB.js → info-BimGN4NK.js} +1 -1
  64. package/packages/ui/dist/assets/{info-circle-BPCcX-Iq.js → info-circle-lnKEJu2R.js} +1 -1
  65. package/packages/ui/dist/assets/{lightbulb-BRwr4IaC.js → lightbulb-DS1vEMce.js} +1 -1
  66. package/packages/ui/dist/assets/{mail-D-k_Vhe4.js → mail-DoS3Wibk.js} +1 -1
  67. package/packages/ui/dist/assets/{metamask-sdk-AcOgwGrj.js → metamask-sdk-CMIU3BwW.js} +1 -1
  68. package/packages/ui/dist/assets/{mobile-B1ok9PfY.js → mobile-B09Cs95_.js} +1 -1
  69. package/packages/ui/dist/assets/{more-DeMTMLWs.js → more-CLBZT8WI.js} +1 -1
  70. package/packages/ui/dist/assets/{network-placeholder-J0Rc1I89.js → network-placeholder-C45Cp8rY.js} +1 -1
  71. package/packages/ui/dist/assets/{nftPlaceholder-dXGrzzoa.js → nftPlaceholder-CAVclL3u.js} +1 -1
  72. package/packages/ui/dist/assets/{off-CS0f77lw.js → off-CwE8HoZ0.js} +1 -1
  73. package/packages/ui/dist/assets/{parseSignature-DJ16fHC4.js → parseSignature-kmMNHYjl.js} +1 -1
  74. package/packages/ui/dist/assets/{play-store-DmK8UaNC.js → play-store-DiFF_6n4.js} +1 -1
  75. package/packages/ui/dist/assets/{plus-CSGSnoCh.js → plus-aygpl_pZ.js} +1 -1
  76. package/packages/ui/dist/assets/{qr-code-BJMH0xmM.js → qr-code-DpIS4fFK.js} +1 -1
  77. package/packages/ui/dist/assets/{recycle-horizontal-D5XP5VgJ.js → recycle-horizontal-Qk2N74JE.js} +1 -1
  78. package/packages/ui/dist/assets/{refresh-MLX3Rdp6.js → refresh-sWRq9Vnt.js} +1 -1
  79. package/packages/ui/dist/assets/{reown-logo-CVoDFQbM.js → reown-logo-YQxBa9i9.js} +1 -1
  80. package/packages/ui/dist/assets/{search-BDJDyvjC.js → search-Bt6BSPcC.js} +1 -1
  81. package/packages/ui/dist/assets/{secp256k1-BWh3B02k.js → secp256k1-BUh3OoFd.js} +1 -1
  82. package/packages/ui/dist/assets/{send-BOq2svx_.js → send-ym-Fn17O.js} +1 -1
  83. package/packages/ui/dist/assets/{swapHorizontal-ByjLi_-_.js → swapHorizontal-fBE2gnqc.js} +1 -1
  84. package/packages/ui/dist/assets/{swapHorizontalBold-fmCp_4AE.js → swapHorizontalBold-CekQ22FJ.js} +1 -1
  85. package/packages/ui/dist/assets/{swapHorizontalMedium-D6dFseYw.js → swapHorizontalMedium-B9BI9ovU.js} +1 -1
  86. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-B2V3m4ha.js → swapHorizontalRoundedBold-DOC75mBH.js} +1 -1
  87. package/packages/ui/dist/assets/{swapVertical-DVmCsmeT.js → swapVertical-CSQAPH0z.js} +1 -1
  88. package/packages/ui/dist/assets/{telegram-Bv8JRbMM.js → telegram-BPjzFxKU.js} +1 -1
  89. package/packages/ui/dist/assets/{three-dots-YYDJ18z6.js → three-dots-DP6hdXH8.js} +1 -1
  90. package/packages/ui/dist/assets/{twitch-B2_Jy-Ft.js → twitch-CgHMd-f3.js} +1 -1
  91. package/packages/ui/dist/assets/{twitterIcon-pKQzPQRt.js → twitterIcon-BrR8ynXm.js} +1 -1
  92. package/packages/ui/dist/assets/{verify-Bty2jIav.js → verify-D35D7Wtv.js} +1 -1
  93. package/packages/ui/dist/assets/{verify-filled-CKz2fuKl.js → verify-filled-DCP5CT59.js} +1 -1
  94. package/packages/ui/dist/assets/{w3m-modal-Wg1mtmVM.js → w3m-modal-B-ZptWk0.js} +1 -1
  95. package/packages/ui/dist/assets/{wallet-DslzC8rD.js → wallet-Cnb4dkr3.js} +1 -1
  96. package/packages/ui/dist/assets/{wallet-placeholder-Dyw8jJ56.js → wallet-placeholder-CCzk0PqU.js} +1 -1
  97. package/packages/ui/dist/assets/{walletconnect-DZBfbgp4.js → walletconnect-f6tQZYRi.js} +1 -1
  98. package/packages/ui/dist/assets/{warning-circle-D7NPDx4k.js → warning-circle-zxMagvfx.js} +1 -1
  99. package/packages/ui/dist/assets/{x-DRR_9FNs.js → x-D0nEGO1G.js} +1 -1
  100. package/packages/ui/dist/index.html +1 -1
  101. package/packages/ui/dist/assets/cursor-B_xyJnjj.js +0 -3
@@ -22626,7 +22626,7 @@ var require_websocket = __commonJS({
22626
22626
  var EventEmitter = require("events");
22627
22627
  var https = require("https");
22628
22628
  var http2 = require("http");
22629
- var net2 = require("net");
22629
+ var net3 = require("net");
22630
22630
  var tls = require("tls");
22631
22631
  var { randomBytes: randomBytes4, createHash } = require("crypto");
22632
22632
  var { Duplex, Readable } = require("stream");
@@ -23360,12 +23360,12 @@ var require_websocket = __commonJS({
23360
23360
  }
23361
23361
  function netConnect(options) {
23362
23362
  options.path = options.socketPath;
23363
- return net2.connect(options);
23363
+ return net3.connect(options);
23364
23364
  }
23365
23365
  function tlsConnect(options) {
23366
23366
  options.path = void 0;
23367
23367
  if (!options.servername && options.servername !== "") {
23368
- options.servername = net2.isIP(options.host) ? "" : options.host;
23368
+ options.servername = net3.isIP(options.host) ? "" : options.host;
23369
23369
  }
23370
23370
  return tls.connect(options);
23371
23371
  }
@@ -33756,7 +33756,7 @@ var require_websocket2 = __commonJS({
33756
33756
  var EventEmitter = require("events");
33757
33757
  var https = require("https");
33758
33758
  var http2 = require("http");
33759
- var net2 = require("net");
33759
+ var net3 = require("net");
33760
33760
  var tls = require("tls");
33761
33761
  var { randomBytes: randomBytes4, createHash } = require("crypto");
33762
33762
  var { Duplex, Readable } = require("stream");
@@ -34500,12 +34500,12 @@ var require_websocket2 = __commonJS({
34500
34500
  }
34501
34501
  function netConnect(options) {
34502
34502
  options.path = options.socketPath;
34503
- return net2.connect(options);
34503
+ return net3.connect(options);
34504
34504
  }
34505
34505
  function tlsConnect(options) {
34506
34506
  options.path = void 0;
34507
34507
  if (!options.servername && options.servername !== "") {
34508
- options.servername = net2.isIP(options.host) ? "" : options.host;
34508
+ options.servername = net3.isIP(options.host) ? "" : options.host;
34509
34509
  }
34510
34510
  return tls.connect(options);
34511
34511
  }
@@ -37445,18 +37445,12 @@ var LocalKeyring = class _LocalKeyring {
37445
37445
  this.account = account2;
37446
37446
  this.address = account2.address;
37447
37447
  this.privateKey = options.privateKey;
37448
- } else if (options.type === "mnemonic") {
37448
+ } else {
37449
37449
  const account2 = mnemonicToAccount(options.mnemonic, options.derivationPath ? { path: options.derivationPath } : void 0);
37450
37450
  this.account = account2;
37451
37451
  this.address = account2.address;
37452
- } else {
37453
- throw new Error("keystore decryption not implemented \u2014 use type: 'privateKey' or type: 'mnemonic' instead");
37454
37452
  }
37455
37453
  }
37456
- /** Returns a lightweight signer stub for read-only contexts where the key is not available. */
37457
- static fromAddress(address) {
37458
- return { address };
37459
- }
37460
37454
  /** Generates a brand-new keyring backed by a random private key. */
37461
37455
  static generate() {
37462
37456
  return new _LocalKeyring({ type: "privateKey", privateKey: generatePrivateKey() });
@@ -38497,15 +38491,58 @@ function upsertAccountInList(account2, name, baseSailDir = sailDir()) {
38497
38491
  }
38498
38492
 
38499
38493
  // src/signing/client.ts
38500
- var import_node_fs6 = require("node:fs");
38494
+ var import_node_fs7 = require("node:fs");
38501
38495
  var import_node_path5 = require("node:path");
38502
38496
 
38503
38497
  // src/signing/server.ts
38504
38498
  var import_node_crypto2 = require("node:crypto");
38505
- var import_node_fs5 = require("node:fs");
38499
+ var import_node_fs6 = require("node:fs");
38506
38500
  var import_node_http = require("node:http");
38507
- var import_node_net = require("node:net");
38508
38501
  var import_node_path4 = require("node:path");
38502
+
38503
+ // src/lib/process.ts
38504
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
38505
+ var import_node_net = __toESM(require("node:net"), 1);
38506
+ function agentPidPath(chainId) {
38507
+ return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
38508
+ }
38509
+ function writeAgentPid(chainId) {
38510
+ import_node_fs5.default.mkdirSync(sailPath(), { recursive: true });
38511
+ import_node_fs5.default.writeFileSync(agentPidPath(chainId), `${process.pid}
38512
+ `);
38513
+ }
38514
+ function clearAgentPid(chainId) {
38515
+ try {
38516
+ import_node_fs5.default.rmSync(agentPidPath(chainId));
38517
+ } catch {
38518
+ }
38519
+ }
38520
+ function readAgentPid(chainId) {
38521
+ try {
38522
+ const pid = Number.parseInt(import_node_fs5.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
38523
+ return Number.isNaN(pid) ? null : pid;
38524
+ } catch {
38525
+ return null;
38526
+ }
38527
+ }
38528
+ function isProcessAlive(pid) {
38529
+ try {
38530
+ process.kill(pid, 0);
38531
+ return true;
38532
+ } catch (err) {
38533
+ return err.code === "EPERM";
38534
+ }
38535
+ }
38536
+ function findFreePort(from14) {
38537
+ return new Promise((resolve3, reject) => {
38538
+ const server = import_node_net.default.createServer();
38539
+ server.unref();
38540
+ server.on("error", () => findFreePort(from14 + 1).then(resolve3, reject));
38541
+ server.listen(from14, "127.0.0.1", () => server.close(() => resolve3(from14)));
38542
+ });
38543
+ }
38544
+
38545
+ // src/signing/server.ts
38509
38546
  init_esm2();
38510
38547
 
38511
38548
  // ../../node_modules/.pnpm/ws@8.21.0_bufferutil@4.1.0_utf-8-validate@5.0.10/node_modules/ws/wrapper.mjs
@@ -38545,7 +38582,7 @@ function findUiDist() {
38545
38582
  (0, import_node_path4.join)(process.cwd(), "..", "ui", "dist")
38546
38583
  ];
38547
38584
  for (const c of candidates) {
38548
- if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(c, "index.html"))) return c;
38585
+ if ((0, import_node_fs6.existsSync)((0, import_node_path4.join)(c, "index.html"))) return c;
38549
38586
  }
38550
38587
  return null;
38551
38588
  }
@@ -38593,7 +38630,7 @@ var SigningServer = class {
38593
38630
  return this.httpServer?.listening ?? false;
38594
38631
  }
38595
38632
  async start() {
38596
- this.port = await findAvailablePort(this.port);
38633
+ this.port = await findFreePort(this.port);
38597
38634
  this._url = `http://localhost:${this.port}`;
38598
38635
  this.requestSecret = (0, import_node_crypto2.randomBytes)(16).toString("hex");
38599
38636
  const http2 = (0, import_node_http.createServer)((req, res) => this.handleHttp(req, res));
@@ -38773,13 +38810,13 @@ var SigningServer = class {
38773
38810
  const path11 = this.sailFile("config.json");
38774
38811
  let config = {};
38775
38812
  try {
38776
- config = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf-8"));
38813
+ config = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf-8"));
38777
38814
  } catch {
38778
38815
  }
38779
38816
  if (Number(config.chainId) === Number(chainId)) return;
38780
38817
  config.chainId = Number(chainId);
38781
- (0, import_node_fs5.mkdirSync)(this.sailFile(), { recursive: true });
38782
- (0, import_node_fs5.writeFileSync)(path11, `${JSON.stringify(config, null, 2)}
38818
+ (0, import_node_fs6.mkdirSync)(this.sailFile(), { recursive: true });
38819
+ (0, import_node_fs6.writeFileSync)(path11, `${JSON.stringify(config, null, 2)}
38783
38820
  `);
38784
38821
  } catch {
38785
38822
  }
@@ -38787,7 +38824,7 @@ var SigningServer = class {
38787
38824
  /** Stream a JSON file back, or a fallback body when it is missing/invalid. */
38788
38825
  sendJsonFile(res, filePath, fallback2) {
38789
38826
  try {
38790
- const raw = (0, import_node_fs5.readFileSync)(filePath, "utf-8");
38827
+ const raw = (0, import_node_fs6.readFileSync)(filePath, "utf-8");
38791
38828
  JSON.parse(raw);
38792
38829
  res.writeHead(200, { "Content-Type": "application/json" });
38793
38830
  res.end(raw);
@@ -38854,8 +38891,8 @@ var SigningServer = class {
38854
38891
  };
38855
38892
  const baseSailDir = this.sailFile();
38856
38893
  upsertAccountInList(record, void 0, baseSailDir);
38857
- (0, import_node_fs5.mkdirSync)(baseSailDir, { recursive: true });
38858
- (0, import_node_fs5.writeFileSync)(this.sailFile("account.json"), `${JSON.stringify(record, null, 2)}
38894
+ (0, import_node_fs6.mkdirSync)(baseSailDir, { recursive: true });
38895
+ (0, import_node_fs6.writeFileSync)(this.sailFile("account.json"), `${JSON.stringify(record, null, 2)}
38859
38896
  `);
38860
38897
  this.syncConfigChainId(chainId);
38861
38898
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -38870,12 +38907,12 @@ var SigningServer = class {
38870
38907
  res.writeHead(200, { "Content-Type": "application/json" });
38871
38908
  let active = null;
38872
38909
  try {
38873
- active = JSON.parse((0, import_node_fs5.readFileSync)(this.sailFile("account.json"), "utf-8")).safe;
38910
+ active = JSON.parse((0, import_node_fs6.readFileSync)(this.sailFile("account.json"), "utf-8")).safe;
38874
38911
  } catch {
38875
38912
  }
38876
38913
  try {
38877
38914
  const accounts = JSON.parse(
38878
- (0, import_node_fs5.readFileSync)(this.sailFile("state", "accounts.json"), "utf-8")
38915
+ (0, import_node_fs6.readFileSync)(this.sailFile("state", "accounts.json"), "utf-8")
38879
38916
  );
38880
38917
  res.end(
38881
38918
  JSON.stringify(
@@ -38888,7 +38925,7 @@ var SigningServer = class {
38888
38925
  } catch {
38889
38926
  try {
38890
38927
  const a = JSON.parse(
38891
- (0, import_node_fs5.readFileSync)(this.sailFile("account.json"), "utf-8")
38928
+ (0, import_node_fs6.readFileSync)(this.sailFile("account.json"), "utf-8")
38892
38929
  );
38893
38930
  res.end(JSON.stringify([{ ...a, name: "My SMA", active: true, addedAt: null }]));
38894
38931
  } catch {
@@ -39011,16 +39048,16 @@ var SigningServer = class {
39011
39048
  res.end();
39012
39049
  return;
39013
39050
  }
39014
- if ((0, import_node_fs5.existsSync)(filePath)) {
39051
+ if ((0, import_node_fs6.existsSync)(filePath)) {
39015
39052
  const mime = MIME[(0, import_node_path4.extname)(filePath)] ?? "application/octet-stream";
39016
39053
  res.writeHead(200, { "Content-Type": mime });
39017
- res.end((0, import_node_fs5.readFileSync)(filePath));
39054
+ res.end((0, import_node_fs6.readFileSync)(filePath));
39018
39055
  return;
39019
39056
  }
39020
39057
  const indexHtml = (0, import_node_path4.join)(this.uiDist, "index.html");
39021
- if ((0, import_node_fs5.existsSync)(indexHtml)) {
39058
+ if ((0, import_node_fs6.existsSync)(indexHtml)) {
39022
39059
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
39023
- res.end((0, import_node_fs5.readFileSync)(indexHtml));
39060
+ res.end((0, import_node_fs6.readFileSync)(indexHtml));
39024
39061
  return;
39025
39062
  }
39026
39063
  }
@@ -39107,15 +39144,15 @@ var SigningServer = class {
39107
39144
  }
39108
39145
  writeRuntimeState() {
39109
39146
  const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39110
- if ((0, import_node_fs5.existsSync)(path11)) {
39147
+ if ((0, import_node_fs6.existsSync)(path11)) {
39111
39148
  try {
39112
- const existing = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39113
- if (existing.pid != null && existing.pid !== process.pid && pidAlive(existing.pid)) return;
39149
+ const existing = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
39150
+ if (existing.pid != null && existing.pid !== process.pid && isProcessAlive(existing.pid)) return;
39114
39151
  } catch {
39115
39152
  }
39116
39153
  }
39117
- if (!(0, import_node_fs5.existsSync)(this.runtimeDir)) (0, import_node_fs5.mkdirSync)(this.runtimeDir, { recursive: true });
39118
- (0, import_node_fs5.writeFileSync)(
39154
+ if (!(0, import_node_fs6.existsSync)(this.runtimeDir)) (0, import_node_fs6.mkdirSync)(this.runtimeDir, { recursive: true });
39155
+ (0, import_node_fs6.writeFileSync)(
39119
39156
  (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE),
39120
39157
  JSON.stringify(
39121
39158
  {
@@ -39134,43 +39171,25 @@ var SigningServer = class {
39134
39171
  removeRuntimeState() {
39135
39172
  const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39136
39173
  try {
39137
- if (!(0, import_node_fs5.existsSync)(path11)) return;
39138
- const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39174
+ if (!(0, import_node_fs6.existsSync)(path11)) return;
39175
+ const state = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
39139
39176
  if (state.pid != null && state.pid !== process.pid) return;
39140
- (0, import_node_fs5.unlinkSync)(path11);
39177
+ (0, import_node_fs6.unlinkSync)(path11);
39141
39178
  } catch {
39142
39179
  }
39143
39180
  }
39144
39181
  };
39145
- function pidAlive(pid) {
39146
- try {
39147
- process.kill(pid, 0);
39148
- return true;
39149
- } catch (err) {
39150
- return err.code === "EPERM";
39151
- }
39152
- }
39153
39182
  function reapStaleRuntimeState(projectRoot = process.cwd()) {
39154
39183
  const path11 = (0, import_node_path4.join)(projectRoot, RUNTIME_SUBDIR, SERVER_STATE_FILE);
39155
39184
  try {
39156
- if (!(0, import_node_fs5.existsSync)(path11)) return;
39157
- const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39158
- if (state.pid != null && state.pid !== process.pid && !pidAlive(state.pid)) {
39159
- (0, import_node_fs5.unlinkSync)(path11);
39185
+ if (!(0, import_node_fs6.existsSync)(path11)) return;
39186
+ const state = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
39187
+ if (state.pid != null && state.pid !== process.pid && !isProcessAlive(state.pid)) {
39188
+ (0, import_node_fs6.unlinkSync)(path11);
39160
39189
  }
39161
39190
  } catch {
39162
39191
  }
39163
39192
  }
39164
- async function findAvailablePort(startPort) {
39165
- return new Promise((res) => {
39166
- const probe = (0, import_node_net.createServer)();
39167
- probe.listen(startPort, "127.0.0.1", () => {
39168
- const addr = probe.address();
39169
- probe.close(() => res(addr.port));
39170
- });
39171
- probe.on("error", () => res(findAvailablePort(startPort + 1)));
39172
- });
39173
- }
39174
39193
 
39175
39194
  // src/signing/client.ts
39176
39195
  var RUNTIME_SERVER_FILE = (0, import_node_path5.join)(".sail", "runtime", "server.json");
@@ -39252,14 +39271,14 @@ function toLoopbackIPv4(url) {
39252
39271
  }
39253
39272
  function readRuntimeServerState(projectRoot) {
39254
39273
  const file = (0, import_node_path5.join)(projectRoot, RUNTIME_SERVER_FILE);
39255
- if (!(0, import_node_fs6.existsSync)(file)) return null;
39274
+ if (!(0, import_node_fs7.existsSync)(file)) return null;
39256
39275
  try {
39257
- return JSON.parse((0, import_node_fs6.readFileSync)(file, "utf8"));
39276
+ return JSON.parse((0, import_node_fs7.readFileSync)(file, "utf8"));
39258
39277
  } catch {
39259
39278
  return null;
39260
39279
  }
39261
39280
  }
39262
- function signingPageUrl(_channel, dashboardPort) {
39281
+ function signingPageUrl(dashboardPort) {
39263
39282
  return `http://localhost:${dashboardPort}/#/station`;
39264
39283
  }
39265
39284
  async function discoverDaemon(projectRoot = process.cwd()) {
@@ -39530,7 +39549,7 @@ Deploying SMA on ${getChainById(targetChainId).name} (chain ${targetChainId})\u2
39530
39549
  const channel = await createSigningChannel(process.cwd());
39531
39550
  try {
39532
39551
  await channel.start();
39533
- const stationUrl = signingPageUrl(channel, projectPort(process.cwd()));
39552
+ const stationUrl = signingPageUrl(projectPort(process.cwd()));
39534
39553
  if (json) {
39535
39554
  console.log(
39536
39555
  JSON.stringify(
@@ -39667,100 +39686,6 @@ function emit(json, human, payload) {
39667
39686
 
39668
39687
  // src/lib/project.ts
39669
39688
  init_esm2();
39670
-
39671
- // src/lib/keys.ts
39672
- init_esm2();
39673
- var ROLES = ["manager", "permissionSigner"];
39674
- function normalizeRole(input) {
39675
- const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
39676
- if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
39677
- return "manager";
39678
- }
39679
- if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
39680
- return "permissionSigner";
39681
- }
39682
- return null;
39683
- }
39684
- function roleLabel(role) {
39685
- return role === "manager" ? "agent wallet" : "mandate signer";
39686
- }
39687
- function safeHex(safe) {
39688
- return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
39689
- }
39690
- function keyPath(role, safe) {
39691
- if (safe) {
39692
- return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
39693
- }
39694
- return sailPath("keys", `${role}.json`);
39695
- }
39696
- function legacyKeyPath(role, safe) {
39697
- return sailPath("keys", `${role}-${safeHex(safe)}.json`);
39698
- }
39699
- function resolveKeyPath(role, safe) {
39700
- if (safe) {
39701
- const perSma = keyPath(role, safe);
39702
- if (fileExists(perSma)) return perSma;
39703
- const legacy = legacyKeyPath(role, safe);
39704
- if (fileExists(legacy)) return legacy;
39705
- }
39706
- return keyPath(role);
39707
- }
39708
- function keyExists(role, safe) {
39709
- return fileExists(resolveKeyPath(role, safe));
39710
- }
39711
- async function loadKeyring(role, safe) {
39712
- const keystore = readJsonFile(resolveKeyPath(role, safe));
39713
- if (!keystore) {
39714
- throw new Error(
39715
- `No ${roleLabel(role)} found.
39716
- Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
39717
- );
39718
- }
39719
- const password = await promptHidden(`Password for ${roleLabel(role)} key`);
39720
- try {
39721
- return await LocalKeyring.fromKeystore(keystore, password);
39722
- } catch {
39723
- throw new Error("Invalid password.");
39724
- }
39725
- }
39726
- async function loadManagerSigner(safe) {
39727
- const passphrase = process.env.SAIL_PASSPHRASE;
39728
- if (passphrase) {
39729
- const keystore = readJsonFile(resolveKeyPath("manager", safe));
39730
- if (!keystore) {
39731
- throw new Error(
39732
- 'No agent wallet found.\nRun "sailor keys generate" and choose "agent wallet".'
39733
- );
39734
- }
39735
- try {
39736
- return await LocalKeyring.fromKeystore(keystore, passphrase);
39737
- } catch {
39738
- throw new Error(
39739
- "SAIL_PASSPHRASE does not match this keystore.\nCheck the value in .sail/.env.local (or the SAIL_PASSPHRASE CI secret) \u2014 it must be the passphrase the agent wallet was encrypted with."
39740
- );
39741
- }
39742
- }
39743
- if (process.stdin.isTTY !== true && keyExists("manager", safe)) {
39744
- throw new Error(
39745
- 'Agent keystore found but SAIL_PASSPHRASE is not set.\nIf you created the key in the dashboard, add SAIL_PASSPHRASE to .sail/.env.local, or run "sailor keys generate".\nFor CI, set the SAIL_PASSPHRASE GitHub Actions secret.'
39746
- );
39747
- }
39748
- return loadKeyring("manager", safe);
39749
- }
39750
- function managerKeystorePath(managerAddr) {
39751
- if (!isAddress(managerAddr, { strict: false })) {
39752
- throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
39753
- }
39754
- const hex = managerAddr.toLowerCase().replace(/^0x/, "");
39755
- return sailPath("keys", "managers", `${hex}.json`);
39756
- }
39757
- async function loadAnySigner() {
39758
- if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
39759
- if (keyExists("manager")) return loadKeyring("manager");
39760
- throw new Error('No signing key found.\nRun "sailor keys generate" first.');
39761
- }
39762
-
39763
- // src/lib/project.ts
39764
39689
  function nonEmpty(value) {
39765
39690
  return typeof value === "string" && value.trim().length > 0;
39766
39691
  }
@@ -39819,24 +39744,6 @@ var ProjectContext = class {
39819
39744
  });
39820
39745
  }
39821
39746
  };
39822
- async function loadManagerSigner2() {
39823
- if (!process.env.SAIL_PASSPHRASE) {
39824
- try {
39825
- const env = parseEnvFile(sailPath(".env.local"));
39826
- if (env.SAIL_PASSPHRASE) process.env.SAIL_PASSPHRASE = env.SAIL_PASSPHRASE;
39827
- } catch {
39828
- }
39829
- }
39830
- const passphrase = process.env.SAIL_PASSPHRASE;
39831
- if (passphrase) {
39832
- const keystore = readJsonFile(keyPath("manager"));
39833
- if (!keystore) {
39834
- throw new Error('No manager key found.\nRun "sailor keys generate" and choose "manager".');
39835
- }
39836
- return LocalKeyring.fromKeystore(keystore, passphrase);
39837
- }
39838
- return loadKeyring("manager");
39839
- }
39840
39747
 
39841
39748
  // src/commands/capabilities.ts
39842
39749
  function chainName(chainId) {
@@ -40044,6 +39951,101 @@ function checkSelectorRoutes(calldata, bytecode) {
40044
39951
  return { selector, routes: body.includes(selector) };
40045
39952
  }
40046
39953
 
39954
+ // src/lib/keys.ts
39955
+ init_esm2();
39956
+ var ROLES = ["manager", "permissionSigner"];
39957
+ function normalizeRole(input) {
39958
+ const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
39959
+ if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
39960
+ return "manager";
39961
+ }
39962
+ if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
39963
+ return "permissionSigner";
39964
+ }
39965
+ return null;
39966
+ }
39967
+ function roleLabel(role) {
39968
+ return role === "manager" ? "agent wallet" : "mandate signer";
39969
+ }
39970
+ function safeHex(safe) {
39971
+ return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
39972
+ }
39973
+ function keyPath(role, safe) {
39974
+ if (safe) {
39975
+ return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
39976
+ }
39977
+ return sailPath("keys", `${role}.json`);
39978
+ }
39979
+ function legacyKeyPath(role, safe) {
39980
+ return sailPath("keys", `${role}-${safeHex(safe)}.json`);
39981
+ }
39982
+ function resolveKeyPath(role, safe) {
39983
+ if (safe) {
39984
+ const perSma = keyPath(role, safe);
39985
+ if (fileExists(perSma)) return perSma;
39986
+ const legacy = legacyKeyPath(role, safe);
39987
+ if (fileExists(legacy)) return legacy;
39988
+ }
39989
+ return keyPath(role);
39990
+ }
39991
+ function keyExists(role, safe) {
39992
+ return fileExists(resolveKeyPath(role, safe));
39993
+ }
39994
+ async function loadKeyring(role, safe) {
39995
+ const keystore = readJsonFile(resolveKeyPath(role, safe));
39996
+ if (!keystore) {
39997
+ throw new Error(
39998
+ `No ${roleLabel(role)} found.
39999
+ Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
40000
+ );
40001
+ }
40002
+ const passphrase = process.env.SAIL_PASSPHRASE;
40003
+ if (passphrase) {
40004
+ try {
40005
+ return await LocalKeyring.fromKeystore(keystore, passphrase);
40006
+ } catch {
40007
+ throw new Error(
40008
+ `SAIL_PASSPHRASE does not match the ${roleLabel(role)} keystore.
40009
+ Check the value in .sail/.env.local (or the SAIL_PASSPHRASE CI secret) \u2014 it must be the passphrase this key was encrypted with.`
40010
+ );
40011
+ }
40012
+ }
40013
+ if (process.stdin.isTTY !== true) {
40014
+ throw new Error(
40015
+ `${roleLabel(role)} keystore found but SAIL_PASSPHRASE is not set.
40016
+ Set SAIL_PASSPHRASE in .sail/.env.local (or as a CI secret) to run non-interactively.`
40017
+ );
40018
+ }
40019
+ const password = await promptHidden(`Password for ${roleLabel(role)} key`);
40020
+ try {
40021
+ return await LocalKeyring.fromKeystore(keystore, password);
40022
+ } catch {
40023
+ throw new Error("Invalid password.");
40024
+ }
40025
+ }
40026
+ async function loadManagerSigner(safe) {
40027
+ if (!process.env.SAIL_PASSPHRASE) {
40028
+ try {
40029
+ const env = parseEnvFile(sailPath(".env.local"));
40030
+ if (env.SAIL_PASSPHRASE) process.env.SAIL_PASSPHRASE = env.SAIL_PASSPHRASE;
40031
+ } catch {
40032
+ }
40033
+ }
40034
+ return loadKeyring("manager", safe);
40035
+ }
40036
+ function managerKeystorePath(managerAddr) {
40037
+ if (!isAddress(managerAddr, { strict: false })) {
40038
+ throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
40039
+ }
40040
+ const hex = managerAddr.toLowerCase().replace(/^0x/, "");
40041
+ return sailPath("keys", "managers", `${hex}.json`);
40042
+ }
40043
+ async function loadAnySigner() {
40044
+ if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
40045
+ if (keyExists("manager")) return loadKeyring("manager");
40046
+ throw new Error('No signing key found.\nRun "sailor keys generate" first.');
40047
+ }
40048
+
40047
40049
  // src/lib/permission-resolver.ts
40048
40050
  var IPERMISSION_ABI = [
40049
40051
  {
@@ -40439,11 +40441,11 @@ Probe is heuristic: an unknown selector (${PROBE_SELECTOR}) to a neutral target
40439
40441
  }
40440
40442
 
40441
40443
  // src/commands/init.ts
40442
- var import_node_fs9 = __toESM(require("node:fs"), 1);
40444
+ var import_node_fs10 = __toESM(require("node:fs"), 1);
40443
40445
  var import_node_path8 = __toESM(require("node:path"), 1);
40444
40446
 
40445
40447
  // src/lib/foundry.ts
40446
- var import_node_fs7 = require("node:fs");
40448
+ var import_node_fs8 = require("node:fs");
40447
40449
  var import_node_path6 = require("node:path");
40448
40450
  var FOUNDRY_TOML = `[profile.default]
40449
40451
  src = "mandates"
@@ -40592,7 +40594,7 @@ Compiled artifacts are written to \`out/\` and the deployed address is tracked i
40592
40594
  function scaffoldFoundryWorkspace(root) {
40593
40595
  const dirs = [(0, import_node_path6.join)(root, "mandates"), (0, import_node_path6.join)(root, ".sail", "contracts", "interfaces")];
40594
40596
  for (const d of dirs) {
40595
- if (!(0, import_node_fs7.existsSync)(d)) (0, import_node_fs7.mkdirSync)(d, { recursive: true });
40597
+ if (!(0, import_node_fs8.existsSync)(d)) (0, import_node_fs8.mkdirSync)(d, { recursive: true });
40596
40598
  }
40597
40599
  writeIfMissing((0, import_node_path6.join)(root, "foundry.toml"), FOUNDRY_TOML);
40598
40600
  writeIfMissing(
@@ -40607,11 +40609,11 @@ function scaffoldFoundryWorkspace(root) {
40607
40609
  writeIfMissing((0, import_node_path6.join)(root, "mandates", "README.md"), MANDATES_README);
40608
40610
  }
40609
40611
  function writeIfMissing(path11, content) {
40610
- if (!(0, import_node_fs7.existsSync)(path11)) (0, import_node_fs7.writeFileSync)(path11, content, "utf8");
40612
+ if (!(0, import_node_fs8.existsSync)(path11)) (0, import_node_fs8.writeFileSync)(path11, content, "utf8");
40611
40613
  }
40612
40614
 
40613
40615
  // src/lib/template.ts
40614
- var import_node_fs8 = __toESM(require("node:fs"), 1);
40616
+ var import_node_fs9 = __toESM(require("node:fs"), 1);
40615
40617
  var import_node_path7 = __toESM(require("node:path"), 1);
40616
40618
  var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
40617
40619
  "node_modules",
@@ -40622,8 +40624,8 @@ var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
40622
40624
  ".git"
40623
40625
  ]);
40624
40626
  function copyDirSync(src, dest) {
40625
- import_node_fs8.default.mkdirSync(dest, { recursive: true });
40626
- for (const entry of import_node_fs8.default.readdirSync(src, { withFileTypes: true })) {
40627
+ import_node_fs9.default.mkdirSync(dest, { recursive: true });
40628
+ for (const entry of import_node_fs9.default.readdirSync(src, { withFileTypes: true })) {
40627
40629
  if (TEMPLATE_COPY_EXCLUDES.has(entry.name)) continue;
40628
40630
  const srcPath = import_node_path7.default.join(src, entry.name);
40629
40631
  const destName = entry.name.startsWith("_") ? `.${entry.name.slice(1)}` : entry.name;
@@ -40631,24 +40633,24 @@ function copyDirSync(src, dest) {
40631
40633
  if (entry.isDirectory()) {
40632
40634
  copyDirSync(srcPath, destPath);
40633
40635
  } else {
40634
- import_node_fs8.default.copyFileSync(srcPath, destPath);
40636
+ import_node_fs9.default.copyFileSync(srcPath, destPath);
40635
40637
  }
40636
40638
  }
40637
40639
  }
40638
40640
  function writeIfMissing2(file, content) {
40639
- if (!import_node_fs8.default.existsSync(file)) import_node_fs8.default.writeFileSync(file, content, "utf-8");
40641
+ if (!import_node_fs9.default.existsSync(file)) import_node_fs9.default.writeFileSync(file, content, "utf-8");
40640
40642
  }
40641
40643
  function copyDirSyncIfMissing(src, dest, added = [], base2 = dest) {
40642
- import_node_fs8.default.mkdirSync(dest, { recursive: true });
40643
- for (const entry of import_node_fs8.default.readdirSync(src, { withFileTypes: true })) {
40644
+ import_node_fs9.default.mkdirSync(dest, { recursive: true });
40645
+ for (const entry of import_node_fs9.default.readdirSync(src, { withFileTypes: true })) {
40644
40646
  if (TEMPLATE_COPY_EXCLUDES.has(entry.name)) continue;
40645
40647
  const srcPath = import_node_path7.default.join(src, entry.name);
40646
40648
  const destName = entry.name.startsWith("_") ? `.${entry.name.slice(1)}` : entry.name;
40647
40649
  const destPath = import_node_path7.default.join(dest, destName);
40648
40650
  if (entry.isDirectory()) {
40649
40651
  copyDirSyncIfMissing(srcPath, destPath, added, base2);
40650
- } else if (!import_node_fs8.default.existsSync(destPath)) {
40651
- import_node_fs8.default.copyFileSync(srcPath, destPath);
40652
+ } else if (!import_node_fs9.default.existsSync(destPath)) {
40653
+ import_node_fs9.default.copyFileSync(srcPath, destPath);
40652
40654
  added.push(import_node_path7.default.relative(base2, destPath));
40653
40655
  }
40654
40656
  }
@@ -40674,7 +40676,7 @@ var DEV_PKG = "@dev.sail.money/sailor";
40674
40676
  function cliPackageInfo() {
40675
40677
  try {
40676
40678
  const pkg = JSON.parse(
40677
- import_node_fs9.default.readFileSync(import_node_path8.default.join(packageRoot(), "package.json"), "utf-8")
40679
+ import_node_fs10.default.readFileSync(import_node_path8.default.join(packageRoot(), "package.json"), "utf-8")
40678
40680
  );
40679
40681
  return {
40680
40682
  name: pkg.name ?? CANONICAL_PKG,
@@ -40691,13 +40693,13 @@ function scaffoldProjectWorkspace(dest, name, options) {
40691
40693
  return n;
40692
40694
  })() : null;
40693
40695
  const sailDir2 = import_node_path8.default.join(dest, ".sail");
40694
- import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "keys"), { recursive: true });
40695
- import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "runtime"), { recursive: true });
40696
- import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "state"), { recursive: true });
40696
+ import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "keys"), { recursive: true });
40697
+ import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "runtime"), { recursive: true });
40698
+ import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "state"), { recursive: true });
40697
40699
  const installMode = process.env.SAILOR_INSTALL_MODE === "docker" ? "docker" : "local";
40698
40700
  const _rawContainerName = process.env.SAILOR_CONTAINER_NAME ?? "agent";
40699
40701
  const containerName = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(_rawContainerName) ? _rawContainerName : "agent";
40700
- import_node_fs9.default.writeFileSync(
40702
+ import_node_fs10.default.writeFileSync(
40701
40703
  import_node_path8.default.join(sailDir2, "config.json"),
40702
40704
  `${JSON.stringify(
40703
40705
  {
@@ -40724,7 +40726,7 @@ function scaffoldProjectWorkspace(dest, name, options) {
40724
40726
  const chainEntries = Object.values(chains);
40725
40727
  const perChainVarLines = chainEntries.map((c) => `# ${c.rpcEnvVar}=https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`).join("\n");
40726
40728
  const chainIdExample = chainId != null ? `CHAIN_ID=${chainId}` : `# CHAIN_ID=8453 # set after choosing your chain in Stage 1`;
40727
- import_node_fs9.default.writeFileSync(
40729
+ import_node_fs10.default.writeFileSync(
40728
40730
  import_node_path8.default.join(dest, ".env.example"),
40729
40731
  `# Sailor agent environment
40730
40732
  #
@@ -40750,7 +40752,7 @@ ${perChainVarLines}
40750
40752
  const val = isActive && options.rpcUrl ? options.rpcUrl : `https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`;
40751
40753
  return isActive && options.rpcUrl ? `${c.rpcEnvVar}=${val}` : `# ${c.rpcEnvVar}=${val}`;
40752
40754
  }).join("\n");
40753
- import_node_fs9.default.writeFileSync(
40755
+ import_node_fs10.default.writeFileSync(
40754
40756
  import_node_path8.default.join(sailDir2, ".env.local"),
40755
40757
  `# Real values \u2014 never commit this file.
40756
40758
  #
@@ -40777,8 +40779,8 @@ async function initCommand(dir, options = {}) {
40777
40779
  throw new Error(`Invalid template name: "${templateName}"`);
40778
40780
  }
40779
40781
  const templateSrc = import_node_path8.default.join(templatesDir, templateName);
40780
- const availableTemplates = () => import_node_fs9.default.existsSync(templatesDir) ? import_node_fs9.default.readdirSync(templatesDir).filter((e) => import_node_fs9.default.existsSync(import_node_path8.default.join(templatesDir, e, "package.json"))).join(", ") || "none" : "none";
40781
- if (!import_node_fs9.default.existsSync(templateSrc) || !import_node_fs9.default.existsSync(import_node_path8.default.join(templateSrc, "package.json"))) {
40782
+ const availableTemplates = () => import_node_fs10.default.existsSync(templatesDir) ? import_node_fs10.default.readdirSync(templatesDir).filter((e) => import_node_fs10.default.existsSync(import_node_path8.default.join(templatesDir, e, "package.json"))).join(", ") || "none" : "none";
40783
+ if (!import_node_fs10.default.existsSync(templateSrc) || !import_node_fs10.default.existsSync(import_node_path8.default.join(templateSrc, "package.json"))) {
40782
40784
  const available = availableTemplates();
40783
40785
  const hint = available === "none" ? `
40784
40786
  No templates found under ${templatesDir}.
@@ -40791,19 +40793,19 @@ run from the repo root.` : ` Available: ${available}`;
40791
40793
  if (!inPlace && !dest.startsWith(cwd + import_node_path8.default.sep) && dest !== cwd) {
40792
40794
  throw new Error(`Directory must be inside the current working directory`);
40793
40795
  }
40794
- if (!inPlace && import_node_fs9.default.existsSync(dest) && !options.force) {
40796
+ if (!inPlace && import_node_fs10.default.existsSync(dest) && !options.force) {
40795
40797
  throw new Error(`Directory already exists: ${dest}
40796
40798
  Pass --force to scaffold into it anyway (existing files with the same name are overwritten).`);
40797
40799
  }
40798
- if (inPlace && import_node_fs9.default.existsSync(import_node_path8.default.join(dest, ".sail", "config.json")) && !options.force) {
40800
+ if (inPlace && import_node_fs10.default.existsSync(import_node_path8.default.join(dest, ".sail", "config.json")) && !options.force) {
40799
40801
  throw new Error(
40800
40802
  "This project is already initialized.\nRun `sailor update` to re-sync template files, or `sailor init --force` to re-initialize (overwrites scaffold files; your .sail/keys/ and .sail/state/ are left in place)."
40801
40803
  );
40802
40804
  }
40803
40805
  const existingConfigPath = import_node_path8.default.join(dest, ".sail", "config.json");
40804
- const previousConfig = import_node_fs9.default.existsSync(existingConfigPath) ? (() => {
40806
+ const previousConfig = import_node_fs10.default.existsSync(existingConfigPath) ? (() => {
40805
40807
  try {
40806
- return JSON.parse(import_node_fs9.default.readFileSync(existingConfigPath, "utf-8"));
40808
+ return JSON.parse(import_node_fs10.default.readFileSync(existingConfigPath, "utf-8"));
40807
40809
  } catch {
40808
40810
  return null;
40809
40811
  }
@@ -40811,37 +40813,37 @@ Pass --force to scaffold into it anyway (existing files with the same name are o
40811
40813
  copyDirSync(templateSrc, dest);
40812
40814
  const pkgRoot = packageRoot();
40813
40815
  const examplesPermSrc = import_node_path8.default.join(pkgRoot, "examples", "permissions");
40814
- if (import_node_fs9.default.existsSync(examplesPermSrc)) {
40816
+ if (import_node_fs10.default.existsSync(examplesPermSrc)) {
40815
40817
  copyDirSync(examplesPermSrc, import_node_path8.default.join(dest, "examples", "permissions"));
40816
40818
  }
40817
40819
  const customMandateSrc = import_node_path8.default.join(pkgRoot, "examples", "custom-mandate");
40818
- if (import_node_fs9.default.existsSync(customMandateSrc)) {
40820
+ if (import_node_fs10.default.existsSync(customMandateSrc)) {
40819
40821
  copyDirSync(customMandateSrc, import_node_path8.default.join(dest, "examples", "custom-mandate"));
40820
40822
  }
40821
40823
  const permModelSrc = import_node_path8.default.join(pkgRoot, "docs", "PERMISSION_MODEL.md");
40822
- if (import_node_fs9.default.existsSync(permModelSrc)) {
40823
- import_node_fs9.default.mkdirSync(import_node_path8.default.join(dest, "docs"), { recursive: true });
40824
- writeIfMissing2(import_node_path8.default.join(dest, "docs", "PERMISSION_MODEL.md"), import_node_fs9.default.readFileSync(permModelSrc, "utf-8"));
40824
+ if (import_node_fs10.default.existsSync(permModelSrc)) {
40825
+ import_node_fs10.default.mkdirSync(import_node_path8.default.join(dest, "docs"), { recursive: true });
40826
+ writeIfMissing2(import_node_path8.default.join(dest, "docs", "PERMISSION_MODEL.md"), import_node_fs10.default.readFileSync(permModelSrc, "utf-8"));
40825
40827
  }
40826
40828
  const pkgPath = import_node_path8.default.join(dest, "package.json");
40827
- if (import_node_fs9.default.existsSync(pkgPath)) {
40828
- const pkg = JSON.parse(import_node_fs9.default.readFileSync(pkgPath, "utf-8"));
40829
+ if (import_node_fs10.default.existsSync(pkgPath)) {
40830
+ const pkg = JSON.parse(import_node_fs10.default.readFileSync(pkgPath, "utf-8"));
40829
40831
  pkg.name = name;
40830
40832
  const devDeps = pkg.devDependencies ?? {};
40831
40833
  const { name: cliName, version: cliVer } = cliPackageInfo();
40832
40834
  devDeps[CANONICAL_PKG] = cliName === DEV_PKG ? `npm:${DEV_PKG}@^${cliVer}` : `^${cliVer}`;
40833
40835
  pkg.devDependencies = devDeps;
40834
- import_node_fs9.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
40836
+ import_node_fs10.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
40835
40837
  `);
40836
40838
  }
40837
40839
  const tsconfigPath = import_node_path8.default.join(dest, "tsconfig.json");
40838
- if (import_node_fs9.default.existsSync(tsconfigPath)) {
40839
- const tsconfig = JSON.parse(import_node_fs9.default.readFileSync(tsconfigPath, "utf-8"));
40840
+ if (import_node_fs10.default.existsSync(tsconfigPath)) {
40841
+ const tsconfig = JSON.parse(import_node_fs10.default.readFileSync(tsconfigPath, "utf-8"));
40840
40842
  const co = tsconfig.compilerOptions;
40841
40843
  if (co && "paths" in co) {
40842
40844
  delete co.paths;
40843
40845
  delete co.baseUrl;
40844
- import_node_fs9.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
40846
+ import_node_fs10.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
40845
40847
  `);
40846
40848
  }
40847
40849
  }
@@ -40879,20 +40881,20 @@ function chainLabel(chainId) {
40879
40881
  }
40880
40882
  function detectState(dest) {
40881
40883
  try {
40882
- const configRaw = import_node_fs9.default.readFileSync(import_node_path8.default.join(dest, ".sail", "config.json"), "utf-8");
40884
+ const configRaw = import_node_fs10.default.readFileSync(import_node_path8.default.join(dest, ".sail", "config.json"), "utf-8");
40883
40885
  const config = JSON.parse(configRaw);
40884
40886
  const projectName = config.name ?? import_node_path8.default.basename(dest);
40885
40887
  const accountPath = import_node_path8.default.join(dest, ".sail", "account.json");
40886
- if (!import_node_fs9.default.existsSync(accountPath)) {
40888
+ if (!import_node_fs10.default.existsSync(accountPath)) {
40887
40889
  return { kind: "B", projectName, chain: chainLabel(config.chainId ?? 0) };
40888
40890
  }
40889
- const accountRaw = import_node_fs9.default.readFileSync(accountPath, "utf-8");
40891
+ const accountRaw = import_node_fs10.default.readFileSync(accountPath, "utf-8");
40890
40892
  const account2 = JSON.parse(accountRaw);
40891
40893
  const sma = account2.safe ?? "";
40892
40894
  const chain2 = chainLabel(account2.chainId ?? config.chainId ?? 0);
40893
40895
  let permissionCount = 0;
40894
40896
  try {
40895
- const mandatesRaw = import_node_fs9.default.readFileSync(
40897
+ const mandatesRaw = import_node_fs10.default.readFileSync(
40896
40898
  import_node_path8.default.join(dest, ".sail", "state", "mandates.json"),
40897
40899
  "utf-8"
40898
40900
  );
@@ -40981,7 +40983,7 @@ Created ${name}/`);
40981
40983
  }
40982
40984
 
40983
40985
  // src/commands/update.ts
40984
- var import_node_fs10 = __toESM(require("node:fs"), 1);
40986
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
40985
40987
  var import_node_path9 = __toESM(require("node:path"), 1);
40986
40988
  var UPDATE_PATHS = [
40987
40989
  ".agents",
@@ -40997,18 +40999,18 @@ var STALE_PATHS = [
40997
40999
  ];
40998
41000
  async function updateCommand() {
40999
41001
  const dest = process.cwd();
41000
- if (!import_node_fs10.default.existsSync(import_node_path9.default.join(dest, ".sail", "config.json"))) {
41002
+ if (!import_node_fs11.default.existsSync(import_node_path9.default.join(dest, ".sail", "config.json"))) {
41001
41003
  throw new Error("Not a sailor project \u2014 .sail/config.json not found. Run `sailor init` first.");
41002
41004
  }
41003
41005
  const templateSrc = import_node_path9.default.join(packageRoot(), "templates", "default");
41004
- if (!import_node_fs10.default.existsSync(templateSrc)) {
41006
+ if (!import_node_fs11.default.existsSync(templateSrc)) {
41005
41007
  throw new Error(`Template directory not found at ${templateSrc}`);
41006
41008
  }
41007
41009
  const removed = [];
41008
41010
  for (const p of STALE_PATHS) {
41009
41011
  const target = import_node_path9.default.join(dest, p);
41010
- if (import_node_fs10.default.existsSync(target)) {
41011
- import_node_fs10.default.rmSync(target, { recursive: true, force: true });
41012
+ if (import_node_fs11.default.existsSync(target)) {
41013
+ import_node_fs11.default.rmSync(target, { recursive: true, force: true });
41012
41014
  removed.push(p);
41013
41015
  }
41014
41016
  }
@@ -41016,13 +41018,13 @@ async function updateCommand() {
41016
41018
  for (const p of UPDATE_PATHS) {
41017
41019
  const src = import_node_path9.default.join(templateSrc, p);
41018
41020
  const dst = import_node_path9.default.join(dest, p);
41019
- if (!import_node_fs10.default.existsSync(src)) continue;
41020
- const stat = import_node_fs10.default.statSync(src);
41021
+ if (!import_node_fs11.default.existsSync(src)) continue;
41022
+ const stat = import_node_fs11.default.statSync(src);
41021
41023
  if (stat.isDirectory()) {
41022
41024
  copyDirSync(src, dst);
41023
41025
  } else {
41024
- import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(dst), { recursive: true });
41025
- import_node_fs10.default.copyFileSync(src, dst);
41026
+ import_node_fs11.default.mkdirSync(import_node_path9.default.dirname(dst), { recursive: true });
41027
+ import_node_fs11.default.copyFileSync(src, dst);
41026
41028
  }
41027
41029
  updated.push(p);
41028
41030
  }
@@ -41030,7 +41032,7 @@ async function updateCommand() {
41030
41032
  copyDirSyncIfMissing(templateSrc, dest, added);
41031
41033
  const configPath = import_node_path9.default.join(dest, ".sail", "config.json");
41032
41034
  try {
41033
- const config = JSON.parse(import_node_fs10.default.readFileSync(configPath, "utf-8"));
41035
+ const config = JSON.parse(import_node_fs11.default.readFileSync(configPath, "utf-8"));
41034
41036
  const newMode = process.env.SAILOR_INSTALL_MODE === "docker" ? "docker" : "local";
41035
41037
  const _rawContainerName = process.env.SAILOR_CONTAINER_NAME ?? "agent";
41036
41038
  const containerName = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(_rawContainerName) ? _rawContainerName : "agent";
@@ -41043,7 +41045,7 @@ async function updateCommand() {
41043
41045
  } else {
41044
41046
  delete config.containerName;
41045
41047
  }
41046
- import_node_fs10.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
41048
+ import_node_fs11.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
41047
41049
  `, "utf-8");
41048
41050
  if (previousMode === "docker" && newMode === "local") {
41049
41051
  const prev = previousContainer ?? "agent";
@@ -41057,7 +41059,7 @@ Switched to Docker install (container: ${containerName}).`);
41057
41059
  }
41058
41060
  } else if (newMode === "docker" && config.containerName !== containerName) {
41059
41061
  config.containerName = containerName;
41060
- import_node_fs10.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
41062
+ import_node_fs11.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
41061
41063
  `, "utf-8");
41062
41064
  }
41063
41065
  } catch {
@@ -41086,7 +41088,7 @@ Added (new in template):`);
41086
41088
  }
41087
41089
 
41088
41090
  // src/commands/keys.ts
41089
- var import_node_fs11 = __toESM(require("node:fs"), 1);
41091
+ var import_node_fs12 = __toESM(require("node:fs"), 1);
41090
41092
  var import_node_path10 = __toESM(require("node:path"), 1);
41091
41093
  function readStdin() {
41092
41094
  return new Promise((resolve3, reject) => {
@@ -41193,14 +41195,14 @@ async function keysExportCi() {
41193
41195
  );
41194
41196
  }
41195
41197
  const dest = import_node_path10.default.resolve(process.cwd(), "ci-keystore.json");
41196
- import_node_fs11.default.copyFileSync(src, dest);
41198
+ import_node_fs12.default.copyFileSync(src, dest);
41197
41199
  console.log(`\u2713 Keystore copied to ci-keystore.json`);
41198
41200
  console.log(` Source: ${src}`);
41199
41201
  const gitignorePath = import_node_path10.default.resolve(process.cwd(), ".gitignore");
41200
- if (import_node_fs11.default.existsSync(gitignorePath)) {
41201
- const content = import_node_fs11.default.readFileSync(gitignorePath, "utf-8");
41202
+ if (import_node_fs12.default.existsSync(gitignorePath)) {
41203
+ const content = import_node_fs12.default.readFileSync(gitignorePath, "utf-8");
41202
41204
  if (!content.includes("ci-keystore.json")) {
41203
- import_node_fs11.default.appendFileSync(
41205
+ import_node_fs12.default.appendFileSync(
41204
41206
  gitignorePath,
41205
41207
  "\n# CI keystore \u2014 encrypted agent wallet, safe to commit\n!ci-keystore.json\n"
41206
41208
  );
@@ -41238,7 +41240,7 @@ async function keysShow() {
41238
41240
 
41239
41241
  // src/commands/mandate-contracts.ts
41240
41242
  var import_node_child_process = require("node:child_process");
41241
- var import_node_fs12 = require("node:fs");
41243
+ var import_node_fs13 = require("node:fs");
41242
41244
  var import_node_path11 = require("node:path");
41243
41245
  init_esm2();
41244
41246
 
@@ -41370,7 +41372,7 @@ Project: ${project.name}`));
41370
41372
  if (!keyExists("manager")) {
41371
41373
  throw new Error('No agent wallet found. Run "sailor keys generate" and choose "agent wallet" first.');
41372
41374
  }
41373
- const agentSigner = await loadManagerSigner2();
41375
+ const agentSigner = await loadManagerSigner();
41374
41376
  const agentAddress = agentSigner.address;
41375
41377
  say(() => console.log("\u2713", `Agent wallet: ${agentAddress}`));
41376
41378
  const chain2 = getChainById(project.chainId);
@@ -41383,7 +41385,7 @@ Project: ${project.name}`));
41383
41385
  () => console.log(
41384
41386
  `
41385
41387
  \u2192 Open the Sailor dashboard to approve signing requests:
41386
- ${signingPageUrl(channel, projectPort(process.cwd()))}
41388
+ ${signingPageUrl(projectPort(process.cwd()))}
41387
41389
  `
41388
41390
  )
41389
41391
  );
@@ -41884,7 +41886,7 @@ Mandate command failed: ${msg}`), {
41884
41886
  process.exit(1);
41885
41887
  }
41886
41888
  function announceSigningUrl(json) {
41887
- const url = signingPageUrl(void 0, projectPort(process.cwd()));
41889
+ const url = signingPageUrl(projectPort(process.cwd()));
41888
41890
  if (json) {
41889
41891
  process.stdout.write(`${JSON.stringify({ status: "waiting_for_signature", url })}
41890
41892
  `);
@@ -41937,7 +41939,7 @@ async function runDeploy(project, channel, options) {
41937
41939
  if (options.argsFile) {
41938
41940
  const argsFilePath = (0, import_node_path11.resolve)(options.argsFile);
41939
41941
  try {
41940
- argsJson = (0, import_node_fs12.readFileSync)(argsFilePath, "utf8").trim();
41942
+ argsJson = (0, import_node_fs13.readFileSync)(argsFilePath, "utf8").trim();
41941
41943
  } catch {
41942
41944
  throw new Error(`Cannot read --args-file: ${argsFilePath}`);
41943
41945
  }
@@ -42145,7 +42147,7 @@ async function runDeployClone(project, channel, options) {
42145
42147
  }
42146
42148
  const chain2 = getChainById(project.chainId);
42147
42149
  const publicClient = publicClientFor(project);
42148
- const agentSigner = await loadManagerSigner2();
42150
+ const agentSigner = await loadManagerSigner();
42149
42151
  const registered = await publicClient.readContract({
42150
42152
  address: project.contracts.kernel,
42151
42153
  abi: SailKernelAbi,
@@ -42489,7 +42491,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
42489
42491
  } catch (err) {
42490
42492
  if (err.message.startsWith("Security:")) throw err;
42491
42493
  }
42492
- const agentSigner = await loadManagerSigner2();
42494
+ const agentSigner = await loadManagerSigner();
42493
42495
  const walletClient = createWalletClient({
42494
42496
  account: agentSigner.viemAccount,
42495
42497
  chain: getChainById(project.chainId),
@@ -42531,7 +42533,7 @@ async function attachToSma(project, channel, publicClient, sma, mandate2, label,
42531
42533
  const say = (fn) => {
42532
42534
  if (!json) fn();
42533
42535
  };
42534
- const agentSigner = await loadManagerSigner2();
42536
+ const agentSigner = await loadManagerSigner();
42535
42537
  const registered = await publicClient.readContract({
42536
42538
  address: project.contracts.kernel,
42537
42539
  abi: SailKernelAbi,
@@ -42572,7 +42574,7 @@ async function attachBatchToSma(project, channel, publicClient, sma, permissions
42572
42574
  const say = (fn) => {
42573
42575
  if (!json) fn();
42574
42576
  };
42575
- const agentSigner = await loadManagerSigner2();
42577
+ const agentSigner = await loadManagerSigner();
42576
42578
  const registered = await publicClient.readContract({
42577
42579
  address: project.contracts.kernel,
42578
42580
  abi: SailKernelAbi,
@@ -42788,14 +42790,14 @@ Resolved: ${resolved}`
42788
42790
  }
42789
42791
  artifactPath = resolved;
42790
42792
  if (options.build) runForgeBuild();
42791
- if (!(0, import_node_fs12.existsSync)(artifactPath)) {
42793
+ if (!(0, import_node_fs13.existsSync)(artifactPath)) {
42792
42794
  ensureForgeHint();
42793
42795
  throw new Error(
42794
42796
  `Artifact not found: ${artifactPath}
42795
42797
  Compile your mandate first (e.g. \`forge build\`), or pass --build.`
42796
42798
  );
42797
42799
  }
42798
- const artifact = JSON.parse((0, import_node_fs12.readFileSync)(artifactPath, "utf8"));
42800
+ const artifact = JSON.parse((0, import_node_fs13.readFileSync)(artifactPath, "utf8"));
42799
42801
  const abi2 = artifact.abi;
42800
42802
  const bytecodeRaw = artifact.bytecode?.object ?? artifact.bytecode;
42801
42803
  if (!bytecodeRaw || typeof bytecodeRaw !== "string") {
@@ -42878,13 +42880,13 @@ function runForgeBuild() {
42878
42880
  init_esm2();
42879
42881
 
42880
42882
  // src/lib/permission-explainer.ts
42881
- var import_node_fs13 = require("node:fs");
42883
+ var import_node_fs14 = require("node:fs");
42882
42884
  var import_node_path12 = require("node:path");
42883
42885
  function explainPermission(name, sourcePath) {
42884
42886
  const resolved = sourcePath ?? (0, import_node_path12.join)(process.cwd(), "mandates", `${name}.sol`);
42885
42887
  let src;
42886
42888
  try {
42887
- src = (0, import_node_fs13.readFileSync)(resolved, "utf8");
42889
+ src = (0, import_node_fs14.readFileSync)(resolved, "utf8");
42888
42890
  } catch {
42889
42891
  return null;
42890
42892
  }
@@ -43106,7 +43108,7 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
43106
43108
  }
43107
43109
 
43108
43110
  // src/commands/mandate-simulate.ts
43109
- var import_node_fs14 = require("node:fs");
43111
+ var import_node_fs15 = require("node:fs");
43110
43112
  init_esm2();
43111
43113
  function parseExpect(raw, where) {
43112
43114
  if (raw === void 0) return void 0;
@@ -43122,7 +43124,7 @@ function resolveSampleCalls(options) {
43122
43124
  if (options.calls) {
43123
43125
  let raw;
43124
43126
  try {
43125
- raw = JSON.parse((0, import_node_fs14.readFileSync)(options.calls, "utf8"));
43127
+ raw = JSON.parse((0, import_node_fs15.readFileSync)(options.calls, "utf8"));
43126
43128
  } catch (err) {
43127
43129
  throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
43128
43130
  }
@@ -43338,7 +43340,7 @@ async function mandateSimulate(options) {
43338
43340
  }
43339
43341
 
43340
43342
  // src/commands/rotate-signer.ts
43341
- var import_node_fs15 = require("node:fs");
43343
+ var import_node_fs16 = require("node:fs");
43342
43344
  init_esm2();
43343
43345
  var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
43344
43346
  async function rotateSigner(options) {
@@ -43468,7 +43470,7 @@ SMA: ${smaAddress}`);
43468
43470
  console.log(
43469
43471
  `
43470
43472
  \u2192 Open the Sailor dashboard to approve signing requests:
43471
- ${signingPageUrl(channel, projectPort(process.cwd()))}
43473
+ ${signingPageUrl(projectPort(process.cwd()))}
43472
43474
  `
43473
43475
  );
43474
43476
  });
@@ -43726,7 +43728,7 @@ ensure the agent that signs dispatches holds this key.`
43726
43728
  const keystore = await keyring.exportKeystore(password);
43727
43729
  writeJsonFile(target, keystore, 384);
43728
43730
  const perManagerPath = managerKeystorePath(keyring.address);
43729
- (0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43731
+ (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43730
43732
  writeJsonFile(perManagerPath, keystore, 384);
43731
43733
  say(
43732
43734
  () => console.log(
@@ -43738,7 +43740,7 @@ ensure the agent that signs dispatches holds this key.`
43738
43740
  function promoteManagerKeystore(newManager, say) {
43739
43741
  const stored = readJsonFile(managerKeystorePath(newManager));
43740
43742
  if (!stored) return;
43741
- (0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43743
+ (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43742
43744
  const activeTarget = keyPath("manager");
43743
43745
  const displaced = readJsonFile(activeTarget);
43744
43746
  if (displaced?.address) {
@@ -43790,7 +43792,7 @@ function writePending(pending) {
43790
43792
  }
43791
43793
  function clearPending() {
43792
43794
  try {
43793
- (0, import_node_fs15.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
43795
+ (0, import_node_fs16.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
43794
43796
  } catch {
43795
43797
  }
43796
43798
  }
@@ -43844,7 +43846,7 @@ async function ownerConnect(options) {
43844
43846
  await channel.start();
43845
43847
  if (!options.json) {
43846
43848
  console.log("\u2192 Open the Sailor dashboard and connect your wallet:");
43847
- console.log(` ${signingPageUrl(channel, projectPort(projectRoot))}`);
43849
+ console.log(` ${signingPageUrl(projectPort(projectRoot))}`);
43848
43850
  if (channel.remote) console.log(" (using the running signing station)");
43849
43851
  console.log("\nWaiting for a wallet connection\u2026");
43850
43852
  }
@@ -43893,41 +43895,6 @@ var import_node_fs17 = __toESM(require("node:fs"), 1);
43893
43895
  var import_node_path13 = __toESM(require("node:path"), 1);
43894
43896
  var import_node_url = require("node:url");
43895
43897
  init_esm2();
43896
-
43897
- // src/lib/process.ts
43898
- var import_node_fs16 = __toESM(require("node:fs"), 1);
43899
- function agentPidPath(chainId) {
43900
- return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
43901
- }
43902
- function writeAgentPid(chainId) {
43903
- import_node_fs16.default.mkdirSync(sailPath(), { recursive: true });
43904
- import_node_fs16.default.writeFileSync(agentPidPath(chainId), `${process.pid}
43905
- `);
43906
- }
43907
- function clearAgentPid(chainId) {
43908
- try {
43909
- import_node_fs16.default.rmSync(agentPidPath(chainId));
43910
- } catch {
43911
- }
43912
- }
43913
- function readAgentPid(chainId) {
43914
- try {
43915
- const pid = Number.parseInt(import_node_fs16.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
43916
- return Number.isNaN(pid) ? null : pid;
43917
- } catch {
43918
- return null;
43919
- }
43920
- }
43921
- function isProcessAlive(pid) {
43922
- try {
43923
- process.kill(pid, 0);
43924
- return true;
43925
- } catch {
43926
- return false;
43927
- }
43928
- }
43929
-
43930
- // src/commands/run.ts
43931
43898
  var DEFAULT_INTERVAL_SEC = 60;
43932
43899
  var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
43933
43900
  var ERC20_READ_ABI = [
@@ -44972,7 +44939,7 @@ function requireAccount() {
44972
44939
  }
44973
44940
  return account2;
44974
44941
  }
44975
- async function sessionPause() {
44942
+ async function sessionPause(opts = {}) {
44976
44943
  const account2 = requireAccount();
44977
44944
  const signer = await loadAnySigner();
44978
44945
  const client = makeClient(account2.chainId);
@@ -44987,9 +44954,10 @@ async function sessionPause() {
44987
44954
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
44988
44955
  };
44989
44956
  writeJsonFile(sailPath("session.json"), session2);
44990
- console.log("Session paused \u2014 agent dispatch rights revoked.");
44957
+ if (opts.json) console.log(JSON.stringify(session2));
44958
+ else console.log("Session paused \u2014 agent dispatch rights revoked.");
44991
44959
  }
44992
- async function sessionResume() {
44960
+ async function sessionResume(opts = {}) {
44993
44961
  const account2 = requireAccount();
44994
44962
  const signer = await loadAnySigner();
44995
44963
  const client = makeClient(account2.chainId);
@@ -45004,7 +44972,8 @@ async function sessionResume() {
45004
44972
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
45005
44973
  };
45006
44974
  writeJsonFile(sailPath("session.json"), session2);
45007
- console.log("Session resumed \u2014 agent dispatch rights restored.");
44975
+ if (opts.json) console.log(JSON.stringify(session2));
44976
+ else console.log("Session resumed \u2014 agent dispatch rights restored.");
45008
44977
  }
45009
44978
 
45010
44979
  // src/commands/station.ts
@@ -45122,7 +45091,11 @@ async function status() {
45122
45091
  const hasManager = keyExists("manager");
45123
45092
  const hasPermissionSigner = keyExists("permissionSigner");
45124
45093
  const account2 = readJsonFile(sailPath("account.json"));
45125
- const mandate2 = readJsonFile(sailPath("mandate.json"));
45094
+ const mandateRaw = readJsonFile(sailPath("mandate.json"));
45095
+ const mandates = (Array.isArray(mandateRaw) ? mandateRaw : mandateRaw ? [mandateRaw] : []).filter(
45096
+ Boolean
45097
+ );
45098
+ const hasMandate = mandates.length > 0;
45126
45099
  const session2 = readJsonFile(sailPath("session.json"));
45127
45100
  console.log("Sailor status");
45128
45101
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
@@ -45138,9 +45111,12 @@ async function status() {
45138
45111
  console.log(' \u2717 not deployed run "sailor onboard --new-sma"');
45139
45112
  }
45140
45113
  console.log("Mandate:");
45141
- if (mandate2) {
45142
- const n = mandate2.permissions.length;
45143
- console.log(` \u2713 signed ${n} permission${n === 1 ? "" : "s"}`);
45114
+ if (hasMandate) {
45115
+ const n = mandates.reduce((sum, m) => sum + (m?.permissions?.length ?? 0), 0);
45116
+ const permLabel = `${n} permission${n === 1 ? "" : "s"}`;
45117
+ console.log(
45118
+ mandates.length > 1 ? ` \u2713 signed ${mandates.length} mandates, ${permLabel}` : ` \u2713 signed ${permLabel}`
45119
+ );
45144
45120
  } else {
45145
45121
  console.log(' \u2717 not signed run "sailor mandate sign"');
45146
45122
  }
@@ -45150,7 +45126,7 @@ async function status() {
45150
45126
  let agentState;
45151
45127
  if (running) {
45152
45128
  agentState = `running (PID ${pid})`;
45153
- } else if (!mandate2) {
45129
+ } else if (!hasMandate) {
45154
45130
  agentState = "not configured";
45155
45131
  } else if (session2 && session2.active === false) {
45156
45132
  agentState = "stopped (session paused)";
@@ -45161,10 +45137,52 @@ async function status() {
45161
45137
  }
45162
45138
 
45163
45139
  // src/commands/ui.ts
45164
- var import_node_child_process4 = require("node:child_process");
45140
+ var import_node_child_process5 = require("node:child_process");
45165
45141
  var import_node_fs20 = __toESM(require("node:fs"), 1);
45166
45142
  var import_node_net2 = __toESM(require("node:net"), 1);
45167
45143
  var import_node_path16 = __toESM(require("node:path"), 1);
45144
+
45145
+ // src/lib/tailscale.ts
45146
+ var import_node_child_process4 = require("node:child_process");
45147
+ function tailscaleAvailable() {
45148
+ try {
45149
+ return (0, import_node_child_process4.spawnSync)("tailscale", ["version"], { stdio: "ignore" }).status === 0;
45150
+ } catch {
45151
+ return false;
45152
+ }
45153
+ }
45154
+ function tailnetDnsName() {
45155
+ const r = (0, import_node_child_process4.spawnSync)("tailscale", ["status", "--json"], { encoding: "utf-8" });
45156
+ if (r.status !== 0 || !r.stdout) return null;
45157
+ try {
45158
+ const j = JSON.parse(r.stdout);
45159
+ if (j.BackendState && j.BackendState !== "Running") return null;
45160
+ const dns = j.Self?.DNSName;
45161
+ if (typeof dns !== "string" || dns.length === 0) return null;
45162
+ return dns.replace(/\.$/, "");
45163
+ } catch {
45164
+ return null;
45165
+ }
45166
+ }
45167
+ function tailscaleServeUp(port) {
45168
+ const r = (0, import_node_child_process4.spawnSync)(
45169
+ "tailscale",
45170
+ ["serve", "--bg", "--https=443", `http://127.0.0.1:${port}`],
45171
+ { encoding: "utf-8" }
45172
+ );
45173
+ if (r.status !== 0) {
45174
+ const detail = (r.stderr || r.stdout || "").trim() || `exit ${r.status ?? "?"}`;
45175
+ throw new Error(detail);
45176
+ }
45177
+ }
45178
+ function tailscaleServeDown() {
45179
+ try {
45180
+ (0, import_node_child_process4.spawnSync)("tailscale", ["serve", "--https=443", "off"], { stdio: "ignore" });
45181
+ } catch {
45182
+ }
45183
+ }
45184
+
45185
+ // src/commands/ui.ts
45168
45186
  var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
45169
45187
  function readState2(projectRoot) {
45170
45188
  const file = import_node_path16.default.join(projectRoot, UI_STATE_FILE);
@@ -45175,21 +45193,33 @@ function readState2(projectRoot) {
45175
45193
  return null;
45176
45194
  }
45177
45195
  }
45178
- function isAlive(pid) {
45196
+ function installedCliVersion() {
45179
45197
  try {
45180
- process.kill(pid, 0);
45181
- return true;
45198
+ const pkg = JSON.parse(
45199
+ import_node_fs20.default.readFileSync(import_node_path16.default.join(packageRoot(), "package.json"), "utf-8")
45200
+ );
45201
+ return pkg.version ?? null;
45182
45202
  } catch {
45183
- return false;
45203
+ return null;
45184
45204
  }
45185
45205
  }
45186
- function findFreePort(from14) {
45187
- return new Promise((resolve3, reject) => {
45188
- const server = import_node_net2.default.createServer();
45189
- server.unref();
45190
- server.on("error", () => findFreePort(from14 + 1).then(resolve3, reject));
45191
- server.listen(from14, "127.0.0.1", () => server.close(() => resolve3(from14)));
45192
- });
45206
+ async function warnIfVersionSkew(port) {
45207
+ const cli = installedCliVersion();
45208
+ if (!cli) return;
45209
+ try {
45210
+ const res = await fetch(`http://127.0.0.1:${port}/api/version`, {
45211
+ signal: AbortSignal.timeout(500)
45212
+ });
45213
+ if (!res.ok) return;
45214
+ const { running } = await res.json();
45215
+ if (running && running !== cli) {
45216
+ console.warn(
45217
+ `\u26A0 Version skew: the running dashboard is v${running}, but the installed CLI is v${cli}.
45218
+ Restart it to load the new version: sailor ui stop && sailor ui start`
45219
+ );
45220
+ }
45221
+ } catch {
45222
+ }
45193
45223
  }
45194
45224
  function waitForPort(port, timeoutMs) {
45195
45225
  const deadline = Date.now() + timeoutMs;
@@ -45209,7 +45239,7 @@ function waitForPort(port, timeoutMs) {
45209
45239
  attempt();
45210
45240
  });
45211
45241
  }
45212
- async function uiCommand() {
45242
+ async function uiCommand(opts = {}) {
45213
45243
  const distDir = cliDistDir();
45214
45244
  const uiDistDir = import_node_path16.default.join(packageRoot(), "packages", "ui", "dist");
45215
45245
  const serverBundle = import_node_path16.default.resolve(distDir, "server.cjs");
@@ -45223,19 +45253,49 @@ async function uiCommand() {
45223
45253
  if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
45224
45254
  throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
45225
45255
  }
45256
+ let tailnetUrl = null;
45257
+ if (opts.expose) {
45258
+ if (opts.expose !== "tailscale") {
45259
+ throw new Error(`Unknown --expose mode "${opts.expose}". Supported: tailscale.`);
45260
+ }
45261
+ if (!tailscaleAvailable()) {
45262
+ throw new Error(
45263
+ "tailscale CLI not found. Install Tailscale and run `tailscale up`, or start the dashboard without --expose to keep it local-only."
45264
+ );
45265
+ }
45266
+ const dns = tailnetDnsName();
45267
+ if (!dns) {
45268
+ throw new Error(
45269
+ "Could not resolve this node's tailnet name. Is tailscale running and logged in? Run `tailscale status` to check."
45270
+ );
45271
+ }
45272
+ tailnetUrl = `https://${dns}`;
45273
+ }
45226
45274
  const existing = readState2(projectRoot);
45227
- if (existing && isAlive(existing.pid)) {
45275
+ if (existing && isProcessAlive(existing.pid)) {
45228
45276
  console.log(`Sailor UI is already running (pid ${existing.pid}) at http://localhost:${existing.port}`);
45277
+ if (opts.expose) {
45278
+ console.log("To expose it on the tailnet, stop it first: sailor ui stop && sailor ui start --expose tailscale");
45279
+ }
45280
+ await warnIfVersionSkew(existing.port);
45229
45281
  return;
45230
45282
  }
45231
45283
  const runtimeDir = import_node_path16.default.join(projectRoot, ".sail", "runtime");
45232
45284
  import_node_fs20.default.mkdirSync(runtimeDir, { recursive: true });
45233
45285
  const logFile = import_node_path16.default.join(runtimeDir, "ui.log");
45234
45286
  const logFd = import_node_fs20.default.openSync(logFile, "a");
45235
- const child = (0, import_node_child_process4.spawn)(process.execPath, [serverBundle], {
45287
+ const corsOrigins = [process.env.SAILOR_CORS_ORIGINS, tailnetUrl].filter(Boolean).join(",");
45288
+ const child = (0, import_node_child_process5.spawn)(process.execPath, [serverBundle], {
45236
45289
  detached: true,
45237
45290
  stdio: ["ignore", logFd, logFd],
45238
- env: { ...process.env, SAIL_DIR: sailDir2, SERVE_DIST: "1", PORT: String(port), SAILOR_UI_DIST: uiDistDir }
45291
+ env: {
45292
+ ...process.env,
45293
+ SAIL_DIR: sailDir2,
45294
+ SERVE_DIST: "1",
45295
+ PORT: String(port),
45296
+ SAILOR_UI_DIST: uiDistDir,
45297
+ ...corsOrigins ? { SAILOR_CORS_ORIGINS: corsOrigins } : {}
45298
+ }
45239
45299
  });
45240
45300
  child.unref();
45241
45301
  import_node_fs20.default.closeSync(logFd);
@@ -45243,7 +45303,7 @@ async function uiCommand() {
45243
45303
  const deadline = Date.now() + READY_TIMEOUT_MS;
45244
45304
  let ready = false;
45245
45305
  while (Date.now() < deadline) {
45246
- if (!isAlive(child.pid)) break;
45306
+ if (!isProcessAlive(child.pid)) break;
45247
45307
  if (await waitForPort(port, 500)) {
45248
45308
  ready = true;
45249
45309
  break;
@@ -45262,17 +45322,31 @@ Server output:
45262
45322
  ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
45263
45323
  );
45264
45324
  }
45325
+ let exposed = false;
45326
+ if (tailnetUrl) {
45327
+ try {
45328
+ tailscaleServeUp(port);
45329
+ exposed = true;
45330
+ } catch (err) {
45331
+ console.warn(
45332
+ `\u26A0 Could not expose on the tailnet: ${err.message}
45333
+ The dashboard is still running locally.`
45334
+ );
45335
+ }
45336
+ }
45265
45337
  import_node_fs20.default.writeFileSync(
45266
45338
  import_node_path16.default.join(projectRoot, UI_STATE_FILE),
45267
- JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
45339
+ JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString(), exposed }, null, 2)
45268
45340
  );
45269
45341
  console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
45342
+ if (exposed) console.log(`Exposed on your tailnet at ${tailnetUrl}/`);
45270
45343
  console.log(`Stop it with: sailor ui stop`);
45271
45344
  }
45272
- function uiStatus() {
45345
+ async function uiStatus() {
45273
45346
  const state = readState2(process.cwd());
45274
- if (state && isAlive(state.pid)) {
45347
+ if (state && isProcessAlive(state.pid)) {
45275
45348
  console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
45349
+ await warnIfVersionSkew(state.port);
45276
45350
  } else {
45277
45351
  if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
45278
45352
  console.log("\u25CB Sailor UI is not running");
@@ -45285,12 +45359,16 @@ function uiStop() {
45285
45359
  console.log("Sailor UI is not running.");
45286
45360
  return;
45287
45361
  }
45288
- if (!isAlive(state.pid)) {
45362
+ if (!isProcessAlive(state.pid)) {
45289
45363
  import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
45290
45364
  console.log("Sailor UI is not running (stale state file removed).");
45291
45365
  return;
45292
45366
  }
45293
45367
  process.kill(state.pid, "SIGTERM");
45368
+ if (state.exposed) {
45369
+ tailscaleServeDown();
45370
+ console.log("Tailnet exposure removed.");
45371
+ }
45294
45372
  import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
45295
45373
  console.log(`Stopped Sailor UI (pid ${state.pid}).`);
45296
45374
  }
@@ -45342,7 +45420,7 @@ program2.command("init [dir]").description("Scaffold a new Sail agent into the c
45342
45420
  );
45343
45421
  program2.command("update").description("Re-sync agent tooling files (skills, AGENTS.md, Dockerfile) from the latest template").action(action(updateCommand));
45344
45422
  var ui = program2.command("ui").description("Manage the local Sailor dashboard");
45345
- ui.command("start").description("Start the dashboard at localhost:3333 (default)").action(action(uiCommand));
45423
+ ui.command("start").description("Start the dashboard at localhost:3333 (default)").option("--expose <mode>", "Expose the dashboard over HTTPS on your tailnet (mode: tailscale)").action(actionWith(uiCommand));
45346
45424
  ui.command("stop").description("Stop the running dashboard").action(() => uiStop());
45347
45425
  ui.command("status").description("Show whether the dashboard is running").action(() => uiStatus());
45348
45426
  ui.action(action(uiCommand));
@@ -45407,8 +45485,8 @@ program2.command("run").description("Run the agent execution loop (use --once fo
45407
45485
  var trigger = program2.command("trigger").description("Wake the agent on demand from an external system");
45408
45486
  trigger.command("github").description("Fire the agent's GitHub Actions workflow_dispatch (the same job the cron runs)").option("--workflow <file>", "Workflow file to dispatch", "agent-tick.yml").option("--ref <branch>", "Git ref to run the workflow on", "main").option("--reason <text>", "Why this run fired \u2014 recorded as the workflow's reason input").option("--repo <owner/repo>", "Override the repository (default: from the git origin remote)").option("--json", "Emit machine-readable JSON").action(actionWith(triggerGithub));
45409
45487
  var session = program2.command("session").description("Control the agent session");
45410
- session.command("pause").description("Pause the agent session (revoke dispatch rights)").action(action(sessionPause));
45411
- session.command("resume").description("Resume a paused session").action(action(sessionResume));
45488
+ session.command("pause").description("Pause the agent session (revoke dispatch rights)").option("--json", "Emit machine-readable JSON").action(actionWith(sessionPause));
45489
+ session.command("resume").description("Resume a paused session").option("--json", "Emit machine-readable JSON").action(actionWith(sessionResume));
45412
45490
  program2.command("doctor").description(
45413
45491
  "Preflight (read-only): kernel model, permission health, RPC + gas balances, before spending gas"
45414
45492
  ).option("--json", "Output machine-readable JSON").option("--account <address>", "SMA to check (defaults to .sail/account.json)").action(actionWith(doctor));