opencode-tbot 0.1.17 → 0.1.19
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 +159 -0
- package/README.md +5 -17
- package/README.zh-CN.md +12 -24
- package/dist/assets/{plugin-config-DA71_jD3.js → plugin-config-B8ginwol.js} +7 -51
- package/dist/assets/plugin-config-B8ginwol.js.map +1 -0
- package/dist/cli.js +5 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +17 -348
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -2
- package/tbot.config.example.json +0 -5
- package/dist/assets/plugin-config-DA71_jD3.js.map +0 -1
package/README.ja.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# opencode-tbot
|
|
2
|
+
|
|
3
|
+
チャットから [OpenCode](https://opencode.ai) を操作するための Telegram プラグインです。
|
|
4
|
+
|
|
5
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md)
|
|
6
|
+
|
|
7
|
+
> このプロジェクトは OpenCode チームによって開発されたものではなく、公式な関連もありません。
|
|
8
|
+
|
|
9
|
+
## できること
|
|
10
|
+
|
|
11
|
+
`opencode-tbot` を使うと、Telegram から OpenCode を操作できます。
|
|
12
|
+
|
|
13
|
+
- テキストメッセージは現在アクティブな OpenCode セッションに転送されます。
|
|
14
|
+
- Telegram の画像は OpenCode のファイルパートとしてアップロードされます。
|
|
15
|
+
- Telegram の音声メッセージは、通常の prompt フローに入る前に OpenRouter で文字起こしできます。
|
|
16
|
+
- OpenCode が発行した権限リクエストは、Telegram のインラインボタンから直接承認または拒否できます。
|
|
17
|
+
- セッション完了やエラーイベントは、紐付けられた Telegram チャットへ通知できます。
|
|
18
|
+
- チャット状態は JSON の state ファイルに保存され、Node と Bun の両方のランタイムで動作します。
|
|
19
|
+
|
|
20
|
+
## インストール
|
|
21
|
+
|
|
22
|
+
次を実行します。
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
インストーラーはプラグインをグローバルに登録し、デフォルトのランタイム設定を書き込みます。
|
|
29
|
+
|
|
30
|
+
インストール済み CLI のバージョン確認:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm exec --package opencode-tbot@latest opencode-tbot -- --version
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
OpenCode に登録済みの npm プラグイン spec を更新:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm exec --package opencode-tbot@latest opencode-tbot -- update
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
OpenCode 上でプラグインが `file:///.../node_modules/...` のようなパスで表示される場合は、古いローカル npm インストールを削除します。
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm uninstall opencode-tbot
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 設定
|
|
49
|
+
|
|
50
|
+
ランタイム設定は次の順序で読み込まれます。
|
|
51
|
+
|
|
52
|
+
1. `~/.config/opencode/opencode-tbot/config.json` のグローバルデフォルト
|
|
53
|
+
2. `<worktree>/tbot.config.json` のプロジェクト上書き設定
|
|
54
|
+
|
|
55
|
+
プロジェクト設定はグローバル設定に上書きマージされます。`telegram`、`state`、`openrouter` はセクション単位でディープマージされます。
|
|
56
|
+
|
|
57
|
+
### `tbot.config.json` の例
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"telegram": {
|
|
62
|
+
"botToken": "your_telegram_bot_token",
|
|
63
|
+
"allowedChatIds": [123456789],
|
|
64
|
+
"apiRoot": "https://api.telegram.org"
|
|
65
|
+
},
|
|
66
|
+
"state": {
|
|
67
|
+
"path": "./data/opencode-tbot.state.json"
|
|
68
|
+
},
|
|
69
|
+
"openrouter": {
|
|
70
|
+
"apiKey": "your_openrouter_api_key",
|
|
71
|
+
"model": "openai/gpt-audio-mini",
|
|
72
|
+
"timeoutMs": 30000,
|
|
73
|
+
"transcriptionPrompt": ""
|
|
74
|
+
},
|
|
75
|
+
"logLevel": "info"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### フィールド
|
|
80
|
+
|
|
81
|
+
| フィールド | 必須 | デフォルト | 説明 |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| `telegram.botToken` | はい | - | Telegram bot token。通常はインストーラーがグローバルプラグイン設定へ書き込みます。 |
|
|
84
|
+
| `telegram.allowedChatIds` | いいえ | `[]` | 許可する Telegram chat ID の配列。空の場合はすべての chat を受け付けます。 |
|
|
85
|
+
| `telegram.apiRoot` | いいえ | `https://api.telegram.org` | Telegram Bot API のベース URL。テストやセルフホストのゲートウェイ向けです。 |
|
|
86
|
+
| `state.path` | いいえ | `./data/opencode-tbot.state.json` | JSON state ファイルのパス。現在の OpenCode worktree からの相対パスとして解決されます。 |
|
|
87
|
+
| `openrouter.apiKey` | いいえ | `""` | OpenRouter API key。音声文字起こしを有効にする場合のみ必要です。 |
|
|
88
|
+
| `openrouter.model` | いいえ | `openai/gpt-audio-mini` | 音声文字起こしに使う OpenRouter モデル。 |
|
|
89
|
+
| `openrouter.timeoutMs` | いいえ | `30000` | 音声文字起こしのタイムアウト時間(ミリ秒)。 |
|
|
90
|
+
| `openrouter.transcriptionPrompt` | いいえ | `""` | 文字起こしプロンプトに追加される任意の補足指示。 |
|
|
91
|
+
| `logLevel` | いいえ | `info` | プラグインのログレベル。ログは `client.app.log()` 経由で出力されます。 |
|
|
92
|
+
|
|
93
|
+
## ランタイム上の前提
|
|
94
|
+
|
|
95
|
+
必須:
|
|
96
|
+
|
|
97
|
+
- `telegram.botToken`
|
|
98
|
+
|
|
99
|
+
任意:
|
|
100
|
+
|
|
101
|
+
- `telegram.allowedChatIds`
|
|
102
|
+
- `telegram.apiRoot`
|
|
103
|
+
- `state.path`
|
|
104
|
+
- `openrouter.apiKey`
|
|
105
|
+
- `openrouter.model`
|
|
106
|
+
- `openrouter.timeoutMs`
|
|
107
|
+
- `openrouter.transcriptionPrompt`
|
|
108
|
+
- `logLevel`
|
|
109
|
+
|
|
110
|
+
補足:
|
|
111
|
+
|
|
112
|
+
- `state.path` のデフォルトは `./data/opencode-tbot.state.json` で、現在の OpenCode worktree からの相対パスとして解決されます。
|
|
113
|
+
- ログは `client.app.log()` 経由で出力されます。
|
|
114
|
+
- 権限承認とセッション通知はプラグインの hook で処理されます。
|
|
115
|
+
|
|
116
|
+
## コマンド
|
|
117
|
+
|
|
118
|
+
- `/start` 短いウェルカムメッセージとクイックスタート手順を表示
|
|
119
|
+
- `/status` OpenCode のヘルス、パス、LSP、MCP の状態をまとめて表示
|
|
120
|
+
- `/new [title]` 新しい OpenCode セッションを作成
|
|
121
|
+
- `/agents` または `/agent` 利用可能な agent を一覧表示し、アクティブな agent を切り替え
|
|
122
|
+
- `/sessions` 利用可能なセッションを一覧表示し、アクティブなセッションを切り替え
|
|
123
|
+
- `/cancel` セッション名の変更をキャンセルするか、現在のセッションで実行中のリクエストを中止
|
|
124
|
+
- `/model` または `/models` 利用可能なモデルを一覧表示し、アクティブなモデルを切り替え
|
|
125
|
+
- `/language` bot の表示言語を切り替え
|
|
126
|
+
|
|
127
|
+
コマンド以外のテキストメッセージは prompt として扱われ、OpenCode に送信されます。OpenRouter が設定されている場合、Telegram の `voice` メッセージは文字起こし後に同じ prompt フローへ入ります。Telegram の画像は OpenCode のファイルパートとして転送されます。
|
|
128
|
+
|
|
129
|
+
## 開発
|
|
130
|
+
|
|
131
|
+
プラグインバンドルをビルド:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pnpm build
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
型チェック:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pnpm typecheck
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
テスト実行:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pnpm test
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
このリポジトリでソースベースのローカル読み込みを行う場合、OpenCode は [.opencode/plugins/opencode-tbot.ts](./.opencode/plugins/opencode-tbot.ts) を使って `src/plugin.ts` を再エクスポートできます。
|
|
150
|
+
|
|
151
|
+
## FAQ
|
|
152
|
+
|
|
153
|
+
### 実行中の OpenCode インスタンスは必要ですか?
|
|
154
|
+
|
|
155
|
+
はい。このリポジトリが提供するのは Telegram 連携レイヤーであり、プラグインを読み込む OpenCode ホストプロセスに依存します。
|
|
156
|
+
|
|
157
|
+
### これは OpenCode の公式プロジェクトですか?
|
|
158
|
+
|
|
159
|
+
いいえ。OpenCode と連携するプロジェクトですが、OpenCode チームが開発したものではありません。
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ A Telegram plugin for driving [OpenCode](https://opencode.ai) from chat.
|
|
|
12
12
|
|
|
13
13
|
- Text messages are forwarded to the active OpenCode session.
|
|
14
14
|
- Telegram images are uploaded as OpenCode file parts.
|
|
15
|
-
- Telegram voice messages
|
|
15
|
+
- Telegram voice messages are explicitly rejected with a localized error reply.
|
|
16
16
|
- Permission requests raised by OpenCode can be approved or rejected directly from Telegram inline buttons.
|
|
17
17
|
- Session completion and error events can be reported back to the bound Telegram chat.
|
|
18
18
|
- Chat state is stored in a JSON state file that works in both Node and Bun runtimes.
|
|
@@ -52,7 +52,9 @@ The runtime config is loaded in this order:
|
|
|
52
52
|
1. global defaults from `~/.config/opencode/opencode-tbot/config.json`
|
|
53
53
|
2. project overrides from `<worktree>/tbot.config.json`
|
|
54
54
|
|
|
55
|
-
Project config is merged on top of the global config. `telegram
|
|
55
|
+
Project config is merged on top of the global config. `telegram` and `state` are deep-merged by section.
|
|
56
|
+
|
|
57
|
+
Legacy `openrouter` voice-transcription settings are ignored at runtime. When the installer rewrites the config, it removes them.
|
|
56
58
|
|
|
57
59
|
### Example `tbot.config.json`
|
|
58
60
|
|
|
@@ -66,12 +68,6 @@ Project config is merged on top of the global config. `telegram`, `state`, and `
|
|
|
66
68
|
"state": {
|
|
67
69
|
"path": "./data/opencode-tbot.state.json"
|
|
68
70
|
},
|
|
69
|
-
"openrouter": {
|
|
70
|
-
"apiKey": "your_openrouter_api_key",
|
|
71
|
-
"model": "openai/gpt-audio-mini",
|
|
72
|
-
"timeoutMs": 30000,
|
|
73
|
-
"transcriptionPrompt": ""
|
|
74
|
-
},
|
|
75
71
|
"logLevel": "info"
|
|
76
72
|
}
|
|
77
73
|
```
|
|
@@ -84,10 +80,6 @@ Project config is merged on top of the global config. `telegram`, `state`, and `
|
|
|
84
80
|
| `telegram.allowedChatIds` | No | `[]` | Allowed Telegram chat IDs. If empty, the bot accepts messages from any chat. |
|
|
85
81
|
| `telegram.apiRoot` | No | `https://api.telegram.org` | Telegram Bot API base URL. Useful for tests or self-hosted gateways. |
|
|
86
82
|
| `state.path` | No | `./data/opencode-tbot.state.json` | JSON state file path, resolved relative to the current OpenCode worktree. |
|
|
87
|
-
| `openrouter.apiKey` | No | `""` | OpenRouter API key. Required only when voice transcription is enabled. |
|
|
88
|
-
| `openrouter.model` | No | `openai/gpt-audio-mini` | OpenRouter model for voice transcription. |
|
|
89
|
-
| `openrouter.timeoutMs` | No | `30000` | Voice transcription timeout in milliseconds. |
|
|
90
|
-
| `openrouter.transcriptionPrompt` | No | `""` | Optional extra instruction appended to the transcription prompt. |
|
|
91
83
|
| `logLevel` | No | `info` | Plugin log level. Logs are emitted through `client.app.log()`. |
|
|
92
84
|
|
|
93
85
|
## Runtime Expectations
|
|
@@ -101,10 +93,6 @@ Optional:
|
|
|
101
93
|
- `telegram.allowedChatIds`
|
|
102
94
|
- `telegram.apiRoot`
|
|
103
95
|
- `state.path`
|
|
104
|
-
- `openrouter.apiKey`
|
|
105
|
-
- `openrouter.model`
|
|
106
|
-
- `openrouter.timeoutMs`
|
|
107
|
-
- `openrouter.transcriptionPrompt`
|
|
108
96
|
- `logLevel`
|
|
109
97
|
|
|
110
98
|
Notes:
|
|
@@ -124,7 +112,7 @@ Notes:
|
|
|
124
112
|
- `/model` or `/models` list available models and switch the active one
|
|
125
113
|
- `/language` switch the bot display language
|
|
126
114
|
|
|
127
|
-
Any non-command text message is treated as a prompt and sent to OpenCode. Telegram
|
|
115
|
+
Any non-command text message is treated as a prompt and sent to OpenCode. Telegram images are forwarded as OpenCode file parts. Telegram voice messages are not supported and receive a localized rejection reply.
|
|
128
116
|
|
|
129
117
|
## Development
|
|
130
118
|
|
package/README.zh-CN.md
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
- 文本消息会转发到当前 OpenCode 会话。
|
|
14
14
|
- Telegram 图片会作为 OpenCode 文件片段上传。
|
|
15
|
-
- Telegram
|
|
16
|
-
- OpenCode 触发的权限请求可以直接在 Telegram
|
|
15
|
+
- Telegram 语音消息会被明确拒绝,并返回本地化提示。
|
|
16
|
+
- OpenCode 触发的权限请求可以直接在 Telegram 内联按钮中批准或拒绝。
|
|
17
17
|
- 会话完成和错误事件可以主动回推到绑定的 Telegram chat。
|
|
18
18
|
- 聊天状态通过 JSON 状态文件持久化,可兼容 Node 和 Bun 运行环境。
|
|
19
19
|
|
|
@@ -39,7 +39,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --version
|
|
|
39
39
|
npm exec --package opencode-tbot@latest opencode-tbot -- update
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
如果 OpenCode 把插件显示成 `file:///.../node_modules/...` 路径,可删除旧的本地 npm 安装:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
npm uninstall opencode-tbot
|
|
@@ -52,7 +52,9 @@ npm uninstall opencode-tbot
|
|
|
52
52
|
1. 全局默认配置 `~/.config/opencode/opencode-tbot/config.json`
|
|
53
53
|
2. 项目覆盖配置 `<worktree>/tbot.config.json`
|
|
54
54
|
|
|
55
|
-
项目配置会覆盖全局默认值;`telegram
|
|
55
|
+
项目配置会覆盖全局默认值;`telegram` 和 `state` 会按分段进行深合并。
|
|
56
|
+
|
|
57
|
+
遗留的 `openrouter` 语音转写配置在运行时会被忽略;安装器重写配置时会自动清理这些字段。
|
|
56
58
|
|
|
57
59
|
### `tbot.config.json` 示例
|
|
58
60
|
|
|
@@ -66,12 +68,6 @@ npm uninstall opencode-tbot
|
|
|
66
68
|
"state": {
|
|
67
69
|
"path": "./data/opencode-tbot.state.json"
|
|
68
70
|
},
|
|
69
|
-
"openrouter": {
|
|
70
|
-
"apiKey": "your_openrouter_api_key",
|
|
71
|
-
"model": "openai/gpt-audio-mini",
|
|
72
|
-
"timeoutMs": 30000,
|
|
73
|
-
"transcriptionPrompt": ""
|
|
74
|
-
},
|
|
75
71
|
"logLevel": "info"
|
|
76
72
|
}
|
|
77
73
|
```
|
|
@@ -84,10 +80,6 @@ npm uninstall opencode-tbot
|
|
|
84
80
|
| `telegram.allowedChatIds` | 否 | `[]` | 允许访问的 Telegram chat ID 数组。为空时表示接受任意 chat。 |
|
|
85
81
|
| `telegram.apiRoot` | 否 | `https://api.telegram.org` | Telegram Bot API 根地址,适合测试或自托管网关。 |
|
|
86
82
|
| `state.path` | 否 | `./data/opencode-tbot.state.json` | JSON 状态文件路径,相对当前 OpenCode worktree 解析。 |
|
|
87
|
-
| `openrouter.apiKey` | 否 | `""` | OpenRouter API key。仅在启用语音转写时需要。 |
|
|
88
|
-
| `openrouter.model` | 否 | `openai/gpt-audio-mini` | 语音转写使用的 OpenRouter 模型。 |
|
|
89
|
-
| `openrouter.timeoutMs` | 否 | `30000` | 语音转写超时时间,单位毫秒。 |
|
|
90
|
-
| `openrouter.transcriptionPrompt` | 否 | `""` | 追加到内置转写提示词后的可选说明。 |
|
|
91
83
|
| `logLevel` | 否 | `info` | 插件日志级别。日志统一通过 `client.app.log()` 上报。 |
|
|
92
84
|
|
|
93
85
|
## 运行时约定
|
|
@@ -101,17 +93,13 @@ npm uninstall opencode-tbot
|
|
|
101
93
|
- `telegram.allowedChatIds`
|
|
102
94
|
- `telegram.apiRoot`
|
|
103
95
|
- `state.path`
|
|
104
|
-
- `openrouter.apiKey`
|
|
105
|
-
- `openrouter.model`
|
|
106
|
-
- `openrouter.timeoutMs`
|
|
107
|
-
- `openrouter.transcriptionPrompt`
|
|
108
96
|
- `logLevel`
|
|
109
97
|
|
|
110
98
|
说明:
|
|
111
99
|
|
|
112
|
-
- `state.path` 默认是 `./data/opencode-tbot.state.json`,并相对当前 OpenCode worktree
|
|
113
|
-
- 日志通过 `client.app.log()`
|
|
114
|
-
- 权限审批和会话通知由插件 hook
|
|
100
|
+
- `state.path` 默认是 `./data/opencode-tbot.state.json`,并相对当前 OpenCode worktree 解析。
|
|
101
|
+
- 日志通过 `client.app.log()` 统一输出。
|
|
102
|
+
- 权限审批和会话通知由插件 hook 处理。
|
|
115
103
|
|
|
116
104
|
## 命令
|
|
117
105
|
|
|
@@ -124,7 +112,7 @@ npm uninstall opencode-tbot
|
|
|
124
112
|
- `/model` 或 `/models` 列出可用模型并切换当前模型
|
|
125
113
|
- `/language` 切换 bot 显示语言
|
|
126
114
|
|
|
127
|
-
任意非命令文本都会被当作 prompt 发送给 OpenCode
|
|
115
|
+
任意非命令文本都会被当作 prompt 发送给 OpenCode。图片会作为 OpenCode 文件片段上传。Telegram 语音消息当前不受支持,bot 会直接返回本地化拒绝提示。
|
|
128
116
|
|
|
129
117
|
## 开发
|
|
130
118
|
|
|
@@ -152,8 +140,8 @@ pnpm test
|
|
|
152
140
|
|
|
153
141
|
### 我需要一个正在运行的 OpenCode 实例吗?
|
|
154
142
|
|
|
155
|
-
|
|
143
|
+
需要。这一仓库提供的是 Telegram 集成层,依赖加载它的 OpenCode Host 进程。
|
|
156
144
|
|
|
157
145
|
### 这是 OpenCode 官方项目吗?
|
|
158
146
|
|
|
159
|
-
|
|
147
|
+
不是。它只是一个 OpenCode 集成,并非 OpenCode 官方项目。
|
|
@@ -5,7 +5,6 @@ import { z } from "zod";
|
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
//#region src/app/config.ts
|
|
8
|
-
var DEFAULT_OPENROUTER_MODEL = "openai/gpt-audio-mini";
|
|
9
8
|
var DEFAULT_STATE_FILE_PATH = "./data/opencode-tbot.state.json";
|
|
10
9
|
var DEFAULT_TELEGRAM_API_ROOT = "https://api.telegram.org";
|
|
11
10
|
var AllowedChatIdSchema = z.union([z.number().int(), z.string().regex(/^-?\d+$/u).transform((value) => Number(value))]);
|
|
@@ -15,44 +14,23 @@ var TelegramConfigSchema = z.preprocess((value) => value ?? {}, z.object({
|
|
|
15
14
|
apiRoot: z.string().trim().url().default(DEFAULT_TELEGRAM_API_ROOT)
|
|
16
15
|
}));
|
|
17
16
|
var StateConfigSchema = z.preprocess((value) => value ?? {}, z.object({ path: z.string().trim().min(1).default(DEFAULT_STATE_FILE_PATH) }));
|
|
18
|
-
var OpenRouterConfigSchema = z.preprocess((value) => value ?? {}, z.object({
|
|
19
|
-
apiKey: z.string().default(""),
|
|
20
|
-
model: z.string().default(DEFAULT_OPENROUTER_MODEL),
|
|
21
|
-
timeoutMs: z.coerce.number().int().positive().default(3e4),
|
|
22
|
-
transcriptionPrompt: z.string().default("")
|
|
23
|
-
}));
|
|
24
17
|
var AppConfigSchema = z.object({
|
|
25
18
|
telegram: TelegramConfigSchema,
|
|
26
19
|
state: StateConfigSchema,
|
|
27
|
-
openrouter: OpenRouterConfigSchema,
|
|
28
20
|
logLevel: z.string().default("info")
|
|
29
21
|
});
|
|
30
22
|
function loadAppConfig(configSource = {}, options = {}) {
|
|
31
23
|
return buildAppConfig(parseConfig(AppConfigSchema, configSource), options);
|
|
32
24
|
}
|
|
33
25
|
function buildAppConfig(data, options) {
|
|
34
|
-
const openRouterApiKey = normalizeOptionalString$1(data.openrouter.apiKey);
|
|
35
|
-
const openRouterModel = normalizeOptionalString$1(data.openrouter.model) ?? "openai/gpt-audio-mini";
|
|
36
|
-
const transcriptionPrompt = normalizeOptionalString$1(data.openrouter.transcriptionPrompt);
|
|
37
26
|
return {
|
|
38
27
|
telegramBotToken: data.telegram.botToken,
|
|
39
28
|
telegramAllowedChatIds: data.telegram.allowedChatIds,
|
|
40
29
|
telegramApiRoot: normalizeApiRoot(data.telegram.apiRoot),
|
|
41
30
|
logLevel: data.logLevel,
|
|
42
|
-
stateFilePath: resolveStatePath(data, options.cwd ?? process.cwd())
|
|
43
|
-
openrouter: {
|
|
44
|
-
configured: !!openRouterApiKey,
|
|
45
|
-
apiKey: openRouterApiKey,
|
|
46
|
-
model: openRouterModel,
|
|
47
|
-
timeoutMs: data.openrouter.timeoutMs,
|
|
48
|
-
transcriptionPrompt
|
|
49
|
-
}
|
|
31
|
+
stateFilePath: resolveStatePath(data, options.cwd ?? process.cwd())
|
|
50
32
|
};
|
|
51
33
|
}
|
|
52
|
-
function normalizeOptionalString$1(value) {
|
|
53
|
-
const normalized = value.trim();
|
|
54
|
-
return normalized.length > 0 ? normalized : null;
|
|
55
|
-
}
|
|
56
34
|
function resolveStatePath(data, cwd) {
|
|
57
35
|
return resolve(cwd, data.state.path || "./data/opencode-tbot.state.json");
|
|
58
36
|
}
|
|
@@ -92,8 +70,7 @@ async function preparePluginConfiguration(options) {
|
|
|
92
70
|
const globalConfigFilePath = getGlobalPluginConfigFilePath(options.homeDir ?? homedir());
|
|
93
71
|
const projectConfigFilePath = await resolveProjectPluginConfigFilePath(options.cwd);
|
|
94
72
|
const [globalConfig, projectConfig] = await Promise.all([loadPluginConfigFile(globalConfigFilePath), loadPluginConfigFile(projectConfigFilePath)]);
|
|
95
|
-
const config = mergePluginConfigSources(globalConfig, projectConfig, options.config);
|
|
96
|
-
applyGlobalOpenRouterApiKey(config, globalConfig);
|
|
73
|
+
const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, projectConfig, options.config));
|
|
97
74
|
const configFilePath = await pathExists(projectConfigFilePath) ? projectConfigFilePath : globalConfigFilePath;
|
|
98
75
|
return {
|
|
99
76
|
cwd: options.cwd,
|
|
@@ -123,7 +100,6 @@ function mergePluginConfigSources(...sources) {
|
|
|
123
100
|
const normalized = source;
|
|
124
101
|
const previousTelegram = merged.telegram;
|
|
125
102
|
const previousState = merged.state;
|
|
126
|
-
const previousOpenRouter = merged.openrouter;
|
|
127
103
|
Object.assign(merged, normalized);
|
|
128
104
|
if (normalized.telegram) merged.telegram = {
|
|
129
105
|
...previousTelegram ?? {},
|
|
@@ -133,27 +109,9 @@ function mergePluginConfigSources(...sources) {
|
|
|
133
109
|
...previousState ?? {},
|
|
134
110
|
...normalized.state
|
|
135
111
|
};
|
|
136
|
-
if (normalized.openrouter) merged.openrouter = {
|
|
137
|
-
...previousOpenRouter ?? {},
|
|
138
|
-
...normalized.openrouter
|
|
139
|
-
};
|
|
140
112
|
}
|
|
141
113
|
return merged;
|
|
142
114
|
}
|
|
143
|
-
function applyGlobalOpenRouterApiKey(config, globalConfig) {
|
|
144
|
-
const globalApiKey = normalizeOptionalString(globalConfig.openrouter?.apiKey);
|
|
145
|
-
if (!config.openrouter) {
|
|
146
|
-
if (!globalApiKey) return;
|
|
147
|
-
config.openrouter = { apiKey: globalApiKey };
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
if (globalApiKey) {
|
|
151
|
-
config.openrouter.apiKey = globalApiKey;
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
delete config.openrouter.apiKey;
|
|
155
|
-
if (Object.keys(config.openrouter).length === 0) delete config.openrouter;
|
|
156
|
-
}
|
|
157
115
|
function serializePluginConfig(config) {
|
|
158
116
|
return `${JSON.stringify(orderPluginConfig(config), null, 2)}\n`;
|
|
159
117
|
}
|
|
@@ -178,13 +136,11 @@ function orderPluginConfig(config) {
|
|
|
178
136
|
const prioritizedKeys = new Set([
|
|
179
137
|
"telegram",
|
|
180
138
|
"state",
|
|
181
|
-
"openrouter",
|
|
182
139
|
"logLevel"
|
|
183
140
|
]);
|
|
184
141
|
const ordered = {};
|
|
185
142
|
if (config.telegram) ordered.telegram = config.telegram;
|
|
186
143
|
if (config.state) ordered.state = config.state;
|
|
187
|
-
if (config.openrouter) ordered.openrouter = config.openrouter;
|
|
188
144
|
if (config.logLevel !== void 0) ordered.logLevel = config.logLevel;
|
|
189
145
|
for (const [key, value] of Object.entries(config)) if (!prioritizedKeys.has(key)) ordered[key] = value;
|
|
190
146
|
return ordered;
|
|
@@ -192,10 +148,6 @@ function orderPluginConfig(config) {
|
|
|
192
148
|
function isPlainObject(value) {
|
|
193
149
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
194
150
|
}
|
|
195
|
-
function normalizeOptionalString(value) {
|
|
196
|
-
const normalized = value?.trim();
|
|
197
|
-
return normalized ? normalized : null;
|
|
198
|
-
}
|
|
199
151
|
function isMissingFileError(error) {
|
|
200
152
|
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
201
153
|
}
|
|
@@ -211,7 +163,11 @@ async function pathExists(filePath) {
|
|
|
211
163
|
throw error;
|
|
212
164
|
}
|
|
213
165
|
}
|
|
166
|
+
function stripLegacyVoiceConfig(config) {
|
|
167
|
+
const { openrouter: _openrouter, ...rest } = config;
|
|
168
|
+
return rest;
|
|
169
|
+
}
|
|
214
170
|
//#endregion
|
|
215
171
|
export { writePluginConfigFile as a, loadAppConfig as c, preparePluginConfiguration as i, getOpenCodeConfigFilePath as n, OPENCODE_TBOT_VERSION as o, mergePluginConfigSources as r, DEFAULT_TELEGRAM_API_ROOT as s, getGlobalPluginConfigFilePath as t };
|
|
216
172
|
|
|
217
|
-
//# sourceMappingURL=plugin-config-
|
|
173
|
+
//# sourceMappingURL=plugin-config-B8ginwol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-config-B8ginwol.js","names":[],"sources":["../../src/app/config.ts","../../src/app/package-info.ts","../../src/app/plugin-config.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { z } from \"zod\";\n\nexport const DEFAULT_STATE_FILE_PATH = \"./data/opencode-tbot.state.json\";\nexport const DEFAULT_TELEGRAM_API_ROOT = \"https://api.telegram.org\";\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 AppConfigSchema = z.object({\n telegram: TelegramConfigSchema,\n state: StateConfigSchema,\n logLevel: z.string().default(\"info\"),\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 logLevel?: string;\n [key: string]: unknown;\n}\n\nexport interface AppConfig {\n telegramBotToken: string;\n telegramAllowedChatIds: number[];\n telegramApiRoot: string;\n logLevel: string;\n stateFilePath: 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 return {\n telegramBotToken: data.telegram.botToken,\n telegramAllowedChatIds: data.telegram.allowedChatIds,\n telegramApiRoot: normalizeApiRoot(data.telegram.apiRoot),\n logLevel: data.logLevel,\n stateFilePath: resolveStatePath(data, options.cwd ?? process.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 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\";\n\nexport const PLUGIN_CONFIG_FILE_NAME = \"tbot.config.json\";\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\";\n\nexport interface PreparedPluginConfiguration {\n cwd: string;\n config: PluginConfigSource;\n globalConfigFilePath: string;\n projectConfigFilePath: 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, projectConfig] = await Promise.all([\n loadPluginConfigFile(globalConfigFilePath),\n loadPluginConfigFile(projectConfigFilePath),\n ]);\n const config = stripLegacyVoiceConfig(mergePluginConfigSources(globalConfig, projectConfig, options.config));\n const configFilePath = await pathExists(projectConfigFilePath)\n ? projectConfigFilePath\n : globalConfigFilePath;\n\n return {\n cwd: options.cwd,\n config,\n globalConfigFilePath,\n projectConfigFilePath,\n configFilePath,\n };\n}\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 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 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\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\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 \"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.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 { a as writePluginConfigFile, n as getOpenCodeConfigFilePath, o as OPENCODE_TBOT_VERSION, r as mergePluginConfigSources, t as getGlobalPluginConfigFilePath } from "./assets/plugin-config-
|
|
1
|
+
import { a as writePluginConfigFile, n as getOpenCodeConfigFilePath, o as OPENCODE_TBOT_VERSION, r as mergePluginConfigSources, t as getGlobalPluginConfigFilePath } from "./assets/plugin-config-B8ginwol.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";
|
|
@@ -45,11 +45,10 @@ async function installPlugin(options = {}) {
|
|
|
45
45
|
const prompt = createPromptSession();
|
|
46
46
|
try {
|
|
47
47
|
const botToken = normalizeRequiredString(options.botToken ?? await prompt.askSecret("Telegram bot token: "), "Telegram bot token is required.");
|
|
48
|
-
const openrouterApiKey = options.enableVoice ?? await prompt.confirm("Enable voice transcription? (y/N): ", false) ? normalizeRequiredString(options.openrouterApiKey ?? await prompt.askSecret("OpenRouter API key: "), "OpenRouter API key is required when voice transcription is enabled.") : null;
|
|
49
48
|
const telegramApiRoot = normalizeOptionalString(options.telegramApiRoot) ?? "https://api.telegram.org";
|
|
50
49
|
const pluginSpec = normalizeOptionalString(options.pluginSpec) ?? DEFAULT_PLUGIN_SPEC;
|
|
51
50
|
const nextOpenCodeConfig = options.registerPlugin === false ? openCodeConfig : ensurePluginRegistered(openCodeConfig, pluginSpec);
|
|
52
|
-
const nextPluginConfig = buildInstalledPluginConfig(existingPluginConfig, botToken, telegramApiRoot
|
|
51
|
+
const nextPluginConfig = buildInstalledPluginConfig(existingPluginConfig, botToken, telegramApiRoot);
|
|
53
52
|
if (options.registerPlugin === false) await ensureParentDirectory(openCodeConfigFilePath);
|
|
54
53
|
else await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);
|
|
55
54
|
await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);
|
|
@@ -79,15 +78,6 @@ function parseCliOptions(argv) {
|
|
|
79
78
|
case "--bot-token":
|
|
80
79
|
options.botToken = values[++index];
|
|
81
80
|
break;
|
|
82
|
-
case "--enable-voice":
|
|
83
|
-
options.enableVoice = true;
|
|
84
|
-
break;
|
|
85
|
-
case "--disable-voice":
|
|
86
|
-
options.enableVoice = false;
|
|
87
|
-
break;
|
|
88
|
-
case "--openrouter-api-key":
|
|
89
|
-
options.openrouterApiKey = values[++index];
|
|
90
|
-
break;
|
|
91
81
|
case "--plugin-spec":
|
|
92
82
|
options.pluginSpec = values[++index];
|
|
93
83
|
break;
|
|
@@ -113,24 +103,12 @@ function parseCliOptions(argv) {
|
|
|
113
103
|
}
|
|
114
104
|
return options;
|
|
115
105
|
}
|
|
116
|
-
function buildInstalledPluginConfig(current, botToken, telegramApiRoot
|
|
117
|
-
const
|
|
106
|
+
function buildInstalledPluginConfig(current, botToken, telegramApiRoot) {
|
|
107
|
+
const { openrouter: _openrouter, ...nextConfig } = mergePluginConfigSources(current, { telegram: {
|
|
118
108
|
botToken,
|
|
119
109
|
apiRoot: telegramApiRoot
|
|
120
110
|
} });
|
|
121
|
-
|
|
122
|
-
...merged.openrouter ?? {},
|
|
123
|
-
apiKey: openrouterApiKey
|
|
124
|
-
} : removeOpenRouterApiKey(merged.openrouter);
|
|
125
|
-
return {
|
|
126
|
-
...merged,
|
|
127
|
-
...nextOpenRouter && Object.keys(nextOpenRouter).length > 0 ? { openrouter: nextOpenRouter } : {}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function removeOpenRouterApiKey(config) {
|
|
131
|
-
if (!config) return;
|
|
132
|
-
const { apiKey: _apiKey, ...rest } = config;
|
|
133
|
-
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
111
|
+
return nextConfig;
|
|
134
112
|
}
|
|
135
113
|
function ensurePluginRegistered(config, pluginSpec) {
|
|
136
114
|
const plugins = Array.isArray(config.plugin) ? config.plugin.filter((item) => typeof item === "string") : [];
|
|
@@ -350,9 +328,6 @@ function buildHelpText() {
|
|
|
350
328
|
"",
|
|
351
329
|
"Options:",
|
|
352
330
|
" --bot-token <token>",
|
|
353
|
-
" --enable-voice",
|
|
354
|
-
" --disable-voice",
|
|
355
|
-
" --openrouter-api-key <key>",
|
|
356
331
|
" --telegram-api-root <url>",
|
|
357
332
|
" --plugin-spec <spec>",
|
|
358
333
|
" --skip-register",
|