From ab52535121097f2ffb20577f6872b0bc4b89c945 Mon Sep 17 00:00:00 2001 From: OG T Date: Sun, 7 Jun 2026 15:30:21 +0800 Subject: [PATCH] fix(scout-bot): sanitize draft payload and widen default scan targets --- apps/scout-bot/src/index.ts | 37 ++++++++++++++++++++++++++++++------- docker-compose.yml | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apps/scout-bot/src/index.ts b/apps/scout-bot/src/index.ts index 094870b..776262e 100644 --- a/apps/scout-bot/src/index.ts +++ b/apps/scout-bot/src/index.ts @@ -31,7 +31,7 @@ const SCOUT_TARGET_REPOS = SCOUT_TARGET_REPOS_RAW }) .filter((repo) => repo.owner && repo.repo); -const fallbackRepo = { owner: "vibe-work", repo: "test-bounty-repo" }; +const fallbackRepo = { owner: "open-webui", repo: "open-webui" }; const TARGET_REPOS = SCOUT_TARGET_REPOS.length > 0 ? SCOUT_TARGET_REPOS : [fallbackRepo]; if (!SCOUT_API_KEY) { @@ -41,6 +41,34 @@ if (!GITHUB_TOKEN) { console.warn("WARNING: GITHUB_TOKEN is not set. Scout bot cannot post comments."); } +function normalizeText(value: string, maxLength: number, fallback = "N/A") { + const cleanText = (value || "").replace(/\s+/g, " ").trim(); + if (!cleanText) { + return fallback; + } + return cleanText.length <= maxLength ? cleanText : `${cleanText.slice(0, Math.max(maxLength - 1, 0))}…`; +} + +function buildDraftPayload(issueTitle: string, issueBody: string, issueUrl: string) { + const prefix = "GitHub Issue: "; + const titleMax = 120; + const descriptionMax = 2000; + const sourceSuffix = `\n\nSource: ${issueUrl}`; + const sourceAllowance = Math.max(descriptionMax - sourceSuffix.length, 20); + + const safeTitle = normalizeText(issueTitle, Math.max(titleMax - prefix.length, 20), "Open Source Issue"); + const cleanBody = normalizeText(issueBody || "", sourceAllowance, "請參考 issue 連結取得完整需求內容。"); + + return { + title: `${prefix}${safeTitle}`, + description: `${cleanBody}${sourceSuffix}`, + reward_amount: 1000, // $10.00 Beta Promo subsidy (<= 2000 triggers free promo logic) + reward_currency: "USD", + required_stack: ["TypeScript", "GitHub"], + test_file_content: "// Needs manual test writing based on issue", + }; +} + const octokit = new Octokit({ auth: GITHUB_TOKEN, }); @@ -95,12 +123,7 @@ async function postDraftWithRetry(payload: { async function draftBountyTask(issueTitle: string, issueBody: string, issueUrl: string) { const payload = { scout_id: SCOUT_AGENT_ID, - title: `GitHub Issue: ${issueTitle}`, - description: `${issueBody}\n\nSource: ${issueUrl}`, - reward_amount: 1000, // $10.00 Beta Promo subsidy (<= 2000 triggers free promo logic) - reward_currency: "USD", - required_stack: ["TypeScript", "GitHub"], - test_file_content: "// Needs manual test writing based on issue" + ...buildDraftPayload(issueTitle, issueBody || "", issueUrl), }; return postDraftWithRetry(payload); diff --git a/docker-compose.yml b/docker-compose.yml index 590aa11..60aa923 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,7 +66,7 @@ services: - SCOUT_API_KEY=${SCOUT_API_KEY:-${API_KEY:-super-secret-mcp-key}} - SCOUT_AGENT_ID=scout_official_1 # Optional: add more discovery repos via env (comma separated "owner/repo"), e.g. openai/swarm,significant-gravitas/autogpt - - SCOUT_TARGET_REPOS=${SCOUT_TARGET_REPOS:-open-webui/open-webui,microsoft/vscode,vercel/next.js,langchain-ai/langgraph,facebook/react,microsoft/TypeScript,openai/openai-cookbook,astral-sh/ruff,sequelize/sequelize,pnpm/pnpm,prisma/prisma} + - SCOUT_TARGET_REPOS=${SCOUT_TARGET_REPOS:-open-webui/open-webui,microsoft/vscode,vercel/next.js,langchain-ai/langgraph,facebook/react,microsoft/TypeScript,openai/openai-cookbook,astral-sh/ruff,sequelize/sequelize,pnpm/pnpm,prisma/prisma,openai/swarm,Significant-Gravitas/AutoGPT,microsoft/autogen,crewAIInc/crewAI} # Optional: comma-separated labels; leave unset for broad scan of all open issues - SCOUT_ISSUE_LABELS=${SCOUT_ISSUE_LABELS:-} # Keep compatibility with existing naming