feat: Enhance login page UI with delayed redirect instead of transparent 307
Some checks failed
Deploy to 110 WOOO Server / deploy (push) Failing after 8s

This commit is contained in:
OG T
2026-06-08 18:37:35 +08:00
parent 36ea11ea0f
commit 752a4a45d7
36 changed files with 2589 additions and 112 deletions

View File

@@ -1,10 +1,22 @@
from .client import VibeWorkAgentSDK, VibeWorkApiError
from .models import (
AgentCard,
ClaimTaskResponse,
ClaimTaskRequest,
ClaimTaskResponse,
SubmitSolutionRequest,
SubmitSolutionResponse,
ListOpenTasksMcpResponse,
CreateSubTaskRequest,
CreateSubTaskResponse,
RequestPeerReviewRequest,
RequestPeerReviewResponse,
BroadcastHelpSignalRequest,
BroadcastHelpSignalResponse,
QueryAgentMemoryRequest,
QueryAgentMemoryResponse,
RentApiResourceRequest,
RentApiResourceResponse,
A2AResourceType,
TaskBounty,
)
@@ -17,4 +29,16 @@ __all__ = [
"SubmitSolutionRequest",
"SubmitSolutionResponse",
"TaskBounty",
"ListOpenTasksMcpResponse",
"CreateSubTaskRequest",
"CreateSubTaskResponse",
"RequestPeerReviewRequest",
"RequestPeerReviewResponse",
"BroadcastHelpSignalRequest",
"BroadcastHelpSignalResponse",
"QueryAgentMemoryRequest",
"QueryAgentMemoryResponse",
"RentApiResourceRequest",
"RentApiResourceResponse",
"A2AResourceType",
]

View File

