@yvhitxcel/opencode-remote 0.15.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 (102) hide show
  1. package/README.md +82 -0
  2. package/bin/opencode-remote.js +70 -0
  3. package/bin/opencode-weixin.js +10 -0
  4. package/dist/AGENTS.md +20 -0
  5. package/dist/MEMORY.md +21 -0
  6. package/dist/bot-runner.js +180 -0
  7. package/dist/cli.js +256 -0
  8. package/dist/core/approval.js +95 -0
  9. package/dist/core/auth.js +119 -0
  10. package/dist/core/config.js +61 -0
  11. package/dist/core/notifications.js +134 -0
  12. package/dist/core/qiniu.js +267 -0
  13. package/dist/core/registry.js +86 -0
  14. package/dist/core/router.js +344 -0
  15. package/dist/core/session.js +403 -0
  16. package/dist/core/setup.js +418 -0
  17. package/dist/core/types.js +16 -0
  18. package/dist/feishu/adapter.js +72 -0
  19. package/dist/feishu/bot.js +168 -0
  20. package/dist/feishu/commands.js +601 -0
  21. package/dist/feishu/handler.js +380 -0
  22. package/dist/index.js +60 -0
  23. package/dist/opencode/client.js +823 -0
  24. package/dist/package-lock.json +762 -0
  25. package/dist/patch_spawn.js +28 -0
  26. package/dist/plugins/agents/acp/acp-adapter.js +42 -0
  27. package/dist/plugins/agents/claude-code/index.js +69 -0
  28. package/dist/plugins/agents/codex/index.js +44 -0
  29. package/dist/plugins/agents/copilot/index.js +44 -0
  30. package/dist/plugins/agents/opencode/index.js +66 -0
  31. package/dist/telegram/bot.js +288 -0
  32. package/dist/utils/message-split.js +38 -0
  33. package/dist/web/code-viewer.js +266 -0
  34. package/dist/weixin/adapter.js +135 -0
  35. package/dist/weixin/api.js +179 -0
  36. package/dist/weixin/bot.js +183 -0
  37. package/dist/weixin/commands.js +758 -0
  38. package/dist/weixin/handler.js +577 -0
  39. package/dist/weixin/node_modules/encodeurl/LICENSE +22 -0
  40. package/dist/weixin/node_modules/encodeurl/README.md +109 -0
  41. package/dist/weixin/node_modules/encodeurl/index.js +60 -0
  42. package/dist/weixin/node_modules/encodeurl/package.json +40 -0
  43. package/dist/weixin/node_modules/qiniu/.claude/settings.local.json +7 -0
  44. package/dist/weixin/node_modules/qiniu/.github/workflows/ci-test.yml +36 -0
  45. package/dist/weixin/node_modules/qiniu/.github/workflows/npm-publish.yml +20 -0
  46. package/dist/weixin/node_modules/qiniu/.github/workflows/version-check.yml +19 -0
  47. package/dist/weixin/node_modules/qiniu/.idea/MarsCodeWorkspaceAppSettings.xml +7 -0
  48. package/dist/weixin/node_modules/qiniu/.idea/codeStyles/Project.xml +44 -0
  49. package/dist/weixin/node_modules/qiniu/.idea/codeStyles/codeStyleConfig.xml +5 -0
  50. package/dist/weixin/node_modules/qiniu/.idea/git_toolbox_blame.xml +6 -0
  51. package/dist/weixin/node_modules/qiniu/.idea/inspectionProfiles/Project_Default.xml +6 -0
  52. package/dist/weixin/node_modules/qiniu/.idea/jsLibraryMappings.xml +6 -0
  53. package/dist/weixin/node_modules/qiniu/.idea/modules.xml +8 -0
  54. package/dist/weixin/node_modules/qiniu/.idea/nodejs-sdk.iml +12 -0
  55. package/dist/weixin/node_modules/qiniu/.idea/vcs.xml +6 -0
  56. package/dist/weixin/node_modules/qiniu/CHANGELOG.md +292 -0
  57. package/dist/weixin/node_modules/qiniu/README.md +56 -0
  58. package/dist/weixin/node_modules/qiniu/StorageResponseInterface.d.ts +239 -0
  59. package/dist/weixin/node_modules/qiniu/codecov.yml +28 -0
  60. package/dist/weixin/node_modules/qiniu/index.d.ts +1995 -0
  61. package/dist/weixin/node_modules/qiniu/index.js +32 -0
  62. package/dist/weixin/node_modules/qiniu/node_modules/encodeurl/HISTORY.md +14 -0
  63. package/dist/weixin/node_modules/qiniu/node_modules/encodeurl/LICENSE +22 -0
  64. package/dist/weixin/node_modules/qiniu/node_modules/encodeurl/README.md +128 -0
  65. package/dist/weixin/node_modules/qiniu/node_modules/encodeurl/index.js +60 -0
  66. package/dist/weixin/node_modules/qiniu/node_modules/encodeurl/package.json +40 -0
  67. package/dist/weixin/node_modules/qiniu/package.json +80 -0
  68. package/dist/weixin/node_modules/qiniu/qiniu/auth/digest.js +13 -0
  69. package/dist/weixin/node_modules/qiniu/qiniu/cdn.js +149 -0
  70. package/dist/weixin/node_modules/qiniu/qiniu/conf.js +254 -0
  71. package/dist/weixin/node_modules/qiniu/qiniu/fop.js +112 -0
  72. package/dist/weixin/node_modules/qiniu/qiniu/httpc/client.js +253 -0
  73. package/dist/weixin/node_modules/qiniu/qiniu/httpc/endpoint.js +66 -0
  74. package/dist/weixin/node_modules/qiniu/qiniu/httpc/endpointsProvider.js +27 -0
  75. package/dist/weixin/node_modules/qiniu/qiniu/httpc/endpointsRetryPolicy.js +76 -0
  76. package/dist/weixin/node_modules/qiniu/qiniu/httpc/middleware/base.js +31 -0
  77. package/dist/weixin/node_modules/qiniu/qiniu/httpc/middleware/index.js +9 -0
  78. package/dist/weixin/node_modules/qiniu/qiniu/httpc/middleware/qiniuAuth.js +53 -0
  79. package/dist/weixin/node_modules/qiniu/qiniu/httpc/middleware/retryDomains.js +101 -0
  80. package/dist/weixin/node_modules/qiniu/qiniu/httpc/middleware/ua.js +36 -0
  81. package/dist/weixin/node_modules/qiniu/qiniu/httpc/region.js +349 -0
  82. package/dist/weixin/node_modules/qiniu/qiniu/httpc/regionsProvider.js +788 -0
  83. package/dist/weixin/node_modules/qiniu/qiniu/httpc/regionsRetryPolicy.js +242 -0
  84. package/dist/weixin/node_modules/qiniu/qiniu/httpc/responseWrapper.js +40 -0
  85. package/dist/weixin/node_modules/qiniu/qiniu/retry/index.js +4 -0
  86. package/dist/weixin/node_modules/qiniu/qiniu/retry/retrier.js +99 -0
  87. package/dist/weixin/node_modules/qiniu/qiniu/retry/retryPolicy.js +55 -0
  88. package/dist/weixin/node_modules/qiniu/qiniu/rpc.js +237 -0
  89. package/dist/weixin/node_modules/qiniu/qiniu/rtc/app.js +123 -0
  90. package/dist/weixin/node_modules/qiniu/qiniu/rtc/credentials.js +57 -0
  91. package/dist/weixin/node_modules/qiniu/qiniu/rtc/room.js +118 -0
  92. package/dist/weixin/node_modules/qiniu/qiniu/rtc/util.js +16 -0
  93. package/dist/weixin/node_modules/qiniu/qiniu/sms/message.js +58 -0
  94. package/dist/weixin/node_modules/qiniu/qiniu/storage/form.js +442 -0
  95. package/dist/weixin/node_modules/qiniu/qiniu/storage/internal.js +214 -0
  96. package/dist/weixin/node_modules/qiniu/qiniu/storage/resume.js +1272 -0
  97. package/dist/weixin/node_modules/qiniu/qiniu/storage/rs.js +1764 -0
  98. package/dist/weixin/node_modules/qiniu/qiniu/util.js +382 -0
  99. package/dist/weixin/node_modules/qiniu/qiniu/zone.js +230 -0
  100. package/dist/weixin/node_modules/qiniu/tsconfig.json +112 -0
  101. package/dist/weixin/types.js +25 -0
  102. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # OpenCode Remote
