nextclaw 0.9.8 → 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 (37) hide show
  1. package/dist/cli/index.js +195 -30
  2. package/package.json +4 -3
  3. package/templates/USAGE.md +9 -0
  4. package/ui-dist/assets/ChannelsList-Dz8AGmaQ.js +1 -0
  5. package/ui-dist/assets/ChatPage-BXDyt7BL.js +34 -0
  6. package/ui-dist/assets/DocBrowser-CkKvzF7m.js +1 -0
  7. package/ui-dist/assets/LogoBadge-C_ygxoGB.js +1 -0
  8. package/ui-dist/assets/{MarketplacePage--wFfsNH0.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-B25P3J7V.js → SecretsConfig-CaJLf7oJ.js} +2 -2
  13. package/ui-dist/assets/SessionsConfig-3QF7K9wm.js +2 -0
  14. package/ui-dist/assets/{card-CCSDsedj.js → card-DXo3NsaB.js} +1 -1
  15. package/ui-dist/assets/index-CGo5Vnh0.js +7 -0
  16. package/ui-dist/assets/index-DcxYzrFm.css +1 -0
  17. package/ui-dist/assets/input-CzTldMKo.js +1 -0
  18. package/ui-dist/assets/{label-BxzAKPzU.js → label-De__vsU7.js} +1 -1
  19. package/ui-dist/assets/{page-layout-DaLNSFKw.js → page-layout-BOgLC2tK.js} +1 -1
  20. package/ui-dist/assets/session-run-status-DQVCDxTb.js +5 -0
  21. package/ui-dist/assets/{switch-DHOCEi5L.js → switch-pMrS4heA.js} +1 -1
  22. package/ui-dist/assets/{tabs-custom-zdFy3fnK.js → tabs-custom-DhOxWfCb.js} +1 -1
  23. package/ui-dist/assets/{useConfirmDialog-D3ZVa92J.js → useConfirmDialog-CseKBGh5.js} +2 -2
  24. package/ui-dist/assets/{vendor-Dj2ULvht.js → vendor-D33xZtEC.js} +6 -6
  25. package/ui-dist/index.html +3 -3
  26. package/ui-dist/assets/ChannelsList-VqzbAMCc.js +0 -1
  27. package/ui-dist/assets/ChatPage-CjZqsBmn.js +0 -34
  28. package/ui-dist/assets/DocBrowser-DvU-iUeB.js +0 -1
  29. package/ui-dist/assets/ModelConfig-cY5UsbfA.js +0 -1
  30. package/ui-dist/assets/ProvidersList-qZwaFoFt.js +0 -1
  31. package/ui-dist/assets/RuntimeConfig-BY2Axlte.js +0 -1
  32. package/ui-dist/assets/SessionsConfig-CxA9gIBw.js +0 -2
  33. package/ui-dist/assets/chat-message-pw9oafI4.js +0 -5
  34. package/ui-dist/assets/index-CD8a2KMH.js +0 -2
  35. package/ui-dist/assets/index-DKOXGZc8.css +0 -1
  36. package/ui-dist/assets/logos-C3oHQ9kv.js +0 -1
  37. package/ui-dist/assets/useConfig-CDl9UK5m.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";
@@ -567,12 +567,13 @@ import {
567
567
  loadConfig,
568
568
  saveConfig,
569
569
  getWorkspacePath,
570
- PROVIDERS,
571
570
  expandHome
572
571
  } from "@nextclaw/core";
572
+ import { builtinProviderIds } from "@nextclaw/runtime";
573
573
  import { createInterface } from "readline";
574
574
  import { existsSync as existsSync4 } from "fs";
575
575
  import { resolve as resolve5 } from "path";
