@tencent-connect/dsh-qqbot 0.3.0 → 0.5.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 (183) hide show
  1. package/.orange-ci.yml +2 -68
  2. package/CHANGELOG.md +45 -0
  3. package/CHANGELOG_EN.md +84 -0
  4. package/README.md +107 -17
  5. package/README_EN.md +107 -17
  6. package/cordis.patch.yml +1 -1
  7. package/cordis.yml +46 -3
  8. package/dist/commands/help.d.ts +6 -7
  9. package/dist/commands/help.d.ts.map +1 -1
  10. package/dist/commands/help.js +23 -7
  11. package/dist/commands/help.js.map +1 -1
  12. package/dist/commands/index.d.ts +7 -5
  13. package/dist/commands/index.d.ts.map +1 -1
  14. package/dist/commands/index.js +12 -12
  15. package/dist/commands/index.js.map +1 -1
  16. package/dist/commands/misc.d.ts +10 -8
  17. package/dist/commands/misc.d.ts.map +1 -1
  18. package/dist/commands/misc.js +45 -8
  19. package/dist/commands/misc.js.map +1 -1
  20. package/dist/commands/model.d.ts +3 -4
  21. package/dist/commands/model.d.ts.map +1 -1
  22. package/dist/commands/model.js +8 -7
  23. package/dist/commands/model.js.map +1 -1
  24. package/dist/commands/preset.d.ts +8 -0
  25. package/dist/commands/preset.d.ts.map +1 -0
  26. package/dist/commands/preset.js +52 -0
  27. package/dist/commands/preset.js.map +1 -0
  28. package/dist/commands/session.d.ts +11 -7
  29. package/dist/commands/session.d.ts.map +1 -1
  30. package/dist/commands/session.js +29 -13
  31. package/dist/commands/session.js.map +1 -1
  32. package/dist/commands/status.d.ts +3 -4
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +2 -1
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/commands/types.d.ts +9 -2
  37. package/dist/commands/types.d.ts.map +1 -1
  38. package/dist/config.d.ts +38 -0
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/config.js +35 -1
  41. package/dist/config.js.map +1 -1
  42. package/dist/features/answer-parser.d.ts +9 -0
  43. package/dist/features/answer-parser.d.ts.map +1 -0
  44. package/dist/features/answer-parser.js +23 -0
  45. package/dist/features/answer-parser.js.map +1 -0
  46. package/dist/features/approval-channel.d.ts +76 -0
  47. package/dist/features/approval-channel.d.ts.map +1 -0
  48. package/dist/features/approval-channel.js +142 -0
  49. package/dist/features/approval-channel.js.map +1 -0
  50. package/dist/features/approval-renderer.d.ts +18 -0
  51. package/dist/features/approval-renderer.d.ts.map +1 -0
  52. package/dist/features/approval-renderer.js +52 -0
  53. package/dist/features/approval-renderer.js.map +1 -0
  54. package/dist/features/button-utils.d.ts +33 -0
  55. package/dist/features/button-utils.d.ts.map +1 -0
  56. package/dist/features/button-utils.js +27 -0
  57. package/dist/features/button-utils.js.map +1 -0
  58. package/dist/features/history-store.d.ts.map +1 -1
  59. package/dist/features/history-store.js +0 -1
  60. package/dist/features/history-store.js.map +1 -1
  61. package/dist/features/question-channel.d.ts +96 -0
  62. package/dist/features/question-channel.d.ts.map +1 -0
  63. package/dist/features/question-channel.js +234 -0
  64. package/dist/features/question-channel.js.map +1 -0
  65. package/dist/features/question-renderer.d.ts +20 -0
  66. package/dist/features/question-renderer.d.ts.map +1 -0
  67. package/dist/features/question-renderer.js +70 -0
  68. package/dist/features/question-renderer.js.map +1 -0
  69. package/dist/gateway/bootstrap.d.ts +3 -3
  70. package/dist/gateway/bootstrap.d.ts.map +1 -1
  71. package/dist/gateway/bootstrap.js +76 -8
  72. package/dist/gateway/bootstrap.js.map +1 -1
  73. package/dist/gateway/index.d.ts +2 -2
  74. package/dist/gateway/index.js +2 -2
  75. package/dist/gateway/middleware-setup.d.ts +3 -3
  76. package/dist/gateway/middleware-setup.d.ts.map +1 -1
  77. package/dist/gateway/middleware-setup.js +9 -7
  78. package/dist/gateway/middleware-setup.js.map +1 -1
  79. package/dist/index.d.ts +2 -2
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +16 -11
  82. package/dist/index.js.map +1 -1
  83. package/dist/media/media-cleaner.d.ts +19 -0
  84. package/dist/media/media-cleaner.d.ts.map +1 -0
  85. package/dist/media/media-cleaner.js +81 -0
  86. package/dist/media/media-cleaner.js.map +1 -0
  87. package/dist/media/send-file-tool.d.ts +35 -0
  88. package/dist/media/send-file-tool.d.ts.map +1 -0
  89. package/dist/media/send-file-tool.js +150 -0
  90. package/dist/media/send-file-tool.js.map +1 -0
  91. package/dist/media/vision-tool.d.ts +16 -0
  92. package/dist/media/vision-tool.d.ts.map +1 -0
  93. package/dist/media/vision-tool.js +241 -0
  94. package/dist/media/vision-tool.js.map +1 -0
  95. package/dist/middleware/attachment.d.ts +3 -3
  96. package/dist/middleware/attachment.d.ts.map +1 -1
  97. package/dist/middleware/attachment.js +17 -4
  98. package/dist/middleware/attachment.js.map +1 -1
  99. package/dist/middleware/question-answer.d.ts +10 -0
  100. package/dist/middleware/question-answer.d.ts.map +1 -0
  101. package/dist/middleware/question-answer.js +19 -0
  102. package/dist/middleware/question-answer.js.map +1 -0
  103. package/dist/model/index.d.ts +4 -4
  104. package/dist/model/index.js +3 -3
  105. package/dist/model/model-resolver.d.ts +27 -13
  106. package/dist/model/model-resolver.d.ts.map +1 -1
  107. package/dist/model/model-resolver.js +82 -27
  108. package/dist/model/model-resolver.js.map +1 -1
  109. package/dist/model/prefs-store.d.ts +6 -1
  110. package/dist/model/prefs-store.d.ts.map +1 -1
  111. package/dist/model/prefs-store.js +25 -1
  112. package/dist/model/prefs-store.js.map +1 -1
  113. package/dist/model/settings-reader.d.ts +1 -1
  114. package/dist/session/idle-evictor.d.ts +2 -2
  115. package/dist/session/idle-evictor.d.ts.map +1 -1
  116. package/dist/session/idle-evictor.js +1 -1
  117. package/dist/session/idle-evictor.js.map +1 -1
  118. package/dist/session/index.d.ts +3 -3
  119. package/dist/session/index.d.ts.map +1 -1
  120. package/dist/session/index.js +2 -2
  121. package/dist/session/session-manager.d.ts +67 -9
  122. package/dist/session/session-manager.d.ts.map +1 -1
  123. package/dist/session/session-manager.js +216 -20
  124. package/dist/session/session-manager.js.map +1 -1
  125. package/dist/session/types.d.ts +47 -1
  126. package/dist/session/types.d.ts.map +1 -1
  127. package/dist/setup.d.ts +4 -4
  128. package/dist/setup.d.ts.map +1 -1
  129. package/dist/setup.js +63 -28
  130. package/dist/setup.js.map +1 -1
  131. package/dist/shared/index.d.ts +3 -3
  132. package/dist/shared/index.d.ts.map +1 -1
  133. package/dist/shared/index.js +3 -3
  134. package/dist/shared/index.js.map +1 -1
  135. package/dist/shared/send-helper.js +1 -1
  136. package/dist/shared/utils.d.ts +8 -1
  137. package/dist/shared/utils.d.ts.map +1 -1
  138. package/dist/shared/utils.js +18 -7
  139. package/dist/shared/utils.js.map +1 -1
  140. package/dist/transport/attachment.d.ts +18 -7
  141. package/dist/transport/attachment.d.ts.map +1 -1
  142. package/dist/transport/attachment.js +77 -30
  143. package/dist/transport/attachment.js.map +1 -1
  144. package/dist/transport/inbound.d.ts +5 -4
  145. package/dist/transport/inbound.d.ts.map +1 -1
  146. package/dist/transport/inbound.js +120 -80
  147. package/dist/transport/inbound.js.map +1 -1
  148. package/dist/transport/index.d.ts +4 -4
  149. package/dist/transport/index.js +4 -4
  150. package/dist/transport/msgid-cache.d.ts +23 -0
  151. package/dist/transport/msgid-cache.d.ts.map +1 -0
  152. package/dist/transport/msgid-cache.js +57 -0
  153. package/dist/transport/msgid-cache.js.map +1 -0
  154. package/dist/transport/outbound-buffer.d.ts +9 -6
  155. package/dist/transport/outbound-buffer.d.ts.map +1 -1
  156. package/dist/transport/outbound-buffer.js +5 -5
  157. package/dist/transport/outbound-buffer.js.map +1 -1
  158. package/dist/transport/outbound.d.ts +9 -9
  159. package/dist/transport/outbound.d.ts.map +1 -1
  160. package/dist/transport/outbound.js +7 -7
  161. package/dist/transport/outbound.js.map +1 -1
  162. package/dist/transport/reply-limiter.d.ts +31 -0
  163. package/dist/transport/reply-limiter.d.ts.map +1 -0
  164. package/dist/transport/reply-limiter.js +50 -0
  165. package/dist/transport/reply-limiter.js.map +1 -0
  166. package/dist/transport/reply-target.d.ts +29 -0
  167. package/dist/transport/reply-target.d.ts.map +1 -0
  168. package/dist/transport/reply-target.js +55 -0
  169. package/dist/transport/reply-target.js.map +1 -0
  170. package/dist/transport/streaming-writer.d.ts +3 -3
  171. package/dist/transport/streaming-writer.d.ts.map +1 -1
  172. package/dist/transport/streaming-writer.js +1 -1
  173. package/dist/transport/streaming-writer.js.map +1 -1
  174. package/dist/transport/tool-presenter.d.ts +0 -1
  175. package/dist/transport/tool-presenter.d.ts.map +1 -1
  176. package/dist/transport/tool-presenter.js +0 -1
  177. package/dist/transport/tool-presenter.js.map +1 -1
  178. package/dist/types.d.ts +9 -15
  179. package/dist/types.d.ts.map +1 -1
  180. package/docs/assets/qqchannel.jpg +0 -0
  181. package/docs/assets/qqgroup.jpg +0 -0
  182. package/docs/assets/qrcode.png +0 -0
  183. package/package.json +21 -4
