Add event-driven tasks via Gitea webhooks
Webhook endpoint at /api/webhooks/gitea receives Gitea status events,
matches them against configurable event triggers with conditions
(event type, repo glob, state, context), renders task templates with
{{variable}} substitution, and creates harness tasks automatically.
Includes circuit breaker: after N consecutive task failures from the
same trigger (default 3), the trigger auto-disables. Re-enable
manually via PATCH /api/event-triggers/:id.
New tables: harness_event_triggers (rules + circuit breaker state),
harness_event_log (audit trail + dedup via X-Gitea-Delivery).
This commit is contained in:
@@ -4,13 +4,14 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --port 3100",
|
"dev": "next dev --port 3100",
|
||||||
"build": "next build",
|
"build": "node scripts/build-mcp.mjs && next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "echo \"no tests yet\""
|
"test": "echo \"no tests yet\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@homelab/db": "workspace:^",
|
"@homelab/db": "workspace:^",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
"@xterm/addon-fit": "^0.11.0",
|
"@xterm/addon-fit": "^0.11.0",
|
||||||
"@xterm/xterm": "^6.0.0",
|
"@xterm/xterm": "^6.0.0",
|
||||||
"drizzle-orm": "^0.36.0",
|
"drizzle-orm": "^0.36.0",
|
||||||
@@ -19,14 +20,17 @@
|
|||||||
"postgres": "^3.4.0",
|
"postgres": "^3.4.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
|
"tsx": "^4.19.0",
|
||||||
"ws": "^8.20.0",
|
"ws": "^8.20.0",
|
||||||
"yaml": "^2.7.0"
|
"yaml": "^2.7.0",
|
||||||
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.10.0",
|
"@types/node": "^22.10.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
|
"esbuild": "^0.27.4",
|
||||||
"typescript": "^5.7.0"
|
"typescript": "^5.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
apps/harness/scripts/build-mcp.mjs
Normal file
19
apps/harness/scripts/build-mcp.mjs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { build } from "esbuild";
|
||||||
|
import { resolve, dirname } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
await build({
|
||||||
|
entryPoints: [resolve(__dirname, "../src/mcp-server.ts")],
|
||||||
|
bundle: true,
|
||||||
|
platform: "node",
|
||||||
|
target: "node20",
|
||||||
|
format: "esm",
|
||||||
|
outfile: resolve(__dirname, "../dist/mcp-server.mjs"),
|
||||||
|
external: [],
|
||||||
|
banner: { js: "#!/usr/bin/env node" },
|
||||||
|
// Inline all deps — the output is a self-contained script
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Built dist/mcp-server.mjs");
|
||||||
64
apps/harness/src/app/api/event-triggers/[id]/route.ts
Normal file
64
apps/harness/src/app/api/event-triggers/[id]/route.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import {
|
||||||
|
getEventTrigger,
|
||||||
|
updateEventTrigger,
|
||||||
|
deleteEventTrigger,
|
||||||
|
getEventLogByTrigger,
|
||||||
|
} from "@/lib/event-store";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
_request: NextRequest,
|
||||||
|
{ params }: { params: Promise<{ id: string }> },
|
||||||
|
) {
|
||||||
|
const { id } = await params;
|
||||||
|
const trigger = await getEventTrigger(id);
|
||||||
|
if (!trigger) {
|
||||||
|
return NextResponse.json({ error: "not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
const recentLogs = await getEventLogByTrigger(id, 20);
|
||||||
|
return NextResponse.json({ ...trigger, recentLogs });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function PATCH(
|
||||||
|
request: NextRequest,
|
||||||
|
{ params }: { params: Promise<{ id: string }> },
|
||||||
|
) {
|
||||||
|
const { id } = await params;
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
// Allow re-enabling a circuit-broken trigger
|
||||||
|
const updates: Record<string, unknown> = {};
|
||||||
|
if (body.name !== undefined) updates.name = body.name;
|
||||||
|
if (body.enabled !== undefined) updates.enabled = body.enabled;
|
||||||
|
if (body.eventType !== undefined) updates.eventType = body.eventType;
|
||||||
|
if (body.repoFilter !== undefined) updates.repoFilter = body.repoFilter;
|
||||||
|
if (body.stateFilter !== undefined) updates.stateFilter = body.stateFilter;
|
||||||
|
if (body.contextFilter !== undefined) updates.contextFilter = body.contextFilter;
|
||||||
|
if (body.taskTemplate !== undefined) updates.taskTemplate = body.taskTemplate;
|
||||||
|
if (body.maxConsecutiveFailures !== undefined) updates.maxConsecutiveFailures = body.maxConsecutiveFailures;
|
||||||
|
if (body.webhookSecret !== undefined) updates.webhookSecret = body.webhookSecret;
|
||||||
|
|
||||||
|
// Re-enable: reset circuit breaker
|
||||||
|
if (body.enabled === true) {
|
||||||
|
updates.consecutiveFailures = 0;
|
||||||
|
updates.disabledReason = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await updateEventTrigger(id, updates);
|
||||||
|
if (!updated) {
|
||||||
|
return NextResponse.json({ error: "not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
return NextResponse.json(updated);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function DELETE(
|
||||||
|
_request: NextRequest,
|
||||||
|
{ params }: { params: Promise<{ id: string }> },
|
||||||
|
) {
|
||||||
|
const { id } = await params;
|
||||||
|
const deleted = await deleteEventTrigger(id);
|
||||||
|
if (!deleted) {
|
||||||
|
return NextResponse.json({ error: "not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
}
|
||||||
53
apps/harness/src/app/api/event-triggers/route.ts
Normal file
53
apps/harness/src/app/api/event-triggers/route.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import {
|
||||||
|
getAllEventTriggers,
|
||||||
|
createEventTrigger,
|
||||||
|
} from "@/lib/event-store";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return NextResponse.json(await getAllEventTriggers());
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
if (!body.name || !body.eventType || !body.taskTemplate) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "name, eventType, and taskTemplate are required" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!body.taskTemplate.agentId || !body.taskTemplate.goal) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "taskTemplate must include agentId and goal" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const trigger = await createEventTrigger({
|
||||||
|
id: body.id || `evt-${Date.now()}`,
|
||||||
|
name: body.name,
|
||||||
|
enabled: body.enabled ?? true,
|
||||||
|
eventType: body.eventType,
|
||||||
|
repoFilter: body.repoFilter || null,
|
||||||
|
stateFilter: body.stateFilter || null,
|
||||||
|
contextFilter: body.contextFilter || null,
|
||||||
|
taskTemplate: {
|
||||||
|
slug: body.taskTemplate.slug || "event-{{sha_short}}",
|
||||||
|
goal: body.taskTemplate.goal,
|
||||||
|
project: body.taskTemplate.project || "{{repo}}",
|
||||||
|
gitProvider: body.taskTemplate.gitProvider,
|
||||||
|
gitBaseUrl: body.taskTemplate.gitBaseUrl,
|
||||||
|
agentId: body.taskTemplate.agentId,
|
||||||
|
maxIterations: body.taskTemplate.maxIterations || 6,
|
||||||
|
criteria: body.taskTemplate.criteria || [],
|
||||||
|
constraints: body.taskTemplate.constraints || [],
|
||||||
|
knowledgeRefs: body.taskTemplate.knowledgeRefs || [],
|
||||||
|
},
|
||||||
|
maxConsecutiveFailures: body.maxConsecutiveFailures ?? 3,
|
||||||
|
webhookSecret: body.webhookSecret || null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json(trigger, { status: 201 });
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
running: isRunning(),
|
running: await isRunning(),
|
||||||
currentTaskId: currentRunningTaskId(),
|
currentTaskId: await currentRunningTaskId(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,12 +18,12 @@ export async function POST(request: NextRequest) {
|
|||||||
const action = body.action as string;
|
const action = body.action as string;
|
||||||
|
|
||||||
if (action === "start") {
|
if (action === "start") {
|
||||||
startOrchestrator();
|
await startOrchestrator();
|
||||||
return NextResponse.json({ ok: true, running: true });
|
return NextResponse.json({ ok: true, running: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === "stop") {
|
if (action === "stop") {
|
||||||
stopOrchestrator();
|
await stopOrchestrator();
|
||||||
return NextResponse.json({ ok: true, running: false });
|
return NextResponse.json({ ok: true, running: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export async function POST(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
startOrchestrator();
|
await startOrchestrator();
|
||||||
|
|
||||||
return NextResponse.json({ ok: true, message: "Orchestrator started, task will be picked up" });
|
return NextResponse.json({ ok: true, message: "Orchestrator started, task will be picked up" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { getTask } from "@/lib/store";
|
import { getTask, requestTaskCancel } from "@/lib/store";
|
||||||
import { cancelTask } from "@/lib/orchestrator";
|
|
||||||
|
|
||||||
export async function POST(
|
export async function POST(
|
||||||
_request: NextRequest,
|
_request: NextRequest,
|
||||||
@@ -20,11 +19,11 @@ export async function POST(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelled = cancelTask(id);
|
const cancelled = await requestTaskCancel(id);
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Task is not the currently executing task" },
|
{ error: "Failed to request cancellation" },
|
||||||
{ status: 400 },
|
{ status: 500 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
158
apps/harness/src/app/api/webhooks/gitea/route.ts
Normal file
158
apps/harness/src/app/api/webhooks/gitea/route.ts
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
||||||
|
import { createTask } from "@/lib/store";
|
||||||
|
import { extractVariables, renderTaskTemplate } from "@/lib/template";
|
||||||
|
import { findMatchingTriggers } from "@/lib/event-matching";
|
||||||
|
import {
|
||||||
|
createEventLogEntry,
|
||||||
|
getEventLogByDeliveryId,
|
||||||
|
getEventTrigger,
|
||||||
|
} from "@/lib/event-store";
|
||||||
|
import { startOrchestrator } from "@/lib/orchestrator";
|
||||||
|
import type { Task } from "@/lib/types";
|
||||||
|
|
||||||
|
function verifySignature(
|
||||||
|
rawBody: string,
|
||||||
|
signature: string,
|
||||||
|
secret: string,
|
||||||
|
): boolean {
|
||||||
|
const computed = createHmac("sha256", secret).update(rawBody).digest("hex");
|
||||||
|
try {
|
||||||
|
return timingSafeEqual(
|
||||||
|
Buffer.from(computed, "hex"),
|
||||||
|
Buffer.from(signature, "hex"),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
const deliveryId = request.headers.get("x-gitea-delivery") || "";
|
||||||
|
const eventType = request.headers.get("x-gitea-event") || "";
|
||||||
|
const signature = request.headers.get("x-gitea-signature") || "";
|
||||||
|
|
||||||
|
const rawBody = await request.text();
|
||||||
|
let payload: Record<string, unknown>;
|
||||||
|
try {
|
||||||
|
payload = JSON.parse(rawBody);
|
||||||
|
} catch {
|
||||||
|
return NextResponse.json({ error: "invalid JSON" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// HMAC validation
|
||||||
|
const globalSecret = process.env.GITEA_WEBHOOK_SECRET;
|
||||||
|
if (globalSecret && signature) {
|
||||||
|
if (!verifySignature(rawBody, signature, globalSecret)) {
|
||||||
|
return NextResponse.json({ error: "invalid signature" }, { status: 401 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dedup
|
||||||
|
if (deliveryId) {
|
||||||
|
const existing = await getEventLogByDeliveryId(deliveryId);
|
||||||
|
if (existing) {
|
||||||
|
return NextResponse.json({ status: "duplicate", deliveryId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse event
|
||||||
|
const event = extractVariables(eventType, payload);
|
||||||
|
|
||||||
|
// Find matching triggers
|
||||||
|
const triggers = await findMatchingTriggers(event);
|
||||||
|
|
||||||
|
if (triggers.length === 0) {
|
||||||
|
return NextResponse.json({
|
||||||
|
status: "no_match",
|
||||||
|
eventType,
|
||||||
|
repo: event.repo,
|
||||||
|
state: event.state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const results: { triggerId: string; taskId: string | null; status: string }[] = [];
|
||||||
|
|
||||||
|
for (const trigger of triggers) {
|
||||||
|
const logDeliveryId = triggers.length > 1
|
||||||
|
? `${deliveryId || Date.now()}-${trigger.id}`
|
||||||
|
: deliveryId || String(Date.now());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check per-trigger secret if set
|
||||||
|
if (trigger.webhookSecret && signature) {
|
||||||
|
if (!verifySignature(rawBody, signature, trigger.webhookSecret)) {
|
||||||
|
await createEventLogEntry({
|
||||||
|
triggerId: trigger.id,
|
||||||
|
deliveryId: logDeliveryId,
|
||||||
|
eventType,
|
||||||
|
repo: event.repo,
|
||||||
|
commitSha: event.sha,
|
||||||
|
branch: event.branch,
|
||||||
|
status: "skipped",
|
||||||
|
skipReason: "Per-trigger signature validation failed",
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
results.push({ triggerId: trigger.id, taskId: null, status: "skipped" });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render task spec from template
|
||||||
|
const spec = renderTaskTemplate(trigger.taskTemplate, event);
|
||||||
|
const taskId = `task-${Date.now()}-${trigger.id.slice(-4)}`;
|
||||||
|
|
||||||
|
const task: Task = {
|
||||||
|
id: taskId,
|
||||||
|
slug: spec.slug,
|
||||||
|
goal: spec.goal,
|
||||||
|
project: spec.project,
|
||||||
|
status: "pending",
|
||||||
|
iteration: 0,
|
||||||
|
maxIterations: spec.maxIterations,
|
||||||
|
startedAt: null,
|
||||||
|
evals: {},
|
||||||
|
iterations: [],
|
||||||
|
spec,
|
||||||
|
};
|
||||||
|
|
||||||
|
await createTask(task);
|
||||||
|
|
||||||
|
await createEventLogEntry({
|
||||||
|
triggerId: trigger.id,
|
||||||
|
deliveryId: logDeliveryId,
|
||||||
|
eventType,
|
||||||
|
repo: event.repo,
|
||||||
|
commitSha: event.sha,
|
||||||
|
branch: event.branch,
|
||||||
|
status: "task_created",
|
||||||
|
taskId,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
|
||||||
|
results.push({ triggerId: trigger.id, taskId, status: "task_created" });
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
await createEventLogEntry({
|
||||||
|
triggerId: trigger.id,
|
||||||
|
deliveryId: logDeliveryId,
|
||||||
|
eventType,
|
||||||
|
repo: event.repo,
|
||||||
|
commitSha: event.sha,
|
||||||
|
branch: event.branch,
|
||||||
|
status: "error",
|
||||||
|
error: errorMsg,
|
||||||
|
payload,
|
||||||
|
}).catch(() => {});
|
||||||
|
|
||||||
|
results.push({ triggerId: trigger.id, taskId: null, status: "error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure orchestrator is running to pick up new tasks
|
||||||
|
if (results.some(r => r.status === "task_created")) {
|
||||||
|
startOrchestrator();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ status: "processed", results });
|
||||||
|
}
|
||||||
34
apps/harness/src/lib/event-matching.ts
Normal file
34
apps/harness/src/lib/event-matching.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Match webhook events against event trigger conditions.
|
||||||
|
|
||||||
|
import { getEnabledEventTriggers, type EventTrigger } from "./event-store";
|
||||||
|
import { type ParsedEvent } from "./template";
|
||||||
|
|
||||||
|
function escapeRegex(s: string): string {
|
||||||
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
|
function globMatch(pattern: string, value: string): boolean {
|
||||||
|
const regex = new RegExp(
|
||||||
|
"^" + pattern.split("*").map(escapeRegex).join(".*") + "$",
|
||||||
|
);
|
||||||
|
return regex.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function matchesTrigger(trigger: EventTrigger, event: ParsedEvent): boolean {
|
||||||
|
if (!trigger.enabled) return false;
|
||||||
|
|
||||||
|
if (trigger.eventType !== "*" && trigger.eventType !== event.eventType) return false;
|
||||||
|
|
||||||
|
if (trigger.repoFilter && !globMatch(trigger.repoFilter, event.repo)) return false;
|
||||||
|
|
||||||
|
if (trigger.stateFilter && trigger.stateFilter !== event.state) return false;
|
||||||
|
|
||||||
|
if (trigger.contextFilter && !event.context.includes(trigger.contextFilter)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findMatchingTriggers(event: ParsedEvent): Promise<EventTrigger[]> {
|
||||||
|
const triggers = await getEnabledEventTriggers();
|
||||||
|
return triggers.filter(t => matchesTrigger(t, event));
|
||||||
|
}
|
||||||
123
apps/harness/src/lib/event-store.ts
Normal file
123
apps/harness/src/lib/event-store.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { eq, desc } from "drizzle-orm";
|
||||||
|
import { db } from "./db";
|
||||||
|
import { eventTriggers, eventLog } from "@homelab/db";
|
||||||
|
|
||||||
|
// ─── TYPES ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type EventTrigger = typeof eventTriggers.$inferSelect;
|
||||||
|
export type EventTriggerInsert = typeof eventTriggers.$inferInsert;
|
||||||
|
export type EventLogEntry = typeof eventLog.$inferSelect;
|
||||||
|
|
||||||
|
// ─── EVENT TRIGGERS ─────────────────────────────────────────
|
||||||
|
|
||||||
|
export async function getAllEventTriggers(): Promise<EventTrigger[]> {
|
||||||
|
return db.select().from(eventTriggers);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEnabledEventTriggers(): Promise<EventTrigger[]> {
|
||||||
|
return db.select().from(eventTriggers).where(eq(eventTriggers.enabled, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEventTrigger(id: string): Promise<EventTrigger | undefined> {
|
||||||
|
const [row] = await db.select().from(eventTriggers).where(eq(eventTriggers.id, id));
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEventTrigger(trigger: EventTriggerInsert): Promise<EventTrigger> {
|
||||||
|
const [row] = await db.insert(eventTriggers).values(trigger).returning();
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateEventTrigger(
|
||||||
|
id: string,
|
||||||
|
updates: Partial<EventTriggerInsert>,
|
||||||
|
): Promise<EventTrigger | undefined> {
|
||||||
|
const [row] = await db
|
||||||
|
.update(eventTriggers)
|
||||||
|
.set({ ...updates, updatedAt: new Date() })
|
||||||
|
.where(eq(eventTriggers.id, id))
|
||||||
|
.returning();
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteEventTrigger(id: string): Promise<boolean> {
|
||||||
|
await db.delete(eventLog).where(eq(eventLog.triggerId, id));
|
||||||
|
const result = await db.delete(eventTriggers).where(eq(eventTriggers.id, id));
|
||||||
|
return (result as unknown as { rowCount: number }).rowCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── EVENT LOG ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
export async function createEventLogEntry(
|
||||||
|
entry: typeof eventLog.$inferInsert,
|
||||||
|
): Promise<EventLogEntry> {
|
||||||
|
const [row] = await db.insert(eventLog).values(entry).returning();
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEventLogByDeliveryId(
|
||||||
|
deliveryId: string,
|
||||||
|
): Promise<EventLogEntry | undefined> {
|
||||||
|
const [row] = await db
|
||||||
|
.select()
|
||||||
|
.from(eventLog)
|
||||||
|
.where(eq(eventLog.deliveryId, deliveryId));
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEventLogByTaskId(
|
||||||
|
taskId: string,
|
||||||
|
): Promise<EventLogEntry | undefined> {
|
||||||
|
const [row] = await db
|
||||||
|
.select()
|
||||||
|
.from(eventLog)
|
||||||
|
.where(eq(eventLog.taskId, taskId));
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEventLogByTrigger(
|
||||||
|
triggerId: string,
|
||||||
|
limit = 50,
|
||||||
|
offset = 0,
|
||||||
|
): Promise<EventLogEntry[]> {
|
||||||
|
return db
|
||||||
|
.select()
|
||||||
|
.from(eventLog)
|
||||||
|
.where(eq(eventLog.triggerId, triggerId))
|
||||||
|
.orderBy(desc(eventLog.createdAt))
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── CIRCUIT BREAKER ────────────────────────────────────────
|
||||||
|
|
||||||
|
export async function recordTaskOutcome(
|
||||||
|
triggerId: string,
|
||||||
|
passed: boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
const trigger = await getEventTrigger(triggerId);
|
||||||
|
if (!trigger) return;
|
||||||
|
|
||||||
|
if (passed) {
|
||||||
|
await db
|
||||||
|
.update(eventTriggers)
|
||||||
|
.set({ consecutiveFailures: 0, updatedAt: new Date() })
|
||||||
|
.where(eq(eventTriggers.id, triggerId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCount = trigger.consecutiveFailures + 1;
|
||||||
|
const shouldDisable = newCount >= trigger.maxConsecutiveFailures;
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(eventTriggers)
|
||||||
|
.set({
|
||||||
|
consecutiveFailures: newCount,
|
||||||
|
enabled: shouldDisable ? false : trigger.enabled,
|
||||||
|
disabledReason: shouldDisable
|
||||||
|
? `Circuit breaker: ${newCount} consecutive failures`
|
||||||
|
: trigger.disabledReason,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
})
|
||||||
|
.where(eq(eventTriggers.id, triggerId));
|
||||||
|
}
|
||||||
@@ -5,9 +5,15 @@ import {
|
|||||||
updateIteration,
|
updateIteration,
|
||||||
getFirstPendingTask,
|
getFirstPendingTask,
|
||||||
getRunningTasks,
|
getRunningTasks,
|
||||||
|
getOrchestratorState,
|
||||||
|
setOrchestratorRunning,
|
||||||
|
setOrchestratorCurrentTask,
|
||||||
|
updateOrchestratorHeartbeat,
|
||||||
|
isTaskCancelRequested,
|
||||||
} from "./store";
|
} from "./store";
|
||||||
import { recordUsage } from "./model-store";
|
import { recordUsage } from "./model-store";
|
||||||
import { getAgentConfig } from "./agents";
|
import { getAgentConfig } from "./agents";
|
||||||
|
import { getEventLogByTaskId, recordTaskOutcome } from "./event-store";
|
||||||
import { getRawCredentialsByProvider } from "./credentials";
|
import { getRawCredentialsByProvider } from "./credentials";
|
||||||
import {
|
import {
|
||||||
ensureBareClone,
|
ensureBareClone,
|
||||||
@@ -25,47 +31,48 @@ import { evaluate } from "./evaluator";
|
|||||||
import { Task, Iteration } from "./types";
|
import { Task, Iteration } from "./types";
|
||||||
|
|
||||||
const POLL_INTERVAL_MS = 2000;
|
const POLL_INTERVAL_MS = 2000;
|
||||||
|
const CANCEL_CHECK_INTERVAL_MS = 3000;
|
||||||
|
|
||||||
|
// Local process state — only the AbortController and poll timer live in memory.
|
||||||
|
// Everything else (running, currentTaskId) is in Postgres.
|
||||||
let pollTimer: ReturnType<typeof setInterval> | null = null;
|
let pollTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
let running = false;
|
|
||||||
let currentTaskId: string | null = null;
|
|
||||||
let currentAbort: AbortController | null = null;
|
let currentAbort: AbortController | null = null;
|
||||||
|
|
||||||
export function isRunning(): boolean {
|
// ─── Public API (all DB-backed) ─────────────────────────────
|
||||||
return running;
|
|
||||||
|
export async function isRunning(): Promise<boolean> {
|
||||||
|
const state = await getOrchestratorState();
|
||||||
|
return state.running;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function currentRunningTaskId(): string | null {
|
export async function currentRunningTaskId(): Promise<string | null> {
|
||||||
return currentTaskId;
|
const state = await getOrchestratorState();
|
||||||
|
return state.currentTaskId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startOrchestrator(): void {
|
export async function startOrchestrator(): Promise<void> {
|
||||||
if (running) return;
|
const state = await getOrchestratorState();
|
||||||
running = true;
|
if (state.running && pollTimer) return;
|
||||||
|
|
||||||
recoverCrashedTasks();
|
await setOrchestratorRunning(true);
|
||||||
|
await recoverCrashedTasks();
|
||||||
|
|
||||||
pollTimer = setInterval(() => {
|
pollTimer = setInterval(() => {
|
||||||
if (currentTaskId) return;
|
|
||||||
poll();
|
poll();
|
||||||
}, POLL_INTERVAL_MS);
|
}, POLL_INTERVAL_MS);
|
||||||
|
|
||||||
poll();
|
poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stopOrchestrator(): void {
|
export async function stopOrchestrator(): Promise<void> {
|
||||||
running = false;
|
await setOrchestratorRunning(false);
|
||||||
if (pollTimer) {
|
if (pollTimer) {
|
||||||
clearInterval(pollTimer);
|
clearInterval(pollTimer);
|
||||||
pollTimer = null;
|
pollTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cancelTask(taskId: string): boolean {
|
// ─── Internals ──────────────────────────────────────────────
|
||||||
if (currentTaskId !== taskId) return false;
|
|
||||||
currentAbort?.abort();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function recoverCrashedTasks(): Promise<void> {
|
async function recoverCrashedTasks(): Promise<void> {
|
||||||
const runningTasks = await getRunningTasks();
|
const runningTasks = await getRunningTasks();
|
||||||
@@ -84,15 +91,17 @@ async function recoverCrashedTasks(): Promise<void> {
|
|||||||
completedAt: Date.now(),
|
completedAt: Date.now(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await setOrchestratorCurrentTask(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function poll(): Promise<void> {
|
async function poll(): Promise<void> {
|
||||||
if (!running || currentTaskId) return;
|
const state = await getOrchestratorState();
|
||||||
|
if (!state.running || state.currentTaskId) return;
|
||||||
|
|
||||||
const task = await getFirstPendingTask();
|
const task = await getFirstPendingTask();
|
||||||
if (!task) return;
|
if (!task) return;
|
||||||
|
|
||||||
currentTaskId = task.id;
|
await setOrchestratorCurrentTask(task.id);
|
||||||
currentAbort = new AbortController();
|
currentAbort = new AbortController();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -104,11 +113,43 @@ async function poll(): Promise<void> {
|
|||||||
completedAt: Date.now(),
|
completedAt: Date.now(),
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
currentTaskId = null;
|
// Circuit breaker: update event trigger if this task was event-driven
|
||||||
|
try {
|
||||||
|
const finalTask = await getTask(task.id);
|
||||||
|
if (finalTask && (finalTask.status === "completed" || finalTask.status === "failed")) {
|
||||||
|
const logEntry = await getEventLogByTaskId(task.id);
|
||||||
|
if (logEntry) {
|
||||||
|
await recordTaskOutcome(logEntry.triggerId, finalTask.status === "completed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[orchestrator] Circuit breaker update failed:", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
await setOrchestratorCurrentTask(null);
|
||||||
currentAbort = null;
|
currentAbort = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodically checks the cancel_requested flag in Postgres and triggers
|
||||||
|
* the local AbortController if set. Returns a cleanup function.
|
||||||
|
*/
|
||||||
|
function startCancelWatcher(taskId: string, abort: AbortController): () => void {
|
||||||
|
const timer = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
if (await isTaskCancelRequested(taskId)) {
|
||||||
|
abort.abort();
|
||||||
|
clearInterval(timer);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// DB read failure — don't crash the watcher, just retry next tick
|
||||||
|
}
|
||||||
|
}, CANCEL_CHECK_INTERVAL_MS);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}
|
||||||
|
|
||||||
async function runTask(task: Task): Promise<void> {
|
async function runTask(task: Task): Promise<void> {
|
||||||
const agentConfig = await getAgentConfig(task.spec.agentId);
|
const agentConfig = await getAgentConfig(task.spec.agentId);
|
||||||
if (!agentConfig) {
|
if (!agentConfig) {
|
||||||
@@ -138,11 +179,15 @@ async function runTask(task: Task): Promise<void> {
|
|||||||
startedAt: Date.now(),
|
startedAt: Date.now(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Start watching for cancel_requested in DB
|
||||||
|
const stopCancelWatcher = startCancelWatcher(task.id, currentAbort!);
|
||||||
|
|
||||||
let bareClone: string;
|
let bareClone: string;
|
||||||
try {
|
try {
|
||||||
bareClone = await ensureBareClone(repoUrl, task.slug);
|
bareClone = await ensureBareClone(repoUrl, task.slug);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[orchestrator] Failed to clone repo for task ${task.id}:`, err);
|
console.error(`[orchestrator] Failed to clone repo for task ${task.id}:`, err);
|
||||||
|
stopCancelWatcher();
|
||||||
await updateTask(task.id, {
|
await updateTask(task.id, {
|
||||||
status: "failed",
|
status: "failed",
|
||||||
completedAt: Date.now(),
|
completedAt: Date.now(),
|
||||||
@@ -159,11 +204,17 @@ async function runTask(task: Task): Promise<void> {
|
|||||||
status: "failed",
|
status: "failed",
|
||||||
completedAt: Date.now(),
|
completedAt: Date.now(),
|
||||||
});
|
});
|
||||||
|
stopCancelWatcher();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await updateOrchestratorHeartbeat();
|
||||||
|
|
||||||
const result = await runIteration(task, n, bareClone, branchName);
|
const result = await runIteration(task, n, bareClone, branchName);
|
||||||
if (!result) return;
|
if (!result) {
|
||||||
|
stopCancelWatcher();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.allPassed) {
|
if (result.allPassed) {
|
||||||
converged = true;
|
converged = true;
|
||||||
@@ -171,6 +222,8 @@ async function runTask(task: Task): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopCancelWatcher();
|
||||||
|
|
||||||
if (converged) {
|
if (converged) {
|
||||||
try {
|
try {
|
||||||
const finalTask = await getTask(task.id);
|
const finalTask = await getTask(task.id);
|
||||||
|
|||||||
@@ -1,8 +1,77 @@
|
|||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { db } from "./db";
|
import { db } from "./db";
|
||||||
import { tasks as tasksTable, iterations as iterationsTable } from "@homelab/db";
|
import {
|
||||||
|
tasks as tasksTable,
|
||||||
|
iterations as iterationsTable,
|
||||||
|
orchestratorState as orchTable,
|
||||||
|
} from "@homelab/db";
|
||||||
import { Task, Iteration } from "./types";
|
import { Task, Iteration } from "./types";
|
||||||
|
|
||||||
|
// ─── ORCHESTRATOR STATE ─────────────────────────────────────
|
||||||
|
|
||||||
|
export interface OrchestratorState {
|
||||||
|
running: boolean;
|
||||||
|
currentTaskId: string | null;
|
||||||
|
heartbeat: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureOrchestratorRow(): Promise<void> {
|
||||||
|
await db
|
||||||
|
.insert(orchTable)
|
||||||
|
.values({ id: "singleton", running: false })
|
||||||
|
.onConflictDoNothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getOrchestratorState(): Promise<OrchestratorState> {
|
||||||
|
await ensureOrchestratorRow();
|
||||||
|
const [row] = await db.select().from(orchTable).where(eq(orchTable.id, "singleton"));
|
||||||
|
return {
|
||||||
|
running: row.running,
|
||||||
|
currentTaskId: row.currentTaskId ?? null,
|
||||||
|
heartbeat: row.heartbeat ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setOrchestratorRunning(running: boolean): Promise<void> {
|
||||||
|
await ensureOrchestratorRow();
|
||||||
|
await db
|
||||||
|
.update(orchTable)
|
||||||
|
.set({ running, updatedAt: new Date() })
|
||||||
|
.where(eq(orchTable.id, "singleton"));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setOrchestratorCurrentTask(taskId: string | null): Promise<void> {
|
||||||
|
await db
|
||||||
|
.update(orchTable)
|
||||||
|
.set({ currentTaskId: taskId, heartbeat: Date.now(), updatedAt: new Date() })
|
||||||
|
.where(eq(orchTable.id, "singleton"));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateOrchestratorHeartbeat(): Promise<void> {
|
||||||
|
await db
|
||||||
|
.update(orchTable)
|
||||||
|
.set({ heartbeat: Date.now(), updatedAt: new Date() })
|
||||||
|
.where(eq(orchTable.id, "singleton"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── TASK CANCEL FLAG ───────────────────────────────────────
|
||||||
|
|
||||||
|
export async function requestTaskCancel(taskId: string): Promise<boolean> {
|
||||||
|
const result = await db
|
||||||
|
.update(tasksTable)
|
||||||
|
.set({ cancelRequested: true, updatedAt: new Date() })
|
||||||
|
.where(and(eq(tasksTable.id, taskId), eq(tasksTable.status, "running")));
|
||||||
|
return (result as unknown as { rowCount: number }).rowCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isTaskCancelRequested(taskId: string): Promise<boolean> {
|
||||||
|
const [row] = await db
|
||||||
|
.select({ cancelRequested: tasksTable.cancelRequested })
|
||||||
|
.from(tasksTable)
|
||||||
|
.where(eq(tasksTable.id, taskId));
|
||||||
|
return row?.cancelRequested ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
function rowToTask(
|
function rowToTask(
|
||||||
row: typeof tasksTable.$inferSelect,
|
row: typeof tasksTable.$inferSelect,
|
||||||
iters: (typeof iterationsTable.$inferSelect)[],
|
iters: (typeof iterationsTable.$inferSelect)[],
|
||||||
|
|||||||
95
apps/harness/src/lib/template.ts
Normal file
95
apps/harness/src/lib/template.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Template variable extraction and substitution for event-driven tasks.
|
||||||
|
|
||||||
|
export interface ParsedEvent {
|
||||||
|
eventType: string;
|
||||||
|
repo: string;
|
||||||
|
repoName: string;
|
||||||
|
owner: string;
|
||||||
|
sha: string;
|
||||||
|
shaShort: string;
|
||||||
|
branch: string;
|
||||||
|
state: string;
|
||||||
|
context: string;
|
||||||
|
targetUrl: string;
|
||||||
|
commitMessage: string;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractVariables(
|
||||||
|
eventType: string,
|
||||||
|
payload: Record<string, unknown>,
|
||||||
|
): ParsedEvent {
|
||||||
|
const repository = payload.repository as Record<string, unknown> | undefined;
|
||||||
|
const owner = repository?.owner as Record<string, unknown> | undefined;
|
||||||
|
const commit = payload.commit as Record<string, unknown> | undefined;
|
||||||
|
const branches = payload.branches as { name: string }[] | undefined;
|
||||||
|
const sha = String(payload.sha || "");
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventType,
|
||||||
|
repo: String(repository?.full_name || ""),
|
||||||
|
repoName: String(repository?.name || ""),
|
||||||
|
owner: String(owner?.login || ""),
|
||||||
|
sha,
|
||||||
|
shaShort: sha.slice(0, 7),
|
||||||
|
branch: branches?.[0]?.name || "",
|
||||||
|
state: String(payload.state || ""),
|
||||||
|
context: String(payload.context || ""),
|
||||||
|
targetUrl: String(payload.target_url || ""),
|
||||||
|
commitMessage: String(commit?.message || payload.description || ""),
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderTemplate(
|
||||||
|
template: string,
|
||||||
|
vars: ParsedEvent,
|
||||||
|
): string {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
repo: vars.repo,
|
||||||
|
repo_name: vars.repoName,
|
||||||
|
owner: vars.owner,
|
||||||
|
sha: vars.sha,
|
||||||
|
sha_short: vars.shaShort,
|
||||||
|
branch: vars.branch,
|
||||||
|
state: vars.state,
|
||||||
|
context: vars.context,
|
||||||
|
target_url: vars.targetUrl,
|
||||||
|
commit_message: vars.commitMessage,
|
||||||
|
timestamp: vars.timestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => map[key] ?? match);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderTaskTemplate(
|
||||||
|
template: {
|
||||||
|
slug: string;
|
||||||
|
goal: string;
|
||||||
|
project: string;
|
||||||
|
gitProvider?: string;
|
||||||
|
gitBaseUrl?: string;
|
||||||
|
agentId: string;
|
||||||
|
maxIterations: number;
|
||||||
|
criteria: { label: string; target: string }[];
|
||||||
|
constraints: string[];
|
||||||
|
knowledgeRefs: string[];
|
||||||
|
},
|
||||||
|
vars: ParsedEvent,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
slug: renderTemplate(template.slug, vars),
|
||||||
|
goal: renderTemplate(template.goal, vars),
|
||||||
|
project: renderTemplate(template.project, vars),
|
||||||
|
gitProvider: template.gitProvider as "github" | "gitlab" | "gitea" | undefined,
|
||||||
|
gitBaseUrl: template.gitBaseUrl,
|
||||||
|
agentId: template.agentId,
|
||||||
|
maxIterations: template.maxIterations,
|
||||||
|
criteria: template.criteria.map(c => ({
|
||||||
|
label: renderTemplate(c.label, vars),
|
||||||
|
target: renderTemplate(c.target, vars),
|
||||||
|
})),
|
||||||
|
constraints: template.constraints.map(c => renderTemplate(c, vars)),
|
||||||
|
knowledgeRefs: template.knowledgeRefs.map(k => renderTemplate(k, vars)),
|
||||||
|
};
|
||||||
|
}
|
||||||
383
apps/harness/src/mcp-server.ts
Normal file
383
apps/harness/src/mcp-server.ts
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Harness MCP Server
|
||||||
|
*
|
||||||
|
* Stdio MCP server that exposes harness knowledge management and task
|
||||||
|
* orchestration tools to agents spawned by the harness orchestrator.
|
||||||
|
*
|
||||||
|
* Environment variables:
|
||||||
|
* DATABASE_URL — Postgres connection string (required)
|
||||||
|
* HARNESS_KNOWLEDGE_DIR — Path to knowledge documents directory
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
|
import { eq, and } from "drizzle-orm";
|
||||||
|
import postgres from "postgres";
|
||||||
|
import {
|
||||||
|
tasks as tasksTable,
|
||||||
|
iterations as iterationsTable,
|
||||||
|
agentConfigs as agentTable,
|
||||||
|
curatedModels as modelsTable,
|
||||||
|
orchestratorState as orchTable,
|
||||||
|
} from "@homelab/db";
|
||||||
|
import { readFile, writeFile, readdir, mkdir, stat } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
// ── Database ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const connectionString = process.env.DATABASE_URL;
|
||||||
|
if (!connectionString) {
|
||||||
|
console.error("DATABASE_URL is required");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = postgres(connectionString);
|
||||||
|
const db = drizzle(client);
|
||||||
|
|
||||||
|
// ── Knowledge dir ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
const KNOWLEDGE_DIR = process.env.HARNESS_KNOWLEDGE_DIR || "";
|
||||||
|
|
||||||
|
// ── Helpers ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function taskSummary(row: typeof tasksTable.$inferSelect) {
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
slug: row.slug,
|
||||||
|
goal: row.goal,
|
||||||
|
status: row.status,
|
||||||
|
project: row.project,
|
||||||
|
iteration: row.iteration,
|
||||||
|
maxIterations: row.maxIterations,
|
||||||
|
startedAt: row.startedAt,
|
||||||
|
completedAt: row.completedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Server ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const server = new McpServer({
|
||||||
|
name: "harness",
|
||||||
|
version: "1.0.0",
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Knowledge Tools ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"knowledge_list",
|
||||||
|
"List all knowledge documents available in the harness knowledge base",
|
||||||
|
async () => {
|
||||||
|
if (!KNOWLEDGE_DIR) {
|
||||||
|
return { content: [{ type: "text", text: "HARNESS_KNOWLEDGE_DIR not configured" }] };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const entries = await readdir(KNOWLEDGE_DIR, { withFileTypes: true });
|
||||||
|
const files = entries
|
||||||
|
.filter((e) => e.isFile())
|
||||||
|
.map((e) => e.name);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: files.length > 0 ? files.join("\n") : "(empty)" }],
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return { content: [{ type: "text", text: `Error listing knowledge dir: ${err}` }], isError: true };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"knowledge_read",
|
||||||
|
"Read a specific knowledge document by filename",
|
||||||
|
{ filename: z.string().describe("Filename of the knowledge document to read") },
|
||||||
|
async ({ filename }) => {
|
||||||
|
if (!KNOWLEDGE_DIR) {
|
||||||
|
return { content: [{ type: "text", text: "HARNESS_KNOWLEDGE_DIR not configured" }], isError: true };
|
||||||
|
}
|
||||||
|
const filePath = path.resolve(KNOWLEDGE_DIR, filename);
|
||||||
|
// Prevent path traversal
|
||||||
|
if (!filePath.startsWith(path.resolve(KNOWLEDGE_DIR))) {
|
||||||
|
return { content: [{ type: "text", text: "Invalid path" }], isError: true };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const content = await readFile(filePath, "utf-8");
|
||||||
|
return { content: [{ type: "text", text: content }] };
|
||||||
|
} catch (err) {
|
||||||
|
return { content: [{ type: "text", text: `Error reading ${filename}: ${err}` }], isError: true };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"knowledge_write",
|
||||||
|
"Create or update a knowledge document in the harness knowledge base",
|
||||||
|
{
|
||||||
|
filename: z.string().describe("Filename for the knowledge document (e.g. 'findings.md')"),
|
||||||
|
content: z.string().describe("Content to write to the knowledge document"),
|
||||||
|
},
|
||||||
|
async ({ filename, content }) => {
|
||||||
|
if (!KNOWLEDGE_DIR) {
|
||||||
|
return { content: [{ type: "text", text: "HARNESS_KNOWLEDGE_DIR not configured" }], isError: true };
|
||||||
|
}
|
||||||
|
const filePath = path.resolve(KNOWLEDGE_DIR, filename);
|
||||||
|
if (!filePath.startsWith(path.resolve(KNOWLEDGE_DIR))) {
|
||||||
|
return { content: [{ type: "text", text: "Invalid path" }], isError: true };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await mkdir(path.dirname(filePath), { recursive: true });
|
||||||
|
await writeFile(filePath, content, "utf-8");
|
||||||
|
return { content: [{ type: "text", text: `Wrote ${filename} (${content.length} bytes)` }] };
|
||||||
|
} catch (err) {
|
||||||
|
return { content: [{ type: "text", text: `Error writing ${filename}: ${err}` }], isError: true };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"knowledge_search",
|
||||||
|
"Search across all knowledge documents for a text pattern (case-insensitive substring match)",
|
||||||
|
{ query: z.string().describe("Text to search for across all knowledge documents") },
|
||||||
|
async ({ query }) => {
|
||||||
|
if (!KNOWLEDGE_DIR) {
|
||||||
|
return { content: [{ type: "text", text: "HARNESS_KNOWLEDGE_DIR not configured" }], isError: true };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const entries = await readdir(KNOWLEDGE_DIR, { withFileTypes: true });
|
||||||
|
const files = entries.filter((e) => e.isFile());
|
||||||
|
const results: string[] = [];
|
||||||
|
const lowerQuery = query.toLowerCase();
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(KNOWLEDGE_DIR, file.name);
|
||||||
|
const content = await readFile(filePath, "utf-8");
|
||||||
|
const lines = content.split("\n");
|
||||||
|
const matches = lines
|
||||||
|
.map((line, i) => ({ line, lineNum: i + 1 }))
|
||||||
|
.filter(({ line }) => line.toLowerCase().includes(lowerQuery));
|
||||||
|
|
||||||
|
if (matches.length > 0) {
|
||||||
|
results.push(
|
||||||
|
`## ${file.name}\n` +
|
||||||
|
matches
|
||||||
|
.slice(0, 10)
|
||||||
|
.map(({ line, lineNum }) => ` L${lineNum}: ${line.trim()}`)
|
||||||
|
.join("\n") +
|
||||||
|
(matches.length > 10 ? `\n ... and ${matches.length - 10} more matches` : ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
type: "text",
|
||||||
|
text: results.length > 0 ? results.join("\n\n") : `No matches for "${query}"`,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
return { content: [{ type: "text", text: `Error searching: ${err}` }], isError: true };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Task Tools ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"task_list",
|
||||||
|
"List all harness tasks with their current status and evaluation results",
|
||||||
|
async () => {
|
||||||
|
const rows = await db.select().from(tasksTable);
|
||||||
|
const tasks = rows.map(taskSummary);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"task_get",
|
||||||
|
"Get full details for a harness task including iteration history and evaluations",
|
||||||
|
{ taskId: z.string().describe("Task ID to look up") },
|
||||||
|
async ({ taskId }) => {
|
||||||
|
const [taskRow] = await db.select().from(tasksTable).where(eq(tasksTable.id, taskId));
|
||||||
|
if (!taskRow) {
|
||||||
|
return { content: [{ type: "text", text: `Task ${taskId} not found` }], isError: true };
|
||||||
|
}
|
||||||
|
const iters = await db
|
||||||
|
.select()
|
||||||
|
.from(iterationsTable)
|
||||||
|
.where(eq(iterationsTable.taskId, taskId));
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
...taskSummary(taskRow),
|
||||||
|
spec: taskRow.spec,
|
||||||
|
evals: taskRow.evals,
|
||||||
|
pr: taskRow.pr,
|
||||||
|
iterations: iters
|
||||||
|
.sort((a, b) => a.n - b.n)
|
||||||
|
.map((i) => ({
|
||||||
|
n: i.n,
|
||||||
|
status: i.status,
|
||||||
|
diagnosis: i.diagnosis,
|
||||||
|
evals: i.evals,
|
||||||
|
diffStats: i.diffStats,
|
||||||
|
agentOutput: i.agentOutput ? i.agentOutput.slice(-4000) : null,
|
||||||
|
startedAt: i.startedAt,
|
||||||
|
completedAt: i.completedAt,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"task_create",
|
||||||
|
"Create a new harness task. The task will be created in 'pending' status and can be started with task_start.",
|
||||||
|
{
|
||||||
|
slug: z.string().describe("Unique short identifier for the task (e.g. 'fix-auth-bug')"),
|
||||||
|
goal: z.string().describe("High-level description of what the task should accomplish"),
|
||||||
|
project: z.string().describe("Repository in 'owner/repo' format"),
|
||||||
|
agentId: z.string().describe("ID of the agent configuration to use"),
|
||||||
|
maxIterations: z.number().optional().describe("Maximum iterations before giving up (default: 6)"),
|
||||||
|
criteria: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
label: z.string().describe("Criterion name"),
|
||||||
|
target: z.string().describe("Evaluation target DSL (e.g. 'exitCode:0', 'filesChanged:>0')"),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.describe("Success criteria for evaluation"),
|
||||||
|
constraints: z.array(z.string()).optional().describe("Implementation constraints"),
|
||||||
|
knowledgeRefs: z.array(z.string()).optional().describe("Knowledge document filenames to include in prompt"),
|
||||||
|
gitProvider: z.enum(["github", "gitlab", "gitea"]).optional().describe("Git provider (default: github)"),
|
||||||
|
gitBaseUrl: z.string().optional().describe("Base URL for the git provider API"),
|
||||||
|
},
|
||||||
|
async (args) => {
|
||||||
|
const spec = {
|
||||||
|
slug: args.slug,
|
||||||
|
goal: args.goal,
|
||||||
|
project: args.project,
|
||||||
|
agentId: args.agentId,
|
||||||
|
maxIterations: args.maxIterations ?? 6,
|
||||||
|
criteria: args.criteria ?? [],
|
||||||
|
constraints: args.constraints ?? [],
|
||||||
|
knowledgeRefs: args.knowledgeRefs ?? [],
|
||||||
|
gitProvider: args.gitProvider,
|
||||||
|
gitBaseUrl: args.gitBaseUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskId = `task-${Date.now()}`;
|
||||||
|
await db.insert(tasksTable).values({
|
||||||
|
id: taskId,
|
||||||
|
slug: spec.slug,
|
||||||
|
goal: spec.goal,
|
||||||
|
project: spec.project,
|
||||||
|
status: "pending",
|
||||||
|
iteration: 0,
|
||||||
|
maxIterations: spec.maxIterations,
|
||||||
|
startedAt: null,
|
||||||
|
evals: {},
|
||||||
|
spec,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify({ id: taskId, status: "pending", slug: spec.slug }) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"task_start",
|
||||||
|
"Ensure the orchestrator is running so it will pick up pending tasks. Sets orchestrator state to running in the database.",
|
||||||
|
async () => {
|
||||||
|
// Ensure the singleton row exists
|
||||||
|
await db
|
||||||
|
.insert(orchTable)
|
||||||
|
.values({ id: "singleton", running: false })
|
||||||
|
.onConflictDoNothing();
|
||||||
|
// Set running
|
||||||
|
await db
|
||||||
|
.update(orchTable)
|
||||||
|
.set({ running: true, updatedAt: new Date() })
|
||||||
|
.where(eq(orchTable.id, "singleton"));
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify({ ok: true, message: "Orchestrator set to running — pending tasks will be picked up" }) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"task_stop",
|
||||||
|
"Request cancellation of a running harness task. Sets cancel_requested flag which the orchestrator polls.",
|
||||||
|
{ taskId: z.string().describe("ID of the running task to cancel") },
|
||||||
|
async ({ taskId }) => {
|
||||||
|
const result = await db
|
||||||
|
.update(tasksTable)
|
||||||
|
.set({ cancelRequested: true, updatedAt: new Date() })
|
||||||
|
.where(and(eq(tasksTable.id, taskId), eq(tasksTable.status, "running")));
|
||||||
|
|
||||||
|
const rowCount = (result as unknown as { rowCount: number }).rowCount;
|
||||||
|
if (rowCount === 0) {
|
||||||
|
return { content: [{ type: "text", text: `Task ${taskId} is not running or not found` }], isError: true };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify({ ok: true, message: "Cancellation requested" }) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Agent & Model Tools ─────────────────────────────────────────
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"agent_list",
|
||||||
|
"List all configured agent runtimes (agent configs with runtime, model, and provider)",
|
||||||
|
async () => {
|
||||||
|
const rows = await db.select().from(agentTable);
|
||||||
|
const agents = rows.map((r) => ({
|
||||||
|
id: r.id,
|
||||||
|
name: r.name,
|
||||||
|
runtime: r.runtime,
|
||||||
|
modelId: r.modelId,
|
||||||
|
provider: r.provider,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(agents, null, 2) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"model_list",
|
||||||
|
"List available AI models with pricing information",
|
||||||
|
async () => {
|
||||||
|
const rows = await db.select().from(modelsTable).where(eq(modelsTable.enabled, true));
|
||||||
|
const models = rows.map((r) => ({
|
||||||
|
id: r.id,
|
||||||
|
name: r.name,
|
||||||
|
provider: r.provider,
|
||||||
|
contextWindow: r.contextWindow,
|
||||||
|
costPer1kInput: r.costPer1kInput,
|
||||||
|
costPer1kOutput: r.costPer1kOutput,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(models, null, 2) }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Start ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
|
await server.connect(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error("Harness MCP server failed:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
43
packages/db/drizzle/0001_stale_pride.sql
Normal file
43
packages/db/drizzle/0001_stale_pride.sql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "harness_event_log" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"trigger_id" text NOT NULL,
|
||||||
|
"delivery_id" text NOT NULL,
|
||||||
|
"event_type" text NOT NULL,
|
||||||
|
"repo" text NOT NULL,
|
||||||
|
"commit_sha" text,
|
||||||
|
"branch" text,
|
||||||
|
"status" text DEFAULT 'received' NOT NULL,
|
||||||
|
"task_id" text,
|
||||||
|
"skip_reason" text,
|
||||||
|
"error" text,
|
||||||
|
"payload" jsonb NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT "harness_event_log_delivery_id_unique" UNIQUE("delivery_id")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE IF NOT EXISTS "harness_event_triggers" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"enabled" boolean DEFAULT true NOT NULL,
|
||||||
|
"event_type" text NOT NULL,
|
||||||
|
"repo_filter" text,
|
||||||
|
"state_filter" text,
|
||||||
|
"context_filter" text,
|
||||||
|
"task_template" jsonb NOT NULL,
|
||||||
|
"consecutive_failures" integer DEFAULT 0 NOT NULL,
|
||||||
|
"max_consecutive_failures" integer DEFAULT 3 NOT NULL,
|
||||||
|
"disabled_reason" text,
|
||||||
|
"webhook_secret" text,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE IF NOT EXISTS "harness_orchestrator" (
|
||||||
|
"id" text PRIMARY KEY DEFAULT 'singleton' NOT NULL,
|
||||||
|
"running" boolean DEFAULT false NOT NULL,
|
||||||
|
"current_task_id" text,
|
||||||
|
"heartbeat" bigint,
|
||||||
|
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "harness_tasks" ADD COLUMN "cancel_requested" boolean DEFAULT false NOT NULL;
|
||||||
775
packages/db/drizzle/meta/0001_snapshot.json
Normal file
775
packages/db/drizzle/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,775 @@
|
|||||||
|
{
|
||||||
|
"id": "e84e33a6-8185-4839-ad18-37149f19eb32",
|
||||||
|
"prevId": "629b632c-cf1f-46ff-bb2f-e47d2343ddbd",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.harness_agent_configs": {
|
||||||
|
"name": "harness_agent_configs",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"name": "runtime",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"model_id": {
|
||||||
|
"name": "model_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"name": "provider",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"max_tokens": {
|
||||||
|
"name": "max_tokens",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"name": "env",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_credentials": {
|
||||||
|
"name": "harness_credentials",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"name": "provider",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"name": "label",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"name": "token",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"base_url": {
|
||||||
|
"name": "base_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_curated_models": {
|
||||||
|
"name": "harness_curated_models",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"name": "provider",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"context_window": {
|
||||||
|
"name": "context_window",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"cost_per_1k_input": {
|
||||||
|
"name": "cost_per_1k_input",
|
||||||
|
"type": "real",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"cost_per_1k_output": {
|
||||||
|
"name": "cost_per_1k_output",
|
||||||
|
"type": "real",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_event_log": {
|
||||||
|
"name": "harness_event_log",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"trigger_id": {
|
||||||
|
"name": "trigger_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"delivery_id": {
|
||||||
|
"name": "delivery_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"event_type": {
|
||||||
|
"name": "event_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"repo": {
|
||||||
|
"name": "repo",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"commit_sha": {
|
||||||
|
"name": "commit_sha",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"branch": {
|
||||||
|
"name": "branch",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'received'"
|
||||||
|
},
|
||||||
|
"task_id": {
|
||||||
|
"name": "task_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"skip_reason": {
|
||||||
|
"name": "skip_reason",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"name": "error",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"payload": {
|
||||||
|
"name": "payload",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"harness_event_log_delivery_id_unique": {
|
||||||
|
"name": "harness_event_log_delivery_id_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"delivery_id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_event_triggers": {
|
||||||
|
"name": "harness_event_triggers",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"name": "enabled",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
"event_type": {
|
||||||
|
"name": "event_type",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"repo_filter": {
|
||||||
|
"name": "repo_filter",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"state_filter": {
|
||||||
|
"name": "state_filter",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"context_filter": {
|
||||||
|
"name": "context_filter",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"task_template": {
|
||||||
|
"name": "task_template",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"consecutive_failures": {
|
||||||
|
"name": "consecutive_failures",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"max_consecutive_failures": {
|
||||||
|
"name": "max_consecutive_failures",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 3
|
||||||
|
},
|
||||||
|
"disabled_reason": {
|
||||||
|
"name": "disabled_reason",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"webhook_secret": {
|
||||||
|
"name": "webhook_secret",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_iterations": {
|
||||||
|
"name": "harness_iterations",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"task_id": {
|
||||||
|
"name": "task_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"n": {
|
||||||
|
"name": "n",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"diagnosis": {
|
||||||
|
"name": "diagnosis",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"agent_output": {
|
||||||
|
"name": "agent_output",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"evals": {
|
||||||
|
"name": "evals",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"diff_stats": {
|
||||||
|
"name": "diff_stats",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"started_at": {
|
||||||
|
"name": "started_at",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"completed_at": {
|
||||||
|
"name": "completed_at",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_model_usage": {
|
||||||
|
"name": "harness_model_usage",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"model_id": {
|
||||||
|
"name": "model_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"name": "provider",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"task_id": {
|
||||||
|
"name": "task_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"task_slug": {
|
||||||
|
"name": "task_slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"iteration": {
|
||||||
|
"name": "iteration",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"input_tokens": {
|
||||||
|
"name": "input_tokens",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"output_tokens": {
|
||||||
|
"name": "output_tokens",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"duration_ms": {
|
||||||
|
"name": "duration_ms",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"timestamp": {
|
||||||
|
"name": "timestamp",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_orchestrator": {
|
||||||
|
"name": "harness_orchestrator",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'singleton'"
|
||||||
|
},
|
||||||
|
"running": {
|
||||||
|
"name": "running",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"current_task_id": {
|
||||||
|
"name": "current_task_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"heartbeat": {
|
||||||
|
"name": "heartbeat",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.harness_tasks": {
|
||||||
|
"name": "harness_tasks",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"name": "slug",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"goal": {
|
||||||
|
"name": "goal",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"iteration": {
|
||||||
|
"name": "iteration",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"max_iterations": {
|
||||||
|
"name": "max_iterations",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 6
|
||||||
|
},
|
||||||
|
"started_at": {
|
||||||
|
"name": "started_at",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"completed_at": {
|
||||||
|
"name": "completed_at",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"name": "project",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'—'"
|
||||||
|
},
|
||||||
|
"evals": {
|
||||||
|
"name": "evals",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "'{}'::jsonb"
|
||||||
|
},
|
||||||
|
"pr": {
|
||||||
|
"name": "pr",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"cancel_requested": {
|
||||||
|
"name": "cancel_requested",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"name": "spec",
|
||||||
|
"type": "jsonb",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,13 @@
|
|||||||
"when": 1774124029586,
|
"when": 1774124029586,
|
||||||
"tag": "0000_sparkling_gressill",
|
"tag": "0000_sparkling_gressill",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1774127485727,
|
||||||
|
"tag": "0001_stale_pride",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,16 @@ export const agentConfigs = pgTable("harness_agent_configs", {
|
|||||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── HARNESS: ORCHESTRATOR STATE ──────────────────────────
|
||||||
|
|
||||||
|
export const orchestratorState = pgTable("harness_orchestrator", {
|
||||||
|
id: text("id").primaryKey().default("singleton"),
|
||||||
|
running: boolean("running").default(false).notNull(),
|
||||||
|
currentTaskId: text("current_task_id"),
|
||||||
|
heartbeat: bigint("heartbeat", { mode: "number" }),
|
||||||
|
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
// ─── HARNESS: TASKS ────────────────────────────────────────
|
// ─── HARNESS: TASKS ────────────────────────────────────────
|
||||||
|
|
||||||
export const tasks = pgTable("harness_tasks", {
|
export const tasks = pgTable("harness_tasks", {
|
||||||
@@ -89,6 +99,7 @@ export const tasks = pgTable("harness_tasks", {
|
|||||||
project: text("project").notNull().default("—"),
|
project: text("project").notNull().default("—"),
|
||||||
evals: jsonb("evals").$type<Record<string, unknown>>().default({}).notNull(),
|
evals: jsonb("evals").$type<Record<string, unknown>>().default({}).notNull(),
|
||||||
pr: jsonb("pr").$type<{ number: number; title: string; status: string }>(),
|
pr: jsonb("pr").$type<{ number: number; title: string; status: string }>(),
|
||||||
|
cancelRequested: boolean("cancel_requested").default(false).notNull(),
|
||||||
spec: jsonb("spec").$type<{
|
spec: jsonb("spec").$type<{
|
||||||
slug: string;
|
slug: string;
|
||||||
goal: string;
|
goal: string;
|
||||||
@@ -117,3 +128,60 @@ export const iterations = pgTable("harness_iterations", {
|
|||||||
startedAt: bigint("started_at", { mode: "number" }),
|
startedAt: bigint("started_at", { mode: "number" }),
|
||||||
completedAt: bigint("completed_at", { mode: "number" }),
|
completedAt: bigint("completed_at", { mode: "number" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── HARNESS: EVENT TRIGGERS ───────────────────────────────
|
||||||
|
|
||||||
|
export const eventTriggers = pgTable("harness_event_triggers", {
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
name: text("name").notNull(),
|
||||||
|
enabled: boolean("enabled").default(true).notNull(),
|
||||||
|
|
||||||
|
// Matching conditions (all AND'd, null = any)
|
||||||
|
eventType: text("event_type").notNull(), // "status" | "*"
|
||||||
|
repoFilter: text("repo_filter"), // glob: "lazorgurl/*" or exact "lazorgurl/homelab"
|
||||||
|
stateFilter: text("state_filter"), // "failure" | "error" | null
|
||||||
|
contextFilter: text("context_filter"), // substring match on CI context, null = any
|
||||||
|
|
||||||
|
// Task template with {{variable}} placeholders
|
||||||
|
taskTemplate: jsonb("task_template").$type<{
|
||||||
|
slug: string;
|
||||||
|
goal: string;
|
||||||
|
project: string;
|
||||||
|
gitProvider?: string;
|
||||||
|
gitBaseUrl?: string;
|
||||||
|
agentId: string;
|
||||||
|
maxIterations: number;
|
||||||
|
criteria: { label: string; target: string }[];
|
||||||
|
constraints: string[];
|
||||||
|
knowledgeRefs: string[];
|
||||||
|
}>().notNull(),
|
||||||
|
|
||||||
|
// Circuit breaker
|
||||||
|
consecutiveFailures: integer("consecutive_failures").default(0).notNull(),
|
||||||
|
maxConsecutiveFailures: integer("max_consecutive_failures").default(3).notNull(),
|
||||||
|
disabledReason: text("disabled_reason"),
|
||||||
|
|
||||||
|
// Webhook auth (per-trigger override, falls back to GITEA_WEBHOOK_SECRET env)
|
||||||
|
webhookSecret: text("webhook_secret"),
|
||||||
|
|
||||||
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── HARNESS: EVENT LOG ────────────────────────────────────
|
||||||
|
|
||||||
|
export const eventLog = pgTable("harness_event_log", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
triggerId: text("trigger_id").notNull(),
|
||||||
|
deliveryId: text("delivery_id").notNull().unique(), // X-Gitea-Delivery header (dedup)
|
||||||
|
eventType: text("event_type").notNull(),
|
||||||
|
repo: text("repo").notNull(),
|
||||||
|
commitSha: text("commit_sha"),
|
||||||
|
branch: text("branch"),
|
||||||
|
status: text("status").notNull().default("received"), // received | task_created | skipped | error
|
||||||
|
taskId: text("task_id"),
|
||||||
|
skipReason: text("skip_reason"),
|
||||||
|
error: text("error"),
|
||||||
|
payload: jsonb("payload").$type<Record<string, unknown>>().notNull(),
|
||||||
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|||||||
386
pnpm-lock.yaml
generated
386
pnpm-lock.yaml
generated
@@ -75,6 +75,9 @@ importers:
|
|||||||
'@homelab/db':
|
'@homelab/db':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/db
|
version: link:../../packages/db
|
||||||
|
'@modelcontextprotocol/sdk':
|
||||||
|
specifier: ^1.27.1
|
||||||
|
version: 1.27.1(zod@4.3.6)
|
||||||
'@xterm/addon-fit':
|
'@xterm/addon-fit':
|
||||||
specifier: ^0.11.0
|
specifier: ^0.11.0
|
||||||
version: 0.11.0
|
version: 0.11.0
|
||||||
@@ -99,12 +102,18 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.2.4(react@19.2.4)
|
version: 19.2.4(react@19.2.4)
|
||||||
|
tsx:
|
||||||
|
specifier: ^4.19.0
|
||||||
|
version: 4.21.0
|
||||||
ws:
|
ws:
|
||||||
specifier: ^8.20.0
|
specifier: ^8.20.0
|
||||||
version: 8.20.0
|
version: 8.20.0
|
||||||
yaml:
|
yaml:
|
||||||
specifier: ^2.7.0
|
specifier: ^2.7.0
|
||||||
version: 2.8.2
|
version: 2.8.2
|
||||||
|
zod:
|
||||||
|
specifier: ^4.3.6
|
||||||
|
version: 4.3.6
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.10.0
|
specifier: ^22.10.0
|
||||||
@@ -118,6 +127,9 @@ importers:
|
|||||||
'@types/ws':
|
'@types/ws':
|
||||||
specifier: ^8.18.1
|
specifier: ^8.18.1
|
||||||
version: 8.18.1
|
version: 8.18.1
|
||||||
|
esbuild:
|
||||||
|
specifier: ^0.27.4
|
||||||
|
version: 0.27.4
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.7.0
|
specifier: ^5.7.0
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
@@ -698,6 +710,12 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
'@hono/node-server@1.19.11':
|
||||||
|
resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==}
|
||||||
|
engines: {node: '>=18.14.1'}
|
||||||
|
peerDependencies:
|
||||||
|
hono: ^4
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
@@ -870,6 +888,16 @@ packages:
|
|||||||
'@js-sdsl/ordered-map@4.4.2':
|
'@js-sdsl/ordered-map@4.4.2':
|
||||||
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
|
resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/sdk@1.27.1':
|
||||||
|
resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
'@cfworker/json-schema': ^4.1.1
|
||||||
|
zod: ^3.25 || ^4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@cfworker/json-schema':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@0.2.12':
|
'@napi-rs/wasm-runtime@0.2.12':
|
||||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||||
|
|
||||||
@@ -1937,6 +1965,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
accepts@2.0.0:
|
||||||
|
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
acorn-import-attributes@1.9.5:
|
acorn-import-attributes@1.9.5:
|
||||||
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
|
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1956,9 +1988,20 @@ packages:
|
|||||||
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
ajv-formats@3.0.1:
|
||||||
|
resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
|
||||||
ajv@6.14.0:
|
ajv@6.14.0:
|
||||||
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
|
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
|
||||||
|
|
||||||
|
ajv@8.18.0:
|
||||||
|
resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
|
||||||
|
|
||||||
ansi-regex@5.0.1:
|
ansi-regex@5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2049,6 +2092,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
|
resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||||
|
|
||||||
|
body-parser@2.2.2:
|
||||||
|
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||||
|
|
||||||
@@ -2139,6 +2186,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
content-disposition@1.0.1:
|
||||||
|
resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
content-type@1.0.5:
|
content-type@1.0.5:
|
||||||
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -2146,6 +2197,10 @@ packages:
|
|||||||
cookie-signature@1.0.7:
|
cookie-signature@1.0.7:
|
||||||
resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
|
resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
|
||||||
|
|
||||||
|
cookie-signature@1.2.2:
|
||||||
|
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||||
|
engines: {node: '>=6.6.0'}
|
||||||
|
|
||||||
cookie@0.7.2:
|
cookie@0.7.2:
|
||||||
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -2542,10 +2597,28 @@ packages:
|
|||||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
eventsource-parser@3.0.6:
|
||||||
|
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
eventsource@3.0.7:
|
||||||
|
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
express-rate-limit@8.3.1:
|
||||||
|
resolution: {integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
peerDependencies:
|
||||||
|
express: '>= 4.11'
|
||||||
|
|
||||||
express@4.22.1:
|
express@4.22.1:
|
||||||
resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
|
resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
|
||||||
engines: {node: '>= 0.10.0'}
|
engines: {node: '>= 0.10.0'}
|
||||||
|
|
||||||
|
express@5.2.1:
|
||||||
|
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
extend@3.0.2:
|
extend@3.0.2:
|
||||||
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
|
||||||
|
|
||||||
@@ -2562,6 +2635,9 @@ packages:
|
|||||||
fast-levenshtein@2.0.6:
|
fast-levenshtein@2.0.6:
|
||||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||||
|
|
||||||
|
fast-uri@3.1.0:
|
||||||
|
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
||||||
|
|
||||||
fastq@1.20.1:
|
fastq@1.20.1:
|
||||||
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
|
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
|
||||||
|
|
||||||
@@ -2590,6 +2666,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==}
|
resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
finalhandler@2.1.1:
|
||||||
|
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
|
||||||
|
engines: {node: '>= 18.0.0'}
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2623,6 +2703,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
fresh@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -2723,6 +2807,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hono@4.12.8:
|
||||||
|
resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==}
|
||||||
|
engines: {node: '>=16.9.0'}
|
||||||
|
|
||||||
http-errors@2.0.1:
|
http-errors@2.0.1:
|
||||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -2735,6 +2823,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
iconv-lite@0.7.2:
|
||||||
|
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
ignore@5.3.2:
|
ignore@5.3.2:
|
||||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
@@ -2762,6 +2854,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
ip-address@10.1.0:
|
||||||
|
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
ipaddr.js@1.9.1:
|
ipaddr.js@1.9.1:
|
||||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
@@ -2837,6 +2933,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
is-promise@4.0.0:
|
||||||
|
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
||||||
|
|
||||||
is-regex@1.2.1:
|
is-regex@1.2.1:
|
||||||
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
|
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2887,6 +2986,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
jose@6.2.2:
|
||||||
|
resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==}
|
||||||
|
|
||||||
joycon@3.1.1:
|
joycon@3.1.1:
|
||||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2907,6 +3009,12 @@ packages:
|
|||||||
json-schema-traverse@0.4.1:
|
json-schema-traverse@0.4.1:
|
||||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0:
|
||||||
|
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||||
|
|
||||||
|
json-schema-typed@8.0.2:
|
||||||
|
resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==}
|
||||||
|
|
||||||
json-stable-stringify-without-jsonify@1.0.1:
|
json-stable-stringify-without-jsonify@1.0.1:
|
||||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
|
|
||||||
@@ -3041,9 +3149,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
media-typer@1.1.0:
|
||||||
|
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
merge-descriptors@1.0.3:
|
merge-descriptors@1.0.3:
|
||||||
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
|
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
|
||||||
|
|
||||||
|
merge-descriptors@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
merge2@1.4.1:
|
merge2@1.4.1:
|
||||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -3060,10 +3176,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-db@1.54.0:
|
||||||
|
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
mime-types@2.1.35:
|
mime-types@2.1.35:
|
||||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@3.0.2:
|
||||||
|
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
mime@1.6.0:
|
mime@1.6.0:
|
||||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -3111,6 +3235,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
negotiator@1.0.0:
|
||||||
|
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
next@15.5.14:
|
next@15.5.14:
|
||||||
resolution: {integrity: sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==}
|
resolution: {integrity: sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
@@ -3191,6 +3319,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
once@1.4.0:
|
||||||
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -3229,6 +3360,9 @@ packages:
|
|||||||
path-to-regexp@0.1.12:
|
path-to-regexp@0.1.12:
|
||||||
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
|
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
|
||||||
|
|
||||||
|
path-to-regexp@8.3.0:
|
||||||
|
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
|
||||||
|
|
||||||
pathe@2.0.3:
|
pathe@2.0.3:
|
||||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
@@ -3271,6 +3405,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
pkce-challenge@5.0.1:
|
||||||
|
resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==}
|
||||||
|
engines: {node: '>=16.20.0'}
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||||
|
|
||||||
@@ -3369,6 +3507,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==}
|
resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
raw-body@3.0.2:
|
||||||
|
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
react-dom@19.2.4:
|
react-dom@19.2.4:
|
||||||
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
|
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3401,6 +3543,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
require-from-string@2.0.2:
|
||||||
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
require-in-the-middle@8.0.1:
|
require-in-the-middle@8.0.1:
|
||||||
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
|
resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==}
|
||||||
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
|
engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'}
|
||||||
@@ -3435,6 +3581,10 @@ packages:
|
|||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
router@2.2.0:
|
||||||
|
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
|
|
||||||
@@ -3476,10 +3626,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==}
|
resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
send@1.2.1:
|
||||||
|
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
serve-static@1.16.3:
|
serve-static@1.16.3:
|
||||||
resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==}
|
resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
serve-static@2.2.1:
|
||||||
|
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||||
|
engines: {node: '>= 18'}
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3709,6 +3867,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
type-is@2.0.1:
|
||||||
|
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
typed-array-buffer@1.0.3:
|
typed-array-buffer@1.0.3:
|
||||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3791,6 +3953,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
wrappy@1.0.2:
|
||||||
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
ws@8.20.0:
|
ws@8.20.0:
|
||||||
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
|
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@@ -3828,6 +3993,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
zod-to-json-schema@3.25.1:
|
||||||
|
resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==}
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.25 || ^4
|
||||||
|
|
||||||
|
zod@4.3.6:
|
||||||
|
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
'@alloc/quick-lru@5.2.0': {}
|
'@alloc/quick-lru@5.2.0': {}
|
||||||
@@ -4131,6 +4304,10 @@ snapshots:
|
|||||||
protobufjs: 7.5.4
|
protobufjs: 7.5.4
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
|
|
||||||
|
'@hono/node-server@1.19.11(hono@4.12.8)':
|
||||||
|
dependencies:
|
||||||
|
hono: 4.12.8
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
'@humanfs/node@0.16.7':
|
'@humanfs/node@0.16.7':
|
||||||
@@ -4260,6 +4437,28 @@ snapshots:
|
|||||||
|
|
||||||
'@js-sdsl/ordered-map@4.4.2': {}
|
'@js-sdsl/ordered-map@4.4.2': {}
|
||||||
|
|
||||||
|
'@modelcontextprotocol/sdk@1.27.1(zod@4.3.6)':
|
||||||
|
dependencies:
|
||||||
|
'@hono/node-server': 1.19.11(hono@4.12.8)
|
||||||
|
ajv: 8.18.0
|
||||||
|
ajv-formats: 3.0.1(ajv@8.18.0)
|
||||||
|
content-type: 1.0.5
|
||||||
|
cors: 2.8.6
|
||||||
|
cross-spawn: 7.0.6
|
||||||
|
eventsource: 3.0.7
|
||||||
|
eventsource-parser: 3.0.6
|
||||||
|
express: 5.2.1
|
||||||
|
express-rate-limit: 8.3.1(express@5.2.1)
|
||||||
|
hono: 4.12.8
|
||||||
|
jose: 6.2.2
|
||||||
|
json-schema-typed: 8.0.2
|
||||||
|
pkce-challenge: 5.0.1
|
||||||
|
raw-body: 3.0.2
|
||||||
|
zod: 4.3.6
|
||||||
|
zod-to-json-schema: 3.25.1(zod@4.3.6)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@napi-rs/wasm-runtime@0.2.12':
|
'@napi-rs/wasm-runtime@0.2.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@emnapi/core': 1.9.1
|
'@emnapi/core': 1.9.1
|
||||||
@@ -5453,6 +5652,11 @@ snapshots:
|
|||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
negotiator: 0.6.3
|
negotiator: 0.6.3
|
||||||
|
|
||||||
|
accepts@2.0.0:
|
||||||
|
dependencies:
|
||||||
|
mime-types: 3.0.2
|
||||||
|
negotiator: 1.0.0
|
||||||
|
|
||||||
acorn-import-attributes@1.9.5(acorn@8.16.0):
|
acorn-import-attributes@1.9.5(acorn@8.16.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.16.0
|
acorn: 8.16.0
|
||||||
@@ -5465,6 +5669,10 @@ snapshots:
|
|||||||
|
|
||||||
agent-base@7.1.4: {}
|
agent-base@7.1.4: {}
|
||||||
|
|
||||||
|
ajv-formats@3.0.1(ajv@8.18.0):
|
||||||
|
optionalDependencies:
|
||||||
|
ajv: 8.18.0
|
||||||
|
|
||||||
ajv@6.14.0:
|
ajv@6.14.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
@@ -5472,6 +5680,13 @@ snapshots:
|
|||||||
json-schema-traverse: 0.4.1
|
json-schema-traverse: 0.4.1
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
|
|
||||||
|
ajv@8.18.0:
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-uri: 3.1.0
|
||||||
|
json-schema-traverse: 1.0.0
|
||||||
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
@@ -5590,6 +5805,20 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
body-parser@2.2.2:
|
||||||
|
dependencies:
|
||||||
|
bytes: 3.1.2
|
||||||
|
content-type: 1.0.5
|
||||||
|
debug: 4.4.3
|
||||||
|
http-errors: 2.0.1
|
||||||
|
iconv-lite: 0.7.2
|
||||||
|
on-finished: 2.4.1
|
||||||
|
qs: 6.14.2
|
||||||
|
raw-body: 3.0.2
|
||||||
|
type-is: 2.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
@@ -5672,10 +5901,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
content-disposition@1.0.1: {}
|
||||||
|
|
||||||
content-type@1.0.5: {}
|
content-type@1.0.5: {}
|
||||||
|
|
||||||
cookie-signature@1.0.7: {}
|
cookie-signature@1.0.7: {}
|
||||||
|
|
||||||
|
cookie-signature@1.2.2: {}
|
||||||
|
|
||||||
cookie@0.7.2: {}
|
cookie@0.7.2: {}
|
||||||
|
|
||||||
cors@2.8.6:
|
cors@2.8.6:
|
||||||
@@ -6184,6 +6417,17 @@ snapshots:
|
|||||||
|
|
||||||
etag@1.8.1: {}
|
etag@1.8.1: {}
|
||||||
|
|
||||||
|
eventsource-parser@3.0.6: {}
|
||||||
|
|
||||||
|
eventsource@3.0.7:
|
||||||
|
dependencies:
|
||||||
|
eventsource-parser: 3.0.6
|
||||||
|
|
||||||
|
express-rate-limit@8.3.1(express@5.2.1):
|
||||||
|
dependencies:
|
||||||
|
express: 5.2.1
|
||||||
|
ip-address: 10.1.0
|
||||||
|
|
||||||
express@4.22.1:
|
express@4.22.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts: 1.3.8
|
accepts: 1.3.8
|
||||||
@@ -6220,6 +6464,39 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
express@5.2.1:
|
||||||
|
dependencies:
|
||||||
|
accepts: 2.0.0
|
||||||
|
body-parser: 2.2.2
|
||||||
|
content-disposition: 1.0.1
|
||||||
|
content-type: 1.0.5
|
||||||
|
cookie: 0.7.2
|
||||||
|
cookie-signature: 1.2.2
|
||||||
|
debug: 4.4.3
|
||||||
|
depd: 2.0.0
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
etag: 1.8.1
|
||||||
|
finalhandler: 2.1.1
|
||||||
|
fresh: 2.0.0
|
||||||
|
http-errors: 2.0.1
|
||||||
|
merge-descriptors: 2.0.0
|
||||||
|
mime-types: 3.0.2
|
||||||
|
on-finished: 2.4.1
|
||||||
|
once: 1.4.0
|
||||||
|
parseurl: 1.3.3
|
||||||
|
proxy-addr: 2.0.7
|
||||||
|
qs: 6.14.2
|
||||||
|
range-parser: 1.2.1
|
||||||
|
router: 2.2.0
|
||||||
|
send: 1.2.1
|
||||||
|
serve-static: 2.2.1
|
||||||
|
statuses: 2.0.2
|
||||||
|
type-is: 2.0.1
|
||||||
|
vary: 1.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
@@ -6236,6 +6513,8 @@ snapshots:
|
|||||||
|
|
||||||
fast-levenshtein@2.0.6: {}
|
fast-levenshtein@2.0.6: {}
|
||||||
|
|
||||||
|
fast-uri@3.1.0: {}
|
||||||
|
|
||||||
fastq@1.20.1:
|
fastq@1.20.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.1.0
|
reusify: 1.1.0
|
||||||
@@ -6269,6 +6548,17 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
finalhandler@2.1.1:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
on-finished: 2.4.1
|
||||||
|
parseurl: 1.3.3
|
||||||
|
statuses: 2.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path: 6.0.0
|
locate-path: 6.0.0
|
||||||
@@ -6301,6 +6591,8 @@ snapshots:
|
|||||||
|
|
||||||
fresh@0.5.2: {}
|
fresh@0.5.2: {}
|
||||||
|
|
||||||
|
fresh@2.0.0: {}
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -6408,6 +6700,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
hono@4.12.8: {}
|
||||||
|
|
||||||
http-errors@2.0.1:
|
http-errors@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
depd: 2.0.0
|
depd: 2.0.0
|
||||||
@@ -6427,6 +6721,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
|
iconv-lite@0.7.2:
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
ignore@5.3.2: {}
|
ignore@5.3.2: {}
|
||||||
|
|
||||||
ignore@7.0.5: {}
|
ignore@7.0.5: {}
|
||||||
@@ -6453,6 +6751,8 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
side-channel: 1.1.0
|
side-channel: 1.1.0
|
||||||
|
|
||||||
|
ip-address@10.1.0: {}
|
||||||
|
|
||||||
ipaddr.js@1.9.1: {}
|
ipaddr.js@1.9.1: {}
|
||||||
|
|
||||||
is-array-buffer@3.0.5:
|
is-array-buffer@3.0.5:
|
||||||
@@ -6530,6 +6830,8 @@ snapshots:
|
|||||||
|
|
||||||
is-number@7.0.0: {}
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
|
is-promise@4.0.0: {}
|
||||||
|
|
||||||
is-regex@1.2.1:
|
is-regex@1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@@ -6584,6 +6886,8 @@ snapshots:
|
|||||||
|
|
||||||
jiti@2.6.1: {}
|
jiti@2.6.1: {}
|
||||||
|
|
||||||
|
jose@6.2.2: {}
|
||||||
|
|
||||||
joycon@3.1.1: {}
|
joycon@3.1.1: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
@@ -6600,6 +6904,10 @@ snapshots:
|
|||||||
|
|
||||||
json-schema-traverse@0.4.1: {}
|
json-schema-traverse@0.4.1: {}
|
||||||
|
|
||||||
|
json-schema-traverse@1.0.0: {}
|
||||||
|
|
||||||
|
json-schema-typed@8.0.2: {}
|
||||||
|
|
||||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||||
|
|
||||||
json5@1.0.2:
|
json5@1.0.2:
|
||||||
@@ -6705,8 +7013,12 @@ snapshots:
|
|||||||
|
|
||||||
media-typer@0.3.0: {}
|
media-typer@0.3.0: {}
|
||||||
|
|
||||||
|
media-typer@1.1.0: {}
|
||||||
|
|
||||||
merge-descriptors@1.0.3: {}
|
merge-descriptors@1.0.3: {}
|
||||||
|
|
||||||
|
merge-descriptors@2.0.0: {}
|
||||||
|
|
||||||
merge2@1.4.1: {}
|
merge2@1.4.1: {}
|
||||||
|
|
||||||
methods@1.1.2: {}
|
methods@1.1.2: {}
|
||||||
@@ -6718,10 +7030,16 @@ snapshots:
|
|||||||
|
|
||||||
mime-db@1.52.0: {}
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-db@1.54.0: {}
|
||||||
|
|
||||||
mime-types@2.1.35:
|
mime-types@2.1.35:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db: 1.52.0
|
mime-db: 1.52.0
|
||||||
|
|
||||||
|
mime-types@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.54.0
|
||||||
|
|
||||||
mime@1.6.0: {}
|
mime@1.6.0: {}
|
||||||
|
|
||||||
minimatch@10.2.4:
|
minimatch@10.2.4:
|
||||||
@@ -6761,6 +7079,8 @@ snapshots:
|
|||||||
|
|
||||||
negotiator@0.6.3: {}
|
negotiator@0.6.3: {}
|
||||||
|
|
||||||
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
next@15.5.14(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next@15.5.14(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.5.14
|
'@next/env': 15.5.14
|
||||||
@@ -6854,6 +7174,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ee-first: 1.1.1
|
ee-first: 1.1.1
|
||||||
|
|
||||||
|
once@1.4.0:
|
||||||
|
dependencies:
|
||||||
|
wrappy: 1.0.2
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
@@ -6891,6 +7215,8 @@ snapshots:
|
|||||||
|
|
||||||
path-to-regexp@0.1.12: {}
|
path-to-regexp@0.1.12: {}
|
||||||
|
|
||||||
|
path-to-regexp@8.3.0: {}
|
||||||
|
|
||||||
pathe@2.0.3: {}
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
pg-int8@1.0.1: {}
|
pg-int8@1.0.1: {}
|
||||||
@@ -6940,6 +7266,8 @@ snapshots:
|
|||||||
|
|
||||||
pirates@4.0.7: {}
|
pirates@4.0.7: {}
|
||||||
|
|
||||||
|
pkce-challenge@5.0.1: {}
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
confbox: 0.1.8
|
confbox: 0.1.8
|
||||||
@@ -7032,6 +7360,13 @@ snapshots:
|
|||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
|
|
||||||
|
raw-body@3.0.2:
|
||||||
|
dependencies:
|
||||||
|
bytes: 3.1.2
|
||||||
|
http-errors: 2.0.1
|
||||||
|
iconv-lite: 0.7.2
|
||||||
|
unpipe: 1.0.0
|
||||||
|
|
||||||
react-dom@19.2.4(react@19.2.4):
|
react-dom@19.2.4(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
@@ -7067,6 +7402,8 @@ snapshots:
|
|||||||
|
|
||||||
require-directory@2.1.1: {}
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
require-in-the-middle@8.0.1:
|
require-in-the-middle@8.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
@@ -7128,6 +7465,16 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc': 4.59.0
|
'@rollup/rollup-win32-x64-msvc': 4.59.0
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
router@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
depd: 2.0.0
|
||||||
|
is-promise: 4.0.0
|
||||||
|
parseurl: 1.3.3
|
||||||
|
path-to-regexp: 8.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
run-parallel@1.2.0:
|
run-parallel@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
@@ -7181,6 +7528,22 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
send@1.2.1:
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
etag: 1.8.1
|
||||||
|
fresh: 2.0.0
|
||||||
|
http-errors: 2.0.1
|
||||||
|
mime-types: 3.0.2
|
||||||
|
ms: 2.1.3
|
||||||
|
on-finished: 2.4.1
|
||||||
|
range-parser: 1.2.1
|
||||||
|
statuses: 2.0.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
serve-static@1.16.3:
|
serve-static@1.16.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
encodeurl: 2.0.0
|
encodeurl: 2.0.0
|
||||||
@@ -7190,6 +7553,15 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
serve-static@2.2.1:
|
||||||
|
dependencies:
|
||||||
|
encodeurl: 2.0.0
|
||||||
|
escape-html: 1.0.3
|
||||||
|
parseurl: 1.3.3
|
||||||
|
send: 1.2.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
set-function-length@1.2.2:
|
set-function-length@1.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-data-property: 1.1.4
|
define-data-property: 1.1.4
|
||||||
@@ -7490,6 +7862,12 @@ snapshots:
|
|||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
|
type-is@2.0.1:
|
||||||
|
dependencies:
|
||||||
|
content-type: 1.0.5
|
||||||
|
media-typer: 1.1.0
|
||||||
|
mime-types: 3.0.2
|
||||||
|
|
||||||
typed-array-buffer@1.0.3:
|
typed-array-buffer@1.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bound: 1.0.4
|
call-bound: 1.0.4
|
||||||
@@ -7625,6 +8003,8 @@ snapshots:
|
|||||||
string-width: 4.2.3
|
string-width: 4.2.3
|
||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
ws@8.20.0: {}
|
ws@8.20.0: {}
|
||||||
|
|
||||||
xtend@4.0.2: {}
|
xtend@4.0.2: {}
|
||||||
@@ -7646,3 +8026,9 @@ snapshots:
|
|||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
zod-to-json-schema@3.25.1(zod@4.3.6):
|
||||||
|
dependencies:
|
||||||
|
zod: 4.3.6
|
||||||
|
|
||||||
|
zod@4.3.6: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user