@nodetool-ai/telegram 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/bot-api.d.ts +115 -0
- package/dist/bot-api.d.ts.map +1 -0
- package/dist/bot-api.js +145 -0
- package/dist/bot-api.js.map +1 -0
- package/dist/chunk.d.ts +36 -0
- package/dist/chunk.d.ts.map +1 -0
- package/dist/chunk.js +280 -0
- package/dist/chunk.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +83 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +191 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +139 -0
- package/dist/config.js.map +1 -0
- package/dist/frame-renderer.d.ts +135 -0
- package/dist/frame-renderer.d.ts.map +1 -0
- package/dist/frame-renderer.js +407 -0
- package/dist/frame-renderer.js.map +1 -0
- package/dist/identity-client.d.ts +132 -0
- package/dist/identity-client.d.ts.map +1 -0
- package/dist/identity-client.js +204 -0
- package/dist/identity-client.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +90 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown-html.d.ts +32 -0
- package/dist/markdown-html.d.ts.map +1 -0
- package/dist/markdown-html.js +251 -0
- package/dist/markdown-html.js.map +1 -0
- package/dist/nodetool-client.d.ts +32 -0
- package/dist/nodetool-client.d.ts.map +1 -0
- package/dist/nodetool-client.js +127 -0
- package/dist/nodetool-client.js.map +1 -0
- package/dist/register-commands.d.ts +24 -0
- package/dist/register-commands.d.ts.map +1 -0
- package/dist/register-commands.js +29 -0
- package/dist/register-commands.js.map +1 -0
- package/dist/strings.d.ts +7 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +13 -0
- package/dist/strings.js.map +1 -0
- package/dist/telegram-adapter.d.ts +100 -0
- package/dist/telegram-adapter.d.ts.map +1 -0
- package/dist/telegram-adapter.js +366 -0
- package/dist/telegram-adapter.js.map +1 -0
- package/dist/turn-router.d.ts +167 -0
- package/dist/turn-router.d.ts.map +1 -0
- package/dist/turn-router.js +360 -0
- package/dist/turn-router.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent markdown → Telegram HTML parse mode.
|
|
3
|
+
*
|
|
4
|
+
* MarkdownV2 makes every `.`, `-` and `(` in prose an escaping obligation, so
|
|
5
|
+
* one missed character fails the whole message. Telegram's HTML mode needs
|
|
6
|
+
* three characters escaped and supports the five tags this converter emits:
|
|
7
|
+
* `<b>`, `<i>`, `<code>`, `<pre>` and `<a href>`. Anything the converter
|
|
8
|
+
* cannot express — an unbalanced fence, markup it does not recognize as
|
|
9
|
+
* balanced — falls back to plain text, because a turn must never fail on
|
|
10
|
+
* formatting.
|
|
11
|
+
*
|
|
12
|
+
* Hand-rolled on purpose: the supported subset is small enough that a parser
|
|
13
|
+
* dependency would cost more than it covers.
|
|
14
|
+
*/
|
|
15
|
+
/** URL schemes allowed on a link. Anything else renders as its label. */
|
|
16
|
+
const SAFE_SCHEMES = new Set(["http:", "https:", "tg:", "mailto:"]);
|
|
17
|
+
// The info string is validated in code rather than the regex: the
|
|
18
|
+
// \s*([^\s`~]*)\s* form is quadratic on trailing whitespace
|
|
19
|
+
// (CodeQL js/polynomial-redos), while (.*)$ is linear.
|
|
20
|
+
const FENCE_LINE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
21
|
+
/**
|
|
22
|
+
* Parse a fence delimiter line. Returns null for lines that are not a fence,
|
|
23
|
+
* including a fence-like line whose info string carries whitespace, backticks,
|
|
24
|
+
* or tildes — the same lines the previous single-token regex refused.
|
|
25
|
+
*/
|
|
26
|
+
function parseFenceLine(line) {
|
|
27
|
+
const match = FENCE_LINE.exec(line);
|
|
28
|
+
if (match === null) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const info = match[2].trim();
|
|
32
|
+
if (/[\s`~]/.test(info)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return { marker: match[1], info };
|
|
36
|
+
}
|
|
37
|
+
/** Escape the three characters Telegram's HTML mode reserves. */
|
|
38
|
+
export function escapeHtml(text) {
|
|
39
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
40
|
+
}
|
|
41
|
+
function escapeAttribute(text) {
|
|
42
|
+
return escapeHtml(text).replace(/"/g, """);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Split into prose and fenced-code blocks. Returns null when a fence opens and
|
|
46
|
+
* never closes — the one input shape that must not be guessed at.
|
|
47
|
+
*/
|
|
48
|
+
function splitBlocks(markdown) {
|
|
49
|
+
const lines = markdown.split("\n");
|
|
50
|
+
const blocks = [];
|
|
51
|
+
let prose = [];
|
|
52
|
+
let code = null;
|
|
53
|
+
let marker = "";
|
|
54
|
+
let language = "";
|
|
55
|
+
const flushProse = () => {
|
|
56
|
+
if (prose.length > 0) {
|
|
57
|
+
blocks.push({ kind: "prose", text: prose.join("\n") });
|
|
58
|
+
prose = [];
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const fence = parseFenceLine(line);
|
|
63
|
+
if (code === null) {
|
|
64
|
+
if (fence !== null) {
|
|
65
|
+
flushProse();
|
|
66
|
+
code = [];
|
|
67
|
+
marker = fence.marker[0];
|
|
68
|
+
language = fence.info;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
prose.push(line);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (fence !== null && fence.marker[0] === marker) {
|
|
75
|
+
blocks.push({ kind: "code", text: code.join("\n"), language });
|
|
76
|
+
code = null;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
code.push(line);
|
|
80
|
+
}
|
|
81
|
+
if (code !== null) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
flushProse();
|
|
85
|
+
return blocks;
|
|
86
|
+
}
|
|
87
|
+
function renderLink(label, url) {
|
|
88
|
+
const trimmed = url.trim();
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = new URL(trimmed);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (!SAFE_SCHEMES.has(parsed.protocol)) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return `<a href="${escapeAttribute(trimmed)}">${inline(label)}</a>`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Match `](url)` starting at `open` in `text`, tolerating balanced parentheses
|
|
103
|
+
* inside the URL (Wikipedia links are the usual offender).
|
|
104
|
+
*/
|
|
105
|
+
function matchLink(text, open) {
|
|
106
|
+
let depth = 1;
|
|
107
|
+
let i = open + 1;
|
|
108
|
+
for (; i < text.length; i += 1) {
|
|
109
|
+
const ch = text[i];
|
|
110
|
+
if (ch === "\n") {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (ch === "[") {
|
|
114
|
+
depth += 1;
|
|
115
|
+
}
|
|
116
|
+
else if (ch === "]") {
|
|
117
|
+
depth -= 1;
|
|
118
|
+
if (depth === 0) {
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (depth !== 0 || text[i + 1] !== "(") {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const label = text.slice(open + 1, i);
|
|
127
|
+
let parens = 1;
|
|
128
|
+
let j = i + 2;
|
|
129
|
+
for (; j < text.length; j += 1) {
|
|
130
|
+
const ch = text[j];
|
|
131
|
+
if (ch === "\n") {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (ch === "(") {
|
|
135
|
+
parens += 1;
|
|
136
|
+
}
|
|
137
|
+
else if (ch === ")") {
|
|
138
|
+
parens -= 1;
|
|
139
|
+
if (parens === 0) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (parens !== 0) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return { label, url: text.slice(i + 2, j), end: j };
|
|
148
|
+
}
|
|
149
|
+
function findClosing(text, from, token) {
|
|
150
|
+
const next = text.indexOf(token, from);
|
|
151
|
+
// An emphasis run does not span a blank line.
|
|
152
|
+
if (next === -1 || text.slice(from, next).includes("\n\n")) {
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
return next;
|
|
156
|
+
}
|
|
157
|
+
/** Is `_` at `index` a word-internal underscore (`snake_case`) rather than emphasis? */
|
|
158
|
+
function isWordUnderscore(text, index, run) {
|
|
159
|
+
const before = index > 0 ? text[index - 1] : "";
|
|
160
|
+
const after = text[index + run] ?? "";
|
|
161
|
+
return /[A-Za-z0-9]/.test(before) && /[A-Za-z0-9]/.test(after);
|
|
162
|
+
}
|
|
163
|
+
/** Convert one prose run: bold, italic, inline code and links, rest escaped. */
|
|
164
|
+
function inline(text) {
|
|
165
|
+
let out = "";
|
|
166
|
+
let i = 0;
|
|
167
|
+
while (i < text.length) {
|
|
168
|
+
const ch = text[i];
|
|
169
|
+
if (ch === "`") {
|
|
170
|
+
const close = text.indexOf("`", i + 1);
|
|
171
|
+
if (close > i + 1) {
|
|
172
|
+
out += `<code>${escapeHtml(text.slice(i + 1, close))}</code>`;
|
|
173
|
+
i = close + 1;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (ch === "[") {
|
|
178
|
+
const link = matchLink(text, i);
|
|
179
|
+
if (link !== null) {
|
|
180
|
+
const rendered = renderLink(link.label, link.url);
|
|
181
|
+
if (rendered !== null) {
|
|
182
|
+
out += rendered;
|
|
183
|
+
i = link.end + 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const double = text.slice(i, i + 2);
|
|
189
|
+
if ((double === "**" || double === "__") && !(double === "__" && isWordUnderscore(text, i, 2))) {
|
|
190
|
+
const close = findClosing(text, i + 2, double);
|
|
191
|
+
if (close > i + 2) {
|
|
192
|
+
out += `<b>${inline(text.slice(i + 2, close))}</b>`;
|
|
193
|
+
i = close + 2;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if ((ch === "*" || ch === "_") && !(ch === "_" && isWordUnderscore(text, i, 1))) {
|
|
198
|
+
const close = findClosing(text, i + 1, ch);
|
|
199
|
+
if (close > i + 1 && text[i + 1] !== " ") {
|
|
200
|
+
out += `<i>${inline(text.slice(i + 1, close))}</i>`;
|
|
201
|
+
i = close + 1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
out += escapeHtml(ch);
|
|
206
|
+
i += 1;
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
const TAG_SCAN = /<(\/?)([a-z]+)(?:\s[^<>]*)?>/g;
|
|
211
|
+
/** Every emitted tag balanced and properly nested? The converter's own audit. */
|
|
212
|
+
function isBalanced(html) {
|
|
213
|
+
const stack = [];
|
|
214
|
+
for (const match of html.matchAll(TAG_SCAN)) {
|
|
215
|
+
if (match[1] === "/") {
|
|
216
|
+
if (stack.pop() !== match[2]) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
stack.push(match[2]);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return stack.length === 0;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Convert agent markdown to Telegram HTML.
|
|
228
|
+
*
|
|
229
|
+
* @returns `{ok: true, html}` when the safe subset covered the input, or
|
|
230
|
+
* `{ok: false, text}` with the original text for a plain-text send.
|
|
231
|
+
*/
|
|
232
|
+
export function markdownToTelegramHtml(markdown) {
|
|
233
|
+
const blocks = splitBlocks(markdown);
|
|
234
|
+
if (blocks === null) {
|
|
235
|
+
return { ok: false, text: markdown };
|
|
236
|
+
}
|
|
237
|
+
const parts = blocks.map((block) => {
|
|
238
|
+
if (block.kind === "code") {
|
|
239
|
+
const language = block.language ?? "";
|
|
240
|
+
const open = language.length > 0 ? `<pre><code class="language-${escapeAttribute(language)}">` : "<pre><code>";
|
|
241
|
+
return `${open}${escapeHtml(block.text)}</code></pre>`;
|
|
242
|
+
}
|
|
243
|
+
return inline(block.text);
|
|
244
|
+
});
|
|
245
|
+
const html = parts.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
246
|
+
if (!isBalanced(html)) {
|
|
247
|
+
return { ok: false, text: markdown };
|
|
248
|
+
}
|
|
249
|
+
return { ok: true, html };
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=markdown-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-html.js","sourceRoot":"","sources":["../src/markdown-html.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one module that touches `@nodetool-ai/sdk` and `ws`.
|
|
3
|
+
*
|
|
4
|
+
* The router talks to a narrow `BridgeClient` interface; this is the real
|
|
5
|
+
* implementation of it — a `ChatSocket` on the user's delegated token, plus
|
|
6
|
+
* the two tRPC reads the bridge makes (thread ids for `n` recovery, asset
|
|
7
|
+
* metadata for outbound files). Keeping the SDK behind this seam is what lets
|
|
8
|
+
* the router's whole test suite run on a scripted fake socket.
|
|
9
|
+
*/
|
|
10
|
+
import type { ChatSocket } from "@nodetool-ai/sdk";
|
|
11
|
+
import type { AssetAttachment } from "./frame-renderer.js";
|
|
12
|
+
import type { BridgeChatSocket, MakeBridgeClient } from "./turn-router.js";
|
|
13
|
+
import type { DeliveryContext } from "./turn-router.js";
|
|
14
|
+
import type { ResolvedAsset } from "./telegram-adapter.js";
|
|
15
|
+
/** Wrap the SDK socket in the narrow interface the router consumes. */
|
|
16
|
+
export declare function wrapChatSocket(socket: ChatSocket): BridgeChatSocket;
|
|
17
|
+
export interface BridgeClientFactoryOptions {
|
|
18
|
+
/** NodeTool server base URL. */
|
|
19
|
+
readonly apiUrl: string;
|
|
20
|
+
readonly fetch?: typeof fetch;
|
|
21
|
+
}
|
|
22
|
+
/** A `makeClient` for the router, backed by the real server. */
|
|
23
|
+
export declare function createBridgeClientFactory(options: BridgeClientFactoryOptions): MakeBridgeClient;
|
|
24
|
+
/** Bare asset id out of `asset://<id>[.ext]`, or null for anything else. */
|
|
25
|
+
export declare function assetIdOf(uri: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch an attachment's bytes as the linked user. An `asset://` reference is
|
|
28
|
+
* resolved through tRPC first, because only the server knows where the object
|
|
29
|
+
* lives; an absolute URL is fetched directly with the same token.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createAssetResolver(options: BridgeClientFactoryOptions): (asset: AssetAttachment, context: DeliveryContext) => Promise<ResolvedAsset | null>;
|
|
32
|
+
//# sourceMappingURL=nodetool-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodetool-client.d.ts","sourceRoot":"","sources":["../src/nodetool-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAgB,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAa3D,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAwBnE;AAED,MAAM,WAAW,0BAA0B;IACzC,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC/B;AAED,gEAAgE;AAChE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,gBAAgB,CA+BlB;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,WAG5D,eAAe,WACb,eAAe,KACvB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAmCjC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one module that touches `@nodetool-ai/sdk` and `ws`.
|
|
3
|
+
*
|
|
4
|
+
* The router talks to a narrow `BridgeClient` interface; this is the real
|
|
5
|
+
* implementation of it — a `ChatSocket` on the user's delegated token, plus
|
|
6
|
+
* the two tRPC reads the bridge makes (thread ids for `n` recovery, asset
|
|
7
|
+
* metadata for outbound files). Keeping the SDK behind this seam is what lets
|
|
8
|
+
* the router's whole test suite run on a scripted fake socket.
|
|
9
|
+
*/
|
|
10
|
+
import { WebSocket as NodeWebSocket } from "ws";
|
|
11
|
+
import { createNodetoolClient } from "@nodetool-ai/sdk";
|
|
12
|
+
// SAFETY: `ws`'s WebSocket implements the browser constructor shape the SDK
|
|
13
|
+
// asks for (`new (url) => WebSocket` plus the readyState constants), but its
|
|
14
|
+
// declarations are written against Node's own event types rather than the DOM
|
|
15
|
+
// lib, so the two never structurally match. Every SDK use of it — construct,
|
|
16
|
+
// `readyState`, `send`, `close`, the `on*` handlers — is in the intersection.
|
|
17
|
+
const WS_CTOR = NodeWebSocket;
|
|
18
|
+
/** Thread ids are read in pages; this bounds the recovery scan. */
|
|
19
|
+
const THREAD_PAGE_LIMIT = 200;
|
|
20
|
+
const THREAD_PAGE_MAX = 10;
|
|
21
|
+
/** Wrap the SDK socket in the narrow interface the router consumes. */
|
|
22
|
+
export function wrapChatSocket(socket) {
|
|
23
|
+
return {
|
|
24
|
+
connect: () => socket.connect(),
|
|
25
|
+
disconnect: () => socket.disconnect(),
|
|
26
|
+
send: (options) => socket.send({
|
|
27
|
+
threadId: options.threadId,
|
|
28
|
+
text: options.text,
|
|
29
|
+
provider: options.provider ?? null,
|
|
30
|
+
model: options.model ?? null,
|
|
31
|
+
agentMode: options.agentMode ?? true,
|
|
32
|
+
permissionMode: options.permissionMode ?? "auto"
|
|
33
|
+
}),
|
|
34
|
+
stop: (threadId) => socket.stop(threadId),
|
|
35
|
+
resume: (threadId, lastSeq) => socket.resume(threadId, lastSeq),
|
|
36
|
+
onFrame: (listener) => socket.on("raw", (frame) => {
|
|
37
|
+
// SAFETY: the renderer's `RenderFrame` is the protocol union plus an
|
|
38
|
+
// optional seq; the socket's `raw` event carries exactly the decoded
|
|
39
|
+
// frame, whose unmodelled members the renderer's `default` case drops.
|
|
40
|
+
listener(frame);
|
|
41
|
+
}),
|
|
42
|
+
onStateChange: (listener) => socket.on("state", (state) => listener(state))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** A `makeClient` for the router, backed by the real server. */
|
|
46
|
+
export function createBridgeClientFactory(options) {
|
|
47
|
+
return ({ token }) => {
|
|
48
|
+
const client = createNodetoolClient({
|
|
49
|
+
baseUrl: options.apiUrl,
|
|
50
|
+
authToken: token,
|
|
51
|
+
fetch: options.fetch,
|
|
52
|
+
WebSocket: WS_CTOR
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
socket: wrapChatSocket(client.chat()),
|
|
56
|
+
async listThreadIds() {
|
|
57
|
+
const ids = [];
|
|
58
|
+
let cursor;
|
|
59
|
+
for (let page = 0; page < THREAD_PAGE_MAX; page += 1) {
|
|
60
|
+
const input = { limit: THREAD_PAGE_LIMIT };
|
|
61
|
+
if (cursor !== undefined) {
|
|
62
|
+
input.cursor = cursor;
|
|
63
|
+
}
|
|
64
|
+
const result = await client.trpc.threads.list.query(input);
|
|
65
|
+
for (const thread of result.threads) {
|
|
66
|
+
ids.push(thread.id);
|
|
67
|
+
}
|
|
68
|
+
if (!result.next) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
cursor = result.next;
|
|
72
|
+
}
|
|
73
|
+
return ids;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Bare asset id out of `asset://<id>[.ext]`, or null for anything else. */
|
|
79
|
+
export function assetIdOf(uri) {
|
|
80
|
+
if (!uri.startsWith("asset://")) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const rest = uri.slice("asset://".length);
|
|
84
|
+
const withoutExt = rest.replace(/\.[A-Za-z0-9]+$/, "");
|
|
85
|
+
return withoutExt.length > 0 ? withoutExt : null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Fetch an attachment's bytes as the linked user. An `asset://` reference is
|
|
89
|
+
* resolved through tRPC first, because only the server knows where the object
|
|
90
|
+
* lives; an absolute URL is fetched directly with the same token.
|
|
91
|
+
*/
|
|
92
|
+
export function createAssetResolver(options) {
|
|
93
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
94
|
+
return async (asset, context) => {
|
|
95
|
+
const client = createNodetoolClient({
|
|
96
|
+
baseUrl: options.apiUrl,
|
|
97
|
+
authToken: context.token,
|
|
98
|
+
fetch: fetchImpl,
|
|
99
|
+
WebSocket: WS_CTOR
|
|
100
|
+
});
|
|
101
|
+
let url = asset.uri;
|
|
102
|
+
let filename = asset.name ?? "attachment";
|
|
103
|
+
let contentType = asset.contentType;
|
|
104
|
+
const assetId = assetIdOf(asset.uri);
|
|
105
|
+
if (assetId !== null) {
|
|
106
|
+
const record = await client.trpc.assets.get.query({ id: assetId });
|
|
107
|
+
if (!record.get_url) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
url = record.get_url;
|
|
111
|
+
filename = record.name;
|
|
112
|
+
contentType = record.content_type;
|
|
113
|
+
}
|
|
114
|
+
const response = await fetchImpl(url, {
|
|
115
|
+
headers: { authorization: `Bearer ${context.token}` }
|
|
116
|
+
});
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
bytes: new Uint8Array(await response.arrayBuffer()),
|
|
122
|
+
filename,
|
|
123
|
+
contentType
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=nodetool-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodetool-client.js","sourceRoot":"","sources":["../src/nodetool-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `setMyCommands` — a deploy step, not a boot step (design §12).
|
|
3
|
+
*
|
|
4
|
+
* Registering the command list is what makes `/link`, `/new` and the rest
|
|
5
|
+
* autocomplete in the Telegram UI. It is idempotent: Telegram replaces the
|
|
6
|
+
* whole list on every call, so running it twice leaves the same six commands.
|
|
7
|
+
*/
|
|
8
|
+
import { type BotCommandSpec } from "./bot-api.js";
|
|
9
|
+
import { BOT_COMMANDS } from "./commands.js";
|
|
10
|
+
export interface RegisterCommandsOptions {
|
|
11
|
+
readonly fetch?: typeof fetch;
|
|
12
|
+
/** Bot API base URL, for tests. */
|
|
13
|
+
readonly baseUrl?: string;
|
|
14
|
+
/** Command list to register. Defaults to the bridge's own six. */
|
|
15
|
+
readonly commands?: readonly BotCommandSpec[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Publish the bot's command list.
|
|
19
|
+
*
|
|
20
|
+
* @returns the commands that were registered.
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerCommands(botToken: string, options?: RegisterCommandsOptions): Promise<readonly BotCommandSpec[]>;
|
|
23
|
+
export { BOT_COMMANDS };
|
|
24
|
+
//# sourceMappingURL=register-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-commands.d.ts","sourceRoot":"","sources":["../src/register-commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,mCAAmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC,CAYpC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `setMyCommands` — a deploy step, not a boot step (design §12).
|
|
3
|
+
*
|
|
4
|
+
* Registering the command list is what makes `/link`, `/new` and the rest
|
|
5
|
+
* autocomplete in the Telegram UI. It is idempotent: Telegram replaces the
|
|
6
|
+
* whole list on every call, so running it twice leaves the same six commands.
|
|
7
|
+
*/
|
|
8
|
+
import { BotApi } from "./bot-api.js";
|
|
9
|
+
import { BOT_COMMANDS } from "./commands.js";
|
|
10
|
+
/**
|
|
11
|
+
* Publish the bot's command list.
|
|
12
|
+
*
|
|
13
|
+
* @returns the commands that were registered.
|
|
14
|
+
*/
|
|
15
|
+
export async function registerCommands(botToken, options = {}) {
|
|
16
|
+
const init = {
|
|
17
|
+
botToken,
|
|
18
|
+
fetch: options.fetch ?? fetch
|
|
19
|
+
};
|
|
20
|
+
if (options.baseUrl !== undefined) {
|
|
21
|
+
init.baseUrl = options.baseUrl;
|
|
22
|
+
}
|
|
23
|
+
const api = new BotApi(init);
|
|
24
|
+
const commands = options.commands ?? BOT_COMMANDS;
|
|
25
|
+
await api.setMyCommands(commands);
|
|
26
|
+
return commands;
|
|
27
|
+
}
|
|
28
|
+
export { BOT_COMMANDS };
|
|
29
|
+
//# sourceMappingURL=register-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-commands.js","sourceRoot":"","sources":["../src/register-commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAU7C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,OAAO,GAA4B,EAAE;IAErC,MAAM,IAAI,GAAgE;QACxE,QAAQ;QACR,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;KAC9B,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,YAAY,CAAC;IAClD,MAAM,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trim trailing slashes from a URL base. A loop rather than `/\/+$/`: the
|
|
3
|
+
* unanchored regex backtracks quadratically on inputs with long interior
|
|
4
|
+
* slash runs (CodeQL js/polynomial-redos), and a base URL is operator input.
|
|
5
|
+
*/
|
|
6
|
+
export declare function trimTrailingSlashes(value: string): string;
|
|
7
|
+
//# sourceMappingURL=strings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD"}
|
package/dist/strings.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trim trailing slashes from a URL base. A loop rather than `/\/+$/`: the
|
|
3
|
+
* unanchored regex backtracks quadratically on inputs with long interior
|
|
4
|
+
* slash runs (CodeQL js/polynomial-redos), and a base URL is operator input.
|
|
5
|
+
*/
|
|
6
|
+
export function trimTrailingSlashes(value) {
|
|
7
|
+
let end = value.length;
|
|
8
|
+
while (end > 0 && value[end - 1] === "/") {
|
|
9
|
+
end -= 1;
|
|
10
|
+
}
|
|
11
|
+
return value.slice(0, end);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strings.js","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QACzC,GAAG,IAAI,CAAC,CAAC;IACX,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TelegramAdapter — the only module that knows Telegram (design §3).
|
|
3
|
+
*
|
|
4
|
+
* Two directions meet here. Inbound: `getUpdates` long polling, private-chat
|
|
5
|
+
* and command routing, the unlinked-user prompt, group declines. Outbound: the
|
|
6
|
+
* renderer's ops become Bot API calls, with the platform's two facts of life
|
|
7
|
+
* handled rather than hoped away — 429 `retry_after`, and the parse mode
|
|
8
|
+
* rejecting text, which falls back to plain rather than failing the turn.
|
|
9
|
+
*
|
|
10
|
+
* Ops are executed off a per-chat queue. That queue is what makes superseded
|
|
11
|
+
* edits droppable: while a backoff holds the head of the queue, later edits to
|
|
12
|
+
* the same message pile up behind it, and only the last one is worth sending.
|
|
13
|
+
*/
|
|
14
|
+
import type { RenderOp } from "./frame-renderer.js";
|
|
15
|
+
import type { AssetAttachment } from "./frame-renderer.js";
|
|
16
|
+
import type { DeliveryContext } from "./turn-router.js";
|
|
17
|
+
import type { TurnRouter } from "./turn-router.js";
|
|
18
|
+
import type { BotApi, TelegramUpdate } from "./bot-api.js";
|
|
19
|
+
import type { CommandIdentity } from "./commands.js";
|
|
20
|
+
/** Long-poll seconds asked of `getUpdates`. */
|
|
21
|
+
export declare const POLL_TIMEOUT_SECONDS = 50;
|
|
22
|
+
/** Callback data on the inline stop button. */
|
|
23
|
+
export declare const STOP_CALLBACK_DATA = "stop";
|
|
24
|
+
/** Bytes of an asset, ready to upload. */
|
|
25
|
+
export interface ResolvedAsset {
|
|
26
|
+
readonly bytes: Uint8Array;
|
|
27
|
+
readonly filename: string;
|
|
28
|
+
readonly contentType: string | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fetch an attachment's bytes on the user's own delegated token. Injected so
|
|
32
|
+
* the adapter's tests never touch the network and the asset-URL scheme stays
|
|
33
|
+
* the client module's business.
|
|
34
|
+
*/
|
|
35
|
+
export type ResolveAsset = (asset: AssetAttachment, context: DeliveryContext) => Promise<ResolvedAsset | null>;
|
|
36
|
+
export interface AdapterConfig {
|
|
37
|
+
readonly allowUsers: readonly string[];
|
|
38
|
+
readonly apiUrl: string;
|
|
39
|
+
}
|
|
40
|
+
export interface TelegramAdapterOptions {
|
|
41
|
+
readonly api: BotApi;
|
|
42
|
+
readonly identity: CommandIdentity;
|
|
43
|
+
readonly router: TurnRouter;
|
|
44
|
+
readonly config: AdapterConfig;
|
|
45
|
+
readonly fetch: typeof fetch;
|
|
46
|
+
/** Sleep, injected so backoff tests do not wait. */
|
|
47
|
+
readonly wait?: (ms: number) => Promise<void>;
|
|
48
|
+
readonly resolveAsset?: ResolveAsset;
|
|
49
|
+
readonly log?: (message: string, detail?: unknown) => void;
|
|
50
|
+
}
|
|
51
|
+
/** Why the poll loop stopped. */
|
|
52
|
+
export type PollStopReason = "stopped" | "conflict" | "fatal";
|
|
53
|
+
export declare class TelegramAdapter {
|
|
54
|
+
private readonly api;
|
|
55
|
+
private readonly identity;
|
|
56
|
+
private readonly router;
|
|
57
|
+
private readonly config;
|
|
58
|
+
private readonly fetchImpl;
|
|
59
|
+
private readonly wait;
|
|
60
|
+
private readonly resolveAsset;
|
|
61
|
+
private readonly log;
|
|
62
|
+
private offset;
|
|
63
|
+
private running;
|
|
64
|
+
private readonly messages;
|
|
65
|
+
private readonly queues;
|
|
66
|
+
private readonly declinedGroups;
|
|
67
|
+
constructor(options: TelegramAdapterOptions);
|
|
68
|
+
/**
|
|
69
|
+
* Long-poll until {@link stop} is called.
|
|
70
|
+
*
|
|
71
|
+
* A 409 means another process holds this bot token's update stream — two
|
|
72
|
+
* consumers silently steal each other's updates, so the loop ends and says
|
|
73
|
+
* so instead of fighting for them.
|
|
74
|
+
*/
|
|
75
|
+
poll(): Promise<PollStopReason>;
|
|
76
|
+
/** Ask the poll loop to end after the in-flight request. */
|
|
77
|
+
stop(): void;
|
|
78
|
+
/** Wait until every queued op has been executed. */
|
|
79
|
+
flush(): Promise<void>;
|
|
80
|
+
handleUpdate(update: TelegramUpdate): Promise<void>;
|
|
81
|
+
private handleCallbackQuery;
|
|
82
|
+
private handleMessage;
|
|
83
|
+
private commandDeps;
|
|
84
|
+
/**
|
|
85
|
+
* Accept a batch of render ops for delivery. Returns once the ops are
|
|
86
|
+
* queued; the per-chat drain loop executes them in order.
|
|
87
|
+
*/
|
|
88
|
+
executeOps: (context: DeliveryContext, ops: readonly RenderOp[]) => void;
|
|
89
|
+
private queueFor;
|
|
90
|
+
private drain;
|
|
91
|
+
/** An intermediate edit nothing will see, because a later one is queued. */
|
|
92
|
+
private isSuperseded;
|
|
93
|
+
private executeOne;
|
|
94
|
+
private messagesFor;
|
|
95
|
+
private send;
|
|
96
|
+
private edit;
|
|
97
|
+
private sendPlain;
|
|
98
|
+
private attach;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=telegram-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-adapter.d.ts","sourceRoot":"","sources":["../src/telegram-adapter.ts"],"names":[],"mappings":"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"}
|