nextclaw 0.5.4 → 0.5.5
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 +93 -0
- package/package.json +3 -3
- package/templates/USAGE.md +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -954,6 +954,7 @@ var CliRuntime = class {
|
|
|
954
954
|
logo;
|
|
955
955
|
restartCoordinator;
|
|
956
956
|
serviceRestartTask = null;
|
|
957
|
+
selfRelaunchArmed = false;
|
|
957
958
|
constructor(options = {}) {
|
|
958
959
|
this.logo = options.logo ?? LOGO;
|
|
959
960
|
this.restartCoordinator = new RestartCoordinator({
|
|
@@ -1002,7 +1003,99 @@ var CliRuntime = class {
|
|
|
1002
1003
|
this.serviceRestartTask = null;
|
|
1003
1004
|
}
|
|
1004
1005
|
}
|
|
1006
|
+
armManagedServiceRelaunch(params) {
|
|
1007
|
+
const strategy = params.strategy ?? "background-service-or-manual";
|
|
1008
|
+
if (strategy !== "background-service-or-exit" && strategy !== "exit-process") {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
if (this.selfRelaunchArmed) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const state = readServiceState();
|
|
1015
|
+
if (!state || state.pid !== process.pid) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
|
|
1019
|
+
const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
|
|
1020
|
+
const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath2(new URL("./index.js", import.meta.url));
|
|
1021
|
+
const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
|
|
1022
|
+
const serviceStatePath = resolve4(getDataDir2(), "run", "service.json");
|
|
1023
|
+
const helperScript = [
|
|
1024
|
+
'const { spawnSync } = require("node:child_process");',
|
|
1025
|
+
'const { readFileSync } = require("node:fs");',
|
|
1026
|
+
`const parentPid = ${process.pid};`,
|
|
1027
|
+
`const delayMs = ${delayMs};`,
|
|
1028
|
+
"const maxWaitMs = 120000;",
|
|
1029
|
+
"const retryIntervalMs = 1000;",
|
|
1030
|
+
"const startTimeoutMs = 60000;",
|
|
1031
|
+
`const nodePath = ${JSON.stringify(process.execPath)};`,
|
|
1032
|
+
`const startArgs = ${JSON.stringify(startArgs)};`,
|
|
1033
|
+
`const serviceStatePath = ${JSON.stringify(serviceStatePath)};`,
|
|
1034
|
+
"function isRunning(pid) {",
|
|
1035
|
+
" try {",
|
|
1036
|
+
" process.kill(pid, 0);",
|
|
1037
|
+
" return true;",
|
|
1038
|
+
" } catch {",
|
|
1039
|
+
" return false;",
|
|
1040
|
+
" }",
|
|
1041
|
+
"}",
|
|
1042
|
+
"function hasReplacementService() {",
|
|
1043
|
+
" try {",
|
|
1044
|
+
' const raw = readFileSync(serviceStatePath, "utf-8");',
|
|
1045
|
+
" const state = JSON.parse(raw);",
|
|
1046
|
+
" const pid = Number(state?.pid);",
|
|
1047
|
+
" return Number.isFinite(pid) && pid > 0 && pid !== parentPid && isRunning(pid);",
|
|
1048
|
+
" } catch {",
|
|
1049
|
+
" return false;",
|
|
1050
|
+
" }",
|
|
1051
|
+
"}",
|
|
1052
|
+
"function tryStart() {",
|
|
1053
|
+
" spawnSync(nodePath, startArgs, {",
|
|
1054
|
+
' stdio: "ignore",',
|
|
1055
|
+
" env: process.env,",
|
|
1056
|
+
" timeout: startTimeoutMs",
|
|
1057
|
+
" });",
|
|
1058
|
+
"}",
|
|
1059
|
+
"setTimeout(() => {",
|
|
1060
|
+
" const startedAt = Date.now();",
|
|
1061
|
+
" const tick = () => {",
|
|
1062
|
+
" if (hasReplacementService()) {",
|
|
1063
|
+
" process.exit(0);",
|
|
1064
|
+
" return;",
|
|
1065
|
+
" }",
|
|
1066
|
+
" if (Date.now() - startedAt >= maxWaitMs) {",
|
|
1067
|
+
" process.exit(0);",
|
|
1068
|
+
" return;",
|
|
1069
|
+
" }",
|
|
1070
|
+
" tryStart();",
|
|
1071
|
+
" if (hasReplacementService()) {",
|
|
1072
|
+
" process.exit(0);",
|
|
1073
|
+
" return;",
|
|
1074
|
+
" }",
|
|
1075
|
+
" setTimeout(tick, retryIntervalMs);",
|
|
1076
|
+
" };",
|
|
1077
|
+
" tick();",
|
|
1078
|
+
"}, delayMs);"
|
|
1079
|
+
].join("\n");
|
|
1080
|
+
try {
|
|
1081
|
+
const helper = spawn2(process.execPath, ["-e", helperScript], {
|
|
1082
|
+
detached: true,
|
|
1083
|
+
stdio: "ignore",
|
|
1084
|
+
env: process.env
|
|
1085
|
+
});
|
|
1086
|
+
helper.unref();
|
|
1087
|
+
this.selfRelaunchArmed = true;
|
|
1088
|
+
console.warn(`Gateway self-restart armed (${params.reason}).`);
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
console.error(`Failed to arm gateway self-restart: ${String(error)}`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1005
1093
|
async requestRestart(params) {
|
|
1094
|
+
this.armManagedServiceRelaunch({
|
|
1095
|
+
reason: params.reason,
|
|
1096
|
+
strategy: params.strategy,
|
|
1097
|
+
delayMs: params.delayMs
|
|
1098
|
+
});
|
|
1006
1099
|
const result = await this.restartCoordinator.requestRestart({
|
|
1007
1100
|
reason: params.reason,
|
|
1008
1101
|
strategy: params.strategy,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextclaw",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"chokidar": "^3.6.0",
|
|
40
40
|
"commander": "^12.1.0",
|
|
41
|
-
"@nextclaw/core": "^0.5.
|
|
41
|
+
"@nextclaw/core": "^0.5.3",
|
|
42
42
|
"@nextclaw/server": "^0.3.7",
|
|
43
|
-
"@nextclaw/openclaw-compat": "^0.1.
|
|
43
|
+
"@nextclaw/openclaw-compat": "^0.1.4"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^20.17.6",
|
package/templates/USAGE.md
CHANGED
|
@@ -285,8 +285,9 @@ Behavior:
|
|
|
285
285
|
- If `NEXTCLAW_UPDATE_COMMAND` is set, the CLI executes it (useful for custom update flows).
|
|
286
286
|
- Otherwise it falls back to `npm i -g nextclaw`.
|
|
287
287
|
- If the background service is running, restart it after the update to apply changes.
|
|
288
|
+
- When update is triggered from the running gateway (agent `update.run`), NextClaw arms a self-relaunch helper before exiting, so the service comes back automatically (like an OS reboot flow).
|
|
288
289
|
|
|
289
|
-
If the gateway is running, you can also ask the agent to update; the agent will call the gateway update tool only when you explicitly request it, and
|
|
290
|
+
If the gateway is running, you can also ask the agent to update; the agent will call the gateway update tool only when you explicitly request it, and restart/relaunch will be scheduled afterward.
|
|
290
291
|
|
|
291
292
|
---
|
|
292
293
|
|