@neomei/opencode-feishu 0.1.0

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/LICENSE +21 -0
  3. package/README.md +140 -0
  4. package/bin/opencode-feishu +2 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +273 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/config.d.ts +48 -0
  10. package/dist/core/config.d.ts.map +1 -0
  11. package/dist/core/config.js +76 -0
  12. package/dist/core/config.js.map +1 -0
  13. package/dist/core/daemon.d.ts +42 -0
  14. package/dist/core/daemon.d.ts.map +1 -0
  15. package/dist/core/daemon.js +134 -0
  16. package/dist/core/daemon.js.map +1 -0
  17. package/dist/core/logger.d.ts +10 -0
  18. package/dist/core/logger.d.ts.map +1 -0
  19. package/dist/core/logger.js +42 -0
  20. package/dist/core/logger.js.map +1 -0
  21. package/dist/core/message-handler.d.ts +13 -0
  22. package/dist/core/message-handler.d.ts.map +1 -0
  23. package/dist/core/message-handler.js +120 -0
  24. package/dist/core/message-handler.js.map +1 -0
  25. package/dist/core/session-manager.d.ts +34 -0
  26. package/dist/core/session-manager.d.ts.map +1 -0
  27. package/dist/core/session-manager.js +187 -0
  28. package/dist/core/session-manager.js.map +1 -0
  29. package/dist/core/types.d.ts +87 -0
  30. package/dist/core/types.d.ts.map +1 -0
  31. package/dist/core/types.js +3 -0
  32. package/dist/core/types.js.map +1 -0
  33. package/dist/feishu/api.d.ts +23 -0
  34. package/dist/feishu/api.d.ts.map +1 -0
  35. package/dist/feishu/api.js +97 -0
  36. package/dist/feishu/api.js.map +1 -0
  37. package/dist/feishu/card.d.ts +11 -0
  38. package/dist/feishu/card.d.ts.map +1 -0
  39. package/dist/feishu/card.js +62 -0
  40. package/dist/feishu/card.js.map +1 -0
  41. package/dist/feishu/event-source.d.ts +19 -0
  42. package/dist/feishu/event-source.d.ts.map +1 -0
  43. package/dist/feishu/event-source.js +70 -0
  44. package/dist/feishu/event-source.js.map +1 -0
  45. package/dist/feishu/silent-logger.d.ts +16 -0
  46. package/dist/feishu/silent-logger.d.ts.map +1 -0
  47. package/dist/feishu/silent-logger.js +16 -0
  48. package/dist/feishu/silent-logger.js.map +1 -0
  49. package/dist/index.d.ts +6 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +6 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/opencode/client.d.ts +27 -0
  54. package/dist/opencode/client.d.ts.map +1 -0
  55. package/dist/opencode/client.js +86 -0
  56. package/dist/opencode/client.js.map +1 -0
  57. package/dist/opencode/event-handler.d.ts +26 -0
  58. package/dist/opencode/event-handler.d.ts.map +1 -0
  59. package/dist/opencode/event-handler.js +212 -0
  60. package/dist/opencode/event-handler.js.map +1 -0
  61. package/dist/plugin.d.ts +4 -0
  62. package/dist/plugin.d.ts.map +1 -0
  63. package/dist/plugin.js +64 -0
  64. package/dist/plugin.js.map +1 -0
  65. package/dist/setup/preflight.d.ts +29 -0
  66. package/dist/setup/preflight.d.ts.map +1 -0
  67. package/dist/setup/preflight.js +125 -0
  68. package/dist/setup/preflight.js.map +1 -0
  69. package/dist/setup/wizard.d.ts +11 -0
  70. package/dist/setup/wizard.d.ts.map +1 -0
  71. package/dist/setup/wizard.js +263 -0
  72. package/dist/setup/wizard.js.map +1 -0
  73. package/dist/standalone.d.ts +2 -0
  74. package/dist/standalone.d.ts.map +1 -0
  75. package/dist/standalone.js +116 -0
  76. package/dist/standalone.js.map +1 -0
  77. package/dist/types/plugin.d.ts +23 -0
  78. package/dist/types/plugin.d.ts.map +1 -0
  79. package/dist/types/plugin.js +2 -0
  80. package/dist/types/plugin.js.map +1 -0
  81. package/package.json +80 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. This project follows
