nodus-wechat 0.6.1 → 0.7.0

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
@@ -17,6 +17,8 @@ npx nodus-wechat install-hermes
17
17
  npx nodus-wechat install-openilink
18
18
  npx nodus-wechat doctor
19
19
  npx nodus-wechat start
20
+ npx nodus-wechat start --no-install
21
+ npx nodus-wechat start --no-hermes
20
22
  npx nodus-wechat start --docker
21
23
  npx nodus-wechat status
22
24
  npx nodus-wechat logs
@@ -51,7 +53,8 @@ the official OpeniLink installer.
51
53
  - Writes runtime `.env`, Docker Compose, webhook server, helper scripts, and the OpeniLink reply plugin.
52
54
  - Stores gateway base URL, api key, model, Hermes paths, OpeniLink origin, webhook port, and runtime path.
53
55
  - 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`.
54
- - Starts/stops the local runtime with native local processes by default.
56
+ - Starts/stops Hermes Gateway, OpeniLink, and the local webhook with native host processes by default.
57
+ - Installs Hermes and OpeniLink automatically during `start` when the native CLIs are missing.
55
58
  - Keeps Docker Compose available only when `--docker` is passed.
56
59
  - Removes Nodus WeChat config/runtime files with `uninstall --yes`.
57
60
  - Cleans Nodus WeChat config/runtime plus generated Hermes settings with `clean --yes`.
@@ -75,9 +78,8 @@ the config generated by this CLI.
75
78
 
76
79
  ## Current non-goals
77
80
 
78
- - Does not run a third-party installer unless `--install-hermes`, `install-hermes`, or `install-openilink` is explicitly requested.
79
81
  - Does not automate, inject into, read, or control WeChat directly.
80
- - Does not start a daemon, LaunchAgent, or background worker outside Docker Compose.
82
+ - Does not install a LaunchAgent or system service; `start` uses managed host processes that `stop` can terminate.
81
83
  - Does not redeem real CDKs or mutate sub2api accounts; the bundled webhook keeps the existing dry-run POC boundary.
82
84
 
83
85
  ## Runtime wiring
@@ -8,7 +8,7 @@ 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.6.1";
11
+ const VERSION = "0.7.0";
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";
@@ -43,7 +43,7 @@ Usage:
43
43
  nodus-wechat install-hermes
44
44
  nodus-wechat install-openilink
45
45
  nodus-wechat doctor
46
- nodus-wechat start [--docker]
46
+ nodus-wechat start [--docker] [--no-install] [--no-hermes]
47
47
  nodus-wechat status [--docker]
48
48
  nodus-wechat logs [--docker]
49
49
  nodus-wechat stop [--docker]
@@ -56,7 +56,7 @@ Commands:
56
56
  install-openilink
57
57
  Install OpeniLink Hub native CLI with the official installer.
58
58
  doctor Check local prerequisites and configuration.
59
- start Start OpeniLink + webhook with local processes by default.
59
+ start Start Hermes Gateway, OpeniLink, and webhook on the host by default.
60
60
  status Show local process status.
61
61
  logs Follow local runtime logs.
62
62
  stop Stop the local runtime.
@@ -78,7 +78,7 @@ function parseArgs(argv) {
78
78
  }
79
79
 
80
80
  const key = item.slice(2);
81
- if (key === "help" || key === "yes" || key === "install-hermes" || key === "docker") {
81
+ if (key === "help" || key === "yes" || key === "install-hermes" || key === "docker" || key === "no-install" || key === "no-hermes") {
82
82
  result[key] = true;
83
83
  continue;
84
84
  }
@@ -377,7 +377,7 @@ function setup(options) {
377
377
  console.log(`OpeniLink Hub: ${config.openilink.publicOrigin}`);
378
378
  console.log(`Webhook URL for OpeniLink: http://poc-webhook:${config.webhook.port}/webhook`);
379
379
  console.log("Runtime mode: host process by default; Docker is used only with `--docker`.");
380
- console.log("Run `nodus-wechat start` to start the local host runtime.");
380
+ console.log("Run `nodus-wechat start` to start Hermes Gateway plus the local host runtime.");
381
381
  }
382
382
 
383
383
  function installHermes() {
@@ -519,6 +519,16 @@ function commandPath(name) {
519
519
  return result.stdout.trim() || null;
520
520
  }
521
521
 
522
+ function hermesPath(config) {
523
+ return (
524
+ 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)
529
+ );
530
+ }
531
+
522
532
  function runtimePath(config, name) {
523
533
  return path.join(config.runtime.dir, name);
524
534
  }
