@zhin.js/scaffold-wizard 0.1.2 → 0.1.4
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/lib/adapter-configurers.d.ts +7 -7
- package/lib/adapter-configurers.d.ts.map +1 -1
- package/lib/adapter-configurers.js +55 -55
- package/lib/adapter-configurers.js.map +1 -1
- package/lib/adapter.d.ts +8 -8
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +42 -42
- package/lib/adapter.js.map +1 -1
- package/lib/ai.d.ts.map +1 -1
- package/lib/ai.js +17 -2
- package/lib/ai.js.map +1 -1
- package/lib/apply.js +3 -3
- package/lib/apply.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter-configurers.ts +58 -58
- package/src/adapter.ts +47 -47
- package/src/ai.ts +18 -2
- package/src/apply.ts +3 -3
- package/src/index.ts +3 -3
|
@@ -13,19 +13,19 @@ export const ADAPTERS_ESSENTIALS_URL = 'https://zhin.js.org/essentials/adapters'
|
|
|
13
13
|
/** 各适配器文档页(与 docs/adapters/*.md 同步) */
|
|
14
14
|
export const adapterDocsUrl = (slug: string) => `https://zhin.js.org/adapters/${slug}`;
|
|
15
15
|
|
|
16
|
-
export interface
|
|
16
|
+
export interface EndpointConfigureContext {
|
|
17
17
|
envVars: Record<string, string>;
|
|
18
18
|
markRequiresDatabase: () => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export async function
|
|
21
|
+
export async function configureTelegramEndpoint(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
22
22
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('telegram')));
|
|
23
23
|
|
|
24
|
-
const {
|
|
24
|
+
const { endpointName } = await inquirer.prompt([
|
|
25
25
|
{
|
|
26
26
|
type: 'input',
|
|
27
|
-
name: '
|
|
28
|
-
message: '
|
|
27
|
+
name: 'endpointName',
|
|
28
|
+
message: ' Endpoint 标识名称:',
|
|
29
29
|
default: 'telegram-bot',
|
|
30
30
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
31
31
|
},
|
|
@@ -35,7 +35,7 @@ export async function configureTelegramBot(ctx: BotConfigureContext): Promise<Re
|
|
|
35
35
|
{
|
|
36
36
|
type: 'password',
|
|
37
37
|
name: 'token',
|
|
38
|
-
message: ' Telegram
|
|
38
|
+
message: ' Telegram Endpoint Token(@BotFather 获取):',
|
|
39
39
|
validate: (v: string) => (v.trim() ? true : 'Token 不能为空'),
|
|
40
40
|
},
|
|
41
41
|
]);
|
|
@@ -60,9 +60,9 @@ export async function configureTelegramBot(ctx: BotConfigureContext): Promise<Re
|
|
|
60
60
|
},
|
|
61
61
|
]);
|
|
62
62
|
|
|
63
|
-
const
|
|
63
|
+
const endpointConfig: Record<string, unknown> = {
|
|
64
64
|
context: 'telegram',
|
|
65
|
-
name:
|
|
65
|
+
name: endpointName.trim(),
|
|
66
66
|
token: '${TELEGRAM_TOKEN}',
|
|
67
67
|
polling: transport === 'polling',
|
|
68
68
|
};
|
|
@@ -70,7 +70,7 @@ export async function configureTelegramBot(ctx: BotConfigureContext): Promise<Re
|
|
|
70
70
|
if (transport === 'webhook') {
|
|
71
71
|
console.log('');
|
|
72
72
|
console.log(chalk.yellow(' Webhook 前置条件:'));
|
|
73
|
-
console.log(chalk.gray(' •
|
|
73
|
+
console.log(chalk.gray(' • Endpoint 进程需绑定可被 Telegram 访问的 HTTPS 域名(有效 TLS 证书)'));
|
|
74
74
|
console.log(chalk.gray(' • 在 @BotFather 可配合 setWebhook,或由 Telegraf 在 launch 时注册'));
|
|
75
75
|
console.log(chalk.gray(' • 防火墙/反向代理需放行 webhook.port(默认由 Telegraf 监听)'));
|
|
76
76
|
|
|
@@ -104,26 +104,26 @@ export async function configureTelegramBot(ctx: BotConfigureContext): Promise<Re
|
|
|
104
104
|
},
|
|
105
105
|
]);
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
endpointConfig.polling = false;
|
|
108
|
+
endpointConfig.webhook = {
|
|
109
109
|
domain: webhook.domain.trim(),
|
|
110
110
|
path: webhook.path.trim(),
|
|
111
111
|
port: parseInt(webhook.port, 10),
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
return
|
|
115
|
+
return endpointConfig;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export async function
|
|
118
|
+
export async function configureGitHubEndpoint(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
119
119
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('github')));
|
|
120
120
|
ctx.markRequiresDatabase();
|
|
121
121
|
|
|
122
|
-
const {
|
|
122
|
+
const { endpointName } = await inquirer.prompt([
|
|
123
123
|
{
|
|
124
124
|
type: 'input',
|
|
125
|
-
name: '
|
|
126
|
-
message: '
|
|
125
|
+
name: 'endpointName',
|
|
126
|
+
message: ' Endpoint 标识名称:',
|
|
127
127
|
default: 'github-bot',
|
|
128
128
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
129
129
|
},
|
|
@@ -148,9 +148,9 @@ export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Reco
|
|
|
148
148
|
},
|
|
149
149
|
]);
|
|
150
150
|
|
|
151
|
-
const
|
|
151
|
+
const endpointConfig: Record<string, unknown> = {
|
|
152
152
|
context: 'github',
|
|
153
|
-
name:
|
|
153
|
+
name: endpointName.trim(),
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
if (authMode === 'github_app') {
|
|
@@ -173,8 +173,8 @@ export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Reco
|
|
|
173
173
|
]);
|
|
174
174
|
|
|
175
175
|
ctx.envVars.GITHUB_APP_ID = appConfig.app_id.trim();
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
endpointConfig.app_id = '${GITHUB_APP_ID}';
|
|
177
|
+
endpointConfig.private_key = appConfig.private_key.trim();
|
|
178
178
|
} else {
|
|
179
179
|
console.log(chalk.gray(' 请确保已执行 gh auth login,且 gh 在 PATH 中'));
|
|
180
180
|
}
|
|
@@ -221,8 +221,8 @@ export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Reco
|
|
|
221
221
|
]);
|
|
222
222
|
|
|
223
223
|
ctx.envVars.GITHUB_WEBHOOK_SECRET = webhook.webhook_secret;
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
endpointConfig.webhook_secret = '${GITHUB_WEBHOOK_SECRET}';
|
|
225
|
+
endpointConfig.webhook_path = webhook.webhook_path.trim();
|
|
226
226
|
} else {
|
|
227
227
|
const { poll_interval } = await inquirer.prompt([
|
|
228
228
|
{
|
|
@@ -237,7 +237,7 @@ export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Reco
|
|
|
237
237
|
},
|
|
238
238
|
},
|
|
239
239
|
]);
|
|
240
|
-
|
|
240
|
+
endpointConfig.poll_interval = parseInt(poll_interval, 10);
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
const { enableMcp } = await inquirer.prompt([
|
|
@@ -261,10 +261,10 @@ export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Reco
|
|
|
261
261
|
ctx.envVars.GITHUB_PERSONAL_ACCESS_TOKEN = pat;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
return
|
|
264
|
+
return endpointConfig;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
export async function configureOneBot11Bot(ctx:
|
|
267
|
+
export async function configureOneBot11Bot(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
268
268
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('onebot11')));
|
|
269
269
|
|
|
270
270
|
const { connection } = await inquirer.prompt([
|
|
@@ -273,27 +273,27 @@ export async function configureOneBot11Bot(ctx: BotConfigureContext): Promise<Re
|
|
|
273
273
|
name: 'connection',
|
|
274
274
|
message: ' 连接方式:',
|
|
275
275
|
choices: [
|
|
276
|
-
{ name: '正向 WebSocket (ws) — 本
|
|
276
|
+
{ name: '正向 WebSocket (ws) — 本 Endpoint 连接 OneBot 实现', value: 'ws' },
|
|
277
277
|
{ name: '反向 WebSocket (wss) — OneBot 实现连本 Bot(需 host-router)', value: 'wss' },
|
|
278
278
|
],
|
|
279
279
|
default: 'ws',
|
|
280
280
|
},
|
|
281
281
|
]);
|
|
282
282
|
|
|
283
|
-
const {
|
|
283
|
+
const { endpointName } = await inquirer.prompt([
|
|
284
284
|
{
|
|
285
285
|
type: 'input',
|
|
286
|
-
name: '
|
|
287
|
-
message: '
|
|
286
|
+
name: 'endpointName',
|
|
287
|
+
message: ' Endpoint 标识名称:',
|
|
288
288
|
default: 'onebot-bot',
|
|
289
289
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
290
290
|
},
|
|
291
291
|
]);
|
|
292
292
|
|
|
293
|
-
const
|
|
293
|
+
const endpointConfig: Record<string, unknown> = {
|
|
294
294
|
context: 'onebot11',
|
|
295
295
|
connection,
|
|
296
|
-
name:
|
|
296
|
+
name: endpointName.trim(),
|
|
297
297
|
};
|
|
298
298
|
|
|
299
299
|
if (connection === 'ws') {
|
|
@@ -306,7 +306,7 @@ export async function configureOneBot11Bot(ctx: BotConfigureContext): Promise<Re
|
|
|
306
306
|
validate: (v: string) => (v.trim() ? true : '地址不能为空'),
|
|
307
307
|
},
|
|
308
308
|
]);
|
|
309
|
-
|
|
309
|
+
endpointConfig.url = url.trim();
|
|
310
310
|
} else {
|
|
311
311
|
console.log(chalk.gray(' 反向 WS 需启用 @zhin.js/host-router,OneBot 实现配置连接到此 Bot'));
|
|
312
312
|
const { path: wsPath } = await inquirer.prompt([
|
|
@@ -318,7 +318,7 @@ export async function configureOneBot11Bot(ctx: BotConfigureContext): Promise<Re
|
|
|
318
318
|
validate: (v: string) => (v.trim() ? true : '路径不能为空'),
|
|
319
319
|
},
|
|
320
320
|
]);
|
|
321
|
-
|
|
321
|
+
endpointConfig.path = wsPath.trim();
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
const { accessToken } = await inquirer.prompt([
|
|
@@ -331,20 +331,20 @@ export async function configureOneBot11Bot(ctx: BotConfigureContext): Promise<Re
|
|
|
331
331
|
]);
|
|
332
332
|
if (accessToken?.trim()) {
|
|
333
333
|
ctx.envVars.ONEBOT11_ACCESS_TOKEN = accessToken.trim();
|
|
334
|
-
|
|
334
|
+
endpointConfig.access_token = '${ONEBOT11_ACCESS_TOKEN}';
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
return
|
|
337
|
+
return endpointConfig;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
export async function configureQQBot(ctx:
|
|
340
|
+
export async function configureQQBot(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
341
341
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('qq')));
|
|
342
342
|
|
|
343
343
|
const credentials = await inquirer.prompt([
|
|
344
344
|
{
|
|
345
345
|
type: 'input',
|
|
346
346
|
name: 'name',
|
|
347
|
-
message: '
|
|
347
|
+
message: ' Endpoint 标识名称:',
|
|
348
348
|
default: 'qq-bot',
|
|
349
349
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
350
350
|
},
|
|
@@ -403,7 +403,7 @@ export async function configureQQBot(ctx: BotConfigureContext): Promise<Record<s
|
|
|
403
403
|
};
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
export async function
|
|
406
|
+
export async function configureDiscordEndpoint(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
407
407
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('discord')));
|
|
408
408
|
|
|
409
409
|
const { connection } = await inquirer.prompt([
|
|
@@ -419,27 +419,27 @@ export async function configureDiscordBot(ctx: BotConfigureContext): Promise<Rec
|
|
|
419
419
|
},
|
|
420
420
|
]);
|
|
421
421
|
|
|
422
|
-
const {
|
|
422
|
+
const { endpointName, token } = await inquirer.prompt([
|
|
423
423
|
{
|
|
424
424
|
type: 'input',
|
|
425
|
-
name: '
|
|
426
|
-
message: '
|
|
425
|
+
name: 'endpointName',
|
|
426
|
+
message: ' Endpoint 标识名称:',
|
|
427
427
|
default: 'discord-bot',
|
|
428
428
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
429
429
|
},
|
|
430
430
|
{
|
|
431
431
|
type: 'password',
|
|
432
432
|
name: 'token',
|
|
433
|
-
message: '
|
|
433
|
+
message: ' Endpoint Token:',
|
|
434
434
|
validate: (v: string) => (v.trim() ? true : 'Token 不能为空'),
|
|
435
435
|
},
|
|
436
436
|
]);
|
|
437
437
|
ctx.envVars.DISCORD_TOKEN = token;
|
|
438
438
|
|
|
439
|
-
const
|
|
439
|
+
const endpointConfig: Record<string, unknown> = {
|
|
440
440
|
context: 'discord',
|
|
441
441
|
connection,
|
|
442
|
-
name:
|
|
442
|
+
name: endpointName.trim(),
|
|
443
443
|
token: '${DISCORD_TOKEN}',
|
|
444
444
|
};
|
|
445
445
|
|
|
@@ -465,15 +465,15 @@ export async function configureDiscordBot(ctx: BotConfigureContext): Promise<Rec
|
|
|
465
465
|
default: '/discord/interactions',
|
|
466
466
|
},
|
|
467
467
|
]);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
468
|
+
endpointConfig.applicationId = interactions.applicationId.trim();
|
|
469
|
+
endpointConfig.publicKey = interactions.publicKey.trim();
|
|
470
|
+
endpointConfig.interactionsPath = interactions.interactionsPath.trim();
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
return
|
|
473
|
+
return endpointConfig;
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
export async function
|
|
476
|
+
export async function configureSlackEndpoint(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
|
|
477
477
|
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('slack')));
|
|
478
478
|
|
|
479
479
|
const { socketMode } = await inquirer.prompt([
|
|
@@ -489,18 +489,18 @@ export async function configureSlackBot(ctx: BotConfigureContext): Promise<Recor
|
|
|
489
489
|
},
|
|
490
490
|
]);
|
|
491
491
|
|
|
492
|
-
const {
|
|
492
|
+
const { endpointName, token, signingSecret } = await inquirer.prompt([
|
|
493
493
|
{
|
|
494
494
|
type: 'input',
|
|
495
|
-
name: '
|
|
496
|
-
message: '
|
|
495
|
+
name: 'endpointName',
|
|
496
|
+
message: ' Endpoint 标识名称:',
|
|
497
497
|
default: 'slack-bot',
|
|
498
498
|
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
499
499
|
},
|
|
500
500
|
{
|
|
501
501
|
type: 'password',
|
|
502
502
|
name: 'token',
|
|
503
|
-
message: '
|
|
503
|
+
message: ' Endpoint Token (xoxb-...):',
|
|
504
504
|
validate: (v: string) => (v.trim() ? true : '不能为空'),
|
|
505
505
|
},
|
|
506
506
|
{
|
|
@@ -514,9 +514,9 @@ export async function configureSlackBot(ctx: BotConfigureContext): Promise<Recor
|
|
|
514
514
|
ctx.envVars.SLACK_BOT_TOKEN = token;
|
|
515
515
|
ctx.envVars.SLACK_SIGNING_SECRET = signingSecret;
|
|
516
516
|
|
|
517
|
-
const
|
|
517
|
+
const endpointConfig: Record<string, unknown> = {
|
|
518
518
|
context: 'slack',
|
|
519
|
-
name:
|
|
519
|
+
name: endpointName.trim(),
|
|
520
520
|
token: '${SLACK_BOT_TOKEN}',
|
|
521
521
|
signingSecret: '${SLACK_SIGNING_SECRET}',
|
|
522
522
|
socketMode: socketMode === 'true',
|
|
@@ -532,7 +532,7 @@ export async function configureSlackBot(ctx: BotConfigureContext): Promise<Recor
|
|
|
532
532
|
},
|
|
533
533
|
]);
|
|
534
534
|
ctx.envVars.SLACK_APP_TOKEN = appToken;
|
|
535
|
-
|
|
535
|
+
endpointConfig.appToken = '${SLACK_APP_TOKEN}';
|
|
536
536
|
} else {
|
|
537
537
|
console.log(chalk.yellow(' HTTP 模式需公网可达并配置 Slack Event Subscriptions URL'));
|
|
538
538
|
const { port } = await inquirer.prompt([
|
|
@@ -543,8 +543,8 @@ export async function configureSlackBot(ctx: BotConfigureContext): Promise<Recor
|
|
|
543
543
|
default: '3000',
|
|
544
544
|
},
|
|
545
545
|
]);
|
|
546
|
-
|
|
546
|
+
endpointConfig.port = parseInt(port, 10);
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
-
return
|
|
549
|
+
return endpointConfig;
|
|
550
550
|
}
|
package/src/adapter.ts
CHANGED
|
@@ -3,18 +3,18 @@ import chalk from 'chalk';
|
|
|
3
3
|
import {
|
|
4
4
|
ADAPTERS_DOCS_URL,
|
|
5
5
|
adapterDocsUrl,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
configureDiscordEndpoint,
|
|
7
|
+
configureGitHubEndpoint,
|
|
8
8
|
configureOneBot11Bot,
|
|
9
9
|
configureQQBot,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
configureSlackEndpoint,
|
|
11
|
+
configureTelegramEndpoint,
|
|
12
12
|
} from './adapter-configurers.js';
|
|
13
13
|
|
|
14
14
|
export interface AdapterSetupResult {
|
|
15
15
|
packages: string[];
|
|
16
16
|
plugins: string[];
|
|
17
|
-
|
|
17
|
+
endpoints: Array<Record<string, any>>;
|
|
18
18
|
envVars: Record<string, string>;
|
|
19
19
|
/** 所选适配器需要 database 插件(如 GitHub 订阅/OAuth 表) */
|
|
20
20
|
requiresDatabase?: boolean;
|
|
@@ -33,7 +33,7 @@ interface AdapterDefinition {
|
|
|
33
33
|
/** 文档:zhin.js.org/adapters/<slug>;索引见 ADAPTERS_DOCS_URL */
|
|
34
34
|
docUrl?: string;
|
|
35
35
|
requiresDatabase?: boolean;
|
|
36
|
-
configure?: (ctx: import('./adapter-configurers.js').
|
|
36
|
+
configure?: (ctx: import('./adapter-configurers.js').EndpointConfigureContext) => Promise<Record<string, unknown>>;
|
|
37
37
|
fields: AdapterField[];
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -89,10 +89,10 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
89
89
|
package: '@zhin.js/adapter-kook',
|
|
90
90
|
plugin: '@zhin.js/adapter-kook',
|
|
91
91
|
needsHttp: true,
|
|
92
|
-
description: 'KOOK 语音平台
|
|
93
|
-
setupHint: '在 KOOK 开发者中心创建
|
|
92
|
+
description: 'KOOK 语音平台 Endpoint',
|
|
93
|
+
setupHint: '在 KOOK 开发者中心创建 Endpoint 并复制 Token;需配置 Webhook 回调地址(公网或内网穿透)。',
|
|
94
94
|
fields: [
|
|
95
|
-
{ key: 'token', message: 'KOOK
|
|
95
|
+
{ key: 'token', message: 'KOOK Endpoint Token:', required: true, type: 'password', envKey: 'KOOK_TOKEN' },
|
|
96
96
|
],
|
|
97
97
|
},
|
|
98
98
|
{
|
|
@@ -104,7 +104,7 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
104
104
|
description: 'Discord(Gateway 或 Interactions)',
|
|
105
105
|
setupHint: 'Developer Portal 创建 Bot;Gateway 用 Token,Interactions 需 Application ID + Public Key。',
|
|
106
106
|
docUrl: adapterDocsUrl('discord'),
|
|
107
|
-
configure:
|
|
107
|
+
configure: configureDiscordEndpoint,
|
|
108
108
|
fields: [],
|
|
109
109
|
},
|
|
110
110
|
{
|
|
@@ -116,7 +116,7 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
116
116
|
description: 'Telegram Bot(polling / webhook 可选)',
|
|
117
117
|
setupHint: '@BotFather 获取 Token;polling 适合本地,webhook 需 HTTPS 公网域名。',
|
|
118
118
|
docUrl: adapterDocsUrl('telegram'),
|
|
119
|
-
configure:
|
|
119
|
+
configure: configureTelegramEndpoint,
|
|
120
120
|
fields: [],
|
|
121
121
|
},
|
|
122
122
|
{
|
|
@@ -128,7 +128,7 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
128
128
|
description: 'Slack(Socket Mode 或 HTTP Events)',
|
|
129
129
|
setupHint: 'Socket Mode 无需公网;HTTP 模式需配置 Event Subscriptions URL。',
|
|
130
130
|
docUrl: adapterDocsUrl('slack'),
|
|
131
|
-
configure:
|
|
131
|
+
configure: configureSlackEndpoint,
|
|
132
132
|
fields: [],
|
|
133
133
|
},
|
|
134
134
|
{
|
|
@@ -213,19 +213,19 @@ const ADAPTERS: AdapterDefinition[] = [
|
|
|
213
213
|
setupHint: 'GitHub App 或 gh CLI;Webhook 用 /pub/github/webhook;需 database 存订阅与 OAuth。',
|
|
214
214
|
docUrl: adapterDocsUrl('github'),
|
|
215
215
|
requiresDatabase: true,
|
|
216
|
-
configure:
|
|
216
|
+
configure: configureGitHubEndpoint,
|
|
217
217
|
fields: [],
|
|
218
218
|
},
|
|
219
219
|
];
|
|
220
220
|
|
|
221
221
|
/**
|
|
222
|
-
* 适配器选择与
|
|
222
|
+
* 适配器选择与 Endpoint 配置引导向导
|
|
223
223
|
*/
|
|
224
224
|
export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
225
225
|
console.log('');
|
|
226
226
|
console.log(chalk.blue('🔌 配置聊天适配器'));
|
|
227
227
|
console.log(chalk.gray(' Sandbox 适合本地调试;生产环境请额外勾选目标 IM 平台。'));
|
|
228
|
-
console.log(chalk.gray(' 带 Webhook 的适配器需要
|
|
228
|
+
console.log(chalk.gray(' 带 Webhook 的适配器需要 Endpoint 可被公网访问(或使用内网穿透)。'));
|
|
229
229
|
|
|
230
230
|
const { selectedAdapters } = await inquirer.prompt([
|
|
231
231
|
{
|
|
@@ -266,7 +266,7 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
266
266
|
const result: AdapterSetupResult = {
|
|
267
267
|
packages: [],
|
|
268
268
|
plugins: [],
|
|
269
|
-
|
|
269
|
+
endpoints: [],
|
|
270
270
|
envVars: {},
|
|
271
271
|
requiresDatabase: false,
|
|
272
272
|
};
|
|
@@ -303,7 +303,7 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
303
303
|
|
|
304
304
|
if (adapterDef.configure) {
|
|
305
305
|
let markedDb = false;
|
|
306
|
-
const
|
|
306
|
+
const endpointConfig = await adapterDef.configure({
|
|
307
307
|
envVars: result.envVars,
|
|
308
308
|
markRequiresDatabase: () => {
|
|
309
309
|
markedDb = true;
|
|
@@ -311,11 +311,11 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
311
311
|
},
|
|
312
312
|
});
|
|
313
313
|
if (markedDb) result.requiresDatabase = true;
|
|
314
|
-
result.
|
|
314
|
+
result.endpoints.push(endpointConfig);
|
|
315
315
|
continue;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
const
|
|
318
|
+
const endpointConfig: Record<string, any> = {
|
|
319
319
|
context: adapterDef.value,
|
|
320
320
|
};
|
|
321
321
|
|
|
@@ -343,13 +343,13 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
|
|
|
343
343
|
if (field.envKey) {
|
|
344
344
|
// 敏感信息存 .env,配置引用环境变量
|
|
345
345
|
result.envVars[field.envKey] = value || '';
|
|
346
|
-
|
|
346
|
+
endpointConfig[field.key] = `\${${field.envKey}}`;
|
|
347
347
|
} else {
|
|
348
|
-
|
|
348
|
+
endpointConfig[field.key] = value;
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
result.
|
|
352
|
+
result.endpoints.push(endpointConfig);
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
return result;
|
|
@@ -370,7 +370,7 @@ export function generateAdapterEnvVars(result: AdapterSetupResult): string {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
/**
|
|
373
|
-
* 将
|
|
373
|
+
* 将 Endpoint 配置项格式化为 YAML 行
|
|
374
374
|
*/
|
|
375
375
|
function appendYamlValue(lines: string[], key: string, value: unknown, indent: number): void {
|
|
376
376
|
const pad = ' '.repeat(indent);
|
|
@@ -405,20 +405,20 @@ function appendYamlValue(lines: string[], key: string, value: unknown, indent: n
|
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
/**
|
|
408
|
-
* 生成 YAML 格式的
|
|
408
|
+
* 生成 YAML 格式的 endpoints 配置段
|
|
409
409
|
*/
|
|
410
|
-
export function
|
|
411
|
-
if (result.
|
|
410
|
+
export function generateEndpointsConfigYaml(result: AdapterSetupResult): string {
|
|
411
|
+
if (result.endpoints.length === 0) {
|
|
412
412
|
return `
|
|
413
413
|
|
|
414
|
-
# Sandbox:Remote Console 打开「沙盒」页并通过 WebSocket 连接时自动创建
|
|
415
|
-
|
|
414
|
+
# Sandbox:Remote Console 打开「沙盒」页并通过 WebSocket 连接时自动创建 Endpoint
|
|
415
|
+
endpoints: []
|
|
416
416
|
`;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
const lines: string[] = ['', '
|
|
420
|
-
for (const
|
|
421
|
-
const { context, ...config } =
|
|
419
|
+
const lines: string[] = ['', 'endpoints:'];
|
|
420
|
+
for (const entry of result.endpoints) {
|
|
421
|
+
const { context, ...config } = entry;
|
|
422
422
|
lines.push(` - context: ${context}`);
|
|
423
423
|
for (const [key, value] of Object.entries(config)) {
|
|
424
424
|
appendYamlValue(lines, key, value, 2);
|
|
@@ -429,15 +429,15 @@ bots: []
|
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
/**
|
|
432
|
-
* 生成 TOML 格式的
|
|
432
|
+
* 生成 TOML 格式的 endpoints 配置段
|
|
433
433
|
*/
|
|
434
|
-
export function
|
|
435
|
-
if (result.
|
|
434
|
+
export function generateEndpointsConfigToml(result: AdapterSetupResult): string {
|
|
435
|
+
if (result.endpoints.length === 0) return '';
|
|
436
436
|
|
|
437
437
|
const lines: string[] = [''];
|
|
438
|
-
for (const
|
|
439
|
-
lines.push('[[
|
|
440
|
-
for (const [key, value] of Object.entries(
|
|
438
|
+
for (const entry of result.endpoints) {
|
|
439
|
+
lines.push('[[endpoints]]');
|
|
440
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
441
441
|
if (typeof value === 'string') {
|
|
442
442
|
lines.push(`${key} = "${value.replace(/"/g, '\\"')}"`);
|
|
443
443
|
} else {
|
|
@@ -450,11 +450,11 @@ export function generateBotsConfigToml(result: AdapterSetupResult): string {
|
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
/**
|
|
453
|
-
* 生成 JSON 格式的
|
|
453
|
+
* 生成 JSON 格式的 endpoints 配置段
|
|
454
454
|
*/
|
|
455
|
-
export function
|
|
456
|
-
if (result.
|
|
457
|
-
return ` "
|
|
455
|
+
export function generateEndpointsConfigJSON(result: AdapterSetupResult): string {
|
|
456
|
+
if (result.endpoints.length === 0) return '';
|
|
457
|
+
return ` "endpoints": ${JSON.stringify(result.endpoints, null, 4).replace(/^/gm, ' ').trimStart()},`;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
/**
|
|
@@ -462,21 +462,21 @@ export function generateBotsConfigJSON(result: AdapterSetupResult): string {
|
|
|
462
462
|
*/
|
|
463
463
|
export function getAdapterSetupNotes(result: AdapterSetupResult): string[] {
|
|
464
464
|
const notes: string[] = [];
|
|
465
|
-
for (const
|
|
466
|
-
const context =
|
|
467
|
-
if (context === 'telegram' &&
|
|
465
|
+
for (const entry of result.endpoints) {
|
|
466
|
+
const context = entry.context as string;
|
|
467
|
+
if (context === 'telegram' && entry.polling === false) {
|
|
468
468
|
notes.push('Telegram Webhook: 确保 HTTPS 域名可从公网访问,且 webhook.port 已放行');
|
|
469
469
|
}
|
|
470
|
-
if (context === 'telegram' &&
|
|
470
|
+
if (context === 'telegram' && entry.polling !== false) {
|
|
471
471
|
notes.push('Telegram polling: 本地开发可直接 pnpm dev,无需公网');
|
|
472
472
|
}
|
|
473
473
|
if (context === 'github') {
|
|
474
|
-
if (
|
|
474
|
+
if (entry.webhook_secret) {
|
|
475
475
|
notes.push('GitHub Webhook: App 设置 URL 为 https://<域名>/pub/github/webhook');
|
|
476
476
|
} else {
|
|
477
477
|
notes.push('GitHub polling: 无 Webhook 时将轮询 Events API(默认 60s)');
|
|
478
478
|
}
|
|
479
|
-
if (
|
|
479
|
+
if (entry.app_id) {
|
|
480
480
|
notes.push('GitHub App: 私钥路径需存在,且 App 已安装到目标仓库');
|
|
481
481
|
} else {
|
|
482
482
|
notes.push('GitHub gh CLI: 运行 gh auth login 完成认证');
|
package/src/ai.ts
CHANGED
|
@@ -76,6 +76,22 @@ const PROVIDERS = [
|
|
|
76
76
|
},
|
|
77
77
|
] as const;
|
|
78
78
|
|
|
79
|
+
function providerApiFor(name: string): string {
|
|
80
|
+
switch (name) {
|
|
81
|
+
case 'anthropic':
|
|
82
|
+
return 'anthropic-messages';
|
|
83
|
+
case 'google':
|
|
84
|
+
case 'gemini':
|
|
85
|
+
return 'google-generative-ai';
|
|
86
|
+
case 'ollama':
|
|
87
|
+
return 'ollama-chat';
|
|
88
|
+
case 'cloudflare':
|
|
89
|
+
return 'cloudflare-workers-ai';
|
|
90
|
+
default:
|
|
91
|
+
return 'openai-completions';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
function defaultModelForProvider(driver: string): string {
|
|
80
96
|
const entry = PROVIDERS.find(p => p.value === driver);
|
|
81
97
|
return entry?.defaultModel ?? '';
|
|
@@ -207,7 +223,7 @@ export async function configureAI(): Promise<AISetupConfig> {
|
|
|
207
223
|
|
|
208
224
|
console.log('');
|
|
209
225
|
console.log(chalk.blue('⚡ 配置 AI 触发方式'));
|
|
210
|
-
console.log(chalk.gray(' 决定
|
|
226
|
+
console.log(chalk.gray(' 决定 Endpoint 何时自动调用大模型;命令(/ 开头)不会被 AI 拦截。'));
|
|
211
227
|
|
|
212
228
|
const triggerConfig = await inquirer.prompt([
|
|
213
229
|
{
|
|
@@ -332,7 +348,7 @@ export function generateAIConfigYaml(config: AISetupConfig): string {
|
|
|
332
348
|
if (config.providers) {
|
|
333
349
|
for (const [name, providerConfig] of Object.entries(config.providers)) {
|
|
334
350
|
lines.push(` ${name}:`);
|
|
335
|
-
lines.push(`
|
|
351
|
+
lines.push(` api: ${providerApiFor(name)}`);
|
|
336
352
|
if (providerConfig.apiKey) {
|
|
337
353
|
lines.push(` apiKey: ${providerConfig.apiKey}`);
|
|
338
354
|
}
|
package/src/apply.ts
CHANGED
|
@@ -30,9 +30,9 @@ export function applyAdaptersToConfig(config: Record<string, unknown>, result: A
|
|
|
30
30
|
}
|
|
31
31
|
config.plugins = plugins;
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
config.
|
|
33
|
+
const endpoints = Array.isArray(config.endpoints) ? [...config.endpoints as Record<string, unknown>[]] : [];
|
|
34
|
+
endpoints.push(...result.endpoints);
|
|
35
|
+
config.endpoints = endpoints;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function applyAIToConfig(config: Record<string, unknown>, ai: AISetupConfig): void {
|
package/src/index.ts
CHANGED
|
@@ -5,9 +5,9 @@ export type { AdapterSetupResult } from './adapter.js';
|
|
|
5
5
|
export {
|
|
6
6
|
configureAdapters,
|
|
7
7
|
generateAdapterEnvVars,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
generateEndpointsConfigYaml,
|
|
9
|
+
generateEndpointsConfigJSON,
|
|
10
|
+
generateEndpointsConfigToml,
|
|
11
11
|
getAdapterDependencies,
|
|
12
12
|
getAdapterSetupNotes,
|
|
13
13
|
} from './adapter.js';
|