omp-wechat 1.3.0 → 1.4.1

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 (3) hide show
  1. package/README.md +10 -10
  2. package/dist/index.js +29 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,9 +12,9 @@ WeChat user → iLink Bot API → [OMP/Pi process] → SDK → AI provider
12
12
  └──── reply ← message_end ─┘
13
13
  ```
14
14
 
15
- The extension runs **inside** the OMP/Pi process. On `session_start`, the iLink long-poll loop starts in-process as a background promise. A singleton port lock ensures only one process runs the poll loop at a time — other OMP/Pi processes standby with a 30s failover timer to take over if the lock holder crashes.
15
+ The extension runs **inside** the OMP/Pi process. The iLink long-poll loop starts at extension load time (not `session_start`) as a background promise. A singleton port lock ensures only one process runs the poll loop at a time — other OMP/Pi processes standby with a 30s failover timer to take over if the lock holder crashes.
16
16
 
17
- For boot-time persistence, install a launchd/systemd service via `/wechat install`. The service runs `omp --mode rpc` (or `pi --mode rpc`) with `KeepAlive`/`Restart=always`, so the host (and the poll loop) survive crashes and reboots.
17
+ For boot-time persistence, install a launchd/systemd service via `/wechat install`. The service runs `omp --mode rpc` (or `pi --mode rpc`) with a `get_state` JSON-RPC heartbeat piped to stdin every 5s — without an active RPC client, `omp --mode rpc` exits on idle stdin, so the heartbeat keeps the process alive. `KeepAlive`/`Restart=always` handles crashes and reboots.
18
18
 
19
19
  - **No external `bun` required** — OMP/Pi is a standalone binary with an embedded runtime
20
20
  - **Singleton** — port lock guarantees one poll loop across all concurrent OMP/Pi processes
@@ -26,7 +26,7 @@ For boot-time persistence, install a launchd/systemd service via `/wechat instal
26
26
 
27
27
  ## Features
28
28
 
29
- - **OMP/Pi extension**: installs via `omp plugin link .` or `pi plugin link .`, auto-starts poll loop on `session_start`
29
+ - **OMP/Pi extension**: installs via `omp plugin link .` or `pi plugin link .`, auto-starts poll loop at extension load time
30
30
  - **Slash commands**: `/wechat login`, `/wechat status`, `/wechat pair`, `/wechat allow`, `/wechat revoke`, `/wechat list`, `/wechat stop`, `/wechat install`, `/wechat uninstall`
31
31
  - **Singleton**: port lock guarantees one poll loop across all concurrent OMP/Pi processes — no duplicate replies
32
32
  - **Failover**: 30s timer takes over automatically if the lock holder crashes
@@ -56,7 +56,7 @@ bun run build
56
56
  omp plugin link . # or: pi plugin link .
57
57
  ```
58
58
 
59
- This links the extension into OMP/Pi. The poll loop starts automatically on your next `session_start`.
59
+ This links the extension into OMP/Pi. The poll loop starts at extension load time — no `session_start` required.
60
60
 
61
61
  ### Login (scan QR code)
62
62
 
@@ -68,7 +68,7 @@ A QR code appears in the terminal. Scan it with WeChat and confirm on your phone
68
68
 
69
69
  ### Run
70
70
 
71
- No explicit run command needed — the poll loop auto-starts on `session_start`. Once running, send a message to the bot on WeChat — it will be processed and the reply sent back.
71
+ No explicit run command needed — the poll loop starts at extension load time. Once running, send a message to the bot on WeChat — it will be processed and the reply sent back.
72
72
 
73
73
  To check status: `/wechat status`. To stop: `/wechat stop`.
74
74
 
@@ -78,9 +78,9 @@ To check status: `/wechat status`. To stop: `/wechat stop`.
78
78
  /wechat install
79
79
  ```
80
80
 
