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.
- package/README.md +3 -3
- package/dist/broker/main.js +406 -389
- package/dist/broker/main.js.map +7 -7
- package/dist/opencode/config-helper.d.ts.map +1 -1
- package/dist/plugin.js +24 -28
- package/dist/plugin.js.map +4 -4
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/uninstall.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/broker/main.js
CHANGED
|
@@ -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
|
-
|
|
15786
|
-
|
|
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
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
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
|
-
|
|
15825
|
-
|
|
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
|
-
|
|
15849
|
-
|
|
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
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
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
|
-
|
|
16810
|
+
stdout.write(`\u25C7 Language / \u8A9E\u8A00:
|
|
16807
16811
|
`);
|
|
16808
|
-
|
|
16812
|
+
stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
|
|
16809
16813
|
`);
|
|
16810
|
-
|
|
16814
|
+
stdout.write(`\u2502 2) English (en)
|
|
16811
16815
|
`);
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16849
|
-
|
|
16850
|
-
|
|
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
|
-
|
|
16854
|
-
|
|
16855
|
-
|
|
16857
|
+
return 1;
|
|
16858
|
+
}
|
|
16859
|
+
stdout.write(`\u2502
|
|
16856
16860
|
`);
|
|
16857
|
-
|
|
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
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
16872
|
-
|
|
16873
|
-
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
16888
|
-
|
|
16889
|
-
|
|
16890
|
-
|
|
16891
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
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
|
-
|
|
16911
|
-
|
|
16912
|
-
|
|
16914
|
+
return 1;
|
|
16915
|
+
}
|
|
16916
|
+
stdout.write(`\u2502
|
|
16913
16917
|
`);
|
|
16914
|
-
|
|
16915
|
-
|
|
16918
|
+
if (isZh) {
|
|
16919
|
+
stdout.write(`\u25C7 \u8ACB\u8F38\u5165\u5411 @BotFather \u7533\u8ACB\u7684 Telegram Bot Token:
|
|
16916
16920
|
`);
|
|
16917
|
-
|
|
16921
|
+
stdout.write(`\u2502 (\u7BC4\u4F8B: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
|
|
16918
16922
|
`);
|
|
16919
|
-
|
|
16920
|
-
|
|
16923
|
+
} else {
|
|
16924
|
+
stdout.write(`\u25C7 Enter your Telegram Bot Token from @BotFather:
|
|
16921
16925
|
`);
|
|
16922
|
-
|
|
16926
|
+
stdout.write(`\u2502 (Example: 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ)
|
|
16923
16927
|
`);
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
|
|
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
|
-
|
|
16931
|
-
|
|
16932
|
-
|
|
16933
|
-
|
|
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
|
-
|
|
16937
|
-
|
|
16938
|
-
|
|
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
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
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
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
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
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
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
|
-
|
|
16961
|
-
|
|
16964
|
+
if (isZh) {
|
|
16965
|
+
stdout.write(`\u25C7 [\u8EAB\u5206\u9A57\u8B49\u914D\u5C0D]
|
|
16962
16966
|
`);
|
|
16963
|
-
|
|
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
|
-
|
|
16969
|
+
stdout.write(`\u2502
|
|
16966
16970
|
\u2502 \uD83D\uDC49 ${nonce}
|
|
16967
16971
|
\u2502
|
|
16968
16972
|
`);
|
|
16969
|
-
|
|
16973
|
+
stdout.write(`\u2502 \u280B \u7B49\u5F85 Telegram \u79C1\u8A0A\u4E2D...
|
|
16970
16974
|
`);
|
|
16971
|
-
|
|
16972
|
-
|
|
16975
|
+
} else {
|
|
16976
|
+
stdout.write(`\u25C7 [Identity Pairing]
|
|
16973
16977
|
`);
|
|
16974
|
-
|
|
16978
|
+
stdout.write(`\u2502 Open a private chat with ${botName} on Telegram and send this code:
|
|
16975
16979
|
`);
|
|
16976
|
-
|
|
16980
|
+
stdout.write(`\u2502
|
|
16977
16981
|
\u2502 \uD83D\uDC49 ${nonce}
|
|
16978
16982
|
\u2502
|
|
16979
16983
|
`);
|
|
16980
|
-
|
|
16984
|
+
stdout.write(`\u2502 \u280B Waiting for Telegram private message...
|
|
16981
16985
|
`);
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
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
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
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
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
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
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17044
|
-
|
|
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
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
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
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
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
|
-
|
|
17064
|
-
|
|
17065
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17078
|
+
stdout.write(`\u2502
|
|
17075
17079
|
${generatePluginConfigSnippet(configData)}
|
|
17076
17080
|
\u2502
|
|
17077
17081
|
`);
|
|
17082
|
+
}
|
|
17078
17083
|
}
|
|
17079
|
-
|
|
17080
|
-
|
|
17081
|
-
|
|
17082
|
-
stdout.write(`\u2502
|
|
17084
|
+
let dockerFailed = false;
|
|
17085
|
+
if (isDocker) {
|
|
17086
|
+
stdout.write(`\u2502
|
|
17083
17087
|
`);
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
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
|
-
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
|
|
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
|
-
|
|
17113
|
-
|
|
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
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
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
|
-
|
|
17134
|
-
|
|
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
|
-
|
|
17139
|
-
|
|
17141
|
+
}
|
|
17142
|
+
} else {
|
|
17143
|
+
stdout.write(`\u2502
|
|
17140
17144
|
`);
|
|
17141
|
-
|
|
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
|
-
|
|
17146
|
-
|
|
17148
|
+
}
|
|
17149
|
+
if (!isNode && pairing && api2) {
|
|
17150
|
+
stdout.write(`\u2502
|
|
17147
17151
|
`);
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
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
|
-
|
|
17166
|
-
|
|
17167
|
-
|
|
17168
|
-
|
|
17169
|
-
|
|
17170
|
-
|
|
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
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
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
|
-
|
|
17184
|
-
|
|
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
|
-
|
|
17190
|
-
|
|
17191
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
21698
|
-
|
|
21699
|
-
|
|
21700
|
-
|
|
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
|
-
|
|
21712
|
+
stdout.write(`\u25C7 Language / \u8A9E\u8A00:
|
|
21705
21713
|
`);
|
|
21706
|
-
|
|
21714
|
+
stdout.write(`\u2502 1) \u7E41\u9AD4\u4E2D\u6587 (zh-TW) [\u9810\u8A2D/Default]
|
|
21707
21715
|
`);
|
|
21708
|
-
|
|
21716
|
+
stdout.write(`\u2502 2) English (en)
|
|
21709
21717
|
`);
|
|
21710
|
-
|
|
21711
|
-
|
|
21712
|
-
|
|
21713
|
-
|
|
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
|
-
|
|
21716
|
-
|
|
21717
|
-
|
|
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
|
-
|
|
21725
|
-
|
|
21726
|
-
|
|
21732
|
+
return 0;
|
|
21733
|
+
}
|
|
21734
|
+
stdout.write(`\u2502
|
|
21727
21735
|
`);
|
|
21728
|
-
|
|
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
|
-
|
|
21732
|
-
|
|
21733
|
-
|
|
21734
|
-
|
|
21735
|
-
|
|
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
|
-
|
|
21740
|
-
|
|
21741
|
-
|
|
21742
|
-
|
|
21743
|
-
|
|
21744
|
-
|
|
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
|
-
|
|
21749
|
-
|
|
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
|
-
|
|
21754
|
-
|
|
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
|
-
|
|
21770
|
+
}
|
|
21771
|
+
stdout.write(`\u2502
|
|
21759
21772
|
`);
|
|
21760
|
-
|
|
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
|
-
|
|
21764
|
-
|
|
21765
|
-
|
|
21766
|
-
|
|
21767
|
-
|
|
21768
|
-
|
|
21769
|
-
|
|
21770
|
-
|
|
21771
|
-
|
|
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
|
-
|
|
21775
|
-
|
|
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
|
-
|
|
21782
|
-
|
|
21783
|
-
|
|
21784
|
-
|
|
21785
|
-
|
|
21786
|
-
|
|
21787
|
-
|
|
21788
|
-
|
|
21789
|
-
}
|
|
21790
|
-
}
|
|
21791
|
-
|
|
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
|
-
|
|
21808
|
+
}
|
|
21809
|
+
stdout.write(`\u2502
|
|
21797
21810
|
`);
|
|
21798
|
-
|
|
21799
|
-
|
|
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
|
-
|
|
21809
|
-
|
|
21810
|
-
|
|
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
|
-
|
|
21814
|
-
|
|
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
|
-
|
|
21822
|
-
|
|
21823
|
-
|
|
21824
|
-
|
|
21825
|
-
|
|
21826
|
-
|
|
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
|
-
|
|
21830
|
-
|
|
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
|
-
|
|
21835
|
-
|
|
21836
|
-
|
|
21837
|
-
|
|
21838
|
-
|
|
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
|
-
|
|
21854
|
+
} catch {}
|
|
21855
|
+
}
|
|
21842
21856
|
}
|
|
21843
|
-
|
|
21844
|
-
stdout.write(`\u2502
|
|
21857
|
+
stdout.write(`\u2502
|
|
21845
21858
|
`);
|
|
21846
|
-
|
|
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
|
-
|
|
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=
|
|
21917
|
+
//# debugId=81AE8738AC7A8E1264756E2164756E21
|