@dev.sail.money/sailor 1.3.0-223 → 1.3.0-225

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 (135) hide show
  1. package/package.json +1 -1
  2. package/packages/cli/dist/index.cjs +694 -133
  3. package/packages/cli/dist/server.cjs +15884 -17950
  4. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts +130 -0
  5. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts.map +1 -0
  6. package/packages/sdk/dist/abis/ConfigurablePermission.js +79 -0
  7. package/packages/sdk/dist/abis/ConfigurablePermission.js.map +1 -0
  8. package/packages/sdk/dist/abis/index.d.ts +1 -0
  9. package/packages/sdk/dist/abis/index.d.ts.map +1 -1
  10. package/packages/sdk/dist/abis/index.js +1 -0
  11. package/packages/sdk/dist/abis/index.js.map +1 -1
  12. package/packages/sdk/dist/index.d.ts +1 -0
  13. package/packages/sdk/dist/index.d.ts.map +1 -1
  14. package/packages/sdk/dist/index.js +1 -0
  15. package/packages/sdk/dist/index.js.map +1 -1
  16. package/packages/sdk/dist/intelligence.d.ts +1 -1
  17. package/packages/sdk/dist/intelligence.js +1 -1
  18. package/packages/ui/dist/assets/{add-DLYFRKuI.js → add-QIu9SGBU.js} +1 -1
  19. package/packages/ui/dist/assets/{all-wallets-Dz5SZj5e.js → all-wallets-2auxp9E5.js} +1 -1
  20. package/packages/ui/dist/assets/{app-store-DCYRFDwY.js → app-store-BVtVvk-F.js} +1 -1
  21. package/packages/ui/dist/assets/{apple-BcAi8XTW.js → apple-Dq7Ppvqv.js} +1 -1
  22. package/packages/ui/dist/assets/{arrow-bottom-Bik1zp_1.js → arrow-bottom-DpT3R4Rr.js} +1 -1
  23. package/packages/ui/dist/assets/{arrow-bottom-circle-BSA6CcrO.js → arrow-bottom-circle-hkf9bWhW.js} +1 -1
  24. package/packages/ui/dist/assets/{arrow-left-07r0E796.js → arrow-left-3PhiE4SD.js} +1 -1
  25. package/packages/ui/dist/assets/{arrow-right-CW1Ka1mT.js → arrow-right-DUIXJHib.js} +1 -1
  26. package/packages/ui/dist/assets/{arrow-top-BoAo8_T9.js → arrow-top-Cf--02la.js} +1 -1
  27. package/packages/ui/dist/assets/{bank-Bpa5anN0.js → bank-CkRSSgJy.js} +1 -1
  28. package/packages/ui/dist/assets/{basic-CbzdfUfs.js → basic-D9puI79I.js} +1 -1
  29. package/packages/ui/dist/assets/{browser-B4YQX2Tn.js → browser-BC8qo0Bh.js} +1 -1
  30. package/packages/ui/dist/assets/{card-CF8oS4fb.js → card-BYuGTxNc.js} +1 -1
  31. package/packages/ui/dist/assets/{ccip-DssR-Uhz.js → ccip-BS_rjbk9.js} +1 -1
  32. package/packages/ui/dist/assets/{checkmark-BihQ9h3G.js → checkmark-C-MZeapS.js} +1 -1
  33. package/packages/ui/dist/assets/{checkmark-bold-c5EPMyPR.js → checkmark-bold-Bs3hadsW.js} +1 -1
  34. package/packages/ui/dist/assets/{chevron-bottom-Lijs68sl.js → chevron-bottom-C8mqtkD4.js} +1 -1
  35. package/packages/ui/dist/assets/{chevron-left-Cj7sg_Iw.js → chevron-left-Dxj-vVDJ.js} +1 -1
  36. package/packages/ui/dist/assets/{chevron-right-CqD-gBdk.js → chevron-right-BoIrOTtL.js} +1 -1
  37. package/packages/ui/dist/assets/{chevron-top-DDShxMva.js → chevron-top-BlgziB_P.js} +1 -1
  38. package/packages/ui/dist/assets/{chrome-store-DSBNmkPw.js → chrome-store-sDKm_6es.js} +1 -1
  39. package/packages/ui/dist/assets/{clock-CjlvcnX9.js → clock-BwfFymxk.js} +1 -1
  40. package/packages/ui/dist/assets/{close-QhgNd0Xc.js → close-BewD5_E0.js} +1 -1
  41. package/packages/ui/dist/assets/{coinPlaceholder-C7TUN8V-.js → coinPlaceholder-DRPvf0NA.js} +1 -1
  42. package/packages/ui/dist/assets/{compass-DrE1sLwt.js → compass-BHPvd2dw.js} +1 -1
  43. package/packages/ui/dist/assets/{copy-CFx1aksp.js → copy-BMv4Y3KF.js} +1 -1
  44. package/packages/ui/dist/assets/{core-CcaMuBIR.js → core-BMNH6XFj.js} +3 -3
  45. package/packages/ui/dist/assets/cursor-ir0cjMUl.js +3 -0
  46. package/packages/ui/dist/assets/{cursor-transparent-DxBdWZi8.js → cursor-transparent-DT8YBqHU.js} +1 -1
  47. package/packages/ui/dist/assets/{desktop-B-ATHCGS.js → desktop-BeePUUqi.js} +1 -1
  48. package/packages/ui/dist/assets/{disconnect-Bvu0LqoD.js → disconnect-C2qT29qY.js} +1 -1
  49. package/packages/ui/dist/assets/{discord-DrRGYSx6.js → discord-I4ToELfU.js} +1 -1
  50. package/packages/ui/dist/assets/{etherscan-BaQiGNrH.js → etherscan-3psXC5Ef.js} +1 -1
  51. package/packages/ui/dist/assets/{events-BYNiBrPy.js → events-Cdt6abbf.js} +1 -1
  52. package/packages/ui/dist/assets/{exclamation-triangle-B3yAGM1Q.js → exclamation-triangle-BCaWksGJ.js} +1 -1
  53. package/packages/ui/dist/assets/{extension-CYA4bLg-.js → extension-DfYavvbY.js} +1 -1
  54. package/packages/ui/dist/assets/{external-link-D5PwFm0_.js → external-link-B8uBskz7.js} +1 -1
  55. package/packages/ui/dist/assets/{facebook-CEwgL1NC.js → facebook-BSvrXy3w.js} +1 -1
  56. package/packages/ui/dist/assets/{fallback-B9sp077f.js → fallback-BSMYROva.js} +1 -1
  57. package/packages/ui/dist/assets/{farcaster-Ak6D7Lp_.js → farcaster-CcAuzEDi.js} +1 -1
  58. package/packages/ui/dist/assets/{filters-CJduAQRB.js → filters-umCx-UFU.js} +1 -1
  59. package/packages/ui/dist/assets/{github-PLN2sD5Q.js → github-CBNoBl67.js} +1 -1
  60. package/packages/ui/dist/assets/{google-BRRn9hKY.js → google-yyR1jNOC.js} +1 -1
  61. package/packages/ui/dist/assets/{help-circle-BnvqUg2y.js → help-circle-C0WjKnIM.js} +1 -1
  62. package/packages/ui/dist/assets/{id-CJlrhYxX.js → id-BwIK6e_N.js} +1 -1
  63. package/packages/ui/dist/assets/{image-CPduqsY3.js → image-D5oAo1Te.js} +1 -1
  64. package/packages/ui/dist/assets/{index-Bp21-hUb.js → index-B_Gc8xPp.js} +1 -1
  65. package/packages/ui/dist/assets/{index-D-OgqpEg.js → index-BaEe036r.js} +1 -1
  66. package/packages/ui/dist/assets/{index-vEKHRoHD.js → index-BuqyrXYX.js} +1 -1
  67. package/packages/ui/dist/assets/index-CKoOBsKn.css +1 -0
  68. package/packages/ui/dist/assets/index-CPrhuDfP.js +1782 -0
  69. package/packages/ui/dist/assets/{index-C0GUKQXc.js → index-DCWU1RuS.js} +1 -1
  70. package/packages/ui/dist/assets/{index-Dyr25y7I.js → index-DMNSiu42.js} +3 -3
  71. package/packages/ui/dist/assets/{index.es-Dks2goS7.js → index.es-DImsg59a.js} +4 -4
  72. package/packages/ui/dist/assets/{info-CGNHPKFR.js → info-CtcvKk8x.js} +1 -1
  73. package/packages/ui/dist/assets/{info-circle-D8mpP6LN.js → info-circle-BIm6XYyt.js} +1 -1
  74. package/packages/ui/dist/assets/{lightbulb-BWGaqAI3.js → lightbulb-CMj21RIi.js} +1 -1
  75. package/packages/ui/dist/assets/{mail-BcfubTQ2.js → mail-C805tcaU.js} +1 -1
  76. package/packages/ui/dist/assets/{metamask-sdk-B1i24ALe.js → metamask-sdk-DDk4vkrl.js} +1 -1
  77. package/packages/ui/dist/assets/{mobile-CglLmMeZ.js → mobile-BEii3NyZ.js} +1 -1
  78. package/packages/ui/dist/assets/{more-OgWHs8eZ.js → more-RSnRt81X.js} +1 -1
  79. package/packages/ui/dist/assets/{network-placeholder-BUhDcMI_.js → network-placeholder-s9JaqzCQ.js} +1 -1
  80. package/packages/ui/dist/assets/{nftPlaceholder-wxKy5Q-x.js → nftPlaceholder-Ccur2yVc.js} +1 -1
  81. package/packages/ui/dist/assets/{off-DxJM1N51.js → off-DUtG4rlI.js} +1 -1
  82. package/packages/ui/dist/assets/{parseSignature-D838ybCF.js → parseSignature-BbS8pxpB.js} +1 -1
  83. package/packages/ui/dist/assets/{play-store-jm12tVwP.js → play-store-Bt2HvO3e.js} +1 -1
  84. package/packages/ui/dist/assets/{plus--zRIsRuA.js → plus-BtEzQu0h.js} +1 -1
  85. package/packages/ui/dist/assets/{qr-code-DhBJbVz8.js → qr-code-xbQZ-Nio.js} +1 -1
  86. package/packages/ui/dist/assets/{recycle-horizontal-1zQ5U9as.js → recycle-horizontal-d9pzE6vq.js} +1 -1
  87. package/packages/ui/dist/assets/{refresh-mqmMmKBD.js → refresh-B3-2Sch0.js} +1 -1
  88. package/packages/ui/dist/assets/{reown-logo-RZHJRVcN.js → reown-logo-DBkmQAX8.js} +1 -1
  89. package/packages/ui/dist/assets/{search-Bd45qI2U.js → search-dgDA8Ou5.js} +1 -1
  90. package/packages/ui/dist/assets/{secp256k1-Uhtu7xK-.js → secp256k1-C4g4FCze.js} +1 -1
  91. package/packages/ui/dist/assets/{send-4lxASBOR.js → send-CEDXr4l7.js} +1 -1
  92. package/packages/ui/dist/assets/{swapHorizontal-Cj2JRLJS.js → swapHorizontal-Co6_f8oH.js} +1 -1
  93. package/packages/ui/dist/assets/{swapHorizontalBold-Byv9z2H0.js → swapHorizontalBold-k4d22FNy.js} +1 -1
  94. package/packages/ui/dist/assets/{swapHorizontalMedium-BadlZnn_.js → swapHorizontalMedium-Des6w_mz.js} +1 -1
  95. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Dl20h9DP.js → swapHorizontalRoundedBold-CVd4538j.js} +1 -1
  96. package/packages/ui/dist/assets/{swapVertical-DoFYn8T-.js → swapVertical-Cn_eRfv8.js} +1 -1
  97. package/packages/ui/dist/assets/{telegram-DKM_HViT.js → telegram-BE46RC0s.js} +1 -1
  98. package/packages/ui/dist/assets/{three-dots-CBLRiosA.js → three-dots-CvqWsUWa.js} +1 -1
  99. package/packages/ui/dist/assets/{twitch-Dj3xBpb1.js → twitch-9V7YXB0X.js} +1 -1
  100. package/packages/ui/dist/assets/{twitterIcon-D-wqABHa.js → twitterIcon-DbUxk6yk.js} +1 -1
  101. package/packages/ui/dist/assets/{verify-BYs2pfC2.js → verify-CQSnwVrh.js} +1 -1
  102. package/packages/ui/dist/assets/{verify-filled-zJG9VG5w.js → verify-filled-DG_hDTr8.js} +1 -1
  103. package/packages/ui/dist/assets/{w3m-modal-sbOUP95Y.js → w3m-modal-BsHGIrg7.js} +1 -1
  104. package/packages/ui/dist/assets/{wallet-CMl9HrCE.js → wallet-lbVIReb_.js} +1 -1
  105. package/packages/ui/dist/assets/{wallet-placeholder-BneM1wji.js → wallet-placeholder-C_ndmKAs.js} +1 -1
  106. package/packages/ui/dist/assets/{walletconnect-DVz3u0az.js → walletconnect-ByDpG-E0.js} +1 -1
  107. package/packages/ui/dist/assets/{warning-circle-B_P5OBud.js → warning-circle-hrS3II0d.js} +1 -1
  108. package/packages/ui/dist/assets/{x-CU4y53M6.js → x-BPMn3x5J.js} +1 -1
  109. package/packages/ui/dist/index.html +2 -2
  110. package/templates/default/.agents/skills/sail-mandates/references/approvals.md +3 -3
  111. package/templates/default/.agents/skills/sail-swap-quote/SKILL.md +76 -0
  112. package/templates/default/.agents/skills/sail-template-approve-batch/SKILL.md +98 -0
  113. package/templates/default/.agents/skills/sail-template-borrow/SKILL.md +68 -0
  114. package/templates/default/.agents/skills/sail-template-deposit/SKILL.md +61 -0
  115. package/templates/default/.agents/skills/sail-template-swap/SKILL.md +207 -0
  116. package/templates/default/.agents/skills/sail-template-swap-no-oracle/SKILL.md +100 -0
  117. package/templates/default/.agents/skills/sail-template-transfer/SKILL.md +52 -0
  118. package/templates/default/.agents/skills/sail-template-withdraw/SKILL.md +50 -0
  119. package/templates/default/.agents/skills/sail-templates/SKILL.md +142 -0
  120. package/templates/default/.agents/skills/sail-templates/catalog.mjs +200 -0
  121. package/templates/default/.agents/skills/sail-templates/deployed.json +133 -0
  122. package/templates/default/.agents/skills/sail-templates/oracles/IOracle.sol +13 -0
  123. package/templates/default/.agents/skills/sail-templates/oracles/UniV3TwapOracle.sol +114 -0
  124. package/templates/default/.agents/skills/sail-templates/oracles/libs/FullMath.sol +62 -0
  125. package/templates/default/.agents/skills/sail-templates/oracles/libs/TickMath.sol +44 -0
  126. package/templates/default/.agents/skills/sail-templates/references/config-schemas.md +139 -0
  127. package/templates/default/.agents/skills/sail-templates/references/reuse-flow.md +139 -0
  128. package/templates/default/.agents/skills/sail-token-resolve/SKILL.md +174 -0
  129. package/templates/default/AGENTS.md +21 -2
  130. package/templates/default/scripts/quote-swap.mjs +211 -0
  131. package/templates/default/scripts/resolve-token.mjs +992 -0
  132. package/templates/default/scripts/shared-template-addr.mjs +110 -0
  133. package/packages/ui/dist/assets/cursor-DXDOwzFz.js +0 -3
  134. package/packages/ui/dist/assets/index-BGmLv6wn.js +0 -1789
  135. package/packages/ui/dist/assets/index-lhNfD4jA.css +0 -1
