nextclaw 0.6.9 → 0.6.11
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/cli/index.js +104 -16
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -1788,6 +1788,23 @@ import { resolve as resolve6 } from "path";
|
|
|
1788
1788
|
import { getDataDir as getDataDir4 } from "@nextclaw/core";
|
|
1789
1789
|
var RESTART_SENTINEL_FILENAME = "restart-sentinel.json";
|
|
1790
1790
|
var PENDING_SYSTEM_EVENTS_KEY = "pending_system_events";
|
|
1791
|
+
var RESTART_REASON_MAX_CHARS = 240;
|
|
1792
|
+
var RESTART_NOTE_MAX_CHARS = 600;
|
|
1793
|
+
var RESTART_OUTBOUND_MAX_CHARS = 1200;
|
|
1794
|
+
function trimTo(value, maxChars) {
|
|
1795
|
+
const text = value.trim();
|
|
1796
|
+
if (!text) {
|
|
1797
|
+
return "";
|
|
1798
|
+
}
|
|
1799
|
+
if (text.length <= maxChars) {
|
|
1800
|
+
return text;
|
|
1801
|
+
}
|
|
1802
|
+
return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}\u2026`;
|
|
1803
|
+
}
|
|
1804
|
+
function normalizeLine(value, maxChars) {
|
|
1805
|
+
const trimmed = trimTo(value, maxChars);
|
|
1806
|
+
return trimmed ? trimmed : null;
|
|
1807
|
+
}
|
|
1791
1808
|
function resolveRestartSentinelPath() {
|
|
1792
1809
|
return resolve6(getDataDir4(), "run", RESTART_SENTINEL_FILENAME);
|
|
1793
1810
|
}
|
|
@@ -1822,7 +1839,7 @@ async function consumeRestartSentinel() {
|
|
|
1822
1839
|
}
|
|
1823
1840
|
}
|
|
1824
1841
|
function summarizeRestartSentinel(payload) {
|
|
1825
|
-
const reason = payload.stats?.reason
|
|
1842
|
+
const reason = normalizeLine(payload.stats?.reason ?? "", RESTART_REASON_MAX_CHARS);
|
|
1826
1843
|
if (payload.kind === "update.run") {
|
|
1827
1844
|
return payload.status === "ok" ? "\u2705 NextClaw update completed and service restarted." : "\u26A0\uFE0F NextClaw update finished with issues.";
|
|
1828
1845
|
}
|
|
@@ -1836,15 +1853,16 @@ function summarizeRestartSentinel(payload) {
|
|
|
1836
1853
|
}
|
|
1837
1854
|
function formatRestartSentinelMessage(payload) {
|
|
1838
1855
|
const lines = [summarizeRestartSentinel(payload)];
|
|
1839
|
-
const note = payload.message
|
|
1856
|
+
const note = normalizeLine(payload.message ?? "", RESTART_NOTE_MAX_CHARS);
|
|
1840
1857
|
if (note) {
|
|
1841
1858
|
lines.push(note);
|
|
1842
1859
|
}
|
|
1843
|
-
const reason = payload.stats?.reason
|
|
1860
|
+
const reason = normalizeLine(payload.stats?.reason ?? "", RESTART_REASON_MAX_CHARS);
|
|
1844
1861
|
if (reason && !lines.some((line) => line.includes(reason))) {
|
|
1845
1862
|
lines.push(`Reason: ${reason}`);
|
|
1846
1863
|
}
|
|
1847
|
-
|
|
1864
|
+
const message = lines.join("\n").trim();
|
|
1865
|
+
return trimTo(message, RESTART_OUTBOUND_MAX_CHARS);
|
|
1848
1866
|
}
|
|
1849
1867
|
function parseSessionKey(sessionKey) {
|
|
1850
1868
|
const value = sessionKey?.trim();
|
|
@@ -2481,7 +2499,7 @@ var ServiceCommands = class {
|
|
|
2481
2499
|
}
|
|
2482
2500
|
}
|
|
2483
2501
|
await reloader.getChannels().startAll();
|
|
2484
|
-
await this.wakeFromRestartSentinel({
|
|
2502
|
+
await this.wakeFromRestartSentinel({ channels: reloader.getChannels(), sessionManager });
|
|
2485
2503
|
await agent.run();
|
|
2486
2504
|
} finally {
|
|
2487
2505
|
await stopPluginChannelGateways(pluginGatewayHandles);
|
|
@@ -2495,6 +2513,72 @@ var ServiceCommands = class {
|
|
|
2495
2513
|
const trimmed = value.trim();
|
|
2496
2514
|
return trimmed || void 0;
|
|
2497
2515
|
}
|
|
2516
|
+
async sleep(ms) {
|
|
2517
|
+
await new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
2518
|
+
}
|
|
2519
|
+
resolveMostRecentRoutableSessionKey(sessionManager) {
|
|
2520
|
+
const sessions = sessionManager.listSessions();
|
|
2521
|
+
let best = null;
|
|
2522
|
+
for (const session of sessions) {
|
|
2523
|
+
const key = this.normalizeOptionalString(session.key);
|
|
2524
|
+
if (!key || key.startsWith("cli:")) {
|
|
2525
|
+
continue;
|
|
2526
|
+
}
|
|
2527
|
+
const metadataRaw = session.metadata;
|
|
2528
|
+
const metadata = metadataRaw && typeof metadataRaw === "object" && !Array.isArray(metadataRaw) ? metadataRaw : {};
|
|
2529
|
+
const contextRaw = metadata.last_delivery_context;
|
|
2530
|
+
const context = contextRaw && typeof contextRaw === "object" && !Array.isArray(contextRaw) ? contextRaw : {};
|
|
2531
|
+
const hasRoute = Boolean(this.normalizeOptionalString(context.channel)) && Boolean(this.normalizeOptionalString(context.chatId));
|
|
2532
|
+
const hasFallbackRoute = Boolean(this.normalizeOptionalString(metadata.last_channel)) && Boolean(this.normalizeOptionalString(metadata.last_to));
|
|
2533
|
+
if (!hasRoute && !hasFallbackRoute) {
|
|
2534
|
+
continue;
|
|
2535
|
+
}
|
|
2536
|
+
const updatedAtRaw = this.normalizeOptionalString(session.updated_at);
|
|
2537
|
+
const updatedAt = updatedAtRaw ? Date.parse(updatedAtRaw) : Number.NaN;
|
|
2538
|
+
const score = Number.isFinite(updatedAt) ? updatedAt : 0;
|
|
2539
|
+
if (!best || score >= best.updatedAt) {
|
|
2540
|
+
best = { key, updatedAt: score };
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
return best?.key;
|
|
2544
|
+
}
|
|
2545
|
+
async sendRestartSentinelNotice(params) {
|
|
2546
|
+
const outboundBase = {
|
|
2547
|
+
channel: params.channel,
|
|
2548
|
+
chatId: params.chatId,
|
|
2549
|
+
content: params.content,
|
|
2550
|
+
media: [],
|
|
2551
|
+
metadata: params.metadata
|
|
2552
|
+
};
|
|
2553
|
+
const variants = params.replyTo ? [
|
|
2554
|
+
{ ...outboundBase, replyTo: params.replyTo },
|
|
2555
|
+
{ ...outboundBase }
|
|
2556
|
+
] : [{ ...outboundBase }];
|
|
2557
|
+
for (let variantIndex = 0; variantIndex < variants.length; variantIndex += 1) {
|
|
2558
|
+
const outbound = variants[variantIndex];
|
|
2559
|
+
const isLastVariant = variantIndex === variants.length - 1;
|
|
2560
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
2561
|
+
try {
|
|
2562
|
+
const delivered = await params.channels.deliver(outbound);
|
|
2563
|
+
if (delivered) {
|
|
2564
|
+
return true;
|
|
2565
|
+
}
|
|
2566
|
+
return false;
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
if (attempt < 3) {
|
|
2569
|
+
await this.sleep(attempt * 500);
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
if (isLastVariant) {
|
|
2573
|
+
console.warn(
|
|
2574
|
+
`Warning: restart sentinel notify failed for ${params.channel}:${params.chatId} (attempt ${attempt}): ${String(error)}`
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return false;
|
|
2581
|
+
}
|
|
2498
2582
|
async wakeFromRestartSentinel(params) {
|
|
2499
2583
|
const sentinel = await consumeRestartSentinel();
|
|
2500
2584
|
if (!sentinel) {
|
|
@@ -2503,7 +2587,12 @@ var ServiceCommands = class {
|
|
|
2503
2587
|
await new Promise((resolve10) => setTimeout(resolve10, 750));
|
|
2504
2588
|
const payload = sentinel.payload;
|
|
2505
2589
|
const message = formatRestartSentinelMessage(payload);
|
|
2506
|
-
const
|
|
2590
|
+
const sentinelSessionKey = this.normalizeOptionalString(payload.sessionKey);
|
|
2591
|
+
const fallbackSessionKey = sentinelSessionKey ? void 0 : this.resolveMostRecentRoutableSessionKey(params.sessionManager);
|
|
2592
|
+
if (!sentinelSessionKey && fallbackSessionKey) {
|
|
2593
|
+
console.warn(`Warning: restart sentinel missing sessionKey; fallback to ${fallbackSessionKey}.`);
|
|
2594
|
+
}
|
|
2595
|
+
const sessionKey = sentinelSessionKey ?? fallbackSessionKey ?? "cli:default";
|
|
2507
2596
|
const parsedSession = parseSessionKey(sessionKey);
|
|
2508
2597
|
const context = payload.deliveryContext;
|
|
2509
2598
|
const channel = this.normalizeOptionalString(context?.channel) ?? parsedSession?.channel ?? this.normalizeOptionalString((params.sessionManager.getIfExists(sessionKey)?.metadata ?? {}).last_channel);
|
|
@@ -2519,16 +2608,15 @@ var ServiceCommands = class {
|
|
|
2519
2608
|
enqueuePendingSystemEvent(params.sessionManager, sessionKey, message);
|
|
2520
2609
|
return;
|
|
2521
2610
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
} catch {
|
|
2611
|
+
const delivered = await this.sendRestartSentinelNotice({
|
|
2612
|
+
channels: params.channels,
|
|
2613
|
+
channel,
|
|
2614
|
+
chatId,
|
|
2615
|
+
content: message,
|
|
2616
|
+
...replyTo ? { replyTo } : {},
|
|
2617
|
+
metadata
|
|
2618
|
+
});
|
|
2619
|
+
if (!delivered) {
|
|
2532
2620
|
enqueuePendingSystemEvent(params.sessionManager, sessionKey, message);
|
|
2533
2621
|
}
|
|
2534
2622
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextclaw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"chokidar": "^3.6.0",
|
|
40
40
|
"commander": "^12.1.0",
|
|
41
|
-
"@nextclaw/core": "^0.6.
|
|
41
|
+
"@nextclaw/core": "^0.6.9",
|
|
42
42
|
"@nextclaw/server": "^0.4.2",
|
|
43
43
|
"@nextclaw/openclaw-compat": "^0.1.5"
|
|
44
44
|
},
|