This commit is contained in:
2026-05-17 12:43:53 -07:00
commit 7c8def0aaa
7507 changed files with 1419399 additions and 0 deletions

121
dist/tools/gitea-api.js vendored Normal file
View File

@@ -0,0 +1,121 @@
"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)}`
};
}
}