@zhin.js/scaffold-wizard 0.1.8 → 0.2.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 +4 -0
- package/lib/adapter-configurers.d.ts +10 -0
- package/lib/adapter-configurers.d.ts.map +1 -1
- package/lib/adapter-configurers.js +436 -71
- package/lib/adapter-configurers.js.map +1 -1
- package/lib/adapter.d.ts +55 -9
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +206 -110
- package/lib/adapter.js.map +1 -1
- package/lib/apply.d.ts +22 -1
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +156 -23
- package/lib/apply.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/project-config-plan.d.ts +2 -0
- package/lib/project-config-plan.d.ts.map +1 -1
- package/lib/project-config-plan.js +54 -15
- package/lib/project-config-plan.js.map +1 -1
- package/lib/weixin-ilink-login.d.ts +33 -0
- package/lib/weixin-ilink-login.d.ts.map +1 -0
- package/lib/weixin-ilink-login.js +90 -0
- package/lib/weixin-ilink-login.js.map +1 -0
- package/lib/zhin-stack-deps.d.ts +9 -1
- package/lib/zhin-stack-deps.d.ts.map +1 -1
- package/lib/zhin-stack-deps.js +26 -10
- package/lib/zhin-stack-deps.js.map +1 -1
- package/package.json +3 -1
- package/src/adapter-configurers.ts +464 -70
- package/src/adapter.ts +224 -112
- package/src/apply.ts +170 -21
- package/src/index.ts +10 -4
- package/src/project-config-plan.ts +55 -15
- package/src/weixin-ilink-login.ts +151 -0
- package/src/zhin-stack-deps.ts +26 -10
package/src/adapter.ts
CHANGED
|
@@ -5,24 +5,39 @@ import {
|
|
|
5
5
|
adapterDocsUrl,
|
|
6
6
|
configureDiscordEndpoint,
|
|
7
7
|
configureGitHubEndpoint,
|
|
8
|
+
configureMilkyBot,
|
|
9
|
+
configureNapcatBot,
|
|
8
10
|
configureOneBot11Bot,
|
|
11
|
+
configureOneBot12Bot,
|
|
9
12
|
configureQQBot,
|
|
13
|
+
configureSatoriBot,
|
|
10
14
|
configureSlackEndpoint,
|
|
11
15
|
configureTelegramEndpoint,
|
|
16
|
+
configureWeixinIlinkBot,
|
|
12
17
|
} from './adapter-configurers.js';
|
|
13
18
|
import { ZHIN_STACK_VERSIONS } from './zhin-stack-deps.js';
|
|
14
19
|
|
|
20
|
+
/** 一个待挂载的子插件实例(package.json zhin.plugins 清单 + zhin.config plugins.<instanceKey> 配置) */
|
|
21
|
+
export interface AdapterPluginInstance {
|
|
22
|
+
/** 适配器/插件包名,如 @zhin.js/adapter-sandbox */
|
|
23
|
+
package: string;
|
|
24
|
+
/** 运行时实例键,如 sandbox / telegram;同名包多实例时需区分 */
|
|
25
|
+
instanceKey: string;
|
|
26
|
+
/** 写入 zhin.config plugins.<instanceKey> 的配置(对齐该插件 schema.json) */
|
|
27
|
+
config: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
|
|
15
30
|
export interface AdapterSetupResult {
|
|
16
31
|
packages: string[];
|
|
17
32
|
plugins: string[];
|
|
18
|
-
|
|
33
|
+
instances: AdapterPluginInstance[];
|
|
19
34
|
envVars: Record<string, string>;
|
|
20
35
|
/** 所选适配器需要 database 插件(如 GitHub 订阅/OAuth 表) */
|
|
21
36
|
requiresDatabase?: boolean;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
// 适配器定义
|
|
25
|
-
interface AdapterDefinition {
|
|
40
|
+
export interface AdapterDefinition {
|
|
26
41
|
name: string;
|
|
27
42
|
value: string;
|
|
28
43
|
package: string;
|
|
@@ -38,7 +53,7 @@ interface AdapterDefinition {
|
|
|
38
53
|
fields: AdapterField[];
|
|
39
54
|
}
|
|
40
55
|
|
|
41
|
-
interface AdapterField {
|
|
56
|
+
export interface AdapterField {
|
|
42
57
|
key: string;
|
|
43
58
|
message: string;
|
|
44
59
|
type?: 'input' | 'password' | 'select';
|
|
@@ -46,6 +61,8 @@ interface AdapterField {
|
|
|
46
61
|
required?: boolean;
|
|
47
62
|
envKey?: string; // 如果是敏感信息,对应的环境变量名
|
|
48
63
|
choices?: { name: string; value: string }[];
|
|
64
|
+
/** 字段归属:endpoint 级(默认,进 endpoints[i])或 shared(顶层共享字段) */
|
|
65
|
+
scope?: 'endpoint' | 'shared';
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
const ADAPTERS: AdapterDefinition[] = [
|
|
@@ -56,7 +73,7 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
56
73
|
plugin: '@zhin.js/adapter-sandbox',
|
|
57
74
|
needsHttp: true,
|
|
58
75
|
description: '终端 + Remote Console 沙盒页,本地开发首选',
|
|
59
|
-
setupHint: '无需额外凭据;pnpm dev
|
|
76
|
+
setupHint: '无需额外凭据;pnpm dev(zhin runtime start)后可在 Console 沙盒页直接对话。',
|
|
60
77
|
fields: [],
|
|
61
78
|
},
|
|
62
79
|
{
|
|
@@ -70,6 +87,8 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
70
87
|
docUrl: adapterDocsUrl('icqq'),
|
|
71
88
|
fields: [
|
|
72
89
|
{ key: 'name', message: 'QQ 号(与 icqq login 时一致):', required: true, envKey: 'ICQQ_ACCOUNT' },
|
|
90
|
+
// schema 顶层 required: master(所有 endpoint 共享)
|
|
91
|
+
{ key: 'master', message: '主人 QQ 号(master,/approve 等管理命令):', required: true, scope: 'shared' },
|
|
73
92
|
],
|
|
74
93
|
},
|
|
75
94
|
{
|
|
@@ -143,6 +162,8 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
143
162
|
fields: [
|
|
144
163
|
{ key: 'appKey', message: 'App Key:', required: true, envKey: 'DINGTALK_APP_KEY' },
|
|
145
164
|
{ key: 'appSecret', message: 'App Secret:', required: true, type: 'password', envKey: 'DINGTALK_APP_SECRET' },
|
|
165
|
+
// schema endpoints[i] required 含 robotCode(主动发送 /robot/send 需要)
|
|
166
|
+
{ key: 'robotCode', message: 'Robot Code:', required: true, envKey: 'DINGTALK_ROBOT_CODE' },
|
|
146
167
|
{ key: 'webhookPath', message: 'Webhook 路径:', default: '/dingtalk/webhook' },
|
|
147
168
|
],
|
|
148
169
|
},
|
|
@@ -157,7 +178,24 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
157
178
|
fields: [
|
|
158
179
|
{ key: 'appId', message: 'App ID:', required: true, envKey: 'LARK_APP_ID' },
|
|
159
180
|
{ key: 'appSecret', message: 'App Secret:', required: true, type: 'password', envKey: 'LARK_APP_SECRET' },
|
|
160
|
-
|
|
181
|
+
// schema 顶层共享字段(endpoints[i] 不含 webhookPath)
|
|
182
|
+
{ key: 'webhookPath', message: 'Webhook 路径:', default: '/lark/webhook', scope: 'shared' },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'LINE',
|
|
187
|
+
value: 'line',
|
|
188
|
+
package: '@zhin.js/adapter-line',
|
|
189
|
+
plugin: '@zhin.js/adapter-line',
|
|
190
|
+
needsHttp: true,
|
|
191
|
+
description: 'LINE Messaging API',
|
|
192
|
+
setupHint: '在 LINE Developers Console 创建 Messaging API Channel;Webhook URL 需 HTTPS 公网可达,并关闭 Auto-reply。',
|
|
193
|
+
docUrl: adapterDocsUrl('line'),
|
|
194
|
+
fields: [
|
|
195
|
+
{ key: 'channelSecret', message: 'Channel Secret:', required: true, type: 'password', envKey: 'LINE_CHANNEL_SECRET' },
|
|
196
|
+
{ key: 'channelAccessToken', message: 'Channel Access Token:', required: true, type: 'password', envKey: 'LINE_CHANNEL_ACCESS_TOKEN' },
|
|
197
|
+
// schema 顶层共享字段(endpoints[i] 不含 webhookPath)
|
|
198
|
+
{ key: 'webhookPath', message: 'Webhook 路径:', default: '/line/webhook', scope: 'shared' },
|
|
161
199
|
],
|
|
162
200
|
},
|
|
163
201
|
{
|
|
@@ -167,11 +205,59 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
167
205
|
plugin: '@zhin.js/adapter-onebot11',
|
|
168
206
|
needsHttp: false,
|
|
169
207
|
description: 'OneBot v11(ws 正向 / wss 反向)',
|
|
170
|
-
setupHint: 'connection: ws 连 OneBot 实现;connection: wss
|
|
208
|
+
setupHint: 'connection: ws 连 OneBot 实现;connection: wss 反向连接(由内置 HTTP Host 承接)。',
|
|
171
209
|
docUrl: adapterDocsUrl('onebot11'),
|
|
172
210
|
configure: configureOneBot11Bot,
|
|
173
211
|
fields: [],
|
|
174
212
|
},
|
|
213
|
+
{
|
|
214
|
+
name: 'OneBot v12',
|
|
215
|
+
value: 'onebot12',
|
|
216
|
+
package: '@zhin.js/adapter-onebot12',
|
|
217
|
+
plugin: '@zhin.js/adapter-onebot12',
|
|
218
|
+
needsHttp: false,
|
|
219
|
+
description: 'OneBot v12(ws / webhook / wss)',
|
|
220
|
+
setupHint: 'connection: ws 连 OneBot 实现;webhook / wss 由内置 HTTP Host 承接。',
|
|
221
|
+
docUrl: adapterDocsUrl('onebot12'),
|
|
222
|
+
configure: configureOneBot12Bot,
|
|
223
|
+
fields: [],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'NapCat',
|
|
227
|
+
value: 'napcat',
|
|
228
|
+
package: '@zhin.js/adapter-napcat',
|
|
229
|
+
plugin: '@zhin.js/adapter-napcat',
|
|
230
|
+
needsHttp: false,
|
|
231
|
+
description: 'NapCat(OneBot 11 + NapCat 扩展)',
|
|
232
|
+
setupHint: 'connection: ws 连 NapCat(默认 ws://127.0.0.1:3001);wss / http 由内置 HTTP Host 承接。',
|
|
233
|
+
docUrl: adapterDocsUrl('napcat'),
|
|
234
|
+
configure: configureNapcatBot,
|
|
235
|
+
fields: [],
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: 'Milky',
|
|
239
|
+
value: 'milky',
|
|
240
|
+
package: '@zhin.js/adapter-milky',
|
|
241
|
+
plugin: '@zhin.js/adapter-milky',
|
|
242
|
+
needsHttp: false,
|
|
243
|
+
description: 'Milky 协议(ws / sse / webhook / wss)',
|
|
244
|
+
setupHint: 'connection: ws 连协议端 ws(s)://baseUrl/event;webhook / wss 由内置 HTTP Host 承接。',
|
|
245
|
+
docUrl: adapterDocsUrl('milky'),
|
|
246
|
+
configure: configureMilkyBot,
|
|
247
|
+
fields: [],
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'Satori',
|
|
251
|
+
value: 'satori',
|
|
252
|
+
package: '@zhin.js/adapter-satori',
|
|
253
|
+
plugin: '@zhin.js/adapter-satori',
|
|
254
|
+
needsHttp: false,
|
|
255
|
+
description: 'Satori 聊天协议(ws / webhook)',
|
|
256
|
+
setupHint: 'connection: ws 连 Satori SDK(默认 http://127.0.0.1:5140);webhook 由内置 HTTP Host 承接。',
|
|
257
|
+
docUrl: adapterDocsUrl('satori'),
|
|
258
|
+
configure: configureSatoriBot,
|
|
259
|
+
fields: [],
|
|
260
|
+
},
|
|
175
261
|
{
|
|
176
262
|
name: '微信公众号',
|
|
177
263
|
value: 'wechat-mp',
|
|
@@ -184,7 +270,26 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
184
270
|
{ key: 'appId', message: 'App ID:', required: true, envKey: 'WECHAT_APP_ID' },
|
|
185
271
|
{ key: 'appSecret', message: 'App Secret:', required: true, type: 'password', envKey: 'WECHAT_APP_SECRET' },
|
|
186
272
|
{ key: 'token', message: '验证 Token:', required: true, envKey: 'WECHAT_TOKEN' },
|
|
187
|
-
|
|
273
|
+
// schema 顶层共享字段名为 path(endpoints[i] 不含此项)
|
|
274
|
+
{ key: 'webhookPath', message: 'Webhook 路径:', default: '/wechat/webhook', scope: 'shared' },
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: '企业微信',
|
|
279
|
+
value: 'wecom',
|
|
280
|
+
package: '@zhin.js/adapter-wecom',
|
|
281
|
+
plugin: '@zhin.js/adapter-wecom',
|
|
282
|
+
needsHttp: true,
|
|
283
|
+
description: '企业微信自建应用',
|
|
284
|
+
setupHint: '管理后台创建自建应用并配置「接收消息」URL(需公网可达);Token / EncodingAESKey 与后台一致。',
|
|
285
|
+
docUrl: adapterDocsUrl('wecom'),
|
|
286
|
+
fields: [
|
|
287
|
+
{ key: 'corpId', message: 'Corp ID:', required: true, envKey: 'WECOM_CORP_ID' },
|
|
288
|
+
{ key: 'agentSecret', message: '应用 Secret:', required: true, type: 'password', envKey: 'WECOM_AGENT_SECRET' },
|
|
289
|
+
{ key: 'token', message: '回调 Token:', required: true, type: 'password', envKey: 'WECOM_TOKEN' },
|
|
290
|
+
{ key: 'encodingAESKey', message: 'EncodingAESKey(43 字符):', required: true, type: 'password', envKey: 'WECOM_AES_KEY' },
|
|
291
|
+
// schema 顶层共享字段(endpoints[i] 不含 webhookPath)
|
|
292
|
+
{ key: 'webhookPath', message: '回调路径:', default: '/wecom/callback', scope: 'shared' },
|
|
188
293
|
],
|
|
189
294
|
},
|
|
190
295
|
{
|
|
@@ -211,12 +316,24 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
211
316
|
plugin: '@zhin.js/adapter-github',
|
|
212
317
|
needsHttp: true,
|
|
213
318
|
description: 'GitHub Issue/PR 聊天 + App 认证',
|
|
214
|
-
setupHint: 'GitHub App 或 gh CLI;Webhook
|
|
319
|
+
setupHint: 'GitHub App 或 gh CLI;Webhook 由内置 HTTP Host 承接(默认 /github/webhook);需 database 存订阅与 OAuth。',
|
|
215
320
|
docUrl: adapterDocsUrl('github'),
|
|
216
321
|
requiresDatabase: true,
|
|
217
322
|
configure: configureGitHubEndpoint,
|
|
218
323
|
fields: [],
|
|
219
324
|
},
|
|
325
|
+
{
|
|
326
|
+
name: '微信 iLink(个人微信)',
|
|
327
|
+
value: 'weixin-ilink',
|
|
328
|
+
package: '@zhin.js/adapter-weixin-ilink',
|
|
329
|
+
plugin: '@zhin.js/adapter-weixin-ilink',
|
|
330
|
+
needsHttp: false,
|
|
331
|
+
description: '个人微信(iLink / ClawBot 灰度入口,长轮询,仅私聊)',
|
|
332
|
+
setupHint: '需最新版微信 + ClawBot 灰度资格;默认扫码绑定(bot_token 写入 .env),也可手动输入或放 data/weixin-ilink/<name>.json 侧车凭证文件。',
|
|
333
|
+
docUrl: adapterDocsUrl('weixin-ilink'),
|
|
334
|
+
configure: configureWeixinIlinkBot,
|
|
335
|
+
fields: [],
|
|
336
|
+
},
|
|
220
337
|
];
|
|
221
338
|
|
|
222
339
|
/**
|
|
@@ -243,7 +360,7 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
243
360
|
if (input.length === 0) return '至少选择一个适配器';
|
|
244
361
|
return true;
|
|
245
362
|
},
|
|
246
|
-
pageSize:
|
|
363
|
+
pageSize: 22,
|
|
247
364
|
}
|
|
248
365
|
]);
|
|
249
366
|
|
|
@@ -261,13 +378,13 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
261
378
|
for (const adapter of httpAdapters) {
|
|
262
379
|
console.log(chalk.gray(` • ${adapter.name.split(' (')[0]}`));
|
|
263
380
|
}
|
|
264
|
-
console.log(chalk.gray(' 开发时可先用 Sandbox;上线前请确保 http 端口(默认
|
|
381
|
+
console.log(chalk.gray(' 开发时可先用 Sandbox;上线前请确保 http 端口(默认 8068)可从平台回调。'));
|
|
265
382
|
}
|
|
266
383
|
|
|
267
384
|
const result: AdapterSetupResult = {
|
|
268
385
|
packages: [],
|
|
269
386
|
plugins: [],
|
|
270
|
-
|
|
387
|
+
instances: [],
|
|
271
388
|
envVars: {},
|
|
272
389
|
requiresDatabase: false,
|
|
273
390
|
};
|
|
@@ -287,10 +404,23 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
287
404
|
result.requiresDatabase = true;
|
|
288
405
|
}
|
|
289
406
|
|
|
290
|
-
if (adapterValue === 'sandbox')
|
|
407
|
+
if (adapterValue === 'sandbox') {
|
|
408
|
+
// 对齐 examples/test-bot:Console 沙盒页与本地调试共用的默认 Endpoint
|
|
409
|
+
result.instances.push({
|
|
410
|
+
package: adapterDef.package,
|
|
411
|
+
instanceKey: 'sandbox',
|
|
412
|
+
config: {
|
|
413
|
+
endpoints: [{ context: 'sandbox', name: 'sandbox-bot', owner: 'sandbox-user' }],
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
291
418
|
|
|
292
419
|
const hasConfig = adapterDef.configure || adapterDef.fields.length > 0;
|
|
293
|
-
if (!hasConfig)
|
|
420
|
+
if (!hasConfig) {
|
|
421
|
+
result.instances.push({ package: adapterDef.package, instanceKey: adapterDef.value, config: {} });
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
294
424
|
|
|
295
425
|
console.log('');
|
|
296
426
|
console.log(chalk.yellow(` 📝 配置 ${adapterDef.name.split(' (')[0]} Bot`));
|
|
@@ -304,7 +434,7 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
304
434
|
|
|
305
435
|
if (adapterDef.configure) {
|
|
306
436
|
let markedDb = false;
|
|
307
|
-
const
|
|
437
|
+
const instanceConfig = await adapterDef.configure({
|
|
308
438
|
envVars: result.envVars,
|
|
309
439
|
markRequiresDatabase: () => {
|
|
310
440
|
markedDb = true;
|
|
@@ -312,13 +442,11 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
312
442
|
},
|
|
313
443
|
});
|
|
314
444
|
if (markedDb) result.requiresDatabase = true;
|
|
315
|
-
result.
|
|
445
|
+
result.instances.push({ package: adapterDef.package, instanceKey: adapterDef.value, config: instanceConfig });
|
|
316
446
|
continue;
|
|
317
447
|
}
|
|
318
448
|
|
|
319
|
-
const
|
|
320
|
-
context: adapterDef.value,
|
|
321
|
-
};
|
|
449
|
+
const fieldValues: Record<string, any> = {};
|
|
322
450
|
|
|
323
451
|
for (const field of adapterDef.fields) {
|
|
324
452
|
const promptConfig: any = {
|
|
@@ -344,127 +472,108 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
344
472
|
if (field.envKey) {
|
|
345
473
|
// 敏感信息存 .env,配置引用环境变量
|
|
346
474
|
result.envVars[field.envKey] = value || '';
|
|
347
|
-
|
|
475
|
+
fieldValues[field.key] = `\${${field.envKey}}`;
|
|
348
476
|
} else {
|
|
349
|
-
|
|
477
|
+
fieldValues[field.key] = value;
|
|
350
478
|
}
|
|
351
479
|
}
|
|
352
480
|
|
|
353
|
-
result.
|
|
481
|
+
result.instances.push({
|
|
482
|
+
package: adapterDef.package,
|
|
483
|
+
instanceKey: adapterDef.value,
|
|
484
|
+
config: buildFieldBasedInstanceConfig(adapterDef, fieldValues),
|
|
485
|
+
});
|
|
354
486
|
}
|
|
355
487
|
|
|
356
488
|
return result;
|
|
357
489
|
}
|
|
358
490
|
|
|
359
491
|
/**
|
|
360
|
-
*
|
|
492
|
+
* 字段式适配器(无自定义 configure)的实例配置整形,对齐各插件 schema.json 新格式:
|
|
493
|
+
* - endpoint 级字段(name + 凭据等,字段默认 scope: 'endpoint')进 `endpoints: [{ name, ... }]`
|
|
494
|
+
* - scope: 'shared' 的字段(如 lark/wechat-mp 的 webhookPath)留顶层
|
|
495
|
+
* - wechat-mp:顶层字段名为 path(向导沿用 webhookPath 提问)
|
|
496
|
+
* - email:smtp/imap 平铺字段 → endpoint 内 smtp/imap 嵌套对象
|
|
497
|
+
* - 未单独询问 name 的适配器用 `<adapter>-bot` 兜底
|
|
361
498
|
*/
|
|
362
|
-
export function
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
499
|
+
export function buildFieldBasedInstanceConfig(adapter: AdapterDefinition, values: Record<string, any>): Record<string, unknown> {
|
|
500
|
+
if (adapter.value === 'email') {
|
|
501
|
+
const user = values.user ?? '';
|
|
502
|
+
const password = values.password ?? '';
|
|
503
|
+
return {
|
|
504
|
+
endpoints: [{
|
|
505
|
+
name: 'email-bot',
|
|
506
|
+
smtp: {
|
|
507
|
+
host: values.smtpHost,
|
|
508
|
+
port: Number(values.smtpPort) || 465,
|
|
509
|
+
secure: true,
|
|
510
|
+
auth: { user, pass: password },
|
|
511
|
+
},
|
|
512
|
+
imap: {
|
|
513
|
+
host: values.imapHost,
|
|
514
|
+
port: Number(values.imapPort) || 993,
|
|
515
|
+
tls: true,
|
|
516
|
+
user,
|
|
517
|
+
password,
|
|
518
|
+
},
|
|
519
|
+
}],
|
|
520
|
+
};
|
|
369
521
|
}
|
|
370
|
-
return lines.join('\n');
|
|
371
|
-
}
|
|
372
522
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
for (const [subKey, subValue] of Object.entries(value)) {
|
|
383
|
-
appendYamlValue(lines, subKey, subValue, indent + 1);
|
|
523
|
+
const endpoint: Record<string, unknown> = {};
|
|
524
|
+
const shared: Record<string, unknown> = {};
|
|
525
|
+
for (const field of adapter.fields) {
|
|
526
|
+
const value = values[field.key];
|
|
527
|
+
if (value === undefined) continue;
|
|
528
|
+
if (field.scope === 'shared') {
|
|
529
|
+
shared[field.key] = value;
|
|
530
|
+
} else {
|
|
531
|
+
endpoint[field.key] = value;
|
|
384
532
|
}
|
|
385
|
-
return;
|
|
386
533
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
for (const item of value) {
|
|
391
|
-
if (typeof item === 'string') {
|
|
392
|
-
lines.push(`${pad} - "${item}"`);
|
|
393
|
-
} else {
|
|
394
|
-
lines.push(`${pad} - ${item}`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
return;
|
|
534
|
+
if (adapter.value === 'wechat-mp' && shared.webhookPath !== undefined) {
|
|
535
|
+
shared.path = shared.webhookPath;
|
|
536
|
+
delete shared.webhookPath;
|
|
398
537
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
lines.push(`${pad}${key}: ${value}`);
|
|
402
|
-
return;
|
|
538
|
+
if (!endpoint.name) {
|
|
539
|
+
endpoint.name = `${adapter.value}-bot`;
|
|
403
540
|
}
|
|
404
|
-
|
|
405
|
-
lines.push(`${pad}${key}: ${value}`);
|
|
541
|
+
return { ...shared, endpoints: [endpoint] };
|
|
406
542
|
}
|
|
407
543
|
|
|
408
544
|
/**
|
|
409
|
-
*
|
|
545
|
+
* 获取适配器配置需要写入 .env 的环境变量文本
|
|
410
546
|
*/
|
|
411
|
-
export function
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
# Sandbox:Remote Console 打开「沙盒」页并通过 WebSocket 连接时自动创建 Endpoint
|
|
416
|
-
endpoints: []
|
|
417
|
-
`;
|
|
418
|
-
}
|
|
547
|
+
export function generateAdapterEnvVars(result: AdapterSetupResult): string {
|
|
548
|
+
const entries = Object.entries(result.envVars);
|
|
549
|
+
if (entries.length === 0) return '';
|
|
419
550
|
|
|
420
|
-
const lines: string[] = ['', '
|
|
421
|
-
for (const
|
|
422
|
-
|
|
423
|
-
lines.push(` - context: ${context}`);
|
|
424
|
-
for (const [key, value] of Object.entries(config)) {
|
|
425
|
-
appendYamlValue(lines, key, value, 2);
|
|
426
|
-
}
|
|
551
|
+
const lines: string[] = ['', '# 适配器配置'];
|
|
552
|
+
for (const [key, value] of entries) {
|
|
553
|
+
lines.push(`${key}=${value}`);
|
|
427
554
|
}
|
|
428
|
-
|
|
429
555
|
return lines.join('\n');
|
|
430
556
|
}
|
|
431
557
|
|
|
432
558
|
/**
|
|
433
|
-
*
|
|
559
|
+
* 汇总适配器实例为 zhin.config `plugins.<instanceKey>` 配置映射(新 Plugin Runtime 格式)
|
|
434
560
|
*/
|
|
435
|
-
export function
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
for (const entry of result.endpoints) {
|
|
440
|
-
lines.push('[[endpoints]]');
|
|
441
|
-
for (const [key, value] of Object.entries(entry)) {
|
|
442
|
-
if (typeof value === 'string') {
|
|
443
|
-
lines.push(`${key} = "${escapeTomlString(value)}"`);
|
|
444
|
-
} else {
|
|
445
|
-
lines.push(`${key} = ${JSON.stringify(value)}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
lines.push('');
|
|
449
|
-
}
|
|
450
|
-
return lines.join('\n');
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function escapeTomlString(value: string): string {
|
|
454
|
-
let out = '';
|
|
455
|
-
for (const ch of value) {
|
|
456
|
-
if (ch === '"' || ch === '\\') out += `\\${ch}`;
|
|
457
|
-
else out += ch;
|
|
561
|
+
export function collectAdapterPluginConfigs(result: AdapterSetupResult): Record<string, Record<string, unknown>> {
|
|
562
|
+
const plugins: Record<string, Record<string, unknown>> = {};
|
|
563
|
+
for (const instance of result.instances) {
|
|
564
|
+
plugins[instance.instanceKey] = instance.config;
|
|
458
565
|
}
|
|
459
|
-
return
|
|
566
|
+
return plugins;
|
|
460
567
|
}
|
|
461
568
|
|
|
462
569
|
/**
|
|
463
|
-
*
|
|
570
|
+
* 汇总适配器实例为 package.json `zhin.plugins` 清单条目
|
|
464
571
|
*/
|
|
465
|
-
export function
|
|
466
|
-
|
|
467
|
-
|
|
572
|
+
export function collectAdapterPluginManifest(result: AdapterSetupResult): Array<{ package: string; instanceKey: string }> {
|
|
573
|
+
return result.instances.map((instance) => ({
|
|
574
|
+
package: instance.package,
|
|
575
|
+
instanceKey: instance.instanceKey,
|
|
576
|
+
}));
|
|
468
577
|
}
|
|
469
578
|
|
|
470
579
|
/**
|
|
@@ -472,21 +581,24 @@ export function generateEndpointsConfigJSON(result: AdapterSetupResult): string
|
|
|
472
581
|
*/
|
|
473
582
|
export function getAdapterSetupNotes(result: AdapterSetupResult): string[] {
|
|
474
583
|
const notes: string[] = [];
|
|
475
|
-
for (const
|
|
476
|
-
const context =
|
|
584
|
+
for (const instance of result.instances) {
|
|
585
|
+
const context = instance.instanceKey;
|
|
586
|
+
const entry = instance.config;
|
|
477
587
|
if (context === 'telegram' && entry.polling === false) {
|
|
478
|
-
notes.push('Telegram Webhook: 确保 HTTPS 域名可从公网访问,且
|
|
588
|
+
notes.push('Telegram Webhook: 确保 HTTPS 域名可从公网访问,且 http 端口已放行');
|
|
479
589
|
}
|
|
480
590
|
if (context === 'telegram' && entry.polling !== false) {
|
|
481
591
|
notes.push('Telegram polling: 本地开发可直接 pnpm dev,无需公网');
|
|
482
592
|
}
|
|
483
593
|
if (context === 'github') {
|
|
484
|
-
|
|
485
|
-
|
|
594
|
+
const endpoints = Array.isArray(entry.endpoints) ? entry.endpoints as Array<Record<string, unknown>> : [];
|
|
595
|
+
const firstEndpoint = endpoints[0] ?? {};
|
|
596
|
+
if (firstEndpoint.webhook_secret) {
|
|
597
|
+
notes.push('GitHub Webhook: App 设置 URL 为 https://<域名>/github/webhook');
|
|
486
598
|
} else {
|
|
487
599
|
notes.push('GitHub polling: 无 Webhook 时将轮询 Events API(默认 60s)');
|
|
488
600
|
}
|
|
489
|
-
if (
|
|
601
|
+
if (firstEndpoint.app_id) {
|
|
490
602
|
notes.push('GitHub App: 私钥路径需存在,且 App 已安装到目标仓库');
|
|
491
603
|
} else {
|
|
492
604
|
notes.push('GitHub gh CLI: 运行 gh auth login 完成认证');
|