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.ja.md +139 -43
- package/README.md +96 -43
- package/README.zh-CN.md +117 -61
- package/dist/assets/{plugin-config-jkAZYbFW.js → plugin-config-LIr8LS0-.js} +25 -2
- package/dist/assets/plugin-config-LIr8LS0-.js.map +1 -0
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +111 -83
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/plugin-config-jkAZYbFW.js.map +0 -1
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`
|
|
11
|
+
`opencode-tbot` 让你可以在 Telegram 中直接操作 OpenCode,并为每个聊天维护一份绑定状态。
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
- Telegram
|
|
15
|
-
- 图片轮次会在临时 fork
|
|
16
|
-
- Telegram
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
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
|
-
|
|
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
|
-
|
|
91
|
+
遗留行为说明:
|
|
69
92
|
|
|
70
|
-
|
|
93
|
+
- `<worktree>/tbot.config.json` 会在运行时被忽略;如果检测到,插件会记录警告,提示你迁移配置
|
|
94
|
+
- 遗留的 `openrouter` 语音转写配置会在运行时被忽略;安装器重写配置时也会自动移除
|
|
71
95
|
|
|
72
|
-
###
|
|
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` |
|
|
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
|
-
-
|
|
153
|
+
- `logging.file.dir` 如果是相对路径,也会相对当前 worktree 解析。
|
|
154
|
+
- Telegram prompt 采用 async-first:插件先提交 `session.promptAsync()`,再通过消息与状态恢复最终回复。
|
|
130
155
|
- 如果 `telegram.allowedChatIds` 为空,bot 会接受任意 chat 的消息;生产环境建议显式限制。
|
|
131
|
-
-
|
|
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.
|
|
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.
|
|
166
|
+
5. 运行 `/new [title]`,或直接发送文本消息开始使用。
|
|
152
167
|
|
|
153
168
|
## 命令
|
|
154
169
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
168
|
-
-
|
|
169
|
-
- `/cancel` 会同时中止 OpenCode
|
|
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
|
-
|
|
224
|
+
运行最接近 CI 的集成路径:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm install -g opencode-ai
|
|
228
|
+
OPENCODE_HOST_E2E=1 pnpm test
|
|
229
|
+
```
|
|
193
230
|
|
|
194
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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-
|
|
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
|
|
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 =
|
|
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 =
|
|
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-
|
|
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 };
|