pockcode 0.0.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.
@@ -0,0 +1,1334 @@
1
+ import { c as readWorkspaceTree, n as onProviderEvent, p as __exportAll } from "./socket.server-7dC-9Fnw.js";
2
+ import { E as listAccounts, H as ensureDatabase, T as listAccountModels, U as prisma, _ as serializeChat, a as executeMessage, b as updateChat, h as respondToServerRequest, l as listChats, s as getChat, u as listMessages } from "./chats.service-H6m23Fna.js";
3
+ import { randomBytes, randomUUID } from "node:crypto";
4
+ import { Bot, InlineKeyboard } from "grammy";
5
+ //#region app/server/workspace-history.service.ts
6
+ async function listWorkspaceHistory() {
7
+ await ensureDatabase();
8
+ return (await prisma.$queryRaw`
9
+ SELECT "id", "path", "name", "lastOpenedAt", "createdAt", "updatedAt"
10
+ FROM "WorkspaceHistory"
11
+ ORDER BY "lastOpenedAt" DESC
12
+ `).map(serializeWorkspaceHistory);
13
+ }
14
+ async function saveWorkspaceHistory(inputPath) {
15
+ await ensureDatabase();
16
+ const tree = await readWorkspaceTree(inputPath, false);
17
+ const id = randomUUID();
18
+ await prisma.$executeRaw`
19
+ INSERT INTO "WorkspaceHistory" ("id", "path", "name", "lastOpenedAt", "createdAt", "updatedAt")
20
+ VALUES (${id}, ${tree.path}, ${tree.name}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
21
+ ON CONFLICT("path") DO UPDATE SET
22
+ "name" = excluded."name",
23
+ "lastOpenedAt" = CURRENT_TIMESTAMP,
24
+ "updatedAt" = CURRENT_TIMESTAMP
25
+ `;
26
+ return readWorkspaceHistory(tree.path);
27
+ }
28
+ async function deleteWorkspaceHistory(inputPath) {
29
+ await ensureDatabase();
30
+ await prisma.$executeRaw`DELETE FROM "WorkspaceHistory" WHERE "path" = ${inputPath}`;
31
+ return { path: inputPath };
32
+ }
33
+ async function readWorkspaceHistory(path) {
34
+ const row = (await prisma.$queryRaw`
35
+ SELECT "id", "path", "name", "lastOpenedAt", "createdAt", "updatedAt"
36
+ FROM "WorkspaceHistory"
37
+ WHERE "path" = ${path}
38
+ LIMIT 1
39
+ `)[0];
40
+ if (!row) throw new Error("Workspace history was not saved.");
41
+ return serializeWorkspaceHistory(row);
42
+ }
43
+ function serializeWorkspaceHistory(row) {
44
+ return {
45
+ id: row.id,
46
+ path: row.path,
47
+ name: row.name,
48
+ lastOpenedAt: toIsoString(row.lastOpenedAt),
49
+ createdAt: toIsoString(row.createdAt),
50
+ updatedAt: toIsoString(row.updatedAt)
51
+ };
52
+ }
53
+ function toIsoString(value) {
54
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
55
+ }
56
+ //#endregion
57
+ //#region app/server/plugins/telegram.server.ts
58
+ var telegramPluginId = "telegram";
59
+ var callbackTtlMs = 3600 * 1e3;
60
+ var telegramListPageSize = 4;
61
+ var telegramMessageMaxLength = 3900;
62
+ var telegramMessageTailLines = 20;
63
+ async function verifyTelegramBotToken(token) {
64
+ if (!token.trim()) throw new Error("Telegram bot token is required.");
65
+ try {
66
+ await new Bot(token.trim()).api.getMe();
67
+ } catch (error) {
68
+ throw new Error(`Telegram bot token is invalid: ${readErrorMessage$1(error)}`);
69
+ }
70
+ }
71
+ var telegramPluginRegistration = {
72
+ definition: {
73
+ id: telegramPluginId,
74
+ label: "Telegram",
75
+ description: "Receive chat updates, browse workspaces, and reply from Telegram.",
76
+ icon: "telegram",
77
+ defaultSettings: {},
78
+ settingsFields: [],
79
+ secretFields: [{
80
+ key: "botToken",
81
+ label: "Bot token",
82
+ placeholder: "123456:ABC...",
83
+ required: true,
84
+ secret: true,
85
+ type: "secret"
86
+ }]
87
+ },
88
+ runtime: () => new TelegramPluginRuntime(),
89
+ actions: {
90
+ async clearOwner(context) {
91
+ const pairingCode = createPairingCode();
92
+ await context.updateState((state) => serializeTelegramState({
93
+ botUsername: readTelegramState(state).botUsername,
94
+ owner: null,
95
+ pairingCode,
96
+ selectedChats: {},
97
+ subscriptions: {}
98
+ }));
99
+ return { message: "Telegram owner cleared." };
100
+ },
101
+ async refreshPairingCode(context) {
102
+ const pairingCode = createPairingCode();
103
+ await context.updateState((state) => serializeTelegramState({
104
+ ...readTelegramState(state),
105
+ pairingCode
106
+ }));
107
+ return { message: "Pairing code refreshed." };
108
+ }
109
+ },
110
+ summarizeState(state) {
111
+ const telegramState = readTelegramState(state);
112
+ return {
113
+ ownerChatId: telegramState.owner?.chatId ?? null,
114
+ ownerLabel: telegramState.owner?.displayName ?? null,
115
+ ownerUserId: telegramState.owner?.userId ?? null,
116
+ botUsername: telegramState.botUsername,
117
+ pairingCode: telegramState.pairingCode,
118
+ subscriptionCount: Object.keys(telegramState.subscriptions).length
119
+ };
120
+ }
121
+ };
122
+ var TelegramPluginRuntime = class {
123
+ constructor() {
124
+ this.bot = null;
125
+ this.callbackTokens = /* @__PURE__ */ new Map();
126
+ this.context = null;
127
+ this.pollingPromise = null;
128
+ this.replyTargetsByTelegramMessageId = /* @__PURE__ */ new Map();
129
+ this.subscriptionTailUpdateQueue = /* @__PURE__ */ new Map();
130
+ }
131
+ async start(context) {
132
+ this.context = context;
133
+ const token = readRecordString(context.secrets, "botToken");
134
+ await verifyTelegramBotToken(token);
135
+ await this.ensurePairingCode();
136
+ const bot = new Bot(token);
137
+ this.bot = bot;
138
+ this.installHandlers(bot);
139
+ bot.catch((error) => {
140
+ context.setStatus({
141
+ state: "error",
142
+ message: error.error instanceof Error ? error.error.message : "Telegram bot failed."
143
+ });
144
+ });
145
+ const me = await bot.api.getMe();
146
+ await context.updateState((state) => serializeTelegramState({
147
+ ...readTelegramState(state),
148
+ botUsername: me.username ?? null
149
+ }));
150
+ await bot.api.setMyCommands([
151
+ {
152
+ command: "start",
153
+ description: "Pair or open Pockcode"
154
+ },
155
+ {
156
+ command: "workspaces",
157
+ description: "List workspaces"
158
+ },
159
+ {
160
+ command: "subscriptions",
161
+ description: "List chat subscriptions"
162
+ },
163
+ {
164
+ command: "help",
165
+ description: "Show actions"
166
+ }
167
+ ]).catch(() => void 0);
168
+ await this.rememberSubscriptionReplyTargets();
169
+ context.setStatus({
170
+ state: "running",
171
+ message: `Connected as @${me.username ?? me.first_name}`
172
+ });
173
+ this.pollingPromise = this.runPolling(bot, context);
174
+ }
175
+ async stop() {
176
+ const bot = this.bot;
177
+ const pollingPromise = this.pollingPromise;
178
+ this.bot = null;
179
+ this.context = null;
180
+ this.pollingPromise = null;
181
+ this.callbackTokens.clear();
182
+ this.replyTargetsByTelegramMessageId.clear();
183
+ this.subscriptionTailUpdateQueue.clear();
184
+ if (!bot) return;
185
+ try {
186
+ if (bot.isRunning()) await bot.stop();
187
+ } catch {}
188
+ await pollingPromise?.catch(() => void 0);
189
+ }
190
+ async runPolling(bot, context) {
191
+ try {
192
+ await bot.start({ drop_pending_updates: true });
193
+ } catch (error) {
194
+ if (this.bot === bot) {
195
+ this.bot = null;
196
+ context.setStatus({
197
+ state: "error",
198
+ message: readTelegramPollingError(error)
199
+ });
200
+ }
201
+ }
202
+ }
203
+ async handleProviderEvent(event) {
204
+ if (!this.bot || !this.context) return;
205
+ if (event.type === "message.created") {
206
+ const message = readChatMessage(event.payload);
207
+ if (message && shouldUpdateSubscriptionTailForMessage(message)) await this.queueSubscribedChatTailUpdate(message.chatId);
208
+ return;
209
+ }
210
+ }
211
+ installHandlers(bot) {
212
+ bot.command("start", (ctx) => this.handleStart(ctx));
213
+ bot.command("help", (ctx) => this.showMainMenu(ctx));
214
+ bot.command("workspaces", (ctx) => this.showWorkspaces(ctx));
215
+ bot.command("subscriptions", (ctx) => this.showSubscriptions(ctx));
216
+ bot.on("callback_query:data", (ctx) => this.handleCallback(ctx));
217
+ bot.on("message:text", (ctx) => this.handleTextMessage(ctx));
218
+ }
219
+ async handleStart(ctx) {
220
+ const payload = (ctx.message?.text ?? "").split(/\s+/)[1]?.trim();
221
+ const subscribeChatId = readSubscribeStartPayload(payload);
222
+ const from = ctx.from;
223
+ const chat = ctx.chat;
224
+ if (!from || !chat) return;
225
+ const state = await this.readState();
226
+ if (!state.owner) {
227
+ if (!payload || subscribeChatId || payload.toUpperCase() !== state.pairingCode?.toUpperCase()) {
228
+ await ctx.reply("Open Pockcode Plugins and send /start followed by the current pairing code.");
229
+ return;
230
+ }
231
+ const owner = {
232
+ chatId: chat.id,
233
+ displayName: displayNameForTelegramUser(from),
234
+ pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
235
+ userId: from.id,
236
+ username: from.username ?? null
237
+ };
238
+ await this.writeState({
239
+ ...state,
240
+ owner,
241
+ pairingCode: null
242
+ });
243
+ await ctx.reply("Telegram is paired with Pockcode.", { reply_markup: this.mainKeyboard() });
244
+ return;
245
+ }
246
+ if (!this.isOwner(from.id, state)) {
247
+ await ctx.reply("This bot is already paired.");
248
+ return;
249
+ }
250
+ const nextState = await this.rememberOwnerChat(ctx, state);
251
+ if (subscribeChatId) {
252
+ try {
253
+ await this.subscribeChatForTelegramChat(ctx, subscribeChatId, nextState);
254
+ } catch (error) {
255
+ await ctx.reply(readErrorMessage$1(error), { reply_markup: this.mainKeyboard() });
256
+ }
257
+ return;
258
+ }
259
+ await this.showMainMenu(ctx);
260
+ }
261
+ async handleTextMessage(ctx) {
262
+ const text = ctx.message?.text?.trim();
263
+ if (!text || text.startsWith("/")) return;
264
+ const state = await this.requireOwner(ctx);
265
+ if (!state) return;
266
+ const replyMessageId = ctx.message?.reply_to_message?.message_id;
267
+ const chatId = (replyMessageId ? this.replyTargetsByTelegramMessageId.get(replyMessageId) : null) ?? state.selectedChats[String(ctx.chat?.id ?? state.owner?.chatId ?? "")];
268
+ if (!chatId) {
269
+ await ctx.reply("Choose a chat first.", { reply_markup: this.mainKeyboard() });
270
+ return;
271
+ }
272
+ try {
273
+ await executeMessage(chatId, { content: text });
274
+ await ctx.reply("Sent to chat.");
275
+ } catch (error) {
276
+ await ctx.reply(readErrorMessage$1(error));
277
+ }
278
+ }
279
+ async handleCallback(ctx) {
280
+ const state = await this.requireOwner(ctx);
281
+ if (!state) {
282
+ await ctx.answerCallbackQuery().catch(() => void 0);
283
+ return;
284
+ }
285
+ const payload = this.readCallbackPayload(ctx.callbackQuery?.data);
286
+ if (!payload) {
287
+ await ctx.answerCallbackQuery({ text: "This action expired." }).catch(() => void 0);
288
+ return;
289
+ }
290
+ await ctx.answerCallbackQuery().catch(() => void 0);
291
+ await this.rememberOwnerChat(ctx, state);
292
+ try {
293
+ await this.runCallback(ctx, payload);
294
+ } catch (error) {
295
+ await this.replyOrEdit(ctx, readErrorMessage$1(error), this.mainKeyboard());
296
+ }
297
+ }
298
+ async runCallback(ctx, payload) {
299
+ if (payload.type === "main") {
300
+ await this.showMainMenu(ctx);
301
+ return;
302
+ }
303
+ if (payload.type === "workspaces") {
304
+ await this.showWorkspaces(ctx, payload.page);
305
+ return;
306
+ }
307
+ if (payload.type === "workspace") {
308
+ await this.showWorkspaceChats(ctx, payload.path, payload.page);
309
+ return;
310
+ }
311
+ if (payload.type === "subscriptions") {
312
+ await this.showSubscriptions(ctx, payload.page);
313
+ return;
314
+ }
315
+ if (payload.type === "chat") {
316
+ await this.showChat(ctx, payload.chatId);
317
+ return;
318
+ }
319
+ if (payload.type === "subscribe") {
320
+ await this.toggleSubscription(ctx, payload.chatId);
321
+ return;
322
+ }
323
+ if (payload.type === "reply") {
324
+ await this.selectReplyChat(ctx, payload.chatId);
325
+ return;
326
+ }
327
+ if (payload.type === "settings") {
328
+ await this.showChatSettings(ctx, payload.chatId);
329
+ return;
330
+ }
331
+ if (payload.type === "accountMenu") {
332
+ await this.showAccountMenu(ctx, payload.chatId, payload.page);
333
+ return;
334
+ }
335
+ if (payload.type === "setAccount") {
336
+ await this.updateChatSetting(ctx, payload.chatId, { accountId: payload.accountId }, "Provider account updated.");
337
+ return;
338
+ }
339
+ if (payload.type === "modelMenu") {
340
+ await this.showModelMenu(ctx, payload.chatId, payload.page);
341
+ return;
342
+ }
343
+ if (payload.type === "model") {
344
+ await this.updateChatSetting(ctx, payload.chatId, { model: payload.value }, "Model updated.");
345
+ return;
346
+ }
347
+ if (payload.type === "reasoningMenu") {
348
+ await this.showReasoningMenu(ctx, payload.chatId);
349
+ return;
350
+ }
351
+ if (payload.type === "reasoning") {
352
+ await this.updateChatSetting(ctx, payload.chatId, { reasoningEffort: payload.value }, "Reasoning updated.");
353
+ return;
354
+ }
355
+ if (payload.type === "serviceTierMenu") {
356
+ await this.showServiceTierMenu(ctx, payload.chatId);
357
+ return;
358
+ }
359
+ if (payload.type === "serviceTier") {
360
+ await this.updateChatSetting(ctx, payload.chatId, { serviceTier: payload.value }, "Speed updated.");
361
+ return;
362
+ }
363
+ if (payload.type === "permissionMenu") {
364
+ await this.showPermissionMenu(ctx, payload.chatId);
365
+ return;
366
+ }
367
+ if (payload.type === "permission") {
368
+ await this.updateChatSetting(ctx, payload.chatId, { permissionMode: payload.value }, "Security mode updated.");
369
+ return;
370
+ }
371
+ if (payload.type === "mode") {
372
+ await this.updateChatSetting(ctx, payload.chatId, { collaborationMode: payload.value }, "Mode updated.");
373
+ return;
374
+ }
375
+ if (payload.type === "serverRequest") {
376
+ await this.respondToServerRequest(ctx, payload);
377
+ return;
378
+ }
379
+ if (payload.type === "userInput") await this.respondToUserInput(ctx, payload);
380
+ }
381
+ async showMainMenu(ctx) {
382
+ if (!await this.requireOwner(ctx)) return;
383
+ await this.replyOrEdit(ctx, "Pockcode Telegram", this.mainKeyboard());
384
+ }
385
+ async showWorkspaces(ctx, requestedPage = 0) {
386
+ if (!await this.requireOwner(ctx)) return;
387
+ const workspaces = await listWorkspaceHistory();
388
+ const page = paginatedItems(workspaces, requestedPage);
389
+ const keyboard = new InlineKeyboard();
390
+ for (const workspace of page.items) keyboard.text(workspace.name, this.callbackData({
391
+ type: "workspace",
392
+ path: workspace.path
393
+ })).row();
394
+ this.addPaginationControls(keyboard, page, (nextPage) => ({
395
+ type: "workspaces",
396
+ page: nextPage
397
+ }));
398
+ keyboard.text("Subscriptions", this.callbackData({ type: "subscriptions" })).text("Back", this.callbackData({ type: "main" }));
399
+ await this.replyOrEdit(ctx, workspaces.length ? listTitle("Workspaces", page) : "No workspaces yet.", keyboard);
400
+ }
401
+ async showWorkspaceChats(ctx, workspacePath, requestedPage = 0) {
402
+ const chats = await listChats(workspacePath);
403
+ const page = paginatedItems(chats, requestedPage);
404
+ const keyboard = new InlineKeyboard();
405
+ for (const chat of page.items) keyboard.text(compactLabel(chat.title, 36), this.callbackData({
406
+ type: "chat",
407
+ chatId: chat.id
408
+ })).row();
409
+ this.addPaginationControls(keyboard, page, (nextPage) => ({
410
+ type: "workspace",
411
+ path: workspacePath,
412
+ page: nextPage
413
+ }));
414
+ keyboard.text("Workspaces", this.callbackData({ type: "workspaces" })).text("Back", this.callbackData({ type: "main" }));
415
+ await this.replyOrEdit(ctx, chats.length ? `${listTitle("Chats", page)}\n${workspacePath}` : `No chats\n${workspacePath}`, keyboard);
416
+ }
417
+ async showSubscriptions(ctx, requestedPage = 0) {
418
+ const state = await this.readState();
419
+ const keyboard = new InlineKeyboard();
420
+ const chatIds = Object.keys(state.subscriptions);
421
+ const page = paginatedItems(chatIds, requestedPage);
422
+ for (const chatId of page.items) {
423
+ const chat = await this.readChatResponse(chatId).catch(() => null);
424
+ keyboard.text(compactLabel(chat?.title ?? chatId, 36), this.callbackData({
425
+ type: "chat",
426
+ chatId
427
+ })).row();
428
+ }
429
+ this.addPaginationControls(keyboard, page, (nextPage) => ({
430
+ type: "subscriptions",
431
+ page: nextPage
432
+ }));
433
+ keyboard.text("Workspaces", this.callbackData({ type: "workspaces" })).text("Back", this.callbackData({ type: "main" }));
434
+ await this.replyOrEdit(ctx, chatIds.length ? listTitle("Subscriptions", page) : "No subscriptions.", keyboard);
435
+ }
436
+ async showChat(ctx, chatId) {
437
+ const chat = await this.readChatResponse(chatId);
438
+ const state = await this.readState();
439
+ await this.writeState({
440
+ ...state,
441
+ selectedChats: {
442
+ ...state.selectedChats,
443
+ [String(ctx.chat?.id ?? state.owner?.chatId ?? "")]: chatId
444
+ }
445
+ });
446
+ const recent = (await listMessages(chatId, 5).catch(() => ({ data: [] }))).data.filter(isAssistantChatMessage).slice(-3).map(formatAssistantTailBlock).join("\n\n");
447
+ await this.replyOrEdit(ctx, [
448
+ chat.title,
449
+ chat.workingDirectory ?? "",
450
+ `Status: ${chat.status}`,
451
+ `Model: ${chat.model ?? "default"}`,
452
+ `Security: ${chat.permissionMode}`,
453
+ recent ? `\nRecent\n${recent}` : ""
454
+ ].filter(Boolean).join("\n"), this.chatKeyboard(chat, state));
455
+ }
456
+ async showChatSettings(ctx, chatId) {
457
+ const chat = await this.readChatResponse(chatId);
458
+ const keyboard = new InlineKeyboard().text("Account", this.callbackData({
459
+ type: "accountMenu",
460
+ chatId
461
+ })).text("Model", this.callbackData({
462
+ type: "modelMenu",
463
+ chatId
464
+ })).row().text("Reasoning", this.callbackData({
465
+ type: "reasoningMenu",
466
+ chatId
467
+ })).text("Speed", this.callbackData({
468
+ type: "serviceTierMenu",
469
+ chatId
470
+ })).row().text(chat.collaborationMode === "plan" ? "Mode: Plan" : "Mode: Default", this.callbackData({
471
+ type: "mode",
472
+ chatId,
473
+ value: chat.collaborationMode === "plan" ? "default" : "plan"
474
+ })).row().text("Security", this.callbackData({
475
+ type: "permissionMenu",
476
+ chatId
477
+ })).text("Back", this.callbackData({
478
+ type: "chat",
479
+ chatId
480
+ }));
481
+ await this.replyOrEdit(ctx, [
482
+ "Chat settings",
483
+ `Account: ${chat.accountId ?? "none"}`,
484
+ `Model: ${chat.model ?? "default"}`,
485
+ `Reasoning: ${chat.reasoningEffort ?? "default"}`,
486
+ `Speed: ${chat.serviceTier ?? "default"}`,
487
+ `Mode: ${chat.collaborationMode}`,
488
+ `Security: ${chat.permissionMode}`
489
+ ].join("\n"), keyboard);
490
+ }
491
+ async showAccountMenu(ctx, chatId, requestedPage = 0) {
492
+ const chat = await this.readChatResponse(chatId);
493
+ const accounts = (await listAccounts()).filter((account) => account.status === "CONNECTED" && account.providerId === chat.providerId);
494
+ const page = paginatedItems(accounts, requestedPage);
495
+ const keyboard = new InlineKeyboard();
496
+ for (const account of page.items) keyboard.text(`${account.id === chat.accountId ? "* " : ""}${account.displayName}`, this.callbackData({
497
+ type: "setAccount",
498
+ chatId,
499
+ accountId: account.id
500
+ })).row();
501
+ this.addPaginationControls(keyboard, page, (nextPage) => ({
502
+ type: "accountMenu",
503
+ chatId,
504
+ page: nextPage
505
+ }));
506
+ keyboard.text("Back", this.callbackData({
507
+ type: "settings",
508
+ chatId
509
+ }));
510
+ await this.replyOrEdit(ctx, accounts.length ? listTitle("Provider accounts", page) : "No connected accounts for this provider.", keyboard);
511
+ }
512
+ async showModelMenu(ctx, chatId, requestedPage = 0) {
513
+ const accountId = (await this.readChatResponse(chatId)).accountId;
514
+ const keyboard = new InlineKeyboard();
515
+ let page = null;
516
+ if (accountId) {
517
+ page = paginatedItems((await listAccountModels(accountId).catch(() => ({ data: [] }))).data.filter((item) => !item.hidden), requestedPage);
518
+ for (const option of page.items) keyboard.text(option.displayName, this.callbackData({
519
+ type: "model",
520
+ chatId,
521
+ value: option.model
522
+ })).row();
523
+ this.addPaginationControls(keyboard, page, (nextPage) => ({
524
+ type: "modelMenu",
525
+ chatId,
526
+ page: nextPage
527
+ }));
528
+ }
529
+ keyboard.text("Back", this.callbackData({
530
+ type: "settings",
531
+ chatId
532
+ }));
533
+ await this.replyOrEdit(ctx, accountId ? page && page.total ? listTitle("Models", page) : "No models." : "Choose a provider account first.", keyboard);
534
+ }
535
+ async showReasoningMenu(ctx, chatId) {
536
+ const keyboard = new InlineKeyboard();
537
+ for (const option of [
538
+ "none",
539
+ "minimal",
540
+ "low",
541
+ "medium",
542
+ "high",
543
+ "xhigh"
544
+ ]) keyboard.text(option, this.callbackData({
545
+ type: "reasoning",
546
+ chatId,
547
+ value: option
548
+ })).row();
549
+ keyboard.text("Back", this.callbackData({
550
+ type: "settings",
551
+ chatId
552
+ }));
553
+ await this.replyOrEdit(ctx, "Reasoning", keyboard);
554
+ }
555
+ async showServiceTierMenu(ctx, chatId) {
556
+ const keyboard = new InlineKeyboard().text("standard", this.callbackData({
557
+ type: "serviceTier",
558
+ chatId,
559
+ value: "standard"
560
+ })).row().text("fast", this.callbackData({
561
+ type: "serviceTier",
562
+ chatId,
563
+ value: "fast"
564
+ })).row().text("Back", this.callbackData({
565
+ type: "settings",
566
+ chatId
567
+ }));
568
+ await this.replyOrEdit(ctx, "Speed", keyboard);
569
+ }
570
+ async showPermissionMenu(ctx, chatId) {
571
+ const keyboard = new InlineKeyboard().text("Ask for approval", this.callbackData({
572
+ type: "permission",
573
+ chatId,
574
+ value: "askForApproval"
575
+ })).row().text("Full access", this.callbackData({
576
+ type: "permission",
577
+ chatId,
578
+ value: "fullAccess"
579
+ })).row().text("Back", this.callbackData({
580
+ type: "settings",
581
+ chatId
582
+ }));
583
+ await this.replyOrEdit(ctx, "Security mode", keyboard);
584
+ }
585
+ async updateChatSetting(ctx, chatId, data, message) {
586
+ await updateChat(chatId, data);
587
+ await this.replyOrEdit(ctx, message, new InlineKeyboard().text("Back", this.callbackData({
588
+ type: "settings",
589
+ chatId
590
+ })));
591
+ }
592
+ async toggleSubscription(ctx, chatId) {
593
+ const state = await this.readState();
594
+ const subscriptions = { ...state.subscriptions };
595
+ const existingSubscription = subscriptions[chatId];
596
+ if (existingSubscription) {
597
+ delete subscriptions[chatId];
598
+ await this.writeState({
599
+ ...state,
600
+ subscriptions
601
+ });
602
+ if (existingSubscription.messageId) this.replyTargetsByTelegramMessageId.delete(existingSubscription.messageId);
603
+ await this.replyOrEdit(ctx, "Unsubscribed.", new InlineKeyboard().text("Subscribe", this.callbackData({
604
+ type: "subscribe",
605
+ chatId
606
+ })));
607
+ } else await this.subscribeChatForTelegramChat(ctx, chatId, state);
608
+ }
609
+ async subscribeChatForTelegramChat(ctx, chatId, state) {
610
+ const chat = await this.readChatResponse(chatId);
611
+ const telegramChatId = ctx.chat?.id ?? state.owner?.chatId ?? 0;
612
+ const startedAt = state.subscriptions[chatId]?.tailStartedAt ?? (/* @__PURE__ */ new Date()).toISOString();
613
+ const nextState = {
614
+ ...state,
615
+ selectedChats: {
616
+ ...state.selectedChats,
617
+ [String(telegramChatId)]: chatId
618
+ },
619
+ subscriptions: {
620
+ ...state.subscriptions,
621
+ [chatId]: {
622
+ messageId: state.subscriptions[chatId]?.messageId ?? null,
623
+ subscribedAt: state.subscriptions[chatId]?.subscribedAt ?? startedAt,
624
+ tailStartedAt: startedAt,
625
+ telegramChatId,
626
+ workspacePath: chat.workingDirectory ?? null
627
+ }
628
+ }
629
+ };
630
+ await this.writeState(nextState);
631
+ return this.queueSubscribedChatTailUpdate(chatId, nextState, ctx, chat);
632
+ }
633
+ async selectReplyChat(ctx, chatId) {
634
+ const state = await this.readState();
635
+ await this.writeState({
636
+ ...state,
637
+ selectedChats: {
638
+ ...state.selectedChats,
639
+ [String(ctx.chat?.id ?? state.owner?.chatId ?? "")]: chatId
640
+ }
641
+ });
642
+ await this.replyOrEdit(ctx, "Send a Telegram message now, or reply to a chat update.", new InlineKeyboard().text("Back", this.callbackData({
643
+ type: "chat",
644
+ chatId
645
+ })));
646
+ }
647
+ async respondToServerRequest(ctx, payload) {
648
+ const requestId = payload.message.requestId;
649
+ if (!requestId) {
650
+ await this.replyOrEdit(ctx, "Request is missing an id.", this.mainKeyboard());
651
+ return;
652
+ }
653
+ await respondToServerRequest(payload.chatId, requestId, serverRequestResponseFor(payload.message, payload.approved));
654
+ await this.replyOrEdit(ctx, payload.approved ? "Approved." : "Declined.", new InlineKeyboard().text("Chat", this.callbackData({
655
+ type: "chat",
656
+ chatId: payload.chatId
657
+ })));
658
+ }
659
+ async respondToUserInput(ctx, payload) {
660
+ const requestId = payload.message.requestId;
661
+ if (!requestId) {
662
+ await this.replyOrEdit(ctx, "Request is missing an id.", this.mainKeyboard());
663
+ return;
664
+ }
665
+ await respondToServerRequest(payload.chatId, requestId, {
666
+ kind: "userInput",
667
+ result: { answers: payload.answers }
668
+ });
669
+ await this.replyOrEdit(ctx, "Submitted.", new InlineKeyboard().text("Chat", this.callbackData({
670
+ type: "chat",
671
+ chatId: payload.chatId
672
+ })));
673
+ }
674
+ async updateSubscribedChatTail(chatId, state, ctx, chat) {
675
+ const currentState = state ?? await this.readState();
676
+ const subscription = currentState.subscriptions[chatId];
677
+ if (!subscription) return currentState;
678
+ const targetChatId = subscription.telegramChatId || currentState.owner?.chatId;
679
+ if (!targetChatId || !this.bot) return currentState;
680
+ let activeState = currentState;
681
+ let activeSubscription = subscription;
682
+ const tailStartedAt = activeSubscription.tailStartedAt ?? (/* @__PURE__ */ new Date()).toISOString();
683
+ if (!activeSubscription.tailStartedAt) {
684
+ activeSubscription = {
685
+ ...activeSubscription,
686
+ tailStartedAt
687
+ };
688
+ activeState = {
689
+ ...currentState,
690
+ subscriptions: {
691
+ ...currentState.subscriptions,
692
+ [chatId]: activeSubscription
693
+ }
694
+ };
695
+ await this.writeState(activeState);
696
+ }
697
+ const text = await this.formatSubscribedChatTail(chatId, tailStartedAt, chat);
698
+ const messageId = await this.sendOrEditSubscriptionMessage(ctx, targetChatId, subscription.messageId ?? null, text, this.subscriptionTailKeyboard(chatId));
699
+ this.replyTargetsByTelegramMessageId.set(messageId, chatId);
700
+ if (messageId === subscription.messageId) return activeState;
701
+ const latestState = await this.readState();
702
+ const latestSubscription = latestState.subscriptions[chatId];
703
+ if (!latestSubscription) return latestState;
704
+ const nextState = {
705
+ ...latestState,
706
+ subscriptions: {
707
+ ...latestState.subscriptions,
708
+ [chatId]: {
709
+ ...latestSubscription,
710
+ messageId
711
+ }
712
+ }
713
+ };
714
+ await this.writeState(nextState);
715
+ return nextState;
716
+ }
717
+ async queueSubscribedChatTailUpdate(chatId, state, ctx, chat) {
718
+ const previous = this.subscriptionTailUpdateQueue.get(chatId);
719
+ const queued = (async () => {
720
+ await previous?.catch(() => void 0);
721
+ return this.updateSubscribedChatTail(chatId, previous ? void 0 : state, ctx, chat);
722
+ })();
723
+ this.subscriptionTailUpdateQueue.set(chatId, queued);
724
+ try {
725
+ return await queued;
726
+ } finally {
727
+ if (this.subscriptionTailUpdateQueue.get(chatId) === queued) this.subscriptionTailUpdateQueue.delete(chatId);
728
+ }
729
+ }
730
+ async sendOrEditSubscriptionMessage(ctx, telegramChatId, messageId, text, keyboard) {
731
+ const bot = this.bot;
732
+ if (!bot) throw new Error("Telegram bot is not running.");
733
+ const messageText = fitTelegramMessage(text, telegramMessageMaxLength);
734
+ const callbackMessage = ctx?.callbackQuery?.message;
735
+ const callbackMessageId = typeof callbackMessage?.chat.id === "number" && callbackMessage.chat.id === telegramChatId ? callbackMessage.message_id : null;
736
+ const editableMessageId = messageId ?? callbackMessageId;
737
+ if (editableMessageId) try {
738
+ await bot.api.editMessageText(telegramChatId, editableMessageId, messageText, { reply_markup: keyboard });
739
+ return editableMessageId;
740
+ } catch (error) {
741
+ if (readErrorMessage$1(error).toLowerCase().includes("message is not modified")) return editableMessageId;
742
+ if (messageId) await bot.api.deleteMessage(telegramChatId, messageId).catch(() => void 0);
743
+ }
744
+ return (await bot.api.sendMessage(telegramChatId, messageText, { reply_markup: keyboard })).message_id;
745
+ }
746
+ async formatSubscribedChatTail(chatId, since, chat) {
747
+ const chatResponse = chat ?? await this.readChatResponse(chatId);
748
+ const tail = tailLines((await listMessages(chatId, 50).catch(() => ({ data: [] }))).data.filter((message) => isAssistantChatMessage(message) && isMessageAtOrAfter(message, since)).map(formatAssistantTailBlock).flatMap((block, index) => [...index > 0 ? [""] : [], ...block.split("\n")]), telegramMessageTailLines).join("\n").trim();
749
+ return [compactLabel(chatResponse.title, 80), tail || "(no assistant messages yet)"].join("\n");
750
+ }
751
+ async replyOrEdit(ctx, text, keyboard) {
752
+ const options = keyboard ? { reply_markup: keyboard } : void 0;
753
+ if (ctx.callbackQuery?.message) try {
754
+ await ctx.editMessageText(text, options);
755
+ return;
756
+ } catch {}
757
+ await ctx.reply(text, options);
758
+ }
759
+ chatKeyboard(chat, state) {
760
+ const subscribed = Boolean(state.subscriptions[chat.id]);
761
+ return new InlineKeyboard().text(subscribed ? "Unsubscribe" : "Subscribe", this.callbackData({
762
+ type: "subscribe",
763
+ chatId: chat.id
764
+ })).text("Reply", this.callbackData({
765
+ type: "reply",
766
+ chatId: chat.id
767
+ })).row().text("Settings", this.callbackData({
768
+ type: "settings",
769
+ chatId: chat.id
770
+ })).text("Chats", this.callbackData({
771
+ type: "workspace",
772
+ path: chat.workingDirectory ?? ""
773
+ }));
774
+ }
775
+ subscriptionTailKeyboard(chatId) {
776
+ return new InlineKeyboard().text("Unsubscribe", this.callbackData({
777
+ type: "subscribe",
778
+ chatId
779
+ }));
780
+ }
781
+ mainKeyboard() {
782
+ return new InlineKeyboard().text("Workspaces", this.callbackData({ type: "workspaces" })).text("Subscriptions", this.callbackData({ type: "subscriptions" }));
783
+ }
784
+ addPaginationControls(keyboard, page, payloadForPage) {
785
+ if (page.totalPages <= 1) return;
786
+ if (page.page > 0) keyboard.text("Prev", this.callbackData(payloadForPage(page.page - 1)));
787
+ if (page.page < page.totalPages - 1) keyboard.text("Next", this.callbackData(payloadForPage(page.page + 1)));
788
+ keyboard.row();
789
+ }
790
+ serverRequestKeyboard(message) {
791
+ if (message.kind === "USER_INPUT_PROMPT") {
792
+ const question = readUserInputQuestions(message.rawPayload)[0];
793
+ const keyboard = new InlineKeyboard();
794
+ if (question) for (const option of question.options.slice(0, 8)) keyboard.text(option.label, this.callbackData({
795
+ answers: { [question.id]: { answers: [option.label] } },
796
+ chatId: message.chatId,
797
+ message,
798
+ type: "userInput"
799
+ })).row();
800
+ keyboard.text("Submit empty", this.callbackData({
801
+ answers: {},
802
+ chatId: message.chatId,
803
+ message,
804
+ type: "userInput"
805
+ }));
806
+ keyboard.text("Chat", this.callbackData({
807
+ type: "chat",
808
+ chatId: message.chatId
809
+ }));
810
+ return keyboard;
811
+ }
812
+ return new InlineKeyboard().text("Approve", this.callbackData({
813
+ type: "serverRequest",
814
+ chatId: message.chatId,
815
+ message,
816
+ approved: true
817
+ })).text("Decline", this.callbackData({
818
+ type: "serverRequest",
819
+ chatId: message.chatId,
820
+ message,
821
+ approved: false
822
+ })).row().text("Chat", this.callbackData({
823
+ type: "chat",
824
+ chatId: message.chatId
825
+ }));
826
+ }
827
+ callbackData(payload) {
828
+ const direct = directCallbackData(payload);
829
+ if (direct && direct.length <= 64) return direct;
830
+ this.collectExpiredCallbackTokens();
831
+ const token = randomToken(8);
832
+ this.callbackTokens.set(token, {
833
+ expiresAt: Date.now() + callbackTtlMs,
834
+ payload
835
+ });
836
+ return `t:${token}`;
837
+ }
838
+ readCallbackPayload(data) {
839
+ if (!data) return null;
840
+ if (data.startsWith("t:")) {
841
+ const token = this.callbackTokens.get(data.slice(2));
842
+ if (!token || token.expiresAt < Date.now()) return null;
843
+ return token.payload;
844
+ }
845
+ if (data === "m") return { type: "main" };
846
+ if (data === "wl") return { type: "workspaces" };
847
+ if (data === "subs") return { type: "subscriptions" };
848
+ if (data.startsWith("c:")) return {
849
+ type: "chat",
850
+ chatId: data.slice(2)
851
+ };
852
+ return null;
853
+ }
854
+ collectExpiredCallbackTokens() {
855
+ const now = Date.now();
856
+ for (const [token, value] of this.callbackTokens) if (value.expiresAt < now) this.callbackTokens.delete(token);
857
+ }
858
+ async requireOwner(ctx) {
859
+ const from = ctx.from;
860
+ if (!from) return null;
861
+ const state = await this.readState();
862
+ if (!state.owner) {
863
+ await ctx.reply("Open Pockcode Plugins and pair this bot with /start <code>.").catch(() => void 0);
864
+ return null;
865
+ }
866
+ if (!this.isOwner(from.id, state)) {
867
+ await ctx.reply("Only the paired Telegram owner can control Pockcode.").catch(() => void 0);
868
+ return null;
869
+ }
870
+ return state;
871
+ }
872
+ isOwner(userId, state) {
873
+ return state.owner?.userId === userId;
874
+ }
875
+ async rememberOwnerChat(ctx, state) {
876
+ if (!ctx.chat || !state.owner || state.owner.chatId === ctx.chat.id) return state;
877
+ const nextState = {
878
+ ...state,
879
+ owner: {
880
+ ...state.owner,
881
+ chatId: ctx.chat.id
882
+ }
883
+ };
884
+ await this.writeState(nextState);
885
+ return nextState;
886
+ }
887
+ async ensurePairingCode() {
888
+ const state = await this.readState();
889
+ if (!state.owner && !state.pairingCode) await this.writeState({
890
+ ...state,
891
+ pairingCode: createPairingCode()
892
+ });
893
+ }
894
+ async readState() {
895
+ return this.context ? readTelegramState(await this.context.getState()) : emptyTelegramState();
896
+ }
897
+ async writeState(state) {
898
+ await this.context?.setState(serializeTelegramState(state));
899
+ }
900
+ async rememberSubscriptionReplyTargets() {
901
+ const state = await this.readState();
902
+ for (const [chatId, subscription] of Object.entries(state.subscriptions)) if (subscription.messageId) this.replyTargetsByTelegramMessageId.set(subscription.messageId, chatId);
903
+ }
904
+ async readChatResponse(chatId) {
905
+ return serializeChat(await getChat(chatId));
906
+ }
907
+ };
908
+ function directCallbackData(payload) {
909
+ if (payload.type === "main") return "m";
910
+ if (payload.type === "workspaces" && !payload.page) return "wl";
911
+ if (payload.type === "subscriptions" && !payload.page) return "subs";
912
+ if (payload.type === "chat") return `c:${payload.chatId}`;
913
+ return null;
914
+ }
915
+ function paginatedItems(items, requestedPage) {
916
+ const total = items.length;
917
+ const totalPages = Math.max(1, Math.ceil(total / telegramListPageSize));
918
+ const page = Math.min(Math.max(typeof requestedPage === "number" && Number.isFinite(requestedPage) ? Math.trunc(requestedPage) : 0, 0), totalPages - 1);
919
+ const start = page * telegramListPageSize;
920
+ return {
921
+ items: items.slice(start, start + telegramListPageSize),
922
+ page,
923
+ total,
924
+ totalPages
925
+ };
926
+ }
927
+ function listTitle(title, page) {
928
+ return page.totalPages > 1 ? `${title}\nPage ${page.page + 1}/${page.totalPages}` : title;
929
+ }
930
+ function readTelegramState(value) {
931
+ const record = readRecord(value);
932
+ return {
933
+ botUsername: readRecordString(record, "botUsername") || null,
934
+ owner: readOwner(record.owner),
935
+ pairingCode: readRecordString(record, "pairingCode") || null,
936
+ selectedChats: readStringRecord(record.selectedChats),
937
+ subscriptions: readSubscriptions(record.subscriptions)
938
+ };
939
+ }
940
+ function emptyTelegramState() {
941
+ return {
942
+ botUsername: null,
943
+ owner: null,
944
+ pairingCode: null,
945
+ selectedChats: {},
946
+ subscriptions: {}
947
+ };
948
+ }
949
+ function serializeTelegramState(state) {
950
+ return {
951
+ botUsername: state.botUsername,
952
+ owner: state.owner,
953
+ pairingCode: state.pairingCode,
954
+ selectedChats: state.selectedChats,
955
+ subscriptions: state.subscriptions
956
+ };
957
+ }
958
+ function readSubscribeStartPayload(value) {
959
+ if (!value?.startsWith("sub_")) return null;
960
+ return value.slice(4).trim() || null;
961
+ }
962
+ function readOwner(value) {
963
+ const record = readRecord(value);
964
+ const userId = readNumber(record.userId);
965
+ const chatId = readNumber(record.chatId);
966
+ if (!userId || !chatId) return null;
967
+ return {
968
+ chatId,
969
+ displayName: readRecordString(record, "displayName") || String(userId),
970
+ pairedAt: readRecordString(record, "pairedAt") || (/* @__PURE__ */ new Date()).toISOString(),
971
+ userId,
972
+ username: readRecordString(record, "username") || null
973
+ };
974
+ }
975
+ function readSubscriptions(value) {
976
+ const record = readRecord(value);
977
+ const subscriptions = {};
978
+ for (const [chatId, subscriptionValue] of Object.entries(record)) {
979
+ const subscription = readRecord(subscriptionValue);
980
+ const telegramChatId = readNumber(subscription.telegramChatId);
981
+ if (!telegramChatId) continue;
982
+ subscriptions[chatId] = {
983
+ messageId: readNumber(subscription.messageId),
984
+ subscribedAt: readRecordString(subscription, "subscribedAt") || (/* @__PURE__ */ new Date()).toISOString(),
985
+ tailStartedAt: readRecordString(subscription, "tailStartedAt") || null,
986
+ telegramChatId,
987
+ workspacePath: readRecordString(subscription, "workspacePath") || null
988
+ };
989
+ }
990
+ return subscriptions;
991
+ }
992
+ function readStringRecord(value) {
993
+ const record = readRecord(value);
994
+ const result = {};
995
+ for (const [key, recordValue] of Object.entries(record)) if (typeof recordValue === "string") result[key] = recordValue;
996
+ return result;
997
+ }
998
+ function readChatMessage(value) {
999
+ const record = readRecord(value);
1000
+ const chatId = readRecordString(record, "chatId");
1001
+ const id = readRecordString(record, "id");
1002
+ const role = readRecordString(record, "role");
1003
+ if (!chatId || !id || !isMessageRole(role)) return null;
1004
+ return {
1005
+ chatId,
1006
+ completedAt: readRecordString(record, "completedAt") || null,
1007
+ content: readRecordString(record, "content"),
1008
+ createdAt: readRecordString(record, "createdAt") || (/* @__PURE__ */ new Date()).toISOString(),
1009
+ id,
1010
+ itemId: readRecordString(record, "itemId") || null,
1011
+ kind: readMessageKind(readRecordString(record, "kind")),
1012
+ metadata: readRecord(record.metadata),
1013
+ rawPayload: readRecord(record.rawPayload),
1014
+ requestId: readRecordString(record, "requestId") || null,
1015
+ role,
1016
+ runId: readRecordString(record, "runId") || null,
1017
+ sequence: readNumber(record.sequence) ?? 0,
1018
+ status: readMessageStatus(readRecordString(record, "status")),
1019
+ turnId: readRecordString(record, "turnId") || null
1020
+ };
1021
+ }
1022
+ function serverRequestResponseFor(message, approved) {
1023
+ const method = readRecordString(readRecord(message.metadata), "serverRequestMethod");
1024
+ if (method === "item/permissions/requestApproval") return {
1025
+ kind: "permissions",
1026
+ result: {
1027
+ permissions: approved ? grantedPermissionsFromRequest(message) : {},
1028
+ scope: "turn"
1029
+ }
1030
+ };
1031
+ if (method === "item/tool/requestUserInput") return {
1032
+ kind: "userInput",
1033
+ result: { answers: {} }
1034
+ };
1035
+ return {
1036
+ decision: approved ? "accept" : "decline",
1037
+ kind: "approval",
1038
+ result: { decision: approved ? "accept" : "decline" }
1039
+ };
1040
+ }
1041
+ function grantedPermissionsFromRequest(message) {
1042
+ const requested = readRecord(readRecord(message.rawPayload).permissions);
1043
+ const granted = {};
1044
+ const network = readRecord(requested.network);
1045
+ const fileSystem = readRecord(requested.fileSystem);
1046
+ if (Object.keys(network).length) granted.network = network;
1047
+ if (Object.keys(fileSystem).length) granted.fileSystem = fileSystem;
1048
+ return granted;
1049
+ }
1050
+ function readUserInputQuestions(value) {
1051
+ const questions = readRecord(value).questions;
1052
+ if (!Array.isArray(questions)) return [];
1053
+ return questions.flatMap((questionValue) => {
1054
+ const question = readRecord(questionValue);
1055
+ const id = readRecordString(question, "id");
1056
+ if (!id) return [];
1057
+ return [{
1058
+ id,
1059
+ options: readUserInputOptions(question.options)
1060
+ }];
1061
+ });
1062
+ }
1063
+ function readUserInputOptions(value) {
1064
+ if (!Array.isArray(value)) return [];
1065
+ return value.flatMap((optionValue) => {
1066
+ const label = readRecordString(optionValue, "label");
1067
+ return label ? [{ label }] : [];
1068
+ });
1069
+ }
1070
+ function formatAssistantTailBlock(message) {
1071
+ return `Assistant:\n${normalizeTailText(message.content) || "(empty)"}`;
1072
+ }
1073
+ function isAssistantChatMessage(message) {
1074
+ return message.role === "ASSISTANT" && message.kind === "CHAT";
1075
+ }
1076
+ function shouldUpdateSubscriptionTailForMessage(message) {
1077
+ return isAssistantChatMessage(message);
1078
+ }
1079
+ function isMessageAtOrAfter(message, since) {
1080
+ if (message.status === "STREAMING") return true;
1081
+ const messageTime = Date.parse(message.createdAt);
1082
+ const completedTime = Date.parse(message.completedAt ?? "");
1083
+ const sinceTime = Date.parse(since);
1084
+ if (!Number.isFinite(sinceTime)) return true;
1085
+ if (Number.isFinite(completedTime) && completedTime >= sinceTime) return true;
1086
+ return Number.isFinite(messageTime) ? messageTime >= sinceTime : true;
1087
+ }
1088
+ function tailLines(lines, maxLines) {
1089
+ return lines.slice(-maxLines);
1090
+ }
1091
+ function normalizeTailText(value) {
1092
+ return value.trim().replace(/\r\n?/gu, "\n").replace(/\n{3,}/g, "\n\n");
1093
+ }
1094
+ function fitTelegramMessage(value, maxLength) {
1095
+ const normalized = value.trim();
1096
+ if (normalized.length <= maxLength) return normalized;
1097
+ return "...\n" + normalized.slice(-(maxLength - 4));
1098
+ }
1099
+ function compactLabel(value, maxLength) {
1100
+ const normalized = value.trim().replace(/\s+/g, " ");
1101
+ return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 1)}...` : normalized || "Untitled";
1102
+ }
1103
+ function displayNameForTelegramUser(user) {
1104
+ return [user.first_name, user.last_name].filter(Boolean).join(" ") || user.username || String(user.id);
1105
+ }
1106
+ function createPairingCode() {
1107
+ return randomToken(4).toUpperCase();
1108
+ }
1109
+ function randomToken(bytes) {
1110
+ const length = Math.max(8, bytes);
1111
+ let token = "";
1112
+ while (token.length < length) token += randomBytes(bytes).toString("base64url").replace(/[^a-z0-9]/giu, "");
1113
+ return token.slice(0, length);
1114
+ }
1115
+ function readRecord(value) {
1116
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1117
+ }
1118
+ function readRecordString(value, key) {
1119
+ const recordValue = readRecord(value)[key];
1120
+ return typeof recordValue === "string" ? recordValue.trim() : "";
1121
+ }
1122
+ function readNumber(value) {
1123
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1124
+ }
1125
+ function isMessageRole(value) {
1126
+ return value === "USER" || value === "ASSISTANT" || value === "SYSTEM" || value === "TOOL";
1127
+ }
1128
+ function readMessageKind(value) {
1129
+ return value === "THINKING" || value === "TOOL_ACTIVITY" || value === "COMMAND_EXECUTION" || value === "FILE_CHANGE" || value === "PLAN" || value === "APPROVAL" || value === "USER_INPUT_PROMPT" || value === "ERROR" ? value : "CHAT";
1130
+ }
1131
+ function readMessageStatus(value) {
1132
+ return value === "PENDING" || value === "STREAMING" || value === "FAILED" ? value : "COMPLETED";
1133
+ }
1134
+ function readErrorMessage$1(error) {
1135
+ return error instanceof Error ? error.message : "Telegram action failed.";
1136
+ }
1137
+ function readTelegramPollingError(error) {
1138
+ const message = readErrorMessage$1(error);
1139
+ return message.includes("409") ? "Telegram polling conflict. Stop the other bot instance, then restart this plugin." : message || "Telegram polling stopped.";
1140
+ }
1141
+ //#endregion
1142
+ //#region app/server/plugins/registry.server.ts
1143
+ var registrations = new Map([[telegramPluginRegistration.definition.id, telegramPluginRegistration]]);
1144
+ function listPluginRegistrations() {
1145
+ return [...registrations.values()];
1146
+ }
1147
+ function readPluginRegistration(pluginId) {
1148
+ const registration = registrations.get(pluginId);
1149
+ if (!registration) throw new Error(`Unknown plugin: ${pluginId}`);
1150
+ return registration;
1151
+ }
1152
+ //#endregion
1153
+ //#region app/server/plugins/storage.server.ts
1154
+ async function readPluginSetting(pluginId, defaults = {}) {
1155
+ await ensureDatabase();
1156
+ const row = (await prisma.$queryRawUnsafe(`SELECT "pluginId", "enabled", "settings", "secrets" FROM "PluginSetting" WHERE "pluginId" = ? LIMIT 1`, pluginId))[0];
1157
+ if (!row) return {
1158
+ enabled: false,
1159
+ pluginId,
1160
+ secrets: {},
1161
+ settings: defaults
1162
+ };
1163
+ return {
1164
+ enabled: row.enabled === true || row.enabled === 1,
1165
+ pluginId: row.pluginId,
1166
+ secrets: readJsonObject(row.secrets),
1167
+ settings: {
1168
+ ...defaults,
1169
+ ...readJsonObject(row.settings)
1170
+ }
1171
+ };
1172
+ }
1173
+ async function writePluginSetting(record) {
1174
+ await ensureDatabase();
1175
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1176
+ await prisma.$executeRawUnsafe(`INSERT INTO "PluginSetting" ("pluginId", "enabled", "settings", "secrets", "createdAt", "updatedAt")
1177
+ VALUES (?, ?, ?, ?, ?, ?)
1178
+ ON CONFLICT("pluginId") DO UPDATE SET
1179
+ "enabled" = excluded."enabled",
1180
+ "settings" = excluded."settings",
1181
+ "secrets" = excluded."secrets",
1182
+ "updatedAt" = excluded."updatedAt"`, record.pluginId, record.enabled ? 1 : 0, JSON.stringify(record.settings), JSON.stringify(record.secrets), timestamp, timestamp);
1183
+ return record;
1184
+ }
1185
+ async function readPluginState(pluginId) {
1186
+ await ensureDatabase();
1187
+ return readJsonObject((await prisma.$queryRawUnsafe(`SELECT "pluginId", "state" FROM "PluginState" WHERE "pluginId" = ? LIMIT 1`, pluginId))[0]?.state);
1188
+ }
1189
+ async function writePluginState(pluginId, state) {
1190
+ await ensureDatabase();
1191
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1192
+ await prisma.$executeRawUnsafe(`INSERT INTO "PluginState" ("pluginId", "state", "createdAt", "updatedAt")
1193
+ VALUES (?, ?, ?, ?)
1194
+ ON CONFLICT("pluginId") DO UPDATE SET
1195
+ "state" = excluded."state",
1196
+ "updatedAt" = excluded."updatedAt"`, pluginId, JSON.stringify(state), timestamp, timestamp);
1197
+ return state;
1198
+ }
1199
+ async function updatePluginState(pluginId, updater) {
1200
+ return writePluginState(pluginId, await updater(await readPluginState(pluginId)));
1201
+ }
1202
+ function readJsonObject(value) {
1203
+ if (typeof value === "string") try {
1204
+ return readJsonObject(JSON.parse(value));
1205
+ } catch {
1206
+ return {};
1207
+ }
1208
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
1209
+ return value;
1210
+ }
1211
+ //#endregion
1212
+ //#region app/server/plugins/manager.server.ts
1213
+ var manager_server_exports = /* @__PURE__ */ __exportAll({
1214
+ createPluginContext: () => createPluginContext,
1215
+ getPluginStatus: () => getPluginStatus,
1216
+ reconcilePlugins: () => reconcilePlugins,
1217
+ restartPlugin: () => restartPlugin,
1218
+ setPluginStatus: () => setPluginStatus,
1219
+ startPluginRuntimeManager: () => startPluginRuntimeManager,
1220
+ stopPlugin: () => stopPlugin
1221
+ });
1222
+ var globalForPlugins = globalThis;
1223
+ var managerState = globalForPlugins.pockcodePluginRuntimeManager ?? {
1224
+ activePlugins: /* @__PURE__ */ new Map(),
1225
+ pluginStatuses: /* @__PURE__ */ new Map(),
1226
+ unsubscribeProviderEvents: null
1227
+ };
1228
+ globalForPlugins.pockcodePluginRuntimeManager = managerState;
1229
+ var activePlugins = managerState.activePlugins;
1230
+ var pluginStatuses = managerState.pluginStatuses;
1231
+ function startPluginRuntimeManager() {
1232
+ if (managerState.unsubscribeProviderEvents) managerState.unsubscribeProviderEvents();
1233
+ managerState.unsubscribeProviderEvents = onProviderEvent((event) => {
1234
+ dispatchProviderEvent(event).catch(() => void 0);
1235
+ });
1236
+ reconcilePlugins().catch((error) => {
1237
+ for (const registration of listPluginRegistrations()) setPluginStatus(registration.definition.id, {
1238
+ state: "error",
1239
+ message: readErrorMessage(error)
1240
+ });
1241
+ });
1242
+ }
1243
+ async function reconcilePlugins() {
1244
+ for (const registration of listPluginRegistrations()) await restartPlugin(registration.definition.id);
1245
+ }
1246
+ async function restartPlugin(pluginId) {
1247
+ const registration = readPluginRegistration(pluginId);
1248
+ await stopPlugin(pluginId);
1249
+ const setting = await readPluginSetting(pluginId, registration.definition.defaultSettings);
1250
+ if (!setting.enabled) {
1251
+ setPluginStatus(pluginId, {
1252
+ state: "disabled",
1253
+ message: null
1254
+ });
1255
+ return;
1256
+ }
1257
+ setPluginStatus(pluginId, {
1258
+ state: "starting",
1259
+ message: null
1260
+ });
1261
+ const runtime = registration.runtime();
1262
+ activePlugins.set(pluginId, { runtime });
1263
+ try {
1264
+ await runtime.start(createPluginContext(pluginId, setting));
1265
+ if (getPluginStatus(pluginId).state === "starting") setPluginStatus(pluginId, {
1266
+ state: "running",
1267
+ message: null
1268
+ });
1269
+ } catch (error) {
1270
+ activePlugins.delete(pluginId);
1271
+ setPluginStatus(pluginId, {
1272
+ state: "error",
1273
+ message: readErrorMessage(error)
1274
+ });
1275
+ await Promise.resolve(runtime.stop()).catch(() => void 0);
1276
+ }
1277
+ }
1278
+ async function stopPlugin(pluginId) {
1279
+ const active = activePlugins.get(pluginId);
1280
+ activePlugins.delete(pluginId);
1281
+ if (!active) return;
1282
+ try {
1283
+ await active.runtime.stop();
1284
+ } catch (error) {
1285
+ setPluginStatus(pluginId, {
1286
+ state: "error",
1287
+ message: readErrorMessage(error)
1288
+ });
1289
+ }
1290
+ }
1291
+ function getPluginStatus(pluginId) {
1292
+ return pluginStatuses.get(pluginId) ?? {
1293
+ state: "disabled",
1294
+ message: null,
1295
+ updatedAt: null
1296
+ };
1297
+ }
1298
+ function setPluginStatus(pluginId, status) {
1299
+ pluginStatuses.set(pluginId, {
1300
+ ...status,
1301
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1302
+ });
1303
+ }
1304
+ function createPluginContext(pluginId, setting) {
1305
+ return {
1306
+ async getState() {
1307
+ return readPluginState(pluginId);
1308
+ },
1309
+ pluginId,
1310
+ secrets: setting.secrets,
1311
+ async setState(state) {
1312
+ await writePluginState(pluginId, state);
1313
+ },
1314
+ setStatus(status) {
1315
+ setPluginStatus(pluginId, status);
1316
+ },
1317
+ settings: setting.settings,
1318
+ updateState(updater) {
1319
+ return updatePluginState(pluginId, updater);
1320
+ }
1321
+ };
1322
+ }
1323
+ async function dispatchProviderEvent(event) {
1324
+ await Promise.all([...activePlugins.values()].map(async (active) => {
1325
+ try {
1326
+ await active.runtime.handleProviderEvent?.(event);
1327
+ } catch {}
1328
+ }));
1329
+ }
1330
+ function readErrorMessage(error) {
1331
+ return error instanceof Error ? error.message : "Plugin failed.";
1332
+ }
1333
+ //#endregion
1334
+ export { readPluginSetting as a, listPluginRegistrations as c, deleteWorkspaceHistory as d, listWorkspaceHistory as f, restartPlugin as i, readPluginRegistration as l, getPluginStatus as n, readPluginState as o, saveWorkspaceHistory as p, manager_server_exports as r, writePluginSetting as s, createPluginContext as t, verifyTelegramBotToken as u };