81
- Installs a launchd (macOS) or systemd (Linux) service that runs the host (`omp --mode rpc` or `pi --mode rpc`) at boot. The host stays alive via `KeepAlive`/`Restart=always`, keeping the poll loop running across crashes and reboots.
81
+ Installs a launchd (macOS) or systemd (Linux) service that runs the host (`omp --mode rpc` or `pi --mode rpc`) at boot. A `get_state` JSON-RPC heartbeat is piped to stdin every 5s to keep the process alive (without an active RPC client, `omp --mode rpc` exits on idle stdin). `KeepAlive`/`Restart=always` handles crashes and reboots.
82
82
 
83
- Logs: `~/.omp-wechat/logs/rpc.log`
83
+ Logs: `~/.omp/logs/rpc.log` (stderr only; stdout discarded) and `~/.omp/logs/wechat.log` (poll loop)
84
84
  Manage: `launchctl start|stop com.omp-wechat` (macOS) or `sudo systemctl start|stop omp-wechat` (Linux)
85
85
 
86
86
  To remove: `/wechat uninstall`
@@ -149,14 +149,14 @@ The logged-in user (who scanned the QR code) is automatically added to the allow
149
149
 
150
150
  | Scenario | Behavior |
151
151
  |---|---|
152
- | Host process starts | Poll loop starts automatically (acquires singleton lock) |
152
+ | Host process starts | Poll loop starts at extension load time (acquires singleton lock) |
153
153
  | Other host processes | Standby with 30s failover timer, take over if lock holder dies |
154
154
  | Host process exits | Poll loop stops, lock released, all sessions disposed |
155
155
  | Host crashes | Failover timer in another process detects dead lock and takes over; or launchd/systemd restarts the host (if `/wechat install` was run) |
156
156
  | Machine reboots | Service auto-starts the host (if installed), poll loop resumes |
157
157
  | No boot service | Poll loop only runs while a host process is active |
158
158
 
159
- Logs: `~/.omp-wechat/logs/daemon.log` (poll loop) and `~/.omp-wechat/logs/rpc.log` (boot service RPC output)
159
+ Logs: `~/.omp/logs/wechat.log` (poll loop) and `~/.omp/logs/rpc.log` (boot service stderr)
160
160
 
161
161
  ## Project Structure
162
162
 
