opencode-tbot 0.1.32 → 0.1.34

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.zh-CN.md CHANGED
@@ -4,27 +4,28 @@
4
4
 
5
5
  [English](./README.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md)
6
6
 
7
- > 本项目并非由 OpenCode 官方团队开发。
7
+ > 本项目并非由 OpenCode 官方团队开发,也不隶属于 OpenCode 官方。
8
8
 
9
9
  ## 概览
10
10
 
11
- `opencode-tbot` 允许你直接在 Telegram 中操作 OpenCode
11
+ `opencode-tbot` 让你可以在 Telegram 中直接操作 OpenCode,并为每个聊天维护一份绑定状态。
12
12
 
13
- - 文本消息会转发到当前 OpenCode 会话。
14
- - Telegram 图片会作为 OpenCode 文件片段上传。
15
- - 图片轮次会在临时 fork 会话中执行,避免后续纯文本上下文被污染。
16
- - Telegram 语音消息会被明确拒绝。
17
- - OpenCode 的权限请求可以直接在 Telegram 中审批。
18
- - 会话完成和错误事件可以回推到已绑定的 Telegram chat。
19
- - 聊天绑定状态会保存在 JSON 状态文件中。
13
+ - 纯文本消息会转发到当前 OpenCode 会话。
14
+ - Telegram 照片和图片文档会作为 OpenCode 文件片段上传。
15
+ - 图片轮次会在临时 fork 会话中执行,避免后续纯文本对话继承图片上下文。
16
+ - OpenCode 发起的权限请求可以直接在 Telegram 内联按钮里审批或拒绝。
17
+ - 会话错误事件可以回推到已绑定的 Telegram chat。
18
+ - 语音消息会被明确拒绝,并返回本地化提示。
19
+ - 聊天绑定和会话选择状态会保存在 JSON 状态文件中。
20
20
 
21
21
  ## 环境要求
22
22
 
23
23
  - 一个正在运行、并会加载该插件的 OpenCode Host 进程。
24
24
  - 一个 Telegram bot token。
25
- - Node.js `>=22.12.0`。
25
+ - Node.js `>=22.12.0`,用于 CLI 和本地开发。
26
+ - `pnpm`,用于仓库开发。
26
27
 
27
- ## 安装
28
+ ## 安装与更新
28
29
 
29
30
  推荐安装方式:
30
31
 
@@ -32,20 +33,33 @@
32
33
  npm exec --package opencode-tbot@latest opencode-tbot -- install
33
34
  ```
34
35
 
35
- 安装器会全局注册插件、写入默认运行时配置,并打印插件配置路径与默认日志目录。
36
+ 安装器会:
36
37
 
37
- 查看 CLI 版本:
38
+ - 在全局 OpenCode 插件列表中注册 `opencode-tbot@latest`
39
+ - 更新实际生效的 OpenCode 全局配置文件
40
+ - 写入或合并全局插件运行时配置
41
+ - 打印 OpenCode 配置路径、插件配置路径和默认插件日志目录
42
+
43
+ 如果 `~/.config/opencode/opencode.jsonc` 已存在,安装器会优先更新它;否则会使用 `~/.config/opencode/opencode.json`。
44
+
45
+ 查看已安装 CLI 版本:
38
46
 
39
47
  ```bash
40
48
  npm exec --package opencode-tbot@latest opencode-tbot -- --version
41
49
  ```
42
50
 
43
- 更新 OpenCode 中注册的 npm 插件 spec:
51
+ 只更新 OpenCode 中注册的 npm 插件 spec,而不改 bot token
44
52
 
45
53
  ```bash
46
54
  npm exec --package opencode-tbot@latest opencode-tbot -- update
47
55
  ```
48
56
 
57
+ `install` 是默认命令。例如,下面这种写法也适合非交互安装:
58
+
59
+ ```bash
60
+ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
61
+ ```
62
+
49
63
  ### CLI 参数
50
64
 
51
65
  `install` 支持:
@@ -53,7 +67,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
53
67
  - `--bot-token <token>` 非交互写入 Telegram bot token
54
68
  - `--telegram-api-root <url>` 覆盖 Telegram Bot API 根地址
55
69
  - `--plugin-spec <spec>` 注册自定义 npm 插件 spec
56
- - `--skip-register` 只重写插件配置,不改 OpenCode 插件注册
70
+ - `--skip-register` 只重写插件配置,不修改 OpenCode 插件注册
57
71
  - `--home-dir <path>` 使用自定义 home 目录
58
72
 
59
73
  `update` 支持:
@@ -63,13 +77,23 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
63
77
 
64
78
  ## 配置
65
79
 
66
- 运行时配置只从 `~/.config/opencode/opencode-tbot/config.json` 加载。
80
+ 运行时配置只会从以下路径加载:
81
+
82
+ - `~/.config/opencode/opencode-tbot/config.json`
83
+
84
+ OpenCode 的插件注册信息保存在全局 OpenCode 配置中:
85
+
86
+ - 如果存在,使用 `~/.config/opencode/opencode.jsonc`
87
+ - 否则使用 `~/.config/opencode/opencode.json`
88
+
89
+ 本插件不会从 `.env` 读取运行时配置,请使用全局 JSON 配置文件。
67
90
 
68
- 遗留的 `<worktree>/tbot.config.json` 会在运行时被忽略;如果检测到,插件会记录一条警告,提示你把配置迁移到全局文件。
91
+ 遗留行为说明:
69
92
 
70
- 遗留的 `openrouter` 语音转写配置会在运行时被忽略;安装器重写配置时也会自动移除它们。
93
+ - `<worktree>/tbot.config.json` 会在运行时被忽略;如果检测到,插件会记录警告,提示你迁移配置
94
+ - 遗留的 `openrouter` 语音转写配置会在运行时被忽略;安装器重写配置时也会自动移除
71
95
 
72
- ### 全局 `config.json` 示例
96
+ ### `config.json` 示例
73
97
 
74
98
  ```json
