nextclaw 0.9.9 → 0.9.10

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 (32) hide show
  1. package/dist/cli/index.js +181 -19
  2. package/package.json +2 -2
  3. package/templates/USAGE.md +9 -0
  4. package/ui-dist/assets/ChannelsList-Dz8AGmaQ.js +1 -0
  5. package/ui-dist/assets/{ChatPage-Def8_fbf.js → ChatPage-BXDyt7BL.js} +21 -21
  6. package/ui-dist/assets/DocBrowser-CkKvzF7m.js +1 -0
  7. package/ui-dist/assets/{LogoBadge-uiBOKAEe.js → LogoBadge-C_ygxoGB.js} +1 -1
  8. package/ui-dist/assets/{MarketplacePage-BwMsA6VY.js → MarketplacePage-DEvRs-Jc.js} +2 -2
  9. package/ui-dist/assets/ModelConfig-BGfliN2Z.js +1 -0
  10. package/ui-dist/assets/ProvidersList-BHLGLSvs.js +1 -0
  11. package/ui-dist/assets/RuntimeConfig-Clltld_h.js +1 -0
  12. package/ui-dist/assets/{SecretsConfig-BLZW7-5k.js → SecretsConfig-CaJLf7oJ.js} +2 -2
  13. package/ui-dist/assets/SessionsConfig-3QF7K9wm.js +2 -0
  14. package/ui-dist/assets/{card-BoxPL3fs.js → card-DXo3NsaB.js} +1 -1
  15. package/ui-dist/assets/index-CGo5Vnh0.js +7 -0
  16. package/ui-dist/assets/input-CzTldMKo.js +1 -0
  17. package/ui-dist/assets/{label-BdIFsV9G.js → label-De__vsU7.js} +1 -1
  18. package/ui-dist/assets/{page-layout-BdhEwv_X.js → page-layout-BOgLC2tK.js} +1 -1
  19. package/ui-dist/assets/{session-run-status-DJ7s-TnE.js → session-run-status-DQVCDxTb.js} +2 -2
  20. package/ui-dist/assets/{switch-DD9iXp2E.js → switch-pMrS4heA.js} +1 -1
  21. package/ui-dist/assets/{tabs-custom-DPMQcAvE.js → tabs-custom-DhOxWfCb.js} +1 -1
  22. package/ui-dist/assets/{useConfirmDialog-D0WoeDcH.js → useConfirmDialog-CseKBGh5.js} +2 -2
  23. package/ui-dist/assets/{vendor-CzIVZvq9.js → vendor-D33xZtEC.js} +6 -6
  24. package/ui-dist/index.html +2 -2
  25. package/ui-dist/assets/ChannelsList-CrQ3OXby.js +0 -1
  26. package/ui-dist/assets/DocBrowser-Dg-b4Atq.js +0 -1
  27. package/ui-dist/assets/ModelConfig-DVsQVvIt.js +0 -1
  28. package/ui-dist/assets/ProvidersList-BSJGEhJe.js +0 -1
  29. package/ui-dist/assets/RuntimeConfig-CZpxTqCn.js +0 -1
  30. package/ui-dist/assets/SessionsConfig-H_gq5Wtk.js +0 -2
  31. package/ui-dist/assets/index-Cwtv_w6q.js +0 -2
  32. package/ui-dist/assets/useConfig-BiE7PGOv.js +0 -6
package/dist/cli/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
27
27
  setPluginRuntimeBridge as setPluginRuntimeBridge2
28
28
  } from "@nextclaw/openclaw-compat";
29
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
29
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
30
30
  import { join as join7, resolve as resolve9 } from "path";
31
31
  import { createInterface as createInterface2 } from "readline";
