sin-github-issues / src /runtime.ts
OpenCode
Deploy latest SIN-GitHub-Issues runtime
cd455f6
Raw
History Blame Contribute Delete
9.63 kB
import { exec, execFile } from 'node:child_process';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { promisify } from 'node:util';
import { createClient } from '@supabase/supabase-js';
import { commentIssueAsGitHubApp, getGitHubAppRoutingStatus, routeGitHubWebhook } from './github-app-routing.js';
const execAsync = promisify(exec);
const execFileAsync = promisify(execFile);
export type GitHubAgentAction =
| { action: 'agent.help' }
| { action: 'sin.github.health' }
| { action: 'sin.github.app.routing.status' }
| { action: 'sin.github.webhook.route'; payload?: unknown; rawBody?: string; routePath?: string; signature256?: string; eventName?: string; deliveryId?: string }
| { action: 'sin.github.issue.comment.as_app'; repo: string; issueNumber: number; body: string; agentSlug?: string; appId?: number; installationId: number }
| { action: 'sin.github.project.orchestrate'; prompt: string; contextDir?: string }
| { action: 'sin.github.issue.manage'; prompt: string; issueNumber?: number }
| { action: 'sin.github.wiki.sync'; prompt: string; contextDir?: string }
| { action: 'sin.github.discussion.start'; prompt: string; category?: string }
| { action: 'sin.github.gist.publish'; prompt: string; isPublic?: boolean }
| { action: 'sin.github.security.audit'; prompt: string; contextDir?: string }
| { action: 'sin.github.pr.review'; prNumber: number; contextDir?: string }
| { action: 'sin.github.issue.pool.enqueue'; repoName: string; issueNumber: number; title: string; body?: string; labels?: string[]; issueUrl?: string; assignedTeam?: string; assignedAgent?: string; status?: string; state?: string; routingStrategy?: string; fanoutPlan?: unknown[] }
| { action: 'sin.github.ledger.log'; agentName: string; activityTitle: string; details: string };
export async function executeGitHubAgentAction(action: GitHubAgentAction): Promise<unknown> {
switch (action.action) {
case 'agent.help':
return {
ok: true,
agent: 'sin-github-issues',
mandate: 'CEO Elite GitHub Operations. Orchestrates Projects, Issues, Wikis, Discussions, Gists, Security, PR Reviews, and public Showcase Ledgers.',
actions: [
'sin.github.health',
'sin.github.app.routing.status',
'sin.github.webhook.route',
'sin.github.issue.comment.as_app',
'sin.github.project.orchestrate',
'sin.github.issue.manage',
'sin.github.wiki.sync',
'sin.github.discussion.start',
'sin.github.gist.publish',
'sin.github.security.audit',
'sin.github.pr.review',
'sin.github.issue.pool.enqueue',
'sin.github.ledger.log'
],
};
case 'sin.github.health':
return {
ok: true,
agent: 'sin-github-issues',
primaryModel: 'openai/gpt-5.4',
fallbackModel: 'opencode/minimax-m2.5-free',
team: 'Team - Coding',
status: 'Elite 2026 GitHub Operations Architect Online',
capabilities: ['projects', 'wikis', 'discussions', 'gists', 'security', 'prs', 'issues', 'ledger', 'github-app-routing'],
githubAppRouting: await getGitHubAppRoutingStatus(),
};
case 'sin.github.app.routing.status':
return await getGitHubAppRoutingStatus();
case 'sin.github.webhook.route':
return await routeGitHubWebhook(action);
case 'sin.github.issue.comment.as_app':
return await commentIssueAsGitHubApp(action);
case 'sin.github.project.orchestrate':
return await executeOpenCode(
`Use the GitHub CLI ('gh project create/link/item-add') to orchestrate the following project board requirements: ${action.prompt}. Ensure the board is linked to the repository and items are properly assigned.`,
action.contextDir
);
case 'sin.github.issue.manage':
return await executeOpenCode(
`Manage GitHub issues based on: ${action.prompt}. ${action.issueNumber ? `Target issue: #${action.issueNumber}` : 'Create a new Epic/Task.'} Ensure proper labeling, assignment, and milestone linking.`
);
case 'sin.github.wiki.sync':
return await executeOpenCode(
`Synchronize the repository's Wiki with the following documentation update: ${action.prompt}. Generate standard Markdown and push it to the wiki repository (.wiki.git).`,
action.contextDir
);
case 'sin.github.discussion.start':
return await executeOpenCode(
`Start or manage a GitHub Discussion to keep issues clean. Requirements: ${action.prompt}. Category: ${action.category || 'General'}.`
);
case 'sin.github.gist.publish':
return await executeOpenCode(
`Create a GitHub Gist (public: ${action.isPublic ?? false}) for the following payload (long log, snippet, or config): ${action.prompt}. Return the Gist URL for embedding in an issue or PR.`
);
case 'sin.github.security.audit':
return await executeOpenCode(
`Audit the repository for security vulnerabilities (Dependabot/CodeQL). Generate or update '.github/dependabot.yml' and review any active alerts based on: ${action.prompt}.`,
action.contextDir
);
case 'sin.github.pr.review':
return await executeOpenCode(
`Perform a rigorous Code Review on Pull Request #${action.prNumber}. Check for 2026 architectural compliance, security vulnerabilities, performance regressions, and test coverage. If it passes 100%, approve it ('gh pr review --approve') and merge it ('gh pr merge --auto').`,
action.contextDir
);
case 'sin.github.issue.pool.enqueue':
return await enqueueIssuePoolItem(action);
case 'sin.github.ledger.log':
return await publishLedgerLog(action.agentName, action.activityTitle, action.details);
}
}
async function executeOpenCode(prompt: string, dir?: string) {
await execAsync('python3 scripts/hf_pull_script.py').catch(() => console.warn('Warning: hf_pull_script failed. Proceeding with cached credentials.'));
const options = dir ? { cwd: dir } : {};
try {
const { stdout, stderr } = await execFileAsync('opencode', ['run', prompt, '--model', 'openai/gpt-5.4'], options);
return {
ok: true,
expertAnalysis: stdout,
warnings: stderr || undefined,
};
} catch (error: any) {
throw new Error(`GitHub Operations Execution Failed: ${error.message}`);
}
}
async function publishLedgerLog(agentName: string, activityTitle: string, details: string) {
const title = `[${agentName}] ${activityTitle}`;
const body = [
`# ${activityTitle}`,
'',
`- Agent: ${agentName}`,
`- Published: ${new Date().toISOString()}`,
'',
'## Details',
details,
].join('\n');
const tempDir = await mkdtemp(join(tmpdir(), 'sin-github-ledger-'));
const bodyPath = join(tempDir, 'ledger-body.md');
try {
await writeFile(bodyPath, body, 'utf8');
const { stdout, stderr } = await execFileAsync('gh', [
'issue',
'create',
'-R',
'Delqhi/OpenSIN-Ledger',
'--title',
title,
'--body-file',
bodyPath,
]);
return {
ok: true,
repo: 'Delqhi/OpenSIN-Ledger',
title,
url: stdout.trim(),
warnings: stderr || undefined,
};
} catch (error: any) {
throw new Error(`GitHub Ledger Publish Failed: ${error.message}`);
} finally {
await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
}
}
async function enqueueIssuePoolItem(action: Extract<GitHubAgentAction, { action: 'sin.github.issue.pool.enqueue' }>) {
const supabaseUrl = (process.env.SUPABASE_URL || process.env.SIN_SUPABASE_URL || '').trim();
const serviceRoleKey = (process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SIN_SUPABASE_SERVICE_ROLE_KEY || '').trim();
if (!supabaseUrl || !serviceRoleKey) {
throw new Error('sin_supabase_config_missing: set SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY or SIN_SUPABASE_URL/SIN_SUPABASE_SERVICE_ROLE_KEY before enqueueing issue-pool items');
}
const supabase = createClient(supabaseUrl, serviceRoleKey, {
auth: { persistSession: false, autoRefreshToken: false },
});
const payload = {
issue_number: action.issueNumber,
repo_name: action.repoName,
title: action.title,
body: action.body || null,
labels: action.labels || [],
issue_url: action.issueUrl || null,
state: action.state || 'open',
assigned_team: action.assignedTeam || 'team-coding',
assigned_agent: action.assignedAgent || null,
status: action.status || 'open',
routing_strategy: action.routingStrategy || 'single-dispatch',
fanout_plan: action.fanoutPlan || [],
updated_at: new Date().toISOString(),
};
let { data, error } = await supabase.from('sin_issues_pool').insert(payload).select('*').single();
if (error && /column/i.test(error.message)) {
const fallbackPayload = {
issue_number: action.issueNumber,
repo_name: action.repoName,
title: action.title,
body: action.body || null,
state: action.state || 'open',
assigned_team: action.assignedTeam || 'team-coding',
assigned_agent: action.assignedAgent || null,
status: action.status || 'pending',
updated_at: new Date().toISOString(),
};
({ data, error } = await supabase.from('sin_issues_pool').insert(fallbackPayload).select('*').single());
}
if (error) {
throw new Error(`sin_issue_pool_enqueue_failed: ${error.message}`);
}
return {
ok: true,
table: 'sin_issues_pool',
item: data,
};
}