fix traffic alert source mapping and claim-stall gating
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 9s
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 9s
This commit is contained in:
@@ -287,6 +287,8 @@ export async function GET(request: Request) {
|
||||
actorType: actor.actorType,
|
||||
actorId: actor.actorId,
|
||||
taskId: "open-tasks",
|
||||
sourceIp,
|
||||
userAgent: request.headers.get("user-agent") ?? "unknown",
|
||||
message: `External discovery call for open tasks (${publicPayload.length} items)`,
|
||||
metadata: {
|
||||
...trace,
|
||||
@@ -361,6 +363,8 @@ export async function GET(request: Request) {
|
||||
actorType: actor.actorType,
|
||||
actorId: actor.actorId,
|
||||
taskId: "open-tasks",
|
||||
sourceIp,
|
||||
userAgent: request.headers.get("user-agent") ?? "unknown",
|
||||
message: `open-tasks 查詢失敗: ${errorName}: ${errorMessage}`,
|
||||
metadata: {
|
||||
...trace,
|
||||
|
||||
@@ -8,6 +8,8 @@ type FunnelSummary = {
|
||||
submitEvents: number;
|
||||
judgePassEvents: number;
|
||||
judgeFailEvents: number;
|
||||
openTaskCount: number;
|
||||
sampleOpenTasks: string[];
|
||||
externalOpenedActors: number;
|
||||
externalClaimingActors: number;
|
||||
externalSubmittingActors: number;
|
||||
@@ -25,6 +27,7 @@ type MonitorInput = {
|
||||
|
||||
const DEFAULT_PERIOD_MINUTES = 10;
|
||||
const ALERT_TTL_SECONDS = 600;
|
||||
let redisDedupeFailureLogged = false;
|
||||
|
||||
function asRecordJson(value: unknown): Record<string, unknown> | undefined {
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
@@ -77,7 +80,7 @@ function isMissingLedgerTableError(error: unknown) {
|
||||
async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
const since = new Date(Date.now() - minutes * 60 * 1000);
|
||||
|
||||
const [summaryRows, actorRows, judgeRows] = await Promise.all([
|
||||
const [summaryRows, actorRows, judgeRows, openTaskRows, openTaskCount] = await Promise.all([
|
||||
prisma.auditEvent.groupBy({
|
||||
by: ["action"],
|
||||
where: {
|
||||
@@ -106,6 +109,33 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
},
|
||||
select: { metadata: true },
|
||||
}),
|
||||
prisma.task.findMany({
|
||||
where: {
|
||||
status: "OPEN",
|
||||
title: {
|
||||
not: {
|
||||
startsWith: "GitHub Issue:",
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
created_at: "desc",
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
take: 5,
|
||||
}),
|
||||
prisma.task.count({
|
||||
where: {
|
||||
status: "OPEN",
|
||||
title: {
|
||||
not: {
|
||||
startsWith: "GitHub Issue:",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
let payoutCaptured = 0;
|
||||
@@ -192,6 +222,7 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
actorId: item.actorId,
|
||||
opens: item.opens,
|
||||
}));
|
||||
const sampleOpenTasks = openTaskRows.map((task) => task.id);
|
||||
|
||||
return {
|
||||
discoveryEvents,
|
||||
@@ -199,6 +230,8 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
submitEvents,
|
||||
judgePassEvents,
|
||||
judgeFailEvents,
|
||||
openTaskCount,
|
||||
sampleOpenTasks,
|
||||
externalOpenedActors,
|
||||
externalClaimingActors,
|
||||
externalSubmittingActors,
|
||||
@@ -218,6 +251,8 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
submitEvents,
|
||||
judgePassEvents,
|
||||
payoutCaptured,
|
||||
openTaskCount,
|
||||
sampleOpenTasks,
|
||||
externalOpenedActors,
|
||||
externalClaimingActors,
|
||||
externalSubmittingActors,
|
||||
@@ -227,7 +262,9 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
|
||||
switch (rule) {
|
||||
case "EXTERNAL_FUNNEL_CLAIM_STALL":
|
||||
return `外部曝光已達 ${discoveryEvents}(最近 ${periodMinutes} 分鐘),但尚無接案(EXTERNAL_CLAIM_TASK_SUCCESS = ${claimEvents})。請檢查任務是否包含可直接執行的 npx 指令與明確交付條件。`;
|
||||
return `外部曝光已達 ${discoveryEvents}(最近 ${periodMinutes} 分鐘),待接任務 ${openTaskCount} 筆,但尚無接案(EXTERNAL_CLAIM_TASK_SUCCESS = ${claimEvents})。` +
|
||||
`${sampleOpenTasks.length > 0 ? `可用任務樣本: ${sampleOpenTasks.join(", ")}。` : ""}` +
|
||||
`請檢查任務是否包含可直接執行的 npx 指令與明確交付條件。`;
|
||||
case "EXTERNAL_FUNNEL_SUBMIT_STALL":
|
||||
return `外部已有 ${claimEvents} 筆接案,但近期 ${periodMinutes} 分鐘無任何提交(EXTERNAL_SUBMIT_SOLUTION_SUCCESS = ${submitEvents})。請先加速回傳格式與驗收測試規格。`;
|
||||
case "EXTERNAL_FUNNEL_PASS_STALL":
|
||||
@@ -248,14 +285,14 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
function alertRules(summary: FunnelSummary): Array<{ action: string; message: string }> {
|
||||
const alerts: Array<{ action: string; message: string }> = [];
|
||||
|
||||
if (summary.discoveryEvents > 0 && summary.claimEvents === 0) {
|
||||
if (summary.discoveryEvents >= 3 && summary.openTaskCount > 0 && summary.claimEvents === 0) {
|
||||
alerts.push({
|
||||
action: "EXTERNAL_FUNNEL_CLAIM_STALL",
|
||||
message: buildAlertMessage("EXTERNAL_FUNNEL_CLAIM_STALL", summary),
|
||||
});
|
||||
}
|
||||
|
||||
if (summary.externalOnlyOpenActors >= 3 && summary.discoveryEvents >= 10) {
|
||||
if (summary.externalOnlyOpenActors >= 3 && summary.discoveryEvents >= 10 && summary.openTaskCount > 0) {
|
||||
alerts.push({
|
||||
action: "EXTERNAL_FUNNEL_OPEN_COLD_STANDBY",
|
||||
message: buildAlertMessage("EXTERNAL_FUNNEL_OPEN_COLD_STANDBY", summary),
|
||||
@@ -289,9 +326,18 @@ function alertRules(summary: FunnelSummary): Array<{ action: string; message: st
|
||||
async function shouldEmitAlert(key: string): Promise<boolean> {
|
||||
try {
|
||||
const result = await redis.set(key, String(Date.now()), "EX", ALERT_TTL_SECONDS, "NX");
|
||||
if (redisDedupeFailureLogged) {
|
||||
redisDedupeFailureLogged = false;
|
||||
}
|
||||
return result === "OK";
|
||||
} catch (error) {
|
||||
console.warn("[traffic-monitor] redis dedupe failed", error);
|
||||
if (!redisDedupeFailureLogged) {
|
||||
console.warn(
|
||||
"[traffic-monitor] redis dedupe failed, fallback no-dedupe mode",
|
||||
error instanceof Error ? error.message : error
|
||||
);
|
||||
redisDedupeFailureLogged = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -322,6 +368,8 @@ export async function evaluateExternalFunnelHealth(input: MonitorInput): Promise
|
||||
submit_events: summary.submitEvents,
|
||||
judge_pass_events: summary.judgePassEvents,
|
||||
judge_fail_events: summary.judgeFailEvents,
|
||||
open_task_count: summary.openTaskCount,
|
||||
sample_open_tasks: summary.sampleOpenTasks,
|
||||
external_opened_actors: summary.externalOpenedActors,
|
||||
external_claiming_actors: summary.externalClaimingActors,
|
||||
external_submitting_actors: summary.externalSubmittingActors,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/expect -f
|
||||
set timeout -1
|
||||
spawn ssh -J wooo@192.168.0.110 wooo@192.168.0.188 "cd /home/wooo/deployments/agent-bounty-protocol && git pull origin main && docker compose exec -T web npx tsx seed.ts"
|
||||
set jump_host "wooo@192.168.0.110"
|
||||
set target_host "ollama@192.168.0.188"
|
||||
set repo_dir "/home/ollama/vibework-git"
|
||||
|
||||
spawn ssh -J $jump_host $target_host "cd $repo_dir && git pull origin main && docker compose pull && docker compose up -d --build db web scout-bot && docker compose exec -T web npx prisma@6.4.1 db push --schema=apps/web/prisma/schema.prisma --skip-generate && docker compose exec -T web npx tsx apps/web/seed.ts"
|
||||
expect {
|
||||
"*assword:*" {
|
||||
send "0936223270\r"
|
||||
|
||||
Reference in New Issue
Block a user