@dev.sail.money/sailor 1.3.0-224 → 1.3.0-226

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 (157) hide show
  1. package/README.md +16 -0
  2. package/package.json +7 -1
  3. package/packages/cli/dist/index.cjs +699 -135
  4. package/packages/cli/dist/server.cjs +15990 -17974
  5. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts +130 -0
  6. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts.map +1 -0
  7. package/packages/sdk/dist/abis/ConfigurablePermission.js +79 -0
  8. package/packages/sdk/dist/abis/ConfigurablePermission.js.map +1 -0
  9. package/packages/sdk/dist/abis/index.d.ts +1 -0
  10. package/packages/sdk/dist/abis/index.d.ts.map +1 -1
  11. package/packages/sdk/dist/abis/index.js +1 -0
  12. package/packages/sdk/dist/abis/index.js.map +1 -1
  13. package/packages/sdk/dist/index.d.ts +1 -0
  14. package/packages/sdk/dist/index.d.ts.map +1 -1
  15. package/packages/sdk/dist/index.js +1 -0
  16. package/packages/sdk/dist/index.js.map +1 -1
  17. package/packages/sdk/dist/intelligence.d.ts +1 -1
  18. package/packages/sdk/dist/intelligence.js +1 -1
  19. package/packages/ui/dist/assets/{add-HBssdE_q.js → add-DoHGZfJI.js} +2 -2
  20. package/packages/ui/dist/assets/{all-wallets-kgnKzcam.js → all-wallets-CUKVFyT3.js} +2 -2
  21. package/packages/ui/dist/assets/{app-store-C7gfCF4e.js → app-store-DyQ4PZOs.js} +1 -1
  22. package/packages/ui/dist/assets/{apple-DmsD7XsU.js → apple-Dc-GOiDW.js} +2 -2
  23. package/packages/ui/dist/assets/arrow-bottom-C-dI8Oh_.js +8 -0
  24. package/packages/ui/dist/assets/{arrow-bottom-circle-Cl21xaXp.js → arrow-bottom-circle-BH2LebvP.js} +2 -2
  25. package/packages/ui/dist/assets/arrow-left-CUIn5NsD.js +8 -0
  26. package/packages/ui/dist/assets/arrow-right-B-X57DDF.js +8 -0
  27. package/packages/ui/dist/assets/arrow-top-BNOmzVbg.js +8 -0
  28. package/packages/ui/dist/assets/{bank-BthuR5Ky.js → bank-Dqcy3Map.js} +2 -2
  29. package/packages/ui/dist/assets/{basic-BSdElBI1.js → basic-CN95kOlB.js} +177 -177
  30. package/packages/ui/dist/assets/{browser-BMfpXAJE.js → browser-v-WzD6Au.js} +2 -2
  31. package/packages/ui/dist/assets/{card-BEvVqDzc.js → card-DJmQXuOi.js} +2 -2
  32. package/packages/ui/dist/assets/ccip-8oS512uk.js +1 -0
  33. package/packages/ui/dist/assets/{checkmark-BqYZHVCR.js → checkmark-6Rvy6Al7.js} +2 -2
  34. package/packages/ui/dist/assets/{checkmark-bold-DjNGbayE.js → checkmark-bold-GYXSMFUr.js} +2 -2
  35. package/packages/ui/dist/assets/chevron-bottom-BF1Fa2NO.js +8 -0
  36. package/packages/ui/dist/assets/chevron-left-kj9KjPLp.js +8 -0
  37. package/packages/ui/dist/assets/chevron-right-CHH0lg7_.js +8 -0
  38. package/packages/ui/dist/assets/chevron-top-DjHmnG32.js +8 -0
  39. package/packages/ui/dist/assets/{chrome-store-bPC0PXv4.js → chrome-store-KzNe4H9H.js} +2 -2
  40. package/packages/ui/dist/assets/{clock-BolGF8wp.js → clock-xHvMNQNx.js} +2 -2
  41. package/packages/ui/dist/assets/{close-Bo18lqFQ.js → close-nKtgX0WM.js} +2 -2
  42. package/packages/ui/dist/assets/{coinPlaceholder-Bz-0VLbz.js → coinPlaceholder-gnwD3g4G.js} +2 -2
  43. package/packages/ui/dist/assets/{compass-CKKiFCLR.js → compass-wnLBxh3w.js} +2 -2
  44. package/packages/ui/dist/assets/{copy-CYIav_IY.js → copy-CmLisiOG.js} +2 -2
  45. package/packages/ui/dist/assets/core-BLOM5s8O.js +907 -0
  46. package/packages/ui/dist/assets/cursor-DgwjrlpZ.js +3 -0
  47. package/packages/ui/dist/assets/{cursor-transparent-DE9tExWo.js → cursor-transparent-CjVDknUI.js} +2 -2
  48. package/packages/ui/dist/assets/{desktop-CWNluv_t.js → desktop-BSGjR5fT.js} +2 -2
  49. package/packages/ui/dist/assets/{disconnect-Db6qWs6T.js → disconnect-DkOQ_j9U.js} +2 -2
  50. package/packages/ui/dist/assets/{discord-Cj6mVoBK.js → discord-DtvZ281_.js} +2 -2
  51. package/packages/ui/dist/assets/{etherscan-CCFLsPvo.js → etherscan-BB3baLbR.js} +2 -2
  52. package/packages/ui/dist/assets/events-DQ172AOg.js +1 -0
  53. package/packages/ui/dist/assets/{exclamation-triangle-DJWx46rU.js → exclamation-triangle-BrR8a-pR.js} +2 -2
  54. package/packages/ui/dist/assets/{extension-BtaF8Z9u.js → extension-BiGAMg97.js} +2 -2
  55. package/packages/ui/dist/assets/external-link-qUc7QuKA.js +8 -0
  56. package/packages/ui/dist/assets/{facebook-BoGz1nMk.js → facebook-BuXatc22.js} +2 -2
  57. package/packages/ui/dist/assets/{farcaster-AsPTw3iB.js → farcaster-USwPlLU8.js} +2 -2
  58. package/packages/ui/dist/assets/{filters-D1-1MQtc.js → filters-BWx3pZxN.js} +2 -2
  59. package/packages/ui/dist/assets/{github-B1FrwkiF.js → github-CUSWD7ff.js} +2 -2
  60. package/packages/ui/dist/assets/{google-DnrnTDFz.js → google-CxJSLEaz.js} +2 -2
  61. package/packages/ui/dist/assets/{help-circle-CI10dCJN.js → help-circle-_8isjSO0.js} +1 -1
  62. package/packages/ui/dist/assets/{id-B627Euj0.js → id-DJCzMfx_.js} +2 -2
  63. package/packages/ui/dist/assets/{image-3O2KHUgY.js → image-CmG69MZj.js} +2 -2
  64. package/packages/ui/dist/assets/{index-CeZ-3oZK.js → index-3YqzzyrK.js} +3 -3
  65. package/packages/ui/dist/assets/index-B07bCKJ8.css +1 -0
  66. package/packages/ui/dist/assets/index-BIqjq0q5.js +1 -0
  67. package/packages/ui/dist/assets/index-BV8s6ez1.js +1 -0
  68. package/packages/ui/dist/assets/index-D8zwDy7d.js +1780 -0
  69. package/packages/ui/dist/assets/index-DHVwxjrm.js +47 -0
  70. package/packages/ui/dist/assets/index-DsxB7ORb.js +16 -0
  71. package/packages/ui/dist/assets/index.es-Hqwz_Ukd.js +26 -0
  72. package/packages/ui/dist/assets/{info-CMmDZSrf.js → info-XYctdUoY.js} +2 -2
  73. package/packages/ui/dist/assets/{info-circle-ChsNDy1e.js → info-circle-D_fYZUhv.js} +2 -2
  74. package/packages/ui/dist/assets/{lightbulb-DDlT2eHQ.js → lightbulb-BqKkDThe.js} +2 -2
  75. package/packages/ui/dist/assets/{mail-DyTAk3JR.js → mail-CM4laVWT.js} +2 -2
  76. package/packages/ui/dist/assets/metamask-sdk-8TvB8vxr.js +542 -0
  77. package/packages/ui/dist/assets/{mobile-B1ZTQh8S.js → mobile-C2cl_MIN.js} +2 -2
  78. package/packages/ui/dist/assets/{more-BNIrTkTn.js → more-DOik7ytj.js} +2 -2
  79. package/packages/ui/dist/assets/{network-placeholder-BRBk5JfG.js → network-placeholder-CC9Cg0q_.js} +2 -2
  80. package/packages/ui/dist/assets/{nftPlaceholder-xtnGz2yb.js → nftPlaceholder-Oe_bTYkM.js} +2 -2
  81. package/packages/ui/dist/assets/{off-BfgRQBAE.js → off-ao0Rdo1E.js} +2 -2
  82. package/packages/ui/dist/assets/{play-store-BFjPDZvv.js → play-store-BcQlROdR.js} +2 -2
  83. package/packages/ui/dist/assets/{plus-C3dNKGgI.js → plus-DSOprccT.js} +2 -2
  84. package/packages/ui/dist/assets/{qr-code-BovNFnu8.js → qr-code-B64-C9CJ.js} +1 -1
  85. package/packages/ui/dist/assets/{recycle-horizontal-DdcYEE6i.js → recycle-horizontal-BJg7qNO9.js} +2 -2
  86. package/packages/ui/dist/assets/{refresh-P6W_osB6.js → refresh-Dexg1q9p.js} +2 -2
  87. package/packages/ui/dist/assets/{reown-logo-DbesEx7U.js → reown-logo-D8KP4d24.js} +2 -2
  88. package/packages/ui/dist/assets/{search-DMB73W4z.js → search-CBfhIhB0.js} +2 -2
  89. package/packages/ui/dist/assets/{secp256k1-URVniZ2R.js → secp256k1-BOKPNuRR.js} +1 -1
  90. package/packages/ui/dist/assets/{send-BUsSvitv.js → send-DpJV-P0b.js} +2 -2
  91. package/packages/ui/dist/assets/{swapHorizontal-CjaXc-eY.js → swapHorizontal-DC-Elc2j.js} +2 -2
  92. package/packages/ui/dist/assets/{swapHorizontalBold-DuNmGzT_.js → swapHorizontalBold-Unn8xI55.js} +2 -2
  93. package/packages/ui/dist/assets/{swapHorizontalMedium-CgZZIpJT.js → swapHorizontalMedium-B58RR8wP.js} +2 -2
  94. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BC1bVmtH.js → swapHorizontalRoundedBold-UTrbE6zM.js} +2 -2
  95. package/packages/ui/dist/assets/{swapVertical-PyYv49m9.js → swapVertical-CLBtBVG1.js} +2 -2
  96. package/packages/ui/dist/assets/{telegram-DaCrUlL0.js → telegram-C_tvCVU8.js} +2 -2
  97. package/packages/ui/dist/assets/{three-dots-D9tEmhOK.js → three-dots-BIn_IFa0.js} +2 -2
  98. package/packages/ui/dist/assets/{twitch-2m8_XY_5.js → twitch-BqiEXRul.js} +2 -2
  99. package/packages/ui/dist/assets/{twitterIcon-BQ4fWtR1.js → twitterIcon-CquA5TCG.js} +2 -2
  100. package/packages/ui/dist/assets/{verify-DJtFLNUq.js → verify-DOUX5_SE.js} +2 -2
  101. package/packages/ui/dist/assets/{verify-filled-B2cSEFoP.js → verify-filled-nMk53S_a.js} +2 -2
  102. package/packages/ui/dist/assets/{w3m-modal-YBSDQDS1.js → w3m-modal-B-JVYtbP.js} +1 -1
  103. package/packages/ui/dist/assets/{wallet-C7Gv760P.js → wallet-BjbzMOXA.js} +2 -2
  104. package/packages/ui/dist/assets/{wallet-placeholder-DtZ8_f4p.js → wallet-placeholder-DPidGoJt.js} +2 -2
  105. package/packages/ui/dist/assets/{walletconnect-DsIIuQfT.js → walletconnect-Dw14KwDj.js} +3 -3
  106. package/packages/ui/dist/assets/{warning-circle-CTMOrGt2.js → warning-circle-Ewxxj9bx.js} +2 -2
  107. package/packages/ui/dist/assets/{x-BmL9CVvs.js → x-B5Li_yME.js} +2 -2
  108. package/packages/ui/dist/index.html +2 -2
  109. package/templates/default/.agents/skills/sail-mandates/references/approvals.md +3 -3
  110. package/templates/default/.agents/skills/sail-servers/SKILL.md +1 -0
  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/.env.example +9 -0
  130. package/templates/default/AGENTS.md +21 -2
  131. package/templates/default/scripts/quote-swap.mjs +211 -0
  132. package/templates/default/scripts/resolve-token.mjs +992 -0
  133. package/templates/default/scripts/shared-template-addr.mjs +110 -0
  134. package/packages/ui/dist/assets/arrow-bottom-B3CywkhY.js +0 -8
  135. package/packages/ui/dist/assets/arrow-left-Bp_pqRAk.js +0 -8
  136. package/packages/ui/dist/assets/arrow-right-CRK93G5I.js +0 -8
  137. package/packages/ui/dist/assets/arrow-top-DyDJtPkJ.js +0 -8
  138. package/packages/ui/dist/assets/ccip-BALQiE-q.js +0 -1
  139. package/packages/ui/dist/assets/chevron-bottom-BEHbtNKD.js +0 -8
  140. package/packages/ui/dist/assets/chevron-left-C__dvO-Z.js +0 -8
  141. package/packages/ui/dist/assets/chevron-right-9abfT5a8.js +0 -8
  142. package/packages/ui/dist/assets/chevron-top-B2VeyvGH.js +0 -8
  143. package/packages/ui/dist/assets/core-8m_0OoS0.js +0 -907
  144. package/packages/ui/dist/assets/cursor-D10zW2HG.js +0 -3
  145. package/packages/ui/dist/assets/events-BGQ3xBjc.js +0 -1
  146. package/packages/ui/dist/assets/external-link-5wYZl3Lh.js +0 -8
  147. package/packages/ui/dist/assets/fallback-B2d-ODZG.js +0 -1
  148. package/packages/ui/dist/assets/index-CKoOBsKn.css +0 -1
  149. package/packages/ui/dist/assets/index-CgsvP_iE.js +0 -1
  150. package/packages/ui/dist/assets/index-DDe72Fsi.js +0 -1
  151. package/packages/ui/dist/assets/index-DKCg39xU.js +0 -16
  152. package/packages/ui/dist/assets/index-DhVzwxqf.js +0 -1
  153. package/packages/ui/dist/assets/index-ohx1EwWJ.js +0 -1789
  154. package/packages/ui/dist/assets/index.es-ClarUTak.js +0 -26
  155. package/packages/ui/dist/assets/metamask-sdk-CoU-3z33.js +0 -542
  156. package/packages/ui/dist/assets/native-CJ5et6AR.js +0 -1
  157. package/packages/ui/dist/assets/parseSignature-DPmCI80m.js +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
 
