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.
- package/dist/cli/index.js +195 -30
- package/package.json +4 -3
- package/templates/USAGE.md +9 -0
- package/ui-dist/assets/ChannelsList-Dz8AGmaQ.js +1 -0
- package/ui-dist/assets/ChatPage-BXDyt7BL.js +34 -0
- package/ui-dist/assets/DocBrowser-CkKvzF7m.js +1 -0
- package/ui-dist/assets/LogoBadge-C_ygxoGB.js +1 -0
- package/ui-dist/assets/{MarketplacePage--wFfsNH0.js → MarketplacePage-DEvRs-Jc.js} +2 -2
- package/ui-dist/assets/ModelConfig-BGfliN2Z.js +1 -0
- package/ui-dist/assets/ProvidersList-BHLGLSvs.js +1 -0
- package/ui-dist/assets/RuntimeConfig-Clltld_h.js +1 -0
- package/ui-dist/assets/{SecretsConfig-B25P3J7V.js → SecretsConfig-CaJLf7oJ.js} +2 -2
- package/ui-dist/assets/SessionsConfig-3QF7K9wm.js +2 -0
- package/ui-dist/assets/{card-CCSDsedj.js → card-DXo3NsaB.js} +1 -1
- package/ui-dist/assets/index-CGo5Vnh0.js +7 -0
- package/ui-dist/assets/index-DcxYzrFm.css +1 -0
- package/ui-dist/assets/input-CzTldMKo.js +1 -0
- package/ui-dist/assets/{label-BxzAKPzU.js → label-De__vsU7.js} +1 -1
- package/ui-dist/assets/{page-layout-DaLNSFKw.js → page-layout-BOgLC2tK.js} +1 -1
- package/ui-dist/assets/session-run-status-DQVCDxTb.js +5 -0
- package/ui-dist/assets/{switch-DHOCEi5L.js → switch-pMrS4heA.js} +1 -1
- package/ui-dist/assets/{tabs-custom-zdFy3fnK.js → tabs-custom-DhOxWfCb.js} +1 -1
- package/ui-dist/assets/{useConfirmDialog-D3ZVa92J.js → useConfirmDialog-CseKBGh5.js} +2 -2
- package/ui-dist/assets/{vendor-Dj2ULvht.js → vendor-D33xZtEC.js} +6 -6
- package/ui-dist/index.html +3 -3
- package/ui-dist/assets/ChannelsList-VqzbAMCc.js +0 -1
- package/ui-dist/assets/ChatPage-CjZqsBmn.js +0 -34
- package/ui-dist/assets/DocBrowser-DvU-iUeB.js +0 -1
- package/ui-dist/assets/ModelConfig-cY5UsbfA.js +0 -1
- package/ui-dist/assets/ProvidersList-qZwaFoFt.js +0 -1
- package/ui-dist/assets/RuntimeConfig-BY2Axlte.js +0 -1
- package/ui-dist/assets/SessionsConfig-CxA9gIBw.js +0 -2
- package/ui-dist/assets/chat-message-pw9oafI4.js +0 -5
- package/ui-dist/assets/index-CD8a2KMH.js +0 -2
- package/ui-dist/assets/index-DKOXGZc8.css +0 -1
- package/ui-dist/assets/logos-C3oHQ9kv.js +0 -1
- 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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 {
|
|
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:
|
|
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
|
|
1832
|
-
const jobs =
|
|
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
|
|
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 =
|
|
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
|
|
1877
|
-
if (
|
|
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
|
|
1886
|
-
const job =
|
|
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
|
|
1896
|
-
const ok = await
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
42
|
-
"@nextclaw/
|
|
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": {
|
package/templates/USAGE.md
CHANGED
|
@@ -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};
|