opencode-tbot 0.1.34 → 0.1.35
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 +20 -15
- package/README.md +20 -15
- package/README.zh-CN.md +20 -15
- package/dist/assets/{plugin-config-LIr8LS0-.js → plugin-config-Be3vV2kr.js} +10 -2
- package/dist/assets/plugin-config-Be3vV2kr.js.map +1 -0
- package/dist/cli.js +54 -40
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +1 -1
- package/dist/assets/plugin-config-LIr8LS0-.js.map +0 -1
package/README.ja.md
CHANGED
|
@@ -35,12 +35,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
|
35
35
|
|
|
36
36
|
インストーラーは次を行います。
|
|
37
37
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
38
|
+
- `~/.config/opencode/plugins/opencode-tbot.js` を書き込み
|
|
39
|
+
- 必要な親ディレクトリがなければ自動で作成
|
|
40
40
|
- グローバルのプラグイン実行設定を作成またはマージ
|
|
41
|
-
- OpenCode
|
|
41
|
+
- 古い `opencode-tbot` の npm 登録がグローバル OpenCode 設定に残っていれば削除し、二重読み込みを防止
|
|
42
|
+
- OpenCode 設定パス、プラグイン bridge パス、プラグイン設定パス、既定のプラグインログディレクトリを表示
|
|
42
43
|
|
|
43
|
-
`~/.config/opencode/opencode.jsonc`
|
|
44
|
+
`~/.config/opencode/opencode.jsonc` または `~/.config/opencode/opencode.json` に古い `opencode-tbot` の npm 登録が残っている場合、インストーラーが自動で削除し、グローバル bridge に一本化します。
|
|
44
45
|
|
|
45
46
|
インストール済み CLI のバージョン確認:
|
|
46
47
|
|
|
@@ -48,7 +49,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
|
48
49
|
npm exec --package opencode-tbot@latest opencode-tbot -- --version
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
bot token
|
|
52
|
+
bot token を変更せず、グローバルプラグイン bridge を更新:
|
|
52
53
|
|
|
53
54
|
```bash
|
|
54
55
|
npm exec --package opencode-tbot@latest opencode-tbot -- update
|
|
@@ -66,13 +67,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
|
|
|
66
67
|
|
|
67
68
|
- `--bot-token <token>` Telegram bot token を非対話で設定
|
|
68
69
|
- `--telegram-api-root <url>` Telegram Bot API のベース URL を上書き
|
|
69
|
-
- `--plugin-spec <spec>`
|
|
70
|
-
- `--skip-register`
|
|
70
|
+
- `--plugin-spec <spec>` 非推奨。無視されます
|
|
71
|
+
- `--skip-register` グローバルプラグイン bridge を変更せず、プラグイン設定だけを書き換え
|
|
71
72
|
- `--home-dir <path>` カスタム home ディレクトリを使用
|
|
72
73
|
|
|
73
74
|
`update` で利用可能:
|
|
74
75
|
|
|
75
|
-
- `--plugin-spec <spec>`
|
|
76
|
+
- `--plugin-spec <spec>` 非推奨。無視されます
|
|
76
77
|
- `--home-dir <path>`
|
|
77
78
|
|
|
78
79
|
## 設定
|
|
@@ -81,10 +82,14 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
|
|
|
81
82
|
|
|
82
83
|
- `~/.config/opencode/opencode-tbot/config.json`
|
|
83
84
|
|
|
84
|
-
OpenCode
|
|
85
|
+
OpenCode はこのプラグインのグローバル bridge を次の場所から読み込みます。
|
|
85
86
|
|
|
86
|
-
-
|
|
87
|
-
|
|
87
|
+
- `~/.config/opencode/plugins/opencode-tbot.js`
|
|
88
|
+
|
|
89
|
+
古い npm 登録の移行時だけ、インストーラーは次のグローバル OpenCode 設定を必要に応じて掃除します。
|
|
90
|
+
|
|
91
|
+
- `~/.config/opencode/opencode.jsonc`
|
|
92
|
+
- `~/.config/opencode/opencode.json`
|
|
88
93
|
|
|
89
94
|
このプラグインは `.env` からランタイム設定を読み込みません。グローバル JSON 設定を使ってください。
|
|
90
95
|
|
|
@@ -235,7 +240,7 @@ npm install -g opencode-ai
|
|
|
235
240
|
$env:OPENCODE_HOST_E2E="1"; pnpm test
|
|
236
241
|
```
|
|
237
242
|
|
|
238
|
-
|
|
243
|
+
通常利用では、自動生成されるグローバル bridge `~/.config/opencode/plugins/opencode-tbot.js` とグローバルプラグイン設定を使ってください。このリポジトリは既定のプロジェクトローカル `.opencode/plugins` bridge を含みません。
|
|
239
244
|
|
|
240
245
|
このリポジトリでソースベースのローカル読み込みを行いたい場合は、`.opencode/plugins/opencode-tbot.ts` を手動で作成してください。
|
|
241
246
|
|
|
@@ -243,7 +248,7 @@ $env:OPENCODE_HOST_E2E="1"; pnpm test
|
|
|
243
248
|
export { default } from "../../src/plugin.ts";
|
|
244
249
|
```
|
|
245
250
|
|
|
246
|
-
有効にする読み込み経路は常に 1 つだけにしてください。手動で作成したローカル bridge
|
|
251
|
+
有効にする読み込み経路は常に 1 つだけにしてください。手動で作成したローカル bridge か、`~/.config/opencode/plugins/` に生成されたグローバル bridge のどちらかを使います。
|
|
247
252
|
|
|
248
253
|
## FAQ
|
|
249
254
|
|
|
@@ -251,9 +256,9 @@ export { default } from "../../src/plugin.ts";
|
|
|
251
256
|
|
|
252
257
|
はい。このリポジトリは Telegram 連携レイヤーのみを提供しており、プラグインを読み込む OpenCode Host プロセスに依存します。
|
|
253
258
|
|
|
254
|
-
###
|
|
259
|
+
### 生成された bridge が `file:///.../node_modules/...` を指すことがあるのはなぜですか?
|
|
255
260
|
|
|
256
|
-
|
|
261
|
+
通常は、`opencode-tbot` をローカル `node_modules` に持つプロジェクト内でインストーラーを実行したため、生成された bridge がそのパッケージ位置を参照していることを意味します。CLI はその状態を検出すると警告します。そのプロジェクトで `npm uninstall opencode-tbot` を実行してローカル依存を外し、推奨の `npm exec --package ...` インストールフローを使ってください。
|
|
257
262
|
|
|
258
263
|
### これは OpenCode の公式プロジェクトですか?
|
|
259
264
|
|
package/README.md
CHANGED
|
@@ -35,12 +35,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
|
35
35
|
|
|
36
36
|
The installer:
|
|
37
37
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
38
|
+
- writes `~/.config/opencode/plugins/opencode-tbot.js`
|
|
39
|
+
- creates missing parent directories automatically
|
|
40
40
|
- writes or merges the global plugin runtime config
|
|
41
|
-
-
|
|
41
|
+
- removes legacy `opencode-tbot` npm registration from the global OpenCode config when present
|
|
42
|
+
- prints the OpenCode config path, plugin bridge path, plugin config path, and default plugin log directory
|
|
42
43
|
|
|
43
|
-
If `~/.config/opencode/opencode.jsonc` already
|
|
44
|
+
If `~/.config/opencode/opencode.jsonc` or `~/.config/opencode/opencode.json` already contains an old `opencode-tbot` npm registration, the installer cleans it up so OpenCode only loads the global bridge once.
|
|
44
45
|
|
|
45
46
|
Check the installed CLI version:
|
|
46
47
|
|
|
@@ -48,7 +49,7 @@ Check the installed CLI version:
|
|
|
48
49
|
npm exec --package opencode-tbot@latest opencode-tbot -- --version
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
Refresh the global plugin bridge without touching your bot token:
|
|
52
53
|
|
|
53
54
|
```bash
|
|
54
55
|
npm exec --package opencode-tbot@latest opencode-tbot -- update
|
|
@@ -66,13 +67,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
|
|
|
66
67
|
|
|
67
68
|
- `--bot-token <token>` set the Telegram bot token non-interactively
|
|
68
69
|
- `--telegram-api-root <url>` override the Telegram Bot API root
|
|
69
|
-
- `--plugin-spec <spec>`
|
|
70
|
-
- `--skip-register` only rewrite plugin config without touching
|
|
70
|
+
- `--plugin-spec <spec>` deprecated; ignored
|
|
71
|
+
- `--skip-register` only rewrite plugin config without touching the global plugin bridge
|
|
71
72
|
- `--home-dir <path>` use a custom home directory
|
|
72
73
|
|
|
73
74
|
`update` supports:
|
|
74
75
|
|
|
75
|
-
- `--plugin-spec <spec>`
|
|
76
|
+
- `--plugin-spec <spec>` deprecated; ignored
|
|
76
77
|
- `--home-dir <path>`
|
|
77
78
|
|
|
78
79
|
## Configuration
|
|
@@ -81,10 +82,14 @@ Runtime config is loaded only from:
|
|
|
81
82
|
|
|
82
83
|
- `~/.config/opencode/opencode-tbot/config.json`
|
|
83
84
|
|
|
84
|
-
OpenCode
|
|
85
|
+
OpenCode loads the plugin bridge from:
|
|
85
86
|
|
|
86
|
-
- `~/.config/opencode/opencode.
|
|
87
|
-
|
|
87
|
+
- `~/.config/opencode/plugins/opencode-tbot.js`
|
|
88
|
+
|
|
89
|
+
Legacy npm registration cleanup only touches the global OpenCode config when an older install left `opencode-tbot` in:
|
|
90
|
+
|
|
91
|
+
- `~/.config/opencode/opencode.jsonc`
|
|
92
|
+
- or `~/.config/opencode/opencode.json`
|
|
88
93
|
|
|
89
94
|
This plugin does not read runtime settings from `.env`. Use the global JSON config instead.
|
|
90
95
|
|
|
@@ -235,7 +240,7 @@ npm install -g opencode-ai
|
|
|
235
240
|
$env:OPENCODE_HOST_E2E="1"; pnpm test
|
|
236
241
|
```
|
|
237
242
|
|
|
238
|
-
Normal usage should rely on the
|
|
243
|
+
Normal usage should rely on the generated global bridge at `~/.config/opencode/plugins/opencode-tbot.js` plus the global plugin config. This repository does not ship a default project-local `.opencode/plugins` bridge.
|
|
239
244
|
|
|
240
245
|
If you need source-based local loading while developing in this repository, create `.opencode/plugins/opencode-tbot.ts` manually:
|
|
241
246
|
|
|
@@ -243,7 +248,7 @@ If you need source-based local loading while developing in this repository, crea
|
|
|
243
248
|
export { default } from "../../src/plugin.ts";
|
|
244
249
|
```
|
|
245
250
|
|
|
246
|
-
Keep only one loading path enabled at a time: either the manual local bridge or the
|
|
251
|
+
Keep only one loading path enabled at a time: either the manual local bridge or the generated global bridge in `~/.config/opencode/plugins/`.
|
|
247
252
|
|
|
248
253
|
## FAQ
|
|
249
254
|
|
|
@@ -251,9 +256,9 @@ Keep only one loading path enabled at a time: either the manual local bridge or
|
|
|
251
256
|
|
|
252
257
|
Yes. This repository provides a Telegram integration layer and depends on the OpenCode host process that loads the plugin.
|
|
253
258
|
|
|
254
|
-
### Why
|
|
259
|
+
### Why can the generated bridge point to `file:///.../node_modules/...`?
|
|
255
260
|
|
|
256
|
-
That usually means a local project has `opencode-tbot`
|
|
261
|
+
That usually means the installer was run from a local project that has `opencode-tbot` in its own `node_modules`, so the generated bridge now points at that package location. The CLI warns when it detects that layout. Remove the local package with `npm uninstall opencode-tbot` in that project and use the recommended `npm exec --package ...` install flow instead.
|
|
257
262
|
|
|
258
263
|
### Is this an official OpenCode project?
|
|
259
264
|
|
package/README.zh-CN.md
CHANGED
|
@@ -35,12 +35,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
|
35
35
|
|
|
36
36
|
安装器会:
|
|
37
37
|
|
|
38
|
-
-
|
|
39
|
-
-
|
|
38
|
+
- 写入 `~/.config/opencode/plugins/opencode-tbot.js`
|
|
39
|
+
- 在目录不存在时自动创建所需的父级目录
|
|
40
40
|
- 写入或合并全局插件运行时配置
|
|
41
|
-
-
|
|
41
|
+
- 如果发现旧的 `opencode-tbot` npm 注册,会从全局 OpenCode 配置中清理掉,避免重复加载
|
|
42
|
+
- 打印 OpenCode 配置路径、插件 bridge 路径、插件配置路径和默认插件日志目录
|
|
42
43
|
|
|
43
|
-
如果 `~/.config/opencode/opencode.jsonc`
|
|
44
|
+
如果 `~/.config/opencode/opencode.jsonc` 或 `~/.config/opencode/opencode.json` 中还残留旧的 `opencode-tbot` npm 注册,安装器会自动清理,只保留全局 bridge 加载方式。
|
|
44
45
|
|
|
45
46
|
查看已安装 CLI 版本:
|
|
46
47
|
|
|
@@ -48,7 +49,7 @@ npm exec --package opencode-tbot@latest opencode-tbot -- install
|
|
|
48
49
|
npm exec --package opencode-tbot@latest opencode-tbot -- --version
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
刷新全局插件 bridge,而不改 bot token:
|
|
52
53
|
|
|
53
54
|
```bash
|
|
54
55
|
npm exec --package opencode-tbot@latest opencode-tbot -- update
|
|
@@ -66,13 +67,13 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
|
|
|
66
67
|
|
|
67
68
|
- `--bot-token <token>` 非交互写入 Telegram bot token
|
|
68
69
|
- `--telegram-api-root <url>` 覆盖 Telegram Bot API 根地址
|
|
69
|
-
- `--plugin-spec <spec>`
|
|
70
|
-
- `--skip-register`
|
|
70
|
+
- `--plugin-spec <spec>` 已弃用;会被忽略
|
|
71
|
+
- `--skip-register` 只重写插件配置,不修改全局插件 bridge
|
|
71
72
|
- `--home-dir <path>` 使用自定义 home 目录
|
|
72
73
|
|
|
73
74
|
`update` 支持:
|
|
74
75
|
|
|
75
|
-
- `--plugin-spec <spec>`
|
|
76
|
+
- `--plugin-spec <spec>` 已弃用;会被忽略
|
|
76
77
|
- `--home-dir <path>`
|
|
77
78
|
|
|
78
79
|
## 配置
|
|
@@ -81,10 +82,14 @@ npm exec --package opencode-tbot@latest opencode-tbot -- --bot-token <token>
|
|
|
81
82
|
|
|
82
83
|
- `~/.config/opencode/opencode-tbot/config.json`
|
|
83
84
|
|
|
84
|
-
OpenCode
|
|
85
|
+
OpenCode 会从以下路径加载这个插件的全局 bridge:
|
|
85
86
|
|
|
86
|
-
-
|
|
87
|
-
|
|
87
|
+
- `~/.config/opencode/plugins/opencode-tbot.js`
|
|
88
|
+
|
|
89
|
+
旧版 npm 注册迁移时,安装器只会在发现残留配置时清理全局 OpenCode 配置中的以下文件:
|
|
90
|
+
|
|
91
|
+
- `~/.config/opencode/opencode.jsonc`
|
|
92
|
+
- `~/.config/opencode/opencode.json`
|
|
88
93
|
|
|
89
94
|
本插件不会从 `.env` 读取运行时配置,请使用全局 JSON 配置文件。
|
|
90
95
|
|
|
@@ -235,7 +240,7 @@ npm install -g opencode-ai
|
|
|
235
240
|
$env:OPENCODE_HOST_E2E="1"; pnpm test
|
|
236
241
|
```
|
|
237
242
|
|
|
238
|
-
|
|
243
|
+
正常使用应依赖自动生成的全局 bridge `~/.config/opencode/plugins/opencode-tbot.js` 加上全局插件配置。本仓库不再默认提供项目级 `.opencode/plugins` bridge。
|
|
239
244
|
|
|
240
245
|
如果你在本仓库里做源码调试,需要手动创建 `.opencode/plugins/opencode-tbot.ts`:
|
|
241
246
|
|
|
@@ -243,7 +248,7 @@ $env:OPENCODE_HOST_E2E="1"; pnpm test
|
|
|
243
248
|
export { default } from "../../src/plugin.ts";
|
|
244
249
|
```
|
|
245
250
|
|
|
246
|
-
同一时间只保留一种加载方式即可:要么使用手动创建的本地 bridge
|
|
251
|
+
同一时间只保留一种加载方式即可:要么使用手动创建的本地 bridge,要么使用 `~/.config/opencode/plugins/` 下自动生成的全局 bridge。
|
|
247
252
|
|
|
248
253
|
## FAQ
|
|
249
254
|
|
|
@@ -251,9 +256,9 @@ export { default } from "../../src/plugin.ts";
|
|
|
251
256
|
|
|
252
257
|
需要。本仓库只提供 Telegram 集成层,依赖加载该插件的 OpenCode Host 进程。
|
|
253
258
|
|
|
254
|
-
###
|
|
259
|
+
### 为什么生成出来的 bridge 可能会指向 `file:///.../node_modules/...`?
|
|
255
260
|
|
|
256
|
-
|
|
261
|
+
这通常表示你是在某个已经把 `opencode-tbot` 安装进本地 `node_modules` 的项目里运行了安装器,所以生成的 bridge 指向了那个包路径。CLI 检测到这种布局时会给出警告。请在那个项目里执行 `npm uninstall opencode-tbot` 移除本地包,然后改用推荐的 `npm exec --package ...` 安装流程。
|
|
257
262
|
|
|
258
263
|
### 这是 OpenCode 官方项目吗?
|
|
259
264
|
|
|
@@ -7,6 +7,8 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
//#region src/app/opencode-paths.ts
|
|
8
8
|
var GLOBAL_PLUGIN_DIRECTORY_NAME = "opencode-tbot";
|
|
9
9
|
var GLOBAL_PLUGIN_CONFIG_FILE_NAME = "config.json";
|
|
10
|
+
var GLOBAL_PLUGINS_DIRECTORY_NAME = "plugins";
|
|
11
|
+
var GLOBAL_PLUGIN_BRIDGE_FILE_NAME = "opencode-tbot.js";
|
|
10
12
|
var OPENCODE_CONFIG_FILE_NAME = "opencode.json";
|
|
11
13
|
var OPENCODE_CONFIG_JSONC_FILE_NAME = "opencode.jsonc";
|
|
12
14
|
function getOpenCodeConfigDirectory(homeDir = homedir()) {
|
|
@@ -28,6 +30,12 @@ async function resolveWritableOpenCodeConfigFilePath(homeDir = homedir()) {
|
|
|
28
30
|
function getGlobalPluginConfigFilePath(homeDir = homedir()) {
|
|
29
31
|
return join(getOpenCodeConfigDirectory(homeDir), GLOBAL_PLUGIN_DIRECTORY_NAME, GLOBAL_PLUGIN_CONFIG_FILE_NAME);
|
|
30
32
|
}
|
|
33
|
+
function getGlobalPluginsDirectory(homeDir = homedir()) {
|
|
34
|
+
return join(getOpenCodeConfigDirectory(homeDir), GLOBAL_PLUGINS_DIRECTORY_NAME);
|
|
35
|
+
}
|
|
36
|
+
function getGlobalPluginBridgeFilePath(homeDir = homedir()) {
|
|
37
|
+
return join(getGlobalPluginsDirectory(homeDir), GLOBAL_PLUGIN_BRIDGE_FILE_NAME);
|
|
38
|
+
}
|
|
31
39
|
function getOpenCodeLogDirectory(homeDir = homedir()) {
|
|
32
40
|
return join(homeDir, ".local", "share", "opencode", "log");
|
|
33
41
|
}
|
|
@@ -285,6 +293,6 @@ function stripLegacyVoiceConfig(config) {
|
|
|
285
293
|
return rest;
|
|
286
294
|
}
|
|
287
295
|
//#endregion
|
|
288
|
-
export { DEFAULT_TELEGRAM_API_ROOT as a,
|
|
296
|
+
export { DEFAULT_TELEGRAM_API_ROOT as a, getGlobalPluginBridgeFilePath as c, OPENCODE_TBOT_VERSION as i, getGlobalPluginConfigFilePath as l, preparePluginConfiguration as n, loadAppConfig as o, writePluginConfigFile as r, getDefaultPluginLogDirectory as s, mergePluginConfigSources as t, resolveWritableOpenCodeConfigFilePath as u };
|
|
289
297
|
|
|
290
|
-
//# sourceMappingURL=plugin-config-
|
|
298
|
+
//# sourceMappingURL=plugin-config-Be3vV2kr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-config-Be3vV2kr.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 GLOBAL_PLUGINS_DIRECTORY_NAME = \"plugins\";\nexport const GLOBAL_PLUGIN_BRIDGE_FILE_NAME = \"opencode-tbot.js\";\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 getGlobalPluginsDirectory(homeDir: string = homedir()): string {\n return join(getOpenCodeConfigDirectory(homeDir), GLOBAL_PLUGINS_DIRECTORY_NAME);\n}\n\nexport function getGlobalPluginBridgeFilePath(homeDir: string = homedir()): string {\n return join(\n getGlobalPluginsDirectory(homeDir),\n GLOBAL_PLUGIN_BRIDGE_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 getGlobalPluginBridgeFilePath,\n getGlobalPluginConfigFilePath,\n getGlobalPluginsDirectory,\n getOpenCodeConfigDirectory,\n getOpenCodeConfigFilePath,\n getOpenCodeJsoncConfigFilePath,\n GLOBAL_PLUGIN_BRIDGE_FILE_NAME,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n GLOBAL_PLUGINS_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 getGlobalPluginBridgeFilePath,\n getGlobalPluginConfigFilePath,\n getGlobalPluginsDirectory,\n getOpenCodeConfigDirectory,\n getOpenCodeConfigFilePath,\n getOpenCodeJsoncConfigFilePath,\n GLOBAL_PLUGIN_BRIDGE_FILE_NAME,\n GLOBAL_PLUGIN_CONFIG_FILE_NAME,\n GLOBAL_PLUGIN_DIRECTORY_NAME,\n GLOBAL_PLUGINS_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
|
|
1
|
+
import { c as getGlobalPluginBridgeFilePath, i as OPENCODE_TBOT_VERSION, l as getGlobalPluginConfigFilePath, r as writePluginConfigFile, s as getDefaultPluginLogDirectory, t as mergePluginConfigSources, u as resolveWritableOpenCodeConfigFilePath } from "./assets/plugin-config-Be3vV2kr.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";
|
|
@@ -6,7 +6,6 @@ import { stderr, stdin, stdout } from "node:process";
|
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { parse } from "jsonc-parser";
|
|
8
8
|
//#region src/cli.ts
|
|
9
|
-
var DEFAULT_PLUGIN_SPEC = "opencode-tbot@latest";
|
|
10
9
|
var PROMPT_CANCELLED_ERROR = "Prompt cancelled.";
|
|
11
10
|
async function main(argv = process.argv.slice(2)) {
|
|
12
11
|
try {
|
|
@@ -39,21 +38,20 @@ async function runCli(argv) {
|
|
|
39
38
|
async function installPlugin(options = {}) {
|
|
40
39
|
const homeDir = options.homeDir ?? homedir();
|
|
41
40
|
const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);
|
|
41
|
+
const globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);
|
|
42
42
|
const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);
|
|
43
|
-
const openCodeConfig = await readJsoncObject(openCodeConfigFilePath);
|
|
44
43
|
const existingPluginConfig = await readPluginConfigFile(globalPluginConfigFilePath);
|
|
45
44
|
const prompt = createPromptSession();
|
|
46
45
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (options.registerPlugin === false) await ensureParentDirectory(openCodeConfigFilePath);
|
|
53
|
-
else await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);
|
|
46
|
+
const nextPluginConfig = buildInstalledPluginConfig(existingPluginConfig, normalizeRequiredString(options.botToken ?? await prompt.askSecret("Telegram bot token: "), "Telegram bot token is required."), normalizeOptionalString(options.telegramApiRoot) ?? "https://api.telegram.org");
|
|
47
|
+
if (options.registerPlugin !== false) await installGlobalPluginBridge({
|
|
48
|
+
openCodeConfigFilePath,
|
|
49
|
+
pluginBridgeFilePath: globalPluginBridgeFilePath
|
|
50
|
+
});
|
|
54
51
|
await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);
|
|
55
52
|
stdout.write("Success.\n");
|
|
56
53
|
stdout.write(`OpenCode config: ${openCodeConfigFilePath}\n`);
|
|
54
|
+
stdout.write(`Plugin bridge: ${globalPluginBridgeFilePath}${options.registerPlugin === false ? " (skipped)" : ""}\n`);
|
|
57
55
|
stdout.write(`Plugin config: ${globalPluginConfigFilePath}\n`);
|
|
58
56
|
stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\n`);
|
|
59
57
|
await warnAboutLegacyLocalInstallations(homeDir);
|
|
@@ -64,9 +62,14 @@ async function installPlugin(options = {}) {
|
|
|
64
62
|
async function updatePlugin(options = {}) {
|
|
65
63
|
const homeDir = options.homeDir ?? homedir();
|
|
66
64
|
const openCodeConfigFilePath = await resolveWritableOpenCodeConfigFilePath(homeDir);
|
|
67
|
-
|
|
65
|
+
const globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);
|
|
66
|
+
await installGlobalPluginBridge({
|
|
67
|
+
openCodeConfigFilePath,
|
|
68
|
+
pluginBridgeFilePath: globalPluginBridgeFilePath
|
|
69
|
+
});
|
|
68
70
|
stdout.write("Success.\n");
|
|
69
71
|
stdout.write(`OpenCode config: ${openCodeConfigFilePath}\n`);
|
|
72
|
+
stdout.write(`Plugin bridge: ${globalPluginBridgeFilePath}\n`);
|
|
70
73
|
stdout.write(`Plugin config: ${getGlobalPluginConfigFilePath(homeDir)}\n`);
|
|
71
74
|
stdout.write(`Plugin logs: ${getDefaultPluginLogDirectory(homeDir)}\n`);
|
|
72
75
|
await warnAboutLegacyLocalInstallations(homeDir);
|
|
@@ -116,33 +119,12 @@ function buildInstalledPluginConfig(current, botToken, telegramApiRoot) {
|
|
|
116
119
|
} });
|
|
117
120
|
return nextConfig;
|
|
118
121
|
}
|
|
119
|
-
function
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function replacePluginRegistration(config, pluginSpec) {
|
|
128
|
-
const currentPlugins = Array.isArray(config.plugin) ? config.plugin.filter((item) => typeof item === "string") : [];
|
|
129
|
-
const nextPlugins = [];
|
|
130
|
-
let inserted = false;
|
|
131
|
-
for (const currentPlugin of currentPlugins) {
|
|
132
|
-
if (isOpencodeTbotPluginSpec(currentPlugin)) {
|
|
133
|
-
if (!inserted) {
|
|
134
|
-
nextPlugins.push(pluginSpec);
|
|
135
|
-
inserted = true;
|
|
136
|
-
}
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
nextPlugins.push(currentPlugin);
|
|
140
|
-
}
|
|
141
|
-
if (!inserted) nextPlugins.push(pluginSpec);
|
|
142
|
-
return {
|
|
143
|
-
...config,
|
|
144
|
-
plugin: nextPlugins
|
|
145
|
-
};
|
|
122
|
+
function removePluginRegistration(config) {
|
|
123
|
+
const nextPlugins = (Array.isArray(config.plugin) ? config.plugin : []).filter((currentPlugin) => typeof currentPlugin !== "string" || !isOpencodeTbotPluginSpec(currentPlugin));
|
|
124
|
+
const nextConfig = { ...config };
|
|
125
|
+
if (nextPlugins.length > 0) nextConfig.plugin = nextPlugins;
|
|
126
|
+
else delete nextConfig.plugin;
|
|
127
|
+
return nextConfig;
|
|
146
128
|
}
|
|
147
129
|
async function readPluginConfigFile(configFilePath) {
|
|
148
130
|
try {
|
|
@@ -167,9 +149,41 @@ async function writeJsonFile(filePath, value) {
|
|
|
167
149
|
await ensureParentDirectory(filePath);
|
|
168
150
|
await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
169
151
|
}
|
|
152
|
+
async function installGlobalPluginBridge(input) {
|
|
153
|
+
const pluginModuleUrl = await resolveInstalledPluginModuleUrl();
|
|
154
|
+
await writePluginBridgeFile(input.pluginBridgeFilePath, pluginModuleUrl);
|
|
155
|
+
await removeLegacyNpmPluginRegistration(input.openCodeConfigFilePath);
|
|
156
|
+
}
|
|
170
157
|
async function ensureParentDirectory(filePath) {
|
|
171
158
|
await mkdir(dirname(filePath), { recursive: true });
|
|
172
159
|
}
|
|
160
|
+
async function writePluginBridgeFile(pluginBridgeFilePath, pluginModuleUrl) {
|
|
161
|
+
await ensureParentDirectory(pluginBridgeFilePath);
|
|
162
|
+
await writeFile(pluginBridgeFilePath, buildPluginBridgeSource(pluginModuleUrl), "utf8");
|
|
163
|
+
}
|
|
164
|
+
function buildPluginBridgeSource(pluginModuleUrl) {
|
|
165
|
+
return `export { default } from ${JSON.stringify(pluginModuleUrl)};\n`;
|
|
166
|
+
}
|
|
167
|
+
async function resolveInstalledPluginModuleUrl() {
|
|
168
|
+
const candidates = [
|
|
169
|
+
new URL("./plugin.js", import.meta.url),
|
|
170
|
+
new URL("./plugin.ts", import.meta.url),
|
|
171
|
+
new URL("../src/plugin.ts", import.meta.url),
|
|
172
|
+
new URL("../dist/plugin.js", import.meta.url)
|
|
173
|
+
];
|
|
174
|
+
for (const candidate of candidates) if (await pathExists(candidate)) return candidate.href;
|
|
175
|
+
throw new Error("Failed to resolve the opencode-tbot plugin entry for the global OpenCode bridge.");
|
|
176
|
+
}
|
|
177
|
+
async function removeLegacyNpmPluginRegistration(openCodeConfigFilePath) {
|
|
178
|
+
if (!await pathExists(openCodeConfigFilePath)) return;
|
|
179
|
+
const currentOpenCodeConfig = await readJsoncObject(openCodeConfigFilePath);
|
|
180
|
+
const nextOpenCodeConfig = removePluginRegistration(currentOpenCodeConfig);
|
|
181
|
+
if (areJsonObjectsEqual(currentOpenCodeConfig, nextOpenCodeConfig)) return;
|
|
182
|
+
await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);
|
|
183
|
+
}
|
|
184
|
+
function areJsonObjectsEqual(left, right) {
|
|
185
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
186
|
+
}
|
|
173
187
|
function createPromptSession(options = {}) {
|
|
174
188
|
const input = options.input ?? stdin;
|
|
175
189
|
const output = options.output ?? stdout;
|
|
@@ -294,7 +308,7 @@ function isOpencodeTbotPluginSpec(value) {
|
|
|
294
308
|
async function warnAboutLegacyLocalInstallations(homeDir) {
|
|
295
309
|
const legacyInstallations = await findLegacyLocalInstallations(homeDir);
|
|
296
310
|
if (legacyInstallations.length === 0) return;
|
|
297
|
-
stdout.write("\nDetected local opencode-tbot npm installation(s) that can make
|
|
311
|
+
stdout.write("\nDetected local opencode-tbot npm installation(s) that can make the global OpenCode plugin bridge depend on project-local node_modules paths.\n");
|
|
298
312
|
for (const installation of legacyInstallations) {
|
|
299
313
|
stdout.write(`- ${installation.packagePath}\n`);
|
|
300
314
|
stdout.write(` cleanup: cd "${installation.rootDir}" && npm uninstall opencode-tbot\n`);
|
|
@@ -335,7 +349,7 @@ function buildHelpText() {
|
|
|
335
349
|
"Options:",
|
|
336
350
|
" --bot-token <token>",
|
|
337
351
|
" --telegram-api-root <url>",
|
|
338
|
-
" --plugin-spec <spec>",
|
|
352
|
+
" --plugin-spec <spec> (deprecated; ignored)",
|
|
339
353
|
" --skip-register",
|
|
340
354
|
" --home-dir <path>",
|
|
341
355
|
" --version",
|
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 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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"}
|
|
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 getGlobalPluginBridgeFilePath,\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?: unknown[];\n [key: string]: unknown;\n}\n\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 globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);\n const globalPluginConfigFilePath = getGlobalPluginConfigFilePath(homeDir);\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 nextPluginConfig = buildInstalledPluginConfig(\n existingPluginConfig,\n botToken,\n telegramApiRoot,\n );\n\n if (options.registerPlugin !== false) {\n await installGlobalPluginBridge({\n openCodeConfigFilePath,\n pluginBridgeFilePath: globalPluginBridgeFilePath,\n });\n }\n await writePluginConfigFile(globalPluginConfigFilePath, nextPluginConfig);\n\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(\n `Plugin bridge: ${globalPluginBridgeFilePath}${options.registerPlugin === false ? \" (skipped)\" : \"\"}\\n`,\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 globalPluginBridgeFilePath = getGlobalPluginBridgeFilePath(homeDir);\n\n await installGlobalPluginBridge({\n openCodeConfigFilePath,\n pluginBridgeFilePath: globalPluginBridgeFilePath,\n });\n stdout.write(\"Success.\\n\");\n stdout.write(`OpenCode config: ${openCodeConfigFilePath}\\n`);\n stdout.write(`Plugin bridge: ${globalPluginBridgeFilePath}\\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 removePluginRegistration(\n config: OpenCodeGlobalConfig,\n): OpenCodeGlobalConfig {\n const currentPlugins = Array.isArray(config.plugin)\n ? config.plugin\n : [];\n const nextPlugins = currentPlugins.filter(\n (currentPlugin) => typeof currentPlugin !== \"string\" || !isOpencodeTbotPluginSpec(currentPlugin),\n );\n const nextConfig = {\n ...config,\n };\n\n if (nextPlugins.length > 0) {\n nextConfig.plugin = nextPlugins;\n } else {\n delete nextConfig.plugin;\n }\n\n return nextConfig;\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 installGlobalPluginBridge(\n input: {\n openCodeConfigFilePath: string;\n pluginBridgeFilePath: string;\n },\n): Promise<void> {\n const pluginModuleUrl = await resolveInstalledPluginModuleUrl();\n\n await writePluginBridgeFile(input.pluginBridgeFilePath, pluginModuleUrl);\n await removeLegacyNpmPluginRegistration(input.openCodeConfigFilePath);\n}\n\nasync function ensureParentDirectory(filePath: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n}\n\nasync function writePluginBridgeFile(\n pluginBridgeFilePath: string,\n pluginModuleUrl: string,\n): Promise<void> {\n await ensureParentDirectory(pluginBridgeFilePath);\n await writeFile(\n pluginBridgeFilePath,\n buildPluginBridgeSource(pluginModuleUrl),\n \"utf8\",\n );\n}\n\nfunction buildPluginBridgeSource(pluginModuleUrl: string): string {\n return `export { default } from ${JSON.stringify(pluginModuleUrl)};\\n`;\n}\n\nasync function resolveInstalledPluginModuleUrl(): Promise<string> {\n const candidates = [\n new URL(\"./plugin.js\", import.meta.url),\n new URL(\"./plugin.ts\", import.meta.url),\n new URL(\"../src/plugin.ts\", import.meta.url),\n new URL(\"../dist/plugin.js\", import.meta.url),\n ];\n\n for (const candidate of candidates) {\n if (await pathExists(candidate)) {\n return candidate.href;\n }\n }\n\n throw new Error(\"Failed to resolve the opencode-tbot plugin entry for the global OpenCode bridge.\");\n}\n\nasync function removeLegacyNpmPluginRegistration(openCodeConfigFilePath: string): Promise<void> {\n if (!await pathExists(openCodeConfigFilePath)) {\n return;\n }\n\n const currentOpenCodeConfig = await readJsoncObject<OpenCodeGlobalConfig>(openCodeConfigFilePath);\n const nextOpenCodeConfig = removePluginRegistration(currentOpenCodeConfig);\n\n if (areJsonObjectsEqual(currentOpenCodeConfig, nextOpenCodeConfig)) {\n return;\n }\n\n await writeJsonFile(openCodeConfigFilePath, nextOpenCodeConfig);\n}\n\nfunction areJsonObjectsEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\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(\n \"\\nDetected local opencode-tbot npm installation(s) that can make the global OpenCode plugin bridge depend on project-local node_modules paths.\\n\",\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 | URL): 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> (deprecated; ignored)\",\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-Be3vV2kr.js";
|
|
2
2
|
import { TelegramBotPlugin, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests } from "./plugin.js";
|
|
3
3
|
export { TelegramBotPlugin, TelegramBotPlugin as default, ensureTelegramBotPluginRuntime, resetTelegramBotPluginRuntimeForTests };
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-
|
|
1
|
+
import { i as OPENCODE_TBOT_VERSION, n as preparePluginConfiguration, o as loadAppConfig } from "./assets/plugin-config-Be3vV2kr.js";
|
|
2
2
|
import { appendFile, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, isAbsolute, join } from "node:path";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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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"}
|