@serendb/serendesktop 0.1.2 → 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/dist/server.js +143 -42
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -4,9 +4,10 @@ import { exec as exec2 } from "child_process";
|
|
|
4
4
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
5
5
|
import { createServer as createServer2 } from "http";
|
|
6
6
|
import { request as httpsRequest } from "https";
|
|
7
|
-
import { homedir as
|
|
7
|
+
import { homedir as homedir6, platform as platform4 } from "os";
|
|
8
8
|
import { join as join7, extname as extname2 } from "path";
|
|
9
9
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
10
|
+
import { createRequire as createRequire2 } from "module";
|
|
10
11
|
import { WebSocketServer } from "ws";
|
|
11
12
|
|
|
12
13
|
// src/events.ts
|
|
@@ -209,6 +210,16 @@ import { readFile, writeFile } from "fs/promises";
|
|
|
209
210
|
import { randomUUID } from "crypto";
|
|
210
211
|
import { resolve } from "path";
|
|
211
212
|
import { platform } from "os";
|
|
213
|
+
function isAuthError(message) {
|
|
214
|
+
const lower = message.toLowerCase();
|
|
215
|
+
return lower.includes("invalid api key") || lower.includes("authentication required") || lower.includes("auth required") || lower.includes("please run /login") || lower.includes("authrequired");
|
|
216
|
+
}
|
|
217
|
+
function authErrorMessage(agentType) {
|
|
218
|
+
if (agentType === "claude-code") {
|
|
219
|
+
return "Claude Code is not logged in. Please open a terminal and run:\n\n claude login\n\nThen try starting the agent again.";
|
|
220
|
+
}
|
|
221
|
+
return "Agent authentication required. Please log in via the agent CLI first.";
|
|
222
|
+
}
|
|
212
223
|
var sessions = /* @__PURE__ */ new Map();
|
|
213
224
|
function createClient(sessionId) {
|
|
214
225
|
return {
|
|
@@ -222,8 +233,8 @@ function createClient(sessionId) {
|
|
|
222
233
|
toolCall: params.toolCall,
|
|
223
234
|
options: params.options
|
|
224
235
|
});
|
|
225
|
-
const optionId = await new Promise((
|
|
226
|
-
session.pendingPermissions.set(requestId,
|
|
236
|
+
const optionId = await new Promise((resolve4, reject) => {
|
|
237
|
+
session.pendingPermissions.set(requestId, resolve4);
|
|
227
238
|
setTimeout(() => {
|
|
228
239
|
session.pendingPermissions.delete(requestId);
|
|
229
240
|
reject(new Error("Permission request timed out"));
|
|
@@ -256,8 +267,8 @@ function createClient(sessionId) {
|
|
|
256
267
|
oldText,
|
|
257
268
|
newText: params.content
|
|
258
269
|
});
|
|
259
|
-
const accepted = await new Promise((
|
|
260
|
-
session.pendingDiffProposals.set(proposalId,
|
|
270
|
+
const accepted = await new Promise((resolve4, reject) => {
|
|
271
|
+
session.pendingDiffProposals.set(proposalId, resolve4);
|
|
261
272
|
setTimeout(() => {
|
|
262
273
|
session.pendingDiffProposals.delete(proposalId);
|
|
263
274
|
reject(new Error("Diff proposal timed out"));
|
|
@@ -376,8 +387,8 @@ ${candidates.map((p) => ` - ${p}`).join("\n")}`
|
|
|
376
387
|
}
|
|
377
388
|
async function isCommandAvailable(command) {
|
|
378
389
|
const which = platform() === "win32" ? "where" : "which";
|
|
379
|
-
return new Promise((
|
|
380
|
-
execFile(which, [command], (err) =>
|
|
390
|
+
return new Promise((resolve4) => {
|
|
391
|
+
execFile(which, [command], (err) => resolve4(!err));
|
|
381
392
|
});
|
|
382
393
|
}
|
|
383
394
|
async function acpSpawn(params) {
|
|
@@ -463,11 +474,13 @@ async function acpSpawn(params) {
|
|
|
463
474
|
session.acpSessionId = sessionResult.sessionId ?? sessionId;
|
|
464
475
|
} catch (err) {
|
|
465
476
|
session.status = "error";
|
|
477
|
+
const rawMessage = err instanceof Error ? err.message : JSON.stringify(err);
|
|
478
|
+
const errorMsg = isAuthError(rawMessage) ? authErrorMessage(agentType) : `Failed to initialize agent: ${rawMessage}`;
|
|
466
479
|
emit("acp://error", {
|
|
467
480
|
sessionId,
|
|
468
|
-
error:
|
|
481
|
+
error: errorMsg
|
|
469
482
|
});
|
|
470
|
-
throw
|
|
483
|
+
throw new Error(errorMsg);
|
|
471
484
|
}
|
|
472
485
|
return {
|
|
473
486
|
id: sessionId,
|
|
@@ -504,11 +517,13 @@ async function acpPrompt(params) {
|
|
|
504
517
|
stopReason: result.stopReason ?? "end_turn"
|
|
505
518
|
});
|
|
506
519
|
} catch (err) {
|
|
520
|
+
const rawMessage = err instanceof Error ? err.message : JSON.stringify(err);
|
|
521
|
+
const errorMsg = isAuthError(rawMessage) ? authErrorMessage(session.agentType) : `Prompt failed: ${rawMessage}`;
|
|
507
522
|
emit("acp://error", {
|
|
508
523
|
sessionId,
|
|
509
|
-
error:
|
|
524
|
+
error: errorMsg
|
|
510
525
|
});
|
|
511
|
-
throw
|
|
526
|
+
throw new Error(errorMsg);
|
|
512
527
|
} finally {
|
|
513
528
|
session.cancelling = false;
|
|
514
529
|
if (session.status === "prompting") {
|
|
@@ -608,7 +623,7 @@ async function acpEnsureClaudeCli() {
|
|
|
608
623
|
return "claude";
|
|
609
624
|
}
|
|
610
625
|
const npmCmd = platform() === "win32" ? "npm.cmd" : "npm";
|
|
611
|
-
return new Promise((
|
|
626
|
+
return new Promise((resolve4, reject) => {
|
|
612
627
|
const proc = execFile(
|
|
613
628
|
npmCmd,
|
|
614
629
|
["install", "-g", "@anthropic-ai/claude-code"],
|
|
@@ -622,7 +637,7 @@ async function acpEnsureClaudeCli() {
|
|
|
622
637
|
return;
|
|
623
638
|
}
|
|
624
639
|
console.log(`[ACP] Claude Code CLI installed: ${stdout}`);
|
|
625
|
-
|
|
640
|
+
resolve4("claude");
|
|
626
641
|
}
|
|
627
642
|
);
|
|
628
643
|
});
|
|
@@ -634,17 +649,17 @@ import { platform as platform2 } from "os";
|
|
|
634
649
|
import { dirname } from "path";
|
|
635
650
|
var os = platform2();
|
|
636
651
|
function exec(cmd, args) {
|
|
637
|
-
return new Promise((
|
|
652
|
+
return new Promise((resolve4, reject) => {
|
|
638
653
|
execFile2(cmd, args, { timeout: 6e4 }, (err, stdout) => {
|
|
639
654
|
if (err) {
|
|
640
655
|
if (err.code === 1 || err.killed) {
|
|
641
|
-
|
|
656
|
+
resolve4("");
|
|
642
657
|
return;
|
|
643
658
|
}
|
|
644
659
|
reject(err);
|
|
645
660
|
return;
|
|
646
661
|
}
|
|
647
|
-
|
|
662
|
+
resolve4(stdout.trim());
|
|
648
663
|
});
|
|
649
664
|
});
|
|
650
665
|
}
|
|
@@ -1403,7 +1418,7 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
1403
1418
|
var processes = /* @__PURE__ */ new Map();
|
|
1404
1419
|
function sendRequest(proc, method, params) {
|
|
1405
1420
|
const id = randomUUID2();
|
|
1406
|
-
return new Promise((
|
|
1421
|
+
return new Promise((resolve4, reject) => {
|
|
1407
1422
|
const timeout = setTimeout(() => {
|
|
1408
1423
|
proc.pendingRequests.delete(id);
|
|
1409
1424
|
reject(new Error(`MCP request timeout: ${method}`));
|
|
@@ -1411,7 +1426,7 @@ function sendRequest(proc, method, params) {
|
|
|
1411
1426
|
proc.pendingRequests.set(id, {
|
|
1412
1427
|
resolve: (v) => {
|
|
1413
1428
|
clearTimeout(timeout);
|
|
1414
|
-
|
|
1429
|
+
resolve4(v);
|
|
1415
1430
|
},
|
|
1416
1431
|
reject: (e) => {
|
|
1417
1432
|
clearTimeout(timeout);
|
|
@@ -1489,13 +1504,13 @@ async function setSetting(params) {
|
|
|
1489
1504
|
await saveSettings(settings);
|
|
1490
1505
|
}
|
|
1491
1506
|
function findAvailablePort() {
|
|
1492
|
-
return new Promise((
|
|
1507
|
+
return new Promise((resolve4, reject) => {
|
|
1493
1508
|
const server = createServer();
|
|
1494
1509
|
server.listen(0, "127.0.0.1", () => {
|
|
1495
1510
|
const addr = server.address();
|
|
1496
1511
|
if (addr && typeof addr === "object") {
|
|
1497
1512
|
const p = addr.port;
|
|
1498
|
-
server.close(() =>
|
|
1513
|
+
server.close(() => resolve4(p));
|
|
1499
1514
|
} else {
|
|
1500
1515
|
server.close(() => reject(new Error("Failed to get port")));
|
|
1501
1516
|
}
|
|
@@ -1535,10 +1550,10 @@ async function findOpenClawEntrypoint() {
|
|
|
1535
1550
|
];
|
|
1536
1551
|
try {
|
|
1537
1552
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
1538
|
-
const path = await new Promise((
|
|
1553
|
+
const path = await new Promise((resolve4, reject) => {
|
|
1539
1554
|
execFile3(cmd, ["openclaw"], (err, stdout) => {
|
|
1540
1555
|
if (err) reject(err);
|
|
1541
|
-
else
|
|
1556
|
+
else resolve4(stdout.trim());
|
|
1542
1557
|
});
|
|
1543
1558
|
});
|
|
1544
1559
|
if (path) candidates.unshift(path);
|
|
@@ -1667,7 +1682,7 @@ async function openclawStart(_params) {
|
|
|
1667
1682
|
emit("openclaw://status-changed", { status: "crashed" });
|
|
1668
1683
|
}
|
|
1669
1684
|
});
|
|
1670
|
-
await new Promise((
|
|
1685
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
1671
1686
|
if (childProcess && !childProcess.killed) {
|
|
1672
1687
|
processStatus = "running";
|
|
1673
1688
|
startedAt = Date.now();
|
|
@@ -1708,7 +1723,7 @@ async function openclawStop(_params) {
|
|
|
1708
1723
|
}
|
|
1709
1724
|
async function openclawRestart(_params) {
|
|
1710
1725
|
await openclawStop({});
|
|
1711
|
-
await new Promise((
|
|
1726
|
+
await new Promise((resolve4) => setTimeout(resolve4, 500));
|
|
1712
1727
|
await openclawStart({});
|
|
1713
1728
|
}
|
|
1714
1729
|
async function openclawStatus(_params) {
|
|
@@ -1723,7 +1738,7 @@ async function openclawStatus(_params) {
|
|
|
1723
1738
|
async function openclawListChannels(_params) {
|
|
1724
1739
|
if (processStatus !== "running") return [];
|
|
1725
1740
|
const entrypoint = await findOpenClawEntrypoint();
|
|
1726
|
-
return new Promise((
|
|
1741
|
+
return new Promise((resolve4) => {
|
|
1727
1742
|
execFile3(
|
|
1728
1743
|
"node",
|
|
1729
1744
|
[entrypoint, "channels", "status", "--json"],
|
|
@@ -1731,7 +1746,7 @@ async function openclawListChannels(_params) {
|
|
|
1731
1746
|
(err, stdout) => {
|
|
1732
1747
|
if (err) {
|
|
1733
1748
|
console.error("[OpenClaw] Failed to list channels:", err);
|
|
1734
|
-
|
|
1749
|
+
resolve4([]);
|
|
1735
1750
|
return;
|
|
1736
1751
|
}
|
|
1737
1752
|
try {
|
|
@@ -1750,9 +1765,9 @@ async function openclawListChannels(_params) {
|
|
|
1750
1765
|
}
|
|
1751
1766
|
channels.length = 0;
|
|
1752
1767
|
channels.push(...result);
|
|
1753
|
-
|
|
1768
|
+
resolve4(result);
|
|
1754
1769
|
} catch {
|
|
1755
|
-
|
|
1770
|
+
resolve4([]);
|
|
1756
1771
|
}
|
|
1757
1772
|
}
|
|
1758
1773
|
);
|
|
@@ -1823,14 +1838,14 @@ async function openclawConnectChannel(params) {
|
|
|
1823
1838
|
async function openclawDisconnectChannel(params) {
|
|
1824
1839
|
const { channelId } = params;
|
|
1825
1840
|
const entrypoint = await findOpenClawEntrypoint();
|
|
1826
|
-
return new Promise((
|
|
1841
|
+
return new Promise((resolve4, reject) => {
|
|
1827
1842
|
execFile3(
|
|
1828
1843
|
"node",
|
|
1829
1844
|
[entrypoint, "channels", "remove", "--channel", channelId, "--delete"],
|
|
1830
1845
|
{ cwd: OPENCLAW_DIR, timeout: 1e4 },
|
|
1831
1846
|
(err) => {
|
|
1832
1847
|
if (err) reject(new Error(`Failed to disconnect: ${err.message}`));
|
|
1833
|
-
else
|
|
1848
|
+
else resolve4();
|
|
1834
1849
|
}
|
|
1835
1850
|
);
|
|
1836
1851
|
});
|
|
@@ -1885,7 +1900,7 @@ async function openclawGrantApproval(params) {
|
|
|
1885
1900
|
async function openclawGetQr(params) {
|
|
1886
1901
|
const { platform: plat } = params;
|
|
1887
1902
|
const entrypoint = await findOpenClawEntrypoint();
|
|
1888
|
-
return new Promise((
|
|
1903
|
+
return new Promise((resolve4, reject) => {
|
|
1889
1904
|
execFile3(
|
|
1890
1905
|
"node",
|
|
1891
1906
|
[entrypoint, "channels", "qr", "--platform", plat, "--json"],
|
|
@@ -1895,9 +1910,9 @@ async function openclawGetQr(params) {
|
|
|
1895
1910
|
else {
|
|
1896
1911
|
try {
|
|
1897
1912
|
const data = JSON.parse(stdout);
|
|
1898
|
-
|
|
1913
|
+
resolve4(data.qr || data.qrCode || stdout.trim());
|
|
1899
1914
|
} catch {
|
|
1900
|
-
|
|
1915
|
+
resolve4(stdout.trim());
|
|
1901
1916
|
}
|
|
1902
1917
|
}
|
|
1903
1918
|
}
|
|
@@ -1969,11 +1984,93 @@ async function stopWatching() {
|
|
|
1969
1984
|
});
|
|
1970
1985
|
}
|
|
1971
1986
|
|
|
1987
|
+
// src/handlers/updater.ts
|
|
1988
|
+
import { spawn as spawn3 } from "child_process";
|
|
1989
|
+
import { platform as platform3, homedir as homedir4 } from "os";
|
|
1990
|
+
import { resolve as resolve3 } from "path";
|
|
1991
|
+
import { createRequire } from "module";
|
|
1992
|
+
var require2 = createRequire(import.meta.url);
|
|
1993
|
+
var pkg = require2("../../package.json");
|
|
1994
|
+
var CURRENT_VERSION = pkg.version;
|
|
1995
|
+
var PACKAGE_NAME = "@serendb/serendesktop";
|
|
1996
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
1997
|
+
async function checkForUpdate() {
|
|
1998
|
+
try {
|
|
1999
|
+
const controller = new AbortController();
|
|
2000
|
+
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
2001
|
+
const res = await fetch(REGISTRY_URL, {
|
|
2002
|
+
signal: controller.signal,
|
|
2003
|
+
headers: { Accept: "application/json" }
|
|
2004
|
+
});
|
|
2005
|
+
clearTimeout(timeout);
|
|
2006
|
+
if (!res.ok) {
|
|
2007
|
+
console.warn(`[Updater] Registry returned ${res.status}`);
|
|
2008
|
+
return { currentVersion: CURRENT_VERSION, latestVersion: null, updateAvailable: false };
|
|
2009
|
+
}
|
|
2010
|
+
const data = await res.json();
|
|
2011
|
+
const latestVersion = data.version ?? null;
|
|
2012
|
+
const updateAvailable = latestVersion !== null && latestVersion !== CURRENT_VERSION && isNewer(latestVersion, CURRENT_VERSION);
|
|
2013
|
+
console.log(`[Updater] Current: ${CURRENT_VERSION}, Latest: ${latestVersion}, Update: ${updateAvailable}`);
|
|
2014
|
+
return { currentVersion: CURRENT_VERSION, latestVersion, updateAvailable };
|
|
2015
|
+
} catch (err) {
|
|
2016
|
+
console.warn("[Updater] Failed to check for updates:", err);
|
|
2017
|
+
return { currentVersion: CURRENT_VERSION, latestVersion: null, updateAvailable: false };
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
async function installUpdate() {
|
|
2021
|
+
const home2 = homedir4();
|
|
2022
|
+
const serenDir = resolve3(home2, ".seren-local");
|
|
2023
|
+
const nodeDir = resolve3(serenDir, "node");
|
|
2024
|
+
const binDir = resolve3(serenDir, "bin");
|
|
2025
|
+
const isWin = platform3() === "win32";
|
|
2026
|
+
const nodeBinDir = isWin ? nodeDir : resolve3(nodeDir, "bin");
|
|
2027
|
+
const npmCmd = isWin ? resolve3(nodeBinDir, "npm.cmd") : resolve3(nodeBinDir, "npm");
|
|
2028
|
+
const serendesktopCmd = isWin ? "serendesktop.cmd" : "serendesktop";
|
|
2029
|
+
if (isWin) {
|
|
2030
|
+
const script = [
|
|
2031
|
+
`timeout /t 2 /nobreak >nul`,
|
|
2032
|
+
`set "PATH=${nodeBinDir};${binDir};%PATH%"`,
|
|
2033
|
+
`"${npmCmd}" install -g ${PACKAGE_NAME} --prefix "${serenDir}"`,
|
|
2034
|
+
`"${serendesktopCmd}"`
|
|
2035
|
+
].join(" && ");
|
|
2036
|
+
const child = spawn3("cmd", ["/c", script], {
|
|
2037
|
+
detached: true,
|
|
2038
|
+
stdio: "ignore",
|
|
2039
|
+
windowsHide: true
|
|
2040
|
+
});
|
|
2041
|
+
child.unref();
|
|
2042
|
+
} else {
|
|
2043
|
+
const script = [
|
|
2044
|
+
`sleep 2`,
|
|
2045
|
+
`export PATH="${nodeBinDir}:${binDir}:$PATH"`,
|
|
2046
|
+
`"${npmCmd}" install -g ${PACKAGE_NAME} --prefix "${serenDir}"`,
|
|
2047
|
+
`"${serendesktopCmd}"`
|
|
2048
|
+
].join(" && ");
|
|
2049
|
+
const child = spawn3("bash", ["-c", script], {
|
|
2050
|
+
detached: true,
|
|
2051
|
+
stdio: "ignore"
|
|
2052
|
+
});
|
|
2053
|
+
child.unref();
|
|
2054
|
+
}
|
|
2055
|
+
console.log("[Updater] Detached updater spawned, exiting server...");
|
|
2056
|
+
setTimeout(() => process.exit(0), 500);
|
|
2057
|
+
return { started: true };
|
|
2058
|
+
}
|
|
2059
|
+
function isNewer(a, b) {
|
|
2060
|
+
const pa = a.split(".").map(Number);
|
|
2061
|
+
const pb = b.split(".").map(Number);
|
|
2062
|
+
for (let i = 0; i < 3; i++) {
|
|
2063
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
|
|
2064
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
|
|
2065
|
+
}
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
1972
2069
|
// src/handlers/wallet.ts
|
|
1973
2070
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
1974
2071
|
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3 } from "fs/promises";
|
|
1975
2072
|
import { join as join5 } from "path";
|
|
1976
|
-
import { homedir as
|
|
2073
|
+
import { homedir as homedir5 } from "os";
|
|
1977
2074
|
import {
|
|
1978
2075
|
privateKeyToAccount
|
|
1979
2076
|
} from "viem/accounts";
|
|
@@ -1986,7 +2083,7 @@ import {
|
|
|
1986
2083
|
getAddress
|
|
1987
2084
|
} from "viem";
|
|
1988
2085
|
import { base } from "viem/chains";
|
|
1989
|
-
var SEREN_DIR = join5(
|
|
2086
|
+
var SEREN_DIR = join5(homedir5(), ".seren-local");
|
|
1990
2087
|
var WALLET_FILE = join5(SEREN_DIR, "data", "crypto-wallet.json");
|
|
1991
2088
|
var USDC_CONTRACT_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
1992
2089
|
var BASE_RPC_URL = "https://mainnet.base.org";
|
|
@@ -2302,6 +2399,8 @@ function registerAllHandlers() {
|
|
|
2302
2399
|
registerHandler("get_embedding_dimension", getEmbeddingDimension);
|
|
2303
2400
|
registerHandler("mcp_disconnect", mcpDisconnect);
|
|
2304
2401
|
registerHandler("mcp_read_resource", mcpReadResource);
|
|
2402
|
+
registerHandler("check_for_update", checkForUpdate);
|
|
2403
|
+
registerHandler("install_update", installUpdate);
|
|
2305
2404
|
registerHandler("create_conversation", createConversation);
|
|
2306
2405
|
registerHandler("get_conversations", getConversations);
|
|
2307
2406
|
registerHandler("get_conversation", getConversation);
|
|
@@ -2319,10 +2418,10 @@ import { fileURLToPath } from "url";
|
|
|
2319
2418
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
2320
2419
|
function getInstalledVersion() {
|
|
2321
2420
|
try {
|
|
2322
|
-
const
|
|
2421
|
+
const pkg2 = JSON.parse(
|
|
2323
2422
|
readFileSync2(join6(__dirname, "..", "package.json"), "utf-8")
|
|
2324
2423
|
);
|
|
2325
|
-
return
|
|
2424
|
+
return pkg2.version ?? "0.0.0";
|
|
2326
2425
|
} catch {
|
|
2327
2426
|
return "0.0.0";
|
|
2328
2427
|
}
|
|
@@ -2342,7 +2441,7 @@ async function getLatestVersion() {
|
|
|
2342
2441
|
return null;
|
|
2343
2442
|
}
|
|
2344
2443
|
}
|
|
2345
|
-
function
|
|
2444
|
+
function isNewer2(latest, current) {
|
|
2346
2445
|
const l = latest.split(".").map(Number);
|
|
2347
2446
|
const c = current.split(".").map(Number);
|
|
2348
2447
|
for (let i = 0; i < 3; i++) {
|
|
@@ -2354,7 +2453,7 @@ function isNewer(latest, current) {
|
|
|
2354
2453
|
function checkForUpdates() {
|
|
2355
2454
|
const current = getInstalledVersion();
|
|
2356
2455
|
getLatestVersion().then((latest) => {
|
|
2357
|
-
if (latest &&
|
|
2456
|
+
if (latest && isNewer2(latest, current)) {
|
|
2358
2457
|
console.log("");
|
|
2359
2458
|
console.log(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`);
|
|
2360
2459
|
console.log(` \u2551 Update available: v${current} \u2192 v${latest.padEnd(10)} \u2551`);
|
|
@@ -2367,6 +2466,8 @@ function checkForUpdates() {
|
|
|
2367
2466
|
}
|
|
2368
2467
|
|
|
2369
2468
|
// src/server.ts
|
|
2469
|
+
var require3 = createRequire2(import.meta.url);
|
|
2470
|
+
var APP_VERSION = require3("../package.json").version;
|
|
2370
2471
|
var PORT = Number(process.env.SEREN_PORT) || 19420;
|
|
2371
2472
|
var NO_OPEN = process.argv.includes("--no-open");
|
|
2372
2473
|
var AUTH_TOKEN = process.env.SEREN_RUNTIME_TOKEN || randomBytes3(32).toString("hex");
|
|
@@ -2445,7 +2546,7 @@ function serveSpaFallback(res) {
|
|
|
2445
2546
|
return serveHtml(res);
|
|
2446
2547
|
}
|
|
2447
2548
|
function openBrowser(url) {
|
|
2448
|
-
const cmd =
|
|
2549
|
+
const cmd = platform4() === "darwin" ? `open "${url}"` : platform4() === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
2449
2550
|
exec2(cmd, (err) => {
|
|
2450
2551
|
if (err) console.log(`[Seren Local] Could not open browser: ${err.message}`);
|
|
2451
2552
|
});
|
|
@@ -2520,7 +2621,7 @@ var httpServer = createServer2((req, res) => {
|
|
|
2520
2621
|
}
|
|
2521
2622
|
if (req.url === "/health") {
|
|
2522
2623
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2523
|
-
res.end(JSON.stringify({ status: "ok", version:
|
|
2624
|
+
res.end(JSON.stringify({ status: "ok", version: APP_VERSION, token: AUTH_TOKEN, buildHash: BUILD_HASH }));
|
|
2524
2625
|
return;
|
|
2525
2626
|
}
|
|
2526
2627
|
const urlPath = req.url || "/";
|
|
@@ -2574,7 +2675,7 @@ wss.on("connection", (ws, req) => {
|
|
|
2574
2675
|
console.log("[Seren Local] Browser disconnected");
|
|
2575
2676
|
});
|
|
2576
2677
|
});
|
|
2577
|
-
var dataDir = join7(
|
|
2678
|
+
var dataDir = join7(homedir6(), ".seren-local");
|
|
2578
2679
|
mkdirSync2(dataDir, { recursive: true });
|
|
2579
2680
|
initChatDb(join7(dataDir, "conversations.db"));
|
|
2580
2681
|
registerAllHandlers();
|