omp-wechat 1.7.0 → 1.9.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.
Files changed (2) hide show
  1. package/dist/index.js +146 -67
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ var __toESM = (mod, isNodeMode, target) => {
30
30
  return to;
31
31
  };
32
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __require = import.meta.require;
33
34
 
34
35
  // node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
35
36
  var require_QRMode = __commonJS((exports, module) => {
@@ -1188,6 +1189,11 @@ var logger = {
1188
1189
  var STATE_DIR = join3(homedir2(), ".omp-wechat");
1189
1190
  var CREDENTIALS_FILE = join3(STATE_DIR, "credentials.json");
1190
1191
  var SYNC_BUF_FILE = join3(STATE_DIR, "sync_buf.txt");
1192
+ var CHANNEL_VERSION = "2.2.0";
1193
+ var BOT_AGENT = "OMP-Wechat/1.7.0";
1194
+ function baseInfo() {
1195
+ return { channel_version: CHANNEL_VERSION, bot_agent: BOT_AGENT };
1196
+ }
1191
1197
  function loadCredentials() {
1192
1198
  try {
1193
1199
  return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf8"));
@@ -1241,7 +1247,15 @@ async function apiFetch(creds, endpoint, body, timeoutMs = 15000) {
1241
1247
  const text = await res.text();
1242
1248
  if (!res.ok)
1243
1249
  throw new Error(`${endpoint} ${res.status}: ${text}`);
1244
- return JSON.parse(text);
1250
+ const data = JSON.parse(text);
1251
+ const ret = typeof data.ret === "number" ? data.ret : undefined;
1252
+ const errcode = typeof data.errcode === "number" ? data.errcode : undefined;
1253
+ if (ret !== undefined && ret !== 0 || errcode !== undefined && errcode !== 0) {
1254
+ const code = errcode ?? ret ?? 0;
1255
+ const errmsg = typeof data.errmsg === "string" ? data.errmsg : "";
1256
+ throw new Error(`${endpoint} error ret=${ret} errcode=${errcode}${errmsg ? `: ${errmsg}` : ""} (code ${code})`);
1257
+ }
1258
+ return data;
1245
1259
  } catch (err) {
1246
1260
  clearTimeout(timer);
1247
1261
  throw err;
@@ -1251,7 +1265,7 @@ async function getUpdates(creds, buf) {
1251
1265
  try {
1252
1266
  const resp = await apiFetch(creds, "ilink/bot/getupdates", {
1253
1267
  get_updates_buf: buf,
1254
- base_info: { channel_version: "0.1.0" }
1268
+ base_info: baseInfo()
1255
1269
  }, 35000);
1256
1270
  return resp;
1257
1271
  } catch (err) {
@@ -1272,7 +1286,7 @@ async function sendMessage(creds, to, text, contextToken, clientId) {
1272
1286
  item_list: [{ type: 1, text_item: { text } }],
1273
1287
  context_token: contextToken
1274
1288
  },
1275
- base_info: { channel_version: "0.1.0" }
1289
+ base_info: baseInfo()
1276
1290
  }, 15000);
1277
1291
  }
1278
1292
  var typingTicketCache = new Map;
@@ -1284,7 +1298,7 @@ async function ensureTypingTicket(creds, userId) {
1284
1298
  try {
1285
1299
  const resp = await apiFetch(creds, "ilink/bot/getconfig", {
1286
1300
  ilink_user_id: userId,
1287
- base_info: { channel_version: "2.0.1" }
1301
+ base_info: baseInfo()
1288
1302
  }, 15000);
1289
1303
  const data = resp;
1290
1304
  if (data.typing_ticket) {
@@ -1306,10 +1320,9 @@ async function sendTyping(creds, userId, command) {
1306
1320
  try {
1307
1321
  await apiFetch(creds, "ilink/bot/sendtyping", {
1308
1322
  ilink_user_id: userId,
1309
- to_user_id: userId,
1310
1323
  typing_ticket: ticket,
1311
- command,
1312
- base_info: { channel_version: "2.0.1" }
1324
+ status: command,
1325
+ base_info: baseInfo()
1313
1326
  }, 1e4);
1314
1327
  } catch (err) {
1315
1328
  logger.debug(`sendTyping failed for ${userId}: ${err}`);
@@ -1491,6 +1504,64 @@ function listAllowed() {
1491
1504
  return loadAccess().allowFrom;
1492
1505
  }
1493
1506
 
1507
+ // src/utils/runtime-compat.ts
1508
+ import { spawnSync as nodeSpawnSync } from "child_process";
1509
+ import { createServer as createNetServer } from "net";
1510
+ function isBun() {
1511
+ return typeof globalThis.Bun !== "undefined" && typeof globalThis.Bun.sleep === "function";
1512
+ }
1513
+ function sleep(ms) {
1514
+ if (isBun()) {
1515
+ return globalThis.Bun.sleep(ms);
1516
+ }
1517
+ return new Promise((resolve) => setTimeout(resolve, ms));
1518
+ }
1519
+ function spawnSync(cmd, opts = {}) {
1520
+ if (isBun()) {
1521
+ const res2 = globalThis.Bun.spawnSync(cmd, opts);
1522
+ return {
1523
+ exitCode: res2.exitCode,
1524
+ stdout: Buffer.isBuffer(res2.stdout) ? res2.stdout : Buffer.from(res2.stdout ?? []),
1525
+ stderr: Buffer.isBuffer(res2.stderr) ? res2.stderr : Buffer.from(res2.stderr ?? [])
1526
+ };
1527
+ }
1528
+ const [command, ...args] = cmd;
1529
+ const res = nodeSpawnSync(command, args, {
1530
+ cwd: opts.cwd,
1531
+ stdio: ["pipe", opts.stdout ?? "pipe", opts.stderr ?? "pipe"]
1532
+ });
1533
+ return {
1534
+ exitCode: res.status ?? (res.error || res.signal ? 1 : 0),
1535
+ stdout: res.stdout ?? Buffer.alloc(0),
1536
+ stderr: res.stderr ?? Buffer.alloc(0)
1537
+ };
1538
+ }
1539
+ function listenPort(port) {
1540
+ if (isBun()) {
1541
+ const server2 = globalThis.Bun.serve({
1542
+ port,
1543
+ hostname: "127.0.0.1",
1544
+ fetch() {
1545
+ return new Response("ok");
1546
+ }
1547
+ });
1548
+ return Promise.resolve({ stop: () => server2.stop(true) });
1549
+ }
1550
+ const { promise, resolve, reject } = Promise.withResolvers();
1551
+ const server = createNetServer((socket) => {
1552
+ socket.end("ok");
1553
+ });
1554
+ server.once("error", (err) => {
1555
+ reject(err);
1556
+ });
1557
+ server.listen(port, "127.0.0.1", () => {
1558
+ resolve({
1559
+ stop: () => server.close()
1560
+ });
1561
+ });
1562
+ return promise;
1563
+ }
1564
+
1494
1565
  // src/ilink/login.ts
1495
1566
  var import_qrcode_terminal = __toESM(require_main(), 1);
1496
1567
  var DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com/";
@@ -1511,13 +1582,9 @@ async function login() {
1511
1582
  }
1512
1583
  try {
1513
1584
  import_qrcode_terminal.default.generate(qrUrl, { small: true });
1514
- } catch {
1515
- logger.info("Installing qrcode-terminal...");
1516
- Bun.spawnSync(["bun", "install", "qrcode-terminal"], {
1517
- cwd: process.cwd(),
1518
- stderr: "inherit"
1519
- });
1520
- import_qrcode_terminal.default.generate(qrUrl, { small: true });
1585
+ } catch (err) {
1586
+ logger.error("qrcode-terminal failed to render QR code:", err);
1587
+ throw new Error("Could not render QR code. Please confirm the plugin's dependencies are installed (qrcode-terminal).");
1521
1588
  }
1522
1589
  process.stderr.write(`
1523
1590
  Scan the QR code above with WeChat, or open this link:
@@ -1560,7 +1627,7 @@ Scan the QR code above with WeChat, or open this link:
1560
1627
  return;
1561
1628
  }
1562
1629
  }
1563
- await Bun.sleep(POLL_INTERVAL_MS);
1630
+ await sleep(POLL_INTERVAL_MS);
1564
1631
  }
1565
1632
  throw new Error("Login timeout, please run login again");
1566
1633
  }
@@ -1873,7 +1940,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1873
1940
  filesize: ciphertext.length,
1874
1941
  no_need_thumb: true,
1875
1942
  aeskey: aesKey.toString("hex"),
1876
- base_info: { channel_version: "0.1.0" }
1943
+ base_info: baseInfo()
1877
1944
  }, 15000);
1878
1945
  const uploadFullUrl = uploadParams.upload_full_url?.trim();
1879
1946
  if (!uploadFullUrl && !uploadParams.upload_param) {
@@ -1908,7 +1975,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1908
1975
  lastError = err;
1909
1976
  if (attempt < UPLOAD_MAX_RETRIES) {
1910
1977
  logger.warn(`CDN upload attempt ${attempt}/${UPLOAD_MAX_RETRIES} failed, retrying:`, err);
1911
- await Bun.sleep(1000 * attempt);
1978
+ await sleep(1000 * attempt);
1912
1979
  }
1913
1980
  }
1914
1981
  }
@@ -1920,7 +1987,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1920
1987
  }
1921
1988
  const media = {
1922
1989
  encrypt_query_param: encryptQueryParam,
1923
- aes_key: aesKey.toString("base64"),
1990
+ aes_key: Buffer.from(aesKey.toString("hex"), "utf8").toString("base64"),
1924
1991
  encrypt_type: 1
1925
1992
  };
1926
1993
  const item = mediaType === 1 /* IMAGE */ ? {
@@ -1931,7 +1998,6 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1931
1998
  file_item: {
1932
1999
  media,
1933
2000
  file_name: fileName,
1934
- md5: rawMd5,
1935
2001
  len: String(data.length)
1936
2002
  }
1937
2003
  };
@@ -1948,7 +2014,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1948
2014
  item_list: [item],
1949
2015
  context_token: contextToken
1950
2016
  },
1951
- base_info: { channel_version: "0.1.0" }
2017
+ base_info: baseInfo()
1952
2018
  }, 15000);
1953
2019
  logger.info(`Sent ${mediaType === 1 /* IMAGE */ ? "image" : "file"} to ${to}: ${fileName} (${data.length} bytes)`);
1954
2020
  return { status: "sent", mediaType, bytes: data.length };
@@ -1956,7 +2022,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
1956
2022
  lastSendError = err;
1957
2023
  if (attempt < SEND_MAX_RETRIES) {
1958
2024
  logger.warn(`Send retry ${attempt + 1}/${SEND_MAX_RETRIES}:`, err);
1959
- await Bun.sleep(1000 * (attempt + 1));
2025
+ await sleep(1000 * (attempt + 1));
1960
2026
  }
1961
2027
  }
1962
2028
  }
@@ -1997,9 +2063,6 @@ function stripMarkdown(text) {
1997
2063
  return result.trim();
1998
2064
  }
1999
2065
 
2000
- // src/engine/session.ts
2001
- import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent";
2002
-
2003
2066
  // src/engine/session-store.ts
2004
2067
  import { join as join7 } from "path";
2005
2068
  import { homedir as homedir6 } from "os";
@@ -2137,6 +2200,17 @@ function clearAllSessions() {
2137
2200
  // src/engine/session.ts
2138
2201
  import { mkdirSync as mkdirSync6, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
2139
2202
  import { join as join8 } from "path";
2203
+ var sdkPromise = null;
2204
+ function loadSdk() {
2205
+ sdkPromise ??= (async () => {
2206
+ try {
2207
+ return await import("@oh-my-pi/pi-coding-agent");
2208
+ } catch {
2209
+ return await import("@earendil-works/pi-coding-agent");
2210
+ }
2211
+ })();
2212
+ return sdkPromise;
2213
+ }
2140
2214
  function outboxInstructions(outboxDir) {
2141
2215
  return `## File delivery to WeChat
2142
2216
 
@@ -2195,9 +2269,10 @@ class ChatSession {
2195
2269
  }
2196
2270
  ensureSessionsDir();
2197
2271
  const sessionDir = sessionDirFor(chatId);
2198
- const sessionManager = await SessionManager.continueRecent(config.cwd || process.cwd(), sessionDir);
2272
+ const sdk = await loadSdk();
2273
+ const sessionManager = await sdk.SessionManager.continueRecent(config.cwd || process.cwd(), sessionDir);
2199
2274
  logger.info(`Session dir: ${sessionDir} (resumed=${sessionManager.getSessionFile() !== null})`);
2200
- const { session, modelFallbackMessage } = await createAgentSession({
2275
+ const { session, modelFallbackMessage } = await sdk.createAgentSession({
2201
2276
  sessionManager,
2202
2277
  enableMCP: false,
2203
2278
  enableLsp: false,
@@ -2589,7 +2664,7 @@ class WeChatBridge {
2589
2664
  this.commands.register(new ModelCommand);
2590
2665
  this.commands.register(new NewSessionCommand);
2591
2666
  }
2592
- start() {
2667
+ async start() {
2593
2668
  const config = loadConfig();
2594
2669
  let creds;
2595
2670
  try {
@@ -2600,7 +2675,7 @@ class WeChatBridge {
2600
2675
  if (this.pollActive) {
2601
2676
  return this.state;
2602
2677
  }
2603
- if (!this.acquireLock()) {
2678
+ if (!await this.acquireLock()) {
2604
2679
  logger.debug("Another poll loop is running (port lock held), skipping");
2605
2680
  return { running: false, config, creds, lastError: "another instance running" };
2606
2681
  }
@@ -2653,15 +2728,9 @@ class WeChatBridge {
2653
2728
  getPoolStatus() {
2654
2729
  return this.pool?.getPoolStatus() ?? { count: 0, max: 0, chats: [] };
2655
2730
  }
2656
- acquireLock() {
2731
+ async acquireLock() {
2657
2732
  try {
2658
- this.lockServer = Bun.serve({
2659
- port: LOCK_PORT,
2660
- hostname: "127.0.0.1",
2661
- fetch() {
2662
- return new Response("ok");
2663
- }
2664
- });
2733
+ this.lockServer = await listenPort(LOCK_PORT);
2665
2734
  return true;
2666
2735
  } catch {
2667
2736
  return false;
@@ -2669,7 +2738,7 @@ class WeChatBridge {
2669
2738
  }
2670
2739
  releaseLock() {
2671
2740
  if (this.lockServer) {
2672
- this.lockServer.stop(true);
2741
+ this.lockServer.stop();
2673
2742
  this.lockServer = null;
2674
2743
  }
2675
2744
  }
@@ -2685,9 +2754,9 @@ class WeChatBridge {
2685
2754
  logger.warn(`getupdates error ret=${resp.ret} errmsg=${resp.errmsg ?? ""} (${failures}/${MAX_FAILURES})`);
2686
2755
  if (failures >= MAX_FAILURES) {
2687
2756
  failures = 0;
2688
- await Bun.sleep(BACKOFF_MS);
2757
+ await sleep(BACKOFF_MS);
2689
2758
  } else {
2690
- await Bun.sleep(RETRY_MS);
2759
+ await sleep(RETRY_MS);
2691
2760
  }
2692
2761
  continue;
2693
2762
  }
@@ -2708,9 +2777,9 @@ class WeChatBridge {
2708
2777
  logger.error(`Poll error (${failures}/${MAX_FAILURES}):`, err);
2709
2778
  if (failures >= MAX_FAILURES) {
2710
2779
  failures = 0;
2711
- await Bun.sleep(BACKOFF_MS);
2780
+ await sleep(BACKOFF_MS);
2712
2781
  } else {
2713
- await Bun.sleep(RETRY_MS);
2782
+ await sleep(RETRY_MS);
2714
2783
  }
2715
2784
  }
2716
2785
  }
@@ -2879,7 +2948,7 @@ ${truncated}
2879
2948
  return;
2880
2949
  }
2881
2950
  logger.warn(`[${chatId}] Send retry ${retries}/${MAX_SEND_RETRIES}:`, err);
2882
- await Bun.sleep(1000 * retries);
2951
+ await sleep(1000 * retries);
2883
2952
  }
2884
2953
  }
2885
2954
  }
@@ -2958,21 +3027,21 @@ function installLaunchd() {
2958
3027
  mkdirSync7(getLogDir(), { recursive: true });
2959
3028
  const plist = plistPath();
2960
3029
  if (existsSync3(plist)) {
2961
- Bun.spawnSync(["launchctl", "unload", plist], { stderr: "ignore" });
3030
+ spawnSync(["launchctl", "unload", plist], { stderr: "ignore" });
2962
3031
  }
2963
3032
  writeFileSync4(plist, generatePlist());
2964
- const result = Bun.spawnSync(["launchctl", "load", plist], { stderr: "inherit" });
3033
+ const result = spawnSync(["launchctl", "load", plist], { stderr: "inherit" });
2965
3034
  if (result.exitCode !== 0) {
2966
3035
  throw new Error("launchctl load failed");
2967
3036
  }
2968
- Bun.spawnSync(["launchctl", "start", PLIST_LABEL], { stderr: "inherit" });
3037
+ spawnSync(["launchctl", "start", PLIST_LABEL], { stderr: "inherit" });
2969
3038
  }
2970
3039
  function uninstallLaunchd() {
2971
3040
  const plist = plistPath();
2972
3041
  if (!existsSync3(plist)) {
2973
3042
  throw new Error("No launchd service found (may not be installed)");
2974
3043
  }
2975
- Bun.spawnSync(["launchctl", "unload", plist], { stderr: "inherit" });
3044
+ spawnSync(["launchctl", "unload", plist], { stderr: "inherit" });
2976
3045
  rmSync3(plist);
2977
3046
  }
2978
3047
  function servicePath() {
@@ -3015,20 +3084,20 @@ function installSystemd() {
3015
3084
  const svc = servicePath();
3016
3085
  mkdirSync7(getLogDir(), { recursive: true });
3017
3086
  if (existsSync3(svc)) {
3018
- Bun.spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
3087
+ spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
3019
3088
  }
3020
3089
  const tmp = `/tmp/${SERVICE_NAME}.service`;
3021
3090
  writeFileSync4(tmp, generateService());
3022
- let result = Bun.spawnSync(["sudo", "te", tmp, svc], { stderr: "inherit" });
3091
+ let result = spawnSync(["sudo", "te", tmp, svc], { stderr: "inherit" });
3023
3092
  if (result.exitCode !== 0) {
3024
- result = Bun.spawnSync(["sudo", "cp", tmp, svc], { stderr: "inherit" });
3093
+ result = spawnSync(["sudo", "cp", tmp, svc], { stderr: "inherit" });
3025
3094
  }
3026
3095
  rmSync3(tmp);
3027
3096
  if (result.exitCode !== 0) {
3028
3097
  throw new Error("Failed to write service file (need sudo)");
3029
3098
  }
3030
- Bun.spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
3031
- result = Bun.spawnSync(["sudo", "systemctl", "enable", "--now", SERVICE_NAME], { stderr: "inherit" });
3099
+ spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
3100
+ result = spawnSync(["sudo", "systemctl", "enable", "--now", SERVICE_NAME], { stderr: "inherit" });
3032
3101
  if (result.exitCode !== 0) {
3033
3102
  throw new Error("systemctl enable failed");
3034
3103
  }
@@ -3038,10 +3107,10 @@ function uninstallSystemd() {
3038
3107
  if (!existsSync3(svc)) {
3039
3108
  throw new Error("No systemd service found (may not be installed)");
3040
3109
  }
3041
- Bun.spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
3042
- Bun.spawnSync(["sudo", "systemctl", "disable", SERVICE_NAME], { stderr: "inherit" });
3043
- Bun.spawnSync(["sudo", "rm", svc], { stderr: "inherit" });
3044
- Bun.spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
3110
+ spawnSync(["sudo", "systemctl", "stop", SERVICE_NAME], { stderr: "inherit" });
3111
+ spawnSync(["sudo", "systemctl", "disable", SERVICE_NAME], { stderr: "inherit" });
3112
+ spawnSync(["sudo", "rm", svc], { stderr: "inherit" });
3113
+ spawnSync(["sudo", "systemctl", "daemon-reload"], { stderr: "inherit" });
3045
3114
  }
3046
3115
  var WIN_TASK_NAME = "OMP-Wechat";
3047
3116
  function winScriptPath() {
@@ -3091,22 +3160,22 @@ function installWinTask() {
3091
3160
  mkdirSync7(join9(homedir7(), ".omp-wechat"), { recursive: true });
3092
3161
  mkdirSync7(getLogDir(), { recursive: true });
3093
3162
  writeFileSync4(winScriptPath(), generateWinScript());
3094
- Bun.spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
3095
- Bun.spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "ignore" });
3163
+ spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
3164
+ spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "ignore" });
3096
3165
  const scriptPath = winScriptPath();
3097
3166
  const taskCmd = `powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "${scriptPath}"`;
3098
- const result = Bun.spawnSync(["schtasks", "/create", "/tn", WIN_TASK_NAME, "/tr", taskCmd, "/sc", "onlogon", "/rl", "limited", "/f"], { stderr: "inherit" });
3167
+ const result = spawnSync(["schtasks", "/create", "/tn", WIN_TASK_NAME, "/tr", taskCmd, "/sc", "onlogon", "/rl", "limited", "/f"], { stderr: "inherit" });
3099
3168
  if (result.exitCode !== 0) {
3100
3169
  throw new Error("schtasks /create failed");
3101
3170
  }
3102
- const runResult = Bun.spawnSync(["schtasks", "/run", "/tn", WIN_TASK_NAME], { stderr: "inherit" });
3171
+ const runResult = spawnSync(["schtasks", "/run", "/tn", WIN_TASK_NAME], { stderr: "inherit" });
3103
3172
  if (runResult.exitCode !== 0) {
3104
3173
  logger.warn(`schtasks /run failed (exit ${runResult.exitCode}) \u2014 task will start at next logon`);
3105
3174
  }
3106
3175
  }
3107
3176
  function uninstallWinTask() {
3108
- Bun.spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
3109
- const result = Bun.spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "inherit" });
3177
+ spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
3178
+ const result = spawnSync(["schtasks", "/delete", "/tn", WIN_TASK_NAME, "/f"], { stderr: "inherit" });
3110
3179
  if (result.exitCode !== 0) {
3111
3180
  throw new Error("Failed to delete scheduled task (may not be installed)");
3112
3181
  }
@@ -3116,7 +3185,7 @@ function uninstallWinTask() {
3116
3185
  }
3117
3186
  }
3118
3187
  function winTaskExists() {
3119
- const r = Bun.spawnSync(["schtasks", "/query", "/tn", WIN_TASK_NAME, "/fo", "list"], {
3188
+ const r = spawnSync(["schtasks", "/query", "/tn", WIN_TASK_NAME, "/fo", "list"], {
3120
3189
  stdout: "ignore",
3121
3190
  stderr: "ignore"
3122
3191
  });
@@ -3171,20 +3240,25 @@ function isServiceInstalled() {
3171
3240
  // src/index.ts
3172
3241
  var bridge = null;
3173
3242
  var daemonState = null;
3174
- function wechatExtension(pi) {
3243
+ async function wechatExtension(pi) {
3175
3244
  if (pi.pi && typeof pi.setLabel === "function") {
3176
3245
  pi.setLabel("OMP-Wechat Bridge");
3177
3246
  }
3178
3247
  bridge = new WeChatBridge;
3179
- daemonState = bridge.start();
3248
+ daemonState = await bridge.start();
3180
3249
  if (daemonState.running) {
3181
3250
  logger.info("WeChat bridge started at extension load");
3182
3251
  } else {
3183
3252
  logger.debug("WeChat bridge not running, starting 30s retry", { lastError: daemonState.lastError });
3184
- setInterval(() => {
3253
+ setInterval(async () => {
3185
3254
  if (daemonState?.running)
3186
3255
  return;
3187
- daemonState = bridge.start();
3256
+ try {
3257
+ daemonState = await bridge.start();
3258
+ } catch (err) {
3259
+ logger.error("WeChat bridge restart failed:", err);
3260
+ return;
3261
+ }
3188
3262
  if (daemonState.running) {
3189
3263
  logger.info("WeChat bridge: took over from failed instance");
3190
3264
  }
@@ -3192,7 +3266,12 @@ function wechatExtension(pi) {
3192
3266
  }
3193
3267
  pi.on("session_start", async (_event, ctx) => {
3194
3268
  if (bridge && !daemonState?.running) {
3195
- daemonState = bridge.start();
3269
+ try {
3270
+ daemonState = await bridge.start();
3271
+ } catch (err) {
3272
+ logger.error("WeChat bridge start failed:", err);
3273
+ return;
3274
+ }
3196
3275
  if (daemonState.running) {
3197
3276
  ctx.ui.notify("WeChat bridge started", "info");
3198
3277
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omp-wechat",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
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",
@@ -9,7 +9,7 @@
9
9
  "README.md"
10
10
  ],
11
11
  "scripts": {
12
- "build": "bun build src/index.ts --outdir dist --target bun --external omp-legacy-pi-modules --external '@oh-my-pi/*'",
12
+ "build": "bun build src/index.ts --outdir dist --target bun --external omp-legacy-pi-modules --external '@oh-my-pi/*' --external '@earendil-works/*'",
13
13
  "typecheck": "tsc --noEmit"
14
14
  },
15
15
  "omp": {