@slayzone/cli 0.36.0-beta.12 → 0.36.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/slay.js +455 -491
  2. package/package.json +1 -1
package/dist/slay.js CHANGED
@@ -28443,9 +28443,6 @@ var {
28443
28443
  // ../../shared/platform/src/hub-discovery.ts
28444
28444
  var import_node_http = __toESM(require("node:http"), 1);
28445
28445
 
28446
- // ../../shared/platform/src/paths.ts
28447
- var import_node_path = require("node:path");
28448
-
28449
28446
  // ../../shared/platform/src/dirs.ts
28450
28447
  var import_path = __toESM(require("path"), 1);
28451
28448
  var import_os = __toESM(require("os"), 1);
@@ -28495,8 +28492,13 @@ function parseHubAddress(addr) {
28495
28492
 
28496
28493
  // ../../shared/platform/src/paths.ts
28497
28494
  function getStorageDir() {
28498
- return (0, import_node_path.join)(getSlayzoneHomeDir(), "storage");
28495
+ return getSlayzoneHomeDir();
28499
28496
  }
28497
+ var SIDECAR_FIXED_PORT = {
28498
+ prod: 51100,
28499
+ dev: 51101,
28500
+ test: 51102
28501
+ };
28500
28502
  var HUB_PORT_BLOCK = { start: 51100, end: 51199 };
28501
28503
 
28502
28504
  // ../../shared/platform/src/hub-discovery.ts
@@ -28605,9 +28607,7 @@ async function findHub(nameOrPort, opts = {}) {
28605
28607
  var import_fs2 = __toESM(require("fs"), 1);
28606
28608
  var import_path3 = __toESM(require("path"), 1);
28607
28609
 
28608
- // src/db.ts
28609
- var import_node_sqlite = require("node:sqlite");
28610
- var import_node_http2 = __toESM(require("node:http"), 1);
28610
+ // src/cli-state.ts
28611
28611
  var import_fs = __toESM(require("fs"), 1);
28612
28612
  var import_path2 = __toESM(require("path"), 1);
28613
28613
  var import_os2 = __toESM(require("os"), 1);
@@ -28617,18 +28617,6 @@ var import_child_process = require("child_process");
28617
28617
  var import_util = require("util");
28618
28618
  var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
28619
28619
 
28620
- // ../../shared/platform/src/db.ts
28621
- var DB_PRAGMAS = [
28622
- "journal_mode = WAL",
28623
- "foreign_keys = ON",
28624
- "synchronous = NORMAL",
28625
- "cache_size = -8000",
28626
- "busy_timeout = 5000"
28627
- ];
28628
- function getDbName(packaged) {
28629
- return packaged ? "slayzone.sqlite" : "slayzone.dev.sqlite";
28630
- }
28631
-
28632
28620
  // ../../shared/platform/src/slz-file-url.ts
28633
28621
  var SLZ_FILE_HOST = "app";
28634
28622
  var SLZ_FILE_PREFIX = `slz-file://${SLZ_FILE_HOST}`;
@@ -28636,182 +28624,34 @@ var SLZ_FILE_PREFIX = `slz-file://${SLZ_FILE_HOST}`;
28636
28624
  // ../../shared/platform/src/ipc-dedup.ts
28637
28625
  var UNCHANGED = Object.freeze({ __ipc_unchanged__: true });
28638
28626
 
28639
- // src/db-helpers.mts
28640
- function resolveProjectArg(opt) {
28641
- const val = opt ?? process.env.SLAYZONE_PROJECT_ID;
28642
- if (!val) {
28643
- console.error("No --project provided and $SLAYZONE_PROJECT_ID is not set.");
28644
- process.exit(1);
28645
- }
28646
- return val;
28647
- }
28648
-
28649
- // src/db.ts
28650
- function defaultDir() {
28651
- const dir = getStorageDir();
28652
- if (import_fs.default.existsSync(dir)) return dir;
28653
- if (process.platform === "linux") {
28654
- const configHome = process.env.XDG_CONFIG_HOME || import_path2.default.join(import_os2.default.homedir(), ".config");
28655
- const legacyDir = import_path2.default.join(configHome, "slayzone");
28656
- if (import_fs.default.existsSync(legacyDir)) return legacyDir;
28657
- }
28658
- return dir;
28659
- }
28660
- function getDbPath(dev) {
28661
- return import_path2.default.join(defaultDir(), getDbName(!dev));
28662
- }
28663
- function loopbackHubPort() {
28664
- const parsed = parseHubAddress(process.env.SLAYZONE_HUB_ADDRESS);
28665
- if (!parsed?.port) return null;
28666
- return LOOPBACK_HOSTS.has(parsed.host) ? parsed.port : null;
28667
- }
28668
- function hasLocalDatabase() {
28669
- return import_fs.default.existsSync(getDbPath(process.env.SLAYZONE_DEV === "1"));
28670
- }
28671
- function getServerPort() {
28672
- const envPort = loopbackHubPort();
28673
- if (envPort) return envPort;
28674
- try {
28675
- const db = openDb();
28676
- const row = db.query(
28677
- `SELECT value FROM settings WHERE key = 'server_port' LIMIT 1`
28678
- );
28679
- db.close();
28680
- const port = parseInt(row[0]?.value ?? "", 10);
28681
- return port > 0 && port <= 65535 ? port : null;
28682
- } catch {
28683
- return null;
28684
- }
28685
- }
28686
- function getAlternateServerPort() {
28687
- if (loopbackHubPort()) return null;
28688
- const dev = process.env.SLAYZONE_DEV === "1";
28689
- const altPath = getDbPath(!dev);
28690
- if (!import_fs.default.existsSync(altPath)) return null;
28691
- try {
28692
- const altDb = new import_node_sqlite.DatabaseSync(altPath);
28693
- const row = altDb.prepare(`SELECT value FROM settings WHERE key = 'server_port' LIMIT 1`).get();
28694
- altDb.close();
28695
- const port = parseInt(row?.value ?? "", 10);
28696
- return port > 0 && port <= 65535 ? port : null;
28697
- } catch {
28698
- return null;
28699
- }
28700
- }
28701
- function postJson(port, path9, timeoutMs = 3e3) {
28702
- return new Promise((resolve) => {
28703
- const req = import_node_http2.default.request({ hostname: "127.0.0.1", port, path: path9, method: "POST" }, (res) => {
28704
- res.resume();
28705
- res.on("end", () => {
28706
- const code = res.statusCode ?? 0;
28707
- resolve(code >= 200 && code < 300);
28708
- });
28709
- });
28710
- req.on("error", () => resolve(false));
28711
- req.setTimeout(timeoutMs, () => {
28712
- req.destroy();
28713
- resolve(false);
28714
- });
28715
- req.end();
28716
- });
28717
- }
28718
- function probePort(port) {
28719
- return postJson(port, "/api/notify", 1e3);
28720
- }
28721
- async function postHubNotify(hub, timeoutMs = 3e3) {
28722
- try {
28723
- const headers = hub.token ? { Authorization: `Bearer ${hub.token}` } : {};
28724
- const res = await fetch(`${hub.baseUrl}/api/notify`, {
28725
- method: "POST",
28726
- headers,
28727
- signal: AbortSignal.timeout(timeoutMs)
28728
- });
28729
- return res.ok;
28730
- } catch {
28731
- return false;
28732
- }
28733
- }
28734
- async function notifyApp() {
28735
- const hub = resolveHubTarget();
28736
- if (hub) {
28737
- const ok = await postHubNotify(hub);
28738
- if (!ok) {
28739
- console.error(
28740
- "Warning: hub notify failed (POST /api/notify) \u2014 hub may be stale or unreachable"
28741
- );
28742
- }
28743
- return;
28744
- }
28745
- const port = getServerPort();
28746
- if (port) {
28747
- const ok = await postJson(port, "/api/notify");
28748
- if (!ok) {
28749
- console.error(
28750
- "Warning: app notify failed (POST /api/notify) \u2014 app may be stale or unreachable"
28751
- );
28752
- }
28753
- return;
28754
- }
28755
- const altPort = getAlternateServerPort();
28756
- if (altPort && await probePort(altPort)) {
28757
- const dev = process.env.SLAYZONE_DEV === "1";
28758
- const hint = dev ? "without --dev" : "with --dev";
28759
- console.error(
28760
- `Warning: SlayZone app is running ${hint}. Changes were saved but the app was not notified.`
28761
- );
28762
- console.error(` Re-run ${dev ? "without --dev" : "with --dev"} to target the same database.`);
28763
- }
28627
+ // src/cli-state.ts
28628
+ function getCliStateDir() {
28629
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? import_os2.default.homedir();
28630
+ return import_path2.default.join(home, ".slayzone", "cli");
28764
28631
  }
28765
- function getArtifactsDir() {
28766
- const dir = defaultDir();
28767
- return import_path2.default.join(dir, "artifacts");
28632
+ function getHubTargetPath() {
28633
+ const name = process.env.SLAYZONE_DEV === "1" ? "hub-target.dev.json" : "hub-target.json";
28634
+ return import_path2.default.join(getCliStateDir(), name);
28768
28635
  }
28769
- function openDb() {
28770
- const dev = process.env.SLAYZONE_DEV === "1";
28771
- const dbPath = getDbPath(dev);
28772
- if (!import_fs.default.existsSync(dbPath)) {
28773
- console.error(`Database not found: ${dbPath}`);
28774
- const altPath = getDbPath(!dev);
28775
- if (import_fs.default.existsSync(altPath)) {
28776
- const hint = dev ? "without --dev" : "with --dev";
28777
- console.error(`Found other database at: ${altPath}`);
28778
- console.error(`Re-run ${hint} to target that database.`);
28779
- } else {
28780
- console.error("Make sure SlayZone has been launched at least once.");
28781
- }
28782
- process.exit(1);
28783
- }
28784
- const db = new import_node_sqlite.DatabaseSync(dbPath);
28785
- for (const pragma of DB_PRAGMAS) {
28786
- db.exec(`PRAGMA ${pragma}`);
28787
- }
28788
- return {
28789
- query(sql, params = {}) {
28790
- return db.prepare(sql).all(params);
28791
- },
28792
- run(sql, params = {}) {
28793
- db.prepare(sql).run(params);
28794
- },
28795
- close() {
28796
- db.close();
28797
- },
28798
- raw() {
28799
- return db;
28800
- }
28801
- };
28636
+ function getServiceRuntimeDir(kind) {
28637
+ return import_path2.default.join(getCliStateDir(), `${kind}-runtime`);
28802
28638
  }
28803
- function getDataDir() {
28804
- return defaultDir();
28639
+ function legacyHubTargetPaths() {
28640
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? import_os2.default.homedir();
28641
+ const candidates = [
28642
+ import_path2.default.join(getStorageDir(), "cli-hub-target.json"),
28643
+ import_path2.default.join(home, ".slayzone", "cli-hub-target.json")
28644
+ ];
28645
+ return candidates.filter((p2, i) => candidates.indexOf(p2) === i && import_fs.default.existsSync(p2));
28805
28646
  }
28806
28647
 
28807
28648
  // src/hub-config.ts
28808
- var HUB_CONFIG_FILENAME = "hub.json";
28809
28649
  var hubOverride = null;
28810
28650
  function setHubOverride(target) {
28811
28651
  hubOverride = target;
28812
28652
  }
28813
28653
  function getHubConfigPath() {
28814
- return import_path3.default.join(getDataDir(), HUB_CONFIG_FILENAME);
28654
+ return getHubTargetPath();
28815
28655
  }
28816
28656
  function normalizeHubUrl(raw) {
28817
28657
  const trimmed = raw.trim();
@@ -28825,8 +28665,13 @@ function normalizeHubUrl(raw) {
28825
28665
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
28826
28666
  return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
28827
28667
  }
28668
+ function hubFilePathForRead() {
28669
+ const current = getHubConfigPath();
28670
+ if (import_fs2.default.existsSync(current)) return current;
28671
+ return legacyHubTargetPaths()[0] ?? current;
28672
+ }
28828
28673
  function readHubFile() {
28829
- const configPath = getHubConfigPath();
28674
+ const configPath = hubFilePathForRead();
28830
28675
  let rawText;
28831
28676
  try {
28832
28677
  rawText = import_fs2.default.readFileSync(configPath, "utf-8");
@@ -28888,20 +28733,43 @@ function writeHubConfig(url, token) {
28888
28733
  return configPath;
28889
28734
  }
28890
28735
  function removeHubConfig() {
28736
+ let removed = false;
28737
+ for (const target of [getHubConfigPath(), ...legacyHubTargetPaths()]) {
28738
+ try {
28739
+ import_fs2.default.unlinkSync(target);
28740
+ removed = true;
28741
+ } catch (e) {
28742
+ if (e.code !== "ENOENT") throw e;
28743
+ }
28744
+ }
28745
+ return removed;
28746
+ }
28747
+
28748
+ // src/local-hub.ts
28749
+ function fixedPortForChannel() {
28750
+ return process.env.SLAYZONE_DEV === "1" ? SIDECAR_FIXED_PORT.dev : SIDECAR_FIXED_PORT.prod;
28751
+ }
28752
+ async function probeFixedPort() {
28753
+ const port = fixedPortForChannel();
28754
+ const hub = await findHub(String(port));
28755
+ return hub ? hub.port : null;
28756
+ }
28757
+ async function isCoLocatedHub() {
28758
+ const configured = resolveHubTarget();
28759
+ if (!configured) return true;
28891
28760
  try {
28892
- import_fs2.default.unlinkSync(getHubConfigPath());
28893
- return true;
28894
- } catch (e) {
28895
- if (e.code === "ENOENT") return false;
28896
- throw e;
28761
+ const host = new URL(configured.baseUrl).hostname.replace(/^\[|\]$/g, "");
28762
+ return LOOPBACK_HOSTS.has(host);
28763
+ } catch {
28764
+ return false;
28897
28765
  }
28898
28766
  }
28899
28767
 
28900
28768
  // src/api.ts
28901
- function resolveTarget() {
28769
+ async function resolveTarget() {
28902
28770
  const hub = resolveHubTarget();
28903
28771
  if (hub) return { baseUrl: hub.baseUrl, token: hub.token, hub: true };
28904
- const port = getServerPort();
28772
+ const port = await probeFixedPort();
28905
28773
  if (!port) {
28906
28774
  console.error("SlayZone server port not found. Is the app running?");
28907
28775
  process.exit(1);
@@ -28923,7 +28791,7 @@ function connectError(target) {
28923
28791
  process.exit(1);
28924
28792
  }
28925
28793
  async function request(path9, init) {
28926
- const target = resolveTarget();
28794
+ const target = await resolveTarget();
28927
28795
  let res;
28928
28796
  try {
28929
28797
  res = await fetch(`${target.baseUrl}${path9}`, withAuth(init, target.token));
@@ -28961,7 +28829,7 @@ function apiDelete(path9, body) {
28961
28829
  });
28962
28830
  }
28963
28831
  async function apiFetch(path9, init) {
28964
- const target = resolveTarget();
28832
+ const target = await resolveTarget();
28965
28833
  try {
28966
28834
  return await fetch(`${target.baseUrl}${path9}`, withAuth(init, target.token));
28967
28835
  } catch {
@@ -28969,7 +28837,7 @@ async function apiFetch(path9, init) {
28969
28837
  }
28970
28838
  }
28971
28839
  async function apiGetStream(path9, passThroughCodes = []) {
28972
- const target = resolveTarget();
28840
+ const target = await resolveTarget();
28973
28841
  let res;
28974
28842
  try {
28975
28843
  res = await fetch(`${target.baseUrl}${path9}`, withAuth(void 0, target.token));
@@ -28992,7 +28860,7 @@ async function apiGetStream(path9, passThroughCodes = []) {
28992
28860
  return { body: res.body };
28993
28861
  }
28994
28862
  async function requestStream(method, path9, body) {
28995
- const target = resolveTarget();
28863
+ const target = await resolveTarget();
28996
28864
  let res;
28997
28865
  try {
28998
28866
  res = await fetch(`${target.baseUrl}${path9}`, {
@@ -29035,171 +28903,6 @@ function failFromText(body, status) {
29035
28903
  process.exit(1);
29036
28904
  }
29037
28905
 
29038
- // ../../domains/terminal/src/shared/types.ts
29039
- var BuiltinTerminalMode = {
29040
- ClaudeCode: "claude-code",
29041
- ClaudeChat: "claude-chat",
29042
- Codex: "codex",
29043
- CodexChat: "codex-chat",
29044
- Gemini: "gemini",
29045
- Antigravity: "antigravity",
29046
- CursorAgent: "cursor-agent",
29047
- OpenCode: "opencode",
29048
- QwenCode: "qwen-code",
29049
- Copilot: "copilot"
29050
- };
29051
- var DEFAULT_TERMINAL_MODES = [
29052
- {
29053
- id: BuiltinTerminalMode.ClaudeCode,
29054
- label: "Claude",
29055
- type: "claude-code",
29056
- initialCommand: "claude --session-id {id} {flags}",
29057
- resumeCommand: "claude --resume {id} {flags}",
29058
- headlessCommand: "claude -p {prompt} {flags}",
29059
- defaultFlags: "--allow-dangerously-skip-permissions",
29060
- enabled: true,
29061
- isBuiltin: true,
29062
- order: 0
29063
- },
29064
- {
29065
- id: BuiltinTerminalMode.ClaudeChat,
29066
- label: "Claude Chat",
29067
- type: "claude-code",
29068
- initialCommand: null,
29069
- resumeCommand: null,
29070
- headlessCommand: "claude -p {prompt} {flags}",
29071
- defaultFlags: "--allow-dangerously-skip-permissions",
29072
- enabled: true,
29073
- isBuiltin: true,
29074
- order: 0.5
29075
- },
29076
- {
29077
- id: BuiltinTerminalMode.Codex,
29078
- label: "Codex",
29079
- type: "codex",
29080
- initialCommand: "codex {flags}",
29081
- resumeCommand: "codex {flags} resume {id}",
29082
- headlessCommand: "codex exec {flags} {prompt}",
29083
- defaultFlags: "--sandbox workspace-write",
29084
- enabled: true,
29085
- isBuiltin: true,
29086
- order: 1
29087
- },
29088
- {
29089
- // Codex Chat — structured (non-PTY) chat backed by `codex app-server`'s
29090
- // JSON-RPC protocol. Coexists with the PTY `codex` mode above.
29091
- // `initialCommand`/`resumeCommand` are null: chat is spawned by the
29092
- // chat-transport-manager, not the PTY manager.
29093
- id: BuiltinTerminalMode.CodexChat,
29094
- label: "Codex Chat",
29095
- type: "codex",
29096
- initialCommand: null,
29097
- resumeCommand: null,
29098
- headlessCommand: "codex exec {flags} {prompt}",
29099
- defaultFlags: "",
29100
- enabled: true,
29101
- isBuiltin: true,
29102
- order: 1.5
29103
- },
29104
- {
29105
- id: BuiltinTerminalMode.Gemini,
29106
- label: "Gemini",
29107
- type: "gemini",
29108
- initialCommand: "gemini {flags}",
29109
- resumeCommand: "gemini --resume latest {flags}",
29110
- headlessCommand: "gemini -p {prompt} {flags}",
29111
- defaultFlags: "--yolo",
29112
- enabled: true,
29113
- isBuiltin: true,
29114
- order: 2
29115
- },
29116
- {
29117
- // Antigravity CLI — Google's successor to Gemini CLI (Gemini CLI stops
29118
- // serving free/Pro/Ultra requests 2026-06-18). Binary is `agy`. Commands
29119
- // confirmed against `agy --help` (v1.0.0):
29120
- // resume `agy --conversation=<id>` (short `-c` = most-recent)
29121
- // headless `-p` / `--print` (single non-interactive prompt)
29122
- // auto-approve `--dangerously-skip-permissions` (skip tool permission prompts)
29123
- //
29124
- // `resumeCommand` uses `{id}`: the SessionStart hook captures the CLI
29125
- // session_id into provider_config.antigravity (see agent-hook.ts
29126
- // `persistConversationId`), and `{id}` is only ever interpolated when a
29127
- // captured id exists — pty-manager picks `resumeCommand` solely when
29128
- // `resuming = !!existingConversationId`. No id → `initialCommand` (fresh
29129
- // session).
29130
- id: BuiltinTerminalMode.Antigravity,
29131
- label: "Antigravity",
29132
- type: "antigravity",
29133
- initialCommand: "agy {flags}",
29134
- resumeCommand: "agy --conversation={id} {flags}",
29135
- headlessCommand: "agy -p {prompt} {flags}",
29136
- defaultFlags: "--dangerously-skip-permissions",
29137
- enabled: true,
29138
- isBuiltin: true,
29139
- order: 2.5
29140
- },
29141
- {
29142
- id: BuiltinTerminalMode.CursorAgent,
29143
- label: "Cursor",
29144
- type: "cursor-agent",
29145
- initialCommand: "cursor-agent {flags}",
29146
- resumeCommand: "cursor-agent --resume {id} {flags}",
29147
- headlessCommand: "cursor-agent -p {prompt} {flags}",
29148
- defaultFlags: "--force",
29149
- enabled: true,
29150
- isBuiltin: true,
29151
- order: 3
29152
- },
29153
- {
29154
- id: BuiltinTerminalMode.OpenCode,
29155
- label: "OpenCode",
29156
- type: "opencode",
29157
- initialCommand: "opencode {flags}",
29158
- resumeCommand: "opencode --session {id} {flags}",
29159
- headlessCommand: "opencode run {flags} {prompt}",
29160
- defaultFlags: "",
29161
- enabled: true,
29162
- isBuiltin: true,
29163
- order: 4
29164
- },
29165
- {
29166
- id: BuiltinTerminalMode.QwenCode,
29167
- label: "Qwen",
29168
- type: "qwen-code",
29169
- initialCommand: "qwen --session-id {id} {flags}",
29170
- resumeCommand: "qwen --resume {id} {flags}",
29171
- headlessCommand: "qwen -p {prompt} {flags}",
29172
- defaultFlags: "--yolo",
29173
- enabled: true,
29174
- isBuiltin: true,
29175
- order: 6
29176
- },
29177
- {
29178
- id: BuiltinTerminalMode.Copilot,
29179
- label: "Copilot",
29180
- type: "copilot",
29181
- initialCommand: "copilot --resume={id} {flags}",
29182
- resumeCommand: "copilot --resume={id} {flags}",
29183
- headlessCommand: "copilot -p {prompt} {flags}",
29184
- defaultFlags: "--allow-all-tools",
29185
- enabled: true,
29186
- isBuiltin: true,
29187
- order: 7
29188
- },
29189
- {
29190
- id: "terminal",
29191
- label: "Terminal",
29192
- type: "terminal",
29193
- initialCommand: null,
29194
- resumeCommand: null,
29195
- headlessCommand: null,
29196
- defaultFlags: null,
29197
- enabled: true,
29198
- isBuiltin: true,
29199
- order: 8
29200
- }
29201
- ];
29202
-
29203
28906
  // src/commands/tasks/cli-author.ts
29204
28907
  function cliAuthor() {
29205
28908
  const agentId = process.env.SLAYZONE_AGENT_ID;
@@ -29412,6 +29115,88 @@ async function listAction(opts) {
29412
29115
  printTasks(data, blockedIds);
29413
29116
  }
29414
29117
 
29118
+ // src/db.ts
29119
+ var import_node_http2 = __toESM(require("node:http"), 1);
29120
+
29121
+ // src/db-helpers.mts
29122
+ function resolveProjectArg(opt) {
29123
+ const val = opt ?? process.env.SLAYZONE_PROJECT_ID;
29124
+ if (!val) {
29125
+ console.error("No --project provided and $SLAYZONE_PROJECT_ID is not set.");
29126
+ process.exit(1);
29127
+ }
29128
+ return val;
29129
+ }
29130
+
29131
+ // src/db.ts
29132
+ function postJson(port, path9, timeoutMs = 3e3) {
29133
+ return new Promise((resolve) => {
29134
+ const req = import_node_http2.default.request({ hostname: "127.0.0.1", port, path: path9, method: "POST" }, (res) => {
29135
+ res.resume();
29136
+ res.on("end", () => {
29137
+ const code = res.statusCode ?? 0;
29138
+ resolve(code >= 200 && code < 300);
29139
+ });
29140
+ });
29141
+ req.on("error", () => resolve(false));
29142
+ req.setTimeout(timeoutMs, () => {
29143
+ req.destroy();
29144
+ resolve(false);
29145
+ });
29146
+ req.end();
29147
+ });
29148
+ }
29149
+ async function probeAlternateChannel() {
29150
+ const dev = process.env.SLAYZONE_DEV === "1";
29151
+ const altPort = dev ? SIDECAR_FIXED_PORT.prod : SIDECAR_FIXED_PORT.dev;
29152
+ const hub = await findHub(String(altPort));
29153
+ return hub ? hub.port : null;
29154
+ }
29155
+ async function postHubNotify(hub, timeoutMs = 3e3) {
29156
+ try {
29157
+ const headers = hub.token ? { Authorization: `Bearer ${hub.token}` } : {};
29158
+ const res = await fetch(`${hub.baseUrl}/api/notify`, {
29159
+ method: "POST",
29160
+ headers,
29161
+ signal: AbortSignal.timeout(timeoutMs)
29162
+ });
29163
+ return res.ok;
29164
+ } catch {
29165
+ return false;
29166
+ }
29167
+ }
29168
+ async function notifyApp() {
29169
+ const hub = resolveHubTarget();
29170
+ if (hub) {
29171
+ const ok = await postHubNotify(hub);
29172
+ if (!ok) {
29173
+ console.error(
29174
+ "Warning: hub notify failed (POST /api/notify) \u2014 hub may be stale or unreachable"
29175
+ );
29176
+ }
29177
+ return;
29178
+ }
29179
+ const port = await probeFixedPort();
29180
+ if (port) {
29181
+ const ok = await postJson(port, "/api/notify");
29182
+ if (!ok) {
29183
+ console.error(
29184
+ "Warning: app notify failed (POST /api/notify) \u2014 app may be stale or unreachable"
29185
+ );
29186
+ }
29187
+ return;
29188
+ }
29189
+ const altPort = await probeAlternateChannel();
29190
+ if (altPort) {
29191
+ const dev = process.env.SLAYZONE_DEV === "1";
29192
+ const hint = dev ? "without --dev" : "with --dev";
29193
+ console.error(
29194
+ `Warning: SlayZone app is running ${hint}. Changes were saved but the app was not notified.`
29195
+ );
29196
+ console.error(` Re-run ${dev ? "without --dev" : "with --dev"} to target the same database.`);
29197
+ }
29198
+ }
29199
+
29415
29200
  // src/commands/tasks/create.ts
29416
29201
  async function createAction(title, opts) {
29417
29202
  const projectRef = resolveProjectArg(opts.project);
@@ -30094,27 +29879,6 @@ function getEffectiveRenderMode(title, override) {
30094
29879
  }
30095
29880
 
30096
29881
  // src/commands/tasks/artifacts.ts
30097
- function resolveArtifact(db, prefix) {
30098
- const rows = db.query(
30099
- `SELECT * FROM task_artifacts WHERE id LIKE :prefix || '%' LIMIT 2`,
30100
- { ":prefix": prefix }
30101
- );
30102
- if (rows.length === 0) {
30103
- console.error(`Artifact not found: "${prefix}"`);
30104
- process.exit(1);
30105
- }
30106
- if (rows.length > 1) {
30107
- console.error(
30108
- `Ambiguous artifact id "${prefix}". Matches: ${rows.map((r) => r.id.slice(0, 8)).join(", ")}`
30109
- );
30110
- process.exit(1);
30111
- }
30112
- return rows[0];
30113
- }
30114
- function artifactFilePath(artifactsDir, taskId, artifactId, title) {
30115
- const ext = getExtensionFromTitle(title) || ".txt";
30116
- return import_path6.default.join(artifactsDir, taskId, `${artifactId}${ext}`);
30117
- }
30118
29882
  function printArtifacts(artifacts, folders) {
30119
29883
  if (artifacts.length === 0) {
30120
29884
  console.log("No artifacts.");
@@ -30403,11 +30167,18 @@ function artifactsSubcommand() {
30403
30167
  console.log(`Deleted: ${artifact.id.slice(0, 8)} ${artifact.title}`);
30404
30168
  });
30405
30169
  cmd.command("path <artifactId>").description("Print artifact file path").action(async (artifactId) => {
30406
- const db = openDb();
30407
- const artifact = resolveArtifact(db, artifactId);
30408
- db.close();
30409
- const dir = getArtifactsDir();
30410
- process.stdout.write(artifactFilePath(dir, artifact.task_id, artifact.id, artifact.title));
30170
+ const { data } = await apiGet(
30171
+ `/api/artifacts/${encodeURIComponent(artifactId)}`
30172
+ );
30173
+ const local = await isCoLocatedHub();
30174
+ if (!local) {
30175
+ console.error(
30176
+ `Refusing to print a path for a hub on another machine \u2014 ${data.filePath} exists there, not here.
30177
+ Use \`slay tasks artifacts read ${artifactId}\` to get the content, or \`download\` to write it locally.`
30178
+ );
30179
+ process.exit(1);
30180
+ }
30181
+ process.stdout.write(data.filePath);
30411
30182
  });
30412
30183
  cmd.command("mkdir <name>").description("Create a folder").option("--task <id>", "Task ID (or $SLAYZONE_TASK_ID)").option("--parent <id>", "Parent folder ID").option("--json", "Output as JSON").action(async (name, opts) => {
30413
30184
  const taskRef = await resolveId(opts.task);
@@ -30739,7 +30510,7 @@ function tasksCommand() {
30739
30510
 
30740
30511
  // src/commands/projects.ts
30741
30512
  var import_node_fs = __toESM(require("node:fs"), 1);
30742
- var import_node_path2 = __toESM(require("node:path"), 1);
30513
+ var import_node_path = __toESM(require("node:path"), 1);
30743
30514
 
30744
30515
  // ../../shared/workflow/src/types.ts
30745
30516
  var WORKFLOW_CATEGORIES = [
@@ -30861,7 +30632,7 @@ function normalizeProjectPath(input) {
30861
30632
  if (!input) return null;
30862
30633
  const trimmed = input.trim();
30863
30634
  if (!trimmed) return null;
30864
- return import_node_path2.default.resolve(trimmed);
30635
+ return import_node_path.default.resolve(trimmed);
30865
30636
  }
30866
30637
  function ensureProjectPath(projectPath) {
30867
30638
  if (!projectPath) return false;
@@ -31085,6 +30856,7 @@ complete -c slay -n '__fish_use_subcommand' -a 'automations' -d 'Manage automati
31085
30856
  complete -c slay -n '__fish_use_subcommand' -a 'processes' -d 'Manage processes'
31086
30857
  complete -c slay -n '__fish_use_subcommand' -a 'hub' -d 'Run, list and target SlayZone hubs'
31087
30858
  complete -c slay -n '__fish_use_subcommand' -a 'runner' -d 'Run and manage SlayZone runners'
30859
+ complete -c slay -n '__fish_use_subcommand' -a 'update' -d 'Update globally npm-installed @slayzone packages'
31088
30860
  complete -c slay -n '__fish_use_subcommand' -a 'completions' -d 'Print shell completions'
31089
30861
  complete -c slay -n '__fish_seen_subcommand_from tasks' -a 'list create view done update archive delete open search subtasks subtask-add tag'
31090
30862
  complete -c slay -n '__fish_seen_subcommand_from processes' -a 'list logs kill follow'
@@ -31109,6 +30881,7 @@ _slay() {
31109
30881
  'processes:Manage processes'
31110
30882
  'hub:Run, list and target SlayZone hubs'
31111
30883
  'runner:Run and manage SlayZone runners'
30884
+ 'update:Update globally npm-installed @slayzone packages'
31112
30885
  'completions:Print shell completions'
31113
30886
  )
31114
30887
  local -a task_commands
@@ -31210,6 +30983,9 @@ _slay() {
31210
30983
  runner)
31211
30984
  _describe 'runner commands' runner_commands
31212
30985
  ;;
30986
+ update)
30987
+ _arguments '--check[Report available updates without installing]'
30988
+ ;;
31213
30989
  completions)
31214
30990
  _arguments ':shell:(fish zsh bash)'
31215
30991
  ;;
@@ -31227,7 +31003,7 @@ _slay_completions() {
31227
31003
  cur="\${COMP_WORDS[COMP_CWORD]}"
31228
31004
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
31229
31005
  if [ $COMP_CWORD -eq 1 ]; then
31230
- COMPREPLY=( $(compgen -W "tasks projects tags templates automations processes hub runner completions" -- "$cur") )
31006
+ COMPREPLY=( $(compgen -W "tasks projects tags templates automations processes hub runner update completions" -- "$cur") )
31231
31007
  elif [ $COMP_CWORD -eq 2 ]; then
31232
31008
  case "$prev" in
31233
31009
  tasks)
@@ -31254,6 +31030,9 @@ _slay_completions() {
31254
31030
  runner)
31255
31031
  COMPREPLY=( $(compgen -W "ls mint create start stop rm restart logs" -- "$cur") )
31256
31032
  ;;
31033
+ update)
31034
+ COMPREPLY=( $(compgen -W "--check" -- "$cur") )
31035
+ ;;
31257
31036
  completions)
31258
31037
  COMPREPLY=( $(compgen -W "fish zsh bash" -- "$cur") )
31259
31038
  ;;
@@ -31287,7 +31066,7 @@ function completionsCommand() {
31287
31066
 
31288
31067
  // src/commands/init.ts
31289
31068
  var import_node_fs2 = __toESM(require("node:fs"), 1);
31290
- var import_node_path3 = __toESM(require("node:path"), 1);
31069
+ var import_node_path2 = __toESM(require("node:path"), 1);
31291
31070
 
31292
31071
  // ../../domains/ai-config/src/shared/provider-registry.ts
31293
31072
  var PROVIDER_PATHS = {
@@ -34691,7 +34470,7 @@ function writeInstructionsToDisk(projectPath, providers) {
34691
34470
  }
34692
34471
  const written = [];
34693
34472
  for (const filename of filenames) {
34694
- const filePath = import_node_path3.default.join(projectPath, filename);
34473
+ const filePath = import_node_path2.default.join(projectPath, filename);
34695
34474
  const existing = import_node_fs2.default.existsSync(filePath) ? import_node_fs2.default.readFileSync(filePath, "utf-8") : "";
34696
34475
  const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
34697
34476
  import_node_fs2.default.appendFileSync(filePath, prefix + INSTRUCTIONS, "utf-8");
@@ -34704,8 +34483,8 @@ function writeSkillsToDisk(projectPath, providers, skills) {
34704
34483
  const mapping = PROVIDER_PATHS[provider];
34705
34484
  if (!mapping?.skillsDir) continue;
34706
34485
  for (const skill of skills) {
34707
- const filePath = import_node_path3.default.join(projectPath, mapping.skillsDir, skill.slug, "SKILL.md");
34708
- import_node_fs2.default.mkdirSync(import_node_path3.default.dirname(filePath), { recursive: true });
34486
+ const filePath = import_node_path2.default.join(projectPath, mapping.skillsDir, skill.slug, "SKILL.md");
34487
+ import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(filePath), { recursive: true });
34709
34488
  import_node_fs2.default.writeFileSync(filePath, skill.content, "utf-8");
34710
34489
  }
34711
34490
  }
@@ -35546,94 +35325,119 @@ function panelsCommand() {
35546
35325
  // src/commands/hub.ts
35547
35326
  var import_node_child_process3 = require("node:child_process");
35548
35327
  var import_node_fs7 = require("node:fs");
35549
- var import_node_path7 = require("node:path");
35328
+ var import_node_path6 = require("node:path");
35550
35329
 
35551
35330
  // ../../shared/platform/src/slayzone-config.ts
35552
35331
  var import_node_fs4 = require("node:fs");
35553
- var import_node_path4 = require("node:path");
35332
+ var import_node_path3 = require("node:path");
35554
35333
 
35555
35334
  // ../../shared/platform/src/runner-identity.ts
35556
35335
  var DEFAULT_LOCAL_RUNNER_NAME = "local-runner";
35557
35336
 
35558
35337
  // ../../shared/platform/src/slayzone-config.ts
35559
- function getSlayzoneConfigPath() {
35560
- return (0, import_node_path4.join)(getSlayzoneHomeDir(), "config.json");
35338
+ function getHubConfigFilePath() {
35339
+ return (0, import_node_path3.join)(getSlayzoneHomeDir(), "hub.config.json");
35340
+ }
35341
+ function getRunnerConfigFilePath() {
35342
+ return (0, import_node_path3.join)(getSlayzoneHomeDir(), "runner.config.json");
35561
35343
  }
35562
- function coerce(raw) {
35344
+ function legacyConfigFilePathFor(newFilePath) {
35345
+ return (0, import_node_path3.join)((0, import_node_path3.dirname)(newFilePath), "config.json");
35346
+ }
35347
+ function coerceHubConfig(raw) {
35563
35348
  const cfg = {};
35564
- if (typeof raw.runnerTransportSecret === "string" && raw.runnerTransportSecret.length > 0)
35565
- cfg.runnerTransportSecret = raw.runnerTransportSecret;
35566
35349
  if (typeof raw.address === "string" && raw.address.length > 0) cfg.address = raw.address;
35567
35350
  if (typeof raw.publicAddress === "string" && raw.publicAddress.length > 0)
35568
35351
  cfg.publicAddress = raw.publicAddress;
35569
- if (typeof raw.hubName === "string" && raw.hubName.trim().length > 0)
35570
- cfg.hubName = raw.hubName.trim();
35571
35352
  if (raw.mode === "local" || raw.mode === "remote") cfg.mode = raw.mode;
35353
+ if (typeof raw.hubName === "string" && raw.hubName.trim().length > 0) cfg.hubName = raw.hubName.trim();
35572
35354
  if (typeof raw.port === "number" && Number.isInteger(raw.port)) cfg.port = raw.port;
35573
35355
  if (typeof raw.publicUrl === "string" && raw.publicUrl.length > 0) cfg.publicUrl = raw.publicUrl;
35356
+ return cfg;
35357
+ }
35358
+ function coerceRunnerConfig(raw) {
35359
+ const cfg = {};
35574
35360
  if (typeof raw.joinToken === "string" && raw.joinToken.length > 0) cfg.joinToken = raw.joinToken;
35575
- if (typeof raw.runnerName === "string" && raw.runnerName.length > 0)
35576
- cfg.runnerName = raw.runnerName;
35361
+ if (typeof raw.runnerName === "string" && raw.runnerName.length > 0) cfg.runnerName = raw.runnerName;
35577
35362
  if (typeof raw.hubUrl === "string" && raw.hubUrl.length > 0) cfg.hubUrl = raw.hubUrl;
35578
35363
  if (Array.isArray(raw.allowedRoots)) {
35579
- const roots = raw.allowedRoots.filter(
35580
- (r) => typeof r === "string" && r.length > 0
35581
- );
35364
+ const roots = raw.allowedRoots.filter((r) => typeof r === "string" && r.length > 0);
35582
35365
  if (roots.length > 0) cfg.allowedRoots = roots;
35583
35366
  }
35584
35367
  if (typeof raw.pinnedCertSha256 === "string" && raw.pinnedCertSha256.length > 0)
35585
35368
  cfg.pinnedCertSha256 = raw.pinnedCertSha256;
35586
35369
  return cfg;
35587
35370
  }
35588
- function loadSlayzoneConfig(configPath = getSlayzoneConfigPath()) {
35371
+ function readJsonObject(path9) {
35589
35372
  let raw;
35590
35373
  try {
35591
- raw = (0, import_node_fs4.readFileSync)(configPath, "utf8");
35374
+ raw = (0, import_node_fs4.readFileSync)(path9, "utf8");
35592
35375
  } catch (err) {
35593
- if (err.code === "ENOENT") return {};
35376
+ if (err.code === "ENOENT") return null;
35594
35377
  process.stderr.write(
35595
- `[slayzone-config] cannot read ${configPath}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
35378
+ `[slayzone-config] cannot read ${path9}: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
35596
35379
  `
35597
35380
  );
35598
- return {};
35381
+ return null;
35599
35382
  }
35600
35383
  let parsed;
35601
35384
  try {
35602
35385
  parsed = JSON.parse(raw);
35603
35386
  } catch (err) {
35604
35387
  process.stderr.write(
35605
- `[slayzone-config] ${configPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
35388
+ `[slayzone-config] ${path9} is not valid JSON: ${err instanceof Error ? err.message : String(err)} \u2014 using empty config
35606
35389
  `
35607
35390
  );
35608
- return {};
35391
+ return null;
35609
35392
  }
35610
35393
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
35611
- process.stderr.write(`[slayzone-config] ${configPath} is not a JSON object \u2014 using empty config
35394
+ process.stderr.write(`[slayzone-config] ${path9} is not a JSON object \u2014 using empty config
35612
35395
  `);
35613
- return {};
35396
+ return null;
35614
35397
  }
35615
- return coerce(parsed);
35398
+ return parsed;
35616
35399
  }
35617
- function saveSlayzoneConfig(cfg, configPath = getSlayzoneConfigPath()) {
35618
- const dir = (0, import_node_path4.dirname)(configPath);
35400
+ function writeJsonAtomic(path9, data) {
35401
+ const dir = (0, import_node_path3.dirname)(path9);
35619
35402
  (0, import_node_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
35620
- const tmpPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
35621
- (0, import_node_fs4.writeFileSync)(tmpPath, `${JSON.stringify(cfg, null, 2)}
35403
+ const tmpPath = `${path9}.tmp-${process.pid}-${Date.now()}`;
35404
+ (0, import_node_fs4.writeFileSync)(tmpPath, `${JSON.stringify(data, null, 2)}
35622
35405
  `, { mode: 384 });
35623
35406
  try {
35624
- (0, import_node_fs4.renameSync)(tmpPath, configPath);
35407
+ (0, import_node_fs4.renameSync)(tmpPath, path9);
35625
35408
  } catch (err) {
35626
35409
  (0, import_node_fs4.rmSync)(tmpPath, { force: true });
35627
35410
  throw err;
35628
35411
  }
35629
35412
  }
35630
- function updateSlayzoneConfig(patch, configPath = getSlayzoneConfigPath()) {
35631
- const current = loadSlayzoneConfig(configPath);
35632
- const merged = { ...current };
35413
+ function saveHubConfigFile(cfg, path9 = getHubConfigFilePath()) {
35414
+ writeJsonAtomic(path9, cfg);
35415
+ }
35416
+ function updateHubConfigFile(patch, path9 = getHubConfigFilePath()) {
35417
+ const current = readJsonObject(path9);
35418
+ const merged = current ? coerceHubConfig(current) : {};
35419
+ for (const [k, v2] of Object.entries(patch)) {
35420
+ if (v2 !== void 0) merged[k] = v2;
35421
+ }
35422
+ saveHubConfigFile(merged, path9);
35423
+ return merged;
35424
+ }
35425
+ function loadRunnerConfigFile(path9 = getRunnerConfigFilePath()) {
35426
+ const raw = readJsonObject(path9);
35427
+ if (raw) return coerceRunnerConfig(raw);
35428
+ const legacy = readJsonObject(legacyConfigFilePathFor(path9));
35429
+ return legacy ? coerceRunnerConfig(legacy) : {};
35430
+ }
35431
+ function saveRunnerConfigFile(cfg, path9 = getRunnerConfigFilePath()) {
35432
+ writeJsonAtomic(path9, cfg);
35433
+ }
35434
+ function updateRunnerConfigFile(patch, path9 = getRunnerConfigFilePath()) {
35435
+ const current = readJsonObject(path9);
35436
+ const merged = current ? coerceRunnerConfig(current) : {};
35633
35437
  for (const [k, v2] of Object.entries(patch)) {
35634
35438
  if (v2 !== void 0) merged[k] = v2;
35635
35439
  }
35636
- saveSlayzoneConfig(merged, configPath);
35440
+ saveRunnerConfigFile(merged, path9);
35637
35441
  return merged;
35638
35442
  }
35639
35443
 
@@ -35641,7 +35445,7 @@ function updateSlayzoneConfig(patch, configPath = getSlayzoneConfigPath()) {
35641
35445
  var import_node_child_process = require("node:child_process");
35642
35446
  var import_node_fs5 = require("node:fs");
35643
35447
  var import_node_os = require("node:os");
35644
- var import_node_path5 = require("node:path");
35448
+ var import_node_path4 = require("node:path");
35645
35449
  var KINDS = {
35646
35450
  hub: {
35647
35451
  labelPrefix: "com.slayzone.hub.",
@@ -35677,8 +35481,8 @@ function assertValidServiceName(kind, name) {
35677
35481
  }
35678
35482
  function defaultUnitDir(backend) {
35679
35483
  const home = process.env.HOME ?? (0, import_node_os.homedir)();
35680
- if (backend === "launchd") return (0, import_node_path5.join)(home, "Library", "LaunchAgents");
35681
- if (backend === "systemd") return (0, import_node_path5.join)(home, ".config", "systemd", "user");
35484
+ if (backend === "launchd") return (0, import_node_path4.join)(home, "Library", "LaunchAgents");
35485
+ if (backend === "systemd") return (0, import_node_path4.join)(home, ".config", "systemd", "user");
35682
35486
  throw new Error("[slayzone] no unit directory: this platform has no user service backend");
35683
35487
  }
35684
35488
  function detectBackend() {
@@ -35699,7 +35503,7 @@ function detectBackend() {
35699
35503
  }
35700
35504
  function unitPath(kind, name, backend, unitDir = defaultUnitDir(backend)) {
35701
35505
  assertValidServiceName(kind, name);
35702
- return backend === "launchd" ? (0, import_node_path5.join)(unitDir, `${KINDS[kind].labelPrefix}${name}.plist`) : (0, import_node_path5.join)(unitDir, `${KINDS[kind].systemdPrefix}${name}.service`);
35506
+ return backend === "launchd" ? (0, import_node_path4.join)(unitDir, `${KINDS[kind].labelPrefix}${name}.plist`) : (0, import_node_path4.join)(unitDir, `${KINDS[kind].systemdPrefix}${name}.service`);
35703
35507
  }
35704
35508
  function nameFromUnitFile(kind, file, backend) {
35705
35509
  if (backend === "launchd") {
@@ -35754,9 +35558,9 @@ ${envEntries}
35754
35558
  <false/>
35755
35559
  </dict>
35756
35560
  <key>StandardOutPath</key>
35757
- <string>${xmlEscape((0, import_node_path5.join)(spec.logDir, kind.outLog))}</string>
35561
+ <string>${xmlEscape((0, import_node_path4.join)(spec.logDir, kind.outLog))}</string>
35758
35562
  <key>StandardErrorPath</key>
35759
- <string>${xmlEscape((0, import_node_path5.join)(spec.logDir, kind.errLog))}</string>
35563
+ <string>${xmlEscape((0, import_node_path4.join)(spec.logDir, kind.errLog))}</string>
35760
35564
  <key>ProcessType</key>
35761
35565
  <string>Background</string>
35762
35566
  </dict>
@@ -35811,7 +35615,7 @@ function listRegisteredUnits(kind, backend, unitDir = defaultUnitDir(backend)) {
35811
35615
  const out = [];
35812
35616
  for (const file of files) {
35813
35617
  const name = nameFromUnitFile(kind, file, backend);
35814
- if (name) out.push({ name, unitPath: (0, import_node_path5.join)(unitDir, file) });
35618
+ if (name) out.push({ name, unitPath: (0, import_node_path4.join)(unitDir, file) });
35815
35619
  }
35816
35620
  return out.sort((a, b) => a.name.localeCompare(b.name));
35817
35621
  }
@@ -35843,8 +35647,8 @@ function launchdLabel(kind, name) {
35843
35647
  var import_node_child_process2 = require("node:child_process");
35844
35648
  var import_node_fs6 = require("node:fs");
35845
35649
  var import_node_module = require("node:module");
35846
- var import_node_path6 = require("node:path");
35847
- var CLI_VERSION = true ? "0.36.0-beta.12" : "";
35650
+ var import_node_path5 = require("node:path");
35651
+ var CLI_VERSION = true ? "0.36.0-beta.15" : "";
35848
35652
  function fail(message) {
35849
35653
  console.error(message);
35850
35654
  process.exit(1);
@@ -35877,18 +35681,18 @@ function readServicePkg(kind, pkgJsonPath) {
35877
35681
  }
35878
35682
  const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.[PACKAGES[kind].bin];
35879
35683
  if (!binRel) return null;
35880
- const binPath = (0, import_node_path6.join)((0, import_node_path6.dirname)(pkgJsonPath), binRel);
35684
+ const binPath = (0, import_node_path5.join)((0, import_node_path5.dirname)(pkgJsonPath), binRel);
35881
35685
  if (!(0, import_node_fs6.existsSync)(binPath)) return null;
35882
35686
  return { binPath, version: pkg.version ?? "unknown" };
35883
35687
  }
35884
35688
  function interpreterFor(binPath, version) {
35885
- let dir = (0, import_node_path6.dirname)(binPath);
35689
+ let dir = (0, import_node_path5.dirname)(binPath);
35886
35690
  for (let i = 0; i < 6; i++) {
35887
- const pathTxt = (0, import_node_path6.join)(dir, "node_modules", "electron", "path.txt");
35691
+ const pathTxt = (0, import_node_path5.join)(dir, "node_modules", "electron", "path.txt");
35888
35692
  if ((0, import_node_fs6.existsSync)(pathTxt)) {
35889
35693
  try {
35890
35694
  const rel = (0, import_node_fs6.readFileSync)(pathTxt, "utf8").trim();
35891
- const electron = (0, import_node_path6.join)(dir, "node_modules", "electron", "dist", rel);
35695
+ const electron = (0, import_node_path5.join)(dir, "node_modules", "electron", "dist", rel);
35892
35696
  if ((0, import_node_fs6.existsSync)(electron)) {
35893
35697
  return {
35894
35698
  command: electron,
@@ -35901,7 +35705,7 @@ function interpreterFor(binPath, version) {
35901
35705
  } catch {
35902
35706
  }
35903
35707
  }
35904
- const parent = (0, import_node_path6.dirname)(dir);
35708
+ const parent = (0, import_node_path5.dirname)(dir);
35905
35709
  if (parent === dir) break;
35906
35710
  dir = parent;
35907
35711
  }
@@ -35916,7 +35720,7 @@ function resolveServiceBin(kind) {
35916
35720
  // this package's CJS bundle.
35917
35721
  () => {
35918
35722
  try {
35919
- return (0, import_node_module.createRequire)(process.argv[1] ?? (0, import_node_path6.join)(process.cwd(), "x")).resolve(
35723
+ return (0, import_node_module.createRequire)(process.argv[1] ?? (0, import_node_path5.join)(process.cwd(), "x")).resolve(
35920
35724
  `${pkg}/package.json`
35921
35725
  );
35922
35726
  } catch {
@@ -35927,7 +35731,7 @@ function resolveServiceBin(kind) {
35927
35731
  () => {
35928
35732
  const self2 = process.argv[1];
35929
35733
  if (!self2) return null;
35930
- const sibling = (0, import_node_path6.join)((0, import_node_path6.dirname)(self2), "..", "..", devDir, "package.json");
35734
+ const sibling = (0, import_node_path5.join)((0, import_node_path5.dirname)(self2), "..", "..", devDir, "package.json");
35931
35735
  return (0, import_node_fs6.existsSync)(sibling) ? sibling : null;
35932
35736
  }
35933
35737
  ];
@@ -35937,8 +35741,8 @@ function resolveServiceBin(kind) {
35937
35741
  const found2 = readServicePkg(kind, path9);
35938
35742
  if (found2) return interpreterFor(found2.binPath, found2.version);
35939
35743
  }
35940
- const prefix = (0, import_node_path6.join)(getDataDir(), `${kind}-runtime`);
35941
- const installed = (0, import_node_path6.join)(prefix, "node_modules", pkg, "package.json");
35744
+ const prefix = getServiceRuntimeDir(kind);
35745
+ const installed = (0, import_node_path5.join)(prefix, "node_modules", pkg, "package.json");
35942
35746
  const wanted = CLI_VERSION ? `${pkg}@${CLI_VERSION}` : `${pkg}@beta`;
35943
35747
  if (!(0, import_node_fs6.existsSync)(installed) || CLI_VERSION && readServicePkg(kind, installed)?.version !== CLI_VERSION) {
35944
35748
  console.log(`Installing ${wanted}\u2026`);
@@ -35970,13 +35774,13 @@ function ensureLogDir(kind, root) {
35970
35774
  (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
35971
35775
  return dir;
35972
35776
  }
35973
- function logDirFor(kind, root) {
35974
- return kind === "hub" ? (0, import_node_path6.join)(root, "storage", "logs") : (0, import_node_path6.join)(root, "logs");
35777
+ function logDirFor(_kind, root) {
35778
+ return (0, import_node_path5.join)(root, "logs");
35975
35779
  }
35976
35780
  function serviceLogPaths(kind, root) {
35977
35781
  const dir = logDirFor(kind, root);
35978
35782
  const names = serviceLogNames(kind);
35979
- return { out: (0, import_node_path6.join)(dir, names.out), err: (0, import_node_path6.join)(dir, names.err) };
35783
+ return { out: (0, import_node_path5.join)(dir, names.out), err: (0, import_node_path5.join)(dir, names.err) };
35980
35784
  }
35981
35785
  function failWithLog(kind, name, logPath, what) {
35982
35786
  let detail = "";
@@ -36006,7 +35810,7 @@ function readServiceLogTail(kind, root, lines) {
36006
35810
  const chunks = [];
36007
35811
  for (const f of files) {
36008
35812
  try {
36009
- chunks.push((0, import_node_fs6.readFileSync)((0, import_node_path6.join)(dir, f), "utf8"));
35813
+ chunks.push((0, import_node_fs6.readFileSync)((0, import_node_path5.join)(dir, f), "utf8"));
36010
35814
  } catch {
36011
35815
  }
36012
35816
  }
@@ -36023,7 +35827,7 @@ function followServiceLog(args) {
36023
35827
  }
36024
35828
  const dir = logDirFor(kind, root);
36025
35829
  if (!(0, import_node_fs6.existsSync)(dir)) fail(`No log directory at ${shortenPath(dir)}.`);
36026
- const candidates = (0, import_node_fs6.readdirSync)(dir).filter((f) => f.endsWith(".log")).map((f) => ({ f, path: (0, import_node_path6.join)(dir, f), mtime: (0, import_node_fs6.statSync)((0, import_node_path6.join)(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
35830
+ const candidates = (0, import_node_fs6.readdirSync)(dir).filter((f) => f.endsWith(".log")).map((f) => ({ f, path: (0, import_node_path5.join)(dir, f), mtime: (0, import_node_fs6.statSync)((0, import_node_path5.join)(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
36027
35831
  if (candidates.length === 0) fail(`No log files in ${shortenPath(dir)}.`);
36028
35832
  const target = candidates[0].path;
36029
35833
  const targs = follow ? ["-f", "-n", String(lines), target] : ["-n", String(lines), target];
@@ -36192,20 +35996,11 @@ ${hint}` : ""}`);
36192
35996
 
36193
35997
  // src/commands/hub.ts
36194
35998
  var HUB_PACKAGE = servicePackage("hub");
36195
- function outOfBlockPorts() {
36196
- if (!hasLocalDatabase()) return [];
36197
- try {
36198
- const port = getServerPort();
36199
- return port ? [port] : [];
36200
- } catch {
36201
- return [];
36202
- }
36203
- }
36204
35999
  function discoverAllHubs() {
36205
- return discoverHubs({ extraPorts: outOfBlockPorts() });
36000
+ return discoverHubs();
36206
36001
  }
36207
36002
  function findAnyHub(nameOrPort) {
36208
- return findHub(nameOrPort, { extraPorts: outOfBlockPorts() });
36003
+ return findHub(nameOrPort);
36209
36004
  }
36210
36005
  function formatUptime(ms) {
36211
36006
  const s = Math.floor(ms / 1e3);
@@ -36277,8 +36072,8 @@ async function launchHub(args) {
36277
36072
  const bin = resolveServiceBin("hub");
36278
36073
  const logDir = ensureLogDir("hub", root);
36279
36074
  const logs = serviceLogPaths("hub", root);
36280
- const probePort2 = address !== void 0 ? parseHubAddress(address)?.port : port;
36281
- const findIt = () => probePort2 === void 0 ? findAnyHub(name) : findHub(String(probePort2));
36075
+ const probePort = address !== void 0 ? parseHubAddress(address)?.port : port;
36076
+ const findIt = () => probePort === void 0 ? findAnyHub(name) : findHub(String(probePort));
36282
36077
  if (backend === "none") {
36283
36078
  const out = (0, import_node_fs7.openSync)(logs.out, "a");
36284
36079
  const child = (0, import_node_child_process3.spawn)(bin.command, bin.args, {
@@ -36511,7 +36306,7 @@ function hubCommand() {
36511
36306
  "Externally-reachable address of this hub \u2014 makes it a REMOTE hub (auth + TLS + wss join tokens)"
36512
36307
  ).option("--bind <host[:port]>", "Address to bind (default: loopback, or 0.0.0.0 when remote)").action(
36513
36308
  async (name, opts) => {
36514
- const root = (0, import_node_path7.resolve)(opts.root ?? process.cwd());
36309
+ const root = (0, import_node_path6.resolve)(opts.root ?? process.cwd());
36515
36310
  const port = opts.port === void 0 ? void 0 : Number(opts.port);
36516
36311
  if (port !== void 0 && (!Number.isInteger(port) || port < 1 || port > 65535)) {
36517
36312
  fail(`Invalid --port ${opts.port} \u2014 expected an integer 1-65535.`);
@@ -36556,13 +36351,13 @@ Start it with \`slay hub start ${name}\`, or remove it with \`slay hub rm ${name
36556
36351
  );
36557
36352
  }
36558
36353
  if (publicAddress !== void 0) {
36559
- updateSlayzoneConfig(
36354
+ updateHubConfigFile(
36560
36355
  {
36561
36356
  mode: "remote",
36562
36357
  publicAddress,
36563
36358
  ...address !== void 0 ? { address } : {}
36564
36359
  },
36565
- (0, import_node_path7.join)(root, "config.json")
36360
+ (0, import_node_path6.join)(root, "hub.config.json")
36566
36361
  );
36567
36362
  console.log("Mode: remote (client auth + TLS enforced)");
36568
36363
  console.log(`Bind: ${address ?? "(hub block default)"}`);
@@ -36792,7 +36587,7 @@ machines dial, recreate it with \`--public-address <host:port>\`.`
36792
36587
  // src/commands/runner.ts
36793
36588
  var import_node_child_process4 = require("node:child_process");
36794
36589
  var import_node_fs8 = require("node:fs");
36795
- var import_node_path8 = require("node:path");
36590
+ var import_node_path7 = require("node:path");
36796
36591
 
36797
36592
  // ../../shared/platform/src/join-token.ts
36798
36593
  var JOIN_TOKEN_PREFIX = "szjt1";
@@ -36819,7 +36614,7 @@ var RUNNER_PACKAGE = servicePackage("runner");
36819
36614
  var ENROLL_TIMEOUT_MS = 3e4;
36820
36615
  var CONNECTED_RE = /"mode":"(enroll|hello)"/;
36821
36616
  function configPathFor(root) {
36822
- return (0, import_node_path8.join)(root, "config.json");
36617
+ return (0, import_node_path7.join)(root, "runner.config.json");
36823
36618
  }
36824
36619
  function warnIfLoopbackHub(hubUrl) {
36825
36620
  if (!isLoopbackRunnerUrl(hubUrl)) return;
@@ -36833,7 +36628,8 @@ never connect. For a hub other machines can reach, recreate it with
36833
36628
  }
36834
36629
  function isEnrolled(root) {
36835
36630
  try {
36836
- return (0, import_node_fs8.readdirSync)((0, import_node_path8.join)(root, "runners")).some((f) => f.endsWith(".json"));
36631
+ const parsed = JSON.parse((0, import_node_fs8.readFileSync)((0, import_node_path7.join)(root, "runner.state.json"), "utf8"));
36632
+ return typeof parsed === "object" && parsed !== null && Object.keys(parsed).length > 0;
36837
36633
  } catch {
36838
36634
  return false;
36839
36635
  }
@@ -37014,14 +36810,22 @@ Run \`slay runner mint\` there, or put the hub in remote mode (\`slay hub create
37014
36810
  cmd.command("ls").description("List the runners installed on this machine").option("--json", "Output as JSON").action((opts) => {
37015
36811
  const backend = detectBackend();
37016
36812
  if (backend === "none") {
37017
- console.log("This platform has no user service manager \u2014 no runners can be registered.");
36813
+ if (opts.json) {
36814
+ console.log("[]");
36815
+ return;
36816
+ }
36817
+ console.log(
36818
+ "No runners installed \u2014 this platform has no user service manager, so runners cannot be registered here."
36819
+ );
36820
+ console.log(`Run one in the foreground instead:
36821
+ npx ${RUNNER_PACKAGE}`);
37018
36822
  return;
37019
36823
  }
37020
36824
  const units = listRegisteredUnits("runner", backend);
37021
36825
  const rows = units.map((u) => {
37022
36826
  const root = readUnitRoot("runner", u.name, backend);
37023
36827
  const status = supervisorStatus("runner", backend, u.name);
37024
- const hubUrl = root ? loadSlayzoneConfig(configPathFor(root)).hubUrl ?? null : null;
36828
+ const hubUrl = root ? loadRunnerConfigFile(configPathFor(root)).hubUrl ?? null : null;
37025
36829
  return {
37026
36830
  name: u.name,
37027
36831
  unitPath: u.unitPath,
@@ -37073,7 +36877,7 @@ Runner "${r.name}" is stopped. Start it: slay runner start ${r.name}`);
37073
36877
  "Filesystem root the runner may operate under (repeatable; default: the runner root)",
37074
36878
  (value, previous = []) => [...previous, value]
37075
36879
  ).action(async (name, opts) => {
37076
- const root = (0, import_node_path8.resolve)(opts.root ?? process.cwd());
36880
+ const root = (0, import_node_path7.resolve)(opts.root ?? process.cwd());
37077
36881
  const backend = resolveBackend();
37078
36882
  const decoded = decodeJoinToken(opts.token);
37079
36883
  if (!decoded) {
@@ -37096,15 +36900,15 @@ Mint one on the hub:
37096
36900
  Start it with \`slay runner start ${name}\`, or remove it with \`slay runner rm ${name}\`.`
37097
36901
  );
37098
36902
  }
37099
- const occupant = loadSlayzoneConfig(configPathFor(root)).runnerName;
36903
+ const occupant = loadRunnerConfigFile(configPathFor(root)).runnerName;
37100
36904
  if (occupant !== void 0) {
37101
36905
  fail(
37102
36906
  `A runner is already installed in ${shortenPath(root)} \u2014 "${occupant}".
37103
36907
  ` + (occupant === name ? `Start it with \`slay runner start ${name}\`, or remove it with \`slay runner rm ${name}\`.` : `Give this runner its own directory with \`--root <dir>\`, or remove "${occupant}" first with \`slay runner rm ${occupant}\`.`)
37104
36908
  );
37105
36909
  }
37106
- const allowedRoots = opts.allow && opts.allow.length > 0 ? opts.allow.map((d) => (0, import_node_path8.resolve)(d)) : [root];
37107
- updateSlayzoneConfig(
36910
+ const allowedRoots = opts.allow && opts.allow.length > 0 ? opts.allow.map((d) => (0, import_node_path7.resolve)(d)) : [root];
36911
+ updateRunnerConfigFile(
37108
36912
  {
37109
36913
  joinToken: opts.token,
37110
36914
  hubUrl: decoded.hubUrl,
@@ -37208,30 +37012,189 @@ Start it with \`slay runner start ${name}\`, or remove it with \`slay runner rm
37208
37012
  return cmd;
37209
37013
  }
37210
37014
 
37015
+ // src/commands/update.ts
37016
+ var import_node_child_process5 = require("node:child_process");
37017
+ var import_node_fs9 = require("node:fs");
37018
+ var import_node_os2 = require("node:os");
37019
+ var import_node_path8 = require("node:path");
37020
+ var PACKAGE_NAMES = ["@slayzone/cli", "@slayzone/runner", "@slayzone/hub"];
37021
+ var KIND_FOR_PACKAGE = {
37022
+ "@slayzone/hub": "hub",
37023
+ "@slayzone/runner": "runner"
37024
+ };
37025
+ function runCapture(bin, args) {
37026
+ try {
37027
+ return (0, import_node_child_process5.execFileSync)(bin, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
37028
+ } catch {
37029
+ return null;
37030
+ }
37031
+ }
37032
+ function bunGlobalRoot() {
37033
+ const base = process.env.BUN_INSTALL || (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".bun");
37034
+ const dir = (0, import_node_path8.join)(base, "install", "global", "node_modules");
37035
+ return (0, import_node_fs9.existsSync)(dir) ? dir : null;
37036
+ }
37037
+ var MANAGERS = [
37038
+ {
37039
+ id: "npm",
37040
+ bin: "npm",
37041
+ globalRoot: () => runCapture("npm", ["root", "-g"]),
37042
+ installArgs: (names, target) => ["install", "-g", ...names.map((n) => `${n}@${target}`)]
37043
+ },
37044
+ {
37045
+ id: "pnpm",
37046
+ bin: "pnpm",
37047
+ globalRoot: () => runCapture("pnpm", ["root", "-g"]),
37048
+ // pnpm prompts interactively (or silently skips, off a TTY) before running a
37049
+ // global package's install scripts — `--allow-build=<name>` pre-approves it,
37050
+ // needed for hub/runner's native rebuild (better-sqlite3/node-pty). Verified
37051
+ // via `pnpm add --help` (10.30.3); harmless to pass for cli, which has none.
37052
+ installArgs: (names, target) => [
37053
+ "add",
37054
+ "-g",
37055
+ ...names.filter((n) => KIND_FOR_PACKAGE[n]).map((n) => `--allow-build=${n}`),
37056
+ ...names.map((n) => `${n}@${target}`)
37057
+ ]
37058
+ },
37059
+ {
37060
+ id: "bun",
37061
+ bin: "bun",
37062
+ globalRoot: bunGlobalRoot,
37063
+ // Same problem as pnpm's build gate, bun's own flavor: postinstall scripts
37064
+ // don't run for a package unless it's trusted. `--trust` opts the packages
37065
+ // in this install into that list. Verified via `bun add --help` (1.3.6).
37066
+ installArgs: (names, target) => [
37067
+ "add",
37068
+ "-g",
37069
+ ...names.some((n) => KIND_FOR_PACKAGE[n]) ? ["--trust"] : [],
37070
+ ...names.map((n) => `${n}@${target}`)
37071
+ ]
37072
+ }
37073
+ ];
37074
+ function readInstalledVersion(globalRoot, pkg) {
37075
+ const pkgJsonPath = (0, import_node_path8.join)(globalRoot, ...pkg.split("/"), "package.json");
37076
+ if (!(0, import_node_fs9.existsSync)(pkgJsonPath)) return null;
37077
+ try {
37078
+ const parsed = JSON.parse((0, import_node_fs9.readFileSync)(pkgJsonPath, "utf8"));
37079
+ return typeof parsed.version === "string" ? parsed.version : null;
37080
+ } catch {
37081
+ return null;
37082
+ }
37083
+ }
37084
+ function fetchDistTags() {
37085
+ for (const bin of ["npm", "pnpm"]) {
37086
+ const out = runCapture(bin, ["view", "@slayzone/cli", "dist-tags", "--json"]);
37087
+ if (!out) continue;
37088
+ try {
37089
+ return JSON.parse(out);
37090
+ } catch {
37091
+ }
37092
+ }
37093
+ return null;
37094
+ }
37095
+ function printRestartHints(updatedPackages) {
37096
+ const backend = resolveBackend();
37097
+ if (backend === "none") return;
37098
+ const hints = [];
37099
+ for (const pkg of updatedPackages) {
37100
+ const kind = KIND_FOR_PACKAGE[pkg];
37101
+ if (!kind) continue;
37102
+ for (const unit of listRegisteredUnits(kind, backend)) {
37103
+ hints.push(` slay ${kind} restart ${unit.name} --upgrade`);
37104
+ }
37105
+ }
37106
+ if (hints.length === 0) return;
37107
+ console.log("\nRestart to apply:");
37108
+ for (const hint of hints) console.log(hint);
37109
+ }
37110
+ function updateCommand() {
37111
+ const cmd = new Command("update").description("Update globally installed @slayzone packages (cli, hub, runner) \u2014 npm, pnpm, or bun").option("--check", "Report available updates without installing anything").action((opts) => {
37112
+ const roots = /* @__PURE__ */ new Map();
37113
+ for (const mgr of MANAGERS) {
37114
+ const root = mgr.globalRoot();
37115
+ if (root) roots.set(mgr.id, root);
37116
+ }
37117
+ if (roots.size === 0) {
37118
+ fail("Could not resolve a global install root for npm, pnpm, or bun. Is at least one on PATH?");
37119
+ }
37120
+ const found = [];
37121
+ for (const [manager, root] of roots) {
37122
+ for (const name of PACKAGE_NAMES) {
37123
+ const installedVersion = readInstalledVersion(root, name);
37124
+ if (installedVersion) found.push({ manager, name, installedVersion });
37125
+ }
37126
+ }
37127
+ if (found.length === 0) {
37128
+ console.log("No @slayzone packages found in any global npm, pnpm, or bun install.");
37129
+ return;
37130
+ }
37131
+ const distTags = fetchDistTags();
37132
+ if (!distTags) {
37133
+ fail(
37134
+ "Could not reach the npm registry to check for updates (`npm view @slayzone/cli dist-tags` and `pnpm view` both failed)."
37135
+ );
37136
+ }
37137
+ const onPrerelease = found.some((p2) => p2.installedVersion.includes("-"));
37138
+ const target = (onPrerelease ? distTags.beta : void 0) ?? distTags.latest ?? distTags.beta;
37139
+ if (!target) fail("The registry returned no usable dist-tag for @slayzone/cli.");
37140
+ const managerW = Math.max("MANAGER".length, ...found.map((p2) => p2.manager.length));
37141
+ const nameW = Math.max("PACKAGE".length, ...found.map((p2) => p2.name.length));
37142
+ const versionW = Math.max("INSTALLED".length, ...found.map((p2) => p2.installedVersion.length));
37143
+ console.log(
37144
+ `${"MANAGER".padEnd(managerW)} ${"PACKAGE".padEnd(nameW)} ${"INSTALLED".padEnd(versionW)} STATUS`
37145
+ );
37146
+ for (const p2 of found) {
37147
+ const status = p2.installedVersion === target ? "up to date" : `-> ${target}`;
37148
+ console.log(
37149
+ `${p2.manager.padEnd(managerW)} ${p2.name.padEnd(nameW)} ${p2.installedVersion.padEnd(versionW)} ${status}`
37150
+ );
37151
+ }
37152
+ const outdated = found.filter((p2) => p2.installedVersion !== target);
37153
+ if (outdated.length === 0) {
37154
+ console.log("\nEverything is up to date.");
37155
+ return;
37156
+ }
37157
+ if (opts.check) {
37158
+ console.log("\n--check: nothing installed. Run `slay update` to apply.");
37159
+ return;
37160
+ }
37161
+ const byManager = /* @__PURE__ */ new Map();
37162
+ for (const p2 of outdated) {
37163
+ const list = byManager.get(p2.manager) ?? [];
37164
+ list.push(p2.name);
37165
+ byManager.set(p2.manager, list);
37166
+ }
37167
+ for (const [managerId, names] of byManager) {
37168
+ const mgr = MANAGERS.find((m) => m.id === managerId);
37169
+ if (!mgr) continue;
37170
+ const args = mgr.installArgs(names, target);
37171
+ console.log(`
37172
+ [${managerId}] Installing: ${names.map((n) => `${n}@${target}`).join(" ")}`);
37173
+ try {
37174
+ (0, import_node_child_process5.execFileSync)(mgr.bin, args, { stdio: "inherit" });
37175
+ } catch {
37176
+ fail(
37177
+ `${mgr.bin} install failed. Install the packages yourself and retry:
37178
+ ` + names.map((n) => ` ${mgr.bin} ${mgr.id === "npm" ? "install" : "add"} -g ${n}@${target}`).join("\n")
37179
+ );
37180
+ }
37181
+ }
37182
+ printRestartHints(outdated.map((p2) => p2.name));
37183
+ });
37184
+ return cmd;
37185
+ }
37186
+
37211
37187
  // src/index.ts
37212
- process.removeAllListeners("warning");
37213
- process.on("warning", (warning) => {
37214
- if (warning.name === "ExperimentalWarning" && /SQLite/i.test(warning.message)) return;
37215
- console.warn(`${warning.name}: ${warning.message}`);
37216
- });
37217
- var program2 = new Command().name("slay").description("SlayZone CLI").version("0.36.0-beta.12").option("--dev", "Use development database (slayzone.dev.sqlite)").option("--hub <name|port>", "Target a specific hub on this machine (see `slay hub ls`)").showSuggestionAfterError(true).showHelpAfterError(true).hook("preAction", async (thisCommand) => {
37188
+ var program2 = new Command().name("slay").description("SlayZone CLI").version("0.36.0-beta.15").option("--dev", "Use development database (slayzone.dev.sqlite)").option("--hub <name|port>", "Target a specific hub on this machine (see `slay hub ls`)").showSuggestionAfterError(true).showHelpAfterError(true).hook("preAction", async (thisCommand) => {
37218
37189
  const root = thisCommand.parent ?? thisCommand;
37219
37190
  const opts = root.opts();
37220
37191
  if (opts.dev) process.env.SLAYZONE_DEV = "1";
37221
37192
  if (opts.hub !== void 0) await applyHubOverride(opts.hub);
37222
37193
  });
37223
37194
  async function applyHubOverride(nameOrPort) {
37224
- let extraPorts = [];
37225
- if (hasLocalDatabase()) {
37226
- try {
37227
- const appPort = getServerPort();
37228
- if (appPort) extraPorts = [appPort];
37229
- } catch {
37230
- }
37231
- }
37232
- const hub = await findHub(nameOrPort, { extraPorts });
37195
+ const hub = await findHub(nameOrPort);
37233
37196
  if (!hub) {
37234
- const running = await discoverHubs({ extraPorts });
37197
+ const running = await discoverHubs();
37235
37198
  console.error(`No hub named or listening on "${nameOrPort}".`);
37236
37199
  console.error(
37237
37200
  running.length > 0 ? `Running hubs: ${running.map((h) => `${h.name} (${h.port})`).join(", ")}` : "No hubs are running on this machine."
@@ -37255,6 +37218,7 @@ program2.addCommand(initCommand());
37255
37218
  program2.addCommand(ptyCommand());
37256
37219
  program2.addCommand(hubCommand());
37257
37220
  program2.addCommand(runnerCommand());
37221
+ program2.addCommand(updateCommand());
37258
37222
  program2.parseAsync(process.argv).catch((err) => {
37259
37223
  console.error(err instanceof Error ? err.message : String(err));
37260
37224
  process.exit(1);