@@ -1,3 +1,3 @@
1
- export { setupMiddlewares } from './middleware-setup.js';
2
- export { bootstrapGateway } from './bootstrap.js';
1
+ export { setupMiddlewares } from "./middleware-setup.js";
2
+ export { bootstrapGateway } from "./bootstrap.js";
3
3
  //# sourceMappingURL=index.js.map
@@ -5,8 +5,8 @@
5
5
  * 中间件负责过滤与上下文富化;最终消息统一由 bot.on('message') 处理转发。
6
6
  */
7
7
  import type { QQBot } from '@tencent-connect/qqbot-nodejs';
8
- import type { ImQQBotConfig } from '../config.js';
9
- import type { SessionManager } from '../session/index.js';
10
- import type { Logger } from '../types.js';
8
+ import type { ImQQBotConfig } from '../config.ts';
9
+ import type { SessionManager } from '../session/index.ts';
10
+ import type { Logger } from '../types.ts';
11
11
  export declare function setupMiddlewares(bot: QQBot, config: ImQQBotConfig, manager: SessionManager, logger: Logger): void;
12
12
  //# sourceMappingURL=middleware-setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAqB,MAAM,+BAA+B,CAAC;AAe9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAK1C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,EACV,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAqFN"}
1
+ {"version":3,"file":"middleware-setup.d.ts","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAe3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAM1C,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,EACV,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAuFN"}
@@ -1,7 +1,8 @@
1
1
  import { errorHandler, messageFilter, accessPolicy, mentionGate, contentSanitizer, rateLimiter, slashCommand, concurrencyGuard, typingIndicator, quoteRef, historyBuffer, envelopeFormatter, } from '@tencent-connect/qqbot-nodejs';