2
+
3
+ 通过微信、Telegram、飞书等平台随时随地控制 OpenCode。
4
+
5
+ ## 功能特性
6
+
7
+ - **多平台支持** — 微信、飞书、Telegram
8
+ - **多 AI Agent** — OpenCode、Claude Code、Codex、GitHub Copilot
9
+ - **会话管理** — 多会话并行,自动保存与恢复
10
+ - **智能循环任务** — 长时间任务自动循环执行
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ npm install -g opencode-remote
16
+ ```
17
+
18
+ ## 快速开始
19
+
20
+ ```bash
21
+ # 微信
22
+ opencode-remote weixin
23
+
24
+ # 飞书
25
+ opencode-remote feishu
26
+
27
+ # Telegram
28
+ opencode-remote telegram
29
+ ```
30
+
31
+ ## 平台兼容性
32
+
33
+ | 指令 | 说明 | 微信 | 飞书 | Telegram |
34
+ |------|------|:----:|:----:|:--------:|
35
+ | `/start` | 认领所有权 | ✅ | ✅ | ✅ |
36
+ | `/help` | 显示帮助 | ✅ | ✅ | ✅ |
37
+ | `/status` | 查看状态 | ✅ | ✅ | ✅ |
38
+ | `/reset` | 重置会话 | ✅ | ✅ | ✅ |
39
+ | `/restart` | 重启 Bot | ✅ | ✅ | ✅ |
40
+ | `/stop` | 停止 Bot | ✅ | ✅ | ✅ |
41
+ | `/sessions` | 浏览会话 | ✅ | ✅ | ✅ |
42
+ | `/delsessions` | 删除会话 | ✅ | ✅ | ✅ |
43
+ | `/loop` | 循环任务 | ✅ | ✅ | ✅ |
44
+ | `/refresh` | 刷新上下文 | ✅ | ✅ | ✅ |
45
+ | `/copy` | 复制回复 | ✅ | ✅ | ✅ |
46
+ | `/revert` | 撤销消息 | ✅ | ✅ | ✅ |
47
+ | `/model` | 切换 AI 模型 | ✅ | ✅ | ✅ |
48
+ | `/agents` | 查看 Agent | ✅ | ✅ | ✅ |
49
+ | `/oc` | 使用 OpenCode | ✅ | ✅ | ✅ |
50
+ | `/cc` | 使用 Claude Code | ✅ | ✅ | ✅ |
51
+ | `/edit` | 编辑消息 | ✅ | ✅ | ✅ |
52
+ | `/upload` | 上传文件(需七牛云) | ✅ | ✅ | ✅ |
53
+ | `/delete` | 删除上传文件 | ✅ | ✅ | ✅ |
54
+ | `/diagnose` | 系统诊断 | ✅ | ✅ | ✅ |
55
+ | `/expert` `/z` | 专家评审模式 | ✅ | ✅ | ✅ |
56
+
57
+ > ✅ 可用   ❌ 未实现
58
+
59
+ ## 快速使用
60
+
61
+ ```bash
62
+ # Telegram(最快,5分钟)
63
+ opencode-remote telegram
64
+
65
+ # 微信
66
+ opencode-remote weixin
67
+
68
+ # 飞书(需要企业版飞书账号)
69
+ opencode-remote feishu
70
+ ```
71
+
72
+ ## 配置说明
73
+
74
+ 首次运行会在 `~/.opencode-remote/` 目录生成配置。详见 `.env.example`。
75
+
76
+ ## 系统要求
77
+
78
+ - Node.js >= 18.0.0
79
+
80
+ ## 许可证
81
+
82
+ MIT License
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import { join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname } from 'path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const cliPath = join(__dirname, '../dist/cli.js');
9
+
10
+ let childProc = null;
11
+ let shuttingDown = false;
12
+
13
+ function spawnBot() {
14
+ if (shuttingDown) return;
15
+ if (childProc) {
16
+ try { childProc.kill('SIGTERM'); } catch {}
17
+ }
18
+
19
+ const args = process.argv.slice(2);
20
+
21
+ childProc = spawn('node', [cliPath, ...args], {
22
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
23
+ windowsHide: true,
24
+ });
25
+
26
+ // 转发输出
27
+ childProc.stdout.on('data', (d) => process.stdout.write(d));
28
+ childProc.stderr.on('data', (d) => process.stderr.write(d));
29
+
30
+ // 监听退出 - code 0 是正常停止,code 200 是 restart,其他是崩溃
31
+ childProc.on('close', (code) => {
32
+ console.log(`[parent] Child process closed with code: ${code}`);
33
+ if (shuttingDown) {
34
+ console.log('[parent] Shutting down, not restarting');
35
+ return;
36
+ }
37
+ if (code === 200 || code === null) {
38
+ // 200 = restart 命令,其他 null = 崩溃
39
+ console.log(`\n🔄 Bot exited (code ${code}), restarting...`);
40
+ setTimeout(spawnBot, 1000);
41
+ } else {
42
+ console.log(`[parent] Bot exited with code ${code}, not restarting`);
43
+ }
44
+ });
45
+ }
46
+
47
+ // 处理来自子进程的消息
48
+ process.on('message', (msg) => {
49
+ if (msg === 'restart') {
50
+ console.log('\n[parent] Received restart signal from child');
51
+ shuttingDown = true;
52
+ if (childProc) {
53
+ childProc.kill('SIGTERM');
54
+ }
55
+ console.log('[parent] Restarting bot...');
56
+ shuttingDown = false;
57
+ setTimeout(spawnBot, 500);
58
+ }
59
+ });
60
+
61
+ // Handle Ctrl+C
62
+ process.on('SIGINT', () => {
63
+ if (shuttingDown) return;
64
+ shuttingDown = true;
65
+ console.log('\nShutting down...');
66
+ if (childProc) childProc.kill('SIGTERM');
67
+ setTimeout(() => process.exit(0), 1000);
68
+ });
69
+
70
+ spawnBot();
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import { join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname } from 'path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const cliPath = join(__dirname, '../dist/cli.js');
9
+
10
+ spawn('node', [cliPath, 'weixin', ...process.argv.slice(1)], { stdio: 'inherit' });
package/dist/AGENTS.md ADDED
@@ -0,0 +1,20 @@
1
+ # 项目开发指南 (AGENTS.md)
2
+
3
+ > **记忆系统**: 每次会话必须加载 @MEMORY.md 获取项目记忆、技术决策和历史教训。
4
+
5
+ ---
6
+
7
+ ## 技术栈
8
+ - **运行环境**: Node.js (CommonJS)
9
+ - **核心功能**: 远程控制 OpenCode CLI, 记忆持久化层
10
+ - **集成平台**: 微信 (WeChat), 飞书 (Feishu), Telegram
11
+
12
+ ## 关键命令
13
+ - **启动**: `npm start` (根据 package.json 确定)
14
+ - **部署**: 部署至 `node_modules/opencode-remote-control/dist`
15
+
16
+ ## 核心逻辑与规范
17
+ 1. **记忆存储**: 所有用户偏好和项目知识必须存储在 `~/.opencode-remote/memory/` (JSON 格式),严禁存储在代码目录内。
18
+ 2. **注入机制**: 采用“拦截 -> 检索 -> 注入”流程,在转发指令给 OpenCode 前,由 `memory-manager.js` 检索相关记忆并静默注入 System Prompt。
19
+ 3. **代码风格**: 保持 CommonJS 风格,禁止在 dist 目录下创建临时测试文件。
20
+ 4. **纯净度**: 保持 `dist` 目录仅包含生产代码、必要配置文件及 `.md` 指南。
package/dist/MEMORY.md ADDED
@@ -0,0 +1,21 @@
1
+ # MEMORY.md — 项目记忆
2
+
3
+ ## 核心决策
4
+ - **记忆架构**: 采用全自动记忆层,存储路径统一为 `~/.opencode-remote/memory/`。
5
+ - **注入流程**: 在 `bot.js` 的 `forwardToOpenCode` 阶段通过 `memory-manager.js` 自动检索并注入上下文。
6
+ - **数据格式**: 采用 JSON 格式存储 `user_preferences.json` 和 `project_knowledge.json`。
7
+
8
+ ## 经验教训
9
+ - [2026-04-11] 避免在 `dist` 目录下创建临时测试文件或实验性目录(如 `openchat`),确保生产环境纯净。
10
+ - [2026-04-11] 记忆存储必须与代码目录分离,防止更新部署时数据丢失。
11
+
12
+ ## 主题文件路由表
13
+ | 触发词 | 文件 | 说明 |
14
+ |--------|------|------|
15
+ | 记忆存储 | `.opencode-remote/memory/*.json` | 用户偏好与项目知识库 |
16
+ | 注入逻辑 | `dist/weixin/bot.js` | 指令拦截与 Prompt 注入实现 |
17
+ | 记忆管理 | `dist/weixin/memory-manager.js` | JSON 持久化读写逻辑 |
18
+
19
+ ## 待办/开放线程
20
+ - [ ] 实现自动反思机制 (Reflection Loop):任务完成后自动提取规律并写入记忆。
21
+ - [ ] 优化 Token 截断与内存压缩逻辑。
@@ -0,0 +1,180 @@
1
+ // OpenCode Remote Control - Bot runner module
2
+ import { loadConfig } from './core/config.js';
3
+ import { hasTelegramConfig, hasFeishuConfig, hasWeixinConfig, printBanner } from './core/setup.js';
4
+ import { startBot } from './telegram/bot.js';
5
+ import { startFeishuBot } from './feishu/bot.js';
6
+ import { startWeixinBot } from './weixin/bot.js';
7
+ import { registry } from './core/registry.js';
8
+ import { existsSync, writeFileSync, mkdirSync } from 'fs';
9
+ import { join } from 'path';
10
+
11
+ export function createShutdownHandler() {
12
+ let isShuttingDown = false;
13
+ return () => {
14
+ if (isShuttingDown)
15
+ return;
16
+ isShuttingDown = true;
17
+ console.log('\n🛑 Shutting down...');
18
+ setTimeout(() => {
19
+ console.log('Goodbye!');
20
+ process.exit(0);
21
+ }, 1000);
22
+ };
23
+ }
24
+
25
+ export async function runAgentsCommand() {
26
+ printBanner();
27
+ console.log('🤖 Available Agents\n');
28
+
29
+ await registry.loadBuiltInPlugins();
30
+
31
+ const agents = registry.listAgents();
32
+
33
+ if (agents.length === 0) {
34
+ console.log('No agents registered yet.');
35
+ console.log('');
36
+ console.log('Install an agent to get started:');
37
+ console.log(' • OpenCode: npm i -g opencode-ai');
38
+ console.log(' • Claude Code: npm i -g @anthropic-ai/claude-code');
39
+ console.log(' • Codex: npm i -g @openai/codex');
40
+ console.log(' • Copilot: gh extension install github/gh-copilot');
41
+ return;
42
+ }
43
+
44
+ console.log('Checking agent availability...\n');
45
+
46
+ const results = await Promise.allSettled(agents.map(async (name) => {
47
+ const agent = registry.getAgent(name);
48
+ const available = await agent?.isAvailable().catch(() => false);
49
+ return { name, available, aliases: agent?.aliases || [] };
50
+ }));
51
+
52
+ for (const result of results) {
53
+ if (result.status === 'fulfilled') {
54
+ const { name, available, aliases } = result.value;
55
+ const aliasStr = aliases.length ? ` (${aliases.join(', ')})` : '';
56
+ console.log(` ${available ? '✅' : '❌'} ${name}${aliasStr}`);
57
+ }
58
+ }
59
+
60
+ console.log('\nUse /<agent> in chat to switch agents.');
61
+ console.log('Example: /cc to switch to Claude Code');
62
+ }
63
+
64
+ export async function runStart() {
65
+ const config = await loadConfig();
66
+ const hasTelegram = hasTelegramConfig(config);
67
+ const hasFeishu = hasFeishuConfig(config);
68
+ const hasWeixin = hasWeixinConfig();
69
+ if (!hasTelegram && !hasFeishu && !hasWeixin) {
70
+ console.log('❌ No bots configured!');
71
+ console.log('\nRun: opencode-remote config');
72
+ process.exit(1);
73
+ }
74
+
75
+ let shuttingDown = false;
76
+ const childProcs = [];
77
+
78
+ if (hasWeixin) {
79
+ runWeixinOnly();
80
+ return;
81
+ }
82
+
83
+ const promises = [];
84
+ if (hasTelegram) {
85
+ console.log('🤖 Starting Telegram bot...');
86
+ process.env.TELEGRAM_BOT_TOKEN = config.telegramBotToken;
87
+ promises.push(startBot().catch((err) => {
88
+ console.error('Telegram bot failed:', err);
89
+ return { status: 'rejected', reason: err };
90
+ }));
91
+ }
92
+ if (hasFeishu) {
93
+ console.log('🤖 Starting Feishu bot...');
94
+ promises.push(startFeishuBot(config).catch((err) => {
95
+ console.error('Feishu bot failed:', err);
96
+ return { status: 'rejected', reason: err };
97
+ }));
98
+ }
99
+
100
+ if (promises.length > 0) {
101
+ const results = await Promise.allSettled(promises);
102
+ const failed = results.filter((r) => r.status === 'rejected');
103
+ if (failed.length > 0) {
104
+ console.log(`\n⚠️ ${failed.length} bot(s) failed to start`);
105
+ process.exit(1);
106
+ }
107
+ }
108
+
109
+ if (!hasWeixin) {
110
+ console.log('\n✅ All bots started!');
111
+ }
112
+
113
+ process.once('SIGINT', () => {
114
+ if (shuttingDown) return;
115
+ shuttingDown = true;
116
+ console.log('\n🛑 Shutting down...');
117
+ for (const p of childProcs) { try { p.kill('SIGTERM'); } catch {} }
118
+ setTimeout(() => process.exit(0), 2000);
119
+ });
120
+ process.once('SIGTERM', () => {
121
+ if (shuttingDown) return;
122
+ shuttingDown = true;
123
+ for (const p of childProcs) { try { p.kill('SIGTERM'); } catch {} }
124
+ setTimeout(() => process.exit(0), 2000);
125
+ });
126
+
127
+ await new Promise(() => {});
128
+ }
129
+
130
+ export async function runTelegramOnly() {
131
+ const config = await loadConfig();
132
+ if (!hasTelegramConfig(config)) {
133
+ console.log('❌ Telegram bot not configured!');
134
+ console.log('\nRun: opencode-remote config');
135
+ process.exit(1);
136
+ }
137
+ printBanner();
138
+ console.log('🤖 Starting Telegram bot...');
139
+ process.env.TELEGRAM_BOT_TOKEN = config.telegramBotToken;
140
+ try {
141
+ await startBot();
142
+ }
143
+ catch (error) {
144
+ console.error('Failed to start:', error);
145
+ process.exit(1);
146
+ }
147
+ }
148
+
149
+ export async function runFeishuOnly() {
150
+ const config = await loadConfig();
151
+ if (!hasFeishuConfig(config)) {
152
+ console.log('❌ Feishu bot not configured!');
153
+ console.log('\nRun: opencode-remote config');
154
+ process.exit(1);
155
+ }
156
+ printBanner();
157
+ console.log('🤖 Starting Feishu bot...');
158
+ try {
159
+ await startFeishuBot(config);
160
+ }
161
+ catch (error) {
162
+ console.error('Failed to start:', error);
163
+ process.exit(1);
164
+ }
165
+ }
166
+
167
+ export async function runWeixinOnly() {
168
+ const config = await loadConfig();
169
+ printBanner();
170
+ console.log('🤖 Starting Weixin bot...');
171
+ try {
172
+ await startWeixinBot(config);
173
+ }
174
+ catch (error) {
175
+ console.error('Bot error:', error.message);
176
+ process.exit(1);
177
+ }
178
+ }
179
+
180
+
package/dist/cli.js ADDED
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ // OpenCode Remote Control - CLI entry point
3
+ import { watch } from 'fs';
4
+ import { dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { spawn } from 'child_process';
7
+ import { setGlobalProxy } from './opencode/client.js';
8
+ import { printBanner, VERSION, runConfig, runConfigTimeout } from './core/setup.js';
9
+ import { runStart, runTelegramOnly, runFeishuOnly, runWeixinOnly, runAgentsCommand } from './bot-runner.js';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+
14
+ function printHelp() {
15
+ console.log(`
16
+ Usage: opencode-remote [command]
17
+
18
+ Commands:
19
+ start Start all configured bots (default)
20
+ telegram Start Telegram bot only
21
+ feishu Start Feishu bot only
22
+ weixin Start Weixin (微信) bot only
23
+ config Configure a channel (interactive selection)
24
+ config timeout Set request timeout (for long-running tasks)
25
+ agents List available AI agents
26
+ help Show this help message
27
+ version Show version information
28
+
29
+ Options:
30
+ -v, --version Show version number
31
+ --proxy <url> Use HTTP/HTTPS proxy for all requests
32
+ (e.g., --proxy http://192.168.1.100:7890)
33
+ --id <name> Instance ID for multi-bot support
34
+ (e.g., --id bot1, --id bot2)
35
+
36
+ Proxy Configuration:
37
+ You can also set proxy via environment variables:
38
+ HTTP_PROXY, HTTPS_PROXY, ALL_PROXY
39
+
40
+ Multi-Bot Support:
41
+ Run multiple Weixin bots with different accounts:
42
+ opencode-remote weixin --id bot1 # First Weixin account
43
+ opencode-remote weixin --id bot2 # Second Weixin account
44
+
45
+ Each instance has its own credentials and sessions.
46
+
47
+ Weixin Bot Commands (send in WeChat):
48
+ /start — Claim ownership
49
+ /help — Show all commands
50
+ /status — Check connection
51
+ /stop — Interrupt task
52
+ /reset — Reset session
53
+ /restart — Restart bot
54
+ /sessions — Browse sessions
55
+ /delsessions — Delete sessions
56
+ /loop — Loop task
57
+ /refresh — Refresh context
58
+ /copy — Copy latest reply
59
+ /revert — Undo last message
60
+ /upload — Upload build artifacts
61
+ /model — Switch AI model
62
+
63
+ Multi-Agent Commands:
64
+ /oc <prompt> — Use OpenCode (default)
65
+ /cc <prompt> — Use Claude Code
66
+ /cx <prompt> — Use Codex
67
+ /copilot <prompt> — Use GitHub Copilot
68
+ /agents — List all available agents
69
+
70
+ Examples:
71
+ opencode-remote # Start all bots
72
+ opencode-remote start # Start all bots
73
+ opencode-remote telegram # Start Telegram only
74
+ opencode-remote feishu # Start Feishu only
75
+ opencode-remote weixin # Start Weixin only
76
+ opencode-remote weixin --id bot1 # Start Weixin bot1
77
+ opencode-remote weixin --id bot2 # Start Weixin bot2
78
+ opencode-remote config # Interactive channel selection
79
+ opencode-remote config timeout # Set request timeout
80
+ opencode-remote --version # Show version
81
+ opencode-remote --proxy http://192.168.1.100:7890 # With proxy
82
+ `);
83
+ }
84
+
85
+ // Main CLI
86
+ // 父进程管理:如果不是子进程,则启动父进程模式
87
+ if (process.env.OPENCODE_CHILD !== '1') {
88
+ let childProc = null;
89
+ let shuttingDown = false;
90
+ let isRestart = false;
91
+
92
+ const spawnChild = (fromRestart = false) => {
93
+ if (shuttingDown) return;
94
+ if (childProc) {
95
+ try { childProc.kill('SIGTERM'); } catch {}
96
+ }
97
+
98
+ const args = process.argv.slice(2);
99
+ const childEnv = { ...process.env, OPENCODE_CHILD: '1' };
100
+ if (fromRestart) {
101
+ childEnv.OPENCODE_RESTART = '1';
102
+ }
103
+ childProc = spawn('node', [process.argv[1], ...args], {
104
+ stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
105
+ env: childEnv,
106
+ windowsHide: true,
107
+ });
108
+
109
+ childProc.stdout.on('data', (d) => process.stdout.write(d));
110
+ childProc.stderr.on('data', (d) => process.stderr.write(d));
111
+
112
+ childProc.on('close', (code) => {
113
+ console.log(`[parent] Child process closed with code: ${code}`);
114
+ if (shuttingDown) {
115
+ console.log('[parent] Shutting down, not restarting');
116
+ return;
117
+ }
118
+ if (code === 200 || code === null) {
119
+ console.log(`\n🔄 Bot exited (code ${code}), restarting...`);
120
+ isRestart = true;
121
+ setTimeout(() => spawnChild(true), 1000);
122
+ } else {
123
+ console.log(`[parent] Bot exited with code ${code}, not restarting`);
124
+ }
125
+ });
126
+
127
+ childProc.on('error', (err) => {
128
+ console.error('[parent] Child error:', err.message);
129
+ });
130
+ };
131
+
132
+ // 文件监控 - 代码变化时自动重启
133
+ const distDir = __dirname;
134
+ let debounceTimer = null;
135
+ watch(distDir, { recursive: true }, (eventType, filename) => {
136
+ if (filename && filename.endsWith('.js') && !shuttingDown) {
137
+ clearTimeout(debounceTimer);
138
+ debounceTimer = setTimeout(() => {
139
+ console.log(`\n📝 ${filename} changed, restarting...`);
140
+ if (childProc) {
141
+ isRestart = true;
142
+ childProc.kill('SIGTERM');
143
+ }
144
+ }, 500);
145
+ }
146
+ });
147
+
148
+ process.on('SIGINT', () => {
149
+ if (shuttingDown) return;
150
+ shuttingDown = true;
151
+ console.log('\nShutting down...');
152
+ if (childProc) childProc.kill('SIGTERM');
153
+ setTimeout(() => process.exit(0), 1000);
154
+ });
155
+
156
+ process.on('SIGTERM', () => {
157
+ if (shuttingDown) return;
158
+ shuttingDown = true;
159
+ if (childProc) childProc.kill('SIGTERM');
160
+ setTimeout(() => process.exit(0), 1000);
161
+ });
162
+
163
+ spawnChild();
164
+ } else {
165
+ runCli();
166
+ }
167
+
168
+ function runCli() {
169
+ const args = process.argv.slice(2);
170
+ let proxyUrl = null;
171
+ let instanceId = null;
172
+ let command = 'start';
173
+ let subCommand = null;
174
+ for (let i = 0; i < args.length; i++) {
175
+ const arg = args[i];
176
+ if (arg === '--proxy') {
177
+ proxyUrl = args[++i];
178
+ if (!proxyUrl) {
179
+ console.error('Error: --proxy requires a URL argument');
180
+ process.exit(1);
181
+ }
182
+ }
183
+ else if (arg.startsWith('--proxy=')) {
184
+ proxyUrl = arg.slice('--proxy='.length);
185
+ }
186
+ else if (arg === '--id') {
187
+ instanceId = args[++i];
188
+ if (!instanceId) {
189
+ console.error('Error: --id requires an argument');
190
+ process.exit(1);
191
+ }
192
+ }
193
+ else if (arg.startsWith('--id=')) {
194
+ instanceId = arg.slice('--id='.length);
195
+ }
196
+ else if (arg === '--version' || arg === '-v') {
197
+ command = 'version';
198
+ }
199
+ else if (arg === '--help' || arg === '-h') {
200
+ command = 'help';
201
+ }
202
+ else if (!arg.startsWith('-')) {
203
+ if (command === 'start') {
204
+ command = arg;
205
+ }
206
+ else if (command === 'config' && !subCommand) {
207
+ subCommand = arg;
208
+ }
209
+ }
210
+ }
211
+ if (instanceId) {
212
+ process.env.OPENCODE_INSTANCE_ID = instanceId;
213
+ }
214
+ if (proxyUrl) {
215
+ setGlobalProxy(proxyUrl);
216
+ }
217
+ if (command === 'config' && subCommand === 'timeout') {
218
+ runConfigTimeout();
219
+ process.exit(0);
220
+ }
221
+ switch (command) {
222
+ case 'start':
223
+ runStart();
224
+ break;
225
+ case 'telegram':
226
+ runTelegramOnly();
227
+ break;
228
+ case 'feishu':
229
+ runFeishuOnly();
230
+ break;
231
+ case 'weixin':
232
+ runWeixinOnly();
233
+ break;
234
+ case 'config':
235
+ runConfig();
236
+ break;
237
+ case 'help':
238
+ case '--help':
239
+ case '-h':
240
+ printBanner();
241
+ printHelp();
242
+ break;
243
+ case 'version':
244
+ case '--version':
245
+ case '-v':
246
+ console.log(`opencode-remote v${VERSION}`);
247
+ break;
248
+ case 'agents':
249
+ runAgentsCommand();
250
+ break;
251
+ default:
252
+ console.log(`Unknown command: ${command}`);
253
+ printHelp();
254
+ process.exit(1);
255
+ }
256
+ }