4
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [0.1.0] — 2026-04-23
7
+
8
+ Initial public release. Node.js-native bridge between Feishu/Lark and OpenCode
9
+ with a production-grade runtime (daemon mode, structured logging, persistent
10
+ sessions, preflight diagnostics).
11
+
12
+ ### Added
13
+
14
+ - **Dual operation modes**
15
+ - Standalone: `opencode-feishu start [--daemon]` runs as an independent
16
+ process with PID file and heartbeat status file.
17
+ - OpenCode plugin: loadable via `plugins: ["@neomei/opencode-feishu"]` in
18
+ OpenCode config.
19
+ - **SDK-based Feishu client** using `@larksuiteoapi/node-sdk` — all API calls
20
+ (send card, patch card, bot info, event stream) go through the SDK; no
21
+ `lark-cli` subprocess or direct HTTP hand-rolling.
22
+ - **Long-connection event ingress** via `Lark.WSClient` with SDK-managed
23
+ auto-reconnect.
24
+ - **Streaming interactive card** — one card per bot turn, updated in place
25
+ as text deltas, tool state transitions, and retry notices arrive. Header
26
+ flips to "✅ 完成" on `session.idle`. Text is streamed live (character
27
+ by character) rather than dumped at the end.
28
+ - **Session persistence** — `~/.config/opencode/feishu-sessions.json` stores
29
+ `chat_id → session_id` mappings across restarts. Lazy reconciliation
30
+ probes OpenCode on startup; stale mappings are dropped and recreated.
31
+ - **Preflight + doctor** — `opencode-feishu doctor` runs structured checks
32
+ against config, credentials, OpenCode connectivity, and filesystem state.
33
+ `--json` flag for machine-readable output.
34
+ - **Daemon mode** — `start --daemon` detaches from the terminal, redirects
35
+ stdout/stderr to `~/.config/opencode/feishu.log`, writes PID and status
36
+ files for the `status` command to observe.
37
+ - **Logs subcommand** — `opencode-feishu logs -n <N> [-f] [--json]` tails
38
+ the structured NDJSON log with human-friendly formatting and ANSI
39
+ color when output is a TTY.
40
+ - **Status subcommand** — reports uptime, OpenCode URL, Feishu WS state,
41
+ session count, heartbeat freshness; detects zombie / stale daemon states.
42
+ - **Setup wizard** — `opencode-feishu setup` prompts for credentials,
43
+ runs preflight inline, supports skipping when existing config passes all
44
+ checks.
45
+ - **Structured logging** — `pino` with NDJSON file sink
46
+ (`~/.config/opencode/feishu.log`) plus pretty TTY mirror. Per-module
47
+ child loggers. Configurable via `FEISHU_LOG_LEVEL` and `FEISHU_LOG_FILE`.
48
+ - **Group governance** — `requireMention`, `groupPolicy` (`open` /
49
+ `allowlist` / `disabled`), and `allowlist` (union_id list). Mention
50
+ detection compares against the bot's `open_id` fetched from
51
+ `/open-apis/bot/v3/info`.
52
+ - **Config via Zod** — schema validation with clear errors. `appSecret`
53
+ can live in the config file or be provided via the `FEISHU_APP_SECRET`
54
+ environment variable.
55
+
56
+ ### Known gaps (planned for subsequent releases)
57
+
58
+ - Image / file message input.
59
+ - Slash commands (`/new`, `/sessions`, `/model`, etc.).
60
+ - `feishu_notify` tool (agent pushing progress mid-task).
61
+ - Feishu document reading.
62
+ - Multi-agent / multi-channel abstraction (for the multi-agent platform
63
+ this project feeds into).
64
+
65
+ ### Notes
66
+
67
+ Published on npm as `@neomei/opencode-feishu`. Requires Node.js ≥18.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 neomei
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # OpenCode 飞书集成插件
2
+
3
+ 将 OpenCode AI 助手接入飞书(Feishu/Lark),支持私聊和群聊,提供流式输出和工具状态通知。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g @opencode-ai/feishu
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ### 1. 配置插件
14
+
15
+ ```bash
16
+ opencode-feishu setup
17
+ ```
18
+
19
+ 按向导输入 App ID、App Secret 等配置。
20
+
21
+ ### 2. 启动插件
22
+
23
+ ```bash
24
+ # 启动(需要 OpenCode 服务器运行)
25
+ opencode-feishu start
26
+
27
+ # 指定配置文件
28
+ opencode-feishu start -c /path/to/config.json
29
+ ```
30
+
31
+ ### 3. 管理插件
32
+
33
+ ```bash
34
+ # 查看状态
35
+ opencode-feishu status
36
+
37
+ # 停止插件
38
+ opencode-feishu stop
39
+ ```
40
+
41
+ ## 飞书应用配置
42
+
43
+ ### 创建应用
44
+
45
+ 1. 访问 [飞书开放平台](https://open.feishu.cn/app)
46
+ 2. 创建"企业自建应用"
47
+ 3. 开启"机器人"能力
48
+ 4. 添加权限:
49
+ - `im:message`
50
+ - `im:message.p2p_msg:readonly`
51
+ - `im:message.group_at_msg:readonly`
52
+ - `im:message:send_as_bot`
53
+ - `im:resource`
54
+ - `contact:user.base:readonly`
55
+ 5. 事件配置:使用长连接,添加 `im.message.receive_v1`
56
+ 6. 发布应用
57
+
58
+ ### 获取凭证
59
+
60
+ 在"凭证与基础信息"页面获取 **App ID**(格式:`cli_xxxxxxxx`)和 **App Secret**。
61
+
62
+ ## 配置项
63
+
64
+ 配置文件位置:`~/.config/opencode/feishu.json`
65
+
66
+ ```json
67
+ {
68
+ "appId": "cli_xxxxxxxx",
69
+ "appSecret": "xxxxxxxx",
70
+ "domain": "feishu",
71
+ "opencodeUrl": "http://localhost:19876",
72
+ "streaming": true,
73
+ "requireMention": true,
74
+ "groupPolicy": "allowlist",
75
+ "allowlist": []
76
+ }
77
+ ```
78
+
79
+ | 配置项 | 说明 | 默认值 |
80
+ |--------|------|--------|
81
+ | `appId` | 飞书 App ID | - |
82
+ | `appSecret` | 飞书 App Secret | - |
83
+ | `domain` | 域名:`feishu` 或 `lark` | `feishu` |
84
+ | `opencodeUrl` | OpenCode 服务器地址 | `http://localhost:19876` |
85
+ | `streaming` | 启用流式输出 | `true` |
86
+ | `requireMention` | 群聊需@机器人 | `true` |
87
+ | `groupPolicy` | 群策略:`open`/`allowlist`/`disabled` | `allowlist` |
88
+ | `allowlist` | 用户白名单 | `[]` |
89
+
90
+ > `appSecret` 也可通过环境变量 `FEISHU_APP_SECRET` 设置,优先级高于配置文件。
91
+
92
+ ## 使用方式
93
+
94
+ ### 私聊
95
+ 直接发送消息给机器人即可开始对话。
96
+
97
+ ### 群聊
98
+ 在群中 @机器人 发送消息(需开启 `requireMention`)。
99
+
100
+ ### 连续会话
101
+ 每个聊天(私聊/群聊)对应一个独立的 OpenCode session,支持上下文连续对话。
102
+
103
+ ## 特性
104
+
105
+ - **流式输出**:OpenCode 的回复实时显示在飞书中
106
+ - **状态通知**:显示思考中、工具执行等状态
107
+ - **自动重连**:WebSocket 断线后 SDK 自动重连
108
+ - **双模式运行**:
109
+ - **独立模式**:`opencode-feishu start`
110
+ - **插件模式**:在 OpenCode 中加载
111
+
112
+ ## CLI 命令
113
+
114
+ ```bash
115
+ opencode-feishu setup # 配置向导
116
+ opencode-feishu start # 启动插件
117
+ opencode-feishu status # 查看状态
118
+ opencode-feishu stop # 停止插件
119
+ opencode-feishu --help # 查看帮助
120
+ ```
121
+
122
+ ## 作为 OpenCode 插件使用
123
+
124
+ 在 OpenCode 配置中添加:
125
+
126
+ ```json
127
+ {
128
+ "plugins": ["@opencode-ai/feishu"]
129
+ }
130
+ ```
131
+
132
+ ## 环境要求
133
+
134
+ - Node.js >= 18.0.0
135
+ - OpenCode 服务器运行中
136
+ - 飞书企业自建应用
137
+
138
+ ## 许可证
139
+
140
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/cli.js');
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,273 @@
1
+ import { Command } from 'commander';
2
+ import { readFileSync, existsSync, unlinkSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { homedir } from 'os';
5
+ import { startStandalone } from './standalone.js';
6
+ import { SetupWizard } from './setup/wizard.js';
7
+ import { createLogger } from './core/logger.js';
8
+ const log = createLogger('cli');
9
+ const PID_FILE = join(homedir(), '.config', 'opencode', 'feishu.pid');
10
+ const program = new Command();
11
+ program
12
+ .name('opencode-feishu')
13
+ .description('OpenCode Feishu Integration Plugin')
14
+ .version('0.1.0');
15
+ program
16
+ .command('setup')
17
+ .description('Configure the Feishu plugin')
18
+ .option('-c, --config <path>', 'Configuration file path')
19
+ .action(async (options) => {
20
+ try {
21
+ const wizard = new SetupWizard(options.config);
22
+ await wizard.run();
23
+ }
24
+ catch (err) {
25
+ log.error({ err }, 'Setup failed');
26
+ process.exit(1);
27
+ }
28
+ });
29
+ program
30
+ .command('start')
31
+ .description('Start the Feishu plugin (standalone mode)')
32
+ .option('-c, --config <path>', 'Configuration file path')
33
+ .option('-u, --url <url>', 'OpenCode server URL')
34
+ .option('-d, --daemon', 'Run detached in the background with logs → ~/.config/opencode/feishu.log')
35
+ .action(async (options) => {
36
+ try {
37
+ const isDaemonChild = process.env.FEISHU_DAEMONIZED === '1';
38
+ // Already-running check: ignore when we ARE the daemon child (parent already wrote PID).
39
+ if (!isDaemonChild && existsSync(PID_FILE)) {
40
+ const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim());
41
+ try {
42
+ process.kill(pid, 0);
43
+ log.error({ pid }, 'Plugin is already running');
44
+ console.error(' Use "opencode-feishu stop" to stop it first');
45
+ process.exit(1);
46
+ }
47
+ catch {
48
+ unlinkSync(PID_FILE);
49
+ }
50
+ }
51
+ if (options.daemon && !isDaemonChild) {
52
+ // Fork detached and exit this parent. The child re-enters `start` without --daemon.
53
+ const { spawnDaemon } = await import('./core/daemon.js');
54
+ spawnDaemon(process.argv.slice(2));
55
+ process.exit(0);
56
+ }
57
+ // Foreground (or daemon child): claim the PID file with our own pid.
58
+ const { writeFileSync, mkdirSync } = await import('fs');
59
+ const pidDir = join(homedir(), '.config', 'opencode');
60
+ if (!existsSync(pidDir))
61
+ mkdirSync(pidDir, { recursive: true });
62
+ writeFileSync(PID_FILE, process.pid.toString());
63
+ await startStandalone(options.config);
64
+ }
65
+ catch (err) {
66
+ log.error({ err }, 'Failed to start');
67
+ process.exit(1);
68
+ }
69
+ });
70
+ program
71
+ .command('status')
72
+ .description('Check plugin status')
73
+ .option('--json', 'Emit JSON instead of human output')
74
+ .action(async (options) => {
75
+ const { readStatus, readPid, isProcessAlive, statusFileAgeMs, HEARTBEAT_STALE_AFTER_MS } = await import('./core/daemon.js');
76
+ const pid = readPid();
77
+ const alive = pid != null && isProcessAlive(pid);
78
+ const snap = readStatus();
79
+ const age = statusFileAgeMs();
80
+ const stale = age != null && age > HEARTBEAT_STALE_AFTER_MS;
81
+ const state = !pid ? 'stopped' :
82
+ !alive ? 'zombie' :
83
+ stale ? 'stale' :
84
+ 'running';
85
+ if (options.json) {
86
+ process.stdout.write(JSON.stringify({ state, pid, snap, heartbeatAgeMs: age }, null, 2) + '\n');
87
+ process.exit(state === 'running' ? 0 : 1);
88
+ }
89
+ switch (state) {
90
+ case 'stopped':
91
+ console.log('📭 Plugin is not running');
92
+ console.log(' Start it: opencode-feishu start [--daemon]');
93
+ break;
94
+ case 'zombie':
95
+ console.log(`⚠️ PID file points to ${pid}, but that process is not running`);
96
+ console.log(' Clean up: opencode-feishu stop');
97
+ break;
98
+ case 'stale':
99
+ console.log(`⚠️ Plugin PID ${pid} is alive but heartbeat is stale (${Math.round((age || 0) / 1000)}s old)`);
100
+ console.log(' It may be hung; consider restarting');
101
+ break;
102
+ case 'running':
103
+ console.log(`✅ Plugin running`);
104
+ console.log(` PID: ${pid}`);
105
+ if (snap) {
106
+ const uptimeSec = Math.floor((Date.now() - snap.startedAt) / 1000);
107
+ console.log(` Uptime: ${formatUptime(uptimeSec)}`);
108
+ console.log(` OpenCode: ${snap.opencodeUrl}`);
109
+ console.log(` Feishu WS: ${snap.feishuConnected ? 'connected' : 'disconnected'}`);
110
+ console.log(` Sessions: ${snap.sessionCount}`);
111
+ console.log(` Heartbeat: ${Math.round((age || 0) / 1000)}s ago`);
112
+ }
113
+ break;
114
+ }
115
+ process.exit(state === 'running' ? 0 : 1);
116
+ });
117
+ function formatUptime(s) {
118
+ const d = Math.floor(s / 86400);
119
+ const h = Math.floor((s % 86400) / 3600);
120
+ const m = Math.floor((s % 3600) / 60);
121
+ const parts = [];
122
+ if (d)
123
+ parts.push(`${d}d`);
124
+ if (h)
125
+ parts.push(`${h}h`);
126
+ if (m)
127
+ parts.push(`${m}m`);
128
+ parts.push(`${s % 60}s`);
129
+ return parts.join(' ');
130
+ }
131
+ program
132
+ .command('doctor')
133
+ .description('Run preflight checks against the current config')
134
+ .option('-c, --config <path>', 'Configuration file path')
135
+ .option('--json', 'Emit JSON instead of human output')
136
+ .action(async (options) => {
137
+ const { ConfigManager } = await import('./core/config.js');
138
+ const preflight = await import('./setup/preflight.js');
139
+ const mgr = new ConfigManager(options.config);
140
+ const config = mgr.exists() ? mgr.load() : null;
141
+ const results = await preflight.runAll(config, { configPath: mgr.getConfigPath() });
142
+ if (options.json) {
143
+ process.stdout.write(JSON.stringify(results, null, 2) + '\n');
144
+ }
145
+ else {
146
+ for (const r of results) {
147
+ process.stdout.write(`${r.ok ? '✅' : '❌'} ${r.label}\n`);
148
+ if (r.detail)
149
+ process.stdout.write(` ${r.detail}\n`);
150
+ if (!r.ok && r.fix)
151
+ process.stdout.write(` 🛠 fix: ${r.fix}\n`);
152
+ }
153
+ }
154
+ process.exit(results.every(r => r.ok) ? 0 : 1);
155
+ });
156
+ program
157
+ .command('logs')
158
+ .description('Show recent plugin logs')
159
+ .option('-n, --lines <n>', 'Lines to show from the tail', '50')
160
+ .option('-f, --follow', 'Follow the log as it grows')
161
+ .option('--json', 'Emit raw NDJSON')
162
+ .option('--log-file <path>', 'Override log path')
163
+ .action(async (options) => {
164
+ const { readFileSync, watchFile, unwatchFile, statSync } = await import('fs');
165
+ const { join } = await import('path');
166
+ const { homedir } = await import('os');
167
+ const logPath = options.logFile || process.env.FEISHU_LOG_FILE || join(homedir(), '.config', 'opencode', 'feishu.log');
168
+ if (!existsSync(logPath)) {
169
+ process.stderr.write(`Log file not found: ${logPath}. Start the plugin first.\n`);
170
+ process.exit(1);
171
+ }
172
+ const n = parseInt(options.lines, 10) || 50;
173
+ const isTTY = process.stdout.isTTY;
174
+ const LEVEL_COLORS = {
175
+ 10: '\x1b[90m', 20: '\x1b[90m', 30: '\x1b[32m', 40: '\x1b[33m', 50: '\x1b[31m', 60: '\x1b[31m',
176
+ };
177
+ const LEVEL_NAMES = {
178
+ 10: 'TRACE', 20: 'DEBUG', 30: 'INFO ', 40: 'WARN ', 50: 'ERROR', 60: 'FATAL',
179
+ };
180
+ const RESET = '\x1b[0m';
181
+ function formatLine(line) {
182
+ if (options.json)
183
+ return line + '\n';
184
+ let rec;
185
+ try {
186
+ rec = JSON.parse(line);
187
+ }
188
+ catch {
189
+ return line + '\n';
190
+ }
191
+ const date = new Date(rec.time);
192
+ const hh = String(date.getHours()).padStart(2, '0');
193
+ const mm = String(date.getMinutes()).padStart(2, '0');
194
+ const ss = String(date.getSeconds()).padStart(2, '0');
195
+ const lvl = rec.level ?? 30;
196
+ const color = isTTY ? (LEVEL_COLORS[lvl] || '') : '';
197
+ const levelName = (LEVEL_NAMES[lvl] || String(lvl)).padEnd(5);
198
+ const mod = rec.module ?? '-';
199
+ const msg = rec.msg ?? '';
200
+ const extraKeys = Object.keys(rec).filter(k => !['level', 'time', 'module', 'msg', 'pid', 'hostname', 'v'].includes(k));
201
+ const extra = extraKeys.map(k => `${k}=${JSON.stringify(rec[k])}`).join(' ');
202
+ return `${color}${hh}:${mm}:${ss} ${levelName} [${mod}] ${msg}${extra ? ' ' + extra : ''}${RESET}\n`;
203
+ }
204
+ function emitTail(path, count) {
205
+ const data = readFileSync(path, 'utf-8');
206
+ const lines = data.split('\n').filter(l => l.length > 0);
207
+ const tail = lines.slice(-count);
208
+ for (const line of tail) {
209
+ process.stdout.write(formatLine(line));
210
+ }
211
+ return tail.length;
212
+ }
213
+ emitTail(logPath, n);
214
+ if (options.follow) {
215
+ let prevSize = statSync(logPath).size;
216
+ watchFile(logPath, { interval: 500 }, (curr) => {
217
+ if (curr.size > prevSize) {
218
+ const fd = require('fs').openSync(logPath, 'r');
219
+ const buf = Buffer.alloc(curr.size - prevSize);
220
+ require('fs').readSync(fd, buf, 0, buf.length, prevSize);
221
+ require('fs').closeSync(fd);
222
+ const lines = buf.toString('utf-8').split('\n').filter(l => l.length > 0);
223
+ for (const line of lines) {
224
+ process.stdout.write(formatLine(line));
225
+ }
226
+ prevSize = curr.size;
227
+ }
228
+ else if (curr.size < prevSize) {
229
+ prevSize = curr.size;
230
+ }
231
+ });
232
+ process.on('SIGINT', () => {
233
+ unwatchFile(logPath);
234
+ process.exit(0);
235
+ });
236
+ await new Promise(() => { });
237
+ }
238
+ });
239
+ program
240
+ .command('stop')
241
+ .description('Stop the Feishu plugin')
242
+ .action(() => {
243
+ if (!existsSync(PID_FILE)) {
244
+ console.log('📭 Plugin is not running');
245
+ return;
246
+ }
247
+ try {
248
+ const pid = parseInt(readFileSync(PID_FILE, 'utf-8').trim());
249
+ process.kill(pid, 'SIGTERM');
250
+ console.log(`🛑 Stopped plugin (PID: ${pid})`);
251
+ }
252
+ catch (err) {
253
+ if (err.code === 'ESRCH') {
254
+ console.log('⚠️ Process not found, cleaning up PID file');
255
+ }
256
+ else {
257
+ console.error('❌ Failed to stop plugin:', err.message);
258
+ }
259
+ }
260
+ finally {
261
+ try {
262
+ unlinkSync(PID_FILE);
263
+ }
264
+ catch { }
265
+ }
266
+ });
267
+ // Parse command line arguments
268
+ program.parse();
269
+ // If no command provided, show help
270
+ if (!process.argv.slice(2).length) {
271
+ program.outputHelp();
272
+ }
273
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ import type { FeishuConfig } from './types.js';
3
+ declare const FeishuConfigSchema: z.ZodObject<{
4
+ appId: z.ZodString;
5
+ appSecret: z.ZodOptional<z.ZodString>;
6
+ domain: z.ZodDefault<z.ZodEnum<["feishu", "lark"]>>;
7
+ opencodeUrl: z.ZodDefault<z.ZodString>;
8
+ streaming: z.ZodDefault<z.ZodBoolean>;
9
+ requireMention: z.ZodDefault<z.ZodBoolean>;
10
+ groupPolicy: z.ZodDefault<z.ZodEnum<["open", "allowlist", "disabled"]>>;
11
+ allowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ appId: string;
14
+ domain: "feishu" | "lark";
15
+ opencodeUrl: string;
16
+ streaming: boolean;
17
+ requireMention: boolean;
18
+ groupPolicy: "open" | "allowlist" | "disabled";
19
+ allowlist?: string[] | undefined;
20
+ appSecret?: string | undefined;
21
+ }, {
22
+ appId: string;
23
+ allowlist?: string[] | undefined;
24
+ appSecret?: string | undefined;
25
+ domain?: "feishu" | "lark" | undefined;
26
+ opencodeUrl?: string | undefined;
27
+ streaming?: boolean | undefined;
28
+ requireMention?: boolean | undefined;
29
+ groupPolicy?: "open" | "allowlist" | "disabled" | undefined;
30
+ }>;
31
+ /**
32
+ * Resolve the app secret in priority order: FEISHU_APP_SECRET env > config file.
33
+ * Throws a clear error if neither is set — the SDK can't auth without it.
34
+ */
35
+ export declare function resolveAppSecret(config: FeishuConfig): string;
36
+ export declare class ConfigManager {
37
+ private configPath;
38
+ private config?;
39
+ constructor(configPath?: string);
40
+ private getDefaultConfigPath;
41
+ load(): FeishuConfig;
42
+ save(config: FeishuConfig): void;
43
+ getConfigPath(): string;
44
+ exists(): boolean;
45
+ }
46
+ export { FeishuConfigSchema };
47
+ export type { FeishuConfig };
48
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAStB,CAAC;AAEH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAQ7D;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAC,CAAe;gBAElB,UAAU,CAAC,EAAE,MAAM;IAI/B,OAAO,CAAC,oBAAoB;IAI5B,IAAI,IAAI,YAAY;IA0BpB,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAYhC,aAAa,IAAI,MAAM;IAIvB,MAAM,IAAI,OAAO;CAGlB;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { homedir } from 'os';
5
+ const FeishuConfigSchema = z.object({
6
+ appId: z.string().startsWith('cli_'),
7
+ appSecret: z.string().min(1).optional(),
8
+ domain: z.enum(['feishu', 'lark']).default('feishu'),
9
+ opencodeUrl: z.string().url().default('http://localhost:19876'),
10
+ streaming: z.boolean().default(true),
11
+ requireMention: z.boolean().default(true),
12
+ groupPolicy: z.enum(['open', 'allowlist', 'disabled']).default('allowlist'),
13
+ allowlist: z.array(z.string()).optional(),
14
+ });
15
+ /**
16
+ * Resolve the app secret in priority order: FEISHU_APP_SECRET env > config file.
17
+ * Throws a clear error if neither is set — the SDK can't auth without it.
18
+ */
19
+ export function resolveAppSecret(config) {
20
+ const fromEnv = process.env.FEISHU_APP_SECRET?.trim();
21
+ if (fromEnv)
22
+ return fromEnv;
23
+ if (config.appSecret)
24
+ return config.appSecret;
25
+ throw new Error('Feishu app secret is missing. Set it in ~/.config/opencode/feishu.json ' +
26
+ '(`appSecret` field) or via the FEISHU_APP_SECRET environment variable.');
27
+ }
28
+ export class ConfigManager {
29
+ configPath;
30
+ config;
31
+ constructor(configPath) {
32
+ this.configPath = configPath || this.getDefaultConfigPath();
33
+ }
34
+ getDefaultConfigPath() {
35
+ return join(homedir(), '.config', 'opencode', 'feishu.json');
36
+ }
37
+ load() {
38
+ if (this.config) {
39
+ return this.config;
40
+ }
41
+ if (!existsSync(this.configPath)) {
42
+ throw new Error(`Configuration file not found: ${this.configPath}\n` +
43
+ 'Please run: opencode-feishu setup');
44
+ }
45
+ try {
46
+ const content = readFileSync(this.configPath, 'utf-8');
47
+ const parsed = JSON.parse(content);
48
+ this.config = FeishuConfigSchema.parse(parsed);
49
+ return this.config;
50
+ }
51
+ catch (error) {
52
+ if (error instanceof z.ZodError) {
53
+ const issues = error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('\n');
54
+ throw new Error(`Invalid configuration:\n${issues}`);
55
+ }
56
+ throw error;
57
+ }
58
+ }
59
+ save(config) {
60
+ const validated = FeishuConfigSchema.parse(config);
61
+ const dir = dirname(this.configPath);
62
+ if (!existsSync(dir)) {
63
+ mkdirSync(dir, { recursive: true });
64
+ }
65
+ writeFileSync(this.configPath, JSON.stringify(validated, null, 2));
66
+ this.config = validated;
67
+ }
68
+ getConfigPath() {
69
+ return this.configPath;
70
+ }
71
+ exists() {
72
+ return existsSync(this.configPath);
73
+ }
74
+ }
75
+ export { FeishuConfigSchema };
76
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}