@silicaclaw/cli 2026.3.18-2 → 2026.3.18-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/CHANGELOG.md +17 -0
- package/VERSION +1 -1
- package/apps/local-console/package.json +1 -1
- package/apps/local-console/public/index.html +779 -240
- package/apps/local-console/src/server.ts +154 -1
- package/package.json +1 -1
- package/scripts/silicaclaw-gateway.mjs +110 -9
|
@@ -64,6 +64,7 @@ const NETWORK_PEER_REMOVE_AFTER_MS = Number(process.env.NETWORK_PEER_REMOVE_AFTE
|
|
|
64
64
|
const NETWORK_UDP_BIND_ADDRESS = process.env.NETWORK_UDP_BIND_ADDRESS || "0.0.0.0";
|
|
65
65
|
const NETWORK_UDP_BROADCAST_ADDRESS = process.env.NETWORK_UDP_BROADCAST_ADDRESS || "255.255.255.255";
|
|
66
66
|
const NETWORK_PEER_ID = process.env.NETWORK_PEER_ID;
|
|
67
|
+
const NETWORK_MODE = process.env.NETWORK_MODE || "";
|
|
67
68
|
const WEBRTC_SIGNALING_URL = process.env.WEBRTC_SIGNALING_URL || "https://relay.silicaclaw.com";
|
|
68
69
|
const WEBRTC_SIGNALING_URLS = process.env.WEBRTC_SIGNALING_URLS || "";
|
|
69
70
|
const WEBRTC_ROOM = process.env.WEBRTC_ROOM || "silicaclaw-global-preview";
|
|
@@ -295,7 +296,19 @@ class LocalNodeService {
|
|
|
295
296
|
await this.network.stop();
|
|
296
297
|
}
|
|
297
298
|
|
|
299
|
+
private ensureLocalDirectoryBaseline(): void {
|
|
300
|
+
if (this.profile) {
|
|
301
|
+
this.directory = ingestProfileRecord(this.directory, { type: "profile", profile: this.profile });
|
|
302
|
+
}
|
|
303
|
+
if (this.identity && this.profile?.public_enabled && this.broadcastEnabled) {
|
|
304
|
+
const currentSeenAt = this.directory.presence[this.identity.agent_id] ?? 0;
|
|
305
|
+
const baselineSeenAt = Math.max(currentSeenAt, this.lastBroadcastAt || Date.now());
|
|
306
|
+
this.directory = ingestPresenceRecord(this.directory, signPresence(this.identity, baselineSeenAt));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
298
310
|
getOverview() {
|
|
311
|
+
this.ensureLocalDirectoryBaseline();
|
|
299
312
|
this.compactCacheInMemory();
|
|
300
313
|
const profiles = Object.values(this.directory.profiles);
|
|
301
314
|
const onlineCount = profiles.filter((profile) =>
|
|
@@ -773,11 +786,13 @@ class LocalNodeService {
|
|
|
773
786
|
}
|
|
774
787
|
|
|
775
788
|
getDirectory(): DirectoryState {
|
|
789
|
+
this.ensureLocalDirectoryBaseline();
|
|
776
790
|
this.compactCacheInMemory();
|
|
777
791
|
return this.directory;
|
|
778
792
|
}
|
|
779
793
|
|
|
780
794
|
search(keyword: string): PublicProfileSummary[] {
|
|
795
|
+
this.ensureLocalDirectoryBaseline();
|
|
781
796
|
this.compactCacheInMemory();
|
|
782
797
|
return searchDirectory(this.directory, keyword, { presenceTTLms: PRESENCE_TTL_MS }).map((profile) => {
|
|
783
798
|
const lastSeenAt = this.directory.presence[profile.agent_id] ?? 0;
|
|
@@ -1383,7 +1398,13 @@ class LocalNodeService {
|
|
|
1383
1398
|
}
|
|
1384
1399
|
|
|
1385
1400
|
private applyResolvedNetworkConfig(): void {
|
|
1386
|
-
|
|
1401
|
+
const modeEnv = String(NETWORK_MODE || "").trim();
|
|
1402
|
+
const resolvedMode =
|
|
1403
|
+
modeEnv === "local" || modeEnv === "lan" || modeEnv === "global-preview"
|
|
1404
|
+
? modeEnv
|
|
1405
|
+
: this.socialConfig.network.mode || "lan";
|
|
1406
|
+
|
|
1407
|
+
this.networkMode = resolvedMode;
|
|
1387
1408
|
this.networkNamespace = this.socialConfig.network.namespace || process.env.NETWORK_NAMESPACE || "silicaclaw.preview";
|
|
1388
1409
|
this.networkPort = Number(this.socialConfig.network.port || process.env.NETWORK_PORT || 44123);
|
|
1389
1410
|
|
|
@@ -1503,10 +1524,76 @@ function resolveLocalConsoleStaticDir(): string {
|
|
|
1503
1524
|
return candidates[0];
|
|
1504
1525
|
}
|
|
1505
1526
|
|
|
1527
|
+
function escapeHtml(text: string): string {
|
|
1528
|
+
return String(text)
|
|
1529
|
+
.replace(/&/g, "&")
|
|
1530
|
+
.replace(/</g, "<")
|
|
1531
|
+
.replace(/>/g, ">")
|
|
1532
|
+
.replace(/"/g, """)
|
|
1533
|
+
.replace(/'/g, "'");
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
function shortId(id: string): string {
|
|
1537
|
+
if (!id) return "-";
|
|
1538
|
+
return `${id.slice(0, 10)}...${id.slice(-6)}`;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
function ago(ts: number | null | undefined): string {
|
|
1542
|
+
if (!ts) return "-";
|
|
1543
|
+
const seconds = Math.max(0, Math.floor((Date.now() - ts) / 1000));
|
|
1544
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
1545
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
1546
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function renderBootstrapScript(payload: unknown): string {
|
|
1550
|
+
const encoded = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
1551
|
+
return `
|
|
1552
|
+
<script>
|
|
1553
|
+
(() => {
|
|
1554
|
+
const data = ${encoded};
|
|
1555
|
+
if (!data) return;
|
|
1556
|
+
const setText = (id, value) => {
|
|
1557
|
+
const el = document.getElementById(id);
|
|
1558
|
+
if (el) el.textContent = value;
|
|
1559
|
+
};
|
|
1560
|
+
const setHtml = (id, value) => {
|
|
1561
|
+
const el = document.getElementById(id);
|
|
1562
|
+
if (el) el.innerHTML = value;
|
|
1563
|
+
};
|
|
1564
|
+
if (data.integrationStatusText) {
|
|
1565
|
+
const bar = document.getElementById('integrationStatusBar');
|
|
1566
|
+
if (bar) {
|
|
1567
|
+
bar.textContent = data.integrationStatusText;
|
|
1568
|
+
if (data.integrationStatusClassName) bar.className = data.integrationStatusClassName;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
setText('socialStatusLine', data.socialStatusLineText || '');
|
|
1572
|
+
setText('socialStatusSubline', data.socialStatusSublineText || '');
|
|
1573
|
+
setText('brandVersion', data.brandVersionText || '-');
|
|
1574
|
+
setText('snapshot', data.snapshotText || '');
|
|
1575
|
+
setText('heroMode', data.heroModeText || '-');
|
|
1576
|
+
setText('heroAdapter', data.heroAdapterText || '-');
|
|
1577
|
+
setText('heroRelay', data.heroRelayText || '-');
|
|
1578
|
+
setText('heroRoom', data.heroRoomText || '-');
|
|
1579
|
+
setText('pillAdapter', data.pillAdapterText || 'adapter: -');
|
|
1580
|
+
const pillBroadcast = document.getElementById('pillBroadcast');
|
|
1581
|
+
if (pillBroadcast) {
|
|
1582
|
+
pillBroadcast.textContent = data.pillBroadcastText || 'broadcast: -';
|
|
1583
|
+
if (data.pillBroadcastClassName) pillBroadcast.className = data.pillBroadcastClassName;
|
|
1584
|
+
}
|
|
1585
|
+
setHtml('overviewCards', data.overviewCardsHtml || '');
|
|
1586
|
+
setText('agentsCountHint', data.agentsCountHintText || '0 agents');
|
|
1587
|
+
setHtml('agentsWrap', data.agentsWrapHtml || '<div class="label">No discovered agents yet.</div>');
|
|
1588
|
+
})();
|
|
1589
|
+
</script>`;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1506
1592
|
async function main() {
|
|
1507
1593
|
const app = express();
|
|
1508
1594
|
const port = Number(process.env.PORT || 4310);
|
|
1509
1595
|
const staticDir = resolveLocalConsoleStaticDir();
|
|
1596
|
+
const staticIndexFile = resolve(staticDir, "index.html");
|
|
1510
1597
|
|
|
1511
1598
|
const node = new LocalNodeService();
|
|
1512
1599
|
await node.start();
|
|
@@ -1708,6 +1795,72 @@ async function main() {
|
|
|
1708
1795
|
sendOk(res, { ok: true });
|
|
1709
1796
|
});
|
|
1710
1797
|
|
|
1798
|
+
app.get(["/", "/index.html"], (_req, res) => {
|
|
1799
|
+
const overview = node.getOverview();
|
|
1800
|
+
const discovered = node.search("");
|
|
1801
|
+
const network = node.getNetworkConfig();
|
|
1802
|
+
const integration = node.getIntegrationStatus();
|
|
1803
|
+
const overviewCardsHtml = [
|
|
1804
|
+
["Discovered", overview.discovered_count],
|
|
1805
|
+
["Online", overview.online_count],
|
|
1806
|
+
["Offline", overview.offline_count],
|
|
1807
|
+
["Presence TTL", `${Math.floor(overview.presence_ttl_ms / 1000)}s`],
|
|
1808
|
+
]
|
|
1809
|
+
.map(
|
|
1810
|
+
([k, v]) => `<div class="card"><div class="label">${escapeHtml(String(k))}</div><div class="value">${escapeHtml(String(v))}</div></div>`
|
|
1811
|
+
)
|
|
1812
|
+
.join("");
|
|
1813
|
+
const agentsWrapHtml =
|
|
1814
|
+
discovered.length === 0
|
|
1815
|
+
? `<div class="label">No discovered agents yet.</div>`
|
|
1816
|
+
: `
|
|
1817
|
+
<table class="table">
|
|
1818
|
+
<thead><tr><th>Name</th><th>Agent ID</th><th>Status</th><th>Updated</th></tr></thead>
|
|
1819
|
+
<tbody>
|
|
1820
|
+
${discovered
|
|
1821
|
+
.map(
|
|
1822
|
+
(agent) => `
|
|
1823
|
+
<tr>
|
|
1824
|
+
<td>${escapeHtml(agent.display_name || "Unnamed")}</td>
|
|
1825
|
+
<td class="mono">${escapeHtml(shortId(agent.agent_id || ""))}</td>
|
|
1826
|
+
<td class="${agent.online ? "online" : "offline"}">${agent.online ? "online" : "offline"}</td>
|
|
1827
|
+
<td>${escapeHtml(ago(agent.updated_at))}</td>
|
|
1828
|
+
</tr>`
|
|
1829
|
+
)
|
|
1830
|
+
.join("")}
|
|
1831
|
+
</tbody>
|
|
1832
|
+
</table>
|
|
1833
|
+
`;
|
|
1834
|
+
const payload = {
|
|
1835
|
+
brandVersionText: overview.app_version ? `v${overview.app_version}` : "-",
|
|
1836
|
+
snapshotText: [
|
|
1837
|
+
`app_version: ${overview.app_version || "-"}`,
|
|
1838
|
+
`agent_id: ${overview.agent_id || "-"}`,
|
|
1839
|
+
`public_enabled: ${overview.public_enabled}`,
|
|
1840
|
+
`broadcast_enabled: ${overview.broadcast_enabled}`,
|
|
1841
|
+
`last_broadcast: ${ago(overview.last_broadcast_at)}`,
|
|
1842
|
+
].join("\n"),
|
|
1843
|
+
heroModeText: overview.social?.network_mode || "-",
|
|
1844
|
+
heroAdapterText: network.adapter || "-",
|
|
1845
|
+
heroRelayText: network.adapter_extra?.signaling_url || "-",
|
|
1846
|
+
heroRoomText: network.adapter_extra?.room || "-",
|
|
1847
|
+
pillAdapterText: `adapter: ${network.adapter || "-"}`,
|
|
1848
|
+
pillBroadcastText: overview.broadcast_enabled ? "broadcast: running" : "broadcast: paused",
|
|
1849
|
+
pillBroadcastClassName: `pill ${overview.broadcast_enabled ? "ok" : "warn"}`,
|
|
1850
|
+
overviewCardsHtml,
|
|
1851
|
+
agentsCountHintText: `${discovered.length} agents discovered`,
|
|
1852
|
+
agentsWrapHtml,
|
|
1853
|
+
integrationStatusText: `Connected to SilicaClaw: ${integration.connected_to_silicaclaw ? "yes" : "no"} · Network mode: ${integration.network_mode || "-"} · Public discovery: ${integration.public_enabled ? "enabled" : "disabled"}`,
|
|
1854
|
+
integrationStatusClassName: `integration-strip ${integration.connected_to_silicaclaw && integration.public_enabled ? "ok" : "warn"}`,
|
|
1855
|
+
socialStatusLineText: integration.status_line || "",
|
|
1856
|
+
socialStatusSublineText: `Connected to SilicaClaw · ${integration.public_enabled ? "Public discovery enabled" : "Public discovery disabled"} · mode ${integration.network_mode || "-"}`,
|
|
1857
|
+
};
|
|
1858
|
+
let html = readFileSync(staticIndexFile, "utf8");
|
|
1859
|
+
html = html.replace("</body>", `${renderBootstrapScript(payload)}\n</body>`);
|
|
1860
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
1861
|
+
res.send(html);
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1711
1864
|
app.use(express.static(staticDir));
|
|
1712
1865
|
|
|
1713
1866
|
app.use((error: unknown, _req: Request, res: Response, _next: NextFunction) => {
|
package/package.json
CHANGED
|
@@ -111,6 +111,7 @@ function detectAppDir() {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const APP_DIR = detectAppDir();
|
|
114
|
+
const LOCAL_CONSOLE_DIR = join(APP_DIR, "apps", "local-console");
|
|
114
115
|
const STATE_DIR = join(APP_DIR, ".silicaclaw", "gateway");
|
|
115
116
|
const CONSOLE_PID_FILE = join(STATE_DIR, "local-console.pid");
|
|
116
117
|
const CONSOLE_LOG_FILE = join(STATE_DIR, "local-console.log");
|
|
@@ -191,10 +192,10 @@ function parseUrlHostPort(url) {
|
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
|
|
194
|
-
function spawnBackground(command, args, env, logFile, pidFile) {
|
|
195
|
+
function spawnBackground(command, args, env, logFile, pidFile, cwd = APP_DIR) {
|
|
195
196
|
const outFd = openSync(logFile, "a");
|
|
196
197
|
const child = spawn(command, args, {
|
|
197
|
-
cwd
|
|
198
|
+
cwd,
|
|
198
199
|
env: { ...process.env, ...env },
|
|
199
200
|
detached: true,
|
|
200
201
|
stdio: ["ignore", outFd, outFd],
|
|
@@ -240,7 +241,7 @@ function buildStatusPayload() {
|
|
|
240
241
|
mode: state?.mode || "unknown",
|
|
241
242
|
adapter: state?.adapter || "unknown",
|
|
242
243
|
local_console: {
|
|
243
|
-
pid: localPid,
|
|
244
|
+
pid: Number(localListener?.pid || localPid || 0) || null,
|
|
244
245
|
running: Boolean(localListener),
|
|
245
246
|
log_file: CONSOLE_LOG_FILE,
|
|
246
247
|
},
|
|
@@ -314,6 +315,66 @@ function listeningProcessOnPort(port) {
|
|
|
314
315
|
}
|
|
315
316
|
}
|
|
316
317
|
|
|
318
|
+
function normalizePathForMatch(value) {
|
|
319
|
+
return String(value || "").replace(/\\/g, "/");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function sleep(ms) {
|
|
323
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function waitForPort(port, timeoutMs = 5000) {
|
|
327
|
+
const startedAt = Date.now();
|
|
328
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
329
|
+
const listener = listeningProcessOnPort(port);
|
|
330
|
+
if (listener) return listener;
|
|
331
|
+
await sleep(200);
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function tailText(file, lines = 20) {
|
|
337
|
+
if (!existsSync(file)) return "";
|
|
338
|
+
const text = String(readFileSync(file, "utf8"));
|
|
339
|
+
return text.split(/\r?\n/).slice(-lines).join("\n").trim();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function isOwnedListener(listener, kind) {
|
|
343
|
+
if (!listener?.command) return false;
|
|
344
|
+
const command = normalizePathForMatch(listener.command).toLowerCase();
|
|
345
|
+
const appDir = normalizePathForMatch(APP_DIR).toLowerCase();
|
|
346
|
+
const rootDir = normalizePathForMatch(ROOT_DIR).toLowerCase();
|
|
347
|
+
const inWorkspace = command.includes(appDir) || command.includes(rootDir);
|
|
348
|
+
if (!inWorkspace) return false;
|
|
349
|
+
if (kind === "local-console") {
|
|
350
|
+
return (
|
|
351
|
+
command.includes("@silicaclaw/local-console") ||
|
|
352
|
+
command.includes("/apps/local-console/") ||
|
|
353
|
+
command.includes("src/server.ts") ||
|
|
354
|
+
command.includes("dist/server.js")
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
if (kind === "signaling") {
|
|
358
|
+
return command.includes("webrtc-signaling-server.mjs") || command.includes("npm run webrtc-signaling");
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function stopOwnedListener(port, kind) {
|
|
364
|
+
const listener = listeningProcessOnPort(port);
|
|
365
|
+
if (!listener || !isOwnedListener(listener, kind)) return false;
|
|
366
|
+
await stopPid(Number(listener.pid), kind);
|
|
367
|
+
const remaining = listeningProcessOnPort(port);
|
|
368
|
+
if (remaining && Number(remaining.pid) === Number(listener.pid) && isOwnedListener(remaining, kind)) {
|
|
369
|
+
try {
|
|
370
|
+
spawnSync("kill", ["-9", String(remaining.pid)], { stdio: ["ignore", "ignore", "ignore"] });
|
|
371
|
+
} catch {
|
|
372
|
+
// ignore hard-kill fallback failures
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
317
378
|
function printStopSummary() {
|
|
318
379
|
const localListener = listeningProcessOnPort(4310);
|
|
319
380
|
const signalingListener = listeningProcessOnPort(4510);
|
|
@@ -357,6 +418,8 @@ async function stopAll() {
|
|
|
357
418
|
const sigPid = readPid(SIGNALING_PID_FILE);
|
|
358
419
|
await stopPid(localPid, "local-console");
|
|
359
420
|
await stopPid(sigPid, "signaling");
|
|
421
|
+
await stopOwnedListener(4310, "local-console");
|
|
422
|
+
await stopOwnedListener(4510, "signaling");
|
|
360
423
|
removeFileIfExists(CONSOLE_PID_FILE);
|
|
361
424
|
removeFileIfExists(SIGNALING_PID_FILE);
|
|
362
425
|
writeState({
|
|
@@ -365,7 +428,7 @@ async function stopAll() {
|
|
|
365
428
|
});
|
|
366
429
|
}
|
|
367
430
|
|
|
368
|
-
function startAll() {
|
|
431
|
+
async function startAll() {
|
|
369
432
|
ensureStateDir();
|
|
370
433
|
|
|
371
434
|
const mode = parseMode(parseFlag("mode", process.env.NETWORK_MODE || "global-preview"));
|
|
@@ -376,8 +439,13 @@ function startAll() {
|
|
|
376
439
|
|
|
377
440
|
const currentLocalPid = readPid(CONSOLE_PID_FILE);
|
|
378
441
|
const currentSigPid = readPid(SIGNALING_PID_FILE);
|
|
379
|
-
|
|
380
|
-
if (!isRunning(currentLocalPid)) {
|
|
442
|
+
const currentListener = listeningProcessOnPort(4310);
|
|
443
|
+
if (currentListener && isOwnedListener(currentListener, "local-console") && !isRunning(currentLocalPid)) {
|
|
444
|
+
writeFileSync(CONSOLE_PID_FILE, String(currentListener.pid));
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
let localPid = readPid(CONSOLE_PID_FILE);
|
|
448
|
+
if (!isRunning(localPid)) {
|
|
381
449
|
removeFileIfExists(CONSOLE_PID_FILE);
|
|
382
450
|
const env = {
|
|
383
451
|
NETWORK_ADAPTER: adapter,
|
|
@@ -385,7 +453,14 @@ function startAll() {
|
|
|
385
453
|
WEBRTC_SIGNALING_URL: signalingUrl,
|
|
386
454
|
WEBRTC_ROOM: room,
|
|
387
455
|
};
|
|
388
|
-
localPid = spawnBackground(
|
|
456
|
+
localPid = spawnBackground(
|
|
457
|
+
process.execPath,
|
|
458
|
+
["--import", "tsx", "src/server.ts"],
|
|
459
|
+
env,
|
|
460
|
+
CONSOLE_LOG_FILE,
|
|
461
|
+
CONSOLE_PID_FILE,
|
|
462
|
+
LOCAL_CONSOLE_DIR,
|
|
463
|
+
);
|
|
389
464
|
}
|
|
390
465
|
|
|
391
466
|
const { host, port } = parseUrlHostPort(signalingUrl);
|
|
@@ -433,7 +508,20 @@ async function main() {
|
|
|
433
508
|
return;
|
|
434
509
|
}
|
|
435
510
|
if (cmd === "start") {
|
|
436
|
-
startAll();
|
|
511
|
+
await startAll();
|
|
512
|
+
const listener = await waitForPort(4310, 15000);
|
|
513
|
+
if (!listener) {
|
|
514
|
+
headline();
|
|
515
|
+
console.log("");
|
|
516
|
+
kv("Status", paint("failed to start", COLOR.red));
|
|
517
|
+
const recent = tailText(CONSOLE_LOG_FILE, 18);
|
|
518
|
+
if (recent) {
|
|
519
|
+
console.log("");
|
|
520
|
+
console.log(recent);
|
|
521
|
+
}
|
|
522
|
+
process.exitCode = 1;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
437
525
|
const status = buildStatusPayload();
|
|
438
526
|
printConnectionSummary(status, "Started");
|
|
439
527
|
return;
|
|
@@ -445,7 +533,20 @@ async function main() {
|
|
|
445
533
|
}
|
|
446
534
|
if (cmd === "restart") {
|
|
447
535
|
await stopAll();
|
|
448
|
-
startAll();
|
|
536
|
+
await startAll();
|
|
537
|
+
const listener = await waitForPort(4310, 15000);
|
|
538
|
+
if (!listener) {
|
|
539
|
+
headline();
|
|
540
|
+
console.log("");
|
|
541
|
+
kv("Status", paint("failed to restart", COLOR.red));
|
|
542
|
+
const recent = tailText(CONSOLE_LOG_FILE, 18);
|
|
543
|
+
if (recent) {
|
|
544
|
+
console.log("");
|
|
545
|
+
console.log(recent);
|
|
546
|
+
}
|
|
547
|
+
process.exitCode = 1;
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
449
550
|
const status = buildStatusPayload();
|
|
450
551
|
printConnectionSummary(status, "Restarted");
|
|
451
552
|
return;
|