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

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 (99) hide show
  1. package/package.json +1 -1
  2. package/packages/cli/dist/index.cjs +166 -100
  3. package/packages/cli/dist/server.cjs +1256 -48
  4. package/packages/sdk/dist/intelligence.d.ts +1 -1
  5. package/packages/sdk/dist/intelligence.js +1 -1
  6. package/packages/sdk/dist/signing.d.ts +29 -0
  7. package/packages/sdk/dist/signing.d.ts.map +1 -1
  8. package/packages/ui/dist/assets/{add-CfTRVkh-.js → add-CLs0-B0p.js} +1 -1
  9. package/packages/ui/dist/assets/{all-wallets-VZChJwJf.js → all-wallets-CDOk28sj.js} +1 -1
  10. package/packages/ui/dist/assets/{app-store-CuFK1xBJ.js → app-store-DVzpi1qN.js} +1 -1
  11. package/packages/ui/dist/assets/{apple-DETqVk_I.js → apple-C9U9iH1l.js} +1 -1
  12. package/packages/ui/dist/assets/{arrow-bottom-CnNYsZfo.js → arrow-bottom-DjQ4jGMe.js} +1 -1
  13. package/packages/ui/dist/assets/{arrow-bottom-circle-DZwJF3UO.js → arrow-bottom-circle-jxial9Si.js} +1 -1
  14. package/packages/ui/dist/assets/{arrow-left-CBnisTAa.js → arrow-left-BjAyI8D1.js} +1 -1
  15. package/packages/ui/dist/assets/{arrow-right-DA3Y1Yzf.js → arrow-right-BlFO4Tcx.js} +1 -1
  16. package/packages/ui/dist/assets/{arrow-top-e8-FE_fo.js → arrow-top-DJ3oh3DH.js} +1 -1
  17. package/packages/ui/dist/assets/{bank-DdwromQn.js → bank-B98it1qN.js} +1 -1
  18. package/packages/ui/dist/assets/{basic-DLYER2un.js → basic-DsgeyPNu.js} +1 -1
  19. package/packages/ui/dist/assets/{browser-BJNrv4Zj.js → browser-j4vCeHwE.js} +1 -1
  20. package/packages/ui/dist/assets/{card-DB6j4DSk.js → card-CzxbMvEd.js} +1 -1
  21. package/packages/ui/dist/assets/{ccip-Dy_s323Y.js → ccip-U9yiu_EK.js} +1 -1
  22. package/packages/ui/dist/assets/{checkmark-Db9oicko.js → checkmark-D5sWBbk3.js} +1 -1
  23. package/packages/ui/dist/assets/{checkmark-bold-D9MgGHxv.js → checkmark-bold-BW6w3KCC.js} +1 -1
  24. package/packages/ui/dist/assets/{chevron-bottom-IGUDJRKN.js → chevron-bottom-BbHhmTQq.js} +1 -1
  25. package/packages/ui/dist/assets/{chevron-left-CWpfP-rw.js → chevron-left-D8BDH4Wo.js} +1 -1
  26. package/packages/ui/dist/assets/{chevron-right-BejY0vtE.js → chevron-right-BQuQk5zc.js} +1 -1
  27. package/packages/ui/dist/assets/{chevron-top-BaUN0LGF.js → chevron-top-1oeSXkx4.js} +1 -1
  28. package/packages/ui/dist/assets/{chrome-store-fox1tIqs.js → chrome-store-B1OMtzC-.js} +1 -1
  29. package/packages/ui/dist/assets/{clock-DlqB4Ehi.js → clock-e0HueehS.js} +1 -1
  30. package/packages/ui/dist/assets/{close-03e52feP.js → close-Cy1ytr26.js} +1 -1
  31. package/packages/ui/dist/assets/{coinPlaceholder-BO3nv4hx.js → coinPlaceholder-DqJu3ZPV.js} +1 -1
  32. package/packages/ui/dist/assets/{compass-CIqPRE0k.js → compass-XDz_N2Hg.js} +1 -1
  33. package/packages/ui/dist/assets/{copy-D5cJBwZq.js → copy-DmDo3Nob.js} +1 -1
  34. package/packages/ui/dist/assets/{core-COelOg3-.js → core-DbkMGSYW.js} +3 -3
  35. package/packages/ui/dist/assets/cursor-DRFqL8nZ.js +3 -0
  36. package/packages/ui/dist/assets/{cursor-transparent-Cvbiq9Br.js → cursor-transparent-DF2aA10D.js} +1 -1
  37. package/packages/ui/dist/assets/{desktop-Dp2wtF3n.js → desktop-CVydOWLn.js} +1 -1
  38. package/packages/ui/dist/assets/{disconnect-Dx_IGM73.js → disconnect-BuN2Whnz.js} +1 -1
  39. package/packages/ui/dist/assets/{discord-Cxryn14a.js → discord-DMNPvTB2.js} +1 -1
  40. package/packages/ui/dist/assets/{etherscan-D8aNXWCx.js → etherscan-CacW-93f.js} +1 -1
  41. package/packages/ui/dist/assets/{events-DR6OOHz2.js → events-CZkVDFih.js} +1 -1
  42. package/packages/ui/dist/assets/{exclamation-triangle-BBCaPLj1.js → exclamation-triangle-B26hIvqO.js} +1 -1
  43. package/packages/ui/dist/assets/{extension-bACRNBl5.js → extension-B7rI0gv7.js} +1 -1
  44. package/packages/ui/dist/assets/{external-link-BZRqHo3q.js → external-link-CX8skCbH.js} +1 -1
  45. package/packages/ui/dist/assets/{facebook-TAdUitg4.js → facebook-BDqbE6kl.js} +1 -1
  46. package/packages/ui/dist/assets/{fallback-DGiwrboN.js → fallback-DWQYKf3l.js} +1 -1
  47. package/packages/ui/dist/assets/{farcaster-YzdobBfR.js → farcaster-zQ3oQzZ8.js} +1 -1
  48. package/packages/ui/dist/assets/{filters-CT-xLEoI.js → filters-DCglZYsM.js} +1 -1
  49. package/packages/ui/dist/assets/{github-DxPBbmEL.js → github-BH-BYjss.js} +1 -1
  50. package/packages/ui/dist/assets/{google-B0TMgFhK.js → google-DYFWxZbC.js} +1 -1
  51. package/packages/ui/dist/assets/{help-circle-Be6sRPTw.js → help-circle-BXH_DtZ_.js} +1 -1
  52. package/packages/ui/dist/assets/{id-Dem_akbE.js → id-Dr2yTeX1.js} +1 -1
  53. package/packages/ui/dist/assets/{image-CszBzFVG.js → image-DeJey1Eg.js} +1 -1
  54. package/packages/ui/dist/assets/{index-DXqj6oYp.js → index-BP-UuQ8g.js} +1 -1
  55. package/packages/ui/dist/assets/{index-DAawBKd2.js → index-BksbyFjf.js} +1 -1
  56. package/packages/ui/dist/assets/{index-homh6mWC.js → index-C55-Ne92.js} +1 -1
  57. package/packages/ui/dist/assets/{index-Cb5eBfrN.js → index-D6_Zei-R.js} +71 -71
  58. package/packages/ui/dist/assets/{index-C2n9XvGG.js → index-DVjAiPHI.js} +3 -3
  59. package/packages/ui/dist/assets/{index-B0FDHAiK.js → index-uSdXE5b_.js} +1 -1
  60. package/packages/ui/dist/assets/{index.es-DJgl78dj.js → index.es-dZlNPpfR.js} +4 -4
  61. package/packages/ui/dist/assets/{info-BimGN4NK.js → info-B91pD5Rw.js} +1 -1
  62. package/packages/ui/dist/assets/{info-circle-lnKEJu2R.js → info-circle-CySl3_on.js} +1 -1
  63. package/packages/ui/dist/assets/{lightbulb-DS1vEMce.js → lightbulb-wwG8LQLo.js} +1 -1
  64. package/packages/ui/dist/assets/{mail-DoS3Wibk.js → mail-D617YZ8y.js} +1 -1
  65. package/packages/ui/dist/assets/{metamask-sdk-CMIU3BwW.js → metamask-sdk-RbLbxUgF.js} +1 -1
  66. package/packages/ui/dist/assets/{mobile-B09Cs95_.js → mobile-CD9KlA0m.js} +1 -1
  67. package/packages/ui/dist/assets/{more-CLBZT8WI.js → more-BQ_rpPXo.js} +1 -1
  68. package/packages/ui/dist/assets/{network-placeholder-C45Cp8rY.js → network-placeholder-BA7nl9e6.js} +1 -1
  69. package/packages/ui/dist/assets/{nftPlaceholder-CAVclL3u.js → nftPlaceholder-D1EaaeDn.js} +1 -1
  70. package/packages/ui/dist/assets/{off-CwE8HoZ0.js → off-DnGG35_w.js} +1 -1
  71. package/packages/ui/dist/assets/{parseSignature-kmMNHYjl.js → parseSignature-E29x8poM.js} +1 -1
  72. package/packages/ui/dist/assets/{play-store-DiFF_6n4.js → play-store-C5u2FvYB.js} +1 -1
  73. package/packages/ui/dist/assets/{plus-aygpl_pZ.js → plus-CM4xJkvI.js} +1 -1
  74. package/packages/ui/dist/assets/{qr-code-DpIS4fFK.js → qr-code-kM3FUIdR.js} +1 -1
  75. package/packages/ui/dist/assets/{recycle-horizontal-Qk2N74JE.js → recycle-horizontal-DWrRWIBM.js} +1 -1
  76. package/packages/ui/dist/assets/{refresh-sWRq9Vnt.js → refresh-CztbjK2d.js} +1 -1
  77. package/packages/ui/dist/assets/{reown-logo-YQxBa9i9.js → reown-logo-DUHEIodF.js} +1 -1
  78. package/packages/ui/dist/assets/{search-Bt6BSPcC.js → search-BJtb4mn1.js} +1 -1
  79. package/packages/ui/dist/assets/{secp256k1-BUh3OoFd.js → secp256k1-BjrTZpQx.js} +1 -1
  80. package/packages/ui/dist/assets/{send-ym-Fn17O.js → send-B3_kavG1.js} +1 -1
  81. package/packages/ui/dist/assets/{swapHorizontal-fBE2gnqc.js → swapHorizontal-BrF9POiv.js} +1 -1
  82. package/packages/ui/dist/assets/{swapHorizontalBold-CekQ22FJ.js → swapHorizontalBold-Dgf8OjXj.js} +1 -1
  83. package/packages/ui/dist/assets/{swapHorizontalMedium-B9BI9ovU.js → swapHorizontalMedium-C6wX0JZj.js} +1 -1
  84. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-DOC75mBH.js → swapHorizontalRoundedBold-DTAVw6oF.js} +1 -1
  85. package/packages/ui/dist/assets/{swapVertical-CSQAPH0z.js → swapVertical-KGlTFNoi.js} +1 -1
  86. package/packages/ui/dist/assets/{telegram-BPjzFxKU.js → telegram-DnLrK-4y.js} +1 -1
  87. package/packages/ui/dist/assets/{three-dots-DP6hdXH8.js → three-dots-C6_oMJfw.js} +1 -1
  88. package/packages/ui/dist/assets/{twitch-CgHMd-f3.js → twitch-DvPqPD4f.js} +1 -1
  89. package/packages/ui/dist/assets/{twitterIcon-BrR8ynXm.js → twitterIcon-BZdk82Am.js} +1 -1
  90. package/packages/ui/dist/assets/{verify-D35D7Wtv.js → verify-CbESD5G0.js} +1 -1
  91. package/packages/ui/dist/assets/{verify-filled-DCP5CT59.js → verify-filled-BRjTrLxw.js} +1 -1
  92. package/packages/ui/dist/assets/{w3m-modal-B-ZptWk0.js → w3m-modal-D9b2TGHc.js} +1 -1
  93. package/packages/ui/dist/assets/{wallet-Cnb4dkr3.js → wallet-B87E5UId.js} +1 -1
  94. package/packages/ui/dist/assets/{wallet-placeholder-CCzk0PqU.js → wallet-placeholder-DmssSbYf.js} +1 -1
  95. package/packages/ui/dist/assets/{walletconnect-f6tQZYRi.js → walletconnect-CZkLKWMZ.js} +1 -1
  96. package/packages/ui/dist/assets/{warning-circle-zxMagvfx.js → warning-circle-BPTpHB1P.js} +1 -1
  97. package/packages/ui/dist/assets/{x-D0nEGO1G.js → x-D_dEtEDU.js} +1 -1
  98. package/packages/ui/dist/index.html +1 -1
  99. package/packages/ui/dist/assets/cursor-CRb7R3u1.js +0 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.sail.money/sailor",