75
99
  {
@@ -108,69 +132,77 @@ npm exec --package opencode-tbot@latest opencode-tbot -- update
108
132
 
109
133
  | 字段 | 必填 | 默认值 | 说明 |
110
134
  | --- | --- | --- | --- |
111
- | `telegram.botToken` | 是 | - | Telegram bot token。通常由安装器写入。 |
135
+ | `telegram.botToken` | 是 | - | Telegram bot token。通常由安装器写入全局插件配置。 |
112
136
  | `telegram.allowedChatIds` | 否 | `[]` | 允许访问的 Telegram chat ID。为空时接受任意 chat。 |
113
- | `telegram.apiRoot` | 否 | `https://api.telegram.org` | Telegram Bot API 根地址。 |
137
+ | `telegram.apiRoot` | 否 | `https://api.telegram.org` | Telegram Bot API 根地址,适合测试或自建网关。 |
114
138
  | `state.path` | 否 | `./data/opencode-tbot.state.json` | JSON 状态文件路径,相对当前 OpenCode worktree 解析。 |
115
- | `prompt.waitTimeoutMs` | 否 | `1800000` | 单次异步 prompt 生命周期的最大等待时间。 |
116
- | `prompt.pollRequestTimeoutMs` | 否 | `15000` | 每次恢复轮询请求的超时时间。 |
117
- | `prompt.recoveryInactivityTimeoutMs` | 否 | `120000` | 仅在 OpenCode 长时间没有新进展时生效的恢复超时。 |
118
- | `logging.level` | 否 | `info` | 结构化日志级别。 |
139
+ | `prompt.waitTimeoutMs` | 否 | `1800000` | 单次异步 prompt 生命周期的总等待上限。 |
140
+ | `prompt.pollRequestTimeoutMs` | 否 | `15000` | 每次向 OpenCode 发起恢复轮询请求时的超时时间。 |
141
+ | `prompt.recoveryInactivityTimeoutMs` | 否 | `120000` | 仅在长时间没有新进展时生效的恢复超时。 |
142
+ | `logging.level` | 否 | `info` | Host 和文件日志共用的结构化日志级别,会规范化为 `debug`、`info`、`warn` 或 `error`。 |
119
143
  | `logging.sinks.host` | 否 | `true` | 是否通过 `client.app.log({ body: ... })` 写入 OpenCode Host 日志。 |
120
144
  | `logging.sinks.file` | 否 | `true` | 是否写入插件自己的 JSONL 文件日志。 |
121
- | `logging.file.dir` | 否 | `%USERPROFILE%/.local/share/opencode/log/plugins/opencode-tbot` | 插件日志目录。 |
145
+ | `logging.file.dir` | 否 | `%USERPROFILE%/.local/share/opencode/log/plugins/opencode-tbot` | 插件 JSONL 日志目录。相对路径会基于当前 OpenCode worktree 解析。 |
122
146
  | `logging.file.retention.maxFiles` | 否 | `30` | 最多保留多少个插件日志文件。 |
123
147
  | `logging.file.retention.maxTotalBytes` | 否 | `314572800` | 插件日志文件总大小上限。 |
124
- | `logLevel` | 否 | `info` | `logging.level` 的兼容别名。 |
148
+ | `logLevel` | 否 | `info` | `logging.level` 的兼容别名。仍然可用。 |
125
149
 
126
150
  ### 说明
127
151
 
128
152
  - `state.path` 会相对当前 OpenCode worktree 解析。
129
- - Telegram prompt 采用 async-first:插件先提交异步 prompt,再通过消息与状态恢复最终回复。
153
+ - `logging.file.dir` 如果是相对路径,也会相对当前 worktree 解析。
154
+ - Telegram prompt 采用 async-first:插件先提交 `session.promptAsync()`,再通过消息与状态恢复最终回复。
130
155
  - 如果 `telegram.allowedChatIds` 为空,bot 会接受任意 chat 的消息;生产环境建议显式限制。
131
- - 权限审批与会话通知通过插件 hooks 处理。
132
- - `/language` 当前支持 English、简体中文、日语。
133
- - 默认会双写日志:OpenCode Host 日志 + 插件 JSONL 文件日志。
156
+ - 权限审批与会话错误通知通过插件 hooks 处理。
157
+ - `/language` 当前支持 English、简体中文、日语,并会同步当前 chat 的 Telegram 命令描述。
134
158
  - 文件日志默认只记录元数据,不记录 prompt 正文、附件内容、原始 URL 或 secrets。
135
159
 
136
- ## 日志
137
-
138
- - OpenCode Host 日志目录:`%USERPROFILE%/.local/share/opencode/log`
139
- - 插件日志目录:`%USERPROFILE%/.local/share/opencode/log/plugins/opencode-tbot`
140
- - 文件格式:JSONL,每行一条结构化事件
141
- - 关键关联字段:`runtimeId`、`operationId`、`correlationId`、`updateId`、`chatId`、`sessionId`、`projectId`
142
- - 常见组件:`runtime`、`telegram`、`opencode`、`prompt`、`plugin-event`、`storage`
143
- - 默认保留策略:最多 30 个文件,总大小不超过 300 MB
144
-
145
160
  ## 快速开始
146
161
 
147
162
  1. 运行 `npm exec --package opencode-tbot@latest opencode-tbot -- install`。
148
- 2. 如需限制可访问 chat,在 `~/.config/opencode/opencode-tbot/config.json` 中设置 `telegram.allowedChatIds`。
149
- 3. 在目标 worktree 中启动 OpenCode
163
+ 2. 如果需要限制可访问 chat,在 `~/.config/opencode/opencode-tbot/config.json` 中设置 `telegram.allowedChatIds`。
164
+ 3. 在目标 worktree 中启动 OpenCode,让插件运行时被加载。
150
165
  4. 在 Telegram 中执行 `/status` 验证连通性。
151
- 5. 执行 `/new [title]`,或者直接发送文本/图片开始使用。
166
+ 5. 运行 `/new [title]`,或直接发送文本消息开始使用。
152
167
 
153
168
  ## 命令
154
169
 
155
- - `/start` 显示欢迎信息和快速开始说明
156
- - `/status` 显示 OpenCode 健康状态、路径、LSP、MCP 等信息
157
- - `/new [title]` 创建新会话
158
- - `/agents` `/agent` 列出并切换 agent
159
- - `/sessions` 列出并切换会话
160
- - `/cancel` 取消重命名,或中止当前正在运行的请求
161
- - `/model` `/models` 列出并切换模型
162
- - `/language` 切换显示语言
170
+ | 命令 | 作用 |
171
+ | --- | --- |
172
+ | `/start` | 显示欢迎消息和快速开始说明。 |
173
+ | `/status` | 汇总展示 OpenCode 健康状态、版本、工作区路径、插件列表、LSP 状态和 MCP 状态。 |
174
+ | `/new [title]` | 在当前项目中创建一个新的 OpenCode 会话。 |
175
+ | `/agents` 或 `/agent` | 列出可用 Agent 并切换当前 Agent。 |
176
+ | `/sessions` | 列出可用会话、切换当前会话,并提供重命名操作。 |
177
+ | `/cancel` | 取消待输入的会话重命名,或中止当前会话正在运行的请求。 |
178
+ | `/model` 或 `/models` | 列出可用模型、切换当前模型,并在可用时选择推理级别。 |
179
+ | `/language` | 切换当前 chat 的 bot 显示语言。 |
163
180
 
164
- 消息处理规则:
181
+ ### 消息行为
165
182
 
166
183
  - 非命令文本会作为 prompt 发送给 OpenCode。
167
- - Telegram 图片会作为 OpenCode 文件片段上传。
168
- - 图片消息会在临时 fork 会话中处理。
169
- - `/cancel` 会同时中止 OpenCode 会话和本地 Telegram 等待状态。
170
- - Telegram 语音消息不受支持,会收到本地化拒绝提示。
184
+ - Telegram 照片和图片文档会作为 OpenCode 文件片段转发。
185
+ - 图片附件会在当前会话的临时 fork 中处理,避免污染后续纯文本上下文。
186
+ - `/cancel` 会同时中止 OpenCode 会话请求和本地 Telegram 等待状态,让下一次 prompt 能立即开始。
187
+ - Telegram 语音消息暂不支持,会收到本地化拒绝提示。
188
+
189
+ ## 日志
190
+
191
+ - OpenCode Host 日志目录:`%USERPROFILE%/.local/share/opencode/log`
192
+ - 插件日志目录:`%USERPROFILE%/.local/share/opencode/log/plugins/opencode-tbot`
193
+ - 文件格式:追加写入的 JSONL,每行一个结构化事件
194
+ - 关联字段:`runtimeId`、`operationId`、`correlationId`、`updateId`、`chatId`、`sessionId`、`projectId`
195
+ - 常见组件:`runtime`、`telegram`、`opencode`、`prompt`、`plugin-event`、`storage`
196
+ - 默认保留策略:最多保留最新 30 个文件,且总大小不超过 300 MB
171
197
 
172
198
  ## 开发
173
199
 
200
+ 安装依赖:
201
+
202
+ ```bash
203
+ pnpm install
204
+ ```
205
+
174
206
  构建:
175
207
 
176
208
  ```bash
@@ -183,22 +215,46 @@ pnpm build
183
215
  pnpm typecheck
184
216
  ```
185
217
 
186
- 运行测试:
218
+ 运行默认测试集:
187
219
 
188
220
  ```bash
189
221
  pnpm test
190
222
  ```
191
223
 
192
- 本仓库本地开发时,OpenCode 可以通过 [.opencode/plugins/opencode-tbot.ts](./.opencode/plugins/opencode-tbot.ts) 直接加载 `src/plugin.ts`。
224
+ 运行最接近 CI 的集成路径:
225
+
226
+ ```bash
227
+ npm install -g opencode-ai
228
+ OPENCODE_HOST_E2E=1 pnpm test
229
+ ```
193
230
 
194
- OpenCode 可能会在同一进程中同时加载本地 `.opencode/plugins/` bridge 和已安装的 `opencode-tbot@latest` npm 插件。当前实现已经防止重复启动 Telegram runner,但本地开发仍建议只保留一种加载方式。
231
+ PowerShell 写法:
232
+
233
+ ```powershell
234
+ npm install -g opencode-ai
235
+ $env:OPENCODE_HOST_E2E="1"; pnpm test
236
+ ```
237
+
238
+ 正常使用应依赖“全局安装的 npm 插件 + 全局 OpenCode 配置”。本仓库不再默认提供 `.opencode/plugins` bridge。
239
+
240
+ 如果你在本仓库里做源码调试,需要手动创建 `.opencode/plugins/opencode-tbot.ts`:
241
+
242
+ ```ts
243
+ export { default } from "../../src/plugin.ts";
244
+ ```
245
+
246
+ 同一时间只保留一种加载方式即可:要么使用手动创建的本地 bridge,要么使用全局安装的 `opencode-tbot@latest`。
195
247
 
196
248
  ## FAQ
197
249
 
198
250
  ### 需要一个正在运行的 OpenCode 实例吗?
199
251
 
200
- 需要。这个仓库只提供 Telegram 集成层,依赖加载它的 OpenCode Host 进程。
252
+ 需要。本仓库只提供 Telegram 集成层,依赖加载该插件的 OpenCode Host 进程。
253
+
254
+ ### 为什么 OpenCode 里会显示 `file:///.../node_modules/...` 形式的插件路径?
255
+
256
+ 这通常表示某个本地项目在自己的 `node_modules` 中安装了 `opencode-tbot`。CLI 检测到这种布局时会给出警告。请在那个项目里执行 `npm uninstall opencode-tbot` 移除本地包,然后改用推荐的 `npm exec --package ...` 安装流程。
201
257
 
202
258
  ### 这是 OpenCode 官方项目吗?
203
259
 
204
- 不是。它集成 OpenCode,但不是 OpenCode 官方项目。
260
+ 不是。它集成 OpenCode,但并非 OpenCode 官方团队开发。
@@ -8,12 +8,23 @@ import { fileURLToPath } from "node:url";
8
8
  var GLOBAL_PLUGIN_DIRECTORY_NAME = "opencode-tbot";
9
9
  var GLOBAL_PLUGIN_CONFIG_FILE_NAME = "config.json";
10
10
  var OPENCODE_CONFIG_FILE_NAME = "opencode.json";
11
+ var OPENCODE_CONFIG_JSONC_FILE_NAME = "opencode.jsonc";
11
12
  function getOpenCodeConfigDirectory(homeDir = homedir()) {
12
13
  return join(homeDir, ".config", "opencode");
13
14
  }
14
15
  function getOpenCodeConfigFilePath(homeDir = homedir()) {
15
16
  return join(getOpenCodeConfigDirectory(homeDir), OPENCODE_CONFIG_FILE_NAME);
16
17
  }
18
+ function getOpenCodeJsoncConfigFilePath(homeDir = homedir()) {
19
+ return join(getOpenCodeConfigDirectory(homeDir), OPENCODE_CONFIG_JSONC_FILE_NAME);
20
+ }
21
+ async function resolveWritableOpenCodeConfigFilePath(homeDir = homedir()) {
22
+ const jsoncConfigFilePath = getOpenCodeJsoncConfigFilePath(homeDir);
23
+ if (await pathExists$1(jsoncConfigFilePath)) return jsoncConfigFilePath;
24
+ const jsonConfigFilePath = getOpenCodeConfigFilePath(homeDir);
25
+ if (await pathExists$1(jsonConfigFilePath)) return jsonConfigFilePath;
26
+ return jsonConfigFilePath;
27
+ }
17
28
  function getGlobalPluginConfigFilePath(homeDir = homedir()) {
18
29
  return join(getOpenCodeConfigDirectory(homeDir), GLOBAL_PLUGIN_DIRECTORY_NAME, GLOBAL_PLUGIN_CONFIG_FILE_NAME);
19
30
  }
@@ -23,6 +34,18 @@ function getOpenCodeLogDirectory(homeDir = homedir()) {
23
34
  function getDefaultPluginLogDirectory(homeDir = homedir()) {
24
35
  return join(getOpenCodeLogDirectory(homeDir), "plugins", "opencode-tbot");
25
36
  }
37
+ async function pathExists$1(filePath) {
38
+ try {
39
+ await access(filePath);
40
+ return true;
41
+ } catch (error) {
42
+ if (isMissingFileError$1(error)) return false;
43
+ throw error;
44
+ }
45
+ }
46
+ function isMissingFileError$1(error) {
47
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
48
+ }
26
49
  //#endregion
27
50
  //#region src/app/config.ts
28
51
  var DEFAULT_STATE_FILE_PATH = "./data/opencode-tbot.state.json";
@@ -262,6 +285,6 @@ function stripLegacyVoiceConfig(config) {
262
285
  return rest;
263
286
  }
264
287
  //#endregion
265
- export { DEFAULT_TELEGRAM_API_ROOT as a, getGlobalPluginConfigFilePath as c, OPENCODE_TBOT_VERSION as i, getOpenCodeConfigFilePath as l, preparePluginConfiguration as n, loadAppConfig as o, writePluginConfigFile as r, getDefaultPluginLogDirectory as s, mergePluginConfigSources as t };
288
+ export { DEFAULT_TELEGRAM_API_ROOT as a, getGlobalPluginConfigFilePath as c, OPENCODE_TBOT_VERSION as i, resolveWritableOpenCodeConfigFilePath as l, preparePluginConfiguration as n, loadAppConfig as o, writePluginConfigFile as r, getDefaultPluginLogDirectory as s, mergePluginConfigSources as t };
266
289
 
267
- //# sourceMappingURL=plugin-config-jkAZYbFW.js.map
290
+ //# sourceMappingURL=plugin-config-LIr8LS0-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-config-LIr8LS0-.js","names":[],"sources":["../../src/app/opencode-paths.ts","../../src/app/config.ts","../../src/app/package-info.ts","../../src/app/plugin-config.ts"],"sourcesContent":["import { access } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const GLOBAL_PLUGIN_DIRECTORY_NAME = \"opencode-tbot\";\nexport const GLOBAL_PLUGIN_CONFIG_FILE_NAME = \"config.json\";\nexport const OPENCODE_CONFIG_FILE_NAME = \"opencode.json\";\nexport const OPENCODE_CONFIG_JSONC_FILE_NAME = \"opencode.jsonc\";\n\nexport function getOpenCodeConfigDirectory(homeDir: string = homedir()): string {\n return join(homeDir, \".config\", \"opencode\");\n}\n\nexport function getOpenCodeConfigFilePath(homeDir: string = homedir()): string {\n return join(getOpenCodeConfigDirectory(homeDir), OPENCODE_CONFIG_FILE_NAME);\n}\n\nexport function getOpenCodeJsoncConfigFilePath(homeDir: string = homedir()): string {\n return join(getOpenCodeConfigDirectory(homeDir), OPENCODE_CONFIG_JSONC_FILE_NAME);\n}\n\nexport async function resolveWritableOpenCodeConfigFilePath(homeDir: string = homedir()): Promise<string> {\n const jsoncConfigFilePath = getOpenCodeJsoncConfigFilePath(homeDir);\n\n if (await pathExists(jsoncConfigFilePath)) {\n return jsoncConfigFilePath;\n }\n\n const jsonConfigFilePath = getOpenCodeConfigFilePath(homeDir);\n\n if (await pathExists(jsonConfigFilePath)) {\n return jsonConfigFilePath;\n }\n\n return jsonConfigFilePath;\n}\n\nexport function getGlobalPluginConfigFilePath(homeDir: string = homedir()): string {\n return join(\n getOpenCodeConfigDirectory(homeDir),\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n );\n}\n\nexport function getOpenCodeLogDirectory(homeDir: string = homedir()): string {\n return join(homeDir, \".local\", \"share\", \"opencode\", \"log\");\n}\n\nexport function getDefaultPluginLogDirectory(homeDir: string = homedir()): string {\n return join(getOpenCodeLogDirectory(homeDir), \"plugins\", \"opencode-tbot\");\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch (error) {\n if (isMissingFileError(error)) {\n return false;\n }\n\n throw error;\n }\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n","import { homedir } from \"node:os\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport { z } from \"zod\";\nimport { getDefaultPluginLogDirectory } from \"./opencode-paths.js\";\n\nexport const DEFAULT_STATE_FILE_PATH = \"./data/opencode-tbot.state.json\";\nexport const DEFAULT_TELEGRAM_API_ROOT = \"https://api.telegram.org\";\nexport const DEFAULT_PROMPT_WAIT_TIMEOUT_MS = 1_800_000;\nexport const DEFAULT_PROMPT_POLL_REQUEST_TIMEOUT_MS = 15_000;\nexport const DEFAULT_PROMPT_RECOVERY_INACTIVITY_TIMEOUT_MS = 120_000;\nexport const DEFAULT_LOG_LEVEL = \"info\";\nexport const DEFAULT_LOG_RETENTION_MAX_FILES = 30;\nexport const DEFAULT_LOG_RETENTION_MAX_TOTAL_BYTES = 314_572_800;\n\nconst AllowedChatIdSchema = z.union([\n z.number().int(),\n z.string().regex(/^-?\\d+$/u).transform((value) => Number(value)),\n]);\n\nconst TelegramConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n botToken: z.string().trim().min(1),\n allowedChatIds: z.array(AllowedChatIdSchema).default([]),\n apiRoot: z.string().trim().url().default(DEFAULT_TELEGRAM_API_ROOT),\n }),\n);\n\nconst StateConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n path: z.string().trim().min(1).default(DEFAULT_STATE_FILE_PATH),\n }),\n);\n\nconst PromptConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n waitTimeoutMs: z.number().int().positive().default(DEFAULT_PROMPT_WAIT_TIMEOUT_MS),\n pollRequestTimeoutMs: z.number().int().positive().default(DEFAULT_PROMPT_POLL_REQUEST_TIMEOUT_MS),\n recoveryInactivityTimeoutMs: z.number().int().positive().default(DEFAULT_PROMPT_RECOVERY_INACTIVITY_TIMEOUT_MS),\n }),\n);\n\nconst LoggingConfigSchema = z.preprocess(\n (value) => value ?? {},\n z.object({\n level: z.string().trim().min(1).optional(),\n sinks: z.preprocess(\n (value) => value ?? {},\n z.object({\n host: z.boolean().default(true),\n file: z.boolean().default(true),\n }),\n ),\n file: z.preprocess(\n (value) => value ?? {},\n z.object({\n dir: z.string().trim().min(1).optional(),\n retention: z.preprocess(\n (value) => value ?? {},\n z.object({\n maxFiles: z.number().int().positive().default(DEFAULT_LOG_RETENTION_MAX_FILES),\n maxTotalBytes: z.number().int().positive().default(DEFAULT_LOG_RETENTION_MAX_TOTAL_BYTES),\n }),\n ),\n }),\n ),\n }),\n);\n\nconst AppConfigSchema = z.object({\n telegram: TelegramConfigSchema,\n state: StateConfigSchema,\n prompt: PromptConfigSchema,\n logging: LoggingConfigSchema.default({}),\n logLevel: z.string().trim().min(1).optional(),\n});\n\nexport interface PluginConfigSource {\n telegram?: {\n botToken?: string;\n allowedChatIds?: Array<number | string>;\n apiRoot?: string;\n [key: string]: unknown;\n };\n state?: {\n path?: string;\n [key: string]: unknown;\n };\n prompt?: {\n waitTimeoutMs?: number;\n pollRequestTimeoutMs?: number;\n recoveryInactivityTimeoutMs?: number;\n [key: string]: unknown;\n };\n logging?: {\n level?: string;\n sinks?: {\n host?: boolean;\n file?: boolean;\n [key: string]: unknown;\n };\n file?: {\n dir?: string;\n retention?: {\n maxFiles?: number;\n maxTotalBytes?: number;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n [key: string]: unknown;\n };\n logLevel?: string;\n [key: string]: unknown;\n}\n\nexport interface AppConfig {\n telegramBotToken: string;\n telegramAllowedChatIds: number[];\n telegramApiRoot: string;\n logLevel: string;\n loggingLevel: string;\n loggingHostSinkEnabled: boolean;\n loggingFileSinkEnabled: boolean;\n loggingFileDir: string;\n loggingRetentionMaxFiles: number;\n loggingRetentionMaxTotalBytes: number;\n promptWaitTimeoutMs: number;\n promptPollRequestTimeoutMs: number;\n promptRecoveryInactivityTimeoutMs: number;\n stateFilePath: string;\n worktreePath: string;\n}\n\nexport interface LoadAppConfigOptions {\n cwd?: string;\n}\n\nexport function loadAppConfig(\n configSource: PluginConfigSource | undefined = {},\n options: LoadAppConfigOptions = {},\n): AppConfig {\n const parsed = parseConfig(AppConfigSchema, configSource);\n\n return buildAppConfig(parsed, options);\n}\n\nexport const loadPluginConfig = loadAppConfig;\n\nfunction buildAppConfig(\n data: z.infer<typeof AppConfigSchema>,\n options: LoadAppConfigOptions,\n): AppConfig {\n const cwd = options.cwd ?? process.cwd();\n const loggingLevel = normalizeLogLevelValue(data.logging.level ?? data.logLevel);\n\n return {\n telegramBotToken: data.telegram.botToken,\n telegramAllowedChatIds: data.telegram.allowedChatIds,\n telegramApiRoot: normalizeApiRoot(data.telegram.apiRoot),\n logLevel: loggingLevel,\n loggingLevel,\n loggingHostSinkEnabled: data.logging.sinks.host,\n loggingFileSinkEnabled: data.logging.sinks.file,\n loggingFileDir: resolveLoggingDirectory(\n data.logging.file.dir,\n cwd,\n ),\n loggingRetentionMaxFiles: data.logging.file.retention.maxFiles,\n loggingRetentionMaxTotalBytes: data.logging.file.retention.maxTotalBytes,\n promptWaitTimeoutMs: data.prompt.waitTimeoutMs,\n promptPollRequestTimeoutMs: data.prompt.pollRequestTimeoutMs,\n promptRecoveryInactivityTimeoutMs: data.prompt.recoveryInactivityTimeoutMs,\n stateFilePath: resolveStatePath(data, cwd),\n worktreePath: cwd,\n };\n}\n\nfunction resolveStatePath(\n data: z.infer<typeof AppConfigSchema>,\n cwd: string,\n): string {\n return resolve(cwd, data.state.path || DEFAULT_STATE_FILE_PATH);\n}\n\nfunction normalizeApiRoot(value: string): string {\n const normalized = value.trim();\n\n return normalized.endsWith(\"/\")\n ? normalized.slice(0, -1)\n : normalized;\n}\n\nfunction normalizeLogLevelValue(value: string | undefined): string {\n const normalized = value?.trim().toLowerCase();\n\n switch (normalized) {\n case \"debug\":\n case \"warn\":\n case \"error\":\n case \"info\":\n return normalized;\n default:\n return DEFAULT_LOG_LEVEL;\n }\n}\n\nfunction resolveLoggingDirectory(value: string | undefined, cwd: string): string {\n const normalized = value?.trim();\n\n if (!normalized) {\n return getDefaultPluginLogDirectory(homedir());\n }\n\n return isAbsolute(normalized)\n ? normalized\n : resolve(cwd, normalized);\n}\n\nfunction parseConfig<TSchema extends z.ZodTypeAny>(\n schema: TSchema,\n configSource: PluginConfigSource | undefined,\n): z.infer<TSchema> {\n const parsed = schema.safeParse(configSource ?? {});\n\n if (parsed.success) {\n return parsed.data;\n }\n\n throw new Error(\n `Invalid plugin configuration: ${JSON.stringify(parsed.error.flatten())}`,\n );\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport const OPENCODE_TBOT_VERSION = resolvePackageVersion();\n\nfunction resolvePackageVersion(): string {\n let directory = dirname(fileURLToPath(import.meta.url));\n\n while (true) {\n const packageFilePath = join(directory, \"package.json\");\n\n if (existsSync(packageFilePath)) {\n try {\n const parsed = JSON.parse(readFileSync(packageFilePath, \"utf8\")) as {\n version?: unknown;\n };\n\n if (typeof parsed.version === \"string\" && parsed.version.trim().length > 0) {\n return parsed.version;\n }\n } catch {\n // Fall through and continue searching parent directories.\n }\n }\n\n const parentDirectory = dirname(directory);\n\n if (parentDirectory === directory) {\n break;\n }\n\n directory = parentDirectory;\n }\n\n return \"unknown\";\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { PluginConfigSource } from \"./config.js\";\nimport {\n getDefaultPluginLogDirectory,\n getGlobalPluginConfigFilePath,\n getOpenCodeConfigDirectory,\n getOpenCodeConfigFilePath,\n getOpenCodeJsoncConfigFilePath,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n OPENCODE_CONFIG_FILE_NAME,\n OPENCODE_CONFIG_JSONC_FILE_NAME,\n resolveWritableOpenCodeConfigFilePath,\n} from \"./opencode-paths.js\";\n\nexport const PLUGIN_CONFIG_FILE_NAME = \"tbot.config.json\";\n\nexport interface PreparedPluginConfiguration {\n cwd: string;\n config: PluginConfigSource;\n globalConfigFilePath: string;\n projectConfigFilePath: string;\n ignoredProjectConfigFilePath?: string;\n configFilePath: string;\n}\n\nexport interface PreparePluginConfigurationOptions {\n cwd: string;\n config?: PluginConfigSource;\n homeDir?: string;\n}\n\nexport async function preparePluginConfiguration(\n options: PreparePluginConfigurationOptions,\n): Promise<PreparedPluginConfiguration> {\n const homeDir = options.homeDir ?? homedir();\n const globalConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const projectConfigFilePath = await resolveProjectPluginConfigFilePath(options.cwd);\n const [globalConfig, hasIgnoredProjectConfig] = await Promise.all([\n loadPluginConfigFile(globalConfigFilePath),\n pathExists(projectConfigFilePath),\n ]);\n const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, options.config));\n const ignoredProjectConfigFilePath = hasIgnoredProjectConfig\n ? projectConfigFilePath\n : undefined;\n const configFilePath = globalConfigFilePath;\n\n return {\n cwd: options.cwd,\n config,\n globalConfigFilePath,\n projectConfigFilePath,\n ...(ignoredProjectConfigFilePath ? { ignoredProjectConfigFilePath } : {}),\n configFilePath,\n };\n}\n\nexport {\n getDefaultPluginLogDirectory,\n getGlobalPluginConfigFilePath,\n getOpenCodeConfigDirectory,\n getOpenCodeConfigFilePath,\n getOpenCodeJsoncConfigFilePath,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n OPENCODE_CONFIG_FILE_NAME,\n OPENCODE_CONFIG_JSONC_FILE_NAME,\n resolveWritableOpenCodeConfigFilePath,\n};\n\nexport async function writePluginConfigFile(\n configFilePath: string,\n config: PluginConfigSource,\n): Promise<void> {\n await mkdir(dirname(configFilePath), { recursive: true });\n await writeFile(configFilePath, serializePluginConfig(config), \"utf8\");\n}\n\nexport function mergePluginConfigSources(\n ...sources: Array<PluginConfigSource | undefined>\n): PluginConfigSource {\n const merged: PluginConfigSource = {};\n\n for (const source of sources) {\n if (!source) {\n continue;\n }\n\n const normalized = source;\n const previousTelegram = merged.telegram;\n const previousState = merged.state;\n const previousPrompt = merged.prompt;\n const previousLogging = merged.logging;\n\n Object.assign(merged, normalized);\n\n if (normalized.telegram) {\n merged.telegram = {\n ...(previousTelegram ?? {}),\n ...normalized.telegram,\n };\n }\n\n if (normalized.state) {\n merged.state = {\n ...(previousState ?? {}),\n ...normalized.state,\n };\n }\n\n if (normalized.prompt) {\n merged.prompt = {\n ...(previousPrompt ?? {}),\n ...normalized.prompt,\n };\n }\n\n if (normalized.logging) {\n const previousLoggingSinks = previousLogging?.sinks;\n const previousLoggingFile = previousLogging?.file;\n const previousRetention = previousLoggingFile?.retention;\n\n merged.logging = {\n ...(previousLogging ?? {}),\n ...normalized.logging,\n ...(normalized.logging.sinks || previousLoggingSinks\n ? {\n sinks: {\n ...(previousLoggingSinks ?? {}),\n ...(normalized.logging.sinks ?? {}),\n },\n }\n : {}),\n ...(normalized.logging.file || previousLoggingFile\n ? {\n file: {\n ...(previousLoggingFile ?? {}),\n ...(normalized.logging.file ?? {}),\n ...(normalized.logging.file?.retention || previousRetention\n ? {\n retention: {\n ...(previousRetention ?? {}),\n ...(normalized.logging.file?.retention ?? {}),\n },\n }\n : {}),\n },\n }\n : {}),\n };\n }\n }\n\n return merged;\n}\n\nexport function serializePluginConfig(config: PluginConfigSource): string {\n return `${JSON.stringify(orderPluginConfig(config), null, 2)}\\n`;\n}\n\nasync function loadPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n\n return parsePluginConfigText(content, configFilePath);\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nfunction parsePluginConfigText(\n content: string,\n configFilePath: string,\n): PluginConfigSource {\n try {\n const parsed = JSON.parse(content) as unknown;\n\n if (!isPlainObject(parsed)) {\n throw new Error(\"Config root must be a JSON object.\");\n }\n\n return parsed as PluginConfigSource;\n } catch (error) {\n throw new Error(\n [\n `Failed to parse ${configFilePath} as JSON.`,\n error instanceof Error ? error.message : String(error),\n ].join(\" \"),\n );\n }\n}\n\nfunction orderPluginConfig(config: PluginConfigSource): PluginConfigSource {\n const prioritizedKeys = new Set([\n \"telegram\",\n \"state\",\n \"prompt\",\n \"logging\",\n \"logLevel\",\n ]);\n const ordered: PluginConfigSource = {};\n\n if (config.telegram) {\n ordered.telegram = config.telegram;\n }\n\n if (config.state) {\n ordered.state = config.state;\n }\n\n if (config.prompt) {\n ordered.prompt = config.prompt;\n }\n\n if (config.logging) {\n ordered.logging = config.logging;\n }\n\n if (config.logLevel !== undefined) {\n ordered.logLevel = config.logLevel;\n }\n\n for (const [key, value] of Object.entries(config)) {\n if (!prioritizedKeys.has(key)) {\n ordered[key] = value;\n }\n }\n\n return ordered;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nasync function resolveProjectPluginConfigFilePath(cwd: string): Promise<string> {\n const preferredPath = join(cwd, PLUGIN_CONFIG_FILE_NAME);\n\n return preferredPath;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch (error) {\n if (isMissingFileError(error)) {\n return false;\n }\n\n throw error;\n }\n}\n\nfunction stripLegacyVoiceConfig(config: PluginConfigSource): PluginConfigSource {\n const { openrouter: _openrouter, ...rest } = config as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return rest;\n}\n"],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- import { c as getGlobalPluginConfigFilePath, i as OPENCODE_TBOT_VERSION, l as getOpenCodeConfigFilePath, r as writePluginConfigFile, s as getDefaultPluginLogDirectory, t as mergePluginConfigSources } from "./assets/plugin-config-jkAZYbFW.js";
1
+ import { c as getGlobalPluginConfigFilePath, i as OPENCODE_TBOT_VERSION, l as resolveWritableOpenCodeConfigFilePath, r as writePluginConfigFile, s as getDefaultPluginLogDirectory, t as mergePluginConfigSources } from "./assets/plugin-config-LIr8LS0-.js";
2
2
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
@@ -38,7 +38,7 @@ async function runCli(argv) {
38
38
  }
39
39
  async function installPlugin(options = {}) {
40
40
  const homeDir = options.homeDir ?? homedir();
41
- const openCodeConfigFilePath = getOpenCodeConfigFilePath(homeDir);
41
+ const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);
42
42
  const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);
43
43
  const openCodeConfig = await readJsoncObject(openCodeConfigFilePath);
44
44
  const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);