32
32
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -1831,8 +1831,8 @@ import { join as join3 } from "path";
1831
1831
  var CronCommands = class {
1832
1832
  cronList(opts) {
1833
1833
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1834
- const service = new CronService(storePath);
1835
- const jobs = service.listJobs(Boolean(opts.all));
1834
+ const service2 = new CronService(storePath);
1835
+ const jobs = service2.listJobs(Boolean(opts.all));
1836
1836
  if (!jobs.length) {
1837
1837
  console.log("No scheduled jobs.");
1838
1838
  return;
@@ -1851,7 +1851,7 @@ var CronCommands = class {
1851
1851
  }
1852
1852
  cronAdd(opts) {
1853
1853
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1854
- const service = new CronService(storePath);
1854
+ const service2 = new CronService(storePath);
1855
1855
  let schedule = null;
1856
1856
  if (opts.every) {
1857
1857
  schedule = { kind: "every", everyMs: Number(opts.every) * 1e3 };
@@ -1864,7 +1864,7 @@ var CronCommands = class {
1864
1864
  console.error("Error: Must specify --every, --cron, or --at");
1865
1865
  return;
1866
1866
  }
1867
- const job = service.addJob({
1867
+ const job = service2.addJob({
1868
1868
  name: opts.name,
1869
1869
  schedule,
1870
1870
  message: opts.message,
@@ -1876,8 +1876,8 @@ var CronCommands = class {
1876
1876
  }
1877
1877
  cronRemove(jobId) {
1878
1878
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1879
- const service = new CronService(storePath);
1880
- if (service.removeJob(jobId)) {
1879
+ const service2 = new CronService(storePath);
1880
+ if (service2.removeJob(jobId)) {
1881
1881
  console.log(`\u2713 Removed job ${jobId}`);
1882
1882
  } else {
1883
1883
  console.log(`Job ${jobId} not found`);
@@ -1885,8 +1885,8 @@ var CronCommands = class {
1885
1885
  }
1886
1886
  cronEnable(jobId, opts) {
1887
1887
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1888
- const service = new CronService(storePath);
1889
- const job = service.enableJob(jobId, !opts.disable);
1888
+ const service2 = new CronService(storePath);
1889
+ const job = service2.enableJob(jobId, !opts.disable);
1890
1890
  if (job) {
1891
1891
  console.log(`\u2713 Job '${job.name}' ${opts.disable ? "disabled" : "enabled"}`);
1892
1892
  } else {
@@ -1895,8 +1895,8 @@ var CronCommands = class {
1895
1895
  }
1896
1896
  async cronRun(jobId, opts) {
1897
1897
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1898
- const service = new CronService(storePath);
1899
- const ok = await service.runJob(jobId, Boolean(opts.force));
1898
+ const service2 = new CronService(storePath);
1899
+ const ok = await service2.runJob(jobId, Boolean(opts.force));
1900
1900
  console.log(ok ? "\u2713 Job executed" : `Failed to run job ${jobId}`);
1901
1901
  }
1902
1902
  };
@@ -2252,11 +2252,12 @@ import {
2252
2252
  stopPluginChannelGateways
2253
2253
  } from "@nextclaw/openclaw-compat";
2254
2254
  import { startUiServer } from "@nextclaw/server";
2255
- import { appendFileSync, closeSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, openSync, rmSync as rmSync3 } from "fs";
2255
+ import { appendFileSync, closeSync, cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, openSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
2256
2256
  import { dirname, isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve7 } from "path";
2257
- import { spawn as spawn2 } from "child_process";
2257
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
2258
2258
  import { request as httpRequest } from "http";
2259
2259
  import { request as httpsRequest } from "https";
2260
+ import { homedir, userInfo } from "os";
2260
2261
  import { fileURLToPath as fileURLToPath2 } from "url";
2261
2262
  import chokidar from "chokidar";
2262
2263
 
@@ -4034,6 +4035,85 @@ var ServiceCommands = class {
4034
4035
  clearServiceState();
4035
4036
  console.log(`\u2713 ${APP_NAME2} stopped`);
4036
4037
  }
4038
+ async installSystemdService(options) {
4039
+ if (process.platform !== "linux") {
4040
+ console.error("Error: systemd installation is only supported on Linux.");
4041
+ return;
4042
+ }
4043
+ if (typeof process.getuid === "function" && process.getuid() !== 0) {
4044
+ console.error("Error: Run this command as root (for example: sudo nextclaw service install-systemd).");
4045
+ return;
4046
+ }
4047
+ const serviceName = this.resolveSystemdServiceName(options.name);
4048
+ const config2 = loadConfig6();
4049
+ const uiConfig = resolveUiConfig(config2, { enabled: true, host: "0.0.0.0" });
4050
+ const uiPort = this.parseSystemdUiPort(options.uiPort, uiConfig.port);
4051
+ if (uiPort === null) {
4052
+ console.error("Error: Invalid --ui-port. Provide a positive integer.");
4053
+ return;
4054
+ }
4055
+ const systemctlAvailable = this.runSystemCommand("systemctl", ["--version"]);
4056
+ if (!systemctlAvailable.ok) {
4057
+ console.error("Error: systemctl is not available on this machine.");
4058
+ return;
4059
+ }
4060
+ const runUser = this.resolveSystemdRunUser();
4061
+ const runHome = this.resolveSystemdRunHome(runUser);
4062
+ const servicePath = `/etc/systemd/system/${serviceName}.service`;
4063
+ const cliPath = fileURLToPath2(new URL("../index.js", import.meta.url));
4064
+ const execArgs = [process.execPath, ...process.execArgv, cliPath, "serve", "--ui-port", String(uiPort)];
4065
+ const unit = this.buildSystemdUnit({
4066
+ runUser,
4067
+ runHome,
4068
+ execArgs
4069
+ });
4070
+ writeFileSync4(servicePath, unit, "utf-8");
4071
+ const daemonReload = this.runSystemCommand("systemctl", ["daemon-reload"]);
4072
+ if (!daemonReload.ok) {
4073
+ console.error(`Error: Failed to reload systemd. ${daemonReload.stderr}`.trim());
4074
+ return;
4075
+ }
4076
+ const enableStart = this.runSystemCommand("systemctl", ["enable", "--now", `${serviceName}.service`]);
4077
+ if (!enableStart.ok) {
4078
+ console.error(`Error: Failed to enable/start ${serviceName}.service. ${enableStart.stderr}`.trim());
4079
+ return;
4080
+ }
4081
+ const active = this.runSystemCommand("systemctl", ["is-active", `${serviceName}.service`]);
4082
+ if (!active.ok || active.stdout.trim() !== "active") {
4083
+ console.error(`Error: ${serviceName}.service is not active. ${active.stderr || active.stdout}`.trim());
4084
+ return;
4085
+ }
4086
+ console.log(`\u2713 Installed systemd service: ${serviceName}.service`);
4087
+ console.log(`Run user: ${runUser}`);
4088
+ console.log(`UI port: ${uiPort}`);
4089
+ console.log(`Unit file: ${servicePath}`);
4090
+ console.log(`Health: http://127.0.0.1:${uiPort}/api/health`);
4091
+ console.log(`Logs: journalctl -u ${serviceName}.service -f`);
4092
+ }
4093
+ async uninstallSystemdService(options) {
4094
+ if (process.platform !== "linux") {
4095
+ console.error("Error: systemd removal is only supported on Linux.");
4096
+ return;
4097
+ }
4098
+ if (typeof process.getuid === "function" && process.getuid() !== 0) {
4099
+ console.error("Error: Run this command as root.");
4100
+ return;
4101
+ }
4102
+ const serviceName = this.resolveSystemdServiceName(options.name);
4103
+ const servicePath = `/etc/systemd/system/${serviceName}.service`;
4104
+ if (!existsSync8(servicePath)) {
4105
+ console.error(`Error: ${servicePath} does not exist.`);
4106
+ return;
4107
+ }
4108
+ this.runSystemCommand("systemctl", ["disable", "--now", `${serviceName}.service`]);
4109
+ rmSync3(servicePath, { force: true });
4110
+ const daemonReload = this.runSystemCommand("systemctl", ["daemon-reload"]);
4111
+ if (!daemonReload.ok) {
4112
+ console.error(`Warning: Removed unit file but failed to reload systemd. ${daemonReload.stderr}`.trim());
4113
+ return;
4114
+ }
4115
+ console.log(`\u2713 Removed systemd service: ${serviceName}.service`);
4116
+ }
4037
4117
  async waitForBackgroundServiceReady(params) {
4038
4118
  const startedAt = Date.now();
4039
4119
  let lastProbeError = null;
@@ -4064,6 +4144,78 @@ var ServiceCommands = class {
4064
4144
  const resolved = fromOverride ?? fromEnv ?? fallback;
4065
4145
  return Math.max(3e3, resolved);
4066
4146
  }
4147
+ resolveSystemdServiceName(rawName) {
4148
+ const trimmed = rawName?.trim() || APP_NAME2;
4149
+ return trimmed.endsWith(".service") ? trimmed.slice(0, -".service".length) : trimmed;
4150
+ }
4151
+ parseSystemdUiPort(rawPort, fallbackPort) {
4152
+ if (rawPort === void 0 || rawPort === null || rawPort === "") {
4153
+ return fallbackPort;
4154
+ }
4155
+ const parsed = Number(rawPort);
4156
+ if (!Number.isInteger(parsed) || parsed <= 0) {
4157
+ return null;
4158
+ }
4159
+ return parsed;
4160
+ }
4161
+ resolveSystemdRunUser() {
4162
+ const sudoUser = process.env.SUDO_USER?.trim();
4163
+ if (sudoUser && sudoUser !== "root") {
4164
+ return sudoUser;
4165
+ }
4166
+ return userInfo().username;
4167
+ }
4168
+ resolveSystemdRunHome(runUser) {
4169
+ const passwd = this.runSystemCommand("getent", ["passwd", runUser]);
4170
+ if (passwd.ok) {
4171
+ const fields = passwd.stdout.trim().split(":");
4172
+ if (fields.length >= 6 && fields[5]) {
4173
+ return fields[5];
4174
+ }
4175
+ }
4176
+ return process.env.HOME?.trim() || homedir();
4177
+ }
4178
+ buildSystemdUnit(params) {
4179
+ const execStart = params.execArgs.map((arg) => this.escapeSystemdArg(arg)).join(" ");
4180
+ return [
4181
+ "[Unit]",
4182
+ `Description=${APP_NAME2} gateway + UI`,
4183
+ "After=network-online.target",
4184
+ "Wants=network-online.target",
4185
+ "",
4186
+ "[Service]",
4187
+ "Type=simple",
4188
+ `User=${params.runUser}`,
4189
+ `WorkingDirectory=${params.runHome}`,
4190
+ `Environment=HOME=${params.runHome}`,
4191
+ "Environment=NODE_ENV=production",
4192
+ `ExecStart=${execStart}`,
4193
+ "Restart=always",
4194
+ "RestartSec=3",
4195
+ "TimeoutStopSec=20",
4196
+ "",
4197
+ "[Install]",
4198
+ "WantedBy=multi-user.target",
4199
+ ""
4200
+ ].join("\n");
4201
+ }
4202
+ escapeSystemdArg(value) {
4203
+ if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) {
4204
+ return value;
4205
+ }
4206
+ return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
4207
+ }
4208
+ runSystemCommand(command, args) {
4209
+ const result = spawnSync4(command, args, {
4210
+ encoding: "utf-8",
4211
+ stdio: ["ignore", "pipe", "pipe"]
4212
+ });
4213
+ return {
4214
+ ok: result.status === 0,
4215
+ stdout: result.stdout ?? "",
4216
+ stderr: result.stderr ?? ""
4217
+ };
4218
+ }
4067
4219
  appendStartupStage(logPath, message) {
4068
4220
  try {
4069
4221
  appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [startup] ${message}
@@ -4226,6 +4378,7 @@ var ServiceCommands = class {
4226
4378
  host: uiConfig.host,
4227
4379
  port: uiConfig.port,
4228
4380
  configPath: getConfigPath3(),
4381
+ productVersion: getPackageVersion(),
4229
4382
  staticDir: uiStaticDir ?? void 0,
4230
4383
  cronService,
4231
4384
  marketplace: {
@@ -4610,12 +4763,12 @@ ${stderr}`.trim();
4610
4763
  };
4611
4764
 
4612
4765
  // src/cli/workspace.ts
4613
- import { cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
4766
+ import { cpSync as cpSync2, existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync2, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
4614
4767
  import { createRequire } from "module";
4615
4768
  import { dirname as dirname2, join as join6, resolve as resolve8 } from "path";
4616
4769
  import { fileURLToPath as fileURLToPath3 } from "url";
4617
4770
  import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
4618
- import { spawnSync as spawnSync4 } from "child_process";
4771
+ import { spawnSync as spawnSync5 } from "child_process";
4619
4772
  var WorkspaceManager = class {
4620
4773
  constructor(logo) {
4621
4774
  this.logo = logo;
@@ -4654,7 +4807,7 @@ var WorkspaceManager = class {
4654
4807
  const raw = readFileSync7(templatePath, "utf-8");
4655
4808
  const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
4656
4809
  mkdirSync5(dirname2(filePath), { recursive: true });
4657
- writeFileSync4(filePath, content);
4810
+ writeFileSync5(filePath, content);
4658
4811
  created.push(entry.target);
4659
4812
  }
4660
4813
  const memoryDir = join6(workspace, "memory");
@@ -4762,7 +4915,7 @@ var WorkspaceManager = class {
4762
4915
  recursive: true,
4763
4916
  filter: (src) => !src.includes("node_modules") && !src.includes("dist")
4764
4917
  });
4765
- const install = spawnSync4("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
4918
+ const install = spawnSync5("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
4766
4919
  if (install.status !== 0) {
4767
4920
  console.error(`Bridge install failed: ${install.status ?? 1}`);
4768
4921
  if (install.stderr) {
@@ -4770,7 +4923,7 @@ var WorkspaceManager = class {
4770
4923
  }
4771
4924
  process.exit(1);
4772
4925
  }
4773
- const build = spawnSync4("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
4926
+ const build = spawnSync5("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
4774
4927
  if (build.status !== 0) {
4775
4928
  console.error(`Bridge build failed: ${build.status ?? 1}`);
4776
4929
  if (build.stderr) {
@@ -5140,6 +5293,12 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
5140
5293
  async stop() {
5141
5294
  await this.serviceCommands.stopService();
5142
5295
  }
5296
+ async serviceInstallSystemd(opts) {
5297
+ await this.serviceCommands.installSystemdService(opts);
5298
+ }
5299
+ async serviceUninstallSystemd(opts) {
5300
+ await this.serviceCommands.uninstallSystemdService(opts);
5301
+ }
5143
5302
  async agent(opts) {
5144
5303
  const configPath = getConfigPath4();
5145
5304
  const config2 = resolveConfigSecrets3(loadConfig7(), { configPath });
@@ -5222,7 +5381,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
5222
5381
  const merged = history.concat(
5223
5382
  rl.history ?? []
5224
5383
  );
5225
- writeFileSync5(historyFile, merged.join("\n"));
5384
+ writeFileSync6(historyFile, merged.join("\n"));
5226
5385
  process.exit(0);
5227
5386
  });
5228
5387
  let running = true;
@@ -5402,6 +5561,9 @@ program.command("start").description(`Start the ${APP_NAME5} gateway + UI in the
5402
5561
  program.command("restart").description(`Restart the ${APP_NAME5} background service`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after restart", false).action(async (opts) => runtime.restart(opts));
5403
5562
  program.command("serve").description(`Run the ${APP_NAME5} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
5404
5563
  program.command("stop").description(`Stop the ${APP_NAME5} background service`).action(async () => runtime.stop());
5564
+ var service = program.command("service").description("Manage OS service integration");
5565
+ service.command("install-systemd").description(`Install a systemd unit for ${APP_NAME5} on Linux servers`).option("--name <name>", "Systemd unit name", APP_NAME5).option("--ui-port <port>", "UI port").action(async (opts) => runtime.serviceInstallSystemd(opts));
5566
+ service.command("uninstall-systemd").description(`Remove the systemd unit for ${APP_NAME5}`).option("--name <name>", "Systemd unit name", APP_NAME5).action(async (opts) => runtime.serviceUninstallSystemd(opts));
5405
5567
  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("--model <model>", "Session model override for this run").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
5406
5568
  program.command("update").description(`Update ${APP_NAME5}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
5407
5569
  var registerClawHubInstall = (cmd) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.9.9",
3
+ "version": "0.9.10",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -40,7 +40,7 @@
40
40
  "commander": "^12.1.0",
41
41
  "@nextclaw/core": "^0.7.1",
42
42
  "@nextclaw/runtime": "^0.1.1",
43
- "@nextclaw/server": "^0.6.3",
43
+ "@nextclaw/server": "^0.6.4",
44
44
  "@nextclaw/openclaw-compat": "^0.2.0"
45
45
  },
46
46
  "devDependencies": {
@@ -418,6 +418,8 @@ Created under the workspace:
418
418
  | `nextclaw start` | Start gateway + UI in the background |
419
419
  | `nextclaw restart` | Restart the background service with optional start flags |
420
420
  | `nextclaw stop` | Stop the background service |
421
+ | `sudo nextclaw service install-systemd` | Install a managed Linux `systemd` service for public/server deployment |
422
+ | `sudo nextclaw service uninstall-systemd` | Remove the managed Linux `systemd` service |
421
423
  | `nextclaw ui` | Start UI and gateway in the foreground |
422
424
  | `nextclaw gateway` | Start gateway only (for channels) |
423
425
  | `nextclaw serve` | Run gateway + UI in the foreground (no background) |
@@ -458,6 +460,13 @@ Gateway options (when running `nextclaw gateway` or `nextclaw start`):
458
460
 
459
461
  If service is already running, new UI port flags do not hot-apply; use `nextclaw restart ...` to apply them.
460
462
 
463
+ Linux server deployment tip:
464
+
465
+ - If you put Nginx/Caddy/Traefik in front of NextClaw, do not rely on a one-time `nextclaw start` only.
466
+ - `nextclaw start` is a background convenience command, not a crash/reboot supervisor.
467
+ - On Linux servers, install a managed unit with `sudo nextclaw service install-systemd` so the UI/API comes back automatically after reboot or unexpected exit.
468
+ - Verify with `systemctl status nextclaw`, `journalctl -u nextclaw -f`, and `curl http://127.0.0.1:18791/api/health`.
469
+
461
470
  Status/diagnostics tips:
462
471
 
463
472
  - `nextclaw status` shows runtime truth (process + health + config summary).
@@ -0,0 +1 @@
1
+ import{r as v,j as a,ax as Z,z as ee,d as T,K as ae,a9 as te,aL as se,aM as ne,aN as le,w as re,a1 as oe,aw as ce,q as ie}from"./vendor-D33xZtEC.js";import{t as e,c as I,H as me,u as q,a as $,b as H,J as pe,K as de,S as be,e as ue,f as xe,g as ye,h as ge}from"./index-CGo5Vnh0.js";import{B as E,P as he,a as fe}from"./page-layout-BOgLC2tK.js";import{I as D}from"./input-CzTldMKo.js";import{L as we}from"./label-De__vsU7.js";import{S as ve}from"./switch-pMrS4heA.js";import{C as je,a as ke,L as K,S as J,c as Se,b as Ce}from"./LogoBadge-C_ygxoGB.js";import{h as _}from"./config-hints-CApS3K_7.js";import{T as Ne}from"./tabs-custom-DhOxWfCb.js";function Pe({value:t,onChange:m,className:i,placeholder:r=""}){const[o,u]=v.useState(""),d=x=>{x.key==="Enter"&&o.trim()?(x.preventDefault(),m([...t,o.trim()]),u("")):x.key==="Backspace"&&!o&&t.length>0&&m(t.slice(0,-1))},g=x=>{m(t.filter((j,h)=>h!==x))};return a.jsxs("div",{className:I("flex flex-wrap gap-2 p-2 border rounded-md min-h-[42px]",i),children:[t.map((x,j)=>a.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-primary text-primary-foreground rounded text-sm",children:[x,a.jsx("button",{type:"button",onClick:()=>g(j),className:"hover:text-red-300 transition-colors",children:a.jsx(Z,{className:"h-3 w-3"})})]},j)),a.jsx("input",{type:"text",value:o,onChange:x=>u(x.target.value),onKeyDown:d,className:"flex-1 outline-none min-w-[100px] bg-transparent text-sm",placeholder:r||e("enterTag")})]})}function z(t){var r,o;const m=me();return((r=t.tutorialUrls)==null?void 0:r[m])||((o=t.tutorialUrls)==null?void 0:o.default)||t.tutorialUrl}const Ie={telegram:"telegram.svg",slack:"slack.svg",discord:"discord.svg",whatsapp:"whatsapp.svg",qq:"qq.svg",feishu:"feishu.svg",dingtalk:"dingtalk.svg",wecom:"wecom.svg",mochat:"mochat.svg",email:"email.svg"};function Fe(t,m){const i=m.toLowerCase(),r=t[i];return r?`/logos/${r}`:null}function Y(t){return Fe(Ie,t)}const B=[{value:"pairing",label:"pairing"},{value:"allowlist",label:"allowlist"},{value:"open",label:"open"},{value:"disabled",label:"disabled"}],R=[{value:"open",label:"open"},{value:"allowlist",label:"allowlist"},{value:"disabled",label:"disabled"}],Te=[{value:"off",label:"off"},{value:"partial",label:"partial"},{value:"block",label:"block"},{value:"progress",label:"progress"}],De=t=>t.includes("token")||t.includes("secret")||t.includes("password")?a.jsx(ae,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("url")||t.includes("host")?a.jsx(te,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("email")||t.includes("mail")?a.jsx(se,{className:"h-3.5 w-3.5 text-gray-500"}):t.includes("id")||t.includes("from")?a.jsx(ne,{className:"h-3.5 w-3.5 text-gray-500"}):t==="enabled"||t==="consentGranted"?a.jsx(le,{className:"h-3.5 w-3.5 text-gray-500"}):a.jsx(re,{className:"h-3.5 w-3.5 text-gray-500"});function G(){return{telegram:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"token",type:"password",label:e("botToken")},{name:"allowFrom",type:"tags",label:e("allowFrom")},{name:"proxy",type:"text",label:e("proxy")},{name:"accountId",type:"text",label:e("accountId")},{name:"dmPolicy",type:"select",label:e("dmPolicy"),options:B},{name:"groupPolicy",type:"select",label:e("groupPolicy"),options:R},{name:"groupAllowFrom",type:"tags",label:e("groupAllowFrom")},{name:"requireMention",type:"boolean",label:e("requireMention")},{name:"mentionPatterns",type:"tags",label:e("mentionPatterns")},{name:"groups",type:"json",label:e("groupRulesJson")}],discord:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"token",type:"password",label:e("botToken")},{name:"allowBots",type:"boolean",label:e("allowBotMessages")},{name:"allowFrom",type:"tags",label:e("allowFrom")},{name:"gatewayUrl",type:"text",label:e("gatewayUrl")},{name:"intents",type:"number",label:e("intents")},{name:"proxy",type:"text",label:e("proxy")},{name:"mediaMaxMb",type:"number",label:e("attachmentMaxSizeMb")},{name:"streaming",type:"select",label:e("streamingMode"),options:Te},{name:"draftChunk",type:"json",label:e("draftChunkingJson")},{name:"textChunkLimit",type:"number",label:e("textChunkLimit")},{name:"accountId",type:"text",label:e("accountId")},{name:"dmPolicy",type:"select",label:e("dmPolicy"),options:B},{name:"groupPolicy",type:"select",label:e("groupPolicy"),options:R},{name:"groupAllowFrom",type:"tags",label:e("groupAllowFrom")},{name:"requireMention",type:"boolean",label:e("requireMention")},{name:"mentionPatterns",type:"tags",label:e("mentionPatterns")},{name:"groups",type:"json",label:e("groupRulesJson")}],whatsapp:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"bridgeUrl",type:"text",label:e("bridgeUrl")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],feishu:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"appId",type:"text",label:e("appId")},{name:"appSecret",type:"password",label:e("appSecret")},{name:"encryptKey",type:"password",label:e("encryptKey")},{name:"verificationToken",type:"password",label:e("verificationToken")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],dingtalk:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"clientId",type:"text",label:e("clientId")},{name:"clientSecret",type:"password",label:e("clientSecret")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],wecom:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"corpId",type:"text",label:e("corpId")},{name:"agentId",type:"text",label:e("agentId")},{name:"secret",type:"password",label:e("secret")},{name:"token",type:"password",label:e("token")},{name:"callbackPort",type:"number",label:e("callbackPort")},{name:"callbackPath",type:"text",label:e("callbackPath")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],slack:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"mode",type:"text",label:e("mode")},{name:"webhookPath",type:"text",label:e("webhookPath")},{name:"allowBots",type:"boolean",label:e("allowBotMessages")},{name:"botToken",type:"password",label:e("botToken")},{name:"appToken",type:"password",label:e("appToken")}],email:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"consentGranted",type:"boolean",label:e("consentGranted")},{name:"imapHost",type:"text",label:e("imapHost")},{name:"imapPort",type:"number",label:e("imapPort")},{name:"imapUsername",type:"text",label:e("imapUsername")},{name:"imapPassword",type:"password",label:e("imapPassword")},{name:"fromAddress",type:"email",label:e("fromAddress")}],mochat:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"baseUrl",type:"text",label:e("baseUrl")},{name:"clawToken",type:"password",label:e("clawToken")},{name:"agentUserId",type:"text",label:e("agentUserId")},{name:"allowFrom",type:"tags",label:e("allowFrom")}],qq:[{name:"enabled",type:"boolean",label:e("enabled")},{name:"appId",type:"text",label:e("appId")},{name:"secret",type:"password",label:e("appSecret")},{name:"markdownSupport",type:"boolean",label:e("markdownSupport")},{name:"allowFrom",type:"tags",label:e("allowFrom")}]}}function A(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function V(t,m){const i={...t};for(const[r,o]of Object.entries(m)){const u=i[r];if(A(u)&&A(o)){i[r]=V(u,o);continue}i[r]=o}return i}function Ae(t,m){const i=t.split("."),r={};let o=r;for(let u=0;u<i.length-1;u+=1){const d=i[u];o[d]={},o=o[d]}return o[i[i.length-1]]=m,r}function Le({channelName:t}){var O,U;const{data:m}=q(),{data:i}=$(),{data:r}=H(),o=pe(),u=de(),[d,g]=v.useState({}),[x,j]=v.useState({}),[h,f]=v.useState(null),k=t?m==null?void 0:m.channels[t]:null,w=t?G()[t]??[]:[],c=r==null?void 0:r.uiHints,p=t?`channels.${t}`:null,S=((O=r==null?void 0:r.actions)==null?void 0:O.filter(s=>s.scope===p))??[],C=t&&(((U=_(`channels.${t}`,c))==null?void 0:U.label)??t),P=i==null?void 0:i.channels.find(s=>s.name===t),F=P?z(P):void 0;v.useEffect(()=>{if(k){g({...k});const s={};(t?G()[t]??[]:[]).filter(l=>l.type==="json").forEach(l=>{const y=k[l.name];s[l.name]=JSON.stringify(y??{},null,2)}),j(s)}else g({}),j({})},[k,t]);const N=(s,n)=>{g(l=>({...l,[s]:n}))},L=s=>{if(s.preventDefault(),!t)return;const n={...d};for(const l of w){if(l.type!=="password")continue;const y=n[l.name];(typeof y!="string"||y.length===0)&&delete n[l.name]}for(const l of w){if(l.type!=="json")continue;const y=x[l.name]??"";try{n[l.name]=y.trim()?JSON.parse(y):{}}catch{T.error(`${e("invalidJson")}: ${l.name}`);return}}o.mutate({channel:t,data:n})},Q=s=>{if(!s||!t)return;const n=s.channels;if(!A(n))return;const l=n[t];A(l)&&g(y=>V(y,l))},W=async s=>{if(!(!t||!p)){f(s.id);try{let n={...d};s.saveBeforeRun&&(n={...n,...s.savePatch??{}},g(n),await o.mutateAsync({channel:t,data:n}));const l=await u.mutateAsync({actionId:s.id,data:{scope:p,draftConfig:Ae(p,n)}});Q(l.patch),l.ok?T.success(l.message||e("success")):T.error(l.message||e("error"))}catch(n){const l=n instanceof Error?n.message:String(n);T.error(`${e("error")}: ${l}`)}finally{f(null)}}};if(!t||!P||!k)return a.jsx("div",{className:je,children:a.jsxs("div",{children:[a.jsx("h3",{className:"text-base font-semibold text-gray-900",children:e("channelsSelectTitle")}),a.jsx("p",{className:"mt-2 text-sm text-gray-500",children:e("channelsSelectDescription")})]})});const M=!!k.enabled;return a.jsxs("div",{className:ke,children:[a.jsx("div",{className:"border-b border-gray-100 px-6 py-5",children:a.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[a.jsxs("div",{className:"min-w-0",children:[a.jsxs("div",{className:"flex items-center gap-3",children:[a.jsx(K,{name:t,src:Y(t),className:I("h-9 w-9 rounded-lg border",M?"border-primary/30 bg-white":"border-gray-200/70 bg-white"),imgClassName:"h-5 w-5 object-contain",fallback:a.jsx("span",{className:"text-sm font-semibold uppercase text-gray-500",children:t[0]})}),a.jsx("h3",{className:"truncate text-lg font-semibold text-gray-900 capitalize",children:C})]}),a.jsx("p",{className:"mt-2 text-sm text-gray-500",children:e("channelsFormDescription")}),F&&a.jsxs("a",{href:F,className:"mt-2 inline-flex items-center gap-1.5 text-xs text-primary transition-colors hover:text-primary-hover",children:[a.jsx(ee,{className:"h-3.5 w-3.5"}),e("channelsGuideTitle")]})]}),a.jsx(J,{status:M?"active":"inactive",label:M?e("statusActive"):e("statusInactive")})]})}),a.jsxs("form",{onSubmit:L,className:"flex min-h-0 flex-1 flex-col",children:[a.jsx("div",{className:"min-h-0 flex-1 space-y-6 overflow-y-auto overscroll-contain px-6 py-5",children:w.map(s=>{const n=t?_(`channels.${t}.${s.name}`,c):void 0,l=(n==null?void 0:n.label)??s.label,y=n==null?void 0:n.placeholder;return a.jsxs("div",{className:"space-y-2.5",children:[a.jsxs(we,{htmlFor:s.name,className:"flex items-center gap-2 text-sm font-medium text-gray-900",children:[De(s.name),l]}),s.type==="boolean"&&a.jsxs("div",{className:"flex items-center justify-between rounded-xl bg-gray-50 p-3",children:[a.jsx("span",{className:"text-sm text-gray-500",children:d[s.name]?e("enabled"):e("disabled")}),a.jsx(ve,{id:s.name,checked:d[s.name]||!1,onCheckedChange:b=>N(s.name,b),className:"data-[state=checked]:bg-emerald-500"})]}),(s.type==="text"||s.type==="email")&&a.jsx(D,{id:s.name,type:s.type,value:d[s.name]||"",onChange:b=>N(s.name,b.target.value),placeholder:y,className:"rounded-xl"}),s.type==="password"&&a.jsx(D,{id:s.name,type:"password",value:d[s.name]||"",onChange:b=>N(s.name,b.target.value),placeholder:y??e("leaveBlankToKeepUnchanged"),className:"rounded-xl"}),s.type==="number"&&a.jsx(D,{id:s.name,type:"number",value:d[s.name]||0,onChange:b=>N(s.name,parseInt(b.target.value,10)||0),placeholder:y,className:"rounded-xl"}),s.type==="tags"&&a.jsx(Pe,{value:d[s.name]||[],onChange:b=>N(s.name,b)}),s.type==="select"&&a.jsxs(be,{value:d[s.name]||"",onValueChange:b=>N(s.name,b),children:[a.jsx(ue,{className:"rounded-xl",children:a.jsx(xe,{})}),a.jsx(ye,{children:(s.options??[]).map(b=>a.jsx(ge,{value:b.value,children:b.label},b.value))})]}),s.type==="json"&&a.jsx("textarea",{id:s.name,value:x[s.name]??"{}",onChange:b=>j(X=>({...X,[s.name]:b.target.value})),className:"min-h-[120px] w-full resize-none rounded-lg border border-gray-200 bg-white px-3 py-2 text-xs font-mono"})]},s.name)})}),a.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-3 border-t border-gray-100 px-6 py-4",children:[a.jsx("div",{className:"flex flex-wrap items-center gap-2",children:S.filter(s=>s.trigger==="manual").map(s=>a.jsx(E,{type:"button",onClick:()=>W(s),disabled:o.isPending||!!h,variant:"secondary",children:h===s.id?e("connecting"):s.title},s.id))}),a.jsx(E,{type:"submit",disabled:o.isPending||!!h,children:o.isPending?e("saving"):e("save")})]})]})]})}const Me={telegram:"channelDescTelegram",slack:"channelDescSlack",email:"channelDescEmail",webhook:"channelDescWebhook",discord:"channelDescDiscord",feishu:"channelDescFeishu"};function He(){const{data:t}=q(),{data:m}=$(),{data:i}=H(),[r,o]=v.useState("enabled"),[u,d]=v.useState(),[g,x]=v.useState(""),j=i==null?void 0:i.uiHints,h=m==null?void 0:m.channels,f=t==null?void 0:t.channels,k=[{id:"enabled",label:e("channelsTabEnabled"),count:(h??[]).filter(c=>{var p;return(p=f==null?void 0:f[c.name])==null?void 0:p.enabled}).length},{id:"all",label:e("channelsTabAll"),count:(h??[]).length}],w=v.useMemo(()=>{const c=g.trim().toLowerCase();return(h??[]).filter(p=>{var C;const S=((C=f==null?void 0:f[p.name])==null?void 0:C.enabled)||!1;return r==="enabled"?S:!0}).filter(p=>c?(p.displayName||p.name).toLowerCase().includes(c)||p.name.toLowerCase().includes(c):!0)},[r,f,h,g]);return v.useEffect(()=>{if(w.length===0){d(void 0);return}w.some(p=>p.name===u)||d(w[0].name)},[w,u]),!t||!m?a.jsx("div",{className:"p-8 text-gray-400",children:e("channelsLoading")}):a.jsxs(he,{className:"xl:flex xl:h-full xl:min-h-0 xl:flex-col xl:pb-0",children:[a.jsx(fe,{title:e("channelsPageTitle"),description:e("channelsPageDescription")}),a.jsxs("div",{className:I(Ce,"xl:min-h-0 xl:flex-1"),children:[a.jsxs("section",{className:Se,children:[a.jsx("div",{className:"border-b border-gray-100 px-4 pt-4",children:a.jsx(Ne,{tabs:k,activeTab:r,onChange:o,className:"mb-0"})}),a.jsx("div",{className:"border-b border-gray-100 px-4 py-3",children:a.jsxs("div",{className:"relative",children:[a.jsx(oe,{className:"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"}),a.jsx(D,{value:g,onChange:c=>x(c.target.value),placeholder:e("channelsFilterPlaceholder"),className:"h-10 rounded-xl pl-9"})]})}),a.jsxs("div",{className:"min-h-0 flex-1 space-y-2 overflow-y-auto overscroll-contain p-3",children:[w.map(c=>{const p=t.channels[c.name],S=(p==null?void 0:p.enabled)||!1,C=_(`channels.${c.name}`,j),P=z(c),F=(C==null?void 0:C.help)||e(Me[c.name]||"channelDescriptionDefault"),N=u===c.name;return a.jsx("button",{type:"button",onClick:()=>d(c.name),className:I("w-full rounded-xl border p-2.5 text-left transition-all",N?"border-primary/30 bg-primary-50/40 shadow-sm":"border-gray-200/70 bg-white hover:border-gray-300 hover:bg-gray-50/70"),children:a.jsxs("div",{className:"flex items-start justify-between gap-3",children:[a.jsxs("div",{className:"flex min-w-0 items-center gap-3",children:[a.jsx(K,{name:c.name,src:Y(c.name),className:I("h-10 w-10 rounded-lg border",S?"border-primary/30 bg-white":"border-gray-200/70 bg-white"),imgClassName:"h-5 w-5 object-contain",fallback:a.jsx("span",{className:"text-sm font-semibold uppercase text-gray-500",children:c.name[0]})}),a.jsxs("div",{className:"min-w-0",children:[a.jsx("p",{className:"truncate text-sm font-semibold text-gray-900",children:c.displayName||c.name}),a.jsx("p",{className:"line-clamp-1 text-[11px] text-gray-500",children:F})]})]}),a.jsxs("div",{className:"flex items-center gap-2",children:[P&&a.jsx("a",{href:P,onClick:L=>L.stopPropagation(),className:"inline-flex h-7 w-7 items-center justify-center rounded-md text-gray-300 transition-colors hover:bg-gray-100/70 hover:text-gray-500",title:e("channelsGuideTitle"),children:a.jsx(ce,{className:"h-3.5 w-3.5"})}),a.jsx(J,{status:S?"active":"inactive",label:S?e("statusActive"):e("statusInactive"),className:"min-w-[56px] justify-center"})]})]})},c.name)}),w.length===0&&a.jsxs("div",{className:"flex h-full min-h-[220px] flex-col items-center justify-center rounded-xl border border-dashed border-gray-200 bg-gray-50/70 py-10 text-center",children:[a.jsx("div",{className:"mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-white",children:a.jsx(ie,{className:"h-5 w-5 text-gray-300"})}),a.jsx("p",{className:"text-sm font-medium text-gray-700",children:e("channelsNoMatch")})]})]})]}),a.jsx(Le,{channelName:u})]})]})}export{He as ChannelsList};