@dev.sail.money/sailor 1.2.1-94 → 1.3.0-208
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 +63 -0
- package/package.json +2 -1
- package/packages/cli/dist/index.cjs +467 -321
- package/packages/cli/dist/server.cjs +7 -8
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/dist/keyring.d.ts +0 -6
- package/packages/sdk/dist/keyring.d.ts.map +1 -1
- package/packages/sdk/dist/keyring.js +1 -8
- package/packages/sdk/dist/keyring.js.map +1 -1
- package/packages/ui/dist/assets/{add-Dj4_n773.js → add-CfTRVkh-.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-BaFysN1G.js → all-wallets-VZChJwJf.js} +1 -1
- package/packages/ui/dist/assets/{app-store-DOc31zzY.js → app-store-CuFK1xBJ.js} +1 -1
- package/packages/ui/dist/assets/{apple-BvrvOkPH.js → apple-DETqVk_I.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-E1AfjXOT.js → arrow-bottom-CnNYsZfo.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-sou7Vo1V.js → arrow-bottom-circle-DZwJF3UO.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-wk3G9Ywf.js → arrow-left-CBnisTAa.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-_a703_Pw.js → arrow-right-DA3Y1Yzf.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-txyg3R3e.js → arrow-top-e8-FE_fo.js} +1 -1
- package/packages/ui/dist/assets/{bank-BB1kd0NJ.js → bank-DdwromQn.js} +1 -1
- package/packages/ui/dist/assets/{basic-D0dy7nIZ.js → basic-DLYER2un.js} +1 -1
- package/packages/ui/dist/assets/{browser-BwHLhsTn.js → browser-BJNrv4Zj.js} +1 -1
- package/packages/ui/dist/assets/{card-DRC6HmPF.js → card-DB6j4DSk.js} +1 -1
- package/packages/ui/dist/assets/{ccip-Dq6KeJQl.js → ccip-Dy_s323Y.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-TbAAQGjd.js → checkmark-Db9oicko.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-4723vJ0-.js → checkmark-bold-D9MgGHxv.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-DtLoBRe9.js → chevron-bottom-IGUDJRKN.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-BtydsF9j.js → chevron-left-CWpfP-rw.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-BbkU_Kf8.js → chevron-right-BejY0vtE.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-BWf-sN-F.js → chevron-top-BaUN0LGF.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-CVFxc5H2.js → chrome-store-fox1tIqs.js} +1 -1
- package/packages/ui/dist/assets/{clock-B4hJww1X.js → clock-DlqB4Ehi.js} +1 -1
- package/packages/ui/dist/assets/{close-Ci93jqGe.js → close-03e52feP.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-cRv_xDgC.js → coinPlaceholder-BO3nv4hx.js} +1 -1
- package/packages/ui/dist/assets/{compass-_sZZPtm6.js → compass-CIqPRE0k.js} +1 -1
- package/packages/ui/dist/assets/{copy-DLaiLK0n.js → copy-D5cJBwZq.js} +1 -1
- package/packages/ui/dist/assets/{core-YssZmpJa.js → core-COelOg3-.js} +3 -3
- package/packages/ui/dist/assets/cursor-CRb7R3u1.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-CzXQbB2p.js → cursor-transparent-Cvbiq9Br.js} +1 -1
- package/packages/ui/dist/assets/{desktop-BiqQI1Ss.js → desktop-Dp2wtF3n.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-DYu7YCzu.js → disconnect-Dx_IGM73.js} +1 -1
- package/packages/ui/dist/assets/{discord-C-ife-UZ.js → discord-Cxryn14a.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-Dq7ac-p9.js → etherscan-D8aNXWCx.js} +1 -1
- package/packages/ui/dist/assets/{events-BhTGLk8n.js → events-DR6OOHz2.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-BcOdRdbp.js → exclamation-triangle-BBCaPLj1.js} +1 -1
- package/packages/ui/dist/assets/{extension-DmJLIidM.js → extension-bACRNBl5.js} +1 -1
- package/packages/ui/dist/assets/{external-link-HNjp7-Po.js → external-link-BZRqHo3q.js} +1 -1
- package/packages/ui/dist/assets/{facebook-BrSvHNyw.js → facebook-TAdUitg4.js} +1 -1
- package/packages/ui/dist/assets/{fallback-Ubz80uXP.js → fallback-DGiwrboN.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-BsjltM6k.js → farcaster-YzdobBfR.js} +1 -1
- package/packages/ui/dist/assets/{filters-B4yQbYZP.js → filters-CT-xLEoI.js} +1 -1
- package/packages/ui/dist/assets/{github-CFq82Dds.js → github-DxPBbmEL.js} +1 -1
- package/packages/ui/dist/assets/{google-DwZutf4a.js → google-B0TMgFhK.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-CyA_8KQg.js → help-circle-Be6sRPTw.js} +1 -1
- package/packages/ui/dist/assets/{id-jyZYn4LJ.js → id-Dem_akbE.js} +1 -1
- package/packages/ui/dist/assets/{image-Zv0yoBcH.js → image-CszBzFVG.js} +1 -1
- package/packages/ui/dist/assets/{index-BxRaYDiX.js → index-B0FDHAiK.js} +1 -1
- package/packages/ui/dist/assets/{index-67NncC_u.js → index-C2n9XvGG.js} +3 -3
- package/packages/ui/dist/assets/{index-_DkPx8RN.js → index-Cb5eBfrN.js} +77 -77
- package/packages/ui/dist/assets/{index-BY8A6EK8.js → index-DAawBKd2.js} +1 -1
- package/packages/ui/dist/assets/{index-CeGppB9s.js → index-DXqj6oYp.js} +1 -1
- package/packages/ui/dist/assets/{index-BdG43zCl.js → index-homh6mWC.js} +1 -1
- package/packages/ui/dist/assets/{index.es-C8EklrJY.js → index.es-DJgl78dj.js} +4 -4
- package/packages/ui/dist/assets/{info-BwGEOdmB.js → info-BimGN4NK.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-BPCcX-Iq.js → info-circle-lnKEJu2R.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-BRwr4IaC.js → lightbulb-DS1vEMce.js} +1 -1
- package/packages/ui/dist/assets/{mail-D-k_Vhe4.js → mail-DoS3Wibk.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-AcOgwGrj.js → metamask-sdk-CMIU3BwW.js} +1 -1
- package/packages/ui/dist/assets/{mobile-B1ok9PfY.js → mobile-B09Cs95_.js} +1 -1
- package/packages/ui/dist/assets/{more-DeMTMLWs.js → more-CLBZT8WI.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-J0Rc1I89.js → network-placeholder-C45Cp8rY.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-dXGrzzoa.js → nftPlaceholder-CAVclL3u.js} +1 -1
- package/packages/ui/dist/assets/{off-CS0f77lw.js → off-CwE8HoZ0.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-DJ16fHC4.js → parseSignature-kmMNHYjl.js} +1 -1
- package/packages/ui/dist/assets/{play-store-DmK8UaNC.js → play-store-DiFF_6n4.js} +1 -1
- package/packages/ui/dist/assets/{plus-CSGSnoCh.js → plus-aygpl_pZ.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-BJMH0xmM.js → qr-code-DpIS4fFK.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-D5XP5VgJ.js → recycle-horizontal-Qk2N74JE.js} +1 -1
- package/packages/ui/dist/assets/{refresh-MLX3Rdp6.js → refresh-sWRq9Vnt.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-CVoDFQbM.js → reown-logo-YQxBa9i9.js} +1 -1
- package/packages/ui/dist/assets/{search-BDJDyvjC.js → search-Bt6BSPcC.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-BWh3B02k.js → secp256k1-BUh3OoFd.js} +1 -1
- package/packages/ui/dist/assets/{send-BOq2svx_.js → send-ym-Fn17O.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-ByjLi_-_.js → swapHorizontal-fBE2gnqc.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-fmCp_4AE.js → swapHorizontalBold-CekQ22FJ.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-D6dFseYw.js → swapHorizontalMedium-B9BI9ovU.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-B2V3m4ha.js → swapHorizontalRoundedBold-DOC75mBH.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-DVmCsmeT.js → swapVertical-CSQAPH0z.js} +1 -1
- package/packages/ui/dist/assets/{telegram-Bv8JRbMM.js → telegram-BPjzFxKU.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-YYDJ18z6.js → three-dots-DP6hdXH8.js} +1 -1
- package/packages/ui/dist/assets/{twitch-B2_Jy-Ft.js → twitch-CgHMd-f3.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-pKQzPQRt.js → twitterIcon-BrR8ynXm.js} +1 -1
- package/packages/ui/dist/assets/{verify-Bty2jIav.js → verify-D35D7Wtv.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-CKz2fuKl.js → verify-filled-DCP5CT59.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-Wg1mtmVM.js → w3m-modal-B-ZptWk0.js} +1 -1
- package/packages/ui/dist/assets/{wallet-DslzC8rD.js → wallet-Cnb4dkr3.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-Dyw8jJ56.js → wallet-placeholder-CCzk0PqU.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-DZBfbgp4.js → walletconnect-f6tQZYRi.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-D7NPDx4k.js → warning-circle-zxMagvfx.js} +1 -1
- package/packages/ui/dist/assets/{x-DRR_9FNs.js → x-D0nEGO1G.js} +1 -1
- package/packages/ui/dist/index.html +1 -1
- package/scripts/clean.mjs +17 -0
- package/templates/default/.agents/skills/sail-onboarding/SKILL.md +36 -0
- package/templates/default/.agents/skills/sail-servers/SKILL.md +26 -0
- package/templates/default/AGENTS.md +1 -1
- package/packages/ui/dist/assets/cursor-B_xyJnjj.js +0 -3
|
@@ -22626,7 +22626,7 @@ var require_websocket = __commonJS({
|
|
|
22626
22626
|
var EventEmitter = require("events");
|
|
22627
22627
|
var https = require("https");
|
|
22628
22628
|
var http2 = require("http");
|
|
22629
|
-
var
|
|
22629
|
+
var net3 = require("net");
|
|
22630
22630
|
var tls = require("tls");
|
|
22631
22631
|
var { randomBytes: randomBytes4, createHash } = require("crypto");
|
|
22632
22632
|
var { Duplex, Readable } = require("stream");
|
|
@@ -23360,12 +23360,12 @@ var require_websocket = __commonJS({
|
|
|
23360
23360
|
}
|
|
23361
23361
|
function netConnect(options) {
|
|
23362
23362
|
options.path = options.socketPath;
|
|
23363
|
-
return
|
|
23363
|
+
return net3.connect(options);
|
|
23364
23364
|
}
|
|
23365
23365
|
function tlsConnect(options) {
|
|
23366
23366
|
options.path = void 0;
|
|
23367
23367
|
if (!options.servername && options.servername !== "") {
|
|
23368
|
-
options.servername =
|
|
23368
|
+
options.servername = net3.isIP(options.host) ? "" : options.host;
|
|
23369
23369
|
}
|
|
23370
23370
|
return tls.connect(options);
|
|
23371
23371
|
}
|
|
@@ -33756,7 +33756,7 @@ var require_websocket2 = __commonJS({
|
|
|
33756
33756
|
var EventEmitter = require("events");
|
|
33757
33757
|
var https = require("https");
|
|
33758
33758
|
var http2 = require("http");
|
|
33759
|
-
var
|
|
33759
|
+
var net3 = require("net");
|
|
33760
33760
|
var tls = require("tls");
|
|
33761
33761
|
var { randomBytes: randomBytes4, createHash } = require("crypto");
|
|
33762
33762
|
var { Duplex, Readable } = require("stream");
|
|
@@ -34500,12 +34500,12 @@ var require_websocket2 = __commonJS({
|
|
|
34500
34500
|
}
|
|
34501
34501
|
function netConnect(options) {
|
|
34502
34502
|
options.path = options.socketPath;
|
|
34503
|
-
return
|
|
34503
|
+
return net3.connect(options);
|
|
34504
34504
|
}
|
|
34505
34505
|
function tlsConnect(options) {
|
|
34506
34506
|
options.path = void 0;
|
|
34507
34507
|
if (!options.servername && options.servername !== "") {
|
|
34508
|
-
options.servername =
|
|
34508
|
+
options.servername = net3.isIP(options.host) ? "" : options.host;
|
|
34509
34509
|
}
|
|
34510
34510
|
return tls.connect(options);
|
|
34511
34511
|
}
|
|
@@ -37445,18 +37445,12 @@ var LocalKeyring = class _LocalKeyring {
|
|
|
37445
37445
|
this.account = account2;
|
|
37446
37446
|
this.address = account2.address;
|
|
37447
37447
|
this.privateKey = options.privateKey;
|
|
37448
|
-
} else
|
|
37448
|
+
} else {
|
|
37449
37449
|
const account2 = mnemonicToAccount(options.mnemonic, options.derivationPath ? { path: options.derivationPath } : void 0);
|
|
37450
37450
|
this.account = account2;
|
|
37451
37451
|
this.address = account2.address;
|
|
37452
|
-
} else {
|
|
37453
|
-
throw new Error("keystore decryption not implemented \u2014 use type: 'privateKey' or type: 'mnemonic' instead");
|
|
37454
37452
|
}
|
|
37455
37453
|
}
|
|
37456
|
-
/** Returns a lightweight signer stub for read-only contexts where the key is not available. */
|
|
37457
|
-
static fromAddress(address) {
|
|
37458
|
-
return { address };
|
|
37459
|
-
}
|
|
37460
37454
|
/** Generates a brand-new keyring backed by a random private key. */
|
|
37461
37455
|
static generate() {
|
|
37462
37456
|
return new _LocalKeyring({ type: "privateKey", privateKey: generatePrivateKey() });
|
|
@@ -38497,15 +38491,58 @@ function upsertAccountInList(account2, name, baseSailDir = sailDir()) {
|
|
|
38497
38491
|
}
|
|
38498
38492
|
|
|
38499
38493
|
// src/signing/client.ts
|
|
38500
|
-
var
|
|
38494
|
+
var import_node_fs7 = require("node:fs");
|
|
38501
38495
|
var import_node_path5 = require("node:path");
|
|
38502
38496
|
|
|
38503
38497
|
// src/signing/server.ts
|
|
38504
38498
|
var import_node_crypto2 = require("node:crypto");
|
|
38505
|
-
var
|
|
38499
|
+
var import_node_fs6 = require("node:fs");
|
|
38506
38500
|
var import_node_http = require("node:http");
|
|
38507
|
-
var import_node_net = require("node:net");
|
|
38508
38501
|
var import_node_path4 = require("node:path");
|
|
38502
|
+
|
|
38503
|
+
// src/lib/process.ts
|
|
38504
|
+
var import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
38505
|
+
var import_node_net = __toESM(require("node:net"), 1);
|
|
38506
|
+
function agentPidPath(chainId) {
|
|
38507
|
+
return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
|
|
38508
|
+
}
|
|
38509
|
+
function writeAgentPid(chainId) {
|
|
38510
|
+
import_node_fs5.default.mkdirSync(sailPath(), { recursive: true });
|
|
38511
|
+
import_node_fs5.default.writeFileSync(agentPidPath(chainId), `${process.pid}
|
|
38512
|
+
`);
|
|
38513
|
+
}
|
|
38514
|
+
function clearAgentPid(chainId) {
|
|
38515
|
+
try {
|
|
38516
|
+
import_node_fs5.default.rmSync(agentPidPath(chainId));
|
|
38517
|
+
} catch {
|
|
38518
|
+
}
|
|
38519
|
+
}
|
|
38520
|
+
function readAgentPid(chainId) {
|
|
38521
|
+
try {
|
|
38522
|
+
const pid = Number.parseInt(import_node_fs5.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
|
|
38523
|
+
return Number.isNaN(pid) ? null : pid;
|
|
38524
|
+
} catch {
|
|
38525
|
+
return null;
|
|
38526
|
+
}
|
|
38527
|
+
}
|
|
38528
|
+
function isProcessAlive(pid) {
|
|
38529
|
+
try {
|
|
38530
|
+
process.kill(pid, 0);
|
|
38531
|
+
return true;
|
|
38532
|
+
} catch (err) {
|
|
38533
|
+
return err.code === "EPERM";
|
|
38534
|
+
}
|
|
38535
|
+
}
|
|
38536
|
+
function findFreePort(from14) {
|
|
38537
|
+
return new Promise((resolve3, reject) => {
|
|
38538
|
+
const server = import_node_net.default.createServer();
|
|
38539
|
+
server.unref();
|
|
38540
|
+
server.on("error", () => findFreePort(from14 + 1).then(resolve3, reject));
|
|
38541
|
+
server.listen(from14, "127.0.0.1", () => server.close(() => resolve3(from14)));
|
|
38542
|
+
});
|
|
38543
|
+
}
|
|
38544
|
+
|
|
38545
|
+
// src/signing/server.ts
|
|
38509
38546
|
init_esm2();
|
|
38510
38547
|
|
|
38511
38548
|
// ../../node_modules/.pnpm/ws@8.21.0_bufferutil@4.1.0_utf-8-validate@5.0.10/node_modules/ws/wrapper.mjs
|
|
@@ -38519,7 +38556,8 @@ var import_websocket2 = __toESM(require_websocket2(), 1);
|
|
|
38519
38556
|
var import_websocket_server2 = __toESM(require_websocket_server2(), 1);
|
|
38520
38557
|
|
|
38521
38558
|
// src/signing/server.ts
|
|
38522
|
-
var
|
|
38559
|
+
var _signingPort = parseInt(process.env.SAILOR_STATION_PORT ?? "", 10);
|
|
38560
|
+
var DEFAULT_SIGNING_PORT = Number.isFinite(_signingPort) && _signingPort >= 1 && _signingPort <= 65535 ? _signingPort : 3141;
|
|
38523
38561
|
var RUNTIME_SUBDIR = (0, import_node_path4.join)(".sail", "runtime");
|
|
38524
38562
|
var SERVER_STATE_FILE = "server.json";
|
|
38525
38563
|
var REQUEST_SECRET_HEADER = "x-sailor-secret";
|
|
@@ -38544,7 +38582,7 @@ function findUiDist() {
|
|
|
38544
38582
|
(0, import_node_path4.join)(process.cwd(), "..", "ui", "dist")
|
|
38545
38583
|
];
|
|
38546
38584
|
for (const c of candidates) {
|
|
38547
|
-
if ((0,
|
|
38585
|
+
if ((0, import_node_fs6.existsSync)((0, import_node_path4.join)(c, "index.html"))) return c;
|
|
38548
38586
|
}
|
|
38549
38587
|
return null;
|
|
38550
38588
|
}
|
|
@@ -38592,7 +38630,7 @@ var SigningServer = class {
|
|
|
38592
38630
|
return this.httpServer?.listening ?? false;
|
|
38593
38631
|
}
|
|
38594
38632
|
async start() {
|
|
38595
|
-
this.port = await
|
|
38633
|
+
this.port = await findFreePort(this.port);
|
|
38596
38634
|
this._url = `http://localhost:${this.port}`;
|
|
38597
38635
|
this.requestSecret = (0, import_node_crypto2.randomBytes)(16).toString("hex");
|
|
38598
38636
|
const http2 = (0, import_node_http.createServer)((req, res) => this.handleHttp(req, res));
|
|
@@ -38772,13 +38810,13 @@ var SigningServer = class {
|
|
|
38772
38810
|
const path11 = this.sailFile("config.json");
|
|
38773
38811
|
let config = {};
|
|
38774
38812
|
try {
|
|
38775
|
-
config = JSON.parse((0,
|
|
38813
|
+
config = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf-8"));
|
|
38776
38814
|
} catch {
|
|
38777
38815
|
}
|
|
38778
38816
|
if (Number(config.chainId) === Number(chainId)) return;
|
|
38779
38817
|
config.chainId = Number(chainId);
|
|
38780
|
-
(0,
|
|
38781
|
-
(0,
|
|
38818
|
+
(0, import_node_fs6.mkdirSync)(this.sailFile(), { recursive: true });
|
|
38819
|
+
(0, import_node_fs6.writeFileSync)(path11, `${JSON.stringify(config, null, 2)}
|
|
38782
38820
|
`);
|
|
38783
38821
|
} catch {
|
|
38784
38822
|
}
|
|
@@ -38786,7 +38824,7 @@ var SigningServer = class {
|
|
|
38786
38824
|
/** Stream a JSON file back, or a fallback body when it is missing/invalid. */
|
|
38787
38825
|
sendJsonFile(res, filePath, fallback2) {
|
|
38788
38826
|
try {
|
|
38789
|
-
const raw = (0,
|
|
38827
|
+
const raw = (0, import_node_fs6.readFileSync)(filePath, "utf-8");
|
|
38790
38828
|
JSON.parse(raw);
|
|
38791
38829
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
38792
38830
|
res.end(raw);
|
|
@@ -38853,8 +38891,8 @@ var SigningServer = class {
|
|
|
38853
38891
|
};
|
|
38854
38892
|
const baseSailDir = this.sailFile();
|
|
38855
38893
|
upsertAccountInList(record, void 0, baseSailDir);
|
|
38856
|
-
(0,
|
|
38857
|
-
(0,
|
|
38894
|
+
(0, import_node_fs6.mkdirSync)(baseSailDir, { recursive: true });
|
|
38895
|
+
(0, import_node_fs6.writeFileSync)(this.sailFile("account.json"), `${JSON.stringify(record, null, 2)}
|
|
38858
38896
|
`);
|
|
38859
38897
|
this.syncConfigChainId(chainId);
|
|
38860
38898
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
@@ -38869,12 +38907,12 @@ var SigningServer = class {
|
|
|
38869
38907
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
38870
38908
|
let active = null;
|
|
38871
38909
|
try {
|
|
38872
|
-
active = JSON.parse((0,
|
|
38910
|
+
active = JSON.parse((0, import_node_fs6.readFileSync)(this.sailFile("account.json"), "utf-8")).safe;
|
|
38873
38911
|
} catch {
|
|
38874
38912
|
}
|
|
38875
38913
|
try {
|
|
38876
38914
|
const accounts = JSON.parse(
|
|
38877
|
-
(0,
|
|
38915
|
+
(0, import_node_fs6.readFileSync)(this.sailFile("state", "accounts.json"), "utf-8")
|
|
38878
38916
|
);
|
|
38879
38917
|
res.end(
|
|
38880
38918
|
JSON.stringify(
|
|
@@ -38887,7 +38925,7 @@ var SigningServer = class {
|
|
|
38887
38925
|
} catch {
|
|
38888
38926
|
try {
|
|
38889
38927
|
const a = JSON.parse(
|
|
38890
|
-
(0,
|
|
38928
|
+
(0, import_node_fs6.readFileSync)(this.sailFile("account.json"), "utf-8")
|
|
38891
38929
|
);
|
|
38892
38930
|
res.end(JSON.stringify([{ ...a, name: "My SMA", active: true, addedAt: null }]));
|
|
38893
38931
|
} catch {
|
|
@@ -39010,16 +39048,16 @@ var SigningServer = class {
|
|
|
39010
39048
|
res.end();
|
|
39011
39049
|
return;
|
|
39012
39050
|
}
|
|
39013
|
-
if ((0,
|
|
39051
|
+
if ((0, import_node_fs6.existsSync)(filePath)) {
|
|
39014
39052
|
const mime = MIME[(0, import_node_path4.extname)(filePath)] ?? "application/octet-stream";
|
|
39015
39053
|
res.writeHead(200, { "Content-Type": mime });
|
|
39016
|
-
res.end((0,
|
|
39054
|
+
res.end((0, import_node_fs6.readFileSync)(filePath));
|
|
39017
39055
|
return;
|
|
39018
39056
|
}
|
|
39019
39057
|
const indexHtml = (0, import_node_path4.join)(this.uiDist, "index.html");
|
|
39020
|
-
if ((0,
|
|
39058
|
+
if ((0, import_node_fs6.existsSync)(indexHtml)) {
|
|
39021
39059
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
39022
|
-
res.end((0,
|
|
39060
|
+
res.end((0, import_node_fs6.readFileSync)(indexHtml));
|
|
39023
39061
|
return;
|
|
39024
39062
|
}
|
|
39025
39063
|
}
|
|
@@ -39106,15 +39144,15 @@ var SigningServer = class {
|
|
|
39106
39144
|
}
|
|
39107
39145
|
writeRuntimeState() {
|
|
39108
39146
|
const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
|
|
39109
|
-
if ((0,
|
|
39147
|
+
if ((0, import_node_fs6.existsSync)(path11)) {
|
|
39110
39148
|
try {
|
|
39111
|
-
const existing = JSON.parse((0,
|
|
39112
|
-
if (existing.pid != null && existing.pid !== process.pid &&
|
|
39149
|
+
const existing = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
|
|
39150
|
+
if (existing.pid != null && existing.pid !== process.pid && isProcessAlive(existing.pid)) return;
|
|
39113
39151
|
} catch {
|
|
39114
39152
|
}
|
|
39115
39153
|
}
|
|
39116
|
-
if (!(0,
|
|
39117
|
-
(0,
|
|
39154
|
+
if (!(0, import_node_fs6.existsSync)(this.runtimeDir)) (0, import_node_fs6.mkdirSync)(this.runtimeDir, { recursive: true });
|
|
39155
|
+
(0, import_node_fs6.writeFileSync)(
|
|
39118
39156
|
(0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE),
|
|
39119
39157
|
JSON.stringify(
|
|
39120
39158
|
{
|
|
@@ -39133,43 +39171,25 @@ var SigningServer = class {
|
|
|
39133
39171
|
removeRuntimeState() {
|
|
39134
39172
|
const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
|
|
39135
39173
|
try {
|
|
39136
|
-
if (!(0,
|
|
39137
|
-
const state = JSON.parse((0,
|
|
39174
|
+
if (!(0, import_node_fs6.existsSync)(path11)) return;
|
|
39175
|
+
const state = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
|
|
39138
39176
|
if (state.pid != null && state.pid !== process.pid) return;
|
|
39139
|
-
(0,
|
|
39177
|
+
(0, import_node_fs6.unlinkSync)(path11);
|
|
39140
39178
|
} catch {
|
|
39141
39179
|
}
|
|
39142
39180
|
}
|
|
39143
39181
|
};
|
|
39144
|
-
function pidAlive(pid) {
|
|
39145
|
-
try {
|
|
39146
|
-
process.kill(pid, 0);
|
|
39147
|
-
return true;
|
|
39148
|
-
} catch (err) {
|
|
39149
|
-
return err.code === "EPERM";
|
|
39150
|
-
}
|
|
39151
|
-
}
|
|
39152
39182
|
function reapStaleRuntimeState(projectRoot = process.cwd()) {
|
|
39153
39183
|
const path11 = (0, import_node_path4.join)(projectRoot, RUNTIME_SUBDIR, SERVER_STATE_FILE);
|
|
39154
39184
|
try {
|
|
39155
|
-
if (!(0,
|
|
39156
|
-
const state = JSON.parse((0,
|
|
39157
|
-
if (state.pid != null && state.pid !== process.pid && !
|
|
39158
|
-
(0,
|
|
39185
|
+
if (!(0, import_node_fs6.existsSync)(path11)) return;
|
|
39186
|
+
const state = JSON.parse((0, import_node_fs6.readFileSync)(path11, "utf8"));
|
|
39187
|
+
if (state.pid != null && state.pid !== process.pid && !isProcessAlive(state.pid)) {
|
|
39188
|
+
(0, import_node_fs6.unlinkSync)(path11);
|
|
39159
39189
|
}
|
|
39160
39190
|
} catch {
|
|
39161
39191
|
}
|
|
39162
39192
|
}
|
|
39163
|
-
async function findAvailablePort(startPort) {
|
|
39164
|
-
return new Promise((res) => {
|
|
39165
|
-
const probe = (0, import_node_net.createServer)();
|
|
39166
|
-
probe.listen(startPort, "127.0.0.1", () => {
|
|
39167
|
-
const addr = probe.address();
|
|
39168
|
-
probe.close(() => res(addr.port));
|
|
39169
|
-
});
|
|
39170
|
-
probe.on("error", () => res(findAvailablePort(startPort + 1)));
|
|
39171
|
-
});
|
|
39172
|
-
}
|
|
39173
39193
|
|
|
39174
39194
|
// src/signing/client.ts
|
|
39175
39195
|
var RUNTIME_SERVER_FILE = (0, import_node_path5.join)(".sail", "runtime", "server.json");
|
|
@@ -39251,14 +39271,14 @@ function toLoopbackIPv4(url) {
|
|
|
39251
39271
|
}
|
|
39252
39272
|
function readRuntimeServerState(projectRoot) {
|
|
39253
39273
|
const file = (0, import_node_path5.join)(projectRoot, RUNTIME_SERVER_FILE);
|
|
39254
|
-
if (!(0,
|
|
39274
|
+
if (!(0, import_node_fs7.existsSync)(file)) return null;
|
|
39255
39275
|
try {
|
|
39256
|
-
return JSON.parse((0,
|
|
39276
|
+
return JSON.parse((0, import_node_fs7.readFileSync)(file, "utf8"));
|
|
39257
39277
|
} catch {
|
|
39258
39278
|
return null;
|
|
39259
39279
|
}
|
|
39260
39280
|
}
|
|
39261
|
-
function signingPageUrl(
|
|
39281
|
+
function signingPageUrl(dashboardPort) {
|
|
39262
39282
|
return `http://localhost:${dashboardPort}/#/station`;
|
|
39263
39283
|
}
|
|
39264
39284
|
async function discoverDaemon(projectRoot = process.cwd()) {
|
|
@@ -39529,7 +39549,7 @@ Deploying SMA on ${getChainById(targetChainId).name} (chain ${targetChainId})\u2
|
|
|
39529
39549
|
const channel = await createSigningChannel(process.cwd());
|
|
39530
39550
|
try {
|
|
39531
39551
|
await channel.start();
|
|
39532
|
-
const stationUrl = signingPageUrl(
|
|
39552
|
+
const stationUrl = signingPageUrl(projectPort(process.cwd()));
|
|
39533
39553
|
if (json) {
|
|
39534
39554
|
console.log(
|
|
39535
39555
|
JSON.stringify(
|
|
@@ -39666,100 +39686,6 @@ function emit(json, human, payload) {
|
|
|
39666
39686
|
|
|
39667
39687
|
// src/lib/project.ts
|
|
39668
39688
|
init_esm2();
|
|
39669
|
-
|
|
39670
|
-
// src/lib/keys.ts
|
|
39671
|
-
init_esm2();
|
|
39672
|
-
var ROLES = ["manager", "permissionSigner"];
|
|
39673
|
-
function normalizeRole(input) {
|
|
39674
|
-
const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
|
|
39675
|
-
if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
|
|
39676
|
-
return "manager";
|
|
39677
|
-
}
|
|
39678
|
-
if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
|
|
39679
|
-
return "permissionSigner";
|
|
39680
|
-
}
|
|
39681
|
-
return null;
|
|
39682
|
-
}
|
|
39683
|
-
function roleLabel(role) {
|
|
39684
|
-
return role === "manager" ? "agent wallet" : "mandate signer";
|
|
39685
|
-
}
|
|
39686
|
-
function safeHex(safe) {
|
|
39687
|
-
return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
|
|
39688
|
-
}
|
|
39689
|
-
function keyPath(role, safe) {
|
|
39690
|
-
if (safe) {
|
|
39691
|
-
return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
|
|
39692
|
-
}
|
|
39693
|
-
return sailPath("keys", `${role}.json`);
|
|
39694
|
-
}
|
|
39695
|
-
function legacyKeyPath(role, safe) {
|
|
39696
|
-
return sailPath("keys", `${role}-${safeHex(safe)}.json`);
|
|
39697
|
-
}
|
|
39698
|
-
function resolveKeyPath(role, safe) {
|
|
39699
|
-
if (safe) {
|
|
39700
|
-
const perSma = keyPath(role, safe);
|
|
39701
|
-
if (fileExists(perSma)) return perSma;
|
|
39702
|
-
const legacy = legacyKeyPath(role, safe);
|
|
39703
|
-
if (fileExists(legacy)) return legacy;
|
|
39704
|
-
}
|
|
39705
|
-
return keyPath(role);
|
|
39706
|
-
}
|
|
39707
|
-
function keyExists(role, safe) {
|
|
39708
|
-
return fileExists(resolveKeyPath(role, safe));
|
|
39709
|
-
}
|
|
39710
|
-
async function loadKeyring(role, safe) {
|
|
39711
|
-
const keystore = readJsonFile(resolveKeyPath(role, safe));
|
|
39712
|
-
if (!keystore) {
|
|
39713
|
-
throw new Error(
|
|
39714
|
-
`No ${roleLabel(role)} found.
|
|
39715
|
-
Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
|
|
39716
|
-
);
|
|
39717
|
-
}
|
|
39718
|
-
const password = await promptHidden(`Password for ${roleLabel(role)} key`);
|
|
39719
|
-
try {
|
|
39720
|
-
return await LocalKeyring.fromKeystore(keystore, password);
|
|
39721
|
-
} catch {
|
|
39722
|
-
throw new Error("Invalid password.");
|
|
39723
|
-
}
|
|
39724
|
-
}
|
|
39725
|
-
async function loadManagerSigner(safe) {
|
|
39726
|
-
const passphrase = process.env.SAIL_PASSPHRASE;
|
|
39727
|
-
if (passphrase) {
|
|
39728
|
-
const keystore = readJsonFile(resolveKeyPath("manager", safe));
|
|
39729
|
-
if (!keystore) {
|
|
39730
|
-
throw new Error(
|
|
39731
|
-
'No agent wallet found.\nRun "sailor keys generate" and choose "agent wallet".'
|
|
39732
|
-
);
|
|
39733
|
-
}
|
|
39734
|
-
try {
|
|
39735
|
-
return await LocalKeyring.fromKeystore(keystore, passphrase);
|
|
39736
|
-
} catch {
|
|
39737
|
-
throw new Error(
|
|
39738
|
-
"SAIL_PASSPHRASE does not match this keystore.\nCheck the value in .sail/.env.local (or the SAIL_PASSPHRASE CI secret) \u2014 it must be the passphrase the agent wallet was encrypted with."
|
|
39739
|
-
);
|
|
39740
|
-
}
|
|
39741
|
-
}
|
|
39742
|
-
if (process.stdin.isTTY !== true && keyExists("manager", safe)) {
|
|
39743
|
-
throw new Error(
|
|
39744
|
-
'Agent keystore found but SAIL_PASSPHRASE is not set.\nIf you created the key in the dashboard, add SAIL_PASSPHRASE to .sail/.env.local, or run "sailor keys generate".\nFor CI, set the SAIL_PASSPHRASE GitHub Actions secret.'
|
|
39745
|
-
);
|
|
39746
|
-
}
|
|
39747
|
-
return loadKeyring("manager", safe);
|
|
39748
|
-
}
|
|
39749
|
-
function managerKeystorePath(managerAddr) {
|
|
39750
|
-
if (!isAddress(managerAddr, { strict: false })) {
|
|
39751
|
-
throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
|
|
39752
|
-
}
|
|
39753
|
-
const hex = managerAddr.toLowerCase().replace(/^0x/, "");
|
|
39754
|
-
return sailPath("keys", "managers", `${hex}.json`);
|
|
39755
|
-
}
|
|
39756
|
-
async function loadAnySigner() {
|
|
39757
|
-
if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
|
|
39758
|
-
if (keyExists("manager")) return loadKeyring("manager");
|
|
39759
|
-
throw new Error('No signing key found.\nRun "sailor keys generate" first.');
|
|
39760
|
-
}
|
|
39761
|
-
|
|
39762
|
-
// src/lib/project.ts
|
|
39763
39689
|
function nonEmpty(value) {
|
|
39764
39690
|
return typeof value === "string" && value.trim().length > 0;
|
|
39765
39691
|
}
|
|
@@ -39818,24 +39744,6 @@ var ProjectContext = class {
|
|
|
39818
39744
|
});
|
|
39819
39745
|
}
|
|
39820
39746
|
};
|
|
39821
|
-
async function loadManagerSigner2() {
|
|
39822
|
-
if (!process.env.SAIL_PASSPHRASE) {
|
|
39823
|
-
try {
|
|
39824
|
-
const env = parseEnvFile(sailPath(".env.local"));
|
|
39825
|
-
if (env.SAIL_PASSPHRASE) process.env.SAIL_PASSPHRASE = env.SAIL_PASSPHRASE;
|
|
39826
|
-
} catch {
|
|
39827
|
-
}
|
|
39828
|
-
}
|
|
39829
|
-
const passphrase = process.env.SAIL_PASSPHRASE;
|
|
39830
|
-
if (passphrase) {
|
|
39831
|
-
const keystore = readJsonFile(keyPath("manager"));
|
|
39832
|
-
if (!keystore) {
|
|
39833
|
-
throw new Error('No manager key found.\nRun "sailor keys generate" and choose "manager".');
|
|
39834
|
-
}
|
|
39835
|
-
return LocalKeyring.fromKeystore(keystore, passphrase);
|
|
39836
|
-
}
|
|
39837
|
-
return loadKeyring("manager");
|
|
39838
|
-
}
|
|
39839
39747
|
|
|
39840
39748
|
// src/commands/capabilities.ts
|
|
39841
39749
|
function chainName(chainId) {
|
|
@@ -40043,6 +39951,101 @@ function checkSelectorRoutes(calldata, bytecode) {
|
|
|
40043
39951
|
return { selector, routes: body.includes(selector) };
|
|
40044
39952
|
}
|
|
40045
39953
|
|
|
39954
|
+
// src/lib/keys.ts
|
|
39955
|
+
init_esm2();
|
|
39956
|
+
var ROLES = ["manager", "permissionSigner"];
|
|
39957
|
+
function normalizeRole(input) {
|
|
39958
|
+
const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
|
|
39959
|
+
if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
|
|
39960
|
+
return "manager";
|
|
39961
|
+
}
|
|
39962
|
+
if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
|
|
39963
|
+
return "permissionSigner";
|
|
39964
|
+
}
|
|
39965
|
+
return null;
|
|
39966
|
+
}
|
|
39967
|
+
function roleLabel(role) {
|
|
39968
|
+
return role === "manager" ? "agent wallet" : "mandate signer";
|
|
39969
|
+
}
|
|
39970
|
+
function safeHex(safe) {
|
|
39971
|
+
return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
|
|
39972
|
+
}
|
|
39973
|
+
function keyPath(role, safe) {
|
|
39974
|
+
if (safe) {
|
|
39975
|
+
return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
|
|
39976
|
+
}
|
|
39977
|
+
return sailPath("keys", `${role}.json`);
|
|
39978
|
+
}
|
|
39979
|
+
function legacyKeyPath(role, safe) {
|
|
39980
|
+
return sailPath("keys", `${role}-${safeHex(safe)}.json`);
|
|
39981
|
+
}
|
|
39982
|
+
function resolveKeyPath(role, safe) {
|
|
39983
|
+
if (safe) {
|
|
39984
|
+
const perSma = keyPath(role, safe);
|
|
39985
|
+
if (fileExists(perSma)) return perSma;
|
|
39986
|
+
const legacy = legacyKeyPath(role, safe);
|
|
39987
|
+
if (fileExists(legacy)) return legacy;
|
|
39988
|
+
}
|
|
39989
|
+
return keyPath(role);
|
|
39990
|
+
}
|
|
39991
|
+
function keyExists(role, safe) {
|
|
39992
|
+
return fileExists(resolveKeyPath(role, safe));
|
|
39993
|
+
}
|
|
39994
|
+
async function loadKeyring(role, safe) {
|
|
39995
|
+
const keystore = readJsonFile(resolveKeyPath(role, safe));
|
|
39996
|
+
if (!keystore) {
|
|
39997
|
+
throw new Error(
|
|
39998
|
+
`No ${roleLabel(role)} found.
|
|
39999
|
+
Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
|
|
40000
|
+
);
|
|
40001
|
+
}
|
|
40002
|
+
const passphrase = process.env.SAIL_PASSPHRASE;
|
|
40003
|
+
if (passphrase) {
|
|
40004
|
+
try {
|
|
40005
|
+
return await LocalKeyring.fromKeystore(keystore, passphrase);
|
|
40006
|
+
} catch {
|
|
40007
|
+
throw new Error(
|
|
40008
|
+
`SAIL_PASSPHRASE does not match the ${roleLabel(role)} keystore.
|
|
40009
|
+
Check the value in .sail/.env.local (or the SAIL_PASSPHRASE CI secret) \u2014 it must be the passphrase this key was encrypted with.`
|
|
40010
|
+
);
|
|
40011
|
+
}
|
|
40012
|
+
}
|
|
40013
|
+
if (process.stdin.isTTY !== true) {
|
|
40014
|
+
throw new Error(
|
|
40015
|
+
`${roleLabel(role)} keystore found but SAIL_PASSPHRASE is not set.
|
|
40016
|
+
Set SAIL_PASSPHRASE in .sail/.env.local (or as a CI secret) to run non-interactively.`
|
|
40017
|
+
);
|
|
40018
|
+
}
|
|
40019
|
+
const password = await promptHidden(`Password for ${roleLabel(role)} key`);
|
|
40020
|
+
try {
|
|
40021
|
+
return await LocalKeyring.fromKeystore(keystore, password);
|
|
40022
|
+
} catch {
|
|
40023
|
+
throw new Error("Invalid password.");
|
|
40024
|
+
}
|
|
40025
|
+
}
|
|
40026
|
+
async function loadManagerSigner(safe) {
|
|
40027
|
+
if (!process.env.SAIL_PASSPHRASE) {
|
|
40028
|
+
try {
|
|
40029
|
+
const env = parseEnvFile(sailPath(".env.local"));
|
|
40030
|
+
if (env.SAIL_PASSPHRASE) process.env.SAIL_PASSPHRASE = env.SAIL_PASSPHRASE;
|
|
40031
|
+
} catch {
|
|
40032
|
+
}
|
|
40033
|
+
}
|
|
40034
|
+
return loadKeyring("manager", safe);
|
|
40035
|
+
}
|
|
40036
|
+
function managerKeystorePath(managerAddr) {
|
|
40037
|
+
if (!isAddress(managerAddr, { strict: false })) {
|
|
40038
|
+
throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
|
|
40039
|
+
}
|
|
40040
|
+
const hex = managerAddr.toLowerCase().replace(/^0x/, "");
|
|
40041
|
+
return sailPath("keys", "managers", `${hex}.json`);
|
|
40042
|
+
}
|
|
40043
|
+
async function loadAnySigner() {
|
|
40044
|
+
if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
|
|
40045
|
+
if (keyExists("manager")) return loadKeyring("manager");
|
|
40046
|
+
throw new Error('No signing key found.\nRun "sailor keys generate" first.');
|
|
40047
|
+
}
|
|
40048
|
+
|
|
40046
40049
|
// src/lib/permission-resolver.ts
|
|
40047
40050
|
var IPERMISSION_ABI = [
|
|
40048
40051
|
{
|
|
@@ -40438,11 +40441,11 @@ Probe is heuristic: an unknown selector (${PROBE_SELECTOR}) to a neutral target
|
|
|
40438
40441
|
}
|
|
40439
40442
|
|
|
40440
40443
|
// src/commands/init.ts
|
|
40441
|
-
var
|
|
40444
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
40442
40445
|
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
40443
40446
|
|
|
40444
40447
|
// src/lib/foundry.ts
|
|
40445
|
-
var
|
|
40448
|
+
var import_node_fs8 = require("node:fs");
|
|
40446
40449
|
var import_node_path6 = require("node:path");
|
|
40447
40450
|
var FOUNDRY_TOML = `[profile.default]
|
|
40448
40451
|
src = "mandates"
|
|
@@ -40591,7 +40594,7 @@ Compiled artifacts are written to \`out/\` and the deployed address is tracked i
|
|
|
40591
40594
|
function scaffoldFoundryWorkspace(root) {
|
|
40592
40595
|
const dirs = [(0, import_node_path6.join)(root, "mandates"), (0, import_node_path6.join)(root, ".sail", "contracts", "interfaces")];
|
|
40593
40596
|
for (const d of dirs) {
|
|
40594
|
-
if (!(0,
|
|
40597
|
+
if (!(0, import_node_fs8.existsSync)(d)) (0, import_node_fs8.mkdirSync)(d, { recursive: true });
|
|
40595
40598
|
}
|
|
40596
40599
|
writeIfMissing((0, import_node_path6.join)(root, "foundry.toml"), FOUNDRY_TOML);
|
|
40597
40600
|
writeIfMissing(
|
|
@@ -40606,11 +40609,11 @@ function scaffoldFoundryWorkspace(root) {
|
|
|
40606
40609
|
writeIfMissing((0, import_node_path6.join)(root, "mandates", "README.md"), MANDATES_README);
|
|
40607
40610
|
}
|
|
40608
40611
|
function writeIfMissing(path11, content) {
|
|
40609
|
-
if (!(0,
|
|
40612
|
+
if (!(0, import_node_fs8.existsSync)(path11)) (0, import_node_fs8.writeFileSync)(path11, content, "utf8");
|
|
40610
40613
|
}
|
|
40611
40614
|
|
|
40612
40615
|
// src/lib/template.ts
|
|
40613
|
-
var
|
|
40616
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
40614
40617
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
40615
40618
|
var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
40616
40619
|
"node_modules",
|
|
@@ -40621,8 +40624,8 @@ var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
|
|
|
40621
40624
|
".git"
|
|
40622
40625
|
]);
|
|
40623
40626
|
function copyDirSync(src, dest) {
|
|
40624
|
-
|
|
40625
|
-
for (const entry of
|
|
40627
|
+
import_node_fs9.default.mkdirSync(dest, { recursive: true });
|
|
40628
|
+
for (const entry of import_node_fs9.default.readdirSync(src, { withFileTypes: true })) {
|
|
40626
40629
|
if (TEMPLATE_COPY_EXCLUDES.has(entry.name)) continue;
|
|
40627
40630
|
const srcPath = import_node_path7.default.join(src, entry.name);
|
|
40628
40631
|
const destName = entry.name.startsWith("_") ? `.${entry.name.slice(1)}` : entry.name;
|
|
@@ -40630,24 +40633,24 @@ function copyDirSync(src, dest) {
|
|
|
40630
40633
|
if (entry.isDirectory()) {
|
|
40631
40634
|
copyDirSync(srcPath, destPath);
|
|
40632
40635
|
} else {
|
|
40633
|
-
|
|
40636
|
+
import_node_fs9.default.copyFileSync(srcPath, destPath);
|
|
40634
40637
|
}
|
|
40635
40638
|
}
|
|
40636
40639
|
}
|
|
40637
40640
|
function writeIfMissing2(file, content) {
|
|
40638
|
-
if (!
|
|
40641
|
+
if (!import_node_fs9.default.existsSync(file)) import_node_fs9.default.writeFileSync(file, content, "utf-8");
|
|
40639
40642
|
}
|
|
40640
40643
|
function copyDirSyncIfMissing(src, dest, added = [], base2 = dest) {
|
|
40641
|
-
|
|
40642
|
-
for (const entry of
|
|
40644
|
+
import_node_fs9.default.mkdirSync(dest, { recursive: true });
|
|
40645
|
+
for (const entry of import_node_fs9.default.readdirSync(src, { withFileTypes: true })) {
|
|
40643
40646
|
if (TEMPLATE_COPY_EXCLUDES.has(entry.name)) continue;
|
|
40644
40647
|
const srcPath = import_node_path7.default.join(src, entry.name);
|
|
40645
40648
|
const destName = entry.name.startsWith("_") ? `.${entry.name.slice(1)}` : entry.name;
|
|
40646
40649
|
const destPath = import_node_path7.default.join(dest, destName);
|
|
40647
40650
|
if (entry.isDirectory()) {
|
|
40648
40651
|
copyDirSyncIfMissing(srcPath, destPath, added, base2);
|
|
40649
|
-
} else if (!
|
|
40650
|
-
|
|
40652
|
+
} else if (!import_node_fs9.default.existsSync(destPath)) {
|
|
40653
|
+
import_node_fs9.default.copyFileSync(srcPath, destPath);
|
|
40651
40654
|
added.push(import_node_path7.default.relative(base2, destPath));
|
|
40652
40655
|
}
|
|
40653
40656
|
}
|
|
@@ -40673,7 +40676,7 @@ var DEV_PKG = "@dev.sail.money/sailor";
|
|
|
40673
40676
|
function cliPackageInfo() {
|
|
40674
40677
|
try {
|
|
40675
40678
|
const pkg = JSON.parse(
|
|
40676
|
-
|
|
40679
|
+
import_node_fs10.default.readFileSync(import_node_path8.default.join(packageRoot(), "package.json"), "utf-8")
|
|
40677
40680
|
);
|
|
40678
40681
|
return {
|
|
40679
40682
|
name: pkg.name ?? CANONICAL_PKG,
|
|
@@ -40690,10 +40693,13 @@ function scaffoldProjectWorkspace(dest, name, options) {
|
|
|
40690
40693
|
return n;
|
|
40691
40694
|
})() : null;
|
|
40692
40695
|
const sailDir2 = import_node_path8.default.join(dest, ".sail");
|
|
40693
|
-
|
|
40694
|
-
|
|
40695
|
-
|
|
40696
|
-
|
|
40696
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "keys"), { recursive: true });
|
|
40697
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "runtime"), { recursive: true });
|
|
40698
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(sailDir2, "state"), { recursive: true });
|
|
40699
|
+
const installMode = process.env.SAILOR_INSTALL_MODE === "docker" ? "docker" : "local";
|
|
40700
|
+
const _rawContainerName = process.env.SAILOR_CONTAINER_NAME ?? "agent";
|
|
40701
|
+
const containerName = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(_rawContainerName) ? _rawContainerName : "agent";
|
|
40702
|
+
import_node_fs10.default.writeFileSync(
|
|
40697
40703
|
import_node_path8.default.join(sailDir2, "config.json"),
|
|
40698
40704
|
`${JSON.stringify(
|
|
40699
40705
|
{
|
|
@@ -40703,6 +40709,8 @@ function scaffoldProjectWorkspace(dest, name, options) {
|
|
|
40703
40709
|
// null = chain not yet chosen; Stage 1 will set this
|
|
40704
40710
|
stateDir: ".sail/state",
|
|
40705
40711
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40712
|
+
installMode,
|
|
40713
|
+
...installMode === "docker" ? { containerName } : {},
|
|
40706
40714
|
contracts: {
|
|
40707
40715
|
kernel: "",
|
|
40708
40716
|
mandateFactory: ""
|
|
@@ -40718,7 +40726,7 @@ function scaffoldProjectWorkspace(dest, name, options) {
|
|
|
40718
40726
|
const chainEntries = Object.values(chains);
|
|
40719
40727
|
const perChainVarLines = chainEntries.map((c) => `# ${c.rpcEnvVar}=https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`).join("\n");
|
|
40720
40728
|
const chainIdExample = chainId != null ? `CHAIN_ID=${chainId}` : `# CHAIN_ID=8453 # set after choosing your chain in Stage 1`;
|
|
40721
|
-
|
|
40729
|
+
import_node_fs10.default.writeFileSync(
|
|
40722
40730
|
import_node_path8.default.join(dest, ".env.example"),
|
|
40723
40731
|
`# Sailor agent environment
|
|
40724
40732
|
#
|
|
@@ -40744,7 +40752,7 @@ ${perChainVarLines}
|
|
|
40744
40752
|
const val = isActive && options.rpcUrl ? options.rpcUrl : `https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`;
|
|
40745
40753
|
return isActive && options.rpcUrl ? `${c.rpcEnvVar}=${val}` : `# ${c.rpcEnvVar}=${val}`;
|
|
40746
40754
|
}).join("\n");
|
|
40747
|
-
|
|
40755
|
+
import_node_fs10.default.writeFileSync(
|
|
40748
40756
|
import_node_path8.default.join(sailDir2, ".env.local"),
|
|
40749
40757
|
`# Real values \u2014 never commit this file.
|
|
40750
40758
|
#
|
|
@@ -40771,8 +40779,8 @@ async function initCommand(dir, options = {}) {
|
|
|
40771
40779
|
throw new Error(`Invalid template name: "${templateName}"`);
|
|
40772
40780
|
}
|
|
40773
40781
|
const templateSrc = import_node_path8.default.join(templatesDir, templateName);
|
|
40774
|
-
const availableTemplates = () =>
|
|
40775
|
-
if (!
|
|
40782
|
+
const availableTemplates = () => import_node_fs10.default.existsSync(templatesDir) ? import_node_fs10.default.readdirSync(templatesDir).filter((e) => import_node_fs10.default.existsSync(import_node_path8.default.join(templatesDir, e, "package.json"))).join(", ") || "none" : "none";
|
|
40783
|
+
if (!import_node_fs10.default.existsSync(templateSrc) || !import_node_fs10.default.existsSync(import_node_path8.default.join(templateSrc, "package.json"))) {
|
|
40776
40784
|
const available = availableTemplates();
|
|
40777
40785
|
const hint = available === "none" ? `
|
|
40778
40786
|
No templates found under ${templatesDir}.
|
|
@@ -40785,54 +40793,74 @@ run from the repo root.` : ` Available: ${available}`;
|
|
|
40785
40793
|
if (!inPlace && !dest.startsWith(cwd + import_node_path8.default.sep) && dest !== cwd) {
|
|
40786
40794
|
throw new Error(`Directory must be inside the current working directory`);
|
|
40787
40795
|
}
|
|
40788
|
-
if (!inPlace &&
|
|
40796
|
+
if (!inPlace && import_node_fs10.default.existsSync(dest) && !options.force) {
|
|
40789
40797
|
throw new Error(`Directory already exists: ${dest}
|
|
40790
40798
|
Pass --force to scaffold into it anyway (existing files with the same name are overwritten).`);
|
|
40791
40799
|
}
|
|
40792
|
-
if (inPlace &&
|
|
40800
|
+
if (inPlace && import_node_fs10.default.existsSync(import_node_path8.default.join(dest, ".sail", "config.json")) && !options.force) {
|
|
40793
40801
|
throw new Error(
|
|
40794
40802
|
"This project is already initialized.\nRun `sailor update` to re-sync template files, or `sailor init --force` to re-initialize (overwrites scaffold files; your .sail/keys/ and .sail/state/ are left in place)."
|
|
40795
40803
|
);
|
|
40796
40804
|
}
|
|
40805
|
+
const existingConfigPath = import_node_path8.default.join(dest, ".sail", "config.json");
|
|
40806
|
+
const previousConfig = import_node_fs10.default.existsSync(existingConfigPath) ? (() => {
|
|
40807
|
+
try {
|
|
40808
|
+
return JSON.parse(import_node_fs10.default.readFileSync(existingConfigPath, "utf-8"));
|
|
40809
|
+
} catch {
|
|
40810
|
+
return null;
|
|
40811
|
+
}
|
|
40812
|
+
})() : null;
|
|
40797
40813
|
copyDirSync(templateSrc, dest);
|
|
40798
40814
|
const pkgRoot = packageRoot();
|
|
40799
40815
|
const examplesPermSrc = import_node_path8.default.join(pkgRoot, "examples", "permissions");
|
|
40800
|
-
if (
|
|
40816
|
+
if (import_node_fs10.default.existsSync(examplesPermSrc)) {
|
|
40801
40817
|
copyDirSync(examplesPermSrc, import_node_path8.default.join(dest, "examples", "permissions"));
|
|
40802
40818
|
}
|
|
40803
40819
|
const customMandateSrc = import_node_path8.default.join(pkgRoot, "examples", "custom-mandate");
|
|
40804
|
-
if (
|
|
40820
|
+
if (import_node_fs10.default.existsSync(customMandateSrc)) {
|
|
40805
40821
|
copyDirSync(customMandateSrc, import_node_path8.default.join(dest, "examples", "custom-mandate"));
|
|
40806
40822
|
}
|
|
40807
40823
|
const permModelSrc = import_node_path8.default.join(pkgRoot, "docs", "PERMISSION_MODEL.md");
|
|
40808
|
-
if (
|
|
40809
|
-
|
|
40810
|
-
writeIfMissing2(import_node_path8.default.join(dest, "docs", "PERMISSION_MODEL.md"),
|
|
40824
|
+
if (import_node_fs10.default.existsSync(permModelSrc)) {
|
|
40825
|
+
import_node_fs10.default.mkdirSync(import_node_path8.default.join(dest, "docs"), { recursive: true });
|
|
40826
|
+
writeIfMissing2(import_node_path8.default.join(dest, "docs", "PERMISSION_MODEL.md"), import_node_fs10.default.readFileSync(permModelSrc, "utf-8"));
|
|
40811
40827
|
}
|
|
40812
40828
|
const pkgPath = import_node_path8.default.join(dest, "package.json");
|
|
40813
|
-
if (
|
|
40814
|
-
const pkg = JSON.parse(
|
|
40829
|
+
if (import_node_fs10.default.existsSync(pkgPath)) {
|
|
40830
|
+
const pkg = JSON.parse(import_node_fs10.default.readFileSync(pkgPath, "utf-8"));
|
|
40815
40831
|
pkg.name = name;
|
|
40816
40832
|
const devDeps = pkg.devDependencies ?? {};
|
|
40817
40833
|
const { name: cliName, version: cliVer } = cliPackageInfo();
|
|
40818
40834
|
devDeps[CANONICAL_PKG] = cliName === DEV_PKG ? `npm:${DEV_PKG}@^${cliVer}` : `^${cliVer}`;
|
|
40819
40835
|
pkg.devDependencies = devDeps;
|
|
40820
|
-
|
|
40836
|
+
import_node_fs10.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
40821
40837
|
`);
|
|
40822
40838
|
}
|
|
40823
40839
|
const tsconfigPath = import_node_path8.default.join(dest, "tsconfig.json");
|
|
40824
|
-
if (
|
|
40825
|
-
const tsconfig = JSON.parse(
|
|
40840
|
+
if (import_node_fs10.default.existsSync(tsconfigPath)) {
|
|
40841
|
+
const tsconfig = JSON.parse(import_node_fs10.default.readFileSync(tsconfigPath, "utf-8"));
|
|
40826
40842
|
const co = tsconfig.compilerOptions;
|
|
40827
40843
|
if (co && "paths" in co) {
|
|
40828
40844
|
delete co.paths;
|
|
40829
40845
|
delete co.baseUrl;
|
|
40830
|
-
|
|
40846
|
+
import_node_fs10.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
40831
40847
|
`);
|
|
40832
40848
|
}
|
|
40833
40849
|
}
|
|
40834
40850
|
scaffoldProjectWorkspace(dest, name, options);
|
|
40835
40851
|
scaffoldFoundryWorkspace(dest);
|
|
40852
|
+
const newMode = process.env.SAILOR_INSTALL_MODE === "docker" ? "docker" : "local";
|
|
40853
|
+
if (previousConfig?.installMode === "docker" && newMode === "local") {
|
|
40854
|
+
const prev = previousConfig.containerName ?? "agent";
|
|
40855
|
+
console.log(`
|
|
40856
|
+
Switched to local install. If the Docker container is still running:`);
|
|
40857
|
+
console.log(` docker stop ${prev}`);
|
|
40858
|
+
console.log(`You can restart it anytime with the standard docker run command.`);
|
|
40859
|
+
} else if (previousConfig?.installMode === "local" && newMode === "docker") {
|
|
40860
|
+
const containerName = process.env.SAILOR_CONTAINER_NAME ?? "agent";
|
|
40861
|
+
console.log(`
|
|
40862
|
+
Switched to Docker install (container: ${containerName}).`);
|
|
40863
|
+
}
|
|
40836
40864
|
printWelcome(
|
|
40837
40865
|
dest,
|
|
40838
40866
|
name,
|
|
@@ -40853,20 +40881,20 @@ function chainLabel(chainId) {
|
|
|
40853
40881
|
}
|
|
40854
40882
|
function detectState(dest) {
|
|
40855
40883
|
try {
|
|
40856
|
-
const configRaw =
|
|
40884
|
+
const configRaw = import_node_fs10.default.readFileSync(import_node_path8.default.join(dest, ".sail", "config.json"), "utf-8");
|
|
40857
40885
|
const config = JSON.parse(configRaw);
|
|
40858
40886
|
const projectName = config.name ?? import_node_path8.default.basename(dest);
|
|
40859
40887
|
const accountPath = import_node_path8.default.join(dest, ".sail", "account.json");
|
|
40860
|
-
if (!
|
|
40888
|
+
if (!import_node_fs10.default.existsSync(accountPath)) {
|
|
40861
40889
|
return { kind: "B", projectName, chain: chainLabel(config.chainId ?? 0) };
|
|
40862
40890
|
}
|
|
40863
|
-
const accountRaw =
|
|
40891
|
+
const accountRaw = import_node_fs10.default.readFileSync(accountPath, "utf-8");
|
|
40864
40892
|
const account2 = JSON.parse(accountRaw);
|
|
40865
40893
|
const sma = account2.safe ?? "";
|
|
40866
40894
|
const chain2 = chainLabel(account2.chainId ?? config.chainId ?? 0);
|
|
40867
40895
|
let permissionCount = 0;
|
|
40868
40896
|
try {
|
|
40869
|
-
const mandatesRaw =
|
|
40897
|
+
const mandatesRaw = import_node_fs10.default.readFileSync(
|
|
40870
40898
|
import_node_path8.default.join(dest, ".sail", "state", "mandates.json"),
|
|
40871
40899
|
"utf-8"
|
|
40872
40900
|
);
|
|
@@ -40943,13 +40971,19 @@ Created ${name}/`);
|
|
|
40943
40971
|
"\u2551 If you skip this step, setup WILL break and you will have to \u2551",
|
|
40944
40972
|
"\u2551 restart. There are no shortcuts. \u2551",
|
|
40945
40973
|
"\u2551 \u2551",
|
|
40974
|
+
"\u2551 IF SAILOR IS RUNNING IN DOCKER: \u2551",
|
|
40975
|
+
"\u2551 \u2022 Read project files from your local filesystem \u2014 they are \u2551",
|
|
40976
|
+
"\u2551 shared via volume mount, do NOT use docker exec to read them. \u2551",
|
|
40977
|
+
"\u2551 \u2022 Prefix every sailor command with: \u2551",
|
|
40978
|
+
"\u2551 docker exec <containerName> sailor <command> \u2551",
|
|
40979
|
+
"\u2551 \u2551",
|
|
40946
40980
|
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D",
|
|
40947
40981
|
""
|
|
40948
40982
|
].join("\n"));
|
|
40949
40983
|
}
|
|
40950
40984
|
|
|
40951
40985
|
// src/commands/update.ts
|
|
40952
|
-
var
|
|
40986
|
+
var import_node_fs11 = __toESM(require("node:fs"), 1);
|
|
40953
40987
|
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
40954
40988
|
var UPDATE_PATHS = [
|
|
40955
40989
|
".agents",
|
|
@@ -40965,18 +40999,18 @@ var STALE_PATHS = [
|
|
|
40965
40999
|
];
|
|
40966
41000
|
async function updateCommand() {
|
|
40967
41001
|
const dest = process.cwd();
|
|
40968
|
-
if (!
|
|
41002
|
+
if (!import_node_fs11.default.existsSync(import_node_path9.default.join(dest, ".sail", "config.json"))) {
|
|
40969
41003
|
throw new Error("Not a sailor project \u2014 .sail/config.json not found. Run `sailor init` first.");
|
|
40970
41004
|
}
|
|
40971
41005
|
const templateSrc = import_node_path9.default.join(packageRoot(), "templates", "default");
|
|
40972
|
-
if (!
|
|
41006
|
+
if (!import_node_fs11.default.existsSync(templateSrc)) {
|
|
40973
41007
|
throw new Error(`Template directory not found at ${templateSrc}`);
|
|
40974
41008
|
}
|
|
40975
41009
|
const removed = [];
|
|
40976
41010
|
for (const p of STALE_PATHS) {
|
|
40977
41011
|
const target = import_node_path9.default.join(dest, p);
|
|
40978
|
-
if (
|
|
40979
|
-
|
|
41012
|
+
if (import_node_fs11.default.existsSync(target)) {
|
|
41013
|
+
import_node_fs11.default.rmSync(target, { recursive: true, force: true });
|
|
40980
41014
|
removed.push(p);
|
|
40981
41015
|
}
|
|
40982
41016
|
}
|
|
@@ -40984,18 +41018,53 @@ async function updateCommand() {
|
|
|
40984
41018
|
for (const p of UPDATE_PATHS) {
|
|
40985
41019
|
const src = import_node_path9.default.join(templateSrc, p);
|
|
40986
41020
|
const dst = import_node_path9.default.join(dest, p);
|
|
40987
|
-
if (!
|
|
40988
|
-
const stat =
|
|
41021
|
+
if (!import_node_fs11.default.existsSync(src)) continue;
|
|
41022
|
+
const stat = import_node_fs11.default.statSync(src);
|
|
40989
41023
|
if (stat.isDirectory()) {
|
|
40990
41024
|
copyDirSync(src, dst);
|
|
40991
41025
|
} else {
|
|
40992
|
-
|
|
40993
|
-
|
|
41026
|
+
import_node_fs11.default.mkdirSync(import_node_path9.default.dirname(dst), { recursive: true });
|
|
41027
|
+
import_node_fs11.default.copyFileSync(src, dst);
|
|
40994
41028
|
}
|
|
40995
41029
|
updated.push(p);
|
|
40996
41030
|
}
|
|
40997
41031
|
const added = [];
|
|
40998
41032
|
copyDirSyncIfMissing(templateSrc, dest, added);
|
|
41033
|
+
const configPath = import_node_path9.default.join(dest, ".sail", "config.json");
|
|
41034
|
+
try {
|
|
41035
|
+
const config = JSON.parse(import_node_fs11.default.readFileSync(configPath, "utf-8"));
|
|
41036
|
+
const newMode = process.env.SAILOR_INSTALL_MODE === "docker" ? "docker" : "local";
|
|
41037
|
+
const _rawContainerName = process.env.SAILOR_CONTAINER_NAME ?? "agent";
|
|
41038
|
+
const containerName = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(_rawContainerName) ? _rawContainerName : "agent";
|
|
41039
|
+
if (config.installMode !== newMode) {
|
|
41040
|
+
const previousMode = config.installMode;
|
|
41041
|
+
const previousContainer = config.containerName;
|
|
41042
|
+
config.installMode = newMode;
|
|
41043
|
+
if (newMode === "docker") {
|
|
41044
|
+
config.containerName = containerName;
|
|
41045
|
+
} else {
|
|
41046
|
+
delete config.containerName;
|
|
41047
|
+
}
|
|
41048
|
+
import_node_fs11.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
41049
|
+
`, "utf-8");
|
|
41050
|
+
if (previousMode === "docker" && newMode === "local") {
|
|
41051
|
+
const prev = previousContainer ?? "agent";
|
|
41052
|
+
console.log(`
|
|
41053
|
+
Switched to local install. If the Docker container is still running:`);
|
|
41054
|
+
console.log(` docker stop ${prev}`);
|
|
41055
|
+
console.log(`You can restart it anytime with the standard docker run command.`);
|
|
41056
|
+
} else if (newMode === "docker") {
|
|
41057
|
+
console.log(`
|
|
41058
|
+
Switched to Docker install (container: ${containerName}).`);
|
|
41059
|
+
}
|
|
41060
|
+
} else if (newMode === "docker" && config.containerName !== containerName) {
|
|
41061
|
+
config.containerName = containerName;
|
|
41062
|
+
import_node_fs11.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
41063
|
+
`, "utf-8");
|
|
41064
|
+
}
|
|
41065
|
+
} catch {
|
|
41066
|
+
console.warn("Warning: could not update install mode in .sail/config.json");
|
|
41067
|
+
}
|
|
40999
41068
|
if (removed.length === 0 && updated.length === 0 && added.length === 0) {
|
|
41000
41069
|
console.log("Nothing to update.");
|
|
41001
41070
|
return;
|
|
@@ -41019,7 +41088,7 @@ Added (new in template):`);
|
|
|
41019
41088
|
}
|
|
41020
41089
|
|
|
41021
41090
|
// src/commands/keys.ts
|
|
41022
|
-
var
|
|
41091
|
+
var import_node_fs12 = __toESM(require("node:fs"), 1);
|
|
41023
41092
|
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
41024
41093
|
function readStdin() {
|
|
41025
41094
|
return new Promise((resolve3, reject) => {
|
|
@@ -41126,14 +41195,14 @@ async function keysExportCi() {
|
|
|
41126
41195
|
);
|
|
41127
41196
|
}
|
|
41128
41197
|
const dest = import_node_path10.default.resolve(process.cwd(), "ci-keystore.json");
|
|
41129
|
-
|
|
41198
|
+
import_node_fs12.default.copyFileSync(src, dest);
|
|
41130
41199
|
console.log(`\u2713 Keystore copied to ci-keystore.json`);
|
|
41131
41200
|
console.log(` Source: ${src}`);
|
|
41132
41201
|
const gitignorePath = import_node_path10.default.resolve(process.cwd(), ".gitignore");
|
|
41133
|
-
if (
|
|
41134
|
-
const content =
|
|
41202
|
+
if (import_node_fs12.default.existsSync(gitignorePath)) {
|
|
41203
|
+
const content = import_node_fs12.default.readFileSync(gitignorePath, "utf-8");
|
|
41135
41204
|
if (!content.includes("ci-keystore.json")) {
|
|
41136
|
-
|
|
41205
|
+
import_node_fs12.default.appendFileSync(
|
|
41137
41206
|
gitignorePath,
|
|
41138
41207
|
"\n# CI keystore \u2014 encrypted agent wallet, safe to commit\n!ci-keystore.json\n"
|
|
41139
41208
|
);
|
|
@@ -41171,7 +41240,7 @@ async function keysShow() {
|
|
|
41171
41240
|
|
|
41172
41241
|
// src/commands/mandate-contracts.ts
|
|
41173
41242
|
var import_node_child_process = require("node:child_process");
|
|
41174
|
-
var
|
|
41243
|
+
var import_node_fs13 = require("node:fs");
|
|
41175
41244
|
var import_node_path11 = require("node:path");
|
|
41176
41245
|
init_esm2();
|
|
41177
41246
|
|
|
@@ -41303,7 +41372,7 @@ Project: ${project.name}`));
|
|
|
41303
41372
|
if (!keyExists("manager")) {
|
|
41304
41373
|
throw new Error('No agent wallet found. Run "sailor keys generate" and choose "agent wallet" first.');
|
|
41305
41374
|
}
|
|
41306
|
-
const agentSigner = await
|
|
41375
|
+
const agentSigner = await loadManagerSigner();
|
|
41307
41376
|
const agentAddress = agentSigner.address;
|
|
41308
41377
|
say(() => console.log("\u2713", `Agent wallet: ${agentAddress}`));
|
|
41309
41378
|
const chain2 = getChainById(project.chainId);
|
|
@@ -41316,7 +41385,7 @@ Project: ${project.name}`));
|
|
|
41316
41385
|
() => console.log(
|
|
41317
41386
|
`
|
|
41318
41387
|
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
41319
|
-
${signingPageUrl(
|
|
41388
|
+
${signingPageUrl(projectPort(process.cwd()))}
|
|
41320
41389
|
`
|
|
41321
41390
|
)
|
|
41322
41391
|
);
|
|
@@ -41817,7 +41886,7 @@ Mandate command failed: ${msg}`), {
|
|
|
41817
41886
|
process.exit(1);
|
|
41818
41887
|
}
|
|
41819
41888
|
function announceSigningUrl(json) {
|
|
41820
|
-
const url = signingPageUrl(
|
|
41889
|
+
const url = signingPageUrl(projectPort(process.cwd()));
|
|
41821
41890
|
if (json) {
|
|
41822
41891
|
process.stdout.write(`${JSON.stringify({ status: "waiting_for_signature", url })}
|
|
41823
41892
|
`);
|
|
@@ -41870,7 +41939,7 @@ async function runDeploy(project, channel, options) {
|
|
|
41870
41939
|
if (options.argsFile) {
|
|
41871
41940
|
const argsFilePath = (0, import_node_path11.resolve)(options.argsFile);
|
|
41872
41941
|
try {
|
|
41873
|
-
argsJson = (0,
|
|
41942
|
+
argsJson = (0, import_node_fs13.readFileSync)(argsFilePath, "utf8").trim();
|
|
41874
41943
|
} catch {
|
|
41875
41944
|
throw new Error(`Cannot read --args-file: ${argsFilePath}`);
|
|
41876
41945
|
}
|
|
@@ -42078,7 +42147,7 @@ async function runDeployClone(project, channel, options) {
|
|
|
42078
42147
|
}
|
|
42079
42148
|
const chain2 = getChainById(project.chainId);
|
|
42080
42149
|
const publicClient = publicClientFor(project);
|
|
42081
|
-
const agentSigner = await
|
|
42150
|
+
const agentSigner = await loadManagerSigner();
|
|
42082
42151
|
const registered = await publicClient.readContract({
|
|
42083
42152
|
address: project.contracts.kernel,
|
|
42084
42153
|
abi: SailKernelAbi,
|
|
@@ -42422,7 +42491,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
|
|
|
42422
42491
|
} catch (err) {
|
|
42423
42492
|
if (err.message.startsWith("Security:")) throw err;
|
|
42424
42493
|
}
|
|
42425
|
-
const agentSigner = await
|
|
42494
|
+
const agentSigner = await loadManagerSigner();
|
|
42426
42495
|
const walletClient = createWalletClient({
|
|
42427
42496
|
account: agentSigner.viemAccount,
|
|
42428
42497
|
chain: getChainById(project.chainId),
|
|
@@ -42464,7 +42533,7 @@ async function attachToSma(project, channel, publicClient, sma, mandate2, label,
|
|
|
42464
42533
|
const say = (fn) => {
|
|
42465
42534
|
if (!json) fn();
|
|
42466
42535
|
};
|
|
42467
|
-
const agentSigner = await
|
|
42536
|
+
const agentSigner = await loadManagerSigner();
|
|
42468
42537
|
const registered = await publicClient.readContract({
|
|
42469
42538
|
address: project.contracts.kernel,
|
|
42470
42539
|
abi: SailKernelAbi,
|
|
@@ -42505,7 +42574,7 @@ async function attachBatchToSma(project, channel, publicClient, sma, permissions
|
|
|
42505
42574
|
const say = (fn) => {
|
|
42506
42575
|
if (!json) fn();
|
|
42507
42576
|
};
|
|
42508
|
-
const agentSigner = await
|
|
42577
|
+
const agentSigner = await loadManagerSigner();
|
|
42509
42578
|
const registered = await publicClient.readContract({
|
|
42510
42579
|
address: project.contracts.kernel,
|
|
42511
42580
|
abi: SailKernelAbi,
|
|
@@ -42721,14 +42790,14 @@ Resolved: ${resolved}`
|
|
|
42721
42790
|
}
|
|
42722
42791
|
artifactPath = resolved;
|
|
42723
42792
|
if (options.build) runForgeBuild();
|
|
42724
|
-
if (!(0,
|
|
42793
|
+
if (!(0, import_node_fs13.existsSync)(artifactPath)) {
|
|
42725
42794
|
ensureForgeHint();
|
|
42726
42795
|
throw new Error(
|
|
42727
42796
|
`Artifact not found: ${artifactPath}
|
|
42728
42797
|
Compile your mandate first (e.g. \`forge build\`), or pass --build.`
|
|
42729
42798
|
);
|
|
42730
42799
|
}
|
|
42731
|
-
const artifact = JSON.parse((0,
|
|
42800
|
+
const artifact = JSON.parse((0, import_node_fs13.readFileSync)(artifactPath, "utf8"));
|
|
42732
42801
|
const abi2 = artifact.abi;
|
|
42733
42802
|
const bytecodeRaw = artifact.bytecode?.object ?? artifact.bytecode;
|
|
42734
42803
|
if (!bytecodeRaw || typeof bytecodeRaw !== "string") {
|
|
@@ -42811,13 +42880,13 @@ function runForgeBuild() {
|
|
|
42811
42880
|
init_esm2();
|
|
42812
42881
|
|
|
42813
42882
|
// src/lib/permission-explainer.ts
|
|
42814
|
-
var
|
|
42883
|
+
var import_node_fs14 = require("node:fs");
|
|
42815
42884
|
var import_node_path12 = require("node:path");
|
|
42816
42885
|
function explainPermission(name, sourcePath) {
|
|
42817
42886
|
const resolved = sourcePath ?? (0, import_node_path12.join)(process.cwd(), "mandates", `${name}.sol`);
|
|
42818
42887
|
let src;
|
|
42819
42888
|
try {
|
|
42820
|
-
src = (0,
|
|
42889
|
+
src = (0, import_node_fs14.readFileSync)(resolved, "utf8");
|
|
42821
42890
|
} catch {
|
|
42822
42891
|
return null;
|
|
42823
42892
|
}
|
|
@@ -43039,7 +43108,7 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
|
|
|
43039
43108
|
}
|
|
43040
43109
|
|
|
43041
43110
|
// src/commands/mandate-simulate.ts
|
|
43042
|
-
var
|
|
43111
|
+
var import_node_fs15 = require("node:fs");
|
|
43043
43112
|
init_esm2();
|
|
43044
43113
|
function parseExpect(raw, where) {
|
|
43045
43114
|
if (raw === void 0) return void 0;
|
|
@@ -43055,7 +43124,7 @@ function resolveSampleCalls(options) {
|
|
|
43055
43124
|
if (options.calls) {
|
|
43056
43125
|
let raw;
|
|
43057
43126
|
try {
|
|
43058
|
-
raw = JSON.parse((0,
|
|
43127
|
+
raw = JSON.parse((0, import_node_fs15.readFileSync)(options.calls, "utf8"));
|
|
43059
43128
|
} catch (err) {
|
|
43060
43129
|
throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
|
|
43061
43130
|
}
|
|
@@ -43271,7 +43340,7 @@ async function mandateSimulate(options) {
|
|
|
43271
43340
|
}
|
|
43272
43341
|
|
|
43273
43342
|
// src/commands/rotate-signer.ts
|
|
43274
|
-
var
|
|
43343
|
+
var import_node_fs16 = require("node:fs");
|
|
43275
43344
|
init_esm2();
|
|
43276
43345
|
var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
|
|
43277
43346
|
async function rotateSigner(options) {
|
|
@@ -43401,7 +43470,7 @@ SMA: ${smaAddress}`);
|
|
|
43401
43470
|
console.log(
|
|
43402
43471
|
`
|
|
43403
43472
|
\u2192 Open the Sailor dashboard to approve signing requests:
|
|
43404
|
-
${signingPageUrl(
|
|
43473
|
+
${signingPageUrl(projectPort(process.cwd()))}
|
|
43405
43474
|
`
|
|
43406
43475
|
);
|
|
43407
43476
|
});
|
|
@@ -43659,7 +43728,7 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
43659
43728
|
const keystore = await keyring.exportKeystore(password);
|
|
43660
43729
|
writeJsonFile(target, keystore, 384);
|
|
43661
43730
|
const perManagerPath = managerKeystorePath(keyring.address);
|
|
43662
|
-
(0,
|
|
43731
|
+
(0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43663
43732
|
writeJsonFile(perManagerPath, keystore, 384);
|
|
43664
43733
|
say(
|
|
43665
43734
|
() => console.log(
|
|
@@ -43671,7 +43740,7 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
43671
43740
|
function promoteManagerKeystore(newManager, say) {
|
|
43672
43741
|
const stored = readJsonFile(managerKeystorePath(newManager));
|
|
43673
43742
|
if (!stored) return;
|
|
43674
|
-
(0,
|
|
43743
|
+
(0, import_node_fs16.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43675
43744
|
const activeTarget = keyPath("manager");
|
|
43676
43745
|
const displaced = readJsonFile(activeTarget);
|
|
43677
43746
|
if (displaced?.address) {
|
|
@@ -43723,7 +43792,7 @@ function writePending(pending) {
|
|
|
43723
43792
|
}
|
|
43724
43793
|
function clearPending() {
|
|
43725
43794
|
try {
|
|
43726
|
-
(0,
|
|
43795
|
+
(0, import_node_fs16.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
|
|
43727
43796
|
} catch {
|
|
43728
43797
|
}
|
|
43729
43798
|
}
|
|
@@ -43777,7 +43846,7 @@ async function ownerConnect(options) {
|
|
|
43777
43846
|
await channel.start();
|
|
43778
43847
|
if (!options.json) {
|
|
43779
43848
|
console.log("\u2192 Open the Sailor dashboard and connect your wallet:");
|
|
43780
|
-
console.log(` ${signingPageUrl(
|
|
43849
|
+
console.log(` ${signingPageUrl(projectPort(projectRoot))}`);
|
|
43781
43850
|
if (channel.remote) console.log(" (using the running signing station)");
|
|
43782
43851
|
console.log("\nWaiting for a wallet connection\u2026");
|
|
43783
43852
|
}
|
|
@@ -43826,41 +43895,6 @@ var import_node_fs17 = __toESM(require("node:fs"), 1);
|
|
|
43826
43895
|
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
43827
43896
|
var import_node_url = require("node:url");
|
|
43828
43897
|
init_esm2();
|
|
43829
|
-
|
|
43830
|
-
// src/lib/process.ts
|
|
43831
|
-
var import_node_fs16 = __toESM(require("node:fs"), 1);
|
|
43832
|
-
function agentPidPath(chainId) {
|
|
43833
|
-
return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
|
|
43834
|
-
}
|
|
43835
|
-
function writeAgentPid(chainId) {
|
|
43836
|
-
import_node_fs16.default.mkdirSync(sailPath(), { recursive: true });
|
|
43837
|
-
import_node_fs16.default.writeFileSync(agentPidPath(chainId), `${process.pid}
|
|
43838
|
-
`);
|
|
43839
|
-
}
|
|
43840
|
-
function clearAgentPid(chainId) {
|
|
43841
|
-
try {
|
|
43842
|
-
import_node_fs16.default.rmSync(agentPidPath(chainId));
|
|
43843
|
-
} catch {
|
|
43844
|
-
}
|
|
43845
|
-
}
|
|
43846
|
-
function readAgentPid(chainId) {
|
|
43847
|
-
try {
|
|
43848
|
-
const pid = Number.parseInt(import_node_fs16.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
|
|
43849
|
-
return Number.isNaN(pid) ? null : pid;
|
|
43850
|
-
} catch {
|
|
43851
|
-
return null;
|
|
43852
|
-
}
|
|
43853
|
-
}
|
|
43854
|
-
function isProcessAlive(pid) {
|
|
43855
|
-
try {
|
|
43856
|
-
process.kill(pid, 0);
|
|
43857
|
-
return true;
|
|
43858
|
-
} catch {
|
|
43859
|
-
return false;
|
|
43860
|
-
}
|
|
43861
|
-
}
|
|
43862
|
-
|
|
43863
|
-
// src/commands/run.ts
|
|
43864
43898
|
var DEFAULT_INTERVAL_SEC = 60;
|
|
43865
43899
|
var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
43866
43900
|
var ERC20_READ_ABI = [
|
|
@@ -44905,7 +44939,7 @@ function requireAccount() {
|
|
|
44905
44939
|
}
|
|
44906
44940
|
return account2;
|
|
44907
44941
|
}
|
|
44908
|
-
async function sessionPause() {
|
|
44942
|
+
async function sessionPause(opts = {}) {
|
|
44909
44943
|
const account2 = requireAccount();
|
|
44910
44944
|
const signer = await loadAnySigner();
|
|
44911
44945
|
const client = makeClient(account2.chainId);
|
|
@@ -44920,9 +44954,10 @@ async function sessionPause() {
|
|
|
44920
44954
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
44921
44955
|
};
|
|
44922
44956
|
writeJsonFile(sailPath("session.json"), session2);
|
|
44923
|
-
console.log(
|
|
44957
|
+
if (opts.json) console.log(JSON.stringify(session2));
|
|
44958
|
+
else console.log("Session paused \u2014 agent dispatch rights revoked.");
|
|
44924
44959
|
}
|
|
44925
|
-
async function sessionResume() {
|
|
44960
|
+
async function sessionResume(opts = {}) {
|
|
44926
44961
|
const account2 = requireAccount();
|
|
44927
44962
|
const signer = await loadAnySigner();
|
|
44928
44963
|
const client = makeClient(account2.chainId);
|
|
@@ -44937,7 +44972,8 @@ async function sessionResume() {
|
|
|
44937
44972
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
44938
44973
|
};
|
|
44939
44974
|
writeJsonFile(sailPath("session.json"), session2);
|
|
44940
|
-
console.log(
|
|
44975
|
+
if (opts.json) console.log(JSON.stringify(session2));
|
|
44976
|
+
else console.log("Session resumed \u2014 agent dispatch rights restored.");
|
|
44941
44977
|
}
|
|
44942
44978
|
|
|
44943
44979
|
// src/commands/station.ts
|
|
@@ -45055,7 +45091,11 @@ async function status() {
|
|
|
45055
45091
|
const hasManager = keyExists("manager");
|
|
45056
45092
|
const hasPermissionSigner = keyExists("permissionSigner");
|
|
45057
45093
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
45058
|
-
const
|
|
45094
|
+
const mandateRaw = readJsonFile(sailPath("mandate.json"));
|
|
45095
|
+
const mandates = (Array.isArray(mandateRaw) ? mandateRaw : mandateRaw ? [mandateRaw] : []).filter(
|
|
45096
|
+
Boolean
|
|
45097
|
+
);
|
|
45098
|
+
const hasMandate = mandates.length > 0;
|
|
45059
45099
|
const session2 = readJsonFile(sailPath("session.json"));
|
|
45060
45100
|
console.log("Sailor status");
|
|
45061
45101
|
console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
@@ -45071,9 +45111,12 @@ async function status() {
|
|
|
45071
45111
|
console.log(' \u2717 not deployed run "sailor onboard --new-sma"');
|
|
45072
45112
|
}
|
|
45073
45113
|
console.log("Mandate:");
|
|
45074
|
-
if (
|
|
45075
|
-
const n =
|
|
45076
|
-
|
|
45114
|
+
if (hasMandate) {
|
|
45115
|
+
const n = mandates.reduce((sum, m) => sum + (m?.permissions?.length ?? 0), 0);
|
|
45116
|
+
const permLabel = `${n} permission${n === 1 ? "" : "s"}`;
|
|
45117
|
+
console.log(
|
|
45118
|
+
mandates.length > 1 ? ` \u2713 signed ${mandates.length} mandates, ${permLabel}` : ` \u2713 signed ${permLabel}`
|
|
45119
|
+
);
|
|
45077
45120
|
} else {
|
|
45078
45121
|
console.log(' \u2717 not signed run "sailor mandate sign"');
|
|
45079
45122
|
}
|
|
@@ -45083,7 +45126,7 @@ async function status() {
|
|
|
45083
45126
|
let agentState;
|
|
45084
45127
|
if (running) {
|
|
45085
45128
|
agentState = `running (PID ${pid})`;
|
|
45086
|
-
} else if (!
|
|
45129
|
+
} else if (!hasMandate) {
|
|
45087
45130
|
agentState = "not configured";
|
|
45088
45131
|
} else if (session2 && session2.active === false) {
|
|
45089
45132
|
agentState = "stopped (session paused)";
|
|
@@ -45094,10 +45137,52 @@ async function status() {
|
|
|
45094
45137
|
}
|
|
45095
45138
|
|
|
45096
45139
|
// src/commands/ui.ts
|
|
45097
|
-
var
|
|
45140
|
+
var import_node_child_process5 = require("node:child_process");
|
|
45098
45141
|
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
45099
45142
|
var import_node_net2 = __toESM(require("node:net"), 1);
|
|
45100
45143
|
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
45144
|
+
|
|
45145
|
+
// src/lib/tailscale.ts
|
|
45146
|
+
var import_node_child_process4 = require("node:child_process");
|
|
45147
|
+
function tailscaleAvailable() {
|
|
45148
|
+
try {
|
|
45149
|
+
return (0, import_node_child_process4.spawnSync)("tailscale", ["version"], { stdio: "ignore" }).status === 0;
|
|
45150
|
+
} catch {
|
|
45151
|
+
return false;
|
|
45152
|
+
}
|
|
45153
|
+
}
|
|
45154
|
+
function tailnetDnsName() {
|
|
45155
|
+
const r = (0, import_node_child_process4.spawnSync)("tailscale", ["status", "--json"], { encoding: "utf-8" });
|
|
45156
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
45157
|
+
try {
|
|
45158
|
+
const j = JSON.parse(r.stdout);
|
|
45159
|
+
if (j.BackendState && j.BackendState !== "Running") return null;
|
|
45160
|
+
const dns = j.Self?.DNSName;
|
|
45161
|
+
if (typeof dns !== "string" || dns.length === 0) return null;
|
|
45162
|
+
return dns.replace(/\.$/, "");
|
|
45163
|
+
} catch {
|
|
45164
|
+
return null;
|
|
45165
|
+
}
|
|
45166
|
+
}
|
|
45167
|
+
function tailscaleServeUp(port) {
|
|
45168
|
+
const r = (0, import_node_child_process4.spawnSync)(
|
|
45169
|
+
"tailscale",
|
|
45170
|
+
["serve", "--bg", "--https=443", `http://127.0.0.1:${port}`],
|
|
45171
|
+
{ encoding: "utf-8" }
|
|
45172
|
+
);
|
|
45173
|
+
if (r.status !== 0) {
|
|
45174
|
+
const detail = (r.stderr || r.stdout || "").trim() || `exit ${r.status ?? "?"}`;
|
|
45175
|
+
throw new Error(detail);
|
|
45176
|
+
}
|
|
45177
|
+
}
|
|
45178
|
+
function tailscaleServeDown() {
|
|
45179
|
+
try {
|
|
45180
|
+
(0, import_node_child_process4.spawnSync)("tailscale", ["serve", "--https=443", "off"], { stdio: "ignore" });
|
|
45181
|
+
} catch {
|
|
45182
|
+
}
|
|
45183
|
+
}
|
|
45184
|
+
|
|
45185
|
+
// src/commands/ui.ts
|
|
45101
45186
|
var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
|
|
45102
45187
|
function readState2(projectRoot) {
|
|
45103
45188
|
const file = import_node_path16.default.join(projectRoot, UI_STATE_FILE);
|
|
@@ -45108,21 +45193,33 @@ function readState2(projectRoot) {
|
|
|
45108
45193
|
return null;
|
|
45109
45194
|
}
|
|
45110
45195
|
}
|
|
45111
|
-
function
|
|
45196
|
+
function installedCliVersion() {
|
|
45112
45197
|
try {
|
|
45113
|
-
|
|
45114
|
-
|
|
45198
|
+
const pkg = JSON.parse(
|
|
45199
|
+
import_node_fs20.default.readFileSync(import_node_path16.default.join(packageRoot(), "package.json"), "utf-8")
|
|
45200
|
+
);
|
|
45201
|
+
return pkg.version ?? null;
|
|
45115
45202
|
} catch {
|
|
45116
|
-
return
|
|
45203
|
+
return null;
|
|
45117
45204
|
}
|
|
45118
45205
|
}
|
|
45119
|
-
function
|
|
45120
|
-
|
|
45121
|
-
|
|
45122
|
-
|
|
45123
|
-
|
|
45124
|
-
|
|
45125
|
-
|
|
45206
|
+
async function warnIfVersionSkew(port) {
|
|
45207
|
+
const cli = installedCliVersion();
|
|
45208
|
+
if (!cli) return;
|
|
45209
|
+
try {
|
|
45210
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/version`, {
|
|
45211
|
+
signal: AbortSignal.timeout(500)
|
|
45212
|
+
});
|
|
45213
|
+
if (!res.ok) return;
|
|
45214
|
+
const { running } = await res.json();
|
|
45215
|
+
if (running && running !== cli) {
|
|
45216
|
+
console.warn(
|
|
45217
|
+
`\u26A0 Version skew: the running dashboard is v${running}, but the installed CLI is v${cli}.
|
|
45218
|
+
Restart it to load the new version: sailor ui stop && sailor ui start`
|
|
45219
|
+
);
|
|
45220
|
+
}
|
|
45221
|
+
} catch {
|
|
45222
|
+
}
|
|
45126
45223
|
}
|
|
45127
45224
|
function waitForPort(port, timeoutMs) {
|
|
45128
45225
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -45142,32 +45239,63 @@ function waitForPort(port, timeoutMs) {
|
|
|
45142
45239
|
attempt();
|
|
45143
45240
|
});
|
|
45144
45241
|
}
|
|
45145
|
-
async function uiCommand() {
|
|
45242
|
+
async function uiCommand(opts = {}) {
|
|
45146
45243
|
const distDir = cliDistDir();
|
|
45147
45244
|
const uiDistDir = import_node_path16.default.join(packageRoot(), "packages", "ui", "dist");
|
|
45148
45245
|
const serverBundle = import_node_path16.default.resolve(distDir, "server.cjs");
|
|
45149
45246
|
const projectRoot = process.cwd();
|
|
45150
45247
|
const sailDir2 = import_node_path16.default.join(projectRoot, ".sail");
|
|
45151
|
-
const
|
|
45248
|
+
const envPort = Number(process.env.PORT);
|
|
45249
|
+
const port = await findFreePort(Number.isInteger(envPort) && envPort > 0 && envPort <= 65535 ? envPort : projectPort(projectRoot));
|
|
45152
45250
|
if (!import_node_fs20.default.existsSync(serverBundle)) {
|
|
45153
45251
|
throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
|
|
45154
45252
|
}
|
|
45155
45253
|
if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
|
|
45156
45254
|
throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
|
|
45157
45255
|
}
|
|
45256
|
+
let tailnetUrl = null;
|
|
45257
|
+
if (opts.expose) {
|
|
45258
|
+
if (opts.expose !== "tailscale") {
|
|
45259
|
+
throw new Error(`Unknown --expose mode "${opts.expose}". Supported: tailscale.`);
|
|
45260
|
+
}
|
|
45261
|
+
if (!tailscaleAvailable()) {
|
|
45262
|
+
throw new Error(
|
|
45263
|
+
"tailscale CLI not found. Install Tailscale and run `tailscale up`, or start the dashboard without --expose to keep it local-only."
|
|
45264
|
+
);
|
|
45265
|
+
}
|
|
45266
|
+
const dns = tailnetDnsName();
|
|
45267
|
+
if (!dns) {
|
|
45268
|
+
throw new Error(
|
|
45269
|
+
"Could not resolve this node's tailnet name. Is tailscale running and logged in? Run `tailscale status` to check."
|
|
45270
|
+
);
|
|
45271
|
+
}
|
|
45272
|
+
tailnetUrl = `https://${dns}`;
|
|
45273
|
+
}
|
|
45158
45274
|
const existing = readState2(projectRoot);
|
|
45159
|
-
if (existing &&
|
|
45275
|
+
if (existing && isProcessAlive(existing.pid)) {
|
|
45160
45276
|
console.log(`Sailor UI is already running (pid ${existing.pid}) at http://localhost:${existing.port}`);
|
|
45277
|
+
if (opts.expose) {
|
|
45278
|
+
console.log("To expose it on the tailnet, stop it first: sailor ui stop && sailor ui start --expose tailscale");
|
|
45279
|
+
}
|
|
45280
|
+
await warnIfVersionSkew(existing.port);
|
|
45161
45281
|
return;
|
|
45162
45282
|
}
|
|
45163
45283
|
const runtimeDir = import_node_path16.default.join(projectRoot, ".sail", "runtime");
|
|
45164
45284
|
import_node_fs20.default.mkdirSync(runtimeDir, { recursive: true });
|
|
45165
45285
|
const logFile = import_node_path16.default.join(runtimeDir, "ui.log");
|
|
45166
45286
|
const logFd = import_node_fs20.default.openSync(logFile, "a");
|
|
45167
|
-
const
|
|
45287
|
+
const corsOrigins = [process.env.SAILOR_CORS_ORIGINS, tailnetUrl].filter(Boolean).join(",");
|
|
45288
|
+
const child = (0, import_node_child_process5.spawn)(process.execPath, [serverBundle], {
|
|
45168
45289
|
detached: true,
|
|
45169
45290
|
stdio: ["ignore", logFd, logFd],
|
|
45170
|
-
env: {
|
|
45291
|
+
env: {
|
|
45292
|
+
...process.env,
|
|
45293
|
+
SAIL_DIR: sailDir2,
|
|
45294
|
+
SERVE_DIST: "1",
|
|
45295
|
+
PORT: String(port),
|
|
45296
|
+
SAILOR_UI_DIST: uiDistDir,
|
|
45297
|
+
...corsOrigins ? { SAILOR_CORS_ORIGINS: corsOrigins } : {}
|
|
45298
|
+
}
|
|
45171
45299
|
});
|
|
45172
45300
|
child.unref();
|
|
45173
45301
|
import_node_fs20.default.closeSync(logFd);
|
|
@@ -45175,7 +45303,7 @@ async function uiCommand() {
|
|
|
45175
45303
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
45176
45304
|
let ready = false;
|
|
45177
45305
|
while (Date.now() < deadline) {
|
|
45178
|
-
if (!
|
|
45306
|
+
if (!isProcessAlive(child.pid)) break;
|
|
45179
45307
|
if (await waitForPort(port, 500)) {
|
|
45180
45308
|
ready = true;
|
|
45181
45309
|
break;
|
|
@@ -45194,17 +45322,31 @@ Server output:
|
|
|
45194
45322
|
${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
|
|
45195
45323
|
);
|
|
45196
45324
|
}
|
|
45325
|
+
let exposed = false;
|
|
45326
|
+
if (tailnetUrl) {
|
|
45327
|
+
try {
|
|
45328
|
+
tailscaleServeUp(port);
|
|
45329
|
+
exposed = true;
|
|
45330
|
+
} catch (err) {
|
|
45331
|
+
console.warn(
|
|
45332
|
+
`\u26A0 Could not expose on the tailnet: ${err.message}
|
|
45333
|
+
The dashboard is still running locally.`
|
|
45334
|
+
);
|
|
45335
|
+
}
|
|
45336
|
+
}
|
|
45197
45337
|
import_node_fs20.default.writeFileSync(
|
|
45198
45338
|
import_node_path16.default.join(projectRoot, UI_STATE_FILE),
|
|
45199
|
-
JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
45339
|
+
JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString(), exposed }, null, 2)
|
|
45200
45340
|
);
|
|
45201
45341
|
console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
|
|
45342
|
+
if (exposed) console.log(`Exposed on your tailnet at ${tailnetUrl}/`);
|
|
45202
45343
|
console.log(`Stop it with: sailor ui stop`);
|
|
45203
45344
|
}
|
|
45204
|
-
function uiStatus() {
|
|
45345
|
+
async function uiStatus() {
|
|
45205
45346
|
const state = readState2(process.cwd());
|
|
45206
|
-
if (state &&
|
|
45347
|
+
if (state && isProcessAlive(state.pid)) {
|
|
45207
45348
|
console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
|
|
45349
|
+
await warnIfVersionSkew(state.port);
|
|
45208
45350
|
} else {
|
|
45209
45351
|
if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
|
|
45210
45352
|
console.log("\u25CB Sailor UI is not running");
|
|
@@ -45217,12 +45359,16 @@ function uiStop() {
|
|
|
45217
45359
|
console.log("Sailor UI is not running.");
|
|
45218
45360
|
return;
|
|
45219
45361
|
}
|
|
45220
|
-
if (!
|
|
45362
|
+
if (!isProcessAlive(state.pid)) {
|
|
45221
45363
|
import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
|
|
45222
45364
|
console.log("Sailor UI is not running (stale state file removed).");
|
|
45223
45365
|
return;
|
|
45224
45366
|
}
|
|
45225
45367
|
process.kill(state.pid, "SIGTERM");
|
|
45368
|
+
if (state.exposed) {
|
|
45369
|
+
tailscaleServeDown();
|
|
45370
|
+
console.log("Tailnet exposure removed.");
|
|
45371
|
+
}
|
|
45226
45372
|
import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
|
|
45227
45373
|
console.log(`Stopped Sailor UI (pid ${state.pid}).`);
|
|
45228
45374
|
}
|
|
@@ -45274,7 +45420,7 @@ program2.command("init [dir]").description("Scaffold a new Sail agent into the c
|
|
|
45274
45420
|
);
|
|
45275
45421
|
program2.command("update").description("Re-sync agent tooling files (skills, AGENTS.md, Dockerfile) from the latest template").action(action(updateCommand));
|
|
45276
45422
|
var ui = program2.command("ui").description("Manage the local Sailor dashboard");
|
|
45277
|
-
ui.command("start").description("Start the dashboard at localhost:3333 (default)").
|
|
45423
|
+
ui.command("start").description("Start the dashboard at localhost:3333 (default)").option("--expose <mode>", "Expose the dashboard over HTTPS on your tailnet (mode: tailscale)").action(actionWith(uiCommand));
|
|
45278
45424
|
ui.command("stop").description("Stop the running dashboard").action(() => uiStop());
|
|
45279
45425
|
ui.command("status").description("Show whether the dashboard is running").action(() => uiStatus());
|
|
45280
45426
|
ui.action(action(uiCommand));
|
|
@@ -45339,8 +45485,8 @@ program2.command("run").description("Run the agent execution loop (use --once fo
|
|
|
45339
45485
|
var trigger = program2.command("trigger").description("Wake the agent on demand from an external system");
|
|
45340
45486
|
trigger.command("github").description("Fire the agent's GitHub Actions workflow_dispatch (the same job the cron runs)").option("--workflow <file>", "Workflow file to dispatch", "agent-tick.yml").option("--ref <branch>", "Git ref to run the workflow on", "main").option("--reason <text>", "Why this run fired \u2014 recorded as the workflow's reason input").option("--repo <owner/repo>", "Override the repository (default: from the git origin remote)").option("--json", "Emit machine-readable JSON").action(actionWith(triggerGithub));
|
|
45341
45487
|
var session = program2.command("session").description("Control the agent session");
|
|
45342
|
-
session.command("pause").description("Pause the agent session (revoke dispatch rights)").
|
|
45343
|
-
session.command("resume").description("Resume a paused session").
|
|
45488
|
+
session.command("pause").description("Pause the agent session (revoke dispatch rights)").option("--json", "Emit machine-readable JSON").action(actionWith(sessionPause));
|
|
45489
|
+
session.command("resume").description("Resume a paused session").option("--json", "Emit machine-readable JSON").action(actionWith(sessionResume));
|
|
45344
45490
|
program2.command("doctor").description(
|
|
45345
45491
|
"Preflight (read-only): kernel model, permission health, RPC + gas balances, before spending gas"
|
|
45346
45492
|
).option("--json", "Output machine-readable JSON").option("--account <address>", "SMA to check (defaults to .sail/account.json)").action(actionWith(doctor));
|