seshmux 0.1.3 → 0.1.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.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +1 -1
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page.js +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/app/page-cd707a4bc18d9497.js +1 -0
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/seshmux-server.js +154 -124
- package/bin/seshmux.js +46 -2
- package/package.json +2 -2
- package/.next/standalone/.next/static/chunks/app/page-7104066577bb8e5f.js +0 -1
- /package/.next/standalone/.next/static/{kPJ9M1iZAJK4qUIbW1h9b → Xjuy0Fy4Y1goqBeK0eRol}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{kPJ9M1iZAJK4qUIbW1h9b → Xjuy0Fy4Y1goqBeK0eRol}/_ssgManifest.js +0 -0
|
@@ -4710,11 +4710,143 @@ var init_search2 = __esm({
|
|
|
4710
4710
|
}
|
|
4711
4711
|
});
|
|
4712
4712
|
|
|
4713
|
+
// server/lib/update.ts
|
|
4714
|
+
function compareVersions(a, b) {
|
|
4715
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4716
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4717
|
+
const len = Math.max(pa.length, pb.length);
|
|
4718
|
+
for (let i = 0; i < len; i++) {
|
|
4719
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
4720
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
4721
|
+
}
|
|
4722
|
+
return 0;
|
|
4723
|
+
}
|
|
4724
|
+
function isDaemonStale(daemonVersion, serverVersion) {
|
|
4725
|
+
if (!daemonVersion || !serverVersion) return false;
|
|
4726
|
+
return compareVersions(serverVersion, daemonVersion) > 0;
|
|
4727
|
+
}
|
|
4728
|
+
function detectInstallMethod(opts) {
|
|
4729
|
+
const p = opts.argvRealPath;
|
|
4730
|
+
if (p.includes("/_npx/") || p.includes("\\_npx\\")) return "npx";
|
|
4731
|
+
const prefix = realpath2(opts.globalPrefix);
|
|
4732
|
+
if (prefix && realpath2(p).startsWith(prefix)) return "global";
|
|
4733
|
+
return "local";
|
|
4734
|
+
}
|
|
4735
|
+
function realpath2(p) {
|
|
4736
|
+
if (!p) return "";
|
|
4737
|
+
try {
|
|
4738
|
+
return (0, import_node_fs9.realpathSync)(p);
|
|
4739
|
+
} catch {
|
|
4740
|
+
return p;
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
function resolveArgvRealPath() {
|
|
4744
|
+
const raw = process.argv[1] ?? "";
|
|
4745
|
+
try {
|
|
4746
|
+
return (0, import_node_fs9.realpathSync)(raw);
|
|
4747
|
+
} catch {
|
|
4748
|
+
return raw;
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
function defaultExec(cmd, args) {
|
|
4752
|
+
return new Promise((resolve, reject) => {
|
|
4753
|
+
(0, import_node_child_process7.execFile)(cmd, args, { timeout: 12e4, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
4754
|
+
if (err) reject(Object.assign(err, { stdout, stderr }));
|
|
4755
|
+
else resolve({ stdout, stderr });
|
|
4756
|
+
});
|
|
4757
|
+
});
|
|
4758
|
+
}
|
|
4759
|
+
async function defaultGlobalPrefix(exec) {
|
|
4760
|
+
try {
|
|
4761
|
+
const { stdout } = await exec("npm", ["prefix", "-g"]);
|
|
4762
|
+
return stdout.trim();
|
|
4763
|
+
} catch {
|
|
4764
|
+
return "";
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
async function checkUpdate(deps = {}) {
|
|
4768
|
+
const current = deps.current ?? process.env.SESHMUX_VERSION ?? process.env.npm_package_version ?? "0.0.0";
|
|
4769
|
+
const fetchFn = deps.fetchFn ?? defaultFetch;
|
|
4770
|
+
const exec = deps.exec ?? defaultExec;
|
|
4771
|
+
const argvRealPath = deps.argvRealPath ?? resolveArgvRealPath();
|
|
4772
|
+
const globalPrefix = deps.globalPrefix ?? await defaultGlobalPrefix(exec);
|
|
4773
|
+
const installMethod = detectInstallMethod({ argvRealPath, globalPrefix });
|
|
4774
|
+
let latest = current;
|
|
4775
|
+
if (cache && Date.now() - cache.ts < CACHE_TTL_MS) {
|
|
4776
|
+
latest = cache.latest;
|
|
4777
|
+
} else {
|
|
4778
|
+
latest = await fetchLatest(fetchFn, current);
|
|
4779
|
+
cache = { ts: Date.now(), latest };
|
|
4780
|
+
}
|
|
4781
|
+
return {
|
|
4782
|
+
current,
|
|
4783
|
+
latest,
|
|
4784
|
+
updateAvailable: compareVersions(latest, current) > 0,
|
|
4785
|
+
installMethod
|
|
4786
|
+
};
|
|
4787
|
+
}
|
|
4788
|
+
async function fetchLatest(fetchFn, current) {
|
|
4789
|
+
const controller = new AbortController();
|
|
4790
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
4791
|
+
try {
|
|
4792
|
+
const res = await fetchFn(REGISTRY_URL, { signal: controller.signal });
|
|
4793
|
+
if (!res.ok) return current;
|
|
4794
|
+
const body = await res.json();
|
|
4795
|
+
return typeof body.version === "string" ? body.version : current;
|
|
4796
|
+
} catch {
|
|
4797
|
+
return current;
|
|
4798
|
+
} finally {
|
|
4799
|
+
clearTimeout(timer);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
async function applyUpdate(deps) {
|
|
4803
|
+
if (deps.installMethod === "npx") {
|
|
4804
|
+
throw new Error("cannot self-update an npx invocation \u2014 run `npx seshmux@latest` next time");
|
|
4805
|
+
}
|
|
4806
|
+
const exec = deps.exec ?? defaultExec;
|
|
4807
|
+
const previous = deps.current;
|
|
4808
|
+
const version2 = /^\d+\.\d+\.\d+[A-Za-z0-9.\-+]*$/.test(deps.target ?? "") ? deps.target : null;
|
|
4809
|
+
const spec = `seshmux@${version2 ?? "latest"}`;
|
|
4810
|
+
try {
|
|
4811
|
+
const { stdout, stderr } = await exec("npm", ["i", "-g", spec, "--prefer-online"]);
|
|
4812
|
+
return { ok: true, log: [stdout, stderr].filter(Boolean).join("\n"), previous };
|
|
4813
|
+
} catch (e) {
|
|
4814
|
+
const err = e;
|
|
4815
|
+
const log = [err.stdout, err.stderr, err.message].filter(Boolean).join("\n");
|
|
4816
|
+
return { ok: false, log, previous };
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
var import_node_child_process7, import_node_fs9, REGISTRY_URL, FETCH_TIMEOUT_MS, CACHE_TTL_MS, defaultFetch, cache;
|
|
4820
|
+
var init_update = __esm({
|
|
4821
|
+
"server/lib/update.ts"() {
|
|
4822
|
+
"use strict";
|
|
4823
|
+
import_node_child_process7 = require("node:child_process");
|
|
4824
|
+
import_node_fs9 = require("node:fs");
|
|
4825
|
+
REGISTRY_URL = "https://registry.npmjs.org/seshmux/latest";
|
|
4826
|
+
FETCH_TIMEOUT_MS = 5e3;
|
|
4827
|
+
CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
4828
|
+
defaultFetch = (url, init) => fetch(url, init);
|
|
4829
|
+
cache = null;
|
|
4830
|
+
}
|
|
4831
|
+
});
|
|
4832
|
+
|
|
4713
4833
|
// server/routes/env.ts
|
|
4714
4834
|
var env_exports = {};
|
|
4715
4835
|
__export(env_exports, {
|
|
4716
4836
|
default: () => envRoutes
|
|
4717
4837
|
});
|
|
4838
|
+
async function realDaemonVersion() {
|
|
4839
|
+
let conn = null;
|
|
4840
|
+
try {
|
|
4841
|
+
conn = await dial();
|
|
4842
|
+
const { version: version2 } = await conn.hello();
|
|
4843
|
+
return version2 || null;
|
|
4844
|
+
} catch {
|
|
4845
|
+
return null;
|
|
4846
|
+
} finally {
|
|
4847
|
+
conn?.close();
|
|
4848
|
+
}
|
|
4849
|
+
}
|
|
4718
4850
|
function commandPreview(commands) {
|
|
4719
4851
|
return {
|
|
4720
4852
|
fresh: commands.fresh("").join(" "),
|
|
@@ -4725,12 +4857,16 @@ function commandPreview(commands) {
|
|
|
4725
4857
|
}
|
|
4726
4858
|
async function envRoutes(f, deps = {}) {
|
|
4727
4859
|
const bridgeStatus2 = deps.bridgeStatus ?? (() => bridgeStatus());
|
|
4860
|
+
const daemonVersion = deps.daemonVersion ?? realDaemonVersion;
|
|
4861
|
+
const serverVersion = deps.serverVersion ?? (() => process.env.SESHMUX_VERSION ?? "");
|
|
4728
4862
|
f.get("/api/env", async () => {
|
|
4729
|
-
const [env, bridge, providers] = await Promise.all([
|
|
4863
|
+
const [env, bridge, providers, dVersion] = await Promise.all([
|
|
4730
4864
|
detectEnv(),
|
|
4731
4865
|
bridgeStatus2().catch(() => ({ claude: false, codex: false })),
|
|
4732
|
-
getProviders()
|
|
4866
|
+
getProviders(),
|
|
4867
|
+
daemonVersion().catch(() => null)
|
|
4733
4868
|
]);
|
|
4869
|
+
const sVersion = serverVersion();
|
|
4734
4870
|
const commands = {};
|
|
4735
4871
|
const teams = {};
|
|
4736
4872
|
for (const p of providers) {
|
|
@@ -4744,7 +4880,14 @@ async function envRoutes(f, deps = {}) {
|
|
|
4744
4880
|
codex: { registered: bridge.codex }
|
|
4745
4881
|
},
|
|
4746
4882
|
commands,
|
|
4747
|
-
teams
|
|
4883
|
+
teams,
|
|
4884
|
+
// The daemon outlives server updates by design — tell the UI when it's older than us
|
|
4885
|
+
// so the user can be nudged to fully restart. Never auto-restart: that kills live PTYs.
|
|
4886
|
+
daemon: {
|
|
4887
|
+
version: dVersion,
|
|
4888
|
+
serverVersion: sVersion || null,
|
|
4889
|
+
stale: isDaemonStale(dVersion, sVersion)
|
|
4890
|
+
}
|
|
4748
4891
|
};
|
|
4749
4892
|
});
|
|
4750
4893
|
}
|
|
@@ -4754,6 +4897,8 @@ var init_env = __esm({
|
|
|
4754
4897
|
init_detect();
|
|
4755
4898
|
init_registry();
|
|
4756
4899
|
init_types();
|
|
4900
|
+
init_daemon_client();
|
|
4901
|
+
init_update();
|
|
4757
4902
|
}
|
|
4758
4903
|
});
|
|
4759
4904
|
|
|
@@ -5016,10 +5161,11 @@ async function termRoutes(f, deps = {}) {
|
|
|
5016
5161
|
try {
|
|
5017
5162
|
conn = await (deps.dialFn ?? dial)();
|
|
5018
5163
|
const { data } = await withTimeout(conn.history(req.params.ptyId, lines), 5e3, "history timed out");
|
|
5019
|
-
return reply.send({ data });
|
|
5164
|
+
return reply.send({ supported: true, data });
|
|
5020
5165
|
} catch (e) {
|
|
5021
5166
|
const msg = e.message || String(e);
|
|
5022
|
-
|
|
5167
|
+
if (msg.includes("unknown method")) return reply.send({ supported: false, data: "" });
|
|
5168
|
+
return reply.code(500).send({ error: msg });
|
|
5023
5169
|
} finally {
|
|
5024
5170
|
conn?.close();
|
|
5025
5171
|
}
|
|
@@ -5602,7 +5748,7 @@ async function notifyRoutes(f) {
|
|
|
5602
5748
|
return { ok: true, delivered: false, reason: "disabled" };
|
|
5603
5749
|
}
|
|
5604
5750
|
await new Promise((resolve) => {
|
|
5605
|
-
const child = (0,
|
|
5751
|
+
const child = (0, import_node_child_process8.execFile)(
|
|
5606
5752
|
"osascript",
|
|
5607
5753
|
[
|
|
5608
5754
|
"-e",
|
|
@@ -5620,11 +5766,11 @@ async function notifyRoutes(f) {
|
|
|
5620
5766
|
return { ok: true, delivered: true };
|
|
5621
5767
|
});
|
|
5622
5768
|
}
|
|
5623
|
-
var
|
|
5769
|
+
var import_node_child_process8, import_promises18, import_node_path22, import_node_os10;
|
|
5624
5770
|
var init_notify = __esm({
|
|
5625
5771
|
"server/routes/notify.ts"() {
|
|
5626
5772
|
"use strict";
|
|
5627
|
-
|
|
5773
|
+
import_node_child_process8 = require("node:child_process");
|
|
5628
5774
|
import_promises18 = require("node:fs/promises");
|
|
5629
5775
|
import_node_path22 = __toESM(require("node:path"));
|
|
5630
5776
|
import_node_os10 = __toESM(require("node:os"));
|
|
@@ -5656,122 +5802,6 @@ var init_approval = __esm({
|
|
|
5656
5802
|
}
|
|
5657
5803
|
});
|
|
5658
5804
|
|
|
5659
|
-
// server/lib/update.ts
|
|
5660
|
-
function compareVersions(a, b) {
|
|
5661
|
-
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
5662
|
-
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
5663
|
-
const len = Math.max(pa.length, pb.length);
|
|
5664
|
-
for (let i = 0; i < len; i++) {
|
|
5665
|
-
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
5666
|
-
if (d !== 0) return d < 0 ? -1 : 1;
|
|
5667
|
-
}
|
|
5668
|
-
return 0;
|
|
5669
|
-
}
|
|
5670
|
-
function detectInstallMethod(opts) {
|
|
5671
|
-
const p = opts.argvRealPath;
|
|
5672
|
-
if (p.includes("/_npx/") || p.includes("\\_npx\\")) return "npx";
|
|
5673
|
-
const prefix = realpath2(opts.globalPrefix);
|
|
5674
|
-
if (prefix && realpath2(p).startsWith(prefix)) return "global";
|
|
5675
|
-
return "local";
|
|
5676
|
-
}
|
|
5677
|
-
function realpath2(p) {
|
|
5678
|
-
if (!p) return "";
|
|
5679
|
-
try {
|
|
5680
|
-
return (0, import_node_fs9.realpathSync)(p);
|
|
5681
|
-
} catch {
|
|
5682
|
-
return p;
|
|
5683
|
-
}
|
|
5684
|
-
}
|
|
5685
|
-
function resolveArgvRealPath() {
|
|
5686
|
-
const raw = process.argv[1] ?? "";
|
|
5687
|
-
try {
|
|
5688
|
-
return (0, import_node_fs9.realpathSync)(raw);
|
|
5689
|
-
} catch {
|
|
5690
|
-
return raw;
|
|
5691
|
-
}
|
|
5692
|
-
}
|
|
5693
|
-
function defaultExec(cmd, args) {
|
|
5694
|
-
return new Promise((resolve, reject) => {
|
|
5695
|
-
(0, import_node_child_process8.execFile)(cmd, args, { timeout: 12e4, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
5696
|
-
if (err) reject(Object.assign(err, { stdout, stderr }));
|
|
5697
|
-
else resolve({ stdout, stderr });
|
|
5698
|
-
});
|
|
5699
|
-
});
|
|
5700
|
-
}
|
|
5701
|
-
async function defaultGlobalPrefix(exec) {
|
|
5702
|
-
try {
|
|
5703
|
-
const { stdout } = await exec("npm", ["prefix", "-g"]);
|
|
5704
|
-
return stdout.trim();
|
|
5705
|
-
} catch {
|
|
5706
|
-
return "";
|
|
5707
|
-
}
|
|
5708
|
-
}
|
|
5709
|
-
async function checkUpdate(deps = {}) {
|
|
5710
|
-
const current = deps.current ?? process.env.SESHMUX_VERSION ?? process.env.npm_package_version ?? "0.0.0";
|
|
5711
|
-
const fetchFn = deps.fetchFn ?? defaultFetch;
|
|
5712
|
-
const exec = deps.exec ?? defaultExec;
|
|
5713
|
-
const argvRealPath = deps.argvRealPath ?? resolveArgvRealPath();
|
|
5714
|
-
const globalPrefix = deps.globalPrefix ?? await defaultGlobalPrefix(exec);
|
|
5715
|
-
const installMethod = detectInstallMethod({ argvRealPath, globalPrefix });
|
|
5716
|
-
let latest = current;
|
|
5717
|
-
if (cache && Date.now() - cache.ts < CACHE_TTL_MS) {
|
|
5718
|
-
latest = cache.latest;
|
|
5719
|
-
} else {
|
|
5720
|
-
latest = await fetchLatest(fetchFn, current);
|
|
5721
|
-
cache = { ts: Date.now(), latest };
|
|
5722
|
-
}
|
|
5723
|
-
return {
|
|
5724
|
-
current,
|
|
5725
|
-
latest,
|
|
5726
|
-
updateAvailable: compareVersions(latest, current) > 0,
|
|
5727
|
-
installMethod
|
|
5728
|
-
};
|
|
5729
|
-
}
|
|
5730
|
-
async function fetchLatest(fetchFn, current) {
|
|
5731
|
-
const controller = new AbortController();
|
|
5732
|
-
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
5733
|
-
try {
|
|
5734
|
-
const res = await fetchFn(REGISTRY_URL, { signal: controller.signal });
|
|
5735
|
-
if (!res.ok) return current;
|
|
5736
|
-
const body = await res.json();
|
|
5737
|
-
return typeof body.version === "string" ? body.version : current;
|
|
5738
|
-
} catch {
|
|
5739
|
-
return current;
|
|
5740
|
-
} finally {
|
|
5741
|
-
clearTimeout(timer);
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
5744
|
-
async function applyUpdate(deps) {
|
|
5745
|
-
if (deps.installMethod === "npx") {
|
|
5746
|
-
throw new Error("cannot self-update an npx invocation \u2014 run `npx seshmux@latest` next time");
|
|
5747
|
-
}
|
|
5748
|
-
const exec = deps.exec ?? defaultExec;
|
|
5749
|
-
const previous = deps.current;
|
|
5750
|
-
const version2 = /^\d+\.\d+\.\d+[A-Za-z0-9.\-+]*$/.test(deps.target ?? "") ? deps.target : null;
|
|
5751
|
-
const spec = `seshmux@${version2 ?? "latest"}`;
|
|
5752
|
-
try {
|
|
5753
|
-
const { stdout, stderr } = await exec("npm", ["i", "-g", spec, "--prefer-online"]);
|
|
5754
|
-
return { ok: true, log: [stdout, stderr].filter(Boolean).join("\n"), previous };
|
|
5755
|
-
} catch (e) {
|
|
5756
|
-
const err = e;
|
|
5757
|
-
const log = [err.stdout, err.stderr, err.message].filter(Boolean).join("\n");
|
|
5758
|
-
return { ok: false, log, previous };
|
|
5759
|
-
}
|
|
5760
|
-
}
|
|
5761
|
-
var import_node_child_process8, import_node_fs9, REGISTRY_URL, FETCH_TIMEOUT_MS, CACHE_TTL_MS, defaultFetch, cache;
|
|
5762
|
-
var init_update = __esm({
|
|
5763
|
-
"server/lib/update.ts"() {
|
|
5764
|
-
"use strict";
|
|
5765
|
-
import_node_child_process8 = require("node:child_process");
|
|
5766
|
-
import_node_fs9 = require("node:fs");
|
|
5767
|
-
REGISTRY_URL = "https://registry.npmjs.org/seshmux/latest";
|
|
5768
|
-
FETCH_TIMEOUT_MS = 5e3;
|
|
5769
|
-
CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
5770
|
-
defaultFetch = (url, init) => fetch(url, init);
|
|
5771
|
-
cache = null;
|
|
5772
|
-
}
|
|
5773
|
-
});
|
|
5774
|
-
|
|
5775
5805
|
// server/routes/update.ts
|
|
5776
5806
|
var update_exports = {};
|
|
5777
5807
|
__export(update_exports, {
|
package/bin/seshmux.js
CHANGED
|
@@ -18,7 +18,17 @@ const http = require('node:http');
|
|
|
18
18
|
const { randomBytes } = require('node:crypto');
|
|
19
19
|
const path = require('node:path');
|
|
20
20
|
const fs = require('node:fs');
|
|
21
|
-
const { ensureDaemon } = require('../daemon/ensure');
|
|
21
|
+
const { ensureDaemon, pidAlive, paths, configDir } = require('../daemon/ensure');
|
|
22
|
+
|
|
23
|
+
// The daemon's pid, from the pidfile it writes in the config dir. null when it isn't running.
|
|
24
|
+
function readDaemonPid() {
|
|
25
|
+
try {
|
|
26
|
+
const pid = Number(fs.readFileSync(paths(configDir()).pid, 'utf8').trim());
|
|
27
|
+
return Number.isInteger(pid) && pid > 0 && pidAlive(pid) ? pid : null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
22
32
|
|
|
23
33
|
// Absolute path to THIS cli entry, inherited by the server child. The MCP
|
|
24
34
|
// bridge registration writes it into agent configs (`node <bin> mcp-bridge`) —
|
|
@@ -88,12 +98,17 @@ function parseArgs(argv) {
|
|
|
88
98
|
// $PORT is honoured because server/index.ts already does — without this the CLI
|
|
89
99
|
// silently ignored it and booted on 4700 anyway. --port still wins over the env.
|
|
90
100
|
const envPort = Number(process.env.PORT);
|
|
91
|
-
const args = {
|
|
101
|
+
const args = {
|
|
102
|
+
port: Number.isInteger(envPort) && envPort > 0 ? envPort : 4700,
|
|
103
|
+
noOpen: false,
|
|
104
|
+
restartDaemon: false,
|
|
105
|
+
};
|
|
92
106
|
for (let i = 0; i < argv.length; i++) {
|
|
93
107
|
const a = argv[i];
|
|
94
108
|
if (a === '--port' || a === '-p') args.port = Number(argv[++i]);
|
|
95
109
|
else if (a.startsWith('--port=')) args.port = Number(a.slice(7));
|
|
96
110
|
else if (a === '--no-open') args.noOpen = true;
|
|
111
|
+
else if (a === '--restart-daemon') args.restartDaemon = true;
|
|
97
112
|
}
|
|
98
113
|
if (!Number.isInteger(args.port) || args.port <= 0) args.port = 4700;
|
|
99
114
|
return args;
|
|
@@ -143,6 +158,35 @@ async function main() {
|
|
|
143
158
|
|
|
144
159
|
const args = parseArgs(argv);
|
|
145
160
|
|
|
161
|
+
// --restart-daemon: the ONLY way to upgrade the daemon. ensureDaemon() treats any daemon that
|
|
162
|
+
// answers hello as 'ok' and reuses it (daemon/ensure.js classify()), which is what keeps your
|
|
163
|
+
// sessions alive across server updates — but it also means a daemon started months ago runs
|
|
164
|
+
// forever, missing every RPC added since. Restarting seshmux does NOT replace it. This does.
|
|
165
|
+
//
|
|
166
|
+
// Destructive on purpose, so it is explicit and never automatic: tmux-tier sessions rehydrate
|
|
167
|
+
// from `tmux ls` and survive, but PLAIN-tier PTYs die with the daemon. Hence a flag the user
|
|
168
|
+
// types, not something the update flow does behind their back.
|
|
169
|
+
if (args.restartDaemon) {
|
|
170
|
+
const before = readDaemonPid();
|
|
171
|
+
if (before) {
|
|
172
|
+
console.log(`[seshmux] stopping daemon ${before} — tmux-backed sessions survive; any non-tmux PTYs will end`);
|
|
173
|
+
try {
|
|
174
|
+
process.kill(before, 'SIGTERM');
|
|
175
|
+
} catch {
|
|
176
|
+
/* already gone */
|
|
177
|
+
}
|
|
178
|
+
for (let i = 0; i < 40 && pidAlive(before); i++) await new Promise((r) => setTimeout(r, 100));
|
|
179
|
+
if (pidAlive(before)) {
|
|
180
|
+
console.error(`[seshmux] daemon ${before} did not stop; not starting a second one`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
console.log('[seshmux] no daemon running');
|
|
185
|
+
}
|
|
186
|
+
const { spawned } = await ensureDaemon();
|
|
187
|
+
console.log(`[seshmux] daemon ${spawned ? 'restarted' : 'already up'} (pid ${readDaemonPid() ?? '?'})`);
|
|
188
|
+
}
|
|
189
|
+
|
|
146
190
|
// If a healthy seshmux already runs on the requested port range, just open the
|
|
147
191
|
// browser to it — no duplicate server, no port fight.
|
|
148
192
|
const reuse = await resolvePort(args.port);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seshmux",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Local-first mission control for AI coding agents (Claude Code + Codex)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"seshmux": "bin/seshmux.js"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"predev": "node -e \"require('./daemon/ensure').ensureDaemon()\"",
|
|
13
|
-
"dev": "tsx server/index.ts",
|
|
13
|
+
"dev": "PORT=${PORT:-4800} tsx server/index.ts",
|
|
14
14
|
"build": "bash scripts/build-standalone.sh",
|
|
15
15
|
"start": "node bin/seshmux.js",
|
|
16
16
|
"lint:styles": "bash scripts/lint-styles.sh",
|