nextclaw 0.4.17 → 0.5.1
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 -48
- package/package.json +4 -4
package/dist/cli/index.js
CHANGED
|
@@ -130,7 +130,7 @@ async function resolvePublicIp(timeoutMs = 1500) {
|
|
|
130
130
|
}
|
|
131
131
|
function buildServeArgs(options) {
|
|
132
132
|
const cliPath = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
133
|
-
return [cliPath, "serve", "--ui-
|
|
133
|
+
return [cliPath, "serve", "--ui-port", String(options.uiPort)];
|
|
134
134
|
}
|
|
135
135
|
function readServiceState() {
|
|
136
136
|
const path = resolveServiceStatePath();
|
|
@@ -627,6 +627,7 @@ function buildClawHubArgs(slug, options) {
|
|
|
627
627
|
// src/cli/runtime.ts
|
|
628
628
|
var LOGO = "\u{1F916}";
|
|
629
629
|
var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "/exit", "/quit", ":q"]);
|
|
630
|
+
var FORCED_PUBLIC_UI_HOST = "0.0.0.0";
|
|
630
631
|
function isIndexSegment(raw) {
|
|
631
632
|
return /^[0-9]+$/.test(raw);
|
|
632
633
|
}
|
|
@@ -980,7 +981,7 @@ var CliRuntime = class {
|
|
|
980
981
|
if (!state || !isProcessRunning(state.pid) || state.pid === process.pid) {
|
|
981
982
|
return false;
|
|
982
983
|
}
|
|
983
|
-
const uiHost =
|
|
984
|
+
const uiHost = FORCED_PUBLIC_UI_HOST;
|
|
984
985
|
const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
|
|
985
986
|
console.log(`Applying changes (${reason}): restarting ${APP_NAME} background service...`);
|
|
986
987
|
await this.stopService();
|
|
@@ -1063,58 +1064,41 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1063
1064
|
}
|
|
1064
1065
|
}
|
|
1065
1066
|
async gateway(opts) {
|
|
1066
|
-
const uiOverrides = {
|
|
1067
|
+
const uiOverrides = {
|
|
1068
|
+
host: FORCED_PUBLIC_UI_HOST
|
|
1069
|
+
};
|
|
1067
1070
|
if (opts.ui) {
|
|
1068
1071
|
uiOverrides.enabled = true;
|
|
1069
1072
|
}
|
|
1070
|
-
if (opts.uiHost) {
|
|
1071
|
-
uiOverrides.host = String(opts.uiHost);
|
|
1072
|
-
}
|
|
1073
1073
|
if (opts.uiPort) {
|
|
1074
1074
|
uiOverrides.port = Number(opts.uiPort);
|
|
1075
1075
|
}
|
|
1076
1076
|
if (opts.uiOpen) {
|
|
1077
1077
|
uiOverrides.open = true;
|
|
1078
1078
|
}
|
|
1079
|
-
if (opts.public) {
|
|
1080
|
-
uiOverrides.enabled = true;
|
|
1081
|
-
if (!opts.uiHost) {
|
|
1082
|
-
uiOverrides.host = "0.0.0.0";
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
1079
|
await this.startGateway({ uiOverrides });
|
|
1086
1080
|
}
|
|
1087
1081
|
async ui(opts) {
|
|
1088
1082
|
const uiOverrides = {
|
|
1089
1083
|
enabled: true,
|
|
1084
|
+
host: FORCED_PUBLIC_UI_HOST,
|
|
1090
1085
|
open: Boolean(opts.open)
|
|
1091
1086
|
};
|
|
1092
|
-
if (opts.host) {
|
|
1093
|
-
uiOverrides.host = String(opts.host);
|
|
1094
|
-
}
|
|
1095
1087
|
if (opts.port) {
|
|
1096
1088
|
uiOverrides.port = Number(opts.port);
|
|
1097
1089
|
}
|
|
1098
|
-
if (opts.public && !opts.host) {
|
|
1099
|
-
uiOverrides.host = "0.0.0.0";
|
|
1100
|
-
}
|
|
1101
1090
|
await this.startGateway({ uiOverrides, allowMissingProvider: true });
|
|
1102
1091
|
}
|
|
1103
1092
|
async start(opts) {
|
|
1104
1093
|
await this.init({ source: "start", auto: true });
|
|
1105
1094
|
const uiOverrides = {
|
|
1106
1095
|
enabled: true,
|
|
1096
|
+
host: FORCED_PUBLIC_UI_HOST,
|
|
1107
1097
|
open: false
|
|
1108
1098
|
};
|
|
1109
|
-
if (opts.uiHost) {
|
|
1110
|
-
uiOverrides.host = String(opts.uiHost);
|
|
1111
|
-
}
|
|
1112
1099
|
if (opts.uiPort) {
|
|
1113
1100
|
uiOverrides.port = Number(opts.uiPort);
|
|
1114
1101
|
}
|
|
1115
|
-
if (opts.public && !opts.uiHost) {
|
|
1116
|
-
uiOverrides.host = "0.0.0.0";
|
|
1117
|
-
}
|
|
1118
1102
|
await this.startService({
|
|
1119
1103
|
uiOverrides,
|
|
1120
1104
|
open: Boolean(opts.open)
|
|
@@ -1136,17 +1120,12 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1136
1120
|
async serve(opts) {
|
|
1137
1121
|
const uiOverrides = {
|
|
1138
1122
|
enabled: true,
|
|
1123
|
+
host: FORCED_PUBLIC_UI_HOST,
|
|
1139
1124
|
open: false
|
|
1140
1125
|
};
|
|
1141
|
-
if (opts.uiHost) {
|
|
1142
|
-
uiOverrides.host = String(opts.uiHost);
|
|
1143
|
-
}
|
|
1144
1126
|
if (opts.uiPort) {
|
|
1145
1127
|
uiOverrides.port = Number(opts.uiPort);
|
|
1146
1128
|
}
|
|
1147
|
-
if (opts.public && !opts.uiHost) {
|
|
1148
|
-
uiOverrides.host = "0.0.0.0";
|
|
1149
|
-
}
|
|
1150
1129
|
await this.runForeground({
|
|
1151
1130
|
uiOverrides,
|
|
1152
1131
|
open: Boolean(opts.open)
|
|
@@ -1168,6 +1147,10 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1168
1147
|
bus,
|
|
1169
1148
|
providerManager,
|
|
1170
1149
|
workspace,
|
|
1150
|
+
model: config2.agents.defaults.model,
|
|
1151
|
+
maxIterations: config2.agents.defaults.maxToolIterations,
|
|
1152
|
+
maxTokens: config2.agents.defaults.maxTokens,
|
|
1153
|
+
temperature: config2.agents.defaults.temperature,
|
|
1171
1154
|
braveApiKey: config2.tools.web.search.apiKey || void 0,
|
|
1172
1155
|
execConfig: config2.tools.exec,
|
|
1173
1156
|
restrictToWorkspace: config2.tools.restrictToWorkspace,
|
|
@@ -1424,16 +1407,19 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1424
1407
|
process.exit(1);
|
|
1425
1408
|
return;
|
|
1426
1409
|
}
|
|
1427
|
-
const
|
|
1410
|
+
const prevConfig = loadConfig();
|
|
1411
|
+
const nextConfig = structuredClone(prevConfig);
|
|
1428
1412
|
try {
|
|
1429
|
-
setAtConfigPath(
|
|
1413
|
+
setAtConfigPath(nextConfig, parsedPath, parsedValue);
|
|
1430
1414
|
} catch (error) {
|
|
1431
1415
|
console.error(String(error));
|
|
1432
1416
|
process.exit(1);
|
|
1433
1417
|
return;
|
|
1434
1418
|
}
|
|
1435
|
-
saveConfig(
|
|
1436
|
-
await this.
|
|
1419
|
+
saveConfig(nextConfig);
|
|
1420
|
+
await this.requestRestartForConfigDiff({
|
|
1421
|
+
prevConfig,
|
|
1422
|
+
nextConfig,
|
|
1437
1423
|
reason: `config.set ${pathExpr}`,
|
|
1438
1424
|
manualMessage: `Updated ${pathExpr}. Restart the gateway to apply.`
|
|
1439
1425
|
});
|
|
@@ -1447,19 +1433,36 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
1447
1433
|
process.exit(1);
|
|
1448
1434
|
return;
|
|
1449
1435
|
}
|
|
1450
|
-
const
|
|
1451
|
-
const
|
|
1436
|
+
const prevConfig = loadConfig();
|
|
1437
|
+
const nextConfig = structuredClone(prevConfig);
|
|
1438
|
+
const removed = unsetAtConfigPath(nextConfig, parsedPath);
|
|
1452
1439
|
if (!removed) {
|
|
1453
1440
|
console.error(`Config path not found: ${pathExpr}`);
|
|
1454
1441
|
process.exit(1);
|
|
1455
1442
|
return;
|
|
1456
1443
|
}
|
|
1457
|
-
saveConfig(
|
|
1458
|
-
await this.
|
|
1444
|
+
saveConfig(nextConfig);
|
|
1445
|
+
await this.requestRestartForConfigDiff({
|
|
1446
|
+
prevConfig,
|
|
1447
|
+
nextConfig,
|
|
1459
1448
|
reason: `config.unset ${pathExpr}`,
|
|
1460
1449
|
manualMessage: `Removed ${pathExpr}. Restart the gateway to apply.`
|
|
1461
1450
|
});
|
|
1462
1451
|
}
|
|
1452
|
+
async requestRestartForConfigDiff(params) {
|
|
1453
|
+
const changedPaths = diffConfigPaths(params.prevConfig, params.nextConfig);
|
|
1454
|
+
if (!changedPaths.length) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
const plan = buildReloadPlan(changedPaths);
|
|
1458
|
+
if (plan.restartRequired.length === 0) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
await this.requestRestart({
|
|
1462
|
+
reason: `${params.reason} (${plan.restartRequired.join(", ")})`,
|
|
1463
|
+
manualMessage: params.manualMessage
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1463
1466
|
async pluginsEnable(id) {
|
|
1464
1467
|
const config2 = loadConfig();
|
|
1465
1468
|
const next = enablePluginInConfig(config2, id);
|
|
@@ -2067,6 +2070,8 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2067
2070
|
workspace,
|
|
2068
2071
|
model: config2.agents.defaults.model,
|
|
2069
2072
|
maxIterations: config2.agents.defaults.maxToolIterations,
|
|
2073
|
+
maxTokens: config2.agents.defaults.maxTokens,
|
|
2074
|
+
temperature: config2.agents.defaults.temperature,
|
|
2070
2075
|
braveApiKey: config2.tools.web.search.apiKey || void 0,
|
|
2071
2076
|
execConfig: config2.tools.exec,
|
|
2072
2077
|
cronService: cron2,
|
|
@@ -2197,7 +2202,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2197
2202
|
}
|
|
2198
2203
|
async printPublicUiUrls(host, port) {
|
|
2199
2204
|
if (isLoopbackHost(host)) {
|
|
2200
|
-
console.log(
|
|
2205
|
+
console.log("Public URL: disabled (UI host is loopback). Current release expects public exposure; run nextclaw restart.");
|
|
2201
2206
|
return;
|
|
2202
2207
|
}
|
|
2203
2208
|
const publicIp = await resolvePublicIp();
|
|
@@ -2268,13 +2273,19 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2268
2273
|
};
|
|
2269
2274
|
}
|
|
2270
2275
|
})();
|
|
2271
|
-
await this.printPublicUiUrls(parsedUi.host, parsedUi.port);
|
|
2272
2276
|
if (parsedUi.host !== uiConfig.host || parsedUi.port !== uiConfig.port) {
|
|
2273
2277
|
console.log(
|
|
2274
|
-
`
|
|
2278
|
+
`Detected running service UI bind (${parsedUi.host}:${parsedUi.port}); enforcing (${uiConfig.host}:${uiConfig.port})...`
|
|
2275
2279
|
);
|
|
2276
|
-
|
|
2280
|
+
await this.stopService();
|
|
2281
|
+
const stateAfterStop = readServiceState();
|
|
2282
|
+
if (stateAfterStop && isProcessRunning(stateAfterStop.pid)) {
|
|
2283
|
+
console.error("Error: Failed to stop running service while enforcing public UI exposure.");
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
return this.startService(options);
|
|
2277
2287
|
}
|
|
2288
|
+
await this.printPublicUiUrls(parsedUi.host, parsedUi.port);
|
|
2278
2289
|
console.log(`Logs: ${existing.logPath}`);
|
|
2279
2290
|
console.log(`Stop: ${APP_NAME} stop`);
|
|
2280
2291
|
return;
|
|
@@ -2290,7 +2301,6 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2290
2301
|
mkdirSync2(logDir, { recursive: true });
|
|
2291
2302
|
const logFd = openSync(logPath, "a");
|
|
2292
2303
|
const serveArgs = buildServeArgs({
|
|
2293
|
-
uiHost: uiConfig.host,
|
|
2294
2304
|
uiPort: uiConfig.port
|
|
2295
2305
|
});
|
|
2296
2306
|
const child = spawn2(process.execPath, [...process.execArgv, ...serveArgs], {
|
|
@@ -2303,6 +2313,24 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2303
2313
|
console.error("Error: Failed to start background service.");
|
|
2304
2314
|
return;
|
|
2305
2315
|
}
|
|
2316
|
+
const healthUrl = `${apiUrl}/health`;
|
|
2317
|
+
const started = await this.waitForBackgroundServiceReady({
|
|
2318
|
+
pid: child.pid,
|
|
2319
|
+
healthUrl,
|
|
2320
|
+
timeoutMs: 8e3
|
|
2321
|
+
});
|
|
2322
|
+
if (!started) {
|
|
2323
|
+
if (isProcessRunning(child.pid)) {
|
|
2324
|
+
try {
|
|
2325
|
+
process.kill(child.pid, "SIGTERM");
|
|
2326
|
+
await waitForExit(child.pid, 2e3);
|
|
2327
|
+
} catch {
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
clearServiceState();
|
|
2331
|
+
console.error(`Error: Failed to start background service. Check logs: ${logPath}`);
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2306
2334
|
child.unref();
|
|
2307
2335
|
const state = {
|
|
2308
2336
|
pid: child.pid,
|
|
@@ -2324,6 +2352,23 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
|
|
|
2324
2352
|
openBrowser(uiUrl);
|
|
2325
2353
|
}
|
|
2326
2354
|
}
|
|
2355
|
+
async waitForBackgroundServiceReady(params) {
|
|
2356
|
+
const startedAt = Date.now();
|
|
2357
|
+
while (Date.now() - startedAt < params.timeoutMs) {
|
|
2358
|
+
if (!isProcessRunning(params.pid)) {
|
|
2359
|
+
return false;
|
|
2360
|
+
}
|
|
2361
|
+
try {
|
|
2362
|
+
const response = await fetch(params.healthUrl, { method: "GET" });
|
|
2363
|
+
if (response.ok) {
|
|
2364
|
+
return true;
|
|
2365
|
+
}
|
|
2366
|
+
} catch {
|
|
2367
|
+
}
|
|
2368
|
+
await new Promise((resolve5) => setTimeout(resolve5, 200));
|
|
2369
|
+
}
|
|
2370
|
+
return false;
|
|
2371
|
+
}
|
|
2327
2372
|
async stopService() {
|
|
2328
2373
|
const state = readServiceState();
|
|
2329
2374
|
if (!state) {
|
|
@@ -2592,11 +2637,11 @@ var runtime = new CliRuntime({ logo: LOGO });
|
|
|
2592
2637
|
program.name(APP_NAME2).description(`${LOGO} ${APP_NAME2} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
|
|
2593
2638
|
program.command("onboard").description(`Initialize ${APP_NAME2} configuration and workspace`).action(async () => runtime.onboard());
|
|
2594
2639
|
program.command("init").description(`Initialize ${APP_NAME2} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
|
|
2595
|
-
program.command("gateway").description(`Start the ${APP_NAME2} gateway`).option("-p, --port <port>", "Gateway port", "18790").option("-v, --verbose", "Verbose output", false).option("--ui", "Enable UI server", false).option("--ui-
|
|
2596
|
-
program.command("ui").description(`Start the ${APP_NAME2} UI with gateway`).option("--
|
|
2597
|
-
program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the background`).option("--ui-
|
|
2598
|
-
program.command("restart").description(`Restart the ${APP_NAME2} background service`).option("--ui-
|
|
2599
|
-
program.command("serve").description(`Run the ${APP_NAME2} gateway + UI in the foreground`).option("--ui-
|
|
2640
|
+
program.command("gateway").description(`Start the ${APP_NAME2} gateway`).option("-p, --port <port>", "Gateway port", "18790").option("-v, --verbose", "Verbose output", false).option("--ui", "Enable UI server", false).option("--ui-port <port>", "UI port").option("--ui-open", "Open browser when UI starts", false).action(async (opts) => runtime.gateway(opts));
|
|
2641
|
+
program.command("ui").description(`Start the ${APP_NAME2} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.ui(opts));
|
|
2642
|
+
program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the background`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.start(opts));
|
|
2643
|
+
program.command("restart").description(`Restart the ${APP_NAME2} background service`).option("--ui-port <port>", "UI port").option("--open", "Open browser after restart", false).action(async (opts) => runtime.restart(opts));
|
|
2644
|
+
program.command("serve").description(`Run the ${APP_NAME2} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
|
|
2600
2645
|
program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
|
|
2601
2646
|
program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
|
|
2602
2647
|
program.command("update").description(`Update ${APP_NAME2}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
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.
|
|
42
|
-
"@nextclaw/server": "^0.3.
|
|
43
|
-
"@nextclaw/openclaw-compat": "^0.1.
|
|
41
|
+
"@nextclaw/core": "^0.5.1",
|
|
42
|
+
"@nextclaw/server": "^0.3.7",
|
|
43
|
+
"@nextclaw/openclaw-compat": "^0.1.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^20.17.6",
|