2
- import { buildCommandList } from '../commands/index.js';
3
- import { attachmentProcessor } from '../middleware/attachment.js';
4
- import { getHistoryStore, historyGroupKey } from '../features/history-store.js';
2
+ import { buildCommandList } from "../commands/index.js";
3
+ import { attachmentProcessor } from "../middleware/attachment.js";
4
+ import { questionAnswer } from "../middleware/question-answer.js";
5
+ import { getHistoryStore, historyGroupKey } from "../features/history-store.js";
5
6
  export function setupMiddlewares(bot, config, manager, logger) {
6
7
  // 1. 错误兜底(最外层洋葱皮)
7
8
  bot.use(errorHandler());
@@ -48,18 +49,19 @@ export function setupMiddlewares(bot, config, manager, logger) {
48
49
  bot.use(rateLimiter());
49
50
  // 8. 斜杠命令(在 concurrencyGuard 之前,命令匹配后不排队直接响应)
50
51
  const slash = slashCommand({
51
- autoHelp: true,
52
+ // 关闭 autoHelp:help 命令由 qqbot 注册为 /bot-help,
53
+ // 避免 SDK 再自动注册一个无前缀的 /help 造成重复
54
+ autoHelp: false,
52
55
  commands: buildCommandList({ manager, config }),
53
56
  });
54
57
  bot.use(slash.middleware);
58
+ // 8.5. 待答问题截获(在 concurrencyGuard 之前,答案消息不排队直接作答)
59
+ bot.use(questionAnswer(manager));
55
60
  // 9. 并发串行 + 消息合并(同 peer 排队,避免 session 冲突)
56
61
  bot.use(concurrencyGuard({
57
62
  strategy: 'merge',
58
63
  maxQueue: config.maxQueue,
59
64
  maxProcessingMs: config.processingTimeoutMs,
60
- urgentPredicate: (mCtx) => {
61
- return (mCtx.message.content ?? '').trim() === '/bot-stop';
62
- },
63
65
  }));
64
66
  // 10. C2C 输入状态指示
65
67
  bot.use(typingIndicator());
@@ -1 +1 @@
1
- {"version":3,"file":"middleware-setup.js","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware-setup.js","sourceRoot":"","sources":["../../src/gateway/middleware-setup.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -5,10 +5,10 @@
5
5
  * 网关组装(中间件编排 + 事件 + 出站 + 生命周期)见 src/gateway/。
6
6
  */
7
7
  import type { Context } from '@deepseek-ai/cordis';
8
- import { type ImQQBotConfig } from './config.js';
8
+ import { type ImQQBotConfig } from './config.ts';
9
9
  export declare const name = "im-qqbot";
10
10
  export declare const inject: string[];
11
11
  export declare const Config: import("@deepseek-ai/schemastery").default<ImQQBotConfig>;
12
- export type { ImQQBotConfig } from './config.js';
12
+ export type { ImQQBotConfig } from './config.ts';
13
13
  export declare function apply(ctx: Context, config: ImQQBotConfig): Promise<void>;
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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;AACnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/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"}
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;AACnD,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/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,CAuC9E"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { ConfigSchema } from './config.js';
2
- import { bootstrapGateway } from './gateway/index.js';
3
- import { getProfileDir, resolveEnv } from './shared/index.js';
4
- import { runQrSetup, persistCredentialsToProfile } from './setup.js';
1
+ import { ConfigSchema } from "./config.js";
2
+ import { bootstrapGateway } from "./gateway/index.js";
3
+ import { getProfileDir, resolveEnv } from "./shared/index.js";
4
+ import { runQrSetup, persistCredentialsToProfile } from "./setup.js";
5
5
  // ── Cordis 插件元数据 ──
6
6
  export const name = 'im-qqbot';
7
7
  export const inject = ['agents'];
@@ -21,15 +21,20 @@ export async function apply(ctx, config) {
21
21
  logger.error('无法获取 QQ Bot 凭据,插件未启动');
22
22
  return;
23
23
  }
24
- // 持久化到 profile 配置
25
- persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
26
- // 写入环境变量供热更新后的下次 apply 读取
24
+ // 写入环境变量(供热更新后的下次 apply 或本次直接启动读取)
27
25
  process.env.QQBOT_APPID = credentials.appId;
28
26
  process.env.QQBOT_SECRET = credentials.appSecret;
29
- // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件
30
- // 此处直接返回,避免与热更新产生竞态
31
- logger.info('配置已保存,等待热更新重新加载...');
32
- return;
27
+ appId = credentials.appId;
28
+ appSecret = credentials.appSecret;
29
+ // 持久化到 profile:成功则等待热更新重载,失败则用 env 凭据直接启动
30
+ const persisted = persistCredentialsToProfile(credentials, getProfileDir() ?? undefined, logger);
31
+ if (persisted) {
32
+ // 写入 cordis.patch.yml 会触发 dsh 热更新,自动重新加载本插件。
33
+ // 直接返回,避免与热更新产生竞态。
34
+ logger.info('配置已保存,等待热更新重新加载...');
35
+ return;
36
+ }
37
+ logger.warn('凭据未能持久化,本次进程将使用环境变量凭据启动(重启后需重新绑定)');
33
38
  }
34
39
  const resolvedConfig = { ...config, appId, appSecret };
35
40
  await bootstrapGateway(ctx, agents, resolvedConfig, logger);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAGrE,qBAAqB;AACrB,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC;AAC/B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC;AAInC,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqB;IAC7D,MAAM,MAAM,GAAI,GAA0C,CAAC,MAA0B,CAAC;IACtF,MAAM,MAAM,GAAa,GAA0C,CAAC,MAAiB,IAAI,OAAO,CAAC;IAEjG,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAEzC,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE7D,oBAAoB;IACpB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,WAAW,GAAG,MAAM,UAAU,EAAE,CAAC;QAEvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAED,kBAAkB;QAClB,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;QAE/E,0BAA0B;QAC1B,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC;QAEjD,4CAA4C;QAC5C,oBAAoB;QACpB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAkB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEtE,MAAM,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAGrE,qBAAqB;AACrB,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC;AAC/B,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC;AACjC,MAAM,CAAC,MAAM,MAAM,GAAG,YAAY,CAAC;AAInC,aAAa;AACb,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,MAAqB;IAC7D,MAAM,MAAM,GAAI,GAA0C,CAAC,MAA0B,CAAC;IACtF,MAAM,MAAM,GAAa,GAA0C,CAAC,MAAiB,IAAI,OAAO,CAAC;IAEjG,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAEzC,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACpD,IAAI,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAE7D,oBAAoB;IACpB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,WAAW,GAAG,MAAM,UAAU,EAAE,CAAC;QAEvC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAED,mCAAmC;QACnC,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC;QACjD,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC;QAC1B,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;QAElC,0CAA0C;QAC1C,MAAM,SAAS,GAAG,2BAA2B,CAAC,WAAW,EAAE,aAAa,EAAE,IAAI,SAAS,EAAE,MAAM,CAAC,CAAC;QACjG,IAAI,SAAS,EAAE,CAAC;YACd,6CAA6C;YAC7C,mBAAmB;YACnB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,cAAc,GAAkB,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAEtE,MAAM,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { Logger } from '../types.ts';
3
+ /** 富媒体下载根目录(attachment.ts 复用) */
4
+ export declare const MEDIA_ROOT: string;
5
+ /**
6
+ * 清理过期的富媒体文件(按文件 mtime 判断,异步并发执行,不阻塞事件循环)
7
+ *
8
+ * @param ttlHours 保留时长(小时),<=0 表示永不过期(跳过清理)
9
+ * @returns 删除的文件数
10
+ */
11
+ export declare function cleanupExpiredMedia(ttlHours: number, logger?: Logger): Promise<number>;
12
+ /**
13
+ * 启动富媒体过期清理:启动时清理一次 + 定期清理(每 1 小时)
14
+ *
15
+ * 用 ctx.effect 挂载定时器,插件卸载时自动 clearInterval。
16
+ * ttlHours <= 0 时不启动(永不过期)。
17
+ */
18
+ export declare function startMediaCleanup(ctx: Context, ttlHours: number, logger: Logger): void;
19
+ //# sourceMappingURL=media-cleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-cleaner.d.ts","sourceRoot":"","sources":["../../src/media/media-cleaner.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,iCAAiC;AACjC,eAAO,MAAM,UAAU,QAA4C,CAAC;AAKpE;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB5F;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA4BtF"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 富媒体过期清理 — 避免下载文件持续膨胀
3
+ *
4
+ * 下载目录 ~/.dsh-qqbot/media,文件写入后不再变更,故文件 mtime 即「下载时间」。
5
+ * 清理时按文件 mtime 判断是否超过保留时长。
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { readdir, rm, stat } from 'node:fs/promises';
9
+ import { join, resolve } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ /** 富媒体下载根目录(attachment.ts 复用) */
12
+ export const MEDIA_ROOT = resolve(homedir(), '.dsh-qqbot', 'media');
13
+ /** 定期清理间隔(毫秒),固定 1 小时 */
14
+ const CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
15
+ /**
16
+ * 清理过期的富媒体文件(按文件 mtime 判断,异步并发执行,不阻塞事件循环)
17
+ *
18
+ * @param ttlHours 保留时长(小时),<=0 表示永不过期(跳过清理)
19
+ * @returns 删除的文件数
20
+ */
21
+ export async function cleanupExpiredMedia(ttlHours, logger) {
22
+ if (ttlHours <= 0)
23
+ return 0;
24
+ if (!existsSync(MEDIA_ROOT))
25
+ return 0;
26
+ const now = Date.now();
27
+ const ttlMs = ttlHours * 60 * 60 * 1000;
28
+ const entries = await readdir(MEDIA_ROOT, { withFileTypes: true });
29
+ const files = entries.filter(entry => entry.isFile()).map(entry => entry.name);
30
+ const results = await Promise.all(files.map(async (name) => {
31
+ const filePath = join(MEDIA_ROOT, name);
32
+ try {
33
+ const info = await stat(filePath);
34
+ if (now - info.mtimeMs <= ttlMs)
35
+ return false;
36
+ await rm(filePath, { force: true });
37
+ logger?.debug(`im-qqbot: cleaned expired media: ${name}`);
38
+ return true;
39
+ }
40
+ catch (err) {
41
+ logger?.warn(`im-qqbot: media cleanup failed for ${name}: ${err instanceof Error ? err.message : String(err)}`);
42
+ return false;
43
+ }
44
+ }));
45
+ return results.filter(Boolean).length;
46
+ }
47
+ /**
48
+ * 启动富媒体过期清理:启动时清理一次 + 定期清理(每 1 小时)
49
+ *
50
+ * 用 ctx.effect 挂载定时器,插件卸载时自动 clearInterval。
51
+ * ttlHours <= 0 时不启动(永不过期)。
52
+ */
53
+ export function startMediaCleanup(ctx, ttlHours, logger) {
54
+ if (ttlHours <= 0)
55
+ return;
56
+ logger.info(`im-qqbot: media cleanup started (ttl=${ttlHours}h, interval=1h, root=${MEDIA_ROOT})`);
57
+ // 启动时立即清理一次(异步,不阻塞启动流程)
58
+ void cleanupExpiredMedia(ttlHours, logger)
59
+ .then((cleaned) => {
60
+ if (cleaned > 0)
61
+ logger.info(`im-qqbot: cleaned ${cleaned} expired media files`);
62
+ })
63
+ .catch((err) => {
64
+ logger.warn(`im-qqbot: media cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
65
+ });
66
+ ctx
67
+ .effect(() => {
68
+ const timer = setInterval(() => {
69
+ void cleanupExpiredMedia(ttlHours, logger)
70
+ .then((n) => {
71
+ if (n > 0)
72
+ logger.info(`im-qqbot: cleaned ${n} expired media files`);
73
+ })
74
+ .catch((err) => {
75
+ logger.warn(`im-qqbot: media cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
76
+ });
77
+ }, CLEANUP_INTERVAL_MS);
78
+ return async () => { clearInterval(timer); };
79
+ }, 'im-qqbot.media-cleanup');
80
+ }
81
+ //# sourceMappingURL=media-cleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-cleaner.js","sourceRoot":"","sources":["../../src/media/media-cleaner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ImQQBotConfig } from '../config.ts';
3
+ import type { SessionManager } from '../session/index.ts';
4
+ import type { Logger, ReplyTarget } from '../types.ts';
5
+ /** 工具名(qqbot 前缀避免与其他通道的同名工具冲突) */
6
+ export declare const SEND_FILE_TOOL_NAME = "qqbot_send_file";
7
+ /** 发送器最小接口(QQBot 实例满足) */
8
+ interface MediaSendResult {
9
+ upload: {
10
+ file_uuid: string;
11
+ };
12
+ message?: {
13
+ id?: string;
14
+ };
15
+ }
16
+ export interface MediaSenderLike {
17
+ sendImage(target: ReplyTarget, source: {
18
+ localPath?: string;
19
+ }): Promise<MediaSendResult>;
20
+ sendVideo(target: ReplyTarget, source: {
21
+ localPath?: string;
22
+ }): Promise<MediaSendResult>;
23
+ sendVoice(target: ReplyTarget, source: {
24
+ localPath?: string;
25
+ }): Promise<MediaSendResult>;
26
+ sendFile(target: ReplyTarget, source: {
27
+ localPath?: string;
28
+ }, opts?: {
29
+ fileName?: string;
30
+ }): Promise<MediaSendResult>;
31
+ }
32
+ /** 注册 qqbot_send_file 工具(tools 服务缺失时优雅降级,不阻断插件启动) */
33
+ export declare function registerSendFileTool(ctx: Context, bot: MediaSenderLike, manager: SessionManager, config: ImQQBotConfig, logger: Logger): void;
34
+ export {};
35
+ //# sourceMappingURL=send-file-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-file-tool.d.ts","sourceRoot":"","sources":["../../src/media/send-file-tool.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGvD,kCAAkC;AAClC,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAQrD,0BAA0B;AAC1B,UAAU,eAAe;IACvB,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CACvH;AAwCD,qDAAqD;AACrD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,GACb,IAAI,CA4GN"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * 内置 qqbot_send_file 工具 — 将本地文件发送给 QQ 用户或群。
3
+ *
4
+ * - 发送目标默认自动定位(exec.agent → SessionManager.findByAgent → replyTarget),
5
+ * 无需模型手动传 openid;显式 target 参数作为覆盖。
6
+ * - 按扩展名分发到 SDK 的 sendImage / sendVideo / sendVoice / sendFile。
7
+ * - 路径白名单:默认只允许访问 media 目录 + agent cwd,可开关 + 扩展白名单。
8
+ */
9
+ import { existsSync } from 'node:fs';
10
+ import { stat } from 'node:fs/promises';
11
+ import { basename, isAbsolute, relative, resolve } from 'node:path';
12
+ import { MEDIA_ROOT } from "./media-cleaner.js";
13
+ /** 工具名(qqbot 前缀避免与其他通道的同名工具冲突) */
14
+ export const SEND_FILE_TOOL_NAME = 'qqbot_send_file';
15
+ const DESCRIPTION = 'Send a local file (image, video, voice, or generic file) to the QQ user or group '
16
+ + 'of the current conversation. Pass the absolute path of a file that already exists on disk. '
17
+ + 'Use this when the user asks you to deliver a generated file (chart, report, exported data, '
18
+ + 'screenshot, etc.) or to send an existing media/file back to them.';
19
+ const IMAGE_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp']);
20
+ const VIDEO_EXTS = new Set(['.mp4', '.mov', '.avi', '.mkv', '.webm']);
21
+ const VOICE_EXTS = new Set(['.mp3', '.wav', '.ogg', '.aac', '.silk', '.amr']);
22
+ /** 解析显式 target("c2c:openid" / "group:openid"),非法返回 undefined */
23
+ function parseTarget(input) {
24
+ const idx = input.indexOf(':');
25
+ if (idx <= 0)
26
+ return undefined;
27
+ const scope = input.slice(0, idx);
28
+ const targetId = input.slice(idx + 1);
29
+ if (!targetId || (scope !== 'c2c' && scope !== 'group'))
30
+ return undefined;
31
+ return { scope, targetId };
32
+ }
33
+ /** 判断 filePath 是否位于 root 目录内(含 root 本身) */
34
+ function isWithinRoot(filePath, root) {
35
+ const rel = relative(resolve(root), filePath);
36
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
37
+ }
38
+ /** 路径白名单校验:restrictPaths 关闭时放行任意路径,否则限定 media + cwd + extraRoots */
39
+ function isPathAllowed(filePath, config, cwd) {
40
+ if (!config.restrictPaths)
41
+ return true;
42
+ const roots = [MEDIA_ROOT, cwd, ...config.extraRoots];
43
+ return roots.some((root) => isWithinRoot(filePath, root));
44
+ }
45
+ /** 注册 qqbot_send_file 工具(tools 服务缺失时优雅降级,不阻断插件启动) */
46
+ export function registerSendFileTool(ctx, bot, manager, config, logger) {
47
+ const tools = ctx.get('tools');
48
+ if (!tools?.register) {
49
+ logger.warn('im-qqbot: tools 服务不可用,qqbot_send_file 工具未注册');
50
+ return;
51
+ }
52
+ const cwd = config.cwd || process.cwd();
53
+ const definition = {
54
+ name: SEND_FILE_TOOL_NAME,
55
+ description: DESCRIPTION,
56
+ parameters: {
57
+ type: 'object',
58
+ properties: {
59
+ file_path: {
60
+ type: 'string',
61
+ description: 'Absolute path to the local file to send (image/video/voice/generic file).',
62
+ },
63
+ target: {
64
+ type: 'string',
65
+ description: 'Optional send target as "c2c:openid" or "group:openid". Omit to send to the current conversation.',
66
+ },
67
+ },
68
+ required: ['file_path'],
69
+ additionalProperties: false,
70
+ },
71
+ output: {
72
+ schema: {
73
+ type: 'object',
74
+ properties: {
75
+ fileName: { type: 'string' },
76
+ fileSize: { type: 'integer' },
77
+ target: { type: 'string' },
78
+ fileUuid: { type: 'string' },
79
+ messageId: { type: 'string' },
80
+ },
81
+ required: ['fileName', 'fileSize', 'target', 'fileUuid'],
82
+ additionalProperties: false,
83
+ },
84
+ render: (_args, value) => {
85
+ const v = value;
86
+ return [{ type: 'text', text: `已发送 ${v.fileName} → ${v.target}` }];
87
+ },
88
+ },
89
+ async execute(args, exec) {
90
+ const { file_path, target } = args;
91
+ if (typeof file_path !== 'string' || file_path.length === 0) {
92
+ throw new Error('qqbot_send_file: `file_path` must be a non-empty string');
93
+ }
94
+ // 1. 解析发送目标:显式 target > 当前会话(exec.agent → SessionRecord)
95
+ let replyTarget;
96
+ if (target !== undefined && target !== '') {
97
+ replyTarget = parseTarget(target);
98
+ if (replyTarget === undefined) {
99
+ throw new Error(`qqbot_send_file: target 格式错误,需要 c2c:openid 或 group:openid,收到: ${target}`);
100
+ }
101
+ }
102
+ else if (exec.agent !== undefined) {
103
+ replyTarget = manager.findByAgent(exec.agent)?.replyTarget;
104
+ }
105
+ if (replyTarget === undefined) {
106
+ throw new Error('qqbot_send_file: 无法确定发送目标,请显式传 target 参数');
107
+ }
108
+ // 2. 校验文件 + 路径白名单
109
+ const absPath = resolve(file_path);
110
+ if (!isPathAllowed(absPath, config.sendFile, cwd)) {
111
+ throw new Error(`qqbot_send_file: file_path 不在允许的目录内: ${absPath}`);
112
+ }
113
+ if (!existsSync(absPath)) {
114
+ throw new Error(`qqbot_send_file: 文件不存在: ${absPath}`);
115
+ }
116
+ const info = await stat(absPath);
117
+ if (!info.isFile()) {
118
+ throw new Error(`qqbot_send_file: 路径不是普通文件: ${absPath}`);
119
+ }
120
+ // 3. 按扩展名分类发送
121
+ const fileName = basename(absPath);
122
+ const ext = absPath.slice(absPath.lastIndexOf('.')).toLowerCase();
123
+ const source = { localPath: absPath };
124
+ let result;
125
+ if (IMAGE_EXTS.has(ext)) {
126
+ result = await bot.sendImage(replyTarget, source);
127
+ }
128
+ else if (VIDEO_EXTS.has(ext)) {
129
+ result = await bot.sendVideo(replyTarget, source);
130
+ }
131
+ else if (VOICE_EXTS.has(ext)) {
132
+ result = await bot.sendVoice(replyTarget, source);
133
+ }
134
+ else {
135
+ result = await bot.sendFile(replyTarget, source, { fileName });
136
+ }
137
+ logger.info(`im-qqbot: qqbot_send_file 发送 ${fileName} (${info.size} bytes) → ${replyTarget.scope}:${replyTarget.targetId}`);
138
+ return {
139
+ fileName,
140
+ fileSize: info.size,
141
+ target: `${replyTarget.scope}:${replyTarget.targetId}`,
142
+ fileUuid: result.upload.file_uuid,
143
+ ...(result.message?.id !== undefined ? { messageId: result.message.id } : {}),
144
+ };
145
+ },
146
+ };
147
+ tools.register(definition);
148
+ logger.info('im-qqbot: qqbot_send_file 工具已注册');
149
+ }
150
+ //# sourceMappingURL=send-file-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-file-tool.js","sourceRoot":"","sources":["../../src/media/send-file-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { VisionConfig } from '../config.ts';
3
+ import type { Logger } from '../types.ts';
4
+ /** 工具名(qqbot 前缀避免与外部 describe-image 包的同名工具冲突) */
5
+ export declare const DESCRIBE_IMAGE_TOOL_NAME = "qqbot_describe_image";
6
+ /**
7
+ * 启动时检查 settings.yaml 里 vision 模型的 input 模态是否声明了 image。
8
+ *
9
+ * 模型配置界面不暴露 `input` 字段,用户通过界面配置的视觉模型默认是纯文本,
10
+ * 会导致 pi-ai adapter 拒绝图片输入。这里在启动时兜底:未声明 image 时自动补 `[text, image]`。
11
+ * 幂等(已声明则跳过),解析/写入失败仅 warn 不阻断启动。
12
+ */
13
+ export declare function ensureVisionInputModal(vision: VisionConfig, logger: Logger): void;
14
+ /** 注册 qqbot_describe_image 工具(服务缺失时优雅降级,不阻断插件启动) */
15
+ export declare function registerDescribeImageTool(ctx: Context, vision: VisionConfig, logger: Logger): void;
16
+ //# sourceMappingURL=vision-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vision-tool.d.ts","sourceRoot":"","sources":["../../src/media/vision-tool.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AASnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AA2B1C,iDAAiD;AACjD,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAwH/D;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAwCjF;AAED,oDAAoD;AACpD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAqElG"}