@cryptiklemur/lattice 5.2.0 → 5.3.0

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.
@@ -407,7 +407,12 @@ export async function startDaemon(portOverride, tlsOverride) {
407
407
  log.server("TLS enabled (cert: %s)", certs.cert);
408
408
  }
409
409
  catch (err) {
410
- console.error("[lattice] Failed to load TLS certs, falling back to HTTP:", err);
410
+ if (err?.code === "EACCES") {
411
+ console.error("[lattice] Permission denied reading TLS certs. Run 'lattice setup-tls' to fix permissions.");
412
+ }
413
+ else {
414
+ console.error("[lattice] Failed to load TLS certs, falling back to HTTP:", err);
415
+ }
411
416
  }
412
417
  }
413
418
  if (tlsOptions) {
@@ -18,7 +18,7 @@ function getCurrentVersion() {
18
18
  }
19
19
  }
20
20
  var args = process.argv.slice(2);
21
- var command = "start";
21
+ var command = "help";
22
22
  var portOverride = null;
23
23
  var tlsOverride = null;
24
24
  for (var i = 0; i < args.length; i++) {
@@ -92,11 +92,11 @@ function spawnDaemon(port, tls) {
92
92
  var pkgRoot = join(__dirname_local, "..");
93
93
  var localTsx = join(pkgRoot, "node_modules", ".bin", "tsx");
94
94
  spawnCmd = existsSync(localTsx) ? localTsx : "tsx";
95
- spawnArgs = ["--tsconfig", join(pkgRoot, "tsconfig.json"), __filename_local, "daemon", "--port", String(port)];
95
+ spawnArgs = ["--tsconfig", join(pkgRoot, "tsconfig.json"), __filename_local, "run", "--port", String(port)];
96
96
  }
97
97
  else {
98
98
  spawnCmd = process.execPath;
99
- spawnArgs = [__filename_local, "daemon", "--port", String(port)];
99
+ spawnArgs = [__filename_local, "run", "--port", String(port)];
100
100
  }
101
101
  if (tls === true)
102
102
  spawnArgs.push("--tls");
@@ -111,6 +111,7 @@ function spawnDaemon(port, tls) {
111
111
  return child.pid;
112
112
  }
113
113
  switch (command) {
114
+ case "run":
114
115
  case "daemon":
115
116
  await runDaemon();
116
117
  break;
@@ -263,7 +264,8 @@ function runHelp() {
263
264
  console.log(" Usage: lattice [command] [options]");
264
265
  console.log("");
265
266
  console.log(" Commands:");
266
- console.log(" start Start the daemon and open the UI (default)");
267
+ console.log(" start Start the daemon and open the UI");
268
+ console.log(" run Run the daemon in the foreground");
267
269
  console.log(" stop Stop the running daemon");
268
270
  console.log(" restart Stop and restart the daemon");
269
271
  console.log(" status Show daemon status and connection info");
@@ -273,7 +275,7 @@ function runHelp() {
273
275
  console.log(" open Open the UI in the browser");
274
276
  console.log(" config Show configuration paths and settings");
275
277
  console.log(" setup-tls Set up HTTPS (Tailscale or self-signed)");
276
- console.log(" help Show this help message");
278
+ console.log(" help Show this help message (default)");
277
279
  console.log("");
278
280
  console.log(" Options:");
279
281
  console.log(" --port=N Override the server port");
@@ -286,51 +288,148 @@ function runHelp() {
286
288
  console.log("");
287
289
  }
288
290
  async function runSetupTls() {
289
- var { execSync: exec } = await import("node:child_process");
291
+ var { spawnSync } = await import("node:child_process");
292
+ var { createInterface } = await import("node:readline");
293
+ var { mkdirSync, chmodSync, chownSync, statSync } = await import("node:fs");
290
294
  var certsDir = join(getLatticeHome(), "certs");
291
295
  var certPath = join(certsDir, "cert.pem");
292
296
  var keyPath = join(certsDir, "key.pem");
293
- console.log("[lattice] TLS Setup");
297
+ function prompt(question) {
298
+ var rl = createInterface({ input: process.stdin, output: process.stdout });
299
+ return new Promise(function (resolve) {
300
+ rl.question(question, function (answer) {
301
+ rl.close();
302
+ resolve(answer.trim().toLowerCase());
303
+ });
304
+ });
305
+ }
306
+ console.log("");
307
+ console.log(" lattice — TLS Setup");
294
308
  console.log("");
295
309
  var hasTailscale = false;
310
+ var hostname = "";
296
311
  try {
297
- exec("tailscale version", { stdio: "ignore" });
312
+ spawnSync("tailscale", ["version"], { stdio: "ignore" });
298
313
  hasTailscale = true;
314
+ var statusResult = spawnSync("tailscale", ["status", "--json"], { encoding: "utf-8" });
315
+ if (statusResult.status === 0) {
316
+ hostname = JSON.parse(statusResult.stdout).Self.DNSName.replace(/\.$/, "");
317
+ }
299
318
  }
300
319
  catch { }
301
- if (hasTailscale) {
302
- console.log(" Tailscale detected. To use trusted Tailscale HTTPS certs:");
320
+ if (hasTailscale && hostname) {
321
+ console.log(" Tailscale detected: " + hostname);
303
322
  console.log("");
304
- try {
305
- var dnsName = exec("tailscale status --json", { encoding: "utf-8" });
306
- var hostname = JSON.parse(dnsName).Self.DNSName.replace(/\.$/, "");
307
- console.log(" 1. Generate cert:");
308
- console.log(" sudo tailscale cert --cert-file " + certPath + " --key-file " + keyPath + " " + hostname);
323
+ var answer = await prompt(" Set up Tailscale HTTPS certs automatically? [Y/n] ");
324
+ if (answer !== "n" && answer !== "no") {
325
+ if (!existsSync(certsDir)) {
326
+ mkdirSync(certsDir, { recursive: true });
327
+ }
309
328
  console.log("");
310
- console.log(" 2. Enable TLS and restart:");
311
- console.log(" lattice restart --tls");
329
+ console.log(" Generating Tailscale cert (sudo required)...");
312
330
  console.log("");
313
- console.log(" 3. Access at:");
314
- console.log(" https://" + hostname + ":" + loadConfig().port);
315
- }
316
- catch {
317
- console.log(" 1. Run: sudo tailscale cert --cert-file " + certPath + " --key-file " + keyPath + " <your-hostname>.ts.net");
318
- console.log(" 2. Run: lattice restart --tls");
331
+ var certResult = spawnSync("sudo", ["tailscale", "cert", "--cert-file", certPath, "--key-file", keyPath, hostname], { stdio: "inherit" });
332
+ if (certResult.status !== 0) {
333
+ console.error("");
334
+ console.error(" Failed to generate Tailscale certs.");
335
+ console.error(" You can run manually:");
336
+ console.error(" sudo tailscale cert --cert-file " + certPath + " --key-file " + keyPath + " " + hostname);
337
+ process.exit(1);
338
+ }
339
+ var uid = process.getuid ? process.getuid() : 0;
340
+ var gid = process.getgid ? process.getgid() : 0;
341
+ try {
342
+ chownSync(certPath, uid, gid);
343
+ chownSync(keyPath, uid, gid);
344
+ chmodSync(certPath, 0o644);
345
+ chmodSync(keyPath, 0o600);
346
+ }
347
+ catch {
348
+ console.log(" Fixing permissions with sudo...");
349
+ spawnSync("sudo", ["chown", uid + ":" + gid, certPath, keyPath], { stdio: "inherit" });
350
+ spawnSync("sudo", ["chmod", "644", certPath], { stdio: "inherit" });
351
+ spawnSync("sudo", ["chmod", "600", keyPath], { stdio: "inherit" });
352
+ }
353
+ console.log("");
354
+ console.log(" Certs installed and permissions fixed.");
355
+ var config = loadConfig();
356
+ if (!config.tls) {
357
+ var { saveConfig: saveCfg } = await import("./config.js");
358
+ config.tls = true;
359
+ saveCfg(config);
360
+ console.log(" TLS enabled in config.");
361
+ }
362
+ var pid = readPid();
363
+ if (pid !== null && isDaemonRunning(pid)) {
364
+ var restartAnswer = await prompt(" Daemon is running. Restart with TLS? [Y/n] ");
365
+ if (restartAnswer !== "n" && restartAnswer !== "no") {
366
+ console.log(" Restarting daemon...");
367
+ try {
368
+ process.kill(pid, "SIGTERM");
369
+ }
370
+ catch { }
371
+ removePid();
372
+ await new Promise(function (r) { setTimeout(r, 1500); });
373
+ var port = portOverride ?? config.port;
374
+ var childPid = spawnDaemon(port, true);
375
+ writePid(childPid);
376
+ console.log(" Daemon restarted (PID " + childPid + ")");
377
+ }
378
+ }
379
+ console.log("");
380
+ console.log(" Access at: https://" + hostname + ":" + loadConfig().port);
381
+ console.log("");
382
+ return;
319
383
  }
320
384
  }
321
- else {
322
- console.log(" Tailscale not found. Using self-signed certificate.");
385
+ else if (hasTailscale) {
386
+ console.log(" Tailscale detected but could not determine hostname.");
387
+ console.log(" Run 'tailscale status' to verify your connection.");
323
388
  console.log("");
324
389
  }
325
- console.log("");
326
390
  console.log(" ── Self-signed certificate ──");
327
391
  console.log("");
328
- if (existsSync(certPath)) {
392
+ if (existsSync(certPath) && existsSync(keyPath)) {
329
393
  console.log(" Cert exists: " + certPath);
330
- console.log(" To regenerate: rm -rf " + certsDir + " && lattice restart --tls");
394
+ console.log(" To regenerate: rm -rf " + certsDir + " && lattice setup-tls");
331
395
  }
332
396
  else {
333
- console.log(" No cert yet. Run 'lattice restart --tls' to auto-generate one.");
397
+ var selfSignAnswer = await prompt(" Generate a self-signed certificate? [Y/n] ");
398
+ if (selfSignAnswer !== "n" && selfSignAnswer !== "no") {
399
+ var { ensureCerts } = await import("./tls.js");
400
+ try {
401
+ var certs = ensureCerts();
402
+ console.log(" Certificate generated: " + certs.cert);
403
+ var config = loadConfig();
404
+ if (!config.tls) {
405
+ var { saveConfig: saveCfg } = await import("./config.js");
406
+ config.tls = true;
407
+ saveCfg(config);
408
+ console.log(" TLS enabled in config.");
409
+ }
410
+ var pid = readPid();
411
+ if (pid !== null && isDaemonRunning(pid)) {
412
+ var restartAnswer = await prompt(" Daemon is running. Restart with TLS? [Y/n] ");
413
+ if (restartAnswer !== "n" && restartAnswer !== "no") {
414
+ console.log(" Restarting daemon...");
415
+ try {
416
+ process.kill(pid, "SIGTERM");
417
+ }
418
+ catch { }
419
+ removePid();
420
+ await new Promise(function (r) { setTimeout(r, 1500); });
421
+ var port = portOverride ?? config.port;
422
+ var childPid = spawnDaemon(port, true);
423
+ writePid(childPid);
424
+ console.log(" Daemon restarted (PID " + childPid + ")");
425
+ }
426
+ }
427
+ }
428
+ catch (err) {
429
+ console.error(" Failed to generate certificate:", err);
430
+ process.exit(1);
431
+ }
432
+ }
334
433
  }
335
434
  console.log("");
336
435
  console.log(" To trust the self-signed cert (removes browser warnings):");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
5
5
  "license": "MIT",
6
6
  "author": "Aaron Scherer <me@aaronscherer.me>",