fix: POST for file creation (Gitea 1.25+), add edit_issue tool, persist poll state
- files.ts: Use POST for new files, PUT for updates (Gitea 1.25 requires this) - issues.ts: Add editIssue() for state/title/body changes - write-tools.ts: Add gitea_edit_issue tool (open/close/edit issues) - webhook/server.ts: Persist lastPollAt to disk to prevent duplicate events on reload; use followUp delivery to queue events during LLM turns - index.ts: Use deliverAs:'followUp' for sendUserMessage
This commit is contained in:
@@ -20,6 +20,8 @@ const POLL_INTERVAL = parseInt(process.env.PI_BOT_POLL_INTERVAL ?? "300", 10);
|
||||
const EVENT_POLL_INTERVAL = parseInt(process.env.PI_EVENT_POLL_INTERVAL ?? "60", 10);
|
||||
const BOT_USER = process.env.PI_GIT_USER ?? "";
|
||||
|
||||
const POLL_STATE_FILE = "/home/pibot/.pi/agent/gitea-poll-state.json";
|
||||
|
||||
let server: Server | null = null;
|
||||
let processingQueue: Array<{ event: any; timestamp: number }> = [];
|
||||
let maxQueueDepth = 50;
|
||||
@@ -37,6 +39,31 @@ let pollOnlyRepos: Map<string, { addedAt: number; lastPollAt: string }> = new Ma
|
||||
/** Track all known repos so we don't re-attempt webhook install every cycle */
|
||||
let knownRepos: Set<string> = new Set();
|
||||
|
||||
/** Load poll timestamps from disk (survives reloads) */
|
||||
async function loadPollState(): Promise<Record<string, string>> {
|
||||
try {
|
||||
const fs = await import("node:fs/promises");
|
||||
const data = await fs.readFile(POLL_STATE_FILE, "utf-8");
|
||||
return JSON.parse(data);
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** Save poll timestamps to disk */
|
||||
async function savePollState(): Promise<void> {
|
||||
try {
|
||||
const fs = await import("node:fs/promises");
|
||||
const state: Record<string, string> = {};
|
||||
for (const [name, s] of pollOnlyRepos) {
|
||||
state[name] = s.lastPollAt;
|
||||
}
|
||||
await fs.writeFile(POLL_STATE_FILE, JSON.stringify(state), "utf-8");
|
||||
} catch (err) {
|
||||
console.error("[gitea-polling] Error saving poll state:", err instanceof Error ? err.message : err);
|
||||
}
|
||||
}
|
||||
|
||||
export function setSendMessage(fn: (message: string) => Promise<void>) {
|
||||
sendMessage = fn;
|
||||
}
|
||||
@@ -273,6 +300,7 @@ async function discoverRepos() {
|
||||
try {
|
||||
const client = new GiteaClient();
|
||||
const repos = await client.get<any[]>("/user/repos?limit=100");
|
||||
const savedState = await loadPollState();
|
||||
|
||||
let newWebhooks = 0;
|
||||
let newPollOnly = 0;
|
||||
@@ -303,11 +331,11 @@ async function discoverRepos() {
|
||||
} catch (err) {
|
||||
if (err instanceof GiteaError && err.status === 403) {
|
||||
// No admin access — fall back to polling
|
||||
// Set lastPollAt to 5 minutes ago so we catch recent events
|
||||
const fiveMinAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
|
||||
// Use persisted timestamp if available, otherwise 5 min ago
|
||||
const lastPoll = savedState[name] ?? new Date(Date.now() - 5 * 60 * 1000).toISOString();
|
||||
pollOnlyRepos.set(name, {
|
||||
addedAt: Date.now(),
|
||||
lastPollAt: fiveMinAgo,
|
||||
lastPollAt: lastPoll,
|
||||
});
|
||||
knownRepos.add(name);
|
||||
newPollOnly++;
|
||||
@@ -319,10 +347,10 @@ async function discoverRepos() {
|
||||
}
|
||||
} else {
|
||||
// No webhook URL configured — all repos are poll-only
|
||||
const fiveMinAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
|
||||
const lastPoll = savedState[name] ?? new Date(Date.now() - 5 * 60 * 1000).toISOString();
|
||||
pollOnlyRepos.set(name, {
|
||||
addedAt: Date.now(),
|
||||
lastPollAt: fiveMinAgo,
|
||||
lastPollAt: lastPoll,
|
||||
});
|
||||
knownRepos.add(name);
|
||||
newPollOnly++;
|
||||
@@ -430,6 +458,9 @@ async function pollForEvents() {
|
||||
console.error(`[gitea-poll-events] Error polling ${repoName}: ${err instanceof Error ? err.message : err}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist poll timestamps
|
||||
await savePollState();
|
||||
}
|
||||
|
||||
// ── Public API ───────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user