delqhi/sin-github-issues
0
1#!/usr/bin/env node2 3import { createHmac } from 'node:crypto';4import { readFile } from 'node:fs/promises';5import path from 'node:path';6 7const cwd = process.cwd();8 9function parseArgs(argv) {10 const out = {};11 for (let i = 0; i < argv.length; i += 1) {12 const token = argv[i];13 if (!token.startsWith('--')) continue;14 const key = token.slice(2);15 const next = argv[i + 1];16 if (!next || next.startsWith('--')) {17 out[key] = 'true';18 continue;19 }20 out[key] = next;21 i += 1;22 }23 return out;24}25 26function firstString(...values) {27 for (const value of values) {28 if (typeof value === 'string' && value.trim()) return value.trim();29 }30 return null;31}32 33function readEnv(name) {34 if (!name) return null;35 const value = process.env[name];36 return typeof value === 'string' && value.trim() ? value.trim() : null;37}38 39function normalizeRoute(entry, defaultWebhookPath) {40 return {41 agentSlug: firstString(entry.agentSlug, entry.preferredAgentSlug, entry.appSlug),42 appSlug: firstString(entry.appSlug),43 botName: firstString(entry.botName),44 appId: Number(firstString(String(entry.appId || '').trim(), readEnv(entry.appIdEnv))),45 webhookPath: firstString(entry.routePath, entry.webhookPath, defaultWebhookPath || '/github/webhook'),46 webhookSecret: firstString(entry.webhookSecret, readEnv(entry.webhookSecretEnv)),47 };48}49 50async function main() {51 const args = parseArgs(process.argv.slice(2));52 const configPath = path.resolve(cwd, args.config || process.env.SIN_GITHUB_APP_ROUTING_PATH || './config/github-app-routing.local.json');53 const baseUrl = firstString(args['base-url'], process.env.SIN_GITHUB_ISSUES_PUBLIC_BASE_URL, 'http://127.0.0.1:46031');54 const agent = firstString(args.agent, args.slug, args['app-slug']);55 if (!agent) throw new Error('missing_required_arg: --agent <slug-or-app-slug>');56 57 const raw = await readFile(configPath, 'utf8');58 const config = JSON.parse(raw);59 const defaultWebhookPath = firstString(config.defaultWebhookPath, '/github/webhook');60 const apps = Array.isArray(config.apps) ? config.apps.map((entry) => normalizeRoute(entry, defaultWebhookPath)) : [];61 const target = apps.find((entry) => entry.agentSlug === agent || entry.appSlug === agent);62 if (!target) throw new Error(`unknown_agent_route:${agent}`);63 if (!target.webhookSecret) throw new Error(`missing_webhook_secret:${agent}`);64 if (!Number.isFinite(target.appId)) throw new Error(`missing_app_id:${agent}`);65 66 const payload = JSON.stringify({67 action: args.action || 'opened',68 installation: { id: Number(args['installation-id'] || 987654321), app_id: target.appId },69 repository: { full_name: args.repo || 'OpenSIN-AI/OpenSIN-backend' },70 });71 const signature256 = `sha256=${createHmac('sha256', target.webhookSecret).update(payload).digest('hex')}`;72 const response = await fetch(`${baseUrl.replace(/\/+$/, '')}${target.webhookPath}`, {73 method: 'POST',74 headers: {75 'content-type': 'application/json',76 'x-hub-signature-256': signature256,77 'x-github-event': args.event || 'pull_request',78 'x-github-delivery': args.delivery || `smoke-${target.appSlug || target.agentSlug}`,79 },80 body: payload,81 });82 const text = await response.text();83 console.log(JSON.stringify({84 ok: response.ok,85 baseUrl,86 routePath: target.webhookPath,87 agentSlug: target.agentSlug,88 appSlug: target.appSlug,89 appId: target.appId,90 status: response.status,91 response: text ? JSON.parse(text) : null,92 }, null, 2));93 if (!response.ok) process.exitCode = 1;94}95 96main().catch((error) => {97 console.error(error instanceof Error ? error.message : String(error));98 process.exit(1);99});100 