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 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`.
@@ -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.0";
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: { ...process.env, HERMES_HOME: hermesDir },
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(path.join(os.homedir(), ".local", "bin", "hermes")) ? path.join(os.homedir(), ".local", "bin", "hermes") : null) ||
526
- (config?.hermes?.home && fs.existsSync(path.join(config.hermes.home, "hermes-agent", "venv", "bin", "hermes"))
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 ["hermes", "openilink", "webhook"]) {
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 = ["hermes", "openilink", "webhook"].map((name) => logPath(config, name)).filter((filePath) => fs.existsSync(filePath));
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodus-wechat",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "CLI installer for Nodus WeChat, Hermes, and the local OpeniLink webhook runtime.",
5
5
  "license": "MIT",
6
6
  "private": false,