3
- "version": "1.3.0-208",
3
+ "version": "1.3.0-210",
4
4
  "description": "Operator toolkit for Sail Protocol",
5
5
  "bin": {
6
6
  "sailor": "packages/cli/dist/index.cjs"
@@ -41240,9 +41240,124 @@ async function keysShow() {
41240
41240
 
41241
41241
  // src/commands/mandate-contracts.ts
41242
41242
  var import_node_child_process = require("node:child_process");
41243
+ var import_node_fs14 = require("node:fs");
41244
+ var import_node_path12 = require("node:path");
41245
+ init_esm2();
41246
+
41247
+ // src/lib/permission-explainer.ts
41243
41248
  var import_node_fs13 = require("node:fs");
41244
41249
  var import_node_path11 = require("node:path");
41245
- init_esm2();
41250
+ function explainPermission(name, sourcePath) {
41251
+ const resolved = sourcePath ?? (0, import_node_path11.join)(process.cwd(), "mandates", `${name}.sol`);
41252
+ let src;
41253
+ try {
41254
+ src = (0, import_node_fs13.readFileSync)(resolved, "utf8");
41255
+ } catch {
41256
+ return null;
41257
+ }
41258
+ return parseStructured(src) ?? parseNatSpec(src) ?? parseRequires(src);
41259
+ }
41260
+ var ENFORCED_HEADER = /ENFORCE[SD]\s+ON-CHAIN/i;
41261
+ var NOT_ENFORCED_HEADER = /NOT\s+(ENFORCED|BOUNDED)/i;
41262
+ function parseStructured(src) {
41263
+ if (!ENFORCED_HEADER.test(src)) return null;
41264
+ const lines = src.split("\n");
41265
+ const result = {
41266
+ source: "structured",
41267
+ enforced: [],
41268
+ notEnforced: []
41269
+ };
41270
+ const headerLines = [];
41271
+ for (const line of lines) {
41272
+ const t = line.trim();
41273
+ if (t.startsWith("import ") || t.startsWith("contract ") || t.startsWith("abstract contract ")) break;
41274
+ if (t.startsWith("//")) headerLines.push(t.slice(2));
41275
+ }
41276
+ let section = null;
41277
+ for (const raw of headerLines) {
41278
+ const trimmed = raw.trim();
41279
+ if (/^─+$/.test(trimmed)) continue;
41280
+ if (NOT_ENFORCED_HEADER.test(trimmed)) {
41281
+ section = "notEnforced";
41282
+ continue;
41283
+ }
41284
+ if (ENFORCED_HEADER.test(trimmed)) {
41285
+ section = "enforced";
41286
+ continue;
41287
+ }
41288
+ if (trimmed.includes("VERIFY BEFORE USE")) {
41289
+ section = null;
41290
+ continue;
41291
+ }
41292
+ if (section === null) {
41293
+ const kv = trimmed.match(/^(Protocol|Version|Chain|Target)\s*:\s*(.+)$/i);
41294
+ if (kv) {
41295
+ const key = kv[1].toLowerCase();
41296
+ result[key] = kv[2].trim();
41297
+ }
41298
+ }
41299
+ if (section && trimmed.startsWith("\u2022")) {
41300
+ const bullet = trimmed.slice(1).trim();
41301
+ if (bullet) result[section].push(bullet);
41302
+ }
41303
+ }
41304
+ if (result.enforced.length === 0 && !result.protocol) return null;
41305
+ return result;
41306
+ }
41307
+ function parseNatSpec(src) {
41308
+ const enforced = [];
41309
+ const notEnforced = [];
41310
+ let current = null;
41311
+ let buf = [];
41312
+ const flush = () => {
41313
+ if (current && buf.length) {
41314
+ const text = buf.join(" ").replace(/\s+/g, " ").trim();
41315
+ if (text) (current === "enforced" ? enforced : notEnforced).push(text);
41316
+ }
41317
+ buf = [];
41318
+ };
41319
+ for (const line of src.split("\n")) {
41320
+ const t = line.trim();
41321
+ const tagged = t.match(/^\/\/\/\s*@(\w+)\s+(.+)$/);
41322
+ if (tagged) {
41323
+ const [, tag, text] = tagged;
41324
+ if (tag === "notice") {
41325
+ flush();
41326
+ current = "enforced";
41327
+ buf.push(text.trim());
41328
+ } else if (tag === "dev") {
41329
+ flush();
41330
+ current = "notEnforced";
41331
+ buf.push(text.trim());
41332
+ } else {
41333
+ flush();
41334
+ current = null;
41335
+ }
41336
+ continue;
41337
+ }
41338
+ const cont = t.match(/^\/\/\/\s?(.*)$/);
41339
+ if (cont && current) {
41340
+ const text = cont[1].trim();
41341
+ if (text) buf.push(text);
41342
+ continue;
41343
+ }
41344
+ if (!t.startsWith("///")) {
41345
+ flush();
41346
+ current = null;
41347
+ }
41348
+ }
41349
+ flush();
41350
+ if (enforced.length === 0 && notEnforced.length === 0) return null;
41351
+ return { source: "natspec", enforced, notEnforced };
41352
+ }
41353
+ function parseRequires(src) {
41354
+ const enforced = [];
41355
+ const re = /require\s*\([^,)]+,\s*["']([^"']+)["']\)/g;
41356
+ let m;
41357
+ while ((m = re.exec(src)) !== null) enforced.push(m[1]);
41358
+ if (enforced.length === 0) return null;
41359
+ return { source: "require-only", enforced, notEnforced: [] };
41360
+ }
41246
41361
 
41247
41362
  // src/lib/mandates.ts
41248
41363
  var MandateStore = class {
@@ -41708,6 +41823,8 @@ Pushing signing request for "${template.label}" permission\u2026`));
41708
41823
  ` The mandate signer (${permissionSigner}) must sign in the browser \u2014 not the agent wallet.`
41709
41824
  )
41710
41825
  );
41826
+ const permRecord = new MandateStore().find(templateAddress);
41827
+ const permExplanation = (permRecord ? explainPermission(permRecord.name, permRecord.sourcePath) : explainPermission(template.label)) ?? void 0;
41711
41828
  const response = await channel.requestSignature({
41712
41829
  type: "typed-data",
41713
41830
  kind: "register-permission",
@@ -41720,6 +41837,7 @@ Pushing signing request for "${template.label}" permission\u2026`));
41720
41837
  { label: "Permission", value: template.label },
