@sojaner/telex 0.1.15

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/dist/ask.js ADDED
@@ -0,0 +1,200 @@
1
+ import { TelegramError, escapeHtml, MAX_MESSAGE_LEN } from "./telegram.js";
2
+ let counter = 0;
3
+ const nextRequestId = () => `${Date.now().toString(36)}${(counter++).toString(36)}`;
4
+ /** Telegram truncates long inline-button labels, so long choices move into the body as a numbered list. */
5
+ const LABEL_LIMIT = 24;
6
+ export function compose(input) {
7
+ const options = input.options ?? [];
8
+ const numbered = options.some((o) => o.length > LABEL_LIMIT);
9
+ const list = numbered ? `\n\n${options.map((o, i) => `${i + 1}. ${escapeHtml(o)}`).join("\n")}` : "";
10
+ return {
11
+ text: `<b>${escapeHtml(input.project)}</b>\n\n${input.message}${list}`,
12
+ buttonLabels: numbered ? options.map((_, i) => String(i + 1)) : options,
13
+ };
14
+ }
15
+ /** Split on line boundaries where possible; the parse-mode fallback covers chunks that break a tag. */
16
+ export function chunk(text, limit = MAX_MESSAGE_LEN) {
17
+ const out = [];
18
+ let rest = text;
19
+ while (rest.length > limit) {
20
+ const cut = rest.lastIndexOf("\n", limit) > limit / 2 ? rest.lastIndexOf("\n", limit) : limit;
21
+ out.push(rest.slice(0, cut));
22
+ rest = rest.slice(cut).replace(/^\n/, "");
23
+ }
24
+ out.push(rest);
25
+ return out;
26
+ }
27
+ /** Send, wait for the user, then always leave the message in a settled state. */
28
+ export async function ask(session, chatId, input) {
29
+ const requestId = nextRequestId();
30
+ const options = input.options ?? [];
31
+ if (options.length && input.expectText)
32
+ throw new Error("telex: use either options or expect_text, not both");
33
+ const { text, buttonLabels } = compose(input);
34
+ const keyboard = options.length
35
+ ? buttonLabels.map((label, i) => [{ text: label, callback_data: `telex:${requestId}:${i}` }])
36
+ : input.expectText
37
+ ? [[{ text: "✍️ Reply", callback_data: `telex:${requestId}:text` }]]
38
+ : [];
39
+ // Only the final chunk carries the keyboard, so the buttons sit under the whole question.
40
+ const chunks = chunk(text);
41
+ let sent;
42
+ for (const [i, part] of chunks.entries()) {
43
+ sent = await send(session, chatId, part, i === chunks.length - 1 ? keyboard : []);
44
+ }
45
+ const messageId = sent.message_id;
46
+ const tail = chunks[chunks.length - 1];
47
+ if (!keyboard.length)
48
+ return { status: "sent", message_id: messageId };
49
+ const deadline = Date.now() + input.timeoutSeconds * 1000;
50
+ const answer = await waitForAnswer(session, chatId, requestId, options, deadline, input.allowFrom);
51
+ if (!answer) {
52
+ await settle(session, chatId, messageId, tail, "⏳ <i>No response — stale.</i>");
53
+ return { status: "timeout", message_id: messageId };
54
+ }
55
+ await settle(session, chatId, messageId, tail, `✅ <b>${escapeHtml(answer.value)}</b>`);
56
+ return { status: "answered", response: answer.value, kind: answer.kind, message_id: messageId };
57
+ }
58
+ /** What the user sees under their own message as it moves through the queue. */
59
+ const RECEIPT = {
60
+ held: "📥 <i>Held for the agent's next check-in.</i>",
61
+ delivered: "📬 <i>Delivered to the agent.</i>",
62
+ expired: "⌛ <i>Expired — the agent never picked this up. Send it again if it still matters.</i>",
63
+ refused: "⛔ <i>Not accepted — no agent has checked in for this project yet.</i>",
64
+ };
65
+ /**
66
+ * Telegram gives bots no delivery receipt, so telex sends its own and edits it in place. A project
67
+ * that is not running never posts one at all, which is how the user can tell nobody is listening.
68
+ */
69
+ export async function receipt(session, chatId, message) {
70
+ const sent = await session.api("sendMessage", {
71
+ chat_id: chatId,
72
+ text: RECEIPT.held,
73
+ parse_mode: "HTML",
74
+ reply_parameters: { message_id: message.message_id, allow_sending_without_reply: true },
75
+ }).catch(() => undefined);
76
+ if (sent)
77
+ message.receipt_id = sent.message_id;
78
+ }
79
+ function updateReceipt(session, chatId, message, text) {
80
+ if (message.receipt_id === undefined)
81
+ return;
82
+ return session.api("editMessageText", {
83
+ chat_id: chatId,
84
+ message_id: message.receipt_id,
85
+ text,
86
+ parse_mode: "HTML",
87
+ }).catch(() => { }); // an unchanged or deleted receipt is not worth failing a heartbeat over
88
+ }
89
+ export const markExpired = (session, chatId, messages) => {
90
+ for (const message of messages)
91
+ void updateReceipt(session, chatId, message, RECEIPT.expired);
92
+ };
93
+ /** Tell the user their message went nowhere, rather than holding it for an agent that may never come. */
94
+ export function refuse(session, chatId, message) {
95
+ return session.api("sendMessage", {
96
+ chat_id: chatId,
97
+ text: RECEIPT.refused,
98
+ parse_mode: "HTML",
99
+ reply_parameters: { message_id: message.message_id, allow_sending_without_reply: true },
100
+ }).catch(() => { });
101
+ }
102
+ /**
103
+ * One heartbeat: drop whatever the agent left too long, then hand over the rest. Returns
104
+ * immediately — the agent's own interval is the clock, and its silence is what expires a message.
105
+ */
106
+ export function heartbeat(session, chatId, now = Date.now()) {
107
+ session.sweepInbox(now);
108
+ return deliver(session, chatId, now);
109
+ }
110
+ /** Hand the queue to the agent and say so in the chat. */
111
+ export function deliver(session, chatId, now = Date.now()) {
112
+ return session.take(chatId).map((message) => {
113
+ void updateReceipt(session, chatId, message, RECEIPT.delivered);
114
+ return {
115
+ text: message.text,
116
+ received_at: new Date(message.received_at).toISOString(),
117
+ waited_seconds: Math.round((now - message.received_at) / 1000),
118
+ };
119
+ });
120
+ }
121
+ /** An inline keyboard is clickable by anyone who can see it, so authorise the tap, not the send. */
122
+ function authorized(ctx, chatId, allowFrom) {
123
+ if (ctx.chatId !== undefined && String(ctx.chatId) !== String(chatId))
124
+ return false;
125
+ return !allowFrom?.length || (ctx.fromId !== undefined && allowFrom.includes(ctx.fromId));
126
+ }
127
+ async function waitForAnswer(session, chatId, requestId, options, deadline, allowFrom) {
128
+ const tap = await until(deadline, (resolve) => session.onCallback(requestId, (payload, ctx) => {
129
+ if (!authorized(ctx, chatId, allowFrom)) {
130
+ void session.api("answerCallbackQuery", {
131
+ callback_query_id: ctx.callbackId,
132
+ text: "⛔ Not your prompt.",
133
+ }).catch(() => { });
134
+ return;
135
+ }
136
+ resolve({ payload, ctx });
137
+ }));
138
+ if (!tap)
139
+ return null;
140
+ const toast = (text) => session.api("answerCallbackQuery", { callback_query_id: tap.ctx.callbackId, text }).catch(() => { });
141
+ if (tap.payload !== "text") {
142
+ const value = options[Number(tap.payload)];
143
+ await toast(value === undefined ? "" : `✓ ${value.slice(0, 60)}`);
144
+ return value === undefined ? null : { value, kind: "choice" };
145
+ }
146
+ await toast("✍️ Type your answer in the chat.");
147
+ const prompt = await send(session, chatId, "✍️ <i>Reply to this message with your response.</i>", [], {
148
+ force_reply: true,
149
+ });
150
+ const text = await until(deadline, (resolve) => session.onText(chatId, (value, fromId) => {
151
+ if (allowFrom?.length && (fromId === undefined || !allowFrom.includes(fromId)))
152
+ return;
153
+ resolve(value);
154
+ }));
155
+ await session.api("deleteMessage", { chat_id: chatId, message_id: prompt.message_id }).catch(() => { });
156
+ return text === null ? null : { value: text, kind: "text" };
157
+ }
158
+ /** Resolve with the first value the subscriber emits, or null once the deadline passes. */
159
+ function until(deadline, subscribe) {
160
+ return new Promise((resolve) => {
161
+ let done = false;
162
+ const finish = (v) => {
163
+ if (done)
164
+ return;
165
+ done = true;
166
+ clearTimeout(timer);
167
+ unsubscribe();
168
+ resolve(v);
169
+ };
170
+ const timer = setTimeout(() => finish(null), Math.max(0, deadline - Date.now()));
171
+ const unsubscribe = subscribe((v) => finish(v));
172
+ });
173
+ }
174
+ /** Drop the keyboard and stamp the outcome onto the message that carried it. */
175
+ async function settle(session, chatId, messageId, text, footer) {
176
+ await session.api("editMessageText", {
177
+ chat_id: chatId,
178
+ message_id: messageId,
179
+ text: `${text}\n\n${footer}`,
180
+ parse_mode: "HTML",
181
+ reply_markup: { inline_keyboard: [] },
182
+ }).catch(() => { }); // "message is not modified" and friends are not worth failing the answer over
183
+ }
184
+ /** HTML is Telegram's forgiving parse mode, but agents still emit stray tags — fall back to plain text. */
185
+ async function send(session, chatId, text, keyboard, extraMarkup = {}) {
186
+ const reply_markup = keyboard.length
187
+ ? { inline_keyboard: keyboard }
188
+ : Object.keys(extraMarkup).length
189
+ ? extraMarkup
190
+ : undefined;
191
+ try {
192
+ return await session.api("sendMessage", { chat_id: chatId, text, parse_mode: "HTML", reply_markup });
193
+ }
194
+ catch (err) {
195
+ if (!(err instanceof TelegramError) || !/parse|entit|tag/i.test(err.description))
196
+ throw err;
197
+ return await session.api("sendMessage", { chat_id: chatId, text, reply_markup });
198
+ }
199
+ }
200
+ //# sourceMappingURL=ask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask.js","sourceRoot":"","sources":["../src/ask.ts"],"names":[],"mappings":"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"}
package/dist/cli.js ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
3
+ import { fileURLToPath } from "node:url";
4
+ import { readConfig, writeConfig, configPath, maskToken } from "./config.js";
5
+ import { addBotInteractive, installConfig } from "./setup.js";
6
+ import { agents, snippet } from "./agents.js";
7
+ const USAGE = `telex — send messages from local AI agents to Telegram
8
+
9
+ telex serve run the MCP server (stdio); what agents launch
10
+ telex add [name] add a bot, guided; or pass --token/--chat-id
11
+ telex list show configured bots
12
+ telex set <name> [options] change a bot
13
+ telex remove <name> delete a bot
14
+ telex config [name] install the MCP registration into this project
15
+
16
+ Options for config:
17
+ --agent <id> skip the prompts: claude, codex, cursor, vscode, zed, ...
18
+ --scope <local|project> for agents with both: gitignored file or committed file
19
+ --print only show the commands and file syntax; write nothing
20
+ -y, --yes don't ask about the current directory
21
+
22
+ Options for add/set:
23
+ --token <token> bot token from @BotFather
24
+ --chat-id <id> chat the bot writes to (negative ids: --chat-id=-1001234567890)
25
+ --allow <id,id> Telegram user ids allowed to answer ("any" to clear)
26
+ --default make this the default bot
27
+
28
+ Config lives at ${configPath()} (override with TELEX_CONFIG).`;
29
+ // Resolves to src/index.ts in a checkout and dist/index.js once built.
30
+ const serverEntry = fileURLToPath(new URL(import.meta.url.endsWith(".ts") ? "index.ts" : "index.js", import.meta.url));
31
+ /** Group chat ids are negative, and parseArgs reads a leading dash as another flag. */
32
+ const { values: flags, positionals } = parseArgsFriendly({
33
+ allowPositionals: true,
34
+ options: {
35
+ token: { type: "string" },
36
+ "chat-id": { type: "string" },
37
+ allow: { type: "string" },
38
+ default: { type: "boolean" },
39
+ json: { type: "boolean" },
40
+ agent: { type: "string" },
41
+ scope: { type: "string" },
42
+ print: { type: "boolean" },
43
+ yes: { type: "boolean", short: "y" },
44
+ help: { type: "boolean", short: "h" },
45
+ },
46
+ });
47
+ const [command, arg] = positionals;
48
+ function parseArgsFriendly(options) {
49
+ try {
50
+ return parseArgs(options);
51
+ }
52
+ catch (err) {
53
+ const message = err.message;
54
+ const negative = message.match(/Option '(--[a-z-]+)' argument is ambiguous/i);
55
+ if (negative) {
56
+ console.error(`✗ ${negative[1]} looks like it got a negative value. Write it as ${negative[1]}=-1001234567890.`);
57
+ process.exit(1);
58
+ }
59
+ console.error(`✗ ${message.split("\n")[0]}`);
60
+ process.exit(1);
61
+ }
62
+ }
63
+ async function run(command, name) {
64
+ switch (command) {
65
+ case "serve":
66
+ await import("./index.js");
67
+ return;
68
+ case "add": {
69
+ const config = readConfig();
70
+ if (name && config.bots[name])
71
+ throw new Error(`bot "${name}" already exists — use "telex set ${name}"`);
72
+ // A token on the command line means the caller already knows everything; don't make them talk to a wizard.
73
+ if (!flags.token) {
74
+ const added = await addBotInteractive(name);
75
+ if (flags.default)
76
+ setDefault(added);
77
+ return;
78
+ }
79
+ if (!flags["chat-id"])
80
+ throw new Error("--token also needs --chat-id");
81
+ if (!name)
82
+ throw new Error("give the bot a name: telex add <name> --token ... --chat-id ...");
83
+ config.bots[name] = { token: flags.token, chatId: numeric(flags["chat-id"]), allowFrom: parseAllow(flags.allow) };
84
+ config.defaultBot = flags.default || !config.defaultBot ? name : config.defaultBot;
85
+ writeConfig(config);
86
+ console.log(`✓ Added "${name}"${config.defaultBot === name ? " (default)" : ""}`);
87
+ return;
88
+ }
89
+ case "set": {
90
+ const config = readConfig();
91
+ const bot = required(config.bots, name);
92
+ if (flags.token)
93
+ bot.token = flags.token;
94
+ if (flags["chat-id"])
95
+ bot.chatId = numeric(flags["chat-id"]);
96
+ if (flags.allow !== undefined)
97
+ bot.allowFrom = parseAllow(flags.allow);
98
+ if (flags.default)
99
+ config.defaultBot = name;
100
+ writeConfig(config);
101
+ console.log(`✓ Updated "${name}"`);
102
+ return;
103
+ }
104
+ case "remove": {
105
+ const config = readConfig();
106
+ required(config.bots, name);
107
+ delete config.bots[name];
108
+ // Never leave defaultBot pointing at a bot that no longer exists.
109
+ if (config.defaultBot === name)
110
+ config.defaultBot = Object.keys(config.bots)[0];
111
+ writeConfig(config);
112
+ console.log(`✓ Removed "${name}"`);
113
+ return;
114
+ }
115
+ case "list": {
116
+ const config = readConfig();
117
+ const names = Object.keys(config.bots);
118
+ if (!names.length)
119
+ return console.log(`No bots configured. Run "telex add".`);
120
+ if (flags.json)
121
+ return console.log(JSON.stringify(redact(config), null, 2));
122
+ for (const n of names) {
123
+ const bot = config.bots[n];
124
+ const allow = bot.allowFrom?.length ? bot.allowFrom.join(", ") : "anyone in chat";
125
+ console.log(`${n === config.defaultBot ? "*" : " "} ${n.padEnd(16)} chat ${String(bot.chatId).padEnd(16)} ${maskToken(bot.token)} allow: ${allow}`);
126
+ }
127
+ console.log(`\n* = default. Config: ${configPath()}`);
128
+ return;
129
+ }
130
+ case "config": {
131
+ const config = readConfig();
132
+ if (name)
133
+ required(config.bots, name);
134
+ const entry = { command: "telex", args: ["serve"], ...(name ? { env: { TELEX_BOT: name } } : {}) };
135
+ if (flags.json)
136
+ return console.log(JSON.stringify({ mcpServers: { telex: entry } }, null, 2));
137
+ if (flags.print)
138
+ return printConfigs(entry, name);
139
+ await installConfig(name, { agent: flags.agent, scope: flags.scope, yes: flags.yes });
140
+ return;
141
+ }
142
+ default:
143
+ throw new Error(`unknown command "${command}"\n\n${USAGE}`);
144
+ }
145
+ }
146
+ /** What --print shows: every supported agent, its installer command or its config file. */
147
+ function printConfigs(entry, name) {
148
+ const list = agents();
149
+ console.log(`Agents that install it for you — run in the project root:\n`);
150
+ for (const agent of list.filter((a) => a.cli))
151
+ console.log(` ${agent.label.padEnd(15)}${agent.cli(name).join(" ")}`);
152
+ console.log(`\nAgents you configure by writing a file:\n`);
153
+ for (const agent of list.filter((a) => a.file)) {
154
+ console.log(` ${agent.label.padEnd(15)}${agent.file}${agent.localFile ? ` (or ${agent.localFile}, gitignored)` : ""}`);
155
+ for (const line of snippet(agent, entry).split("\n"))
156
+ console.log(` ${" ".repeat(15)}${line}`);
157
+ console.log();
158
+ }
159
+ console.log(`Codex reads .codex/config.toml only for projects you have trusted.`);
160
+ if (name)
161
+ console.log(`Messages from this project default to "${name}"; the agent can still pass another bot.`);
162
+ else
163
+ console.log(`Pass a bot name to pin this project to one: telex config <name>`);
164
+ console.log(`\nIf "telex" is not on PATH, use: "command": "node", "args": ["${serverEntry}"]`);
165
+ }
166
+ function required(bots, name) {
167
+ if (!name)
168
+ throw new Error("which bot? pass its name");
169
+ const bot = bots[name];
170
+ if (!bot)
171
+ throw new Error(`unknown bot "${name}". Configured: ${Object.keys(bots).join(", ") || "none"}`);
172
+ return bot;
173
+ }
174
+ function setDefault(name) {
175
+ const config = readConfig();
176
+ config.defaultBot = name;
177
+ writeConfig(config);
178
+ }
179
+ const numeric = (v) => (/^-?\d+$/.test(v.trim()) ? Number(v.trim()) : v.trim());
180
+ function parseAllow(value) {
181
+ if (!value || value === "any")
182
+ return undefined;
183
+ return value.split(",").map((v) => {
184
+ const id = Number(v.trim());
185
+ if (!Number.isInteger(id))
186
+ throw new Error(`--allow takes numeric Telegram user ids, got "${v.trim()}"`);
187
+ return id;
188
+ });
189
+ }
190
+ const redact = (config) => ({
191
+ ...config,
192
+ bots: Object.fromEntries(Object.entries(config.bots).map(([n, b]) => [n, { ...b, token: maskToken(b.token) }])),
193
+ });
194
+ try {
195
+ if (flags.help || !command) {
196
+ console.log(USAGE);
197
+ }
198
+ else {
199
+ await run(command, arg);
200
+ }
201
+ }
202
+ catch (err) {
203
+ console.error(`✗ ${err.message}`);
204
+ process.exitCode = 1;
205
+ }
206
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/config.js ADDED
@@ -0,0 +1,50 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ export const configPath = () => process.env.TELEX_CONFIG ?? join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "telex", "config.json");
5
+ /** Config as it is on disk, or an empty one. For the CLI, which has to cope with "nothing yet". */
6
+ export function readConfig() {
7
+ const path = configPath();
8
+ if (!existsSync(path))
9
+ return { bots: {} };
10
+ try {
11
+ const raw = JSON.parse(readFileSync(path, "utf8"));
12
+ return { defaultBot: raw.defaultBot, bots: raw.bots ?? {} };
13
+ }
14
+ catch (err) {
15
+ throw new Error(`telex: ${path} is not valid JSON: ${err.message}`);
16
+ }
17
+ }
18
+ export function writeConfig(config) {
19
+ const path = configPath();
20
+ mkdirSync(dirname(path), { recursive: true });
21
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
22
+ }
23
+ /** Validated config for the server, which cannot do anything useful without at least one bot. */
24
+ export function loadConfig() {
25
+ const config = readConfig();
26
+ const names = Object.keys(config.bots);
27
+ if (names.length === 0)
28
+ throw new Error(`telex: no bots configured in ${configPath()} — run "telex add"`);
29
+ for (const [name, bot] of Object.entries(config.bots)) {
30
+ if (!bot?.token || bot.chatId === undefined)
31
+ throw new Error(`telex: bot "${name}" needs both token and chatId`);
32
+ }
33
+ if (config.defaultBot && !config.bots[config.defaultBot]) {
34
+ throw new Error(`telex: defaultBot "${config.defaultBot}" is not in bots`);
35
+ }
36
+ return { defaultBot: config.defaultBot ?? names[0], bots: config.bots };
37
+ }
38
+ /**
39
+ * Which bot a call goes to: the agent's argument wins, then the bot pinned for this
40
+ * project via TELEX_BOT, then the global default.
41
+ */
42
+ export function pickBot(config, name) {
43
+ const key = name || process.env.TELEX_BOT || config.defaultBot;
44
+ const bot = config.bots[key];
45
+ if (!bot)
46
+ throw new Error(`telex: unknown bot "${key}". Configured: ${Object.keys(config.bots).join(", ")}`);
47
+ return { name: key, bot };
48
+ }
49
+ export const maskToken = (token) => `${token.split(":")[0]}:${"*".repeat(6)}${token.slice(-4)}`;
50
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}