profclaw 0.0.1-beta.1 → 0.0.1-beta.2
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/README.md +1 -1
- package/dist/adapters/openclaw.js +1 -1
- package/dist/agents/executor.js +38 -6
- package/dist/auth/auth-service.js +8 -6
- package/dist/auth/device-identity.js +7 -5
- package/dist/auth/github-oauth.js +3 -1
- package/dist/auth/pairing-codes.js +6 -4
- package/dist/browser/service.js +5 -3
- package/dist/chat/execution/guardrails.js +3 -1
- package/dist/chat/execution/workflows/executor.js +4 -2
- package/dist/chat/memory.js +3 -1
- package/dist/chat/proactive/index.js +13 -11
- package/dist/chat/tools.js +8 -7
- package/dist/cli/commands/auth.js +22 -13
- package/dist/cli/commands/onboard.js +248 -59
- package/dist/cli/commands/setup.js +180 -104
- package/dist/cron/issue-poller.js +1 -1
- package/dist/hooks/agent-webhook.js +10 -7
- package/dist/hooks/prompt-submit.js +5 -6
- package/dist/hooks/session-end.js +10 -6
- package/dist/hooks/tool-use.js +9 -6
- package/dist/integrations/github-ticket-sync.js +8 -6
- package/dist/integrations/github.js +8 -6
- package/dist/integrations/jira.js +5 -3
- package/dist/mcp/server.js +12 -9
- package/dist/memory/experience-store.js +15 -14
- package/dist/notifications/slack.js +4 -2
- package/dist/providers/types.d.ts +2 -2
- package/dist/queue/failure-handler.js +15 -13
- package/dist/queue/index.js +6 -4
- package/dist/queue/memory-queue.js +23 -27
- package/dist/queue/task-queue.js +18 -22
- package/dist/routes/agents.js +3 -1
- package/dist/routes/auth.js +11 -9
- package/dist/routes/devices.js +17 -15
- package/dist/routes/gateway.js +3 -1
- package/dist/routes/hooks.js +7 -5
- package/dist/routes/import.js +10 -8
- package/dist/routes/labels.js +11 -9
- package/dist/routes/oobe.js +8 -6
- package/dist/routes/openapi.js +1 -1
- package/dist/routes/projects.js +27 -25
- package/dist/routes/settings.js +7 -5
- package/dist/routes/setup.js +12 -11
- package/dist/routes/slack.js +28 -27
- package/dist/routes/stats.js +6 -4
- package/dist/routes/summaries.js +4 -3
- package/dist/routes/sync.js +7 -5
- package/dist/routes/tasks.js +10 -8
- package/dist/routes/tickets.js +26 -26
- package/dist/routes/tokens.js +5 -3
- package/dist/routes/users.js +14 -12
- package/dist/routes/webhooks.js +5 -3
- package/dist/settings/index.js +3 -1
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.js +9 -4
- package/dist/storage/libsql.js +29 -21
- package/dist/storage/schema.d.ts +107 -46
- package/dist/summaries/storage.js +3 -1
- package/dist/tickets/index.js +10 -8
- package/dist/utils/circuit-breaker.js +10 -5
- package/dist/utils/logger.d.ts +5 -1
- package/dist/utils/logger.js +23 -7
- package/package.json +4 -3
- package/ui/dist/assets/{ActivityView-CkPgKhwm.js → ActivityView-DIPQSJ_d.js} +1 -1
- package/ui/dist/assets/{AgentList-C3SVdeEz.js → AgentList-CqyaYrcq.js} +1 -1
- package/ui/dist/assets/{AnalyticsDashboard-DVU--vlP.js → AnalyticsDashboard-C-mjuGHT.js} +1 -1
- package/ui/dist/assets/{AreaChart-B5qMEGWj.js → AreaChart-CfYWmbtr.js} +1 -1
- package/ui/dist/assets/{CartesianChart-B50bcUJi.js → CartesianChart-BaQ3It0c.js} +1 -1
- package/ui/dist/assets/{ChatView-BZFJs7w_.js → ChatView-DxxQy6pl.js} +1 -1
- package/ui/dist/assets/CostsDashboard-CDBBcGkg.js +1 -0
- package/ui/dist/assets/{EstimateSelect-CLpB4NQO.js → EstimateSelect-CDjf5qKl.js} +1 -1
- package/ui/dist/assets/{FloatingChatbot-CBzBY9a0.js → FloatingChatbot-CZHjvJIe.js} +1 -1
- package/ui/dist/assets/{InviteCodeManagement-DsbdPa6W.js → InviteCodeManagement-DTm-IPTm.js} +1 -1
- package/ui/dist/assets/{ProjectDetail-BEM3tFXg.js → ProjectDetail-DlYUcOss.js} +1 -1
- package/ui/dist/assets/{ProjectIcon-CN1FmFAk.js → ProjectIcon-BMRpnM04.js} +1 -1
- package/ui/dist/assets/{ProjectList-DSDaNhxQ.js → ProjectList-D85TN0i0.js} +1 -1
- package/ui/dist/assets/{Settings-dPVcqC_d.js → Settings-CgPqzUvj.js} +3 -3
- package/ui/dist/assets/{StatusIndicator-DA3NAo_b.js → StatusIndicator-yig0uZEA.js} +1 -1
- package/ui/dist/assets/{SummaryDetail-Bjnf9lT8.js → SummaryDetail-CwLAfHgJ.js} +1 -1
- package/ui/dist/assets/{SummaryList-ENwKDBKW.js → SummaryList-BvS5Idnu.js} +1 -1
- package/ui/dist/assets/{TaskDetail-DnwG6MzB.js → TaskDetail-zarC1fOt.js} +1 -1
- package/ui/dist/assets/{TaskList-D9Ug_HiV.js → TaskList-CyJq3ANE.js} +1 -1
- package/ui/dist/assets/{TicketBoard-CviH1571.js → TicketBoard-BlWr3-iH.js} +1 -1
- package/ui/dist/assets/{TicketDetail-8Jk8O-33.js → TicketDetail-DmqWKj2W.js} +1 -1
- package/ui/dist/assets/{TicketList-B-iV4rRA.js → TicketList-Scj4pMWa.js} +1 -1
- package/ui/dist/assets/{UserManagement-CPZ8s7s2.js → UserManagement-BH7uG7dw.js} +1 -1
- package/ui/dist/assets/{ViewSwitcher-BUyOOWDI.js → ViewSwitcher-3HzZUdWn.js} +1 -1
- package/ui/dist/assets/{alert-dialog-BOmnTkN-.js → alert-dialog-DWPb2IfQ.js} +1 -1
- package/ui/dist/assets/{archive-D5my9zOG.js → archive-CGck7gxR.js} +1 -1
- package/ui/dist/assets/{badge-B4ov81_m.js → badge-DEmc3Z_l.js} +1 -1
- package/ui/dist/assets/{brain-C53ElFLD.js → brain-CfFdLVqb.js} +1 -1
- package/ui/dist/assets/{bug-D0TJoCYn.js → bug-DqSGPUUE.js} +1 -1
- package/ui/dist/assets/{calendar-BkWIhKa1.js → calendar-BuZ4zbPW.js} +1 -1
- package/ui/dist/assets/{chevron-up-BRTtndUZ.js → chevron-up-DcJ4LL_N.js} +1 -1
- package/ui/dist/assets/{circle-x-BN89YhbS.js → circle-x-C78PNZjF.js} +1 -1
- package/ui/dist/assets/{constants-fc7TFI7u.js → constants-DhDjZe4P.js} +1 -1
- package/ui/dist/assets/{cpu-B41x1-vV.js → cpu-Ck6AoG8c.js} +1 -1
- package/ui/dist/assets/{database-D9Cnfmzj.js → database-CA2w8Lvt.js} +1 -1
- package/ui/dist/assets/{dialog-DrWB4EYD.js → dialog-BOdGWhgE.js} +1 -1
- package/ui/dist/assets/{ellipsis-NuUWekvu.js → ellipsis-FhHKRbfl.js} +1 -1
- package/ui/dist/assets/{ellipsis-vertical-BueMfuCw.js → ellipsis-vertical-CO0UqE4-.js} +1 -1
- package/ui/dist/assets/{file-code-HR9829Jc.js → file-code-ChnhYYj1.js} +1 -1
- package/ui/dist/assets/{folder-open-C_7oYEqe.js → folder-open-CHwA1-ve.js} +1 -1
- package/ui/dist/assets/{funnel-B5gmNXGf.js → funnel-ByEjRFNB.js} +1 -1
- package/ui/dist/assets/{git-branch-BRMiJmV3.js → git-branch-GTOPQgU4.js} +1 -1
- package/ui/dist/assets/{git-commit-horizontal-CXOUQeJI.js → git-commit-horizontal-F5M84Kut.js} +1 -1
- package/ui/dist/assets/{globe-CCspjvus.js → globe-CQWfr7hT.js} +1 -1
- package/ui/dist/assets/{hash-g3102kPC.js → hash-nKC9gtLg.js} +1 -1
- package/ui/dist/assets/{history-CNqcBBrV.js → history-DfFSA9xJ.js} +1 -1
- package/ui/dist/assets/{index-kb4AJXUB.js → index--JkIxVXb.js} +3 -3
- package/ui/dist/assets/index-Ca3vk7Gq.js +1 -0
- package/ui/dist/assets/{index-C9jNQXzL.js → index-DHGvP9mp.js} +1 -1
- package/ui/dist/assets/index-DeI8lKRR.js +1 -0
- package/ui/dist/assets/{index-DURTouJI.js → index-Z4-RtZcs.js} +1 -1
- package/ui/dist/assets/{index-CEZ6R2Uw.js → index-a4Fxq0Hb.js} +1 -1
- package/ui/dist/assets/{layers-CLXiHN6o.js → layers-DFeLExSa.js} +1 -1
- package/ui/dist/assets/{layout-dashboard-B07MFhBs.js → layout-dashboard-WmgjMhWF.js} +1 -1
- package/ui/dist/assets/{lightbulb-B2dgG5ks.js → lightbulb-Bo8hEr7B.js} +1 -1
- package/ui/dist/assets/{link-2-BxNmr9hL.js → link-2-YqrpQXBH.js} +1 -1
- package/ui/dist/assets/{markdown-renderer-CjU6hBcW.js → markdown-renderer-Croje6Xe.js} +1 -1
- package/ui/dist/assets/{message-square-Bf2-CVbt.js → message-square-CD0H5hot.js} +1 -1
- package/ui/dist/assets/{pencil-D096Ey62.js → pencil-DpFnj5dn.js} +1 -1
- package/ui/dist/assets/{play-DfdcSI3x.js → play-DVgea_jT.js} +1 -1
- package/ui/dist/assets/{popover-BV0QJDQq.js → popover-JXecfWG5.js} +1 -1
- package/ui/dist/assets/{progress-DjPEDJfI.js → progress-C1ghf1Fu.js} +1 -1
- package/ui/dist/assets/{rocket-CPsTc-5G.js → rocket-BFxPYGx9.js} +1 -1
- package/ui/dist/assets/{select-LsO16slr.js → select-MADP8m6Q.js} +1 -1
- package/ui/dist/assets/{send-BDYBZDUZ.js → send-eYnW_7n-.js} +1 -1
- package/ui/dist/assets/{server-B3k6noce.js → server-CPMaysH8.js} +1 -1
- package/ui/dist/assets/{settings-2-Cu4xTjhG.js → settings-2-scXUOuE_.js} +1 -1
- package/ui/dist/assets/{sheet-BScLnPKX.js → sheet-r6NSOxi-.js} +1 -1
- package/ui/dist/assets/{shield-check-CQqkwufI.js → shield-check-PEr7nmVw.js} +1 -1
- package/ui/dist/assets/{shield-off-ZFruH9KO.js → shield-off-CLtXio62.js} +1 -1
- package/ui/dist/assets/{skeleton-CtkIA_MU.js → skeleton-C9oEzNlU.js} +1 -1
- package/ui/dist/assets/{smartphone-DnoksVkc.js → smartphone-DusFfY0U.js} +1 -1
- package/ui/dist/assets/{square-uD9ZHMfW.js → square-e1yxB2eR.js} +1 -1
- package/ui/dist/assets/{switch-MQPnwGJK.js → switch-tNs0n-kz.js} +1 -1
- package/ui/dist/assets/{table-B8cqlywA.js → table-CXxtLicg.js} +1 -1
- package/ui/dist/assets/{tag-wEJQRWlV.js → tag-tAwMmQ9G.js} +1 -1
- package/ui/dist/assets/{textarea-D5LJCOGl.js → textarea-BjiEeIWh.js} +1 -1
- package/ui/dist/assets/{timer-mbGHegmr.js → timer-Vel1N3RP.js} +1 -1
- package/ui/dist/assets/{tooltip-Df1CunSp.js → tooltip-ZMwtfr1J.js} +1 -1
- package/ui/dist/assets/{trash-2-B4oAXsX-.js → trash-2-eM-CNwsZ.js} +1 -1
- package/ui/dist/assets/{trending-up-BGWxvlmZ.js → trending-up-BGafmjRo.js} +1 -1
- package/ui/dist/assets/{wand-sparkles-CZy4xvL3.js → wand-sparkles-DPvcd9Pi.js} +1 -1
- package/ui/dist/assets/{wrench-ByZkjO6m.js → wrench-B8s3zYVV.js} +1 -1
- package/ui/dist/index.html +1 -1
- package/ui/dist/assets/CostsDashboard-ChfH8BlG.js +0 -1
- package/ui/dist/assets/index-27mTPKuh.js +0 -1
- package/ui/dist/assets/index-BMx3fH-e.js +0 -1
|
@@ -9,6 +9,8 @@ import { randomUUID } from 'crypto';
|
|
|
9
9
|
import { getDb } from '../storage/index.js';
|
|
10
10
|
import { tickets, ticketExternalLinks, ticketComments, ticketHistory, githubCommentSyncs, } from '../storage/schema.js';
|
|
11
11
|
import { findOrCreateLabel, setTicketLabels } from '../labels/index.js';
|
|
12
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
13
|
+
const log = createContextualLogger('GitHubSync');
|
|
12
14
|
function isGitHubSyncWebhookPayload(payload) {
|
|
13
15
|
return typeof payload === 'object' && payload !== null;
|
|
14
16
|
}
|
|
@@ -181,7 +183,7 @@ export async function handleIssueEvent(action, issue, repository) {
|
|
|
181
183
|
await setTicketLabels(ticketId, labelIds);
|
|
182
184
|
}
|
|
183
185
|
catch (e) {
|
|
184
|
-
|
|
186
|
+
log.warn('Failed to sync labels', { error: e instanceof Error ? e.message : String(e) });
|
|
185
187
|
}
|
|
186
188
|
}
|
|
187
189
|
// Update last synced timestamp
|
|
@@ -189,7 +191,7 @@ export async function handleIssueEvent(action, issue, repository) {
|
|
|
189
191
|
lastSyncedAt: now,
|
|
190
192
|
syncError: null,
|
|
191
193
|
}).where(eq(ticketExternalLinks.ticketId, ticketId));
|
|
192
|
-
|
|
194
|
+
log.info('Updated ticket from issue', { ticketId, issueNumber: issue.number, fields: changes.map(c => c.field) });
|
|
193
195
|
return { action: 'updated', ticketId };
|
|
194
196
|
}
|
|
195
197
|
/**
|
|
@@ -250,7 +252,7 @@ export async function handleCommentCreated(issue, comment, repository) {
|
|
|
250
252
|
githubCommentId: comment.id,
|
|
251
253
|
createdAt: now,
|
|
252
254
|
});
|
|
253
|
-
|
|
255
|
+
log.info('Created comment from GitHub', { commentId, githubCommentId: comment.id });
|
|
254
256
|
return { action: 'comment_created', ticketId, commentId };
|
|
255
257
|
}
|
|
256
258
|
/**
|
|
@@ -279,7 +281,7 @@ export async function handleCommentEdited(issue, comment, repository) {
|
|
|
279
281
|
content: comment.body,
|
|
280
282
|
updatedAt: new Date(),
|
|
281
283
|
}).where(eq(ticketComments.id, syncRecord[0].commentId));
|
|
282
|
-
|
|
284
|
+
log.info('Updated comment from GitHub', { commentId: syncRecord[0].commentId, githubCommentId: comment.id });
|
|
283
285
|
return { action: 'comment_updated', ticketId: synced.ticketId, commentId: syncRecord[0].commentId };
|
|
284
286
|
}
|
|
285
287
|
/**
|
|
@@ -305,7 +307,7 @@ export async function handleCommentDeleted(issue, comment, repository) {
|
|
|
305
307
|
// Delete sync record and comment
|
|
306
308
|
await db.delete(githubCommentSyncs).where(eq(githubCommentSyncs.id, syncRecord[0].id));
|
|
307
309
|
await db.delete(ticketComments).where(eq(ticketComments.id, syncRecord[0].commentId));
|
|
308
|
-
|
|
310
|
+
log.info('Deleted comment', { commentId: syncRecord[0].commentId });
|
|
309
311
|
return { action: 'comment_deleted', ticketId: synced.ticketId };
|
|
310
312
|
}
|
|
311
313
|
// MAIN WEBHOOK HANDLER
|
|
@@ -345,7 +347,7 @@ export async function processGitHubWebhookForTicketSync(event, payload) {
|
|
|
345
347
|
return { action: 'ignored', reason: `unhandled_event: ${event}` };
|
|
346
348
|
}
|
|
347
349
|
catch (error) {
|
|
348
|
-
|
|
350
|
+
log.error('Error processing webhook', error instanceof Error ? error : new Error(String(error)));
|
|
349
351
|
throw error;
|
|
350
352
|
}
|
|
351
353
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
3
|
+
const log = createContextualLogger('GitHub');
|
|
2
4
|
/**
|
|
3
5
|
* GitHub Webhook Integration
|
|
4
6
|
*
|
|
@@ -20,7 +22,7 @@ const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET || '';
|
|
|
20
22
|
*/
|
|
21
23
|
export function verifyGitHubSignature(payload, signature) {
|
|
22
24
|
if (!WEBHOOK_SECRET) {
|
|
23
|
-
|
|
25
|
+
log.warn('GITHUB_WEBHOOK_SECRET not set - skipping signature verification');
|
|
24
26
|
return true;
|
|
25
27
|
}
|
|
26
28
|
if (!signature) {
|
|
@@ -49,10 +51,10 @@ export async function handleGitHubWebhook(c) {
|
|
|
49
51
|
throw new Error('Invalid webhook signature');
|
|
50
52
|
}
|
|
51
53
|
const payload = JSON.parse(rawBody);
|
|
52
|
-
|
|
54
|
+
log.info('GitHub webhook received', { event, deliveryId });
|
|
53
55
|
switch (event) {
|
|
54
56
|
case 'ping':
|
|
55
|
-
|
|
57
|
+
log.info('GitHub ping received');
|
|
56
58
|
return null;
|
|
57
59
|
case 'issues':
|
|
58
60
|
return handleIssueEvent(payload);
|
|
@@ -61,7 +63,7 @@ export async function handleGitHubWebhook(c) {
|
|
|
61
63
|
case 'pull_request':
|
|
62
64
|
return handlePullRequestEvent(payload);
|
|
63
65
|
default:
|
|
64
|
-
|
|
66
|
+
log.info('Unhandled event', { event });
|
|
65
67
|
return null;
|
|
66
68
|
}
|
|
67
69
|
}
|
|
@@ -75,7 +77,7 @@ function handleIssueEvent(payload) {
|
|
|
75
77
|
// Check if issue has the AI task label
|
|
76
78
|
const hasAiLabel = issue.labels?.some((l) => l.name === AI_TASK_LABEL || l.name === AI_REVIEW_LABEL);
|
|
77
79
|
if (!hasAiLabel) {
|
|
78
|
-
|
|
80
|
+
log.info('Issue opened without AI label, skipping', { issueNumber: issue.number });
|
|
79
81
|
return null;
|
|
80
82
|
}
|
|
81
83
|
}
|
|
@@ -121,7 +123,7 @@ function handleIssueCommentEvent(payload) {
|
|
|
121
123
|
return null;
|
|
122
124
|
}
|
|
123
125
|
const prompt = triggerMatch[1].trim();
|
|
124
|
-
|
|
126
|
+
log.info('AI trigger command received', { issueNumber: issue.number, promptPreview: prompt.substring(0, 50) });
|
|
125
127
|
return {
|
|
126
128
|
title: `AI Task: ${issue.title}`,
|
|
127
129
|
description: `Triggered by @${comment.user.login} in issue #${issue.number}`,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
2
|
+
const log = createContextualLogger('Jira');
|
|
1
3
|
/**
|
|
2
4
|
* Jira Webhook Integration
|
|
3
5
|
*
|
|
@@ -16,7 +18,7 @@ const WEBHOOK_SECRET = process.env.JIRA_WEBHOOK_SECRET || '';
|
|
|
16
18
|
*/
|
|
17
19
|
export function verifyJiraToken(c) {
|
|
18
20
|
if (!WEBHOOK_SECRET) {
|
|
19
|
-
|
|
21
|
+
log.warn('JIRA_WEBHOOK_SECRET not set - skipping verification');
|
|
20
22
|
return true;
|
|
21
23
|
}
|
|
22
24
|
const token = c.req.query('token');
|
|
@@ -33,7 +35,7 @@ export async function handleJiraWebhook(c) {
|
|
|
33
35
|
const rawBody = await c.req.text();
|
|
34
36
|
const payload = JSON.parse(rawBody);
|
|
35
37
|
const { webhookEvent, issue } = payload;
|
|
36
|
-
|
|
38
|
+
log.info('Jira webhook received', { webhookEvent, issueKey: issue.key });
|
|
37
39
|
// Only handle created or updated events
|
|
38
40
|
if (webhookEvent !== 'jira:issue_created' && webhookEvent !== 'jira:issue_updated') {
|
|
39
41
|
return null;
|
|
@@ -41,7 +43,7 @@ export async function handleJiraWebhook(c) {
|
|
|
41
43
|
// Check for AI label
|
|
42
44
|
const hasAiLabel = issue.fields.labels?.includes(JIRA_AI_TASK_LABEL);
|
|
43
45
|
if (!hasAiLabel) {
|
|
44
|
-
|
|
46
|
+
log.info('Issue does not have AI label, skipping', { issueKey: issue.key });
|
|
45
47
|
return null;
|
|
46
48
|
}
|
|
47
49
|
// Construct source URL (convert API URL to browser URL)
|
package/dist/mcp/server.js
CHANGED
|
@@ -24,6 +24,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
24
24
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
25
|
import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
26
26
|
import { BROWSER_TOOLS, handleBrowserTool } from './browser-tools.js';
|
|
27
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
28
|
+
const log = createContextualLogger('MCPServer', { stream: 'stderr' });
|
|
27
29
|
// MCP Server configuration
|
|
28
30
|
const PROFCLAW_API_URL = process.env.PROFCLAW_API_URL || process.env.PROFCLAW_API_URL || 'http://localhost:3000';
|
|
29
31
|
const sessionState = {
|
|
@@ -405,12 +407,12 @@ export async function handleLogTask(args) {
|
|
|
405
407
|
}),
|
|
406
408
|
});
|
|
407
409
|
if (!response.ok) {
|
|
408
|
-
|
|
410
|
+
log.error('[MCP] Failed to report to profClaw', { statusText: response.statusText });
|
|
409
411
|
}
|
|
410
412
|
}
|
|
411
413
|
catch (error) {
|
|
412
414
|
// Silently fail - don't block the agent
|
|
413
|
-
|
|
415
|
+
log.error('[MCP] Failed to connect to profClaw', error instanceof Error ? error : { error });
|
|
414
416
|
}
|
|
415
417
|
return {
|
|
416
418
|
content: [
|
|
@@ -447,11 +449,11 @@ export async function handleCompleteTask(args) {
|
|
|
447
449
|
}),
|
|
448
450
|
});
|
|
449
451
|
if (!response.ok) {
|
|
450
|
-
|
|
452
|
+
log.error('[MCP] Failed to report completion to profClaw', { statusText: response.statusText });
|
|
451
453
|
}
|
|
452
454
|
}
|
|
453
455
|
catch (error) {
|
|
454
|
-
|
|
456
|
+
log.error('[MCP] Failed to connect to profClaw', error instanceof Error ? error : { error });
|
|
455
457
|
}
|
|
456
458
|
// Clear current task
|
|
457
459
|
sessionState.currentTask = undefined;
|
|
@@ -510,7 +512,7 @@ async function handleGetContext(args) {
|
|
|
510
512
|
}
|
|
511
513
|
}
|
|
512
514
|
catch (error) {
|
|
513
|
-
|
|
515
|
+
log.error('[MCP] Failed to fetch context from profClaw', error instanceof Error ? error : { error });
|
|
514
516
|
}
|
|
515
517
|
// Fallback if API unavailable
|
|
516
518
|
return {
|
|
@@ -815,12 +817,13 @@ async function main() {
|
|
|
815
817
|
const transport = new StdioServerTransport();
|
|
816
818
|
await server.connect(transport);
|
|
817
819
|
// Log startup to stderr (stdout is for MCP protocol)
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
820
|
+
log.info('[profClaw MCP] Server started', {
|
|
821
|
+
apiUrl: PROFCLAW_API_URL,
|
|
822
|
+
sessionId: sessionState.sessionId,
|
|
823
|
+
});
|
|
821
824
|
}
|
|
822
825
|
main().catch((error) => {
|
|
823
|
-
|
|
826
|
+
log.error('[profClaw MCP] Fatal error', error instanceof Error ? error : { error });
|
|
824
827
|
process.exit(1);
|
|
825
828
|
});
|
|
826
829
|
//# sourceMappingURL=server.js.map
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { randomUUID } from 'node:crypto';
|
|
12
12
|
import { getClient } from '../storage/index.js';
|
|
13
|
-
import {
|
|
13
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
14
|
+
const log = createContextualLogger('ExperienceStore');
|
|
14
15
|
// Table Initialization
|
|
15
16
|
/**
|
|
16
17
|
* Initialize the experiences table and indexes.
|
|
@@ -90,7 +91,7 @@ export async function recordExperience(exp) {
|
|
|
90
91
|
return id;
|
|
91
92
|
}
|
|
92
93
|
catch (error) {
|
|
93
|
-
|
|
94
|
+
log.error('Failed to record experience', error instanceof Error ? error : new Error(String(error)));
|
|
94
95
|
throw error;
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -109,7 +110,7 @@ export async function getExperience(id) {
|
|
|
109
110
|
return rowToExperience(result.rows[0]);
|
|
110
111
|
}
|
|
111
112
|
catch (error) {
|
|
112
|
-
|
|
113
|
+
log.error('Failed to get experience', error instanceof Error ? error : new Error(String(error)));
|
|
113
114
|
return null;
|
|
114
115
|
}
|
|
115
116
|
}
|
|
@@ -129,7 +130,7 @@ export async function markUsed(id) {
|
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
132
|
catch (error) {
|
|
132
|
-
|
|
133
|
+
log.error('Failed to mark experience used', error instanceof Error ? error : new Error(String(error)));
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
/**
|
|
@@ -145,7 +146,7 @@ export async function deleteExperience(id) {
|
|
|
145
146
|
return true;
|
|
146
147
|
}
|
|
147
148
|
catch (error) {
|
|
148
|
-
|
|
149
|
+
log.error('Failed to delete experience', error instanceof Error ? error : new Error(String(error)));
|
|
149
150
|
return false;
|
|
150
151
|
}
|
|
151
152
|
}
|
|
@@ -190,7 +191,7 @@ export async function listExperiences(options = {}) {
|
|
|
190
191
|
};
|
|
191
192
|
}
|
|
192
193
|
catch (error) {
|
|
193
|
-
|
|
194
|
+
log.error('Failed to list experiences', error instanceof Error ? error : new Error(String(error)));
|
|
194
195
|
return { experiences: [], total: 0 };
|
|
195
196
|
}
|
|
196
197
|
}
|
|
@@ -246,7 +247,7 @@ export async function findSimilarExperiences(intent, tags, limit = 10) {
|
|
|
246
247
|
return results;
|
|
247
248
|
}
|
|
248
249
|
catch (error) {
|
|
249
|
-
|
|
250
|
+
log.error('Similarity search failed', error instanceof Error ? error : new Error(String(error)));
|
|
250
251
|
return [];
|
|
251
252
|
}
|
|
252
253
|
}
|
|
@@ -306,7 +307,7 @@ export async function trackPreference(userId, category, value) {
|
|
|
306
307
|
}
|
|
307
308
|
}
|
|
308
309
|
catch (error) {
|
|
309
|
-
|
|
310
|
+
log.error('Failed to track preference', error instanceof Error ? error : new Error(String(error)));
|
|
310
311
|
}
|
|
311
312
|
}
|
|
312
313
|
/**
|
|
@@ -334,7 +335,7 @@ export async function getUserPreferences(userId) {
|
|
|
334
335
|
return grouped;
|
|
335
336
|
}
|
|
336
337
|
catch (error) {
|
|
337
|
-
|
|
338
|
+
log.error('Failed to get user preferences', error instanceof Error ? error : new Error(String(error)));
|
|
338
339
|
return {};
|
|
339
340
|
}
|
|
340
341
|
}
|
|
@@ -369,11 +370,11 @@ export async function applyDecay(halfLifeDays = DEFAULT_HALF_LIFE_DAYS) {
|
|
|
369
370
|
});
|
|
370
371
|
updated++;
|
|
371
372
|
}
|
|
372
|
-
|
|
373
|
+
log.info('Decay applied', { count: updated, halfLifeDays });
|
|
373
374
|
return updated;
|
|
374
375
|
}
|
|
375
376
|
catch (error) {
|
|
376
|
-
|
|
377
|
+
log.error('Decay calculation failed', error instanceof Error ? error : new Error(String(error)));
|
|
377
378
|
return 0;
|
|
378
379
|
}
|
|
379
380
|
}
|
|
@@ -394,12 +395,12 @@ export async function pruneExpired(minWeight = 0.05) {
|
|
|
394
395
|
sql: `DELETE FROM experiences WHERE weight < ?`,
|
|
395
396
|
args: [minWeight],
|
|
396
397
|
});
|
|
397
|
-
|
|
398
|
+
log.info('Pruned expired experiences', { count, minWeight });
|
|
398
399
|
}
|
|
399
400
|
return count;
|
|
400
401
|
}
|
|
401
402
|
catch (error) {
|
|
402
|
-
|
|
403
|
+
log.error('Prune failed', error instanceof Error ? error : new Error(String(error)));
|
|
403
404
|
return 0;
|
|
404
405
|
}
|
|
405
406
|
}
|
|
@@ -432,7 +433,7 @@ export async function getStats() {
|
|
|
432
433
|
return { total, byType, avgAge: Math.round(avgAge * 10) / 10 };
|
|
433
434
|
}
|
|
434
435
|
catch (error) {
|
|
435
|
-
|
|
436
|
+
log.error('Failed to get stats', error instanceof Error ? error : new Error(String(error)));
|
|
436
437
|
return { total: 0, byType: {}, avgAge: 0 };
|
|
437
438
|
}
|
|
438
439
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Sends notifications to Slack via incoming webhooks.
|
|
5
5
|
*/
|
|
6
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
7
|
+
const log = createContextualLogger('SlackNotify');
|
|
6
8
|
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL || '';
|
|
7
9
|
const SLACK_CHANNEL = process.env.SLACK_CHANNEL || '';
|
|
8
10
|
const NOTIFY_ON_START = process.env.SLACK_NOTIFY_ON_START === 'true';
|
|
@@ -84,11 +86,11 @@ export async function sendSlackNotification(notification) {
|
|
|
84
86
|
body: JSON.stringify(payload),
|
|
85
87
|
});
|
|
86
88
|
if (!response.ok) {
|
|
87
|
-
|
|
89
|
+
log.error('Failed to send notification', new Error(`HTTP ${response.status}`));
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
catch (error) {
|
|
91
|
-
|
|
93
|
+
log.error('Error sending notification', error instanceof Error ? error : new Error(String(error)));
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
/**
|
|
@@ -306,6 +306,7 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
|
|
|
306
306
|
ticketId: z.ZodOptional<z.ZodString>;
|
|
307
307
|
taskId: z.ZodOptional<z.ZodString>;
|
|
308
308
|
}, "strip", z.ZodTypeAny, {
|
|
309
|
+
stream: boolean;
|
|
309
310
|
messages: {
|
|
310
311
|
id: string;
|
|
311
312
|
timestamp: string;
|
|
@@ -317,7 +318,6 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
|
|
|
317
318
|
imageUrls?: string[] | undefined;
|
|
318
319
|
}[];
|
|
319
320
|
temperature: number;
|
|
320
|
-
stream: boolean;
|
|
321
321
|
taskId?: string | undefined;
|
|
322
322
|
model?: string | undefined;
|
|
323
323
|
ticketId?: string | undefined;
|
|
@@ -342,6 +342,7 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
|
|
|
342
342
|
imageUrls?: string[] | undefined;
|
|
343
343
|
}[];
|
|
344
344
|
taskId?: string | undefined;
|
|
345
|
+
stream?: boolean | undefined;
|
|
345
346
|
model?: string | undefined;
|
|
346
347
|
ticketId?: string | undefined;
|
|
347
348
|
provider?: "custom" | "anthropic" | "openai" | "google" | "azure" | "ollama" | "openrouter" | "bedrock" | undefined;
|
|
@@ -354,7 +355,6 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
|
|
|
354
355
|
systemPrompt?: string | undefined;
|
|
355
356
|
temperature?: number | undefined;
|
|
356
357
|
maxTokens?: number | undefined;
|
|
357
|
-
stream?: boolean | undefined;
|
|
358
358
|
}>;
|
|
359
359
|
export type ChatCompletionRequest = z.infer<typeof ChatCompletionRequestSchema>;
|
|
360
360
|
export declare const ChatCompletionResponseSchema: z.ZodObject<{
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { sendSlackNotification } from '../notifications/slack.js';
|
|
10
10
|
import { getStorage } from '../storage/index.js';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
13
|
+
const log = createContextualLogger('FailureHandler');
|
|
12
14
|
// Reference to the task queue (set during init)
|
|
13
15
|
let taskQueueRef = null;
|
|
14
16
|
/**
|
|
@@ -69,12 +71,12 @@ export async function initDeadLetterQueue() {
|
|
|
69
71
|
* Handle a failed task
|
|
70
72
|
*/
|
|
71
73
|
export async function handleTaskFailure(task, error) {
|
|
72
|
-
|
|
74
|
+
log.error(`Task ${task.id} failed`, new Error(error.message));
|
|
73
75
|
task.attempts = (task.attempts || 0) + 1;
|
|
74
76
|
// Check if we should retry
|
|
75
77
|
if (task.attempts < task.maxAttempts) {
|
|
76
78
|
const delay = calculateBackoffWithJitter(task.attempts);
|
|
77
|
-
|
|
79
|
+
log.info('Scheduling retry', { taskId: task.id, attempt: task.attempts, maxAttempts: task.maxAttempts, delaySeconds: Math.round(delay / 1000) });
|
|
78
80
|
// Actually retry with BullMQ delay
|
|
79
81
|
if (taskQueueRef) {
|
|
80
82
|
await taskQueueRef.add(`retry-${task.id}-${task.attempts}`, { ...task, status: 'pending' }, {
|
|
@@ -82,10 +84,10 @@ export async function handleTaskFailure(task, error) {
|
|
|
82
84
|
priority: task.priority,
|
|
83
85
|
jobId: `${task.id}-retry-${task.attempts}`,
|
|
84
86
|
});
|
|
85
|
-
|
|
87
|
+
log.info('Task requeued with delay', { taskId: task.id, delayMs: delay });
|
|
86
88
|
}
|
|
87
89
|
else {
|
|
88
|
-
|
|
90
|
+
log.warn('Task queue not available, cannot retry');
|
|
89
91
|
}
|
|
90
92
|
await sendSlackNotification({
|
|
91
93
|
type: 'task_retry',
|
|
@@ -93,11 +95,11 @@ export async function handleTaskFailure(task, error) {
|
|
|
93
95
|
message: `Attempt ${task.attempts}/${task.maxAttempts} failed. Retrying in ${Math.round(delay / 60000)} minutes.\nError: ${error.message}`,
|
|
94
96
|
task,
|
|
95
97
|
severity: 'info',
|
|
96
|
-
}).catch(
|
|
98
|
+
}).catch((err) => log.error('Slack notification failed', err instanceof Error ? err : new Error(String(err))));
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
99
101
|
// Max retries reached - move to dead letter queue
|
|
100
|
-
|
|
102
|
+
log.error(`Task ${task.id} exceeded max retries, moving to dead letter queue`, new Error('MAX_RETRIES_EXCEEDED'));
|
|
101
103
|
task.status = 'failed';
|
|
102
104
|
task.result = {
|
|
103
105
|
success: false,
|
|
@@ -119,7 +121,7 @@ export async function handleTaskFailure(task, error) {
|
|
|
119
121
|
message: `Task failed after ${task.maxAttempts} attempts and moved to dead letter queue.\n\nLast error: ${error.message}\n\nSource: ${task.sourceUrl || 'N/A'}`,
|
|
120
122
|
task,
|
|
121
123
|
severity: 'error',
|
|
122
|
-
}).catch(
|
|
124
|
+
}).catch((err) => log.error('Slack notification failed', err instanceof Error ? err : new Error(String(err))));
|
|
123
125
|
}
|
|
124
126
|
/**
|
|
125
127
|
* Calculate exponential backoff with jitter
|
|
@@ -167,7 +169,7 @@ async function addToDeadLetterQueue(task, error) {
|
|
|
167
169
|
toUnixSeconds(task.createdAt),
|
|
168
170
|
toUnixSeconds(task.startedAt),
|
|
169
171
|
]);
|
|
170
|
-
|
|
172
|
+
log.info('Task persisted to DLQ', { taskId: task.id, dlqId: id });
|
|
171
173
|
}
|
|
172
174
|
/**
|
|
173
175
|
* Post failure comment on GitHub issue
|
|
@@ -216,7 +218,7 @@ This task has been moved to the dead letter queue for manual review. Please:
|
|
|
216
218
|
});
|
|
217
219
|
}
|
|
218
220
|
catch (err) {
|
|
219
|
-
|
|
221
|
+
log.error('Failed to post GitHub comment', err instanceof Error ? err : new Error(String(err)));
|
|
220
222
|
}
|
|
221
223
|
}
|
|
222
224
|
/**
|
|
@@ -289,14 +291,14 @@ export async function discardFromDeadLetterQueue(dlqId, resolvedBy, resolutionNo
|
|
|
289
291
|
*/
|
|
290
292
|
export async function retryDeadLetterTask(dlqId) {
|
|
291
293
|
if (!taskQueueRef) {
|
|
292
|
-
|
|
294
|
+
log.error('Task queue not available, cannot retry', new Error('QUEUE_NOT_INITIALIZED'));
|
|
293
295
|
return false;
|
|
294
296
|
}
|
|
295
297
|
const storage = getStorage();
|
|
296
298
|
// Get the DLQ entry
|
|
297
299
|
const rows = await storage.query('SELECT * FROM dead_letter_tasks WHERE id = ? AND status = ?', [dlqId, 'pending']);
|
|
298
300
|
if (rows.length === 0) {
|
|
299
|
-
|
|
301
|
+
log.error(`DLQ entry not found or not pending`, new Error(`DLQ entry ${dlqId} not found`));
|
|
300
302
|
return false;
|
|
301
303
|
}
|
|
302
304
|
const dlqEntry = rows[0];
|
|
@@ -334,7 +336,7 @@ export async function retryDeadLetterTask(dlqId) {
|
|
|
334
336
|
await storage.execute(`UPDATE dead_letter_tasks
|
|
335
337
|
SET retry_count = retry_count + 1, last_retry_at = strftime('%s', 'now'), status = 'resolved', resolution_note = 'Retried'
|
|
336
338
|
WHERE id = ?`, [dlqId]);
|
|
337
|
-
|
|
339
|
+
log.info('Task moved from DLQ back to main queue', { taskId: task.id, dlqId });
|
|
338
340
|
return true;
|
|
339
341
|
}
|
|
340
342
|
/**
|
|
@@ -350,7 +352,7 @@ export async function cleanupDeadLetterQueue(retentionDays = parseInt(process.en
|
|
|
350
352
|
// SQLite execute doesn't reliably return affected rows, so count separately
|
|
351
353
|
const remaining = await storage.query(`SELECT COUNT(*) as count FROM dead_letter_tasks WHERE status IN ('resolved', 'discarded')`);
|
|
352
354
|
const remainingCount = remaining[0]?.count ?? 0;
|
|
353
|
-
|
|
355
|
+
log.info('DLQ cleanup complete', { retentionDays, remainingResolved: remainingCount });
|
|
354
356
|
return remainingCount;
|
|
355
357
|
}
|
|
356
358
|
/**
|
package/dist/queue/index.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* All consumers import from this module instead of task-queue or memory-queue directly.
|
|
6
6
|
*/
|
|
7
7
|
import { shouldUseRedis } from '../core/deployment.js';
|
|
8
|
+
import { createContextualLogger } from '../utils/logger.js';
|
|
9
|
+
const log = createContextualLogger('Queue');
|
|
8
10
|
let backend = null;
|
|
9
11
|
let backendType = null;
|
|
10
12
|
async function loadBackend() {
|
|
@@ -25,21 +27,21 @@ async function loadBackend() {
|
|
|
25
27
|
await testRedis.disconnect();
|
|
26
28
|
backend = await import('./task-queue.js');
|
|
27
29
|
backendType = 'redis';
|
|
28
|
-
|
|
30
|
+
log.info('Using Redis-backed BullMQ queue');
|
|
29
31
|
return backend;
|
|
30
32
|
}
|
|
31
33
|
catch {
|
|
32
34
|
if (process.env.PROFCLAW_REQUIRE_REDIS === 'true') {
|
|
33
|
-
|
|
35
|
+
log.error('PROFCLAW_REQUIRE_REDIS=true but Redis is unreachable', new Error('REDIS_UNREACHABLE'));
|
|
34
36
|
process.exit(1);
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
log.warn('Redis unavailable, falling back to in-memory queue');
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
// Fallback: in-memory queue
|
|
40
42
|
backend = await import('./memory-queue.js');
|
|
41
43
|
backendType = 'memory';
|
|
42
|
-
|
|
44
|
+
log.info('Using in-memory queue (no Redis)');
|
|
43
45
|
return backend;
|
|
44
46
|
}
|
|
45
47
|
function getBackend() {
|