@yvhitxcel/opencode-remote 0.15.1 → 0.16.1

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.md CHANGED
@@ -2,12 +2,28 @@
2
2
 
3
3
  通过微信、Telegram、飞书等平台随时随地控制 OpenCode。
4
4
 
5
+ ## 为什么这么爽?
6
+
7
+ **输入一个字母 `z`,14 位 AI 专家帮你审项目。** 架构师、安全研究员、测试工程师、运维……每人给出犀利点评,技术经理汇总 P0-P2 任务清单。你在手机上躺着看就行。
8
+
9
+ ```bash
10
+ # 安装
11
+ npm install -g @yvhitxcel/opencode-remote
12
+
13
+ # 开干
14
+ opencode-remote telegram
15
+ # → 输入 /z 叫专家团队
16
+ # → 输入 z 让专家分析项目
17
+ # → AI 自动干活,你喝茶
18
+ ```
19
+
5
20
  ## 功能特性
6
21
 
7
- - **多平台支持**微信、飞书、Telegram
8
- - **多 AI Agent** OpenCode、Claude Code、Codex、GitHub Copilot
9
- - **会话管理**多会话并行,自动保存与恢复
10
- - **智能循环任务**长时间任务自动循环执行
22
+ - **🤖 专家评审系统** `/z` 一键召唤 14 位 AI 专家,自动分析、投票、出方案
23
+ - **📱 多平台支持**微信、飞书、Telegram
24
+ - **🧠 多 AI Agent** OpenCode、Claude Code、Codex、GitHub Copilot
25
+ - **🔄 循环任务** `/loop` 让 AI 持续干活
26
+ - **🔍 一键诊断** — `/diagnose` 检查各组件状态
11
27
 
12
28
  ## 安装
13
29
 
@@ -17,15 +33,42 @@ npm install -g opencode-remote
17
33
 
18
34
  ## 快速开始
19
35
 
36
+ 推荐路径:**Telegram(5分钟)→ 微信(10分钟)→ 飞书(30分钟)**
37
+
20
38
  ```bash
21
- # 微信
39
+ # 1. 安装
40
+ npm install -g @yvhitxcel/opencode-remote
41
+
42
+ # 2. 启动 Telegram(最快上手,无需配置)
43
+ opencode-remote telegram
44
+ # 在 Telegram 里搜索你的 bot,发送 /start
45
+
46
+ # 3. 微信(需要 iLink 平台账号)
22
47
  opencode-remote weixin
48
+ # 扫码登录后即可使用
23
49
 
24
- # 飞书
50
+ # 4. 飞书(需要企业版账号)
25
51
  opencode-remote feishu
52
+ ```
26
53
 
27
- # Telegram
28
- opencode-remote telegram
54
+ ## 首次使用
55
+
56
+ 1. 安装后运行 `opencode-remote telegram`
57
+ 2. 在 Telegram 里找到你的 bot,发送 `/start`
58
+ 3. 发送 `/help` 查看所有命令
59
+ 4. 发送一条消息给 AI,比如"你好"
60
+ 5. **发送 `/z` 启动专家模式,然后发送 `z` —— 14 位 AI 专家开始分析你的项目**
61
+
62
+ > 💡 所有核心功能(对话、会话管理、AI 模型切换、专家评审)无需任何配置。只有 `/upload` 上传才需要七牛云。
63
+
64
+ ## 手机开发工作流
65
+
66
+ 把 `weixin.bat` 复制到项目根目录,双击运行,扫码登录后即可在手机上通过微信开发该项目。
67
+
68
+ ```bash
69
+ # 或者手动指定目录
70
+ cd 你的项目
71
+ opencode-remote
29
72
  ```
30
73
 
31
74
  ## 平台兼容性
@@ -90,10 +133,37 @@ opencode-remote
90
133
 
91
134
  **七牛云是可选的**,不配也能用全部核心功能。只有上传构建产物才需要配置。
92
135
 
