@coremail/lunkr-openclaw 1.0.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.
- package/README.md +221 -0
- package/dist/bin/lunkr-cli.js +26 -0
- package/dist/index.js +43 -0
- package/docs/AUTHENTICATION.md +254 -0
- package/docs/CHEATSHEET.md +112 -0
- package/docs/DEVELOPMENT.md +239 -0
- package/docs/USAGE.md +337 -0
- package/openclaw.plugin.json +61 -0
- package/package.json +93 -0
- package/skills/lunkr/SKILL.md +80 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Lunkr Authentication Flow
|
|
2
|
+
|
|
3
|
+
This document describes the complete authentication flow for the Lunkr channel plugin.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Lunkr authentication involves two separate systems:
|
|
8
|
+
|
|
9
|
+
1. **Coremail** - The email server that handles user authentication
|
|
10
|
+
2. **Lunkr** - The collaboration platform that uses Coremail for authentication
|
|
11
|
+
|
|
12
|
+
The authentication flow must first authenticate with Coremail, then obtain a Lunkr session.
|
|
13
|
+
|
|
14
|
+
## Authentication Flow Diagram
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
18
|
+
│ Login Flow │
|
|
19
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
20
|
+
|
|
21
|
+
User Input Coremail Server Lunkr Server
|
|
22
|
+
│ │ │
|
|
23
|
+
│ 1. Discover Server │ │
|
|
24
|
+
│ ─────────────────────> │ │
|
|
25
|
+
│ │ │
|
|
26
|
+
│ 2. Get RSA Key │ │
|
|
27
|
+
│ <───────────────────── │ │
|
|
28
|
+
│ │ │
|
|
29
|
+
│ 3. Encrypt Password │ │
|
|
30
|
+
│ ─────────────────────> │ │
|
|
31
|
+
│ │ │
|
|
32
|
+
│ 4. user:login │ │
|
|
33
|
+
│ ─────────────────────> │ │
|
|
34
|
+
│ │ │
|
|
35
|
+
│ 5. Return Code │ │
|
|
36
|
+
│ <───────────────────── │ │
|
|
37
|
+
│ │ │
|
|
38
|
+
│ ╔═════════════════════════════════════════════════╗ │
|
|
39
|
+
│ ║ Branch based on return code ║ │
|
|
40
|
+
│ ╠═════════════════════════════════════════════════╣ │
|
|
41
|
+
│ ║ S_OK / FA_NEED_ACCESS_SECRET → Continue ║ │
|
|
42
|
+
│ ║ FA_NEED_DYNAMIC_PWD → OTP Flow ║ │
|
|
43
|
+
│ ║ FA_NEED_VERIFY_CODE → Manual Login Required ║ │
|
|
44
|
+
│ ╚═════════════════════════════════════════════════╝ │
|
|
45
|
+
│ │ │
|
|
46
|
+
│ 6. getLunkrSession │ │
|
|
47
|
+
│ ──────────────────────────────────────────────────────────> │
|
|
48
|
+
│ │ │
|
|
49
|
+
│ 7. cim.common:verify │ │
|
|
50
|
+
│ <───────────────────────────────────────────────────────── │
|
|
51
|
+
│ (returns login_key) │ │
|
|
52
|
+
│ │ │
|
|
53
|
+
│ 8. Generate X-CM-TOKEN │ │
|
|
54
|
+
│ │ │
|
|
55
|
+
│ 9. cim.user:login │ │
|
|
56
|
+
│ ──────────────────────────────────────────────────────────> │
|
|
57
|
+
│ │ │
|
|
58
|
+
│ 10. Lunkr Session │ │
|
|
59
|
+
│ <───────────────────────────────────────────────────────── │
|
|
60
|
+
│ (Cim cookie, Lunkr SID) │ │
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Step-by-Step Flow
|
|
64
|
+
|
|
65
|
+
### Step 1: Server Discovery
|
|
66
|
+
|
|
67
|
+
First, discover the Coremail server for the user's email domain:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const serverInfo = await DomainDiscoveryService.discoverAvailableServer(email);
|
|
71
|
+
// Returns: { baseUrl, apiPath, apiUrl, uri }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Step 2: Get RSA Key
|
|
75
|
+
|
|
76
|
+
Request RSA public key for password encryption:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const rsaInfo = await client.postJson({
|
|
80
|
+
func: 'user:get_rsa',
|
|
81
|
+
payload: { uid: email }
|
|
82
|
+
});
|
|
83
|
+
// Returns: { sid, authType, rsaE, rsaN }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 3: Encrypt Password
|
|
87
|
+
|
|
88
|
+
Encrypt the password using RSA:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
const encryptedPassword = encryptPasswordRsa(password, rsaInfo.rsaE, rsaInfo.rsaN);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Step 4-5: Login Request
|
|
95
|
+
|
|
96
|
+
Send login request with encrypted password and device info:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const loginPayload = {
|
|
100
|
+
uid: email,
|
|
101
|
+
password: encryptedPassword,
|
|
102
|
+
authType: rsaInfo.authType,
|
|
103
|
+
device: {
|
|
104
|
+
uuid: deviceUuid,
|
|
105
|
+
os: 'macOS',
|
|
106
|
+
model: 'Desktop',
|
|
107
|
+
friendlyName: deviceName,
|
|
108
|
+
},
|
|
109
|
+
supportDynamicPwd: true,
|
|
110
|
+
supportBind2FA: true,
|
|
111
|
+
returnCookie: true,
|
|
112
|
+
// ... other fields
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const response = await client.postJson({
|
|
116
|
+
func: 'user:login',
|
|
117
|
+
sid: rsaInfo.sid,
|
|
118
|
+
payload: loginPayload
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Step 6: Handle Return Code
|
|
123
|
+
|
|
124
|
+
The login response returns different codes:
|
|
125
|
+
|
|
126
|
+
| Code | Meaning | Action |
|
|
127
|
+
|------|---------|--------|
|
|
128
|
+
| `S_OK` | Success | Proceed to getLunkrSession |
|
|
129
|
+
| `FA_NEED_ACCESS_SECRET` | Device auth success | Proceed to getLunkrSession |
|
|
130
|
+
| `FA_NEED_DYNAMIC_PWD` | OTP required | Trigger OTP flow |
|
|
131
|
+
| `FA_NEED_VERIFY_CODE` | Captcha required | Manual login required |
|
|
132
|
+
| `FA_USER_IN_GRAYLIST` | User restricted | Manual login required |
|
|
133
|
+
|
|
134
|
+
### OTP Flow (FA_NEED_DYNAMIC_PWD)
|
|
135
|
+
|
|
136
|
+
When OTP is required:
|
|
137
|
+
|
|
138
|
+
1. **Trigger second auth**:
|
|
139
|
+
```typescript
|
|
140
|
+
await client.postJson({
|
|
141
|
+
func: 'user:triggerSecondAuth',
|
|
142
|
+
payload: { type: 'OTP', device, tempSid, trans: 'login' }
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
2. **Wait for user** to complete OTP on another device
|
|
147
|
+
|
|
148
|
+
3. **Query validation status**:
|
|
149
|
+
```typescript
|
|
150
|
+
const result = await client.postJson({
|
|
151
|
+
func: 'user:querySecondAuthValidateStage',
|
|
152
|
+
payload: { tempSid, trans: 'login' }
|
|
153
|
+
});
|
|
154
|
+
// Wait for stage === 'VALIDATED'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
4. **Re-login** without password (auth already verified)
|
|
158
|
+
|
|
159
|
+
5. **Proceed to getLunkrSession**
|
|
160
|
+
|
|
161
|
+
### Step 6-10: Get Lunkr Session
|
|
162
|
+
|
|
163
|
+
After successful Coremail login, obtain a Lunkr session:
|
|
164
|
+
|
|
165
|
+
#### 6.1: Call cim.common:verify
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const verifyResp = await httpsPost(
|
|
169
|
+
'https://lunkr.coremail.cn/lunkr/s/json?func=cim.common:verify',
|
|
170
|
+
'{}',
|
|
171
|
+
{ Cookie: `Coremail=${coremailCookie}` }
|
|
172
|
+
);
|
|
173
|
+
// Returns: { login_key, login_index }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### 6.2: Generate X-CM-TOKEN
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const dataToEncrypt = `${coremailSid}:${coremailCookie}`;
|
|
180
|
+
const dataB64 = Buffer.from(dataToEncrypt).toString('base64');
|
|
181
|
+
const xCmToken = encryptWithLoginKey(dataB64, loginKey);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### 6.3: Call cim.user:login
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const loginResp = await httpsPost(
|
|
188
|
+
'https://lunkr.coremail.cn/lunkr/s/json?func=cim.user:login',
|
|
189
|
+
{
|
|
190
|
+
uid: email,
|
|
191
|
+
device: { uuid, os, model, friendlyName },
|
|
192
|
+
forceCookieCheck: true,
|
|
193
|
+
setCookieKey: true,
|
|
194
|
+
login_index: loginIndex
|
|
195
|
+
},
|
|
196
|
+
{ 'X-CM-TOKEN': xCmToken }
|
|
197
|
+
);
|
|
198
|
+
// Returns: { sid, self_uid }
|
|
199
|
+
// Set-Cookie header: Cim=...
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Session Data
|
|
203
|
+
|
|
204
|
+
### Coremail Session
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
{
|
|
208
|
+
sid: string, // Coremail session ID
|
|
209
|
+
cookie: string, // Coremail=...
|
|
210
|
+
baseUrl: string, // e.g., https://mail.example.com
|
|
211
|
+
apiPath: string // e.g., /coremail/s/json
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Lunkr Session
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
sid: string, // Lunkr session ID
|
|
220
|
+
selfUid: string, // User's Lunkr ID
|
|
221
|
+
cookie: string, // Cim=...
|
|
222
|
+
baseUrl: string, // https://lunkr.coremail.cn
|
|
223
|
+
apiPath: string // /lunkr/s/json
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Key Difference
|
|
228
|
+
|
|
229
|
+
The critical difference is:
|
|
230
|
+
|
|
231
|
+
- **Coremail session** is for the email server API
|
|
232
|
+
- **Lunkr session** is for the collaboration platform API
|
|
233
|
+
|
|
234
|
+
Both OTP flow and normal login flow must call `getLunkrSession()` to convert the Coremail session to a Lunkr session. The bug fixed in this version was that the OTP flow was returning the Coremail session directly instead of calling `getLunkrSession()`.
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
|
|
238
|
+
| Error | Cause | Solution |
|
|
239
|
+
|-------|-------|----------|
|
|
240
|
+
| `PasswordError` | Wrong password | Check credentials |
|
|
241
|
+
| `FA_NEED_VERIFY_CODE` | Captcha required | Login via webmail first |
|
|
242
|
+
| `FA_USER_IN_GRAYLIST` | Account restricted | Contact admin |
|
|
243
|
+
| `No Coremail cookie` | Login incomplete | Check server response |
|
|
244
|
+
| `cim.common:verify failed` | Session invalid | Re-login required |
|
|
245
|
+
|
|
246
|
+
## Session Validation
|
|
247
|
+
|
|
248
|
+
Sessions are validated periodically (every 5 minutes) by calling:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
await client.checkSessionValid(sid);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
If the session is invalid, the user must re-authenticate.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# 命令速查表
|
|
2
|
+
|
|
3
|
+
## npm scripts
|
|
4
|
+
|
|
5
|
+
### 开发调试
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev # 热重载开发模式
|
|
9
|
+
npm run dev:cli # 直接运行 CLI (不编译)
|
|
10
|
+
|
|
11
|
+
# 调试单个命令
|
|
12
|
+
npx tsx src/bin/lunkr-cli.ts status
|
|
13
|
+
npx tsx src/bin/lunkr-cli.ts login
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 测试
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm test # 运行测试
|
|
20
|
+
npm run test:watch # 测试监视模式
|
|
21
|
+
npm run lint # 代码检查
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 构建
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run clean # 清理 dist/
|
|
28
|
+
npm run build # 编译 TypeScript
|
|
29
|
+
npm run build:release # 生产构建 (esbuild)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 部署
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run deploy:local # 快速部署 (仅复制)
|
|
36
|
+
npm run deploy:local:full # 完整: clean → test → build → deploy
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 版本与发布
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm version patch # 提升补丁版本 (1.0.0 → 1.0.1)
|
|
43
|
+
npm version minor # 提升小版本 (1.0.0 → 1.1.0)
|
|
44
|
+
npm version major # 提升大版本 (1.0.0 → 2.0.0)
|
|
45
|
+
|
|
46
|
+
npm run pack # 打包 .tgz
|
|
47
|
+
npm publish # 发布到 npm
|
|
48
|
+
git push --follow-tags # 推送代码和标签
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 环境变量
|
|
52
|
+
|
|
53
|
+
| 变量 | 默认值 | 说明 |
|
|
54
|
+
|------|--------|------|
|
|
55
|
+
| `OPENCLAW_ROOT` | `~/work/ai/openclaw` | OpenClaw 根目录 |
|
|
56
|
+
| `OPENCLAW_EXT` | `$OPENCLAW_ROOT/extensions` | 扩展目录 |
|
|
57
|
+
| `LUNKR_LANG` | 自动检测 | 语言 (zh-CN, en-US) |
|
|
58
|
+
|
|
59
|
+
## 典型工作流
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
┌─────────────────────────────────────────────────────────┐
|
|
63
|
+
│ 开发 │
|
|
64
|
+
├─────────────────────────────────────────────────────────┤
|
|
65
|
+
│ npm run test:watch # 开着测试监视 │
|
|
66
|
+
│ # 编写代码... │
|
|
67
|
+
│ npm run deploy:local # 快速部署测试 │
|
|
68
|
+
└─────────────────────────────────────────────────────────┘
|
|
69
|
+
↓
|
|
70
|
+
┌─────────────────────────────────────────────────────────┐
|
|
71
|
+
│ 发布前验证 │
|
|
72
|
+
├─────────────────────────────────────────────────────────┤
|
|
73
|
+
│ npm run deploy:local:full # 完整流程 │
|
|
74
|
+
└─────────────────────────────────────────────────────────┘
|
|
75
|
+
↓
|
|
76
|
+
┌─────────────────────────────────────────────────────────┐
|
|
77
|
+
│ 打包/发布 │
|
|
78
|
+
├─────────────────────────────────────────────────────────┤
|
|
79
|
+
│ npm version patch # 提升版本号 │
|
|
80
|
+
│ git push --follow-tags # 推送代码和标签 │
|
|
81
|
+
│ npm run pack # 生成 .tgz (可选) │
|
|
82
|
+
│ npm publish # 发布到 npm │
|
|
83
|
+
└─────────────────────────────────────────────────────────┘
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## CLI 命令
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# 认证
|
|
90
|
+
openclaw lunkr login --email <email> # 登录
|
|
91
|
+
openclaw lunkr login --email <email> --password <pwd> # 密码登录
|
|
92
|
+
openclaw lunkr logout # 登出
|
|
93
|
+
openclaw lunkr status # 状态
|
|
94
|
+
|
|
95
|
+
# 消息
|
|
96
|
+
openclaw lunkr send -t <uid> -m "hello" # 发送消息
|
|
97
|
+
openclaw lunkr send-file -t <uid> -f file.pdf # 发送文件
|
|
98
|
+
openclaw lunkr download --file-id <id> # 下载文件
|
|
99
|
+
|
|
100
|
+
# 搜索
|
|
101
|
+
openclaw lunkr search-contact <keyword> # 搜索联系人
|
|
102
|
+
openclaw lunkr search-group <keyword> # 搜索讨论组
|
|
103
|
+
|
|
104
|
+
# Bot 讨论
|
|
105
|
+
openclaw lunkr create <name> # 创建 Bot 讨论
|
|
106
|
+
openclaw lunkr bot-list # 列出 Bot 讨论
|
|
107
|
+
openclaw lunkr bot-delete --name <name> # 删除 Bot 讨论
|
|
108
|
+
|
|
109
|
+
# 其他
|
|
110
|
+
openclaw lunkr list-msg -t <uid> # 列出消息
|
|
111
|
+
openclaw lunkr listen # 监听实时消息
|
|
112
|
+
```
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Lunkr Extension 开发指南
|
|
2
|
+
|
|
3
|
+
本文档提供 Lunkr 扩展的完整开发、测试和部署命令参考。
|
|
4
|
+
|
|
5
|
+
## 快速参考
|
|
6
|
+
|
|
7
|
+
| 场景 | 命令 |
|
|
8
|
+
|------|------|
|
|
9
|
+
| 安装依赖 | `npm install` |
|
|
10
|
+
| 运行测试 | `npm test` |
|
|
11
|
+
| 测试监视模式 | `npm run test:watch` |
|
|
12
|
+
| 编译 | `npm run build` |
|
|
13
|
+
| 快速部署 | `npm run deploy:local` |
|
|
14
|
+
| 完整部署 | `npm run deploy:local:full` |
|
|
15
|
+
| 打包发布 | `npm run pack` |
|
|
16
|
+
|
|
17
|
+
## 开发工作流
|
|
18
|
+
|
|
19
|
+
### 1. 日常开发
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 终端 1: 运行测试监视器
|
|
23
|
+
npm run test:watch
|
|
24
|
+
|
|
25
|
+
# 终端 2: 编辑代码...
|
|
26
|
+
# 测试会自动运行
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. 调试 CLI 命令
|
|
30
|
+
|
|
31
|
+
直接运行 TypeScript,无需编译:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 使用 tsx 直接运行
|
|
35
|
+
npx tsx src/bin/lunkr-cli.ts status
|
|
36
|
+
npx tsx src/bin/lunkr-cli.ts login --email your@email.com
|
|
37
|
+
npx tsx src/bin/lunkr-cli.ts search-contact test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. 在 OpenClaw 中测试
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 快速部署(假设已编译)
|
|
44
|
+
npm run deploy:local
|
|
45
|
+
|
|
46
|
+
# 或完整流程:清理 → 测试 → 编译 → 部署
|
|
47
|
+
npm run deploy:local:full
|
|
48
|
+
|
|
49
|
+
# 测试
|
|
50
|
+
openclaw lunkr status
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 命令详解
|
|
54
|
+
|
|
55
|
+
### 开发命令
|
|
56
|
+
|
|
57
|
+
| 命令 | 说明 |
|
|
58
|
+
|------|------|
|
|
59
|
+
| `npm run dev` | 热重载开发模式(监视文件变化) |
|
|
60
|
+
| `npm run dev:cli` | 直接运行 CLI(不编译) |
|
|
61
|
+
|
|
62
|
+
### 测试命令
|
|
63
|
+
|
|
64
|
+
| 命令 | 说明 |
|
|
65
|
+
|------|------|
|
|
66
|
+
| `npm test` | 运行所有测试 |
|
|
67
|
+
| `npm run test:watch` | 监视模式,文件变化自动重跑 |
|
|
68
|
+
| `npm run lint` | 代码检查 |
|
|
69
|
+
|
|
70
|
+
### 构建命令
|
|
71
|
+
|
|
72
|
+
| 命令 | 说明 |
|
|
73
|
+
|------|------|
|
|
74
|
+
| `npm run clean` | 清理 dist/ 目录 |
|
|
75
|
+
| `npm run build` | 编译 TypeScript → dist/ |
|
|
76
|
+
| `npm run build:release` | 生产构建(esbuild 打包) |
|
|
77
|
+
|
|
78
|
+
### 部署命令
|
|
79
|
+
|
|
80
|
+
| 命令 | 说明 |
|
|
81
|
+
|------|------|
|
|
82
|
+
| `npm run deploy:local` | 快速部署到本地 OpenClaw(仅复制) |
|
|
83
|
+
| `npm run deploy:local:full` | 完整部署:clean → test → build → deploy |
|
|
84
|
+
|
|
85
|
+
### 发布命令
|
|
86
|
+
|
|
87
|
+
| 命令 | 说明 |
|
|
88
|
+
|------|------|
|
|
89
|
+
| `npm run pack` | 打包为 .tgz 文件 |
|
|
90
|
+
| `npm publish` | 发布到 npm registry |
|
|
91
|
+
|
|
92
|
+
## 部署目标配置
|
|
93
|
+
|
|
94
|
+
默认部署路径:
|
|
95
|
+
```
|
|
96
|
+
~/work/ai/openclaw/extensions/lunkr
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
可通过环境变量自定义:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# 自定义 OpenClaw 根目录
|
|
103
|
+
export OPENCLAW_ROOT=/path/to/openclaw
|
|
104
|
+
npm run deploy:local
|
|
105
|
+
|
|
106
|
+
# 或自定义扩展目录
|
|
107
|
+
export OPENCLAW_EXT=/custom/extensions/path
|
|
108
|
+
npm run deploy:local
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 典型工作日
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# 早上开始工作
|
|
115
|
+
cd extensions/lunkr
|
|
116
|
+
npm run test:watch # 终端 1: 开着测试
|
|
117
|
+
|
|
118
|
+
# 编写代码...
|
|
119
|
+
# 测试自动运行
|
|
120
|
+
|
|
121
|
+
# 中午想集成测试
|
|
122
|
+
npm run deploy:local # 快速部署(跳过测试,因为刚才测过了)
|
|
123
|
+
|
|
124
|
+
# 继续开发...
|
|
125
|
+
|
|
126
|
+
# 下班前提交
|
|
127
|
+
npm run deploy:local:full # 完整流程确保没问题
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 发布流程
|
|
131
|
+
|
|
132
|
+
### 版本管理
|
|
133
|
+
|
|
134
|
+
使用语义化版本号 (SemVer):`MAJOR.MINOR.PATCH`
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# 补丁版本 - bug fixes
|
|
138
|
+
npm version patch # 1.0.0 → 1.0.1
|
|
139
|
+
|
|
140
|
+
# 小版本 - 新功能,向后兼容
|
|
141
|
+
npm version minor # 1.0.1 → 1.1.0
|
|
142
|
+
|
|
143
|
+
# 大版本 - 破坏性变更
|
|
144
|
+
npm version major # 1.1.0 → 2.0.0
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`npm version` 命令会:
|
|
148
|
+
1. 更新 `package.json` 中的版本号
|
|
149
|
+
2. 自动创建 git commit
|
|
150
|
+
3. 自动创建 git tag
|
|
151
|
+
|
|
152
|
+
### 打包测试
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 打包为 tar.gz
|
|
156
|
+
npm run pack
|
|
157
|
+
|
|
158
|
+
# 输出: coremail-lunkr-openclaw-1.0.0.tgz
|
|
159
|
+
|
|
160
|
+
# 在其他地方测试安装
|
|
161
|
+
npm install ./coremail-lunkr-openclaw-1.0.0.tgz
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 发布到 npm
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# 1. 确保测试通过
|
|
168
|
+
npm test
|
|
169
|
+
|
|
170
|
+
# 2. 提升版本号
|
|
171
|
+
npm version patch # 或 minor / major
|
|
172
|
+
|
|
173
|
+
# 3. 推送代码和标签
|
|
174
|
+
git push --follow-tags
|
|
175
|
+
|
|
176
|
+
# 4. 发布到 npm
|
|
177
|
+
npm publish --access public
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 项目结构
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
extensions/lunkr/
|
|
184
|
+
├── src/ # TypeScript 源码
|
|
185
|
+
│ ├── adapters/ # OpenClaw 适配器
|
|
186
|
+
│ ├── cli/ # CLI 命令
|
|
187
|
+
│ ├── core/ # 核心逻辑
|
|
188
|
+
│ ├── i18n/ # 国际化
|
|
189
|
+
│ ├── internal/ # 内部模块
|
|
190
|
+
│ └── index.ts # 入口
|
|
191
|
+
├── dist/ # 编译输出
|
|
192
|
+
├── skills/ # OpenClaw 技能
|
|
193
|
+
├── docs/ # 文档
|
|
194
|
+
├── scripts/ # 构建脚本
|
|
195
|
+
│ ├── deploy-local.mjs # 本地部署
|
|
196
|
+
│ └── build-release.js # 生产构建
|
|
197
|
+
├── package.json
|
|
198
|
+
├── tsconfig.json
|
|
199
|
+
└── README.md
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 环境要求
|
|
203
|
+
|
|
204
|
+
- **Node.js**: v22+
|
|
205
|
+
- **npm**: v10+
|
|
206
|
+
- **TypeScript**: v5+
|
|
207
|
+
|
|
208
|
+
## 故障排除
|
|
209
|
+
|
|
210
|
+
### 编译失败
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# 清理后重试
|
|
214
|
+
npm run clean
|
|
215
|
+
npm run build
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 部署后不生效
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# 确保 OpenClaw gateway 重启
|
|
222
|
+
openclaw gateway restart
|
|
223
|
+
|
|
224
|
+
# 或重启整个服务
|
|
225
|
+
openclaw restart
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 测试失败
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# 查看详细输出
|
|
232
|
+
npm test -- --reporter=verbose
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 相关文档
|
|
236
|
+
|
|
237
|
+
- [README.md](../README.md) - 项目介绍和配置
|
|
238
|
+
- [USAGE.md](./USAGE.md) - 使用指南
|
|
239
|
+
- [AUTHENTICATION.md](./AUTHENTICATION.md) - 认证说明
|