@llblab/pi-telegram 0.2.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/AGENTS.md +90 -0
- package/BACKLOG.md +5 -0
- package/CHANGELOG.md +17 -0
- package/README.md +202 -0
- package/docs/README.md +9 -0
- package/docs/architecture.md +148 -0
- package/index.ts +1968 -0
- package/lib/api.ts +222 -0
- package/lib/attachments.ts +98 -0
- package/lib/media.ts +234 -0
- package/lib/menu.ts +951 -0
- package/lib/model-switch.ts +62 -0
- package/lib/polling.ts +122 -0
- package/lib/queue.ts +534 -0
- package/lib/registration.ts +163 -0
- package/lib/rendering.ts +697 -0
- package/lib/replies.ts +313 -0
- package/lib/setup.ts +41 -0
- package/lib/status.ts +109 -0
- package/lib/turns.ts +144 -0
- package/lib/updates.ts +397 -0
- package/package.json +40 -0
- package/screenshot.png +0 -0
- package/tests/api.test.ts +89 -0
- package/tests/attachments.test.ts +132 -0
- package/tests/config.test.ts +80 -0
- package/tests/media.test.ts +77 -0
- package/tests/menu.test.ts +645 -0
- package/tests/polling.test.ts +129 -0
- package/tests/queue.test.ts +2982 -0
- package/tests/registration.test.ts +268 -0
- package/tests/rendering.test.ts +308 -0
- package/tests/replies.test.ts +362 -0
- package/tests/turns.test.ts +132 -0
- package/tests/updates.test.ts +366 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram extension registration helpers
|
|
3
|
+
* Owns tool, command, and lifecycle-hook registration so index.ts can stay focused on runtime orchestration state and side effects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ExtensionAPI,
|
|
8
|
+
ExtensionCommandContext,
|
|
9
|
+
ExtensionContext,
|
|
10
|
+
} from "@mariozechner/pi-coding-agent";
|
|
11
|
+
import { Type } from "@sinclair/typebox";
|
|
12
|
+
|
|
13
|
+
import { queueTelegramAttachments } from "./attachments.ts";
|
|
14
|
+
import type { PendingTelegramTurn } from "./queue.ts";
|
|
15
|
+
|
|
16
|
+
// --- Tool Registration ---
|
|
17
|
+
|
|
18
|
+
export interface TelegramAttachmentToolRegistrationDeps {
|
|
19
|
+
maxAttachmentsPerTurn: number;
|
|
20
|
+
getActiveTurn: () => PendingTelegramTurn | undefined;
|
|
21
|
+
statPath: (path: string) => Promise<{ isFile(): boolean }>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function registerTelegramAttachmentTool(
|
|
25
|
+
pi: ExtensionAPI,
|
|
26
|
+
deps: TelegramAttachmentToolRegistrationDeps,
|
|
27
|
+
): void {
|
|
28
|
+
pi.registerTool({
|
|
29
|
+
name: "telegram_attach",
|
|
30
|
+
label: "Telegram Attach",
|
|
31
|
+
description:
|
|
32
|
+
"Queue one or more local files to be sent with the next Telegram reply.",
|
|
33
|
+
promptSnippet: "Queue local files to be sent with the next Telegram reply.",
|
|
34
|
+
promptGuidelines: [
|
|
35
|
+
"When handling a [telegram] message and the user asked for a file or generated artifact, call telegram_attach with the local path instead of only mentioning the path in text.",
|
|
36
|
+
],
|
|
37
|
+
parameters: Type.Object({
|
|
38
|
+
paths: Type.Array(
|
|
39
|
+
Type.String({ description: "Local file path to attach" }),
|
|
40
|
+
{ minItems: 1, maxItems: deps.maxAttachmentsPerTurn },
|
|
41
|
+
),
|
|
42
|
+
}),
|
|
43
|
+
async execute(_toolCallId, params) {
|
|
44
|
+
return queueTelegramAttachments({
|
|
45
|
+
activeTurn: deps.getActiveTurn(),
|
|
46
|
+
paths: params.paths,
|
|
47
|
+
maxAttachmentsPerTurn: deps.maxAttachmentsPerTurn,
|
|
48
|
+
statPath: deps.statPath,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Command Registration ---
|
|
55
|
+
|
|
56
|
+
export interface TelegramCommandRegistrationDeps {
|
|
57
|
+
promptForConfig: (ctx: ExtensionCommandContext) => Promise<void>;
|
|
58
|
+
getStatusLines: () => string[];
|
|
59
|
+
reloadConfig: () => Promise<void>;
|
|
60
|
+
hasBotToken: () => boolean;
|
|
61
|
+
startPolling: (ctx: ExtensionCommandContext) => Promise<void>;
|
|
62
|
+
stopPolling: () => Promise<void>;
|
|
63
|
+
updateStatus: (ctx: ExtensionCommandContext) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function registerTelegramCommands(
|
|
67
|
+
pi: ExtensionAPI,
|
|
68
|
+
deps: TelegramCommandRegistrationDeps,
|
|
69
|
+
): void {
|
|
70
|
+
pi.registerCommand("telegram-setup", {
|
|
71
|
+
description: "Configure Telegram bot token",
|
|
72
|
+
handler: async (_args, ctx) => {
|
|
73
|
+
await deps.promptForConfig(ctx);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
pi.registerCommand("telegram-status", {
|
|
77
|
+
description: "Show Telegram bridge status",
|
|
78
|
+
handler: async (_args, ctx) => {
|
|
79
|
+
ctx.ui.notify(deps.getStatusLines().join(" | "), "info");
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
pi.registerCommand("telegram-connect", {
|
|
83
|
+
description: "Start the Telegram bridge in this pi session",
|
|
84
|
+
handler: async (_args, ctx) => {
|
|
85
|
+
await deps.reloadConfig();
|
|
86
|
+
if (!deps.hasBotToken()) {
|
|
87
|
+
await deps.promptForConfig(ctx);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
await deps.startPolling(ctx);
|
|
91
|
+
deps.updateStatus(ctx);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
pi.registerCommand("telegram-disconnect", {
|
|
95
|
+
description: "Stop the Telegram bridge in this pi session",
|
|
96
|
+
handler: async (_args, ctx) => {
|
|
97
|
+
await deps.stopPolling();
|
|
98
|
+
deps.updateStatus(ctx);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// --- Lifecycle Hook Registration ---
|
|
104
|
+
|
|
105
|
+
export interface TelegramLifecycleRegistrationDeps {
|
|
106
|
+
onSessionStart: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
107
|
+
onSessionShutdown: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
108
|
+
onBeforeAgentStart: (
|
|
109
|
+
event: unknown,
|
|
110
|
+
ctx: ExtensionContext,
|
|
111
|
+
) => Promise<unknown> | unknown;
|
|
112
|
+
onModelSelect: (
|
|
113
|
+
event: unknown,
|
|
114
|
+
ctx: ExtensionContext,
|
|
115
|
+
) => Promise<void> | void;
|
|
116
|
+
onAgentStart: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
117
|
+
onToolExecutionStart: (
|
|
118
|
+
event: unknown,
|
|
119
|
+
ctx: ExtensionContext,
|
|
120
|
+
) => Promise<void> | void;
|
|
121
|
+
onToolExecutionEnd: (
|
|
122
|
+
event: unknown,
|
|
123
|
+
ctx: ExtensionContext,
|
|
124
|
+
) => Promise<void> | void;
|
|
125
|
+
onMessageStart: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
126
|
+
onMessageUpdate: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
127
|
+
onAgentEnd: (event: unknown, ctx: ExtensionContext) => Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function registerTelegramLifecycleHooks(
|
|
131
|
+
pi: ExtensionAPI,
|
|
132
|
+
deps: TelegramLifecycleRegistrationDeps,
|
|
133
|
+
): void {
|
|
134
|
+
pi.on("session_start", async (event, ctx) => {
|
|
135
|
+
await deps.onSessionStart(event, ctx);
|
|
136
|
+
});
|
|
137
|
+
pi.on("session_shutdown", async (event, ctx) => {
|
|
138
|
+
await deps.onSessionShutdown(event, ctx);
|
|
139
|
+
});
|
|
140
|
+
pi.on("before_agent_start", (async (event: unknown, ctx: ExtensionContext) =>
|
|
141
|
+
deps.onBeforeAgentStart(event, ctx)) as never);
|
|
142
|
+
pi.on("model_select", async (event, ctx) => {
|
|
143
|
+
await deps.onModelSelect(event, ctx);
|
|
144
|
+
});
|
|
145
|
+
pi.on("agent_start", async (event, ctx) => {
|
|
146
|
+
await deps.onAgentStart(event, ctx);
|
|
147
|
+
});
|
|
148
|
+
pi.on("tool_execution_start", async (event, ctx) => {
|
|
149
|
+
await deps.onToolExecutionStart(event, ctx);
|
|
150
|
+
});
|
|
151
|
+
pi.on("tool_execution_end", async (event, ctx) => {
|
|
152
|
+
await deps.onToolExecutionEnd(event, ctx);
|
|
153
|
+
});
|
|
154
|
+
pi.on("message_start", async (event, ctx) => {
|
|
155
|
+
await deps.onMessageStart(event, ctx);
|
|
156
|
+
});
|
|
157
|
+
pi.on("message_update", async (event, ctx) => {
|
|
158
|
+
await deps.onMessageUpdate(event, ctx);
|
|
159
|
+
});
|
|
160
|
+
pi.on("agent_end", async (event, ctx) => {
|
|
161
|
+
await deps.onAgentEnd(event, ctx);
|
|
162
|
+
});
|
|
163
|
+
}
|