"use strict"; // ============================================================================= // Pure Gitea API — no ToolContext coupling, no registry coupling. // // Takes a plain { apiUrl, apiToken, username } config. Security guardrails // (PROTECTED_GITEA_REPOS, assertGiteaWritable) are enforced inside each // function so every caller gets the same protection. // // Consumed by: // - tools/gitea.ts (in-process registry used by agent-runner loop) // - mcp/gitea-server.ts (stdio MCP server exposed to any MCP client) // ============================================================================= Object.defineProperty(exports, "__esModule", { value: true }); exports.createIssue = createIssue; exports.listIssues = listIssues; exports.closeIssue = closeIssue; exports.listRepos = listRepos; exports.listAllIssues = listAllIssues; exports.readRepoFile = readRepoFile; const security_1 = require("./security"); async function giteaFetch(cfg, path, method = 'GET', body) { const res = await fetch(`${cfg.apiUrl}/api/v1${path}`, { method, headers: { 'Authorization': `token ${cfg.apiToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: body ? JSON.stringify(body) : undefined }); if (!res.ok) { return { error: `Gitea API error: ${res.status} ${res.statusText}` }; } return res.json(); } async function createIssue(cfg, input) { (0, security_1.assertGiteaWritable)(input.repo); return giteaFetch(cfg, `/repos/${input.repo}/issues`, 'POST', { title: input.title, body: input.body, labels: input.labels, }); } async function listIssues(cfg, repo, state = 'open') { return giteaFetch(cfg, `/repos/${repo}/issues?state=${state}&limit=20`); } async function closeIssue(cfg, repo, issueNumber) { (0, security_1.assertGiteaWritable)(repo); return giteaFetch(cfg, `/repos/${repo}/issues/${issueNumber}`, 'PATCH', { state: 'closed' }); } async function listRepos(cfg) { const res = await fetch(`${cfg.apiUrl}/api/v1/repos/search?limit=50`, { headers: { 'Authorization': `token ${cfg.apiToken}` } }); if (!res.ok) { return { error: `Gitea API error: ${res.status} ${res.statusText}` }; } const data = await res.json(); return (data.data || []) .filter((r) => !security_1.PROTECTED_GITEA_REPOS.has(r.full_name)) .map((r) => ({ name: r.full_name, description: r.description, default_branch: r.default_branch, updated: r.updated, stars: r.stars_count, open_issues: r.open_issues_count, })); } async function listAllIssues(cfg, opts = {}) { const state = opts.state || 'open'; if (opts.repo) { if (security_1.PROTECTED_GITEA_REPOS.has(opts.repo)) { return { error: `SECURITY: "${opts.repo}" is a protected Vibn platform repo. Agents cannot access its issues.` }; } return giteaFetch(cfg, `/repos/${opts.repo}/issues?state=${state}&limit=20`); } // Fetch across all non-protected repos (cap at 10 repos to bound request count) const reposRes = await fetch(`${cfg.apiUrl}/api/v1/repos/search?limit=50`, { headers: { 'Authorization': `token ${cfg.apiToken}` } }); if (!reposRes.ok) { return { error: `Gitea API error: ${reposRes.status} ${reposRes.statusText}` }; } const reposData = await reposRes.json(); const repos = (reposData.data || []).filter((r) => !security_1.PROTECTED_GITEA_REPOS.has(r.full_name)); const allIssues = []; for (const r of repos.slice(0, 10)) { const issues = await giteaFetch(cfg, `/repos/${r.full_name}/issues?state=${state}&limit=10`); if (Array.isArray(issues)) { allIssues.push(...issues.map((i) => ({ repo: r.full_name, number: i.number, title: i.title, state: i.state, labels: i.labels?.map((l) => l.name), created: i.created_at, }))); } } return allIssues; } async function readRepoFile(cfg, repo, filePath) { try { const res = await fetch(`${cfg.apiUrl}/api/v1/repos/${repo}/contents/${filePath}`, { headers: { 'Authorization': `token ${cfg.apiToken}` } }); if (!res.ok) return { error: `File not found: ${filePath} in ${repo}` }; const data = await res.json(); const content = Buffer.from(data.content, 'base64').toString('utf8'); return { repo, path: filePath, content }; } catch (err) { return { error: `Failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}` }; } }