Deduplicate requests and include pubkey in notifications

This commit is contained in:
root
2026-03-12 12:33:46 +00:00
parent 213d6bf183
commit 165f84b1df
3 changed files with 42 additions and 8 deletions

View File

@@ -67,7 +67,7 @@ async function createSystemdService(repoName: string, repoDir: string, seedPath:
return serviceName; return serviceName;
} }
function appendNotification(payload: { serviceName: string; description: string }) { function appendNotification(payload: { serviceName: string; description: string; pubkey?: string }) {
const file = '/root/.meta-mcp/notifications.jsonl'; const file = '/root/.meta-mcp/notifications.jsonl';
const dir = path.dirname(file); const dir = path.dirname(file);
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
@@ -201,7 +201,11 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
const cvmiArgs = ['-y', 'cvmi', 'serve', '--', 'node', 'dist/server.js']; const cvmiArgs = ['-y', 'cvmi', 'serve', '--', 'node', 'dist/server.js'];
const child = spawn('npx', cvmiArgs, { cwd: repoDir, env, stdio: 'pipe' }); const child = spawn('npx', cvmiArgs, { cwd: repoDir, env, stdio: 'pipe' });
const serviceName = toServiceName(repoName);
const desc = request.requirements[0]?.description ?? repoName;
let pubkey: string | undefined; let pubkey: string | undefined;
let notified = false;
child.stdout.on('data', (d) => { child.stdout.on('data', (d) => {
const text = d.toString(); const text = d.toString();
if (!pubkey) { if (!pubkey) {
@@ -213,6 +217,10 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
message: `Server running for ${repoName}`, message: `Server running for ${repoName}`,
serverPubkey: pubkey, serverPubkey: pubkey,
}); });
if (!notified) {
appendNotification({ serviceName, description: desc, pubkey });
notified = true;
}
} }
} }
}); });
@@ -223,10 +231,11 @@ export async function runPipeline(jobId: string, request: MetaRequest): Promise<
}); });
// create a systemd service to keep the MCP alive // create a systemd service to keep the MCP alive
const serviceName = await createSystemdService(repoName, repoDir, repoSeedPath); await createSystemdService(repoName, repoDir, repoSeedPath);
const desc = request.requirements[0]?.description ?? repoName; if (!notified) {
appendNotification({ serviceName, description: desc }); appendNotification({ serviceName, description: desc });
}
return { return {
status: 'running', status: 'running',

View File

@@ -1,7 +1,7 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod'; import { z } from 'zod';
import { createJob, getJob, listJobs, updateJob } from './storage.js'; import { createJob, findRecentJobBySignature, getJob, listJobs, updateJob } from './storage.js';
import type { MetaRequest, MetaResponse } from './types.js'; import type { MetaRequest, MetaResponse } from './types.js';
import { runPipeline } from './pipeline.js'; import { runPipeline } from './pipeline.js';
@@ -47,7 +47,21 @@ server.registerTool(
}, },
async (args) => { async (args) => {
const parsed = MetaRequestSchema.parse(args); const parsed = MetaRequestSchema.parse(args);
const job = createJob(parsed as MetaRequest); const signature = JSON.stringify(parsed.requirements ?? []);
const existing = findRecentJobBySignature(signature, 10 * 60 * 1000);
if (existing) {
return {
content: [
{
type: 'text',
text: JSON.stringify(existing.response, null, 2),
},
],
};
}
const job = createJob(parsed as MetaRequest, signature);
updateJob(job.id, { updateJob(job.id, {
status: 'building', status: 'building',

View File

@@ -6,6 +6,7 @@ import type { MetaRequest, MetaResponse } from './types.js';
export type Job = { export type Job = {
id: string; id: string;
request: MetaRequest; request: MetaRequest;
signature?: string;
createdAt: number; createdAt: number;
updatedAt: number; updatedAt: number;
response: MetaResponse; response: MetaResponse;
@@ -31,7 +32,7 @@ function hydrate() {
hydrate(); hydrate();
export function createJob(request: MetaRequest): Job { export function createJob(request: MetaRequest, signature?: string): Job {
const id = randomUUID(); const id = randomUUID();
const now = Date.now(); const now = Date.now();
const response: MetaResponse = { const response: MetaResponse = {
@@ -39,7 +40,7 @@ export function createJob(request: MetaRequest): Job {
message: 'Request received. Preparing build pipeline.', message: 'Request received. Preparing build pipeline.',
jobId: id, jobId: id,
}; };
const job: Job = { id, request, createdAt: now, updatedAt: now, response }; const job: Job = { id, request, signature, createdAt: now, updatedAt: now, response };
jobs.set(id, job); jobs.set(id, job);
persist(); persist();
return job; return job;
@@ -65,3 +66,13 @@ export function getJob(id: string): Job | null {
export function listJobs(): Job[] { export function listJobs(): Job[] {
return [...jobs.values()].sort((a, b) => b.createdAt - a.createdAt); return [...jobs.values()].sort((a, b) => b.createdAt - a.createdAt);
} }
export function findRecentJobBySignature(signature: string, maxAgeMs: number): Job | null {
const now = Date.now();
for (const job of jobs.values()) {
if (job.signature === signature && now - job.createdAt <= maxAgeMs) {
return job;
}
}
return null;
}