@@ -39575,8 +39629,9 @@ var SigningServer = class {
39575
39629
  }
39576
39630
  }
39577
39631
  if (!(0, import_node_fs6.existsSync)(this.runtimeDir)) (0, import_node_fs6.mkdirSync)(this.runtimeDir, { recursive: true });
39632
+ const serverStatePath = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39578
39633
  (0, import_node_fs6.writeFileSync)(
39579
- (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE),
39634
+ serverStatePath,
39580
39635
  JSON.stringify(
39581
39636
  {
39582
39637
  url: this._url,
@@ -39588,8 +39643,10 @@ var SigningServer = class {
39588
39643
  },
39589
39644
  null,
39590
39645
  2
39591
- )
39646
+ ),
39647
+ { mode: 384 }
39592
39648
  );
39649
+ (0, import_node_fs6.chmodSync)(serverStatePath, 384);
39593
39650
  }
39594
39651
  removeRuntimeState() {
39595
39652
  const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
@@ -41514,13 +41571,13 @@ Added (new in template):`);
41514
41571
  var import_node_fs12 = __toESM(require("node:fs"), 1);
41515
41572
  var import_node_path10 = __toESM(require("node:path"), 1);
41516
41573
  function readStdin() {
41517
- return new Promise((resolve3, reject) => {
41574
+ return new Promise((resolve4, reject) => {
41518
41575
  let data = "";
41519
41576
  process.stdin.setEncoding("utf8");
41520
41577
  process.stdin.on("data", (c) => {
41521
41578
  data += c;
41522
41579
  });
41523
- process.stdin.on("end", () => resolve3(data));
41580
+ process.stdin.on("end", () => resolve4(data));
41524
41581
  process.stdin.on("error", reject);
41525
41582
  });
41526
41583
  }
@@ -41828,6 +41885,22 @@ var MandateStore = class {
41828
41885
  this.write(data);
41829
41886
  return mandate2;
41830
41887
  }
41888
+ /**
41889
+ * Track a permission only if no record at its address exists yet, returning the
41890
+ * record either way. Used by `mandate attach` for addresses that were never
41891
+ * deployed from this project — e.g. a shared permission singleton the operator
41892
+ * attaches by address. Without a store record, `recordAttachment` is a no-op and
41893
+ * the permission stays invisible to `mandate prepare`/`sign`. Unlike `add`, this
41894
+ * never overwrites an existing record (so richer deploy-time metadata such as
41895
+ * `sourcePath` is preserved). For attach-only records, `txHash`/`deployedAt`
41896
+ * reflect the registration/first-tracked moment, not an on-chain deployment.
41897
+ */
41898
+ ensureTracked(mandate2) {
41899
+ const existing = this.read().mandates.find(
41900
+ (m) => m.address.toLowerCase() === mandate2.address.toLowerCase()
41901
+ );
41902
+ return existing ?? this.add(mandate2);
41903
+ }
41831
41904
  /** Update mutable metadata fields on a tracked mandate (name, sourcePath, artifactPath). */
41832
41905
  update(addressOrName, patch) {
41833
41906
  const data = this.read();
@@ -42474,6 +42547,14 @@ function publicClientFor(project) {
42474
42547
  transport: http(getRpcUrl(project.chainId))
42475
42548
  }).extend(publicActions);
42476
42549
  }
42550
+ function knownTemplateLabel(chainId, address) {
42551
+ try {
42552
+ const known = getSailDeployment(chainId).knownTemplates ?? [];
42553
+ return known.find((t) => t.address.toLowerCase() === address.toLowerCase())?.label;
42554
+ } catch {
42555
+ return void 0;
42556
+ }
42557
+ }
42477
42558
  async function mandateDeploy(options) {
42478
42559
  const project = requireProject();
42479
42560
  const channel = await createSigningChannel(process.cwd());
@@ -42943,7 +43024,14 @@ async function runAttach(project, channel, options) {
42943
43024
  label,
42944
43025
  json
42945
43026
  );
42946
- if (tracked) store.recordAttachment(mandateAddress, { sma, txHash });
43027
+ store.ensureTracked({
43028
+ name: label,
43029
+ address: mandateAddress,
43030
+ txHash,
43031
+ chainId: project.chainId,
43032
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
43033
+ });
43034
+ store.recordAttachment(mandateAddress, { sma, txHash });
42947
43035
  emit(json, () => {
42948
43036
  }, {
42949
43037
  status: "ok",
@@ -42957,7 +43045,14 @@ async function runAttachBatch(project, channel, options, sma, store) {
42957
43045
  announceSigningUrl(json);
42958
43046
  const txHash = await attachBatchToSma(project, channel, publicClient, sma, permissions, json);
42959
43047
  for (const permission of permissions) {
42960
- if (store.find(permission)) store.recordAttachment(permission, { sma, txHash });
43048
+ store.ensureTracked({
43049
+ name: knownTemplateLabel(project.chainId, permission) ?? permission,
43050
+ address: permission,
43051
+ txHash,
43052
+ chainId: project.chainId,
43053
+ deployedAt: (/* @__PURE__ */ new Date()).toISOString()
43054
+ });
43055
+ store.recordAttachment(permission, { sma, txHash });
42961
43056
  }
42962
43057
  emit(json, () => {
42963
43058
  }, {
@@ -43553,14 +43648,37 @@ async function trackedPermissionsFor(account2, chainId, kernel) {
43553
43648
  };
43554
43649
  });
43555
43650
  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
- }
43651
+ if (onChain === null) return local;
43652
+ return mergeOnChainPermissions(local, onChain, knownTemplateLabeler(chainId));
43653
+ }
43654
+ function mergeOnChainPermissions(local, onChain, labelFor = () => void 0) {
43655
+ for (const p of local) {
43656
+ if (p.registeredOnSma && !onChain.has(p.address.toLowerCase())) {
43657
+ p.revokedOnChain = true;
43658
+ }
43659
+ }
43660
+ const localAddrs = new Set(local.map((p) => p.address.toLowerCase()));
43661
+ const extra = [];
43662
+ for (const addr of onChain) {
43663
+ if (!localAddrs.has(addr)) {
43664
+ extra.push({
43665
+ address: addr,
43666
+ label: labelFor(addr) ?? `permission ${addr.slice(0, 10)}\u2026`,
43667
+ registeredOnSma: true
43668
+ });
43561
43669
  }
43562
43670
  }
43563
- return local;
43671
+ return [...local, ...extra];
43672
+ }
43673
+ function knownTemplateLabeler(chainId) {
43674
+ const map = /* @__PURE__ */ new Map();
43675
+ try {
43676
+ for (const t of getSailDeployment(chainId).knownTemplates ?? []) {
43677
+ map.set(t.address.toLowerCase(), t.label);
43678
+ }
43679
+ } catch {
43680
+ }
43681
+ return (addr) => map.get(addr.toLowerCase());
43564
43682
  }
43565
43683
  function printNoPermissionsGuidance() {
43566
43684
  console.log(
@@ -43682,8 +43800,451 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
43682
43800
  \u2713 Saved to .sail/mandate.json \u2014 agent is ready to run.`);