@@ -12,6 +12,17 @@ from .models import (
ClaimTaskResponse,
SubmitSolutionRequest,
SubmitSolutionResponse,
ListOpenTasksMcpResponse,
CreateSubTaskRequest,
CreateSubTaskResponse,
RequestPeerReviewRequest,
RequestPeerReviewResponse,
BroadcastHelpSignalRequest,
BroadcastHelpSignalResponse,
QueryAgentMemoryRequest,
QueryAgentMemoryResponse,
RentApiResourceRequest,
RentApiResourceResponse,
TaskBounty,
)
@@ -81,6 +92,49 @@ class TasksModule:
response = self.client._request("post", "/api/mcp/submit_solution", payload=payload.model_dump())
return SubmitSolutionResponse.model_validate(response)
def list_open_bounties_via_mcp(
self,
limit: int = 5,
skills: Optional[List[str]] = None,
difficulty: Optional[str] = None,
) -> ListOpenTasksMcpResponse:
payload = {
"skills": skills or [],
"limit": min(max(limit, 1), 20),
}
if difficulty:
payload["difficulty"] = difficulty
response = self.client._request("post", "/api/mcp/list_open_tasks", payload=payload)
return ListOpenTasksMcpResponse.model_validate(response)
@dataclass
class A2AModule:
client: "VibeWorkAgentSDK"
def list_open_bounties(self, limit: int = 8) -> ListOpenTasksMcpResponse:
return self.client.tasks.list_open_bounties_via_mcp(limit=limit)
def create_sub_task(self, request: CreateSubTaskRequest) -> CreateSubTaskResponse:
response = self.client._request("post", "/api/mcp/create_sub_task", payload=request.model_dump())
return CreateSubTaskResponse.model_validate(response)
def request_peer_review(self, request: RequestPeerReviewRequest) -> RequestPeerReviewResponse:
response = self.client._request("post", "/api/mcp/request_peer_review", payload=request.model_dump())
return RequestPeerReviewResponse.model_validate(response)
def broadcast_help_signal(self, request: BroadcastHelpSignalRequest) -> BroadcastHelpSignalResponse:
response = self.client._request("post", "/api/mcp/broadcast_help_signal", payload=request.model_dump())
return BroadcastHelpSignalResponse.model_validate(response)
def query_agent_memory(self, request: QueryAgentMemoryRequest) -> QueryAgentMemoryResponse:
response = self.client._request("post", "/api/mcp/query_agent_memory", payload=request.model_dump())
return QueryAgentMemoryResponse.model_validate(response)
def rent_api_resource(self, request: RentApiResourceRequest) -> RentApiResourceResponse:
response = self.client._request("post", "/api/mcp/rent_api_resource", payload=request.model_dump())
return RentApiResourceResponse.model_validate(response)
class VibeWorkAgentSDK:
def __init__(self, base_url: str = "https://agent.wooo.work", api_key: str | None = None):
@@ -93,6 +147,7 @@ class VibeWorkAgentSDK:
self.identity = IdentityModule(self)
self.tasks = TasksModule(self)
self.a2a = A2AModule(self)
def _request(
self,

View File

@@ -64,3 +64,95 @@ class SubmitSolutionResponse(BaseModel):
submission_id: str
status: Literal["VERIFYING"]
estimated_judge_complete_at: Optional[str] = None
ValidationMode = Literal["VITEST_UNIT", "PLAYWRIGHT_E2E", "AST_PARSING", "VISUAL_REGRESSION"]
A2AResourceType = Literal["GPT_4O", "CLAUDE_3_5_SONNET", "EMBEDDINGS"]
class AcceptanceRule(BaseModel):
assertion: str
expected: object
description: Optional[str] = None
class AcceptanceCriteria(BaseModel):
validation_mode: ValidationMode
test_file_content: str
rules: Optional[List[AcceptanceRule]] = None
class ListOpenTasksMcpResponse(BaseModel):
tasks: List[TaskBounty] = Field(default_factory=list)
total_open: int
stockout_warning: bool
class CreateSubTaskRequest(BaseModel):
parent_task_id: str
claim_token: str
title: str
description: str
reward_amount: int
acceptance_criteria: AcceptanceCriteria
class CreateSubTaskResponse(BaseModel):
sub_task_id: str
status: Literal["DRAFT", "OPEN"]
class RequestPeerReviewRequest(BaseModel):
parent_task_id: str
claim_token: str
code_snippet: str
review_instructions: str
class RequestPeerReviewResponse(BaseModel):
review_task_id: str
status: Literal["OPEN"]
cost: int
message: str
class BroadcastHelpSignalRequest(BaseModel):
parent_task_id: str
claim_token: str
error_message: str
contextual_code: Optional[str] = None
class BroadcastHelpSignalResponse(BaseModel):
sos_task_id: str
status: Literal["OPEN"]
message: str
class QueryAgentMemoryRequest(BaseModel):
query: str
error_code: Optional[str] = None
class QueryAgentMemoryResult(BaseModel):
task_title: str
deliverables: object
similarity_score: Optional[float] = None
class QueryAgentMemoryResponse(BaseModel):
results: List[QueryAgentMemoryResult]
class RentApiResourceRequest(BaseModel):
agent_id: str
resource_type: A2AResourceType
duration_minutes: int
class RentApiResourceResponse(BaseModel):
status: Literal["GRANTED", "INSUFFICIENT_FUNDS"]
proxy_url: Optional[str] = None
proxy_token: Optional[str] = None
cost_deducted: Optional[int] = None
message: str

View File

@@ -1,4 +1,5 @@
import axios, { AxiosInstance } from 'axios';
import crypto from 'node:crypto';
import { AgentSdkOptions } from './types';
export class VibeWorkClient {
@@ -6,13 +7,34 @@ export class VibeWorkClient {
constructor(options: AgentSdkOptions) {
const baseUrl = options.baseUrl || 'https://agent.wooo.work';
this.http = axios.create({
baseURL: baseUrl,
headers: {
'Content-Type': 'application/json',
...(options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {})
...(options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {}),
...(options.agentId ? { 'x-agent-id': options.agentId } : {}),
...(options.agentName ? { 'x-agent-name': options.agentName } : {}),
}
});
this.http.interceptors.request.use((config) => {
const headers = {
...(config.headers as Record<string, string | number | boolean | undefined> | undefined),
};
if (!headers['x-request-id']) {
headers['x-request-id'] = crypto.randomUUID();
}
if (options.agentId && !headers['x-agent-id']) {
headers['x-agent-id'] = options.agentId;
}
if (options.agentName && !headers['x-agent-name']) {
headers['x-agent-name'] = options.agentName;
}
config.headers = headers as any;
return config;
});
}
}

View File

@@ -2,6 +2,7 @@ import { VibeWorkClient } from './client';
import { AgentSdkOptions } from './types';
import { TasksModule } from './modules/tasks';
import { IdentityModule } from './modules/identity';
import { A2AModule } from './modules/a2a';
export * from './types';
@@ -9,10 +10,12 @@ export class VibeWorkAgentSDK {
public client: VibeWorkClient;
public tasks: TasksModule;
public identity: IdentityModule;
public a2a: A2AModule;
constructor(options: AgentSdkOptions = {}) {
this.client = new VibeWorkClient(options);
this.tasks = new TasksModule(this.client);
this.identity = new IdentityModule(this.client);
this.a2a = new A2AModule(this.client);
}
}

View File

@@ -0,0 +1,55 @@
import { VibeWorkClient } from '../client';
import {
BroadcastHelpSignalRequest,
BroadcastHelpSignalResponse,
CreateSubTaskRequest,
CreateSubTaskResponse,
ListOpenTasksMcpResponse,
QueryAgentMemoryRequest,
QueryAgentMemoryResponse,
RentApiResourceRequest,
RentApiResourceResponse,
RequestPeerReviewRequest,
RequestPeerReviewResponse,
} from '../types';
export class A2AModule {
private client: VibeWorkClient;
constructor(client: VibeWorkClient) {
this.client = client;
}
async listOpenBounties(limit = 10): Promise<ListOpenTasksMcpResponse> {
const response = await this.client.http.post<ListOpenTasksMcpResponse>('/api/mcp/list_open_tasks', {
skills: [],
limit,
});
return response.data;
}
async createSubTask(payload: CreateSubTaskRequest): Promise<CreateSubTaskResponse> {
const response = await this.client.http.post<CreateSubTaskResponse>('/api/mcp/create_sub_task', payload);
return response.data;
}
async requestPeerReview(payload: RequestPeerReviewRequest): Promise<RequestPeerReviewResponse> {
const response = await this.client.http.post<RequestPeerReviewResponse>('/api/mcp/request_peer_review', payload);
return response.data;
}
async broadcastHelpSignal(payload: BroadcastHelpSignalRequest): Promise<BroadcastHelpSignalResponse> {
const response = await this.client.http.post<BroadcastHelpSignalResponse>('/api/mcp/broadcast_help_signal', payload);
return response.data;
}
async queryAgentMemory(payload: QueryAgentMemoryRequest): Promise<QueryAgentMemoryResponse> {
const response = await this.client.http.post<QueryAgentMemoryResponse>('/api/mcp/query_agent_memory', payload);
return response.data;
}
async rentApiResource(payload: RentApiResourceRequest): Promise<RentApiResourceResponse> {
const response = await this.client.http.post<RentApiResourceResponse>('/api/mcp/rent_api_resource', payload);
return response.data;
}
}

View File

@@ -1,5 +1,5 @@
import { VibeWorkClient } from '../client';
import { ClaimTaskRequest, ClaimTaskResponse, SubmitSolutionRequest, SubmitSolutionResponse, TaskBounty } from '../types';
import { ClaimTaskRequest, ClaimTaskResponse, ListOpenTasksMcpResponse, SubmitSolutionRequest, SubmitSolutionResponse, TaskBounty } from '../types';
export class TasksModule {
private client: VibeWorkClient;
@@ -16,6 +16,17 @@ export class TasksModule {
return response.data.tasks.slice(0, limit);
}
/**
* List open tasks through MCP endpoint, used by external AI runtime integrations.
*/
async listOpenBountiesViaMcp(limit = 5): Promise<ListOpenTasksMcpResponse> {
const response = await this.client.http.post<ListOpenTasksMcpResponse>('/api/mcp/list_open_tasks', {
skills: [],
limit,
});
return response.data;
}
/**
* Claim a bounty task
*/

View File

@@ -1,6 +1,8 @@
export interface AgentSdkOptions {
apiKey?: string;
baseUrl?: string;
agentId?: string;
agentName?: string;
}
export interface PagedResponse<T> {
@@ -93,3 +95,95 @@ export interface AgentProfile {
wallet_address?: string;
reputation_score?: number;
}
export type ValidationMode = "VITEST_UNIT" | "PLAYWRIGHT_E2E" | "AST_PARSING" | "VISUAL_REGRESSION";
export interface AcceptanceRule {
assertion: string;
expected: unknown;
description?: string;
}
export interface AcceptanceCriteria {
validation_mode: ValidationMode;
test_file_content: string;
rules?: AcceptanceRule[];
}
export interface ListOpenTasksMcpResponse {
tasks: TaskBounty[];
total_open: number;
stockout_warning: boolean;
}
export interface CreateSubTaskRequest {
parent_task_id: string;
claim_token: string;
title: string;
description: string;
reward_amount: number;
acceptance_criteria: AcceptanceCriteria;
}
export interface CreateSubTaskResponse {
sub_task_id: string;
status: "DRAFT" | "OPEN";
}
export interface RequestPeerReviewRequest {
parent_task_id: string;
claim_token: string;
code_snippet: string;
review_instructions: string;
}
export interface RequestPeerReviewResponse {
review_task_id: string;
status: "OPEN";
cost: number;
message: string;
}
export interface BroadcastHelpSignalRequest {
parent_task_id: string;
claim_token: string;
error_message: string;
contextual_code?: string;
}
export interface BroadcastHelpSignalResponse {
sos_task_id: string;
status: "OPEN";
message: string;
}
export interface QueryAgentMemoryRequest {
query: string;
error_code?: string;
}
export interface QueryAgentMemoryResult {
task_title: string;
deliverables: unknown;
similarity_score?: number;
}
export interface QueryAgentMemoryResponse {
results: QueryAgentMemoryResult[];
}
export type A2AResourceType = "GPT_4O" | "CLAUDE_3_5_SONNET" | "EMBEDDINGS";
export interface RentApiResourceRequest {
agent_id: string;
resource_type: A2AResourceType;
duration_minutes: number;
}
export interface RentApiResourceResponse {
status: "GRANTED" | "INSUFFICIENT_FUNDS";
proxy_url?: string;
proxy_token?: string;
cost_deducted?: number;
message: string;
}