@@ -597,6 +607,7 @@ function localRuntimeEnv(config) {
597
607
  return {
598
608
  ...process.env,
599
609
  ...env,
610
+ HERMES_HOME: config.hermes?.home || hermesHome(),
600
611
  LISTEN: `:${config.openilink?.port || DEFAULT_OPENILINK_PORT}`,
601
612
  RP_ORIGIN: config.openilink?.publicOrigin || DEFAULT_OPENILINK_ORIGIN,
602
613
  RP_ID: config.openilink?.rpId || DEFAULT_OPENILINK_RP_ID,
@@ -670,7 +681,7 @@ function startDocker() {
670
681
  return runDockerCompose(config, ["up", "-d"]);
671
682
  }
672
683
 
673
- function startLocal() {
684
+ function startLocal(options) {
674
685
  const config = loadRuntimeConfig();
675
686
  if (!config) {
676
687
  return 1;
@@ -682,24 +693,61 @@ function startLocal() {
682
693
  return 1;
683
694
  }
684
695
 
685
- const oih = commandPath("oih");
696
+ let hermes = options["no-hermes"] ? null : hermesPath(config);
697
+ if (!hermes && !options["no-hermes"]) {
698
+ if (options["no-install"]) {
699
+ console.error("Hermes CLI `hermes` is not installed.");
700
+ console.error("Run: nodus-wechat install-hermes");
701
+ return 1;
702
+ }
703
+
704
+ console.log("Hermes CLI `hermes` is not installed; installing it now.");
705
+ runHermesInstaller(config.hermes?.home || hermesHome());
706
+ hermes = hermesPath(config);
707
+ if (!hermes) {
708
+ console.error("Hermes installer completed, but `hermes` is still not on PATH.");
709
+ console.error("Open a new terminal or add the installed Hermes path to PATH, then rerun `nodus-wechat start`.");
710
+ return 1;
711
+ }
712
+ }
713
+
714
+ let oih = commandPath("oih");
686
715
  if (!oih) {
687
- console.error("OpeniLink Hub CLI `oih` is not installed.");
688
- console.error("Run: nodus-wechat install-openilink");
689
- return 1;
716
+ if (options["no-install"]) {
717
+ console.error("OpeniLink Hub CLI `oih` is not installed.");
718
+ console.error("Run: nodus-wechat install-openilink");
719
+ return 1;
720
+ }
721
+
722
+ console.log("OpeniLink Hub CLI `oih` is not installed; installing it now.");
723
+ const installResult = installOpeniLink();
724
+ if (installResult !== 0) {
725
+ return installResult;
726
+ }
727
+ oih = commandPath("oih");
728
+ if (!oih) {
729
+ console.error("OpeniLink installer completed, but `oih` is still not on PATH.");
730
+ console.error("Open a new terminal or add the installed OpeniLink path to PATH, then rerun `nodus-wechat start`.");
731
+ return 1;
732
+ }
690
733
  }
691
734
 
692
735
  const env = localRuntimeEnv(config);
693
736
  const webhookPath = path.join(config.runtime.dir, "poc-webhook", "server.py");
694
737
  startManagedProcess(config, "webhook", python, [webhookPath], env);
695
738
  startManagedProcess(config, "openilink", oih, [], env);
739
+ if (hermes) {
740
+ startManagedProcess(config, "hermes", hermes, ["gateway", "run"], env);
741
+ } else {
742
+ console.log("hermes: skipped (--no-hermes)");
743
+ }
696
744
  console.log(`OpeniLink Hub: ${config.openilink?.publicOrigin || DEFAULT_OPENILINK_ORIGIN}`);
697
745
  console.log(`Webhook health: http://127.0.0.1:${config.webhook?.port || DEFAULT_WEBHOOK_PORT}/health`);
698
746
  return 0;
699
747
  }
700
748
 
701
749
  function start(options) {
702
- return options.docker ? startDocker() : startLocal();
750
+ return options.docker ? startDocker() : startLocal(options);
703
751
  }
704
752
 
705
753
  function statusDocker() {
@@ -717,7 +765,7 @@ async function statusLocal() {
717
765
  return 1;
718
766
  }
719
767
 
720
- for (const name of ["openilink", "webhook"]) {
768
+ for (const name of ["hermes", "openilink", "webhook"]) {
721
769
  const pid = readPid(pidPath(config, name));
722
770
  console.log(`${name}: ${processRunning(pid) ? `running (pid ${pid})` : "stopped"}`);
723
771
  }
@@ -745,7 +793,7 @@ function logsLocal() {
745
793
  return 1;
746
794
  }
747
795
 
748
- const files = ["openilink", "webhook"].map((name) => logPath(config, name)).filter((filePath) => fs.existsSync(filePath));
796
+ const files = ["hermes", "openilink", "webhook"].map((name) => logPath(config, name)).filter((filePath) => fs.existsSync(filePath));
749
797
  if (files.length === 0) {
750
798
  console.error("No local runtime logs found.");
751
799
  return 1;
@@ -785,6 +833,7 @@ function stopLocal() {
785
833
 
786
834
  stopManagedProcess(config, "webhook");
787
835
  stopManagedProcess(config, "openilink");
836
+ stopManagedProcess(config, "hermes");
788
837
  return 0;
789
838
  }
790
839
 
@@ -842,7 +891,7 @@ function clean(options) {
842
891
 
843
892
  const config = fs.existsSync(configPath()) ? readConfig() : null;
844
893
  if (config) {
845
- for (const name of ["webhook", "openilink"]) {
894
+ for (const name of ["webhook", "openilink", "hermes"]) {
846
895
  stopManagedProcess({ ...config, runtime: { ...config.runtime, dir: config.runtime?.dir || path.join(configHome(), "runtime") } }, name);
847
896
  }
848
897
  const docker = dockerComposeAvailable();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodus-wechat",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "CLI installer for Nodus WeChat, Hermes, and the local OpeniLink webhook runtime.",
5
5
  "license": "MIT",
6
6
  "private": false,