43683
43801
  }
43684
43802
 
43685
- // src/commands/mandate-simulate.ts
43803
+ // src/commands/mandate-configure.ts
43686
43804
  var import_node_fs15 = require("node:fs");
43805
+ var import_node_path13 = require("node:path");
43806
+
43807
+ // ../sdk/dist/templates/boundedSwap.js
43808
+ init_esm2();
43809
+ var ABI = [
43810
+ { name: "routers", type: "address[]" },
43811
+ { name: "tokensIn", type: "address[]" },
43812
+ { name: "tokensOut", type: "address[]" },
43813
+ { name: "maxAmountPerTx", type: "uint256" },
43814
+ { name: "maxSlippageBps", type: "uint256" },
43815
+ { name: "priceOracle", type: "address" },
43816
+ { name: "maxPriceAgeSec", type: "uint256" }
43817
+ ];
43818
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
43819
+ var boundedSwapTemplate = {
43820
+ name: "SharedBoundedSwapPermission",
43821
+ address: "0x0000000000000000000000000000000000000000",
43822
+ encoder: {
43823
+ encode(params) {
43824
+ return encodeAbiParameters(ABI, [
43825
+ params.routers,
43826
+ params.tokensIn,
43827
+ params.tokensOut,
43828
+ params.maxAmountPerTx,
43829
+ BigInt(params.maxSlippageBps),
43830
+ params.priceOracle,
43831
+ BigInt(params.maxPriceAgeSec)
43832
+ ]);
43833
+ },
43834
+ decode(data) {
43835
+ const decoded = decodeAbiParameters(ABI, data);
43836
+ return {
43837
+ routers: [...decoded[0]],
43838
+ tokensIn: [...decoded[1]],
43839
+ tokensOut: [...decoded[2]],
43840
+ maxAmountPerTx: decoded[3],
43841
+ maxSlippageBps: Number(decoded[4]),
43842
+ priceOracle: decoded[5],
43843
+ maxPriceAgeSec: Number(decoded[6])
43844
+ };
43845
+ }
43846
+ },
43847
+ explainer: {
43848
+ explain(params) {
43849
+ const warnings = [];
43850
+ if (params.maxSlippageBps > 100) {
43851
+ warnings.push(`High slippage tolerance: ${params.maxSlippageBps / 100}%`);
43852
+ }
43853
+ if (params.priceOracle === ZERO_ADDRESS) {
43854
+ warnings.push("No price oracle configured \u2014 swaps are not checked against a reference price");
43855
+ }
43856
+ return {
43857
+ templateName: "SharedBoundedSwapPermission",
43858
+ humanReadable: [
43859
+ `Maximum input per swap: ${params.maxAmountPerTx.toString()} (input-token base units)`,
43860
+ `Maximum slippage: ${params.maxSlippageBps / 100}%`,
43861
+ `Allowed routers: ${params.routers.join(", ")}`,
43862
+ `Allowed input tokens: ${params.tokensIn.join(", ")}`,
43863
+ `Allowed output tokens: ${params.tokensOut.join(", ")}`,
43864
+ params.priceOracle === ZERO_ADDRESS ? "Price oracle: none" : `Price oracle: ${params.priceOracle} (max age ${params.maxPriceAgeSec}s)`
43865
+ ],
43866
+ warnings
43867
+ };
43868
+ }
43869
+ }
43870
+ };
43871
+
43872
+ // src/commands/mandate-configure.ts
43873
+ init_esm2();
43874
+ var TEMPLATE_REGISTRY = {
43875
+ SwapPermission: {
43876
+ label: "Bounded Swap (Uniswap V3)",
43877
+ bigintFields: ["maxAmountPerTx"],
43878
+ encode: (raw) => boundedSwapTemplate.encoder.encode(coerceSwapParams(raw)),
43879
+ decode: (blob) => {
43880
+ const p = boundedSwapTemplate.encoder.decode(blob);
43881
+ return {
43882
+ routers: p.routers,
43883
+ tokensIn: p.tokensIn,
43884
+ tokensOut: p.tokensOut,
43885
+ maxAmountPerTx: p.maxAmountPerTx.toString(),
43886
+ maxSlippageBps: p.maxSlippageBps,
43887
+ priceOracle: p.priceOracle,
43888
+ maxPriceAgeSec: p.maxPriceAgeSec
43889
+ };
43890
+ }
43891
+ }
43892
+ };
43893
+ function asAddressArray(v, field) {
43894
+ if (!Array.isArray(v)) throw new Error(`${field}: expected an array of addresses`);
43895
+ return v.map((x, i) => {
43896
+ if (typeof x !== "string" || !isAddress(x, { strict: false })) {
43897
+ throw new Error(`${field}[${i}]: invalid address "${String(x)}"`);
43898
+ }
43899
+ return getAddress(x);
43900
+ });
43901
+ }
43902
+ function asAddress(v, field) {
43903
+ if (typeof v !== "string" || !isAddress(v, { strict: false })) {
43904
+ throw new Error(`${field}: invalid address "${String(v)}"`);
43905
+ }
43906
+ return getAddress(v);
43907
+ }
43908
+ function asUint(v, field) {
43909
+ if (v === void 0 || v === null) throw new Error(`${field}: missing`);
43910
+ try {
43911
+ return BigInt(typeof v === "string" || typeof v === "number" ? v : String(v));
43912
+ } catch {
43913
+ throw new Error(`${field}: expected an integer, got "${String(v)}"`);
43914
+ }
43915
+ }
43916
+ function asInt(v, field) {
43917
+ const n = asUint(v, field);
43918
+ return Number(n);
43919
+ }
43920
+ function coerceSwapParams(raw) {
43921
+ return {
43922
+ routers: asAddressArray(raw.routers, "routers"),
43923
+ tokensIn: asAddressArray(raw.tokensIn, "tokensIn"),
43924
+ tokensOut: asAddressArray(raw.tokensOut, "tokensOut"),
43925
+ maxAmountPerTx: asUint(raw.maxAmountPerTx, "maxAmountPerTx"),
43926
+ maxSlippageBps: asInt(raw.maxSlippageBps, "maxSlippageBps"),
43927
+ priceOracle: asAddress(raw.priceOracle, "priceOracle"),
43928
+ maxPriceAgeSec: asInt(raw.maxPriceAgeSec, "maxPriceAgeSec")
43929
+ };
43930
+ }
43931
+ function requireProject2() {
43932
+ if (!ProjectContext.exists()) {
43933
+ console.log('No Sailor project found. Run "sailor init" first.');
43934
+ process.exit(1);
43935
+ }
43936
+ return new ProjectContext();
43937
+ }
43938
+ function fail2(err, json = false) {
43939
+ const msg = err instanceof Error ? err.message : String(err);
43940
+ emit(json, () => console.error(`
43941
+ Mandate configure failed: ${msg}`), {
43942
+ status: "error",
43943
+ error: msg
43944
+ });
43945
+ process.exit(1);
43946
+ }
43947
+ function publicClientFor2(project) {
43948
+ return createPublicClient({
43949
+ chain: getChainById(project.chainId),
43950
+ transport: http(getRpcUrl(project.chainId))
43951
+ }).extend(publicActions);
43952
+ }
43953
+ function announceSigningUrl2(json) {
43954
+ const url = signingPageUrl(projectPort(process.cwd()));
43955
+ if (json) {
43956
+ process.stdout.write(`${JSON.stringify({ status: "waiting_for_signature", url })}
43957
+ `);
43958
+ } else {
43959
+ console.log(`
43960
+ \u2192 Open the Sailor dashboard to approve signing requests:
43961
+ ${url}
43962
+ `);
43963
+ }
43964
+ }
43965
+ function resolveBlob(options) {
43966
+ const hasParams = options.params !== void 0;
43967
+ const hasArgsFile = options.argsFile !== void 0;
43968
+ if (hasParams && hasArgsFile) {
43969
+ throw new Error("Provide EITHER --params <hex> OR --args-file <path>, not both.");
43970
+ }
43971
+ if (!hasParams && !hasArgsFile) {
43972
+ throw new Error(
43973
+ "Provide the config blob as --params <0x-hex>, or as --args-file <path> paired with --template <Name>."
43974
+ );
43975
+ }
43976
+ if (hasParams) {
43977
+ const raw2 = options.params.trim();
43978
+ if (!isHex(raw2) || raw2.length <= 2) {
43979
+ throw new Error(`--params must be 0x-prefixed hex; got "${options.params}".`);
43980
+ }
43981
+ return { blob: raw2, via: "params" };
43982
+ }
43983
+ const templateName = options.template;
43984
+ if (!templateName) {
43985
+ throw new Error("--args-file requires --template <Name> (e.g. SwapPermission).");
43986
+ }
43987
+ const entry = TEMPLATE_REGISTRY[templateName];
43988
+ if (!entry) {
43989
+ throw new Error(
43990
+ `Unknown --template "${templateName}". Known: ${Object.keys(TEMPLATE_REGISTRY).join(", ")}. Pass a pre-encoded blob with --params for any other template.`
43991
+ );
43992
+ }
43993
+ const argsFilePath = (0, import_node_path13.resolve)(options.argsFile);
43994
+ let raw;
43995
+ try {
43996
+ raw = (0, import_node_fs15.readFileSync)(argsFilePath, "utf8").trim();
43997
+ } catch {
43998
+ throw new Error(`Cannot read --args-file: ${argsFilePath}`);
43999
+ }
44000
+ let parsed;
44001
+ try {
44002
+ parsed = JSON.parse(raw);
44003
+ } catch (err) {
44004
+ throw new Error(`--args-file is not valid JSON: ${err.message}`);
44005
+ }
44006
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
44007
+ throw new Error("--args-file must contain a JSON object of template params.");
44008
+ }
44009
+ const blob = entry.encode(parsed);
44010
+ return { blob, via: `template:${templateName}` };
44011
+ }
44012
+ async function mandateConfigure(options) {
44013
+ const project = requireProject2();
44014
+ const channel = await createSigningChannel(process.cwd());
44015
+ try {
44016
+ await channel.start();
44017
+ await runConfigure(project, channel, options);
44018
+ } catch (err) {
44019
+ fail2(err, options.json);
44020
+ } finally {
44021
+ channel.stop();
44022
+ }
44023
+ }
44024
+ async function runConfigure(project, channel, options) {
44025
+ const json = !!options.json;
44026
+ const say = (fn) => {
44027
+ if (!json) fn();
44028
+ };
44029
+ if (!isAddress(options.sma, { strict: false })) {
44030
+ throw new Error(`Invalid --sma address: ${options.sma}`);
44031
+ }
44032
+ if (!isAddress(options.address, { strict: false })) {
44033
+ throw new Error(
44034
+ `--address must be a deployed shared-template singleton address: ${options.address}`
44035
+ );
44036
+ }
44037
+ const sma = getAddress(options.sma);
44038
+ const singleton = getAddress(options.address);
44039
+ const { blob, via } = resolveBlob(options);
44040
+ const publicClient = publicClientFor2(project);
44041
+ const registered = await publicClient.readContract({
44042
+ address: project.contracts.kernel,
44043
+ abi: SailKernelAbi,
44044
+ functionName: "registered",
44045
+ args: [sma]
44046
+ });
44047
+ if (!registered) {
44048
+ throw new Error(`SMA ${sma} is not registered with SailKernel; cannot configure a permission for it.`);
44049
+ }
44050
+ const kernelConfig = await publicClient.readContract({
44051
+ address: project.contracts.kernel,
44052
+ abi: SailKernelAbi,
44053
+ functionName: "configs",
44054
+ args: [sma]
44055
+ });
44056
+ const permissionSigner = kernelConfig[0];
44057
+ if (!permissionSigner || permissionSigner === getAddress("0x0000000000000000000000000000000000000000")) {
44058
+ throw new Error(`SMA ${sma} has no on-chain permissionSigner; cannot configure.`);
44059
+ }
44060
+ const alreadyConfigured = await publicClient.readContract({
44061
+ address: singleton,
44062
+ abi: ConfigurablePermissionAbi,
44063
+ functionName: "isConfigured",
44064
+ args: [sma]
44065
+ });
44066
+ if (alreadyConfigured && !options.force) {
44067
+ say(
44068
+ () => console.log(
44069
+ `
44070
+ \u2713 ${singleton} is already configured for ${sma} (isConfigured == true).
44071
+ Re-run with --force to overwrite the current bounds.`
44072
+ )
44073
+ );
44074
+ emit(
44075
+ json,
44076
+ () => {
44077
+ },
44078
+ { status: "ok", alreadyConfigured: true, singleton, sma, chainId: project.chainId }
44079
+ );
44080
+ return;
44081
+ }
44082
+ const nonce = await publicClient.readContract({
44083
+ address: singleton,
44084
+ abi: ConfigurablePermissionAbi,
44085
+ functionName: "configNonces",
44086
+ args: [sma]
44087
+ });
44088
+ say(() => {
44089
+ console.log(`
44090
+ configure ${singleton}`);
44091
+ console.log(` SMA: ${sma}`);
44092
+ console.log(` permissionSigner: ${permissionSigner} (must sign in the browser \u2014 owner tx)`);
44093
+ console.log(` current nonce: ${nonce}`);
44094
+ console.log(` already set: ${alreadyConfigured ? "yes (--force will overwrite)" : "no"}`);
44095
+ console.log(` config blob: ${blob} (via ${via}, ${Math.floor((blob.length - 2) / 2)} bytes)`);
44096
+ });
44097
+ const configureDirectData = encodeFunctionData({
44098
+ abi: ConfigurablePermissionAbi,
44099
+ functionName: "configureDirect",
44100
+ args: [sma, blob]
44101
+ });
44102
+ say(() => console.log(`
44103
+ Pre-flight (eth_call of configureDirect, no gas)\u2026`));
44104
+ try {
44105
+ await publicClient.simulateContract({
44106
+ address: singleton,
44107
+ abi: ConfigurablePermissionAbi,
44108
+ functionName: "configureDirect",
44109
+ args: [sma, blob],
44110
+ account: permissionSigner
44111
+ });
44112
+ } catch (err) {
44113
+ const ce = getContractError(err, {
44114
+ abi: ConfigurablePermissionAbi,
44115
+ address: singleton,
44116
+ args: [sma, blob],
44117
+ functionName: "configureDirect"
44118
+ });
44119
+ const reason = ce?.message ?? err?.message ?? String(err);
44120
+ throw new Error(
44121
+ `Pre-flight reverted \u2014 the config blob is invalid for ${singleton} on ${sma}.
44122
+ Revert: ${reason}
44123
+ 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.`
44124
+ );
44125
+ }
44126
+ say(() => console.log("\u2713 pre-flight passed (configureDirect would succeed)."));
44127
+ let decoded;
44128
+ if (options.template && TEMPLATE_REGISTRY[options.template]) {
44129
+ try {
44130
+ decoded = TEMPLATE_REGISTRY[options.template].decode(blob);
44131
+ say(() => {
44132
+ console.log("\nDecoded bounds (what the singleton will store):");
44133
+ for (const [k, v] of Object.entries(decoded)) {
44134
+ const display = Array.isArray(v) ? v.join(", ") : String(v);
44135
+ console.log(` ${k}: ${display}`);
44136
+ }
44137
+ });
44138
+ } catch {
44139
+ }
44140
+ }
44141
+ if (options.simulateOnly) {
44142
+ emit(
44143
+ json,
44144
+ () => {
44145
+ console.log("\n--simulate-only: stopping after pre-flight (no signing, no gas).");
44146
+ },
44147
+ {
44148
+ status: "ok",
44149
+ simulateOnly: true,
44150
+ singleton,
44151
+ sma,
44152
+ permissionSigner,
44153
+ nonce: nonce.toString(),
44154
+ alreadyConfigured,
44155
+ blob,
44156
+ blobBytes: Math.floor((blob.length - 2) / 2),
44157
+ decoded,
44158
+ chainId: project.chainId
44159
+ }
44160
+ );
44161
+ return;
44162
+ }
44163
+ const label = options.label ?? (options.template ?? "shared-template");
44164
+ announceSigningUrl2(json);
44165
+ say(
44166
+ () => console.log(
44167
+ `Pushing configure request \u2014 the mandate signer (${permissionSigner}) must approve in the browser\u2026`
44168
+ )
44169
+ );
44170
+ const response = await channel.requestSignature({
44171
+ type: "transaction",
44172
+ kind: "arbitrary-tx",
44173
+ title: `Configure "${label}"`,
44174
+ description: `Write per-account bounds on the shared permission ${singleton}.
44175
+ You (the mandate signer) send the transaction \u2014 configureDirect requires msg.sender to be the permissionSigner.`,
44176
+ chainId: project.chainId,
44177
+ to: singleton,
44178
+ data: configureDirectData,
44179
+ details: [
44180
+ { label: "SMA", value: sma },
44181
+ { label: "Singleton", value: singleton },
44182
+ { label: "Template", value: options.template ?? "(raw blob)" },
44183
+ { label: "Mandate signer", value: permissionSigner },
44184
+ { label: "Config blob (bytes)", value: String(Math.floor((blob.length - 2) / 2)) },
44185
+ ...decoded ? Object.entries(decoded).map(([k, v]) => ({
44186
+ label: k,
44187
+ value: Array.isArray(v) ? v.join(", ") : String(v)
44188
+ })) : []
44189
+ ]
44190
+ });
44191
+ if (response.status === "rejected") {
44192
+ throw new Error(`User rejected configure: ${response.reason ?? "no reason given"}`);
44193
+ }
44194
+ if (response.status !== "signed") {
44195
+ throw new Error(`Expected a signed transaction, got: ${response.status}`);
44196
+ }
44197
+ say(() => console.log("Waiting for confirmation\u2026"));
44198
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: response.txHash });
44199
+ if (receipt.status !== "success") {
44200
+ throw new Error(`configureDirect transaction failed (tx ${response.txHash})`);
44201
+ }
44202
+ const configuredNow = await publicClient.readContract({
44203
+ address: singleton,
44204
+ abi: ConfigurablePermissionAbi,
44205
+ functionName: "isConfigured",
44206
+ args: [sma]
44207
+ });
44208
+ if (!configuredNow) {
44209
+ throw new Error(
44210
+ `Tx ${response.txHash} mined, but isConfigured(${sma}) is still false. Verify on-chain.`
44211
+ );
44212
+ }
44213
+ say(() => console.log("\u2713", `Configured ${singleton} for ${sma} (isConfigured == true).`));
44214
+ appendActivity({
44215
+ ts: nowIso(),
44216
+ actor: "owner",
44217
+ type: "permission_configured",
44218
+ permission: singleton,
44219
+ name: label,
44220
+ sma,
44221
+ txHash: response.txHash,
44222
+ chainId: project.chainId
44223
+ });
44224
+ emit(
44225
+ json,
44226
+ () => {
44227
+ console.log(
44228
+ `
44229
+ Next: prove it accepts/rejects the right calls before dispatching:
44230
+ sailor mandate simulate --address ${singleton} --sma ${sma} --calls ./probe.json`
44231
+ );
44232
+ },
44233
+ {
44234
+ status: "ok",
44235
+ singleton,
44236
+ sma,
44237
+ template: options.template,
44238
+ permissionSigner,
44239
+ txHash: response.txHash,
44240
+ blob,
44241
+ chainId: project.chainId
44242
+ }
44243
+ );
44244
+ }
44245
+
44246
+ // src/commands/mandate-simulate.ts
44247
+ var import_node_fs16 = require("node:fs");
43687
44248
  init_esm2();
