@telepat/snoopy 0.1.13 → 0.1.15
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 +70 -215
- package/README.zh-CN.md +137 -0
- package/dist/src/agent/install.d.ts +18 -0
- package/dist/src/agent/install.js +488 -0
- package/dist/src/cli/commands/feedback.d.ts +18 -0
- package/dist/src/cli/commands/feedback.js +276 -0
- package/dist/src/cli/commands/prompt.d.ts +6 -0
- package/dist/src/cli/commands/prompt.js +92 -0
- package/dist/src/cli/commands/promptEditor.d.ts +1 -0
- package/dist/src/cli/commands/promptEditor.js +17 -0
- package/dist/src/cli/flows/jobAddFlow.js +1 -1
- package/dist/src/cli/index.js +86 -1
- package/dist/src/mcp/helpers.d.ts +46 -0
- package/dist/src/mcp/helpers.js +506 -0
- package/dist/src/mcp/server.d.ts +1 -0
- package/dist/src/mcp/server.js +299 -0
- package/dist/src/mcp/tools.d.ts +90 -0
- package/dist/src/mcp/tools.js +106 -0
- package/dist/src/services/db/migrations/002_feedback_fields.d.ts +7 -0
- package/dist/src/services/db/migrations/002_feedback_fields.js +22 -0
- package/dist/src/services/db/migrations/index.js +2 -1
- package/dist/src/services/db/repositories/jobsRepo.d.ts +2 -0
- package/dist/src/services/db/repositories/jobsRepo.js +15 -0
- package/dist/src/services/db/repositories/scanItemsRepo.d.ts +17 -0
- package/dist/src/services/db/repositories/scanItemsRepo.js +197 -2
- package/dist/src/services/feedback/consolidationService.d.ts +28 -0
- package/dist/src/services/feedback/consolidationService.js +124 -0
- package/dist/src/services/openrouter/client.d.ts +23 -0
- package/dist/src/services/openrouter/client.js +67 -0
- package/dist/src/types/settings.d.ts +1 -1
- package/dist/src/types/settings.js +1 -1
- package/dist/src/ui/components/MultilinePrompt.d.ts +10 -0
- package/dist/src/ui/components/MultilinePrompt.js +87 -0
- package/dist/src/ui/components/multilinePromptModel.d.ts +25 -0
- package/dist/src/ui/components/multilinePromptModel.js +76 -0
- package/package.json +4 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { snoopyDoctorToolInputSchema, snoopyDaemonStatusToolInputSchema, snoopyDaemonStartToolInputSchema, snoopyDaemonStopToolInputSchema, snoopyDaemonReloadToolInputSchema, snoopyJobListToolInputSchema, snoopyJobRunsToolInputSchema, snoopyJobAddToolInputSchema, snoopyJobDeleteToolInputSchema, snoopyJobEnableToolInputSchema, snoopyJobDisableToolInputSchema, snoopyJobRunToolInputSchema, snoopyAnalyticsToolInputSchema, snoopyExportToolInputSchema, snoopyConsumeToolInputSchema, snoopyFeedbackReviewToolInputSchema, snoopyFeedbackSubmitToolInputSchema, snoopyFeedbackConsolidateToolInputSchema, snoopyErrorsToolInputSchema, snoopyLogsToolInputSchema, snoopySettingsGetToolInputSchema, snoopySettingsSetToolInputSchema, } from './tools.js';
|
|
4
|
+
import { getSnoopyVersion, formatToolError, formatToolResult, buildDoctorReport, buildDaemonStatusReport, startDaemonReport, stopDaemonReport, reloadDaemonReport, listJobsReport, listJobRunsReport, addJobReport, deleteJobReport, enableJobReport, disableJobReport, runJobReport, analyticsReport, exportReport, consumeReport, feedbackReviewReport, feedbackSubmitReport, feedbackConsolidateReport, errorsReport, logsReport, settingsGetReport, settingsSetReport, } from './helpers.js';
|
|
5
|
+
export async function startSnoopyMcpServer() {
|
|
6
|
+
const version = getSnoopyVersion();
|
|
7
|
+
const server = new McpServer({ name: 'snoopy', version });
|
|
8
|
+
// --- snoopy_doctor ---
|
|
9
|
+
server.registerTool('snoopy_doctor', {
|
|
10
|
+
title: 'Health Check',
|
|
11
|
+
description: 'Run full system health check: database, API key, daemon, jobs, startup, recent errors.',
|
|
12
|
+
inputSchema: snoopyDoctorToolInputSchema,
|
|
13
|
+
}, async () => {
|
|
14
|
+
try {
|
|
15
|
+
const report = await buildDoctorReport();
|
|
16
|
+
return formatToolResult(report);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return formatToolError(error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
// --- snoopy_daemon_status ---
|
|
23
|
+
server.registerTool('snoopy_daemon_status', {
|
|
24
|
+
title: 'Daemon Status',
|
|
25
|
+
description: 'Show whether the Snoopy daemon is running and its PID.',
|
|
26
|
+
inputSchema: snoopyDaemonStatusToolInputSchema,
|
|
27
|
+
}, () => {
|
|
28
|
+
try {
|
|
29
|
+
return formatToolResult(buildDaemonStatusReport());
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return formatToolError(error);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// --- snoopy_daemon_start ---
|
|
36
|
+
server.registerTool('snoopy_daemon_start', {
|
|
37
|
+
title: 'Start Daemon',
|
|
38
|
+
description: 'Start the Snoopy background daemon.',
|
|
39
|
+
inputSchema: snoopyDaemonStartToolInputSchema,
|
|
40
|
+
}, () => {
|
|
41
|
+
try {
|
|
42
|
+
return formatToolResult(startDaemonReport());
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return formatToolError(error);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// --- snoopy_daemon_stop ---
|
|
49
|
+
server.registerTool('snoopy_daemon_stop', {
|
|
50
|
+
title: 'Stop Daemon',
|
|
51
|
+
description: 'Stop the Snoopy background daemon.',
|
|
52
|
+
inputSchema: snoopyDaemonStopToolInputSchema,
|
|
53
|
+
}, () => {
|
|
54
|
+
try {
|
|
55
|
+
return formatToolResult(stopDaemonReport());
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return formatToolError(error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// --- snoopy_daemon_reload ---
|
|
62
|
+
server.registerTool('snoopy_daemon_reload', {
|
|
63
|
+
title: 'Reload Daemon',
|
|
64
|
+
description: 'Hot-reload daemon job schedules without restart.',
|
|
65
|
+
inputSchema: snoopyDaemonReloadToolInputSchema,
|
|
66
|
+
}, () => {
|
|
67
|
+
try {
|
|
68
|
+
return formatToolResult(reloadDaemonReport());
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return formatToolError(error);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// --- snoopy_job_list ---
|
|
75
|
+
server.registerTool('snoopy_job_list', {
|
|
76
|
+
title: 'List Jobs',
|
|
77
|
+
description: 'List all monitoring jobs with their state, subreddits, and schedule.',
|
|
78
|
+
inputSchema: snoopyJobListToolInputSchema,
|
|
79
|
+
}, () => {
|
|
80
|
+
try {
|
|
81
|
+
return formatToolResult(listJobsReport());
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return formatToolError(error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// --- snoopy_job_runs ---
|
|
88
|
+
server.registerTool('snoopy_job_runs', {
|
|
89
|
+
title: 'Job Run History',
|
|
90
|
+
description: 'List recent run history for a job or all jobs.',
|
|
91
|
+
inputSchema: snoopyJobRunsToolInputSchema,
|
|
92
|
+
}, (input) => {
|
|
93
|
+
try {
|
|
94
|
+
return formatToolResult(listJobRunsReport(input.jobRef, input.limit));
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return formatToolError(error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// --- snoopy_job_add ---
|
|
101
|
+
server.registerTool('snoopy_job_add', {
|
|
102
|
+
title: 'Add Job',
|
|
103
|
+
description: 'Create a new monitoring job with subreddits and qualification prompt.',
|
|
104
|
+
inputSchema: snoopyJobAddToolInputSchema,
|
|
105
|
+
}, (input) => {
|
|
106
|
+
try {
|
|
107
|
+
return formatToolResult(addJobReport(input));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return formatToolError(error);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// --- snoopy_job_delete ---
|
|
114
|
+
server.registerTool('snoopy_job_delete', {
|
|
115
|
+
title: 'Delete Job',
|
|
116
|
+
description: 'Delete a job and all its runs, scan items, and log files.',
|
|
117
|
+
inputSchema: snoopyJobDeleteToolInputSchema,
|
|
118
|
+
}, (input) => {
|
|
119
|
+
try {
|
|
120
|
+
return formatToolResult(deleteJobReport(input.jobRef));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return formatToolError(error);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// --- snoopy_job_enable ---
|
|
127
|
+
server.registerTool('snoopy_job_enable', {
|
|
128
|
+
title: 'Enable Job',
|
|
129
|
+
description: 'Enable scheduling for a monitoring job.',
|
|
130
|
+
inputSchema: snoopyJobEnableToolInputSchema,
|
|
131
|
+
}, (input) => {
|
|
132
|
+
try {
|
|
133
|
+
return formatToolResult(enableJobReport(input.jobRef));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return formatToolError(error);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// --- snoopy_job_disable ---
|
|
140
|
+
server.registerTool('snoopy_job_disable', {
|
|
141
|
+
title: 'Disable Job',
|
|
142
|
+
description: 'Disable scheduling for a monitoring job.',
|
|
143
|
+
inputSchema: snoopyJobDisableToolInputSchema,
|
|
144
|
+
}, (input) => {
|
|
145
|
+
try {
|
|
146
|
+
return formatToolResult(disableJobReport(input.jobRef));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return formatToolError(error);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
// --- snoopy_job_run ---
|
|
153
|
+
server.registerTool('snoopy_job_run', {
|
|
154
|
+
title: 'Run Job Now',
|
|
155
|
+
description: 'Trigger an immediate run for a monitoring job.',
|
|
156
|
+
inputSchema: snoopyJobRunToolInputSchema,
|
|
157
|
+
}, (input) => {
|
|
158
|
+
try {
|
|
159
|
+
return formatToolResult(runJobReport(input.jobRef, input.limit));
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
return formatToolError(error);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// --- snoopy_analytics ---
|
|
166
|
+
server.registerTool('snoopy_analytics', {
|
|
167
|
+
title: 'Analytics',
|
|
168
|
+
description: 'Show analytics for all jobs or a single job (tokens, cost, posts, comments).',
|
|
169
|
+
inputSchema: snoopyAnalyticsToolInputSchema,
|
|
170
|
+
}, (input) => {
|
|
171
|
+
try {
|
|
172
|
+
return formatToolResult(analyticsReport(input.jobRef, input.days));
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return formatToolError(error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
// --- snoopy_export ---
|
|
179
|
+
server.registerTool('snoopy_export', {
|
|
180
|
+
title: 'Export Results',
|
|
181
|
+
description: 'Export qualified scan items as JSON or CSV for downstream processing.',
|
|
182
|
+
inputSchema: snoopyExportToolInputSchema,
|
|
183
|
+
}, (input) => {
|
|
184
|
+
try {
|
|
185
|
+
return formatToolResult(exportReport(input.jobRef, input.format, input.lastRun, input.limit));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return formatToolError(error);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// --- snoopy_consume ---
|
|
192
|
+
server.registerTool('snoopy_consume', {
|
|
193
|
+
title: 'Consume Results',
|
|
194
|
+
description: 'List and mark unconsumed qualified results as consumed.',
|
|
195
|
+
inputSchema: snoopyConsumeToolInputSchema,
|
|
196
|
+
}, (input) => {
|
|
197
|
+
try {
|
|
198
|
+
return formatToolResult(consumeReport(input.jobRef, input.limit, input.dryRun));
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return formatToolError(error);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
// --- snoopy_feedback_review ---
|
|
205
|
+
server.registerTool('snoopy_feedback_review', {
|
|
206
|
+
title: 'Review Feedback Queue',
|
|
207
|
+
description: 'List unvalidated qualified results for user feedback collection.',
|
|
208
|
+
inputSchema: snoopyFeedbackReviewToolInputSchema,
|
|
209
|
+
}, (input) => {
|
|
210
|
+
try {
|
|
211
|
+
return formatToolResult(feedbackReviewReport(input.jobRef, input.limit));
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
return formatToolError(error);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
// --- snoopy_feedback_submit ---
|
|
218
|
+
server.registerTool('snoopy_feedback_submit', {
|
|
219
|
+
title: 'Submit Feedback',
|
|
220
|
+
description: 'Submit validity feedback for a qualified result.',
|
|
221
|
+
inputSchema: snoopyFeedbackSubmitToolInputSchema,
|
|
222
|
+
}, (input) => {
|
|
223
|
+
try {
|
|
224
|
+
return formatToolResult(feedbackSubmitReport(input.resultId, input.isValid, input.reason));
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return formatToolError(error);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
// --- snoopy_feedback_consolidate ---
|
|
231
|
+
server.registerTool('snoopy_feedback_consolidate', {
|
|
232
|
+
title: 'Consolidate Feedback',
|
|
233
|
+
description: 'Consolidate feedback into improved qualification prompts.',
|
|
234
|
+
inputSchema: snoopyFeedbackConsolidateToolInputSchema,
|
|
235
|
+
}, async (input) => {
|
|
236
|
+
try {
|
|
237
|
+
return formatToolResult(await feedbackConsolidateReport(input.jobRef, input.limit));
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return formatToolError(error);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
// --- snoopy_errors ---
|
|
244
|
+
server.registerTool('snoopy_errors', {
|
|
245
|
+
title: 'Recent Errors',
|
|
246
|
+
description: 'Show recent failed or errored runs for a job.',
|
|
247
|
+
inputSchema: snoopyErrorsToolInputSchema,
|
|
248
|
+
}, (input) => {
|
|
249
|
+
try {
|
|
250
|
+
return formatToolResult(errorsReport(input.jobRef, input.hours));
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
return formatToolError(error);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
// --- snoopy_logs ---
|
|
257
|
+
server.registerTool('snoopy_logs', {
|
|
258
|
+
title: 'Run Logs',
|
|
259
|
+
description: 'View the log output for a specific run.',
|
|
260
|
+
inputSchema: snoopyLogsToolInputSchema,
|
|
261
|
+
}, (input) => {
|
|
262
|
+
try {
|
|
263
|
+
return formatToolResult(logsReport(input.runId));
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
return formatToolError(error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
// --- snoopy_settings_get ---
|
|
270
|
+
server.registerTool('snoopy_settings_get', {
|
|
271
|
+
title: 'Get Settings',
|
|
272
|
+
description: 'Read current Snoopy settings (model, API key status, schedule, notifications).',
|
|
273
|
+
inputSchema: snoopySettingsGetToolInputSchema,
|
|
274
|
+
}, async () => {
|
|
275
|
+
try {
|
|
276
|
+
const report = await settingsGetReport();
|
|
277
|
+
return formatToolResult(report);
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
return formatToolError(error);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
// --- snoopy_settings_set ---
|
|
284
|
+
server.registerTool('snoopy_settings_set', {
|
|
285
|
+
title: 'Update Setting',
|
|
286
|
+
description: 'Update a single Snoopy setting.',
|
|
287
|
+
inputSchema: snoopySettingsSetToolInputSchema,
|
|
288
|
+
}, (input) => {
|
|
289
|
+
try {
|
|
290
|
+
return formatToolResult(settingsSetReport(input.key, input.value));
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
return formatToolError(error);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
const transport = new StdioServerTransport();
|
|
297
|
+
await server.connect(transport);
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const snoopyDoctorToolInputSchema: {};
|
|
3
|
+
export declare const snoopyDaemonStatusToolInputSchema: {};
|
|
4
|
+
export declare const snoopyDaemonStartToolInputSchema: {};
|
|
5
|
+
export declare const snoopyDaemonStopToolInputSchema: {};
|
|
6
|
+
export declare const snoopyDaemonReloadToolInputSchema: {};
|
|
7
|
+
export declare const snoopyJobListToolInputSchema: {};
|
|
8
|
+
export declare const snoopyJobRunsToolInputSchema: {
|
|
9
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
10
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
11
|
+
};
|
|
12
|
+
export declare const snoopyJobAddToolInputSchema: {
|
|
13
|
+
name: z.ZodString;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
subreddits: z.ZodArray<z.ZodString>;
|
|
16
|
+
qualificationPrompt: z.ZodString;
|
|
17
|
+
scheduleCron: z.ZodOptional<z.ZodString>;
|
|
18
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
monitorComments: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
};
|
|
21
|
+
export declare const snoopyJobDeleteToolInputSchema: {
|
|
22
|
+
jobRef: z.ZodString;
|
|
23
|
+
};
|
|
24
|
+
export declare const snoopyJobEnableToolInputSchema: {
|
|
25
|
+
jobRef: z.ZodString;
|
|
26
|
+
};
|
|
27
|
+
export declare const snoopyJobDisableToolInputSchema: {
|
|
28
|
+
jobRef: z.ZodString;
|
|
29
|
+
};
|
|
30
|
+
export declare const snoopyJobRunToolInputSchema: {
|
|
31
|
+
jobRef: z.ZodString;
|
|
32
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
33
|
+
};
|
|
34
|
+
export declare const snoopyAnalyticsToolInputSchema: {
|
|
35
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
36
|
+
days: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
37
|
+
};
|
|
38
|
+
export declare const snoopyExportToolInputSchema: {
|
|
39
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
40
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
csv: "csv";
|
|
42
|
+
json: "json";
|
|
43
|
+
}>>;
|
|
44
|
+
lastRun: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
46
|
+
};
|
|
47
|
+
export declare const snoopyConsumeToolInputSchema: {
|
|
48
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
49
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
50
|
+
dryRun: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
};
|
|
52
|
+
export declare const snoopyFeedbackReviewToolInputSchema: {
|
|
53
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
54
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
55
|
+
};
|
|
56
|
+
export declare const snoopyFeedbackSubmitToolInputSchema: {
|
|
57
|
+
resultId: z.ZodString;
|
|
58
|
+
isValid: z.ZodBoolean;
|
|
59
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
60
|
+
};
|
|
61
|
+
export declare const snoopyFeedbackConsolidateToolInputSchema: {
|
|
62
|
+
jobRef: z.ZodOptional<z.ZodString>;
|
|
63
|
+
limit: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
64
|
+
};
|
|
65
|
+
export declare const snoopyErrorsToolInputSchema: {
|
|
66
|
+
jobRef: z.ZodString;
|
|
67
|
+
hours: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
68
|
+
};
|
|
69
|
+
export declare const snoopyLogsToolInputSchema: {
|
|
70
|
+
runId: z.ZodString;
|
|
71
|
+
};
|
|
72
|
+
export declare const snoopySettingsGetToolInputSchema: {};
|
|
73
|
+
export declare const snoopySettingsSetToolInputSchema: {
|
|
74
|
+
key: z.ZodEnum<{
|
|
75
|
+
model: "model";
|
|
76
|
+
cronIntervalMinutes: "cronIntervalMinutes";
|
|
77
|
+
temperature: "temperature";
|
|
78
|
+
maxTokens: "maxTokens";
|
|
79
|
+
topP: "topP";
|
|
80
|
+
notificationsEnabled: "notificationsEnabled";
|
|
81
|
+
jobTimeoutMs: "jobTimeoutMs";
|
|
82
|
+
}>;
|
|
83
|
+
value: z.ZodString;
|
|
84
|
+
};
|
|
85
|
+
export interface ToolContract {
|
|
86
|
+
name: string;
|
|
87
|
+
required: string[];
|
|
88
|
+
enums: Record<string, string[]>;
|
|
89
|
+
}
|
|
90
|
+
export declare const snoopyToolContracts: ToolContract[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const snoopyDoctorToolInputSchema = {};
|
|
3
|
+
export const snoopyDaemonStatusToolInputSchema = {};
|
|
4
|
+
export const snoopyDaemonStartToolInputSchema = {};
|
|
5
|
+
export const snoopyDaemonStopToolInputSchema = {};
|
|
6
|
+
export const snoopyDaemonReloadToolInputSchema = {};
|
|
7
|
+
export const snoopyJobListToolInputSchema = {};
|
|
8
|
+
export const snoopyJobRunsToolInputSchema = {
|
|
9
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, returns runs for all jobs.'),
|
|
10
|
+
limit: z.coerce.number().int().positive().optional().describe('Max runs to return. Default: 20.'),
|
|
11
|
+
};
|
|
12
|
+
export const snoopyJobAddToolInputSchema = {
|
|
13
|
+
name: z.string().min(1).describe('Job name.'),
|
|
14
|
+
description: z.string().optional().describe('Job description. Default: empty.'),
|
|
15
|
+
subreddits: z.array(z.string()).min(1).describe('Subreddits to monitor (e.g. ["startups","SaaS"]).'),
|
|
16
|
+
qualificationPrompt: z.string().min(1).describe('Plain-language criteria for qualifying posts/comments.'),
|
|
17
|
+
scheduleCron: z.string().optional().describe('Cron expression. Default: "*/30 * * * *" (every 30 min).'),
|
|
18
|
+
enabled: z.boolean().optional().describe('Enable scheduling immediately. Default: true.'),
|
|
19
|
+
monitorComments: z.boolean().optional().describe('Also monitor comments. Default: true.'),
|
|
20
|
+
};
|
|
21
|
+
export const snoopyJobDeleteToolInputSchema = {
|
|
22
|
+
jobRef: z.string().min(1).describe('Job ID or slug to delete.'),
|
|
23
|
+
};
|
|
24
|
+
export const snoopyJobEnableToolInputSchema = {
|
|
25
|
+
jobRef: z.string().min(1).describe('Job ID or slug to enable.'),
|
|
26
|
+
};
|
|
27
|
+
export const snoopyJobDisableToolInputSchema = {
|
|
28
|
+
jobRef: z.string().min(1).describe('Job ID or slug to disable.'),
|
|
29
|
+
};
|
|
30
|
+
export const snoopyJobRunToolInputSchema = {
|
|
31
|
+
jobRef: z.string().min(1).describe('Job ID or slug to run immediately.'),
|
|
32
|
+
limit: z.coerce.number().int().positive().optional().describe('Max new items to qualify.'),
|
|
33
|
+
};
|
|
34
|
+
export const snoopyAnalyticsToolInputSchema = {
|
|
35
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, returns analytics for all jobs.'),
|
|
36
|
+
days: z.coerce.number().int().positive().optional().describe('Lookback window in days. Default: 30.'),
|
|
37
|
+
};
|
|
38
|
+
export const snoopyExportToolInputSchema = {
|
|
39
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, exports all jobs.'),
|
|
40
|
+
format: z.enum(['json', 'csv']).optional().describe('Export format. Default: "json".'),
|
|
41
|
+
lastRun: z.boolean().optional().describe('Only export items from the latest run.'),
|
|
42
|
+
limit: z.coerce.number().int().positive().optional().describe('Max rows per job. Default: 100.'),
|
|
43
|
+
};
|
|
44
|
+
export const snoopyConsumeToolInputSchema = {
|
|
45
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, applies to all jobs.'),
|
|
46
|
+
limit: z.coerce.number().int().positive().optional().describe('Max results to consume.'),
|
|
47
|
+
dryRun: z.boolean().optional().describe('Preview without marking consumed.'),
|
|
48
|
+
};
|
|
49
|
+
export const snoopyFeedbackReviewToolInputSchema = {
|
|
50
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, review queue spans all jobs.'),
|
|
51
|
+
limit: z.coerce.number().int().positive().optional().describe('Max unvalidated results to return. Default: 10.'),
|
|
52
|
+
};
|
|
53
|
+
export const snoopyFeedbackSubmitToolInputSchema = {
|
|
54
|
+
resultId: z.string().min(1).describe('Qualified result ID to update.'),
|
|
55
|
+
isValid: z.boolean().describe('Whether the result should be considered valid.'),
|
|
56
|
+
reason: z.string().optional().describe('Required when isValid=false; ignored when isValid=true.'),
|
|
57
|
+
};
|
|
58
|
+
export const snoopyFeedbackConsolidateToolInputSchema = {
|
|
59
|
+
jobRef: z.string().optional().describe('Job ID or slug. If omitted, consolidates across all jobs.'),
|
|
60
|
+
limit: z.coerce.number().int().positive().optional().describe('Max pending feedback items to process.'),
|
|
61
|
+
};
|
|
62
|
+
export const snoopyErrorsToolInputSchema = {
|
|
63
|
+
jobRef: z.string().min(1).describe('Job ID or slug.'),
|
|
64
|
+
hours: z.coerce.number().int().positive().optional().describe('Look back hours. Default: 24.'),
|
|
65
|
+
};
|
|
66
|
+
export const snoopyLogsToolInputSchema = {
|
|
67
|
+
runId: z.string().min(1).describe('Run ID to view logs for.'),
|
|
68
|
+
};
|
|
69
|
+
export const snoopySettingsGetToolInputSchema = {};
|
|
70
|
+
export const snoopySettingsSetToolInputSchema = {
|
|
71
|
+
key: z.enum([
|
|
72
|
+
'model',
|
|
73
|
+
'temperature',
|
|
74
|
+
'maxTokens',
|
|
75
|
+
'topP',
|
|
76
|
+
'cronIntervalMinutes',
|
|
77
|
+
'jobTimeoutMs',
|
|
78
|
+
'notificationsEnabled',
|
|
79
|
+
]).describe('Setting key to update.'),
|
|
80
|
+
value: z.string().describe('New value for the setting.'),
|
|
81
|
+
};
|
|
82
|
+
export const snoopyToolContracts = [
|
|
83
|
+
{ name: 'snoopy_doctor', required: [], enums: {} },
|
|
84
|
+
{ name: 'snoopy_daemon_status', required: [], enums: {} },
|
|
85
|
+
{ name: 'snoopy_daemon_start', required: [], enums: {} },
|
|
86
|
+
{ name: 'snoopy_daemon_stop', required: [], enums: {} },
|
|
87
|
+
{ name: 'snoopy_daemon_reload', required: [], enums: {} },
|
|
88
|
+
{ name: 'snoopy_job_list', required: [], enums: {} },
|
|
89
|
+
{ name: 'snoopy_job_runs', required: [], enums: {} },
|
|
90
|
+
{ name: 'snoopy_job_add', required: ['name', 'subreddits', 'qualificationPrompt'], enums: {} },
|
|
91
|
+
{ name: 'snoopy_job_delete', required: ['jobRef'], enums: {} },
|
|
92
|
+
{ name: 'snoopy_job_enable', required: ['jobRef'], enums: {} },
|
|
93
|
+
{ name: 'snoopy_job_disable', required: ['jobRef'], enums: {} },
|
|
94
|
+
{ name: 'snoopy_job_run', required: ['jobRef'], enums: {} },
|
|
95
|
+
{ name: 'snoopy_analytics', required: [], enums: {} },
|
|
96
|
+
{ name: 'snoopy_export', required: [], enums: { format: ['json', 'csv'] } },
|
|
97
|
+
{ name: 'snoopy_consume', required: [], enums: {} },
|
|
98
|
+
{ name: 'snoopy_feedback_review', required: [], enums: {} },
|
|
99
|
+
{ name: 'snoopy_feedback_submit', required: ['resultId', 'isValid'], enums: {} },
|
|
100
|
+
{ name: 'snoopy_feedback_consolidate', required: [], enums: {} },
|
|
101
|
+
{ name: 'snoopy_errors', required: ['jobRef'], enums: {} },
|
|
102
|
+
{ name: 'snoopy_logs', required: ['runId'], enums: {} },
|
|
103
|
+
{ name: 'snoopy_settings_get', required: [], enums: {} },
|
|
104
|
+
{ name: 'snoopy_settings_set', required: ['key', 'value'], enums: { key: ['model', 'temperature', 'maxTokens', 'topP', 'cronIntervalMinutes', 'jobTimeoutMs', 'notificationsEnabled'] } },
|
|
105
|
+
];
|
|
106
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
id: 2,
|
|
3
|
+
name: 'feedback_fields',
|
|
4
|
+
up(db) {
|
|
5
|
+
const safeAddColumn = (table, column, type) => {
|
|
6
|
+
try {
|
|
7
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// Column already exists or table does not exist.
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
safeAddColumn('scan_items', 'is_valid', 'INTEGER NOT NULL DEFAULT 0');
|
|
14
|
+
safeAddColumn('scan_items', 'is_valid_reason', 'TEXT');
|
|
15
|
+
safeAddColumn('scan_items', 'feedback_consolidated', 'INTEGER NOT NULL DEFAULT 0');
|
|
16
|
+
db.exec(`
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_scan_items_feedback_pending
|
|
18
|
+
ON scan_items(job_id, qualified, validated, feedback_consolidated, created_at DESC)
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=002_feedback_fields.js.map
|
|
@@ -28,6 +28,8 @@ export declare class JobsRepository {
|
|
|
28
28
|
listEnabled(): Job[];
|
|
29
29
|
setEnabled(id: string, enabled: boolean): void;
|
|
30
30
|
setEnabledByRef(ref: string, enabled: boolean): Job | null;
|
|
31
|
+
updateQualificationPromptById(id: string, qualificationPrompt: string): Job | null;
|
|
32
|
+
updateQualificationPromptByRef(ref: string, qualificationPrompt: string): Job | null;
|
|
31
33
|
remove(id: string): void;
|
|
32
34
|
removeByRef(ref: string): Job | null;
|
|
33
35
|
listWithStats(): JobSummaryRow[];
|
|
@@ -141,6 +141,21 @@ export class JobsRepository {
|
|
|
141
141
|
this.setEnabled(job.id, enabled);
|
|
142
142
|
return this.getById(job.id);
|
|
143
143
|
}
|
|
144
|
+
updateQualificationPromptById(id, qualificationPrompt) {
|
|
145
|
+
this.db
|
|
146
|
+
.prepare(`UPDATE jobs
|
|
147
|
+
SET qualification_prompt = ?, updated_at = datetime('now')
|
|
148
|
+
WHERE id = ?`)
|
|
149
|
+
.run(qualificationPrompt, id);
|
|
150
|
+
return this.getById(id);
|
|
151
|
+
}
|
|
152
|
+
updateQualificationPromptByRef(ref, qualificationPrompt) {
|
|
153
|
+
const job = this.getByRef(ref);
|
|
154
|
+
if (!job) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return this.updateQualificationPromptById(job.id, qualificationPrompt);
|
|
158
|
+
}
|
|
144
159
|
remove(id) {
|
|
145
160
|
this.removeCascadeStmt(id);
|
|
146
161
|
}
|
|
@@ -14,6 +14,9 @@ export interface NewScanItem {
|
|
|
14
14
|
qualified: boolean;
|
|
15
15
|
viewed?: boolean;
|
|
16
16
|
validated?: boolean;
|
|
17
|
+
isValid?: boolean;
|
|
18
|
+
isValidReason?: string | null;
|
|
19
|
+
feedbackConsolidated?: boolean;
|
|
17
20
|
processed?: boolean;
|
|
18
21
|
consumed?: boolean;
|
|
19
22
|
promptTokens?: number;
|
|
@@ -39,6 +42,8 @@ export interface QualifiedScanItemRow {
|
|
|
39
42
|
id: string;
|
|
40
43
|
jobId: string;
|
|
41
44
|
runId: string;
|
|
45
|
+
type: ScanItemType;
|
|
46
|
+
subreddit: string;
|
|
42
47
|
author: string;
|
|
43
48
|
title: string | null;
|
|
44
49
|
body: string;
|
|
@@ -46,6 +51,9 @@ export interface QualifiedScanItemRow {
|
|
|
46
51
|
redditPostedAt: string;
|
|
47
52
|
viewed: boolean;
|
|
48
53
|
validated: boolean;
|
|
54
|
+
isValid: boolean;
|
|
55
|
+
isValidReason: string | null;
|
|
56
|
+
feedbackConsolidated: boolean;
|
|
49
57
|
processed: boolean;
|
|
50
58
|
consumed: boolean;
|
|
51
59
|
qualificationReason: string | null;
|
|
@@ -67,6 +75,9 @@ export interface ScanItemRow {
|
|
|
67
75
|
qualified: boolean;
|
|
68
76
|
viewed: boolean;
|
|
69
77
|
validated: boolean;
|
|
78
|
+
isValid: boolean;
|
|
79
|
+
isValidReason: string | null;
|
|
80
|
+
feedbackConsolidated: boolean;
|
|
70
81
|
processed: boolean;
|
|
71
82
|
consumed: boolean;
|
|
72
83
|
qualificationReason: string | null;
|
|
@@ -117,6 +128,12 @@ export declare class ScanItemsRepository {
|
|
|
117
128
|
listAnalyticsByJob(days: number): AnalyticsByJobRow[];
|
|
118
129
|
existsComment(jobId: string, postId: string, commentId: string): boolean;
|
|
119
130
|
listUnconsumedQualified(jobId?: string, limit?: number): QualifiedScanItemRow[];
|
|
131
|
+
getQualifiedById(id: string): QualifiedScanItemRow | null;
|
|
132
|
+
listUnvalidatedQualified(jobId?: string, limit?: number): QualifiedScanItemRow[];
|
|
133
|
+
submitFeedback(resultId: string, isValid: boolean, reason: string | null): boolean;
|
|
134
|
+
listPendingFeedbackConsolidation(jobId?: string, limit?: number): QualifiedScanItemRow[];
|
|
135
|
+
countPendingFeedbackConsolidation(jobId?: string): number;
|
|
136
|
+
markFeedbackConsolidated(ids: string[]): number;
|
|
120
137
|
markConsumed(ids: string[]): number;
|
|
121
138
|
createWithStatus(item: NewScanItem): CreateScanItemResult;
|
|
122
139
|
create(item: NewScanItem): string;
|