opencode-telegram-link 1.0.0-rc.2 → 1.0.0-rc.4

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.
@@ -15781,14 +15781,11 @@ async function injectOpenCodeConfig(targetPath, config2) {
15781
15781
  chatId: config2.telegram.chatId
15782
15782
  };
15783
15783
  }
15784
+ delete existingJson["opencode-telegram-link"];
15785
+ delete existingJson["telegram-link"];
15784
15786
  const isMatch = (entry) => {
15785
- if (typeof entry === "string") {
15786
- return entry === "opencode-telegram-link" || entry.endsWith("opencode-telegram-link");
15787
- }
15788
- if (Array.isArray(entry) && typeof entry[0] === "string") {
15789
- return entry[0] === "opencode-telegram-link" || entry[0].endsWith("opencode-telegram-link");
15790
- }
15791
- return false;
15787
+ const name = typeof entry === "string" ? entry : Array.isArray(entry) && typeof entry[0] === "string" ? entry[0] : "";
15788
+ return name === "opencode-telegram-link" || name.startsWith("opencode-telegram-link") || name.includes("opencode-telegram-link") || name.includes("opencode_notification") || name.includes("telegram-link");
15792
15789
  };
15793
15790
  function mergePluginOptions(existing, updated) {
15794
15791
  const existingObj = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
@@ -15811,19 +15808,15 @@ async function injectOpenCodeConfig(targetPath, config2) {
15811
15808
  return merged;
15812
15809
  }
15813
15810
  if (Array.isArray(existingJson.plugin)) {
15814
- const index = existingJson.plugin.findIndex(isMatch);
15815
- if (index >= 0) {
15816
- const existingEntry = existingJson.plugin[index];
15817
- const existingOptions = Array.isArray(existingEntry) ? existingEntry[1] : undefined;
15818
- const pluginName = Array.isArray(existingEntry) && typeof existingEntry[0] === "string" ? existingEntry[0] : "opencode-telegram-link";
15819
- existingJson.plugin[index] = [pluginName, mergePluginOptions(existingOptions, pluginConfig)];
15820
- } else {
15821
- existingJson.plugin.push(["opencode-telegram-link", pluginConfig]);
15822
- }
15811
+ const existingEntry = existingJson.plugin.find(isMatch);
15812
+ const existingOptions = Array.isArray(existingEntry) ? existingEntry[1] : undefined;
15813
+ const mergedOptions = mergePluginOptions(existingOptions, pluginConfig);
15814
+ const cleanedPlugins = existingJson.plugin.filter((p) => !isMatch(p));
15815
+ cleanedPlugins.push(["opencode-telegram-link", mergedOptions]);
15816
+ existingJson.plugin = cleanedPlugins;
15823
15817
  } else if (Array.isArray(existingJson.plugins)) {
15824
- if (!existingJson.plugins.includes("opencode-telegram-link")) {
15825
- existingJson.plugins.push("opencode-telegram-link");
15826
- }
15818
+ const plugins = existingJson.plugins;
15819
+ existingJson.plugins = [...plugins.filter((p) => !isMatch(p)), "opencode-telegram-link"];
15827
15820
  const existingPluginMap = existingJson.plugin && typeof existingJson.plugin === "object" ? existingJson.plugin : {};
15828
15821
  existingPluginMap["opencode-telegram-link"] = mergePluginOptions(existingPluginMap["opencode-telegram-link"], pluginConfig);
15829
15822
  existingJson.plugin = existingPluginMap;
@@ -15845,14 +15838,17 @@ async function removeOpenCodeConfig(targetPath) {
15845
15838
  const existingJson = parseJsonc(existingContent);
15846
15839
  let modified = false;
15847
15840
  const isPluginMatch = (entry) => {
15848
- if (typeof entry === "string") {
15849
- return entry === "opencode-telegram-link" || entry.endsWith("opencode_notification") || entry.endsWith("opencode-telegram-link");
15850
- }
15851
- if (Array.isArray(entry) && typeof entry[0] === "string") {
15852
- return isPluginMatch(entry[0]);
15853
- }
15854
- return false;
15841
+ const name = typeof entry === "string" ? entry : Array.isArray(entry) && typeof entry[0] === "string" ? entry[0] : "";
15842
+ return name === "opencode-telegram-link" || name.startsWith("opencode-telegram-link") || name.includes("opencode-telegram-link") || name.includes("opencode_notification") || name.includes("telegram-link");
15855
15843
  };
15844
+ if ("opencode-telegram-link" in existingJson) {
15845
+ delete existingJson["opencode-telegram-link"];
15846
+ modified = true;
15847
+ }
15848
+ if ("telegram-link" in existingJson) {
15849
+ delete existingJson["telegram-link"];
15850
+ modified = true;
15851
+ }
15856
15852
  if (Array.isArray(existingJson.plugin)) {
15857
15853
  const originalLength = existingJson.plugin.length;
15858
15854
  const filtered = existingJson.plugin.filter((p) => !isPluginMatch(p));
@@ -16704,11 +16700,18 @@ class SetupError extends Error {
16704
16700
  }
16705
16701
 
16706
16702
  class AsyncPromptReader {
16703
+ input;
16707
16704
  iterator;
16708
16705
  buffer = "";
16709
16706
  constructor(input) {
16707
+ this.input = input;
16710
16708
  this.iterator = input[Symbol.asyncIterator]();
16711
16709
  }
16710
+ close() {
16711
+ if (this.input && "pause" in this.input && typeof this.input.pause === "function") {
16712
+ this.input.pause();
16713
+ }
16714
+ }
16712
16715
  async readLine() {
16713
16716
  while (true) {
16714
16717
  const newlineIndex = this.buffer.indexOf(`
@@ -16796,360 +16799,361 @@ async function runInteractiveSetup(options = {}) {
16796
16799
  const stderr = options.stderr ?? process.stderr;
16797
16800
  const fetchImpl = options.fetch ?? fetch;
16798
16801
  const reader = new AsyncPromptReader(options.stdin ?? process.stdin);
16799
- const now = options.now ?? Date.now;
16800
- const stateDirectory = options.stateDirectory ?? defaultStateDirectory();
16801
- const cwd = options.cwd ?? process.cwd();
16802
- stdout.write(`
16802
+ try {
16803
+ const now = options.now ?? Date.now;
16804
+ const stateDirectory = options.stateDirectory ?? defaultStateDirectory();
16805
+ const cwd = options.cwd ?? process.cwd();
16806
+ stdout.write(`
16803
16807
  \u250C OpenCode Telegram Notifier \u2014 Setup Wizard
16804
16808
  \u2502
16805
16809
  `);
16806
- stdout.write(`\u25C7 Language / \u8A9E\u8A00:
16810
+ stdout.write(`\u25C7 Language / \u8A9E\u8A00:
16807
16811
  `);
16808
- stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
16812
+ stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
16809
16813
  `);
16810
- stdout.write(`\u2502 2) English (en)
16814
+ stdout.write(`\u2502 2) English (en)
16811
16815
  `);
16812
- const langChoice = await reader.ask("\u2502 \u8ACB\u9078\u64C7 / Select [1]: ", stdout, "1");
16813
- const locale = langChoice === "2" || langChoice.toLowerCase() === "en" ? "en" : "zh-TW";
16814
- const isZh = locale === "zh-TW";
16815
- stdout.write(`\u2502
16816
+ const langChoice = await reader.ask("\u2502 \u8ACB\u9078\u64C7 / Select [1]: ", stdout, "1");
16817
+ const locale = langChoice === "2" || langChoice.toLowerCase() === "en" ? "en" : "zh-TW";
16818
+ const isZh = locale === "zh-TW";
16819
+ stdout.write(`\u2502
16816
16820
  `);
16817
- stdout.write(isZh ? `\u25C7 \u8ACB\u9078\u64C7\u6B64\u6A5F\u5668\u7684\u89D2\u8272 (Role):
16821
+ stdout.write(isZh ? `\u25C7 \u8ACB\u9078\u64C7\u6B64\u6A5F\u5668\u7684\u89D2\u8272 (Role):
16818
16822
  \u2502 1) \u7368\u7ACB Gateway \u6A21\u5F0F (Gateway Mode) [\u9810\u8A2D/Default] \u2014 \u64C1\u6709\u5C08\u5C6C Telegram Bot\uFF0C\u53EF\u4F9B\u672C\u6A5F\u8207\u5176\u4ED6\u7BC0\u9EDE\u9023\u7DDA
16819
16823
  \u2502 2) \u7BC0\u9EDE Agent \u6A21\u5F0F (Node Agent Mode) \u2014 \u9023\u7DDA\u81F3\u73FE\u6709\u7684 Gateway\uFF0C\u5171\u7528 Telegram Bot
16820
16824
  ` : `\u25C7 Select machine role:
16821
16825
  \u2502 1) Standalone Gateway Mode [Default] \u2014 Owns a Telegram Bot, serves local and remote nodes
16822
16826
  \u2502 2) Node Agent Mode \u2014 Connects to an existing Gateway, shares Telegram Bot
16823
16827
  `);
16824
- const roleChoice = await reader.ask(isZh ? "\u2502 \u8ACB\u9078\u64C7 / Select [1]: " : "\u2502 Select [1]: ", stdout, "1");
16825
- const isNode = roleChoice === "2" || roleChoice.toLowerCase().includes("node");
16826
- const defaultHost = hostname3() || "host";
16827
- stdout.write(`\u2502
16828
+ const roleChoice = await reader.ask(isZh ? "\u2502 \u8ACB\u9078\u64C7 / Select [1]: " : "\u2502 Select [1]: ", stdout, "1");
16829
+ const isNode = roleChoice === "2" || roleChoice.toLowerCase().includes("node");
16830
+ const defaultHost = hostname3() || "host";
16831
+ stdout.write(`\u2502
16828
16832
  `);
16829
- stdout.write(isZh ? `\u25C7 \u4E3B\u6A5F\u8B58\u5225\u6A19\u7C64 (Host Label) [\u9810\u8A2D: ${defaultHost}]:
16833
+ stdout.write(isZh ? `\u25C7 \u4E3B\u6A5F\u8B58\u5225\u6A19\u7C64 (Host Label) [\u9810\u8A2D: ${defaultHost}]:
16830
16834
  \u2502 (\u6B64\u6A19\u7C64\u5C07\u986F\u793A\u65BC Telegram \u901A\u77E5\u9802\u90E8\uFF0C\u4FBF\u65BC\u8B58\u5225\u4F86\u6E90\u4E3B\u6A5F)
16831
16835
  ` : `\u25C7 Host Label [Default: ${defaultHost}]:
16832
16836
  \u2502 (Shown in notification header to identify this machine)
16833
16837
  `);
16834
- const hostLabel = await reader.ask(isZh ? `\u2502 \u6A19\u7C64\u540D\u7A31 [${defaultHost}]: ` : `\u2502 Label [${defaultHost}]: `, stdout, defaultHost);
16835
- let configData;
16836
- let isDocker = false;
16837
- let pairing;
16838
- let api2;
16839
- let botInfo;
16840
- if (isNode) {
16841
- stdout.write(`\u2502
16838
+ const hostLabel = await reader.ask(isZh ? `\u2502 \u6A19\u7C64\u540D\u7A31 [${defaultHost}]: ` : `\u2502 Label [${defaultHost}]: `, stdout, defaultHost);
16839
+ let configData;
16840
+ let isDocker = false;
16841
+ let pairing;
16842
+ let api2;
16843
+ let botInfo;
16844
+ if (isNode) {
16845
+ stdout.write(`\u2502
16842
16846
  `);
16843
- stdout.write(isZh ? `\u25C7 \u8ACB\u8F38\u5165 Central Gateway \u7684 WebSocket \u4F4D\u5740:
16847
+ stdout.write(isZh ? `\u25C7 \u8ACB\u8F38\u5165 Central Gateway \u7684 WebSocket \u4F4D\u5740:
16844
16848
  \u2502 (\u7BC4\u4F8B: ws://192.168.1.100:42617 \u6216 wss://gateway.example.com)
16845
16849
  ` : `\u25C7 Enter Central Gateway WebSocket URL:
16846
16850
  \u2502 (Example: ws://192.168.1.100:42617 or wss://gateway.example.com)
16847
16851
  `);
16848
- const gatewayUrl = await reader.ask("\u2502 Gateway URL: ", stdout);
16849
- if (!gatewayUrl) {
16850
- stderr.write(isZh ? `\u2716 Gateway URL \u4E0D\u80FD\u70BA\u7A7A\u3002
16852
+ const gatewayUrl = await reader.ask("\u2502 Gateway URL: ", stdout);
16853
+ if (!gatewayUrl) {
16854
+ stderr.write(isZh ? `\u2716 Gateway URL \u4E0D\u80FD\u70BA\u7A7A\u3002
16851
16855
  ` : `\u2716 Gateway URL cannot be empty.
16852
16856
  `);
16853
- return 1;
16854
- }
16855
- stdout.write(`\u2502
16857
+ return 1;
16858
+ }
16859
+ stdout.write(`\u2502
16856
16860
  `);
16857
- stdout.write(isZh ? `\u25C7 \u8ACB\u8F38\u5165 Gateway \u9023\u7DDA\u91D1\u9470 (Secret Token):
16861
+ stdout.write(isZh ? `\u25C7 \u8ACB\u8F38\u5165 Gateway \u9023\u7DDA\u91D1\u9470 (Secret Token):
16858
16862
  \u2502 (\u82E5 Gateway \u7121\u9700\u91D1\u9470\u53EF\u76F4\u63A5\u6309 Enter)
16859
16863
  ` : `\u25C7 Enter Gateway Secret Token:
16860
16864
  \u2502 (Press Enter if no secret required)
16861
16865
  `);
16862
- const gatewaySecret = await reader.ask("\u2502 Secret: ", stdout, "");
16863
- configData = {
16864
- mode: "local",
16865
- role: "node",
16866
- hostLabel,
16867
- locale,
16868
- gateway: {
16869
- url: gatewayUrl,
16870
- secret: gatewaySecret || "default-secret"
16871
- },
16872
- notifications: {
16873
- completion: true,
16874
- error: true,
16875
- question: true,
16876
- permission: true,
16877
- includeChildLifecycle: false,
16878
- completionDebounceMs: 1500,
16879
- pluginBufferSize: 100
16880
- },
16881
- broker: {
16882
- host: "127.0.0.1",
16883
- port: 42617
16884
- },
16885
- interaction: {
16886
- sessionPromptTtlMinutes: 1440,
16887
- questionTtlMinutes: 30
16888
- }
16889
- };
16890
- } else {
16891
- stdout.write(`\u2502
16866
+ const gatewaySecret = await reader.ask("\u2502 Secret: ", stdout, "");
16867
+ configData = {
16868
+ mode: "local",
16869
+ role: "node",
16870
+ hostLabel,
16871
+ locale,
16872
+ gateway: {
16873
+ url: gatewayUrl,
16874
+ secret: gatewaySecret || "default-secret"
16875
+ },
16876
+ notifications: {
16877
+ completion: true,
16878
+ error: true,
16879
+ question: true,
16880
+ permission: true,
16881
+ includeChildLifecycle: false,
16882
+ completionDebounceMs: 1500,
16883
+ pluginBufferSize: 100
16884
+ },
16885
+ broker: {
16886
+ host: "127.0.0.1",
16887
+ port: 42617
16888
+ },
16889
+ interaction: {
16890
+ sessionPromptTtlMinutes: 1440,
16891
+ questionTtlMinutes: 30
16892
+ }
16893
+ };
16894
+ } else {
16895
+ stdout.write(`\u2502
16892
16896
  `);
16893
- stdout.write(isZh ? `\u25C7 \u90E8\u7F72\u6A21\u5F0F\u9078\u64C7 / Deployment Mode:
16897
+ stdout.write(isZh ? `\u25C7 \u90E8\u7F72\u6A21\u5F0F\u9078\u64C7 / Deployment Mode:
16894
16898
  \u2502 1) \u672C\u6A5F\u539F\u751F\u6A21\u5F0F (Native Mode) [\u9810\u8A2D/Default] \u2014 Broker \u4F5C\u70BA\u672C\u6A5F\u5E38\u99D0\u7A0B\u5E8F\uFF0COpenCode \u81EA\u52D5\u5728\u80CC\u666F\u62C9\u8D77
16895
16899
  \u2502 2) Docker \u5BB9\u5668\u6A21\u5F0F (Docker Container) \u2014 Broker \u9694\u96E2\u65BC Docker \u5BB9\u5668\u4E2D\u57F7\u884C
16896
16900
  ` : `\u25C7 Deployment Mode:
16897
16901
  \u2502 1) Native Mode [Default] \u2014 Broker runs as a local background process, auto-spawned by OpenCode
16898
16902
  \u2502 2) Docker Container Mode \u2014 Broker runs isolated inside a Docker container
16899
16903
  `);
16900
- const modeChoice = await reader.ask(isZh ? "\u2502 \u8ACB\u9078\u64C7 / Select [1]: " : "\u2502 Select [1]: ", stdout, "1");
16901
- isDocker = modeChoice === "2" || modeChoice.toLowerCase().includes("docker");
16902
- let botToken = "";
16903
- let attempts = 0;
16904
- while (!botInfo) {
16905
- attempts += 1;
16906
- if (attempts > 5) {
16907
- stderr.write(isZh ? `\u2716 \u8D85\u904E\u91CD\u8A66\u6B21\u6578\uFF0C\u8A2D\u5B9A\u7D42\u6B62\u3002
16904
+ const modeChoice = await reader.ask(isZh ? "\u2502 \u8ACB\u9078\u64C7 / Select [1]: " : "\u2502 Select [1]: ", stdout, "1");
16905
+ isDocker = modeChoice === "2" || modeChoice.toLowerCase().includes("docker");
16906
+ let botToken = "";
16907
+ let attempts = 0;
16908
+ while (!botInfo) {
16909
+ attempts += 1;
16910
+ if (attempts > 5) {
16911
+ stderr.write(isZh ? `\u2716 \u8D85\u904E\u91CD\u8A66\u6B21\u6578\uFF0C\u8A2D\u5B9A\u7D42\u6B62\u3002
16908
16912
  ` : `\u2716 Too many attempts. Aborted.
16909
16913
  `);
16910
- return 1;
16911
- }
16912
- stdout.write(`\u2502
16914
+ return 1;
16915
+ }
16916
+ stdout.write(`\u2502
16913
16917
  `);
16914
- if (isZh) {
16915
- stdout.write(`\u25C7 \u8ACB\u8F38\u5165\u5411 @BotFather \u7533\u8ACB\u7684 Telegram Bot Token:
16918
+ if (isZh) {
16919
+ stdout.write(`\u25C7 \u8ACB\u8F38\u5165\u5411 @BotFather \u7533\u8ACB\u7684 Telegram Bot Token:
16916
16920
  `);
16917
- stdout.write(`\u2502 (\u7BC4\u4F8B: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
16921
+ stdout.write(`\u2502 (\u7BC4\u4F8B: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
16918
16922
  `);
16919
- } else {
16920
- stdout.write(`\u25C7 Enter your Telegram Bot Token from @BotFather:
16923
+ } else {
16924
+ stdout.write(`\u25C7 Enter your Telegram Bot Token from @BotFather:
16921
16925
  `);
16922
- stdout.write(`\u2502 (Example: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
16926
+ stdout.write(`\u2502 (Example: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
16923
16927
  `);
16924
- }
16925
- botToken = await reader.ask("\u2502 Token: ", stdout);
16926
- if (!botToken) {
16927
- stdout.write(isZh ? `\u2502 \u2716 Token \u4E0D\u80FD\u70BA\u7A7A\uFF0C\u8ACB\u91CD\u65B0\u8F38\u5165\u3002
16928
+ }
16929
+ botToken = await reader.ask("\u2502 Token: ", stdout);
16930
+ if (!botToken) {
16931
+ stdout.write(isZh ? `\u2502 \u2716 Token \u4E0D\u80FD\u70BA\u7A7A\uFF0C\u8ACB\u91CD\u65B0\u8F38\u5165\u3002
16928
16932
  ` : `\u2502 \u2716 Token cannot be empty.
16929
16933
  `);
16930
- continue;
16931
- }
16932
- if (!/^\d+:[A-Za-z0-9_-]{20,}$/.test(botToken)) {
16933
- stdout.write(isZh ? `\u2502 \u2716 Token \u683C\u5F0F\u4E0D\u7B26\u5408 Telegram \u898F\u7BC4\uFF0C\u8ACB\u91CD\u65B0\u8F38\u5165\u3002
16934
+ continue;
16935
+ }
16936
+ if (!/^\d+:[A-Za-z0-9_-]{20,}$/.test(botToken)) {
16937
+ stdout.write(isZh ? `\u2502 \u2716 Token \u683C\u5F0F\u4E0D\u7B26\u5408 Telegram \u898F\u7BC4\uFF0C\u8ACB\u91CD\u65B0\u8F38\u5165\u3002
16934
16938
  ` : `\u2502 \u2716 Invalid token format.
16935
16939
  `);
16936
- continue;
16937
- }
16938
- stdout.write(isZh ? `\u2502 \u280B \u6B63\u5728\u5411 Telegram \u9A57\u8B49 Bot Token...
16940
+ continue;
16941
+ }
16942
+ stdout.write(isZh ? `\u2502 \u280B \u6B63\u5728\u5411 Telegram \u9A57\u8B49 Bot Token...
16939
16943
  ` : `\u2502 \u280B Verifying Bot Token with Telegram...
16940
16944
  `);
16941
- api2 = new TelegramBotApi({ token: botToken, fetch: fetchImpl });
16942
- try {
16943
- botInfo = await api2.getMe();
16944
- const botName2 = botInfo.username ? `@${botInfo.username}` : `bot ${botInfo.id}`;
16945
- stdout.write(isZh ? `\u2502 \u2714 \u9023\u7DDA\u6210\u529F\uFF01\u5DF2\u8FA8\u8B58 Bot: ${botName2} (ID: ${botInfo.id})
16945
+ api2 = new TelegramBotApi({ token: botToken, fetch: fetchImpl });
16946
+ try {
16947
+ botInfo = await api2.getMe();
16948
+ const botName2 = botInfo.username ? `@${botInfo.username}` : `bot ${botInfo.id}`;
16949
+ stdout.write(isZh ? `\u2502 \u2714 \u9023\u7DDA\u6210\u529F\uFF01\u5DF2\u8FA8\u8B58 Bot: ${botName2} (ID: ${botInfo.id})
16946
16950
  ` : `\u2502 \u2714 Connected! Identified Bot: ${botName2} (ID: ${botInfo.id})
16947
16951
  `);
16948
- } catch (err) {
16949
- const errMsg = err instanceof Error ? err.message : "connection failed";
16950
- stdout.write(isZh ? `\u2502 \u2716 Token \u9A57\u8B49\u5931\u6557 (${errMsg})\uFF0C\u8ACB\u78BA\u8A8D\u5F8C\u91CD\u8A66\u3002
16952
+ } catch (err) {
16953
+ const errMsg = err instanceof Error ? err.message : "connection failed";
16954
+ stdout.write(isZh ? `\u2502 \u2716 Token \u9A57\u8B49\u5931\u6557 (${errMsg})\uFF0C\u8ACB\u78BA\u8A8D\u5F8C\u91CD\u8A66\u3002
16951
16955
  ` : `\u2502 \u2716 Verification failed (${errMsg}). Please retry.
16952
16956
  `);
16957
+ }
16953
16958
  }
16954
- }
16955
- api2 = new TelegramBotApi({ token: botToken, fetch: fetchImpl });
16956
- const nonce = createPairingNonce();
16957
- const botName = botInfo.username ? `@${botInfo.username}` : `Bot (ID: ${botInfo.id})`;
16958
- stdout.write(`\u2502
16959
+ api2 = new TelegramBotApi({ token: botToken, fetch: fetchImpl });
16960
+ const nonce = createPairingNonce();
16961
+ const botName = botInfo.username ? `@${botInfo.username}` : `Bot (ID: ${botInfo.id})`;
16962
+ stdout.write(`\u2502
16959
16963
  `);
16960
- if (isZh) {
16961
- stdout.write(`\u25C7 [\u8EAB\u5206\u9A57\u8B49\u914D\u5C0D]
16964
+ if (isZh) {
16965
+ stdout.write(`\u25C7 [\u8EAB\u5206\u9A57\u8B49\u914D\u5C0D]
16962
16966
  `);
16963
- stdout.write(`\u2502 \u8ACB\u5728 Telegram \u6253\u958B\u8207 ${botName} \u7684\u79C1\u804A\u8996\u7A97\uFF0C\u4E26\u767C\u9001\u6B64\u9A57\u8B49\u78BC\uFF1A
16967
+ stdout.write(`\u2502 \u8ACB\u5728 Telegram \u6253\u958B\u8207 ${botName} \u7684\u79C1\u804A\u8996\u7A97\uFF0C\u4E26\u767C\u9001\u6B64\u9A57\u8B49\u78BC\uFF1A
16964
16968
  `);
16965
- stdout.write(`\u2502
16969
+ stdout.write(`\u2502
16966
16970
  \u2502 \uD83D\uDC49 ${nonce}
16967
16971
  \u2502
16968
16972
  `);
16969
- stdout.write(`\u2502 \u280B \u7B49\u5F85 Telegram \u79C1\u8A0A\u4E2D...
16973
+ stdout.write(`\u2502 \u280B \u7B49\u5F85 Telegram \u79C1\u8A0A\u4E2D...
16970
16974
  `);
16971
- } else {
16972
- stdout.write(`\u25C7 [Identity Pairing]
16975
+ } else {
16976
+ stdout.write(`\u25C7 [Identity Pairing]
16973
16977
  `);
16974
- stdout.write(`\u2502 Open a private chat with ${botName} on Telegram and send this code:
16978
+ stdout.write(`\u2502 Open a private chat with ${botName} on Telegram and send this code:
16975
16979
  `);
16976
- stdout.write(`\u2502
16980
+ stdout.write(`\u2502
16977
16981
  \u2502 \uD83D\uDC49 ${nonce}
16978
16982
  \u2502
16979
16983
  `);
16980
- stdout.write(`\u2502 \u280B Waiting for Telegram private message...
16984
+ stdout.write(`\u2502 \u280B Waiting for Telegram private message...
16981
16985
  `);
16982
- }
16983
- const expiresAt = now() + 120000;
16984
- try {
16985
- pairing = await waitForPairingMessage(api2, {
16986
- nonce,
16987
- expiresAt,
16988
- now,
16989
- pollTimeoutSeconds: 5
16990
- });
16991
- } catch {
16992
- stdout.write(isZh ? `\u2502 \u2716 \u914D\u5C0D\u8D85\u6642\u6216\u672A\u6536\u5230\u6709\u6548\u8A0A\u606F\uFF0C\u8A2D\u5B9A\u4E2D\u6B62\u3002
16986
+ }
16987
+ const expiresAt = now() + 120000;
16988
+ try {
16989
+ pairing = await waitForPairingMessage(api2, {
16990
+ nonce,
16991
+ expiresAt,
16992
+ now,
16993
+ pollTimeoutSeconds: 5
16994
+ });
16995
+ } catch {
16996
+ stdout.write(isZh ? `\u2502 \u2716 \u914D\u5C0D\u8D85\u6642\u6216\u672A\u6536\u5230\u6709\u6548\u8A0A\u606F\uFF0C\u8A2D\u5B9A\u4E2D\u6B62\u3002
16993
16997
  ` : `\u2502 \u2716 Pairing timed out or no valid message received. Aborted.
16994
16998
  `);
16995
- return 1;
16996
- }
16997
- stdout.write(isZh ? `\u2502 \u2714 \u6536\u5230\u9A57\u8B49\u8A0A\u606F\uFF01\u4F86\u81EA Telegram \u7528\u6236 (ID: ${pairing.userId}, Chat: ${pairing.chatId})
16999
+ return 1;
17000
+ }
17001
+ stdout.write(isZh ? `\u2502 \u2714 \u6536\u5230\u9A57\u8B49\u8A0A\u606F\uFF01\u4F86\u81EA Telegram \u7528\u6236 (ID: ${pairing.userId}, Chat: ${pairing.chatId})
16998
17002
  ` : `\u2502 \u2714 Received pairing message! Telegram User (ID: ${pairing.userId}, Chat: ${pairing.chatId})
16999
17003
  `);
17000
- const confirmBind = await reader.ask(isZh ? "\u2502 \u662F\u5426\u5C07\u6B64 Telegram \u5E33\u865F\u7D81\u5B9A\u70BA OpenCode \u7BA1\u7406\u54E1\uFF1F [Y/n]: " : "\u2502 Authorize this Telegram user for OpenCode notifications & replies? [Y/n]: ", stdout, "Y");
17001
- if (confirmBind.toLowerCase() === "n" || confirmBind.toLowerCase() === "no") {
17002
- stdout.write(isZh ? `\u2502 \u2716 \u5DF2\u53D6\u6D88\u7D81\u5B9A\u3002
17004
+ const confirmBind = await reader.ask(isZh ? "\u2502 \u662F\u5426\u5C07\u6B64 Telegram \u5E33\u865F\u7D81\u5B9A\u70BA OpenCode \u7BA1\u7406\u54E1\uFF1F [Y/n]: " : "\u2502 Authorize this Telegram user for OpenCode notifications & replies? [Y/n]: ", stdout, "Y");
17005
+ if (confirmBind.toLowerCase() === "n" || confirmBind.toLowerCase() === "no") {
17006
+ stdout.write(isZh ? `\u2502 \u2716 \u5DF2\u53D6\u6D88\u7D81\u5B9A\u3002
17003
17007
  ` : `\u2502 \u2716 Pairing cancelled.
17004
17008
  `);
17005
- return 1;
17006
- }
17007
- const tokenFile = join5(stateDirectory, "telegram-bot-token");
17008
- await writePrivateTokenFile(stateDirectory, tokenFile, botToken);
17009
- const identityFile = join5(stateDirectory, "telegram-identity.json");
17010
- await writeFile3(identityFile, `${JSON.stringify({ userId: pairing.userId, chatId: pairing.chatId, tokenFile, locale }, null, 2)}
17009
+ return 1;
17010
+ }
17011
+ const tokenFile = join5(stateDirectory, "telegram-bot-token");
17012
+ await writePrivateTokenFile(stateDirectory, tokenFile, botToken);
17013
+ const identityFile = join5(stateDirectory, "telegram-identity.json");
17014
+ await writeFile3(identityFile, `${JSON.stringify({ userId: pairing.userId, chatId: pairing.chatId, tokenFile, locale }, null, 2)}
17011
17015
  `, "utf8");
17012
- stdout.write(isZh ? `\u2502 \u2714 \u5B89\u5168 Token \u6A94\u6848\u5DF2\u5132\u5B58 (${tokenFile})
17016
+ stdout.write(isZh ? `\u2502 \u2714 \u5B89\u5168 Token \u6A94\u6848\u5DF2\u5132\u5B58 (${tokenFile})
17013
17017
  ` : `\u2502 \u2714 Secure token file saved (${tokenFile})
17014
17018
  `);
17015
- configData = {
17016
- mode: "local",
17017
- role: "gateway",
17018
- hostLabel,
17019
- locale,
17020
- telegram: {
17021
- tokenFile,
17022
- userId: pairing.userId,
17023
- chatId: pairing.chatId
17024
- },
17025
- notifications: {
17026
- completion: true,
17027
- error: true,
17028
- question: true,
17029
- permission: true,
17030
- includeChildLifecycle: false,
17031
- completionDebounceMs: 1500,
17032
- pluginBufferSize: 100
17033
- },
17034
- broker: {
17035
- host: "127.0.0.1",
17036
- port: 42617
17037
- },
17038
- interaction: {
17039
- sessionPromptTtlMinutes: 1440,
17040
- questionTtlMinutes: 30
17041
- }
17042
- };
17043
- }
17044
- stdout.write(`\u2502
17019
+ configData = {
17020
+ mode: "local",
17021
+ role: "gateway",
17022
+ hostLabel,
17023
+ locale,
17024
+ telegram: {
17025
+ tokenFile,
17026
+ userId: pairing.userId,
17027
+ chatId: pairing.chatId
17028
+ },
17029
+ notifications: {
17030
+ completion: true,
17031
+ error: true,
17032
+ question: true,
17033
+ permission: true,
17034
+ includeChildLifecycle: false,
17035
+ completionDebounceMs: 1500,
17036
+ pluginBufferSize: 100
17037
+ },
17038
+ broker: {
17039
+ host: "127.0.0.1",
17040
+ port: 42617
17041
+ },
17042
+ interaction: {
17043
+ sessionPromptTtlMinutes: 1440,
17044
+ questionTtlMinutes: 30
17045
+ }
17046
+ };
17047
+ }
17048
+ stdout.write(`\u2502
17045
17049
  `);
17046
- const discovered = await discoverOpenCodeConfigFiles(cwd);
17047
- const existingConfigs = discovered.filter((d) => d.exists);
17048
- const fallback = discovered.find((d) => !d.isWorkspace) ?? discovered[0];
17049
- const targets = existingConfigs.length > 0 ? existingConfigs : fallback ? [fallback] : [];
17050
- if (targets.length > 0) {
17051
- const descList = targets.map((t) => `${t.path} [${t.exists ? isZh ? "\u5DF2\u5B58\u5728" : "existing" : isZh ? "\u5C07\u81EA\u52D5\u5EFA\u7ACB" : "will create"}]`).join(", ");
17052
- stdout.write(isZh ? `\u25C7 \u5075\u6E2C\u5230 OpenCode \u8A2D\u5B9A\u6A94 (${descList})
17050
+ const discovered = await discoverOpenCodeConfigFiles(cwd);
17051
+ const existingConfigs = discovered.filter((d) => d.exists);
17052
+ const fallback = discovered.find((d) => !d.isWorkspace) ?? discovered[0];
17053
+ const targets = existingConfigs.length > 0 ? existingConfigs : fallback ? [fallback] : [];
17054
+ if (targets.length > 0) {
17055
+ const descList = targets.map((t) => `${t.path} [${t.exists ? isZh ? "\u5DF2\u5B58\u5728" : "existing" : isZh ? "\u5C07\u81EA\u52D5\u5EFA\u7ACB" : "will create"}]`).join(", ");
17056
+ stdout.write(isZh ? `\u25C7 \u5075\u6E2C\u5230 OpenCode \u8A2D\u5B9A\u6A94 (${descList})
17053
17057
  ` : `\u25C7 Discovered OpenCode config file(s) (${descList})
17054
17058
  `);
17055
- const autoWrite = await reader.ask(isZh ? "\u2502 \u662F\u5426\u81EA\u52D5\u5BEB\u5165\u5916\u639B\u8A2D\u5B9A\uFF1F [Y/n]: " : "\u2502 Automatically update OpenCode config file(s)? [Y/n]: ", stdout, "Y");
17056
- if (autoWrite.toLowerCase() !== "n" && autoWrite.toLowerCase() !== "no") {
17057
- for (const target of targets) {
17058
- try {
17059
- const { backupPath } = await injectOpenCodeConfig(target.path, configData);
17060
- stdout.write(isZh ? `\u2502 \u2714 OpenCode \u8A2D\u5B9A\u6A94\u5DF2\u66F4\u65B0: ${target.path}${backupPath ? ` (\u5099\u4EFD\u65BC ${backupPath})` : ""}
17059
+ const autoWrite = await reader.ask(isZh ? "\u2502 \u662F\u5426\u81EA\u52D5\u5BEB\u5165\u5916\u639B\u8A2D\u5B9A\uFF1F [Y/n]: " : "\u2502 Automatically update OpenCode config file(s)? [Y/n]: ", stdout, "Y");
17060
+ if (autoWrite.toLowerCase() !== "n" && autoWrite.toLowerCase() !== "no") {
17061
+ for (const target of targets) {
17062
+ try {
17063
+ const { backupPath } = await injectOpenCodeConfig(target.path, configData);
17064
+ stdout.write(isZh ? `\u2502 \u2714 OpenCode \u8A2D\u5B9A\u6A94\u5DF2\u66F4\u65B0: ${target.path}${backupPath ? ` (\u5099\u4EFD\u65BC ${backupPath})` : ""}
17061
17065
  ` : `\u2502 \u2714 OpenCode config updated: ${target.path}${backupPath ? ` (backup at ${backupPath})` : ""}
17062
17066
  `);
17063
- } catch (err) {
17064
- const errMsg = err instanceof Error ? err.message : "failed to write config";
17065
- stdout.write(isZh ? `\u2502 \u2716 \u8A2D\u5B9A\u6A94\u5BEB\u5165\u5931\u6557 (${target.path}): ${errMsg}
17067
+ } catch (err) {
17068
+ const errMsg = err instanceof Error ? err.message : "failed to write config";
17069
+ stdout.write(isZh ? `\u2502 \u2716 \u8A2D\u5B9A\u6A94\u5BEB\u5165\u5931\u6557 (${target.path}): ${errMsg}
17066
17070
  ` : `\u2502 \u2716 Failed to write config (${target.path}): ${errMsg}
17067
17071
  `);
17072
+ }
17068
17073
  }
17069
- }
17070
- } else {
17071
- stdout.write(isZh ? `\u2502 \u8ACB\u624B\u52D5\u5C07\u4E0B\u5217\u8A2D\u5B9A\u52A0\u5165\u60A8\u7684 opencode.json:
17074
+ } else {
17075
+ stdout.write(isZh ? `\u2502 \u8ACB\u624B\u52D5\u5C07\u4E0B\u5217\u8A2D\u5B9A\u52A0\u5165\u60A8\u7684 opencode.json:
17072
17076
  ` : `\u2502 Please add the following configuration to your opencode.json:
17073
17077
  `);
17074
- stdout.write(`\u2502
17078
+ stdout.write(`\u2502
17075
17079
  ${generatePluginConfigSnippet(configData)}
17076
17080
  \u2502
17077
17081
  `);
17082
+ }
17078
17083
  }
17079
- }
17080
- let dockerFailed = false;
17081
- if (isDocker) {
17082
- stdout.write(`\u2502
17084
+ let dockerFailed = false;
17085
+ if (isDocker) {
17086
+ stdout.write(`\u2502
17083
17087
  `);
17084
- const autoStartDocker = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u7ACB\u5373\u5728\u80CC\u666F\u81EA\u52D5\u5EFA\u7F6E\u4E26\u555F\u52D5 Docker Broker \u5BB9\u5668\uFF1F [Y/n]: " : "\u25C7 Build and start Docker Broker container in background now? [Y/n]: ", stdout, "Y");
17085
- if (autoStartDocker.toLowerCase() !== "n" && autoStartDocker.toLowerCase() !== "no") {
17086
- stdout.write(isZh ? `\u2502 \u280B \u6B63\u5728\u5EFA\u7F6E\u4E26\u555F\u52D5 Docker \u5BB9\u5668 (docker compose up -d --build)...
17088
+ const autoStartDocker = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u7ACB\u5373\u5728\u80CC\u666F\u81EA\u52D5\u5EFA\u7F6E\u4E26\u555F\u52D5 Docker Broker \u5BB9\u5668\uFF1F [Y/n]: " : "\u25C7 Build and start Docker Broker container in background now? [Y/n]: ", stdout, "Y");
17089
+ if (autoStartDocker.toLowerCase() !== "n" && autoStartDocker.toLowerCase() !== "no") {
17090
+ stdout.write(isZh ? `\u2502 \u280B \u6B63\u5728\u5EFA\u7F6E\u4E26\u555F\u52D5 Docker \u5BB9\u5668 (docker compose up -d --build)...
17087
17091
  ` : `\u2502 \u280B Building and starting Docker container (docker compose up -d --build)...
17088
17092
  `);
17089
- try {
17090
- const dockerCmd = process.platform === "win32" ? "docker.exe" : "docker";
17091
- const composeCtx = resolveDockerComposeContext(cwd);
17092
- if (!composeCtx) {
17093
- throw new Error("Could not find docker-compose.yml in project or package directory");
17094
- }
17095
- const result = Bun.spawnSync([
17096
- dockerCmd,
17097
- "compose",
17098
- "-f",
17099
- composeCtx.composeFile,
17100
- "--project-directory",
17101
- composeCtx.projectDir,
17102
- "up",
17103
- "-d",
17104
- "--build"
17105
- ], {
17106
- cwd: composeCtx.projectDir,
17107
- env: {
17108
- ...process.env,
17109
- HOME: process.env.HOME || process.env.USERPROFILE || ""
17093
+ try {
17094
+ const dockerCmd = process.platform === "win32" ? "docker.exe" : "docker";
17095
+ const composeCtx = resolveDockerComposeContext(cwd);
17096
+ if (!composeCtx) {
17097
+ throw new Error("Could not find docker-compose.yml in project or package directory");
17110
17098
  }
17111
- });
17112
- if (result.exitCode === 0) {
17113
- stdout.write(isZh ? `\u2502 \u2714 Docker Broker \u5BB9\u5668\u5DF2\u6210\u529F\u5728\u80CC\u666F\u555F\u52D5\uFF01
17099
+ const result = Bun.spawnSync([
17100
+ dockerCmd,
17101
+ "compose",
17102
+ "-f",
17103
+ composeCtx.composeFile,
17104
+ "--project-directory",
17105
+ composeCtx.projectDir,
17106
+ "up",
17107
+ "-d",
17108
+ "--build"
17109
+ ], {
17110
+ cwd: composeCtx.projectDir,
17111
+ env: {
17112
+ ...process.env,
17113
+ HOME: process.env.HOME || process.env.USERPROFILE || ""
17114
+ }
17115
+ });
17116
+ if (result.exitCode === 0) {
17117
+ stdout.write(isZh ? `\u2502 \u2714 Docker Broker \u5BB9\u5668\u5DF2\u6210\u529F\u5728\u80CC\u666F\u555F\u52D5\uFF01
17114
17118
  ` : `\u2502 \u2714 Docker Broker container started in background!
17115
17119
  `);
17116
- } else {
17117
- dockerFailed = true;
17118
- const stderr2 = result.stderr ? new TextDecoder().decode(result.stderr).trim() : "";
17119
- stdout.write(isZh ? `\u2502 \u2716 Docker \u81EA\u52D5\u555F\u52D5\u672A\u6210\u529F${stderr2 ? ` (${stderr2})` : ""}\uFF08\u8ACB\u78BA\u8A8D Docker Desktop \u662F\u5426\u904B\u884C\u4E2D\uFF09\u3002
17120
+ } else {
17121
+ dockerFailed = true;
17122
+ const stderr2 = result.stderr ? new TextDecoder().decode(result.stderr).trim() : "";
17123
+ stdout.write(isZh ? `\u2502 \u2716 Docker \u81EA\u52D5\u555F\u52D5\u672A\u6210\u529F${stderr2 ? ` (${stderr2})` : ""}\uFF08\u8ACB\u78BA\u8A8D Docker Desktop \u662F\u5426\u904B\u884C\u4E2D\uFF09\u3002
17120
17124
  ` : `\u2502 \u2716 Docker start failed${stderr2 ? ` (${stderr2})` : ""} (please check if Docker is running).
17121
17125
  `);
17122
- stdout.write(isZh ? `\u2502 \u60A8\u53EF\u65BC\u7A0D\u5F8C\u624B\u52D5\u57F7\u884C: docker compose -f "${composeCtx.composeFile}" up -d
17126
+ stdout.write(isZh ? `\u2502 \u60A8\u53EF\u65BC\u7A0D\u5F8C\u624B\u52D5\u57F7\u884C: docker compose -f "${composeCtx.composeFile}" up -d
17123
17127
  ` : `\u2502 You can manually run later: docker compose -f "${composeCtx.composeFile}" up -d
17124
17128
  `);
17125
- }
17126
- } catch (err) {
17127
- dockerFailed = true;
17128
- const msg = err instanceof Error ? err.message : String(err);
17129
- stdout.write(isZh ? `\u2502 \u2716 Docker \u555F\u52D5\u5931\u6557: ${msg}\uFF0C\u60A8\u53EF\u65BC\u5B89\u88DD/\u555F\u52D5 Docker \u5F8C\u624B\u52D5\u57F7\u884C docker compose up -d
17129
+ }
17130
+ } catch (err) {
17131
+ dockerFailed = true;
17132
+ const msg = err instanceof Error ? err.message : String(err);
17133
+ stdout.write(isZh ? `\u2502 \u2716 Docker \u555F\u52D5\u5931\u6557: ${msg}\uFF0C\u60A8\u53EF\u65BC\u5B89\u88DD/\u555F\u52D5 Docker \u5F8C\u624B\u52D5\u57F7\u884C docker compose up -d
17130
17134
  ` : `\u2502 \u2716 Docker startup failed: ${msg}. You can run 'docker compose up -d' after installing/starting Docker.
17131
17135
  `);
17132
- }
17133
- } else {
17134
- stdout.write(isZh ? `\u2502 \u60A8\u53EF\u65BC\u7A0D\u5F8C\u624B\u52D5\u555F\u52D5\u5BB9\u5668: docker compose up -d
17136
+ }
17137
+ } else {
17138
+ stdout.write(isZh ? `\u2502 \u60A8\u53EF\u65BC\u7A0D\u5F8C\u624B\u52D5\u555F\u52D5\u5BB9\u5668: docker compose up -d
17135
17139
  ` : `\u2502 You can manually start the container later: docker compose up -d
17136
17140
  `);
17137
- }
17138
- } else {
17139
- stdout.write(`\u2502
17141
+ }
17142
+ } else {
17143
+ stdout.write(`\u2502
17140
17144
  `);
17141
- stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F\u539F\u751F\u6A21\u5F0F\u5DF2\u8A2D\u5B9A\u5B8C\u6210\uFF01\u7576 OpenCode \u555F\u52D5\u6642\u5C07\u81EA\u52D5\u5728\u80CC\u666F\u63A5\u7BA1 Broker\u3002
17145
+ stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F\u539F\u751F\u6A21\u5F0F\u5DF2\u8A2D\u5B9A\u5B8C\u6210\uFF01\u7576 OpenCode \u555F\u52D5\u6642\u5C07\u81EA\u52D5\u5728\u80CC\u666F\u63A5\u7BA1 Broker\u3002
17142
17146
  ` : `\u2502 \u2714 Native mode configured! OpenCode will automatically manage Broker in background.
17143
17147
  `);
17144
- }
17145
- if (!isNode && pairing && api2) {
17146
- stdout.write(`\u2502
17148
+ }
17149
+ if (!isNode && pairing && api2) {
17150
+ stdout.write(`\u2502
17147
17151
  `);
17148
- const sendTest = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u767C\u9001\u6E2C\u8A66\u901A\u77E5\u5230\u60A8\u7684 Telegram\uFF1F [Y/n]: " : "\u25C7 Send a welcome test notification to your Telegram now? [Y/n]: ", stdout, "Y");
17149
- if (sendTest.toLowerCase() !== "n" && sendTest.toLowerCase() !== "no") {
17150
- try {
17151
- const botDisplayName = botInfo?.username ? `@${botInfo.username}` : `Bot (ID: ${botInfo?.id})`;
17152
- const welcomeText = isZh ? `\uD83C\uDF89 <b>OpenCode Telegram Notifier \u8A2D\u5B9A\u6210\u529F\uFF01</b>
17152
+ const sendTest = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u767C\u9001\u6E2C\u8A66\u901A\u77E5\u5230\u60A8\u7684 Telegram\uFF1F [Y/n]: " : "\u25C7 Send a welcome test notification to your Telegram now? [Y/n]: ", stdout, "Y");
17153
+ if (sendTest.toLowerCase() !== "n" && sendTest.toLowerCase() !== "no") {
17154
+ try {
17155
+ const botDisplayName = botInfo?.username ? `@${botInfo.username}` : `Bot (ID: ${botInfo?.id})`;
17156
+ const welcomeText = isZh ? `\uD83C\uDF89 <b>OpenCode Telegram Notifier \u8A2D\u5B9A\u6210\u529F\uFF01</b>
17153
17157
 
17154
17158
  \u5DF2\u6210\u529F\u7D81\u5B9A\u4E3B\u6A5F [${hostLabel}] \u8207 Telegram\u3002
17155
17159
  \u7576 OpenCode \u4EFB\u52D9\u5B8C\u6210\u3001\u767C\u751F\u7570\u5E38\u6216\u9700\u8981\u56DE\u8986\u6642\uFF0C\u60A8\u5C07\u5728\u6B64\u6536\u5230\u5373\u6642\u901A\u77E5\u3002
@@ -17162,38 +17166,41 @@ You will receive notifications here when sessions finish or require input.
17162
17166
 
17163
17167
  \u2022 Bot: ${botDisplayName}
17164
17168
  \u2022 Authorized User ID: <code>${pairing.userId}</code>`;
17165
- await api2.sendMessage({
17166
- chatId: pairing.chatId,
17167
- text: welcomeText,
17168
- parseMode: "HTML"
17169
- });
17170
- stdout.write(isZh ? `\u2502 \u2714 \u5DF2\u6210\u529F\u767C\u9001\u6E2C\u8A66\u901A\u77E5\u5230\u60A8\u7684 Telegram\uFF01\u8ACB\u6AA2\u67E5\u624B\u6A5F\u8A0A\u606F\u3002
17169
+ await api2.sendMessage({
17170
+ chatId: pairing.chatId,
17171
+ text: welcomeText,
17172
+ parseMode: "HTML"
17173
+ });
17174
+ stdout.write(isZh ? `\u2502 \u2714 \u5DF2\u6210\u529F\u767C\u9001\u6E2C\u8A66\u901A\u77E5\u5230\u60A8\u7684 Telegram\uFF01\u8ACB\u6AA2\u67E5\u624B\u6A5F\u8A0A\u606F\u3002
17171
17175
  ` : `\u2502 \u2714 Test notification sent to your Telegram! Please check your messages.
17172
17176
  `);
17173
- } catch (err) {
17174
- const errMsg = err instanceof Error ? err.message : "send failed";
17175
- stdout.write(isZh ? `\u2502 \u2716 \u6E2C\u8A66\u901A\u77E5\u767C\u9001\u5931\u6557: ${errMsg}
17177
+ } catch (err) {
17178
+ const errMsg = err instanceof Error ? err.message : "send failed";
17179
+ stdout.write(isZh ? `\u2502 \u2716 \u6E2C\u8A66\u901A\u77E5\u767C\u9001\u5931\u6557: ${errMsg}
17176
17180
  ` : `\u2502 \u2716 Failed to send test notification: ${errMsg}
17177
17181
  `);
17182
+ }
17178
17183
  }
17179
17184
  }
17180
- }
17181
- stdout.write(`\u2502
17185
+ stdout.write(`\u2502
17182
17186
  `);
17183
- if (dockerFailed) {
17184
- stdout.write(isZh ? `\u2514 \u26A0 \u8A2D\u5B9A\u6A94\u5DF2\u5BEB\u5165\uFF0C\u4F46 Docker \u5BB9\u5668\u555F\u52D5\u5931\u6557\u3002\u8ACB\u78BA\u8A8D Docker \u670D\u52D9\u904B\u884C\u5F8C\u624B\u52D5\u555F\u52D5\u5BB9\u5668\u3002
17187
+ if (dockerFailed) {
17188
+ stdout.write(isZh ? `\u2514 \u26A0 \u8A2D\u5B9A\u6A94\u5DF2\u5BEB\u5165\uFF0C\u4F46 Docker \u5BB9\u5668\u555F\u52D5\u5931\u6557\u3002\u8ACB\u78BA\u8A8D Docker \u670D\u52D9\u904B\u884C\u5F8C\u624B\u52D5\u555F\u52D5\u5BB9\u5668\u3002
17185
17189
 
17186
17190
  ` : `\u2514 \u26A0 Configuration written, but Docker container failed to start. Please ensure Docker is running and start the container manually.
17187
17191
 
17188
17192
  `);
17189
- return 1;
17190
- }
17191
- stdout.write(isZh ? `\u2514 \uD83C\uDF89 \u5B89\u88DD\u8A2D\u5B9A\u5B8C\u6210\uFF01\u60A8\u73FE\u5728\u53EF\u4EE5\u56DE\u5230 OpenCode \u958B\u59CB\u5DE5\u4F5C\u3002
17193
+ return 1;
17194
+ }
17195
+ stdout.write(isZh ? `\u2514 \uD83C\uDF89 \u5B89\u88DD\u8A2D\u5B9A\u5B8C\u6210\uFF01\u60A8\u73FE\u5728\u53EF\u4EE5\u56DE\u5230 OpenCode \u958B\u59CB\u5DE5\u4F5C\u3002
17192
17196
 
17193
17197
  ` : `\u2514 \uD83C\uDF89 Setup completed! You can now return to OpenCode and start working.
17194
17198
 
17195
17199
  `);
17196
- return 0;
17200
+ return 0;
17201
+ } finally {
17202
+ reader.close();
17203
+ }
17197
17204
  }
17198
17205
  async function runSetupCli(options = {}) {
17199
17206
  const argv = options.argv ?? process.argv.slice(2);
@@ -17975,7 +17982,7 @@ function rejectionHash(updateId, reason) {
17975
17982
  import { randomUUID as randomUUID4 } from "crypto";
17976
17983
 
17977
17984
  // src/version.ts
17978
- var PACKAGE_VERSION = "1.0.0-rc.2";
17985
+ var PACKAGE_VERSION = "1.0.0-rc.4";
17979
17986
 
17980
17987
  // src/telegram/commands.ts
17981
17988
  function isSlashCommand(text) {
@@ -21694,161 +21701,170 @@ async function runInteractiveUninstall(options = {}) {
21694
21701
  const stdout = options.stdout ?? process.stdout;
21695
21702
  const stderr = options.stderr ?? process.stderr;
21696
21703
  const reader = new AsyncPromptReader(options.stdin ?? process.stdin);
21697
- const stateDirectory = options.stateDirectory ?? defaultStateDirectory();
21698
- const cwd = options.cwd ?? process.cwd();
21699
- const fetchImpl = options.fetch ?? fetch;
21700
- stdout.write(`
21704
+ try {
21705
+ const stateDirectory = options.stateDirectory ?? defaultStateDirectory();
21706
+ const cwd = options.cwd ?? process.cwd();
21707
+ const fetchImpl = options.fetch ?? fetch;
21708
+ stdout.write(`
21701
21709
  \u250C OpenCode Telegram Notifier \u2014 Uninstaller / \u79FB\u9664\u7CBE\u9748
21702
21710
  \u2502
21703
21711
  `);
21704
- stdout.write(`\u25C7 Language / \u8A9E\u8A00:
21712
+ stdout.write(`\u25C7 Language / \u8A9E\u8A00:
21705
21713
  `);
21706
- stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
21714
+ stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
21707
21715
  `);
21708
- stdout.write(`\u2502 2) English (en)
21716
+ stdout.write(`\u2502 2) English (en)
21709
21717
  `);
21710
- const langChoice = await reader.ask("\u2502 \u8ACB\u9078\u64C7 / Select [1]: ", stdout, "1");
21711
- const locale = langChoice === "2" || langChoice.toLowerCase() === "en" ? "en" : "zh-TW";
21712
- const isZh = locale === "zh-TW";
21713
- stdout.write(`\u2502
21718
+ const langChoice = await reader.ask("\u2502 \u8ACB\u9078\u64C7 / Select [1]: ", stdout, "1");
21719
+ const locale = langChoice === "2" || langChoice.toLowerCase() === "en" ? "en" : "zh-TW";
21720
+ const isZh = locale === "zh-TW";
21721
+ stdout.write(`\u2502
21714
21722
  `);
21715
- const confirm = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u78BA\u8A8D\u8981\u89E3\u9664\u5B89\u88DD OpenCode Telegram Notifier\uFF1F [y/N]: " : "\u25C7 Are you sure you want to uninstall OpenCode Telegram Notifier? [y/N]: ", stdout, "N");
21716
- if (confirm.toLowerCase() !== "y" && confirm.toLowerCase() !== "yes") {
21717
- stdout.write(isZh ? `\u2502 \u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002
21723
+ const confirm = await reader.ask(isZh ? "\u25C7 \u662F\u5426\u78BA\u8A8D\u8981\u89E3\u9664\u5B89\u88DD OpenCode Telegram Notifier\uFF1F [y/N]: " : "\u25C7 Are you sure you want to uninstall OpenCode Telegram Notifier? [y/N]: ", stdout, "N");
21724
+ if (confirm.toLowerCase() !== "y" && confirm.toLowerCase() !== "yes") {
21725
+ stdout.write(isZh ? `\u2502 \u64CD\u4F5C\u5DF2\u53D6\u6D88\u3002
21718
21726
  \u2514 \uD83D\uDC4B \u7D50\u675F\u3002
21719
21727
 
21720
21728
  ` : `\u2502 Aborted.
21721
21729
  \u2514 \uD83D\uDC4B Done.
21722
21730
 
21723
21731
  `);
21724
- return 0;
21725
- }
21726
- stdout.write(`\u2502
21732
+ return 0;
21733
+ }
21734
+ stdout.write(`\u2502
21727
21735
  `);
21728
- stdout.write(isZh ? `\u25C7 [1/4] \u6B63\u5728\u6AA2\u67E5\u4E26\u505C\u6B62\u57F7\u884C\u4E2D\u7684 Broker (\u672C\u6A5F\u7A0B\u5E8F\u6216 Docker \u5BB9\u5668)...
21736
+ stdout.write(isZh ? `\u25C7 [1/4] \u6B63\u5728\u6AA2\u67E5\u4E26\u505C\u6B62\u57F7\u884C\u4E2D\u7684 Broker (\u672C\u6A5F\u7A0B\u5E8F\u6216 Docker \u5BB9\u5668)...
21729
21737
  ` : `\u25C7 [1/4] Checking and stopping running Broker (native process or Docker container)...
21730
21738
  `);
21731
- try {
21732
- const dockerCmd = process.platform === "win32" ? "docker.exe" : "docker";
21733
- const dockerDown = Bun.spawnSync([dockerCmd, "compose", "down"], { cwd });
21734
- if (dockerDown.exitCode === 0) {
21735
- stdout.write(isZh ? `\u2502 \u2714 Docker Broker \u5BB9\u5668\u5DF2\u505C\u6B62\u4E26\u6E05\u7406\u3002
21739
+ const composeContext = resolveDockerComposeContext(cwd);
21740
+ if (composeContext) {
21741
+ try {
21742
+ const dockerCmd = process.platform === "win32" ? "docker.exe" : "docker";
21743
+ const dockerDown = Bun.spawnSync([dockerCmd, "compose", "down"], {
21744
+ cwd: composeContext.projectDir
21745
+ });
21746
+ if (dockerDown.exitCode === 0) {
21747
+ stdout.write(isZh ? `\u2502 \u2714 Docker Broker \u5BB9\u5668\u5DF2\u505C\u6B62\u4E26\u6E05\u7406\u3002
21736
21748
  ` : `\u2502 \u2714 Docker Broker container stopped and cleaned up.
21737
21749
  `);
21750
+ }
21751
+ } catch {}
21738
21752
  }
21739
- } catch {}
21740
- try {
21741
- const identity = await loadOrCreateStateIdentity(stateDirectory);
21742
- const isRunning = await probeBroker(42617, identity.brokerSecret);
21743
- if (isRunning) {
21744
- await runStopCommand({ stateDirectory, port: 42617 }, { stdout, stderr }, fetchImpl);
21745
- stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F Broker \u5DF2\u6210\u529F\u505C\u6B62\u3002
21753
+ try {
21754
+ const identity = await loadOrCreateStateIdentity(stateDirectory);
21755
+ const isRunning = await probeBroker(42617, identity.brokerSecret);
21756
+ if (isRunning) {
21757
+ await runStopCommand({ stateDirectory, port: 42617 }, { stdout, stderr }, fetchImpl);
21758
+ stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F Broker \u5DF2\u6210\u529F\u505C\u6B62\u3002
21746
21759
  ` : `\u2502 \u2714 Local broker stopped.
21747
21760
  `);
21748
- } else {
21749
- stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F Broker \u672A\u5728\u57F7\u884C\u4E2D\u3002
21761
+ } else {
21762
+ stdout.write(isZh ? `\u2502 \u2714 \u672C\u6A5F Broker \u672A\u5728\u57F7\u884C\u4E2D\u3002
21750
21763
  ` : `\u2502 \u2714 Local broker is not running.
21751
21764
  `);
21752
- }
21753
- } catch {
21754
- stdout.write(isZh ? `\u2502 \u2714 \u672A\u627E\u5230\u57F7\u884C\u4E2D\u7684\u672C\u6A5F Broker \u6216\u5DF2\u505C\u6B62\u3002
21765
+ }
21766
+ } catch {
21767
+ stdout.write(isZh ? `\u2502 \u2714 \u672A\u627E\u5230\u57F7\u884C\u4E2D\u7684\u672C\u6A5F Broker \u6216\u5DF2\u505C\u6B62\u3002
21755
21768
  ` : `\u2502 \u2714 No active local broker found or already stopped.
21756
21769
  `);
21757
- }
21758
- stdout.write(`\u2502
21770
+ }
21771
+ stdout.write(`\u2502
21759
21772
  `);
21760
- stdout.write(isZh ? `\u25C7 [2/4] \u6B63\u5728\u641C\u5C0B OpenCode \u8A2D\u5B9A\u6A94...
21773
+ stdout.write(isZh ? `\u25C7 [2/4] \u6B63\u5728\u641C\u5C0B OpenCode \u8A2D\u5B9A\u6A94...
21761
21774
  ` : `\u25C7 [2/4] Searching for OpenCode configuration files...
21762
21775
  `);
21763
- const discoveredConfigs = await discoverOpenCodeConfigFiles(cwd);
21764
- const existingConfigs = discoveredConfigs.filter((c) => c.exists);
21765
- if (existingConfigs.length > 0) {
21766
- const cleanConfig = await reader.ask(isZh ? `\u2502 \u627E\u5230 ${existingConfigs.length} \u500B\u8A2D\u5B9A\u6A94\uFF0C\u662F\u5426\u5F9E\u4E2D\u79FB\u9664\u5916\u639B\u8A2D\u5B9A\uFF1F [Y/n]: ` : `\u2502 Found ${existingConfigs.length} config file(s). Remove plugin configuration? [Y/n]: `, stdout, "Y");
21767
- if (cleanConfig.toLowerCase() !== "n" && cleanConfig.toLowerCase() !== "no") {
21768
- for (const config2 of existingConfigs) {
21769
- const { modified, backupPath } = await removeOpenCodeConfig(config2.path);
21770
- if (modified) {
21771
- stdout.write(isZh ? `\u2502 \u2714 \u5DF2\u5F9E ${config2.path} \u79FB\u9664\u5916\u639B\u8A2D\u5B9A${backupPath ? ` (\u5099\u4EFD\u65BC ${backupPath})` : ""}
21776
+ const discoveredConfigs = await discoverOpenCodeConfigFiles(cwd);
21777
+ const existingConfigs = discoveredConfigs.filter((c) => c.exists);
21778
+ if (existingConfigs.length > 0) {
21779
+ const cleanConfig = await reader.ask(isZh ? `\u2502 \u627E\u5230 ${existingConfigs.length} \u500B\u8A2D\u5B9A\u6A94\uFF0C\u662F\u5426\u5F9E\u4E2D\u79FB\u9664\u5916\u639B\u8A2D\u5B9A\uFF1F [Y/n]: ` : `\u2502 Found ${existingConfigs.length} config file(s). Remove plugin configuration? [Y/n]: `, stdout, "Y");
21780
+ if (cleanConfig.toLowerCase() !== "n" && cleanConfig.toLowerCase() !== "no") {
21781
+ for (const config2 of existingConfigs) {
21782
+ const { modified, backupPath } = await removeOpenCodeConfig(config2.path);
21783
+ if (modified) {
21784
+ stdout.write(isZh ? `\u2502 \u2714 \u5DF2\u5F9E ${config2.path} \u79FB\u9664\u5916\u639B\u8A2D\u5B9A${backupPath ? ` (\u5099\u4EFD\u65BC ${backupPath})` : ""}
21772
21785
  ` : `\u2502 \u2714 Removed plugin config from ${config2.path}${backupPath ? ` (backup at ${backupPath})` : ""}
21773
21786
  `);
21774
- } else {
21775
- stdout.write(isZh ? `\u2502 - ${config2.path} \u672A\u5305\u542B\u672C\u5916\u639B\u8A2D\u5B9A\u3002
21787
+ } else {
21788
+ stdout.write(isZh ? `\u2502 - ${config2.path} \u672A\u5305\u542B\u672C\u5916\u639B\u8A2D\u5B9A\u3002
21776
21789
  ` : `\u2502 - No plugin config in ${config2.path}.
21777
21790
  `);
21791
+ }
21778
21792
  }
21779
21793
  }
21780
- }
21781
- const symlinkLocations = [
21782
- join8(cwd, "node_modules", "opencode-telegram-link"),
21783
- join8(homedir3(), ".config", "opencode", "node_modules", "opencode-telegram-link"),
21784
- join8(homedir3(), ".opencode", "node_modules", "opencode-telegram-link")
21785
- ];
21786
- for (const symlink of symlinkLocations) {
21787
- try {
21788
- await rm4(symlink, { recursive: true, force: true });
21789
- } catch {}
21790
- }
21791
- } else {
21792
- stdout.write(isZh ? `\u2502 \u2714 \u672A\u627E\u5230\u4EFB\u4F55 opencode.json \u8A2D\u5B9A\u6A94\u3002
21794
+ const symlinkLocations = [
21795
+ join8(cwd, "node_modules", "opencode-telegram-link"),
21796
+ join8(homedir3(), ".config", "opencode", "node_modules", "opencode-telegram-link"),
21797
+ join8(homedir3(), ".opencode", "node_modules", "opencode-telegram-link")
21798
+ ];
21799
+ for (const symlink of symlinkLocations) {
21800
+ try {
21801
+ await rm4(symlink, { recursive: true, force: true });
21802
+ } catch {}
21803
+ }
21804
+ } else {
21805
+ stdout.write(isZh ? `\u2502 \u2714 \u672A\u627E\u5230\u4EFB\u4F55 opencode.json \u8A2D\u5B9A\u6A94\u3002
21793
21806
  ` : `\u2502 \u2714 No opencode.json configuration files found.
21794
21807
  `);
21795
- }
21796
- stdout.write(`\u2502
21808
+ }
21809
+ stdout.write(`\u2502
21797
21810
  `);
21798
- const cleanState = await reader.ask(isZh ? "\u25C7 [3/4] \u662F\u5426\u6E05\u9664\u904B\u4F5C\u66AB\u5B58\u8CC7\u6599\u5EAB\u8207\u8A0A\u606F\u8DEF\u7531\u72C0\u614B (SQLite)\uFF1F [Y/n]: " : "\u25C7 [3/4] Remove operational database & message routing state (SQLite)? [Y/n]: ", stdout, "Y");
21799
- if (cleanState.toLowerCase() !== "n" && cleanState.toLowerCase() !== "no") {
21800
- try {
21801
- const dbPath = join8(stateDirectory, "state.sqlite");
21802
- await rm4(dbPath, { force: true });
21803
- await rm4(`${dbPath}-wal`, { force: true });
21804
- await rm4(`${dbPath}-shm`, { force: true });
21805
- await rm4(discoveryRecordPath(stateDirectory), { force: true });
21806
- await rm4(join8(stateDirectory, "dashboard-settings.json"), { force: true });
21811
+ const cleanState = await reader.ask(isZh ? "\u25C7 [3/4] \u662F\u5426\u6E05\u9664\u904B\u4F5C\u66AB\u5B58\u8CC7\u6599\u5EAB\u8207\u8A0A\u606F\u8DEF\u7531\u72C0\u614B (SQLite)\uFF1F [Y/n]: " : "\u25C7 [3/4] Remove operational database & message routing state (SQLite)? [Y/n]: ", stdout, "Y");
21812
+ if (cleanState.toLowerCase() !== "n" && cleanState.toLowerCase() !== "no") {
21807
21813
  try {
21808
- await rm4(join8(tmpdir(), "opencode_telegram_plugin.log"), { force: true });
21809
- } catch {}
21810
- stdout.write(isZh ? `\u2502 \u2714 \u904B\u4F5C\u66AB\u5B58\u8CC7\u6599\u5EAB\u8207\u63A7\u5236\u53F0\u8A2D\u5B9A\u6A94 (dashboard-settings.json) \u5DF2\u6E05\u9664\u3002
21814
+ const dbPath = join8(stateDirectory, "state.sqlite");
21815
+ await rm4(dbPath, { force: true });
21816
+ await rm4(`${dbPath}-wal`, { force: true });
21817
+ await rm4(`${dbPath}-shm`, { force: true });
21818
+ await rm4(discoveryRecordPath(stateDirectory), { force: true });
21819
+ await rm4(join8(stateDirectory, "dashboard-settings.json"), { force: true });
21820
+ try {
21821
+ await rm4(join8(tmpdir(), "opencode_telegram_plugin.log"), { force: true });
21822
+ } catch {}
21823
+ stdout.write(isZh ? `\u2502 \u2714 \u904B\u4F5C\u66AB\u5B58\u8CC7\u6599\u5EAB\u8207\u63A7\u5236\u53F0\u8A2D\u5B9A\u6A94 (dashboard-settings.json) \u5DF2\u6E05\u9664\u3002
21811
21824
  ` : `\u2502 \u2714 Operational state database & dashboard settings cleared.
21812
21825
  `);
21813
- } catch {
21814
- stdout.write(isZh ? `\u2502 - \u66AB\u5B58\u8CC7\u6599\u5EAB\u4E0D\u5B58\u5728\u6216\u5DF2\u6E05\u7406\u3002
21826
+ } catch {
21827
+ stdout.write(isZh ? `\u2502 - \u66AB\u5B58\u8CC7\u6599\u5EAB\u4E0D\u5B58\u5728\u6216\u5DF2\u6E05\u7406\u3002
21815
21828
  ` : `\u2502 - State database not found or already clean.
21816
21829
  `);
21830
+ }
21817
21831
  }
21818
- }
21819
- stdout.write(`\u2502
21832
+ stdout.write(`\u2502
21820
21833
  `);
21821
- const cleanToken = await reader.ask(isZh ? "\u25C7 [4/4] \u662F\u5426\u522A\u9664\u5132\u5B58\u7684 Telegram Bot Token \u6A94\u6848\uFF1F [y/N]: " : "\u25C7 [4/4] Delete saved Telegram Bot Token file? [y/N]: ", stdout, "N");
21822
- if (cleanToken.toLowerCase() === "y" || cleanToken.toLowerCase() === "yes") {
21823
- try {
21824
- const tokenPath = join8(stateDirectory, "telegram-bot-token");
21825
- await rm4(tokenPath, { force: true });
21826
- stdout.write(isZh ? `\u2502 \u2714 Token \u6A94\u6848\u5DF2\u5B89\u5168\u522A\u9664\u3002
21834
+ const cleanToken = await reader.ask(isZh ? "\u25C7 [4/4] \u662F\u5426\u522A\u9664\u5132\u5B58\u7684 Telegram Bot Token \u6A94\u6848\uFF1F [y/N]: " : "\u25C7 [4/4] Delete saved Telegram Bot Token file? [y/N]: ", stdout, "N");
21835
+ if (cleanToken.toLowerCase() === "y" || cleanToken.toLowerCase() === "yes") {
21836
+ try {
21837
+ const tokenPath = join8(stateDirectory, "telegram-bot-token");
21838
+ await rm4(tokenPath, { force: true });
21839
+ stdout.write(isZh ? `\u2502 \u2714 Token \u6A94\u6848\u5DF2\u5B89\u5168\u522A\u9664\u3002
21827
21840
  ` : `\u2502 \u2714 Token file securely removed.
21828
21841
  `);
21829
- } catch {
21830
- stdout.write(isZh ? `\u2502 - Token \u6A94\u6848\u4E0D\u5B58\u5728\u3002
21842
+ } catch {
21843
+ stdout.write(isZh ? `\u2502 - Token \u6A94\u6848\u4E0D\u5B58\u5728\u3002
21831
21844
  ` : `\u2502 - Token file does not exist.
21832
21845
  `);
21833
- }
21834
- const cleanAll = await reader.ask(isZh ? `\u2502 \u662F\u5426\u9023\u540C\u91D1\u9470\u76EE\u9304 ${stateDirectory} \u4E00\u4F75\u5B8C\u5168\u522A\u9664\uFF1F [y/N]: ` : `\u2502 Delete entire state directory (${stateDirectory})? [y/N]: `, stdout, "N");
21835
- if (cleanAll.toLowerCase() === "y" || cleanAll.toLowerCase() === "yes") {
21836
- try {
21837
- await rm4(stateDirectory, { recursive: true, force: true });
21838
- stdout.write(isZh ? `\u2502 \u2714 \u91D1\u9470\u8207\u72C0\u614B\u76EE\u9304\u5DF2\u5B8C\u5168\u79FB\u9664\u3002
21846
+ }
21847
+ const cleanAll = await reader.ask(isZh ? `\u2502 \u662F\u5426\u9023\u540C\u91D1\u9470\u76EE\u9304 ${stateDirectory} \u4E00\u4F75\u5B8C\u5168\u522A\u9664\uFF1F [y/N]: ` : `\u2502 Delete entire state directory (${stateDirectory})? [y/N]: `, stdout, "N");
21848
+ if (cleanAll.toLowerCase() === "y" || cleanAll.toLowerCase() === "yes") {
21849
+ try {
21850
+ await rm4(stateDirectory, { recursive: true, force: true });
21851
+ stdout.write(isZh ? `\u2502 \u2714 \u91D1\u9470\u8207\u72C0\u614B\u76EE\u9304\u5DF2\u5B8C\u5168\u79FB\u9664\u3002
21839
21852
  ` : `\u2502 \u2714 State directory completely removed.
21840
21853
  `);
21841
- } catch {}
21854
+ } catch {}
21855
+ }
21842
21856
  }
21843
- }
21844
- stdout.write(`\u2502
21857
+ stdout.write(`\u2502
21845
21858
  `);
21846
- stdout.write(isZh ? `\u2514 \uD83C\uDF89 OpenCode Telegram Notifier \u5DF2\u6210\u529F\u79FB\u9664\uFF01
21859
+ stdout.write(isZh ? `\u2514 \uD83C\uDF89 OpenCode Telegram Notifier \u5DF2\u6210\u529F\u79FB\u9664\uFF01
21847
21860
 
21848
21861
  ` : `\u2514 \uD83C\uDF89 OpenCode Telegram Notifier successfully uninstalled!
21849
21862
 
21850
21863
  `);
21851
- return 0;
21864
+ return 0;
21865
+ } finally {
21866
+ reader.close();
21867
+ }
21852
21868
  }
21853
21869
  async function runUninstallCli(options = {}) {
21854
21870
  const argv = options.argv ?? process.argv.slice(2);
@@ -21892,9 +21908,10 @@ async function runBroker() {
21892
21908
  }
21893
21909
  if (import.meta.main) {
21894
21910
  await runBroker();
21911
+ process.exit(process.exitCode ?? 0);
21895
21912
  }
21896
21913
  export {
21897
21914
  runBroker
21898
21915
  };
21899
21916
 
21900
- //# debugId=02CC163E478BF57D64756E2164756E21
21917
+ //# debugId=81AE8738AC7A8E1264756E2164756E21