576
+ var RESERVED_PROVIDER_IDS = builtinProviderIds();
576
577
  function loadPluginRegistry(config2, workspaceDir) {
577
578
  return loadOpenClawPlugins({
578
579
  config: config2,
@@ -597,7 +598,7 @@ function loadPluginRegistry(config2, workspaceDir) {
597
598
  "cron"
598
599
  ],
599
600
  reservedChannelIds: [],
600
- reservedProviderIds: PROVIDERS.map((provider) => provider.name),
601
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
601
602
  reservedEngineKinds: ["native"],
602
603
  logger: {
603
604
  info: (message) => console.log(message),
@@ -692,7 +693,7 @@ var PluginCommands = class {
692
693
  config: config2,
693
694
  workspaceDir,
694
695
  reservedChannelIds: [],
695
- reservedProviderIds: PROVIDERS.map((provider) => provider.name),
696
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
696
697
  reservedEngineKinds: ["native"]
697
698
  });
698
699
  const list = opts.enabled ? report.plugins.filter((plugin) => plugin.status === "loaded") : report.plugins;
@@ -753,7 +754,7 @@ var PluginCommands = class {
753
754
  config: config2,
754
755
  workspaceDir,
755
756
  reservedChannelIds: [],
756
- reservedProviderIds: PROVIDERS.map((provider) => provider.name),
757
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
757
758
  reservedEngineKinds: ["native"]
758
759
  });
759
760
  const plugin = report.plugins.find((entry) => entry.id === id || entry.name === id);
@@ -838,7 +839,7 @@ var PluginCommands = class {
838
839
  config: config2,
839
840
  workspaceDir,
840
841
  reservedChannelIds: [],
841
- reservedProviderIds: PROVIDERS.map((provider) => provider.name),
842
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
842
843
  reservedEngineKinds: ["native"]
843
844
  });
844
845
  const keepFiles = Boolean(opts.keepFiles || opts.keepConfig);
@@ -1021,7 +1022,7 @@ var PluginCommands = class {
1021
1022
  config: config2,
1022
1023
  workspaceDir,
1023
1024
  reservedChannelIds: [],
1024
- reservedProviderIds: PROVIDERS.map((provider) => provider.name),
1025
+ reservedProviderIds: RESERVED_PROVIDER_IDS,
1025
1026
  reservedEngineKinds: ["native"]
1026
1027
  });
1027
1028
  const pluginErrors = report.plugins.filter((plugin) => plugin.status === "error");
