feat: add sync-server for agent runner git-pull trigger
This commit is contained in:
72
sync-server.js
Normal file
72
sync-server.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* sync-server.js — runs inside the Theia container on port 3001
|
||||||
|
* Called by vibn-agent-runner after an agent session auto-commits to Gitea.
|
||||||
|
* Triggers git pull (or clone) so "Open in Theia" immediately shows new files.
|
||||||
|
*
|
||||||
|
* POST /sync { repo: "mark/sportsy" }
|
||||||
|
* GET /health
|
||||||
|
*/
|
||||||
|
const http = require('http');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
const WORKSPACE = '/home/project';
|
||||||
|
const GITEA_BASE = process.env.GITEA_API_URL || 'https://git.vibnai.com';
|
||||||
|
const GITEA_TOKEN = process.env.GITEA_TOKEN || process.env.GITEA_API_TOKEN || '';
|
||||||
|
const PORT = 3001;
|
||||||
|
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
if (req.url === '/health' && req.method === 'GET') {
|
||||||
|
res.writeHead(200).end(JSON.stringify({ ok: true, workspace: WORKSPACE }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.url === '/sync' && req.method === 'POST') {
|
||||||
|
let body = '';
|
||||||
|
req.on('data', d => { body += d; });
|
||||||
|
req.on('end', () => {
|
||||||
|
try {
|
||||||
|
const { repo } = JSON.parse(body || '{}');
|
||||||
|
if (!repo || !/^[\w.-]+\/[\w.-]+$/.test(repo)) {
|
||||||
|
res.writeHead(400).end(JSON.stringify({ error: 'invalid repo' }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoName = repo.split('/').pop();
|
||||||
|
const cloneDir = `${WORKSPACE}/${repoName}`;
|
||||||
|
const authedUrl = GITEA_BASE.replace('https://', `https://vibn:${GITEA_TOKEN}@`) + `/${repo}.git`;
|
||||||
|
|
||||||
|
let action;
|
||||||
|
try {
|
||||||
|
if (require('fs').existsSync(`${cloneDir}/.git`)) {
|
||||||
|
execSync(`git -C "${cloneDir}" fetch --depth=50 origin main 2>&1`, { timeout: 30000 });
|
||||||
|
execSync(`git -C "${cloneDir}" reset --hard origin/main 2>&1`, { timeout: 10000 });
|
||||||
|
action = 'pulled';
|
||||||
|
} else {
|
||||||
|
execSync(`git clone --depth=50 "${authedUrl}" "${cloneDir}" 2>&1`, { timeout: 60000 });
|
||||||
|
action = 'cloned';
|
||||||
|
}
|
||||||
|
} catch (gitErr) {
|
||||||
|
console.error('[sync-server] git error:', gitErr.message);
|
||||||
|
res.writeHead(500).end(JSON.stringify({ ok: false, error: gitErr.message }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[sync-server] ${action} ${repo} -> ${cloneDir}`);
|
||||||
|
res.writeHead(200).end(JSON.stringify({ ok: true, action, repo, path: cloneDir }));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[sync-server] error:', err.message);
|
||||||
|
res.writeHead(500).end(JSON.stringify({ ok: false, error: err.message }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(404).end(JSON.stringify({ error: 'not found' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(PORT, '0.0.0.0', () => {
|
||||||
|
console.log(`[sync-server] Listening on :${PORT} - workspace: ${WORKSPACE}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user