41721
41838
  { label: "Mandate signer", value: permissionSigner }
41722
41839
  ],
41840
+ explanation: permExplanation,
41723
41841
  typedData
41724
41842
  });
41725
41843
  if (response.status === "rejected") {
@@ -41934,12 +42052,12 @@ async function runDeploy(project, channel, options) {
41934
42052
  )
41935
42053
  );
41936
42054
  }
41937
- const { abi: abi2, bytecode, contractName, artifactPath } = resolveArtifact(options);
42055
+ const { abi: abi2, bytecode, contractName, artifactPath, sourcePath } = resolveArtifact(options);
41938
42056
  let argsJson;
41939
42057
  if (options.argsFile) {
41940
- const argsFilePath = (0, import_node_path11.resolve)(options.argsFile);
42058
+ const argsFilePath = (0, import_node_path12.resolve)(options.argsFile);
41941
42059
  try {
41942
- argsJson = (0, import_node_fs13.readFileSync)(argsFilePath, "utf8").trim();
42060
+ argsJson = (0, import_node_fs14.readFileSync)(argsFilePath, "utf8").trim();
41943
42061
  } catch {
41944
42062
  throw new Error(`Cannot read --args-file: ${argsFilePath}`);
41945
42063
  }
@@ -41952,6 +42070,7 @@ async function runDeploy(project, channel, options) {
41952
42070
  const publicClient = publicClientFor(project);
41953
42071
  announceSigningUrl(json);
41954
42072
  say(() => console.log(`Pushing deploy request for "${contractName}"\u2026`));
42073
+ const explanation = explainPermission(contractName, sourcePath) ?? void 0;
41955
42074
  const response = await channel.requestSignature({
41956
42075
  type: "transaction",
41957
42076
  kind: "deploy-mandate",
@@ -41966,7 +42085,8 @@ async function runDeploy(project, channel, options) {
41966
42085
  label: options.argsFile ? "Constructor args (from file)" : "Constructor args",
41967
42086
  value: argsJson ? argsJson : "(none)"
41968
42087
  }
41969
- ]
42088
+ ],
42089
+ explanation
41970
42090
  });
