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.
Files changed (2) hide show
  1. package/dist/cli/index.js +93 -48
  2. 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-host", options.uiHost, "--ui-port", String(options.uiPort)];
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 = state.uiHost ?? "127.0.0.1";
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 config2 = loadConfig();
1410
+ const prevConfig = loadConfig();
1411
+ const nextConfig = structuredClone(prevConfig);
1428
1412
  try {
1429
- setAtConfigPath(config2, parsedPath, parsedValue);
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(config2);
1436
- await this.requestRestart({
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 config2 = loadConfig();
1451
- const removed = unsetAtConfigPath(config2, parsedPath);
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(config2);
1458
- await this.requestRestart({
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('Public URL: disabled (UI host is loopback). Use "--public" or "--ui-host 0.0.0.0" to expose it.');
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
- `Note: requested UI bind (${uiConfig.host}:${uiConfig.port}) differs from running service (${parsedUi.host}:${parsedUi.port}).`
2278
+ `Detected running service UI bind (${parsedUi.host}:${parsedUi.port}); enforcing (${uiConfig.host}:${uiConfig.port})...`
2275
2279
  );
2276
- console.log(`Run: ${APP_NAME} restart${uiConfig.host === "0.0.0.0" ? " --public" : ""}`);
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-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--ui-open", "Open browser when UI starts", false).option("--public", "Expose UI on 0.0.0.0 and print public URL", false).action(async (opts) => runtime.gateway(opts));
2596
- program.command("ui").description(`Start the ${APP_NAME2} UI with gateway`).option("--host <host>", "UI host").option("--port <port>", "UI port").option("--no-open", "Disable opening browser").option("--public", "Expose UI on 0.0.0.0 and print public URL", false).action(async (opts) => runtime.ui(opts));
2597
- program.command("start").description(`Start the ${APP_NAME2} gateway + UI in the background`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).option("--public", "Expose UI on 0.0.0.0 and print public URL", false).action(async (opts) => runtime.start(opts));
2598
- program.command("restart").description(`Restart the ${APP_NAME2} background service`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--open", "Open browser after restart", false).option("--public", "Expose UI on 0.0.0.0 and print public URL", false).action(async (opts) => runtime.restart(opts));
2599
- program.command("serve").description(`Run the ${APP_NAME2} gateway + UI in the foreground`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).option("--public", "Expose UI on 0.0.0.0 and print public URL", false).action(async (opts) => runtime.serve(opts));
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.4.17",
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.4.14",
42
- "@nextclaw/server": "^0.3.5",
43
- "@nextclaw/openclaw-compat": "^0.1.2"
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",