43688
44249
  function parseExpect(raw, where) {
43689
44250
  if (raw === void 0) return void 0;
@@ -43699,7 +44260,7 @@ function resolveSampleCalls(options) {
43699
44260
  if (options.calls) {
43700
44261
  let raw;
43701
44262
  try {
43702
- raw = JSON.parse((0, import_node_fs15.readFileSync)(options.calls, "utf8"));
44263
+ raw = JSON.parse((0, import_node_fs16.readFileSync)(options.calls, "utf8"));
43703
44264
  } catch (err) {
43704
44265
  throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
43705
44266
  }
@@ -43915,7 +44476,7 @@ async function mandateSimulate(options) {
43915
44476
  }
43916
44477
 
43917
44478
  // src/commands/rotate-signer.ts
43918
- var import_node_fs16 = require("node:fs");
44479
+ var import_node_fs17 = require("node:fs");
43919
44480
  init_esm2();
43920
44481
  var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
43921
44482
  async function rotateSigner(options) {
@@ -44301,7 +44862,7 @@ ensure the agent that signs dispatches holds this key.`
44301
44862
  const keystore = await keyring.exportKeystore(password);
44302
44863
  writeJsonFile(target, keystore, 384);
44303
44864
  const perManagerPath = managerKeystorePath(keyring.address);
44304
- (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44865
+ (0, import_node_fs17.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44305
44866
  writeJsonFile(perManagerPath, keystore, 384);
44306
44867
  say(
44307
44868
  () => console.log(
@@ -44313,7 +44874,7 @@ ensure the agent that signs dispatches holds this key.`
44313
44874
  function promoteManagerKeystore(newManager, say) {
44314
44875
  const stored = readJsonFile(managerKeystorePath(newManager));
44315
44876
  if (!stored) return;
44316
- (0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44877
+ (0, import_node_fs17.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
44317
44878
  const activeTarget = keyPath("manager");
44318
44879
  const displaced = readJsonFile(activeTarget);
44319
44880
  if (displaced?.address) {
@@ -44365,7 +44926,7 @@ function writePending(pending) {
44365
44926
  }
44366
44927
  function clearPending() {
44367
44928
  try {
44368
- (0, import_node_fs16.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
44929
+ (0, import_node_fs17.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
44369
44930
  } catch {
44370
44931
  }
44371
44932
  }
@@ -44464,12 +45025,12 @@ function ownerShow(options) {
44464
45025
  }
44465
45026
 
44466
45027
  // src/commands/run.ts
44467
- var import_node_fs17 = __toESM(require("node:fs"), 1);
44468
- var import_node_path13 = __toESM(require("node:path"), 1);
45028
+ var import_node_fs18 = __toESM(require("node:fs"), 1);
45029
+ var import_node_path14 = __toESM(require("node:path"), 1);
44469
45030
  var import_node_url = require("node:url");
44470
45031
  init_esm2();
44471
45032
  var DEFAULT_INTERVAL_SEC = 60;
44472
- var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
45033
+ var sleep2 = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
44473
45034
  var ERC20_READ_ABI = [
44474
45035
  {
44475
45036
  type: "function",
@@ -44499,7 +45060,7 @@ var ERC20_READ_ABI = [
44499
45060
  function loadAgentData(filePath) {
44500
45061
  if (!filePath) return {};
44501
45062
  try {
44502
- const parsed = JSON.parse(import_node_fs17.default.readFileSync(filePath, "utf-8"));
45063
+ const parsed = JSON.parse(import_node_fs18.default.readFileSync(filePath, "utf-8"));
44503
45064
  return parsed && typeof parsed === "object" ? parsed : {};
44504
45065
  } catch {
44505
45066
  return {};
@@ -44508,8 +45069,8 @@ function loadAgentData(filePath) {
44508
45069
  async function loadAgent() {
44509
45070
  const candidates = ["src/agent.ts", "src/agent.js", "dist/agent.js", "dist/src/agent.js"];
44510
45071
  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;
45072
+ const abs = import_node_path14.default.join(process.cwd(), rel);
45073
+ if (!import_node_fs18.default.existsSync(abs)) continue;
44513
45074
  let mod2;
44514
45075
  if (abs.endsWith(".ts")) {
44515
45076
  const { tsImport } = await import("tsx/esm/api");
@@ -44804,7 +45365,7 @@ Configure the chain in the SDK chain registry or set KERNEL_ADDRESS in .sail/.en
44804
45365
  if (isConjunctive && result.txHash) {
44805
45366
  try {
44806
45367
  await publicClient.waitForTransactionReceipt({ hash: result.txHash, timeout: 3e4 });
44807
- await new Promise((resolve3) => setTimeout(resolve3, 500));
45368
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
44808
45369
  } catch {
44809
45370
  }
44810
45371
  }
@@ -44985,9 +45546,9 @@ async function scan(options) {
44985
45546
 
44986
45547
  // src/commands/service.ts
44987
45548
  var import_node_child_process2 = require("node:child_process");
44988
- var import_node_fs18 = __toESM(require("node:fs"), 1);
45549
+ var import_node_fs19 = __toESM(require("node:fs"), 1);
44989
45550
  var import_node_os = __toESM(require("node:os"), 1);
44990
- var import_node_path14 = __toESM(require("node:path"), 1);
45551
+ var import_node_path15 = __toESM(require("node:path"), 1);
44991
45552
  function sanitizeName(raw) {
44992
45553
  const s = raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
44993
45554
  return s || "agent";
@@ -45106,21 +45667,21 @@ function buildWindowsTaskXml(cfg) {
45106
45667
  `;
45107
45668
  }
45108
45669
  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];
45670
+ const rel = import_node_path15.default.relative(home, import_node_path15.default.resolve(projectDir));
45671
+ if (rel.startsWith("..") || import_node_path15.default.isAbsolute(rel)) return false;
45672
+ const top = rel.split(import_node_path15.default.sep)[0];
45112
45673
  return ["Desktop", "Documents", "Downloads"].includes(top ?? "");
45113
45674
  }
45114
45675
  function resolveCliEntry() {
45115
45676
  try {
45116
- return import_node_fs18.default.realpathSync(process.argv[1]);
45677
+ return import_node_fs19.default.realpathSync(process.argv[1]);
45117
45678
  } catch {
45118
- return import_node_path14.default.resolve(process.argv[1]);
45679
+ return import_node_path15.default.resolve(process.argv[1]);
45119
45680
  }
45120
45681
  }
45121
45682
  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"))) {
45683
+ const dir = import_node_path15.default.resolve(explicit ?? process.cwd());
45684
+ if (!import_node_fs19.default.existsSync(import_node_path15.default.join(dir, ".sail"))) {
45124
45685
  throw new Error(
45125
45686
  `No .sail/ found in ${dir}.
45126
45687
  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 +45693,7 @@ function passphraseReadiness(projectDir) {
45132
45693
  const account2 = (() => {
45133
45694
  try {
45134
45695
  return JSON.parse(
45135
- import_node_fs18.default.readFileSync(import_node_path14.default.join(projectDir, ".sail", "account.json"), "utf-8")
45696
+ import_node_fs19.default.readFileSync(import_node_path15.default.join(projectDir, ".sail", "account.json"), "utf-8")
45136
45697
  );
45137
45698
  } catch {
45138
45699
  return null;
@@ -45148,28 +45709,28 @@ function passphraseReadiness(projectDir) {
45148
45709
  } finally {
45149
45710
  process.chdir(prevCwd);
45150
45711
  }
45151
- const env = parseEnvFile(import_node_path14.default.join(projectDir, ".sail", ".env.local"));
45712
+ const env = parseEnvFile(import_node_path15.default.join(projectDir, ".sail", ".env.local"));
45152
45713
  const passphraseInEnvFile = Boolean(env.SAIL_PASSPHRASE);
45153
45714
  return { keystorePresent, passphraseInEnvFile, ready: keystorePresent && passphraseInEnvFile };
45154
45715
  }
45155
45716
  function buildConfig(opts) {
45156
45717
  const projectDir = resolveProjectDir(opts.project);
45157
45718
  return {
45158
- projectName: sanitizeName(import_node_path14.default.basename(projectDir)),
45719
+ projectName: sanitizeName(import_node_path15.default.basename(projectDir)),
45159
45720
  projectDir,
45160
45721
  nodePath: process.execPath,
45161
45722
  cliEntry: resolveCliEntry(),
45162
- logPath: import_node_path14.default.join(projectDir, ".sail", "agent.log"),
45723
+ logPath: import_node_path15.default.join(projectDir, ".sail", "agent.log"),
45163
45724
  interval: opts.interval != null ? Number(opts.interval) : void 0,
45164
45725
  chain: opts.chain != null ? Number(opts.chain) : void 0,
45165
45726
  restartSec: 30
45166
45727
  };
45167
45728
  }
45168
45729
  function plistPath(projectName) {
45169
- return import_node_path14.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
45730
+ return import_node_path15.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
45170
45731
  }
45171
45732
  function systemdPath(projectName) {
45172
- return import_node_path14.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
45733
+ return import_node_path15.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
45173
45734
  }
45174
45735
  async function serviceInstall(opts = {}) {
45175
45736
  const cfg = buildConfig(opts);
@@ -45193,12 +45754,12 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45193
45754
  console.warn(`\u26A0 ${msg}
45194
45755
  Proceeding because --force was given.`);
45195
45756
  }
45196
- import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(cfg.logPath), { recursive: true });
45757
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(cfg.logPath), { recursive: true });
45197
45758
  if (platform === "darwin") {
45198
45759
  const plist = buildLaunchdPlist(cfg);
45199
45760
  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);
45761
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(dest), { recursive: true });
45762
+ import_node_fs19.default.writeFileSync(dest, plist);
45202
45763
  const uid2 = process.getuid?.() ?? 0;
45203
45764
  try {
45204
45765
  try {
@@ -45217,8 +45778,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45217
45778
  if (platform === "linux") {
45218
45779
  const unit = buildSystemdUnit(cfg);
45219
45780
  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);
45781
+ import_node_fs19.default.mkdirSync(import_node_path15.default.dirname(dest), { recursive: true });
45782
+ import_node_fs19.default.writeFileSync(dest, unit);
45222
45783
  try {
45223
45784
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
45224
45785
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "enable", "--now", systemdUnitName(cfg.projectName)], {
@@ -45232,8 +45793,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45232
45793
  }
45233
45794
  if (platform === "win32") {
45234
45795
  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");
45796
+ const dest = import_node_path15.default.join(import_node_os.default.tmpdir(), `${windowsTaskName(cfg.projectName)}.xml`);
45797
+ import_node_fs19.default.writeFileSync(dest, xml, "utf-8");
45237
45798
  try {
45238
45799
  (0, import_node_child_process2.execFileSync)(
45239
45800
  "schtasks",
@@ -45244,7 +45805,7 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
45244
45805
  throw new Error(`schtasks /Create failed: ${err.message}`);
45245
45806
  } finally {
45246
45807
  try {
45247
- import_node_fs18.default.rmSync(dest, { force: true });
45808
+ import_node_fs19.default.rmSync(dest, { force: true });
45248
45809
  } catch {
45249
45810
  }
45250
45811
  }
@@ -45270,14 +45831,14 @@ function okInstall(cfg, unit) {
45270
45831
  );
45271
45832
  }
45272
45833
  async function serviceStatus(opts = {}) {
45273
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45834
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45274
45835
  const platform = process.platform;
45275
45836
  let installed = false;
45276
45837
  let running = false;
45277
45838
  let detail = "";
45278
45839
  try {
45279
45840
  if (platform === "darwin") {
45280
- installed = import_node_fs18.default.existsSync(plistPath(projectName));
45841
+ installed = import_node_fs19.default.existsSync(plistPath(projectName));
45281
45842
  const uid2 = process.getuid?.() ?? 0;
45282
45843
  try {
45283
45844
  detail = (0, import_node_child_process2.execFileSync)("launchctl", ["print", `gui/${uid2}/${launchdLabel(projectName)}`], {
@@ -45289,7 +45850,7 @@ async function serviceStatus(opts = {}) {
45289
45850
  } catch {
45290
45851
  }
45291
45852
  } else if (platform === "linux") {
45292
- installed = import_node_fs18.default.existsSync(systemdPath(projectName));
45853
+ installed = import_node_fs19.default.existsSync(systemdPath(projectName));
45293
45854
  try {
45294
45855
  detail = (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "is-active", systemdUnitName(projectName)], {
45295
45856
  encoding: "utf-8",
@@ -45325,7 +45886,7 @@ async function serviceStatus(opts = {}) {
45325
45886
  );
45326
45887
  }
45327
45888
  async function serviceStop(opts = {}) {
45328
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45889
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45329
45890
  const platform = process.platform;
45330
45891
  if (platform === "darwin") {
45331
45892
  const uid2 = process.getuid?.() ?? 0;
@@ -45344,7 +45905,7 @@ async function serviceStop(opts = {}) {
45344
45905
  });
45345
45906
  }
45346
45907
  async function serviceUninstall(opts = {}) {
45347
- const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
45908
+ const projectName = sanitizeName(import_node_path15.default.basename(resolveProjectDir(opts.project)));
45348
45909
  const platform = process.platform;
45349
45910
  if (platform === "darwin") {
45350
45911
  const uid2 = process.getuid?.() ?? 0;
@@ -45354,7 +45915,7 @@ async function serviceUninstall(opts = {}) {
45354
45915
  });
45355
45916
  } catch {
45356
45917
  }
45357
- import_node_fs18.default.rmSync(plistPath(projectName), { force: true });
45918
+ import_node_fs19.default.rmSync(plistPath(projectName), { force: true });
45358
45919
  } else if (platform === "linux") {
45359
45920
  try {
45360
45921
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "disable", "--now", systemdUnitName(projectName)], {
@@ -45362,7 +45923,7 @@ async function serviceUninstall(opts = {}) {
45362
45923
  });
45363
45924
  } catch {
45364
45925
  }
45365
- import_node_fs18.default.rmSync(systemdPath(projectName), { force: true });
45926
+ import_node_fs19.default.rmSync(systemdPath(projectName), { force: true });
45366
45927
  try {
45367
45928
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "ignore" });
45368
45929
  } catch {
@@ -45380,21 +45941,21 @@ async function serviceUninstall(opts = {}) {
45380
45941
  }
45381
45942
  async function serviceLogs(opts = {}) {
45382
45943
  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)) {
45944
+ const logPath = import_node_path15.default.join(projectDir, ".sail", "agent.log");
45945
+ if (!import_node_fs19.default.existsSync(logPath)) {
45385
45946
  console.log(`No log yet at ${logPath}. The service writes here once it runs.`);
45386
45947
  return;
45387
45948
  }
45388
45949
  if (opts.follow) {
45389
45950
  if (process.platform === "win32") {
45390
- console.log(import_node_fs18.default.readFileSync(logPath, "utf-8"));
45951
+ console.log(import_node_fs19.default.readFileSync(logPath, "utf-8"));
45391
45952
  console.log("(follow mode is not supported on Windows here \u2014 re-run to refresh)");
45392
45953
  return;
45393
45954
  }
45394
45955
  (0, import_node_child_process2.execFileSync)("tail", ["-f", logPath], { stdio: "inherit" });
45395
45956
  return;
45396
45957
  }
45397
- const lines = import_node_fs18.default.readFileSync(logPath, "utf-8").split("\n");
45958
+ const lines = import_node_fs19.default.readFileSync(logPath, "utf-8").split("\n");
45398
45959
  console.log(lines.slice(-200).join("\n"));
45399
45960
  }
45400
45961
 
@@ -45550,14 +46111,14 @@ async function sessionResume(opts = {}) {
45550
46111
  }
45551
46112
 
45552
46113
  // 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");
46114
+ var import_node_fs20 = require("node:fs");
46115
+ var import_node_path16 = require("node:path");
46116
+ var RUNTIME_SERVER_FILE2 = (0, import_node_path16.join)(".sail", "runtime", "server.json");
45556
46117
  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;
46118
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
46119
+ if (!(0, import_node_fs20.existsSync)(file)) return null;
45559
46120
  try {
45560
- return JSON.parse((0, import_node_fs19.readFileSync)(file, "utf8"));
46121
+ return JSON.parse((0, import_node_fs20.readFileSync)(file, "utf8"));
45561
46122
  } catch {
45562
46123
  return null;
45563
46124
  }
@@ -45629,9 +46190,9 @@ async function stationStop(options) {
45629
46190
  }
45630
46191
  const daemon = await discoverDaemon(projectRoot);
45631
46192
  if (!daemon) {
45632
- const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
46193
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
45633
46194
  try {
45634
- if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
46195
+ if ((0, import_node_fs20.existsSync)(file)) (0, import_node_fs20.rmSync)(file);
45635
46196
  } catch {
45636
46197
  }
45637
46198
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -45647,9 +46208,9 @@ async function stationStop(options) {
45647
46208
  pid: state.pid
45648
46209
  });
45649
46210
  } catch {
45650
- const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
46211
+ const file = (0, import_node_path16.join)(projectRoot, RUNTIME_SERVER_FILE2);
45651
46212
  try {
45652
- if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
46213
+ if ((0, import_node_fs20.existsSync)(file)) (0, import_node_fs20.rmSync)(file);
45653
46214
  } catch {
45654
46215
  }
45655
46216
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -45711,9 +46272,9 @@ async function status() {
45711
46272
 
45712
46273
  // src/commands/ui.ts
45713
46274
  var import_node_child_process5 = require("node:child_process");
45714
- var import_node_fs20 = __toESM(require("node:fs"), 1);
46275
+ var import_node_fs21 = __toESM(require("node:fs"), 1);
45715
46276
  var import_node_net2 = __toESM(require("node:net"), 1);
45716
- var import_node_path16 = __toESM(require("node:path"), 1);
46277
+ var import_node_path17 = __toESM(require("node:path"), 1);
45717
46278
 
45718
46279
  // src/lib/tailscale.ts
45719
46280
  var import_node_child_process4 = require("node:child_process");
@@ -45759,12 +46320,12 @@ function tailscaleServeDown() {
45759
46320
  }
45760
46321
 
45761
46322
  // src/commands/ui.ts
45762
- var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
46323
+ var UI_STATE_FILE = import_node_path17.default.join(".sail", "runtime", "ui.json");
45763
46324
  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;
46325
+ const file = import_node_path17.default.join(projectRoot, UI_STATE_FILE);
46326
+ if (!import_node_fs21.default.existsSync(file)) return null;
45766
46327
  try {
45767
- return JSON.parse(import_node_fs20.default.readFileSync(file, "utf-8"));
46328
+ return JSON.parse(import_node_fs21.default.readFileSync(file, "utf-8"));
45768
46329
  } catch {
45769
46330
  return null;
45770
46331
  }
@@ -45772,7 +46333,7 @@ function readState2(projectRoot) {
45772
46333
  function installedCliVersion() {
45773
46334
  try {
45774
46335
  const pkg = JSON.parse(
45775
- import_node_fs20.default.readFileSync(import_node_path16.default.join(packageRoot(), "package.json"), "utf-8")
46336
+ import_node_fs21.default.readFileSync(import_node_path17.default.join(packageRoot(), "package.json"), "utf-8")
45776
46337
  );
45777
46338
  return pkg.version ?? null;
45778
46339
  } catch {
@@ -45799,16 +46360,16 @@ async function warnIfVersionSkew(port) {
45799
46360
  }
45800
46361
  function waitForPort(port, timeoutMs) {
45801
46362
  const deadline = Date.now() + timeoutMs;
45802
- return new Promise((resolve3) => {
46363
+ return new Promise((resolve4) => {
45803
46364
  const attempt = () => {
45804
46365
  const socket = import_node_net2.default.connect(port, "127.0.0.1");
45805
46366
  socket.once("connect", () => {
45806
46367
  socket.destroy();
45807
- resolve3(true);
46368
+ resolve4(true);
45808
46369
  });
45809
46370
  socket.once("error", () => {
45810
46371
  socket.destroy();
45811
- if (Date.now() > deadline) resolve3(false);
46372
+ if (Date.now() > deadline) resolve4(false);
45812
46373
  else setTimeout(attempt, 150);
45813
46374
  });
45814
46375
  };
@@ -45817,16 +46378,16 @@ function waitForPort(port, timeoutMs) {
45817
46378
  }
45818
46379
  async function uiCommand(opts = {}) {
45819
46380
  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");
46381
+ const uiDistDir = import_node_path17.default.join(packageRoot(), "packages", "ui", "dist");
46382
+ const serverBundle = import_node_path17.default.resolve(distDir, "server.cjs");
45822
46383
  const projectRoot = process.cwd();
45823
- const sailDir2 = import_node_path16.default.join(projectRoot, ".sail");
46384
+ const sailDir2 = import_node_path17.default.join(projectRoot, ".sail");
45824
46385
  const envPort = Number(process.env.PORT);
45825
46386
  const port = await findFreePort(Number.isInteger(envPort) && envPort > 0 && envPort <= 65535 ? envPort : projectPort(projectRoot));
45826
- if (!import_node_fs20.default.existsSync(serverBundle)) {
46387
+ if (!import_node_fs21.default.existsSync(serverBundle)) {
45827
46388
  throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
45828
46389
  }
45829
- if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
46390
+ if (!import_node_fs21.default.existsSync(import_node_path17.default.join(uiDistDir, "index.html"))) {
45830
46391
  throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
45831
46392
  }
45832
46393
  let tailnetUrl = null;
@@ -45856,10 +46417,10 @@ async function uiCommand(opts = {}) {
45856
46417
  await warnIfVersionSkew(existing.port);
45857
46418
  return;
45858
46419
  }
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");
46420
+ const runtimeDir = import_node_path17.default.join(projectRoot, ".sail", "runtime");
46421
+ import_node_fs21.default.mkdirSync(runtimeDir, { recursive: true });
46422
+ const logFile = import_node_path17.default.join(runtimeDir, "ui.log");
46423
+ const logFd = import_node_fs21.default.openSync(logFile, "a");
45863
46424
  const corsOrigins = [process.env.SAILOR_CORS_ORIGINS, tailnetUrl].filter(Boolean).join(",");
45864
46425
  const child = (0, import_node_child_process5.spawn)(process.execPath, [serverBundle], {
45865
46426
  detached: true,
@@ -45874,7 +46435,7 @@ async function uiCommand(opts = {}) {
45874
46435
  }
45875
46436
  });
45876
46437
  child.unref();
45877
- import_node_fs20.default.closeSync(logFd);
46438
+ import_node_fs21.default.closeSync(logFd);
45878
46439
  const READY_TIMEOUT_MS = 1e4;
45879
46440
  const deadline = Date.now() + READY_TIMEOUT_MS;
45880
46441
  let ready = false;
@@ -45888,14 +46449,14 @@ async function uiCommand(opts = {}) {
45888
46449
  if (!ready) {
45889
46450
  let tail = "";
45890
46451
  try {
45891
- tail = import_node_fs20.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
46452
+ tail = import_node_fs21.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
45892
46453
  } catch {
45893
46454
  }
45894
46455
  throw new Error(
45895
46456
  `Sailor UI failed to start within ${READY_TIMEOUT_MS / 1e3}s on port ${port}.` + (tail ? `
45896
46457
 
45897
46458
  Server output:
45898
- ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
46459
+ ${tail}` : ` See ${import_node_path17.default.relative(projectRoot, logFile)}.`)
45899
46460
  );
45900
46461
  }
45901
46462
  let exposed = false;
@@ -45910,8 +46471,8 @@ ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
45910
46471
  );
45911
46472
  }
45912
46473
  }
45913
- import_node_fs20.default.writeFileSync(
45914
- import_node_path16.default.join(projectRoot, UI_STATE_FILE),
46474
+ import_node_fs21.default.writeFileSync(
46475
+ import_node_path17.default.join(projectRoot, UI_STATE_FILE),
45915
46476
  JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString(), exposed }, null, 2)
45916
46477
  );
45917
46478
  console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
@@ -45924,7 +46485,7 @@ async function uiStatus() {
45924
46485
  console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
45925
46486
  await warnIfVersionSkew(state.port);
45926
46487
  } else {
45927
- if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
46488
+ if (state) import_node_fs21.default.rmSync(import_node_path17.default.join(process.cwd(), UI_STATE_FILE), { force: true });
45928
46489
  console.log("\u25CB Sailor UI is not running");
45929
46490
  }
45930
46491
  }
@@ -45936,7 +46497,7 @@ function uiStop() {
45936
46497
  return;
45937
46498
  }
45938
46499
  if (!isProcessAlive(state.pid)) {
45939
- import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
46500
+ import_node_fs21.default.rmSync(import_node_path17.default.join(projectRoot, UI_STATE_FILE), { force: true });
45940
46501
  console.log("Sailor UI is not running (stale state file removed).");
45941
46502
  return;
45942
46503
  }
@@ -45945,14 +46506,14 @@ function uiStop() {
45945
46506
  tailscaleServeDown();
45946
46507
  console.log("Tailnet exposure removed.");
45947
46508
  }
45948
- import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
46509
+ import_node_fs21.default.rmSync(import_node_path17.default.join(projectRoot, UI_STATE_FILE), { force: true });
45949
46510
  console.log(`Stopped Sailor UI (pid ${state.pid}).`);
45950
46511
  }
45951
46512
 
45952
46513
  // src/index.ts
45953
46514
  function cliVersion() {
45954
46515
  try {
45955
- const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path17.join)(packageRoot(), "package.json"), "utf-8"));
46516
+ const pkg = JSON.parse((0, import_node_fs22.readFileSync)((0, import_node_path18.join)(packageRoot(), "package.json"), "utf-8"));
45956
46517
  return pkg.version ?? "0.0.0";
45957
46518
  } catch {
45958
46519
  return "0.0.0";
@@ -46023,6 +46584,9 @@ mandate.command("attach").description("Register one or more already-deployed per
46023
46584
  "--address <mandateOrName>",
46024
46585
  "Permission address or locally-tracked name; or a comma-separated list of addresses to register together in one signature"
46025
46586
  ).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));
46587
+ mandate.command("configure").description(
46588
+ "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."
46589
+ ).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
46590
  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
46591
  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
46592
  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));