Spaces:
Sleeping
Sleeping
| import { access } from 'node:fs/promises'; | |
| import { constants } from 'node:fs'; | |
| import { recordAuditEvent } from './audit.js'; | |
| import type { GitHubIssuesAction } from './contracts.js'; | |
| import { closeIssue, commentIssue, ensureIssue } from './github.js'; | |
| import { commitAll, pushBranch, repoStatus, startBranch } from './git.js'; | |
| import { clean, DEFAULT_STATE_PATH, readState, writeState } from './state.js'; | |
| const PROFILE_EMAIL = 'zukunftsorientierte.energie@gmail.com'; | |
| const PROFILE_DIRECTORY = 'Default'; | |
| const DOCS_TAB_STATUS = 't.tuv2t449f8ze'; | |
| export async function executeTemplateAgentAction(action: GitHubIssuesAction): Promise<unknown> { | |
| switch (action.action) { | |
| case 'agent.help': | |
| return buildHelpPayload(); | |
| case 'sin.github.issues.health': | |
| return healthStatus(); | |
| case 'sin.github.issues.onboarding.status': | |
| return { ok: true, statePath: DEFAULT_STATE_PATH, state: await readState() }; | |
| case 'sin.github.issues.onboarding.save': | |
| if (!action.confirm) throw new Error('input_required:confirm=true required'); | |
| return { | |
| ok: true, | |
| statePath: DEFAULT_STATE_PATH, | |
| state: await writeState({ | |
| ownerEmail: clean(action.ownerEmail), | |
| notes: clean(action.notes), | |
| defaultRepo: clean(action.defaultRepo), | |
| browserProfileEmail: clean(action.browserProfileEmail) || PROFILE_EMAIL, | |
| browserProfileDirectory: clean(action.browserProfileDirectory) || PROFILE_DIRECTORY, | |
| updatedAt: new Date().toISOString(), | |
| }), | |
| }; | |
| case 'sin.github.issues.auth.status': | |
| return authStatus(); | |
| case 'sin.github.issues.issue.ensure': | |
| return withAudit('issue.ensure', ensureIssue(action)); | |
| case 'sin.github.issues.issue.comment': | |
| return withAudit('issue.comment', commentIssue(action.repo, action.issueNumber, action.body)); | |
| case 'sin.github.issues.issue.close': | |
| return withAudit('issue.close', closeIssue(action.repo, action.issueNumber, action.comment)); | |
| case 'sin.github.issues.repo.status': | |
| return repoStatus(action.repoPath); | |
| case 'sin.github.issues.repo.branch.start': | |
| return withAudit('repo.branch.start', startBranch(action.repoPath, action.branchName, action.baseRef)); | |
| case 'sin.github.issues.repo.commit': | |
| return withAudit('repo.commit', commitAll(action.repoPath, action.message)); | |
| case 'sin.github.issues.repo.push': | |
| return withAudit('repo.push', pushBranch(action.repoPath, action.remote, action.branchName)); | |
| case 'sin.github.issues.workflow.finalize_fix': | |
| return finalizeFix(action); | |
| } | |
| } | |
| function buildHelpPayload() { | |
| return { | |
| ok: true, | |
| agent: 'sin-github-issues', | |
| actions: [ | |
| 'sin.github.issues.health', | |
| 'sin.github.issues.onboarding.status', | |
| 'sin.github.issues.onboarding.save', | |
| 'sin.github.issues.auth.status', | |
| 'sin.github.issues.issue.ensure', | |
| 'sin.github.issues.issue.comment', | |
| 'sin.github.issues.issue.close', | |
| 'sin.github.issues.repo.status', | |
| 'sin.github.issues.repo.branch.start', | |
| 'sin.github.issues.repo.commit', | |
| 'sin.github.issues.repo.push', | |
| 'sin.github.issues.workflow.finalize_fix', | |
| ], | |
| }; | |
| } | |
| async function healthStatus() { | |
| return { | |
| ok: true, | |
| agent: 'sin-github-issues', | |
| primaryModel: 'opencode/nemotron-3-super-free', | |
| team: 'Team - Coding', | |
| docsTabStatus: DOCS_TAB_STATUS, | |
| browserProfileEmail: PROFILE_EMAIL, | |
| browserProfileDirectory: PROFILE_DIRECTORY, | |
| auth: await authStatus(), | |
| }; | |
| } | |
| async function authStatus() { | |
| return { | |
| githubProviderInAuthenticator: false, | |
| fallbackMode: 'local gh CLI', | |
| chromeProfileEmail: PROFILE_EMAIL, | |
| chromeProfileDirectory: PROFILE_DIRECTORY, | |
| hasSupabaseEnv: Boolean(process.env.SIN_SUPABASE_URL && process.env.SIN_SUPABASE_SERVICE_ROLE_KEY), | |
| hasDatasetEnv: Boolean(process.env.HF_DATASET_REPO && process.env.HF_TOKEN), | |
| hasGhCli: await commandExists('gh'), | |
| hasGitCli: await commandExists('git'), | |
| }; | |
| } | |
| async function commandExists(command: string) { | |
| try { | |
| await access(`/usr/bin/${command}`, constants.X_OK); | |
| return true; | |
| } catch { | |
| return command === 'gh' || command === 'git'; | |
| } | |
| } | |
| async function finalizeFix(action: Extract<GitHubIssuesAction, { action: 'sin.github.issues.workflow.finalize_fix' }>) { | |
| const body = [ | |
| '## Root Cause', | |
| action.rootCause, | |
| '', | |
| '## Fix', | |
| action.fix, | |
| '', | |
| '## Verification', | |
| action.verification, | |
| '', | |
| '## Affected Files', | |
| ...(action.affectedFiles?.length ? action.affectedFiles.map((file) => `- ${file}`) : ['- not specified']), | |
| ].join('\n'); | |
| await commentIssue(action.repo, action.issueNumber, body); | |
| if (action.closeIssue) { | |
| await closeIssue(action.repo, action.issueNumber, 'Verified and handed off as fixed.'); | |
| } | |
| return withAudit('workflow.finalize_fix', Promise.resolve({ ok: true, repo: action.repo, issueNumber: action.issueNumber })); | |
| } | |
| async function withAudit(kind: string, work: Promise<unknown>) { | |
| const result = await work; | |
| const audit = await recordAuditEvent({ kind, result }); | |
| return { ok: true, result, audit }; | |
| } | |