@cryptiklemur/lattice 1.36.2 → 1.36.4

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.
@@ -23,15 +23,20 @@ export function UserIsland(props: UserIslandProps) {
23
23
  var ws = useWebSocket();
24
24
  var [updateAvailable, setUpdateAvailable] = useState(false);
25
25
  var [latestVersion, setLatestVersion] = useState<string | null>(null);
26
+ var [currentVersion, setCurrentVersion] = useState(pkg.version);
26
27
 
27
28
  useEffect(function () {
28
29
  function handleUpdateStatus(msg: ServerMessage) {
29
30
  if (msg.type !== "update:status") return;
30
- var data = msg as { type: string; updateAvailable: boolean; latestVersion: string | null };
31
+ var data = msg as { type: string; updateAvailable: boolean; latestVersion: string | null; currentVersion: string };
31
32
  setUpdateAvailable(data.updateAvailable);
32
33
  setLatestVersion(data.latestVersion);
34
+ if (data.currentVersion && data.currentVersion !== "0.0.0") {
35
+ setCurrentVersion(data.currentVersion);
36
+ }
33
37
  }
34
38
  ws.subscribe("update:status", handleUpdateStatus);
39
+ ws.send({ type: "update:check" } as any);
35
40
  return function () { ws.unsubscribe("update:status", handleUpdateStatus); };
36
41
  }, []);
37
42
 
@@ -89,7 +94,7 @@ export function UserIsland(props: UserIslandProps) {
89
94
  {props.nodeName}
90
95
  </div>
91
96
  <div className="text-[10px] font-mono flex items-center gap-1">
92
- <span className="text-base-content/30">{"v" + pkg.version}</span>
97
+ <span className="text-base-content/30">{"v" + currentVersion}</span>
93
98
  {updateAvailable && latestVersion && (
94
99
  <span className="flex items-center gap-0.5 text-primary/70">
95
100
  <ArrowUpCircle size={9} />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cryptiklemur/lattice",
3
- "version": "1.36.2",
3
+ "version": "1.36.4",
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>",
@@ -1,4 +1,7 @@
1
- import { chmodSync, renameSync, writeFileSync } from "node:fs";
1
+ import { chmodSync, renameSync, writeFileSync, accessSync, constants as fsConstants } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { execSync } from "node:child_process";
2
5
  import type { ClientMessage } from "@lattice/shared";
3
6
  import { registerHandler } from "../ws/router";
4
7
  import { sendTo, broadcast } from "../ws/broadcast";
@@ -43,11 +46,28 @@ async function downloadBinaryUpdate(): Promise<{ success: boolean; message: stri
43
46
 
44
47
  var binary = new Uint8Array(await downloadRes.arrayBuffer());
45
48
  var execPath = process.execPath;
46
- var tmpPath = execPath + ".update";
49
+ var tmpPath = join(tmpdir(), "lattice-update-" + Date.now());
47
50
 
48
51
  writeFileSync(tmpPath, binary);
49
52
  chmodSync(tmpPath, 0o755);
50
- renameSync(tmpPath, execPath);
53
+
54
+ var needsSudo = false;
55
+ try {
56
+ accessSync(execPath, fsConstants.W_OK);
57
+ } catch {
58
+ needsSudo = true;
59
+ }
60
+
61
+ if (needsSudo) {
62
+ try {
63
+ execSync("sudo mv " + JSON.stringify(tmpPath) + " " + JSON.stringify(execPath), { stdio: "pipe", timeout: 10000 });
64
+ execSync("sudo chmod +x " + JSON.stringify(execPath), { stdio: "pipe", timeout: 5000 });
65
+ } catch {
66
+ return { success: false, message: "Update downloaded but needs sudo to install. Run: sudo mv " + tmpPath + " " + execPath };
67
+ }
68
+ } else {
69
+ renameSync(tmpPath, execPath);
70
+ }
51
71
 
52
72
  return { success: true, message: "Updated successfully. Restart the server to apply." };
53
73
  } catch (err) {
@@ -243,10 +243,27 @@ async function runUpdate(): Promise<void> {
243
243
  console.log("[lattice] Downloading " + assetName + "...");
244
244
  var downloadRes = await fetch(asset.browser_download_url);
245
245
  var binary = new Uint8Array(await downloadRes.arrayBuffer());
246
- var tmpPath = process.execPath + ".update";
246
+ var { tmpdir } = await import("node:os");
247
+ var tmpPath = join(tmpdir(), "lattice-update-" + Date.now());
247
248
  writeFileSync(tmpPath, binary);
248
249
  chmodSync(tmpPath, 0o755);
249
- renameSync(tmpPath, process.execPath);
250
+
251
+ var needsSudo = false;
252
+ try {
253
+ var { accessSync, constants: fsConstants } = await import("node:fs");
254
+ accessSync(process.execPath, fsConstants.W_OK);
255
+ } catch {
256
+ needsSudo = true;
257
+ }
258
+
259
+ if (needsSudo) {
260
+ console.log("[lattice] Needs elevated permissions to replace binary...");
261
+ var { execSync } = await import("node:child_process");
262
+ execSync("sudo mv " + JSON.stringify(tmpPath) + " " + JSON.stringify(process.execPath), { stdio: "inherit" });
263
+ execSync("sudo chmod +x " + JSON.stringify(process.execPath), { stdio: "inherit" });
264
+ } else {
265
+ renameSync(tmpPath, process.execPath);
266
+ }
250
267
  code = 0;
251
268
  } catch (err) {
252
269
  console.error("[lattice] Download failed:", err instanceof Error ? err.message : String(err));