136
+ ## 常见问题
137
+
138
+ **Q: 为什么有些命令不能用?**
139
+ A: 先运行 `/diagnose` 检查各组件状态。Telegram 功能最全,微信和飞书部分命令需要额外配置。
140
+
141
+ **Q: 微信怎么登录?**
142
+ A: 运行 `opencode-remote weixin`,终端会显示二维码,用微信扫码即可。
143
+
144
+ **Q: 专家评审怎么用?**
145
+ A: 发 `z` 或 `叫全部专家`,AI 自动扫项目、组队评审、出 P0/P1 修复方案,**然后自动修代码**。评审过程包括三级质量保障:脑内路径追踪、服务端模拟验证、对抗评审。
146
+
147
+ **Q: `/z` 什么时候用?**
148
+ A: 任何时候。发一个 `z` 让 AI 评审当前项目,发 `z 帮我看看这个bug` 聚焦具体问题。先 `/z` 设置自定义 prompt 再发问题也行。
149
+
150
+ **Q: `/z` 自动修代码会改坏吗?**
151
+ A: **强烈建议在 git 仓库中使用。** 如果改坏了可以 `git checkout .` 回滚。没有 git 的项目,AI 改完不可逆。不确定的话先发 `/z off` 关闭自动执行,只看报告不改代码。
152
+
153
+ **Q: 需要自己的服务器吗?**
154
+ A: 需要一台电脑运行 bot,手机上通过 IM 控制。OpenCode 也运行在这台电脑上。
155
+
156
+ **Q: 如何更新?**
157
+ A: `npm update -g @yvhitxcel/opencode-remote`
158
+
93
159
  ## 系统要求
94
160
 
95
161
  - Node.js >= 18.0.0
96
162
 