@@ -164,7 +164,7 @@ Logs: `~/.omp-wechat/logs/daemon.log` (poll loop) and `~/.omp-wechat/logs/rpc.lo
164
164
  OMP-Wechat/
165
165
  ├── package.json # omp.extensions / pi.extensions manifest
166
166
  ├── src/
167
- │ ├── index.ts # OMP/Pi extension entry (session_start + /wechat commands)
167
+ │ ├── index.ts # OMP/Pi extension entry (extension load + /wechat commands)
168
168
  │ ├── bridge.ts # In-process poll loop + message handling + singleton port lock
169
169
  │ ├── service.ts # Boot-time launchd/systemd install
170
170
  │ ├── config.ts # Config loading (config.yml + defaults)
package/dist/index.js CHANGED
@@ -1914,6 +1914,7 @@ class ChatSession {
1914
1914
  systemPrompt: config.systemPrompt,
1915
1915
  modelPattern: config.model
1916
1916
  });
1917
+ session.setAdvisorEnabled(false);
1917
1918
  if (modelFallbackMessage) {
1918
1919
  logger.warn(`Model fallback: ${modelFallbackMessage}`);
1919
1920
  }
@@ -2471,7 +2472,9 @@ function getLogDir() {
2471
2472
  return join8(homedir7(), ".omp", "logs");
2472
2473
  }
2473
2474
  function resolveHostBinary() {
2474
- return process.execPath || "omp";
2475
+ const exe = process.execPath || "omp";
2476
+ const basename2 = exe.split("/").pop() || "omp";
2477
+ return basename2;
2475
2478
  }
2476
2479
  function plistPath() {
2477
2480
  return join8(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
@@ -2488,10 +2491,9 @@ function generatePlist() {
2488
2491
 
2489
2492
  <key>ProgramArguments</key>
2490
2493
  <array>
2491
- <string>${omp}</string>
2492
- <string>--mode</string>
2493
- <string>rpc</string>
2494
- <string>--no-title</string>
2494
+ <string>/bin/sh</string>
2495
+ <string>-c</string>
2496
+ <string>while true; do echo '{"id":"ka","type":"get_state"}'; sleep 5; done | exec ${omp} --mode rpc --no-title >/dev/null</string>
2495
2497
  </array>
2496
2498
 
2497
2499
  <key>RunAtLoad</key>
@@ -2503,11 +2505,6 @@ function generatePlist() {
2503
2505
  <key>ThrottleInterval</key>
2504
2506
  <integer>10</integer>
2505
2507
 
2506
- <key>StandardInputPath</key>
2507
- <string>/dev/null</string>
2508
-
2509
- <key>StandardOutPath</key>
2510
- <string>${logDir}/rpc.log</string>
2511
2508
  <key>StandardErrorPath</key>
2512
2509
  <string>${logDir}/rpc.log</string>
2513
2510
 
@@ -2561,15 +2558,14 @@ Wants=network-online.target
2561
2558
  Type=simple
2562
2559
  User=${user}
2563
2560
  Group=${user}
2564
- ExecStart=${omp} --mode rpc --no-title
2565
- StandardInput=null
2561
+ ExecStart=/bin/sh -c 'while true; do echo "{\\"id\\":\\"ka\\",\\"type\\":\\"get_state\\"}"; sleep 5; done | exec ${omp} --mode rpc --no-title >/dev/null'
2566
2562
  Restart=always
2567
2563
  RestartSec=10
2568
2564
 
2569
2565
  Environment=HOME=${homedir7()}
2570
2566
  Environment=PATH=/usr/local/bin:/usr/bin:/bin
2571
2567
 
2572
- StandardOutput=append:${logDir}/rpc.log
2568
+ StandardOutput=null
2573
2569
  StandardError=append:${logDir}/rpc.log
2574
2570
 
2575
2571
  NoNewPrivileges=true
@@ -2659,21 +2655,27 @@ function wechatExtension(pi) {
2659
2655
  if (pi.pi && typeof pi.setLabel === "function") {
2660
2656
  pi.setLabel("OMP-Wechat Bridge");
2661
2657
  }
2658
+ bridge = new WeChatBridge;
2659
+ daemonState = bridge.start();
2660
+ if (daemonState.running) {
2661
+ logger.info("WeChat bridge started at extension load");
2662
+ } else {
2663
+ logger.debug("WeChat bridge: another instance holds the lock, starting failover watch");
2664
+ setInterval(() => {
2665
+ if (daemonState?.running)
2666
+ return;
2667
+ daemonState = bridge.start();
2668
+ if (daemonState.running) {
2669
+ logger.info("WeChat bridge: took over from failed instance");
2670
+ }
2671
+ }, 30000);
2672
+ }
2662
2673
  pi.on("session_start", async (_event, ctx) => {
2663
- bridge = new WeChatBridge;
2664
- daemonState = bridge.start();
2665
- if (daemonState.running) {
2666
- ctx.ui.notify("WeChat bridge started", "info");
2667
- } else {
2668
- logger.debug("WeChat bridge: another instance holds the lock, starting failover watch");
2669
- ctx.setInterval(() => {
2670
- if (daemonState?.running)
2671
- return;
2672
- daemonState = bridge.start();
2673
- if (daemonState.running) {
2674
- ctx.ui.notify("WeChat bridge: took over from failed instance", "info");
2675
- }
2676
- }, 30000);
2674
+ if (bridge && !daemonState?.running) {
2675
+ daemonState = bridge.start();
2676
+ if (daemonState.running) {
2677
+ ctx.ui.notify("WeChat bridge started", "info");
2678
+ }
2677
2679
  }
2678
2680
  });
2679
2681
  pi.registerCommand("wechat", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omp-wechat",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "OMP/Pi extension: bridge WeChat messages to OMP's AI engine via the iLink Bot API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",