@@ -15767,13 +15767,13 @@ var init_getCallError = __esm({
15767
15767
 
15768
15768
  // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/utils/promise/withResolvers.js
15769
15769
  function withResolvers() {
15770
- let resolve3 = () => void 0;
15770
+ let resolve4 = () => void 0;
15771
15771
  let reject = () => void 0;
15772
15772
  const promise = new Promise((resolve_, reject_) => {
15773
- resolve3 = resolve_;
15773
+ resolve4 = resolve_;
15774
15774
  reject = reject_;
15775
15775
  });
15776
- return { promise, resolve: resolve3, reject };
15776
+ return { promise, resolve: resolve4, reject };
15777
15777
  }
15778
15778
  var init_withResolvers = __esm({
15779
15779
  "../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/utils/promise/withResolvers.js"() {
@@ -15792,8 +15792,8 @@ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait2 = 0, sort
15792
15792
  if (sort && Array.isArray(data))
15793
15793
  data.sort(sort);
15794
15794
  for (let i = 0; i < scheduler.length; i++) {
15795
- const { resolve: resolve3 } = scheduler[i];
15796
- resolve3?.([data[i], data]);
15795
+ const { resolve: resolve4 } = scheduler[i];
15796
+ resolve4?.([data[i], data]);
15797
15797
  }
15798
15798
  }).catch((err) => {
15799
15799
  for (let i = 0; i < scheduler.length; i++) {
@@ -15809,16 +15809,16 @@ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait2 = 0, sort
15809
15809
  return {
15810
15810
  flush,
15811
15811
  async schedule(args) {
15812
- const { promise, resolve: resolve3, reject } = withResolvers();
15812
+ const { promise, resolve: resolve4, reject } = withResolvers();
15813
15813
  const split2 = shouldSplitBatch?.([...getBatchedArgs(), args]);
15814
15814
  if (split2)
15815
15815
  exec();
15816
15816
  const hasActiveScheduler = getScheduler().length > 0;
15817
15817
  if (hasActiveScheduler) {
15818
- setScheduler({ args, resolve: resolve3, reject });
15818
+ setScheduler({ args, resolve: resolve4, reject });
15819
15819
  return promise;
15820
15820
  }
15821
- setScheduler({ args, resolve: resolve3, reject });
15821
+ setScheduler({ args, resolve: resolve4, reject });
15822
15822
  setTimeout(exec, wait2);
15823
15823
  return promise;
15824
15824
  }
@@ -16595,7 +16595,7 @@ var init_observe = __esm({
16595
16595
 
16596
16596
  // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/utils/wait.js
16597
16597
  async function wait(time, { signal } = {}) {
16598
- return new Promise((resolve3, reject) => {
16598
+ return new Promise((resolve4, reject) => {
16599
16599
  if (signal?.aborted) {
16600
16600
  reject(getAbortError(signal));
16601
16601
  return;
@@ -16603,7 +16603,7 @@ async function wait(time, { signal } = {}) {
16603
16603
  const cleanup = () => signal?.removeEventListener("abort", onAbort);
16604
16604
  const timeout = setTimeout(() => {
16605
16605
  cleanup();
16606
- resolve3();
16606
+ resolve4();
16607
16607
  }, time);
16608
16608
  const onAbort = () => {
16609
16609
  clearTimeout(timeout);
@@ -17419,7 +17419,7 @@ var init_calls = __esm({
17419
17419
 
17420
17420
  // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/utils/promise/withRetry.js
17421
17421
  function withRetry(fn, { delay: delay_ = 100, retryCount = 2, shouldRetry: shouldRetry2 = () => true, signal } = {}) {
17422
- return new Promise((resolve3, reject) => {
17422
+ return new Promise((resolve4, reject) => {
17423
17423
  const attemptRetry = async ({ count = 0 } = {}) => {
17424
17424
  if (signal?.aborted) {
17425
17425
  reject(getAbortError(signal));
@@ -17439,7 +17439,7 @@ function withRetry(fn, { delay: delay_ = 100, retryCount = 2, shouldRetry: shoul
17439
17439
  };
17440
17440
  try {
17441
17441
  const data = await fn();
17442
- resolve3(data);
17442
+ resolve4(data);
17443
17443
  } catch (err) {
17444
17444
  if (signal?.aborted) {
17445
17445
  reject(getAbortError(signal));
@@ -17586,7 +17586,7 @@ async function sendCalls(client, parameters) {
17586
17586
  results.push({ reason, status: "rejected" });
17587
17587
  }
17588
17588
  if (experimental_fallbackDelay > 0)
17589
- await new Promise((resolve3) => setTimeout(resolve3, experimental_fallbackDelay));
17589
+ await new Promise((resolve4) => setTimeout(resolve4, experimental_fallbackDelay));
17590
17590
  }
17591
17591
  if (results.every((r) => r.status === "rejected"))
17592
17592
  throw results[0].reason;
@@ -17716,9 +17716,9 @@ async function waitForCallsStatus(client, parameters) {
17716
17716
  throwOnFailure = false
17717
17717
  } = parameters;
17718
17718
  const observerId = stringify(["waitForCallsStatus", client.uid, id]);
17719
- const { promise, resolve: resolve3, reject } = withResolvers();
17719
+ const { promise, resolve: resolve4, reject } = withResolvers();
17720
17720
  let timer;
17721
- const unobserve = observe(observerId, { resolve: resolve3, reject }, (emit2) => {
17721
+ const unobserve = observe(observerId, { resolve: resolve4, reject }, (emit2) => {
17722
17722
  const unpoll = poll(async () => {
17723
17723
  const done = (fn) => {
17724
17724
  clearTimeout(timer);
@@ -18123,13 +18123,13 @@ async function isImageUri(uri) {
18123
18123
  }
18124
18124
  if (!Object.hasOwn(globalThis, "Image"))
18125
18125
  return false;
18126
- return new Promise((resolve3) => {
18126
+ return new Promise((resolve4) => {
18127
18127
  const img = new Image();
18128
18128
  img.onload = () => {
18129
- resolve3(true);
18129
+ resolve4(true);
18130
18130
  };
18131
18131
  img.onerror = () => {
18132
- resolve3(false);
18132
+ resolve4(false);
18133
18133
  };
18134
18134
  img.src = uri;
18135
18135
  });
@@ -20077,7 +20077,7 @@ var init_nonceManager = __esm({
20077
20077
 
20078
20078
  // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/utils/promise/withTimeout.js
20079
20079
  function withTimeout(fn, { errorInstance = new Error("timed out"), timeout, signal }) {
20080
- return new Promise((resolve3, reject) => {
20080
+ return new Promise((resolve4, reject) => {
20081
20081
  ;
20082
20082
  (async () => {
20083
20083
  let timeoutId;
@@ -20092,7 +20092,7 @@ function withTimeout(fn, { errorInstance = new Error("timed out"), timeout, sign
20092
20092
  }
20093
20093
  }, timeout);
20094
20094
  }
20095
- resolve3(await fn({ signal: controller?.signal || null }));
20095
+ resolve4(await fn({ signal: controller?.signal || null }));
20096
20096
  } catch (err) {
20097
20097
  if (controller?.signal.aborted && isAbortError(err)) {
20098
20098
  reject(errorInstance);
@@ -24137,10 +24137,10 @@ async function getWebSocketRpcClient(url, options = {}) {
24137
24137
  socket.addEventListener("error", onError);
24138
24138
  socket.addEventListener("open", onOpen);
24139
24139
  if (socket.readyState === WebSocket5.CONNECTING) {
24140
- await new Promise((resolve3, reject) => {
24140
+ await new Promise((resolve4, reject) => {
24141
24141
  if (!socket)
24142
24142
  return;
24143
- socket.onopen = resolve3;
24143
+ socket.onopen = resolve4;
24144
24144
  socket.onerror = reject;
24145
24145
  });
24146
24146
  }
@@ -28321,13 +28321,13 @@ async function waitForTransactionReceipt(client, parameters) {
28321
28321
  let retrying = false;
28322
28322
  let _unobserve;
28323
28323
  let _unwatch;
28324
- const { promise, resolve: resolve3, reject } = withResolvers();
28324
+ const { promise, resolve: resolve4, reject } = withResolvers();
28325
28325
  const timer = timeout ? setTimeout(() => {
28326
28326
  _unwatch?.();
28327
28327
  _unobserve?.();
28328
28328
  reject(new WaitForTransactionReceiptTimeoutError({ hash: hash3 }));
28329
28329
  }, timeout) : void 0;
28330
- _unobserve = observe(observerId, { onReplaced, resolve: resolve3, reject }, async (emit2) => {
28330
+ _unobserve = observe(observerId, { onReplaced, resolve: resolve4, reject }, async (emit2) => {
28331
28331
  receipt = await getAction(client, getTransactionReceipt, "getTransactionReceipt")({ hash: hash3 }).catch(() => void 0);
28332
28332
  if (receipt && confirmations <= 1) {
28333
28333
  clearTimeout(timer);
@@ -30586,7 +30586,7 @@ function webSocket(url, config = {}) {
30586
30586
  },
30587
30587
  async subscribe({ params, onData, onError }) {
30588
30588
  const rpcClient = await getWebSocketRpcClient(url_, wsRpcClientOpts);
30589
- const { result: subscriptionId } = await new Promise((resolve3, reject) => rpcClient.request({
30589
+ const { result: subscriptionId } = await new Promise((resolve4, reject) => rpcClient.request({
30590
30590
  body: {
30591
30591
  method: "eth_subscribe",
30592
30592
  params
@@ -30603,7 +30603,7 @@ function webSocket(url, config = {}) {
30603
30603
  return;
30604
30604
  }
30605
30605
  if (typeof response.id === "number") {
30606
- resolve3(response);
30606
+ resolve4(response);
30607
30607
  return;
30608
30608
  }
30609
30609
  if (response.method !== "eth_subscription")
@@ -30614,12 +30614,12 @@ function webSocket(url, config = {}) {
30614
30614
  return {
30615
30615
  subscriptionId,
30616
30616
  async unsubscribe() {
30617
- return new Promise((resolve3) => rpcClient.request({
30617
+ return new Promise((resolve4) => rpcClient.request({
30618
30618
  body: {
30619
30619
  method: "eth_unsubscribe",
30620
30620
  params: [subscriptionId]
30621
30621
  },
30622
- onResponse: resolve3
30622
+ onResponse: resolve4
30623
30623
  }));
30624
30624
  }
30625
30625
  };
@@ -35190,8 +35190,8 @@ var require_websocket_server2 = __commonJS({
35190
35190
  });
35191
35191
 
35192
35192
  // src/index.ts
35193
- var import_node_fs21 = require("node:fs");
35194
- var import_node_path17 = require("node:path");
35193
+ var import_node_fs22 = require("node:fs");
35194
+ var import_node_path18 = require("node:path");
35195
35195
 
35196
35196
  // ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
35197
35197
  var import_index = __toESM(require_commander(), 1);
@@ -36355,7 +36355,7 @@ var CONFIGURABLE_PERMISSION_ABI = [
36355
36355
  ];
36356
36356
  var DEFAULT_DISPATCH_GAS = 2000000n;
36357
36357
  function delay(ms) {
36358
- return new Promise((resolve3) => setTimeout(resolve3, ms));
36358
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
36359
36359
  }
36360
36360
  var CALL_COMPONENTS = [
36361
36361
  { name: "target", type: "address" },
@@ -37877,6 +37877,60 @@ var SailGovernanceAbi = [
37877
37877
  }
37878
37878
  ];
37879
37879
 
37880
+ // ../sdk/dist/abis/ConfigurablePermission.js
37881
+ var ConfigurablePermissionAbi = [
37882
+ {
37883
+ type: "function",
37884
+ name: "configure",
37885
+ stateMutability: "nonpayable",
37886
+ inputs: [
37887
+ { name: "account", type: "address" },
37888
+ { name: "params", type: "bytes" },
37889
+ { name: "deadline", type: "uint256" },
37890
+ { name: "sig", type: "bytes" }
37891
+ ],
37892
+ outputs: []
37893
+ },
37894
+ {
37895
+ type: "function",
37896
+ name: "configureDirect",
37897
+ stateMutability: "nonpayable",
37898
+ inputs: [
37899
+ { name: "account", type: "address" },
37900
+ { name: "params", type: "bytes" }
37901
+ ],
37902
+ outputs: []
37903
+ },
37904
+ {
37905
+ type: "function",
37906
+ name: "isConfigured",
37907
+ stateMutability: "view",
37908
+ inputs: [{ name: "account", type: "address" }],
37909
+ outputs: [{ name: "", type: "bool" }]
37910
+ },
37911
+ {
37912
+ type: "function",
37913
+ name: "configNonces",
37914
+ stateMutability: "view",
37915
+ inputs: [{ name: "account", type: "address" }],
37916
+ outputs: [{ name: "", type: "uint256" }]
37917
+ },
37918
+ {
37919
+ type: "function",
37920
+ name: "configuredEpoch",
37921
+ stateMutability: "view",
37922
+ inputs: [{ name: "account", type: "address" }],
37923
+ outputs: [{ name: "", type: "uint256" }]
37924
+ },
37925
+ {
37926
+ type: "function",
37927
+ name: "hashTypedDataV4",
37928
+ stateMutability: "view",
37929
+ inputs: [{ name: "structHash", type: "bytes32" }],
37930
+ outputs: [{ name: "", type: "bytes32" }]
37931
+ }
37932
+ ];
37933
+
37880
37934
  // ../sdk/dist/safe.js
37881
37935
  init_esm2();
37882
37936
  var setManagerAbi = [
@@ -38759,14 +38813,14 @@ function getRl() {
38759
38813
  function ask(query) {
38760
38814
  getRl();
38761
38815
  process.stdout.write(query);
38762
- return new Promise((resolve3) => {
38816
+ return new Promise((resolve4) => {
38763
38817
  const buffered = lineQueue.shift();
38764
38818
  if (buffered !== void 0) {
38765
- resolve3(buffered);
38819
+ resolve4(buffered);
38766
38820
  } else if (inputClosed) {
38767
- resolve3("");
38821
+ resolve4("");
38768
38822
  } else {
38769
- waiters.push(resolve3);
38823
+ waiters.push(resolve4);
38770
38824
  }
38771
38825
  });
38772
38826
  }
@@ -38957,11 +39011,11 @@ function isProcessAlive(pid) {
38957
39011
  }
38958
39012
  }
38959
39013
  function findFreePort(from14) {
38960
- return new Promise((resolve3, reject) => {
39014
+ return new Promise((resolve4, reject) => {
38961
39015
  const server = import_node_net.default.createServer();
38962
39016
  server.unref();
38963
- server.on("error", () => findFreePort(from14 + 1).then(resolve3, reject));
38964
- server.listen(from14, "127.0.0.1", () => server.close(() => resolve3(from14)));
39017
+ server.on("error", () => findFreePort(from14 + 1).then(resolve4, reject));
39018
+ server.listen(from14, "127.0.0.1", () => server.close(() => resolve4(from14)));
38965
39019
  });
38966
39020
  }
38967
39021
 
@@ -41514,13 +41568,13 @@ Added (new in template):`);
41514
41568
  var import_node_fs12 = __toESM(require("node:fs"), 1);
41515
41569
  var import_node_path10 = __toESM(require("node:path"), 1);
41516
41570
  function readStdin() {
41517
- return new Promise((resolve3, reject) => {
41571
+ return new Promise((resolve4, reject) => {
41518
41572
  let data = "";
41519
41573
  process.stdin.setEncoding("utf8");
41520
41574
  process.stdin.on("data", (c) => {
41521
41575
  data += c;
41522
41576
  });
41523
- process.stdin.on("end", () => resolve3(data));
41577
+ process.stdin.on("end", () => resolve4(data));
41524
41578
  process.stdin.on("error", reject);
41525
41579
  });
41526
41580
  }
@@ -41828,6 +41882,22 @@ var MandateStore = class {
41828
41882
  this.write(data);
41829
41883
  return mandate2;
41830
41884
  }
41885
+ /**
41886
+ * Track a permission only if no record at its address exists yet, returning the
41887
+ * record either way. Used by `mandate attach` for addresses that were never
41888
+ * deployed from this project — e.g. a shared permission singleton the operator
41889
+ * attaches by address. Without a store record, `recordAttachment` is a no-op and
41890
+ * the permission stays invisible to `mandate prepare`/`sign`. Unlike `add`, this
41891
+ * never overwrites an existing record (so richer deploy-time metadata such as
41892
+ * `sourcePath` is preserved). For attach-only records, `txHash`/`deployedAt`
41893
+ * reflect the registration/first-tracked moment, not an on-chain deployment.
41894
+ */
41895
+ ensureTracked(mandate2) {
41896
+ const existing = this.read().mandates.find(
41897
+ (m) => m.address.toLowerCase() === mandate2.address.toLowerCase()
41898
+ );
41899
+ return existing ?? this.add(mandate2);
41900
+ }
41831
41901
  /** Update mutable metadata fields on a tracked mandate (name, sourcePath, artifactPath). */
41832
41902
  update(addressOrName, patch) {
41833
41903
  const data = this.read();
@@ -42474,6 +42544,14 @@ function publicClientFor(project) {
42474
42544
  transport: http(getRpcUrl(project.chainId))
42475
42545
  }).extend(publicActions);
42476
42546
  }
42547
+ function knownTemplateLabel(chainId, address) {
42548
+ try {
42549
+ const known = getSailDeployment(chainId).knownTemplates ?? [];
42550
+ return known.find((t) => t.address.toLowerCase() === address.toLowerCase())?.label;
42551
+ } catch {
42552
+ return void 0;
42553
+ }
42554
+ }
42477
42555
  async function mandateDeploy(options) {
42478
42556
  const project = requireProject();
42479
42557
  const channel = await createSigningChannel(process.cwd());
@@ -42943,7 +43021,14 @@ async function runAttach(project, channel, options) {
42943
43021
  label,
42944
43022
  json
42945
43023
  );
42946
- if (tracked) store.recordAttachment(mandateAddress, { sma, txHash });
43024
+ store.ensureTracked({
43025
+ name: label,
43026
+ address: mandateAddress,
43027
+ txHash,
43028
+ chainId: project.chainId,
43029
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
43030
+ });
43031
+ store.recordAttachment(mandateAddress, { sma, txHash });
42947
43032
  emit(json, () => {
42948
43033
  }, {
42949
43034
  status: "ok",
@@ -42957,7 +43042,14 @@ async function runAttachBatch(project, channel, options, sma, store) {
42957
43042
  announceSigningUrl(json);
42958
43043
  const txHash = await attachBatchToSma(project, channel, publicClient, sma, permissions, json);
42959
43044
  for (const permission of permissions) {
42960
- if (store.find(permission)) store.recordAttachment(permission, { sma, txHash });
43045
+ store.ensureTracked({
43046
+ name: knownTemplateLabel(project.chainId, permission) ?? permission,
43047
+ address: permission,
43048
+ txHash,
43049
+ chainId: project.chainId,
43050
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
43051
+ });
43052
+ store.recordAttachment(permission, { sma, txHash });
42961
43053
  }
42962
43054
  emit(json, () => {
42963
43055
  }, {
@@ -43553,14 +43645,37 @@ async function trackedPermissionsFor(account2, chainId, kernel) {
43553
43645
  };
43554
43646
  });
43555
43647
  const onChain = kernel ? await fetchOnChainPermissions(account2.safe, chainId, kernel) : null;
43556
- if (onChain !== null) {
43557
- for (const p of local) {
43558
- if (p.registeredOnSma && !onChain.has(p.address.toLowerCase())) {
43559
- p.revokedOnChain = true;
43560
- }
43648
+ if (onChain === null) return local;
43649
+ return mergeOnChainPermissions(local, onChain, knownTemplateLabeler(chainId));
43650
+ }
43651
+ function mergeOnChainPermissions(local, onChain, labelFor = () => void 0) {
43652
+ for (const p of local) {
43653
+ if (p.registeredOnSma && !onChain.has(p.address.toLowerCase())) {
43654
+ p.revokedOnChain = true;
43655
+ }
43656
+ }
43657
+ const localAddrs = new Set(local.map((p) => p.address.toLowerCase()));
43658
+ const extra = [];
43659
+ for (const addr of onChain) {
43660
+ if (!localAddrs.has(addr)) {
43661
+ extra.push({
43662
+ address: addr,
43663
+ label: labelFor(addr) ?? `permission ${addr.slice(0, 10)}\u2026`,
43664
+ registeredOnSma: true
43665
+ });
43561
43666
  }
43562
43667
  }
43563
- return local;
43668
+ return [...local, ...extra];
43669
+ }
43670
+ function knownTemplateLabeler(chainId) {
43671
+ const map = /* @__PURE__ */ new Map();
43672
+ try {
43673
+ for (const t of getSailDeployment(chainId).knownTemplates ?? []) {
43674
+ map.set(t.address.toLowerCase(), t.label);
43675
+ }
43676
+ } catch {
43677
+ }
43678
+ return (addr) => map.get(addr.toLowerCase());
43564
43679
  }
43565
43680
  function printNoPermissionsGuidance() {
43566
43681
  console.log(
@@ -43682,8 +43797,451 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
43682
43797
  \u2713 Saved to .sail/mandate.json \u2014 agent is ready to run.`);
43683
43798
  }
43684
43799
 
43685
- // src/commands/mandate-simulate.ts
43800
+ // src/commands/mandate-configure.ts
43686
43801
  var import_node_fs15 = require("node:fs");
43802
+ var import_node_path13 = require("node:path");
43803
+
43804
+ // ../sdk/dist/templates/boundedSwap.js
43805
+ init_esm2();
43806
+ var ABI = [
43807
+ { name: "routers", type: "address[]" },
43808
+ { name: "tokensIn", type: "address[]" },
43809
+ { name: "tokensOut", type: "address[]" },
43810
+ { name: "maxAmountPerTx", type: "uint256" },
43811
+ { name: "maxSlippageBps", type: "uint256" },
43812
+ { name: "priceOracle", type: "address" },
43813
+ { name: "maxPriceAgeSec", type: "uint256" }
43814
+ ];
43815
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
43816
+ var boundedSwapTemplate = {
43817
+ name: "SharedBoundedSwapPermission",
43818
+ address: "0x0000000000000000000000000000000000000000",
43819
+ encoder: {
43820
+ encode(params) {
43821
+ return encodeAbiParameters(ABI, [
43822
+ params.routers,
43823
+ params.tokensIn,
43824
+ params.tokensOut,
43825
+ params.maxAmountPerTx,
43826
+ BigInt(params.maxSlippageBps),
43827
+ params.priceOracle,
43828
+ BigInt(params.maxPriceAgeSec)
43829
+ ]);
43830
+ },
43831
+ decode(data) {
43832
+ const decoded = decodeAbiParameters(ABI, data);
43833
+ return {
43834
+ routers: [...decoded[0]],
43835
+ tokensIn: [...decoded[1]],
43836
+ tokensOut: [...decoded[2]],
43837
+ maxAmountPerTx: decoded[3],
43838
+ maxSlippageBps: Number(decoded[4]),
43839
+ priceOracle: decoded[5],
43840
+ maxPriceAgeSec: Number(decoded[6])
43841
+ };
43842
+ }
43843
+ },
43844
+ explainer: {
43845
+ explain(params) {
43846
+ const warnings = [];
43847
+ if (params.maxSlippageBps > 100) {
43848
+ warnings.push(`High slippage tolerance: ${params.maxSlippageBps / 100}%`);
43849
+ }
43850
+ if (params.priceOracle === ZERO_ADDRESS) {
43851
+ warnings.push("No price oracle configured \u2014 swaps are not checked against a reference price");
43852
+ }
43853
+ return {
43854
+ templateName: "SharedBoundedSwapPermission",
43855
+ humanReadable: [
43856
+ `Maximum input per swap: ${params.maxAmountPerTx.toString()} (input-token base units)`,
43857
+ `Maximum slippage: ${params.maxSlippageBps / 100}%`,
43858
+ `Allowed routers: ${params.routers.join(", ")}`,
43859
+ `Allowed input tokens: ${params.tokensIn.join(", ")}`,
43860
+ `Allowed output tokens: ${params.tokensOut.join(", ")}`,
43861
+ params.priceOracle === ZERO_ADDRESS ? "Price oracle: none" : `Price oracle: ${params.priceOracle} (max age ${params.maxPriceAgeSec}s)`
43862
+ ],
43863
+ warnings
43864
+ };
43865
+ }
43866
+ }
43867
+ };
43868
+
43869
+ // src/commands/mandate-configure.ts
43870
+ init_esm2();
43871
+ var TEMPLATE_REGISTRY = {
43872
+ SwapPermission: {
43873
+ label: "Bounded Swap (Uniswap V3)",
43874
+ bigintFields: ["maxAmountPerTx"],
43875
+ encode: (raw) => boundedSwapTemplate.encoder.encode(coerceSwapParams(raw)),
43876
+ decode: (blob) => {
43877
+ const p = boundedSwapTemplate.encoder.decode(blob);
43878
+ return {
43879
+ routers: p.routers,
43880
+ tokensIn: p.tokensIn,
43881
+ tokensOut: p.tokensOut,
43882
+ maxAmountPerTx: p.maxAmountPerTx.toString(),
43883
+ maxSlippageBps: p.maxSlippageBps,
43884
+ priceOracle: p.priceOracle,
43885
+ maxPriceAgeSec: p.maxPriceAgeSec
43886
+ };
43887
+ }
43888
+ }
43889
+ };
43890
+ function asAddressArray(v, field) {
43891
+ if (!Array.isArray(v)) throw new Error(`${field}: expected an array of addresses`);
43892
+ return v.map((x, i) => {
43893
+ if (typeof x !== "string" || !isAddress(x, { strict: false })) {
43894
+ throw new Error(`${field}[${i}]: invalid address "${String(x)}"`);
43895
+ }
43896
+ return getAddress(x);
43897
+ });
43898
+ }
43899
+ function asAddress(v, field) {
43900
+ if (typeof v !== "string" || !isAddress(v, { strict: false })) {
43901
+ throw new Error(`${field}: invalid address "${String(v)}"`);
43902
+ }
43903
+ return getAddress(v);
43904
+ }
43905
+ function asUint(v, field) {
43906
+ if (v === void 0 || v === null) throw new Error(`${field}: missing`);
43907
+ try {
43908
+ return BigInt(typeof v === "string" || typeof v === "number" ? v : String(v));
43909
+ } catch {
43910
+ throw new Error(`${field}: expected an integer, got "${String(v)}"`);
43911
+ }
43912
+ }
43913
+ function asInt(v, field) {
43914
+ const n = asUint(v, field);
43915
+ return Number(n);
43916
+ }
43917
+ function coerceSwapParams(raw) {
43918
+ return {
43919
+ routers: asAddressArray(raw.routers, "routers"),
43920
+ tokensIn: asAddressArray(raw.tokensIn, "tokensIn"),
43921
+ tokensOut: asAddressArray(raw.tokensOut, "tokensOut"),
43922
+ maxAmountPerTx: asUint(raw.maxAmountPerTx, "maxAmountPerTx"),
43923
+ maxSlippageBps: asInt(raw.maxSlippageBps, "maxSlippageBps"),
43924
+ priceOracle: asAddress(raw.priceOracle, "priceOracle"),
43925
+ maxPriceAgeSec: asInt(raw.maxPriceAgeSec, "maxPriceAgeSec")
43926
+ };
43927
+ }
43928
+ function requireProject2() {
43929
+ if (!ProjectContext.exists()) {
43930
+ console.log('No Sailor project found. Run "sailor init" first.');
43931
+ process.exit(1);
43932
+ }
43933
+ return new ProjectContext();
43934
+ }
43935
+ function fail2(err, json = false) {
43936
+ const msg = err instanceof Error ? err.message : String(err);
43937
+ emit(json, () => console.error(`
43938
+ Mandate configure failed: ${msg}`), {
43939
+ status: "error",
43940
+ error: msg
43941
+ });
43942
+ process.exit(1);
43943
+ }
43944
+ function publicClientFor2(project) {
43945
+ return createPublicClient({
43946
+ chain: getChainById(project.chainId),
43947
+ transport: http(getRpcUrl(project.chainId))
43948
+ }).extend(publicActions);
43949
+ }
43950
+ function announceSigningUrl2(json) {
43951
+ const url = signingPageUrl(projectPort(process.cwd()));
43952
+ if (json) {
43953
+ process.stdout.write(`${JSON.stringify({ status: "waiting_for_signature", url })}
43954
+ `);
43955
+ } else {
43956
+ console.log(`
43957
+ \u2192 Open the Sailor dashboard to approve signing requests:
43958
+ ${url}
43959
+ `);
43960
+ }
43961
+ }
43962
+ function resolveBlob(options) {
43963
+ const hasParams = options.params !== void 0;
43964
+ const hasArgsFile = options.argsFile !== void 0;
43965
+ if (hasParams && hasArgsFile) {
43966
+ throw new Error("Provide EITHER --params <hex> OR --args-file <path>, not both.");
43967
+ }
43968
+ if (!hasParams && !hasArgsFile) {
43969
+ throw new Error(
43970
+ "Provide the config blob as --params <0x-hex>, or as --args-file <path> paired with --template <Name>."
43971
+ );
43972
+ }
43973
+ if (hasParams) {
43974
+ const raw2 = options.params.trim();
43975
+ if (!isHex(raw2) || raw2.length <= 2) {
43976
+ throw new Error(`--params must be 0x-prefixed hex; got "${options.params}".`);
43977
+ }
43978
+ return { blob: raw2, via: "params" };
43979
+ }
43980
+ const templateName = options.template;
43981
+ if (!templateName) {
43982
+ throw new Error("--args-file requires --template <Name> (e.g. SwapPermission).");
43983
+ }
43984
+ const entry = TEMPLATE_REGISTRY[templateName];
43985
+ if (!entry) {
43986
+ throw new Error(
43987
+ `Unknown --template "${templateName}". Known: ${Object.keys(TEMPLATE_REGISTRY).join(", ")}. Pass a pre-encoded blob with --params for any other template.`
43988
+ );
43989
+ }
43990
+ const argsFilePath = (0, import_node_path13.resolve)(options.argsFile);
43991
+ let raw;
43992
+ try {
43993
+ raw = (0, import_node_fs15.readFileSync)(argsFilePath, "utf8").trim();
43994
+ } catch {
43995
+ throw new Error(`Cannot read --args-file: ${argsFilePath}`);
43996
+ }
43997
+ let parsed;
43998
+ try {
43999
+ parsed = JSON.parse(raw);
44000
+ } catch (err) {
44001
+ throw new Error(`--args-file is not valid JSON: ${err.message}`);
44002
+ }
44003
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
44004
+ throw new Error("--args-file must contain a JSON object of template params.");
44005
+ }
44006
+ const blob = entry.encode(parsed);
44007
+ return { blob, via: `template:${templateName}` };
44008
+ }
44009
+ async function mandateConfigure(options) {
44010
+ const project = requireProject2();
44011
+ const channel = await createSigningChannel(process.cwd());
44012
+ try {
44013
+ await channel.start();
44014
+ await runConfigure(project, channel, options);
44015
+ } catch (err) {
44016
+ fail2(err, options.json);
44017
+ } finally {
44018
+ channel.stop();
44019
+ }
44020
+ }
44021
+ async function runConfigure(project, channel, options) {
44022
+ const json = !!options.json;
44023
+ const say = (fn) => {
44024
+ if (!json) fn();
44025
+ };
44026
+ if (!isAddress(options.sma, { strict: false })) {
44027
+ throw new Error(`Invalid --sma address: ${options.sma}`);
44028
+ }
44029
+ if (!isAddress(options.address, { strict: false })) {
44030
+ throw new Error(
44031
+ `--address must be a deployed shared-template singleton address: ${options.address}`
44032
+ );
44033
+ }
44034
+ const sma = getAddress(options.sma);
44035
+ const singleton = getAddress(options.address);
44036
+ const { blob, via } = resolveBlob(options);
44037
+ const publicClient = publicClientFor2(project);
44038
+ const registered = await publicClient.readContract({
44039
+ address: project.contracts.kernel,
44040
+ abi: SailKernelAbi,
44041
+ functionName: "registered",
44042
+ args: [sma]
44043
+ });
44044
+ if (!registered) {
44045
+ throw new Error(`SMA ${sma} is not registered with SailKernel; cannot configure a permission for it.`);
44046
+ }
44047
+ const kernelConfig = await publicClient.readContract({
44048
+ address: project.contracts.kernel,
44049
+ abi: SailKernelAbi,
44050
+ functionName: "configs",
44051
+ args: [sma]
44052
+ });
44053
+ const permissionSigner = kernelConfig[0];
44054
+ if (!permissionSigner || permissionSigner === getAddress("0x0000000000000000000000000000000000000000")) {
44055
+ throw new Error(`SMA ${sma} has no on-chain permissionSigner; cannot configure.`);
44056
+ }
44057
+ const alreadyConfigured = await publicClient.readContract({
44058
+ address: singleton,
44059
+ abi: ConfigurablePermissionAbi,
44060
+ functionName: "isConfigured",
44061
+ args: [sma]
44062
+ });
44063
+ if (alreadyConfigured && !options.force) {
44064
+ say(
44065
+ () => console.log(
44066
+ `
44067
+ \u2713 ${singleton} is already configured for ${sma} (isConfigured == true).
44068
+ Re-run with --force to overwrite the current bounds.`
44069
+ )
44070
+ );
44071
+ emit(
44072
+ json,
44073
+ () => {
44074
+ },
44075
+ { status: "ok", alreadyConfigured: true, singleton, sma, chainId: project.chainId }
44076
+ );
44077
+ return;
44078
+ }
44079
+ const nonce = await publicClient.readContract({
44080
+ address: singleton,
44081
+ abi: ConfigurablePermissionAbi,
44082
+ functionName: "configNonces",
44083
+ args: [sma]
44084
+ });
44085
+ say(() => {
44086
+ console.log(`
44087
+ configure ${singleton}`);
44088
+ console.log(` SMA: ${sma}`);
44089
+ console.log(` permissionSigner: ${permissionSigner} (must sign in the browser \u2014 owner tx)`);
44090
+ console.log(` current nonce: ${nonce}`);
44091
+ console.log(` already set: ${alreadyConfigured ? "yes (--force will overwrite)" : "no"}`);
44092
+ console.log(` config blob: ${blob} (via ${via}, ${Math.floor((blob.length - 2) / 2)} bytes)`);
44093
+ });
44094
+ const configureDirectData = encodeFunctionData({
44095
+ abi: ConfigurablePermissionAbi,
44096
+ functionName: "configureDirect",
44097
+ args: [sma, blob]
44098
+ });
44099
+ say(() => console.log(`
44100
+ Pre-flight (eth_call of configureDirect, no gas)\u2026`));
44101
+ try {
44102
+ await publicClient.simulateContract({
44103
+ address: singleton,
44104
+ abi: ConfigurablePermissionAbi,
44105
+ functionName: "configureDirect",
44106
+ args: [sma, blob],
44107
+ account: permissionSigner
44108
+ });
44109
+ } catch (err) {
44110
+ const ce = getContractError(err, {
44111
+ abi: ConfigurablePermissionAbi,
44112
+ address: singleton,
44113
+ args: [sma, blob],
44114
+ functionName: "configureDirect"
44115
+ });
44116
+ const reason = ce?.message ?? err?.message ?? String(err);
44117
+ throw new Error(
44118
+ `Pre-flight reverted \u2014 the config blob is invalid for ${singleton} on ${sma}.
44119
+ Revert: ${reason}
44120
+ This is the encoding gotcha (flat params vs wrapped struct) or an internal bound check. Re-encode per the template's _applyConfig tuple and retry.`
44121
+ );
44122
+ }
44123
+ say(() => console.log("\u2713 pre-flight passed (configureDirect would succeed)."));
44124
+ let decoded;
44125
+ if (options.template && TEMPLATE_REGISTRY[options.template]) {
44126
+ try {
44127
+ decoded = TEMPLATE_REGISTRY[options.template].decode(blob);
44128
+ say(() => {
44129
+ console.log("\nDecoded bounds (what the singleton will store):");
44130
+ for (const [k, v] of Object.entries(decoded)) {
44131
+ const display = Array.isArray(v) ? v.join(", ") : String(v);
44132
+ console.log(` ${k}: ${display}`);
44133
+ }
44134
+ });
44135
+ } catch {
44136
+ }
44137
+ }
44138
+ if (options.simulateOnly) {
44139
+ emit(
44140
+ json,
44141
+ () => {
44142
+ console.log("\n--simulate-only: stopping after pre-flight (no signing, no gas).");
44143
+ },
44144
+ {
44145
+ status: "ok",
44146
+ simulateOnly: true,
44147
+ singleton,
44148
+ sma,
44149
+ permissionSigner,
44150
+ nonce: nonce.toString(),
44151
+ alreadyConfigured,
44152
+ blob,
44153
+ blobBytes: Math.floor((blob.length - 2) / 2),
44154
+ decoded,
44155
+ chainId: project.chainId
44156
+ }
44157
+ );
44158
+ return;
44159
+ }
44160
+ const label = options.label ?? (options.template ?? "shared-template");
44161
+ announceSigningUrl2(json);
44162
+ say(
44163
+ () => console.log(
44164
+ `Pushing configure request \u2014 the mandate signer (${permissionSigner}) must approve in the browser\u2026`
44165
+ )
44166
+ );
44167
+ const response = await channel.requestSignature({
44168
+ type: "transaction",
44169
+ kind: "arbitrary-tx",
44170
+ title: `Configure "${label}"`,
44171
+ description: `Write per-account bounds on the shared permission ${singleton}.
44172
+ You (the mandate signer) send the transaction \u2014 configureDirect requires msg.sender to be the permissionSigner.`,
44173
+ chainId: project.chainId,
44174
+ to: singleton,
44175
+ data: configureDirectData,
44176
+ details: [
44177
+ { label: "SMA", value: sma },
44178
+ { label: "Singleton", value: singleton },
44179
+ { label: "Template", value: options.template ?? "(raw blob)" },
44180
+ { label: "Mandate signer", value: permissionSigner },
44181
+ { label: "Config blob (bytes)", value: String(Math.floor((blob.length - 2) / 2)) },
44182
+ ...decoded ? Object.entries(decoded).map(([k, v]) => ({
44183
+ label: k,
44184
+ value: Array.isArray(v) ? v.join(", ") : String(v)
44185
+ })) : []
44186
+ ]
44187
+ });
44188
+ if (response.status === "rejected") {
44189
+ throw new Error(`User rejected configure: ${response.reason ?? "no reason given"}`);
44190
+ }
44191
+ if (response.status !== "signed") {
44192
+ throw new Error(`Expected a signed transaction, got: ${response.status}`);
44193
+ }
44194
+ say(() => console.log("Waiting for confirmation\u2026"));
44195
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: response.txHash });
44196
+ if (receipt.status !== "success") {
44197
+ throw new Error(`configureDirect transaction failed (tx ${response.txHash})`);
44198
+ }
44199
+ const configuredNow = await publicClient.readContract({
44200
+ address: singleton,
44201
+ abi: ConfigurablePermissionAbi,
44202
+ functionName: "isConfigured",
44203
+ args: [sma]
44204
+ });
44205
+ if (!configuredNow) {
44206
+ throw new Error(
44207
+ `Tx ${response.txHash} mined, but isConfigured(${sma}) is still false. Verify on-chain.`
44208
+ );
44209
+ }
44210
+ say(() => console.log("\u2713", `Configured ${singleton} for ${sma} (isConfigured == true).`));
44211
+ appendActivity({
44212
+ ts: nowIso(),
44213
+ actor: "owner",
44214
+ type: "permission_configured",
44215
+ permission: singleton,
44216
+ name: label,
44217
+ sma,
44218
+ txHash: response.txHash,
44219
+ chainId: project.chainId
44220
+ });
44221
+ emit(
44222
+ json,
44223
+ () => {
44224
+ console.log(
44225
+ `
44226
+ Next: prove it accepts/rejects the right calls before dispatching:
44227
+ sailor mandate simulate --address ${singleton} --sma ${sma} --calls ./probe.json`
44228
+ );
44229
+ },
44230
+ {
44231
+ status: "ok",
44232
+ singleton,
44233
+ sma,
44234
+ template: options.template,
44235
+ permissionSigner,
44236
+ txHash: response.txHash,
44237
+ blob,
44238
+ chainId: project.chainId
44239
+ }
44240
+ );
44241
+ }
44242
+
44243
+ // src/commands/mandate-simulate.ts
44244
+ var import_node_fs16 = require("node:fs");
43687
44245
  init_esm2();
43688
44246
  function parseExpect(raw, where) {
43689
44247
  if (raw === void 0) return void 0;
@@ -43699,7 +44257,7 @@ function resolveSampleCalls(options) {
43699
44257
  if (options.calls) {
43700
44258
  let raw;
43701
44259
  try {
43702
- raw = JSON.parse((0, import_node_fs15.readFileSync)(options.calls, "utf8"));
44260
+ raw = JSON.parse((0, import_node_fs16.readFileSync)(options.calls, "utf8"));
43703
44261
  } catch (err) {
43704
44262
  throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
43705
44263
  }
@@ -43915,7 +44473,7 @@ async function mandateSimulate(options) {
43915
44473
  }
43916
44474
 
43917
44475
  // src/commands/rotate-signer.ts
43918
- var import_node_fs16 = require("node:fs");
44476
+ var import_node_fs17 = require("node:fs");
43919
44477
  init_esm2();
43920
44478
  var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
43921
44479
  async function rotateSigner(options) {
@@ -44301,7 +44859,7 @@ ensure the agent that signs dispatches holds this key.`
44301
44859
  const keystore = await keyring.exportKeystore(password);
44302
44860
  writeJsonFile(target, keystore, 384);
44303
44861
  const perManagerPath = managerKeystorePath(keyring.address);
44304
- (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44862
+ (0, import_node_fs17.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44305
44863
  writeJsonFile(perManagerPath, keystore, 384);
44306
44864
  say(
44307
44865
  () => console.log(
@@ -44313,7 +44871,7 @@ ensure the agent that signs dispatches holds this key.`
44313
44871
  function promoteManagerKeystore(newManager, say) {
44314
44872
  const stored = readJsonFile(managerKeystorePath(newManager));
44315
44873
  if (!stored) return;
44316
- (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44874
+ (0, import_node_fs17.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44317
44875
  const activeTarget = keyPath("manager");
44318
44876
  const displaced = readJsonFile(activeTarget);
44319
44877
  if (displaced?.address) {
@@ -44365,7 +44923,7 @@ function writePending(pending) {
44365
44923
  }
44366
44924
  function clearPending() {
44367
44925
  try {
44368
- (0, import_node_fs16.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
44926
+ (0, import_node_fs17.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
44369
44927
  } catch {
44370
44928
  }
44371
44929
  }
@@ -44464,12 +45022,12 @@ function ownerShow(options) {
44464
45022
  }
44465
45023
 
44466
45024
  // src/commands/run.ts
44467
- var import_node_fs17 = __toESM(require("node:fs"), 1);
44468
- var import_node_path13 = __toESM(require("node:path"), 1);
45025
+ var import_node_fs18 = __toESM(require("node:fs"), 1);
45026
+ var import_node_path14 = __toESM(require("node:path"), 1);
44469
45027
  var import_node_url = require("node:url");
44470
45028
  init_esm2();
44471
45029
  var DEFAULT_INTERVAL_SEC = 60;
44472
- var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
45030
+ var sleep2 = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
44473
45031
  var ERC20_READ_ABI = [
44474
45032
  {
44475
45033
  type: "function",
@@ -44499,7 +45057,7 @@ var ERC20_READ_ABI = [
44499
45057
  function loadAgentData(filePath) {
44500
45058
  if (!filePath) return {};
44501
45059
  try {
44502
- const parsed = JSON.parse(import_node_fs17.default.readFileSync(filePath, "utf-8"));
45060
+ const parsed = JSON.parse(import_node_fs18.default.readFileSync(filePath, "utf-8"));
44503
45061
  return parsed && typeof parsed === "object" ? parsed : {};
44504
45062
  } catch {
44505
45063
  return {};
@@ -44508,8 +45066,8 @@ function loadAgentData(filePath) {
44508
45066
  async function loadAgent() {
44509
45067
  const candidates = ["src/agent.ts", "src/agent.js", "dist/agent.js", "dist/src/agent.js"];
44510
45068
  for (const rel of candidates) {
44511
- const abs = import_node_path13.default.join(process.cwd(), rel);
44512
- if (!import_node_fs17.default.existsSync(abs)) continue;
45069
+ const abs = import_node_path14.default.join(process.cwd(), rel);
45070
+ if (!import_node_fs18.default.existsSync(abs)) continue;
44513
45071
  let mod2;
44514
45072
  if (abs.endsWith(".ts")) {
44515
45073
  const { tsImport } = await import("tsx/esm/api");
@@ -44804,7 +45362,7 @@ Configure the chain in the SDK chain registry or set KERNEL_ADDRESS in .sail/.en
44804
45362
  if (isConjunctive && result.txHash) {
44805
45363
  try {
44806
45364
  await publicClient.waitForTransactionReceipt({ hash: result.txHash, timeout: 3e4 });
44807
- await new Promise((resolve3) => setTimeout(resolve3, 500));
45365
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
44808
45366
  } catch {
44809
45367
  }
44810
45368
  }
@@ -44985,9 +45543,9 @@ async function scan(options) {
44985
45543
 
44986
45544
  // src/commands/service.ts
44987
45545
  var import_node_child_process2 = require("node:child_process");
44988
- var import_node_fs18 = __toESM(require("node:fs"), 1);
45546
+ var import_node_fs19 = __toESM(require("node:fs"), 1);
44989
45547
  var import_node_os = __toESM(require("node:os"), 1);
44990
- var import_node_path14 = __toESM(require("node:path"), 1);
45548
+ var import_node_path15 = __toESM(require("node:path"), 1);
44991
45549
  function sanitizeName(raw) {
44992
45550
  const s = raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
44993
45551
  return s || "agent";
@@ -45106,21 +45664,21 @@ function buildWindowsTaskXml(cfg) {
45106
45664
  `;
45107
45665
  }
45108
45666
  function isTccProtected(projectDir, home = import_node_os.default.homedir()) {
45109
- const rel = import_node_path14.default.relative(home, import_node_path14.default.resolve(projectDir));
45110
- if (rel.startsWith("..") || import_node_path14.default.isAbsolute(rel)) return false;
45111
- const top = rel.split(import_node_path14.default.sep)[0];
45667
+ const rel = import_node_path15.default.relative(home, import_node_path15.default.resolve(projectDir));
45668
+ if (rel.startsWith("..") || import_node_path15.default.isAbsolute(rel)) return false;
45669
+ const top = rel.split(import_node_path15.default.sep)[0];
45112
45670
  return ["Desktop", "Documents", "Downloads"].includes(top ?? "");
45113
45671
  }
45114
45672
  function resolveCliEntry() {
45115
45673
  try {
45116
- return import_node_fs18.default.realpathSync(process.argv[1]);
45674
+ return import_node_fs19.default.realpathSync(process.argv[1]);
45117
45675
  } catch {
45118
- return import_node_path14.default.resolve(process.argv[1]);
45676
+ return import_node_path15.default.resolve(process.argv[1]);
45119
45677
  }
45120
45678
  }
45121
45679
  function resolveProjectDir(explicit) {
45122
- const dir = import_node_path14.default.resolve(explicit ?? process.cwd());
45123
- if (!import_node_fs18.default.existsSync(import_node_path14.default.join(dir, ".sail"))) {
45680
+ const dir = import_node_path15.default.resolve(explicit ?? process.cwd());
45681
+ if (!import_node_fs19.default.existsSync(import_node_path15.default.join(dir, ".sail"))) {
45124
45682
  throw new Error(
45125
45683
  `No .sail/ found in ${dir}.
45126
45684
  Run this from your Sailor project root, or pass --project <path>. (The installer does not search parent directories \u2014 that would risk targeting the wrong project.)`
@@ -45132,7 +45690,7 @@ function passphraseReadiness(projectDir) {
45132
45690
  const account2 = (() => {
45133
45691
  try {
45134
45692
  return JSON.parse(
45135
- import_node_fs18.default.readFileSync(import_node_path14.default.join(projectDir, ".sail", "account.json"), "utf-8")
45693
+ import_node_fs19.default.readFileSync(import_node_path15.default.join(projectDir, ".sail", "account.json"), "utf-8")
45136
45694
  );
45137
45695
  } catch {
45138
45696
  return null;
@@ -45148,28 +45706,28 @@ function passphraseReadiness(projectDir) {
45148
45706
  } finally {
45149
45707
  process.chdir(prevCwd);
45150
45708
  }
45151
- const env = parseEnvFile(import_node_path14.default.join(projectDir, ".sail", ".env.local"));
45709
+ const env = parseEnvFile(import_node_path15.default.join(projectDir, ".sail", ".env.local"));
45152
45710
  const passphraseInEnvFile = Boolean(env.SAIL_PASSPHRASE);
45153
45711
  return { keystorePresent, passphraseInEnvFile, ready: keystorePresent && passphraseInEnvFile };
45154
45712
  }
45155
45713
  function buildConfig(opts) {
45156
45714
  const projectDir = resolveProjectDir(opts.project);
45157
45715
  return {
45158
- projectName: sanitizeName(import_node_path14.default.basename(projectDir)),
45716
+ projectName: sanitizeName(import_node_path15.default.basename(projectDir)),
45159
45717
  projectDir,
45160
45718
  nodePath: process.execPath,
45161
45719
  cliEntry: resolveCliEntry(),
45162
- logPath: import_node_path14.default.join(projectDir, ".sail", "agent.log"),
45720
+ logPath: import_node_path15.default.join(projectDir, ".sail", "agent.log"),
45163
45721
  interval: opts.interval != null ? Number(opts.interval) : void 0,
45164
45722
  chain: opts.chain != null ? Number(opts.chain) : void 0,
45165
45723
  restartSec: 30
45166
45724
  };
45167
45725
  }
45168
45726
  function plistPath(projectName) {
45169
- return import_node_path14.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
45727
+ return import_node_path15.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
45170
45728
  }
45171
45729
  function systemdPath(projectName) {
45172
- return import_node_path14.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
45730
+ return import_node_path15.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
45173
45731
  }
45174
45732
  async function serviceInstall(opts = {}) {
45175
45733
  const cfg = buildConfig(opts);
@@ -45193,12 +45751,12 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45193
45751
  console.warn(`\u26A0 ${msg}
45194
45752
  Proceeding because --force was given.`);
45195
45753
  }
45196
- import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(cfg.logPath), { recursive: true });
45754
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(cfg.logPath), { recursive: true });
45197
45755
  if (platform === "darwin") {
45198
45756
  const plist = buildLaunchdPlist(cfg);
45199
45757
  const dest = plistPath(cfg.projectName);
45200
- import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
45201
- import_node_fs18.default.writeFileSync(dest, plist);
45758
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(dest), { recursive: true });
45759
+ import_node_fs19.default.writeFileSync(dest, plist);
45202
45760
  const uid2 = process.getuid?.() ?? 0;
45203
45761
  try {
45204
45762
  try {
@@ -45217,8 +45775,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45217
45775
  if (platform === "linux") {
45218
45776
  const unit = buildSystemdUnit(cfg);
45219
45777
  const dest = systemdPath(cfg.projectName);
45220
- import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
45221
- import_node_fs18.default.writeFileSync(dest, unit);
45778
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(dest), { recursive: true });
45779
+ import_node_fs19.default.writeFileSync(dest, unit);
45222
45780
  try {
45223
45781
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
45224
45782
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "enable", "--now", systemdUnitName(cfg.projectName)], {
@@ -45232,8 +45790,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45232
45790
  }
45233
45791
  if (platform === "win32") {
45234
45792
  const xml = buildWindowsTaskXml(cfg);
45235
- const dest = import_node_path14.default.join(import_node_os.default.tmpdir(), `${windowsTaskName(cfg.projectName)}.xml`);
45236
- import_node_fs18.default.writeFileSync(dest, xml, "utf-8");
45793
+ const dest = import_node_path15.default.join(import_node_os.default.tmpdir(), `${windowsTaskName(cfg.projectName)}.xml`);
45794
+ import_node_fs19.default.writeFileSync(dest, xml, "utf-8");
45237
45795
  try {
45238
45796
  (0, import_node_child_process2.execFileSync)(
45239
45797
  "schtasks",
@@ -45244,7 +45802,7 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45244
45802
  throw new Error(`schtasks /Create failed: ${err.message}`);
45245
45803
  } finally {
45246
45804
  try {
45247
- import_node_fs18.default.rmSync(dest, { force: true });
45805
+ import_node_fs19.default.rmSync(dest, { force: true });
45248
45806
  } catch {
45249
45807
  }
45250
45808
  }
@@ -45270,14 +45828,14 @@ function okInstall(cfg, unit) {
45270
45828
  );
45271
45829
  }
45272
45830
  async function serviceStatus(opts = {}) {
45273
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45831
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45274
45832
  const platform = process.platform;
45275
45833
  let installed = false;
45276
45834
  let running = false;
45277
45835
  let detail = "";
45278
45836
  try {
45279
45837
  if (platform === "darwin") {
45280
- installed = import_node_fs18.default.existsSync(plistPath(projectName));
45838
+ installed = import_node_fs19.default.existsSync(plistPath(projectName));
45281
45839
  const uid2 = process.getuid?.() ?? 0;
45282
45840
  try {
45283
45841
  detail = (0, import_node_child_process2.execFileSync)("launchctl", ["print", `gui/${uid2}/${launchdLabel(projectName)}`], {
@@ -45289,7 +45847,7 @@ async function serviceStatus(opts = {}) {
45289
45847
  } catch {
45290
45848
  }
45291
45849
  } else if (platform === "linux") {
45292
- installed = import_node_fs18.default.existsSync(systemdPath(projectName));
45850
+ installed = import_node_fs19.default.existsSync(systemdPath(projectName));
45293
45851
  try {
45294
45852
  detail = (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "is-active", systemdUnitName(projectName)], {
45295
45853
  encoding: "utf-8",
@@ -45325,7 +45883,7 @@ async function serviceStatus(opts = {}) {
45325
45883
  );
45326
45884
  }
45327
45885
  async function serviceStop(opts = {}) {
45328
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45886
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45329
45887
  const platform = process.platform;
45330
45888
  if (platform === "darwin") {
45331
45889
  const uid2 = process.getuid?.() ?? 0;
@@ -45344,7 +45902,7 @@ async function serviceStop(opts = {}) {
45344
45902
  });
45345
45903
  }
45346
45904
  async function serviceUninstall(opts = {}) {
45347
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45905
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45348
45906
  const platform = process.platform;
45349
45907
  if (platform === "darwin") {
45350
45908
  const uid2 = process.getuid?.() ?? 0;
@@ -45354,7 +45912,7 @@ async function serviceUninstall(opts = {}) {
45354
45912
  });
45355
45913
  } catch {
45356
45914
  }
45357
- import_node_fs18.default.rmSync(plistPath(projectName), { force: true });
45915
+ import_node_fs19.default.rmSync(plistPath(projectName), { force: true });
45358
45916
  } else if (platform === "linux") {
45359
45917
  try {
45360
45918
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "disable", "--now", systemdUnitName(projectName)], {
@@ -45362,7 +45920,7 @@ async function serviceUninstall(opts = {}) {
45362
45920
  });
45363
45921
  } catch {
45364
45922
  }
45365
- import_node_fs18.default.rmSync(systemdPath(projectName), { force: true });
45923
+ import_node_fs19.default.rmSync(systemdPath(projectName), { force: true });
45366
45924
  try {
45367
45925
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "ignore" });
45368
45926
  } catch {
@@ -45380,21 +45938,21 @@ async function serviceUninstall(opts = {}) {
45380
45938
  }
45381
45939
  async function serviceLogs(opts = {}) {
45382
45940
  const projectDir = resolveProjectDir(opts.project);
45383
- const logPath = import_node_path14.default.join(projectDir, ".sail", "agent.log");
45384
- if (!import_node_fs18.default.existsSync(logPath)) {
45941
+ const logPath = import_node_path15.default.join(projectDir, ".sail", "agent.log");
45942
+ if (!import_node_fs19.default.existsSync(logPath)) {
45385
45943
  console.log(`No log yet at ${logPath}. The service writes here once it runs.`);
45386
45944
  return;
45387
45945
  }
45388
45946
  if (opts.follow) {
45389
45947
  if (process.platform === "win32") {
45390
- console.log(import_node_fs18.default.readFileSync(logPath, "utf-8"));
45948
+ console.log(import_node_fs19.default.readFileSync(logPath, "utf-8"));
45391
45949
  console.log("(follow mode is not supported on Windows here \u2014 re-run to refresh)");
45392
45950
  return;
45393
45951
  }
45394
45952
  (0, import_node_child_process2.execFileSync)("tail", ["-f", logPath], { stdio: "inherit" });
45395
45953
  return;
45396
45954
  }
45397
- const lines = import_node_fs18.default.readFileSync(logPath, "utf-8").split("\n");
45955
+ const lines = import_node_fs19.default.readFileSync(logPath, "utf-8").split("\n");
45398
45956
  console.log(lines.slice(-200).join("\n"));
45399
45957
  }
45400
45958
 
@@ -45550,14 +46108,14 @@ async function sessionResume(opts = {}) {
45550
46108
  }
45551
46109
 
45552
46110
  // src/commands/station.ts
45553
- var import_node_fs19 = require("node:fs");
45554
- var import_node_path15 = require("node:path");
45555
- var RUNTIME_SERVER_FILE2 = (0, import_node_path15.join)(".sail", "runtime", "server.json");
46111
+ var import_node_fs20 = require("node:fs");
46112
+ var import_node_path16 = require("node:path");
46113
+ var RUNTIME_SERVER_FILE2 = (0, import_node_path16.join)(".sail", "runtime", "server.json");
45556
46114
  function readState(projectRoot) {
45557
- const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
45558
- if (!(0, import_node_fs19.existsSync)(file)) return null;
46115
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
46116
+ if (!(0, import_node_fs20.existsSync)(file)) return null;
45559
46117
  try {
45560
- return JSON.parse((0, import_node_fs19.readFileSync)(file, "utf8"));
46118
+ return JSON.parse((0, import_node_fs20.readFileSync)(file, "utf8"));
45561
46119
  } catch {
45562
46120
  return null;
45563
46121
  }
@@ -45629,9 +46187,9 @@ async function stationStop(options) {
45629
46187
  }
45630
46188
  const daemon = await discoverDaemon(projectRoot);
45631
46189
  if (!daemon) {
45632
- const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
46190
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
45633
46191
  try {
45634
- if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
46192
+ if ((0, import_node_fs20.existsSync)(file)) (0, import_node_fs20.rmSync)(file);
45635
46193
  } catch {
45636
46194
  }
45637
46195
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -45647,9 +46205,9 @@ async function stationStop(options) {
45647
46205
  pid: state.pid
45648
46206
  });
45649
46207
  } catch {
45650
- const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
46208
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
45651
46209
  try {
45652
- if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
46210
+ if ((0, import_node_fs20.existsSync)(file)) (0, import_node_fs20.rmSync)(file);
45653
46211
  } catch {
45654
46212
  }
45655
46213
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -45711,9 +46269,9 @@ async function status() {
45711
46269
 
45712
46270
  // src/commands/ui.ts
45713
46271
  var import_node_child_process5 = require("node:child_process");
45714
- var import_node_fs20 = __toESM(require("node:fs"), 1);
46272
+ var import_node_fs21 = __toESM(require("node:fs"), 1);
45715
46273
  var import_node_net2 = __toESM(require("node:net"), 1);
45716
- var import_node_path16 = __toESM(require("node:path"), 1);
46274
+ var import_node_path17 = __toESM(require("node:path"), 1);
45717
46275
 
45718
46276
  // src/lib/tailscale.ts
45719
46277
  var import_node_child_process4 = require("node:child_process");
@@ -45759,12 +46317,12 @@ function tailscaleServeDown() {
45759
46317
  }
45760
46318
 
45761
46319
  // src/commands/ui.ts
45762
- var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
46320
+ var UI_STATE_FILE = import_node_path17.default.join(".sail", "runtime", "ui.json");
45763
46321
  function readState2(projectRoot) {
45764
- const file = import_node_path16.default.join(projectRoot, UI_STATE_FILE);
45765
- if (!import_node_fs20.default.existsSync(file)) return null;
46322
+ const file = import_node_path17.default.join(projectRoot, UI_STATE_FILE);
46323
+ if (!import_node_fs21.default.existsSync(file)) return null;
45766
46324
  try {
45767
- return JSON.parse(import_node_fs20.default.readFileSync(file, "utf-8"));
46325
+ return JSON.parse(import_node_fs21.default.readFileSync(file, "utf-8"));
45768
46326
  } catch {
45769
46327
  return null;
45770
46328
  }
@@ -45772,7 +46330,7 @@ function readState2(projectRoot) {
45772
46330
  function installedCliVersion() {
45773
46331
  try {
45774
46332
  const pkg = JSON.parse(
45775
- import_node_fs20.default.readFileSync(import_node_path16.default.join(packageRoot(), "package.json"), "utf-8")
46333
+ import_node_fs21.default.readFileSync(import_node_path17.default.join(packageRoot(), "package.json"), "utf-8")
45776
46334
  );
45777
46335
  return pkg.version ?? null;
45778
46336
  } catch {
@@ -45799,16 +46357,16 @@ async function warnIfVersionSkew(port) {
45799
46357
  }
45800
46358
  function waitForPort(port, timeoutMs) {
45801
46359
  const deadline = Date.now() + timeoutMs;
45802
- return new Promise((resolve3) => {
46360
+ return new Promise((resolve4) => {
45803
46361
  const attempt = () => {
45804
46362
  const socket = import_node_net2.default.connect(port, "127.0.0.1");
45805
46363
  socket.once("connect", () => {
45806
46364
  socket.destroy();
45807
- resolve3(true);
46365
+ resolve4(true);
45808
46366
  });
45809
46367
  socket.once("error", () => {
45810
46368
  socket.destroy();
45811
- if (Date.now() > deadline) resolve3(false);
46369
+ if (Date.now() > deadline) resolve4(false);
45812
46370
  else setTimeout(attempt, 150);
45813
46371
  });
45814
46372
  };
@@ -45817,16 +46375,16 @@ function waitForPort(port, timeoutMs) {
45817
46375
  }
45818
46376
  async function uiCommand(opts = {}) {
45819
46377
  const distDir = cliDistDir();
45820
- const uiDistDir = import_node_path16.default.join(packageRoot(), "packages", "ui", "dist");
45821
- const serverBundle = import_node_path16.default.resolve(distDir, "server.cjs");
46378
+ const uiDistDir = import_node_path17.default.join(packageRoot(), "packages", "ui", "dist");
46379
+ const serverBundle = import_node_path17.default.resolve(distDir, "server.cjs");
45822
46380
  const projectRoot = process.cwd();
45823
- const sailDir2 = import_node_path16.default.join(projectRoot, ".sail");
46381
+ const sailDir2 = import_node_path17.default.join(projectRoot, ".sail");
45824
46382
  const envPort = Number(process.env.PORT);
45825
46383
  const port = await findFreePort(Number.isInteger(envPort) && envPort > 0 && envPort <= 65535 ? envPort : projectPort(projectRoot));
45826
- if (!import_node_fs20.default.existsSync(serverBundle)) {
46384
+ if (!import_node_fs21.default.existsSync(serverBundle)) {
45827
46385
  throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
45828
46386
  }
45829
- if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
46387
+ if (!import_node_fs21.default.existsSync(import_node_path17.default.join(uiDistDir, "index.html"))) {
45830
46388
  throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
45831
46389
  }
45832
46390
  let tailnetUrl = null;
@@ -45856,10 +46414,10 @@ async function uiCommand(opts = {}) {
45856
46414
  await warnIfVersionSkew(existing.port);
45857
46415
  return;
45858
46416
  }
45859
- const runtimeDir = import_node_path16.default.join(projectRoot, ".sail", "runtime");
45860
- import_node_fs20.default.mkdirSync(runtimeDir, { recursive: true });
45861
- const logFile = import_node_path16.default.join(runtimeDir, "ui.log");
45862
- const logFd = import_node_fs20.default.openSync(logFile, "a");
46417
+ const runtimeDir = import_node_path17.default.join(projectRoot, ".sail", "runtime");
46418
+ import_node_fs21.default.mkdirSync(runtimeDir, { recursive: true });
46419
+ const logFile = import_node_path17.default.join(runtimeDir, "ui.log");
46420
+ const logFd = import_node_fs21.default.openSync(logFile, "a");
45863
46421
  const corsOrigins = [process.env.SAILOR_CORS_ORIGINS, tailnetUrl].filter(Boolean).join(",");
45864
46422
  const child = (0, import_node_child_process5.spawn)(process.execPath, [serverBundle], {
45865
46423
  detached: true,
@@ -45874,7 +46432,7 @@ async function uiCommand(opts = {}) {
45874
46432
  }
45875
46433
  });
45876
46434
  child.unref();
45877
- import_node_fs20.default.closeSync(logFd);
46435
+ import_node_fs21.default.closeSync(logFd);
45878
46436
  const READY_TIMEOUT_MS = 1e4;
45879
46437
  const deadline = Date.now() + READY_TIMEOUT_MS;
45880
46438
  let ready = false;
@@ -45888,14 +46446,14 @@ async function uiCommand(opts = {}) {
45888
46446
  if (!ready) {
45889
46447
  let tail = "";
45890
46448
  try {
45891
- tail = import_node_fs20.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
46449
+ tail = import_node_fs21.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
45892
46450
  } catch {
45893
46451
  }
45894
46452
  throw new Error(
45895
46453
  `Sailor UI failed to start within ${READY_TIMEOUT_MS / 1e3}s on port ${port}.` + (tail ? `
45896
46454
 
45897
46455
  Server output:
45898
- ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
46456
+ ${tail}` : ` See ${import_node_path17.default.relative(projectRoot, logFile)}.`)
45899
46457
  );
45900
46458
  }
45901
46459
  let exposed = false;
@@ -45910,8 +46468,8 @@ ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
45910
46468
  );
45911
46469
  }
45912
46470
  }
45913
- import_node_fs20.default.writeFileSync(
45914
- import_node_path16.default.join(projectRoot, UI_STATE_FILE),
46471
+ import_node_fs21.default.writeFileSync(
46472
+ import_node_path17.default.join(projectRoot, UI_STATE_FILE),
45915
46473
  JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString(), exposed }, null, 2)
45916
46474
  );
45917
46475
  console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
@@ -45924,7 +46482,7 @@ async function uiStatus() {
45924
46482
  console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
45925
46483
  await warnIfVersionSkew(state.port);
45926
46484
  } else {
45927
- if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
46485
+ if (state) import_node_fs21.default.rmSync(import_node_path17.default.join(process.cwd(), UI_STATE_FILE), { force: true });
45928
46486
  console.log("\u25CB Sailor UI is not running");
45929
46487
  }
45930
46488
  }
@@ -45936,7 +46494,7 @@ function uiStop() {
45936
46494
  return;
45937
46495
  }
45938
46496
  if (!isProcessAlive(state.pid)) {
45939
- import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
46497
+ import_node_fs21.default.rmSync(import_node_path17.default.join(projectRoot, UI_STATE_FILE), { force: true });
45940
46498
  console.log("Sailor UI is not running (stale state file removed).");
45941
46499
  return;
45942
46500
  }
@@ -45945,14 +46503,14 @@ function uiStop() {
45945
46503
  tailscaleServeDown();
45946
46504
  console.log("Tailnet exposure removed.");
45947
46505
  }
45948
- import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
46506
+ import_node_fs21.default.rmSync(import_node_path17.default.join(projectRoot, UI_STATE_FILE), { force: true });
45949
46507
  console.log(`Stopped Sailor UI (pid ${state.pid}).`);
45950
46508
  }
45951
46509
 
45952
46510
  // src/index.ts
45953
46511
  function cliVersion() {
45954
46512
  try {
45955
- const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path17.join)(packageRoot(), "package.json"), "utf-8"));
46513
+ const pkg = JSON.parse((0, import_node_fs22.readFileSync)((0, import_node_path18.join)(packageRoot(), "package.json"), "utf-8"));
45956
46514
  return pkg.version ?? "0.0.0";
45957
46515
  } catch {
45958
46516
  return "0.0.0";
@@ -46023,6 +46581,9 @@ mandate.command("attach").description("Register one or more already-deployed per
46023
46581
  "--address <mandateOrName>",
46024
46582
  "Permission address or locally-tracked name; or a comma-separated list of addresses to register together in one signature"
46025
46583
  ).requiredOption("--sma <address>", "SMA to register the permission on").option("--label <label>", "Human-readable label shown in the signing UI").option("--json", "Emit machine-readable JSON").action(actionWith(mandateAttach));
46584
+ mandate.command("configure").description(
46585
+ "Write per-account bounds on an already-deployed shared permission singleton (configureDirect; owner tx via the signing station). Pairs with `mandate attach`, which only registers \u2014 a registered-but-unconfigured singleton denies every call."
46586
+ ).requiredOption("--address <singleton>", "Shared permission singleton address (deployed on this chain)").requiredOption("--sma <address>", "SMA to configure the singleton for").option("--params <hex>", "Pre-encoded config blob (0x-prefixed hex)").option("--args-file <path>", "JSON file of typed params (paired with --template)").option("--template <name>", "Template name (e.g. SwapPermission) \u2014 resolves the encoder for --args-file").option("--label <label>", "Human-readable label shown in the signing UI").option("--simulate-only", "Stop after the off-chain pre-flight (no signing, no gas)").option("--force", "Re-configure even if isConfigured is already true").option("--json", "Emit machine-readable JSON").action(actionWith(mandateConfigure));
46026
46587
  mandate.command("deploy-clone").description("[currently unavailable \u2014 no clone templates deployed on any chain; use `mandate deploy`] Deploy + register a standalone clone permission via the signing UI").requiredOption("--template <key>", "Standalone clone template key (e.g. boundedApprove)").requiredOption("--sma <address>", "SMA to deploy the clone for and register it on").option("--tokens <csv>", "Comma-separated allowed token addresses").option("--spenders <csv>", "Comma-separated allowed spender addresses").option("--max <amount>", "Max amount per tx in base units (default: uint256 max)").option("--label <label>", "Human-readable label to track this permission under").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeployClone));
46027
46588
  mandate.command("revoke").description("Revoke permission(s) from an SMA (EIP-712 RevokePermissions, owner-authorized)").option("--address <permissionOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "Safe (SMA) to revoke the permission(s) from").option("--all", "Revoke every permission currently registered on the SMA").option("--json", "Output JSON").action(actionWith(mandateRevoke));
46028
46589
  mandate.command("templates").description("Show how to author your own permission contract (and any community-deployed addresses)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateTemplates));