@tencent-connect/dsh-qqbot 0.1.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.
Files changed (126) hide show
  1. package/.orange-ci.yml +68 -0
  2. package/LICENSE +21 -0
  3. package/README.md +142 -0
  4. package/README_EN.md +142 -0
  5. package/cordis.patch.yml +32 -0
  6. package/cordis.yml +69 -0
  7. package/dist/commands/help.d.ts +11 -0
  8. package/dist/commands/help.d.ts.map +1 -0
  9. package/dist/commands/help.js +21 -0
  10. package/dist/commands/help.js.map +1 -0
  11. package/dist/commands/index.d.ts +13 -0
  12. package/dist/commands/index.d.ts.map +1 -0
  13. package/dist/commands/index.js +27 -0
  14. package/dist/commands/index.js.map +1 -0
  15. package/dist/commands/misc.d.ts +12 -0
  16. package/dist/commands/misc.d.ts.map +1 -0
  17. package/dist/commands/misc.js +30 -0
  18. package/dist/commands/misc.js.map +1 -0
  19. package/dist/commands/model.d.ts +7 -0
  20. package/dist/commands/model.d.ts.map +1 -0
  21. package/dist/commands/model.js +58 -0
  22. package/dist/commands/model.js.map +1 -0
  23. package/dist/commands/session.d.ts +10 -0
  24. package/dist/commands/session.d.ts.map +1 -0
  25. package/dist/commands/session.js +26 -0
  26. package/dist/commands/session.js.map +1 -0
  27. package/dist/commands/status.d.ts +8 -0
  28. package/dist/commands/status.d.ts.map +1 -0
  29. package/dist/commands/status.js +24 -0
  30. package/dist/commands/status.js.map +1 -0
  31. package/dist/commands/types.d.ts +12 -0
  32. package/dist/commands/types.d.ts.map +1 -0
  33. package/dist/commands/types.js +2 -0
  34. package/dist/commands/types.js.map +1 -0
  35. package/dist/config.d.ts +50 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +33 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/index.d.ts +14 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +154 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/model/index.d.ts +10 -0
  44. package/dist/model/index.d.ts.map +1 -0
  45. package/dist/model/index.js +9 -0
  46. package/dist/model/index.js.map +1 -0
  47. package/dist/model/model-resolver.d.ts +82 -0
  48. package/dist/model/model-resolver.d.ts.map +1 -0
  49. package/dist/model/model-resolver.js +142 -0
  50. package/dist/model/model-resolver.js.map +1 -0
  51. package/dist/model/prefs-store.d.ts +24 -0
  52. package/dist/model/prefs-store.d.ts.map +1 -0
  53. package/dist/model/prefs-store.js +94 -0
  54. package/dist/model/prefs-store.js.map +1 -0
  55. package/dist/model/settings-reader.d.ts +19 -0
  56. package/dist/model/settings-reader.d.ts.map +1 -0
  57. package/dist/model/settings-reader.js +76 -0
  58. package/dist/model/settings-reader.js.map +1 -0
  59. package/dist/model/types.d.ts +15 -0
  60. package/dist/model/types.d.ts.map +1 -0
  61. package/dist/model/types.js +5 -0
  62. package/dist/model/types.js.map +1 -0
  63. package/dist/session/idle-evictor.d.ts +18 -0
  64. package/dist/session/idle-evictor.d.ts.map +1 -0
  65. package/dist/session/idle-evictor.js +31 -0
  66. package/dist/session/idle-evictor.js.map +1 -0
  67. package/dist/session/index.d.ts +9 -0
  68. package/dist/session/index.d.ts.map +1 -0
  69. package/dist/session/index.js +8 -0
  70. package/dist/session/index.js.map +1 -0
  71. package/dist/session/session-manager.d.ts +44 -0
  72. package/dist/session/session-manager.d.ts.map +1 -0
  73. package/dist/session/session-manager.js +341 -0
  74. package/dist/session/session-manager.js.map +1 -0
  75. package/dist/session/types.d.ts +124 -0
  76. package/dist/session/types.d.ts.map +1 -0
  77. package/dist/session/types.js +2 -0
  78. package/dist/session/types.js.map +1 -0
  79. package/dist/setup.d.ts +24 -0
  80. package/dist/setup.d.ts.map +1 -0
  81. package/dist/setup.js +135 -0
  82. package/dist/setup.js.map +1 -0
  83. package/dist/shared/index.d.ts +7 -0
  84. package/dist/shared/index.d.ts.map +1 -0
  85. package/dist/shared/index.js +7 -0
  86. package/dist/shared/index.js.map +1 -0
  87. package/dist/shared/scope.d.ts +14 -0
  88. package/dist/shared/scope.d.ts.map +1 -0
  89. package/dist/shared/scope.js +12 -0
  90. package/dist/shared/scope.js.map +1 -0
  91. package/dist/shared/send-helper.d.ts +11 -0
  92. package/dist/shared/send-helper.d.ts.map +1 -0
  93. package/dist/shared/send-helper.js +13 -0
  94. package/dist/shared/send-helper.js.map +1 -0
  95. package/dist/shared/utils.d.ts +19 -0
  96. package/dist/shared/utils.d.ts.map +1 -0
  97. package/dist/shared/utils.js +66 -0
  98. package/dist/shared/utils.js.map +1 -0
  99. package/dist/transport/chunker.d.ts +14 -0
  100. package/dist/transport/chunker.d.ts.map +1 -0
  101. package/dist/transport/chunker.js +78 -0
  102. package/dist/transport/chunker.js.map +1 -0
  103. package/dist/transport/inbound.d.ts +8 -0
  104. package/dist/transport/inbound.d.ts.map +1 -0
  105. package/dist/transport/inbound.js +223 -0
  106. package/dist/transport/inbound.js.map +1 -0
  107. package/dist/transport/index.d.ts +10 -0
  108. package/dist/transport/index.d.ts.map +1 -0
  109. package/dist/transport/index.js +10 -0
  110. package/dist/transport/index.js.map +1 -0
  111. package/dist/transport/outbound-buffer.d.ts +32 -0
  112. package/dist/transport/outbound-buffer.d.ts.map +1 -0
  113. package/dist/transport/outbound-buffer.js +65 -0
  114. package/dist/transport/outbound-buffer.js.map +1 -0
  115. package/dist/transport/outbound.d.ts +26 -0
  116. package/dist/transport/outbound.d.ts.map +1 -0
  117. package/dist/transport/outbound.js +92 -0
  118. package/dist/transport/outbound.js.map +1 -0
  119. package/dist/types.d.ts +34 -0
  120. package/dist/types.d.ts.map +1 -0
  121. package/dist/types.js +5 -0
  122. package/dist/types.js.map +1 -0
  123. package/install.sh +108 -0
  124. package/package.json +75 -0
  125. package/pnpm-workspace.yaml +16 -0
  126. package/scripts/prepare-test-pkg.js +23 -0
@@ -0,0 +1,58 @@
1
+ import { getScopePeer, sendMarkdownChunked } from '../shared/index.js';
2
+ export function modelCommand({ manager, config }) {
3
+ return {
4
+ name: 'bot-model',
5
+ description: '查看或切换模型(用法: /bot-model [provider/model])',
6
+ handler: async (cmdCtx) => {
7
+ const { scope, peerId } = getScopePeer(cmdCtx);
8
+ const args = (cmdCtx.command?.raw ?? '').trim();
9
+ // 无参数:显示当前模型 + 可用模型列表(可点击)
10
+ if (!args) {
11
+ const current = manager.getEffectiveModel(scope, peerId);
12
+ const models = manager.listAvailableModels();
13
+ // 当前模型展示:优先用别名(name),找不到别名时回退到 provider/model id
14
+ let currentDisplay = '宿主默认配置';
15
+ if (current) {
16
+ const matched = models.find((m) => m.provider === current.provider && m.id === current.model);
17
+ currentDisplay = matched?.name ?? `${current.provider}/${current.model}`;
18
+ }
19
+ const lines = [
20
+ '### 🤖 模型配置',
21
+ '',
22
+ `**当前模型:** ${currentDisplay}`,
23
+ ];
24
+ if (models.length > 0) {
25
+ lines.push('', '**可用模型(点击切换):**');
26
+ for (const m of models) {
27
+ const modelPath = `${m.provider}/${m.id}`;
28
+ const displayName = m.name ? `${m.name}` : modelPath;
29
+ lines.push(`<qqbot-cmd-input text="/bot-model ${modelPath}" show="/bot-model ${displayName}"/>`);
30
+ }
31
+ }
32
+ lines.push('', '手动指定: `/bot-model provider/model`');
33
+ await sendMarkdownChunked(cmdCtx, lines.join('\n'), config.textChunkLimit);
34
+ return { kind: 'noop' };
35
+ }
36
+ // 解析 provider/model 格式
37
+ let provider;
38
+ let model;
39
+ if (args.includes('/')) {
40
+ const parts = args.split('/');
41
+ provider = parts[0] ?? '';
42
+ model = parts.slice(1).join('/');
43
+ }
44
+ else {
45
+ // 仅指定 model 名,provider 从当前路由继承
46
+ const current = manager.getEffectiveModel(scope, peerId);
47
+ provider = current?.provider ?? 'deepseek-official';
48
+ model = args;
49
+ }
50
+ if (!provider || !model) {
51
+ return '用法: /bot-model provider/model\n示例: /bot-model deepseek-official/deepseek-chat';
52
+ }
53
+ await manager.setModelOverride(scope, peerId, { provider, model });
54
+ return `✅ 模型已切换: ${provider}/${model}\n立即生效,对话上下文保留。`;
55
+ },
56
+ };
57
+ }
58
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 会话相关命令:/bot-reset /bot-clear /bot-new
3
+ */
4
+ import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
5
+ import type { CommandDeps } from './types.js';
6
+ /** /bot-reset /bot-clear — 重置当前会话 */
7
+ export declare function resetCommand({ manager }: CommandDeps): SlashCommand;
8
+ /** /bot-new — 开始新会话 */
9
+ export declare function newCommand({ manager }: CommandDeps): SlashCommand;
10
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,qCAAqC;AACrC,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAUnE;AAED,uBAAuB;AACvB,wBAAgB,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAUjE"}
@@ -0,0 +1,26 @@
1
+ import { getScopePeer } from '../shared/index.js';
2
+ /** /bot-reset /bot-clear — 重置当前会话 */
3
+ export function resetCommand({ manager }) {
4
+ return {
5
+ name: ['bot-reset', 'bot-clear'],
6
+ description: '重置当前会话(清除上下文)',
7
+ handler: (cmdCtx) => {
8
+ const { scope, peerId } = getScopePeer(cmdCtx);
9
+ void manager.remove(scope, peerId);
10
+ return '会话已重置 ✓';
11
+ },
12
+ };
13
+ }
14
+ /** /bot-new — 开始新会话 */
15
+ export function newCommand({ manager }) {
16
+ return {
17
+ name: 'bot-new',
18
+ description: '开始新会话(清空上下文)',
19
+ handler: (cmdCtx) => {
20
+ const { scope, peerId } = getScopePeer(cmdCtx);
21
+ void manager.remove(scope, peerId);
22
+ return '已开启新会话 ✓';
23
+ },
24
+ };
25
+ }
26
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,qCAAqC;AACrC,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,EAAe;IACnD,OAAO;QACL,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;QAChC,WAAW,EAAE,eAAe;QAC5B,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACnC,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,UAAU,CAAC,EAAE,OAAO,EAAe;IACjD,OAAO;QACL,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,cAAc;QAC3B,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACnC,OAAO,UAAU,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 状态命令:/bot-status
3
+ */
4
+ import type { SlashCommand } from '@tencent-connect/qqbot-nodejs';
5
+ import type { CommandDeps } from './types.js';
6
+ /** /bot-status — 查看当前会话状态 */
7
+ export declare function statusCommand({ manager }: CommandDeps): SlashCommand;
8
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,6BAA6B;AAC7B,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,GAAG,YAAY,CAmBpE"}
@@ -0,0 +1,24 @@
1
+ import { getScopePeer, formatRelativeTime } from '../shared/index.js';
2
+ /** /bot-status — 查看当前会话状态 */
3
+ export function statusCommand({ manager }) {
4
+ return {
5
+ name: 'bot-status',
6
+ description: '查看当前会话状态',
7
+ handler: (cmdCtx) => {
8
+ const { scope, peerId } = getScopePeer(cmdCtx);
9
+ const status = manager.getStatus(scope, peerId);
10
+ if (!status.active)
11
+ return '当前无活跃会话';
12
+ const modelInfo = status.model ? `${status.provider}/${status.model}` : '宿主默认';
13
+ return [
14
+ '📊 会话状态',
15
+ `会话: ${status.sessionId ? status.sessionId.slice(0, 8) : '—'}`,
16
+ `模型: ${modelInfo}`,
17
+ `Preset: ${status.preset ?? '无'}`,
18
+ `消息数: ${status.messageCount ?? 0}`,
19
+ `最后活动: ${formatRelativeTime(status.lastActivity)}`,
20
+ ].join('\n');
21
+ },
22
+ };
23
+ }
24
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEtE,6BAA6B;AAC7B,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAe;IACpD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,UAAU;QACvB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;YAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YACrC,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/E,OAAO;gBACL,SAAS;gBACT,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;gBAC9D,OAAO,SAAS,EAAE;gBAClB,WAAW,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE;gBACjC,QAAQ,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE;gBAClC,SAAS,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE;aACnD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 命令依赖类型
3
+ *
4
+ * 每个命令工厂函数接收 CommandDeps,由 commands/index.ts 统一注入。
5
+ */
6
+ import type { SessionManager } from '../session/index.js';
7
+ import type { ImQQBotConfig } from '../config.js';
8
+ export interface CommandDeps {
9
+ manager: SessionManager;
10
+ config: ImQQBotConfig;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;CACvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/commands/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * dsh-im-qqbot 插件配置 Schema
3
+ */
4
+ import Schema from '@deepseek-ai/schemastery';
5
+ export interface AccessControlConfig {
6
+ /** C2C 访问模式 */
7
+ c2cMode: 'open' | 'allowlist' | 'disabled';
8
+ /** C2C 白名单(user openid) */
9
+ c2cAllow: string[];
10
+ /** 群聊访问模式 */
11
+ groupMode: 'open' | 'allowlist' | 'disabled';
12
+ /** 群聊白名单(group openid) */
13
+ groupAllow: string[];
14
+ }
15
+ export interface ImQQBotConfig {
16
+ /** QQ Bot AppID */
17
+ appId: string;
18
+ /** QQ Bot AppSecret */
19
+ appSecret: string;
20
+ /** dsh LLM 提供商名称 */
21
+ provider?: string;
22
+ /** 模型名称 */
23
+ model?: string;
24
+ /** Agent preset id */
25
+ preset?: string;
26
+ /** Agent 工作目录 */
27
+ cwd: string;
28
+ /** 是否启用群消息 @mention 门控 */
29
+ requireMention: boolean;
30
+ /** 群聊额外 system prompt */
31
+ groupPrompt?: string;
32
+ /** 私聊额外 system prompt */
33
+ directPrompt?: string;
34
+ /** 单条消息最大长度(QQ 限制约 5000 字符) */
35
+ textChunkLimit: number;
36
+ /** 每会话最大闲置时长(ms),超时自动回收 */
37
+ sessionIdleTimeout: number;
38
+ /** 并发队列最大长度 */
39
+ maxQueue: number;
40
+ /** 处理超时(ms),超时中断当前 LLM 调用 */
41
+ processingTimeoutMs: number;
42
+ /** 群历史缓冲条数 */
43
+ historyLimit: number;
44
+ /** 访问控制 */
45
+ access: AccessControlConfig;
46
+ /** 调试模式 */
47
+ debug: boolean;
48
+ }
49
+ export declare const ConfigSchema: Schema<ImQQBotConfig>;
50
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,eAAe;IACf,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa;IACb,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAC7C,0BAA0B;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,0BAA0B;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW;IACX,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,CA2B7C,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * dsh-im-qqbot 插件配置 Schema
3
+ */
4
+ import Schema from '@deepseek-ai/schemastery';
5
+ export const ConfigSchema = Schema.object({
6
+ appId: Schema.string().default('').description('QQ Bot AppID'),
7
+ appSecret: Schema.string().default('').description('QQ Bot AppSecret'),
8
+ provider: Schema.string().description('LLM provider name'),
9
+ model: Schema.string().description('Model name'),
10
+ preset: Schema.string().description('Agent preset id'),
11
+ cwd: Schema.string().default('/').description('Agent working directory'),
12
+ requireMention: Schema.boolean().default(true).description('群聊是否需要@bot触发'),
13
+ groupPrompt: Schema.string().description('群聊额外system prompt'),
14
+ directPrompt: Schema.string().description('私聊额外system prompt'),
15
+ textChunkLimit: Schema.number().default(4500).description('单条消息最大字符数'),
16
+ sessionIdleTimeout: Schema.number().default(30 * 60 * 1000).description('会话闲置超时(ms)'),
17
+ maxQueue: Schema.number().default(20).description('并发队列最大长度'),
18
+ processingTimeoutMs: Schema.number().default(120000).description('处理超时(ms)'),
19
+ historyLimit: Schema.number().default(10).description('群历史缓冲条数'),
20
+ access: Schema.object({
21
+ c2cMode: Schema.union(['open', 'allowlist', 'disabled']).default('open').description('C2C访问模式'),
22
+ c2cAllow: Schema.array(Schema.string()).default([]).description('C2C白名单'),
23
+ groupMode: Schema.union(['open', 'allowlist', 'disabled']).default('open').description('群聊访问模式'),
24
+ groupAllow: Schema.array(Schema.string()).default([]).description('群聊白名单'),
25
+ }).default({
26
+ c2cMode: 'open',
27
+ c2cAllow: [],
28
+ groupMode: 'open',
29
+ groupAllow: [],
30
+ }).description('访问控制'),
31
+ debug: Schema.boolean().default(false),
32
+ });
33
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAgD9C,MAAM,CAAC,MAAM,YAAY,GAA0B,MAAM,CAAC,MAAM,CAAC;IAC/D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC;IAC9D,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC;IACtE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,iBAAiB,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC;IACxE,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC;IAC1E,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC7D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,mBAAmB,CAAC;IAC9D,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC;IACtE,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC;IACrF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;IAC7D,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC;IAC5E,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;IAChE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC;QAC/F,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC;QACzE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC;QAChG,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC;KAC3E,CAAC,CAAC,OAAO,CAAC;QACT,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE;KACf,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACvC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * dsh-im-qqbot — QQ Bot IM channel plugin for deepseek-harness
3
+ *
4
+ * Cordis 插件入口。将 QQ 消息平台作为 dsh 的前端协议驱动。
5
+ * 复用 @tencent-connect/qqbot-nodejs SDK 全套中间件链。
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import { type ImQQBotConfig } from './config.js';
9
+ export declare const name = "im-qqbot";
10
+ export declare const inject: string[];
11
+ export declare const Config: import("@deepseek-ai/schemastery").default<ImQQBotConfig>;
12
+ export type { ImQQBotConfig } from './config.js';
13
+ export declare function apply(ctx: Context, config: ImQQBotConfig): Promise<void>;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAmBnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAS/D,eAAO,MAAM,IAAI,aAAa,CAAC;AAC/B,eAAO,MAAM,MAAM,UAAa,CAAC;AACjC,eAAO,MAAM,MAAM,2DAAe,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC9E"}
package/dist/index.js ADDED
@@ -0,0 +1,154 @@
1
+ import { QQBot, errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, rateLimiter, slashCommand, concurrencyGuard, typingIndicator, quoteRef, historyBuffer, envelopeFormatter, MemoryHistoryStore, } from '@tencent-connect/qqbot-nodejs';
2
+ import { ConfigSchema } from './config.js';
3
+ import { SessionManager } from './session/index.js';
4
+ import { handleInbound, createOutboundHandler } from './transport/index.js';
5
+ import { buildCommandList } from './commands/index.js';
6
+ import { getProfileDir, resolveEnv, buildUserAgent } from './shared/index.js';
7
+ import { runQrSetup, persistCredentialsToProfile } from './setup.js';
8
+ // ── Cordis 插件元数据 ──
9
+ export const name = 'im-qqbot';
10
+ export const inject = ['agents'];
11
+ export const Config = ConfigSchema;
12
+ // ── 插件主体 ──
13
+ export async function apply(ctx, config) {
14
+ const agents = ctx.agents;
15
+ const logger = ctx.logger ?? console;
16
+ console.log('[im-qqbot] apply() called');
17
+ let appId = resolveEnv(config.appId, 'QQBOT_APPID');
18
+ let appSecret = resolveEnv(config.appSecret, 'QQBOT_SECRET');
19
+ // ── 凭据缺失时唤起扫码绑定 ──
20
+ if (!appId || !appSecret) {
21
+ logger.info('凭据未配置,尝试扫码绑定...');
22
+ const credentials = await runQrSetup();
23
+ if (!credentials) {
24
+ logger.error('无法获取 QQ Bot 凭据,插件未启动');
25
+ return;
26
+ }
27
+ // 持久化到 profile 配置
28
+ persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
29
+ // 写入环境变量供热更新后的下次 apply 读取
30
+ process.env.QQBOT_APPID = credentials.appId;
31
+ process.env.QQBOT_SECRET = credentials.appSecret;
32
+ // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件
33
+ // 此处直接返回,避免与热更新产生竞态
34
+ logger.info('配置已保存,等待热更新重新加载...');
35
+ return;
36
+ }
37
+ const resolvedConfig = { ...config, appId, appSecret };
38
+ await bootstrap(ctx, agents, resolvedConfig, logger);
39
+ }
40
+ async function bootstrap(ctx, agents, config, logger) {
41
+ const manager = new SessionManager(ctx, agents, config, logger);
42
+ // ── 初始化 QQ Bot SDK ──
43
+ const userAgent = buildUserAgent();
44
+ const bot = new QQBot({
45
+ appId: config.appId,
46
+ appSecret: config.appSecret,
47
+ transport: 'websocket',
48
+ userAgent,
49
+ logger,
50
+ });
51
+ logger.info(`QQBot SDK initialized (UA: ${userAgent})`);
52
+ // ══════════════════════════════════════════════════════════════
53
+ // SDK 中间件链(洋葱模型,按执行顺序编排)
54
+ // ══════════════════════════════════════════════════════════════
55
+ // 1. 错误兜底(最外层洋葱皮)
56
+ bot.use(errorHandler());
57
+ // 2. 消息过滤:bot 回声 + 消息去重
58
+ bot.use(messageFilter({ skipSelfEcho: false }));
59
+ // 3. 访问控制(白名单/开放/禁用)
60
+ bot.use(accessPolicy({
61
+ c2c: {
62
+ mode: config.access.c2cMode,
63
+ allow: config.access.c2cAllow.length > 0 ? config.access.c2cAllow : ['*'],
64
+ },
65
+ group: {
66
+ mode: config.access.groupMode,
67
+ allow: config.access.groupAllow.length > 0 ? config.access.groupAllow : ['*'],
68
+ },
69
+ onBlock: (_mCtx, reason) => {
70
+ if (config.debug) {
71
+ logger.debug(`Access blocked: ${reason}`);
72
+ }
73
+ },
74
+ }));
75
+ // 4. 群历史缓冲 — 放在门控之前,确保所有消息(含未 @bot)都计入上下文
76
+ const historyStore = new MemoryHistoryStore();
77
+ bot.use(historyBuffer({
78
+ limit: config.historyLimit,
79
+ store: historyStore,
80
+ recordOnSkip: true,
81
+ }));
82
+ // 5. 群聊 @bot 门控
83
+ bot.use(mentionGate({
84
+ requireMentionInGroup: config.requireMention,
85
+ }));
86
+ // 6. 内容清洗(去 @marker、表情标签、多余空白)
87
+ bot.use(contentSanitizer({
88
+ parseFaceTags: true,
89
+ }));
90
+ // 7. 三层限流(sender / group / global)
91
+ bot.use(rateLimiter());
92
+ // 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
93
+ const slash = slashCommand({
94
+ autoHelp: true,
95
+ commands: buildCommandList({ manager, config }),
96
+ });
97
+ bot.use(slash.middleware);
98
+ // 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
99
+ bot.use(concurrencyGuard({
100
+ strategy: 'merge',
101
+ maxQueue: config.maxQueue,
102
+ maxProcessingMs: config.processingTimeoutMs,
103
+ urgentPredicate: (mCtx) => {
104
+ return (mCtx.message.content ?? '').trim() === '/bot-stop';
105
+ },
106
+ }));
107
+ // 10. C2C 输入状态指示
108
+ bot.use(typingIndicator());
109
+ // 11. 引用消息解析(记录 + 解析被引用原文)
110
+ bot.use(quoteRef({
111
+ maxSize: 500,
112
+ preferMsgElements: true,
113
+ }));
114
+ // 12. 上下文组装(将 history + quote + sender 组成 envelope)
115
+ bot.use(envelopeFormatter({
116
+ historyLimit: config.historyLimit,
117
+ includeQuote: true,
118
+ includeSender: true,
119
+ }));
120
+ // ══════════════════════════════════════════════════════════════
121
+ // 最终处理:经过中间件链后的消息交给 dsh agent
122
+ // ══════════════════════════════════════════════════════════════
123
+ bot.on('message', async (mCtx) => {
124
+ const msg = mCtx.message;
125
+ if (config.debug) {
126
+ logger.debug(`← message (post-middleware): ${JSON.stringify(msg, null, 2).slice(0, 500)}`);
127
+ }
128
+ await handleInbound(msg, manager, config, logger, mCtx.state);
129
+ });
130
+ // ── 出站 ──
131
+ const outboundHandler = createOutboundHandler(manager, bot, config, logger);
132
+ ctx
133
+ .on('session/event', outboundHandler);
134
+ bot.on('error', (err) => {
135
+ logger.error(`bot error: ${err instanceof Error ? err.message : String(err)}`);
136
+ });
137
+ bot.on('ready', () => {
138
+ console.log(`[im-qqbot] Bot ready! appId=${config.appId}`);
139
+ });
140
+ // ── 生命周期 ──
141
+ ctx
142
+ .effect(() => {
143
+ logger.info(`Starting bot (appId=${config.appId})`);
144
+ bot.start().catch((err) => {
145
+ logger.error(`Bot start failed: ${err instanceof Error ? err.message : String(err)}`);
146
+ });
147
+ return async () => {
148
+ logger.info('Shutting down');
149
+ await manager.disposeAll();
150
+ bot.stop();
151
+ };
152
+ }, 'im-qqbot.lifecycle');
153
+ }
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 模型路由层
3
+ *
4
+ * 统一的模型发现、路由解析、per-peer 偏好管理。
5
+ */
6
+ export { ModelResolver } from './model-resolver.js';
7
+ export { PrefsStore } from './prefs-store.js';
8
+ export { SettingsReader } from './settings-reader.js';
9
+ export type { ModelRoute, ModelEntry } from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 模型路由层
3
+ *
4
+ * 统一的模型发现、路由解析、per-peer 偏好管理。
5
+ */
6
+ export { ModelResolver } from './model-resolver.js';
7
+ export { PrefsStore } from './prefs-store.js';
8
+ export { SettingsReader } from './settings-reader.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * ModelResolver — 统一的模型发现与路由解析
3
+ *
4
+ * 职责:
5
+ * 1. 解析当前生效的默认模型路由
6
+ * 2. 列出可用 providers 和模型
7
+ * 3. 管理 per-peer 的模型偏好(委托 PrefsStore)
8
+ *
9
+ * 优先级(从高到低):
10
+ * per-peer 偏好(~/.dsh-qqbot/model-prefs.json)
11
+ * > config 显式指定(cordis.yml 的 provider/model)
12
+ * > settings.yaml 的 agent-default-model(只读,作为默认兜底)
13
+ * > 宿主 agentDefaultModel 服务
14
+ */
15
+ import type { Context } from '@deepseek-ai/cordis';
16
+ import type { ImQQBotConfig } from '../config.js';
17
+ import type { Logger } from '../types.js';
18
+ import type { ModelRoute, ModelEntry } from './types.js';
19
+ export declare class ModelResolver {
20
+ private readonly ctx;
21
+ private readonly config;
22
+ private readonly logger?;
23
+ private readonly prefs;
24
+ private readonly settings;
25
+ constructor(ctx: Context, config: ImQQBotConfig, logger?: Logger | undefined);
26
+ /**
27
+ * 获取指定 sessionKey 的有效模型路由(create 用)
28
+ *
29
+ * 优先级:per-peer 偏好 > config 显式指定 > settings.yaml > 宿主服务
30
+ */
31
+ getEffectiveRoute(sessionKey: string): ModelRoute | undefined;
32
+ /**
33
+ * 获取 resume 时覆盖 session 的模型路由(对齐 dsh-TUI 语义)
34
+ *
35
+ * 仅「用户显式切换的 per-peer 偏好」和「cordis.yml 显式配置」才覆盖
36
+ * session 自己的 requestHeader;否则返回 undefined,让 session 沿用
37
+ * 自己历史里记录的模型(session 记住模型,避免重启后模型漂移)。
38
+ */
39
+ getResumeRoute(sessionKey: string): ModelRoute | undefined;
40
+ /**
41
+ * 设置 per-peer 模型偏好并持久化到隔离文件
42
+ */
43
+ setOverride(sessionKey: string, route: ModelRoute): void;
44
+ /**
45
+ * 清除 per-peer 模型偏好并持久化
46
+ */
47
+ clearOverride(sessionKey: string): void;
48
+ /**
49
+ * 是否存在指定 session 的模型偏好
50
+ */
51
+ hasOverride(sessionKey: string): boolean;
52
+ /**
53
+ * 获取指定 sessionKey 的最新 sessionId(fork 后记录,重启恢复用)
54
+ */
55
+ getSessionId(sessionKey: string): string | undefined;
56
+ /**
57
+ * 记录指定 sessionKey 的最新 sessionId(fork 后调用)并持久化
58
+ */
59
+ setSessionId(sessionKey: string, sessionId: string): void;
60
+ /**
61
+ * 清除指定 sessionKey 的 sessionId 记录
62
+ */
63
+ clearSessionId(sessionKey: string): void;
64
+ /**
65
+ * 解析默认模型路由(不含 per-peer 偏好)
66
+ *
67
+ * 优先级:config 显式指定 > settings.yaml(只读) > 宿主 agentDefaultModel
68
+ */
69
+ resolveDefault(): ModelRoute | undefined;
70
+ /**
71
+ * 列出所有可用模型
72
+ */
73
+ listModels(): ModelEntry[];
74
+ /**
75
+ * 列出可用 provider 名称
76
+ */
77
+ listProviders(): string[];
78
+ private readFromHost;
79
+ /** 统一的 Cordis 服务访问 */
80
+ private getService;
81
+ }
82
+ //# sourceMappingURL=model-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-resolver.d.ts","sourceRoot":"","sources":["../../src/model/model-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIzD,qBAAa,aAAa;IAKtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAN1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;gBAGvB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,aAAa,EACrB,MAAM,CAAC,EAAE,MAAM,YAAA;IAQlC;;;;OAIG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI7D;;;;;;OAMG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAW1D;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAIxD;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIxC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIzD;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIxC;;;;OAIG;IACH,cAAc,IAAI,UAAU,GAAG,SAAS;IAWxC;;OAEG;IACH,UAAU,IAAI,UAAU,EAAE;IAI1B;;OAEG;IACH,aAAa,IAAI,MAAM,EAAE;IAuBzB,OAAO,CAAC,YAAY;IAoBpB,sBAAsB;IACtB,OAAO,CAAC,UAAU;CAKnB"}