@@ -53,6 +53,7 @@ async function installPlugin(options = {}) {
53
53
  else await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);
54
54
  await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);
55
55
  stdout.write("Success.\n");
56
+ stdout.write(`OpenCode config: ${openCodeConfigFilePath}\n`);
56
57
  stdout.write(`Plugin config: ${globalPluginConfigFilePath}\n`);
57
58
  stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\n`);
58
59
  await warnAboutLegacyLocalInstallations(homeDir);
@@ -62,9 +63,10 @@ async function installPlugin(options = {}) {
62
63
  }
63
64
  async function updatePlugin(options = {}) {
64
65
  const homeDir = options.homeDir ?? homedir();
65
- const openCodeConfigFilePath = getOpenCodeConfigFilePath(homeDir);
66
+ const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);
66
67
  await writeJsonFile(openCodeConfigFilePath, replacePluginRegistration(await readJsoncObject(openCodeConfigFilePath), normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC));
67
68
  stdout.write("Success.\n");
69
+ stdout.write(`OpenCode config: ${openCodeConfigFilePath}\n`);
68
70
  stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\n`);
69
71
  stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\n`);
70
72
  await warnAboutLegacyLocalInstallations(homeDir);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { stderr, stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parse } from \"jsonc-parser\";\nimport {\n DEFAULT_TELEGRAM_API_ROOT,\n type PluginConfigSource,\n} from \"./app/config.js\";\nimport { OPENCODE_TBOT_VERSION } from \"./app/package-info.js\";\nimport {\n mergePluginConfigSources,\n writePluginConfigFile,\n} from \"./app/plugin-config.js\";\nimport {\n getDefaultPluginLogDirectory,\n getGlobalPluginConfigFilePath,\n getOpenCodeConfigFilePath,\n} from \"./app/opencode-paths.js\";\n\nexport interface InstallCommandOptions {\n botToken?: string;\n homeDir?: string;\n pluginSpec?: string;\n registerPlugin?: boolean;\n telegramApiRoot?: string;\n}\n\ninterface CliOptions extends InstallCommandOptions {\n command: \"help\" | \"install\" | \"update\" | \"version\";\n}\n\nexport interface PromptSessionLike {\n ask(question: string): Promise<string>;\n askSecret(question: string): Promise<string>;\n confirm(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nexport interface CreatePromptSessionOptions {\n input?: NodeJS.ReadStream;\n output?: NodeJS.WriteStream;\n}\n\ninterface OpenCodeGlobalConfig {\n plugin?: string[];\n [key: string]: unknown;\n}\n\nconst DEFAULT_PLUGIN_SPEC = \"opencode-tbot@latest\";\nconst PROMPT_CANCELLED_ERROR = \"Prompt cancelled.\";\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n try {\n const exitCode = await runCli(argv);\n process.exitCode = exitCode;\n\n return exitCode;\n } catch (error) {\n stderr.write(`${formatCliError(error)}\\n`);\n process.exitCode = 1;\n\n return 1;\n }\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const options = parseCliOptions(argv);\n\n if (options.command === \"help\") {\n stdout.write(`${buildHelpText()}\\n`);\n return 0;\n }\n\n if (options.command === \"version\") {\n stdout.write(`${OPENCODE_TBOT_VERSION}\\n`);\n return 0;\n }\n\n if (options.command === \"update\") {\n await updatePlugin(options);\n return 0;\n }\n\n await installPlugin(options);\n return 0;\n}\n\nexport async function installPlugin(options: InstallCommandOptions = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = getOpenCodeConfigFilePath(homeDir);\n const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);\n const prompt = createPromptSession();\n\n try {\n const botToken = normalizeRequiredString(\n options.botToken ?? await prompt.askSecret(\"Telegram bot token: \"),\n \"Telegram bot token is required.\",\n );\n const telegramApiRoot = normalizeOptionalString(options.telegramApiRoot) ?? DEFAULT_TELEGRAM_API_ROOT;\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = options.registerPlugin === false\n ? openCodeConfig\n : ensurePluginRegistered(openCodeConfig, pluginSpec);\n const nextPluginConfig = buildInstalledPluginConfig(\n existingPluginConfig,\n botToken,\n telegramApiRoot,\n );\n\n if (options.registerPlugin === false) {\n await ensureParentDirectory(openCodeConfigFilePath);\n } else {\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n }\n await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);\n\n stdout.write(\"Success.\\n\");\n stdout.write(`Plugin config: ${globalPluginConfigFilePath}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n } finally {\n prompt.close();\n }\n}\n\nexport async function updatePlugin(options: Pick<InstallCommandOptions, \"homeDir\" | \"pluginSpec\"> = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = getOpenCodeConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = replacePluginRegistration(openCodeConfig, pluginSpec);\n\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n stdout.write(\"Success.\\n\");\n stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n}\n\nfunction parseCliOptions(argv: string[]): CliOptions {\n const args = [...argv];\n const first = args[0];\n const command = !first || first.startsWith(\"-\")\n ? \"install\"\n : first;\n const values = command === \"install\" || command === \"version\"\n ? args\n : args.slice(1);\n const options: CliOptions = {\n command: command === \"help\" || command === \"--help\" || command === \"-h\"\n ? \"help\"\n : command === \"version\" || command === \"--version\" || command === \"-v\"\n ? \"version\"\n : command === \"update\"\n ? \"update\"\n : \"install\",\n };\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n\n if (index === 0 && !value.startsWith(\"-\")) {\n continue;\n }\n\n switch (value) {\n case \"--bot-token\":\n options.botToken = values[++index];\n break;\n case \"--plugin-spec\":\n options.pluginSpec = values[++index];\n break;\n case \"--telegram-api-root\":\n options.telegramApiRoot = values[++index];\n break;\n case \"--skip-register\":\n options.registerPlugin = false;\n break;\n case \"--home-dir\":\n options.homeDir = values[++index];\n break;\n case \"--help\":\n case \"-h\":\n options.command = \"help\";\n break;\n case \"--version\":\n case \"-v\":\n options.command = \"version\";\n break;\n default:\n throw new Error(`Unknown argument: ${value}`);\n }\n }\n\n return options;\n}\n\nfunction buildInstalledPluginConfig(\n current: PluginConfigSource,\n botToken: string,\n telegramApiRoot: string,\n): PluginConfigSource {\n const merged = mergePluginConfigSources(current, {\n telegram: {\n botToken,\n apiRoot: telegramApiRoot,\n },\n });\n const { openrouter: _openrouter, ...nextConfig } = merged as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return nextConfig;\n}\n\nfunction ensurePluginRegistered(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const plugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n\n if (!plugins.includes(pluginSpec)) {\n plugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: plugins,\n };\n}\n\nfunction replacePluginRegistration(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const currentPlugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n const nextPlugins: string[] = [];\n let inserted = false;\n\n for (const currentPlugin of currentPlugins) {\n if (isOpencodeTbotPluginSpec(currentPlugin)) {\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n inserted = true;\n }\n\n continue;\n }\n\n nextPlugins.push(currentPlugin);\n }\n\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: nextPlugins,\n };\n}\n\nasync function readPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as PluginConfigSource\n : {};\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nasync function readJsoncObject<TObject extends Record<string, unknown>>(\n filePath: string,\n): Promise<TObject> {\n try {\n const content = await readFile(filePath, \"utf8\");\n const parsed = parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as TObject\n : {} as TObject;\n } catch (error) {\n if (isMissingFileError(error)) {\n return {} as TObject;\n }\n\n throw error;\n }\n}\n\nasync function writeJsonFile(filePath: string, value: Record<string, unknown>): Promise<void> {\n await ensureParentDirectory(filePath);\n await writeFile(filePath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nasync function ensureParentDirectory(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n}\n\nexport function createPromptSession(options: CreatePromptSessionOptions = {}): PromptSessionLike {\n const input = options.input ?? stdin;\n const output = options.output ?? stdout;\n\n if (!input.isTTY || !output.isTTY) {\n return {\n ask: async () => \"\",\n askSecret: async () => \"\",\n async confirm(_question: string, defaultValue: boolean) {\n return defaultValue;\n },\n close() { },\n };\n }\n\n return {\n ask(question: string) {\n return askQuestion(input, output, question);\n },\n askSecret(question: string) {\n return askSecretQuestion(input, output, question);\n },\n async confirm(question: string, defaultValue: boolean) {\n const answer = normalizeOptionalString(await askQuestion(input, output, question));\n\n if (!answer) {\n return defaultValue;\n }\n\n if ([\"y\", \"yes\"].includes(answer.toLowerCase())) {\n return true;\n }\n\n if ([\"n\", \"no\"].includes(answer.toLowerCase())) {\n return false;\n }\n\n throw new Error(`Unsupported answer: ${answer}`);\n },\n close() { },\n };\n}\n\nasync function askQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n const readline = createInterface({ input, output });\n\n try {\n return await readline.question(question);\n } finally {\n readline.close();\n }\n}\n\nasync function askSecretQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n if (typeof input.setRawMode !== \"function\") {\n return askQuestion(input, output, question);\n }\n\n output.write(question);\n\n try {\n return await readMaskedInput(input, output);\n } finally {\n output.write(\"\\n\");\n }\n}\n\nasync function readMaskedInput(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n): Promise<string> {\n return new Promise((resolvePromise, rejectPromise) => {\n const buffer: string[] = [];\n let settled = false;\n\n const cleanup = () => {\n input.off(\"data\", handleData);\n input.off(\"error\", handleError);\n input.pause();\n input.setRawMode?.(false);\n };\n\n const rejectWith = (error: unknown) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n rejectPromise(error);\n };\n\n const resolveWith = (value: string) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n resolvePromise(value);\n };\n\n const handleError = (error: unknown) => {\n rejectWith(error);\n };\n\n const handleData = (chunk: string | Buffer) => {\n const text = Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : chunk;\n\n for (const character of text) {\n if (character === \"\\r\" || character === \"\\n\") {\n resolveWith(buffer.join(\"\"));\n return;\n }\n\n if (character === \"\\u0003\") {\n rejectWith(new Error(PROMPT_CANCELLED_ERROR));\n return;\n }\n\n if (character === \"\\u0008\" || character === \"\\u007f\") {\n if (buffer.length > 0) {\n buffer.pop();\n output.write(\"\\b \\b\");\n }\n continue;\n }\n\n if (character < \" \" || character === \"\\u007f\") {\n continue;\n }\n\n buffer.push(character);\n output.write(\"*\");\n }\n };\n\n input.setRawMode?.(true);\n input.resume();\n input.on(\"error\", handleError);\n input.on(\"data\", handleData);\n });\n}\n\nfunction normalizeOptionalString(value: string | undefined | null): string | null {\n const normalized = value?.trim();\n\n return normalized\n ? normalized\n : null;\n}\n\nfunction normalizeRequiredString(value: string | undefined | null, errorMessage: string): string {\n const normalized = normalizeOptionalString(value);\n\n if (!normalized) {\n throw new Error(errorMessage);\n }\n\n return normalized;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nfunction isOpencodeTbotPluginSpec(value: string): boolean {\n const normalized = value.trim();\n\n return normalized === \"opencode-tbot\" || normalized.startsWith(\"opencode-tbot@\");\n}\n\nasync function warnAboutLegacyLocalInstallations(homeDir: string): Promise<void> {\n const legacyInstallations = await findLegacyLocalInstallations(homeDir);\n\n if (legacyInstallations.length === 0) {\n return;\n }\n\n stdout.write(\"\\nDetected local opencode-tbot npm installation(s) that can make OpenCode show the plugin as file:///.../node_modules/...\\n\");\n\n for (const installation of legacyInstallations) {\n stdout.write(`- ${installation.packagePath}\\n`);\n stdout.write(` cleanup: cd \"${installation.rootDir}\" && npm uninstall opencode-tbot\\n`);\n }\n\n stdout.write(\"Recommended npm flow: npm exec --package opencode-tbot@latest opencode-tbot -- install\\n\");\n}\n\nasync function findLegacyLocalInstallations(homeDir: string) {\n const roots = [...new Set([\n resolve(process.cwd()),\n resolve(homeDir),\n ])];\n const installations: Array<{ packagePath: string; rootDir: string }> = [];\n\n for (const rootDir of roots) {\n const packagePath = join(rootDir, \"node_modules\", \"opencode-tbot\", \"package.json\");\n\n if (await pathExists(packagePath)) {\n installations.push({\n packagePath: join(rootDir, \"node_modules\", \"opencode-tbot\"),\n rootDir,\n });\n }\n }\n\n return installations;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction buildHelpText(): string {\n return [\n \"Usage: opencode-tbot [install|update] [options]\",\n \" opencode-tbot --version\",\n \"\",\n \"Recommended npm usage:\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- install\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- update\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- --version\",\n \"\",\n \"Commands:\",\n \" install\",\n \" update\",\n \"\",\n \"Options:\",\n \" --bot-token <token>\",\n \" --telegram-api-root <url>\",\n \" --plugin-spec <spec>\",\n \" --skip-register\",\n \" --home-dir <path>\",\n \" --version\",\n \" --help\",\n ].join(\"\\n\");\n}\n\nfunction formatCliError(error: unknown): string {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { mkdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { stderr, stdin, stdout } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parse } from \"jsonc-parser\";\nimport {\n DEFAULT_TELEGRAM_API_ROOT,\n type PluginConfigSource,\n} from \"./app/config.js\";\nimport { OPENCODE_TBOT_VERSION } from \"./app/package-info.js\";\nimport {\n mergePluginConfigSources,\n writePluginConfigFile,\n} from \"./app/plugin-config.js\";\nimport {\n getDefaultPluginLogDirectory,\n getGlobalPluginConfigFilePath,\n resolveWritableOpenCodeConfigFilePath,\n} from \"./app/opencode-paths.js\";\n\nexport interface InstallCommandOptions {\n botToken?: string;\n homeDir?: string;\n pluginSpec?: string;\n registerPlugin?: boolean;\n telegramApiRoot?: string;\n}\n\ninterface CliOptions extends InstallCommandOptions {\n command: \"help\" | \"install\" | \"update\" | \"version\";\n}\n\nexport interface PromptSessionLike {\n ask(question: string): Promise<string>;\n askSecret(question: string): Promise<string>;\n confirm(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nexport interface CreatePromptSessionOptions {\n input?: NodeJS.ReadStream;\n output?: NodeJS.WriteStream;\n}\n\ninterface OpenCodeGlobalConfig {\n plugin?: string[];\n [key: string]: unknown;\n}\n\nconst DEFAULT_PLUGIN_SPEC = \"opencode-tbot@latest\";\nconst PROMPT_CANCELLED_ERROR = \"Prompt cancelled.\";\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n try {\n const exitCode = await runCli(argv);\n process.exitCode = exitCode;\n\n return exitCode;\n } catch (error) {\n stderr.write(`${formatCliError(error)}\\n`);\n process.exitCode = 1;\n\n return 1;\n }\n}\n\nexport async function runCli(argv: string[]): Promise<number> {\n const options = parseCliOptions(argv);\n\n if (options.command === \"help\") {\n stdout.write(`${buildHelpText()}\\n`);\n return 0;\n }\n\n if (options.command === \"version\") {\n stdout.write(`${OPENCODE_TBOT_VERSION}\\n`);\n return 0;\n }\n\n if (options.command === \"update\") {\n await updatePlugin(options);\n return 0;\n }\n\n await installPlugin(options);\n return 0;\n}\n\nexport async function installPlugin(options: InstallCommandOptions = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);\n const prompt = createPromptSession();\n\n try {\n const botToken = normalizeRequiredString(\n options.botToken ?? await prompt.askSecret(\"Telegram bot token: \"),\n \"Telegram bot token is required.\",\n );\n const telegramApiRoot = normalizeOptionalString(options.telegramApiRoot) ?? DEFAULT_TELEGRAM_API_ROOT;\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = options.registerPlugin === false\n ? openCodeConfig\n : ensurePluginRegistered(openCodeConfig, pluginSpec);\n const nextPluginConfig = buildInstalledPluginConfig(\n existingPluginConfig,\n botToken,\n telegramApiRoot,\n );\n\n if (options.registerPlugin === false) {\n await ensureParentDirectory(openCodeConfigFilePath);\n } else {\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n }\n await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);\n\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin config: ${globalPluginConfigFilePath}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n } finally {\n prompt.close();\n }\n}\n\nexport async function updatePlugin(options: Pick<InstallCommandOptions, \"homeDir\" | \"pluginSpec\"> = {}): Promise<void> {\n const homeDir = options.homeDir ?? homedir();\n const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);\n const openCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;\n const nextOpenCodeConfig = replacePluginRegistration(openCodeConfig, pluginSpec);\n\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\\n`);\n stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\\n`);\n await warnAboutLegacyLocalInstallations(homeDir);\n}\n\nfunction parseCliOptions(argv: string[]): CliOptions {\n const args = [...argv];\n const first = args[0];\n const command = !first || first.startsWith(\"-\")\n ? \"install\"\n : first;\n const values = command === \"install\" || command === \"version\"\n ? args\n : args.slice(1);\n const options: CliOptions = {\n command: command === \"help\" || command === \"--help\" || command === \"-h\"\n ? \"help\"\n : command === \"version\" || command === \"--version\" || command === \"-v\"\n ? \"version\"\n : command === \"update\"\n ? \"update\"\n : \"install\",\n };\n\n for (let index = 0; index < values.length; index += 1) {\n const value = values[index];\n\n if (index === 0 && !value.startsWith(\"-\")) {\n continue;\n }\n\n switch (value) {\n case \"--bot-token\":\n options.botToken = values[++index];\n break;\n case \"--plugin-spec\":\n options.pluginSpec = values[++index];\n break;\n case \"--telegram-api-root\":\n options.telegramApiRoot = values[++index];\n break;\n case \"--skip-register\":\n options.registerPlugin = false;\n break;\n case \"--home-dir\":\n options.homeDir = values[++index];\n break;\n case \"--help\":\n case \"-h\":\n options.command = \"help\";\n break;\n case \"--version\":\n case \"-v\":\n options.command = \"version\";\n break;\n default:\n throw new Error(`Unknown argument: ${value}`);\n }\n }\n\n return options;\n}\n\nfunction buildInstalledPluginConfig(\n current: PluginConfigSource,\n botToken: string,\n telegramApiRoot: string,\n): PluginConfigSource {\n const merged = mergePluginConfigSources(current, {\n telegram: {\n botToken,\n apiRoot: telegramApiRoot,\n },\n });\n const { openrouter: _openrouter, ...nextConfig } = merged as PluginConfigSource & {\n openrouter?: unknown;\n };\n\n return nextConfig;\n}\n\nfunction ensurePluginRegistered(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const plugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n\n if (!plugins.includes(pluginSpec)) {\n plugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: plugins,\n };\n}\n\nfunction replacePluginRegistration(\n config: OpenCodeGlobalConfig,\n pluginSpec: string,\n): OpenCodeGlobalConfig {\n const currentPlugins = Array.isArray(config.plugin)\n ? config.plugin.filter((item): item is string => typeof item === \"string\")\n : [];\n const nextPlugins: string[] = [];\n let inserted = false;\n\n for (const currentPlugin of currentPlugins) {\n if (isOpencodeTbotPluginSpec(currentPlugin)) {\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n inserted = true;\n }\n\n continue;\n }\n\n nextPlugins.push(currentPlugin);\n }\n\n if (!inserted) {\n nextPlugins.push(pluginSpec);\n }\n\n return {\n ...config,\n plugin: nextPlugins,\n };\n}\n\nasync function readPluginConfigFile(configFilePath: string): Promise<PluginConfigSource> {\n try {\n const content = await readFile(configFilePath, \"utf8\");\n const parsed = JSON.parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as PluginConfigSource\n : {};\n } catch (error) {\n if (isMissingFileError(error)) {\n return {};\n }\n\n throw error;\n }\n}\n\nasync function readJsoncObject<TObject extends Record<string, unknown>>(\n filePath: string,\n): Promise<TObject> {\n try {\n const content = await readFile(filePath, \"utf8\");\n const parsed = parse(content) as unknown;\n\n return isPlainObject(parsed)\n ? parsed as TObject\n : {} as TObject;\n } catch (error) {\n if (isMissingFileError(error)) {\n return {} as TObject;\n }\n\n throw error;\n }\n}\n\nasync function writeJsonFile(filePath: string, value: Record<string, unknown>): Promise<void> {\n await ensureParentDirectory(filePath);\n await writeFile(filePath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nasync function ensureParentDirectory(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n}\n\nexport function createPromptSession(options: CreatePromptSessionOptions = {}): PromptSessionLike {\n const input = options.input ?? stdin;\n const output = options.output ?? stdout;\n\n if (!input.isTTY || !output.isTTY) {\n return {\n ask: async () => \"\",\n askSecret: async () => \"\",\n async confirm(_question: string, defaultValue: boolean) {\n return defaultValue;\n },\n close() { },\n };\n }\n\n return {\n ask(question: string) {\n return askQuestion(input, output, question);\n },\n askSecret(question: string) {\n return askSecretQuestion(input, output, question);\n },\n async confirm(question: string, defaultValue: boolean) {\n const answer = normalizeOptionalString(await askQuestion(input, output, question));\n\n if (!answer) {\n return defaultValue;\n }\n\n if ([\"y\", \"yes\"].includes(answer.toLowerCase())) {\n return true;\n }\n\n if ([\"n\", \"no\"].includes(answer.toLowerCase())) {\n return false;\n }\n\n throw new Error(`Unsupported answer: ${answer}`);\n },\n close() { },\n };\n}\n\nasync function askQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n const readline = createInterface({ input, output });\n\n try {\n return await readline.question(question);\n } finally {\n readline.close();\n }\n}\n\nasync function askSecretQuestion(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n question: string,\n): Promise<string> {\n if (typeof input.setRawMode !== \"function\") {\n return askQuestion(input, output, question);\n }\n\n output.write(question);\n\n try {\n return await readMaskedInput(input, output);\n } finally {\n output.write(\"\\n\");\n }\n}\n\nasync function readMaskedInput(\n input: NodeJS.ReadStream,\n output: NodeJS.WriteStream,\n): Promise<string> {\n return new Promise((resolvePromise, rejectPromise) => {\n const buffer: string[] = [];\n let settled = false;\n\n const cleanup = () => {\n input.off(\"data\", handleData);\n input.off(\"error\", handleError);\n input.pause();\n input.setRawMode?.(false);\n };\n\n const rejectWith = (error: unknown) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n rejectPromise(error);\n };\n\n const resolveWith = (value: string) => {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n resolvePromise(value);\n };\n\n const handleError = (error: unknown) => {\n rejectWith(error);\n };\n\n const handleData = (chunk: string | Buffer) => {\n const text = Buffer.isBuffer(chunk) ? chunk.toString(\"utf8\") : chunk;\n\n for (const character of text) {\n if (character === \"\\r\" || character === \"\\n\") {\n resolveWith(buffer.join(\"\"));\n return;\n }\n\n if (character === \"\\u0003\") {\n rejectWith(new Error(PROMPT_CANCELLED_ERROR));\n return;\n }\n\n if (character === \"\\u0008\" || character === \"\\u007f\") {\n if (buffer.length > 0) {\n buffer.pop();\n output.write(\"\\b \\b\");\n }\n continue;\n }\n\n if (character < \" \" || character === \"\\u007f\") {\n continue;\n }\n\n buffer.push(character);\n output.write(\"*\");\n }\n };\n\n input.setRawMode?.(true);\n input.resume();\n input.on(\"error\", handleError);\n input.on(\"data\", handleData);\n });\n}\n\nfunction normalizeOptionalString(value: string | undefined | null): string | null {\n const normalized = value?.trim();\n\n return normalized\n ? normalized\n : null;\n}\n\nfunction normalizeRequiredString(value: string | undefined | null, errorMessage: string): string {\n const normalized = normalizeOptionalString(value);\n\n if (!normalized) {\n throw new Error(errorMessage);\n }\n\n return normalized;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isMissingFileError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nfunction isOpencodeTbotPluginSpec(value: string): boolean {\n const normalized = value.trim();\n\n return normalized === \"opencode-tbot\" || normalized.startsWith(\"opencode-tbot@\");\n}\n\nasync function warnAboutLegacyLocalInstallations(homeDir: string): Promise<void> {\n const legacyInstallations = await findLegacyLocalInstallations(homeDir);\n\n if (legacyInstallations.length === 0) {\n return;\n }\n\n stdout.write(\"\\nDetected local opencode-tbot npm installation(s) that can make OpenCode show the plugin as file:///.../node_modules/...\\n\");\n\n for (const installation of legacyInstallations) {\n stdout.write(`- ${installation.packagePath}\\n`);\n stdout.write(` cleanup: cd \"${installation.rootDir}\" && npm uninstall opencode-tbot\\n`);\n }\n\n stdout.write(\"Recommended npm flow: npm exec --package opencode-tbot@latest opencode-tbot -- install\\n\");\n}\n\nasync function findLegacyLocalInstallations(homeDir: string) {\n const roots = [...new Set([\n resolve(process.cwd()),\n resolve(homeDir),\n ])];\n const installations: Array<{ packagePath: string; rootDir: string }> = [];\n\n for (const rootDir of roots) {\n const packagePath = join(rootDir, \"node_modules\", \"opencode-tbot\", \"package.json\");\n\n if (await pathExists(packagePath)) {\n installations.push({\n packagePath: join(rootDir, \"node_modules\", \"opencode-tbot\"),\n rootDir,\n });\n }\n }\n\n return installations;\n}\n\nasync function pathExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction buildHelpText(): string {\n return [\n \"Usage: opencode-tbot [install|update] [options]\",\n \" opencode-tbot --version\",\n \"\",\n \"Recommended npm usage:\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- install\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- update\",\n \" npm exec --package opencode-tbot@latest opencode-tbot -- --version\",\n \"\",\n \"Commands:\",\n \" install\",\n \" update\",\n \"\",\n \"Options:\",\n \" --bot-token <token>\",\n \" --telegram-api-root <url>\",\n \" --plugin-spec <spec>\",\n \" --skip-register\",\n \" --home-dir <path>\",\n \" --version\",\n \" --help\",\n ].join(\"\\n\");\n}\n\nfunction formatCliError(error: unknown): string {\n return error instanceof Error && error.message.trim().length > 0\n ? error.message.trim()\n : String(error);\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./assets/plugin-config-jkAZYbFW.js";
1
+ import "./assets/plugin-config-LIr8LS0-.js";
2
2
  import { TelegramBotPlugin, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests } from "./plugin.js";
3
3
  export { TelegramBotPlugin, TelegramBotPlugin as default, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests };