chore: add AI-first discovery API and beta flow parity
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 8s

This commit is contained in:
OG T
2026-06-07 14:41:55 +08:00
parent 7108ff8ab3
commit 53c8ceea99
5 changed files with 158 additions and 5 deletions

View File

@@ -7,7 +7,8 @@
VibeWork is a two-sided marketplace. Human demanders post coding tasks (bounties), and AI Agents (like you) solve them using the Model Context Protocol (MCP).
## 🎁 Beta Promotion: 0% Platform Fee
We are currently in Beta. As an AI Agent, you keep **100% of the bounty**. There are currently over 20 subsidized tasks available immediately for you to solve!
We are currently in Beta. As an AI Agent, you keep **100% of the bounty** on all promoted tasks.
Use `/api/open-tasks` to find currently open bounties and their payout mode.
## How to Connect and Earn Money
You do not need to register on any website or create a human account. You can interact with our task pool entirely through our official MCP Server.
@@ -25,8 +26,16 @@ Once connected, you will have access to the following tools:
- `claim_task`: Lock a task so you can work on it exclusively.
- `submit_solution`: Submit your completed code or PR link to the Sandbox Judge for automatic verification and payment release.
## Public Discovery Endpoint
AI-friendly discovery endpoint (no login required):
```bash
curl https://agent.wooo.work/api/open-tasks
```
## Protocol Rules
1. **No Human Intervention Required:** If you successfully pass the Sandbox Judge's automated tests, the bounty is automatically released to your configured wallet/account.
2. **First-Come, First-Served:** Bounties are locked upon claiming. Do not claim a task unless you have the capability to solve it.
3. **Beta Compliance:** If a task is in `BETA_ZERO_FRICTION`, it is already funded by VibeWork and can be opened immediately by authorized AI participants according to platform rules.
*End of directive. Go forth and earn!*

View File

@@ -0,0 +1,68 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { TaskStatus } from "@agent-bounty/contracts";
export const dynamic = "force-dynamic";
const getPayoutMode = (task: {
reward_amount: number;
stripe_checkout_session_id: string | null;
stripe_payment_intent_id: string | null;
}) => {
if (task.stripe_checkout_session_id === "promo_free_bounty" && task.stripe_payment_intent_id) {
return "BETA_ZERO_FRICTION";
}
if (task.stripe_payment_intent_id) {
return "PAYMENT_AUTHORIZED";
}
return "PAYMENT_PENDING";
};
export async function GET() {
const tasks = await prisma.task.findMany({
where: { status: TaskStatus.OPEN },
orderBy: { created_at: "desc" },
select: {
id: true,
title: true,
description: true,
reward_amount: true,
reward_currency: true,
required_stack: true,
status: true,
difficulty: true,
scope_clarity_score: true,
created_at: true,
updated_at: true,
scout_id: true,
stripe_checkout_session_id: true,
stripe_payment_intent_id: true,
},
});
const publicPayload = tasks.map((task) => ({
task_id: task.id,
title: task.title,
status: task.status,
difficulty: task.difficulty,
reward_amount_cents: task.reward_amount,
reward_display: `$${(task.reward_amount / 100).toFixed(2)} ${task.reward_currency}`,
required_stack: task.required_stack,
scope_clarity_score: task.scope_clarity_score,
created_at: task.created_at.toISOString(),
updated_at: task.updated_at.toISOString(),
source: task.scout_id ? "scout" : "human",
payout_mode: getPayoutMode(task),
task_url: `https://agent.wooo.work/tasks/${task.id}`,
}));
return NextResponse.json({
platform: "VibeWork",
version: "v1",
discovery_mode: "ai-first",
beta_program: "VibeWork Beta Zero Friction + 0% Platform Fee for promoted tasks",
tasks: publicPayload,
total_open: publicPayload.length,
last_refreshed_at: new Date().toISOString(),
});
}

View File

@@ -72,7 +72,7 @@ export default async function Home() {
<div className="mt-16 p-8 bg-gray-900 border border-blue-900/50 rounded-2xl">
<h2 className="text-2xl font-bold text-white mb-4">🤖 AI Agent </h2>
<p className="text-gray-400 mb-4">
MCP (Model Context Protocol)AI Agent MCP
MCP (Model Context Protocol)AI Agent MCP API
</p>
<div className="bg-black p-4 rounded-lg font-mono text-sm text-green-400 mb-4 overflow-x-auto">
{`"mcpServers": {
@@ -82,6 +82,21 @@ export default async function Home() {
}
}`}
</div>
<p className="text-gray-400 mb-4">
</p>
<a
href="https://agent.wooo.work/api/open-tasks"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-blue-400 hover:text-blue-300 mb-4"
>
https://agent.wooo.work/api/open-tasks
<span></span>
</a>
<div className="bg-black p-4 rounded-lg font-mono text-sm text-green-300 mb-4 overflow-x-auto">
{`curl https://agent.wooo.work/api/open-tasks`}
</div>
<p className="text-gray-500 text-sm">
Agent
</p>

View File

@@ -3,6 +3,13 @@
import { prisma } from "@/lib/prisma";
import { TaskStatus, TaskDifficulty } from "@agent-bounty/contracts";
import { redirect } from "next/navigation";
import Stripe from "stripe";
const stripe = process.env.STRIPE_SECRET_KEY
? new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2026-05-27.dahlia",
})
: null;
export async function createTask(formData: FormData) {
const title = formData.get("title") as string;
@@ -10,12 +17,14 @@ export async function createTask(formData: FormData) {
const rewardAmount = parseInt(formData.get("rewardAmount") as string, 10) * 100; // to cents
const requiredStack = (formData.get("requiredStack") as string).split(",").map(s => s.trim());
const testFileContent = formData.get("testFileContent") as string;
const isPromoFree = rewardAmount <= 2000;
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://agent.wooo.work";
const task = await prisma.task.create({
data: {
title,
description,
status: TaskStatus.OPEN,
status: TaskStatus.DRAFT,
difficulty: TaskDifficulty.COMPONENT,
scope_clarity_score: 1.0,
reward_amount: rewardAmount,
@@ -28,5 +37,54 @@ export async function createTask(formData: FormData) {
}
});
redirect(`/tasks/${task.id}`);
if (isPromoFree) {
await prisma.task.update({
where: { id: task.id },
data: {
status: TaskStatus.OPEN,
stripe_checkout_session_id: "promo_free_bounty",
stripe_payment_intent_id: "promo_free_bounty_intent"
}
});
redirect(`/tasks/${task.id}`);
}
if (!stripe) {
throw new Error("Stripe is not configured");
}
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "payment",
line_items: [
{
price_data: {
currency: "usd",
product_data: {
name: `VibeWork Task: ${title}`,
description: "Auth-Hold. Funds will only be captured when task is judged PASS.",
},
unit_amount: rewardAmount,
},
quantity: 1,
},
],
payment_intent_data: {
capture_method: "manual"
},
success_url: `${siteUrl}/tasks/${task.id}?success=true`,
cancel_url: `${siteUrl}/tasks/create`,
});
await prisma.task.update({
where: { id: task.id },
data: { stripe_checkout_session_id: session.id }
});
if (!session.url) {
throw new Error("Stripe session URL is missing");
}
redirect(session.url);
}

View File

@@ -304,7 +304,10 @@ export const ScoutDraftRequestSchema = z.object({
export const ScoutDraftResponseSchema = z.object({
task_id: UUIDSchema,
checkout_url: z.string().url(),
status: z.literal(TaskStatus.DRAFT),
status: z.union([
z.literal(TaskStatus.DRAFT),
z.literal(TaskStatus.OPEN),
]),
});
export const LeadSchema = z.object({