@dev.sail.money/sailor 1.2.0-75 → 1.2.0-77

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.
@@ -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 path9 = require("node:path");
970
- var fs10 = require("node:fs");
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 = path9.resolve(baseDir, baseName);
1903
- if (fs10.existsSync(localBin)) return localBin;
1904
- if (sourceExt.includes(path9.extname(baseName))) return void 0;
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) => fs10.existsSync(`${localBin}${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 = fs10.realpathSync(this._scriptPath);
1918
+ resolvedScriptPath = fs12.realpathSync(this._scriptPath);
1919
1919
  } catch (err) {
1920
1920
  resolvedScriptPath = this._scriptPath;
1921
1921
  }
1922
- executableDir = path9.resolve(
1923
- path9.dirname(resolvedScriptPath),
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 = path9.basename(
1930
+ const legacyName = path11.basename(
1931
1931
  this._scriptPath,
1932
- path9.extname(this._scriptPath)
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(path9.extname(executableFile));
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 = path9.basename(filename, path9.extname(filename));
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(path10) {
2798
- if (path10 === void 0) return this._executableDir;
2799
- this._executableDir = path10;
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, path9) {
30775
+ function setupKzg(parameters, path11) {
30776
30776
  try {
30777
- parameters.loadTrustedSetup(path9);
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 import_node_fs19 = require("node:fs");
35194
- var import_node_path15 = require("node:path");
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(path9) {
37127
- if (!/^[mM]'?/.test(path9)) {
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(path9)) {
37130
+ if (/^[mM]'?$/.test(path11)) {
37131
37131
  return this;
37132
37132
  }
37133
- const parts = path9.replace(/^[mM]'?\//, "").split("/");
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: path9, ...options } = {}) {
37466
- const hdKey = hdKey_.derive(path9 || `m/44'/60'/${accountIndex}'/${changeIndex}/${addressIndex}`);
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(path9, password) {
37568
- const keystore = JSON.parse((0, import_node_fs.readFileSync)(path9, "utf-8"));
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. */
@@ -38845,16 +38845,16 @@ var SigningServer = class {
38845
38845
  syncConfigChainId(chainId) {
38846
38846
  if (chainId == null) return;
38847
38847
  try {
38848
- const path9 = this.sailFile("config.json");
38848
+ const path11 = this.sailFile("config.json");
38849
38849
  let config = {};
38850
38850
  try {
38851
- config = JSON.parse((0, import_node_fs5.readFileSync)(path9, "utf-8"));
38851
+ config = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf-8"));
38852
38852
  } catch {
38853
38853
  }
38854
38854
  if (Number(config.chainId) === Number(chainId)) return;
38855
38855
  config.chainId = Number(chainId);
38856
38856
  (0, import_node_fs5.mkdirSync)(this.sailFile(), { recursive: true });
38857
- (0, import_node_fs5.writeFileSync)(path9, `${JSON.stringify(config, null, 2)}
38857
+ (0, import_node_fs5.writeFileSync)(path11, `${JSON.stringify(config, null, 2)}
38858
38858
  `);
38859
38859
  } catch {
38860
38860
  }
@@ -39150,10 +39150,10 @@ var SigningServer = class {
39150
39150
  }
39151
39151
  }
39152
39152
  writeRuntimeState() {
39153
- const path9 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39154
- if ((0, import_node_fs5.existsSync)(path9)) {
39153
+ const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39154
+ if ((0, import_node_fs5.existsSync)(path11)) {
39155
39155
  try {
39156
- const existing = JSON.parse((0, import_node_fs5.readFileSync)(path9, "utf8"));
39156
+ const existing = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39157
39157
  if (existing.pid != null && existing.pid !== process.pid && pidAlive(existing.pid)) return;
39158
39158
  } catch {
39159
39159
  }
@@ -39176,12 +39176,12 @@ var SigningServer = class {
39176
39176
  );
39177
39177
  }
39178
39178
  removeRuntimeState() {
39179
- const path9 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39179
+ const path11 = (0, import_node_path4.join)(this.runtimeDir, SERVER_STATE_FILE);
39180
39180
  try {
39181
- if (!(0, import_node_fs5.existsSync)(path9)) return;
39182
- const state = JSON.parse((0, import_node_fs5.readFileSync)(path9, "utf8"));
39181
+ if (!(0, import_node_fs5.existsSync)(path11)) return;
39182
+ const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39183
39183
  if (state.pid != null && state.pid !== process.pid) return;
39184
- (0, import_node_fs5.unlinkSync)(path9);
39184
+ (0, import_node_fs5.unlinkSync)(path11);
39185
39185
  } catch {
39186
39186
  }
39187
39187
  }
@@ -39195,12 +39195,12 @@ function pidAlive(pid) {
39195
39195
  }
39196
39196
  }
39197
39197
  function reapStaleRuntimeState(projectRoot = process.cwd()) {
39198
- const path9 = (0, import_node_path4.join)(projectRoot, RUNTIME_SUBDIR, SERVER_STATE_FILE);
39198
+ const path11 = (0, import_node_path4.join)(projectRoot, RUNTIME_SUBDIR, SERVER_STATE_FILE);
39199
39199
  try {
39200
- if (!(0, import_node_fs5.existsSync)(path9)) return;
39201
- const state = JSON.parse((0, import_node_fs5.readFileSync)(path9, "utf8"));
39200
+ if (!(0, import_node_fs5.existsSync)(path11)) return;
39201
+ const state = JSON.parse((0, import_node_fs5.readFileSync)(path11, "utf8"));
39202
39202
  if (state.pid != null && state.pid !== process.pid && !pidAlive(state.pid)) {
39203
- (0, import_node_fs5.unlinkSync)(path9);
39203
+ (0, import_node_fs5.unlinkSync)(path11);
39204
39204
  }
39205
39205
  } catch {
39206
39206
  }
@@ -40453,8 +40453,8 @@ Probe is heuristic: an unknown selector (${PROBE_SELECTOR}) to a neutral target
40453
40453
  }
40454
40454
 
40455
40455
  // src/commands/init.ts
40456
- var import_node_fs8 = __toESM(require("node:fs"), 1);
40457
- var import_node_path7 = __toESM(require("node:path"), 1);
40456
+ var import_node_fs9 = __toESM(require("node:fs"), 1);
40457
+ var import_node_path8 = __toESM(require("node:path"), 1);
40458
40458
 
40459
40459
  // src/lib/foundry.ts
40460
40460
  var import_node_fs7 = require("node:fs");
@@ -40620,11 +40620,13 @@ function scaffoldFoundryWorkspace(root) {
40620
40620
  writeIfMissing((0, import_node_path6.join)(root, "mandates", "BoundedCallPermission.sol"), EXAMPLE_MANDATE_SOL);
40621
40621
  writeIfMissing((0, import_node_path6.join)(root, "mandates", "README.md"), MANDATES_README);
40622
40622
  }
40623
- function writeIfMissing(path9, content) {
40624
- if (!(0, import_node_fs7.existsSync)(path9)) (0, import_node_fs7.writeFileSync)(path9, content, "utf8");
40623
+ function writeIfMissing(path11, content) {
40624
+ if (!(0, import_node_fs7.existsSync)(path11)) (0, import_node_fs7.writeFileSync)(path11, content, "utf8");
40625
40625
  }
40626
40626
 
40627
- // src/commands/init.ts
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);
40628
40630
  var TEMPLATE_COPY_EXCLUDES = /* @__PURE__ */ new Set([
40629
40631
  "node_modules",
40630
40632
  "dist",
@@ -40647,6 +40649,26 @@ function copyDirSync(src, dest) {
40647
40649
  }
40648
40650
  }
40649
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
40650
40672
  var SAIL_WORKSPACE_README = `# Sailor Project Workspace
40651
40673
 
40652
40674
  This folder is the local workspace for one Sailor agent deployment.
@@ -40661,15 +40683,12 @@ This folder is the local workspace for one Sailor agent deployment.
40661
40683
  AI coding agents should read the project's \`AGENTS.md\` and this folder's \`config.json\`
40662
40684
  before changing strategy code or running commands that touch funds.
40663
40685
  `;
40664
- function writeIfMissing2(file, content) {
40665
- if (!import_node_fs8.default.existsSync(file)) import_node_fs8.default.writeFileSync(file, content, "utf-8");
40666
- }
40667
40686
  var CANONICAL_PKG = "@sail.money/sailor";
40668
40687
  var DEV_PKG = "@dev.sail.money/sailor";
40669
40688
  function cliPackageInfo() {
40670
40689
  try {
40671
40690
  const pkg = JSON.parse(
40672
- import_node_fs8.default.readFileSync(import_node_path7.default.join(packageRoot(), "package.json"), "utf-8")
40691
+ import_node_fs9.default.readFileSync(import_node_path8.default.join(packageRoot(), "package.json"), "utf-8")
40673
40692
  );
40674
40693
  return {
40675
40694
  name: pkg.name ?? CANONICAL_PKG,
@@ -40685,12 +40704,12 @@ function scaffoldProjectWorkspace(dest, name, options) {
40685
40704
  if (!Number.isInteger(n) || n <= 0) throw new Error(`Invalid chain id: "${options.chain}"`);
40686
40705
  return n;
40687
40706
  })() : null;
40688
- const sailDir2 = import_node_path7.default.join(dest, ".sail");
40689
- import_node_fs8.default.mkdirSync(import_node_path7.default.join(sailDir2, "keys"), { recursive: true });
40690
- import_node_fs8.default.mkdirSync(import_node_path7.default.join(sailDir2, "runtime"), { recursive: true });
40691
- import_node_fs8.default.mkdirSync(import_node_path7.default.join(sailDir2, "state"), { recursive: true });
40692
- import_node_fs8.default.writeFileSync(
40693
- import_node_path7.default.join(sailDir2, "config.json"),
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"),
40694
40713
  `${JSON.stringify(
40695
40714
  {
40696
40715
  version: 1,
@@ -40710,12 +40729,12 @@ function scaffoldProjectWorkspace(dest, name, options) {
40710
40729
  `,
40711
40730
  "utf-8"
40712
40731
  );
40713
- writeIfMissing2(import_node_path7.default.join(sailDir2, "README.md"), SAIL_WORKSPACE_README);
40732
+ writeIfMissing2(import_node_path8.default.join(sailDir2, "README.md"), SAIL_WORKSPACE_README);
40714
40733
  const chainEntries = Object.values(chains);
40715
40734
  const perChainVarLines = chainEntries.map((c) => `# ${c.rpcEnvVar}=https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`).join("\n");
40716
40735
  const chainIdExample = chainId != null ? `CHAIN_ID=${chainId}` : `# CHAIN_ID=8453 # set after choosing your chain in Stage 1`;
40717
- import_node_fs8.default.writeFileSync(
40718
- import_node_path7.default.join(dest, ".env.example"),
40736
+ import_node_fs9.default.writeFileSync(
40737
+ import_node_path8.default.join(dest, ".env.example"),
40719
40738
  `# Sailor agent environment
40720
40739
  #
40721
40740
  # RPC configuration \u2014 two patterns, pick one:
@@ -40740,8 +40759,8 @@ ${perChainVarLines}
40740
40759
  const val = isActive && options.rpcUrl ? options.rpcUrl : `https://your-${c.name.toLowerCase().replace(/\s+/g, "-")}-endpoint`;
40741
40760
  return isActive && options.rpcUrl ? `${c.rpcEnvVar}=${val}` : `# ${c.rpcEnvVar}=${val}`;
40742
40761
  }).join("\n");
40743
- import_node_fs8.default.writeFileSync(
40744
- import_node_path7.default.join(sailDir2, ".env.local"),
40762
+ import_node_fs9.default.writeFileSync(
40763
+ import_node_path8.default.join(sailDir2, ".env.local"),
40745
40764
  `# Real values \u2014 never commit this file.
40746
40765
  #
40747
40766
  # Option A: single active chain (simplest)
@@ -40759,16 +40778,16 @@ ${allChainVarLines}
40759
40778
  }
40760
40779
  async function initCommand(dir, options = {}) {
40761
40780
  const inPlace = !dir || dir === ".";
40762
- const dest = inPlace ? process.cwd() : import_node_path7.default.resolve(process.cwd(), dir);
40763
- const name = import_node_path7.default.basename(dest);
40764
- const templatesDir = import_node_path7.default.join(packageRoot(), "templates");
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");
40765
40784
  const templateName = options.template ?? "default";
40766
40785
  if (/[/\\.]/.test(templateName) || templateName.includes("..")) {
40767
40786
  throw new Error(`Invalid template name: "${templateName}"`);
40768
40787
  }
40769
- const templateSrc = import_node_path7.default.join(templatesDir, templateName);
40770
- const availableTemplates = () => import_node_fs8.default.existsSync(templatesDir) ? import_node_fs8.default.readdirSync(templatesDir).filter((e) => import_node_fs8.default.existsSync(import_node_path7.default.join(templatesDir, e, "package.json"))).join(", ") || "none" : "none";
40771
- if (!import_node_fs8.default.existsSync(templateSrc) || !import_node_fs8.default.existsSync(import_node_path7.default.join(templateSrc, "package.json"))) {
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"))) {
40772
40791
  const available = availableTemplates();
40773
40792
  const hint = available === "none" ? `
40774
40793
  No templates found under ${templatesDir}.
@@ -40778,49 +40797,49 @@ run from the repo root.` : ` Available: ${available}`;
40778
40797
  throw new Error(`Template "${templateName}" not found.${hint}`);
40779
40798
  }
40780
40799
  const cwd = process.cwd();
40781
- if (!inPlace && !dest.startsWith(cwd + import_node_path7.default.sep) && dest !== cwd) {
40800
+ if (!inPlace && !dest.startsWith(cwd + import_node_path8.default.sep) && dest !== cwd) {
40782
40801
  throw new Error(`Directory must be inside the current working directory`);
40783
40802
  }
40784
- if (!inPlace && import_node_fs8.default.existsSync(dest)) {
40803
+ if (!inPlace && import_node_fs9.default.existsSync(dest)) {
40785
40804
  throw new Error(`Directory already exists: ${dest}`);
40786
40805
  }
40787
- if (inPlace && import_node_fs8.default.existsSync(import_node_path7.default.join(dest, ".sail", "config.json"))) {
40788
- throw new Error(`Already initialized \u2014 .sail/config.json exists`);
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.");
40789
40808
  }
40790
40809
  copyDirSync(templateSrc, dest);
40791
40810
  const pkgRoot = packageRoot();
40792
- const examplesPermSrc = import_node_path7.default.join(pkgRoot, "examples", "permissions");
40793
- if (import_node_fs8.default.existsSync(examplesPermSrc)) {
40794
- copyDirSync(examplesPermSrc, import_node_path7.default.join(dest, "examples", "permissions"));
40795
- }
40796
- const customMandateSrc = import_node_path7.default.join(pkgRoot, "examples", "custom-mandate");
40797
- if (import_node_fs8.default.existsSync(customMandateSrc)) {
40798
- copyDirSync(customMandateSrc, import_node_path7.default.join(dest, "examples", "custom-mandate"));
40799
- }
40800
- const permModelSrc = import_node_path7.default.join(pkgRoot, "docs", "PERMISSION_MODEL.md");
40801
- if (import_node_fs8.default.existsSync(permModelSrc)) {
40802
- import_node_fs8.default.mkdirSync(import_node_path7.default.join(dest, "docs"), { recursive: true });
40803
- writeIfMissing2(import_node_path7.default.join(dest, "docs", "PERMISSION_MODEL.md"), import_node_fs8.default.readFileSync(permModelSrc, "utf-8"));
40804
- }
40805
- const pkgPath = import_node_path7.default.join(dest, "package.json");
40806
- if (import_node_fs8.default.existsSync(pkgPath)) {
40807
- const pkg = JSON.parse(import_node_fs8.default.readFileSync(pkgPath, "utf-8"));
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"));
40808
40827
  pkg.name = name;
40809
40828
  const devDeps = pkg.devDependencies ?? {};
40810
40829
  const { name: cliName, version: cliVer } = cliPackageInfo();
40811
40830
  devDeps[CANONICAL_PKG] = cliName === DEV_PKG ? `npm:${DEV_PKG}@^${cliVer}` : `^${cliVer}`;
40812
40831
  pkg.devDependencies = devDeps;
40813
- import_node_fs8.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
40832
+ import_node_fs9.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
40814
40833
  `);
40815
40834
  }
40816
- const tsconfigPath = import_node_path7.default.join(dest, "tsconfig.json");
40817
- if (import_node_fs8.default.existsSync(tsconfigPath)) {
40818
- const tsconfig = JSON.parse(import_node_fs8.default.readFileSync(tsconfigPath, "utf-8"));
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"));
40819
40838
  const co = tsconfig.compilerOptions;
40820
40839
  if (co && "paths" in co) {
40821
40840
  delete co.paths;
40822
40841
  delete co.baseUrl;
40823
- import_node_fs8.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
40842
+ import_node_fs9.default.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
40824
40843
  `);
40825
40844
  }
40826
40845
  }
@@ -40846,21 +40865,21 @@ function chainLabel(chainId) {
40846
40865
  }
40847
40866
  function detectState(dest) {
40848
40867
  try {
40849
- const configRaw = import_node_fs8.default.readFileSync(import_node_path7.default.join(dest, ".sail", "config.json"), "utf-8");
40868
+ const configRaw = import_node_fs9.default.readFileSync(import_node_path8.default.join(dest, ".sail", "config.json"), "utf-8");
40850
40869
  const config = JSON.parse(configRaw);
40851
- const projectName = config.name ?? import_node_path7.default.basename(dest);
40852
- const accountPath = import_node_path7.default.join(dest, ".sail", "account.json");
40853
- if (!import_node_fs8.default.existsSync(accountPath)) {
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)) {
40854
40873
  return { kind: "B", projectName, chain: chainLabel(config.chainId ?? 0) };
40855
40874
  }
40856
- const accountRaw = import_node_fs8.default.readFileSync(accountPath, "utf-8");
40875
+ const accountRaw = import_node_fs9.default.readFileSync(accountPath, "utf-8");
40857
40876
  const account2 = JSON.parse(accountRaw);
40858
40877
  const sma = account2.safe ?? "";
40859
40878
  const chain2 = chainLabel(account2.chainId ?? config.chainId ?? 0);
40860
40879
  let permissionCount = 0;
40861
40880
  try {
40862
- const mandatesRaw = import_node_fs8.default.readFileSync(
40863
- import_node_path7.default.join(dest, ".sail", "state", "mandates.json"),
40881
+ const mandatesRaw = import_node_fs9.default.readFileSync(
40882
+ import_node_path8.default.join(dest, ".sail", "state", "mandates.json"),
40864
40883
  "utf-8"
40865
40884
  );
40866
40885
  const mandates = JSON.parse(mandatesRaw);
@@ -40941,9 +40960,79 @@ Created ${name}/`);
40941
40960
  ].join("\n"));
40942
40961
  }
40943
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
+
40944
41033
  // src/commands/keys.ts
40945
- var import_node_fs9 = __toESM(require("node:fs"), 1);
40946
- var import_node_path8 = __toESM(require("node:path"), 1);
41034
+ var import_node_fs11 = __toESM(require("node:fs"), 1);
41035
+ var import_node_path10 = __toESM(require("node:path"), 1);
40947
41036
  async function keysGenerate() {
40948
41037
  const roleInput = await prompt("Which key? (agent wallet / mandate signer)", "agent wallet");
40949
41038
  const role = normalizeRole(roleInput);
@@ -40996,15 +41085,15 @@ async function keysExportCi() {
40996
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.'
40997
41086
  );
40998
41087
  }
40999
- const dest = import_node_path8.default.resolve(process.cwd(), "ci-keystore.json");
41000
- import_node_fs9.default.copyFileSync(src, dest);
41088
+ const dest = import_node_path10.default.resolve(process.cwd(), "ci-keystore.json");
41089
+ import_node_fs11.default.copyFileSync(src, dest);
41001
41090
  console.log(`\u2713 Keystore copied to ci-keystore.json`);
41002
41091
  console.log(` Source: ${src}`);
41003
- const gitignorePath = import_node_path8.default.resolve(process.cwd(), ".gitignore");
41004
- if (import_node_fs9.default.existsSync(gitignorePath)) {
41005
- const content = import_node_fs9.default.readFileSync(gitignorePath, "utf-8");
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");
41006
41095
  if (!content.includes("ci-keystore.json")) {
41007
- import_node_fs9.default.appendFileSync(
41096
+ import_node_fs11.default.appendFileSync(
41008
41097
  gitignorePath,
41009
41098
  "\n# CI keystore \u2014 encrypted agent wallet, safe to commit\n!ci-keystore.json\n"
41010
41099
  );
@@ -41042,8 +41131,8 @@ async function keysShow() {
41042
41131
 
41043
41132
  // src/commands/mandate-contracts.ts
41044
41133
  var import_node_child_process = require("node:child_process");
41045
- var import_node_fs10 = require("node:fs");
41046
- var import_node_path9 = require("node:path");
41134
+ var import_node_fs12 = require("node:fs");
41135
+ var import_node_path11 = require("node:path");
41047
41136
  init_esm2();
41048
41137
 
41049
41138
  // src/lib/mandates.ts
@@ -41739,9 +41828,9 @@ async function runDeploy(project, channel, options) {
41739
41828
  const { abi: abi2, bytecode, contractName, artifactPath } = resolveArtifact(options);
41740
41829
  let argsJson;
41741
41830
  if (options.argsFile) {
41742
- const argsFilePath = (0, import_node_path9.resolve)(options.argsFile);
41831
+ const argsFilePath = (0, import_node_path11.resolve)(options.argsFile);
41743
41832
  try {
41744
- argsJson = (0, import_node_fs10.readFileSync)(argsFilePath, "utf8").trim();
41833
+ argsJson = (0, import_node_fs12.readFileSync)(argsFilePath, "utf8").trim();
41745
41834
  } catch {
41746
41835
  throw new Error(`Cannot read --args-file: ${argsFilePath}`);
41747
41836
  }
@@ -42444,11 +42533,11 @@ function resolveArtifact(options) {
42444
42533
  let contractName = options.contract ?? options.name ?? "";
42445
42534
  if (!artifactPath) {
42446
42535
  if (!options.contract) throw new Error("Provide --artifact <path> or --contract <name>");
42447
- artifactPath = (0, import_node_path9.join)(options.out, `${options.contract}.sol`, `${options.contract}.json`);
42536
+ artifactPath = (0, import_node_path11.join)(options.out, `${options.contract}.sol`, `${options.contract}.json`);
42448
42537
  }
42449
- const resolved = (0, import_node_path9.resolve)(artifactPath);
42450
- const projectRoot = (0, import_node_path9.resolve)(process.cwd());
42451
- if (!resolved.startsWith(projectRoot + import_node_path9.sep) && resolved !== 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) {
42452
42541
  throw new Error(
42453
42542
  `Artifact path must be inside the project directory.
42454
42543
  Resolved: ${resolved}`
@@ -42456,14 +42545,14 @@ Resolved: ${resolved}`
42456
42545
  }
42457
42546
  artifactPath = resolved;
42458
42547
  if (options.build) runForgeBuild();
42459
- if (!(0, import_node_fs10.existsSync)(artifactPath)) {
42548
+ if (!(0, import_node_fs12.existsSync)(artifactPath)) {
42460
42549
  ensureForgeHint();
42461
42550
  throw new Error(
42462
42551
  `Artifact not found: ${artifactPath}
42463
42552
  Compile your mandate first (e.g. \`forge build\`), or pass --build.`
42464
42553
  );
42465
42554
  }
42466
- const artifact = JSON.parse((0, import_node_fs10.readFileSync)(artifactPath, "utf8"));
42555
+ const artifact = JSON.parse((0, import_node_fs12.readFileSync)(artifactPath, "utf8"));
42467
42556
  const abi2 = artifact.abi;
42468
42557
  const bytecodeRaw = artifact.bytecode?.object ?? artifact.bytecode;
42469
42558
  if (!bytecodeRaw || typeof bytecodeRaw !== "string") {
@@ -42546,13 +42635,13 @@ function runForgeBuild() {
42546
42635
  init_esm2();
42547
42636
 
42548
42637
  // src/lib/permission-explainer.ts
42549
- var import_node_fs11 = require("node:fs");
42550
- var import_node_path10 = require("node:path");
42638
+ var import_node_fs13 = require("node:fs");
42639
+ var import_node_path12 = require("node:path");
42551
42640
  function explainPermission(name, sourcePath) {
42552
- const resolved = sourcePath ?? (0, import_node_path10.join)(process.cwd(), "mandates", `${name}.sol`);
42641
+ const resolved = sourcePath ?? (0, import_node_path12.join)(process.cwd(), "mandates", `${name}.sol`);
42553
42642
  let src;
42554
42643
  try {
42555
- src = (0, import_node_fs11.readFileSync)(resolved, "utf8");
42644
+ src = (0, import_node_fs13.readFileSync)(resolved, "utf8");
42556
42645
  } catch {
42557
42646
  return null;
42558
42647
  }
@@ -42774,7 +42863,7 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
42774
42863
  }
42775
42864
 
42776
42865
  // src/commands/mandate-simulate.ts
42777
- var import_node_fs12 = require("node:fs");
42866
+ var import_node_fs14 = require("node:fs");
42778
42867
  init_esm2();
42779
42868
  function parseExpect(raw, where) {
42780
42869
  if (raw === void 0) return void 0;
@@ -42790,7 +42879,7 @@ function resolveSampleCalls(options) {
42790
42879
  if (options.calls) {
42791
42880
  let raw;
42792
42881
  try {
42793
- raw = JSON.parse((0, import_node_fs12.readFileSync)(options.calls, "utf8"));
42882
+ raw = JSON.parse((0, import_node_fs14.readFileSync)(options.calls, "utf8"));
42794
42883
  } catch (err) {
42795
42884
  throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
42796
42885
  }
@@ -43006,7 +43095,7 @@ async function mandateSimulate(options) {
43006
43095
  }
43007
43096
 
43008
43097
  // src/commands/rotate-signer.ts
43009
- var import_node_fs13 = require("node:fs");
43098
+ var import_node_fs15 = require("node:fs");
43010
43099
  init_esm2();
43011
43100
  var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
43012
43101
  async function rotateSigner(options) {
@@ -43394,7 +43483,7 @@ ensure the agent that signs dispatches holds this key.`
43394
43483
  const keystore = await keyring.exportKeystore(password);
43395
43484
  writeJsonFile(target, keystore, 384);
43396
43485
  const perManagerPath = managerKeystorePath(keyring.address);
43397
- (0, import_node_fs13.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43486
+ (0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43398
43487
  writeJsonFile(perManagerPath, keystore, 384);
43399
43488
  say(
43400
43489
  () => console.log(
@@ -43406,7 +43495,7 @@ ensure the agent that signs dispatches holds this key.`
43406
43495
  function promoteManagerKeystore(newManager, say) {
43407
43496
  const stored = readJsonFile(managerKeystorePath(newManager));
43408
43497
  if (!stored) return;
43409
- (0, import_node_fs13.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43498
+ (0, import_node_fs15.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
43410
43499
  const activeTarget = keyPath("manager");
43411
43500
  const displaced = readJsonFile(activeTarget);
43412
43501
  if (displaced?.address) {
@@ -43458,7 +43547,7 @@ function writePending(pending) {
43458
43547
  }
43459
43548
  function clearPending() {
43460
43549
  try {
43461
- (0, import_node_fs13.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
43550
+ (0, import_node_fs15.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
43462
43551
  } catch {
43463
43552
  }
43464
43553
  }
@@ -43557,30 +43646,30 @@ function ownerShow(options) {
43557
43646
  }
43558
43647
 
43559
43648
  // src/commands/run.ts
43560
- var import_node_fs15 = __toESM(require("node:fs"), 1);
43561
- var import_node_path11 = __toESM(require("node:path"), 1);
43649
+ var import_node_fs17 = __toESM(require("node:fs"), 1);
43650
+ var import_node_path13 = __toESM(require("node:path"), 1);
43562
43651
  var import_node_url = require("node:url");
43563
43652
  init_esm2();
43564
43653
 
43565
43654
  // src/lib/process.ts
43566
- var import_node_fs14 = __toESM(require("node:fs"), 1);
43655
+ var import_node_fs16 = __toESM(require("node:fs"), 1);
43567
43656
  function agentPidPath(chainId) {
43568
43657
  return chainId != null ? sailPath(`agent-${chainId}.pid`) : sailPath("agent.pid");
43569
43658
  }
43570
43659
  function writeAgentPid(chainId) {
43571
- import_node_fs14.default.mkdirSync(sailPath(), { recursive: true });
43572
- import_node_fs14.default.writeFileSync(agentPidPath(chainId), `${process.pid}
43660
+ import_node_fs16.default.mkdirSync(sailPath(), { recursive: true });
43661
+ import_node_fs16.default.writeFileSync(agentPidPath(chainId), `${process.pid}
43573
43662
  `);
43574
43663
  }
43575
43664
  function clearAgentPid(chainId) {
43576
43665
  try {
43577
- import_node_fs14.default.rmSync(agentPidPath(chainId));
43666
+ import_node_fs16.default.rmSync(agentPidPath(chainId));
43578
43667
  } catch {
43579
43668
  }
43580
43669
  }
43581
43670
  function readAgentPid(chainId) {
43582
43671
  try {
43583
- const pid = Number.parseInt(import_node_fs14.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
43672
+ const pid = Number.parseInt(import_node_fs16.default.readFileSync(agentPidPath(chainId), "utf-8").trim(), 10);
43584
43673
  return Number.isNaN(pid) ? null : pid;
43585
43674
  } catch {
43586
43675
  return null;
@@ -43627,7 +43716,7 @@ var ERC20_READ_ABI = [
43627
43716
  function loadAgentData(filePath) {
43628
43717
  if (!filePath) return {};
43629
43718
  try {
43630
- const parsed = JSON.parse(import_node_fs15.default.readFileSync(filePath, "utf-8"));
43719
+ const parsed = JSON.parse(import_node_fs17.default.readFileSync(filePath, "utf-8"));
43631
43720
  return parsed && typeof parsed === "object" ? parsed : {};
43632
43721
  } catch {
43633
43722
  return {};
@@ -43636,8 +43725,8 @@ function loadAgentData(filePath) {
43636
43725
  async function loadAgent() {
43637
43726
  const candidates = ["src/agent.ts", "src/agent.js", "dist/agent.js", "dist/src/agent.js"];
43638
43727
  for (const rel of candidates) {
43639
- const abs = import_node_path11.default.join(process.cwd(), rel);
43640
- if (!import_node_fs15.default.existsSync(abs)) continue;
43728
+ const abs = import_node_path13.default.join(process.cwd(), rel);
43729
+ if (!import_node_fs17.default.existsSync(abs)) continue;
43641
43730
  let mod2;
43642
43731
  if (abs.endsWith(".ts")) {
43643
43732
  const { tsImport } = await import("tsx/esm/api");
@@ -44113,9 +44202,9 @@ async function scan(options) {
44113
44202
 
44114
44203
  // src/commands/service.ts
44115
44204
  var import_node_child_process2 = require("node:child_process");
44116
- var import_node_fs16 = __toESM(require("node:fs"), 1);
44205
+ var import_node_fs18 = __toESM(require("node:fs"), 1);
44117
44206
  var import_node_os = __toESM(require("node:os"), 1);
44118
- var import_node_path12 = __toESM(require("node:path"), 1);
44207
+ var import_node_path14 = __toESM(require("node:path"), 1);
44119
44208
  function sanitizeName(raw) {
44120
44209
  const s = raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
44121
44210
  return s || "agent";
@@ -44234,21 +44323,21 @@ function buildWindowsTaskXml(cfg) {
44234
44323
  `;
44235
44324
  }
44236
44325
  function isTccProtected(projectDir, home = import_node_os.default.homedir()) {
44237
- const rel = import_node_path12.default.relative(home, import_node_path12.default.resolve(projectDir));
44238
- if (rel.startsWith("..") || import_node_path12.default.isAbsolute(rel)) return false;
44239
- const top = rel.split(import_node_path12.default.sep)[0];
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];
44240
44329
  return ["Desktop", "Documents", "Downloads"].includes(top ?? "");
44241
44330
  }
44242
44331
  function resolveCliEntry() {
44243
44332
  try {
44244
- return import_node_fs16.default.realpathSync(process.argv[1]);
44333
+ return import_node_fs18.default.realpathSync(process.argv[1]);
44245
44334
  } catch {
44246
- return import_node_path12.default.resolve(process.argv[1]);
44335
+ return import_node_path14.default.resolve(process.argv[1]);
44247
44336
  }
44248
44337
  }
44249
44338
  function resolveProjectDir(explicit) {
44250
- const dir = import_node_path12.default.resolve(explicit ?? process.cwd());
44251
- if (!import_node_fs16.default.existsSync(import_node_path12.default.join(dir, ".sail"))) {
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"))) {
44252
44341
  throw new Error(
44253
44342
  `No .sail/ found in ${dir}.
44254
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.)`
@@ -44260,7 +44349,7 @@ function passphraseReadiness(projectDir) {
44260
44349
  const account2 = (() => {
44261
44350
  try {
44262
44351
  return JSON.parse(
44263
- import_node_fs16.default.readFileSync(import_node_path12.default.join(projectDir, ".sail", "account.json"), "utf-8")
44352
+ import_node_fs18.default.readFileSync(import_node_path14.default.join(projectDir, ".sail", "account.json"), "utf-8")
44264
44353
  );
44265
44354
  } catch {
44266
44355
  return null;
@@ -44276,28 +44365,28 @@ function passphraseReadiness(projectDir) {
44276
44365
  } finally {
44277
44366
  process.chdir(prevCwd);
44278
44367
  }
44279
- const env = parseEnvFile(import_node_path12.default.join(projectDir, ".sail", ".env.local"));
44368
+ const env = parseEnvFile(import_node_path14.default.join(projectDir, ".sail", ".env.local"));
44280
44369
  const passphraseInEnvFile = Boolean(env.SAIL_PASSPHRASE);
44281
44370
  return { keystorePresent, passphraseInEnvFile, ready: keystorePresent && passphraseInEnvFile };
44282
44371
  }
44283
44372
  function buildConfig(opts) {
44284
44373
  const projectDir = resolveProjectDir(opts.project);
44285
44374
  return {
44286
- projectName: sanitizeName(import_node_path12.default.basename(projectDir)),
44375
+ projectName: sanitizeName(import_node_path14.default.basename(projectDir)),
44287
44376
  projectDir,
44288
44377
  nodePath: process.execPath,
44289
44378
  cliEntry: resolveCliEntry(),
44290
- logPath: import_node_path12.default.join(projectDir, ".sail", "agent.log"),
44379
+ logPath: import_node_path14.default.join(projectDir, ".sail", "agent.log"),
44291
44380
  interval: opts.interval != null ? Number(opts.interval) : void 0,
44292
44381
  chain: opts.chain != null ? Number(opts.chain) : void 0,
44293
44382
  restartSec: 30
44294
44383
  };
44295
44384
  }
44296
44385
  function plistPath(projectName) {
44297
- return import_node_path12.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
44386
+ return import_node_path14.default.join(import_node_os.default.homedir(), "Library", "LaunchAgents", `${launchdLabel(projectName)}.plist`);
44298
44387
  }
44299
44388
  function systemdPath(projectName) {
44300
- return import_node_path12.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
44389
+ return import_node_path14.default.join(import_node_os.default.homedir(), ".config", "systemd", "user", systemdUnitName(projectName));
44301
44390
  }
44302
44391
  async function serviceInstall(opts = {}) {
44303
44392
  const cfg = buildConfig(opts);
@@ -44321,12 +44410,12 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
44321
44410
  console.warn(`\u26A0 ${msg}
44322
44411
  Proceeding because --force was given.`);
44323
44412
  }
44324
- import_node_fs16.default.mkdirSync(import_node_path12.default.dirname(cfg.logPath), { recursive: true });
44413
+ import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(cfg.logPath), { recursive: true });
44325
44414
  if (platform === "darwin") {
44326
44415
  const plist = buildLaunchdPlist(cfg);
44327
44416
  const dest = plistPath(cfg.projectName);
44328
- import_node_fs16.default.mkdirSync(import_node_path12.default.dirname(dest), { recursive: true });
44329
- import_node_fs16.default.writeFileSync(dest, plist);
44417
+ import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
44418
+ import_node_fs18.default.writeFileSync(dest, plist);
44330
44419
  const uid2 = process.getuid?.() ?? 0;
44331
44420
  try {
44332
44421
  try {
@@ -44345,8 +44434,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
44345
44434
  if (platform === "linux") {
44346
44435
  const unit = buildSystemdUnit(cfg);
44347
44436
  const dest = systemdPath(cfg.projectName);
44348
- import_node_fs16.default.mkdirSync(import_node_path12.default.dirname(dest), { recursive: true });
44349
- import_node_fs16.default.writeFileSync(dest, unit);
44437
+ import_node_fs18.default.mkdirSync(import_node_path14.default.dirname(dest), { recursive: true });
44438
+ import_node_fs18.default.writeFileSync(dest, unit);
44350
44439
  try {
44351
44440
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "inherit" });
44352
44441
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "enable", "--now", systemdUnitName(cfg.projectName)], {
@@ -44360,8 +44449,8 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
44360
44449
  }
44361
44450
  if (platform === "win32") {
44362
44451
  const xml = buildWindowsTaskXml(cfg);
44363
- const dest = import_node_path12.default.join(import_node_os.default.tmpdir(), `${windowsTaskName(cfg.projectName)}.xml`);
44364
- import_node_fs16.default.writeFileSync(dest, xml, "utf-8");
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");
44365
44454
  try {
44366
44455
  (0, import_node_child_process2.execFileSync)(
44367
44456
  "schtasks",
@@ -44372,7 +44461,7 @@ Run "sailor keys generate" (and save the passphrase) or set it in .sail/.env.loc
44372
44461
  throw new Error(`schtasks /Create failed: ${err.message}`);
44373
44462
  } finally {
44374
44463
  try {
44375
- import_node_fs16.default.rmSync(dest, { force: true });
44464
+ import_node_fs18.default.rmSync(dest, { force: true });
44376
44465
  } catch {
44377
44466
  }
44378
44467
  }
@@ -44398,14 +44487,14 @@ function okInstall(cfg, unit) {
44398
44487
  );
44399
44488
  }
44400
44489
  async function serviceStatus(opts = {}) {
44401
- const projectName = sanitizeName(import_node_path12.default.basename(resolveProjectDir(opts.project)));
44490
+ const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
44402
44491
  const platform = process.platform;
44403
44492
  let installed = false;
44404
44493
  let running = false;
44405
44494
  let detail = "";
44406
44495
  try {
44407
44496
  if (platform === "darwin") {
44408
- installed = import_node_fs16.default.existsSync(plistPath(projectName));
44497
+ installed = import_node_fs18.default.existsSync(plistPath(projectName));
44409
44498
  const uid2 = process.getuid?.() ?? 0;
44410
44499
  try {
44411
44500
  detail = (0, import_node_child_process2.execFileSync)("launchctl", ["print", `gui/${uid2}/${launchdLabel(projectName)}`], {
@@ -44417,7 +44506,7 @@ async function serviceStatus(opts = {}) {
44417
44506
  } catch {
44418
44507
  }
44419
44508
  } else if (platform === "linux") {
44420
- installed = import_node_fs16.default.existsSync(systemdPath(projectName));
44509
+ installed = import_node_fs18.default.existsSync(systemdPath(projectName));
44421
44510
  try {
44422
44511
  detail = (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "is-active", systemdUnitName(projectName)], {
44423
44512
  encoding: "utf-8",
@@ -44453,7 +44542,7 @@ async function serviceStatus(opts = {}) {
44453
44542
  );
44454
44543
  }
44455
44544
  async function serviceStop(opts = {}) {
44456
- const projectName = sanitizeName(import_node_path12.default.basename(resolveProjectDir(opts.project)));
44545
+ const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
44457
44546
  const platform = process.platform;
44458
44547
  if (platform === "darwin") {
44459
44548
  const uid2 = process.getuid?.() ?? 0;
@@ -44472,7 +44561,7 @@ async function serviceStop(opts = {}) {
44472
44561
  });
44473
44562
  }
44474
44563
  async function serviceUninstall(opts = {}) {
44475
- const projectName = sanitizeName(import_node_path12.default.basename(resolveProjectDir(opts.project)));
44564
+ const projectName = sanitizeName(import_node_path14.default.basename(resolveProjectDir(opts.project)));
44476
44565
  const platform = process.platform;
44477
44566
  if (platform === "darwin") {
44478
44567
  const uid2 = process.getuid?.() ?? 0;
@@ -44482,7 +44571,7 @@ async function serviceUninstall(opts = {}) {
44482
44571
  });
44483
44572
  } catch {
44484
44573
  }
44485
- import_node_fs16.default.rmSync(plistPath(projectName), { force: true });
44574
+ import_node_fs18.default.rmSync(plistPath(projectName), { force: true });
44486
44575
  } else if (platform === "linux") {
44487
44576
  try {
44488
44577
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "disable", "--now", systemdUnitName(projectName)], {
@@ -44490,7 +44579,7 @@ async function serviceUninstall(opts = {}) {
44490
44579
  });
44491
44580
  } catch {
44492
44581
  }
44493
- import_node_fs16.default.rmSync(systemdPath(projectName), { force: true });
44582
+ import_node_fs18.default.rmSync(systemdPath(projectName), { force: true });
44494
44583
  try {
44495
44584
  (0, import_node_child_process2.execFileSync)("systemctl", ["--user", "daemon-reload"], { stdio: "ignore" });
44496
44585
  } catch {
@@ -44508,21 +44597,21 @@ async function serviceUninstall(opts = {}) {
44508
44597
  }
44509
44598
  async function serviceLogs(opts = {}) {
44510
44599
  const projectDir = resolveProjectDir(opts.project);
44511
- const logPath = import_node_path12.default.join(projectDir, ".sail", "agent.log");
44512
- if (!import_node_fs16.default.existsSync(logPath)) {
44600
+ const logPath = import_node_path14.default.join(projectDir, ".sail", "agent.log");
44601
+ if (!import_node_fs18.default.existsSync(logPath)) {
44513
44602
  console.log(`No log yet at ${logPath}. The service writes here once it runs.`);
44514
44603
  return;
44515
44604
  }
44516
44605
  if (opts.follow) {
44517
44606
  if (process.platform === "win32") {
44518
- console.log(import_node_fs16.default.readFileSync(logPath, "utf-8"));
44607
+ console.log(import_node_fs18.default.readFileSync(logPath, "utf-8"));
44519
44608
  console.log("(follow mode is not supported on Windows here \u2014 re-run to refresh)");
44520
44609
  return;
44521
44610
  }
44522
44611
  (0, import_node_child_process2.execFileSync)("tail", ["-f", logPath], { stdio: "inherit" });
44523
44612
  return;
44524
44613
  }
44525
- const lines = import_node_fs16.default.readFileSync(logPath, "utf-8").split("\n");
44614
+ const lines = import_node_fs18.default.readFileSync(logPath, "utf-8").split("\n");
44526
44615
  console.log(lines.slice(-200).join("\n"));
44527
44616
  }
44528
44617
 
@@ -44676,14 +44765,14 @@ async function sessionResume() {
44676
44765
  }
44677
44766
 
44678
44767
  // src/commands/station.ts
44679
- var import_node_fs17 = require("node:fs");
44680
- var import_node_path13 = require("node:path");
44681
- var RUNTIME_SERVER_FILE2 = (0, import_node_path13.join)(".sail", "runtime", "server.json");
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");
44682
44771
  function readState(projectRoot) {
44683
- const file = (0, import_node_path13.join)(projectRoot, RUNTIME_SERVER_FILE2);
44684
- if (!(0, import_node_fs17.existsSync)(file)) return null;
44772
+ const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
44773
+ if (!(0, import_node_fs19.existsSync)(file)) return null;
44685
44774
  try {
44686
- return JSON.parse((0, import_node_fs17.readFileSync)(file, "utf8"));
44775
+ return JSON.parse((0, import_node_fs19.readFileSync)(file, "utf8"));
44687
44776
  } catch {
44688
44777
  return null;
44689
44778
  }
@@ -44755,9 +44844,9 @@ async function stationStop(options) {
44755
44844
  }
44756
44845
  const daemon = await discoverDaemon(projectRoot);
44757
44846
  if (!daemon) {
44758
- const file = (0, import_node_path13.join)(projectRoot, RUNTIME_SERVER_FILE2);
44847
+ const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
44759
44848
  try {
44760
- if ((0, import_node_fs17.existsSync)(file)) (0, import_node_fs17.rmSync)(file);
44849
+ if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
44761
44850
  } catch {
44762
44851
  }
44763
44852
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -44773,9 +44862,9 @@ async function stationStop(options) {
44773
44862
  pid: state.pid
44774
44863
  });
44775
44864
  } catch {
44776
- const file = (0, import_node_path13.join)(projectRoot, RUNTIME_SERVER_FILE2);
44865
+ const file = (0, import_node_path15.join)(projectRoot, RUNTIME_SERVER_FILE2);
44777
44866
  try {
44778
- if ((0, import_node_fs17.existsSync)(file)) (0, import_node_fs17.rmSync)(file);
44867
+ if ((0, import_node_fs19.existsSync)(file)) (0, import_node_fs19.rmSync)(file);
44779
44868
  } catch {
44780
44869
  }
44781
44870
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -44830,15 +44919,15 @@ async function status() {
44830
44919
 
44831
44920
  // src/commands/ui.ts
44832
44921
  var import_node_child_process4 = require("node:child_process");
44833
- var import_node_fs18 = __toESM(require("node:fs"), 1);
44922
+ var import_node_fs20 = __toESM(require("node:fs"), 1);
44834
44923
  var import_node_net2 = __toESM(require("node:net"), 1);
44835
- var import_node_path14 = __toESM(require("node:path"), 1);
44836
- var UI_STATE_FILE = import_node_path14.default.join(".sail", "runtime", "ui.json");
44924
+ var import_node_path16 = __toESM(require("node:path"), 1);
44925
+ var UI_STATE_FILE = import_node_path16.default.join(".sail", "runtime", "ui.json");
44837
44926
  function readState2(projectRoot) {
44838
- const file = import_node_path14.default.join(projectRoot, UI_STATE_FILE);
44839
- if (!import_node_fs18.default.existsSync(file)) return null;
44927
+ const file = import_node_path16.default.join(projectRoot, UI_STATE_FILE);
44928
+ if (!import_node_fs20.default.existsSync(file)) return null;
44840
44929
  try {
44841
- return JSON.parse(import_node_fs18.default.readFileSync(file, "utf-8"));
44930
+ return JSON.parse(import_node_fs20.default.readFileSync(file, "utf-8"));
44842
44931
  } catch {
44843
44932
  return null;
44844
44933
  }
@@ -44879,15 +44968,15 @@ function waitForPort(port, timeoutMs) {
44879
44968
  }
44880
44969
  async function uiCommand() {
44881
44970
  const distDir = cliDistDir();
44882
- const uiDistDir = import_node_path14.default.join(packageRoot(), "packages", "ui", "dist");
44883
- const serverBundle = import_node_path14.default.resolve(distDir, "server.cjs");
44971
+ const uiDistDir = import_node_path16.default.join(packageRoot(), "packages", "ui", "dist");
44972
+ const serverBundle = import_node_path16.default.resolve(distDir, "server.cjs");
44884
44973
  const projectRoot = process.cwd();
44885
- const sailDir2 = import_node_path14.default.join(projectRoot, ".sail");
44974
+ const sailDir2 = import_node_path16.default.join(projectRoot, ".sail");
44886
44975
  const port = await findFreePort(projectPort(projectRoot));
44887
- if (!import_node_fs18.default.existsSync(serverBundle)) {
44976
+ if (!import_node_fs20.default.existsSync(serverBundle)) {
44888
44977
  throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
44889
44978
  }
44890
- if (!import_node_fs18.default.existsSync(import_node_path14.default.join(uiDistDir, "index.html"))) {
44979
+ if (!import_node_fs20.default.existsSync(import_node_path16.default.join(uiDistDir, "index.html"))) {
44891
44980
  throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
44892
44981
  }
44893
44982
  const existing = readState2(projectRoot);
@@ -44895,17 +44984,17 @@ async function uiCommand() {
44895
44984
  console.log(`Sailor UI is already running (pid ${existing.pid}) at http://localhost:${existing.port}`);
44896
44985
  return;
44897
44986
  }
44898
- const runtimeDir = import_node_path14.default.join(projectRoot, ".sail", "runtime");
44899
- import_node_fs18.default.mkdirSync(runtimeDir, { recursive: true });
44900
- const logFile = import_node_path14.default.join(runtimeDir, "ui.log");
44901
- const logFd = import_node_fs18.default.openSync(logFile, "a");
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");
44902
44991
  const child = (0, import_node_child_process4.spawn)(process.execPath, [serverBundle], {
44903
44992
  detached: true,
44904
44993
  stdio: ["ignore", logFd, logFd],
44905
44994
  env: { ...process.env, SAIL_DIR: sailDir2, SERVE_DIST: "1", PORT: String(port), SAILOR_UI_DIST: uiDistDir }
44906
44995
  });
44907
44996
  child.unref();
44908
- import_node_fs18.default.closeSync(logFd);
44997
+ import_node_fs20.default.closeSync(logFd);
44909
44998
  const READY_TIMEOUT_MS = 1e4;
44910
44999
  const deadline = Date.now() + READY_TIMEOUT_MS;
44911
45000
  let ready = false;
@@ -44919,18 +45008,18 @@ async function uiCommand() {
44919
45008
  if (!ready) {
44920
45009
  let tail = "";
44921
45010
  try {
44922
- tail = import_node_fs18.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
45011
+ tail = import_node_fs20.default.readFileSync(logFile, "utf-8").split("\n").filter(Boolean).slice(-15).join("\n");
44923
45012
  } catch {
44924
45013
  }
44925
45014
  throw new Error(
44926
45015
  `Sailor UI failed to start within ${READY_TIMEOUT_MS / 1e3}s on port ${port}.` + (tail ? `
44927
45016
 
44928
45017
  Server output:
44929
- ${tail}` : ` See ${import_node_path14.default.relative(projectRoot, logFile)}.`)
45018
+ ${tail}` : ` See ${import_node_path16.default.relative(projectRoot, logFile)}.`)
44930
45019
  );
44931
45020
  }
44932
- import_node_fs18.default.writeFileSync(
44933
- import_node_path14.default.join(projectRoot, UI_STATE_FILE),
45021
+ import_node_fs20.default.writeFileSync(
45022
+ import_node_path16.default.join(projectRoot, UI_STATE_FILE),
44934
45023
  JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
44935
45024
  );
44936
45025
  console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
@@ -44941,7 +45030,7 @@ function uiStatus() {
44941
45030
  if (state && isAlive(state.pid)) {
44942
45031
  console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
44943
45032
  } else {
44944
- if (state) import_node_fs18.default.rmSync(import_node_path14.default.join(process.cwd(), UI_STATE_FILE), { force: true });
45033
+ if (state) import_node_fs20.default.rmSync(import_node_path16.default.join(process.cwd(), UI_STATE_FILE), { force: true });
44945
45034
  console.log("\u25CB Sailor UI is not running");
44946
45035
  }
44947
45036
  }
@@ -44953,19 +45042,19 @@ function uiStop() {
44953
45042
  return;
44954
45043
  }
44955
45044
  if (!isAlive(state.pid)) {
44956
- import_node_fs18.default.rmSync(import_node_path14.default.join(projectRoot, UI_STATE_FILE), { force: true });
45045
+ import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
44957
45046
  console.log("Sailor UI is not running (stale state file removed).");
44958
45047
  return;
44959
45048
  }
44960
45049
  process.kill(state.pid, "SIGTERM");
44961
- import_node_fs18.default.rmSync(import_node_path14.default.join(projectRoot, UI_STATE_FILE), { force: true });
45050
+ import_node_fs20.default.rmSync(import_node_path16.default.join(projectRoot, UI_STATE_FILE), { force: true });
44962
45051
  console.log(`Stopped Sailor UI (pid ${state.pid}).`);
44963
45052
  }
44964
45053
 
44965
45054
  // src/index.ts
44966
45055
  function cliVersion() {
44967
45056
  try {
44968
- const pkg = JSON.parse((0, import_node_fs19.readFileSync)((0, import_node_path15.join)(packageRoot(), "package.json"), "utf-8"));
45057
+ const pkg = JSON.parse((0, import_node_fs21.readFileSync)((0, import_node_path17.join)(packageRoot(), "package.json"), "utf-8"));
44969
45058
  return pkg.version ?? "0.0.0";
44970
45059
  } catch {
44971
45060
  return "0.0.0";
@@ -45007,6 +45096,7 @@ program2.command("init [dir]").description("Scaffold a new Sail agent into the c
45007
45096
  }
45008
45097
  }
45009
45098
  );
45099
+ program2.command("update").description("Re-sync agent tooling files (skills, AGENTS.md, Dockerfile) from the latest template").action(action(updateCommand));
45010
45100
  var ui = program2.command("ui").description("Manage the local Sailor dashboard");
45011
45101
  ui.command("start").description("Start the dashboard at localhost:3333 (default)").action(action(uiCommand));
45012
45102
  ui.command("stop").description("Stop the running dashboard").action(() => uiStop());