@mulmobridge/telegram 1.1.0 → 1.3.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.
- package/README.md +1 -0
- package/dist/api.d.ts +5 -0
- package/dist/api.js +20 -0
- package/dist/index.js +25 -75
- package/dist/router.js +17 -6
- package/dist/start.d.ts +31 -0
- package/dist/start.js +82 -0
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -61,6 +61,7 @@ The bridge enforces a **chat-ID allowlist**. Only messages from listed chat IDs
|
|
|
61
61
|
- Long-polling (no webhook setup needed)
|
|
62
62
|
- Photo attachment support (downloads and forwards to Claude vision)
|
|
63
63
|
- Long-message chunking (splits replies > 4096 chars)
|
|
64
|
+
- "typing…" while MulmoClaude works on a reply, from receipt until the final reply is sent (including before the first streamed text and while tools run)
|
|
64
65
|
- Server push delivery (Phase B of #268)
|
|
65
66
|
- Graceful shutdown on SIGINT
|
|
66
67
|
|
package/dist/api.d.ts
CHANGED
|
@@ -50,15 +50,20 @@ export interface GetUpdatesOptions {
|
|
|
50
50
|
timeoutSec?: number | undefined;
|
|
51
51
|
signal?: AbortSignal | undefined;
|
|
52
52
|
}
|
|
53
|
+
export type TelegramChatAction = "typing";
|
|
53
54
|
export interface TelegramApi {
|
|
54
55
|
getUpdates(opts?: GetUpdatesOptions): Promise<TelegramUpdate[]>;
|
|
55
56
|
/** Send a message and return its message_id (needed for editMessageText). */
|
|
56
57
|
sendMessage(chatId: number, text: string): Promise<number>;
|
|
57
58
|
/** Edit an existing message in place. Used for streaming text updates. */
|
|
58
59
|
editMessageText(chatId: number, messageId: number, text: string): Promise<void>;
|
|
60
|
+
/** Show a chat action such as "typing…" until the next message or about 5 s. */
|
|
61
|
+
sendChatAction(chatId: number, action: TelegramChatAction): Promise<void>;
|
|
59
62
|
/** Download any file by file_id via the Telegram getFile API.
|
|
60
63
|
* Returns a data URL (`data:<mime>;base64,...`).
|
|
61
64
|
* Works for photos, documents, audio, etc. */
|
|
62
65
|
downloadFile(fileId: string, fallbackMime?: string): Promise<string>;
|
|
63
66
|
}
|
|
64
67
|
export declare function createTelegramApi(opts: TelegramApiOptions): TelegramApi;
|
|
68
|
+
/** The description of a `{ ok: false }` Bot API body, or null when the body does not report a failure. */
|
|
69
|
+
export declare function apiFailureDescription(body: unknown): string | null;
|
package/dist/api.js
CHANGED
|
@@ -54,6 +54,20 @@ export function createTelegramApi(opts) {
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
+
async sendChatAction(chatId, action) {
|
|
58
|
+
const res = await fetchImpl(`${base}/sendChatAction`, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
body: JSON.stringify({ chat_id: chatId, action }),
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
throw new Error(`sendChatAction failed: ${res.status} ${await safeText(res)}`);
|
|
65
|
+
}
|
|
66
|
+
const failure = apiFailureDescription(await res.json().catch(() => null));
|
|
67
|
+
if (failure !== null) {
|
|
68
|
+
throw new Error(`sendChatAction API error: ${failure}`);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
57
71
|
async downloadFile(fileId, fallbackMime = "application/octet-stream") {
|
|
58
72
|
const getFileRes = await fetchImpl(`${base}/getFile?file_id=${encodeURIComponent(fileId)}`);
|
|
59
73
|
if (!getFileRes.ok) {
|
|
@@ -79,6 +93,12 @@ export function createTelegramApi(opts) {
|
|
|
79
93
|
},
|
|
80
94
|
};
|
|
81
95
|
}
|
|
96
|
+
/** The description of a `{ ok: false }` Bot API body, or null when the body does not report a failure. */
|
|
97
|
+
export function apiFailureDescription(body) {
|
|
98
|
+
if (typeof body !== "object" || body === null || !("ok" in body) || body.ok !== false)
|
|
99
|
+
return null;
|
|
100
|
+
return "description" in body && typeof body.description === "string" ? body.description : "unknown";
|
|
101
|
+
}
|
|
82
102
|
async function safeText(res) {
|
|
83
103
|
try {
|
|
84
104
|
return await res.text();
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Telegram bridge (issue #321). Polls Telegram for incoming
|
|
2
|
+
// Telegram bridge (issue #321) — the CLI. Polls Telegram for incoming
|
|
3
3
|
// messages and dispatches them through the shared message router,
|
|
4
4
|
// which enforces the chat-ID allowlist, forwards to MulmoClaude,
|
|
5
5
|
// and delivers Phase-B pushes back through sendMessage.
|
|
6
6
|
//
|
|
7
|
+
// The bridge itself lives in `./start.js` so the server can run it
|
|
8
|
+
// in-process (#3080). This file is the env-reading, `process.exit`-calling
|
|
9
|
+
// half that only a CLI wants.
|
|
10
|
+
//
|
|
7
11
|
// Env surface (see docs/message_apps/telegram/README.md):
|
|
8
12
|
// TELEGRAM_BOT_TOKEN — BotFather token (required)
|
|
9
13
|
// TELEGRAM_ALLOWED_CHAT_IDS — CSV of integer chat IDs (required
|
|
@@ -13,95 +17,41 @@
|
|
|
13
17
|
// TELEGRAM_POLL_TIMEOUT_SEC — optional, default 25
|
|
14
18
|
import "dotenv/config";
|
|
15
19
|
import { createBridgeClient, installProcessGuards } from "@mulmobridge/client";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const botToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
22
|
-
if (!botToken || botToken.trim().length === 0) {
|
|
23
|
-
console.error("TELEGRAM_BOT_TOKEN is required. See docs/message_apps/telegram/.");
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
let allowlist;
|
|
20
|
+
import { readTelegramEnv, startTelegramBridge, TELEGRAM_TRANSPORT_ID as TRANSPORT_ID } from "./start.js";
|
|
21
|
+
async function main() {
|
|
22
|
+
// `readTelegramEnv` throws; the CLI turns that into the same message and exit
|
|
23
|
+
// code it printed when the reading lived here.
|
|
24
|
+
let config;
|
|
27
25
|
try {
|
|
28
|
-
|
|
26
|
+
config = readTelegramEnv(process.env);
|
|
29
27
|
}
|
|
30
28
|
catch (err) {
|
|
31
29
|
console.error(err instanceof Error ? err.message : String(err));
|
|
32
30
|
process.exit(1);
|
|
33
31
|
}
|
|
34
|
-
const pollTimeoutSec =
|
|
35
|
-
if (!Number.isInteger(pollTimeoutSec) || pollTimeoutSec < 0) {
|
|
36
|
-
console.error("TELEGRAM_POLL_TIMEOUT_SEC must be a non-negative integer");
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
return { botToken, allowlist, pollTimeoutSec };
|
|
40
|
-
}
|
|
41
|
-
async function pollLoop(api, router, opts) {
|
|
42
|
-
let offset;
|
|
43
|
-
while (!opts.abortSignal.aborted) {
|
|
44
|
-
let updates;
|
|
45
|
-
try {
|
|
46
|
-
updates = await api.getUpdates({
|
|
47
|
-
offset,
|
|
48
|
-
timeoutSec: opts.pollTimeoutSec,
|
|
49
|
-
signal: opts.abortSignal,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
if (opts.abortSignal.aborted)
|
|
54
|
-
return;
|
|
55
|
-
console.error(`[telegram] getUpdates error: ${String(err)}`);
|
|
56
|
-
// Back off briefly so a broken network doesn't busy-loop.
|
|
57
|
-
await delay(1000);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
for (const update of updates) {
|
|
61
|
-
offset = update.update_id + 1;
|
|
62
|
-
if (update.message) {
|
|
63
|
-
await router.handleMessage(update.message);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function delay(ms) {
|
|
69
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
70
|
-
}
|
|
71
|
-
async function main() {
|
|
72
|
-
const { botToken, allowlist, pollTimeoutSec } = readEnv();
|
|
32
|
+
const { botToken, allowlist, pollTimeoutSec } = config;
|
|
73
33
|
console.log("MulmoClaude Telegram bridge");
|
|
74
34
|
console.log(`Allowlist: ${allowlist.size() > 0 ? allowlist.snapshot().join(", ") : "(empty — all chats will be denied)"}`);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
});
|
|
82
|
-
// Server → Telegram streaming text chunks (Phase C of #268).
|
|
83
|
-
client.onTextChunk((chunk) => {
|
|
84
|
-
router.handleTextChunk(chunk);
|
|
85
|
-
});
|
|
86
|
-
// Server → Telegram async push (Phase B of #268).
|
|
87
|
-
client.onPush((ev) => {
|
|
88
|
-
router.handlePush(ev).catch((err) => console.error(`[telegram] handlePush failed: ${String(err)}`));
|
|
89
|
-
});
|
|
90
|
-
const abortController = new AbortController();
|
|
91
|
-
// Installed here rather than at module level because the shutdown work needs
|
|
92
|
-
// the controller and the socket. `main().catch` still covers the awaited
|
|
93
|
-
// startup path above, so nothing is unguarded in between.
|
|
35
|
+
// Guards BEFORE the bridge starts. Splitting the bridge into a library moved
|
|
36
|
+
// the poll loop's start from `await pollLoop(...)` to inside
|
|
37
|
+
// `startTelegramBridge`, which would otherwise leave a window where the loop
|
|
38
|
+
// is running and a signal is still unhandled. The holder is what lets the
|
|
39
|
+
// shutdown callback reach a handle that does not exist yet.
|
|
40
|
+
const started = { handle: null };
|
|
94
41
|
installProcessGuards({
|
|
95
42
|
name: TRANSPORT_ID,
|
|
96
43
|
onShutdown: () => {
|
|
97
|
-
|
|
98
|
-
client.close();
|
|
44
|
+
started.handle?.close();
|
|
99
45
|
},
|
|
100
46
|
});
|
|
101
|
-
|
|
47
|
+
const handle = startTelegramBridge({
|
|
48
|
+
botToken,
|
|
49
|
+
allowlist,
|
|
102
50
|
pollTimeoutSec,
|
|
103
|
-
|
|
51
|
+
client: createBridgeClient({ transportId: TRANSPORT_ID }),
|
|
104
52
|
});
|
|
53
|
+
started.handle = handle;
|
|
54
|
+
await handle.done;
|
|
105
55
|
}
|
|
106
56
|
main().catch((err) => {
|
|
107
57
|
console.error("Fatal:", err);
|
package/dist/router.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Telegram API, no real MulmoClaude socket, no env reads). The
|
|
4
4
|
// entrypoint in index.ts is then just: read env → wire real deps →
|
|
5
5
|
// drive the polling loop.
|
|
6
|
-
import { parseDataUrl } from "@mulmobridge/client";
|
|
6
|
+
import { parseDataUrl, replyAfterProcessingIndicator } from "@mulmobridge/client";
|
|
7
7
|
// Telegram caps a single message at 4096 chars. We split long
|
|
8
8
|
// replies naively; pretty formatting (preserve markdown, break on
|
|
9
9
|
// sentence boundaries) is a follow-up.
|
|
@@ -17,6 +17,8 @@ const defaultLog = {
|
|
|
17
17
|
// rate limits. 1 second is conservative; Telegram allows ~30 edits
|
|
18
18
|
// per minute per chat.
|
|
19
19
|
const STREAM_EDIT_INTERVAL_MS = 1000;
|
|
20
|
+
// Telegram shows a chat action for about 5 s, or until the next message.
|
|
21
|
+
const TYPING_REFRESH_MS = 4_000;
|
|
20
22
|
export function createMessageRouter(deps) {
|
|
21
23
|
const { api, allowlist, sendToMulmo } = deps;
|
|
22
24
|
const log = deps.log ?? defaultLog;
|
|
@@ -132,12 +134,18 @@ export function createMessageRouter(deps) {
|
|
|
132
134
|
return;
|
|
133
135
|
const mediaLabel = buildMediaLabel(msg);
|
|
134
136
|
log.info(`[telegram] accepted chat=${chatId} user=@${userLabel(msg)} len=${text.length}${mediaLabel}`);
|
|
137
|
+
await replyAfterProcessingIndicator(() => api.sendChatAction(chatId, "typing"), { produce: () => relay(msg, chatId, text), post: (deliver) => deliver() }, { refreshMs: TYPING_REFRESH_MS, label: "telegram", log });
|
|
138
|
+
}
|
|
139
|
+
/** Everything up to the final reply, run while "typing…" shows; returns the
|
|
140
|
+
* step that delivers the reply, which runs once the indicator is down. */
|
|
141
|
+
async function relay(msg, chatId, text) {
|
|
135
142
|
const { attachments, failed } = await tryDownloadAttachments(msg);
|
|
136
143
|
// Attachment-only message where download failed: bail out instead
|
|
137
144
|
// of sending a query without the actual file.
|
|
138
145
|
if (failed && text.trim().length === 0) {
|
|
139
|
-
|
|
140
|
-
|
|
146
|
+
return async () => {
|
|
147
|
+
await api.sendMessage(chatId, "Sorry, I could not download the attachment. Please try again.").catch(() => { });
|
|
148
|
+
};
|
|
141
149
|
}
|
|
142
150
|
// Surface the attachment failure so the user knows the file was lost.
|
|
143
151
|
const messageText = resolveMessageText(text, failed);
|
|
@@ -149,9 +157,12 @@ export function createMessageRouter(deps) {
|
|
|
149
157
|
flushStream();
|
|
150
158
|
const sentMsgId = streamMessageId;
|
|
151
159
|
stopStream();
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
return () => deliverFinal(chatId, sentMsgId, ack);
|
|
161
|
+
}
|
|
162
|
+
// If streaming sent the text, do a final edit with the full ack
|
|
163
|
+
// to catch any trailing chunks. If no streaming happened,
|
|
164
|
+
// send the full reply as a new message.
|
|
165
|
+
async function deliverFinal(chatId, sentMsgId, ack) {
|
|
155
166
|
if (sentMsgId !== null && ack.ok) {
|
|
156
167
|
await api.editMessageText(chatId, sentMsgId, ack.reply ?? "").catch(() => { });
|
|
157
168
|
}
|
package/dist/start.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BridgeClient } from "@mulmobridge/client";
|
|
2
|
+
import { type Allowlist } from "./allowlist.js";
|
|
3
|
+
export declare const TELEGRAM_TRANSPORT_ID = "telegram";
|
|
4
|
+
/** Console-compatible shape, matching `RouterDeps["log"]`. */
|
|
5
|
+
export interface BridgeLog {
|
|
6
|
+
info: (msg: string) => void;
|
|
7
|
+
warn: (msg: string) => void;
|
|
8
|
+
error: (msg: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface TelegramStartOptions {
|
|
11
|
+
botToken: string;
|
|
12
|
+
allowlist: Allowlist;
|
|
13
|
+
/** Long-poll timeout handed to `getUpdates`. */
|
|
14
|
+
pollTimeoutSec: number;
|
|
15
|
+
/** Supplied by the caller so the CLI can pass a socket client and the server
|
|
16
|
+
* an in-process one. */
|
|
17
|
+
client: BridgeClient;
|
|
18
|
+
log?: BridgeLog;
|
|
19
|
+
}
|
|
20
|
+
export interface BridgeHandle {
|
|
21
|
+
/** Stops polling and releases the client. Idempotent. */
|
|
22
|
+
close: () => void;
|
|
23
|
+
/** Resolves when the poll loop has stopped. The CLI awaits it; a host that
|
|
24
|
+
* starts many bridges attaches a rejection handler instead. */
|
|
25
|
+
done: Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/** The env this bridge needs, read from an explicit bag rather than
|
|
28
|
+
* `process.env` and THROWING rather than exiting, so the server can start it
|
|
29
|
+
* in-process (#3080) while the CLI keeps its own exit codes. */
|
|
30
|
+
export declare function readTelegramEnv(env: Record<string, string | undefined>): Omit<TelegramStartOptions, "client" | "log">;
|
|
31
|
+
export declare function startTelegramBridge(opts: TelegramStartOptions): BridgeHandle;
|
package/dist/start.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The Telegram bridge as a LIBRARY (#3080): everything the CLI used to do
|
|
2
|
+
// inline, minus the two things a host cannot tolerate — reading `process.env`
|
|
3
|
+
// and calling `process.exit`.
|
|
4
|
+
//
|
|
5
|
+
// Configuration arrives as arguments and failures arrive as thrown errors, so
|
|
6
|
+
// the same code serves `yarn telegram` (which reads env and exits) and the
|
|
7
|
+
// server's in-process registry (which logs and keeps running).
|
|
8
|
+
import { createTelegramApi } from "./api.js";
|
|
9
|
+
import { parseAllowlist } from "./allowlist.js";
|
|
10
|
+
import { createMessageRouter } from "./router.js";
|
|
11
|
+
export const TELEGRAM_TRANSPORT_ID = "telegram";
|
|
12
|
+
const DEFAULT_POLL_TIMEOUT_SEC = 25;
|
|
13
|
+
/** The env this bridge needs, read from an explicit bag rather than
|
|
14
|
+
* `process.env` and THROWING rather than exiting, so the server can start it
|
|
15
|
+
* in-process (#3080) while the CLI keeps its own exit codes. */
|
|
16
|
+
export function readTelegramEnv(env) {
|
|
17
|
+
const botToken = env.TELEGRAM_BOT_TOKEN;
|
|
18
|
+
if (!botToken || botToken.trim().length === 0) {
|
|
19
|
+
throw new Error("TELEGRAM_BOT_TOKEN is required. See docs/message_apps/telegram/.");
|
|
20
|
+
}
|
|
21
|
+
const allowlist = parseAllowlist(env.TELEGRAM_ALLOWED_CHAT_IDS);
|
|
22
|
+
const pollTimeoutSec = Number(env.TELEGRAM_POLL_TIMEOUT_SEC ?? String(DEFAULT_POLL_TIMEOUT_SEC));
|
|
23
|
+
if (!Number.isInteger(pollTimeoutSec) || pollTimeoutSec < 0) {
|
|
24
|
+
throw new Error("TELEGRAM_POLL_TIMEOUT_SEC must be a non-negative integer");
|
|
25
|
+
}
|
|
26
|
+
return { botToken, allowlist, pollTimeoutSec };
|
|
27
|
+
}
|
|
28
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
|
+
const POLL_ERROR_BACKOFF_MS = 1000;
|
|
30
|
+
async function pollLoop(api, router, opts) {
|
|
31
|
+
let offset;
|
|
32
|
+
while (!opts.abortSignal.aborted) {
|
|
33
|
+
let updates;
|
|
34
|
+
try {
|
|
35
|
+
updates = await api.getUpdates({ offset, timeoutSec: opts.pollTimeoutSec, signal: opts.abortSignal });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (opts.abortSignal.aborted)
|
|
39
|
+
return;
|
|
40
|
+
opts.log.error(`[telegram] getUpdates error: ${String(err)}`);
|
|
41
|
+
// Back off briefly so a broken network doesn't busy-loop.
|
|
42
|
+
await delay(POLL_ERROR_BACKOFF_MS);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
for (const update of updates) {
|
|
46
|
+
offset = update.update_id + 1;
|
|
47
|
+
if (update.message) {
|
|
48
|
+
await router.handleMessage(update.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function startTelegramBridge(opts) {
|
|
54
|
+
const log = opts.log ?? console;
|
|
55
|
+
const api = createTelegramApi({ botToken: opts.botToken });
|
|
56
|
+
const router = createMessageRouter({
|
|
57
|
+
api,
|
|
58
|
+
allowlist: opts.allowlist,
|
|
59
|
+
sendToMulmo: (chatId, text, attachments) => opts.client.send(chatId, text, attachments),
|
|
60
|
+
});
|
|
61
|
+
// Server → Telegram streaming text chunks (Phase C of #268).
|
|
62
|
+
opts.client.onTextChunk((chunk) => {
|
|
63
|
+
router.handleTextChunk(chunk);
|
|
64
|
+
});
|
|
65
|
+
// Server → Telegram async push (Phase B of #268).
|
|
66
|
+
opts.client.onPush((event) => {
|
|
67
|
+
router.handlePush(event).catch((err) => log.error(`[telegram] handlePush failed: ${String(err)}`));
|
|
68
|
+
});
|
|
69
|
+
const abortController = new AbortController();
|
|
70
|
+
let closed = false;
|
|
71
|
+
const done = pollLoop(api, router, { pollTimeoutSec: opts.pollTimeoutSec, abortSignal: abortController.signal, log });
|
|
72
|
+
return {
|
|
73
|
+
close() {
|
|
74
|
+
if (closed)
|
|
75
|
+
return;
|
|
76
|
+
closed = true;
|
|
77
|
+
abortController.abort();
|
|
78
|
+
opts.client.close();
|
|
79
|
+
},
|
|
80
|
+
done,
|
|
81
|
+
};
|
|
82
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mulmobridge/telegram",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Telegram bridge for MulmoBridge — connect a Telegram bot to MulmoClaude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
"import": "./dist/index.js",
|
|
12
12
|
"require": "./dist/index.js",
|
|
13
13
|
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./start": {
|
|
16
|
+
"types": "./dist/start.d.ts",
|
|
17
|
+
"import": "./dist/start.js",
|
|
18
|
+
"require": "./dist/start.js",
|
|
19
|
+
"default": "./dist/start.js"
|
|
14
20
|
}
|
|
15
21
|
},
|
|
16
22
|
"bin": {
|
|
@@ -23,19 +29,19 @@
|
|
|
23
29
|
"scripts": {
|
|
24
30
|
"build": "tsc",
|
|
25
31
|
"prepack": "yarn build",
|
|
26
|
-
"typecheck": "tsc
|
|
32
|
+
"typecheck": "tsc -p tsconfig.typecheck.json",
|
|
27
33
|
"test": "tsx --test test/test_*.ts",
|
|
28
34
|
"lint": "eslint src test"
|
|
29
35
|
},
|
|
30
36
|
"license": "MIT",
|
|
31
37
|
"author": "Receptron Team",
|
|
32
38
|
"dependencies": {
|
|
33
|
-
"@mulmobridge/client": "^1.
|
|
34
|
-
"@mulmobridge/protocol": "^1.0
|
|
35
|
-
"dotenv": "^
|
|
39
|
+
"@mulmobridge/client": "^1.5.0",
|
|
40
|
+
"@mulmobridge/protocol": "^1.1.0",
|
|
41
|
+
"dotenv": "^18.0.8"
|
|
36
42
|
},
|
|
37
43
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^26.
|
|
44
|
+
"@types/node": "^26.6.5",
|
|
39
45
|
"typescript": "^6.0.3"
|
|
40
46
|
},
|
|
41
47
|
"homepage": "https://github.com/receptron/mulmoclaude/tree/main/packages/bridges/telegram#readme",
|