Spaces:
Sleeping
Sleeping
| import { createHmac } from 'node:crypto'; | |
| import { readFile } from 'node:fs/promises'; | |
| import path from 'node:path'; | |
| const cwd = process.cwd(); | |
| function parseArgs(argv) { | |
| const out = {}; | |
| for (let i = 0; i < argv.length; i += 1) { | |
| const token = argv[i]; | |
| if (!token.startsWith('--')) continue; | |
| const key = token.slice(2); | |
| const next = argv[i + 1]; | |
| if (!next || next.startsWith('--')) { | |
| out[key] = 'true'; | |
| continue; | |
| } | |
| out[key] = next; | |
| i += 1; | |
| } | |
| return out; | |
| } | |
| function firstString(...values) { | |
| for (const value of values) { | |
| if (typeof value === 'string' && value.trim()) return value.trim(); | |
| } | |
| return null; | |
| } | |
| function readEnv(name) { | |
| if (!name) return null; | |
| const value = process.env[name]; | |
| return typeof value === 'string' && value.trim() ? value.trim() : null; | |
| } | |
| function normalizeRoute(entry, defaultWebhookPath) { | |
| return { | |
| agentSlug: firstString(entry.agentSlug, entry.preferredAgentSlug, entry.appSlug), | |
| appSlug: firstString(entry.appSlug), | |
| botName: firstString(entry.botName), | |
| appId: Number(firstString(String(entry.appId || '').trim(), readEnv(entry.appIdEnv))), | |
| webhookPath: firstString(entry.routePath, entry.webhookPath, defaultWebhookPath || '/github/webhook'), | |
| webhookSecret: firstString(entry.webhookSecret, readEnv(entry.webhookSecretEnv)), | |
| }; | |
| } | |
| async function main() { | |
| const args = parseArgs(process.argv.slice(2)); | |
| const configPath = path.resolve(cwd, args.config || process.env.SIN_GITHUB_APP_ROUTING_PATH || './config/github-app-routing.local.json'); | |
| const baseUrl = firstString(args['base-url'], process.env.SIN_GITHUB_ISSUES_PUBLIC_BASE_URL, 'http://127.0.0.1:46031'); | |
| const agent = firstString(args.agent, args.slug, args['app-slug']); | |
| if (!agent) throw new Error('missing_required_arg: --agent <slug-or-app-slug>'); | |
| const raw = await readFile(configPath, 'utf8'); | |
| const config = JSON.parse(raw); | |
| const defaultWebhookPath = firstString(config.defaultWebhookPath, '/github/webhook'); | |
| const apps = Array.isArray(config.apps) ? config.apps.map((entry) => normalizeRoute(entry, defaultWebhookPath)) : []; | |
| const target = apps.find((entry) => entry.agentSlug === agent || entry.appSlug === agent); | |
| if (!target) throw new Error(`unknown_agent_route:${agent}`); | |
| if (!target.webhookSecret) throw new Error(`missing_webhook_secret:${agent}`); | |
| if (!Number.isFinite(target.appId)) throw new Error(`missing_app_id:${agent}`); | |
| const payload = JSON.stringify({ | |
| action: args.action || 'opened', | |
| installation: { id: Number(args['installation-id'] || 987654321), app_id: target.appId }, | |
| repository: { full_name: args.repo || 'OpenSIN-AI/OpenSIN-backend' }, | |
| }); | |
| const signature256 = `sha256=${createHmac('sha256', target.webhookSecret).update(payload).digest('hex')}`; | |
| const response = await fetch(`${baseUrl.replace(/\/+$/, '')}${target.webhookPath}`, { | |
| method: 'POST', | |
| headers: { | |
| 'content-type': 'application/json', | |
| 'x-hub-signature-256': signature256, | |
| 'x-github-event': args.event || 'pull_request', | |
| 'x-github-delivery': args.delivery || `smoke-${target.appSlug || target.agentSlug}`, | |
| }, | |
| body: payload, | |
| }); | |
| const text = await response.text(); | |
| console.log(JSON.stringify({ | |
| ok: response.ok, | |
| baseUrl, | |
| routePath: target.webhookPath, | |
| agentSlug: target.agentSlug, | |
| appSlug: target.appSlug, | |
| appId: target.appId, | |
| status: response.status, | |
| response: text ? JSON.parse(text) : null, | |
| }, null, 2)); | |
| if (!response.ok) process.exitCode = 1; | |
| } | |
| main().catch((error) => { | |
| console.error(error instanceof Error ? error.message : String(error)); | |
| process.exit(1); | |
| }); | |