@dev.sail.money/sailor 1.2.0-74 → 1.2.0-76
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/README.md +4 -1
- package/examples/permissions/BoundedSwapNative_UniswapV3_Base.sol +123 -0
- package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +20 -18
- package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +17 -12
- package/examples/permissions/README.md +10 -0
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +401 -244
- package/packages/cli/dist/server.cjs +29 -1
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/scripts/check-docs.mjs +1 -1
- package/scripts/check-init.mjs +16 -13
- package/scripts/check-update.mjs +177 -0
- package/templates/default/.agents/skills/sail-automation/SKILL.md +50 -0
- package/templates/default/.agents/skills/sail-automation/references/docker-vm.md +113 -0
- package/templates/default/.agents/skills/sail-automation/references/github-actions.md +50 -0
- package/templates/default/.agents/skills/sail-automation/references/local-daemon.md +34 -0
- package/templates/default/.agents/skills/sail-automation/references/self-hosted-runner.md +72 -0
- package/templates/default/.agents/skills/sail-mandates/references/examples-index.md +5 -2
- package/templates/default/.agents/skills/sail-onboarding/SKILL.md +2 -0
- package/templates/default/AGENTS.md +1 -1
- package/templates/default/Dockerfile +18 -0
- package/templates/default/_dockerignore +15 -0
- package/templates/default/.agents/skills/sail-ci/SKILL.md +0 -63
|
@@ -966,8 +966,8 @@ var require_command = __commonJS({
|
|
|
966
966
|
"../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports2) {
|
|
967
967
|
var EventEmitter = require("node:events").EventEmitter;
|
|
968
968
|
var childProcess = require("node:child_process");
|
|
969
|
-
var
|
|
970
|
-
var
|
|
969
|
+
var path11 = require("node:path");
|
|
970
|
+
var fs12 = require("node:fs");
|
|
971
971
|
var process2 = require("node:process");
|
|
972
972
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
973
973
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1899,11 +1899,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1899
1899
|
let launchWithNode = false;
|
|
1900
1900
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1901
1901
|
function findFile(baseDir, baseName) {
|
|
1902
|
-
const localBin =
|
|
1903
|
-
if (
|
|
1904
|
-
if (sourceExt.includes(
|
|
1902
|
+
const localBin = path11.resolve(baseDir, baseName);
|
|
1903
|
+
if (fs12.existsSync(localBin)) return localBin;
|
|
1904
|
+
if (sourceExt.includes(path11.extname(baseName))) return void 0;
|
|
1905
1905
|
const foundExt = sourceExt.find(
|
|
1906
|
-
(ext) =>
|
|
1906
|
+
(ext) => fs12.existsSync(`${localBin}${ext}`)
|
|
1907
1907
|
);
|
|
1908
1908
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1909
1909
|
return void 0;
|
|
@@ -1915,21 +1915,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1915
1915
|
if (this._scriptPath) {
|
|
1916
1916
|
let resolvedScriptPath;
|
|
1917
1917
|
try {
|
|
1918
|
-
resolvedScriptPath =
|
|
1918
|
+
resolvedScriptPath = fs12.realpathSync(this._scriptPath);
|
|
1919
1919
|
} catch (err) {
|
|
1920
1920
|
resolvedScriptPath = this._scriptPath;
|
|
1921
1921
|
}
|
|
1922
|
-
executableDir =
|
|
1923
|
-
|
|
1922
|
+
executableDir = path11.resolve(
|
|
1923
|
+
path11.dirname(resolvedScriptPath),
|
|
1924
1924
|
executableDir
|
|
1925
1925
|
);
|
|
1926
1926
|
}
|
|
1927
1927
|
if (executableDir) {
|
|
1928
1928
|
let localFile = findFile(executableDir, executableFile);
|
|
1929
1929
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1930
|
-
const legacyName =
|
|
1930
|
+
const legacyName = path11.basename(
|
|
1931
1931
|
this._scriptPath,
|
|
1932
|
-
|
|
1932
|
+
path11.extname(this._scriptPath)
|
|
1933
1933
|
);
|
|
1934
1934
|
if (legacyName !== this._name) {
|
|
1935
1935
|
localFile = findFile(
|
|
@@ -1940,7 +1940,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1940
1940
|
}
|
|
1941
1941
|
executableFile = localFile || executableFile;
|
|
1942
1942
|
}
|
|
1943
|
-
launchWithNode = sourceExt.includes(
|
|
1943
|
+
launchWithNode = sourceExt.includes(path11.extname(executableFile));
|
|
1944
1944
|
let proc;
|
|
1945
1945
|
if (process2.platform !== "win32") {
|
|
1946
1946
|
if (launchWithNode) {
|
|
@@ -2780,7 +2780,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2780
2780
|
* @return {Command}
|
|
2781
2781
|
*/
|
|
2782
2782
|
nameFromFilename(filename) {
|
|
2783
|
-
this._name =
|
|
2783
|
+
this._name = path11.basename(filename, path11.extname(filename));
|
|
2784
2784
|
return this;
|
|
2785
2785
|
}
|
|
2786
2786
|
/**
|
|
@@ -2794,9 +2794,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2794
2794
|
* @param {string} [path]
|
|
2795
2795
|
* @return {(string|null|Command)}
|
|
2796
2796
|
*/
|
|
2797
|
-
executableDir(
|
|
2798
|
-
if (
|
|
2799
|
-
this._executableDir =
|
|
2797
|
+
executableDir(path12) {
|
|
2798
|
+
if (path12 === void 0) return this._executableDir;
|
|
2799
|
+
this._executableDir = path12;
|
|
2800
2800
|
return this;
|
|
2801
2801
|
}
|
|
2802
2802
|
/**
|
|
@@ -30772,9 +30772,9 @@ var init_defineKzg = __esm({
|
|
|
30772
30772
|
});
|
|
30773
30773
|
|
|
30774
30774
|
// ../../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/kzg/setupKzg.js
|
|
30775
|
-
function setupKzg(parameters,
|
|
30775
|
+
function setupKzg(parameters, path11) {
|
|
30776
30776
|
try {
|
|
30777
|
-
parameters.loadTrustedSetup(
|
|
30777
|
+
parameters.loadTrustedSetup(path11);
|
|
30778
30778
|
} catch (e) {
|
|
30779
30779
|
const error = e;
|
|
30780
30780
|
if (!error.message.includes("trusted setup is already loaded"))
|
|
@@ -35190,8 +35190,8 @@ var require_websocket_server2 = __commonJS({
|
|
|
35190
35190
|
});
|
|
35191
35191
|
|
|
35192
35192
|
// src/index.ts
|
|
35193
|
-
var
|
|
35194
|
-
var
|
|
35193
|
+
var import_node_fs21 = require("node:fs");
|
|
35194
|
+
var import_node_path17 = 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);
|
|
@@ -37123,14 +37123,14 @@ var HDKey = class _HDKey {
|
|
|
37123
37123
|
}
|
|
37124
37124
|
this.pubHash = hash160(this.pubKey);
|
|
37125
37125
|
}
|
|
37126
|
-
derive(
|
|
37127
|
-
if (!/^[mM]'?/.test(
|
|
37126
|
+
derive(path11) {
|
|
37127
|
+
if (!/^[mM]'?/.test(path11)) {
|
|
37128
37128
|
throw new Error('Path must start with "m" or "M"');
|
|
37129
37129
|
}
|
|
37130
|
-
if (/^[mM]'?$/.test(
|
|
37130
|
+
if (/^[mM]'?$/.test(path11)) {
|
|
37131
37131
|
return this;
|
|
37132
37132
|
}
|
|
37133
|
-
const parts =
|
|
37133
|
+
const parts = path11.replace(/^[mM]'?\//, "").split("/");
|
|
37134
37134
|
let child = this;
|
|
37135
37135
|
for (const c of parts) {
|
|
37136
37136
|
const m = /^(\d+)('?)$/.exec(c);
|
|
@@ -37462,8 +37462,8 @@ function privateKeyToAccount(privateKey, options = {}) {
|
|
|
37462
37462
|
}
|
|
37463
37463
|
|
|
37464
37464
|
// ../../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/accounts/hdKeyToAccount.js
|
|
37465
|
-
function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path:
|
|
37466
|
-
const hdKey = hdKey_.derive(
|
|
37465
|
+
function hdKeyToAccount(hdKey_, { accountIndex = 0, addressIndex = 0, changeIndex = 0, path: path11, ...options } = {}) {
|
|
37466
|
+
const hdKey = hdKey_.derive(path11 || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
|
|
37467
37467
|
const account2 = privateKeyToAccount(toHex(hdKey.privateKey), options);
|
|
37468
37468
|
return {
|
|
37469
37469
|
...account2,
|
|
@@ -37564,8 +37564,8 @@ var LocalKeyring = class _LocalKeyring {
|
|
|
37564
37564
|
return _LocalKeyring.fromPrivateKey(`0x${pkBytes.toString("hex")}`);
|
|
37565
37565
|
}
|
|
37566
37566
|
/** Loads a keyring from an encrypted JSON keystore file on disk. */
|
|
37567
|
-
static async fromKeystoreFile(
|
|
37568
|
-
const keystore = JSON.parse((0, import_node_fs.readFileSync)(
|
|
37567
|
+
static async fromKeystoreFile(path11, password) {
|
|
37568
|
+
const keystore = JSON.parse((0, import_node_fs.readFileSync)(path11, "utf-8"));
|
|
37569
37569
|
return _LocalKeyring.fromKeystore(keystore, password);
|
|
37570
37570
|
}
|
|
37571
37571
|
/** Signs a raw 32-byte hash. Returns a 65-byte ECDSA signature. */
|
|
@@ -38686,9 +38686,13 @@ var SigningServer = class {
|
|
|
38686
38686
|
http2.once("error", rej);
|
|
38687
38687
|
});
|
|
38688
38688
|
this.httpServer = http2;
|
|
38689
|
-
if (this.advertise)
|
|
38689
|
+
if (this.advertise) {
|
|
38690
|
+
reapStaleRuntimeState(this.projectRoot);
|
|
38691
|
+
this.writeRuntimeState();
|
|
38692
|
+
}
|
|
38690
38693
|
process.once("SIGINT", () => this.stop());
|
|
38691
38694
|
process.once("SIGTERM", () => this.stop());
|
|
38695
|
+
process.once("exit", () => this.stop());
|
|
38692
38696
|
}
|
|
38693
38697
|
stop() {
|
|
38694
38698
|
for (const [id, entry] of this.pending) {
|
|
@@ -38833,6 +38837,28 @@ var SigningServer = class {
|
|
|
38833
38837
|
sailFile(...segments) {
|
|
38834
38838
|
return (0, import_node_path4.join)(this.projectRoot, ".sail", ...segments);
|
|
38835
38839
|
}
|
|
38840
|
+
/**
|
|
38841
|
+
* Persist the active chain into config.json. The onboarding stage machine keys
|
|
38842
|
+
* off config.json.chainId; leaving it null after SMA creation misclassifies the
|
|
38843
|
+
* stage on resume. Best-effort — never blocks the account save on a config write.
|
|
38844
|
+
*/
|
|
38845
|
+
syncConfigChainId(chainId) {
|
|
38846
|
+
if (chainId == null) return;
|
|
38847
|
+
try {
|
|
38848
|
+
const path11 = this.sailFile("config.json");
|
|
38849
|
+
let config = {};
|
|
38850
|
+
try {
|
|
38851
|
+
config = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf-8"));
|
|
38852
|
+
} catch {
|
|
38853
|
+
}
|
|
38854
|
+
if (Number(config.chainId) === Number(chainId)) return;
|
|
38855
|
+
config.chainId = Number(chainId);
|
|
38856
|
+
(0, import_node_fs5.mkdirSync)(this.sailFile(), { recursive: true });
|
|
38857
|
+
(0, import_node_fs5.writeFileSync)(path11, `${JSON.stringify(config, null, 2)}
|
|
38858
|
+
`);
|
|
38859
|
+
} catch {
|
|
38860
|
+
}
|
|
38861
|
+
}
|
|
38836
38862
|
/** Stream a JSON file back, or a fallback body when it is missing/invalid. */
|
|
38837
38863
|
sendJsonFile(res, filePath, fallback2) {
|
|
38838
38864
|
try {
|
|
@@ -38875,6 +38901,7 @@ var SigningServer = class {
|
|
|
38875
38901
|
(0, import_node_fs5.mkdirSync)(baseSailDir, { recursive: true });
|
|
38876
38902
|
(0, import_node_fs5.writeFileSync)(this.sailFile("account.json"), `${JSON.stringify(record, null, 2)}
|
|
38877
38903
|
`);
|
|
38904
|
+
this.syncConfigChainId(chainId);
|
|
38878
38905
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
38879
38906
|
res.end(JSON.stringify({ ok: true }));
|
|
38880
38907
|
}).catch((err) => {
|
|
@@ -39123,6 +39150,14 @@ var SigningServer = class {
|
|
|
39123
39150
|
}
|
|
39124
39151
|
}
|
|
39125
39152
|
writeRuntimeState() {
|
|
39153
|
+
const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
|
|
39154
|
+
if ((0, import_node_fs5.existsSync)(path11)) {
|
|
39155
|
+
try {
|
|
39156
|
+
const existing = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
|
|
39157
|
+
if (existing.pid != null && existing.pid !== process.pid && pidAlive(existing.pid)) return;
|
|
39158
|
+
} catch {
|
|
39159
|
+
}
|
|
39160
|
+
}
|
|
39126
39161
|
if (!(0, import_node_fs5.existsSync)(this.runtimeDir)) (0, import_node_fs5.mkdirSync)(this.runtimeDir, { recursive: true });
|
|
39127
39162
|
(0, import_node_fs5.writeFileSync)(
|
|
39128
39163
|
(0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE),
|
|
@@ -39141,13 +39176,35 @@ var SigningServer = class {
|
|
|
39141
39176
|
);
|
|
39142
39177
|
}
|
|
39143
39178
|
removeRuntimeState() {
|
|
39144
|
-
const
|
|
39179
|
+
const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
|
|
39145
39180
|
try {
|
|
39146
|
-
if ((0, import_node_fs5.existsSync)(
|
|
39181
|
+
if (!(0, import_node_fs5.existsSync)(path11)) return;
|
|
39182
|
+
const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
|
|
39183
|
+
if (state.pid != null && state.pid !== process.pid) return;
|
|
39184
|
+
(0, import_node_fs5.unlinkSync)(path11);
|
|
39147
39185
|
} catch {
|
|
39148
39186
|
}
|
|
39149
39187
|
}
|
|
39150
39188
|
};
|
|
39189
|
+
function pidAlive(pid) {
|
|
39190
|
+
try {
|
|
39191
|
+
process.kill(pid, 0);
|
|
39192
|
+
return true;
|
|
39193
|
+
} catch (err) {
|
|
39194
|
+
return err.code === "EPERM";
|
|
39195
|
+
}
|
|
39196
|
+
}
|
|
39197
|
+
function reapStaleRuntimeState(projectRoot = process.cwd()) {
|
|
39198
|
+
const path11 = (0, import_node_path4.join)(projectRoot, RUNTIME_SUBDIR, SERVER_STATE_FILE);
|
|
39199
|
+
try {
|
|
39200
|
+
if (!(0, import_node_fs5.existsSync)(path11)) return;
|
|
39201
|
+
const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
|
|
39202
|
+
if (state.pid != null && state.pid !== process.pid && !pidAlive(state.pid)) {
|
|
39203
|
+
(0, import_node_fs5.unlinkSync)(path11);
|
|
39204
|
+
}
|
|
39205
|
+
} catch {
|
|
39206
|
+
}
|
|
39207
|
+
}
|
|
39151
39208
|
async function findAvailablePort(startPort) {
|
|
39152
39209
|
return new Promise((res) => {
|
|
39153
39210
|
const probe = (0, import_node_net.createServer)();
|
|
@@ -39256,9 +39313,10 @@ async function discoverDaemon(projectRoot = process.cwd()) {
|
|
|
39256
39313
|
return await client.ping() ? client : null;
|
|
39257
39314
|
}
|
|
39258
39315
|
async function createSigningChannel(projectRoot = process.cwd()) {
|
|
39316
|
+
reapStaleRuntimeState(projectRoot);
|
|
39259
39317
|
const daemon = await discoverDaemon(projectRoot);
|
|
39260
39318
|
if (daemon) return daemon;
|
|
39261
|
-
return new SigningServer({ projectRoot, advertise:
|
|
39319
|
+
return new SigningServer({ projectRoot, advertise: true });
|
|
39262
39320
|
}
|
|
39263
39321
|
|
|
39264
39322
|
// src/commands/account.ts
|
|
@@ -40121,15 +40179,20 @@ async function resolvePermissionForBatch(params) {
|
|
|
40121
40179
|
}
|
|
40122
40180
|
|
|
40123
40181
|
// src/commands/doctor.ts
|
|
40124
|
-
var
|
|
40125
|
-
|
|
40182
|
+
var LOW_GAS_THRESHOLD_L1_WEI = 5000000000000000n;
|
|
40183
|
+
var LOW_GAS_THRESHOLD_L2_WEI = 200000000000000n;
|
|
40184
|
+
var L1_GAS_CHAINS = /* @__PURE__ */ new Set([1, 11155111]);
|
|
40185
|
+
function lowGasThresholdWei(chainId) {
|
|
40186
|
+
return L1_GAS_CHAINS.has(chainId) ? LOW_GAS_THRESHOLD_L1_WEI : LOW_GAS_THRESHOLD_L2_WEI;
|
|
40187
|
+
}
|
|
40188
|
+
async function nativeBalance(pc, address, chainId) {
|
|
40126
40189
|
const wei = await pc.getBalance({ address });
|
|
40127
40190
|
return {
|
|
40128
40191
|
address,
|
|
40129
40192
|
wei: wei.toString(),
|
|
40130
40193
|
eth: formatEther(wei),
|
|
40131
40194
|
funded: wei > 0n,
|
|
40132
|
-
low: wei > 0n && wei <
|
|
40195
|
+
low: wei > 0n && wei < lowGasThresholdWei(chainId)
|
|
40133
40196
|
};
|
|
40134
40197
|
}
|
|
40135
40198
|
function keystoreAddress(role, safe) {
|
|
@@ -40208,8 +40271,8 @@ async function doctor(options = {}) {
|
|
|
40208
40271
|
let ownerBal = null;
|
|
40209
40272
|
let managerBal = null;
|
|
40210
40273
|
try {
|
|
40211
|
-
if (ownerAddr) ownerBal = await nativeBalance(pc, ownerAddr);
|
|
40212
|
-
if (managerAddr) managerBal = await nativeBalance(pc, managerAddr);
|
|
40274
|
+
if (ownerAddr) ownerBal = await nativeBalance(pc, ownerAddr, chainId);
|
|
40275
|
+
if (managerAddr) managerBal = await nativeBalance(pc, managerAddr, chainId);
|
|
40213
40276
|
} catch {
|
|
40214
40277
|
}
|
|
40215
40278
|
if (options.json) {
|
|
@@ -40390,8 +40453,8 @@ Probe is heuristic: an unknown selector (${PROBE_SELECTOR}) to a neutral target
|
|
|
40390
40453
|
}
|
|
40391
40454
|
|
|
40392
40455
|
// src/commands/init.ts
|
|
40393
|
-
var
|
|
40394
|
-
var
|
|
40456
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
40457
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
40395
40458
|
|
|
40396
40459
|
// src/lib/foundry.ts
|
|
40397
40460
|
var import_node_fs7 = require("node:fs");
|
|
@@ -40557,11 +40620,13 @@ function scaffoldFoundryWorkspace(root) {
|
|
|
40557
40620
|
writeIfMissing((0, import_node_path6.join)(root, "mandates", "BoundedCallPermission.sol"), EXAMPLE_MANDATE_SOL);
|
|
40558
40621
|
writeIfMissing((0, import_node_path6.join)(root, "mandates", "README.md"), MANDATES_README);
|
|
40559
40622
|
}
|
|
40560
|
-
function writeIfMissing(
|
|
40561
|
-
if (!(0, import_node_fs7.existsSync)(
|
|
40623
|
+
function writeIfMissing(path11, content) {
|
|
40624
|
+
if (!(0, import_node_fs7.existsSync)(path11)) (0, import_node_fs7.writeFileSync)(path11, content, "utf8");
|
|
40562
40625
|
}
|
|
40563
40626
|
|
|
40564
|
-
// src/
|
|
40627
|
+
// src/lib/template.ts
|
|
40628
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
40629
|
+
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
40565
40630
|
var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
40566
40631
|
"node_modules",
|
|
40567
40632
|
"dist",
|
|
@@ -40584,6 +40649,26 @@ function copyDirSync(src, dest) {
|
|
|
40584
40649
|
}
|
|
40585
40650
|
}
|
|
40586
40651
|
}
|
|
40652
|
+
function writeIfMissing2(file, content) {
|
|
40653
|
+
if (!import_node_fs8.default.existsSync(file)) import_node_fs8.default.writeFileSync(file, content, "utf-8");
|
|
40654
|
+
}
|
|
40655
|
+
function copyDirSyncIfMissing(src, dest, added = [], base2 = dest) {
|
|
40656
|
+
import_node_fs8.default.mkdirSync(dest, { recursive: true });
|
|
40657
|
+
for (const entry of import_node_fs8.default.readdirSync(src, { withFileTypes: true })) {
|
|
40658
|
+
if (TEMPLATE_COPY_EXCLUDES.has(entry.name)) continue;
|
|
40659
|
+
const srcPath = import_node_path7.default.join(src, entry.name);
|
|
40660
|
+
const destName = entry.name.startsWith("_") ? `.${entry.name.slice(1)}` : entry.name;
|
|
40661
|
+
const destPath = import_node_path7.default.join(dest, destName);
|
|
40662
|
+
if (entry.isDirectory()) {
|
|
40663
|
+
copyDirSyncIfMissing(srcPath, destPath, added, base2);
|
|
40664
|
+
} else if (!import_node_fs8.default.existsSync(destPath)) {
|
|
40665
|
+
import_node_fs8.default.copyFileSync(srcPath, destPath);
|
|
40666
|
+
added.push(import_node_path7.default.relative(base2, destPath));
|
|
40667
|
+
}
|
|
40668
|
+
}
|
|
40669
|
+
}
|
|
40670
|
+
|
|
40671
|
+
// src/commands/init.ts
|
|
40587
40672
|
var SAIL_WORKSPACE_README = `# Sailor Project Workspace
|
|
40588
40673
|
|
|
40589
40674
|
This folder is the local workspace for one Sailor agent deployment.
|
|
@@ -40598,15 +40683,12 @@ This folder is the local workspace for one Sailor agent deployment.
|
|
|
40598
40683
|
AI coding agents should read the project's \`AGENTS.md\` and this folder's \`config.json\`
|
|
40599
40684
|
before changing strategy code or running commands that touch funds.
|
|
40600
40685
|
`;
|
|
40601
|
-
function writeIfMissing2(file, content) {
|
|
40602
|
-
if (!import_node_fs8.default.existsSync(file)) import_node_fs8.default.writeFileSync(file, content, "utf-8");
|
|
40603
|
-
}
|
|
40604
40686
|
var CANONICAL_PKG = "@sail.money/sailor";
|
|
40605
40687
|
var DEV_PKG = "@dev.sail.money/sailor";
|
|
40606
40688
|
function cliPackageInfo() {
|
|
40607
40689
|
try {
|
|
40608
40690
|
const pkg = JSON.parse(
|
|
40609
|
-
|
|
40691
|
+
import_node_fs9.default.readFileSync(import_node_path8.default.join(packageRoot(), "package.json"), "utf-8")
|
|
40610
40692
|
);
|
|
40611
40693
|
return {
|
|
40612
40694
|
name: pkg.name ?? CANONICAL_PKG,
|
|
@@ -40622,12 +40704,12 @@ function scaffoldProjectWorkspace(dest, name, options) {
|
|
|
40622
40704
|
if (!Number.isInteger(n) || n <= 0) throw new Error(`Invalid chain id: "${options.chain}"`);
|
|
40623
40705
|
return n;
|
|
40624
40706
|
})() : null;
|
|
40625
|
-
const sailDir2 =
|
|
40626
|
-
|
|
40627
|
-
|
|
40628
|
-
|
|
40629
|
-
|
|
40630
|
-
|
|
40707
|
+
const sailDir2 = import_node_path8.default.join(dest, ".sail");
|
|
40708
|
+
import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "keys"), { recursive: true });
|
|
40709
|
+
import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "runtime"), { recursive: true });
|
|
40710
|
+
import_node_fs9.default.mkdirSync(import_node_path8.default.join(sailDir2, "state"), { recursive: true });
|
|
40711
|
+
import_node_fs9.default.writeFileSync(
|
|
40712
|
+
import_node_path8.default.join(sailDir2, "config.json"),
|
|
40631
40713
|
`${JSON.stringify(
|
|
40632
40714
|
{
|
|
40633
40715
|
version: 1,
|
|
@@ -40647,12 +40729,12 @@ function scaffoldProjectWorkspace(dest, name, options) {
|
|
|
40647
40729
|
`,
|
|
40648
40730
|
"utf-8"
|
|
40649
40731
|
);
|
|
40650
|
-
writeIfMissing2(
|
|
40732
|
+
writeIfMissing2(import_node_path8.default.join(sailDir2, "README.md"), SAIL_WORKSPACE_README);
|
|
40651
40733
|
const chainEntries = Object.values(chains);
|
|
40652
40734
|
const perChainVarLines = chainEntries.map((c) => `# ${c.rpcEnvVar}=https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`).join("\n");
|
|
40653
40735
|
const chainIdExample = chainId != null ? `CHAIN_ID=${chainId}` : `# CHAIN_ID=8453 # set after choosing your chain in Stage 1`;
|
|
40654
|
-
|
|
40655
|
-
|
|
40736
|
+
import_node_fs9.default.writeFileSync(
|
|
40737
|
+
import_node_path8.default.join(dest, ".env.example"),
|
|
40656
40738
|
`# Sailor agent environment
|
|
40657
40739
|
#
|
|
40658
40740
|
# RPC configuration \u2014 two patterns, pick one:
|
|
@@ -40677,8 +40759,8 @@ ${perChainVarLines}
|
|
|
40677
40759
|
const val = isActive && options.rpcUrl ? options.rpcUrl : `https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`;
|
|
40678
40760
|
return isActive && options.rpcUrl ? `${c.rpcEnvVar}=${val}` : `# ${c.rpcEnvVar}=${val}`;
|
|
40679
40761
|
}).join("\n");
|
|
40680
|
-
|
|
40681
|
-
|
|
40762
|
+
import_node_fs9.default.writeFileSync(
|
|
40763
|
+
import_node_path8.default.join(sailDir2, ".env.local"),
|
|
40682
40764
|
`# Real values \u2014 never commit this file.
|
|
40683
40765
|
#
|
|
40684
40766
|
# Option A: single active chain (simplest)
|
|
@@ -40696,16 +40778,16 @@ ${allChainVarLines}
|
|
|
40696
40778
|
}
|
|
40697
40779
|
async function initCommand(dir, options = {}) {
|
|
40698
40780
|
const inPlace = !dir || dir === ".";
|
|
40699
|
-
const dest = inPlace ? process.cwd() :
|
|
40700
|
-
const name =
|
|
40701
|
-
const templatesDir =
|
|
40781
|
+
const dest = inPlace ? process.cwd() : import_node_path8.default.resolve(process.cwd(), dir);
|
|
40782
|
+
const name = import_node_path8.default.basename(dest);
|
|
40783
|
+
const templatesDir = import_node_path8.default.join(packageRoot(), "templates");
|
|
40702
40784
|
const templateName = options.template ?? "default";
|
|
40703
40785
|
if (/[/\\.]/.test(templateName) || templateName.includes("..")) {
|
|
40704
40786
|
throw new Error(`Invalid template name: "${templateName}"`);
|
|
40705
40787
|
}
|
|
40706
|
-
const templateSrc =
|
|
40707
|
-
const availableTemplates = () =>
|
|
40708
|
-
if (!
|
|
40788
|
+
const templateSrc = import_node_path8.default.join(templatesDir, templateName);
|
|
40789
|
+
const availableTemplates = () => import_node_fs9.default.existsSync(templatesDir) ? import_node_fs9.default.readdirSync(templatesDir).filter((e) => import_node_fs9.default.existsSync(import_node_path8.default.join(templatesDir, e, "package.json"))).join(", ") || "none" : "none";
|
|
40790
|
+
if (!import_node_fs9.default.existsSync(templateSrc) || !import_node_fs9.default.existsSync(import_node_path8.default.join(templateSrc, "package.json"))) {
|
|
40709
40791
|
const available = availableTemplates();
|
|
40710
40792
|
const hint = available === "none" ? `
|
|
40711
40793
|
No templates found under ${templatesDir}.
|
|
@@ -40715,49 +40797,49 @@ run from the repo root.` : ` Available: ${available}`;
|
|
|
40715
40797
|
throw new Error(`Template "${templateName}" not found.${hint}`);
|
|
40716
40798
|
}
|
|
40717
40799
|
const cwd = process.cwd();
|
|
40718
|
-
if (!inPlace && !dest.startsWith(cwd +
|
|
40800
|
+
if (!inPlace && !dest.startsWith(cwd + import_node_path8.default.sep) && dest !== cwd) {
|
|
40719
40801
|
throw new Error(`Directory must be inside the current working directory`);
|
|
40720
40802
|
}
|
|
40721
|
-
if (!inPlace &&
|
|
40803
|
+
if (!inPlace && import_node_fs9.default.existsSync(dest)) {
|
|
40722
40804
|
throw new Error(`Directory already exists: ${dest}`);
|
|
40723
40805
|
}
|
|
40724
|
-
if (inPlace &&
|
|
40725
|
-
throw new Error(
|
|
40806
|
+
if (inPlace && import_node_fs9.default.existsSync(import_node_path8.default.join(dest, ".sail", "config.json"))) {
|
|
40807
|
+
throw new Error("This project is already initialized. Run `sailor update` to re-sync template files.");
|
|
40726
40808
|
}
|
|
40727
40809
|
copyDirSync(templateSrc, dest);
|
|
40728
40810
|
const pkgRoot = packageRoot();
|
|
40729
|
-
const examplesPermSrc =
|
|
40730
|
-
if (
|
|
40731
|
-
copyDirSync(examplesPermSrc,
|
|
40732
|
-
}
|
|
40733
|
-
const customMandateSrc =
|
|
40734
|
-
if (
|
|
40735
|
-
copyDirSync(customMandateSrc,
|
|
40736
|
-
}
|
|
40737
|
-
const permModelSrc =
|
|
40738
|
-
if (
|
|
40739
|
-
|
|
40740
|
-
writeIfMissing2(
|
|
40741
|
-
}
|
|
40742
|
-
const pkgPath =
|
|
40743
|
-
if (
|
|
40744
|
-
const pkg = JSON.parse(
|
|
40811
|
+
const examplesPermSrc = import_node_path8.default.join(pkgRoot, "examples", "permissions");
|
|
40812
|
+
if (import_node_fs9.default.existsSync(examplesPermSrc)) {
|
|
40813
|
+
copyDirSync(examplesPermSrc, import_node_path8.default.join(dest, "examples", "permissions"));
|
|
40814
|
+
}
|
|
40815
|
+
const customMandateSrc = import_node_path8.default.join(pkgRoot, "examples", "custom-mandate");
|
|
40816
|
+
if (import_node_fs9.default.existsSync(customMandateSrc)) {
|
|
40817
|
+
copyDirSync(customMandateSrc, import_node_path8.default.join(dest, "examples", "custom-mandate"));
|
|
40818
|
+
}
|
|
40819
|
+
const permModelSrc = import_node_path8.default.join(pkgRoot, "docs", "PERMISSION_MODEL.md");
|
|
40820
|
+
if (import_node_fs9.default.existsSync(permModelSrc)) {
|
|
40821
|
+
import_node_fs9.default.mkdirSync(import_node_path8.default.join(dest, "docs"), { recursive: true });
|
|
40822
|
+
writeIfMissing2(import_node_path8.default.join(dest, "docs", "PERMISSION_MODEL.md"), import_node_fs9.default.readFileSync(permModelSrc, "utf-8"));
|
|
40823
|
+
}
|
|
40824
|
+
const pkgPath = import_node_path8.default.join(dest, "package.json");
|
|
40825
|
+
if (import_node_fs9.default.existsSync(pkgPath)) {
|
|
40826
|
+
const pkg = JSON.parse(import_node_fs9.default.readFileSync(pkgPath, "utf-8"));
|
|
40745
40827
|
pkg.name = name;
|
|
40746
40828
|
const devDeps = pkg.devDependencies ?? {};
|
|
40747
40829
|
const { name: cliName, version: cliVer } = cliPackageInfo();
|
|
40748
40830
|
devDeps[CANONICAL_PKG] = cliName === DEV_PKG ? `npm:${DEV_PKG}@^${cliVer}` : `^${cliVer}`;
|
|
40749
40831
|
pkg.devDependencies = devDeps;
|
|
40750
|
-
|
|
40832
|
+
import_node_fs9.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
40751
40833
|
`);
|
|
40752
40834
|
}
|
|
40753
|
-
const tsconfigPath =
|
|
40754
|
-
if (
|
|
40755
|
-
const tsconfig = JSON.parse(
|
|
40835
|
+
const tsconfigPath = import_node_path8.default.join(dest, "tsconfig.json");
|
|
40836
|
+
if (import_node_fs9.default.existsSync(tsconfigPath)) {
|
|
40837
|
+
const tsconfig = JSON.parse(import_node_fs9.default.readFileSync(tsconfigPath, "utf-8"));
|
|
40756
40838
|
const co = tsconfig.compilerOptions;
|
|
40757
40839
|
if (co && "paths" in co) {
|
|
40758
40840
|
delete co.paths;
|
|
40759
40841
|
delete co.baseUrl;
|
|
40760
|
-
|
|
40842
|
+
import_node_fs9.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
40761
40843
|
`);
|
|
40762
40844
|
}
|
|
40763
40845
|
}
|
|
@@ -40783,21 +40865,21 @@ function chainLabel(chainId) {
|
|
|
40783
40865
|
}
|
|
40784
40866
|
function detectState(dest) {
|
|
40785
40867
|
try {
|
|
40786
|
-
const configRaw =
|
|
40868
|
+
const configRaw = import_node_fs9.default.readFileSync(import_node_path8.default.join(dest, ".sail", "config.json"), "utf-8");
|
|
40787
40869
|
const config = JSON.parse(configRaw);
|
|
40788
|
-
const projectName = config.name ??
|
|
40789
|
-
const accountPath =
|
|
40790
|
-
if (!
|
|
40870
|
+
const projectName = config.name ?? import_node_path8.default.basename(dest);
|
|
40871
|
+
const accountPath = import_node_path8.default.join(dest, ".sail", "account.json");
|
|
40872
|
+
if (!import_node_fs9.default.existsSync(accountPath)) {
|
|
40791
40873
|
return { kind: "B", projectName, chain: chainLabel(config.chainId ?? 0) };
|
|
40792
40874
|
}
|
|
40793
|
-
const accountRaw =
|
|
40875
|
+
const accountRaw = import_node_fs9.default.readFileSync(accountPath, "utf-8");
|
|
40794
40876
|
const account2 = JSON.parse(accountRaw);
|
|
40795
40877
|
const sma = account2.safe ?? "";
|
|
40796
40878
|
const chain2 = chainLabel(account2.chainId ?? config.chainId ?? 0);
|
|
40797
40879
|
let permissionCount = 0;
|
|
40798
40880
|
try {
|
|
40799
|
-
const mandatesRaw =
|
|
40800
|
-
|
|
40881
|
+
const mandatesRaw = import_node_fs9.default.readFileSync(
|
|
40882
|
+
import_node_path8.default.join(dest, ".sail", "state", "mandates.json"),
|
|
40801
40883
|
"utf-8"
|
|
40802
40884
|
);
|
|
40803
40885
|
const mandates = JSON.parse(mandatesRaw);
|
|
@@ -40878,9 +40960,79 @@ Created ${name}/`);
|
|
|
40878
40960
|
].join("\n"));
|
|
40879
40961
|
}
|
|
40880
40962
|
|
|
40963
|
+
// src/commands/update.ts
|
|
40964
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
40965
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
40966
|
+
var UPDATE_PATHS = [
|
|
40967
|
+
".agents",
|
|
40968
|
+
// all sail-* skills
|
|
40969
|
+
".cursor",
|
|
40970
|
+
// cursor IDE rules
|
|
40971
|
+
".env.example"
|
|
40972
|
+
// documents env vars; not meant to be edited directly
|
|
40973
|
+
];
|
|
40974
|
+
var STALE_PATHS = [
|
|
40975
|
+
".agents/skills/sail-ci"
|
|
40976
|
+
// renamed to sail-automation
|
|
40977
|
+
];
|
|
40978
|
+
async function updateCommand() {
|
|
40979
|
+
const dest = process.cwd();
|
|
40980
|
+
if (!import_node_fs10.default.existsSync(import_node_path9.default.join(dest, ".sail", "config.json"))) {
|
|
40981
|
+
throw new Error("Not a sailor project \u2014 .sail/config.json not found. Run `sailor init` first.");
|
|
40982
|
+
}
|
|
40983
|
+
const templateSrc = import_node_path9.default.join(packageRoot(), "templates", "default");
|
|
40984
|
+
if (!import_node_fs10.default.existsSync(templateSrc)) {
|
|
40985
|
+
throw new Error(`Template directory not found at ${templateSrc}`);
|
|
40986
|
+
}
|
|
40987
|
+
const removed = [];
|
|
40988
|
+
for (const p of STALE_PATHS) {
|
|
40989
|
+
const target = import_node_path9.default.join(dest, p);
|
|
40990
|
+
if (import_node_fs10.default.existsSync(target)) {
|
|
40991
|
+
import_node_fs10.default.rmSync(target, { recursive: true, force: true });
|
|
40992
|
+
removed.push(p);
|
|
40993
|
+
}
|
|
40994
|
+
}
|
|
40995
|
+
const updated = [];
|
|
40996
|
+
for (const p of UPDATE_PATHS) {
|
|
40997
|
+
const src = import_node_path9.default.join(templateSrc, p);
|
|
40998
|
+
const dst = import_node_path9.default.join(dest, p);
|
|
40999
|
+
if (!import_node_fs10.default.existsSync(src)) continue;
|
|
41000
|
+
const stat = import_node_fs10.default.statSync(src);
|
|
41001
|
+
if (stat.isDirectory()) {
|
|
41002
|
+
copyDirSync(src, dst);
|
|
41003
|
+
} else {
|
|
41004
|
+
import_node_fs10.default.mkdirSync(import_node_path9.default.dirname(dst), { recursive: true });
|
|
41005
|
+
import_node_fs10.default.copyFileSync(src, dst);
|
|
41006
|
+
}
|
|
41007
|
+
updated.push(p);
|
|
41008
|
+
}
|
|
41009
|
+
const added = [];
|
|
41010
|
+
copyDirSyncIfMissing(templateSrc, dest, added);
|
|
41011
|
+
if (removed.length === 0 && updated.length === 0 && added.length === 0) {
|
|
41012
|
+
console.log("Nothing to update.");
|
|
41013
|
+
return;
|
|
41014
|
+
}
|
|
41015
|
+
if (removed.length > 0) {
|
|
41016
|
+
console.log(`
|
|
41017
|
+
Removed stale files:`);
|
|
41018
|
+
for (const p of removed) console.log(` ${p}`);
|
|
41019
|
+
}
|
|
41020
|
+
if (updated.length > 0) {
|
|
41021
|
+
console.log(`
|
|
41022
|
+
Updated from template:`);
|
|
41023
|
+
for (const p of updated) console.log(` ${p}`);
|
|
41024
|
+
}
|
|
41025
|
+
if (added.length > 0) {
|
|
41026
|
+
console.log(`
|
|
41027
|
+
Added (new in template):`);
|
|
41028
|
+
for (const p of added) console.log(` ${p}`);
|
|
41029
|
+
}
|
|
41030
|
+
console.log();
|
|
41031
|
+
}
|
|
41032
|
+
|
|
40881
41033
|
// src/commands/keys.ts
|
|
40882
|
-
var
|
|
40883
|
-
var
|
|
41034
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
41035
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
40884
41036
|
async function keysGenerate() {
|
|
40885
41037
|
const roleInput = await prompt("Which key? (agent wallet / mandate signer)", "agent wallet");
|
|
40886
41038
|
const role = normalizeRole(roleInput);
|
|
@@ -40933,15 +41085,15 @@ async function keysExportCi() {
|
|
|
40933
41085
|
'No agent wallet keystore found.\nComplete Stage 1 (browser UI) to generate your agent wallet, or run\n"sailor keys generate" and choose "agent wallet" to create one manually.'
|
|
40934
41086
|
);
|
|
40935
41087
|
}
|
|
40936
|
-
const dest =
|
|
40937
|
-
|
|
41088
|
+
const dest = import_node_path10.default.resolve(process.cwd(), "ci-keystore.json");
|
|
41089
|
+
import_node_fs11.default.copyFileSync(src, dest);
|
|
40938
41090
|
console.log(`\u2713 Keystore copied to ci-keystore.json`);
|
|
40939
41091
|
console.log(` Source: ${src}`);
|
|
40940
|
-
const gitignorePath =
|
|
40941
|
-
if (
|
|
40942
|
-
const content =
|
|
41092
|
+
const gitignorePath = import_node_path10.default.resolve(process.cwd(), ".gitignore");
|
|
41093
|
+
if (import_node_fs11.default.existsSync(gitignorePath)) {
|
|
41094
|
+
const content = import_node_fs11.default.readFileSync(gitignorePath, "utf-8");
|
|
40943
41095
|
if (!content.includes("ci-keystore.json")) {
|
|
40944
|
-
|
|
41096
|
+
import_node_fs11.default.appendFileSync(
|
|
40945
41097
|
gitignorePath,
|
|
40946
41098
|
"\n# CI keystore \u2014 encrypted agent wallet, safe to commit\n!ci-keystore.json\n"
|
|
40947
41099
|
);
|
|
@@ -40979,8 +41131,8 @@ async function keysShow() {
|
|
|
40979
41131
|
|
|
40980
41132
|
// src/commands/mandate-contracts.ts
|
|
40981
41133
|
var import_node_child_process = require("node:child_process");
|
|
40982
|
-
var
|
|
40983
|
-
var
|
|
41134
|
+
var import_node_fs12 = require("node:fs");
|
|
41135
|
+
var import_node_path11 = require("node:path");
|
|
40984
41136
|
init_esm2();
|
|
40985
41137
|
|
|
40986
41138
|
// src/lib/mandates.ts
|
|
@@ -41624,6 +41776,18 @@ Mandate command failed: ${msg}`), {
|
|
|
41624
41776
|
});
|
|
41625
41777
|
process.exit(1);
|
|
41626
41778
|
}
|
|
41779
|
+
function announceSigningUrl(json) {
|
|
41780
|
+
const url = signingPageUrl(void 0, projectPort(process.cwd()));
|
|
41781
|
+
if (json) {
|
|
41782
|
+
process.stdout.write(`${JSON.stringify({ status: "waiting_for_signature", url })}
|
|
41783
|
+
`);
|
|
41784
|
+
} else {
|
|
41785
|
+
console.log(`
|
|
41786
|
+
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
41787
|
+
${url}
|
|
41788
|
+
`);
|
|
41789
|
+
}
|
|
41790
|
+
}
|
|
41627
41791
|
function publicClientFor(project) {
|
|
41628
41792
|
return createPublicClient({
|
|
41629
41793
|
chain: getChainById(project.chainId),
|
|
@@ -41664,9 +41828,9 @@ async function runDeploy(project, channel, options) {
|
|
|
41664
41828
|
const { abi: abi2, bytecode, contractName, artifactPath } = resolveArtifact(options);
|
|
41665
41829
|
let argsJson;
|
|
41666
41830
|
if (options.argsFile) {
|
|
41667
|
-
const argsFilePath = (0,
|
|
41831
|
+
const argsFilePath = (0, import_node_path11.resolve)(options.argsFile);
|
|
41668
41832
|
try {
|
|
41669
|
-
argsJson = (0,
|
|
41833
|
+
argsJson = (0, import_node_fs12.readFileSync)(argsFilePath, "utf8").trim();
|
|
41670
41834
|
} catch {
|
|
41671
41835
|
throw new Error(`Cannot read --args-file: ${argsFilePath}`);
|
|
41672
41836
|
}
|
|
@@ -41677,15 +41841,8 @@ async function runDeploy(project, channel, options) {
|
|
|
41677
41841
|
const deployData = encodeDeployData({ abi: abi2, bytecode, args });
|
|
41678
41842
|
const chainId = project.chainId;
|
|
41679
41843
|
const publicClient = publicClientFor(project);
|
|
41680
|
-
|
|
41681
|
-
|
|
41682
|
-
`
|
|
41683
|
-
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
41684
|
-
${signingPageUrl(channel, projectPort(process.cwd()))}
|
|
41685
|
-
`
|
|
41686
|
-
);
|
|
41687
|
-
console.log(`Pushing deploy request for "${contractName}"\u2026`);
|
|
41688
|
-
});
|
|
41844
|
+
announceSigningUrl(json);
|
|
41845
|
+
say(() => console.log(`Pushing deploy request for "${contractName}"\u2026`));
|
|
41689
41846
|
const response = await channel.requestSignature({
|
|
41690
41847
|
type: "transaction",
|
|
41691
41848
|
kind: "deploy-mandate",
|
|
@@ -41835,6 +41992,15 @@ function parseAddressList(csv, flag) {
|
|
|
41835
41992
|
}
|
|
41836
41993
|
async function mandateDeployClone(options) {
|
|
41837
41994
|
const project = requireProject();
|
|
41995
|
+
const templateMap = project.deployment.standaloneTemplates ?? {};
|
|
41996
|
+
if (Object.keys(templateMap).length === 0) {
|
|
41997
|
+
fail(
|
|
41998
|
+
new Error(
|
|
41999
|
+
`deploy-clone is unavailable on chain ${project.chainId}: no clone templates are deployed against this kernel (${project.deployment.kernel}) yet. Deploy your permission directly with \`sailor mandate deploy\` instead.`
|
|
42000
|
+
),
|
|
42001
|
+
options.json
|
|
42002
|
+
);
|
|
42003
|
+
}
|
|
41838
42004
|
const channel = await createSigningChannel(process.cwd());
|
|
41839
42005
|
try {
|
|
41840
42006
|
await channel.start();
|
|
@@ -41911,11 +42077,8 @@ ${spec.label} clone (${options.template})`);
|
|
|
41911
42077
|
console.log(` predicted clone: ${clone}`);
|
|
41912
42078
|
console.log(` SMA: ${sma}`);
|
|
41913
42079
|
for (const d of spec.describe(initParams)) console.log(` ${d.label}: ${d.value}`);
|
|
41914
|
-
console.log(`
|
|
41915
|
-
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
41916
|
-
${signingPageUrl(channel, projectPort(process.cwd()))}
|
|
41917
|
-
`);
|
|
41918
42080
|
});
|
|
42081
|
+
announceSigningUrl(json);
|
|
41919
42082
|
const nonce = await publicClient.readContract({
|
|
41920
42083
|
address: project.contracts.kernel,
|
|
41921
42084
|
abi: SailKernelAbi,
|
|
@@ -42073,14 +42236,7 @@ async function runAttach(project, channel, options) {
|
|
|
42073
42236
|
const mandateAddress = getAddress(rawAddress);
|
|
42074
42237
|
const label = options.label ?? tracked?.name ?? "mandate";
|
|
42075
42238
|
const publicClient = publicClientFor(project);
|
|
42076
|
-
|
|
42077
|
-
console.log(
|
|
42078
|
-
`
|
|
42079
|
-
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
42080
|
-
${signingPageUrl(channel, projectPort(process.cwd()))}
|
|
42081
|
-
`
|
|
42082
|
-
);
|
|
42083
|
-
}
|
|
42239
|
+
announceSigningUrl(json);
|
|
42084
42240
|
const txHash = await attachToSma(
|
|
42085
42241
|
project,
|
|
42086
42242
|
channel,
|
|
@@ -42157,13 +42313,8 @@ async function runRevoke(project, channel, options) {
|
|
|
42157
42313
|
console.log(`
|
|
42158
42314
|
Revoking ${targets.length} permission(s) from ${sma}:`);
|
|
42159
42315
|
for (const p of targets) console.log(` ${nameFor(p) ?? p} ${p}`);
|
|
42160
|
-
console.log(
|
|
42161
|
-
`
|
|
42162
|
-
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
42163
|
-
${signingPageUrl(channel, projectPort(process.cwd()))}
|
|
42164
|
-
`
|
|
42165
|
-
);
|
|
42166
42316
|
});
|
|
42317
|
+
announceSigningUrl(json);
|
|
42167
42318
|
const response = await channel.requestSignature({
|
|
42168
42319
|
type: "typed-data",
|
|
42169
42320
|
kind: "revoke-permissions",
|
|
@@ -42340,21 +42491,27 @@ function mandateTemplates(options) {
|
|
|
42340
42491
|
{ chainId, community }
|
|
42341
42492
|
);
|
|
42342
42493
|
}
|
|
42343
|
-
function mandateContractsList() {
|
|
42494
|
+
function mandateContractsList(options = {}) {
|
|
42344
42495
|
const store = new MandateStore();
|
|
42345
42496
|
const mandates = store.list();
|
|
42346
|
-
|
|
42347
|
-
|
|
42348
|
-
|
|
42349
|
-
|
|
42350
|
-
|
|
42351
|
-
|
|
42352
|
-
|
|
42353
|
-
|
|
42354
|
-
|
|
42355
|
-
|
|
42356
|
-
|
|
42357
|
-
|
|
42497
|
+
emit(
|
|
42498
|
+
!!options.json,
|
|
42499
|
+
() => {
|
|
42500
|
+
if (mandates.length === 0) {
|
|
42501
|
+
console.log('No permission contracts deployed yet. Use "sailor mandate deploy".');
|
|
42502
|
+
return;
|
|
42503
|
+
}
|
|
42504
|
+
for (const m of mandates) {
|
|
42505
|
+
console.log(m.name, `(chain ${m.chainId})`);
|
|
42506
|
+
console.log(" Address: ", m.address);
|
|
42507
|
+
console.log(" Deployed:", m.deployedAt);
|
|
42508
|
+
if (m.attachments?.length) {
|
|
42509
|
+
console.log(" Registered on:", m.attachments.map((a) => a.sma).join(", "));
|
|
42510
|
+
}
|
|
42511
|
+
}
|
|
42512
|
+
},
|
|
42513
|
+
{ status: "ok", mandates }
|
|
42514
|
+
);
|
|
42358
42515
|
}
|
|
42359
42516
|
function mandateUpdate(options) {
|
|
42360
42517
|
const { address, name, sourcePath, artifactPath, json } = options;
|
|
@@ -42376,11 +42533,11 @@ function resolveArtifact(options) {
|
|
|
42376
42533
|
let contractName = options.contract ?? options.name ?? "";
|
|
42377
42534
|
if (!artifactPath) {
|
|
42378
42535
|
if (!options.contract) throw new Error("Provide --artifact <path> or --contract <name>");
|
|
42379
|
-
artifactPath = (0,
|
|
42536
|
+
artifactPath = (0, import_node_path11.join)(options.out, `${options.contract}.sol`, `${options.contract}.json`);
|
|
42380
42537
|
}
|
|
42381
|
-
const resolved = (0,
|
|
42382
|
-
const projectRoot = (0,
|
|
42383
|
-
if (!resolved.startsWith(projectRoot +
|
|
42538
|
+
const resolved = (0, import_node_path11.resolve)(artifactPath);
|
|
42539
|
+
const projectRoot = (0, import_node_path11.resolve)(process.cwd());
|
|
42540
|
+
if (!resolved.startsWith(projectRoot + import_node_path11.sep) && resolved !== projectRoot) {
|
|
42384
42541
|
throw new Error(
|
|
42385
42542
|
`Artifact path must be inside the project directory.
|
|
42386
42543
|
Resolved: ${resolved}`
|
|
@@ -42388,14 +42545,14 @@ Resolved: ${resolved}`
|
|
|
42388
42545
|
}
|
|
42389
42546
|
artifactPath = resolved;
|
|
42390
42547
|
if (options.build) runForgeBuild();
|
|
42391
|
-
if (!(0,
|
|
42548
|
+
if (!(0, import_node_fs12.existsSync)(artifactPath)) {
|
|
42392
42549
|
ensureForgeHint();
|
|
42393
42550
|
throw new Error(
|
|
42394
42551
|
`Artifact not found: ${artifactPath}
|
|
42395
42552
|
Compile your mandate first (e.g. \`forge build\`), or pass --build.`
|
|
42396
42553
|
);
|
|
42397
42554
|
}
|
|
42398
|
-
const artifact = JSON.parse((0,
|
|
42555
|
+
const artifact = JSON.parse((0, import_node_fs12.readFileSync)(artifactPath, "utf8"));
|
|
42399
42556
|
const abi2 = artifact.abi;
|
|
42400
42557
|
const bytecodeRaw = artifact.bytecode?.object ?? artifact.bytecode;
|
|
42401
42558
|
if (!bytecodeRaw || typeof bytecodeRaw !== "string") {
|
|
@@ -42478,13 +42635,13 @@ function runForgeBuild() {
|
|
|
42478
42635
|
init_esm2();
|
|
42479
42636
|
|
|
42480
42637
|
// src/lib/permission-explainer.ts
|
|
42481
|
-
var
|
|
42482
|
-
var
|
|
42638
|
+
var import_node_fs13 = require("node:fs");
|
|
42639
|
+
var import_node_path12 = require("node:path");
|
|
42483
42640
|
function explainPermission(name, sourcePath) {
|
|
42484
|
-
const resolved = sourcePath ?? (0,
|
|
42641
|
+
const resolved = sourcePath ?? (0, import_node_path12.join)(process.cwd(), "mandates", `${name}.sol`);
|
|
42485
42642
|
let src;
|
|
42486
42643
|
try {
|
|
42487
|
-
src = (0,
|
|
42644
|
+
src = (0, import_node_fs13.readFileSync)(resolved, "utf8");
|
|
42488
42645
|
} catch {
|
|
42489
42646
|
return null;
|
|
42490
42647
|
}
|
|
@@ -42691,7 +42848,6 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
|
|
|
42691
42848
|
safe: account2.safe,
|
|
42692
42849
|
chainId,
|
|
42693
42850
|
signedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
42694
|
-
signature: "",
|
|
42695
42851
|
registeredOnChain: true,
|
|
42696
42852
|
// Only include permissions that are currently active on-chain.
|
|
42697
42853
|
permissions: activePermissions.map((p) => ({ template: p.label, params: {} }))
|
|
@@ -42707,7 +42863,7 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
|
|
|
42707
42863
|
}
|
|
42708
42864
|
|
|
42709
42865
|
// src/commands/mandate-simulate.ts
|
|
42710
|
-
var
|
|
42866
|
+
var import_node_fs14 = require("node:fs");
|
|
42711
42867
|
init_esm2();
|
|
42712
42868
|
function parseExpect(raw, where) {
|
|
42713
42869
|
if (raw === void 0) return void 0;
|
|
@@ -42723,7 +42879,7 @@ function resolveSampleCalls(options) {
|
|
|
42723
42879
|
if (options.calls) {
|
|
42724
42880
|
let raw;
|
|
42725
42881
|
try {
|
|
42726
|
-
raw = JSON.parse((0,
|
|
42882
|
+
raw = JSON.parse((0, import_node_fs14.readFileSync)(options.calls, "utf8"));
|
|
42727
42883
|
} catch (err) {
|
|
42728
42884
|
throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
|
|
42729
42885
|
}
|
|
@@ -42939,7 +43095,7 @@ async function mandateSimulate(options) {
|
|
|
42939
43095
|
}
|
|
42940
43096
|
|
|
42941
43097
|
// src/commands/rotate-signer.ts
|
|
42942
|
-
var
|
|
43098
|
+
var import_node_fs15 = require("node:fs");
|
|
42943
43099
|
init_esm2();
|
|
42944
43100
|
var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
|
|
42945
43101
|
async function rotateSigner(options) {
|
|
@@ -43327,7 +43483,7 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
43327
43483
|
const keystore = await keyring.exportKeystore(password);
|
|
43328
43484
|
writeJsonFile(target, keystore, 384);
|
|
43329
43485
|
const perManagerPath = managerKeystorePath(keyring.address);
|
|
43330
|
-
(0,
|
|
43486
|
+
(0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43331
43487
|
writeJsonFile(perManagerPath, keystore, 384);
|
|
43332
43488
|
say(
|
|
43333
43489
|
() => console.log(
|
|
@@ -43339,7 +43495,7 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
43339
43495
|
function promoteManagerKeystore(newManager, say) {
|
|
43340
43496
|
const stored = readJsonFile(managerKeystorePath(newManager));
|
|
43341
43497
|
if (!stored) return;
|
|
43342
|
-
(0,
|
|
43498
|
+
(0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43343
43499
|
const activeTarget = keyPath("manager");
|
|
43344
43500
|
const displaced = readJsonFile(activeTarget);
|
|
43345
43501
|
if (displaced?.address) {
|
|
@@ -43391,7 +43547,7 @@ function writePending(pending) {
|
|
|
43391
43547
|
}
|
|
43392
43548
|
function clearPending() {
|
|
43393
43549
|
try {
|
|
43394
|
-
(0,
|
|
43550
|
+
(0, import_node_fs15.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
|
|
43395
43551
|
} catch {
|
|
43396
43552
|
}
|
|
43397
43553
|
}
|
|
@@ -43490,30 +43646,30 @@ function ownerShow(options) {
|
|
|
43490
43646
|
}
|
|
43491
43647
|
|
|
43492
43648
|
// src/commands/run.ts
|
|
43493
|
-
var
|
|
43494
|
-
var
|
|
43649
|
+
var import_node_fs17 = __toESM(require("node:fs"), 1);
|
|
43650
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
43495
43651
|
var import_node_url = require("node:url");
|
|
43496
43652
|
init_esm2();
|
|
43497
43653
|
|
|
43498
43654
|
// src/lib/process.ts
|
|
43499
|
-
var
|
|
43655
|
+
var import_node_fs16 = __toESM(require("node:fs"), 1);
|
|
43500
43656
|
function agentPidPath(chainId) {
|
|
43501
43657
|
return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
|
|
43502
43658
|
}
|
|
43503
43659
|
function writeAgentPid(chainId) {
|
|
43504
|
-
|
|
43505
|
-
|
|
43660
|
+
import_node_fs16.default.mkdirSync(sailPath(), { recursive: true });
|
|
43661
|
+
import_node_fs16.default.writeFileSync(agentPidPath(chainId), `${process.pid}
|
|
43506
43662
|
`);
|
|
43507
43663
|
}
|
|
43508
43664
|
function clearAgentPid(chainId) {
|
|
43509
43665
|
try {
|
|
43510
|
-
|
|
43666
|
+
import_node_fs16.default.rmSync(agentPidPath(chainId));
|
|
43511
43667
|
} catch {
|
|
43512
43668
|
}
|
|
43513
43669
|
}
|
|
43514
43670
|
function readAgentPid(chainId) {
|
|
43515
43671
|
try {
|
|
43516
|
-
const pid = Number.parseInt(
|
|
43672
|
+
const pid = Number.parseInt(import_node_fs16.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
|
|
43517
43673
|
return Number.isNaN(pid) ? null : pid;
|
|
43518
43674
|
} catch {
|
|
43519
43675
|
return null;
|
|
@@ -43560,7 +43716,7 @@ var ERC20_READ_ABI = [
|
|
|
43560
43716
|
function loadAgentData(filePath) {
|
|
43561
43717
|
if (!filePath) return {};
|
|
43562
43718
|
try {
|
|
43563
|
-
const parsed = JSON.parse(
|
|
43719
|
+
const parsed = JSON.parse(import_node_fs17.default.readFileSync(filePath, "utf-8"));
|
|
43564
43720
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
43565
43721
|
} catch {
|
|
43566
43722
|
return {};
|
|
@@ -43569,8 +43725,8 @@ function loadAgentData(filePath) {
|
|
|
43569
43725
|
async function loadAgent() {
|
|
43570
43726
|
const candidates = ["src/agent.ts", "src/agent.js", "dist/agent.js", "dist/src/agent.js"];
|
|
43571
43727
|
for (const rel of candidates) {
|
|
43572
|
-
const abs =
|
|
43573
|
-
if (!
|
|
43728
|
+
const abs = import_node_path13.default.join(process.cwd(), rel);
|
|
43729
|
+
if (!import_node_fs17.default.existsSync(abs)) continue;
|
|
43574
43730
|
let mod2;
|
|
43575
43731
|
if (abs.endsWith(".ts")) {
|
|
43576
43732
|
const { tsImport } = await import("tsx/esm/api");
|
|
@@ -44046,9 +44202,9 @@ async function scan(options) {
|
|
|
44046
44202
|
|
|
44047
44203
|
// src/commands/service.ts
|
|
44048
44204
|
var import_node_child_process2 = require("node:child_process");
|
|
44049
|
-
var
|
|
44205
|
+
var import_node_fs18 = __toESM(require("node:fs"), 1);
|
|
44050
44206
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
44051
|
-
var
|
|
44207
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
44052
44208
|
function sanitizeName(raw) {
|
|
44053
44209
|
const s = raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
44054
44210
|
return s || "agent";
|
|
@@ -44167,21 +44323,21 @@ function buildWindowsTaskXml(cfg) {
|
|
|
44167
44323
|
`;
|
|
44168
44324
|
}
|
|
44169
44325
|
function isTccProtected(projectDir, home = import_node_os.default.homedir()) {
|
|
44170
|
-
const rel =
|
|
44171
|
-
if (rel.startsWith("..") ||
|
|
44172
|
-
const top = rel.split(
|
|
44326
|
+
const rel = import_node_path14.default.relative(home, import_node_path14.default.resolve(projectDir));
|
|
44327
|
+
if (rel.startsWith("..") || import_node_path14.default.isAbsolute(rel)) return false;
|
|
44328
|
+
const top = rel.split(import_node_path14.default.sep)[0];
|
|
44173
44329
|
return ["Desktop", "Documents", "Downloads"].includes(top ?? "");
|
|
44174
44330
|
}
|
|
44175
44331
|
function resolveCliEntry() {
|
|
44176
44332
|
try {
|
|
44177
|
-
return
|
|
44333
|
+
return import_node_fs18.default.realpathSync(process.argv[1]);
|
|
44178
44334
|
} catch {
|
|
44179
|
-
return
|
|
44335
|
+
return import_node_path14.default.resolve(process.argv[1]);
|
|
44180
44336
|
}
|
|
44181
44337
|
}
|
|
44182
44338
|
function resolveProjectDir(explicit) {
|
|
44183
|
-
const dir =
|
|
44184
|
-
if (!
|
|
44339
|
+
const dir = import_node_path14.default.resolve(explicit ?? process.cwd());
|
|
44340
|
+
if (!import_node_fs18.default.existsSync(import_node_path14.default.join(dir, ".sail"))) {
|
|
44185
44341
|
throw new Error(
|
|
44186
44342
|
`No .sail/ found in ${dir}.
|
|
44187
44343
|
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.)`
|
|
@@ -44193,7 +44349,7 @@ function passphraseReadiness(projectDir) {
|
|
|
44193
44349
|
const account2 = (() => {
|
|
44194
44350
|
try {
|
|
44195
44351
|
return JSON.parse(
|
|
44196
|
-
|
|
44352
|
+
import_node_fs18.default.readFileSync(import_node_path14.default.join(projectDir, ".sail", "account.json"), "utf-8")
|
|
44197
44353
|
);
|
|
44198
44354
|
} catch {
|
|
44199
44355
|
return null;
|
|
@@ -44209,28 +44365,28 @@ function passphraseReadiness(projectDir) {
|
|
|
44209
44365
|
} finally {
|
|
44210
44366
|
process.chdir(prevCwd);
|
|
44211
44367
|
}
|
|
44212
|
-
const env = parseEnvFile(
|
|
44368
|
+
const env = parseEnvFile(import_node_path14.default.join(projectDir, ".sail", ".env.local"));
|
|
44213
44369
|
const passphraseInEnvFile = Boolean(env.SAIL_PASSPHRASE);
|
|
44214
44370
|
return { keystorePresent, passphraseInEnvFile, ready: keystorePresent && passphraseInEnvFile };
|
|
44215
44371
|
}
|
|
44216
44372
|
function buildConfig(opts) {
|
|
44217
44373
|
const projectDir = resolveProjectDir(opts.project);
|
|
44218
44374
|
return {
|
|
44219
|
-
projectName: sanitizeName(
|
|
44375
|
+
projectName: sanitizeName(import_node_path14.default.basename(projectDir)),
|
|
44220
44376
|
projectDir,
|
|
44221
44377
|
nodePath: process.execPath,
|
|
44222
44378
|
cliEntry: resolveCliEntry(),
|
|
44223
|
-
logPath:
|
|
44379
|
+
logPath: import_node_path14.default.join(projectDir, ".sail", "agent.log"),
|
|
44224
44380
|
interval: opts.interval != null ? Number(opts.interval) : void 0,
|
|
44225
44381
|
chain: opts.chain != null ? Number(opts.chain) : void 0,
|
|
44226
44382
|
restartSec: 30
|
|
44227
44383
|
};
|
|
44228
44384
|
}
|
|
44229
44385
|
function plistPath(projectName) {
|
|
44230
|
-
return
|
|
44386
|
+
return import_node_path14.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
|
|
44231
44387
|
}
|
|
44232
44388
|
function systemdPath(projectName) {
|
|
44233
|
-
return
|
|
44389
|
+
return import_node_path14.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
|
|
44234
44390
|
}
|
|
44235
44391
|
async function serviceInstall(opts = {}) {
|
|
44236
44392
|
const cfg = buildConfig(opts);
|
|
@@ -44254,12 +44410,12 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
|
|
|
44254
44410
|
console.warn(`\u26A0 ${msg}
|
|
44255
44411
|
Proceeding because --force was given.`);
|
|
44256
44412
|
}
|
|
44257
|
-
|
|
44413
|
+
import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(cfg.logPath), { recursive: true });
|
|
44258
44414
|
if (platform === "darwin") {
|
|
44259
44415
|
const plist = buildLaunchdPlist(cfg);
|
|
44260
44416
|
const dest = plistPath(cfg.projectName);
|
|
44261
|
-
|
|
44262
|
-
|
|
44417
|
+
import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
|
|
44418
|
+
import_node_fs18.default.writeFileSync(dest, plist);
|
|
44263
44419
|
const uid2 = process.getuid?.() ?? 0;
|
|
44264
44420
|
try {
|
|
44265
44421
|
try {
|
|
@@ -44278,8 +44434,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
|
|
|
44278
44434
|
if (platform === "linux") {
|
|
44279
44435
|
const unit = buildSystemdUnit(cfg);
|
|
44280
44436
|
const dest = systemdPath(cfg.projectName);
|
|
44281
|
-
|
|
44282
|
-
|
|
44437
|
+
import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
|
|
44438
|
+
import_node_fs18.default.writeFileSync(dest, unit);
|
|
44283
44439
|
try {
|
|
44284
44440
|
(0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
|
|
44285
44441
|
(0, import_node_child_process2.execFileSync)("systemctl", ["--user", "enable", "--now", systemdUnitName(cfg.projectName)], {
|
|
@@ -44293,8 +44449,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
|
|
|
44293
44449
|
}
|
|
44294
44450
|
if (platform === "win32") {
|
|
44295
44451
|
const xml = buildWindowsTaskXml(cfg);
|
|
44296
|
-
const dest =
|
|
44297
|
-
|
|
44452
|
+
const dest = import_node_path14.default.join(import_node_os.default.tmpdir(), `${windowsTaskName(cfg.projectName)}.xml`);
|
|
44453
|
+
import_node_fs18.default.writeFileSync(dest, xml, "utf-8");
|
|
44298
44454
|
try {
|
|
44299
44455
|
(0, import_node_child_process2.execFileSync)(
|
|
44300
44456
|
"schtasks",
|
|
@@ -44305,7 +44461,7 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
|
|
|
44305
44461
|
throw new Error(`schtasks /Create failed: ${err.message}`);
|
|
44306
44462
|
} finally {
|
|
44307
44463
|
try {
|
|
44308
|
-
|
|
44464
|
+
import_node_fs18.default.rmSync(dest, { force: true });
|
|
44309
44465
|
} catch {
|
|
44310
44466
|
}
|
|
44311
44467
|
}
|
|
@@ -44331,14 +44487,14 @@ function okInstall(cfg, unit) {
|
|
|
44331
44487
|
);
|
|
44332
44488
|
}
|
|
44333
44489
|
async function serviceStatus(opts = {}) {
|
|
44334
|
-
const projectName = sanitizeName(
|
|
44490
|
+
const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
|
|
44335
44491
|
const platform = process.platform;
|
|
44336
44492
|
let installed = false;
|
|
44337
44493
|
let running = false;
|
|
44338
44494
|
let detail = "";
|
|
44339
44495
|
try {
|
|
44340
44496
|
if (platform === "darwin") {
|
|
44341
|
-
installed =
|
|
44497
|
+
installed = import_node_fs18.default.existsSync(plistPath(projectName));
|
|
44342
44498
|
const uid2 = process.getuid?.() ?? 0;
|
|
44343
44499
|
try {
|
|
44344
44500
|
detail = (0, import_node_child_process2.execFileSync)("launchctl", ["print", `gui/${uid2}/${launchdLabel(projectName)}`], {
|
|
@@ -44350,7 +44506,7 @@ async function serviceStatus(opts = {}) {
|
|
|
44350
44506
|
} catch {
|
|
44351
44507
|
}
|
|
44352
44508
|
} else if (platform === "linux") {
|
|
44353
|
-
installed =
|
|
44509
|
+
installed = import_node_fs18.default.existsSync(systemdPath(projectName));
|
|
44354
44510
|
try {
|
|
44355
44511
|
detail = (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "is-active", systemdUnitName(projectName)], {
|
|
44356
44512
|
encoding: "utf-8",
|
|
@@ -44386,7 +44542,7 @@ async function serviceStatus(opts = {}) {
|
|
|
44386
44542
|
);
|
|
44387
44543
|
}
|
|
44388
44544
|
async function serviceStop(opts = {}) {
|
|
44389
|
-
const projectName = sanitizeName(
|
|
44545
|
+
const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
|
|
44390
44546
|
const platform = process.platform;
|
|
44391
44547
|
if (platform === "darwin") {
|
|
44392
44548
|
const uid2 = process.getuid?.() ?? 0;
|
|
@@ -44405,7 +44561,7 @@ async function serviceStop(opts = {}) {
|
|
|
44405
44561
|
});
|
|
44406
44562
|
}
|
|
44407
44563
|
async function serviceUninstall(opts = {}) {
|
|
44408
|
-
const projectName = sanitizeName(
|
|
44564
|
+
const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
|
|
44409
44565
|
const platform = process.platform;
|
|
44410
44566
|
if (platform === "darwin") {
|
|
44411
44567
|
const uid2 = process.getuid?.() ?? 0;
|
|
@@ -44415,7 +44571,7 @@ async function serviceUninstall(opts = {}) {
|
|
|
44415
44571
|
});
|
|
44416
44572
|
} catch {
|
|
44417
44573
|
}
|
|
44418
|
-
|
|
44574
|
+
import_node_fs18.default.rmSync(plistPath(projectName), { force: true });
|
|
44419
44575
|
} else if (platform === "linux") {
|
|
44420
44576
|
try {
|
|
44421
44577
|
(0, import_node_child_process2.execFileSync)("systemctl", ["--user", "disable", "--now", systemdUnitName(projectName)], {
|
|
@@ -44423,7 +44579,7 @@ async function serviceUninstall(opts = {}) {
|
|
|
44423
44579
|
});
|
|
44424
44580
|
} catch {
|
|
44425
44581
|
}
|
|
44426
|
-
|
|
44582
|
+
import_node_fs18.default.rmSync(systemdPath(projectName), { force: true });
|
|
44427
44583
|
try {
|
|
44428
44584
|
(0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "ignore" });
|
|
44429
44585
|
} catch {
|
|
@@ -44441,21 +44597,21 @@ async function serviceUninstall(opts = {}) {
|
|
|
44441
44597
|
}
|
|
44442
44598
|
async function serviceLogs(opts = {}) {
|
|
44443
44599
|
const projectDir = resolveProjectDir(opts.project);
|
|
44444
|
-
const logPath =
|
|
44445
|
-
if (!
|
|
44600
|
+
const logPath = import_node_path14.default.join(projectDir, ".sail", "agent.log");
|
|
44601
|
+
if (!import_node_fs18.default.existsSync(logPath)) {
|
|
44446
44602
|
console.log(`No log yet at ${logPath}. The service writes here once it runs.`);
|
|
44447
44603
|
return;
|
|
44448
44604
|
}
|
|
44449
44605
|
if (opts.follow) {
|
|
44450
44606
|
if (process.platform === "win32") {
|
|
44451
|
-
console.log(
|
|
44607
|
+
console.log(import_node_fs18.default.readFileSync(logPath, "utf-8"));
|
|
44452
44608
|
console.log("(follow mode is not supported on Windows here \u2014 re-run to refresh)");
|
|
44453
44609
|
return;
|
|
44454
44610
|
}
|
|
44455
44611
|
(0, import_node_child_process2.execFileSync)("tail", ["-f", logPath], { stdio: "inherit" });
|
|
44456
44612
|
return;
|
|
44457
44613
|
}
|
|
44458
|
-
const lines =
|
|
44614
|
+
const lines = import_node_fs18.default.readFileSync(logPath, "utf-8").split("\n");
|
|
44459
44615
|
console.log(lines.slice(-200).join("\n"));
|
|
44460
44616
|
}
|
|
44461
44617
|
|
|
@@ -44609,14 +44765,14 @@ async function sessionResume() {
|
|
|
44609
44765
|
}
|
|
44610
44766
|
|
|
44611
44767
|
// src/commands/station.ts
|
|
44612
|
-
var
|
|
44613
|
-
var
|
|
44614
|
-
var RUNTIME_SERVER_FILE2 = (0,
|
|
44768
|
+
var import_node_fs19 = require("node:fs");
|
|
44769
|
+
var import_node_path15 = require("node:path");
|
|
44770
|
+
var RUNTIME_SERVER_FILE2 = (0, import_node_path15.join)(".sail", "runtime", "server.json");
|
|
44615
44771
|
function readState(projectRoot) {
|
|
44616
|
-
const file = (0,
|
|
44617
|
-
if (!(0,
|
|
44772
|
+
const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
|
|
44773
|
+
if (!(0, import_node_fs19.existsSync)(file)) return null;
|
|
44618
44774
|
try {
|
|
44619
|
-
return JSON.parse((0,
|
|
44775
|
+
return JSON.parse((0, import_node_fs19.readFileSync)(file, "utf8"));
|
|
44620
44776
|
} catch {
|
|
44621
44777
|
return null;
|
|
44622
44778
|
}
|
|
@@ -44688,9 +44844,9 @@ async function stationStop(options) {
|
|
|
44688
44844
|
}
|
|
44689
44845
|
const daemon = await discoverDaemon(projectRoot);
|
|
44690
44846
|
if (!daemon) {
|
|
44691
|
-
const file = (0,
|
|
44847
|
+
const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
|
|
44692
44848
|
try {
|
|
44693
|
-
if ((0,
|
|
44849
|
+
if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
|
|
44694
44850
|
} catch {
|
|
44695
44851
|
}
|
|
44696
44852
|
emit(options.json, () => console.log("Station process not found; cleared stale state."), {
|
|
@@ -44706,9 +44862,9 @@ async function stationStop(options) {
|
|
|
44706
44862
|
pid: state.pid
|
|
44707
44863
|
});
|
|
44708
44864
|
} catch {
|
|
44709
|
-
const file = (0,
|
|
44865
|
+
const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
|
|
44710
44866
|
try {
|
|
44711
|
-
if ((0,
|
|
44867
|
+
if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
|
|
44712
44868
|
} catch {
|
|
44713
44869
|
}
|
|
44714
44870
|
emit(options.json, () => console.log("Station process not found; cleared stale state."), {
|
|
@@ -44763,15 +44919,15 @@ async function status() {
|
|
|
44763
44919
|
|
|
44764
44920
|
// src/commands/ui.ts
|
|
44765
44921
|
var import_node_child_process4 = require("node:child_process");
|
|
44766
|
-
var
|
|
44922
|
+
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
44767
44923
|
var import_node_net2 = __toESM(require("node:net"), 1);
|
|
44768
|
-
var
|
|
44769
|
-
var UI_STATE_FILE =
|
|
44924
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
44925
|
+
var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
|
|
44770
44926
|
function readState2(projectRoot) {
|
|
44771
|
-
const file =
|
|
44772
|
-
if (!
|
|
44927
|
+
const file = import_node_path16.default.join(projectRoot, UI_STATE_FILE);
|
|
44928
|
+
if (!import_node_fs20.default.existsSync(file)) return null;
|
|
44773
44929
|
try {
|
|
44774
|
-
return JSON.parse(
|
|
44930
|
+
return JSON.parse(import_node_fs20.default.readFileSync(file, "utf-8"));
|
|
44775
44931
|
} catch {
|
|
44776
44932
|
return null;
|
|
44777
44933
|
}
|
|
@@ -44812,15 +44968,15 @@ function waitForPort(port, timeoutMs) {
|
|
|
44812
44968
|
}
|
|
44813
44969
|
async function uiCommand() {
|
|
44814
44970
|
const distDir = cliDistDir();
|
|
44815
|
-
const uiDistDir =
|
|
44816
|
-
const serverBundle =
|
|
44971
|
+
const uiDistDir = import_node_path16.default.join(packageRoot(), "packages", "ui", "dist");
|
|
44972
|
+
const serverBundle = import_node_path16.default.resolve(distDir, "server.cjs");
|
|
44817
44973
|
const projectRoot = process.cwd();
|
|
44818
|
-
const sailDir2 =
|
|
44974
|
+
const sailDir2 = import_node_path16.default.join(projectRoot, ".sail");
|
|
44819
44975
|
const port = await findFreePort(projectPort(projectRoot));
|
|
44820
|
-
if (!
|
|
44976
|
+
if (!import_node_fs20.default.existsSync(serverBundle)) {
|
|
44821
44977
|
throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
|
|
44822
44978
|
}
|
|
44823
|
-
if (!
|
|
44979
|
+
if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
|
|
44824
44980
|
throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
|
|
44825
44981
|
}
|
|
44826
44982
|
const existing = readState2(projectRoot);
|
|
@@ -44828,17 +44984,17 @@ async function uiCommand() {
|
|
|
44828
44984
|
console.log(`Sailor UI is already running (pid ${existing.pid}) at http://localhost:${existing.port}`);
|
|
44829
44985
|
return;
|
|
44830
44986
|
}
|
|
44831
|
-
const runtimeDir =
|
|
44832
|
-
|
|
44833
|
-
const logFile =
|
|
44834
|
-
const logFd =
|
|
44987
|
+
const runtimeDir = import_node_path16.default.join(projectRoot, ".sail", "runtime");
|
|
44988
|
+
import_node_fs20.default.mkdirSync(runtimeDir, { recursive: true });
|
|
44989
|
+
const logFile = import_node_path16.default.join(runtimeDir, "ui.log");
|
|
44990
|
+
const logFd = import_node_fs20.default.openSync(logFile, "a");
|
|
44835
44991
|
const child = (0, import_node_child_process4.spawn)(process.execPath, [serverBundle], {
|
|
44836
44992
|
detached: true,
|
|
44837
44993
|
stdio: ["ignore", logFd, logFd],
|
|
44838
44994
|
env: { ...process.env, SAIL_DIR: sailDir2, SERVE_DIST: "1", PORT: String(port), SAILOR_UI_DIST: uiDistDir }
|
|
44839
44995
|
});
|
|
44840
44996
|
child.unref();
|
|
44841
|
-
|
|
44997
|
+
import_node_fs20.default.closeSync(logFd);
|
|
44842
44998
|
const READY_TIMEOUT_MS = 1e4;
|
|
44843
44999
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
44844
45000
|
let ready = false;
|
|
@@ -44852,18 +45008,18 @@ async function uiCommand() {
|
|
|
44852
45008
|
if (!ready) {
|
|
44853
45009
|
let tail = "";
|
|
44854
45010
|
try {
|
|
44855
|
-
tail =
|
|
45011
|
+
tail = import_node_fs20.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
|
|
44856
45012
|
} catch {
|
|
44857
45013
|
}
|
|
44858
45014
|
throw new Error(
|
|
44859
45015
|
`Sailor UI failed to start within ${READY_TIMEOUT_MS / 1e3}s on port ${port}.` + (tail ? `
|
|
44860
45016
|
|
|
44861
45017
|
Server output:
|
|
44862
|
-
${tail}` : ` See ${
|
|
45018
|
+
${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
|
|
44863
45019
|
);
|
|
44864
45020
|
}
|
|
44865
|
-
|
|
44866
|
-
|
|
45021
|
+
import_node_fs20.default.writeFileSync(
|
|
45022
|
+
import_node_path16.default.join(projectRoot, UI_STATE_FILE),
|
|
44867
45023
|
JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
44868
45024
|
);
|
|
44869
45025
|
console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
|
|
@@ -44874,7 +45030,7 @@ function uiStatus() {
|
|
|
44874
45030
|
if (state && isAlive(state.pid)) {
|
|
44875
45031
|
console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
|
|
44876
45032
|
} else {
|
|
44877
|
-
if (state)
|
|
45033
|
+
if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
|
|
44878
45034
|
console.log("\u25CB Sailor UI is not running");
|
|
44879
45035
|
}
|
|
44880
45036
|
}
|
|
@@ -44886,19 +45042,19 @@ function uiStop() {
|
|
|
44886
45042
|
return;
|
|
44887
45043
|
}
|
|
44888
45044
|
if (!isAlive(state.pid)) {
|
|
44889
|
-
|
|
45045
|
+
import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
|
|
44890
45046
|
console.log("Sailor UI is not running (stale state file removed).");
|
|
44891
45047
|
return;
|
|
44892
45048
|
}
|
|
44893
45049
|
process.kill(state.pid, "SIGTERM");
|
|
44894
|
-
|
|
45050
|
+
import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
|
|
44895
45051
|
console.log(`Stopped Sailor UI (pid ${state.pid}).`);
|
|
44896
45052
|
}
|
|
44897
45053
|
|
|
44898
45054
|
// src/index.ts
|
|
44899
45055
|
function cliVersion() {
|
|
44900
45056
|
try {
|
|
44901
|
-
const pkg = JSON.parse((0,
|
|
45057
|
+
const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path17.join)(packageRoot(), "package.json"), "utf-8"));
|
|
44902
45058
|
return pkg.version ?? "0.0.0";
|
|
44903
45059
|
} catch {
|
|
44904
45060
|
return "0.0.0";
|
|
@@ -44940,6 +45096,7 @@ program2.command("init [dir]").description("Scaffold a new Sail agent into the c
|
|
|
44940
45096
|
}
|
|
44941
45097
|
}
|
|
44942
45098
|
);
|
|
45099
|
+
program2.command("update").description("Re-sync agent tooling files (skills, AGENTS.md, Dockerfile) from the latest template").action(action(updateCommand));
|
|
44943
45100
|
var ui = program2.command("ui").description("Manage the local Sailor dashboard");
|
|
44944
45101
|
ui.command("start").description("Start the dashboard at localhost:3333 (default)").action(action(uiCommand));
|
|
44945
45102
|
ui.command("stop").description("Stop the running dashboard").action(() => uiStop());
|
|
@@ -44965,14 +45122,14 @@ mandate.command("prepare").description("Prepare a mandate draft for review and s
|
|
|
44965
45122
|
mandate.command("sign").description("Review and confirm the permissions authorized for your SMA").option("--yes", "Skip the confirmation prompt (for non-interactive / CI use)").action(actionWith(mandateSign));
|
|
44966
45123
|
mandate.command("deploy").description("Deploy a Foundry-compiled permission contract via the browser signing UI").option("--artifact <path>", "Path to the Foundry artifact JSON (out/<Name>.sol/<Name>.json)").option("--contract <name>", "Contract name; resolves to <out>/<name>.sol/<name>.json").option("--out <dir>", "Foundry output directory", "out").option("--name <label>", "Label to track this permission under (defaults to contract name)").option("--args <json>", `Constructor args as JSON array. Bash: '["0x..","1"]'. PowerShell: '[\\"0x..\\",\\"1\\"]'. Use --args-file to avoid quoting.`).option("--args-file <path>", "Path to a JSON file containing constructor args array (recommended on PowerShell)").option("--build", "Run `forge build` before deploying").option("--attach", "After deploy, register the permission on --sma").option("--sma <address>", "SMA to register on (required with --attach)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeploy));
|
|
44967
45124
|
mandate.command("attach").description("Register an already-deployed permission on an SMA (EIP-712 RegisterPermission)").requiredOption("--address <mandateOrName>", "Permission address, or a name tracked locally").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));
|
|
44968
|
-
mandate.command("deploy-clone").description("Deploy + register a standalone clone permission
|
|
45125
|
+
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));
|
|
44969
45126
|
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));
|
|
44970
45127
|
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));
|
|
44971
45128
|
mandate.command("simulate").description(
|
|
44972
45129
|
"Probe a permission against sample calls off-chain (eth_call, NO gas) \u2014 prove it accepts the calls you want and rejects the ones you don't, before authorizing on-chain"
|
|
44973
45130
|
).requiredOption("--address <permissionOrName>", "Permission to probe (address or tracked name)").option("--sma <address>", "SMA to probe as (ctx.account; defaults to .sail/account.json)").option("--target <address>", "Inline single call: target contract address").option("--calldata <hex>", "Inline single call: 0x-prefixed calldata").option("--value <wei>", "Inline single call: ETH value in wei (default 0)").option("--expect <pass|fail>", "Inline single call: expected outcome (sets non-zero exit on mismatch)").option("--label <text>", "Inline single call: human-readable label").option("--calls <file>", "Batch: JSON array of { target, calldata, value?, expect?, label? }").option("--json", "Emit machine-readable JSON").action(actionWith(mandateSimulate));
|
|
44974
45131
|
mandate.command("update").description("Update metadata for a tracked permission contract (rename, source path, artifact path)").requiredOption("--address <mandateOrName>", "Permission address or tracked name to update").option("--name <label>", "New tracking label (must be unique within the same chain)").option("--source-path <path>", "Update the relative path to the Solidity source file").option("--artifact-path <path>", "Update the relative path to the Foundry artifact JSON").option("--json", "Emit machine-readable JSON").action(actionWith(mandateUpdate));
|
|
44975
|
-
mandate.command("list").description("List permission contracts deployed from this project").
|
|
45132
|
+
mandate.command("list").description("List permission contracts deployed from this project").option("--json", "Emit machine-readable JSON").action(actionWith(mandateContractsList));
|
|
44976
45133
|
program2.command("onboard").description("Set up an SMA, register a permission, confirm the agent is operational").option("--sma <address>", "Use a specific SMA address instead of prompting").option("--new-sma", "Create a new SMA via SailKernel").option("--salt <n>", "CREATE2 salt for deterministic Safe address (default: 0; use 0 for first SMA, increment for subsequent)").option("--template <kindOrAddress>", "Register this permission contract (kind, label, or address)").option("--skip-mandate", "Skip the permission registration step").option("--json", "Emit machine-readable JSON (implies non-interactive)").action(actionWith(onboard));
|
|
44977
45134
|
var station = program2.command("station").description("Manage the persistent signing station (browser signing daemon)");
|
|
44978
45135
|
station.command("start").description("Start the signing station and keep it running (blocks \u2014 run in the background)").option("--json", "Emit machine-readable JSON").action(actionWith(stationStart));
|