dsh-connect 0.7.2 → 0.9.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 (153) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +196 -43
  3. package/README.zh.md +171 -42
  4. package/client/client.js +817 -0
  5. package/client/client.js.map +7 -0
  6. package/client/locale.mjs +227 -0
  7. package/client/panel-state.mjs +54 -0
  8. package/client/settings-client.mjs +389 -0
  9. package/docs/images/settings-advanced-en.png +0 -0
  10. package/docs/images/settings-advanced-zh.png +0 -0
  11. package/docs/images/settings-defaults-en.png +0 -0
  12. package/docs/images/settings-defaults-zh.png +0 -0
  13. package/docs/images/settings-overview-en.png +0 -0
  14. package/docs/images/settings-overview-zh.png +0 -0
  15. package/examples/minimal.config.json +16 -0
  16. package/lib/binding.d.ts.map +1 -1
  17. package/lib/binding.js +2 -2
  18. package/lib/binding.js.map +1 -1
  19. package/lib/channels/dingtalk/adapter.d.ts +59 -0
  20. package/lib/channels/dingtalk/adapter.d.ts.map +1 -0
  21. package/lib/channels/dingtalk/adapter.js +123 -0
  22. package/lib/channels/dingtalk/adapter.js.map +1 -0
  23. package/lib/channels/dingtalk/i18n.d.ts +14 -0
  24. package/lib/channels/dingtalk/i18n.d.ts.map +1 -0
  25. package/lib/channels/dingtalk/i18n.js +16 -0
  26. package/lib/channels/dingtalk/i18n.js.map +1 -0
  27. package/lib/channels/dingtalk/index.d.ts +127 -0
  28. package/lib/channels/dingtalk/index.d.ts.map +1 -0
  29. package/lib/channels/dingtalk/index.js +144 -0
  30. package/lib/channels/dingtalk/index.js.map +1 -0
  31. package/lib/channels/dingtalk/message.d.ts +49 -0
  32. package/lib/channels/dingtalk/message.d.ts.map +1 -0
  33. package/lib/channels/dingtalk/message.js +50 -0
  34. package/lib/channels/dingtalk/message.js.map +1 -0
  35. package/lib/channels/dingtalk/stomp.d.ts +36 -0
  36. package/lib/channels/dingtalk/stomp.d.ts.map +1 -0
  37. package/lib/channels/dingtalk/stomp.js +107 -0
  38. package/lib/channels/dingtalk/stomp.js.map +1 -0
  39. package/lib/channels/dingtalk/stream.d.ts +53 -0
  40. package/lib/channels/dingtalk/stream.d.ts.map +1 -0
  41. package/lib/channels/dingtalk/stream.js +199 -0
  42. package/lib/channels/dingtalk/stream.js.map +1 -0
  43. package/lib/channels/dingtalk/webhook.d.ts +84 -0
  44. package/lib/channels/dingtalk/webhook.d.ts.map +1 -0
  45. package/lib/channels/dingtalk/webhook.js +143 -0
  46. package/lib/channels/dingtalk/webhook.js.map +1 -0
  47. package/lib/channels/feishu/adapter.d.ts +133 -0
  48. package/lib/channels/feishu/adapter.d.ts.map +1 -0
  49. package/lib/channels/feishu/adapter.js +662 -0
  50. package/lib/channels/feishu/adapter.js.map +1 -0
  51. package/lib/channels/feishu/i18n.d.ts +30 -0
  52. package/lib/channels/feishu/i18n.d.ts.map +1 -0
  53. package/lib/channels/feishu/i18n.js +42 -0
  54. package/lib/channels/feishu/i18n.js.map +1 -0
  55. package/lib/channels/feishu/index.d.ts +82 -0
  56. package/lib/channels/feishu/index.d.ts.map +1 -0
  57. package/lib/channels/feishu/index.js +103 -0
  58. package/lib/channels/feishu/index.js.map +1 -0
  59. package/lib/channels/feishu/onboard.d.ts +16 -0
  60. package/lib/channels/feishu/onboard.d.ts.map +1 -0
  61. package/lib/channels/feishu/onboard.js +86 -0
  62. package/lib/channels/feishu/onboard.js.map +1 -0
  63. package/lib/channels/telegram/adapter.d.ts +70 -0
  64. package/lib/channels/telegram/adapter.d.ts.map +1 -0
  65. package/lib/channels/telegram/adapter.js +460 -0
  66. package/lib/channels/telegram/adapter.js.map +1 -0
  67. package/lib/channels/telegram/client.d.ts +115 -0
  68. package/lib/channels/telegram/client.d.ts.map +1 -0
  69. package/lib/channels/telegram/client.js +198 -0
  70. package/lib/channels/telegram/client.js.map +1 -0
  71. package/lib/channels/telegram/i18n.d.ts +14 -0
  72. package/lib/channels/telegram/i18n.d.ts.map +1 -0
  73. package/lib/channels/telegram/i18n.js +18 -0
  74. package/lib/channels/telegram/i18n.js.map +1 -0
  75. package/lib/channels/telegram/index.d.ts +46 -0
  76. package/lib/channels/telegram/index.d.ts.map +1 -0
  77. package/lib/channels/telegram/index.js +54 -0
  78. package/lib/channels/telegram/index.js.map +1 -0
  79. package/lib/channels/web/adapter.d.ts +103 -0
  80. package/lib/channels/web/adapter.d.ts.map +1 -0
  81. package/lib/channels/web/adapter.js +161 -0
  82. package/lib/channels/web/adapter.js.map +1 -0
  83. package/lib/channels/web/index.d.ts +47 -0
  84. package/lib/channels/web/index.d.ts.map +1 -0
  85. package/lib/channels/web/index.js +59 -0
  86. package/lib/channels/web/index.js.map +1 -0
  87. package/lib/chat-key.d.ts +29 -0
  88. package/lib/chat-key.d.ts.map +1 -0
  89. package/lib/chat-key.js +38 -0
  90. package/lib/chat-key.js.map +1 -0
  91. package/lib/index.d.ts +78 -11
  92. package/lib/index.d.ts.map +1 -1
  93. package/lib/index.js +312 -9
  94. package/lib/index.js.map +1 -1
  95. package/lib/runner.d.ts +55 -2
  96. package/lib/runner.d.ts.map +1 -1
  97. package/lib/runner.js +158 -23
  98. package/lib/runner.js.map +1 -1
  99. package/lib/scheduler.d.ts.map +1 -1
  100. package/lib/scheduler.js +2 -2
  101. package/lib/scheduler.js.map +1 -1
  102. package/lib/service.d.ts +17 -1
  103. package/lib/service.d.ts.map +1 -1
  104. package/lib/service.js +47 -3
  105. package/lib/service.js.map +1 -1
  106. package/lib/settings/channel-runtime.d.ts +75 -0
  107. package/lib/settings/channel-runtime.d.ts.map +1 -0
  108. package/lib/settings/channel-runtime.js +165 -0
  109. package/lib/settings/channel-runtime.js.map +1 -0
  110. package/lib/settings/channels.d.ts +62 -0
  111. package/lib/settings/channels.d.ts.map +1 -0
  112. package/lib/settings/channels.js +132 -0
  113. package/lib/settings/channels.js.map +1 -0
  114. package/lib/settings/credential-store.d.ts +85 -0
  115. package/lib/settings/credential-store.d.ts.map +1 -0
  116. package/lib/settings/credential-store.js +142 -0
  117. package/lib/settings/credential-store.js.map +1 -0
  118. package/lib/settings/index.d.ts +14 -0
  119. package/lib/settings/index.d.ts.map +1 -0
  120. package/lib/settings/index.js +14 -0
  121. package/lib/settings/index.js.map +1 -0
  122. package/lib/settings/namespace.d.ts +175 -0
  123. package/lib/settings/namespace.d.ts.map +1 -0
  124. package/lib/settings/namespace.js +200 -0
  125. package/lib/settings/namespace.js.map +1 -0
  126. package/lib/settings/rpc-client.d.ts +36 -0
  127. package/lib/settings/rpc-client.d.ts.map +1 -0
  128. package/lib/settings/rpc-client.js +45 -0
  129. package/lib/settings/rpc-client.js.map +1 -0
  130. package/lib/settings/secret-disclosure.d.ts +50 -0
  131. package/lib/settings/secret-disclosure.d.ts.map +1 -0
  132. package/lib/settings/secret-disclosure.js +132 -0
  133. package/lib/settings/secret-disclosure.js.map +1 -0
  134. package/lib/settings/settings-model.d.ts +124 -0
  135. package/lib/settings/settings-model.d.ts.map +1 -0
  136. package/lib/settings/settings-model.js +118 -0
  137. package/lib/settings/settings-model.js.map +1 -0
  138. package/lib/settings/settings-rpc.d.ts +130 -0
  139. package/lib/settings/settings-rpc.d.ts.map +1 -0
  140. package/lib/settings/settings-rpc.js +233 -0
  141. package/lib/settings/settings-rpc.js.map +1 -0
  142. package/lib/settings/settings-service.d.ts +35 -0
  143. package/lib/settings/settings-service.d.ts.map +1 -0
  144. package/lib/settings/settings-service.js +171 -0
  145. package/lib/settings/settings-service.js.map +1 -0
  146. package/lib/state-dir.d.ts +28 -0
  147. package/lib/state-dir.d.ts.map +1 -0
  148. package/lib/state-dir.js +33 -0
  149. package/lib/state-dir.js.map +1 -0
  150. package/lib/stream.d.ts.map +1 -1
  151. package/lib/stream.js +3 -2
  152. package/lib/stream.js.map +1 -1
  153. package/package.json +56 -12
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Minimal Telegram Bot API client built on the global `fetch` — long-polling
3
+ * update intake, message send / edit / photo, file download, and inline
4
+ * keyboards. Zero third-party HTTP dependency (Node ≥ 18 / ≥ 20).
5
+ *
6
+ * @module dsh-connect/channels/telegram/client
7
+ */
8
+ import { mkdirSync, writeFileSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { extname, join } from "node:path";
11
+ const API_BASE = "https://api.telegram.org";
12
+ export class TelegramClient {
13
+ config;
14
+ offset = 0;
15
+ constructor(config) {
16
+ if (!config.botToken)
17
+ throw new Error("connect-telegram: botToken is required");
18
+ this.config = config;
19
+ }
20
+ base() {
21
+ return this.config.baseUrl ?? API_BASE;
22
+ }
23
+ async call(method, body, opts) {
24
+ const url = `${this.base()}/bot${this.config.botToken}/${method}`;
25
+ const controller = new AbortController();
26
+ const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? this.config.timeoutMs ?? 15_000);
27
+ try {
28
+ const response = await fetch(url, {
29
+ method: "POST",
30
+ headers: { "Content-Type": "application/json" },
31
+ body: body === undefined ? "{}" : JSON.stringify(body),
32
+ signal: controller.signal,
33
+ });
34
+ const parsed = (await response.json());
35
+ if (!parsed.ok || parsed.result === undefined) {
36
+ throw new Error(`Telegram API ${method}: ${parsed.description ?? `error ${parsed.error_code ?? "?"}`}`);
37
+ }
38
+ return parsed.result;
39
+ }
40
+ finally {
41
+ clearTimeout(timer);
42
+ }
43
+ }
44
+ /** Long-poll for new updates. Does NOT advance the offset — the caller confirms each update via {@link confirmOffset} after processing it, so a failure mid-batch never loses the remaining updates. */
45
+ async pollUpdates() {
46
+ // The generic per-call HTTP timeout (15s) must not kill a long poll:
47
+ // give getUpdates a client-side timeout comfortably above the server-side
48
+ // long-poll window (otherwise every idle poll is aborted at 15s and the
49
+ // "50s long poll" degrades into short polling + sleep churn).
50
+ const longPollMs = ((this.config.pollingTimeoutSeconds ?? 50) + 10) * 1000;
51
+ return this.call("getUpdates", {
52
+ offset: this.offset,
53
+ timeout: this.config.pollingTimeoutSeconds ?? 50,
54
+ allowed_updates: ["message", "callback_query"],
55
+ }, { timeoutMs: longPollMs });
56
+ }
57
+ /** Advance the polling offset past `updateId` — call only after the update was fully handled. */
58
+ confirmOffset(updateId) {
59
+ this.offset = Math.max(this.offset, updateId + 1);
60
+ }
61
+ /** Skip all pending updates (used on startup so we don't replay history). */
62
+ async resetOffset() {
63
+ const result = await this.call("getUpdates", { offset: -1, timeout: 0 });
64
+ if (result.length > 0) {
65
+ this.offset = result[result.length - 1].update_id + 1;
66
+ }
67
+ }
68
+ /** Fetch the bot's own identity (id + username) — used for @-mention / reply-to checks. */
69
+ async getMe() {
70
+ return this.call("getMe", {});
71
+ }
72
+ async sendMessage(chatId, text, opts = {}) {
73
+ return this.call("sendMessage", {
74
+ chat_id: chatId,
75
+ text,
76
+ ...(opts.parse_mode === undefined ? {} : { parse_mode: opts.parse_mode }),
77
+ ...(opts.reply_to_message_id === undefined ? {} : { reply_to_message_id: opts.reply_to_message_id }),
78
+ ...(opts.reply_markup === undefined ? {} : { reply_markup: opts.reply_markup }),
79
+ ...(opts.disable_web_page_preview === undefined ? {} : { disable_web_page_preview: opts.disable_web_page_preview }),
80
+ });
81
+ }
82
+ async editMessageText(chatId, messageId, text, opts = {}) {
83
+ return this.call("editMessageText", {
84
+ chat_id: chatId,
85
+ message_id: messageId,
86
+ text,
87
+ ...(opts.parse_mode === undefined ? {} : { parse_mode: opts.parse_mode }),
88
+ ...(opts.reply_markup === undefined ? {} : { reply_markup: opts.reply_markup }),
89
+ });
90
+ }
91
+ async sendPhoto(chatId, photoPath, caption, opts = {}) {
92
+ // sendPhoto needs multipart; the fetch FormData path with a Blob is the
93
+ // modern zero-dependency route. Read the file into a Blob.
94
+ const { readFileSync } = await import("node:fs");
95
+ const buffer = readFileSync(photoPath);
96
+ const blob = new Blob([buffer]);
97
+ const form = new FormData();
98
+ form.append("chat_id", String(chatId));
99
+ form.append("photo", blob, photoPath.split(/[\\/]/).pop() ?? "photo");
100
+ if (caption !== undefined)
101
+ form.append("caption", caption);
102
+ if (opts.parse_mode !== undefined)
103
+ form.append("parse_mode", opts.parse_mode);
104
+ if (opts.reply_to_message_id !== undefined)
105
+ form.append("reply_to_message_id", String(opts.reply_to_message_id));
106
+ const url = `${this.base()}/bot${this.config.botToken}/sendPhoto`;
107
+ const controller = new AbortController();
108
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 15_000);
109
+ try {
110
+ const response = await fetch(url, { method: "POST", body: form, signal: controller.signal });
111
+ const parsed = (await response.json());
112
+ if (!parsed.ok || parsed.result === undefined) {
113
+ throw new Error(`Telegram API sendPhoto: ${parsed.description ?? "unknown error"}`);
114
+ }
115
+ return parsed.result;
116
+ }
117
+ finally {
118
+ clearTimeout(timer);
119
+ }
120
+ }
121
+ async sendDocument(chatId, documentPath, opts = {}) {
122
+ const { readFileSync } = await import("node:fs");
123
+ const buffer = readFileSync(documentPath);
124
+ const blob = new Blob([buffer]);
125
+ const form = new FormData();
126
+ form.append("chat_id", String(chatId));
127
+ form.append("document", blob, documentPath.split(/[\\/]/).pop() ?? "document");
128
+ if (opts.reply_to_message_id !== undefined)
129
+ form.append("reply_to_message_id", String(opts.reply_to_message_id));
130
+ const url = `${this.base()}/bot${this.config.botToken}/sendDocument`;
131
+ const controller = new AbortController();
132
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 15_000);
133
+ try {
134
+ const response = await fetch(url, { method: "POST", body: form, signal: controller.signal });
135
+ const parsed = (await response.json());
136
+ if (!parsed.ok || parsed.result === undefined) {
137
+ throw new Error(`Telegram API sendDocument: ${parsed.description ?? "unknown error"}`);
138
+ }
139
+ return parsed.result;
140
+ }
141
+ finally {
142
+ clearTimeout(timer);
143
+ }
144
+ }
145
+ async answerCallbackQuery(callbackQueryId, text) {
146
+ return this.call("answerCallbackQuery", {
147
+ callback_query_id: callbackQueryId,
148
+ ...(text === undefined ? {} : { text }),
149
+ });
150
+ }
151
+ /** Download a file (photo / document / …) by file_id into a temp dir; returns the local path. */
152
+ async downloadFile(fileId, dir, fileName) {
153
+ const controller = new AbortController();
154
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 30_000);
155
+ try {
156
+ const file = await this.call("getFile", { file_id: fileId });
157
+ if (file.file_path === undefined)
158
+ throw new Error("getFile returned no file_path");
159
+ const url = `${this.base()}/file/bot${this.config.botToken}/${file.file_path}`;
160
+ const response = await fetch(url, { signal: controller.signal });
161
+ if (!response.ok)
162
+ throw new Error(`download HTTP ${response.status}`);
163
+ const buffer = Buffer.from(await response.arrayBuffer());
164
+ const safeName = sanitizeFileName(fileName ?? file.file_path.split("/").pop() ?? fileId);
165
+ const dest = join(dir, safeName);
166
+ mkdirSync(dir, { recursive: true });
167
+ writeFileSync(dest, buffer);
168
+ return { path: dest, kind: "file" };
169
+ }
170
+ catch (error) {
171
+ return { path: "", kind: "file", error: error instanceof Error ? error.message : String(error) };
172
+ }
173
+ finally {
174
+ clearTimeout(timer);
175
+ }
176
+ }
177
+ /** Convenience: download to the OS temp dir. */
178
+ async downloadFileToTemp(fileId, fileName) {
179
+ const dir = join(tmpdir(), "dsh-connect-telegram");
180
+ return this.downloadFile(fileId, dir, fileName);
181
+ }
182
+ }
183
+ /** Image extensions Telegram renders inline via sendPhoto. */
184
+ const PHOTO_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif"]);
185
+ /** Pick the Bot API method for a local file (sendPhoto vs sendDocument). */
186
+ export function telegramFileMethod(filename) {
187
+ const ext = extname(filename).toLowerCase();
188
+ return PHOTO_EXTENSIONS.has(ext) ? "sendPhoto" : "sendDocument";
189
+ }
190
+ /** Strip path separators / control chars from a downloaded file's name. */
191
+ export function sanitizeFileName(name) {
192
+ const clean = name
193
+ .replace(/[\\/:*?"<>|\u0000-\u001f]/g, "_")
194
+ .trim()
195
+ .slice(0, 200);
196
+ return clean === "" ? "file" : clean;
197
+ }
198
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/channels/telegram/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Telegram-adapter user-facing strings (zh/en).
3
+ * @module dsh-connect/channels/telegram/i18n
4
+ */
5
+ import type { Language } from "../../i18n.js";
6
+ export interface TelegramMessages {
7
+ tokenMissing: string;
8
+ pollFailed(error: string): string;
9
+ choiceExpired: string;
10
+ choiceDone: string;
11
+ menuExpired: string;
12
+ }
13
+ export declare function telegramMessages(lang: Language): TelegramMessages;
14
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/i18n.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAkBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,gBAAgB,CAEjE"}
@@ -0,0 +1,18 @@
1
+ const zh = {
2
+ tokenMissing: "connect-telegram: 未配置 botToken(Telegram BotFather 提供的 token),无法连接。",
3
+ pollFailed: (error) => `connect-telegram: 长轮询失败,稍后重试 — ${error}`,
4
+ choiceExpired: "此操作已失效(可能已被处理或已过期)。",
5
+ choiceDone: "✅ 已选择",
6
+ menuExpired: "菜单已过期",
7
+ };
8
+ const en = {
9
+ tokenMissing: "connect-telegram: botToken is not configured (the token BotFather gives you) — cannot connect.",
10
+ pollFailed: (error) => `connect-telegram: long-poll failed, retrying — ${error}`,
11
+ choiceExpired: "This action is no longer active (already handled or expired).",
12
+ choiceDone: "✅ Selected",
13
+ menuExpired: "Menu expired",
14
+ };
15
+ export function telegramMessages(lang) {
16
+ return lang === "en" ? en : zh;
17
+ }
18
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../src/channels/telegram/i18n.ts"],"names":[],"mappings":"AAcA,MAAM,EAAE,GAAqB;IAC3B,YAAY,EAAE,oEAAoE;IAClF,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,kCAAkC,KAAK,EAAE;IAChE,aAAa,EAAE,qBAAqB;IACpC,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,OAAO;CACrB,CAAC;AAEF,MAAM,EAAE,GAAqB;IAC3B,YAAY,EAAE,gGAAgG;IAC9G,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,kDAAkD,KAAK,EAAE;IAChF,aAAa,EAAE,+DAA+D;IAC9E,UAAU,EAAE,YAAY;IACxB,WAAW,EAAE,cAAc;CAC5B,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Telegram channel adapter: getUpdates long-polling intake, normalized message
3
+ * routing into `dsh-connect`, and replies back to Telegram (text / markdown /
4
+ * streaming edits / inline-keyboard choice prompts).
5
+ *
6
+ * @module dsh-connect/telegram
7
+ */
8
+ import type { Context } from "@deepseek-ai/cordis";
9
+ import z from "@deepseek-ai/schemastery";
10
+ import { type TelegramConfig } from "./adapter.js";
11
+ export { TelegramAdapter } from "./adapter.js";
12
+ export type { TelegramConfig } from "./adapter.js";
13
+ export { TelegramClient } from "./client.js";
14
+ export type { TelegramClientConfig, TelegramMessage, TelegramUpdate } from "./client.js";
15
+ export { markdownToTelegramHtml, escapeHtml, buildInlineKeyboard, encodeMessageRef, decodeMessageRef, isBotMentioned } from "./adapter.js";
16
+ /** Plugin config; secrets may come from config or the TELEGRAM_BOT_TOKEN environment. */
17
+ export declare const Config: z<Schemastery.ObjectS<{
18
+ botToken: z<string, string>;
19
+ language: z<"zh" | "en", "zh" | "en">;
20
+ requireMention: z<boolean, boolean>;
21
+ pollingTimeoutSeconds: z<number, number>;
22
+ baseUrl: z<string, string>;
23
+ }>, Schemastery.ObjectT<{
24
+ botToken: z<string, string>;
25
+ language: z<"zh" | "en", "zh" | "en">;
26
+ requireMention: z<boolean, boolean>;
27
+ pollingTimeoutSeconds: z<number, number>;
28
+ baseUrl: z<string, string>;
29
+ }>>;
30
+ interface ConnectLike {
31
+ registerAdapter(adapter: unknown): void;
32
+ }
33
+ /**
34
+ * Register and start the Telegram adapter. `connect` is the already-constructed
35
+ * `ConnectService` instance (passed from the merged entry, not resolved via
36
+ * `ctx.get` — that returns undefined during the same plugin's `apply`).
37
+ */
38
+ export declare function register(connect: ConnectLike, config: (TelegramConfig | null) | undefined, ctx: Context): void;
39
+ /**
40
+ * Compatibility shell for the old split-plugin registration path (when `connect`
41
+ * is an already-active, separate plugin). The merged entry calls `register`
42
+ * directly instead, because `ctx.get("connect")` returns undefined inside this
43
+ * plugin's own `apply`.
44
+ */
45
+ export declare function apply(ctx: Context, config?: TelegramConfig | null): void;
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,CAAC,MAAM,0BAA0B,CAAC;AACzC,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3I,yFAAyF;AACzF,eAAO,MAAM,MAAM;;;;;;;;;;;;GAMjB,CAAC;AAEH,UAAU,WAAW;IACnB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzC;AAcD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,GAAE,cAAc,GAAG,IAAI,aAAK,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CASrG;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,cAAc,GAAG,IAAS,GAAG,IAAI,CAM5E"}
@@ -0,0 +1,54 @@
1
+ import z from "@deepseek-ai/schemastery";
2
+ import { TelegramAdapter } from "./adapter.js";
3
+ export { TelegramAdapter } from "./adapter.js";
4
+ export { TelegramClient } from "./client.js";
5
+ export { markdownToTelegramHtml, escapeHtml, buildInlineKeyboard, encodeMessageRef, decodeMessageRef, isBotMentioned } from "./adapter.js";
6
+ /** Plugin config; secrets may come from config or the TELEGRAM_BOT_TOKEN environment. */
7
+ export const Config = z.object({
8
+ botToken: z.string().role("secret"),
9
+ language: z.union([z.const("zh"), z.const("en")]),
10
+ requireMention: z.boolean(),
11
+ pollingTimeoutSeconds: z.number().min(1).max(60),
12
+ baseUrl: z.string(),
13
+ });
14
+ function start(connect, config, logger) {
15
+ try {
16
+ const adapter = new TelegramAdapter(config, logger);
17
+ connect.registerAdapter(adapter);
18
+ void adapter.start().catch((error) => {
19
+ logger?.warn?.(`connect-telegram: start failed: ${String(error)}`);
20
+ });
21
+ }
22
+ catch (error) {
23
+ logger?.warn?.(`connect-telegram: adapter init failed: ${String(error)}`);
24
+ }
25
+ }
26
+ /**
27
+ * Register and start the Telegram adapter. `connect` is the already-constructed
28
+ * `ConnectService` instance (passed from the merged entry, not resolved via
29
+ * `ctx.get` — that returns undefined during the same plugin's `apply`).
30
+ */
31
+ export function register(connect, config = {}, ctx) {
32
+ // The DSH loader passes `null` for entries without an explicit config.
33
+ config = config ?? {};
34
+ const botToken = config.botToken ?? process.env.TELEGRAM_BOT_TOKEN;
35
+ if (botToken === undefined || botToken === "") {
36
+ ctx.logger?.warn?.("connect-telegram: botToken is not configured (config or TELEGRAM_BOT_TOKEN) — adapter disabled.");
37
+ return;
38
+ }
39
+ start(connect, { ...config, botToken }, ctx.logger);
40
+ }
41
+ /**
42
+ * Compatibility shell for the old split-plugin registration path (when `connect`
43
+ * is an already-active, separate plugin). The merged entry calls `register`
44
+ * directly instead, because `ctx.get("connect")` returns undefined inside this
45
+ * plugin's own `apply`.
46
+ */
47
+ export function apply(ctx, config = {}) {
48
+ const connect = ctx.get("connect");
49
+ if (connect === undefined) {
50
+ throw new Error("connect-telegram: the dsh-connect service is not present; load it before this adapter");
51
+ }
52
+ register(connect, config, ctx);
53
+ }
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/channels/telegram/index.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,0BAA0B,CAAC;AACzC,OAAO,EAAE,eAAe,EAAuB,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3I,yFAAyF;AACzF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;IACnC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE;IAC3B,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC;AAMH,SAAS,KAAK,CAAC,OAAoB,EAAE,MAAsB,EAAE,MAAgD;IAC3G,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACjC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnC,MAAM,EAAE,IAAI,EAAE,CAAC,mCAAmC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,IAAI,EAAE,CAAC,0CAA0C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAoB,EAAE,SAAgC,EAAE,EAAE,GAAY;IAC7F,uEAAuE;IACvE,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IACnE,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,iGAAiG,CAAC,CAAC;QACtH,OAAO;IACT,CAAC;IACD,KAAK,CAAC,OAAO,EAAE,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,SAAgC,EAAE;IACpE,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAA4B,CAAC;IAC9D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;IAC3G,CAAC;IACD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Web channel adapter for dsh-connect: mirrors Feishu conversations to DSH Web GUI.
3
+ * This adapter monitors BindingStore for webMirrorSessionId changes and exposes
4
+ * those sessions to the Web GUI through the standard ChannelAdapter interface.
5
+ * @module dsh-connect/channels/web/adapter
6
+ */
7
+ import type { ChannelAdapter, ChoicePrompt, ChoiceResult, InboundMessage, OutboundTarget, SummaryCard } from "../../types.js";
8
+ import type { BindingStore } from "../../binding.js";
9
+ /**
10
+ * WebAdapter provides a bridge between dsh-connect's mirror sessions and the
11
+ * DSH Web GUI. It monitors the BindingStore for chats that have been mirrored
12
+ * (have webMirrorSessionId set) so the GUI can open the shared session.
13
+ *
14
+ * Honest limitations (documented in README):
15
+ * - The Web GUI reads mirrored sessions directly from DSH's session store; it
16
+ * never sends inbound messages through this adapter. The mirror "lock"
17
+ * (lockOwner) is therefore enforced only on the Feishu side — the Web side
18
+ * is write-always and cannot be fixed from this repository.
19
+ * - Outbound methods (sendText / sendCard / streamText / promptChoice /
20
+ * closeMenu) are no-ops by design: the agent writes events into the shared
21
+ * session store, and the GUI renders them. They exist only to satisfy the
22
+ * ChannelAdapter contract.
23
+ */
24
+ export declare class WebAdapter implements ChannelAdapter {
25
+ private readonly logger?;
26
+ readonly id = "web";
27
+ private handler?;
28
+ private readonly bindings;
29
+ private readonly pollIntervalMs;
30
+ private pollTimer?;
31
+ private unsubscribe?;
32
+ private readonly knownMirrors;
33
+ /**
34
+ * Create a WebAdapter instance.
35
+ * @param bindings - The BindingStore to monitor for mirror sessions
36
+ * @param options - Optional configuration
37
+ */
38
+ constructor(bindings: BindingStore, options?: {
39
+ pollIntervalMs?: number;
40
+ }, logger?: {
41
+ warn?: (...args: unknown[]) => void;
42
+ } | undefined);
43
+ onInbound(handler: (msg: InboundMessage) => void | Promise<void>): void;
44
+ /**
45
+ * Start monitoring for mirror sessions.
46
+ * Uses event-driven detection with fallback polling.
47
+ */
48
+ start(): Promise<void>;
49
+ /**
50
+ * Stop monitoring and clean up resources.
51
+ */
52
+ stop(): Promise<void>;
53
+ /**
54
+ * Record a newly-detected mirror session. Mirror tracking is purely
55
+ * bookkeeping: the mirror is visible in DSH Web through the session store
56
+ * itself, and synthesizing an inbound "mirror created" message here would
57
+ * spin up a spurious web runner and burn a real agent turn — so we do NOT
58
+ * route anything through the handler.
59
+ */
60
+ private recordMirror;
61
+ /**
62
+ * Scan BindingStore for chats with webMirrorSessionId set and record any
63
+ * new mirrors.
64
+ */
65
+ private scanForMirrors;
66
+ /**
67
+ * Check if a session is mirrored and available for Web access.
68
+ * @param sessionId - The session ID to check
69
+ */
70
+ isSessionMirrored(sessionId: string): boolean;
71
+ /**
72
+ * Get the source chat key for a mirrored session.
73
+ * @param sessionId - The session ID
74
+ * @returns The original channel:chatKey or undefined if not mirrored
75
+ */
76
+ getMirrorSource(sessionId: string): string | undefined;
77
+ /**
78
+ * Send text message to Web GUI.
79
+ * No-op: the Web GUI pulls content from DSH session events, not from adapter
80
+ * calls. Kept only to satisfy the ChannelAdapter contract.
81
+ */
82
+ sendText(_target: OutboundTarget, _text: string): Promise<void>;
83
+ /**
84
+ * Send summary card to Web GUI. No-op — see {@link sendText}.
85
+ */
86
+ sendCard(_target: OutboundTarget, _card: SummaryCard): Promise<void>;
87
+ /**
88
+ * Stream text chunks to Web GUI.
89
+ * The agent already writes chunks into the session; consume the iterable so
90
+ * the producer never stalls on backpressure.
91
+ */
92
+ streamText(_target: OutboundTarget, chunks: AsyncIterable<string>): Promise<void>;
93
+ /**
94
+ * Present interactive choice prompt.
95
+ * No-op: the Web GUI renders choices through its own interaction system.
96
+ */
97
+ promptChoice(_target: OutboundTarget, _prompt: ChoicePrompt, updateMessageId?: string): Promise<ChoiceResult>;
98
+ /**
99
+ * Close a menu/card in Web GUI. No-op — see {@link sendText}.
100
+ */
101
+ closeMenu(_messageId: string, _summary: string): Promise<void>;
102
+ }
103
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/channels/web/adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,cAAc,EACd,WAAW,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,kBAAkB,CAAC;AAElE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAW,YAAW,cAAc;IAkB7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAjB1B,QAAQ,CAAC,EAAE,SAAS;IAEpB,OAAO,CAAC,OAAO,CAAC,CAAgD;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAE1D;;;;OAIG;gBAED,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAO,EACxB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,YAAA;IAMnE,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIvE;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAatB;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI7C;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAItD;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1E;;;;OAIG;IACG,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvF;;;OAGG;IACG,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAOnH;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrE"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * WebAdapter provides a bridge between dsh-connect's mirror sessions and the
3
+ * DSH Web GUI. It monitors the BindingStore for chats that have been mirrored
4
+ * (have webMirrorSessionId set) so the GUI can open the shared session.
5
+ *
6
+ * Honest limitations (documented in README):
7
+ * - The Web GUI reads mirrored sessions directly from DSH's session store; it
8
+ * never sends inbound messages through this adapter. The mirror "lock"
9
+ * (lockOwner) is therefore enforced only on the Feishu side — the Web side
10
+ * is write-always and cannot be fixed from this repository.
11
+ * - Outbound methods (sendText / sendCard / streamText / promptChoice /
12
+ * closeMenu) are no-ops by design: the agent writes events into the shared
13
+ * session store, and the GUI renders them. They exist only to satisfy the
14
+ * ChannelAdapter contract.
15
+ */
16
+ export class WebAdapter {
17
+ logger;
18
+ id = "web";
19
+ handler;
20
+ bindings;
21
+ pollIntervalMs;
22
+ pollTimer;
23
+ unsubscribe;
24
+ knownMirrors = new Map(); // sessionId -> chatKey
25
+ /**
26
+ * Create a WebAdapter instance.
27
+ * @param bindings - The BindingStore to monitor for mirror sessions
28
+ * @param options - Optional configuration
29
+ */
30
+ constructor(bindings, options = {}, logger) {
31
+ this.logger = logger;
32
+ this.bindings = bindings;
33
+ this.pollIntervalMs = options.pollIntervalMs ?? 1000; // Default 1 second polling
34
+ }
35
+ onInbound(handler) {
36
+ this.handler = handler;
37
+ }
38
+ /**
39
+ * Start monitoring for mirror sessions.
40
+ * Uses event-driven detection with fallback polling.
41
+ */
42
+ async start() {
43
+ // Subscribe to binding changes for real-time detection
44
+ this.unsubscribe = this.bindings.onChange((binding, changeType) => {
45
+ if (binding.webMirrorSessionId !== undefined) {
46
+ this.recordMirror(binding);
47
+ }
48
+ else if (changeType === "delete") {
49
+ this.knownMirrors.delete(binding.sessionId);
50
+ }
51
+ });
52
+ // Initial scan for existing mirrors
53
+ this.scanForMirrors();
54
+ // Fallback polling in case events are missed
55
+ this.pollTimer = setInterval(() => {
56
+ this.scanForMirrors();
57
+ }, this.pollIntervalMs);
58
+ }
59
+ /**
60
+ * Stop monitoring and clean up resources.
61
+ */
62
+ async stop() {
63
+ if (this.unsubscribe !== undefined) {
64
+ this.unsubscribe();
65
+ this.unsubscribe = undefined;
66
+ }
67
+ if (this.pollTimer !== undefined) {
68
+ clearInterval(this.pollTimer);
69
+ this.pollTimer = undefined;
70
+ }
71
+ }
72
+ /**
73
+ * Record a newly-detected mirror session. Mirror tracking is purely
74
+ * bookkeeping: the mirror is visible in DSH Web through the session store
75
+ * itself, and synthesizing an inbound "mirror created" message here would
76
+ * spin up a spurious web runner and burn a real agent turn — so we do NOT
77
+ * route anything through the handler.
78
+ */
79
+ recordMirror(binding) {
80
+ const sessionId = binding.webMirrorSessionId;
81
+ if (sessionId === undefined)
82
+ return;
83
+ const chatKey = `${binding.channel}:${binding.chatKey}`;
84
+ if (!this.knownMirrors.has(sessionId)) {
85
+ this.knownMirrors.set(sessionId, chatKey);
86
+ }
87
+ }
88
+ /**
89
+ * Scan BindingStore for chats with webMirrorSessionId set and record any
90
+ * new mirrors.
91
+ */
92
+ scanForMirrors() {
93
+ try {
94
+ const bindings = this.bindings.list?.() ?? [];
95
+ for (const binding of bindings) {
96
+ if (binding.webMirrorSessionId !== undefined) {
97
+ this.recordMirror(binding);
98
+ }
99
+ }
100
+ }
101
+ catch {
102
+ // BindingStore may not have list() in older versions — rely on events.
103
+ }
104
+ }
105
+ /**
106
+ * Check if a session is mirrored and available for Web access.
107
+ * @param sessionId - The session ID to check
108
+ */
109
+ isSessionMirrored(sessionId) {
110
+ return this.knownMirrors.has(sessionId);
111
+ }
112
+ /**
113
+ * Get the source chat key for a mirrored session.
114
+ * @param sessionId - The session ID
115
+ * @returns The original channel:chatKey or undefined if not mirrored
116
+ */
117
+ getMirrorSource(sessionId) {
118
+ return this.knownMirrors.get(sessionId);
119
+ }
120
+ /**
121
+ * Send text message to Web GUI.
122
+ * No-op: the Web GUI pulls content from DSH session events, not from adapter
123
+ * calls. Kept only to satisfy the ChannelAdapter contract.
124
+ */
125
+ async sendText(_target, _text) {
126
+ // The agent writes session events; the GUI renders them.
127
+ }
128
+ /**
129
+ * Send summary card to Web GUI. No-op — see {@link sendText}.
130
+ */
131
+ async sendCard(_target, _card) {
132
+ // No-op by design.
133
+ }
134
+ /**
135
+ * Stream text chunks to Web GUI.
136
+ * The agent already writes chunks into the session; consume the iterable so
137
+ * the producer never stalls on backpressure.
138
+ */
139
+ async streamText(_target, chunks) {
140
+ for await (const _chunk of chunks) {
141
+ // Already delivered via session events.
142
+ }
143
+ }
144
+ /**
145
+ * Present interactive choice prompt.
146
+ * No-op: the Web GUI renders choices through its own interaction system.
147
+ */
148
+ async promptChoice(_target, _prompt, updateMessageId) {
149
+ return {
150
+ choice: undefined,
151
+ messageId: updateMessageId ?? `web-${Date.now()}`,
152
+ };
153
+ }
154
+ /**
155
+ * Close a menu/card in Web GUI. No-op — see {@link sendText}.
156
+ */
157
+ async closeMenu(_messageId, _summary) {
158
+ // No-op by design.
159
+ }
160
+ }
161
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/channels/web/adapter.ts"],"names":[],"mappings":"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"}