thepopebot 1.2.76-beta.33 → 1.2.76-beta.34
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/api/index.js +58 -39
- package/drizzle/0025_common_psylocke.sql +51 -0
- package/drizzle/meta/0025_snapshot.json +734 -0
- package/drizzle/meta/_journal.json +7 -0
- package/lib/actions.js +1 -0
- package/lib/ai/index.js +26 -36
- package/lib/ai/sdk-adapters/claude-code.js +3 -1
- package/lib/auth/actions.js +2 -20
- package/lib/auth/index.js +20 -0
- package/lib/chat/actions.js +61 -60
- package/lib/chat/api.js +8 -8
- package/lib/chat/components/app-sidebar.js +6 -6
- package/lib/chat/components/app-sidebar.jsx +9 -9
- package/lib/chat/components/index.js +1 -1
- package/lib/chat/components/messages-page.js +138 -0
- package/lib/chat/components/messages-page.jsx +180 -0
- package/lib/code/actions.js +3 -0
- package/lib/db/chats.js +15 -7
- package/lib/db/messages.js +117 -0
- package/lib/db/schema.js +19 -23
- package/lib/db/user-channels.js +2 -1
- package/lib/db/users.js +3 -2
- package/lib/tools/create-agent-job.js +5 -0
- package/lib/tools/docker.js +8 -3
- package/package.json +1 -1
- package/setup/setup.mjs +2 -1
- package/templates/.github/workflows/notify-pr-complete.yml +5 -1
- package/templates/skills/agent-job-dm/SKILL.md +39 -17
- package/templates/skills/agent-job-dm/agent-job-dm.js +22 -10
- package/lib/chat/components/notifications-page.js +0 -89
- package/lib/chat/components/notifications-page.jsx +0 -126
- package/lib/db/notifications.js +0 -104
package/api/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { dispatchCommand, dispatchPreAuthCommand } from '../lib/channels/command
|
|
|
7
7
|
import { getByChannelChatId, getVerifiedChannels, setActiveThread } from '../lib/db/user-channels.js';
|
|
8
8
|
import { getAllUsers, getUserById } from '../lib/db/users.js';
|
|
9
9
|
import { chat, chatStream, summarizeAgentJob } from '../lib/ai/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { createSystemMessage, getSubscribedAdminIds, markDelivered } from '../lib/db/messages.js';
|
|
11
11
|
import { getFireTriggers } from '../lib/triggers.js';
|
|
12
12
|
import { verifyApiKey } from '../lib/db/api-keys.js';
|
|
13
13
|
import { getConfig } from '../lib/config.js';
|
|
@@ -94,6 +94,7 @@ async function handleCreateAgentJob(request) {
|
|
|
94
94
|
llmModel: body.llm_model,
|
|
95
95
|
agentBackend: body.agent_backend,
|
|
96
96
|
scope: body.scope,
|
|
97
|
+
userId: body.user_id,
|
|
97
98
|
});
|
|
98
99
|
return Response.json(result);
|
|
99
100
|
} catch (err) {
|
|
@@ -179,6 +180,48 @@ async function handleListUsers() {
|
|
|
179
180
|
return Response.json({ users: enriched });
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Push a stored message to the recipient's default channel.
|
|
185
|
+
* On success: stamp `deliveredAt`. On failure: log; row stays undelivered.
|
|
186
|
+
* Best-effort, async — caller does not await.
|
|
187
|
+
*/
|
|
188
|
+
async function pushToDefaultChannel(row) {
|
|
189
|
+
try {
|
|
190
|
+
const verified = getVerifiedChannels(row.userId);
|
|
191
|
+
if (verified.length === 0) return; // No channel — row remains in inbox only
|
|
192
|
+
const target = verified[0]; // Ordered by verifiedAt ASC; first verified is default
|
|
193
|
+
|
|
194
|
+
if (target.channel === 'telegram') {
|
|
195
|
+
const botToken = getTelegramBotToken();
|
|
196
|
+
if (!botToken) {
|
|
197
|
+
console.error(`[pushToDefaultChannel] Telegram token not configured for user ${row.userId}`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const adapter = getTelegramAdapter(botToken);
|
|
201
|
+
await adapter.sendResponse(target.channelChatId, row.content, { chatId: target.channelChatId });
|
|
202
|
+
markDelivered(row.id);
|
|
203
|
+
}
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error(`[pushToDefaultChannel] failed for user ${row.userId}:`, err.message);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Dispatch a system message: store + deliver.
|
|
211
|
+
* - userId set → 1 row to that user, pushed to their default channel.
|
|
212
|
+
* - userId absent → fan-out: 1 row per admin where subscribedToSystemMessages=1, each pushed.
|
|
213
|
+
* Returns the count of rows written.
|
|
214
|
+
*/
|
|
215
|
+
function dispatchSystemMessage({ userId, content, payload }) {
|
|
216
|
+
const recipients = userId ? [userId] : getSubscribedAdminIds();
|
|
217
|
+
const rows = recipients.map((uid) => createSystemMessage(uid, content, payload));
|
|
218
|
+
// Fire-and-forget delivery; deliveredAt updated per row when each push lands.
|
|
219
|
+
for (const row of rows) {
|
|
220
|
+
pushToDefaultChannel(row);
|
|
221
|
+
}
|
|
222
|
+
return rows.length;
|
|
223
|
+
}
|
|
224
|
+
|
|
182
225
|
async function handleSendDm(request) {
|
|
183
226
|
let body;
|
|
184
227
|
try {
|
|
@@ -187,47 +230,18 @@ async function handleSendDm(request) {
|
|
|
187
230
|
return Response.json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
188
231
|
}
|
|
189
232
|
|
|
190
|
-
const { user_id, message,
|
|
191
|
-
if (!user_id) return Response.json({ error: 'Missing user_id' }, { status: 400 });
|
|
233
|
+
const { user_id, message, payload } = body;
|
|
192
234
|
if (!message || typeof message !== 'string') {
|
|
193
235
|
return Response.json({ error: 'Missing message' }, { status: 400 });
|
|
194
236
|
}
|
|
195
237
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const verified = getVerifiedChannels(user_id);
|
|
200
|
-
if (verified.length === 0) {
|
|
201
|
-
return Response.json({ error: 'User has no verified DM channels' }, { status: 409 });
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Pick channel: explicit name, or 'default' / omitted = first verified (Telegram preferred while it's the only impl).
|
|
205
|
-
const wantDefault = !channel || channel === 'default';
|
|
206
|
-
const target = wantDefault
|
|
207
|
-
? (verified.find((c) => c.channel === 'telegram') || verified[0])
|
|
208
|
-
: verified.find((c) => c.channel === channel);
|
|
209
|
-
|
|
210
|
-
if (!target) {
|
|
211
|
-
return Response.json(
|
|
212
|
-
{ error: `User has no verified ${channel} channel`, available: verified.map((c) => c.channel) },
|
|
213
|
-
{ status: 409 }
|
|
214
|
-
);
|
|
238
|
+
if (user_id) {
|
|
239
|
+
const user = getUserById(user_id);
|
|
240
|
+
if (!user) return Response.json({ error: 'User not found' }, { status: 404 });
|
|
215
241
|
}
|
|
216
242
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!botToken) return Response.json({ error: 'Telegram bot token not configured' }, { status: 500 });
|
|
220
|
-
const adapter = getTelegramAdapter(botToken);
|
|
221
|
-
try {
|
|
222
|
-
await adapter.sendResponse(target.channelChatId, message, { chatId: target.channelChatId });
|
|
223
|
-
return Response.json({ ok: true, channel: 'telegram', user_id });
|
|
224
|
-
} catch (err) {
|
|
225
|
-
console.error('Failed to send DM:', err);
|
|
226
|
-
return Response.json({ error: 'Failed to send DM' }, { status: 502 });
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return Response.json({ error: `Channel "${target.channel}" not implemented` }, { status: 501 });
|
|
243
|
+
const count = dispatchSystemMessage({ userId: user_id || null, content: message, payload });
|
|
244
|
+
return Response.json({ ok: true, recipients: count });
|
|
231
245
|
}
|
|
232
246
|
|
|
233
247
|
async function handleListAgentSecrets(request) {
|
|
@@ -369,11 +383,16 @@ async function handleGithubWebhook(request) {
|
|
|
369
383
|
};
|
|
370
384
|
|
|
371
385
|
const message = await summarizeAgentJob(results);
|
|
372
|
-
|
|
386
|
+
const recipientUserId = payload.user_id || null;
|
|
387
|
+
const count = dispatchSystemMessage({
|
|
388
|
+
userId: recipientUserId,
|
|
389
|
+
content: message,
|
|
390
|
+
payload,
|
|
391
|
+
});
|
|
373
392
|
|
|
374
|
-
console.log(`
|
|
393
|
+
console.log(`Notified ${count} recipient(s) for agent-job ${agentJobId.slice(0, 8)}${recipientUserId ? ` (user ${recipientUserId.slice(0, 8)})` : ' (broadcast)'}`);
|
|
375
394
|
|
|
376
|
-
return Response.json({ ok: true, notified: true });
|
|
395
|
+
return Response.json({ ok: true, notified: true, recipients: count });
|
|
377
396
|
} catch (err) {
|
|
378
397
|
console.error('Failed to process GitHub webhook:', err);
|
|
379
398
|
return Response.json({ error: 'Failed to process webhook' }, { status: 500 });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
-- Drop orphan chats (legacy 'telegram'/'unknown' userId literals) and their messages.
|
|
2
|
+
-- Forward-only: never user-owned data.
|
|
3
|
+
DELETE FROM `messages` WHERE `chat_id` IN (SELECT `id` FROM `chats` WHERE `user_id` IN ('telegram','unknown'));--> statement-breakpoint
|
|
4
|
+
DELETE FROM `chats` WHERE `user_id` IN ('telegram','unknown');--> statement-breakpoint
|
|
5
|
+
|
|
6
|
+
-- Drop legacy notification tables (folded into messages).
|
|
7
|
+
DROP TABLE `notifications`;--> statement-breakpoint
|
|
8
|
+
DROP TABLE `subscriptions`;--> statement-breakpoint
|
|
9
|
+
|
|
10
|
+
-- Recreate messages with new shape, backfilling user_id from chats join.
|
|
11
|
+
-- Orphan messages (chat row missing) are dropped by the INNER JOIN.
|
|
12
|
+
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
|
13
|
+
CREATE TABLE `__new_messages` (
|
|
14
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
15
|
+
`chat_id` text,
|
|
16
|
+
`user_id` text NOT NULL,
|
|
17
|
+
`role` text NOT NULL,
|
|
18
|
+
`content` text NOT NULL,
|
|
19
|
+
`payload` text,
|
|
20
|
+
`read` integer DEFAULT 0 NOT NULL,
|
|
21
|
+
`delivered_at` integer,
|
|
22
|
+
`created_at` integer NOT NULL
|
|
23
|
+
);
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
INSERT INTO `__new_messages` (`id`, `chat_id`, `user_id`, `role`, `content`, `payload`, `read`, `delivered_at`, `created_at`)
|
|
26
|
+
SELECT `m`.`id`, `m`.`chat_id`, `c`.`user_id`, `m`.`role`, `m`.`content`, NULL, 0, NULL, `m`.`created_at`
|
|
27
|
+
FROM `messages` `m` INNER JOIN `chats` `c` ON `c`.`id` = `m`.`chat_id`;--> statement-breakpoint
|
|
28
|
+
DROP TABLE `messages`;--> statement-breakpoint
|
|
29
|
+
ALTER TABLE `__new_messages` RENAME TO `messages`;--> statement-breakpoint
|
|
30
|
+
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
|
31
|
+
CREATE INDEX `messages_inbox_lookup` ON `messages` (`user_id`,`read`,`created_at`);--> statement-breakpoint
|
|
32
|
+
|
|
33
|
+
-- Recreate users with subscribed_to_system_messages column (defaults to 1 for existing rows).
|
|
34
|
+
CREATE TABLE `__new_users` (
|
|
35
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
36
|
+
`email` text NOT NULL,
|
|
37
|
+
`password_hash` text NOT NULL,
|
|
38
|
+
`role` text DEFAULT 'user' NOT NULL,
|
|
39
|
+
`first_name` text,
|
|
40
|
+
`last_name` text,
|
|
41
|
+
`nickname` text,
|
|
42
|
+
`subscribed_to_system_messages` integer DEFAULT 1 NOT NULL,
|
|
43
|
+
`created_at` integer NOT NULL,
|
|
44
|
+
`updated_at` integer NOT NULL
|
|
45
|
+
);
|
|
46
|
+
--> statement-breakpoint
|
|
47
|
+
INSERT INTO `__new_users` (`id`, `email`, `password_hash`, `role`, `first_name`, `last_name`, `nickname`, `subscribed_to_system_messages`, `created_at`, `updated_at`)
|
|
48
|
+
SELECT `id`, `email`, `password_hash`, `role`, `first_name`, `last_name`, `nickname`, 1, `created_at`, `updated_at` FROM `users`;--> statement-breakpoint
|
|
49
|
+
DROP TABLE `users`;--> statement-breakpoint
|
|
50
|
+
ALTER TABLE `__new_users` RENAME TO `users`;--> statement-breakpoint
|
|
51
|
+
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);
|