@chaaskit/server 0.1.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/dist/api/admin.js +438 -0
- package/dist/api/admin.js.map +1 -0
- package/dist/api/agents.js +21 -0
- package/dist/api/agents.js.map +1 -0
- package/dist/api/api-keys.js +122 -0
- package/dist/api/api-keys.js.map +1 -0
- package/dist/api/auth.js +399 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/chat.js +900 -0
- package/dist/api/chat.js.map +1 -0
- package/dist/api/config.js +91 -0
- package/dist/api/config.js.map +1 -0
- package/dist/api/documents.js +237 -0
- package/dist/api/documents.js.map +1 -0
- package/dist/api/export.js +107 -0
- package/dist/api/export.js.map +1 -0
- package/dist/api/health.js +25 -0
- package/dist/api/health.js.map +1 -0
- package/dist/api/mcp-server.js +84 -0
- package/dist/api/mcp-server.js.map +1 -0
- package/dist/api/mcp.js +400 -0
- package/dist/api/mcp.js.map +1 -0
- package/dist/api/mentions.js +94 -0
- package/dist/api/mentions.js.map +1 -0
- package/dist/api/oauth.js +366 -0
- package/dist/api/oauth.js.map +1 -0
- package/dist/api/payments.js +473 -0
- package/dist/api/payments.js.map +1 -0
- package/dist/api/projects.js +301 -0
- package/dist/api/projects.js.map +1 -0
- package/dist/api/scheduled-prompts.js +617 -0
- package/dist/api/scheduled-prompts.js.map +1 -0
- package/dist/api/search.js +85 -0
- package/dist/api/search.js.map +1 -0
- package/dist/api/share.js +188 -0
- package/dist/api/share.js.map +1 -0
- package/dist/api/slack.js +468 -0
- package/dist/api/slack.js.map +1 -0
- package/dist/api/teams.js +693 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/templates.js +134 -0
- package/dist/api/templates.js.map +1 -0
- package/dist/api/threads.js +323 -0
- package/dist/api/threads.js.map +1 -0
- package/dist/api/upload.js +57 -0
- package/dist/api/upload.js.map +1 -0
- package/dist/api/user.js +111 -0
- package/dist/api/user.js.map +1 -0
- package/dist/api/v1/openai.js +245 -0
- package/dist/api/v1/openai.js.map +1 -0
- package/dist/app.js +168 -0
- package/dist/app.js.map +1 -0
- package/dist/bin/cli.js +57 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/commands/db-sync.js +108 -0
- package/dist/commands/db-sync.js.map +1 -0
- package/dist/config/loader.js +374 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/documents/extractors.js +136 -0
- package/dist/documents/extractors.js.map +1 -0
- package/dist/extensions/glob.js +53 -0
- package/dist/extensions/glob.js.map +1 -0
- package/dist/extensions/loader.js +72 -0
- package/dist/extensions/loader.js.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/index.js +75 -0
- package/dist/loaders/index.js.map +1 -0
- package/dist/mcp/client.js +551 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/server.js +335 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/middleware/apiKeyAuth.js +136 -0
- package/dist/middleware/apiKeyAuth.js.map +1 -0
- package/dist/middleware/auth.js +192 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/errorHandler.js +41 -0
- package/dist/middleware/errorHandler.js.map +1 -0
- package/dist/middleware/mcpServerAuth.js +164 -0
- package/dist/middleware/mcpServerAuth.js.map +1 -0
- package/dist/middleware/requestLogger.js +9 -0
- package/dist/middleware/requestLogger.js.map +1 -0
- package/dist/middleware/team.js +132 -0
- package/dist/middleware/team.js.map +1 -0
- package/dist/oauth/server.js +410 -0
- package/dist/oauth/server.js.map +1 -0
- package/dist/queue/cli.js +93 -0
- package/dist/queue/cli.js.map +1 -0
- package/dist/queue/handlers/index.js +91 -0
- package/dist/queue/handlers/index.js.map +1 -0
- package/dist/queue/handlers/scheduled-prompt.js +270 -0
- package/dist/queue/handlers/scheduled-prompt.js.map +1 -0
- package/dist/queue/index.js +91 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/providers/memory.js +296 -0
- package/dist/queue/providers/memory.js.map +1 -0
- package/dist/queue/providers/sqs.js +275 -0
- package/dist/queue/providers/sqs.js.map +1 -0
- package/dist/queue/scheduler.js +355 -0
- package/dist/queue/scheduler.js.map +1 -0
- package/dist/queue/types.js +5 -0
- package/dist/queue/types.js.map +1 -0
- package/dist/queue/worker.js +230 -0
- package/dist/queue/worker.js.map +1 -0
- package/dist/registry/index.js +40 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/server.js +207 -0
- package/dist/server.js.map +1 -0
- package/dist/services/agent.js +530 -0
- package/dist/services/agent.js.map +1 -0
- package/dist/services/agents.js +194 -0
- package/dist/services/agents.js.map +1 -0
- package/dist/services/documents.js +507 -0
- package/dist/services/documents.js.map +1 -0
- package/dist/services/email/index.js +91 -0
- package/dist/services/email/index.js.map +1 -0
- package/dist/services/email/providers/ses.js +97 -0
- package/dist/services/email/providers/ses.js.map +1 -0
- package/dist/services/email/templates.js +194 -0
- package/dist/services/email/templates.js.map +1 -0
- package/dist/services/email/types.js +5 -0
- package/dist/services/email/types.js.map +1 -0
- package/dist/services/encryption.js +69 -0
- package/dist/services/encryption.js.map +1 -0
- package/dist/services/oauth-discovery.js +226 -0
- package/dist/services/oauth-discovery.js.map +1 -0
- package/dist/services/pendingConfirmation.js +105 -0
- package/dist/services/pendingConfirmation.js.map +1 -0
- package/dist/services/scheduledPrompts.js +70 -0
- package/dist/services/scheduledPrompts.js.map +1 -0
- package/dist/services/slack/client.js +174 -0
- package/dist/services/slack/client.js.map +1 -0
- package/dist/services/slack/events.js +189 -0
- package/dist/services/slack/events.js.map +1 -0
- package/dist/services/slack/index.js +6 -0
- package/dist/services/slack/index.js.map +1 -0
- package/dist/services/slack/notifications.js +124 -0
- package/dist/services/slack/notifications.js.map +1 -0
- package/dist/services/slack/signature.js +74 -0
- package/dist/services/slack/signature.js.map +1 -0
- package/dist/services/slack/thread-context.js +191 -0
- package/dist/services/slack/thread-context.js.map +1 -0
- package/dist/services/toolConfirmation.js +55 -0
- package/dist/services/toolConfirmation.js.map +1 -0
- package/dist/services/usage.js +241 -0
- package/dist/services/usage.js.map +1 -0
- package/dist/ssr/build.js +90 -0
- package/dist/ssr/build.js.map +1 -0
- package/dist/ssr/components/SSRMessageList.js +120 -0
- package/dist/ssr/components/SSRMessageList.js.map +1 -0
- package/dist/ssr/entry.client.js +8 -0
- package/dist/ssr/entry.client.js.map +1 -0
- package/dist/ssr/entry.server.js +71 -0
- package/dist/ssr/entry.server.js.map +1 -0
- package/dist/ssr/handler.js +51 -0
- package/dist/ssr/handler.js.map +1 -0
- package/dist/ssr/root.js +184 -0
- package/dist/ssr/root.js.map +1 -0
- package/dist/ssr/routes/login.js +140 -0
- package/dist/ssr/routes/login.js.map +1 -0
- package/dist/ssr/routes/pricing.js +195 -0
- package/dist/ssr/routes/pricing.js.map +1 -0
- package/dist/ssr/routes/privacy.js +39 -0
- package/dist/ssr/routes/privacy.js.map +1 -0
- package/dist/ssr/routes/register.js +148 -0
- package/dist/ssr/routes/register.js.map +1 -0
- package/dist/ssr/routes/shared.$shareId.js +153 -0
- package/dist/ssr/routes/shared.$shareId.js.map +1 -0
- package/dist/ssr/routes/terms.js +39 -0
- package/dist/ssr/routes/terms.js.map +1 -0
- package/dist/storage/index.js +43 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/providers/database.js +38 -0
- package/dist/storage/providers/database.js.map +1 -0
- package/dist/storage/providers/filesystem.js +51 -0
- package/dist/storage/providers/filesystem.js.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/tools/documents.js +336 -0
- package/dist/tools/documents.js.map +1 -0
- package/dist/tools/get-plan-usage.js +82 -0
- package/dist/tools/get-plan-usage.js.map +1 -0
- package/dist/tools/index.js +106 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-scrape.js +145 -0
- package/dist/tools/web-scrape.js.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled Prompt Job Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handles execution and notification of scheduled prompts (automations).
|
|
5
|
+
*/
|
|
6
|
+
import { db } from '@chaaskit/db';
|
|
7
|
+
import { registerJobHandler } from '../index.js';
|
|
8
|
+
import { createAgentService } from '../../services/agent.js';
|
|
9
|
+
import { getAgentById, getDefaultAgent, toAgentConfig, isBuiltInAgent } from '../../services/agents.js';
|
|
10
|
+
import { sendEmail, isEmailEnabled } from '../../services/email/index.js';
|
|
11
|
+
import { getConfig } from '../../config/loader.js';
|
|
12
|
+
import { getQueueProvider } from '../index.js';
|
|
13
|
+
/**
|
|
14
|
+
* scheduled-prompt:execute
|
|
15
|
+
*
|
|
16
|
+
* Executes a scheduled prompt:
|
|
17
|
+
* 1. Loads the ScheduledPrompt with user/team context
|
|
18
|
+
* 2. Gets or creates the dedicated thread
|
|
19
|
+
* 3. Creates user message with the prompt
|
|
20
|
+
* 4. Runs the agent and collects the response
|
|
21
|
+
* 5. Saves the assistant message
|
|
22
|
+
* 6. Updates tracking (lastRunAt, runCount)
|
|
23
|
+
* 7. Enqueues notification job
|
|
24
|
+
*/
|
|
25
|
+
registerJobHandler('scheduled-prompt:execute', async (job, ctx) => {
|
|
26
|
+
const { scheduledPromptId } = job.payload;
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
ctx.log(`Executing scheduled prompt: ${scheduledPromptId}`);
|
|
29
|
+
// 1. Load the scheduled prompt
|
|
30
|
+
const prompt = await db.scheduledPrompt.findUnique({
|
|
31
|
+
where: { id: scheduledPromptId },
|
|
32
|
+
include: {
|
|
33
|
+
thread: true,
|
|
34
|
+
user: true,
|
|
35
|
+
team: {
|
|
36
|
+
include: {
|
|
37
|
+
slackIntegration: true,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
if (!prompt) {
|
|
43
|
+
throw new Error(`Scheduled prompt not found: ${scheduledPromptId}`);
|
|
44
|
+
}
|
|
45
|
+
if (!prompt.enabled) {
|
|
46
|
+
ctx.log('Scheduled prompt is disabled, skipping execution');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
ctx.log(`Running prompt: "${prompt.name}"`);
|
|
50
|
+
try {
|
|
51
|
+
// 2. Get or create dedicated thread
|
|
52
|
+
let thread = prompt.thread;
|
|
53
|
+
if (!thread) {
|
|
54
|
+
thread = await db.thread.create({
|
|
55
|
+
data: {
|
|
56
|
+
title: `[Auto] ${prompt.name}`,
|
|
57
|
+
userId: prompt.userId,
|
|
58
|
+
teamId: prompt.teamId,
|
|
59
|
+
agentId: prompt.agentId,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
// Update the scheduled prompt with the new thread
|
|
63
|
+
await db.scheduledPrompt.update({
|
|
64
|
+
where: { id: scheduledPromptId },
|
|
65
|
+
data: { threadId: thread.id },
|
|
66
|
+
});
|
|
67
|
+
ctx.log(`Created new thread: ${thread.id}`);
|
|
68
|
+
}
|
|
69
|
+
// 3. Create user message
|
|
70
|
+
await db.message.create({
|
|
71
|
+
data: {
|
|
72
|
+
threadId: thread.id,
|
|
73
|
+
role: 'user',
|
|
74
|
+
content: prompt.prompt,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// 4. Get agent and run
|
|
78
|
+
const agent = prompt.agentId
|
|
79
|
+
? getAgentById(prompt.agentId)
|
|
80
|
+
: getDefaultAgent();
|
|
81
|
+
if (!agent) {
|
|
82
|
+
throw new Error(`Agent not found: ${prompt.agentId || 'default'}`);
|
|
83
|
+
}
|
|
84
|
+
const agentConfig = toAgentConfig(agent);
|
|
85
|
+
const agentService = createAgentService(agentConfig);
|
|
86
|
+
// Get system prompt
|
|
87
|
+
let systemPrompt = 'You are a helpful assistant.';
|
|
88
|
+
if (isBuiltInAgent(agent)) {
|
|
89
|
+
systemPrompt = agent.systemPrompt;
|
|
90
|
+
}
|
|
91
|
+
// Add team context if applicable
|
|
92
|
+
let teamContext = null;
|
|
93
|
+
if (prompt.team?.context) {
|
|
94
|
+
teamContext = prompt.team.context;
|
|
95
|
+
}
|
|
96
|
+
// Load thread messages for context
|
|
97
|
+
const messages = await db.message.findMany({
|
|
98
|
+
where: { threadId: thread.id },
|
|
99
|
+
orderBy: { createdAt: 'asc' },
|
|
100
|
+
});
|
|
101
|
+
const chatMessages = messages.map((m) => ({
|
|
102
|
+
role: m.role,
|
|
103
|
+
content: m.content,
|
|
104
|
+
}));
|
|
105
|
+
// Run agent
|
|
106
|
+
let result = '';
|
|
107
|
+
let inputTokens = 0;
|
|
108
|
+
let outputTokens = 0;
|
|
109
|
+
ctx.log('Running agent...');
|
|
110
|
+
for await (const chunk of agentService.chat(chatMessages, {
|
|
111
|
+
systemPrompt,
|
|
112
|
+
teamContext,
|
|
113
|
+
})) {
|
|
114
|
+
if (chunk.type === 'text' && chunk.content) {
|
|
115
|
+
result += chunk.content;
|
|
116
|
+
}
|
|
117
|
+
else if (chunk.type === 'usage' && chunk.usage) {
|
|
118
|
+
inputTokens = chunk.usage.inputTokens;
|
|
119
|
+
outputTokens = chunk.usage.outputTokens;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
ctx.log(`Agent response: ${result.length} chars, ${inputTokens} in / ${outputTokens} out`);
|
|
123
|
+
// 5. Save assistant message
|
|
124
|
+
await db.message.create({
|
|
125
|
+
data: {
|
|
126
|
+
threadId: thread.id,
|
|
127
|
+
role: 'assistant',
|
|
128
|
+
content: result,
|
|
129
|
+
inputTokens,
|
|
130
|
+
outputTokens,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
const runDuration = Date.now() - startTime;
|
|
134
|
+
// 6. Update tracking
|
|
135
|
+
await db.scheduledPrompt.update({
|
|
136
|
+
where: { id: scheduledPromptId },
|
|
137
|
+
data: {
|
|
138
|
+
lastRunAt: new Date(),
|
|
139
|
+
lastRunStatus: 'success',
|
|
140
|
+
lastError: null,
|
|
141
|
+
runCount: { increment: 1 },
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
// 7. Enqueue notification
|
|
145
|
+
if (prompt.notifySlack || prompt.notifyEmail) {
|
|
146
|
+
const queue = getQueueProvider();
|
|
147
|
+
await queue.enqueue('scheduled-prompt:notify', {
|
|
148
|
+
scheduledPromptId,
|
|
149
|
+
threadId: thread.id,
|
|
150
|
+
result,
|
|
151
|
+
runDuration,
|
|
152
|
+
});
|
|
153
|
+
ctx.log('Notification job enqueued');
|
|
154
|
+
}
|
|
155
|
+
ctx.log(`Completed in ${runDuration}ms`);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
159
|
+
// Update tracking with failure
|
|
160
|
+
await db.scheduledPrompt.update({
|
|
161
|
+
where: { id: scheduledPromptId },
|
|
162
|
+
data: {
|
|
163
|
+
lastRunAt: new Date(),
|
|
164
|
+
lastRunStatus: 'failed',
|
|
165
|
+
lastError: errorMessage,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}, 'Execute scheduled prompt and store result');
|
|
171
|
+
/**
|
|
172
|
+
* scheduled-prompt:notify
|
|
173
|
+
*
|
|
174
|
+
* Sends notifications after a scheduled prompt completes:
|
|
175
|
+
* 1. Slack notification (if enabled and team has integration)
|
|
176
|
+
* 2. Email notification (if enabled and recipients configured)
|
|
177
|
+
*/
|
|
178
|
+
registerJobHandler('scheduled-prompt:notify', async (job, ctx) => {
|
|
179
|
+
const { scheduledPromptId, threadId, result, runDuration } = job.payload;
|
|
180
|
+
ctx.log(`Sending notifications for scheduled prompt: ${scheduledPromptId}`);
|
|
181
|
+
// Load prompt with team and slack integration
|
|
182
|
+
const prompt = await db.scheduledPrompt.findUnique({
|
|
183
|
+
where: { id: scheduledPromptId },
|
|
184
|
+
include: {
|
|
185
|
+
user: true,
|
|
186
|
+
team: {
|
|
187
|
+
include: {
|
|
188
|
+
slackIntegration: true,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
if (!prompt) {
|
|
194
|
+
throw new Error(`Scheduled prompt not found: ${scheduledPromptId}`);
|
|
195
|
+
}
|
|
196
|
+
const config = getConfig();
|
|
197
|
+
const appUrl = config.app.url;
|
|
198
|
+
const appName = config.app.name;
|
|
199
|
+
const threadUrl = `${appUrl}/chat/${threadId}`;
|
|
200
|
+
// Helper to truncate text
|
|
201
|
+
const truncate = (text, maxLength) => {
|
|
202
|
+
if (text.length <= maxLength)
|
|
203
|
+
return text;
|
|
204
|
+
return text.slice(0, maxLength - 3) + '...';
|
|
205
|
+
};
|
|
206
|
+
// Slack notification
|
|
207
|
+
if (prompt.notifySlack && prompt.team?.slackIntegration) {
|
|
208
|
+
try {
|
|
209
|
+
const { SlackClient } = await import('../../services/slack/client.js');
|
|
210
|
+
const integration = prompt.team.slackIntegration;
|
|
211
|
+
if (integration.status === 'active' && integration.notificationChannel) {
|
|
212
|
+
const client = SlackClient.fromEncrypted(integration.encryptedTokens);
|
|
213
|
+
const message = [
|
|
214
|
+
`*${prompt.name}* completed in ${(runDuration / 1000).toFixed(1)}s`,
|
|
215
|
+
'',
|
|
216
|
+
truncate(result, 500),
|
|
217
|
+
'',
|
|
218
|
+
`<${threadUrl}|View full thread>`,
|
|
219
|
+
].join('\n');
|
|
220
|
+
await client.postMessage(integration.notificationChannel, message);
|
|
221
|
+
ctx.log('Slack notification sent');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
ctx.log(`Slack notification failed: ${error instanceof Error ? error.message : error}`);
|
|
226
|
+
// Don't throw - continue with email
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Email notification
|
|
230
|
+
if (prompt.notifyEmail && prompt.emailRecipients && isEmailEnabled()) {
|
|
231
|
+
try {
|
|
232
|
+
const emails = JSON.parse(prompt.emailRecipients);
|
|
233
|
+
for (const email of emails) {
|
|
234
|
+
await sendEmail({
|
|
235
|
+
to: email,
|
|
236
|
+
subject: `[${appName}] ${prompt.name} completed`,
|
|
237
|
+
html: `
|
|
238
|
+
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
|
|
239
|
+
<h2>${prompt.name}</h2>
|
|
240
|
+
<p style="color: #666;">Completed in ${(runDuration / 1000).toFixed(1)} seconds</p>
|
|
241
|
+
|
|
242
|
+
<div style="background: #f5f5f5; padding: 16px; border-radius: 8px; margin: 16px 0;">
|
|
243
|
+
<pre style="white-space: pre-wrap; word-break: break-word; margin: 0;">${truncate(result, 1000)}</pre>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<p>
|
|
247
|
+
<a href="${threadUrl}" style="display: inline-block; background: #4f46e5; color: white; padding: 8px 16px; text-decoration: none; border-radius: 4px;">
|
|
248
|
+
View Full Thread
|
|
249
|
+
</a>
|
|
250
|
+
</p>
|
|
251
|
+
|
|
252
|
+
<p style="color: #999; font-size: 12px; margin-top: 32px;">
|
|
253
|
+
This email was sent by ${appName}.
|
|
254
|
+
<a href="${appUrl}/automations">Manage automations</a>
|
|
255
|
+
</p>
|
|
256
|
+
</div>
|
|
257
|
+
`,
|
|
258
|
+
text: `${prompt.name} completed in ${(runDuration / 1000).toFixed(1)} seconds\n\n${truncate(result, 1000)}\n\nView thread: ${threadUrl}`,
|
|
259
|
+
});
|
|
260
|
+
ctx.log(`Email sent to ${email}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
ctx.log(`Email notification failed: ${error instanceof Error ? error.message : error}`);
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
ctx.log('Notifications complete');
|
|
269
|
+
}, 'Send notifications for completed scheduled prompt');
|
|
270
|
+
//# sourceMappingURL=scheduled-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-prompt.js","sourceRoot":"","sources":["../../../src/queue/handlers/scheduled-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { MemoryQueueProvider } from './providers/memory.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
// Re-export handlers
|
|
4
|
+
export { registerJobHandler, getJobHandler, hasJobHandler, getRegisteredJobTypes, unregisterJobHandler, clearJobHandlers, executeJob, } from './handlers/index.js';
|
|
5
|
+
// Re-export worker
|
|
6
|
+
export { Worker, startWorker, stopWorker, getWorker, resetWorker, } from './worker.js';
|
|
7
|
+
// Re-export scheduler
|
|
8
|
+
export { Scheduler, startScheduler, stopScheduler, getScheduler, resetScheduler, } from './scheduler.js';
|
|
9
|
+
let queueProvider = null;
|
|
10
|
+
/**
|
|
11
|
+
* Get default queue provider config for development
|
|
12
|
+
*/
|
|
13
|
+
export function getDefaultProviderConfig() {
|
|
14
|
+
return {
|
|
15
|
+
type: 'memory',
|
|
16
|
+
maxHistorySize: 1000,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a queue provider based on configuration.
|
|
21
|
+
* Uses dynamic imports for optional dependencies (SQS SDK only loaded if needed).
|
|
22
|
+
*/
|
|
23
|
+
export async function createQueueProvider(config) {
|
|
24
|
+
switch (config.type) {
|
|
25
|
+
case 'memory':
|
|
26
|
+
return new MemoryQueueProvider(config);
|
|
27
|
+
case 'sqs':
|
|
28
|
+
// Dynamic import - @aws-sdk/client-sqs is optional peer dependency
|
|
29
|
+
try {
|
|
30
|
+
const { SQSQueueProvider } = await import('./providers/sqs.js');
|
|
31
|
+
return new SQSQueueProvider(config);
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
if (e.code === 'ERR_MODULE_NOT_FOUND') {
|
|
35
|
+
throw new Error('SQS provider requires @aws-sdk/client-sqs. Install it with: pnpm add @aws-sdk/client-sqs');
|
|
36
|
+
}
|
|
37
|
+
throw e;
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`Unknown queue provider type: ${config.type}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get or create the queue provider singleton.
|
|
45
|
+
* Must be initialized with initializeQueueProvider() first, or returns a memory provider.
|
|
46
|
+
*/
|
|
47
|
+
export function getQueueProvider() {
|
|
48
|
+
if (!queueProvider) {
|
|
49
|
+
// Default to memory provider if not initialized
|
|
50
|
+
console.warn('[Queue] Provider not initialized, using default memory provider');
|
|
51
|
+
queueProvider = new MemoryQueueProvider({ type: 'memory' });
|
|
52
|
+
}
|
|
53
|
+
return queueProvider;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Initialize the queue provider singleton.
|
|
57
|
+
* Call this during server startup with the app's queue config.
|
|
58
|
+
*/
|
|
59
|
+
export async function initializeQueueProvider(config) {
|
|
60
|
+
if (queueProvider) {
|
|
61
|
+
console.warn('[Queue] Provider already initialized');
|
|
62
|
+
return queueProvider;
|
|
63
|
+
}
|
|
64
|
+
if (!config.enabled) {
|
|
65
|
+
console.log('[Queue] Queue system is disabled');
|
|
66
|
+
// Return a no-op provider that just logs
|
|
67
|
+
queueProvider = new MemoryQueueProvider({ type: 'memory' });
|
|
68
|
+
return queueProvider;
|
|
69
|
+
}
|
|
70
|
+
console.log(`[Queue] Initializing ${config.providerConfig.type} provider...`);
|
|
71
|
+
queueProvider = await createQueueProvider(config.providerConfig);
|
|
72
|
+
console.log(`[Queue] ${queueProvider.name} provider initialized`);
|
|
73
|
+
return queueProvider;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Close and reset the queue provider (for testing or shutdown)
|
|
77
|
+
*/
|
|
78
|
+
export async function closeQueueProvider() {
|
|
79
|
+
if (queueProvider) {
|
|
80
|
+
await queueProvider.close();
|
|
81
|
+
queueProvider = null;
|
|
82
|
+
console.log('[Queue] Provider closed');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reset the queue provider singleton (for testing)
|
|
87
|
+
*/
|
|
88
|
+
export function resetQueueProvider() {
|
|
89
|
+
queueProvider = null;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/queue/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
/**
|
|
4
|
+
* In-memory queue provider for development and testing.
|
|
5
|
+
* Uses EventEmitter for instant notification when jobs are enqueued.
|
|
6
|
+
*/
|
|
7
|
+
export class MemoryQueueProvider {
|
|
8
|
+
name = 'memory';
|
|
9
|
+
jobs = new Map();
|
|
10
|
+
pendingQueue = []; // Job IDs in order
|
|
11
|
+
completedHistory = [];
|
|
12
|
+
maxHistorySize;
|
|
13
|
+
emitter;
|
|
14
|
+
deduplicationKeys = new Map(); // dedupKey -> jobId
|
|
15
|
+
closed = false;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.maxHistorySize = config.maxHistorySize ?? 1000;
|
|
18
|
+
this.emitter = new EventEmitter();
|
|
19
|
+
this.emitter.setMaxListeners(100); // Allow many concurrent receivers
|
|
20
|
+
}
|
|
21
|
+
async enqueue(type, payload, options) {
|
|
22
|
+
if (this.closed) {
|
|
23
|
+
throw new Error('Queue provider is closed');
|
|
24
|
+
}
|
|
25
|
+
// Check deduplication
|
|
26
|
+
if (options?.deduplicationKey) {
|
|
27
|
+
const existingJobId = this.deduplicationKeys.get(options.deduplicationKey);
|
|
28
|
+
if (existingJobId) {
|
|
29
|
+
const existingJob = this.jobs.get(existingJobId);
|
|
30
|
+
if (existingJob && !['completed', 'failed', 'dead'].includes(existingJob.status)) {
|
|
31
|
+
// Return existing job instead of creating duplicate
|
|
32
|
+
return existingJob;
|
|
33
|
+
}
|
|
34
|
+
// Clean up old dedup key if job is done
|
|
35
|
+
this.deduplicationKeys.delete(options.deduplicationKey);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const now = new Date();
|
|
39
|
+
const jobOptions = {
|
|
40
|
+
maxRetries: options?.maxRetries ?? 3,
|
|
41
|
+
timeout: options?.timeout ?? 30000,
|
|
42
|
+
priority: options?.priority ?? 0,
|
|
43
|
+
deduplicationKey: options?.deduplicationKey,
|
|
44
|
+
};
|
|
45
|
+
// Calculate when job becomes visible
|
|
46
|
+
let visibleAt = now;
|
|
47
|
+
let status = 'pending';
|
|
48
|
+
let scheduledFor;
|
|
49
|
+
if (options?.scheduledFor) {
|
|
50
|
+
visibleAt = options.scheduledFor;
|
|
51
|
+
scheduledFor = options.scheduledFor;
|
|
52
|
+
status = 'scheduled';
|
|
53
|
+
}
|
|
54
|
+
else if (options?.delay) {
|
|
55
|
+
visibleAt = new Date(now.getTime() + options.delay);
|
|
56
|
+
scheduledFor = visibleAt;
|
|
57
|
+
status = options.delay > 0 ? 'scheduled' : 'pending';
|
|
58
|
+
}
|
|
59
|
+
const job = {
|
|
60
|
+
id: randomUUID(),
|
|
61
|
+
type,
|
|
62
|
+
payload,
|
|
63
|
+
options: jobOptions,
|
|
64
|
+
status,
|
|
65
|
+
attempts: 0,
|
|
66
|
+
createdAt: now,
|
|
67
|
+
scheduledFor,
|
|
68
|
+
visibleAt,
|
|
69
|
+
};
|
|
70
|
+
this.jobs.set(job.id, job);
|
|
71
|
+
if (options?.deduplicationKey) {
|
|
72
|
+
this.deduplicationKeys.set(options.deduplicationKey, job.id);
|
|
73
|
+
}
|
|
74
|
+
// Add to pending queue based on visibility
|
|
75
|
+
if (status === 'pending') {
|
|
76
|
+
this.insertIntoQueue(job.id);
|
|
77
|
+
// Emit event to wake up any waiting receivers
|
|
78
|
+
this.emitter.emit('job');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Schedule for later
|
|
82
|
+
const delay = visibleAt.getTime() - now.getTime();
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
const j = this.jobs.get(job.id);
|
|
85
|
+
if (j && j.status === 'scheduled') {
|
|
86
|
+
j.status = 'pending';
|
|
87
|
+
this.insertIntoQueue(job.id);
|
|
88
|
+
this.emitter.emit('job');
|
|
89
|
+
}
|
|
90
|
+
}, delay);
|
|
91
|
+
}
|
|
92
|
+
// Return job without internal fields
|
|
93
|
+
return this.toPublicJob(job);
|
|
94
|
+
}
|
|
95
|
+
async receive(maxMessages = 1, waitTimeSeconds = 20) {
|
|
96
|
+
if (this.closed) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
const jobs = this.getVisibleJobs(maxMessages);
|
|
100
|
+
if (jobs.length > 0) {
|
|
101
|
+
return jobs;
|
|
102
|
+
}
|
|
103
|
+
// Wait for jobs to arrive
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const timeout = setTimeout(() => {
|
|
106
|
+
this.emitter.removeListener('job', onJob);
|
|
107
|
+
resolve([]);
|
|
108
|
+
}, waitTimeSeconds * 1000);
|
|
109
|
+
const onJob = () => {
|
|
110
|
+
const jobs = this.getVisibleJobs(maxMessages);
|
|
111
|
+
if (jobs.length > 0) {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
this.emitter.removeListener('job', onJob);
|
|
114
|
+
resolve(jobs);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
this.emitter.on('job', onJob);
|
|
118
|
+
// Check if closed while waiting
|
|
119
|
+
const onClose = () => {
|
|
120
|
+
clearTimeout(timeout);
|
|
121
|
+
this.emitter.removeListener('job', onJob);
|
|
122
|
+
this.emitter.removeListener('close', onClose);
|
|
123
|
+
resolve([]);
|
|
124
|
+
};
|
|
125
|
+
this.emitter.once('close', onClose);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
getVisibleJobs(maxMessages) {
|
|
129
|
+
const now = new Date();
|
|
130
|
+
const results = [];
|
|
131
|
+
const toRemove = [];
|
|
132
|
+
for (let i = 0; i < this.pendingQueue.length && results.length < maxMessages; i++) {
|
|
133
|
+
const jobId = this.pendingQueue[i];
|
|
134
|
+
const job = this.jobs.get(jobId);
|
|
135
|
+
if (!job) {
|
|
136
|
+
toRemove.push(i);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (job.status !== 'pending' || job.visibleAt > now) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// Mark as processing and create receipt handle
|
|
143
|
+
const receiptHandle = randomUUID();
|
|
144
|
+
job.status = 'processing';
|
|
145
|
+
job.receiptHandle = receiptHandle;
|
|
146
|
+
job.startedAt = now;
|
|
147
|
+
job.attempts += 1;
|
|
148
|
+
toRemove.push(i);
|
|
149
|
+
results.push({
|
|
150
|
+
...this.toPublicJob(job),
|
|
151
|
+
receiptHandle,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
// Remove processed jobs from queue (in reverse to maintain indices)
|
|
155
|
+
for (let i = toRemove.length - 1; i >= 0; i--) {
|
|
156
|
+
this.pendingQueue.splice(toRemove[i], 1);
|
|
157
|
+
}
|
|
158
|
+
return results;
|
|
159
|
+
}
|
|
160
|
+
async acknowledge(receiptHandle) {
|
|
161
|
+
const job = this.findByReceiptHandle(receiptHandle);
|
|
162
|
+
if (!job) {
|
|
163
|
+
throw new Error(`Job not found for receipt handle: ${receiptHandle}`);
|
|
164
|
+
}
|
|
165
|
+
job.status = 'completed';
|
|
166
|
+
job.completedAt = new Date();
|
|
167
|
+
delete job.receiptHandle;
|
|
168
|
+
// Add to completed history
|
|
169
|
+
this.completedHistory.push(job.id);
|
|
170
|
+
this.trimHistory();
|
|
171
|
+
// Clean up deduplication key
|
|
172
|
+
if (job.options.deduplicationKey) {
|
|
173
|
+
this.deduplicationKeys.delete(job.options.deduplicationKey);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async fail(receiptHandle, error) {
|
|
177
|
+
const job = this.findByReceiptHandle(receiptHandle);
|
|
178
|
+
if (!job) {
|
|
179
|
+
throw new Error(`Job not found for receipt handle: ${receiptHandle}`);
|
|
180
|
+
}
|
|
181
|
+
job.lastError = error.message;
|
|
182
|
+
delete job.receiptHandle;
|
|
183
|
+
if (job.attempts >= job.options.maxRetries) {
|
|
184
|
+
// No more retries, mark as dead
|
|
185
|
+
job.status = 'dead';
|
|
186
|
+
job.completedAt = new Date();
|
|
187
|
+
// Clean up deduplication key
|
|
188
|
+
if (job.options.deduplicationKey) {
|
|
189
|
+
this.deduplicationKeys.delete(job.options.deduplicationKey);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Requeue with exponential backoff
|
|
194
|
+
const backoffMs = Math.min(1000 * Math.pow(2, job.attempts), 60000);
|
|
195
|
+
job.status = 'pending';
|
|
196
|
+
job.visibleAt = new Date(Date.now() + backoffMs);
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
if (job.status === 'pending') {
|
|
199
|
+
this.insertIntoQueue(job.id);
|
|
200
|
+
this.emitter.emit('job');
|
|
201
|
+
}
|
|
202
|
+
}, backoffMs);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async getJob(jobId) {
|
|
206
|
+
const job = this.jobs.get(jobId);
|
|
207
|
+
return job ? this.toPublicJob(job) : null;
|
|
208
|
+
}
|
|
209
|
+
async getStats() {
|
|
210
|
+
let pending = 0;
|
|
211
|
+
let processing = 0;
|
|
212
|
+
let completed = 0;
|
|
213
|
+
let failed = 0;
|
|
214
|
+
let dead = 0;
|
|
215
|
+
let scheduled = 0;
|
|
216
|
+
for (const job of this.jobs.values()) {
|
|
217
|
+
switch (job.status) {
|
|
218
|
+
case 'pending':
|
|
219
|
+
pending++;
|
|
220
|
+
break;
|
|
221
|
+
case 'scheduled':
|
|
222
|
+
scheduled++;
|
|
223
|
+
break;
|
|
224
|
+
case 'processing':
|
|
225
|
+
processing++;
|
|
226
|
+
break;
|
|
227
|
+
case 'completed':
|
|
228
|
+
completed++;
|
|
229
|
+
break;
|
|
230
|
+
case 'failed':
|
|
231
|
+
failed++;
|
|
232
|
+
break;
|
|
233
|
+
case 'dead':
|
|
234
|
+
dead++;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { pending, processing, completed, failed, dead, scheduled };
|
|
239
|
+
}
|
|
240
|
+
async close() {
|
|
241
|
+
this.closed = true;
|
|
242
|
+
this.emitter.emit('close');
|
|
243
|
+
this.emitter.removeAllListeners();
|
|
244
|
+
}
|
|
245
|
+
findByReceiptHandle(receiptHandle) {
|
|
246
|
+
for (const job of this.jobs.values()) {
|
|
247
|
+
if (job.receiptHandle === receiptHandle) {
|
|
248
|
+
return job;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
insertIntoQueue(jobId) {
|
|
254
|
+
const job = this.jobs.get(jobId);
|
|
255
|
+
if (!job)
|
|
256
|
+
return;
|
|
257
|
+
// Insert by priority (lower = higher priority)
|
|
258
|
+
let insertIndex = this.pendingQueue.length;
|
|
259
|
+
for (let i = 0; i < this.pendingQueue.length; i++) {
|
|
260
|
+
const existingJob = this.jobs.get(this.pendingQueue[i]);
|
|
261
|
+
if (existingJob && job.options.priority < existingJob.options.priority) {
|
|
262
|
+
insertIndex = i;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this.pendingQueue.splice(insertIndex, 0, jobId);
|
|
267
|
+
}
|
|
268
|
+
trimHistory() {
|
|
269
|
+
// Remove oldest completed jobs if we exceed maxHistorySize
|
|
270
|
+
while (this.completedHistory.length > this.maxHistorySize) {
|
|
271
|
+
const oldJobId = this.completedHistory.shift();
|
|
272
|
+
if (oldJobId) {
|
|
273
|
+
const job = this.jobs.get(oldJobId);
|
|
274
|
+
if (job && job.status === 'completed') {
|
|
275
|
+
this.jobs.delete(oldJobId);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
toPublicJob(job) {
|
|
281
|
+
return {
|
|
282
|
+
id: job.id,
|
|
283
|
+
type: job.type,
|
|
284
|
+
payload: job.payload,
|
|
285
|
+
options: job.options,
|
|
286
|
+
status: job.status,
|
|
287
|
+
attempts: job.attempts,
|
|
288
|
+
createdAt: job.createdAt,
|
|
289
|
+
scheduledFor: job.scheduledFor,
|
|
290
|
+
startedAt: job.startedAt,
|
|
291
|
+
completedAt: job.completedAt,
|
|
292
|
+
lastError: job.lastError,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=memory.js.map
|