163
+ ## 致谢
164
+
165
+ 本项目基于 [opencode-remote-control](https://github.com/ceociocto/opencode-remote-control) 开发。
166
+
97
167
  ## 许可证
98
168
 
99
169
  MIT License
package/dist/core/auth.js CHANGED
@@ -1,119 +1,52 @@
1
- // Authorization management for OpenCode Remote Control
2
- // First user to send /start becomes the owner automatically
3
- const authState = {
4
- telegramOwner: null,
5
- feishuOwner: null,
6
- weixinOwner: null,
7
- };
8
- // Auth file path for persistence
9
1
  import { homedir } from 'os';
10
2
  import { join } from 'path';
11
3
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
12
- const AUTH_DIR = join(homedir(), '.opencode-remote');
13
- const AUTH_FILE = join(AUTH_DIR, 'auth.json');
14
- function ensureAuthDir() {
15
- if (!existsSync(AUTH_DIR)) {
16
- mkdirSync(AUTH_DIR, { recursive: true });
17
- }
18
- }
19
- function loadAuth() {
20
- try {
21
- if (existsSync(AUTH_FILE)) {
22
- const data = JSON.parse(readFileSync(AUTH_FILE, 'utf-8'));
23
- authState.telegramOwner = data.telegramOwner || null;
24
- authState.feishuOwner = data.feishuOwner || null;
25
- authState.weixinOwner = data.weixinOwner || null;
26
- }
27
- }
28
- catch (error) {
29
- console.warn('Failed to load auth state, starting fresh:', error);
30
- }
31
- }
32
- function saveAuth() {
33
- try {
34
- ensureAuthDir();
35
- writeFileSync(AUTH_FILE, JSON.stringify(authState, null, 2));
36
- }
37
- catch (error) {
38
- console.error('Failed to save auth state:', error);
39
- }
40
- }
41
- // Initialize on module load
42
- loadAuth();
4
+
5
+ const OWNER_KEY = { telegram: 'telegramOwner', feishu: 'feishuOwner', weixin: 'weixinOwner' };
6
+ const AUTH_FILE = join(homedir(), '.opencode-remote', 'auth.json');
7
+
8
+ const state = { telegramOwner: null, feishuOwner: null, weixinOwner: null };
9
+
10
+ function load() {
11
+ try {
12
+ if (existsSync(AUTH_FILE)) {
13
+ const d = JSON.parse(readFileSync(AUTH_FILE, 'utf-8'));
14
+ for (const k of Object.keys(OWNER_KEY)) state[OWNER_KEY[k]] = d[OWNER_KEY[k]] || null;
15
+ }
16
+ } catch (e) { console.warn('[auth] load failed:', e.message); }
17
+ }
18
+ function save() {
19
+ try {
20
+ const dir = join(homedir(), '.opencode-remote');
21
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
22
+ writeFileSync(AUTH_FILE, JSON.stringify(state, null, 2));
23
+ } catch (e) { console.error('[auth] save failed:', e.message); }
24
+ }
25
+ load();
26
+
43
27
  export function isAuthorized(platform, userId) {
44
- if (platform === 'telegram') {
45
- return authState.telegramOwner === userId;
46
- }
47
- else if (platform === 'feishu') {
48
- return authState.feishuOwner === userId;
49
- }
50
- else {
51
- return authState.weixinOwner === userId;
52
- }
28
+ const key = OWNER_KEY[platform];
29
+ return key ? state[key] === userId : false;
53
30
  }
54
31
  export function hasOwner(platform) {
55
- if (platform === 'telegram') {
56
- return authState.telegramOwner !== null;
57
- }
58
- else if (platform === 'feishu') {
59
- return authState.feishuOwner !== null;
60
- }
61
- else {
62
- return authState.weixinOwner !== null;
63
- }
64
- }
65
- export function claimOwnership(platform, userId) {
66
- if (platform === 'telegram') {
67
- if (authState.telegramOwner) {
68
- if (authState.telegramOwner === userId) {
69
- return { success: true, message: 'already_owner' };
70
- }
71
- return { success: false, message: 'already_claimed' };
72
- }
73
- authState.telegramOwner = userId;
74
- saveAuth();
75
- return { success: true, message: 'claimed' };
76
- }
77
- else if (platform === 'feishu') {
78
- if (authState.feishuOwner) {
79
- if (authState.feishuOwner === userId) {
80
- return { success: true, message: 'already_owner' };
81
- }
82
- return { success: false, message: 'already_claimed' };
83
- }
84
- authState.feishuOwner = userId;
85
- saveAuth();
86
- return { success: true, message: 'claimed' };
87
- }
88
- else {
89
- // weixin
90
- if (authState.weixinOwner) {
91
- if (authState.weixinOwner === userId) {
92
- return { success: true, message: 'already_owner' };
93
- }
94
- return { success: false, message: 'already_claimed' };
95
- }
96
- authState.weixinOwner = userId;
97
- saveAuth();
98
- return { success: true, message: 'claimed' };
99
- }
32
+ const key = OWNER_KEY[platform];
33
+ return key ? state[key] !== null : false;
100
34
  }
101
35
  export function getOwner(platform) {
102
- if (platform === 'telegram') {
103
- return authState.telegramOwner;
104
- }
105
- else if (platform === 'feishu') {
106
- return authState.feishuOwner;
107
- }
108
- else {
109
- return authState.weixinOwner;
110
- }
36
+ const key = OWNER_KEY[platform];
37
+ return key ? state[key] : null;
38
+ }
39
+ export function claimOwnership(platform, userId) {
40
+ const key = OWNER_KEY[platform];
41
+ if (!key) return { success: false, message: 'unknown_platform' };
42
+ if (state[key]) {
43
+ if (state[key] === userId) return { success: true, message: 'already_owner' };
44
+ return { success: false, message: 'already_claimed' };
45
+ }
46
+ state[key] = userId;
47
+ save();
48
+ return { success: true, message: 'claimed' };
111
49
  }
112
- // For debugging/display
113
50
  export function getAuthStatus() {
114
- return {
115
- telegram: authState.telegramOwner !== null,
116
- feishu: authState.feishuOwner !== null,
117
- weixin: authState.weixinOwner !== null,
118
- };
51
+ return { telegram: !!state.telegramOwner, feishu: !!state.feishuOwner, weixin: !!state.weixinOwner };
119
52
  }
@@ -101,6 +101,17 @@ export const TEMPLATES = {
101
101
  details: 'Changes were automatically rejected.',
102
102
  }),
103
103
  };
104
+ // Task completion notification with timing
105
+ export function formatTaskCompletion(taskName, startTime, extra) {
106
+ const elapsed = Date.now() - startTime;
107
+ const seconds = Math.floor(elapsed / 1000);
108
+ const timeStr = seconds >= 60 ? `${Math.floor(seconds / 60)}分${seconds % 60}秒` : `${seconds}秒`;
109
+ const lines = [`✅ 任务完成: ${taskName}`, '', `⏱️ 耗时: ${timeStr}`];
110
+ if (extra?.files && extra.files > 0) lines.push(`📄 修改文件: ${extra.files} 个`);
111
+ if (extra?.iterations && extra.iterations > 0) lines.push(`🔄 迭代次数: ${extra.iterations}`);
112
+ return lines.join('\n');
113
+ }
114
+
104
115
  // Split message for Telegram's 4096 char limit
105
116
  export function splitMessage(text, maxLength = 4000) {
106
117
  if (text.length <= maxLength) {