41971
42091
  if (response.status === "rejected") {
41972
42092
  throw new Error(`User rejected permission deployment: ${response.reason ?? "no reason given"}`);
@@ -41986,6 +42106,7 @@ async function runDeploy(project, channel, options) {
41986
42106
  address: deployed,
41987
42107
  txHash: response.txHash,
41988
42108
  chainId,
42109
+ sourcePath,
41989
42110
  artifactPath,
41990
42111
  constructorArgs: parseArgsRaw(options.args),
41991
42112
  deployedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -42079,9 +42200,12 @@ var CLONE_TEMPLATES = {
42079
42200
  ]
42080
42201
  }
42081
42202
  };
42082
- function predictCloneAddress(impl, factory, submitter, salt) {
42203
+ function predictCloneAddress(impl, factory, submitter, account2, salt) {
42083
42204
  const namespacedSalt = keccak256(
42084
- encodeAbiParameters([{ type: "address" }, { type: "bytes32" }], [submitter, salt])
42205
+ encodeAbiParameters(
42206
+ [{ type: "address" }, { type: "address" }, { type: "bytes32" }],
42207
+ [submitter, account2, salt]
42208
+ )
42085
42209
  );
42086
42210
  const initCode = concatHex([CLONE_INIT_PREFIX, impl, CLONE_INIT_SUFFIX]);
42087
42211
  return getCreate2Address({
@@ -42178,7 +42302,7 @@ async function runDeployClone(project, channel, options) {
42178
42302
  [sma, impl, BigInt(Math.floor(Date.now() / 1e3))]
42179
42303
  )
42180
42304
  );
42181
- const clone = predictCloneAddress(impl, project.contracts.mandateFactory, submitter, salt);
42305
+ const clone = predictCloneAddress(impl, project.contracts.mandateFactory, submitter, sma, salt);
42182
42306
  say(() => {
42183
42307
  console.log(`
42184
42308
  ${spec.label} clone (${options.template})`);
@@ -42620,6 +42744,15 @@ async function attachBatchToSma(project, channel, publicClient, sma, permissions
42620
42744
  Attaching ${permissions.length} permissions in one signature \u2014 the mandate signer (${permissionSigner}) signs in the browser\u2026`
42621
42745
  )
42622
42746
  );
42747
+ const permStore = new MandateStore();
42748
+ const permExplanations = permissions.map((addr) => {
42749
+ const rec = permStore.find(addr);
42750
+ return {
42751
+ label: rec?.name ?? addr,
42752
+ address: addr,
42753
+ explanation: (rec ? explainPermission(rec.name, rec.sourcePath) : null) ?? void 0
42754
+ };
42755
+ });
42623
42756
  const response = await channel.requestSignature({
42624
42757
  type: "typed-data",
42625
42758
  kind: "register-permission",
@@ -42631,6 +42764,7 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42631
42764
  { label: "Permissions", value: String(permissions.length) },
42632
42765
  { label: "Mandate signer", value: permissionSigner }
42633
42766
  ],
42767
+ permissions: permExplanations,
42634
42768
  typedData
42635
42769
  });
42636
42770
  if (response.status === "rejected") {
@@ -42778,11 +42912,11 @@ function resolveArtifact(options) {
42778
42912
  let contractName = options.contract ?? options.name ?? "";
42779
42913
  if (!artifactPath) {
42780
42914
  if (!options.contract) throw new Error("Provide --artifact <path> or --contract <name>");
42781
- artifactPath = (0, import_node_path11.join)(options.out, `${options.contract}.sol`, `${options.contract}.json`);
42915
+ artifactPath = (0, import_node_path12.join)(options.out, `${options.contract}.sol`, `${options.contract}.json`);
42782
42916
  }
42783
- const resolved = (0, import_node_path11.resolve)(artifactPath);
42784
- const projectRoot = (0, import_node_path11.resolve)(process.cwd());
42785
- if (!resolved.startsWith(projectRoot + import_node_path11.sep) && resolved !== projectRoot) {
42917
+ const resolved = (0, import_node_path12.resolve)(artifactPath);
42918
+ const projectRoot = (0, import_node_path12.resolve)(process.cwd());
42919
+ if (!resolved.startsWith(projectRoot + import_node_path12.sep) && resolved !== projectRoot) {
42786
42920
  throw new Error(
42787
42921
  `Artifact path must be inside the project directory.
42788
42922
  Resolved: ${resolved}`
@@ -42790,14 +42924,14 @@ Resolved: ${resolved}`
42790
42924
  }
42791
42925
  artifactPath = resolved;
42792
42926
  if (options.build) runForgeBuild();
42793
- if (!(0, import_node_fs13.existsSync)(artifactPath)) {
42927
+ if (!(0, import_node_fs14.existsSync)(artifactPath)) {
42794
42928
  ensureForgeHint();
42795
42929
  throw new Error(
42796
42930
  `Artifact not found: ${artifactPath}
42797
42931
  Compile your mandate first (e.g. \`forge build\`), or pass --build.`
42798
42932
  );
42799
42933
  }
42800
- const artifact = JSON.parse((0, import_node_fs13.readFileSync)(artifactPath, "utf8"));
42934
+ const artifact = JSON.parse((0, import_node_fs14.readFileSync)(artifactPath, "utf8"));
42801
42935
  const abi2 = artifact.abi;
42802
42936
  const bytecodeRaw = artifact.bytecode?.object ?? artifact.bytecode;
42803
42937
  if (!bytecodeRaw || typeof bytecodeRaw !== "string") {
@@ -42808,7 +42942,16 @@ Compile your mandate first (e.g. \`forge build\`), or pass --build.`
42808
42942
  const m = artifactPath.match(/([^/\\]+)\.json$/);
42809
42943
  contractName = m ? m[1] : "Mandate";
42810
42944
  }
42811
- return { abi: abi2, bytecode, contractName, artifactPath };
42945
+ let sourcePath;
42946
+ const compilationTarget = artifact.metadata?.settings?.compilationTarget;
42947
+ if (compilationTarget && typeof compilationTarget === "object") {
42948
+ sourcePath = Object.keys(compilationTarget)[0];
42949
+ }
42950
+ sourcePath = sourcePath ?? artifact.ast?.absolutePath;
42951
+ if (sourcePath && !sourcePath.startsWith("/")) {
42952
+ sourcePath = (0, import_node_path12.resolve)(projectRoot, sourcePath);
42953
+ }
42954
+ return { abi: abi2, bytecode, contractName, artifactPath, sourcePath };
42812
42955
  }
42813
42956
  function parseArgsRaw(argsJson) {
42814
42957
  if (!argsJson) return void 0;
@@ -42878,88 +43021,6 @@ function runForgeBuild() {
42878
43021
 
42879
43022
  // src/commands/mandate.ts
42880
43023
  init_esm2();
42881
-
42882
- // src/lib/permission-explainer.ts
42883
- var import_node_fs14 = require("node:fs");
42884
- var import_node_path12 = require("node:path");
42885
- function explainPermission(name, sourcePath) {
42886
- const resolved = sourcePath ?? (0, import_node_path12.join)(process.cwd(), "mandates", `${name}.sol`);
42887
- let src;
42888
- try {
42889
- src = (0, import_node_fs14.readFileSync)(resolved, "utf8");
42890
- } catch {
42891
- return null;
42892
- }
42893
- return parseStructured(src) ?? parseNatSpec(src) ?? parseRequires(src);
42894
- }
42895
- function parseStructured(src) {
42896
- if (!src.includes("ENFORCED ON-CHAIN")) return null;
42897
- const lines = src.split("\n");
42898
- const result = {
42899
- source: "structured",
42900
- enforced: [],
42901
- notEnforced: []
42902
- };
42903
- const headerLines = [];
42904
- for (const line of lines) {
42905
- const t = line.trim();
42906
- if (t.startsWith("import ") || t.startsWith("contract ") || t.startsWith("abstract contract ")) break;
42907
- if (t.startsWith("//")) headerLines.push(t.slice(2));
42908
- }
42909
- let section = null;
42910
- for (const raw of headerLines) {
42911
- const trimmed = raw.trim();
42912
- if (/^─+$/.test(trimmed)) continue;
42913
- if (trimmed.includes("ENFORCED ON-CHAIN")) {
42914
- section = "enforced";
42915
- continue;
42916
- }
42917
- if (trimmed.includes("NOT ENFORCED")) {
42918
- section = "notEnforced";
42919
- continue;
42920
- }
42921
- if (trimmed.includes("VERIFY BEFORE USE")) {
42922
- section = null;
42923
- continue;
42924
- }
42925
- if (section === null) {
42926
- const kv = trimmed.match(/^(Protocol|Version|Chain|Target)\s*:\s*(.+)$/i);
42927
- if (kv) {
42928
- const key = kv[1].toLowerCase();
42929
- result[key] = kv[2].trim();
42930
- }
42931
- }
42932
- if (section && trimmed.startsWith("\u2022")) {
42933
- const bullet = trimmed.slice(1).trim();
42934
- if (bullet) result[section].push(bullet);
42935
- }
42936
- }
42937
- if (result.enforced.length === 0 && !result.protocol) return null;
42938
- return result;
42939
- }
42940
- function parseNatSpec(src) {
42941
- const enforced = [];
42942
- const notEnforced = [];
42943
- for (const line of src.split("\n")) {
42944
- const t = line.trim();
42945
- const notice = t.match(/^\/\/\/\s*@notice\s+(.+)$/);
42946
- if (notice) enforced.push(notice[1].trim());
42947
- const dev = t.match(/^\/\/\/\s*@dev\s+(.+)$/);
42948
- if (dev) notEnforced.push(dev[1].trim());
42949
- }
42950
- if (enforced.length === 0 && notEnforced.length === 0) return null;
42951
- return { source: "natspec", enforced, notEnforced };
42952
- }
42953
- function parseRequires(src) {
42954
- const enforced = [];
42955
- const re = /require\s*\([^,)]+,\s*["']([^"']+)["']\)/g;
42956
- let m;
42957
- while ((m = re.exec(src)) !== null) enforced.push(m[1]);
42958
- if (enforced.length === 0) return null;
42959
- return { source: "require-only", enforced, notEnforced: [] };
42960
- }
42961
-
42962
- // src/commands/mandate.ts
42963
43024
  function resolveActiveChain(account2) {
42964
43025
  try {
42965
43026
  const project = new ProjectContext();
@@ -43035,7 +43096,9 @@ ${permissions.length} permission(s) tracked for SMA ${account2.safe}:
43035
43096
  const draft = {
43036
43097
  account: account2.safe,
43037
43098
  chainId,
43038
- permissions: permissions.filter((p) => !p.revokedOnChain).map((p) => {
43099
+ // Only permissions not already registered on this SMA belong in the signing
43100
+ // draft — re-registering an already-registered permission reverts on-chain.
43101
+ permissions: permissions.filter((p) => !p.revokedOnChain && !p.registeredOnSma).map((p) => {
43039
43102
  const mandate2 = store.find(p.address);
43040
43103
  const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
43041
43104
  return { address: p.address, label: p.label, explanation };
@@ -45170,9 +45233,12 @@ function tailscaleServeUp(port) {
45170
45233
  ["serve", "--bg", "--https=443", `http://127.0.0.1:${port}`],
45171
45234
  { encoding: "utf-8" }
45172
45235
  );
45236
+ const out = `${r.stdout ?? ""}${r.stderr ?? ""}`;
45173
45237
  if (r.status !== 0) {
45174
- const detail = (r.stderr || r.stdout || "").trim() || `exit ${r.status ?? "?"}`;
45175
- throw new Error(detail);
45238
+ throw new Error(out.trim() || `exit ${r.status ?? "?"}`);
45239
+ }
45240
+ if (/serve is not enabled/i.test(out)) {
45241
+ throw new Error(out.trim());
45176
45242
  }
45177
45243
  }
45178
45244
  function tailscaleServeDown() {