botmux 2.26.0 → 2.27.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.en.md +50 -46
- package/README.md +49 -46
- package/dist/cli.js +279 -40
- package/dist/cli.js.map +1 -1
- package/dist/setup/bots-store.d.ts +3 -0
- package/dist/setup/bots-store.d.ts.map +1 -0
- package/dist/setup/bots-store.js +24 -0
- package/dist/setup/bots-store.js.map +1 -0
- package/dist/setup/lark-scopes.json +301 -0
- package/dist/setup/register-app.d.ts +46 -0
- package/dist/setup/register-app.d.ts.map +1 -0
- package/dist/setup/register-app.js +87 -0
- package/dist/setup/register-app.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts +115 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -0
- package/dist/setup/verify-permissions.js +207 -0
- package/dist/setup/verify-permissions.js.map +1 -0
- package/package.json +5 -3
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scopes": {
|
|
3
|
+
"tenant": [
|
|
4
|
+
"ai:llpp:task_execute",
|
|
5
|
+
"application:application:self_manage",
|
|
6
|
+
"application:bot.menu:write",
|
|
7
|
+
"audio_video_ai:meeting_assistance",
|
|
8
|
+
"auth:user_access_token:read",
|
|
9
|
+
"board:whiteboard:node:create",
|
|
10
|
+
"board:whiteboard:node:delete",
|
|
11
|
+
"board:whiteboard:node:read",
|
|
12
|
+
"board:whiteboard:node:update",
|
|
13
|
+
"calendar:calendar.acl:create",
|
|
14
|
+
"calendar:calendar.acl:delete",
|
|
15
|
+
"calendar:calendar.acl:read",
|
|
16
|
+
"calendar:calendar.event:create",
|
|
17
|
+
"calendar:calendar.event:delete",
|
|
18
|
+
"calendar:calendar.event:read",
|
|
19
|
+
"calendar:calendar.event:reply",
|
|
20
|
+
"calendar:calendar.event:update",
|
|
21
|
+
"calendar:calendar.free_busy:read",
|
|
22
|
+
"calendar:calendar:create",
|
|
23
|
+
"calendar:calendar:delete",
|
|
24
|
+
"calendar:calendar:read",
|
|
25
|
+
"calendar:calendar:subscribe",
|
|
26
|
+
"calendar:calendar:update",
|
|
27
|
+
"cardkit:card:read",
|
|
28
|
+
"cardkit:card:write",
|
|
29
|
+
"comment_sdk:comment_sdk",
|
|
30
|
+
"component:url_preview",
|
|
31
|
+
"contact:contact.base:readonly",
|
|
32
|
+
"contact:user.base:readonly",
|
|
33
|
+
"contact:user.email:readonly",
|
|
34
|
+
"contact:user.employee_id:readonly",
|
|
35
|
+
"contact:user.id:readonly",
|
|
36
|
+
"directory:employee.base.external_id:read",
|
|
37
|
+
"directory:employee.work.job_number:read",
|
|
38
|
+
"directory:job_title.status:read",
|
|
39
|
+
"docs:document.comment:create",
|
|
40
|
+
"docs:document.comment:delete",
|
|
41
|
+
"docs:document.comment:read",
|
|
42
|
+
"docs:document.comment:update",
|
|
43
|
+
"docs:document.comment:write_only",
|
|
44
|
+
"docs:document.content:read",
|
|
45
|
+
"docs:document.media:download",
|
|
46
|
+
"docs:document.media:upload",
|
|
47
|
+
"docs:document.subscription",
|
|
48
|
+
"docs:document.subscription:read",
|
|
49
|
+
"docs:document:copy",
|
|
50
|
+
"docs:document:export",
|
|
51
|
+
"docs:document:import",
|
|
52
|
+
"docs:event.document_deleted:read",
|
|
53
|
+
"docs:event.document_edited:read",
|
|
54
|
+
"docs:event.document_opened:read",
|
|
55
|
+
"docs:event:subscribe",
|
|
56
|
+
"docs:permission.member:auth",
|
|
57
|
+
"docs:permission.member:create",
|
|
58
|
+
"docs:permission.member:delete",
|
|
59
|
+
"docs:permission.member:retrieve",
|
|
60
|
+
"docs:permission.member:transfer",
|
|
61
|
+
"docs:permission.member:update",
|
|
62
|
+
"docs:permission.setting:read",
|
|
63
|
+
"docs:permission.setting:readonly",
|
|
64
|
+
"docs:permission.setting:write_only",
|
|
65
|
+
"docs_tool:docs_tool",
|
|
66
|
+
"document_ai:document:chunking",
|
|
67
|
+
"document_ai:kie:llm",
|
|
68
|
+
"docx:document.block:convert",
|
|
69
|
+
"docx:document:create",
|
|
70
|
+
"docx:document:readonly",
|
|
71
|
+
"docx:document:write_only",
|
|
72
|
+
"drive:drive.metadata:readonly",
|
|
73
|
+
"drive:drive.search:readonly",
|
|
74
|
+
"drive:drive:version",
|
|
75
|
+
"drive:drive:version:readonly",
|
|
76
|
+
"drive:file.like:readonly",
|
|
77
|
+
"drive:file.meta.sec_label.read_only",
|
|
78
|
+
"drive:file:download",
|
|
79
|
+
"drive:file:favorite",
|
|
80
|
+
"drive:file:favorite:readonly",
|
|
81
|
+
"drive:file:upload",
|
|
82
|
+
"drive:file:view_record:readonly",
|
|
83
|
+
"event:failed_event:readonly",
|
|
84
|
+
"event:ip_list",
|
|
85
|
+
"im:app_feed_card:write",
|
|
86
|
+
"im:chat.access_event.bot_p2p_chat:read",
|
|
87
|
+
"im:chat.announcement:read",
|
|
88
|
+
"im:chat.announcement:write_only",
|
|
89
|
+
"im:chat.chat_pins:read",
|
|
90
|
+
"im:chat.chat_pins:write_only",
|
|
91
|
+
"im:chat.collab_plugins:read",
|
|
92
|
+
"im:chat.collab_plugins:write_only",
|
|
93
|
+
"im:chat.managers:write_only",
|
|
94
|
+
"im:chat.members:bot_access",
|
|
95
|
+
"im:chat.members:read",
|
|
96
|
+
"im:chat.members:write_only",
|
|
97
|
+
"im:chat.menu_tree:read",
|
|
98
|
+
"im:chat.menu_tree:write_only",
|
|
99
|
+
"im:chat.moderation:read",
|
|
100
|
+
"im:chat.tabs:read",
|
|
101
|
+
"im:chat.tabs:write_only",
|
|
102
|
+
"im:chat.top_notice:write_only",
|
|
103
|
+
"im:chat.widgets:read",
|
|
104
|
+
"im:chat.widgets:write_only",
|
|
105
|
+
"im:chat:create",
|
|
106
|
+
"im:chat:delete",
|
|
107
|
+
"im:chat:moderation:write_only",
|
|
108
|
+
"im:chat:operate_as_owner",
|
|
109
|
+
"im:chat:read",
|
|
110
|
+
"im:chat:update",
|
|
111
|
+
"im:message",
|
|
112
|
+
"im:message.group_at_msg.include_bot:readonly",
|
|
113
|
+
"im:message.group_at_msg:readonly",
|
|
114
|
+
"im:message.group_msg",
|
|
115
|
+
"im:message.p2p_msg:readonly",
|
|
116
|
+
"im:message.pins:read",
|
|
117
|
+
"im:message.pins:write_only",
|
|
118
|
+
"im:message.reactions:read",
|
|
119
|
+
"im:message.reactions:write_only",
|
|
120
|
+
"im:message:readonly",
|
|
121
|
+
"im:message:recall",
|
|
122
|
+
"im:message:send_as_bot",
|
|
123
|
+
"im:message:send_multi_depts",
|
|
124
|
+
"im:message:send_multi_users",
|
|
125
|
+
"im:message:send_sys_msg",
|
|
126
|
+
"im:message:update",
|
|
127
|
+
"im:resource",
|
|
128
|
+
"im:url_preview.update",
|
|
129
|
+
"im:user_agent:read",
|
|
130
|
+
"optical_char_recognition:image",
|
|
131
|
+
"sheets:spreadsheet.meta:read",
|
|
132
|
+
"sheets:spreadsheet.meta:write_only",
|
|
133
|
+
"sheets:spreadsheet:create",
|
|
134
|
+
"sheets:spreadsheet:read",
|
|
135
|
+
"sheets:spreadsheet:write_only",
|
|
136
|
+
"slides:presentation:create",
|
|
137
|
+
"slides:presentation:read",
|
|
138
|
+
"slides:presentation:update",
|
|
139
|
+
"slides:presentation:write_only",
|
|
140
|
+
"space:document.event:read",
|
|
141
|
+
"space:document:create",
|
|
142
|
+
"space:document:delete",
|
|
143
|
+
"space:document:move",
|
|
144
|
+
"space:document:retrieve",
|
|
145
|
+
"space:document:shortcut",
|
|
146
|
+
"space:folder:create",
|
|
147
|
+
"speech_to_text:speech",
|
|
148
|
+
"task:task:read",
|
|
149
|
+
"task:task:write",
|
|
150
|
+
"task:tasklist:read",
|
|
151
|
+
"task:tasklist:write",
|
|
152
|
+
"translation:text",
|
|
153
|
+
"wiki:member:create",
|
|
154
|
+
"wiki:member:retrieve",
|
|
155
|
+
"wiki:member:update",
|
|
156
|
+
"wiki:node:copy",
|
|
157
|
+
"wiki:node:create",
|
|
158
|
+
"wiki:node:move",
|
|
159
|
+
"wiki:node:read",
|
|
160
|
+
"wiki:node:retrieve",
|
|
161
|
+
"wiki:node:update",
|
|
162
|
+
"wiki:setting:read",
|
|
163
|
+
"wiki:setting:write_only",
|
|
164
|
+
"wiki:space:read",
|
|
165
|
+
"wiki:space:retrieve",
|
|
166
|
+
"wiki:space:write_only",
|
|
167
|
+
"wiki:wiki:readonly"
|
|
168
|
+
],
|
|
169
|
+
"user": [
|
|
170
|
+
"board:whiteboard:node:create",
|
|
171
|
+
"board:whiteboard:node:delete",
|
|
172
|
+
"board:whiteboard:node:read",
|
|
173
|
+
"board:whiteboard:node:update",
|
|
174
|
+
"calendar:calendar.acl:create",
|
|
175
|
+
"calendar:calendar.acl:delete",
|
|
176
|
+
"calendar:calendar.acl:read",
|
|
177
|
+
"calendar:calendar.event:create",
|
|
178
|
+
"calendar:calendar.event:delete",
|
|
179
|
+
"calendar:calendar.event:read",
|
|
180
|
+
"calendar:calendar.event:reply",
|
|
181
|
+
"calendar:calendar.event:update",
|
|
182
|
+
"calendar:calendar.free_busy:read",
|
|
183
|
+
"calendar:calendar:create",
|
|
184
|
+
"calendar:calendar:delete",
|
|
185
|
+
"calendar:calendar:read",
|
|
186
|
+
"calendar:calendar:subscribe",
|
|
187
|
+
"calendar:calendar:update",
|
|
188
|
+
"comment_sdk:comment_sdk",
|
|
189
|
+
"contact:contact.base:readonly",
|
|
190
|
+
"contact:user.employee_id:readonly",
|
|
191
|
+
"docs:component",
|
|
192
|
+
"docs:document.comment:create",
|
|
193
|
+
"docs:document.comment:read",
|
|
194
|
+
"docs:document.comment:update",
|
|
195
|
+
"docs:document.comment:write_only",
|
|
196
|
+
"docs:document.content:read",
|
|
197
|
+
"docs:document.media:download",
|
|
198
|
+
"docs:document.media:upload",
|
|
199
|
+
"docs:document.subscription",
|
|
200
|
+
"docs:document.subscription:read",
|
|
201
|
+
"docs:document:copy",
|
|
202
|
+
"docs:document:export",
|
|
203
|
+
"docs:document:import",
|
|
204
|
+
"docs:event.document_deleted:read",
|
|
205
|
+
"docs:event.document_edited:read",
|
|
206
|
+
"docs:event.document_opened:read",
|
|
207
|
+
"docs:event:subscribe",
|
|
208
|
+
"docs:permission.member:apply",
|
|
209
|
+
"docs:permission.member:auth",
|
|
210
|
+
"docs:permission.member:create",
|
|
211
|
+
"docs:permission.member:delete",
|
|
212
|
+
"docs:permission.member:retrieve",
|
|
213
|
+
"docs:permission.member:transfer",
|
|
214
|
+
"docs:permission.member:update",
|
|
215
|
+
"docs:permission.setting:read",
|
|
216
|
+
"docs:permission.setting:readonly",
|
|
217
|
+
"docs:permission.setting:write_only",
|
|
218
|
+
"docs_tool:docs_tool",
|
|
219
|
+
"docx:document.block:convert",
|
|
220
|
+
"docx:document:create",
|
|
221
|
+
"docx:document:readonly",
|
|
222
|
+
"docx:document:write_only",
|
|
223
|
+
"drive:drive.metadata:readonly",
|
|
224
|
+
"drive:drive.search:readonly",
|
|
225
|
+
"drive:drive:version",
|
|
226
|
+
"drive:drive:version:readonly",
|
|
227
|
+
"drive:file.like:readonly",
|
|
228
|
+
"drive:file.meta.sec_label.read_only",
|
|
229
|
+
"drive:file:download",
|
|
230
|
+
"drive:file:favorite",
|
|
231
|
+
"drive:file:favorite:readonly",
|
|
232
|
+
"drive:file:upload",
|
|
233
|
+
"drive:file:view_record:readonly",
|
|
234
|
+
"event:ip_list",
|
|
235
|
+
"im:chat.access_event.bot_p2p_chat:read",
|
|
236
|
+
"im:chat.announcement:read",
|
|
237
|
+
"im:chat.announcement:write_only",
|
|
238
|
+
"im:chat.chat_pins:read",
|
|
239
|
+
"im:chat.chat_pins:write_only",
|
|
240
|
+
"im:chat.collab_plugins:read",
|
|
241
|
+
"im:chat.collab_plugins:write_only",
|
|
242
|
+
"im:chat.managers:write_only",
|
|
243
|
+
"im:chat.members:read",
|
|
244
|
+
"im:chat.members:write_only",
|
|
245
|
+
"im:chat.moderation:read",
|
|
246
|
+
"im:chat.tabs:read",
|
|
247
|
+
"im:chat.tabs:write_only",
|
|
248
|
+
"im:chat.top_notice:write_only",
|
|
249
|
+
"im:chat:delete",
|
|
250
|
+
"im:chat:moderation:write_only",
|
|
251
|
+
"im:chat:read",
|
|
252
|
+
"im:chat:update",
|
|
253
|
+
"im:message",
|
|
254
|
+
"im:message.group_msg:get_as_user",
|
|
255
|
+
"im:message.p2p_msg:get_as_user",
|
|
256
|
+
"im:message.pins:read",
|
|
257
|
+
"im:message.pins:write_only",
|
|
258
|
+
"im:message.reactions:read",
|
|
259
|
+
"im:message.reactions:write_only",
|
|
260
|
+
"im:message.urgent.status:write",
|
|
261
|
+
"im:message:readonly",
|
|
262
|
+
"im:message:recall",
|
|
263
|
+
"im:message:update",
|
|
264
|
+
"im:special_focus",
|
|
265
|
+
"offline_access",
|
|
266
|
+
"search:docs:read",
|
|
267
|
+
"search:message",
|
|
268
|
+
"sheets:spreadsheet.meta:read",
|
|
269
|
+
"sheets:spreadsheet.meta:write_only",
|
|
270
|
+
"sheets:spreadsheet:create",
|
|
271
|
+
"sheets:spreadsheet:read",
|
|
272
|
+
"sheets:spreadsheet:write_only",
|
|
273
|
+
"slides:presentation:create",
|
|
274
|
+
"slides:presentation:read",
|
|
275
|
+
"slides:presentation:update",
|
|
276
|
+
"slides:presentation:write_only",
|
|
277
|
+
"space:document.event:read",
|
|
278
|
+
"space:document:create",
|
|
279
|
+
"space:document:delete",
|
|
280
|
+
"space:document:move",
|
|
281
|
+
"space:document:retrieve",
|
|
282
|
+
"space:document:shortcut",
|
|
283
|
+
"space:folder:create",
|
|
284
|
+
"wiki:member:create",
|
|
285
|
+
"wiki:member:retrieve",
|
|
286
|
+
"wiki:member:update",
|
|
287
|
+
"wiki:node:copy",
|
|
288
|
+
"wiki:node:create",
|
|
289
|
+
"wiki:node:move",
|
|
290
|
+
"wiki:node:read",
|
|
291
|
+
"wiki:node:retrieve",
|
|
292
|
+
"wiki:node:update",
|
|
293
|
+
"wiki:setting:read",
|
|
294
|
+
"wiki:setting:write_only",
|
|
295
|
+
"wiki:space:read",
|
|
296
|
+
"wiki:space:retrieve",
|
|
297
|
+
"wiki:space:write_only",
|
|
298
|
+
"wiki:wiki:readonly"
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type RegisterBrand = 'feishu' | 'lark';
|
|
2
|
+
export type RegisterAppOk = {
|
|
3
|
+
ok: true;
|
|
4
|
+
appId: string;
|
|
5
|
+
appSecret: string;
|
|
6
|
+
brand: RegisterBrand;
|
|
7
|
+
};
|
|
8
|
+
export type RegisterAppErr = {
|
|
9
|
+
ok: false;
|
|
10
|
+
/**
|
|
11
|
+
* - `aborted`: 用户 Ctrl-C / 主动取消
|
|
12
|
+
* - `expired`: 二维码过期 (默认 10 分钟)
|
|
13
|
+
* - `denied`: 用户在浏览器里拒绝
|
|
14
|
+
* - `network`: 网络错误 / 端点不可达
|
|
15
|
+
* - `unknown`: 其它 (含 SDK 抛非预期错误)
|
|
16
|
+
*/
|
|
17
|
+
error: 'aborted' | 'expired' | 'denied' | 'network' | 'unknown';
|
|
18
|
+
/** 给用户看的简短错误描述, 不含 secret. */
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
export type RegisterAppResult = RegisterAppOk | RegisterAppErr;
|
|
22
|
+
export interface RegisterAppOptions {
|
|
23
|
+
/** 取消信号 (Ctrl-C 时填充). */
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
/**
|
|
26
|
+
* 渲染前回调, 测试时可注入静默打印. 默认在 stdout 打印二维码 + 链接.
|
|
27
|
+
*/
|
|
28
|
+
onQRCodeReady?: (info: {
|
|
29
|
+
url: string;
|
|
30
|
+
expireIn: number;
|
|
31
|
+
}) => void;
|
|
32
|
+
/** 状态变更回调, 主要用于"已切换到 Lark 域名"提示. */
|
|
33
|
+
onStatusChange?: (info: {
|
|
34
|
+
status: string;
|
|
35
|
+
interval?: number;
|
|
36
|
+
}) => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 尝试扫码建应用. 任何失败都返回 RegisterAppErr (不抛), 调用方可以回退手动粘.
|
|
40
|
+
*
|
|
41
|
+
* Secret 安全约束:
|
|
42
|
+
* - SDK 返回的 client_secret 只放进返回值, 不打印, 不写日志
|
|
43
|
+
* - 错误链里只放错误 code / 阶段, 不带 secret
|
|
44
|
+
*/
|
|
45
|
+
export declare function tryRegisterApp(opts?: RegisterAppOptions): Promise<RegisterAppResult>;
|
|
46
|
+
//# sourceMappingURL=register-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-app.d.ts","sourceRoot":"","sources":["../../src/setup/register-app.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,KAAK,CAAC;IACV;;;;;;OAMG;IACH,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAChE,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,cAAc,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,yBAAyB;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,oCAAoC;IACpC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACxE;AAiBD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8C9F"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书扫码建应用 — 对接 `@larksuiteoapi/node-sdk` 的 `registerApp`.
|
|
3
|
+
*
|
|
4
|
+
* 走 OAuth 2.0 Device Flow (RFC 8628):
|
|
5
|
+
* 1. SDK 向 `accounts.feishu.cn/oauth/v1/app/registration` 发 `action=begin`
|
|
6
|
+
* 请求 (`archetype=PersonalAgent`), 拿到 device_code + 二维码 URL
|
|
7
|
+
* 2. 在终端渲染二维码 + 链接, 等用户扫码
|
|
8
|
+
* 3. SDK 轮询同端点 `action=poll` 直到拿到 client_id + client_secret
|
|
9
|
+
* (=AppID + AppSecret)
|
|
10
|
+
*
|
|
11
|
+
* 注意:
|
|
12
|
+
* - 这个端点 archetype 写死 `PersonalAgent`, 但实测 PersonalAgent 应用是
|
|
13
|
+
* 可以挂 bot 能力的 (`zarazhangrui/feishu-claude-code-bridge` 在用).
|
|
14
|
+
* - 建出来的应用 **没有** 声明 botmux 需要的 scope, 用户仍要在开放平台
|
|
15
|
+
* 「权限管理 → 批量导入/导出权限」粘贴 ~/.botmux/lark-scopes.json (setup
|
|
16
|
+
* 末尾会自动写一份) 一次性提交审批. 事件订阅 + bot 能力维护者实测默认
|
|
17
|
+
* 配好, 收不到消息时见 README 的 fallback 自查清单.
|
|
18
|
+
* - secret 永远不打印; 错误只暴露 error code / 阶段标签, 不暴露 secret.
|
|
19
|
+
*/
|
|
20
|
+
import { registerApp } from '@larksuiteoapi/node-sdk';
|
|
21
|
+
import qrcode from 'qrcode-terminal';
|
|
22
|
+
function defaultPrintQRCode(info) {
|
|
23
|
+
const mins = Math.max(1, Math.round(info.expireIn / 60));
|
|
24
|
+
process.stderr.write('\n请用飞书 App 扫码完成应用创建:\n\n');
|
|
25
|
+
qrcode.generate(info.url, { small: true }, (qr) => process.stderr.write(qr + '\n'));
|
|
26
|
+
process.stderr.write(`\n二维码有效期约 ${mins} 分钟。也可在浏览器打开:\n ${info.url}\n\n`);
|
|
27
|
+
}
|
|
28
|
+
function defaultPrintStatus(info) {
|
|
29
|
+
if (info.status === 'domain_switched') {
|
|
30
|
+
process.stderr.write('识别到国际版租户, 已切换到 larksuite.com 域名继续轮询。\n');
|
|
31
|
+
}
|
|
32
|
+
else if (info.status === 'slow_down' && info.interval) {
|
|
33
|
+
process.stderr.write(`轮询过快, 间隔自动调整到 ${info.interval}s。\n`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 尝试扫码建应用. 任何失败都返回 RegisterAppErr (不抛), 调用方可以回退手动粘.
|
|
38
|
+
*
|
|
39
|
+
* Secret 安全约束:
|
|
40
|
+
* - SDK 返回的 client_secret 只放进返回值, 不打印, 不写日志
|
|
41
|
+
* - 错误链里只放错误 code / 阶段, 不带 secret
|
|
42
|
+
*/
|
|
43
|
+
export async function tryRegisterApp(opts = {}) {
|
|
44
|
+
const onQR = opts.onQRCodeReady ?? defaultPrintQRCode;
|
|
45
|
+
const onStatus = opts.onStatusChange ?? defaultPrintStatus;
|
|
46
|
+
try {
|
|
47
|
+
const result = await registerApp({
|
|
48
|
+
signal: opts.signal,
|
|
49
|
+
source: 'botmux',
|
|
50
|
+
onQRCodeReady: onQR,
|
|
51
|
+
onStatusChange: onStatus,
|
|
52
|
+
});
|
|
53
|
+
if (!result.client_id || !result.client_secret) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
error: 'unknown',
|
|
57
|
+
message: 'SDK 返回的 client_id/client_secret 为空',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const brand = result.user_info?.tenant_brand === 'lark' ? 'lark' : 'feishu';
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
appId: result.client_id,
|
|
64
|
+
appSecret: result.client_secret,
|
|
65
|
+
brand,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
// SDK 抛 LarkChannelError, code 字段对齐 RFC 8628 的 device flow 错误
|
|
70
|
+
const code = err?.code ?? '';
|
|
71
|
+
const rawMsg = err?.message ?? String(err);
|
|
72
|
+
// SDK 不会把 secret 放进 message, 但保险起见再过一次
|
|
73
|
+
const safeMsg = rawMsg.replace(/[a-zA-Z0-9_-]{30,}/g, '***');
|
|
74
|
+
if (code === 'abort')
|
|
75
|
+
return { ok: false, error: 'aborted', message: '用户取消扫码' };
|
|
76
|
+
if (code === 'expired_token')
|
|
77
|
+
return { ok: false, error: 'expired', message: '二维码已过期, 请重试' };
|
|
78
|
+
if (code === 'access_denied')
|
|
79
|
+
return { ok: false, error: 'denied', message: '用户在浏览器里拒绝授权' };
|
|
80
|
+
// 网络层错误 (axios) — 没固定 code, 看 message
|
|
81
|
+
if (/ETIMEDOUT|ECONNREFUSED|ENOTFOUND|ECONNRESET|network/i.test(rawMsg)) {
|
|
82
|
+
return { ok: false, error: 'network', message: `网络错误: ${safeMsg}` };
|
|
83
|
+
}
|
|
84
|
+
return { ok: false, error: 'unknown', message: safeMsg };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=register-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-app.js","sourceRoot":"","sources":["../../src/setup/register-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export type Brand = 'feishu' | 'lark';
|
|
2
|
+
export interface RequiredScope {
|
|
3
|
+
/** 飞书 scope 名 (`im:message` 等) */
|
|
4
|
+
name: string;
|
|
5
|
+
/** 给用户看的中文说明 */
|
|
6
|
+
desc: string;
|
|
7
|
+
/**
|
|
8
|
+
* `critical` = 不开通 botmux 核心功能无法工作 (收发消息).
|
|
9
|
+
* 非 critical 的 scope 缺失只 WARN, 不阻断启动.
|
|
10
|
+
*/
|
|
11
|
+
critical: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* botmux 运行所需的 scope. 这里**只用于检测/提示**, 不用于自动申请——飞书
|
|
15
|
+
* `scope.apply` 只能提交"已声明但未授权"的, 没法给应用 manifest 加新声明.
|
|
16
|
+
* scope 选择必须用户去开放平台勾.
|
|
17
|
+
*/
|
|
18
|
+
export declare const BOTMUX_REQUIRED_SCOPES: RequiredScope[];
|
|
19
|
+
export interface RemainingStep {
|
|
20
|
+
title: string;
|
|
21
|
+
/** 飞书开放平台深链, 用户点了直接到对应页 */
|
|
22
|
+
url: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function buildScopeDeepLink(appId: string, scopeName: string, brand?: Brand): string;
|
|
25
|
+
export declare function buildEventSubDeepLink(appId: string, brand?: Brand): string;
|
|
26
|
+
export declare function buildAppHomeDeepLink(appId: string, brand?: Brand): string;
|
|
27
|
+
export type CredentialValidation = {
|
|
28
|
+
ok: true;
|
|
29
|
+
tenantAccessToken: string;
|
|
30
|
+
tokenExpiresIn: number;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
error: 'invalid_credentials' | 'network' | 'unknown';
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* 用 AppID/Secret 取一次 tenant_access_token, 验证凭证可用.
|
|
38
|
+
*
|
|
39
|
+
* Secret 不进 error.message: 错误信息只来自飞书返回的 msg 字段或 axios 错误类型.
|
|
40
|
+
*
|
|
41
|
+
* Codex review #3: 加 AbortController + 总超时. 网络半挂时 `botmux setup` /
|
|
42
|
+
* `botmux start` 不能无限卡住; 超时归类为 network, 这样 setup 走"凭证校验失败
|
|
43
|
+
* 不写盘" 路径, start 走"network 只 WARN 继续"路径.
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateCredentials(appId: string, appSecret: string, brand?: Brand, opts?: {
|
|
46
|
+
budgetMs?: number;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}): Promise<CredentialValidation>;
|
|
49
|
+
export type ScopeCheckResult = {
|
|
50
|
+
ok: true;
|
|
51
|
+
granted: string[];
|
|
52
|
+
missingCritical: RequiredScope[];
|
|
53
|
+
missingOptional: RequiredScope[];
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
/**
|
|
57
|
+
* - `need_self_manage`: 调 scope.list 被拒, 应用缺 `application:application:self_manage`
|
|
58
|
+
* (鸡生蛋: 没这个 scope 就查不到 scope 列表)
|
|
59
|
+
* - `network` / `unknown`: 其它失败
|
|
60
|
+
*/
|
|
61
|
+
error: 'need_self_manage' | 'network' | 'unknown';
|
|
62
|
+
message: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* 列出应用的 scope grant 状态, 比对 BOTMUX_REQUIRED_SCOPES.
|
|
66
|
+
*
|
|
67
|
+
* **不在主路径使用** — 待 spike 用真实/可复现 mock 证明 grant_status 含义和
|
|
68
|
+
* 状态闭环后再启用. 当前主路径只输出"剩余步骤 + 深链", 不做 grant_status 判定.
|
|
69
|
+
*
|
|
70
|
+
* scope.list 返回 shape (SDK type):
|
|
71
|
+
* `{ data: { scopes: [{ scope_name, grant_status, scope_type }] } }`
|
|
72
|
+
* grant_status 含义未在官方文档明确, 但社区 SDK / 实测一般约定:
|
|
73
|
+
* 1 = 已申请未生效, 2 = 已生效. 启用前 spike 务必确认这个映射.
|
|
74
|
+
*/
|
|
75
|
+
export declare function checkRequiredScopes(appId: string, appSecret: string, brand?: Brand): Promise<ScopeCheckResult>;
|
|
76
|
+
/**
|
|
77
|
+
* 触发管理员审批 (把"已声明但未授权"的 scope 提交).
|
|
78
|
+
*
|
|
79
|
+
* **不在主路径使用** — 见模块顶部注释. 文档表明它无法添加新 scope 到 manifest,
|
|
80
|
+
* 所以即使调成功也不能绕过"用户去开放平台勾 scope". 留作 spike 验证状态闭环
|
|
81
|
+
* 后的可选自动触发能力.
|
|
82
|
+
*
|
|
83
|
+
* 文档/SDK 显示无请求体. 错误码:
|
|
84
|
+
* - 212001: 剩余权限为高敏, 无法申请
|
|
85
|
+
* - 212002: 无可申请的 scope (manifest 全部已授权或为空)
|
|
86
|
+
* - 212003: 申请次数超限 (同租户同版本 > 10 次)
|
|
87
|
+
* - 212004: 重复申请
|
|
88
|
+
*/
|
|
89
|
+
export interface ApplyScopesResult {
|
|
90
|
+
/**
|
|
91
|
+
* - `submitted`: 申请已提交 (code=0). 是否被管理员审批通过需另查 `scope.list`.
|
|
92
|
+
* - `nothing_to_apply`: 212002, manifest 没有"已声明但未授权"的 scope.
|
|
93
|
+
* - `already_applied`: 212004, 重复申请.
|
|
94
|
+
* - `over_limit`: 212003, 同租户同版本 > 10 次申请.
|
|
95
|
+
* - `super_scope_only`: 212001, 剩余为高敏权限不可申请.
|
|
96
|
+
* - `timeout`: 调用本身没在 budgetMs 内返回.
|
|
97
|
+
* - `error`: 其它失败.
|
|
98
|
+
*/
|
|
99
|
+
status: 'submitted' | 'nothing_to_apply' | 'already_applied' | 'over_limit' | 'super_scope_only' | 'timeout' | 'error';
|
|
100
|
+
code?: number;
|
|
101
|
+
msg?: string;
|
|
102
|
+
}
|
|
103
|
+
export declare function applyScopesUnverified(appId: string, appSecret: string, opts?: {
|
|
104
|
+
brand?: Brand;
|
|
105
|
+
budgetMs?: number;
|
|
106
|
+
signal?: AbortSignal;
|
|
107
|
+
}): Promise<ApplyScopesResult>;
|
|
108
|
+
/**
|
|
109
|
+
* setup 后 "还要手动点的步骤" 结构化数据. 跟 cli.ts 的 printRemainingSteps + README
|
|
110
|
+
* "5 分钟快速接入" 一致: 主线就两步 (权限申请 + 按需重定向 URL); PersonalAgent
|
|
111
|
+
* 应用默认订阅事件 + bot 能力, 不在主线提示, 收不到消息时见 README 的 fallback
|
|
112
|
+
* 自查清单.
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildRemainingSteps(appId: string, brand?: Brand): RemainingStep[];
|
|
115
|
+
//# sourceMappingURL=verify-permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-permissions.d.ts","sourceRoot":"","sources":["../../src/setup/verify-permissions.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtC,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,aAAa,EAQjD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,MAAM,CAGpG;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,MAAM,CAGpF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,MAAM,CAGnF;AAID,MAAM,MAAM,oBAAoB,GAC5B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,KAAgB,EACvB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACrD,OAAO,CAAC,oBAAoB,CAAC,CA0D/B;AAID,MAAM,MAAM,gBAAgB,GACxB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,eAAe,EAAE,aAAa,EAAE,CAAC;CAClC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV;;;;OAIG;IACH,KAAK,EAAE,kBAAkB,GAAG,SAAS,GAAG,SAAS,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,KAAgB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAiC3B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;OAQG;IACH,MAAM,EACF,WAAW,GACX,kBAAkB,GAClB,iBAAiB,GACjB,YAAY,GACZ,kBAAkB,GAClB,SAAS,GACT,OAAO,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GACpE,OAAO,CAAC,iBAAiB,CAAC,CAmC5B;AAID;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,KAAgB,GAAG,aAAa,EAAE,CAa3F"}
|