nextclaw 0.4.14 → 0.4.15

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 CHANGED
@@ -81,7 +81,7 @@ import {
81
81
  import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "fs";
82
82
  import { join, resolve } from "path";
83
83
  import { spawn } from "child_process";
84
- import { createServer } from "net";
84
+ import { createServer, isIP } from "net";
85
85
  import { fileURLToPath } from "url";
86
86
  import { getDataDir, getPackageVersion as getCorePackageVersion } from "@nextclaw/core";
87
87
  function resolveUiConfig(config2, overrides) {
@@ -92,6 +92,41 @@ function resolveUiApiBase(host, port) {
92
92
  const normalizedHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
93
93
  return `http://${normalizedHost}:${port}`;
94
94
  }
95
+ function isLoopbackHost(host) {
96
+ const normalized = host.trim().toLowerCase();
97
+ return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1";
98
+ }
99
+ var PUBLIC_IP_CHECK_URLS = ["https://api.ipify.org", "https://ifconfig.me/ip"];
100
+ async function fetchPublicIpFrom(url, timeoutMs) {
101
+ const controller = new AbortController();
102
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
103
+ try {
104
+ const response = await fetch(url, {
105
+ signal: controller.signal,
106
+ headers: {
107
+ Accept: "text/plain"
108
+ }
109
+ });
110
+ if (!response.ok) {
111
+ return null;
112
+ }
113
+ const text = (await response.text()).trim();
114
+ return isIP(text) ? text : null;
115
+ } catch {
116
+ return null;
117
+ } finally {
118
+ clearTimeout(timer);
119
+ }
120
+ }
121
+ async function resolvePublicIp(timeoutMs = 1500) {
122
+ for (const endpoint of PUBLIC_IP_CHECK_URLS) {
123
+ const candidate = await fetchPublicIpFrom(endpoint, timeoutMs);
124
+ if (candidate) {
125
+ return candidate;
126
+ }
127
+ }
128
+ return null;
129
+ }
95
130
  function isDevRuntime() {
96
131
  return import.meta.url.includes("/src/cli/") || process.env.NEXTCLAW_DEV === "1";
97
132
  }
@@ -989,6 +1024,12 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
989
1024
  if (opts.uiOpen) {
990
1025
  uiOverrides.open = true;
991
1026
  }
1027
+ if (opts.public) {
1028
+ uiOverrides.enabled = true;
1029
+ if (!opts.uiHost) {
1030
+ uiOverrides.host = "0.0.0.0";
1031
+ }
1032
+ }
992
1033
  await this.startGateway({ uiOverrides });
993
1034
  }
994
1035
  async ui(opts) {
@@ -1002,6 +1043,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1002
1043
  if (opts.port) {
1003
1044
  uiOverrides.port = Number(opts.port);
1004
1045
  }
1046
+ if (opts.public && !opts.host) {
1047
+ uiOverrides.host = "0.0.0.0";
1048
+ }
1005
1049
  await this.startGateway({ uiOverrides, allowMissingProvider: true });
1006
1050
  }
1007
1051
  async start(opts) {
@@ -1016,6 +1060,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1016
1060
  if (opts.uiPort) {
1017
1061
  uiOverrides.port = Number(opts.uiPort);
1018
1062
  }
1063
+ if (opts.public && !opts.uiHost) {
1064
+ uiOverrides.host = "0.0.0.0";
1065
+ }
1019
1066
  const devMode = isDevRuntime();
1020
1067
  if (devMode) {
1021
1068
  const requestedUiPort = Number.isFinite(Number(opts.uiPort)) ? Number(opts.uiPort) : 18792;
@@ -1072,6 +1119,9 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
1072
1119
  if (opts.uiPort) {
1073
1120
  uiOverrides.port = Number(opts.uiPort);
1074
1121
  }
1122
+ if (opts.public && !opts.uiHost) {
1123
+ uiOverrides.host = "0.0.0.0";
1124
+ }
1075
1125
  const devMode = isDevRuntime();
1076
1126
  if (devMode && uiOverrides.port === void 0) {
1077
1127
  uiOverrides.port = 18792;
@@ -2104,6 +2154,20 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
2104
2154
  setPluginRuntimeBridge(null);
2105
2155
  }
2106
2156
  }
2157
+ async printPublicUiUrls(host, port) {
2158
+ if (isLoopbackHost(host)) {
2159
+ console.log('Public URL: disabled (UI host is loopback). Use "--public" or "--ui-host 0.0.0.0" to expose it.');
2160
+ return;
2161
+ }
2162
+ const publicIp = await resolvePublicIp();
2163
+ if (!publicIp) {
2164
+ console.log("Public URL: UI is exposed, but automatic public IP detection failed.");
2165
+ return;
2166
+ }
2167
+ const publicBase = `http://${publicIp}:${port}`;
2168
+ console.log(`Public UI (if firewall/NAT allows): ${publicBase}`);
2169
+ console.log(`Public API (if firewall/NAT allows): ${publicBase}/api`);
2170
+ }
2107
2171
  startUiIfEnabled(uiConfig, uiStaticDir) {
2108
2172
  if (!uiConfig.enabled) {
2109
2173
  return;
@@ -2119,6 +2183,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
2119
2183
  if (uiStaticDir) {
2120
2184
  console.log(`\u2713 UI frontend: ${uiUrl}`);
2121
2185
  }
2186
+ void this.printPublicUiUrls(uiServer.host, uiServer.port);
2122
2187
  if (uiConfig.open) {
2123
2188
  openBrowser(uiUrl);
2124
2189
  }
@@ -2209,6 +2274,7 @@ ${this.logo} ${APP_NAME} is ready! (${source})`);
2209
2274
  console.log(`\u2713 ${APP_NAME} started in background (PID ${state.pid})`);
2210
2275
  console.log(`UI: ${uiUrl}`);
2211
2276
  console.log(`API: ${apiUrl}`);
2277
+ await this.printPublicUiUrls(uiConfig.host, uiConfig.port);
2212
2278
  console.log(`Logs: ${logPath}`);
2213
2279
  console.log(`Stop: ${APP_NAME} stop`);
2214
2280
  if (options.open) {
@@ -2480,11 +2546,11 @@ var runtime = new CliRuntime({ logo: LOGO });
2480
2546
  program.name(APP_NAME2).description(`${LOGO} ${APP_NAME2} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
2481
2547
  program.command("onboard").description(`Initialize ${APP_NAME2} configuration and workspace`).action(async () => runtime.onboard());
2482
2548
  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) }));
2483
- 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).action(async (opts) => runtime.gateway(opts));
2484
- 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").action(async (opts) => runtime.ui(opts));
2485
- 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("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server port").option("--open", "Open browser after start", false).action(async (opts) => runtime.start(opts));
2486
- program.command("restart").description(`Restart the ${APP_NAME2} background service`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server port").option("--open", "Open browser after restart", false).action(async (opts) => runtime.restart(opts));
2487
- 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("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
2549
+ 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));
2550
+ 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));
2551
+ 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("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server 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));
2552
+ program.command("restart").description(`Restart the ${APP_NAME2} background service`).option("--ui-host <host>", "UI host").option("--ui-port <port>", "UI port").option("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server 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));
2553
+ 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("--frontend", "Start UI frontend dev server").option("--frontend-port <port>", "UI frontend dev server 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));
2488
2554
  program.command("stop").description(`Stop the ${APP_NAME2} background service`).action(async () => runtime.stop());
2489
2555
  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));
2490
2556
  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.14",
3
+ "version": "0.4.15",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",