@@ -1712,7 +1713,8 @@ var SecretsCommands = class {
1712
1713
 
1713
1714
  // src/cli/commands/channels.ts
1714
1715
  import { spawnSync as spawnSync3 } from "child_process";
1715
- import { BUILTIN_CHANNEL_PLUGIN_IDS, getWorkspacePath as getWorkspacePath2, loadConfig as loadConfig4, saveConfig as saveConfig4, PROVIDERS as PROVIDERS2 } from "@nextclaw/core";
1716
+ import { getWorkspacePath as getWorkspacePath2, loadConfig as loadConfig4, saveConfig as saveConfig4 } from "@nextclaw/core";
1717
+ import { BUILTIN_CHANNEL_PLUGIN_IDS, builtinProviderIds as builtinProviderIds2 } from "@nextclaw/runtime";
1716
1718
  import { buildPluginStatusReport as buildPluginStatusReport2, enablePluginInConfig as enablePluginInConfig2, getPluginChannelBindings } from "@nextclaw/openclaw-compat";
1717
1719
  var CHANNEL_LABELS = {
1718
1720
  telegram: "Telegram",
@@ -1726,6 +1728,7 @@ var CHANNEL_LABELS = {
1726
1728
  slack: "Slack",
1727
1729
  qq: "QQ"
1728
1730
  };
1731
+ var RESERVED_PROVIDER_IDS2 = builtinProviderIds2();
1729
1732
  var ChannelCommands = class {
1730
1733
  constructor(deps) {
1731
1734
  this.deps = deps;
@@ -1744,7 +1747,7 @@ var ChannelCommands = class {
1744
1747
  config: config2,
1745
1748
  workspaceDir,
1746
1749
  reservedChannelIds: [],
1747
- reservedProviderIds: PROVIDERS2.map((provider) => provider.name)
1750
+ reservedProviderIds: RESERVED_PROVIDER_IDS2
1748
1751
  });
1749
1752
  const pluginChannels = report.plugins.filter((plugin) => plugin.status === "loaded" && plugin.channelIds.length > 0);
1750
1753
  if (pluginChannels.length > 0) {
@@ -1828,8 +1831,8 @@ import { join as join3 } from "path";
1828
1831
  var CronCommands = class {
1829
1832
  cronList(opts) {
1830
1833
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1831
- const service = new CronService(storePath);
1832
- const jobs = service.listJobs(Boolean(opts.all));
1834
+ const service2 = new CronService(storePath);
1835
+ const jobs = service2.listJobs(Boolean(opts.all));
1833
1836
  if (!jobs.length) {
1834
1837
  console.log("No scheduled jobs.");
1835
1838
  return;
@@ -1848,7 +1851,7 @@ var CronCommands = class {
1848
1851
  }
1849
1852
  cronAdd(opts) {
1850
1853
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1851
- const service = new CronService(storePath);
1854
+ const service2 = new CronService(storePath);
1852
1855
  let schedule = null;
1853
1856
  if (opts.every) {
1854
1857
  schedule = { kind: "every", everyMs: Number(opts.every) * 1e3 };
@@ -1861,7 +1864,7 @@ var CronCommands = class {
1861
1864
  console.error("Error: Must specify --every, --cron, or --at");
1862
1865
  return;
1863
1866
  }
1864
- const job = service.addJob({
1867
+ const job = service2.addJob({
1865
1868
  name: opts.name,
1866
1869
  schedule,
1867
1870
  message: opts.message,
@@ -1873,8 +1876,8 @@ var CronCommands = class {
1873
1876
  }
1874
1877
  cronRemove(jobId) {
1875
1878
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1876
- const service = new CronService(storePath);
1877
- if (service.removeJob(jobId)) {
1879
+ const service2 = new CronService(storePath);
1880
+ if (service2.removeJob(jobId)) {
1878
1881
  console.log(`\u2713 Removed job ${jobId}`);
1879
1882
  } else {
1880
1883
  console.log(`Job ${jobId} not found`);
@@ -1882,8 +1885,8 @@ var CronCommands = class {
1882
1885
  }
1883
1886
  cronEnable(jobId, opts) {
1884
1887
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1885
- const service = new CronService(storePath);
1886
- const job = service.enableJob(jobId, !opts.disable);
1888
+ const service2 = new CronService(storePath);
1889
+ const job = service2.enableJob(jobId, !opts.disable);
1887
1890
  if (job) {
1888
1891
  console.log(`\u2713 Job '${job.name}' ${opts.disable ? "disabled" : "enabled"}`);
1889
1892
  } else {
@@ -1892,8 +1895,8 @@ var CronCommands = class {
1892
1895
  }
1893
1896
  async cronRun(jobId, opts) {
1894
1897
  const storePath = join3(getDataDir3(), "cron", "jobs.json");
1895
- const service = new CronService(storePath);
1896
- 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));
1897
1900
  console.log(ok ? "\u2713 Job executed" : `Failed to run job ${jobId}`);
1898
1901
  }
1899
1902
  };
@@ -1908,9 +1911,9 @@ import {
1908
1911
  getDataDir as getDataDir4,
1909
1912
  getWorkspacePath as getWorkspacePath3,
1910
1913
  hasSecretRef,
1911
- loadConfig as loadConfig5,
1912
- PROVIDERS as PROVIDERS3
1914
+ loadConfig as loadConfig5
1913
1915
  } from "@nextclaw/core";
1916
+ import { listBuiltinProviders } from "@nextclaw/runtime";
1914
1917
  var DiagnosticsCommands = class {
1915
1918
  constructor(deps) {
1916
1919
  this.deps = deps;
@@ -2091,7 +2094,7 @@ var DiagnosticsCommands = class {
2091
2094
  const managedHealth = running && managedApiUrl ? await this.probeApiHealth(`${managedApiUrl}/health`) : { state: "unreachable", detail: "service not running" };
2092
2095
  const configuredHealth = await this.probeApiHealth(`${configuredApiUrl}/health`, 900);
2093
2096
  const orphanSuspected = !running && configuredHealth.state === "ok";
2094
- const providers = PROVIDERS3.map((spec) => {
2097
+ const providers = listBuiltinProviders().map((spec) => {
2095
2098
  const provider = config2.providers[spec.name];
2096
2099
  const apiKeyRefSet = hasSecretRef(config2, `providers.${spec.name}.apiKey`);
2097
2100
  if (!provider) {
@@ -2249,11 +2252,12 @@ import {
2249
2252
  stopPluginChannelGateways
2250
2253
  } from "@nextclaw/openclaw-compat";
2251
2254
  import { startUiServer } from "@nextclaw/server";
2252
- 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";
2253
2256
  import { dirname, isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve7 } from "path";
2254
- import { spawn as spawn2 } from "child_process";
2257
+ import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
2255
2258
  import { request as httpRequest } from "http";
2256
2259
  import { request as httpsRequest } from "https";
2260
+ import { homedir, userInfo } from "os";
2257
2261
  import { fileURLToPath as fileURLToPath2 } from "url";
2258
2262
  import chokidar from "chokidar";
2259
2263
 
@@ -4031,6 +4035,85 @@ var ServiceCommands = class {
4031
4035
  clearServiceState();
4032
4036
  console.log(`\u2713 ${APP_NAME2} stopped`);
4033
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
+ }
4034
4117
  async waitForBackgroundServiceReady(params) {
4035
4118
  const startedAt = Date.now();
4036
4119
  let lastProbeError = null;
@@ -4061,6 +4144,78 @@ var ServiceCommands = class {
4061
4144
  const resolved = fromOverride ?? fromEnv ?? fallback;
4062
4145
  return Math.max(3e3, resolved);
4063
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
+ }
4064
4219
  appendStartupStage(logPath, message) {
4065
4220
  try {
4066
4221
  appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [startup] ${message}
@@ -4223,6 +4378,7 @@ var ServiceCommands = class {
4223
4378
  host: uiConfig.host,
4224
4379
  port: uiConfig.port,
4225
4380
  configPath: getConfigPath3(),
4381
+ productVersion: getPackageVersion(),
4226
4382
  staticDir: uiStaticDir ?? void 0,
4227
4383
  cronService,
4228
4384
  marketplace: {
@@ -4607,12 +4763,12 @@ ${stderr}`.trim();
4607
4763
  };
4608
4764
 
4609
4765
  // src/cli/workspace.ts
4610
- 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";
4611
4767
  import { createRequire } from "module";
4612
4768
  import { dirname as dirname2, join as join6, resolve as resolve8 } from "path";
4613
4769
  import { fileURLToPath as fileURLToPath3 } from "url";
4614
4770
  import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
4615
- import { spawnSync as spawnSync4 } from "child_process";
4771
+ import { spawnSync as spawnSync5 } from "child_process";
4616
4772
  var WorkspaceManager = class {
4617
4773
  constructor(logo) {
4618
4774
  this.logo = logo;
@@ -4651,7 +4807,7 @@ var WorkspaceManager = class {
4651
4807
  const raw = readFileSync7(templatePath, "utf-8");
4652
4808
  const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
4653
4809
  mkdirSync5(dirname2(filePath), { recursive: true });
4654
- writeFileSync4(filePath, content);
4810
+ writeFileSync5(filePath, content);
4655
4811
  created.push(entry.target);
4656
4812
  }
4657
4813
  const memoryDir = join6(workspace, "memory");
@@ -4759,7 +4915,7 @@ var WorkspaceManager = class {
4759
4915
  recursive: true,
4760
4916
  filter: (src) => !src.includes("node_modules") && !src.includes("dist")
4761
4917
  });
4762
- const install = spawnSync4("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
4918
+ const install = spawnSync5("npm", ["install"], { cwd: userBridge, stdio: "pipe" });
4763
4919
  if (install.status !== 0) {
4764
4920
  console.error(`Bridge install failed: ${install.status ?? 1}`);
4765
4921
  if (install.stderr) {
@@ -4767,7 +4923,7 @@ var WorkspaceManager = class {
4767
4923
  }
4768
4924
  process.exit(1);
4769
4925
  }
4770
- const build = spawnSync4("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
4926
+ const build = spawnSync5("npm", ["run", "build"], { cwd: userBridge, stdio: "pipe" });
4771
4927
  if (build.status !== 0) {
4772
4928
  console.error(`Bridge build failed: ${build.status ?? 1}`);
4773
4929
  if (build.stderr) {
@@ -5137,6 +5293,12 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
5137
5293
  async stop() {
5138
5294
  await this.serviceCommands.stopService();
5139
5295
  }
5296
+ async serviceInstallSystemd(opts) {
5297
+ await this.serviceCommands.installSystemdService(opts);
5298
+ }
5299
+ async serviceUninstallSystemd(opts) {
5300
+ await this.serviceCommands.uninstallSystemdService(opts);
5301
+ }
5140
5302
  async agent(opts) {
5141
5303
  const configPath = getConfigPath4();
5142
5304
  const config2 = resolveConfigSecrets3(loadConfig7(), { configPath });
@@ -5219,7 +5381,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
5219
5381
  const merged = history.concat(
5220
5382
  rl.history ?? []
5221
5383
  );
5222
- writeFileSync5(historyFile, merged.join("\n"));
5384
+ writeFileSync6(historyFile, merged.join("\n"));
5223
5385
  process.exit(0);
5224
5386
  });
5225
5387
  let running = true;
@@ -5399,6 +5561,9 @@ program.command("start").description(`Start the ${APP_NAME5} gateway + UI in the
5399
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));
5400
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));
5401
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));
5402
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));
5403
5568
  program.command("update").description(`Update ${APP_NAME5}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
5404
5569
  var registerClawHubInstall = (cmd) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.9.8",
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",
@@ -38,8 +38,9 @@
38
38
  "dependencies": {
39
39
  "chokidar": "^3.6.0",
40
40
  "commander": "^12.1.0",
41
- "@nextclaw/core": "^0.7.0",
42
- "@nextclaw/server": "^0.6.2",
41
+ "@nextclaw/core": "^0.7.1",
42
+ "@nextclaw/runtime": "^0.1.1",
43
+ "@nextclaw/server": "^0.6.4",
43
44
  "@nextclaw/openclaw-compat": "^0.2.0"
44
45
  },
45
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};