@zhin.js/scaffold-wizard 0.2.3 → 0.2.5

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.
@@ -22,10 +22,10 @@ export interface EndpointConfigureContext {
22
22
  export async function configureTelegramEndpoint(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
23
23
  console.log(chalk.gray(' 文档: ' + adapterDocsUrl('telegram')));
24
24
 
25
- const { endpointName } = await inquirer.prompt([
25
+ const { endpointId } = await inquirer.prompt([
26
26
  {
27
27
  type: 'input',
28
- name: 'endpointName',
28
+ name: 'endpointId',
29
29
  message: ' Endpoint 标识名称:',
30
30
  default: 'telegram-bot',
31
31
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -61,10 +61,10 @@ export async function configureTelegramEndpoint(ctx: EndpointConfigureContext):
61
61
  },
62
62
  ]);
63
63
 
64
- // schema:polling / webhook 为顶层共享字段;name / token 为 endpoint 级
64
+ // schema:polling / webhook 为顶层共享字段;id / token 为 endpoint 级
65
65
  const config: Record<string, unknown> = {
66
66
  polling: transport === 'polling',
67
- endpoints: [{ name: endpointName.trim(), token: '${TELEGRAM_TOKEN}' }],
67
+ endpoints: [{ id: endpointId.trim(), token: '${TELEGRAM_TOKEN}' }],
68
68
  };
69
69
 
70
70
  if (transport === 'webhook') {
@@ -106,10 +106,10 @@ export async function configureGitHubEndpoint(ctx: EndpointConfigureContext): Pr
106
106
  console.log(chalk.gray(' 文档: ' + adapterDocsUrl('github')));
107
107
  ctx.markRequiresDatabase();
108
108
 
109
- const { endpointName } = await inquirer.prompt([
109
+ const { endpointId } = await inquirer.prompt([
110
110
  {
111
111
  type: 'input',
112
- name: 'endpointName',
112
+ name: 'endpointId',
113
113
  message: ' Endpoint 标识名称:',
114
114
  default: 'github-bot',
115
115
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -137,7 +137,7 @@ export async function configureGitHubEndpoint(ctx: EndpointConfigureContext): Pr
137
137
 
138
138
  // schema:app_id / private_key / webhook_secret 为 endpoint 级;webhook_path / poll_interval 为顶层共享字段
139
139
  const endpointConfig: Record<string, unknown> = {
140
- name: endpointName.trim(),
140
+ id: endpointId.trim(),
141
141
  };
142
142
  const sharedConfig: Record<string, unknown> = {};
143
143
 
@@ -267,19 +267,19 @@ export async function configureOneBot11Bot(ctx: EndpointConfigureContext): Promi
267
267
  },
268
268
  ]);
269
269
 
270
- const { endpointName } = await inquirer.prompt([
270
+ const { endpointId } = await inquirer.prompt([
271
271
  {
272
272
  type: 'input',
273
- name: 'endpointName',
273
+ name: 'endpointId',
274
274
  message: ' Endpoint 标识名称:',
275
275
  default: 'onebot-bot',
276
276
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
277
277
  },
278
278
  ]);
279
279
 
280
- // schema:connection 为顶层共享字段;name / url / path / access_token 为 endpoint 级
280
+ // schema:connection 为顶层共享字段;id / url / path / access_token 为 endpoint 级
281
281
  const endpointConfig: Record<string, unknown> = {
282
- name: endpointName.trim(),
282
+ id: endpointId.trim(),
283
283
  };
284
284
 
285
285
  if (connection === 'ws') {
@@ -351,7 +351,7 @@ export async function configureQQBot(ctx: EndpointConfigureContext): Promise<Rec
351
351
  ctx.envVars.QQ_APP_ID = credentials.appID.trim();
352
352
  ctx.envVars.QQ_SECRET = credentials.secret;
353
353
 
354
- const { mode, sandbox } = await inquirer.prompt([
354
+ const { mode, sandbox, botKind } = await inquirer.prompt([
355
355
  {
356
356
  type: 'select',
357
357
  name: 'mode',
@@ -368,16 +368,45 @@ export async function configureQQBot(ctx: EndpointConfigureContext): Promise<Rec
368
368
  message: ' 沙箱环境 (sandbox)?',
369
369
  default: false,
370
370
  },
371
+ {
372
+ type: 'select',
373
+ name: 'botKind',
374
+ message: ' 公域 / 私域(决定频道消息 intents;群聊两边都有):',
375
+ choices: [
376
+ {
377
+ name: 'public — 公域(频道仅 @;+ 群聊/私聊)',
378
+ value: 'public',
379
+ },
380
+ {
381
+ name: 'private — 私域(频道全量消息;+ 群聊/私聊;公域勿选)',
382
+ value: 'private',
383
+ },
384
+ ],
385
+ default: 'public',
386
+ },
371
387
  ]);
372
388
 
373
- // schema:mode / sandbox 为顶层共享字段;name / appid / secret 为 endpoint 级
389
+ const sharedIntents = [
390
+ 'GROUP_AND_C2C_EVENT',
391
+ 'GUILDS',
392
+ 'GUILD_MEMBERS',
393
+ 'DIRECT_MESSAGE',
394
+ ];
395
+ const intents =
396
+ botKind === 'private'
397
+ ? [...sharedIntents, 'GUILD_MESSAGES']
398
+ : [...sharedIntents, 'PUBLIC_GUILD_MESSAGES'];
399
+
400
+ // schema:mode / sandbox 可顶层共享;botKind / intents 写在 endpoint,与扫码绑定一致
374
401
  return {
375
402
  mode,
376
403
  sandbox,
377
404
  endpoints: [{
378
- name: credentials.name.trim(),
405
+ id: credentials.name.trim(),
379
406
  appid: '${QQ_APP_ID}',
380
407
  secret: '${QQ_SECRET}',
408
+ botKind,
409
+ intents,
381
410
  }],
382
411
  };
383
412
  }
@@ -398,10 +427,10 @@ export async function configureDiscordEndpoint(ctx: EndpointConfigureContext): P
398
427
  },
399
428
  ]);
400
429
 
401
- const { endpointName, token } = await inquirer.prompt([
430
+ const { endpointId, token } = await inquirer.prompt([
402
431
  {
403
432
  type: 'input',
404
- name: 'endpointName',
433
+ name: 'endpointId',
405
434
  message: ' Endpoint 标识名称:',
406
435
  default: 'discord-bot',
407
436
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -415,10 +444,10 @@ export async function configureDiscordEndpoint(ctx: EndpointConfigureContext): P
415
444
  ]);
416
445
  ctx.envVars.DISCORD_TOKEN = token;
417
446
 
418
- // schema:connection / applicationId / publicKey / interactionsPath 为顶层共享字段;name / token 为 endpoint 级
447
+ // schema:connection / applicationId / publicKey / interactionsPath 为顶层共享字段;id / token 为 endpoint 级
419
448
  const config: Record<string, unknown> = {
420
449
  connection,
421
- endpoints: [{ name: endpointName.trim(), token: '${DISCORD_TOKEN}' }],
450
+ endpoints: [{ id: endpointId.trim(), token: '${DISCORD_TOKEN}' }],
422
451
  };
423
452
 
424
453
  if (connection === 'interactions') {
@@ -467,10 +496,10 @@ export async function configureSlackEndpoint(ctx: EndpointConfigureContext): Pro
467
496
  },
468
497
  ]);
469
498
 
470
- const { endpointName, token, signingSecret } = await inquirer.prompt([
499
+ const { endpointId, token, signingSecret } = await inquirer.prompt([
471
500
  {
472
501
  type: 'input',
473
- name: 'endpointName',
502
+ name: 'endpointId',
474
503
  message: ' Endpoint 标识名称:',
475
504
  default: 'slack-bot',
476
505
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -492,9 +521,9 @@ export async function configureSlackEndpoint(ctx: EndpointConfigureContext): Pro
492
521
  ctx.envVars.SLACK_BOT_TOKEN = token;
493
522
  ctx.envVars.SLACK_SIGNING_SECRET = signingSecret;
494
523
 
495
- // schema:socketMode 为顶层共享字段;name / token / signingSecret / appToken 为 endpoint 级
524
+ // schema:socketMode 为顶层共享字段;id / token / signingSecret / appToken 为 endpoint 级
496
525
  const endpointConfig: Record<string, unknown> = {
497
- name: endpointName.trim(),
526
+ id: endpointId.trim(),
498
527
  token: '${SLACK_BOT_TOKEN}',
499
528
  signingSecret: '${SLACK_SIGNING_SECRET}',
500
529
  };
@@ -534,19 +563,19 @@ export async function configureNapcatBot(ctx: EndpointConfigureContext): Promise
534
563
  },
535
564
  ]);
536
565
 
537
- const { endpointName } = await inquirer.prompt([
566
+ const { endpointId } = await inquirer.prompt([
538
567
  {
539
568
  type: 'input',
540
- name: 'endpointName',
569
+ name: 'endpointId',
541
570
  message: ' Endpoint 标识名称:',
542
571
  default: 'napcat-bot',
543
572
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
544
573
  },
545
574
  ]);
546
575
 
547
- // schema:connection 为顶层共享字段;name / url / path / http_url / post_path / access_token 为 endpoint 级
576
+ // schema:connection 为顶层共享字段;id / url / path / http_url / post_path / access_token 为 endpoint 级
548
577
  const endpointConfig: Record<string, unknown> = {
549
- name: endpointName.trim(),
578
+ id: endpointId.trim(),
550
579
  };
551
580
 
552
581
  if (connection === 'ws') {
@@ -626,19 +655,19 @@ export async function configureOneBot12Bot(ctx: EndpointConfigureContext): Promi
626
655
  },
627
656
  ]);
628
657
 
629
- const { endpointName } = await inquirer.prompt([
658
+ const { endpointId } = await inquirer.prompt([
630
659
  {
631
660
  type: 'input',
632
- name: 'endpointName',
661
+ name: 'endpointId',
633
662
  message: ' Endpoint 标识名称:',
634
663
  default: 'onebot12-bot',
635
664
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
636
665
  },
637
666
  ]);
638
667
 
639
- // schema:connection 为顶层共享字段;name / url / path / api_url / access_token 为 endpoint 级
668
+ // schema:connection 为顶层共享字段;id / url / path / api_url / access_token 为 endpoint 级
640
669
  const endpointConfig: Record<string, unknown> = {
641
- name: endpointName.trim(),
670
+ id: endpointId.trim(),
642
671
  };
643
672
 
644
673
  if (connection === 'ws') {
@@ -719,10 +748,10 @@ export async function configureMilkyBot(ctx: EndpointConfigureContext): Promise<
719
748
  },
720
749
  ]);
721
750
 
722
- const { endpointName, baseUrl } = await inquirer.prompt([
751
+ const { endpointId, baseUrl } = await inquirer.prompt([
723
752
  {
724
753
  type: 'input',
725
- name: 'endpointName',
754
+ name: 'endpointId',
726
755
  message: ' Endpoint 标识名称:',
727
756
  default: 'milky-bot',
728
757
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -736,9 +765,9 @@ export async function configureMilkyBot(ctx: EndpointConfigureContext): Promise<
736
765
  },
737
766
  ]);
738
767
 
739
- // schema:connection 为顶层共享字段;name / baseUrl / path / access_token 为 endpoint 级
768
+ // schema:connection 为顶层共享字段;id / baseUrl / path / access_token 为 endpoint 级
740
769
  const endpointConfig: Record<string, unknown> = {
741
- name: endpointName.trim(),
770
+ id: endpointId.trim(),
742
771
  baseUrl: baseUrl.trim(),
743
772
  };
744
773
 
@@ -787,10 +816,10 @@ export async function configureSatoriBot(ctx: EndpointConfigureContext): Promise
787
816
  },
788
817
  ]);
789
818
 
790
- const { endpointName, baseUrl } = await inquirer.prompt([
819
+ const { endpointId, baseUrl } = await inquirer.prompt([
791
820
  {
792
821
  type: 'input',
793
- name: 'endpointName',
822
+ name: 'endpointId',
794
823
  message: ' Endpoint 标识名称:',
795
824
  default: 'satori-bot',
796
825
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -804,9 +833,9 @@ export async function configureSatoriBot(ctx: EndpointConfigureContext): Promise
804
833
  },
805
834
  ]);
806
835
 
807
- // schema:connection 为顶层共享字段;name / baseUrl / token / path 为 endpoint 级
836
+ // schema:connection 为顶层共享字段;id / baseUrl / token / path 为 endpoint 级
808
837
  const endpointConfig: Record<string, unknown> = {
809
- name: endpointName.trim(),
838
+ id: endpointId.trim(),
810
839
  baseUrl: baseUrl.trim(),
811
840
  };
812
841
 
@@ -839,6 +868,142 @@ export async function configureSatoriBot(ctx: EndpointConfigureContext): Promise
839
868
  return { connection, endpoints: [endpointConfig] };
840
869
  }
841
870
 
871
+ /**
872
+ * ICQQ(QQ 非官方协议)配置向导 —— 直连 @icqqjs/icqq,引导用户配置
873
+ * 登录凭据、设备平台、签名服务等参数。
874
+ */
875
+ export async function configureIcqqBot(ctx: EndpointConfigureContext): Promise<Record<string, unknown>> {
876
+ console.log(chalk.gray(' 文档: ' + adapterDocsUrl('icqq')));
877
+ console.log(chalk.gray(' 直连 @icqqjs/icqq 协议库,启动时自动登录'));
878
+
879
+ const basic = await inquirer.prompt([
880
+ {
881
+ type: 'input',
882
+ name: 'id',
883
+ message: ' QQ 号(uin):',
884
+ validate: (v: string) => /^\d+$/.test(v.trim()) ? true : 'QQ 号必须为纯数字',
885
+ },
886
+ {
887
+ type: 'input',
888
+ name: 'master',
889
+ message: ' 主人 QQ 号(master,/approve 等管理命令):',
890
+ validate: (v: string) => /^\d+$/.test(v.trim()) ? true : '主人 QQ 号必须为纯数字',
891
+ },
892
+ ]);
893
+
894
+ const { loginMethod } = await inquirer.prompt([
895
+ {
896
+ type: 'select',
897
+ name: 'loginMethod',
898
+ message: ' 登录方式:',
899
+ choices: [
900
+ { name: '扫码登录(无需密码,推荐)', value: 'qrcode' },
901
+ { name: '密码登录', value: 'password' },
902
+ ],
903
+ default: 'qrcode',
904
+ },
905
+ ]);
906
+
907
+ let password: string | undefined;
908
+ if (loginMethod === 'password') {
909
+ const { pwd } = await inquirer.prompt([
910
+ {
911
+ type: 'password',
912
+ name: 'pwd',
913
+ message: ' QQ 密码:',
914
+ validate: (v: string) => (v.trim() ? true : '密码不能为空'),
915
+ },
916
+ ]);
917
+ ctx.envVars[`ICQQ_PASSWORD_${basic.id.trim()}`] = pwd;
918
+ password = `\${ICQQ_PASSWORD_${basic.id.trim()}}`;
919
+ }
920
+
921
+ const { platform } = await inquirer.prompt([
922
+ {
923
+ type: 'select',
924
+ name: 'platform',
925
+ message: ' 登录设备平台:',
926
+ choices: [
927
+ { name: 'Android 手机(默认)', value: 1 },
928
+ { name: 'Android 平板 (aPad)', value: 2 },
929
+ { name: 'Android 手表 (Watch)', value: 3 },
930
+ { name: 'MacOS (iMac)', value: 4 },
931
+ { name: 'iPad', value: 5 },
932
+ { name: 'Tim', value: 6 },
933
+ ],
934
+ default: 2,
935
+ },
936
+ ]);
937
+
938
+ const { signApiAddr } = await inquirer.prompt([
939
+ {
940
+ type: 'input',
941
+ name: 'signApiAddr',
942
+ message: ' 签名服务器地址(留空则尝试用本地 @icqqjs/qqsign):',
943
+ default: '',
944
+ },
945
+ ]);
946
+
947
+ const { wantAdvanced } = await inquirer.prompt([
948
+ {
949
+ type: 'confirm',
950
+ name: 'wantAdvanced',
951
+ message: ' 配置高级选项(数据目录、QQNT、协议版本等)?',
952
+ default: false,
953
+ },
954
+ ]);
955
+
956
+ const advanced: Record<string, unknown> = {};
957
+ if (wantAdvanced) {
958
+ const opts = await inquirer.prompt([
959
+ {
960
+ type: 'input',
961
+ name: 'ver',
962
+ message: ' 协议版本号(留空使用9.1.70):',
963
+ default: '9.1.70',
964
+ },
965
+ {
966
+ type: 'input',
967
+ name: 'dataDir',
968
+ message: ' 数据存储目录(留空使用默认):',
969
+ default: `data/icqq/${basic.id.trim()}`,
970
+ },
971
+ {
972
+ type: 'confirm',
973
+ name: 'qqnt',
974
+ message: ' 使用 QQNT 协议?',
975
+ default: true,
976
+ },
977
+ {
978
+ type: 'confirm',
979
+ name: 'cacheGroupMember',
980
+ message: ' 缓存群员列表?(群多时占内存较大)',
981
+ default: true,
982
+ },
983
+ ]);
984
+ if (opts.ver?.trim()) advanced.ver = opts.ver.trim();
985
+ if (opts.dataDir?.trim()) advanced.dataDir = opts.dataDir.trim();
986
+ if (!opts.qqnt) advanced.qqnt = false;
987
+ if (!opts.cacheGroupMember) advanced.cacheGroupMember = false;
988
+ }
989
+
990
+ ctx.envVars.ICQQ_ACCOUNT = basic.id.trim();
991
+
992
+ const endpointConfig: Record<string, unknown> = {
993
+ id: basic.id.trim(),
994
+ ...(password ? { password } : {}),
995
+ ...advanced,
996
+ };
997
+
998
+ const sharedConfig: Record<string, unknown> = {
999
+ master: basic.master.trim(),
1000
+ ...(platform !== 1 ? { platform } : {}),
1001
+ ...(signApiAddr?.trim() ? { signApiAddr: signApiAddr.trim() } : {}),
1002
+ };
1003
+
1004
+ return { ...sharedConfig, endpoints: [endpointConfig] };
1005
+ }
1006
+
842
1007
  /**
843
1008
  * 微信 iLink(个人微信 / ClawBot)配置:默认走扫码绑定 —— 终端展示二维码,
844
1009
  * 微信扫码确认后 bot_token 写入 .env(`WEIXIN_ILINK_TOKEN`),
@@ -848,10 +1013,10 @@ export async function configureWeixinIlinkBot(ctx: EndpointConfigureContext): Pr
848
1013
  console.log(chalk.gray(' 文档: ' + adapterDocsUrl('weixin-ilink')));
849
1014
  console.log(chalk.gray(' 需最新版微信 + ClawBot 灰度资格(仅私聊)'));
850
1015
 
851
- const { endpointName } = await inquirer.prompt([
1016
+ const { endpointId } = await inquirer.prompt([
852
1017
  {
853
1018
  type: 'input',
854
- name: 'endpointName',
1019
+ name: 'endpointId',
855
1020
  message: ' Endpoint 标识名称:',
856
1021
  default: 'weixin-bot',
857
1022
  validate: (v: string) => (v.trim() ? true : '名称不能为空'),
@@ -875,7 +1040,7 @@ export async function configureWeixinIlinkBot(ctx: EndpointConfigureContext): Pr
875
1040
  const scanned = await tryQrLogin(ctx);
876
1041
  if (scanned) {
877
1042
  return {
878
- endpoints: [{ name: endpointName.trim(), botToken: '${WEIXIN_ILINK_TOKEN}' }],
1043
+ endpoints: [{ id: endpointId.trim(), botToken: '${WEIXIN_ILINK_TOKEN}' }],
879
1044
  };
880
1045
  }
881
1046
  console.log(chalk.yellow(' 扫码未完成,降级为手动输入 bot_token'));
@@ -891,7 +1056,7 @@ export async function configureWeixinIlinkBot(ctx: EndpointConfigureContext): Pr
891
1056
  ]);
892
1057
  ctx.envVars.WEIXIN_ILINK_TOKEN = token.trim();
893
1058
  return {
894
- endpoints: [{ name: endpointName.trim(), botToken: '${WEIXIN_ILINK_TOKEN}' }],
1059
+ endpoints: [{ id: endpointId.trim(), botToken: '${WEIXIN_ILINK_TOKEN}' }],
895
1060
  };
896
1061
  }
897
1062
 
package/src/adapter.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  adapterDocsUrl,
6
6
  configureDiscordEndpoint,
7
7
  configureGitHubEndpoint,
8
+ configureIcqqBot,
8
9
  configureMilkyBot,
9
10
  configureNapcatBot,
10
11
  configureOneBot11Bot,
@@ -82,15 +83,16 @@ export const ADAPTERS: AdapterDefinition[] = [
82
83
  value: 'icqq',
83
84
  package: '@zhin.js/adapter-icqq',
84
85
  plugin: '@zhin.js/adapter-icqq',
86
+ extraDeps: {
87
+ '@icqqjs/icqq': 'latest',
88
+ '@icqqjs/qqsign': 'latest',
89
+ },
85
90
  needsHttp: false,
86
- description: '非官方 QQ 协议,需本地登录',
87
- setupHint: '先安装 icqq CLI 并登录:pnpm dlx icqq login <QQ号>,登录态保存在 ~/.icqq/。',
91
+ description: '非官方 QQ 协议,直连 @icqqjs/icqq',
92
+ setupHint: '直连 @icqqjs/icqq 协议库,支持扫码或密码登录;建议配置签名服务以提高稳定性。',
88
93
  docUrl: adapterDocsUrl('icqq'),
89
- fields: [
90
- { key: 'name', message: 'QQ 号(与 icqq login 时一致):', required: true, envKey: 'ICQQ_ACCOUNT' },
91
- // schema 顶层 required: master(所有 endpoint 共享)
92
- { key: 'master', message: '主人 QQ 号(master,/approve 等管理命令):', required: true, scope: 'shared' },
93
- ],
94
+ configure: configureIcqqBot,
95
+ fields: [],
94
96
  },
95
97
  {
96
98
  name: 'QQ 官方',
@@ -411,7 +413,7 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
411
413
  package: adapterDef.package,
412
414
  instanceKey: 'sandbox',
413
415
  config: {
414
- endpoints: [{ context: 'sandbox', name: 'sandbox-bot', owner: 'sandbox-user' }],
416
+ endpoints: [{ context: 'sandbox', id: 'sandbox-bot', owner: 'sandbox-user' }],
415
417
  },
416
418
  });
417
419
  continue;
@@ -491,11 +493,11 @@ export async function configureAdapters(): Promise<AdapterSetupResult> {
491
493
 
492
494
  /**
493
495
  * 字段式适配器(无自定义 configure)的实例配置整形,对齐各插件 schema.json 新格式:
494
- * - endpoint 级字段(name + 凭据等,字段默认 scope: 'endpoint')进 `endpoints: [{ name, ... }]`
496
+ * - endpoint 级字段(id + 凭据等,字段默认 scope: 'endpoint')进 `endpoints: [{ id, ... }]`
495
497
  * - scope: 'shared' 的字段(如 lark/wechat-mp 的 webhookPath)留顶层
496
498
  * - wechat-mp:顶层字段名为 path(向导沿用 webhookPath 提问)
497
499
  * - email:smtp/imap 平铺字段 → endpoint 内 smtp/imap 嵌套对象
498
- * - 未单独询问 name 的适配器用 `<adapter>-bot` 兜底
500
+ * - 未单独询问 id 的适配器用 `<adapter>-bot` 兜底
499
501
  */
500
502
  export function buildFieldBasedInstanceConfig(adapter: AdapterDefinition, values: Record<string, any>): Record<string, unknown> {
501
503
  if (adapter.value === 'email') {
@@ -503,7 +505,7 @@ export function buildFieldBasedInstanceConfig(adapter: AdapterDefinition, values
503
505
  const password = values.password ?? '';
504
506
  return {
505
507
  endpoints: [{
506
- name: 'email-bot',
508
+ id: 'email-bot',
507
509
  smtp: {
508
510
  host: values.smtpHost,
509
511
  port: Number(values.smtpPort) || 465,
@@ -536,8 +538,8 @@ export function buildFieldBasedInstanceConfig(adapter: AdapterDefinition, values
536
538
  shared.path = shared.webhookPath;
537
539
  delete shared.webhookPath;
538
540
  }
539
- if (!endpoint.name) {
540
- endpoint.name = `${adapter.value}-bot`;
541
+ if (!endpoint.id) {
542
+ endpoint.id = `${adapter.value}-bot`;
541
543
  }
542
544
  return { ...shared, endpoints: [endpoint] };
543
545
  }
package/src/apply.ts CHANGED
@@ -50,14 +50,14 @@ export function applyAdaptersToConfig(config: Record<string, unknown>, result: A
50
50
  delete config.endpoints;
51
51
  }
52
52
 
53
- function endpointName(endpoint: unknown): string | undefined {
53
+ function endpointId(endpoint: unknown): string | undefined {
54
54
  if (!endpoint || typeof endpoint !== 'object' || Array.isArray(endpoint)) return undefined;
55
- const name = (endpoint as Record<string, unknown>).name;
56
- return typeof name === 'string' ? name : undefined;
55
+ const id = (endpoint as Record<string, unknown>).id;
56
+ return typeof id === 'string' ? id : undefined;
57
57
  }
58
58
 
59
59
  /**
60
- * 重跑向导时按实例合并配置而非整体覆盖:保留现有 endpoints 中 name 不冲突的项
60
+ * 重跑向导时按实例合并配置而非整体覆盖:保留现有 endpoints 中 id 不冲突的项
61
61
  * 与其他实例级键;被向导结果覆盖的 endpoints / 键在覆盖前打印提示。
62
62
  */
63
63
  function mergeAdapterInstanceConfig(
@@ -74,9 +74,9 @@ function mergeAdapterInstanceConfig(
74
74
  const currentEndpoints = Array.isArray(current.endpoints) ? current.endpoints : [];
75
75
  const nextEndpoints = Array.isArray(next.endpoints) ? next.endpoints : [];
76
76
  if (currentEndpoints.length > 0 && nextEndpoints.length > 0) {
77
- const nextNames = new Set(nextEndpoints.map(endpointName));
77
+ const nextNames = new Set(nextEndpoints.map(endpointId));
78
78
  const kept = currentEndpoints.filter((endpoint) => {
79
- const name = endpointName(endpoint);
79
+ const name = endpointId(endpoint);
80
80
  if (name != null && nextNames.has(name)) {
81
81
  dropped.push(`endpoint "${name}"`);
82
82
  return false;
@@ -18,7 +18,10 @@ export const ZHIN_STACK_VERSIONS = {
18
18
  '@zhin.js/adapter': 'latest',
19
19
  '@zhin.js/command': 'latest',
20
20
  '@zhin.js/component': 'latest',
21
+ '@zhin.js/console-contract': 'latest',
21
22
  '@zhin.js/core': 'latest',
23
+ '@zhin.js/layout': 'latest',
24
+ '@zhin.js/page': 'latest',
22
25
  '@zhin.js/tool': 'latest',
23
26
  '@zhin.js/client': 'latest',
24
27
  '@zhin.js/contract': 'latest',