nodus-wechat 0.7.0 → 0.7.2
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/README.md +4 -1
- package/bin/nodus-wechat.js +49 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ npx nodus-wechat doctor
|
|
|
19
19
|
npx nodus-wechat start
|
|
20
20
|
npx nodus-wechat start --no-install
|
|
21
21
|
npx nodus-wechat start --no-hermes
|
|
22
|
+
npx nodus-wechat start --no-dashboard
|
|
22
23
|
npx nodus-wechat start --docker
|
|
23
24
|
npx nodus-wechat status
|
|
24
25
|
npx nodus-wechat logs
|
|
@@ -50,11 +51,13 @@ the official OpeniLink installer.
|
|
|
50
51
|
- Installs the OpeniLink + webhook POC runtime at `~/.nodus-wechat/runtime`.
|
|
51
52
|
- Writes Hermes common settings to `~/.hermes/config.yaml`.
|
|
52
53
|
- Writes the AstraGate key to `~/.hermes/.env` as `ASTRAGATE_API_KEY`.
|
|
54
|
+
- Sets Hermes static UI/message language to Simplified Chinese with `display.language: zh`.
|
|
53
55
|
- Writes runtime `.env`, Docker Compose, webhook server, helper scripts, and the OpeniLink reply plugin.
|
|
54
56
|
- Stores gateway base URL, api key, model, Hermes paths, OpeniLink origin, webhook port, and runtime path.
|
|
55
57
|
- Checks Node.js, local configuration, Hermes files, runtime files, Python, OpeniLink CLI, optional Docker Compose availability, Hermes CLI availability, and WeChat app detection with `doctor`.
|
|
56
|
-
- Starts/stops Hermes Gateway, OpeniLink, and the local webhook with native host processes by default.
|
|
58
|
+
- Starts/stops Hermes Gateway, Hermes Dashboard, OpeniLink, and the local webhook with native host processes by default.
|
|
57
59
|
- Installs Hermes and OpeniLink automatically during `start` when the native CLIs are missing.
|
|
60
|
+
- Exposes the Hermes management/configuration UI at `http://127.0.0.1:9119`.
|
|
58
61
|
- Keeps Docker Compose available only when `--docker` is passed.
|
|
59
62
|
- Removes Nodus WeChat config/runtime files with `uninstall --yes`.
|
|
60
63
|
- Cleans Nodus WeChat config/runtime plus generated Hermes settings with `clean --yes`.
|
package/bin/nodus-wechat.js
CHANGED
|
@@ -8,13 +8,14 @@ const os = require("node:os");
|
|
|
8
8
|
const path = require("node:path");
|
|
9
9
|
const childProcess = require("node:child_process");
|
|
10
10
|
|
|
11
|
-
const VERSION = "0.7.
|
|
11
|
+
const VERSION = "0.7.1";
|
|
12
12
|
const DEFAULT_BASE_URL = "https://api.nodus.sbs/";
|
|
13
13
|
const DEFAULT_MODEL = "gpt-5.5";
|
|
14
14
|
const DEFAULT_OPENILINK_ORIGIN = "http://localhost:9800";
|
|
15
15
|
const DEFAULT_OPENILINK_RP_ID = "localhost";
|
|
16
16
|
const DEFAULT_OPENILINK_PORT = 9800;
|
|
17
17
|
const DEFAULT_WEBHOOK_PORT = 9811;
|
|
18
|
+
const DEFAULT_HERMES_DASHBOARD_PORT = 9119;
|
|
18
19
|
const TEMPLATE_DIR = path.join(__dirname, "..", "templates", "wechat-agent-poc");
|
|
19
20
|
|
|
20
21
|
function configHome() {
|
|
@@ -43,7 +44,7 @@ Usage:
|
|
|
43
44
|
nodus-wechat install-hermes
|
|
44
45
|
nodus-wechat install-openilink
|
|
45
46
|
nodus-wechat doctor
|
|
46
|
-
nodus-wechat start [--docker] [--no-install] [--no-hermes]
|
|
47
|
+
nodus-wechat start [--docker] [--no-install] [--no-hermes] [--no-dashboard]
|
|
47
48
|
nodus-wechat status [--docker]
|
|
48
49
|
nodus-wechat logs [--docker]
|
|
49
50
|
nodus-wechat stop [--docker]
|
|
@@ -56,7 +57,7 @@ Commands:
|
|
|
56
57
|
install-openilink
|
|
57
58
|
Install OpeniLink Hub native CLI with the official installer.
|
|
58
59
|
doctor Check local prerequisites and configuration.
|
|
59
|
-
start Start Hermes Gateway, OpeniLink, and webhook on the host by default.
|
|
60
|
+
start Start Hermes Gateway, Hermes Dashboard, OpeniLink, and webhook on the host by default.
|
|
60
61
|
status Show local process status.
|
|
61
62
|
logs Follow local runtime logs.
|
|
62
63
|
stop Stop the local runtime.
|
|
@@ -78,7 +79,7 @@ function parseArgs(argv) {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
const key = item.slice(2);
|
|
81
|
-
if (key === "help" || key === "yes" || key === "install-hermes" || key === "docker" || key === "no-install" || key === "no-hermes") {
|
|
82
|
+
if (key === "help" || key === "yes" || key === "install-hermes" || key === "docker" || key === "no-install" || key === "no-hermes" || key === "no-dashboard") {
|
|
82
83
|
result[key] = true;
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
@@ -198,6 +199,7 @@ function buildHermesConfig(config) {
|
|
|
198
199
|
' - "all"',
|
|
199
200
|
"display:",
|
|
200
201
|
' tool_progress: "all"',
|
|
202
|
+
' language: "zh"',
|
|
201
203
|
"compression:",
|
|
202
204
|
" enabled: true",
|
|
203
205
|
"",
|
|
@@ -247,12 +249,24 @@ function openiLinkInstallCommand() {
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
function runHermesInstaller(hermesDir) {
|
|
252
|
+
const checkoutDir = path.join(hermesDir, "hermes-agent");
|
|
253
|
+
if (fs.existsSync(checkoutDir) && fs.existsSync(path.join(checkoutDir, ".git")) && !fs.existsSync(path.join(checkoutDir, "pyproject.toml"))) {
|
|
254
|
+
fs.rmSync(checkoutDir, { recursive: true, force: true });
|
|
255
|
+
}
|
|
256
|
+
|
|
250
257
|
const args = ["--skip-setup", "--hermes-home", hermesDir];
|
|
251
258
|
const command = `${hermesInstallCommand()} ${args.map(shellQuote).join(" ")}`;
|
|
252
259
|
const result = childProcess.spawnSync(command, {
|
|
253
260
|
shell: true,
|
|
254
261
|
stdio: "inherit",
|
|
255
|
-
env: {
|
|
262
|
+
env: {
|
|
263
|
+
...process.env,
|
|
264
|
+
HERMES_HOME: hermesDir,
|
|
265
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
266
|
+
GIT_CONFIG_COUNT: "1",
|
|
267
|
+
GIT_CONFIG_KEY_0: "url.https://github.com/.insteadOf",
|
|
268
|
+
GIT_CONFIG_VALUE_0: "git@github.com:",
|
|
269
|
+
},
|
|
256
270
|
});
|
|
257
271
|
|
|
258
272
|
if (result.error) {
|
|
@@ -520,12 +534,14 @@ function commandPath(name) {
|
|
|
520
534
|
}
|
|
521
535
|
|
|
522
536
|
function hermesPath(config) {
|
|
537
|
+
const configuredHome = config?.hermes?.home || hermesHome();
|
|
538
|
+
const configuredVenvHermes = path.join(configuredHome, "hermes-agent", "venv", "bin", "hermes");
|
|
539
|
+
const defaultUserHermes = path.join(os.homedir(), ".local", "bin", "hermes");
|
|
540
|
+
const defaultHermesHome = path.join(os.homedir(), ".hermes");
|
|
523
541
|
return (
|
|
524
542
|
commandPath("hermes") ||
|
|
525
|
-
(fs.existsSync(
|
|
526
|
-
(
|
|
527
|
-
? path.join(config.hermes.home, "hermes-agent", "venv", "bin", "hermes")
|
|
528
|
-
: null)
|
|
543
|
+
(fs.existsSync(configuredVenvHermes) ? configuredVenvHermes : null) ||
|
|
544
|
+
(configuredHome === defaultHermesHome && fs.existsSync(defaultUserHermes) ? defaultUserHermes : null)
|
|
529
545
|
);
|
|
530
546
|
}
|
|
531
547
|
|
|
@@ -541,6 +557,17 @@ function logPath(config, name) {
|
|
|
541
557
|
return runtimePath(config, `${name}.log`);
|
|
542
558
|
}
|
|
543
559
|
|
|
560
|
+
function localProcessNames(options = {}) {
|
|
561
|
+
const names = ["hermes", "hermes-dashboard", "openilink", "webhook"];
|
|
562
|
+
if (options["no-hermes"]) {
|
|
563
|
+
return names.filter((name) => name !== "hermes" && name !== "hermes-dashboard");
|
|
564
|
+
}
|
|
565
|
+
if (options["no-dashboard"]) {
|
|
566
|
+
return names.filter((name) => name !== "hermes-dashboard");
|
|
567
|
+
}
|
|
568
|
+
return names;
|
|
569
|
+
}
|
|
570
|
+
|
|
544
571
|
function readPid(filePath) {
|
|
545
572
|
if (!fs.existsSync(filePath)) {
|
|
546
573
|
return null;
|
|
@@ -738,10 +765,17 @@ function startLocal(options) {
|
|
|
738
765
|
startManagedProcess(config, "openilink", oih, [], env);
|
|
739
766
|
if (hermes) {
|
|
740
767
|
startManagedProcess(config, "hermes", hermes, ["gateway", "run"], env);
|
|
768
|
+
if (!options["no-dashboard"]) {
|
|
769
|
+
startManagedProcess(config, "hermes-dashboard", hermes, ["dashboard", "--host", "127.0.0.1", "--port", String(DEFAULT_HERMES_DASHBOARD_PORT), "--no-open"], env);
|
|
770
|
+
}
|
|
741
771
|
} else {
|
|
742
772
|
console.log("hermes: skipped (--no-hermes)");
|
|
773
|
+
console.log("hermes-dashboard: skipped (--no-hermes)");
|
|
743
774
|
}
|
|
744
775
|
console.log(`OpeniLink Hub: ${config.openilink?.publicOrigin || DEFAULT_OPENILINK_ORIGIN}`);
|
|
776
|
+
if (hermes && !options["no-dashboard"]) {
|
|
777
|
+
console.log(`Hermes Dashboard: http://127.0.0.1:${DEFAULT_HERMES_DASHBOARD_PORT}`);
|
|
778
|
+
}
|
|
745
779
|
console.log(`Webhook health: http://127.0.0.1:${config.webhook?.port || DEFAULT_WEBHOOK_PORT}/health`);
|
|
746
780
|
return 0;
|
|
747
781
|
}
|
|
@@ -765,10 +799,12 @@ async function statusLocal() {
|
|
|
765
799
|
return 1;
|
|
766
800
|
}
|
|
767
801
|
|
|
768
|
-
for (const name of
|
|
802
|
+
for (const name of localProcessNames()) {
|
|
769
803
|
const pid = readPid(pidPath(config, name));
|
|
770
804
|
console.log(`${name}: ${processRunning(pid) ? `running (pid ${pid})` : "stopped"}`);
|
|
771
805
|
}
|
|
806
|
+
const dashboardHealth = await httpGet(`http://127.0.0.1:${DEFAULT_HERMES_DASHBOARD_PORT}`);
|
|
807
|
+
console.log(`hermes dashboard: ${dashboardHealth.ok ? `ok (${dashboardHealth.statusCode})` : "unreachable"}`);
|
|
772
808
|
const health = await httpGet(`http://127.0.0.1:${config.webhook?.port || DEFAULT_WEBHOOK_PORT}/health`);
|
|
773
809
|
console.log(`webhook health: ${health.ok ? `ok (${health.statusCode})` : "unreachable"}`);
|
|
774
810
|
return 0;
|
|
@@ -793,7 +829,7 @@ function logsLocal() {
|
|
|
793
829
|
return 1;
|
|
794
830
|
}
|
|
795
831
|
|
|
796
|
-
const files =
|
|
832
|
+
const files = localProcessNames().map((name) => logPath(config, name)).filter((filePath) => fs.existsSync(filePath));
|
|
797
833
|
if (files.length === 0) {
|
|
798
834
|
console.error("No local runtime logs found.");
|
|
799
835
|
return 1;
|
|
@@ -833,6 +869,7 @@ function stopLocal() {
|
|
|
833
869
|
|
|
834
870
|
stopManagedProcess(config, "webhook");
|
|
835
871
|
stopManagedProcess(config, "openilink");
|
|
872
|
+
stopManagedProcess(config, "hermes-dashboard");
|
|
836
873
|
stopManagedProcess(config, "hermes");
|
|
837
874
|
return 0;
|
|
838
875
|
}
|
|
@@ -891,7 +928,7 @@ function clean(options) {
|
|
|
891
928
|
|
|
892
929
|
const config = fs.existsSync(configPath()) ? readConfig() : null;
|
|
893
930
|
if (config) {
|
|
894
|
-
for (const name of ["webhook", "openilink", "hermes"]) {
|
|
931
|
+
for (const name of ["webhook", "openilink", "hermes-dashboard", "hermes"]) {
|
|
895
932
|
stopManagedProcess({ ...config, runtime: { ...config.runtime, dir: config.runtime?.dir || path.join(configHome(), "runtime") } }, name);
|
|
896
933
|
}
|
|
897
934
|
const docker = dockerComposeAvailable();
|