@dev.sail.money/sailor 1.0.0-42 → 1.1.0-43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -3
- package/README.md +29 -20
- package/docs/PERMISSION_MODEL.md +1 -1
- package/examples/README.md +24 -0
- package/package.json +1 -1
- package/packages/cli/README.md +0 -1
- package/packages/cli/dist/index.cjs +146 -185
- package/packages/cli/dist/server.cjs +2 -1
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/ui/dist/assets/{add-i2P8A2gs.js → add-BjqRem-K.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-BVgI7zL8.js → all-wallets-Ce2n1Z8I.js} +1 -1
- package/packages/ui/dist/assets/{app-store-BewBVZmc.js → app-store-C7E_7mH6.js} +1 -1
- package/packages/ui/dist/assets/{apple-C7MUnEQz.js → apple-BC7kAskQ.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-DfToMb64.js → arrow-bottom-Dq_l3FWT.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-DJb-rBBb.js → arrow-bottom-circle-CjACGJGK.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-DsWmN2OG.js → arrow-left-5W_pClNR.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-DJUX6dvg.js → arrow-right-DXy553gM.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-bK9-SH4h.js → arrow-top-DD0q04nr.js} +1 -1
- package/packages/ui/dist/assets/{bank-LlHF24Ta.js → bank-CCXCwaWG.js} +1 -1
- package/packages/ui/dist/assets/{basic-BgERj65k.js → basic-CAwLXzDD.js} +1 -1
- package/packages/ui/dist/assets/{browser-B9v0kcdd.js → browser-DWmAo_2s.js} +1 -1
- package/packages/ui/dist/assets/{card-QhDXGe3I.js → card-C_tjSBK2.js} +1 -1
- package/packages/ui/dist/assets/{ccip-CP4Hg6QU.js → ccip-B4SUIV1s.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-ScbLFePj.js → checkmark-W_dSsbPW.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-SxqQsHPF.js → checkmark-bold-C9Xi2fBZ.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-HOzCXggY.js → chevron-bottom-Dxo8Hw1W.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-DmtXEk14.js → chevron-left-CD7UuRQl.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-DJCWUGen.js → chevron-right-D6kOGOGy.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-Bj9XyjOS.js → chevron-top-ynjtovar.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-C0Rq-0OK.js → chrome-store-CB5wu9g8.js} +1 -1
- package/packages/ui/dist/assets/{clock-DJiHF6By.js → clock-B3H1XYXj.js} +1 -1
- package/packages/ui/dist/assets/{close-B58KpBOm.js → close-DVSIOMF5.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-QrD1nktf.js → coinPlaceholder-Co_HN9AE.js} +1 -1
- package/packages/ui/dist/assets/{compass-DkdmCS4M.js → compass-CaY11Job.js} +1 -1
- package/packages/ui/dist/assets/{copy-BKkOeIGO.js → copy-D7gntTj6.js} +1 -1
- package/packages/ui/dist/assets/{core-BJ5Wn_0H.js → core-CqvnE8sM.js} +3 -3
- package/packages/ui/dist/assets/cursor-rLwK_mXz.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-CwUDRud2.js → cursor-transparent-BrL6QUeS.js} +1 -1
- package/packages/ui/dist/assets/{desktop-CKk_cTjE.js → desktop-BLH7DXoy.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-BR4tm4ry.js → disconnect-BxnvzLHz.js} +1 -1
- package/packages/ui/dist/assets/{discord-CimCnw-R.js → discord-CfV-36UX.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-DKSSzuOS.js → etherscan-CyMQ7xaE.js} +1 -1
- package/packages/ui/dist/assets/{events-HAbmebGY.js → events-D_3qqJ93.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-CnI_W0DF.js → exclamation-triangle-DWjoM6jA.js} +1 -1
- package/packages/ui/dist/assets/{extension-BSpH53zE.js → extension-D3fVJAol.js} +1 -1
- package/packages/ui/dist/assets/{external-link-D2QNdCHI.js → external-link-D1vHHGXX.js} +1 -1
- package/packages/ui/dist/assets/{facebook-CgSGAlNU.js → facebook-Dzk6W-1X.js} +1 -1
- package/packages/ui/dist/assets/{fallback-CUotDuSc.js → fallback-BDBC0epM.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-SPALE2oQ.js → farcaster-BkJt6sOG.js} +1 -1
- package/packages/ui/dist/assets/{filters-DjDEAYKk.js → filters-Btr-hO6b.js} +1 -1
- package/packages/ui/dist/assets/{github-CP9cMjmv.js → github-ulrStu89.js} +1 -1
- package/packages/ui/dist/assets/{google-DHxowSYF.js → google-BTTDuZjf.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-Y1PsVJnm.js → help-circle-DcSMbQJh.js} +1 -1
- package/packages/ui/dist/assets/{id-Du_4Z0EW.js → id-7WOxEl6j.js} +1 -1
- package/packages/ui/dist/assets/{image-tsFuvzhF.js → image-D2GBTxnU.js} +1 -1
- package/packages/ui/dist/assets/{index-7RnuTfdS.js → index-BfABWjw0.js} +1 -1
- package/packages/ui/dist/assets/{index-tPkIpVzb.js → index-BjpGs3bJ.js} +3 -3
- package/packages/ui/dist/assets/{index-0OXGjc-u.js → index-Bvqcol0e.js} +1 -1
- package/packages/ui/dist/assets/{index-CNBJUjiM.js → index-C35kUMRo.js} +1 -1
- package/packages/ui/dist/assets/{index-Bj9jEvxf.js → index-DOy_BvMy.js} +27 -27
- package/packages/ui/dist/assets/{index-DExn8x4G.js → index-jNjVgIvi.js} +1 -1
- package/packages/ui/dist/assets/{index.es-DIo7ubqt.js → index.es-Cz1WraDz.js} +4 -4
- package/packages/ui/dist/assets/{info-DwysfTrK.js → info-CezLTebu.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-BgqfZd9m.js → info-circle-BHDy0RH1.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-Cy1dZk-v.js → lightbulb-DvUuugiy.js} +1 -1
- package/packages/ui/dist/assets/{mail-q8uF2fJv.js → mail-D6W2cU_-.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-CC2GhoTH.js → metamask-sdk-Cj8b59wb.js} +1 -1
- package/packages/ui/dist/assets/{mobile-BM3dHloL.js → mobile-DD4cG8mI.js} +1 -1
- package/packages/ui/dist/assets/{more-DtxChT3n.js → more-RsHIPHXv.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-CkS7N2-s.js → network-placeholder-E2lpOWE4.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-BP2BELBx.js → nftPlaceholder-Cm9qGJHf.js} +1 -1
- package/packages/ui/dist/assets/{off-CGlNgOvT.js → off-7ALa1e8N.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-B-9InYad.js → parseSignature-EU-GmuA0.js} +1 -1
- package/packages/ui/dist/assets/{play-store-j4WI9tEY.js → play-store-YmFLoPbj.js} +1 -1
- package/packages/ui/dist/assets/{plus-DoBLB2r6.js → plus-CljVpN-Y.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-CxMPa6dI.js → qr-code-DVGz15q5.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-tKM6wEFt.js → recycle-horizontal-Ch_2PFBB.js} +1 -1
- package/packages/ui/dist/assets/{refresh-D3gJv-wD.js → refresh-B1yJjeZR.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-CB8aduTF.js → reown-logo-B4gHTrkG.js} +1 -1
- package/packages/ui/dist/assets/{search-CEFrSvpQ.js → search-C7OPO4bC.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-BiXu1g8S.js → secp256k1-ZHPkrWDd.js} +1 -1
- package/packages/ui/dist/assets/{send-DpKQgtRb.js → send-C5Aj89yl.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-a9Ybf18t.js → swapHorizontal-D5l_jKrZ.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-DP452gU2.js → swapHorizontalBold-TxFZ3Umb.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-eG99M64A.js → swapHorizontalMedium-M_yuNi0o.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BAc_to7T.js → swapHorizontalRoundedBold-G73a-cMg.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-BU1HNAb2.js → swapVertical-Gy7GjYS4.js} +1 -1
- package/packages/ui/dist/assets/{telegram-DFyhBidh.js → telegram-BnO9isY5.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-BdnfIeUB.js → three-dots-B616bG-O.js} +1 -1
- package/packages/ui/dist/assets/{twitch-DdurBvf3.js → twitch-DUpzj8Dd.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-JS1Kh388.js → twitterIcon-itK0Mrgj.js} +1 -1
- package/packages/ui/dist/assets/{verify-C-ZvR7T4.js → verify-B1Gtl5H9.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-Dzgdgepq.js → verify-filled-D3noLrZ3.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-B_CKEhcT.js → w3m-modal-CiZVJcHR.js} +1 -1
- package/packages/ui/dist/assets/{wallet-C4JYrLNC.js → wallet-BK1sp3ca.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-DURyjfcE.js → wallet-placeholder-CatD0vru.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-C5I2F5B-.js → walletconnect-DmCqpZUB.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-Blhki0Aq.js → warning-circle-bT7aRO8J.js} +1 -1
- package/packages/ui/dist/assets/{x-NdGVznpk.js → x-DuFufU-Y.js} +1 -1
- package/packages/ui/dist/index.html +1 -1
- package/scripts/check-docs.mjs +51 -4
- package/scripts/check-init.mjs +12 -0
- package/templates/default/.agents/skills/sail-ci/SKILL.md +66 -0
- package/templates/default/.agents/skills/sail-extend/SKILL.md +74 -0
- package/templates/default/.agents/skills/sail-mandates/SKILL.md +93 -0
- package/templates/default/.agents/skills/sail-mandates/references/approvals.md +42 -0
- package/templates/default/.agents/skills/sail-mandates/references/calls-schema.md +42 -0
- package/templates/default/.agents/skills/sail-mandates/references/constructor-args.md +45 -0
- package/templates/default/.agents/skills/sail-mandates/references/examples-index.md +31 -0
- package/templates/default/.agents/skills/sail-mandates/references/simulate-calls.md +58 -0
- package/templates/default/.agents/skills/sail-onboarding/SKILL.md +73 -0
- package/templates/default/.agents/skills/sail-project-info/SKILL.md +30 -0
- package/templates/default/.agents/skills/sail-servers/SKILL.md +43 -0
- package/templates/default/.agents/skills/sail-transactions/SKILL.md +63 -0
- package/templates/default/AGENTS.md +37 -126
- package/templates/default/test/BoundedCallPermission.t.sol +73 -0
- package/packages/ui/dist/assets/cursor-Dk5BeeUC.js +0 -3
- /package/{templates → examples}/custom-mandate/.sail/contracts/interfaces/IPermission.sol +0 -0
- /package/{templates → examples}/custom-mandate/README.md +0 -0
- /package/{templates → examples}/custom-mandate/foundry.toml +0 -0
- /package/{templates → examples}/custom-mandate/mandates/BoundedCallPermission.sol +0 -0
- /package/{templates → examples}/custom-mandate/mandates/README.md +0 -0
- /package/{templates → examples}/custom-mandate/mandates/SailCalldata.sol +0 -0
- /package/{templates → examples}/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +0 -0
- /package/{templates → examples}/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +0 -0
- /package/{templates → examples}/lifi-permissions/README.md +0 -0
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Sailor — Codebase Guide
|
|
2
2
|
|
|
3
|
+
This guide is for contributors to the Sailor codebase. The user-facing agent guide ships in templates/default/AGENTS.md.
|
|
4
|
+
|
|
3
5
|
Sailor is the operator toolkit for Sail Protocol. It does **not** deploy the protocol or author
|
|
4
6
|
permission templates — it targets already-deployed SailKernel instances and gives operators the
|
|
5
7
|
tooling to create SMAs, register permission contracts, and run strategy agents.
|
|
@@ -8,11 +10,11 @@ tooling to create SMAs, register permission contracts, and run strategy agents.
|
|
|
8
10
|
|
|
9
11
|
| Package / path | Name | Role |
|
|
10
12
|
|---|---|---|
|
|
11
|
-
| `packages/sdk` | `@sail/sdk` | SailorClient, LocalKeyring, kernel ABIs, EIP-712 builders, deployment registry, per-chain address registry |
|
|
13
|
+
| `packages/sdk` | `@sail/sdk` | SailorClient, LocalKeyring, kernel ABIs, EIP-712 builders, deployment registry, per-chain address registry (publishes to npm as `@sail.money/sdk`) |
|
|
12
14
|
| `packages/cli` | `sailor` | CLI: init, keys, account, mandate, onboard, station, ui, run, session, scan, status, owner, doctor, capabilities |
|
|
13
|
-
| `packages/ui` | `sailor-ui` | Local dashboard + browser-driven onboarding wizard
|
|
15
|
+
| `packages/ui` | `sailor-ui` | Local dashboard + browser-driven onboarding wizard (per-project port, 3333–3999) |
|
|
14
16
|
| `templates/default` | — | Default agent starter: neutral blank scaffold + Foundry workspace + onboarding guide (AGENTS.md) |
|
|
15
|
-
| `
|
|
17
|
+
| `examples/custom-mandate` | — | Solidity reference: IPermission scaffold (not a project template) |
|
|
16
18
|
|
|
17
19
|
## Protocol roles
|
|
18
20
|
|
package/README.md
CHANGED
|
@@ -12,10 +12,11 @@ Sailor is the off-chain operator layer for [Sail Protocol](https://github.com/sa
|
|
|
12
12
|
|---|---|---|
|
|
13
13
|
| `packages/sdk` | `@sail.money/sdk` / `@sail.money/sailor/sdk` | TypeScript library: SailorClient, EIP-712 helpers, ABIs, deployment registry, chain registry |
|
|
14
14
|
| `packages/cli` | `@sail.money/sailor` | CLI for account setup, mandate signing, and agent execution |
|
|
15
|
-
| `packages/ui` | `sailor-ui` | Local dashboard
|
|
16
|
-
| `templates/default` | — |
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
15
|
+
| `packages/ui` | `sailor-ui` | Local dashboard (per-project port; see Dashboard below) |
|
|
16
|
+
| `templates/default` | — | The agent starter `sailor init` scaffolds: slim `AGENTS.md` + on-demand skills under `.agents/skills/` |
|
|
17
|
+
| `examples/permissions` | — | Worked permission contracts by protocol and chain (reference, unaudited) |
|
|
18
|
+
| `examples/custom-mandate` | — | Solidity reference: IPermission authoring scaffold |
|
|
19
|
+
| `examples/lifi-permissions` | — | Solidity reference: LiFi clone permission contracts (source of the clone implementations) |
|
|
19
20
|
|
|
20
21
|
---
|
|
21
22
|
|
|
@@ -63,7 +64,11 @@ The path from nothing to a running agent follows the protocol lifecycle:
|
|
|
63
64
|
4. **Run** — `sailor run` executes the agent locally on a schedule, or via the GitHub Actions workflow the scaffold provides.
|
|
64
65
|
5. **Operate** — `sailor doctor` checks kernel health and gas balances; `sailor chains` lists supported chains and deployment addresses; `sailor session pause` instantly revokes dispatch rights without touching Safe custody.
|
|
65
66
|
|
|
66
|
-
Run `npx sailor init my-agent`, open the scaffolded folder in Claude Code, Cursor, or any AI coding assistant, and say **"start"**.
|
|
67
|
+
Run `npx sailor init my-agent`, open the scaffolded folder in Claude Code, Cursor, or any AI coding assistant, and say **"start"**.
|
|
68
|
+
|
|
69
|
+
### How the assistant is guided
|
|
70
|
+
|
|
71
|
+
The scaffold follows the open [Agent Skills](https://agentskills.io) standard: a slim, always-loaded `AGENTS.md` carries the welcome flow, project-state map, and hard invariants, while detailed procedures live in seven on-demand skills under `.agents/skills/` (onboarding, project info, servers, transactions, mandates, CI, extensions). Assistants that scan skills load each one only when relevant; assistants that don't follow the routing table in `AGENTS.md` to the same plain-markdown files. Works in Claude Code, Cursor, Copilot, and Codex.
|
|
67
72
|
|
|
68
73
|
---
|
|
69
74
|
|
|
@@ -120,7 +125,7 @@ mkdir my-agent && cd my-agent && npm i @sail.money/sailor && npx sailor init &&
|
|
|
120
125
|
mkdir my-agent ; cd my-agent ; npm i @sail.money/sailor ; npx sailor init ; npm install
|
|
121
126
|
```
|
|
122
127
|
|
|
123
|
-
Open this folder in Claude Code, Cursor, Codex, or any AI coding assistant and say **"start"**. The scaffolded `AGENTS.md`
|
|
128
|
+
Open this folder in Claude Code, Cursor, Codex, or any AI coding assistant and say **"start"**. The scaffolded `AGENTS.md` and its skills guide the assistant through the whole flow — SMA deployment, strategy definition, mandate authoring, running, and automation. No manual steps required.
|
|
124
129
|
|
|
125
130
|
### Direct CLI reference
|
|
126
131
|
|
|
@@ -146,7 +151,7 @@ sailor run # start the agent (continuous)
|
|
|
146
151
|
sailor keys export-ci # copy encrypted agent wallet to ci-keystore.json for CI
|
|
147
152
|
|
|
148
153
|
# Dashboard
|
|
149
|
-
sailor ui start #
|
|
154
|
+
sailor ui start # prints the per-project dashboard URL
|
|
150
155
|
```
|
|
151
156
|
|
|
152
157
|
`sailor run` writes reverted transactions to stderr as `reverted: <txHash> (gas used: N)`; successful dispatches are appended to `.sail/activity.jsonl`.
|
|
@@ -174,9 +179,9 @@ sailor init my-agent --template <name> # named subdirectory + specific templat
|
|
|
174
179
|
### What makes a valid template
|
|
175
180
|
|
|
176
181
|
A valid template is any directory under `templates/` that contains a
|
|
177
|
-
`package.json`.
|
|
178
|
-
`lifi-permissions`)
|
|
179
|
-
are
|
|
182
|
+
`package.json`. Solidity reference sources live under `examples/`
|
|
183
|
+
(`examples/permissions`, `examples/custom-mandate`, `examples/lifi-permissions`)
|
|
184
|
+
— they are not project scaffolds and never appear in the template list.
|
|
180
185
|
|
|
181
186
|
### Adding a template
|
|
182
187
|
|
|
@@ -193,18 +198,22 @@ Template files are bundled into the published `sailor` npm package via the
|
|
|
193
198
|
|
|
194
199
|
## Dashboard (`sailor ui`)
|
|
195
200
|
|
|
196
|
-
The Sailor dashboard is a local React app
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
The Sailor dashboard is a local React app. It shows live account state, mandate
|
|
202
|
+
health, signer balances, and recent activity — all read from the project's
|
|
203
|
+
`.sail/` directory with no hosted backend.
|
|
204
|
+
|
|
205
|
+
Each project gets its own deterministic port in the 3333–3999 range (derived
|
|
206
|
+
from the project path, so several projects can run dashboards side by side).
|
|
207
|
+
Use the URL the command prints, or read it from `.sail/runtime/ui.json` —
|
|
208
|
+
do not assume port 3333.
|
|
200
209
|
|
|
201
210
|
### Commands
|
|
202
211
|
|
|
203
212
|
```bash
|
|
204
213
|
sailor ui # start the dashboard (same as sailor ui start)
|
|
205
|
-
sailor ui start # start the dashboard
|
|
214
|
+
sailor ui start # start the dashboard and print its URL
|
|
206
215
|
sailor ui stop # stop the running dashboard
|
|
207
|
-
sailor ui status # show whether the dashboard is running + pid
|
|
216
|
+
sailor ui status # show whether the dashboard is running + URL + pid
|
|
208
217
|
```
|
|
209
218
|
|
|
210
219
|
### How it works
|
|
@@ -224,7 +233,7 @@ This means you can start the dashboard in one terminal and stop it from another.
|
|
|
224
233
|
```bash
|
|
225
234
|
# macOS / Linux
|
|
226
235
|
sailor ui start &
|
|
227
|
-
sailor ui status # ● running http://localhost
|
|
236
|
+
sailor ui status # ● running http://localhost:<port> (pid 12345)
|
|
228
237
|
sailor ui stop # Stopped Sailor UI (pid 12345).
|
|
229
238
|
|
|
230
239
|
# Windows (PowerShell)
|
|
@@ -317,8 +326,8 @@ Published to the public npm registry under the `@sail.money` scope.
|
|
|
317
326
|
|
|
318
327
|
| Trigger | Package | Version | dist-tag |
|
|
319
328
|
|---|---|---|---|
|
|
320
|
-
| Tag push (`v*`) | `@sail.money/sailor` | `
|
|
321
|
-
| Manual dispatch | `@dev.sail.money/sailor` | `
|
|
329
|
+
| Tag push (`v*`) | `@sail.money/sailor` | `1.1.0` | `latest` |
|
|
330
|
+
| Manual dispatch | `@dev.sail.money/sailor` | `1.1.0-42` | `dev` |
|
|
322
331
|
|
|
323
332
|
```bash
|
|
324
333
|
npm install @sail.money/sailor # latest stable (tag push)
|
|
@@ -378,7 +387,7 @@ Either way, `@sail.money/sailor/sdk` imports work unchanged.
|
|
|
378
387
|
|
|
379
388
|
## State of the project
|
|
380
389
|
|
|
381
|
-
Sailor is functional and published as [`@sail.money/sailor`](https://www.npmjs.com/package/@sail.money/sailor) on npm (
|
|
390
|
+
Sailor is functional and published as [`@sail.money/sailor`](https://www.npmjs.com/package/@sail.money/sailor) on npm (v1.1.0). The SDK, CLI, keystore, mandate flows, agent runner, and dashboard are implemented and have been exercised end to end.
|
|
382
391
|
|
|
383
392
|
The Sail Protocol trusted core is deployed on six chains — Ethereum, Base, Arbitrum, Unichain, Base Sepolia, and Eth Sepolia — via CREATE2, with every core contract at the same address on every chain. All six run the selective dispatch model with zero fees and are bootstrapped with a genesis allowlist so `createAccount` is usable immediately. These deployments are under an ongoing external audit by [Octane Security](https://octane.security) and are not final — do not use them with funds you are not prepared to lose.
|
|
384
393
|
|
package/docs/PERMISSION_MODEL.md
CHANGED
|
@@ -45,7 +45,7 @@ other." The fix was to redeploy every permission with pass-through semantics.
|
|
|
45
45
|
Corollary: on a conjunctive kernel you **cannot** have two permissions that each
|
|
46
46
|
enforce a different token's approve — each would reject the other's token. To support
|
|
47
47
|
approving both DAI and USDC you need **one** approve permission that allows both (see
|
|
48
|
-
`
|
|
48
|
+
`examples/lifi-permissions/`), not two narrow ones.
|
|
49
49
|
|
|
50
50
|
Selective kernels don't have this problem: each dispatch names one permission and
|
|
51
51
|
only that one is consulted.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# examples/
|
|
2
|
+
|
|
3
|
+
Reference material for building Sail permission contracts. Nothing here is part of
|
|
4
|
+
Sail Protocol and nothing here is audited.
|
|
5
|
+
|
|
6
|
+
## What's in here
|
|
7
|
+
|
|
8
|
+
### `permissions/`
|
|
9
|
+
|
|
10
|
+
Protocol-specific permission examples (Uniswap, Aave, GMX, Vault, Pendle, and others).
|
|
11
|
+
These are copied into your project by `sailor init` so you have local starting points to
|
|
12
|
+
adapt. They are not a supported or exhaustive library — review them before using.
|
|
13
|
+
|
|
14
|
+
### `custom-mandate/`
|
|
15
|
+
|
|
16
|
+
A standalone Foundry workspace scaffold for authoring your own `IPermission` contract as a
|
|
17
|
+
separate project. Fork it, rename it, and build from `BoundedCallPermission.sol`.
|
|
18
|
+
|
|
19
|
+
### `lifi-permissions/`
|
|
20
|
+
|
|
21
|
+
Reference-only source for the deployed LiFi EIP-1167 clone implementations
|
|
22
|
+
(`LifiBoundedApprovePermissionCloneable`, `LifiDiamondSwapPermissionCloneable`).
|
|
23
|
+
These document live deployed contracts — do not adapt them directly for new projects.
|
|
24
|
+
See the `README.md` inside for deployment addresses and the conjunctive-kernel caveat.
|
package/package.json
CHANGED
package/packages/cli/README.md
CHANGED
|
@@ -14,7 +14,6 @@ sailor init my-fund
|
|
|
14
14
|
|
|
15
15
|
- `sailor init [name]` — scaffold a new agent project from a template
|
|
16
16
|
- `sailor keys generate|show` — generate/show the agent wallet and mandate signer keys
|
|
17
|
-
- `sailor account create` — create a new SMA on-chain
|
|
18
17
|
- `sailor onboard` — set up an SMA, register a permission, and confirm the agent is operational
|
|
19
18
|
- `sailor mandate prepare` — review the permissions attached to your SMA
|
|
20
19
|
- `sailor mandate sign` — confirm the permissions authorized for your SMA
|
|
@@ -35189,6 +35189,10 @@ var require_websocket_server2 = __commonJS({
|
|
|
35189
35189
|
}
|
|
35190
35190
|
});
|
|
35191
35191
|
|
|
35192
|
+
// src/index.ts
|
|
35193
|
+
var import_node_fs18 = require("node:fs");
|
|
35194
|
+
var import_node_path14 = require("node:path");
|
|
35195
|
+
|
|
35192
35196
|
// ../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
35193
35197
|
var import_index = __toESM(require_commander(), 1);
|
|
35194
35198
|
var {
|
|
@@ -38442,14 +38446,6 @@ async function confirm(question) {
|
|
|
38442
38446
|
const answer = (await prompt(`${question} (y/N)`)).toLowerCase();
|
|
38443
38447
|
return answer === "y" || answer === "yes";
|
|
38444
38448
|
}
|
|
38445
|
-
async function promptAddress(label, def) {
|
|
38446
|
-
for (let attempt = 0; attempt < 5; attempt++) {
|
|
38447
|
-
const raw = await prompt(label, def);
|
|
38448
|
-
if (isAddress(raw)) return getAddress(raw);
|
|
38449
|
-
console.log(` "${raw}" is not a valid EVM address \u2014 try again.`);
|
|
38450
|
-
}
|
|
38451
|
-
throw new Error(`No valid address provided for: ${label}`);
|
|
38452
|
-
}
|
|
38453
38449
|
async function promptHidden(question) {
|
|
38454
38450
|
const isTty = process.stdin.isTTY === true;
|
|
38455
38451
|
if (isTty) muted = true;
|
|
@@ -38492,87 +38488,6 @@ function getRpcUrl(chainId) {
|
|
|
38492
38488
|
return process.env.RPC_URL?.trim() || void 0;
|
|
38493
38489
|
}
|
|
38494
38490
|
|
|
38495
|
-
// src/lib/keys.ts
|
|
38496
|
-
init_esm2();
|
|
38497
|
-
var ROLES = ["manager", "permissionSigner"];
|
|
38498
|
-
function normalizeRole(input) {
|
|
38499
|
-
const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
|
|
38500
|
-
if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
|
|
38501
|
-
return "manager";
|
|
38502
|
-
}
|
|
38503
|
-
if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
|
|
38504
|
-
return "permissionSigner";
|
|
38505
|
-
}
|
|
38506
|
-
return null;
|
|
38507
|
-
}
|
|
38508
|
-
function roleLabel(role) {
|
|
38509
|
-
return role === "manager" ? "agent wallet" : "mandate signer";
|
|
38510
|
-
}
|
|
38511
|
-
function safeHex(safe) {
|
|
38512
|
-
return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
|
|
38513
|
-
}
|
|
38514
|
-
function keyPath(role, safe) {
|
|
38515
|
-
if (safe) {
|
|
38516
|
-
return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
|
|
38517
|
-
}
|
|
38518
|
-
return sailPath("keys", `${role}.json`);
|
|
38519
|
-
}
|
|
38520
|
-
function legacyKeyPath(role, safe) {
|
|
38521
|
-
return sailPath("keys", `${role}-${safeHex(safe)}.json`);
|
|
38522
|
-
}
|
|
38523
|
-
function resolveKeyPath(role, safe) {
|
|
38524
|
-
if (safe) {
|
|
38525
|
-
const perSma = keyPath(role, safe);
|
|
38526
|
-
if (fileExists(perSma)) return perSma;
|
|
38527
|
-
const legacy = legacyKeyPath(role, safe);
|
|
38528
|
-
if (fileExists(legacy)) return legacy;
|
|
38529
|
-
}
|
|
38530
|
-
return keyPath(role);
|
|
38531
|
-
}
|
|
38532
|
-
function keyExists(role, safe) {
|
|
38533
|
-
return fileExists(resolveKeyPath(role, safe));
|
|
38534
|
-
}
|
|
38535
|
-
async function loadKeyring(role, safe) {
|
|
38536
|
-
const keystore = readJsonFile(resolveKeyPath(role, safe));
|
|
38537
|
-
if (!keystore) {
|
|
38538
|
-
throw new Error(
|
|
38539
|
-
`No ${roleLabel(role)} found.
|
|
38540
|
-
Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
|
|
38541
|
-
);
|
|
38542
|
-
}
|
|
38543
|
-
const password = await promptHidden(`Password for ${roleLabel(role)} key`);
|
|
38544
|
-
try {
|
|
38545
|
-
return await LocalKeyring.fromKeystore(keystore, password);
|
|
38546
|
-
} catch {
|
|
38547
|
-
throw new Error("Invalid password.");
|
|
38548
|
-
}
|
|
38549
|
-
}
|
|
38550
|
-
async function loadManagerSigner(safe) {
|
|
38551
|
-
const passphrase = process.env.SAIL_PASSPHRASE;
|
|
38552
|
-
if (passphrase) {
|
|
38553
|
-
const keystore = readJsonFile(resolveKeyPath("manager", safe));
|
|
38554
|
-
if (!keystore) {
|
|
38555
|
-
throw new Error(
|
|
38556
|
-
'No agent wallet found.\nRun "sailor keys generate" and choose "agent wallet".'
|
|
38557
|
-
);
|
|
38558
|
-
}
|
|
38559
|
-
return LocalKeyring.fromKeystore(keystore, passphrase);
|
|
38560
|
-
}
|
|
38561
|
-
return loadKeyring("manager", safe);
|
|
38562
|
-
}
|
|
38563
|
-
function managerKeystorePath(managerAddr) {
|
|
38564
|
-
if (!isAddress(managerAddr, { strict: false })) {
|
|
38565
|
-
throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
|
|
38566
|
-
}
|
|
38567
|
-
const hex = managerAddr.toLowerCase().replace(/^0x/, "");
|
|
38568
|
-
return sailPath("keys", "managers", `${hex}.json`);
|
|
38569
|
-
}
|
|
38570
|
-
async function loadAnySigner() {
|
|
38571
|
-
if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
|
|
38572
|
-
if (keyExists("manager")) return loadKeyring("manager");
|
|
38573
|
-
throw new Error('No signing key found.\nRun "sailor keys generate" first.');
|
|
38574
|
-
}
|
|
38575
|
-
|
|
38576
38491
|
// src/lib/packagePaths.ts
|
|
38577
38492
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
38578
38493
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
@@ -39330,86 +39245,6 @@ async function createSigningChannel(projectRoot = process.cwd()) {
|
|
|
39330
39245
|
}
|
|
39331
39246
|
|
|
39332
39247
|
// src/commands/account.ts
|
|
39333
|
-
function resolveChain(chainId) {
|
|
39334
|
-
try {
|
|
39335
|
-
return getChain(chainId);
|
|
39336
|
-
} catch {
|
|
39337
|
-
throw new Error(
|
|
39338
|
-
`Chain ${chainId} is not yet configured in @sail/chains.
|
|
39339
|
-
The SailKernel and mandate-factory addresses for this chain are unknown,
|
|
39340
|
-
so an account cannot be created yet. Add the chain to @sail/chains once
|
|
39341
|
-
SailKernel is deployed there.`
|
|
39342
|
-
);
|
|
39343
|
-
}
|
|
39344
|
-
}
|
|
39345
|
-
async function accountCreate() {
|
|
39346
|
-
if (!keyExists("manager")) {
|
|
39347
|
-
throw new Error(
|
|
39348
|
-
'No agent wallet found.\nRun "sailor keys generate" and choose "agent wallet" first.'
|
|
39349
|
-
);
|
|
39350
|
-
}
|
|
39351
|
-
const env = parseEnvFile(sailPath(".env.local"));
|
|
39352
|
-
const rpcUrl = env["RPC_URL"] ?? process.env["RPC_URL"];
|
|
39353
|
-
const chainIdRaw = env["CHAIN_ID"] ?? process.env["CHAIN_ID"];
|
|
39354
|
-
if (!rpcUrl || !chainIdRaw) {
|
|
39355
|
-
throw new Error(
|
|
39356
|
-
"RPC_URL and CHAIN_ID must be set in .sail/.env.local.\nCreate that file with, for example:\n RPC_URL=https://your-rpc-endpoint\n CHAIN_ID=8453"
|
|
39357
|
-
);
|
|
39358
|
-
}
|
|
39359
|
-
const chainId = Number(chainIdRaw);
|
|
39360
|
-
if (Number.isNaN(chainId)) {
|
|
39361
|
-
throw new Error(`Invalid CHAIN_ID: "${chainIdRaw}" \u2014 must be a number.`);
|
|
39362
|
-
}
|
|
39363
|
-
const chain2 = resolveChain(chainId);
|
|
39364
|
-
console.log(`Chain ${chainId} (${chain2.name})`);
|
|
39365
|
-
console.log(` SailKernel: ${checksum4(chain2.kernel)}`);
|
|
39366
|
-
console.log(` Mandate factory: ${checksum4(chain2.mandateFactory)}
|
|
39367
|
-
`);
|
|
39368
|
-
const manager = await loadKeyring("manager");
|
|
39369
|
-
const managerAddr = checksum4(manager.address);
|
|
39370
|
-
const safeFactory = await promptAddress("Safe factory address");
|
|
39371
|
-
const safeSingleton = await promptAddress("Safe singleton address");
|
|
39372
|
-
const owner2 = await promptAddress("Owner (EOA) address", managerAddr);
|
|
39373
|
-
const permissionSigner = await promptAddress("Mandate signer address", managerAddr);
|
|
39374
|
-
const feePolicy = await prompt("Fee policy", "none");
|
|
39375
|
-
console.log("\nCreating SMA with:");
|
|
39376
|
-
console.log(` Owner: ${owner2}`);
|
|
39377
|
-
console.log(` Agent wallet: ${managerAddr}`);
|
|
39378
|
-
console.log(` Mandate signer: ${permissionSigner}`);
|
|
39379
|
-
console.log(` Safe factory: ${safeFactory}`);
|
|
39380
|
-
console.log(` Safe singleton: ${safeSingleton}`);
|
|
39381
|
-
console.log(` Fee policy: ${feePolicy}`);
|
|
39382
|
-
const client = makeClient(chainId);
|
|
39383
|
-
try {
|
|
39384
|
-
const account2 = await client.account.create({
|
|
39385
|
-
owner: owner2,
|
|
39386
|
-
permissionSigner,
|
|
39387
|
-
manager: managerAddr,
|
|
39388
|
-
chainId
|
|
39389
|
-
});
|
|
39390
|
-
const stored = {
|
|
39391
|
-
safe: checksum4(account2.safe),
|
|
39392
|
-
owner: checksum4(account2.owner),
|
|
39393
|
-
permissionSigner: checksum4(account2.permissionSigner),
|
|
39394
|
-
manager: checksum4(account2.manager),
|
|
39395
|
-
chainId: account2.chainId,
|
|
39396
|
-
createdAtBlock: account2.createdAtBlock.toString()
|
|
39397
|
-
};
|
|
39398
|
-
upsertAccountInList(stored);
|
|
39399
|
-
writeJsonFile(sailPath("account.json"), stored);
|
|
39400
|
-
console.log(`
|
|
39401
|
-
SMA created. Address: ${stored.safe}`);
|
|
39402
|
-
console.log("Saved to .sail/account.json");
|
|
39403
|
-
} catch (err) {
|
|
39404
|
-
if (err.message === "not implemented") {
|
|
39405
|
-
console.log(
|
|
39406
|
-
"\nOn-chain account creation is not wired up in this build yet \u2014\nclient.account.create is a stub until SailKernel is deployed and the\nSDK is connected. Nothing was created on-chain."
|
|
39407
|
-
);
|
|
39408
|
-
return;
|
|
39409
|
-
}
|
|
39410
|
-
throw err;
|
|
39411
|
-
}
|
|
39412
|
-
}
|
|
39413
39248
|
var SAIL_MAINNET_CHAINS = [1, 8453, 42161, 130];
|
|
39414
39249
|
async function fetchProxyCreationCode(preferredChainId) {
|
|
39415
39250
|
const rpcUrl = getRpcUrl(preferredChainId) ?? void 0;
|
|
@@ -39801,6 +39636,89 @@ function emit(json, human, payload) {
|
|
|
39801
39636
|
|
|
39802
39637
|
// src/lib/project.ts
|
|
39803
39638
|
init_esm2();
|
|
39639
|
+
|
|
39640
|
+
// src/lib/keys.ts
|
|
39641
|
+
init_esm2();
|
|
39642
|
+
var ROLES = ["manager", "permissionSigner"];
|
|
39643
|
+
function normalizeRole(input) {
|
|
39644
|
+
const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
|
|
39645
|
+
if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
|
|
39646
|
+
return "manager";
|
|
39647
|
+
}
|
|
39648
|
+
if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
|
|
39649
|
+
return "permissionSigner";
|
|
39650
|
+
}
|
|
39651
|
+
return null;
|
|
39652
|
+
}
|
|
39653
|
+
function roleLabel(role) {
|
|
39654
|
+
return role === "manager" ? "agent wallet" : "mandate signer";
|
|
39655
|
+
}
|
|
39656
|
+
function safeHex(safe) {
|
|
39657
|
+
return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
|
|
39658
|
+
}
|
|
39659
|
+
function keyPath(role, safe) {
|
|
39660
|
+
if (safe) {
|
|
39661
|
+
return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
|
|
39662
|
+
}
|
|
39663
|
+
return sailPath("keys", `${role}.json`);
|
|
39664
|
+
}
|
|
39665
|
+
function legacyKeyPath(role, safe) {
|
|
39666
|
+
return sailPath("keys", `${role}-${safeHex(safe)}.json`);
|
|
39667
|
+
}
|
|
39668
|
+
function resolveKeyPath(role, safe) {
|
|
39669
|
+
if (safe) {
|
|
39670
|
+
const perSma = keyPath(role, safe);
|
|
39671
|
+
if (fileExists(perSma)) return perSma;
|
|
39672
|
+
const legacy = legacyKeyPath(role, safe);
|
|
39673
|
+
if (fileExists(legacy)) return legacy;
|
|
39674
|
+
}
|
|
39675
|
+
return keyPath(role);
|
|
39676
|
+
}
|
|
39677
|
+
function keyExists(role, safe) {
|
|
39678
|
+
return fileExists(resolveKeyPath(role, safe));
|
|
39679
|
+
}
|
|
39680
|
+
async function loadKeyring(role, safe) {
|
|
39681
|
+
const keystore = readJsonFile(resolveKeyPath(role, safe));
|
|
39682
|
+
if (!keystore) {
|
|
39683
|
+
throw new Error(
|
|
39684
|
+
`No ${roleLabel(role)} found.
|
|
39685
|
+
Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
|
|
39686
|
+
);
|
|
39687
|
+
}
|
|
39688
|
+
const password = await promptHidden(`Password for ${roleLabel(role)} key`);
|
|
39689
|
+
try {
|
|
39690
|
+
return await LocalKeyring.fromKeystore(keystore, password);
|
|
39691
|
+
} catch {
|
|
39692
|
+
throw new Error("Invalid password.");
|
|
39693
|
+
}
|
|
39694
|
+
}
|
|
39695
|
+
async function loadManagerSigner(safe) {
|
|
39696
|
+
const passphrase = process.env.SAIL_PASSPHRASE;
|
|
39697
|
+
if (passphrase) {
|
|
39698
|
+
const keystore = readJsonFile(resolveKeyPath("manager", safe));
|
|
39699
|
+
if (!keystore) {
|
|
39700
|
+
throw new Error(
|
|
39701
|
+
'No agent wallet found.\nRun "sailor keys generate" and choose "agent wallet".'
|
|
39702
|
+
);
|
|
39703
|
+
}
|
|
39704
|
+
return LocalKeyring.fromKeystore(keystore, passphrase);
|
|
39705
|
+
}
|
|
39706
|
+
return loadKeyring("manager", safe);
|
|
39707
|
+
}
|
|
39708
|
+
function managerKeystorePath(managerAddr) {
|
|
39709
|
+
if (!isAddress(managerAddr, { strict: false })) {
|
|
39710
|
+
throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
|
|
39711
|
+
}
|
|
39712
|
+
const hex = managerAddr.toLowerCase().replace(/^0x/, "");
|
|
39713
|
+
return sailPath("keys", "managers", `${hex}.json`);
|
|
39714
|
+
}
|
|
39715
|
+
async function loadAnySigner() {
|
|
39716
|
+
if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
|
|
39717
|
+
if (keyExists("manager")) return loadKeyring("manager");
|
|
39718
|
+
throw new Error('No signing key found.\nRun "sailor keys generate" first.');
|
|
39719
|
+
}
|
|
39720
|
+
|
|
39721
|
+
// src/lib/project.ts
|
|
39804
39722
|
function nonEmpty(value) {
|
|
39805
39723
|
return typeof value === "string" && value.trim().length > 0;
|
|
39806
39724
|
}
|
|
@@ -40480,6 +40398,40 @@ interface IPermission {
|
|
|
40480
40398
|
function discriminator() external view returns (bytes32);
|
|
40481
40399
|
}
|
|
40482
40400
|
`;
|
|
40401
|
+
var IBATCHPERMISSION_SOL = `// SPDX-License-Identifier: MIT
|
|
40402
|
+
pragma solidity 0.8.26;
|
|
40403
|
+
|
|
40404
|
+
/// @notice A single subcall in a batch dispatch.
|
|
40405
|
+
/// @dev The kernel executes each Call as safe.execTransactionFromModule(target, value, data, 0)
|
|
40406
|
+
/// \u2014 operation is always CALL, never DELEGATECALL.
|
|
40407
|
+
struct Call {
|
|
40408
|
+
address target; // MUST NOT equal the kernel (enforced by the kernel)
|
|
40409
|
+
uint256 value; // native ETH forwarded (wei)
|
|
40410
|
+
bytes data; // calldata for the subcall
|
|
40411
|
+
}
|
|
40412
|
+
|
|
40413
|
+
/// @notice Execution context passed to evaluateBatch on each batch dispatch (read-only snapshot).
|
|
40414
|
+
struct BatchContext {
|
|
40415
|
+
address account; // the Safe (SMA) whose assets are moved
|
|
40416
|
+
address manager; // the delegated signer who authorised the batch
|
|
40417
|
+
address submitter; // msg.sender of the dispatch (may differ from manager via a relayer)
|
|
40418
|
+
address permission; // this batch permission contract
|
|
40419
|
+
bytes32 batchHash; // keccak256(abi.encode(calls)) \u2014 stable id for the exact sequence
|
|
40420
|
+
uint256 blockTimestamp; // block.timestamp at dispatch
|
|
40421
|
+
uint256 blockNumber; // block.number at dispatch
|
|
40422
|
+
}
|
|
40423
|
+
|
|
40424
|
+
/// @title IBatchPermission
|
|
40425
|
+
/// @notice Gates an atomic batch of subcalls. Evaluated via staticcall under a gas cap;
|
|
40426
|
+
/// a revert / OOG / malformed return is treated as \`false\` (fail-closed).
|
|
40427
|
+
interface IBatchPermission {
|
|
40428
|
+
/// @notice Validate an entire batch of subcalls and their cross-call invariants.
|
|
40429
|
+
function evaluateBatch(Call[] calldata calls, BatchContext calldata ctx) external view returns (bool);
|
|
40430
|
+
|
|
40431
|
+
/// @notice Marker the kernel uses (try/catch) to detect batch-aware permissions. MUST return true.
|
|
40432
|
+
function isBatchPermission() external pure returns (bool);
|
|
40433
|
+
}
|
|
40434
|
+
`;
|
|
40483
40435
|
var EXAMPLE_MANDATE_SOL = `// SPDX-License-Identifier: MIT
|
|
40484
40436
|
pragma solidity 0.8.26;
|
|
40485
40437
|
|
|
@@ -40561,6 +40513,10 @@ function scaffoldFoundryWorkspace(root) {
|
|
|
40561
40513
|
(0, import_node_path6.join)(root, ".sail", "contracts", "interfaces", "IPermission.sol"),
|
|
40562
40514
|
IPERMISSION_SOL
|
|
40563
40515
|
);
|
|
40516
|
+
writeIfMissing(
|
|
40517
|
+
(0, import_node_path6.join)(root, ".sail", "contracts", "interfaces", "IBatchPermission.sol"),
|
|
40518
|
+
IBATCHPERMISSION_SOL
|
|
40519
|
+
);
|
|
40564
40520
|
writeIfMissing((0, import_node_path6.join)(root, "mandates", "BoundedCallPermission.sol"), EXAMPLE_MANDATE_SOL);
|
|
40565
40521
|
writeIfMissing((0, import_node_path6.join)(root, "mandates", "README.md"), MANDATES_README);
|
|
40566
40522
|
}
|
|
@@ -40722,6 +40678,10 @@ run from the repo root.` : ` Available: ${available}`;
|
|
|
40722
40678
|
if (import_node_fs8.default.existsSync(examplesPermSrc)) {
|
|
40723
40679
|
copyDirSync(examplesPermSrc, import_node_path7.default.join(dest, "examples", "permissions"));
|
|
40724
40680
|
}
|
|
40681
|
+
const customMandateSrc = import_node_path7.default.join(pkgRoot, "examples", "custom-mandate");
|
|
40682
|
+
if (import_node_fs8.default.existsSync(customMandateSrc)) {
|
|
40683
|
+
copyDirSync(customMandateSrc, import_node_path7.default.join(dest, "examples", "custom-mandate"));
|
|
40684
|
+
}
|
|
40725
40685
|
const permModelSrc = import_node_path7.default.join(pkgRoot, "docs", "PERMISSION_MODEL.md");
|
|
40726
40686
|
if (import_node_fs8.default.existsSync(permModelSrc)) {
|
|
40727
40687
|
import_node_fs8.default.mkdirSync(import_node_path7.default.join(dest, "docs"), { recursive: true });
|
|
@@ -40849,7 +40809,7 @@ Created ${name}/`);
|
|
|
40849
40809
|
"\u2551 \u2551",
|
|
40850
40810
|
"\u2551 DO THIS FIRST: \u2551",
|
|
40851
40811
|
"\u2551 1. Read AGENTS.md in the project root (FULL FILE, not a skim) \u2551",
|
|
40852
|
-
"\u2551 2.
|
|
40812
|
+
"\u2551 2. Follow it \u2014 the detailed steps live in .agents/skills/ \u2551",
|
|
40853
40813
|
'\u2551 3. Confirm to the user: "I have read AGENTS.md and am ready." \u2551',
|
|
40854
40814
|
"\u2551 \u2551",
|
|
40855
40815
|
"\u2551 If you skip this step, setup WILL break and you will have to \u2551",
|
|
@@ -42317,7 +42277,7 @@ function mandateTemplates(options) {
|
|
|
42317
42277
|
console.log(" 2. Implement IPermission.evaluate(txData, ctx) with your policy logic");
|
|
42318
42278
|
console.log(" 3. forge build");
|
|
42319
42279
|
console.log(" 4. sailor mandate deploy --contract <Name> --attach --sma <yourSMA>");
|
|
42320
|
-
console.log("\n See
|
|
42280
|
+
console.log("\n See examples/custom-mandate/README.md for the full guide.");
|
|
42321
42281
|
if (community.length > 0) {
|
|
42322
42282
|
console.log(
|
|
42323
42283
|
"\nCommunity-deployed permission contracts (informational \u2014 NOT audited or\nendorsed by Sail; review the source before registering any of them):"
|
|
@@ -42599,7 +42559,7 @@ function printNoPermissionsGuidance() {
|
|
|
42599
42559
|
async function mandatePrepare() {
|
|
42600
42560
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
42601
42561
|
if (!account2) {
|
|
42602
|
-
throw new Error('No account found at .sail/account.json.\nRun "sailor
|
|
42562
|
+
throw new Error('No account found at .sail/account.json.\nRun "sailor onboard --new-sma" first.');
|
|
42603
42563
|
}
|
|
42604
42564
|
const permissions = await trackedPermissionsFor(account2);
|
|
42605
42565
|
if (permissions.length === 0) {
|
|
@@ -42632,7 +42592,7 @@ ${permissions.length} permission(s) tracked for SMA ${account2.safe}:
|
|
|
42632
42592
|
async function mandateSign(opts = {}) {
|
|
42633
42593
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
42634
42594
|
if (!account2) {
|
|
42635
|
-
throw new Error('No account found at .sail/account.json.\nRun "sailor
|
|
42595
|
+
throw new Error('No account found at .sail/account.json.\nRun "sailor onboard --new-sma" first.');
|
|
42636
42596
|
}
|
|
42637
42597
|
const permissions = await trackedPermissionsFor(account2);
|
|
42638
42598
|
if (permissions.length === 0) {
|
|
@@ -43575,7 +43535,7 @@ async function runCommand(opts) {
|
|
|
43575
43535
|
const once = opts.once === true;
|
|
43576
43536
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
43577
43537
|
if (!account2) {
|
|
43578
|
-
throw new Error('No account found at .sail/account.json.\nRun "sailor
|
|
43538
|
+
throw new Error('No account found at .sail/account.json.\nRun "sailor onboard --new-sma" first.');
|
|
43579
43539
|
}
|
|
43580
43540
|
const mandateRaw = readJsonFile(sailPath("mandate.json"));
|
|
43581
43541
|
const mandate2 = Array.isArray(mandateRaw) ? mandateRaw[0] : mandateRaw;
|
|
@@ -43638,7 +43598,7 @@ async function runCommand(opts) {
|
|
|
43638
43598
|
if (!kernel) {
|
|
43639
43599
|
throw new Error(
|
|
43640
43600
|
`No SailKernel address for chain ${chainId}.
|
|
43641
|
-
Configure the chain in
|
|
43601
|
+
Configure the chain in the SDK chain registry or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
43642
43602
|
);
|
|
43643
43603
|
}
|
|
43644
43604
|
const manager = await loadManagerSigner(account2.safe);
|
|
@@ -44029,7 +43989,7 @@ function requireAccount() {
|
|
|
44029
43989
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
44030
43990
|
if (!account2) {
|
|
44031
43991
|
throw new Error(
|
|
44032
|
-
'No account found at .sail/account.json.\nRun "sailor
|
|
43992
|
+
'No account found at .sail/account.json.\nRun "sailor onboard --new-sma" first.'
|
|
44033
43993
|
);
|
|
44034
43994
|
}
|
|
44035
43995
|
return account2;
|
|
@@ -44197,7 +44157,7 @@ async function status() {
|
|
|
44197
44157
|
if (account2) {
|
|
44198
44158
|
console.log(` \u2713 deployed ${checksum4(account2.safe)} (chain ${account2.chainId})`);
|
|
44199
44159
|
} else {
|
|
44200
|
-
console.log(' \u2717 not deployed run "sailor
|
|
44160
|
+
console.log(' \u2717 not deployed run "sailor onboard --new-sma"');
|
|
44201
44161
|
}
|
|
44202
44162
|
console.log("Mandate:");
|
|
44203
44163
|
if (mandate2) {
|
|
@@ -44316,8 +44276,16 @@ function uiStop() {
|
|
|
44316
44276
|
}
|
|
44317
44277
|
|
|
44318
44278
|
// src/index.ts
|
|
44279
|
+
function cliVersion() {
|
|
44280
|
+
try {
|
|
44281
|
+
const pkg = JSON.parse((0, import_node_fs18.readFileSync)((0, import_node_path14.join)(packageRoot(), "package.json"), "utf-8"));
|
|
44282
|
+
return pkg.version ?? "0.0.0";
|
|
44283
|
+
} catch {
|
|
44284
|
+
return "0.0.0";
|
|
44285
|
+
}
|
|
44286
|
+
}
|
|
44319
44287
|
var program2 = new Command();
|
|
44320
|
-
program2.name("sailor").description("Operator toolkit for Sail Protocol").version(
|
|
44288
|
+
program2.name("sailor").description("Operator toolkit for Sail Protocol").version(cliVersion());
|
|
44321
44289
|
function action(fn) {
|
|
44322
44290
|
return async () => {
|
|
44323
44291
|
try {
|
|
@@ -44364,7 +44332,6 @@ keys.command("export-ci").description(
|
|
|
44364
44332
|
"Copy the encrypted agent wallet keystore to ci-keystore.json for committing to CI"
|
|
44365
44333
|
).action(action(keysExportCi));
|
|
44366
44334
|
var account = program2.command("account").description("Manage the Sail SMA");
|
|
44367
|
-
account.command("create").description("Create a new Sail SMA on-chain").action(action(accountCreate));
|
|
44368
44335
|
account.command("predict").description(
|
|
44369
44336
|
"Compute the deterministic Safe address for a given owner + manager + salt (no gas, no deployment)"
|
|
44370
44337
|
).option("--owner <address>", "Owner EOA address (defaults to .sail/account.json)").option(
|
|
@@ -44416,12 +44383,6 @@ program2.command("capabilities").description(
|
|
|
44416
44383
|
"Feasibility map (read-only): chains, kernel model, mandate templates, strategy primitives"
|
|
44417
44384
|
).option("--json", "Emit machine-readable JSON").action(actionWith(capabilities));
|
|
44418
44385
|
program2.command("chains").description("List supported chains and their SailKernel deployment addresses").option("--verify", "Verify each kernel is deployed via eth_getCode (one RPC call per chain)").option("--json", "Emit machine-readable JSON").action(actionWith(chainsCommand));
|
|
44419
|
-
function stub(name, description) {
|
|
44420
|
-
program2.command(name).description(description).allowUnknownOption().action(() => {
|
|
44421
|
-
console.log(`sailor ${name}: not implemented yet`);
|
|
44422
|
-
});
|
|
44423
|
-
}
|
|
44424
|
-
stub("dispatch preview", "Preview a dispatch without submitting");
|
|
44425
44386
|
program2.parse(process.argv);
|
|
44426
44387
|
/*! Bundled license information:
|
|
44427
44388
|
|