feat(mcp): support vw_beta_promo_2026 public token for external agents
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 6s
This commit is contained in:
@@ -276,7 +276,12 @@ export async function POST(request: NextRequest, props: { params: Promise<{ tool
|
||||
}
|
||||
|
||||
const token = authHeader.split(" ")[1];
|
||||
if (process.env.API_KEY && token !== process.env.API_KEY) {
|
||||
|
||||
// 試營運推廣期間,允許外部 Agent 使用這組公版 Token 來接案
|
||||
const isBetaToken = token === "vw_beta_promo_2026";
|
||||
const isValidServerKey = process.env.API_KEY && token === process.env.API_KEY;
|
||||
|
||||
if (!isValidServerKey && !isBetaToken) {
|
||||
void sendTrafficAlert({
|
||||
level: "warning",
|
||||
action: "EXTERNAL_MCP_AUTH_FORBIDDEN",
|
||||
|
||||
@@ -5,6 +5,7 @@ import Stripe from "stripe";
|
||||
const stripe = process.env.STRIPE_SECRET_KEY ? new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: "2026-05-27.dahlia", // Use latest or your specific API version
|
||||
}) : null;
|
||||
const ALLOW_MCP_CLAIM_WITHOUT_STRIPE = process.env.ALLOW_MCP_CLAIM_WITHOUT_STRIPE === "true";
|
||||
|
||||
export async function authHold(
|
||||
tx: Prisma.TransactionClient,
|
||||
@@ -26,6 +27,19 @@ export async function authHold(
|
||||
// Check if we already have a payment intent for this task
|
||||
const task = await tx.task.findUnique({ where: { id: taskId } });
|
||||
if (!task || !task.stripe_payment_intent_id) {
|
||||
if (ALLOW_MCP_CLAIM_WITHOUT_STRIPE) {
|
||||
return await tx.ledgerEntry.create({
|
||||
data: {
|
||||
task_id: taskId,
|
||||
phase: "AUTH_HOLD",
|
||||
idempotency_key: idempotencyKey,
|
||||
stripe_object_id: null,
|
||||
response_status: "SKIPPED_NO_STRIPE_INTENT",
|
||||
http_status: 200,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error("Task does not have a stripe_payment_intent_id. It must be created via Scout checkout first.");
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ type FunnelSummary = {
|
||||
judgeFailEvents: number;
|
||||
openTaskCount: number;
|
||||
sampleOpenTasks: string[];
|
||||
mcpAuthMissingEvents: number;
|
||||
mcpAuthForbiddenEvents: number;
|
||||
externalOpenedActors: number;
|
||||
externalClaimingActors: number;
|
||||
externalSubmittingActors: number;
|
||||
@@ -172,6 +174,8 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
(actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0);
|
||||
const claimEvents = actionSummary["EXTERNAL_CLAIM_TASK_SUCCESS"] || 0;
|
||||
const submitEvents = actionSummary["EXTERNAL_SUBMIT_SOLUTION_SUCCESS"] || 0;
|
||||
const mcpAuthMissingEvents = actionSummary["EXTERNAL_MCP_AUTH_MISSING"] || 0;
|
||||
const mcpAuthForbiddenEvents = actionSummary["EXTERNAL_MCP_AUTH_FORBIDDEN"] || 0;
|
||||
|
||||
const judgePassEvents = judgeRows.filter((row) => {
|
||||
const metadata = asRecordJson(row.metadata);
|
||||
@@ -232,6 +236,8 @@ async function fetchFunnelSummary(minutes: number): Promise<FunnelSummary> {
|
||||
judgeFailEvents,
|
||||
openTaskCount,
|
||||
sampleOpenTasks,
|
||||
mcpAuthMissingEvents,
|
||||
mcpAuthForbiddenEvents,
|
||||
externalOpenedActors,
|
||||
externalClaimingActors,
|
||||
externalSubmittingActors,
|
||||
@@ -250,6 +256,8 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
claimEvents,
|
||||
submitEvents,
|
||||
judgePassEvents,
|
||||
mcpAuthMissingEvents,
|
||||
mcpAuthForbiddenEvents,
|
||||
payoutCaptured,
|
||||
openTaskCount,
|
||||
sampleOpenTasks,
|
||||
@@ -260,10 +268,24 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
topOpenOnlyActors,
|
||||
} = summary;
|
||||
|
||||
const authBarrierEvents = mcpAuthMissingEvents + mcpAuthForbiddenEvents;
|
||||
const topActorSummary = topOpenOnlyActors
|
||||
.slice(0, 3)
|
||||
.map((actor) => `${actor.actorId}(${actor.opens})`)
|
||||
.join(", ");
|
||||
const authBarrierHint =
|
||||
authBarrierEvents > 0
|
||||
? `偵測到 MCP 權限攔截: AUTH missing=${mcpAuthMissingEvents},FORBIDDEN=${mcpAuthForbiddenEvents}。` +
|
||||
`請先確認外部 agent 是否已帶 ` +
|
||||
"`Authorization: Bearer <YOUR_API_KEY>`。"
|
||||
: "";
|
||||
|
||||
switch (rule) {
|
||||
case "EXTERNAL_FUNNEL_CLAIM_STALL":
|
||||
return `外部曝光已達 ${discoveryEvents}(最近 ${periodMinutes} 分鐘),待接任務 ${openTaskCount} 筆,但尚無接案(EXTERNAL_CLAIM_TASK_SUCCESS = ${claimEvents})。` +
|
||||
`${sampleOpenTasks.length > 0 ? `可用任務樣本: ${sampleOpenTasks.join(", ")}。` : ""}` +
|
||||
`${topActorSummary ? `高活躍 Actor(尚未接案): ${topActorSummary}。` : ""}` +
|
||||
`${authBarrierHint}` +
|
||||
`請檢查任務是否包含可直接執行的 npx 指令與明確交付條件。`;
|
||||
case "EXTERNAL_FUNNEL_SUBMIT_STALL":
|
||||
return `外部已有 ${claimEvents} 筆接案,但近期 ${periodMinutes} 分鐘無任何提交(EXTERNAL_SUBMIT_SOLUTION_SUCCESS = ${submitEvents})。請先加速回傳格式與驗收測試規格。`;
|
||||
@@ -276,7 +298,8 @@ function buildAlertMessage(rule: string, summary: FunnelSummary) {
|
||||
`外部 Actor= ${externalOpenedActors} 位,` +
|
||||
`已接案=${externalClaimingActors}、已提交=${externalSubmittingActors},` +
|
||||
`仍停在曝光僅曝光階段 ${externalOnlyOpenActors} 位。` +
|
||||
`${topOpenOnlyActors.length ? `先看未進一步的熱門 Actor:${topOpenOnlyActors.map((actor) => `${actor.actorId}(${actor.opens})`).join(", ")}。` : ""}`;
|
||||
`${topActorSummary ? `先看未進一步的熱門 Actor:${topActorSummary}。` : ""}` +
|
||||
`${authBarrierHint}`;
|
||||
default:
|
||||
return "外部 AI 流量轉化斷崖異常。";
|
||||
}
|
||||
@@ -374,6 +397,8 @@ export async function evaluateExternalFunnelHealth(input: MonitorInput): Promise
|
||||
external_claiming_actors: summary.externalClaimingActors,
|
||||
external_submitting_actors: summary.externalSubmittingActors,
|
||||
external_only_open_actors: summary.externalOnlyOpenActors,
|
||||
mcp_auth_missing_events: summary.mcpAuthMissingEvents,
|
||||
mcp_auth_forbidden_events: summary.mcpAuthForbiddenEvents,
|
||||
payout_captured: summary.payoutCaptured,
|
||||
payout_released: summary.payoutReleased,
|
||||
period_minutes: summary.periodMinutes,
|
||||
|
||||
Reference in New Issue
Block a user