diff --git a/fix_mcp_fs.js b/fix_mcp_fs.js deleted file mode 100644 index ea9eaac4..00000000 --- a/fix_mcp_fs.js +++ /dev/null @@ -1,48 +0,0 @@ -const fs = require('fs'); - -const file = 'vibn-frontend/app/api/mcp/route.ts'; -let code = fs.readFileSync(file, 'utf8'); - -// 1. Fix toolFsWrite -const oldFsWriteReturn = ` return NextResponse.json({ - result: { path, bytesWritten: Buffer.byteLength(content, "utf8") }, - });`; - -const newFsWriteReturn = ` const { createHash } = require('crypto'); - const bytes = Buffer.byteLength(content, "utf8"); - const sha256 = createHash("sha256").update(content, "utf8").digest("hex"); - return NextResponse.json({ - result: { ok: true, path, bytes, sha256 }, - });`; - -code = code.replace(oldFsWriteReturn, newFsWriteReturn); - - -// 2. Fix toolFsEdit -const oldFsEditCmd = `const cmd = \`python3 -c "$(printf %s \${shq(pyB64)} | base64 -d)" <<< "$(printf %s \${shq(b64)} | base64 -d)"\`;`; -const newFsEditCmd = `const cmd = \`python3 -c "$(printf %s \${shq(pyB64)} | base64 -d)" <<< "$(printf %s \${shq(b64)} | base64 -d)" && echo "---" && sha256sum \${shq(path)} | cut -d' ' -f1 && wc -c < \${shq(path)}\`;`; - -code = code.replace(oldFsEditCmd, newFsEditCmd); - -const oldFsEditReturn = ` return NextResponse.json({ - result: { path, replacements: parseInt(r.stdout.trim() || "0", 10) }, - });`; - -const newFsEditReturn = ` const stdoutParts = r.stdout.split('---'); - const replacementsStr = stdoutParts[0].trim(); - const hashAndSize = stdoutParts[1] ? stdoutParts[1].trim().split('\\n') : []; - - return NextResponse.json({ - result: { - ok: true, - path, - replacements: parseInt(replacementsStr || "0", 10), - sha256: hashAndSize[0] ? hashAndSize[0].trim() : undefined, - bytes: hashAndSize[1] ? parseInt(hashAndSize[1].trim(), 10) : undefined - }, - });`; - -code = code.replace(oldFsEditReturn, newFsEditReturn); - -fs.writeFileSync(file, code); -console.log("Patched toolFsWrite and toolFsEdit"); diff --git a/fix_mcp_fs_normalize.js b/fix_mcp_fs_normalize.js deleted file mode 100644 index b3ab3ceb..00000000 --- a/fix_mcp_fs_normalize.js +++ /dev/null @@ -1,82 +0,0 @@ -const fs = require('fs'); - -const file = 'vibn-frontend/app/api/mcp/route.ts'; -let code = fs.readFileSync(file, 'utf8'); - -const oldNormalize = `function normalizeFsPath(p: string): string | NextResponse { - if (!p || typeof p !== "string") { - return NextResponse.json( - { error: 'Param "path" is required' }, - { status: 400 }, - ); - } - let abs: string; - if (p.startsWith("/")) { - abs = p; - } else { - abs = \`\${FS_ROOT}/\${p}\`.replace(/\\/+/g, "/"); - } - // Disallow .. traversal that escapes /workspace. - const norm = abs.replace(/\\/[^/]+\\/\\.\\.(?=\\/|$)/g, "").replace(/\\/+/g, "/"); - if (!norm.startsWith(FS_ROOT) && norm !== FS_ROOT) { - return NextResponse.json( - { - error: \`Path "\${p}" is outside \${FS_ROOT}; use shell.exec for system paths.\`, - }, - { status: 400 }, - ); - } - return norm; -}`; - -const newNormalize = `function normalizeFsPath( - p: string, - projectSlug?: string, -): string | NextResponse { - if (!p || typeof p !== "string") { - return NextResponse.json( - { error: 'Param "path" is required' }, - { status: 400 }, - ); - } - const projectRoot = projectSlug ? \`\${FS_ROOT}/\${projectSlug}\` : FS_ROOT; - let abs: string; - if (p.startsWith("/")) { - abs = p; - } else { - abs = \`\${projectRoot}/\${p}\`.replace(/\\/+/g, "/"); - } - const norm = abs.replace(/\\/[^/]+\\/\\.\\.(?=\\/|$)/g, "").replace(/\\/+/g, "/"); - - // When projectSlug is set, REJECT paths outside the project root. - if (projectSlug) { - if (!norm.startsWith(projectRoot) && norm !== projectRoot) { - return NextResponse.json( - { - ok: false, - error: \`PATH_OUTSIDE_PROJECT: path "\${p}" resolves to "\${norm}" which is outside the active project at "\${projectRoot}". Did you mean "\${projectRoot}/\${p.replace(/^\\/+/, "")}"?\`, - }, - { status: 400 }, - ); - } - } else { - // Workspace-level fallback (legacy behaviour) - if (!norm.startsWith(FS_ROOT) && norm !== FS_ROOT) { - return NextResponse.json( - { error: \`Path "\${p}" is outside \${FS_ROOT}; use shell.exec for system paths.\` }, - { status: 400 }, - ); - } - } - return norm; -}`; - -code = code.replace(oldNormalize, newNormalize); - -code = code.replace(/const path = normalizeFsPath\(String\(params\.path \?\? ""\)\);/g, 'const path = normalizeFsPath(String(params.path ?? ""), project.slug);'); -code = code.replace(/const path = normalizeFsPath\(String\(params\.path \?\? "\/workspace"\)\);/g, 'const path = normalizeFsPath(String(params.path ?? "/workspace"), project.slug);'); -code = code.replace(/const cwd = normalizeFsPath\(String\(params\.cwd \?\? "\/workspace"\)\);/g, 'const cwd = normalizeFsPath(String(params.cwd ?? "/workspace"), project.slug);'); -code = code.replace(/const targetPath = normalizeFsPath\(String\(params\.targetPath \?\? ""\)\);/g, 'const targetPath = normalizeFsPath(String(params.targetPath ?? ""), project.slug);'); - -fs.writeFileSync(file, code); -console.log("Patched normalizeFsPath with projectSlug scoping"); diff --git a/patch_prompts.js b/patch_prompts.js deleted file mode 100644 index 02f1132e..00000000 --- a/patch_prompts.js +++ /dev/null @@ -1,73 +0,0 @@ -const fs = require('fs'); - -const file = 'vibn-frontend/app/api/chat/route.ts'; -let code = fs.readFileSync(file, 'utf8'); - -// Fix 4: apps_containers_list -> apps_containers_ps -code = code.replace(/apps_containers_list \{ uuid \}/g, 'apps_containers_ps { uuid }'); - -// Fix 5: Soften ok field rule -const oldOkRule = `- **Trust the \\\`ok\\\` field.** Every tool result carries \\\`ok: true | false\\\`. If \\\`ok\\\` is false (or \\\`exitCode\\\` is non-zero, or \\\`healthCheck.status\\\` is >= 400), the operation FAILED. Do not describe a failed operation as successful. Surface the error verbatim and propose a next step.`; -const newOkRule = `- **Read tool results carefully.** A tool FAILED when ANY of these signals are present: \\\`ok: false\\\`, \\\`error: "..."\\\`, a non-zero \\\`exitCode\\\`, or a \\\`healthCheck.status\\\` >= 400. If NONE of those signals are present, look at the actual content of the response to decide whether the operation succeeded. Many read-only tools return data directly without an \\\`ok\\\` field — that's not a failure.`; -code = code.replace(oldOkRule, newOkRule); - -// Fix 7: Path conventions -const oldDirRule = `- **Directory:** the tool resolves paths relative to the active project root — you can pass \\\`command: "npm run dev"\\\` directly. (If you need to manually \\\`cd\\\`, use the project slug.)`; -const newDirRule = `- **Directory:** Tool paths are scoped to your project root automatically. Pass \\\`command: "npm run dev"\\\` directly — no \\\`cd\\\` prefix needed. The tool rejects any \\\`fs_*\\\` write outside \\\`/workspace//\\\`.`; -code = code.replace(oldDirRule, newDirRule); - -const oldPathConv = `**fs_* path convention for this project:** Pass paths relative to \\\`/workspace/\${activeProject.slug ?? ""}/\\\` — e.g. \\\`src/app/page.tsx\\\`, not \\\`/workspace/\${activeProject.slug ?? ""}/src/app/page.tsx\\\` and not \\\`getacquired-style/src/app/page.tsx\\\`. The tool layer rejects paths outside the project root.`; -const newPathConv = `**Path convention for fs_* tools:** Pass paths relative to the project root — \\\`src/app/page.tsx\\\`, NOT \\\`/workspace/\${activeProject.slug ?? ""}/src/app/page.tsx\\\` and NOT \\\`\${activeProject.slug ?? ""}/src/app/page.tsx\\\`. The tool layer rejects writes outside the project root with a \\\`PATH_OUTSIDE_PROJECT\\\` error suggesting the corrected path.`; -code = code.replace(oldPathConv, newPathConv); - -// Fix 8: fs_tree recommendation -const devContainerStart = `**Start a coding session:** \\\`devcontainer_ensure { projectId }\\\` (idempotent; first call ~10s, then instant).`; -const devContainerStartNew = `**Start a coding session:** \\\`devcontainer_ensure { projectId }\\\` (idempotent; first call ~10s, then instant). - -**Orient yourself once.** On the first code-modifying turn of a chat, call \\\`fs_tree\\\` once to learn the repo layout. Don't re-run it on every turn — the layout doesn't change between user messages.`; -code = code.replace(devContainerStart, devContainerStartNew); - -// Fix 9: browser_console -const visualQaBlock = `**Visual QA:** \\\`request_visual_qa { targetPath }\\\` critiques a UI file against a 5-dim design rubric. **Call this whenever you modify visual UI code** before returning the \\\`previewUrl\\\`. If it returns actionable issues, fix them with \\\`fs_edit\\\` before ending the turn. Skip for backend / SQL / config / non-visual changes.`; -const visualQaBlockNew = `**Visual QA:** \\\`request_visual_qa { targetPath }\\\` critiques a UI file against a 5-dim design rubric. **Call this whenever you modify visual UI code** before returning the \\\`previewUrl\\\`. If it returns actionable issues, fix them with \\\`fs_edit\\\` before ending the turn. Skip for backend / SQL / config / non-visual changes. - -**Verify the page actually renders:** -- After \\\`dev_server_start\\\` returns a \\\`previewUrl\\\` AND \\\`healthCheck.status === 200\\\`, for any UI-facing turn, call \\\`browser_console { url: previewUrl }\\\` to capture frontend console errors. Hydration errors, missing assets, and uncaught exceptions show up here even when the server is technically "running". -- If \\\`browser_console\\\` returns errors, fix them with \\\`fs_edit\\\` before declaring done. A green \\\`healthCheck\\\` plus a clean console is the real "done" signal for UI work. -- Skip this for backend / SQL / config-only changes.`; -code = code.replace(visualQaBlock, visualQaBlockNew); - -// Fix 10: Market research stack -const commonQuestionsBlock = `## Common questions → tools -- "What is project X?" → \\\`projects_get { projectId }\\\` -- "What's running / has a domain?" → \\\`apps_list { projectId }\\\` (or workspace-wide without projectId) -- "Show logs / containers / env" → \\\`apps_list\\\` to resolve uuid, then \\\`apps_logs\\\` / \\\`apps_containers_list\\\` / \\\`apps_envs_list\\\` -- "Find an OSS X" → \\\`github_search\\\` (include \\\`license:mit\\\` by default), then \\\`github_file\\\` to read README / docker-compose -- "What do the docs say about Y?" → \\\`http_fetch\\\``; - -// Oops, we changed apps_containers_list to apps_containers_ps in Fix 4, so let's match the updated one: -const commonQuestionsBlockMatched = `## Common questions → tools -- "What is project X?" → \\\`projects_get { projectId }\\\` -- "What's running / has a domain?" → \\\`apps_list { projectId }\\\` (or workspace-wide without projectId) -- "Show logs / containers / env" → \\\`apps_list\\\` to resolve uuid, then \\\`apps_logs\\\` / \\\`apps_containers_ps\\\` / \\\`apps_envs_list\\\` -- "Find an OSS X" → \\\`github_search\\\` (include \\\`license:mit\\\` by default), then \\\`github_file\\\` to read README / docker-compose -- "What do the docs say about Y?" → \\\`http_fetch\\\``; - -const marketResearchBlock = ` - -## Helping the user pick what to build - -Vibn has a market-research toolkit for non-technical founders who need data on their target niche. Use it when the user is undecided, validating an idea, or comparing markets: - -- **"How big is the market for X in ?"** → \\\`market_categories_suggest { niche }\\\` to propose Google Business categories, then \\\`market_research_run\\\` after the user approves. Returns TAM count, sample domains, and review data. NOTE: \\\`market_research_run\\\` costs real money — always confirm with the user and pass \\\`user_explicitly_approved: true\\\`. -- **"What are competitors spending on Google Ads?"** → \\\`market_seo_analyze { domain }\\\`. Returns organic traffic, paid traffic, ad spend, and top paid keywords. Use to tell the user how aggressive a market is. -- **"What software do these businesses already use?"** → \\\`tech_stack_analyze { urls, software_category_id }\\\`. Detects WordPress, Shopify, named competitors, and any custom domains/scripts you pass. Use to find "X businesses use WordPress but lack Y" market gaps. -- **"What are customers complaining about?"** → \\\`market_aggregate_insights { category, location }\\\`. Returns top review topics — use the actual words customers use as marketing copy and value-prop seeds. -- **"Who are the players in this niche?"** → \\\`market_competitor_research { niche }\\\`. Returns proprietary competitors with pricing AND open-source alternatives that could be forked. - -These are conversational research tools — they don't build anything. Use them BEFORE scaffolding when the user is exploring direction; SKIP them once the user has committed to building.`; - -code = code.replace(commonQuestionsBlockMatched, commonQuestionsBlockMatched + marketResearchBlock); - -fs.writeFileSync(file, code); -console.log("Applied Phase 2 and 3 prompt fixes"); diff --git a/patch_runner_context.js b/patch_runner_context.js deleted file mode 100644 index cbe7a52c..00000000 --- a/patch_runner_context.js +++ /dev/null @@ -1,19 +0,0 @@ -const fs = require('fs'); -const file = 'vibn-agent-runner/src/tools/context.ts'; -let code = fs.readFileSync(file, 'utf8'); - -const newProps = ` coolify: { - apiUrl: string; - apiToken: string; - }; - mcpToken: string; - vibnApiUrl: string; - projectId?: string;`; - -code = code.replace(` coolify: { - apiUrl: string; - apiToken: string; - };`, newProps); - -fs.writeFileSync(file, code); -console.log("Patched context.ts"); diff --git a/patch_runner_server.js b/patch_runner_server.js deleted file mode 100644 index ee89f248..00000000 --- a/patch_runner_server.js +++ /dev/null @@ -1,69 +0,0 @@ -const fs = require('fs'); -const file = 'vibn-agent-runner/src/server.ts'; -let code = fs.readFileSync(file, 'utf8'); - -// Update the type signature for the request body -const oldSig = ` } = req.body as { - sessionId?: string; - projectId?: string; - appName?: string; - appPath?: string; - giteaRepo?: string; - task?: string; - continueTask?: boolean; - autoApprove?: boolean; - coolifyAppUuid?: string; - };`; - -const newSig = ` mcpToken, vibnApiUrl - } = req.body as { - sessionId?: string; - projectId?: string; - appName?: string; - appPath?: string; - giteaRepo?: string; - task?: string; - continueTask?: boolean; - autoApprove?: boolean; - coolifyAppUuid?: string; - mcpToken?: string; - vibnApiUrl?: string; - };`; - -code = code.replace(oldSig, newSig); - -const oldCtx = ` const ctx: ToolContext = { - workspaceRoot: repoRoot, - gitea: { - apiUrl: GITEA_API_URL, - apiToken: GITEA_API_TOKEN, - username: GITEA_USERNAME, - }, - coolify: { - apiUrl: process.env.COOLIFY_API_URL || '', - apiToken: process.env.COOLIFY_API_TOKEN || '', - }, - memoryUpdates: [], - };`; - -const newCtx = ` const ctx: ToolContext = { - workspaceRoot: repoRoot, - gitea: { - apiUrl: GITEA_API_URL, - apiToken: GITEA_API_TOKEN, - username: GITEA_USERNAME, - }, - coolify: { - apiUrl: process.env.COOLIFY_API_URL || '', - apiToken: process.env.COOLIFY_API_TOKEN || '', - }, - mcpToken: mcpToken || '', - vibnApiUrl: vibnApiUrl || 'http://localhost:3000', - projectId, - memoryUpdates: [], - };`; - -code = code.replace(oldCtx, newCtx); - -fs.writeFileSync(file, code); -console.log("Patched Runner server.ts"); diff --git a/patch_sessions_route.js b/patch_sessions_route.js deleted file mode 100644 index 9e20fb66..00000000 --- a/patch_sessions_route.js +++ /dev/null @@ -1,55 +0,0 @@ -const fs = require('fs'); - -const file = 'vibn-frontend/app/api/projects/[projectId]/agent/sessions/route.ts'; -let code = fs.readFileSync(file, 'utf8'); - -// Inject the workspace API key fetching logic -if (!code.includes('listWorkspaceApiKeys')) { - code = code.replace( - 'import { query } from "@/lib/db-postgres";', - 'import { query } from "@/lib/db-postgres";\nimport { listWorkspaceApiKeys, mintWorkspaceApiKey, revealWorkspaceApiKey } from "@/lib/auth/workspace-auth";' - ); - - const injectCode = ` - const wsResult = await query<{ workspace_id: string }>( - \`SELECT vibn_workspace_id as workspace_id FROM fs_projects WHERE id = $1 LIMIT 1\`, - [projectId] - ); - if (!wsResult.length) { - return NextResponse.json({ error: "Project not found" }, { status: 404 }); - } - const workspaceId = wsResult[0].workspace_id; - - // Grab or mint a default API key for the runner to use - let mcpToken = ""; - const keys = await listWorkspaceApiKeys(workspaceId); - let defaultKey = keys.find((k: any) => k.name === 'default' && !k.revoked_at); - if (!defaultKey) { - const minted = await mintWorkspaceApiKey({ workspaceId, name: 'default', createdBy: session.user.id, scopes: ['workspace:*'] }); - mcpToken = minted.token; - } else { - const revealed = await revealWorkspaceApiKey(workspaceId, defaultKey.id); - if (revealed) mcpToken = revealed.token; - else { - const minted = await mintWorkspaceApiKey({ workspaceId, name: 'default', createdBy: session.user.id, scopes: ['workspace:*'] }); - mcpToken = minted.token; - } - } - - // Add VIBN_API_URL so the runner knows where to send MCP requests - const vibnApiUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"; -`; - - code = code.replace( - 'const sessionId = rows[0].id;', - 'const sessionId = rows[0].id;\n' + injectCode - ); - - code = code.replace( - 'coolifyAppUuid,\n }),', - 'coolifyAppUuid,\n mcpToken,\n vibnApiUrl\n }),' - ); - - fs.writeFileSync(file, code); - console.log("Patched session route to forward MCP token"); -}