pi-ghwebhook 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +141 -2
  4. package/README_zh.md +142 -0
  5. package/dist/bus/event-bus.d.ts +14 -0
  6. package/dist/bus/event-bus.d.ts.map +1 -0
  7. package/dist/bus/event-bus.js +46 -0
  8. package/dist/bus/event-bus.js.map +1 -0
  9. package/dist/bus/guards.d.ts +10 -0
  10. package/dist/bus/guards.d.ts.map +1 -0
  11. package/dist/bus/guards.js +88 -0
  12. package/dist/bus/guards.js.map +1 -0
  13. package/dist/bus/matcher.d.ts +7 -0
  14. package/dist/bus/matcher.d.ts.map +1 -0
  15. package/dist/bus/matcher.js +122 -0
  16. package/dist/bus/matcher.js.map +1 -0
  17. package/dist/bus/normalizer.d.ts +14 -0
  18. package/dist/bus/normalizer.d.ts.map +1 -0
  19. package/dist/bus/normalizer.js +25 -0
  20. package/dist/bus/normalizer.js.map +1 -0
  21. package/dist/config/loader.d.ts +6 -0
  22. package/dist/config/loader.d.ts.map +1 -0
  23. package/dist/config/loader.js +63 -0
  24. package/dist/config/loader.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +82 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/template/renderer.d.ts +4 -0
  30. package/dist/template/renderer.d.ts.map +1 -0
  31. package/dist/template/renderer.js +121 -0
  32. package/dist/template/renderer.js.map +1 -0
  33. package/dist/transport/forwarder.d.ts +18 -0
  34. package/dist/transport/forwarder.d.ts.map +1 -0
  35. package/dist/transport/forwarder.js +73 -0
  36. package/dist/transport/forwarder.js.map +1 -0
  37. package/dist/transport/loopback.d.ts +20 -0
  38. package/dist/transport/loopback.d.ts.map +1 -0
  39. package/dist/transport/loopback.js +96 -0
  40. package/dist/transport/loopback.js.map +1 -0
  41. package/dist/types/enums.d.ts +41 -0
  42. package/dist/types/enums.d.ts.map +1 -0
  43. package/dist/types/enums.js +44 -0
  44. package/dist/types/enums.js.map +1 -0
  45. package/dist/types/payload.d.ts +28 -0
  46. package/dist/types/payload.d.ts.map +1 -0
  47. package/dist/types/payload.js +2 -0
  48. package/dist/types/payload.js.map +1 -0
  49. package/dist/types/schema.d.ts +33 -0
  50. package/dist/types/schema.d.ts.map +1 -0
  51. package/dist/types/schema.js +35 -0
  52. package/dist/types/schema.js.map +1 -0
  53. package/package.json +69 -4
  54. package/schemas/ghwebhook.schema.json +225 -0
  55. package/skills/setup-ghwebhook/SKILL.md +149 -0
  56. package/skills/setup-ghwebhook/assets/default-config.json +31 -0
  57. package/src/bus/event-bus.ts +64 -0
  58. package/src/bus/matcher.ts +164 -0
  59. package/src/config/loader.ts +80 -0
  60. package/src/index.ts +107 -0
  61. package/src/template/renderer.ts +145 -0
  62. package/src/transport/forwarder.ts +92 -0
  63. package/src/transport/loopback.ts +130 -0
  64. package/src/types/enums.ts +42 -0
  65. package/src/types/payload.ts +75 -0
  66. package/src/types/schema.ts +73 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-11-09
9
+
10
+ ### Added
11
+
12
+ - **Built-in `setup-ghwebhook` Skill**: Packaged an interactive setup skill that auto-detects `gh` CLI credentials, auto-heals missing `cli/gh-webhook` extensions, senses repo workflow and review bot configs, and scaffolds tailored `.pi/ghwebhook.json` rules.
13
+
14
+ ## [0.1.0] - 2026-11-09
15
+
16
+ ### Added
17
+
18
+ - **Universal Upstream-Aligned Event Bus**: Direct 1-to-1 match with GitHub Webhook wire protocol (`<event>` / `<event>.<action>`), natively supporting all 100+ GitHub events (`workflow_run.completed`, `pull_request.opened`, `push`, `issues.opened`, etc.).
19
+ - **Composable Declarative Filters**: Replaced rigid synthetic code topics with flexible filters (`branch`, `conclusion`, `workflow`, `state`, `senders`, `isBot`, `match`), empowering users to configure any combination of conditions directly in `.pi/ghwebhook.json`.
20
+ - **Official Schema Alignment**: Full type alignment with official `@octokit/openapi-webhooks-types`.
21
+ - **In-Process Loopback Server**: Ephemeral port listener on `127.0.0.1:0` with optional HMAC SHA-256 webhook signature validation.
22
+ - **Full-Event Reverse Tunnel Forwarding**: Managed `gh webhook forward --events=*` subprocess with automatic teardown on `session_shutdown` and `process.exit`.
23
+ - **Template Renderer**: Safe, single-pass $O(N)$ string interpolation engine for prompt synthesis.
24
+ - **Workspace Probing**: Zero-footprint detection of `.pi/ghwebhook.json` and `.agents/ghwebhook.json`.
25
+ - **Tooling & Quality Gates**: Native `mise` and `hk` orchestration with `oxlint`, `tsc`, `prettier`, `lizard` complexity audit, and `jscpd` code duplication gate.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xifan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,142 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # pi-ghwebhook
4
+
5
+ **Universal GitHub Webhook event bus and wake-up driver for the Pi coding agent**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/pi-ghwebhook?logo=npm&logoColor=white)](https://www.npmjs.com/package/pi-ghwebhook)
8
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22.19.0-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.8-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
10
+ [![Pi Package](https://img.shields.io/badge/pi--package-extension-black)](https://github.com/earendil-works/pi)
11
+ [![GitHub License](https://img.shields.io/github/license/xifan2333/pi-ghwebhook?logo=github&logoColor=white)](LICENSE)
12
+
13
+ English | [中文](README_zh.md)
14
+
15
+ </div>
16
+
17
+ ## Overview
18
+
19
+ pi-ghwebhook eliminates manual polling and terminal switching after pushing code or creating pull requests. By hosting an in-process loopback server connected to GitHub CLI's `gh webhook forward` reverse tunnel, it receives real-time GitHub Webhook events, evaluates GitHub Actions Workflow-style trigger rules, and wakes up the Pi Agent via `pi.sendUserMessage()` to close the loop.
20
+
21
+ ## Key Architecture
22
+
23
+ - 100% GitHub Actions Workflow Specification: Uses official `on` and `jobs` syntax. Zero invented intermediate concepts.
24
+ - JSON Schema Autocompletion: Includes an official schema (`$schema`) for instant editor IntelliSense and validation.
25
+ - Zero Public Exposure: Uses `gh webhook forward --events=*` local reverse tunnels. No public server, ngrok, or exposed port required.
26
+ - Dynamic Port Allocation: Binds to `127.0.0.1:0`. The OS kernel dynamically assigns an unused port, eliminating port contention across concurrent sessions.
27
+ - Table-Driven Dispatch: O(1) event routing with strict complexity constraints (CCN <= 15, CogC <= 15).
28
+ - Workspace Detection: Probes `.pi/ghwebhook.json` on `session_start`. If absent, sleeps silently with zero resource consumption.
29
+ - Clean Teardown: Hooks into `session_shutdown` and process exit to terminate child processes and servers idempotently.
30
+
31
+ ## Installation
32
+
33
+ Install into your Pi coding agent:
34
+
35
+ ```bash
36
+ pi install npm:pi-ghwebhook
37
+ ```
38
+
39
+ Or install from GitHub:
40
+
41
+ ```bash
42
+ pi install git:github.com/xifan2333/pi-ghwebhook
43
+ ```
44
+
45
+ To test locally in an active session:
46
+
47
+ ```bash
48
+ pi -e /path/to/pi-ghwebhook
49
+ ```
50
+
51
+ ## Configuration (`.pi/ghwebhook.json`)
52
+
53
+ Place `.pi/ghwebhook.json` in the root of any repository. Point `$schema` to the hosted JSON Schema for auto-completion in VS Code, Cursor, and other editors:
54
+
55
+ ```json
56
+ {
57
+ "$schema": "https://raw.githubusercontent.com/xifan2333/pi-ghwebhook/main/schemas/ghwebhook.schema.json",
58
+ "name": "CI & Review Automation",
59
+ "on": {
60
+ "workflow_run": {
61
+ "workflows": ["CI"],
62
+ "types": ["completed"],
63
+ "branches": ["@current"]
64
+ },
65
+ "pull_request_review": {
66
+ "types": ["submitted"],
67
+ "branches": ["@current"]
68
+ },
69
+ "issues": {
70
+ "types": ["opened"]
71
+ },
72
+ "push": {
73
+ "branches": ["main"]
74
+ }
75
+ },
76
+ "jobs": {
77
+ "ci-failure": {
78
+ "if": "github.event.workflow_run.conclusion == 'failure'",
79
+ "prompt": "CI run failed on branch {{ branch }}.\n\nRun URL: {{ run_url }}\nCommit: {{ head_sha }}\n\nPlease run `gh run view {{ run_id }} --log-failed` to diagnose the error and prepare a fix.",
80
+ "deliverAs": "followUp"
81
+ },
82
+ "ci-success": {
83
+ "if": "github.event.workflow_run.conclusion == 'success'",
84
+ "prompt": "Remote CI has passed on branch {{ branch }}.\n\nRun URL: {{ run_url }}\n\nPlease check PR checks status and verify if it is ready to merge.",
85
+ "deliverAs": "followUp"
86
+ },
87
+ "review-bot": {
88
+ "if": "github.event.review.user.type == 'Bot'",
89
+ "prompt": "Received review comments from {{ sender }}:\n\nState: {{ review_state }}\nDetails:\n{{ review_body }}\n\nPlease evaluate these comments critically: triage true issues to fix, and refute false positives.",
90
+ "deliverAs": "steer"
91
+ },
92
+ "new-issue": {
93
+ "prompt": "New issue opened #{{ issue_number }}: {{ issue_title }}\n\n{{ issue_body }}"
94
+ },
95
+ "push-main": {
96
+ "prompt": "New commit pushed to main: {{ head_sha }} - {{ commit_message }}"
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Configuration Fields
103
+
104
+ - `name`: Optional workflow name.
105
+ - `repo`: GitHub repository (`owner/repo`). Defaults to `"auto"` (resolved from `git remote get-url origin`).
106
+ - `secret`: Optional webhook HMAC SHA-256 verification secret.
107
+ - `on`: GitHub Actions event triggers specification:
108
+ - `<event_name>`: Event name (e.g. `workflow_run`, `pull_request`, `push`, `issues`).
109
+ - `types`: Activity types filtering `payload.action` (e.g. `["completed"]`, `["opened"]`).
110
+ - `branches`: Branches filter supporting `@current` and globs (e.g. `["@current", "main", "feat/*"]`).
111
+ - `workflows`: Workflow names filter (e.g. `["CI"]`).
112
+ - `jobs`: Map of jobs to execute:
113
+ - `if`: Conditional expression (e.g. `github.event.workflow_run.conclusion == 'failure'`).
114
+ - `prompt`: Prompt template sent to Pi Agent.
115
+ - `deliverAs`: Pi message delivery mode: `"followUp"` (default) or `"steer"`.
116
+
117
+ ### Template Variables
118
+
119
+ - `{{ branch }}`: Current head branch.
120
+ - `{{ head_sha }}`: Commit SHA.
121
+ - `{{ commit_message }}`: Head commit message.
122
+ - `{{ repo }}` / `{{ repo_url }}`: Repository name and URL.
123
+ - `{{ run_id }}` / `{{ run_url }}` / `{{ workflow }}` / `{{ conclusion }}`: Workflow run metadata.
124
+ - `{{ pr_number }}` / `{{ pr_title }}` / `{{ pr_url }}`: Pull request details.
125
+ - `{{ issue_number }}` / `{{ issue_title }}` / `{{ issue_url }}` / `{{ issue_body }}`: Issue details.
126
+ - `{{ sender }}` / `{{ sender_type }}`: Triggering actor.
127
+ - `{{ review_state }}` / `{{ review_body }}` / `{{ review_url }}`: Review details.
128
+ - `{{ comment_body }}` / `{{ comment_url }}`: Comment details.
129
+
130
+ ## Development & Quality Gates
131
+
132
+ ```bash
133
+ mise run check # Run all quality gates (tsc, oxlint, prettier, lizard, jscpd, typos)
134
+ mise run check:plan # Preview execution plan
135
+ mise run check:changed # Check only staged/modified files (< 50ms)
136
+ mise run fix # Auto-format and fix lint/package issues
137
+ mise run test # Run test suite
138
+ ```
139
+
140
+ ## License
141
+
142
+ [MIT](LICENSE) © 2026 xifan
package/README_zh.md ADDED
@@ -0,0 +1,142 @@
1
+ <div align="center">
2
+
3
+ # pi-ghwebhook
4
+
5
+ **适用于 Pi coding agent 的通用 GitHub Webhook 事件总线与自动唤醒驱动**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/pi-ghwebhook?logo=npm&logoColor=white)](https://www.npmjs.com/package/pi-ghwebhook)
8
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22.19.0-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.8-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
10
+ [![Pi Package](https://img.shields.io/badge/pi--package-extension-black)](https://github.com/earendil-works/pi)
11
+ [![GitHub License](https://img.shields.io/github/license/xifan2333/pi-ghwebhook?logo=github&logoColor=white)](LICENSE)
12
+
13
+ [English](README.md) | 简体中文
14
+
15
+ </div>
16
+
17
+ ## 概述
18
+
19
+ pi-ghwebhook 旨在彻底消除提交代码或创建 Pull Request 后的人工轮询与终端反复切换。通过在进程内运行回环 HTTP 服务并托管 GitHub CLI 的 `gh webhook forward` 反向长连接隧道,它可以实时接收 GitHub Webhook 事件,依据 GitHub Actions Workflow 官方规范解析规则,并通过 `pi.sendUserMessage()` 主动唤醒 Pi Agent 进入闭环处置。
20
+
21
+ ## 核心架构特性
22
+
23
+ - 100% 对齐 GitHub Actions Workflow 官方规范:完全采用官方标准的 `on` 触发器与 `jobs` 任务语法,零中间层生造概念。
24
+ - JSON Schema 智能提示:提供官方 Schema(`$schema`),在 VS Code、Cursor 等现代编辑器中编写即享全字段类型补全与静态校验。
25
+ - 零公网暴露:依托 `gh webhook forward --events=*` 反向隧道,无需公网服务器、固定 IP 或内网穿透工具。
26
+ - 动态端口分配:监听 `127.0.0.1:0`,由操作系统内核动态分配空闲端口,杜绝多终端或多会话并发运行时的端口冲突。
27
+ - 表驱动分发:基于哈希表实现 O(1) 路由分发,执行严格的复杂度门禁(CCN <= 15, CogC <= 15)。
28
+ - 工作区自感知:在 `session_start` 生命周期探测 `.pi/ghwebhook.json`,无配置时静默休眠,零资源占用。
29
+ - 幂等清理保障:深度绑定 `session_shutdown` 与进程退出钩子,确保子进程与本地服务干净释放,不留孤儿进程。
30
+
31
+ ## 安装
32
+
33
+ 安装到 Pi coding agent:
34
+
35
+ ```bash
36
+ pi install npm:pi-ghwebhook
37
+ ```
38
+
39
+ 或直接通过 GitHub 仓库安装:
40
+
41
+ ```bash
42
+ pi install git:github.com/xifan2333/pi-ghwebhook
43
+ ```
44
+
45
+ 在当前会话中即时测试:
46
+
47
+ ```bash
48
+ pi -e /path/to/pi-ghwebhook
49
+ ```
50
+
51
+ ## 配置契约 (`.pi/ghwebhook.json`)
52
+
53
+ 在仓库根目录下创建 `.pi/ghwebhook.json`。配置 `$schema` 即可享受现代 IDE 的实时悬浮提示与自动补全:
54
+
55
+ ```json
56
+ {
57
+ "$schema": "https://raw.githubusercontent.com/xifan2333/pi-ghwebhook/main/schemas/ghwebhook.schema.json",
58
+ "name": "CI & Review Automation",
59
+ "on": {
60
+ "workflow_run": {
61
+ "workflows": ["CI"],
62
+ "types": ["completed"],
63
+ "branches": ["@current"]
64
+ },
65
+ "pull_request_review": {
66
+ "types": ["submitted"],
67
+ "branches": ["@current"]
68
+ },
69
+ "issues": {
70
+ "types": ["opened"]
71
+ },
72
+ "push": {
73
+ "branches": ["main"]
74
+ }
75
+ },
76
+ "jobs": {
77
+ "ci-failure": {
78
+ "if": "github.event.workflow_run.conclusion == 'failure'",
79
+ "prompt": "分支 {{ branch }} 的 CI 运行失败。\n\n运行链接: {{ run_url }}\n提交哈希: {{ head_sha }}\n\n请使用 `gh run view {{ run_id }} --log-failed` 查看失败日志并进行修复。",
80
+ "deliverAs": "followUp"
81
+ },
82
+ "ci-success": {
83
+ "if": "github.event.workflow_run.conclusion == 'success'",
84
+ "prompt": "分支 {{ branch }} 的远程 CI 已全部通过。\n\n运行链接: {{ run_url }}\n\n请检查 PR 状态并确认是否准备合入。",
85
+ "deliverAs": "followUp"
86
+ },
87
+ "review-bot": {
88
+ "if": "github.event.review.user.type == 'Bot'",
89
+ "prompt": "收到来自 {{ sender }} 的审查意见:\n\n状态: {{ review_state }}\n详情:\n{{ review_body }}\n\n请批判性评估上述审查意见:修复真实问题,驳回误报。",
90
+ "deliverAs": "steer"
91
+ },
92
+ "new-issue": {
93
+ "prompt": "收到新 Issue #{{ issue_number }}: {{ issue_title }}\n\n{{ issue_body }}"
94
+ },
95
+ "push-main": {
96
+ "prompt": "main 分支收到新代码推送: {{ head_sha }} - {{ commit_message }}"
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### 字段说明
103
+
104
+ - `name`: 可选的工作流名称。
105
+ - `repo`: GitHub 仓库名(`owner/repo`)。默认为 `"auto"`(自动从 `git remote get-url origin` 提取)。
106
+ - `secret`: 可选的 Webhook HMAC SHA-256 鉴权密钥。
107
+ - `on`: GitHub Actions 事件触发器配置:
108
+ - `<event_name>`: 官方事件名称(如 `workflow_run`, `pull_request`, `push`, `issues`)。
109
+ - `types`: 官方活动类型过滤 `payload.action`(如 `["completed"]`, `["opened"]`)。
110
+ - `branches`: 分支过滤,支持 `@current` 动态分支与 glob(如 `["@current", "main", "feat/*"]`)。
111
+ - `workflows`: 工作流过滤(如 `["CI"]`)。
112
+ - `jobs`: 触发时执行的任务字典:
113
+ - `if`: 表达式条件(如 `github.event.workflow_run.conclusion == 'failure'`)。
114
+ - `prompt`: 传给 Pi Agent 的提示词内容模板。
115
+ - `deliverAs`: Pi 消息投递模式:`"followUp"`(默认排队执行)或 `"steer"`(实时插话打断)。
116
+
117
+ ### 模板插值变量
118
+
119
+ - `{{ branch }}`: 分支名称
120
+ - `{{ head_sha }}`: 触发提交哈希
121
+ - `{{ commit_message }}`: 提交日志
122
+ - `{{ repo }}` / `{{ repo_url }}`: 仓库名及网页链接
123
+ - `{{ run_id }}` / `{{ run_url }}` / `{{ workflow }}` / `{{ conclusion }}`: 工作流运行元数据
124
+ - `{{ pr_number }}` / `{{ pr_title }}` / `{{ pr_url }}`: PR 详情
125
+ - `{{ issue_number }}` / `{{ issue_title }}` / `{{ issue_url }}` / `{{ issue_body }}`: Issue 详情
126
+ - `{{ sender }}` / `{{ sender_type }}`: 触发人或机器人
127
+ - `{{ review_state }}` / `{{ review_body }}` / `{{ review_url }}`: 评审详情
128
+ - `{{ comment_body }}` / `{{ comment_url }}`: 评论详情
129
+
130
+ ## 开发与质量门禁
131
+
132
+ ```bash
133
+ mise run check # 执行全量质量检查(tsc, oxlint, prettier, lizard, jscpd, typos)
134
+ mise run check:plan # 预览执行计划
135
+ mise run check:changed # 增量检查暂存区及修改文件 (< 50ms)
136
+ mise run fix # 自动格式化并修复问题
137
+ mise run test # 运行测试套件
138
+ ```
139
+
140
+ ## 许可证
141
+
142
+ [MIT](LICENSE) © 2026 xifan
@@ -0,0 +1,14 @@
1
+ import type { GhWebhookWorkflow } from "../types/schema.js";
2
+ import type { WebhookPayload } from "../types/payload.js";
3
+ export interface ActionPlan {
4
+ prompt: string;
5
+ deliverAs: "steer" | "followUp";
6
+ jobId: string;
7
+ }
8
+ export declare class WebhookEventBus {
9
+ private readonly workflow;
10
+ constructor(workflow: GhWebhookWorkflow);
11
+ private getTriggerForEvent;
12
+ dispatch(incomingEvent: string, payload: WebhookPayload, currentBranch: string): ActionPlan[];
13
+ }
14
+ //# sourceMappingURL=event-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../../src/bus/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAsB,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,GAAG,UAAU,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,iBAAiB;IAExD,OAAO,CAAC,kBAAkB;IAkB1B,QAAQ,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,MAAM,GACpB,UAAU,EAAE;CA2BhB"}
@@ -0,0 +1,46 @@
1
+ import { isTriggerMatched, evaluateCondition } from "./matcher.js";
2
+ import { extractVariables, renderTemplate } from "../template/renderer.js";
3
+ export class WebhookEventBus {
4
+ workflow;
5
+ constructor(workflow) {
6
+ this.workflow = workflow;
7
+ }
8
+ getTriggerForEvent(incomingEvent) {
9
+ const on = this.workflow.on;
10
+ if (typeof on === "string") {
11
+ return on === incomingEvent ? null : undefined;
12
+ }
13
+ if (Array.isArray(on)) {
14
+ return on.includes(incomingEvent) ? null : undefined;
15
+ }
16
+ if (typeof on === "object" && on !== null) {
17
+ if (Object.prototype.hasOwnProperty.call(on, incomingEvent)) {
18
+ return on[incomingEvent];
19
+ }
20
+ }
21
+ return undefined;
22
+ }
23
+ dispatch(incomingEvent, payload, currentBranch) {
24
+ const trigger = this.getTriggerForEvent(incomingEvent);
25
+ if (trigger === undefined) {
26
+ return [];
27
+ }
28
+ if (!isTriggerMatched(trigger, payload, currentBranch)) {
29
+ return [];
30
+ }
31
+ const vars = extractVariables(payload, currentBranch);
32
+ const plans = [];
33
+ for (const [jobId, job] of Object.entries(this.workflow.jobs)) {
34
+ if (!evaluateCondition(job.if, payload, currentBranch)) {
35
+ continue;
36
+ }
37
+ plans.push({
38
+ prompt: renderTemplate(job.prompt, vars),
39
+ deliverAs: job.deliverAs ?? "followUp",
40
+ jobId,
41
+ });
42
+ }
43
+ return plans;
44
+ }
45
+ }
46
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../../src/bus/event-bus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAQ3E,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,QAA2B;QAA3B,aAAQ,GAAR,QAAQ,CAAmB;IAAG,CAAC;IAEpD,kBAAkB,CACxB,aAAqB;QAErB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,EAAE,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACjD,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,CAAC;QACD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC;gBAC5D,OAAO,EAAE,CAAC,aAAa,CAA8B,CAAC;YACxD,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,QAAQ,CACN,aAAqB,EACrB,OAAuB,EACvB,aAAqB;QAErB,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;QACvD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QACtD,MAAM,KAAK,GAAiB,EAAE,CAAC;QAE/B,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC;gBACvD,SAAS;YACX,CAAC;YAED,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC;gBACxC,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,UAAU;gBACtC,KAAK;aACN,CAAC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { WebhookPayload } from "../types/payload.js";
2
+ import type { FilterConfig } from "../types/schema.js";
3
+ export interface FilterContext {
4
+ currentBranch: string;
5
+ payload: WebhookPayload;
6
+ }
7
+ export type GuardPredicate = (expectedValue: unknown, ctx: FilterContext) => boolean;
8
+ export declare const GUARDS: ReadonlyMap<keyof FilterConfig, GuardPredicate>;
9
+ export declare function evaluateGuards(filters: FilterConfig | undefined, ctx: FilterContext): boolean;
10
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/bus/guards.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,aAAa,EAAE,OAAO,EACtB,GAAG,EAAE,aAAa,KACf,OAAO,CAAC;AAiFb,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,YAAY,EAAE,cAAc,CAQjE,CAAC;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,GAAG,EAAE,aAAa,GACjB,OAAO,CAYT"}
@@ -0,0 +1,88 @@
1
+ function resolvePayloadBranch(payload) {
2
+ return (payload.workflow_run?.head_branch ??
3
+ payload.pull_request?.head?.ref ??
4
+ payload.check_suite?.head_branch ??
5
+ payload.workflow_job?.head_branch ??
6
+ (typeof payload.ref === "string"
7
+ ? payload.ref.replace("refs/heads/", "")
8
+ : undefined));
9
+ }
10
+ function resolvePayloadSender(payload) {
11
+ return (payload.sender?.login ??
12
+ payload.review?.user?.login ??
13
+ payload.comment?.user?.login);
14
+ }
15
+ function getNestedValue(obj, path) {
16
+ const parts = path.split(".");
17
+ let current = obj;
18
+ for (const part of parts) {
19
+ if (current == null || typeof current !== "object")
20
+ return undefined;
21
+ current = current[part];
22
+ }
23
+ return current;
24
+ }
25
+ const branchGuard = (expected, { currentBranch, payload }) => {
26
+ const target = expected === "@current" ? currentBranch : expected;
27
+ const actualBranch = resolvePayloadBranch(payload);
28
+ return typeof target === "string" && actualBranch === target;
29
+ };
30
+ const conclusionGuard = (expected, { payload }) => {
31
+ const actual = payload.workflow_run?.conclusion ??
32
+ payload.check_suite?.conclusion ??
33
+ payload.workflow_job?.conclusion;
34
+ return typeof expected === "string" && actual === expected;
35
+ };
36
+ const workflowGuard = (expected, { payload }) => {
37
+ const workflowName = payload.workflow_run?.name ?? payload.workflow_job?.workflow_name;
38
+ return typeof expected === "string" && workflowName === expected;
39
+ };
40
+ const stateGuard = (expected, { payload }) => {
41
+ const actual = payload.review?.state ?? payload.pull_request?.state;
42
+ return typeof expected === "string" && actual === expected;
43
+ };
44
+ const sendersGuard = (allowedList, { payload }) => {
45
+ if (!Array.isArray(allowedList))
46
+ return true;
47
+ const sender = resolvePayloadSender(payload);
48
+ return typeof sender === "string" && allowedList.includes(sender);
49
+ };
50
+ const isBotGuard = (expected, { payload }) => {
51
+ const senderType = payload.review?.user?.type ?? payload.sender?.type;
52
+ const login = payload.review?.user?.login ?? payload.sender?.login ?? "";
53
+ const isBot = senderType === "Bot" || login.endsWith("[bot]");
54
+ return isBot === Boolean(expected);
55
+ };
56
+ const matchGuard = (matchObj, { payload }) => {
57
+ if (!matchObj || typeof matchObj !== "object")
58
+ return true;
59
+ for (const [path, expected] of Object.entries(matchObj)) {
60
+ const actual = getNestedValue(payload, path);
61
+ if (actual !== expected)
62
+ return false;
63
+ }
64
+ return true;
65
+ };
66
+ export const GUARDS = new Map([
67
+ ["branch", branchGuard],
68
+ ["conclusion", conclusionGuard],
69
+ ["workflow", workflowGuard],
70
+ ["state", stateGuard],
71
+ ["senders", sendersGuard],
72
+ ["isBot", isBotGuard],
73
+ ["match", matchGuard],
74
+ ]);
75
+ export function evaluateGuards(filters, ctx) {
76
+ if (!filters)
77
+ return true;
78
+ for (const [key, value] of Object.entries(filters)) {
79
+ if (value === undefined)
80
+ continue;
81
+ const guard = GUARDS.get(key);
82
+ if (guard && !guard(value, ctx)) {
83
+ return false;
84
+ }
85
+ }
86
+ return true;
87
+ }
88
+ //# sourceMappingURL=guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/bus/guards.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { WebhookPayload } from "../types/payload.js";
2
+ import type { EventTriggerConfig } from "../types/schema.js";
3
+ export declare function globMatch(pattern: string, text: string): boolean;
4
+ export declare function resolvePayloadBranch(payload: WebhookPayload): string | undefined;
5
+ export declare function isTriggerMatched(trigger: EventTriggerConfig | null | undefined, payload: WebhookPayload, currentBranch: string): boolean;
6
+ export declare function evaluateCondition(expr: string | undefined, payload: WebhookPayload, currentBranch: string): boolean;
7
+ //# sourceMappingURL=matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/bus/matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQhE;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,GACtB,MAAM,GAAG,SAAS,CAUpB;AA8CD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,EAC9C,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,MAAM,GACpB,OAAO,CAOT;AAmDD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,MAAM,GACpB,OAAO,CA0BT"}
@@ -0,0 +1,122 @@
1
+ export function globMatch(pattern, text) {
2
+ if (pattern === text || pattern === "*")
3
+ return true;
4
+ const escaped = pattern
5
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
6
+ .replace(/\*\*/g, ".*")
7
+ .replace(/\*/g, ".*");
8
+ const regex = new RegExp(`^${escaped}$`);
9
+ return regex.test(text);
10
+ }
11
+ export function resolvePayloadBranch(payload) {
12
+ return (payload.workflow_run?.head_branch ??
13
+ payload.pull_request?.head?.ref ??
14
+ payload.check_suite?.head_branch ??
15
+ payload.workflow_job?.head_branch ??
16
+ (typeof payload.ref === "string"
17
+ ? payload.ref.replace("refs/heads/", "")
18
+ : undefined));
19
+ }
20
+ function matchBranches(branches, actualBranch, currentBranch) {
21
+ for (const pattern of branches) {
22
+ if (pattern === "@current" && actualBranch === currentBranch) {
23
+ return true;
24
+ }
25
+ if (globMatch(pattern, actualBranch)) {
26
+ return true;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ function matchTypes(types, action) {
32
+ if (!Array.isArray(types) || types.length === 0)
33
+ return true;
34
+ const actionStr = typeof action === "string" ? action : "";
35
+ return types.includes(actionStr);
36
+ }
37
+ function matchBranchFilter(branches, payload, currentBranch) {
38
+ if (!Array.isArray(branches) || branches.length === 0)
39
+ return true;
40
+ const actualBranch = resolvePayloadBranch(payload);
41
+ return Boolean(actualBranch && matchBranches(branches, actualBranch, currentBranch));
42
+ }
43
+ function matchWorkflows(workflows, payload) {
44
+ if (!Array.isArray(workflows) || workflows.length === 0)
45
+ return true;
46
+ const workflowName = payload.workflow_run?.name ?? payload.workflow_job?.workflow_name ?? "";
47
+ return workflows.some((w) => globMatch(w, workflowName));
48
+ }
49
+ export function isTriggerMatched(trigger, payload, currentBranch) {
50
+ if (!trigger)
51
+ return true;
52
+ return (matchTypes(trigger.types, payload.action) &&
53
+ matchBranchFilter(trigger.branches, payload, currentBranch) &&
54
+ matchWorkflows(trigger.workflows, payload));
55
+ }
56
+ function getNestedValue(obj, path) {
57
+ const parts = path.split(".");
58
+ let current = obj;
59
+ for (const part of parts) {
60
+ if (current == null || typeof current !== "object")
61
+ return undefined;
62
+ current = current[part];
63
+ }
64
+ return current;
65
+ }
66
+ function resolveContextValue(path, payload, currentBranch) {
67
+ const cleanPath = path
68
+ .replace(/^github\.event\./, "")
69
+ .replace(/^event\./, "")
70
+ .trim();
71
+ // Special shortcuts
72
+ if (cleanPath === "branch")
73
+ return resolvePayloadBranch(payload) ?? currentBranch;
74
+ if (cleanPath === "conclusion")
75
+ return payload.workflow_run?.conclusion ?? payload.check_suite?.conclusion;
76
+ if (cleanPath === "sender")
77
+ return payload.sender?.login ?? payload.review?.user?.login;
78
+ if (cleanPath === "isBot") {
79
+ const senderType = payload.review?.user?.type ?? payload.sender?.type;
80
+ const login = payload.review?.user?.login ?? payload.sender?.login ?? "";
81
+ return senderType === "Bot" || login.endsWith("[bot]");
82
+ }
83
+ return getNestedValue(payload, cleanPath);
84
+ }
85
+ function parseLiteral(literal) {
86
+ const trimmed = literal.trim();
87
+ if (trimmed === "true")
88
+ return true;
89
+ if (trimmed === "false")
90
+ return false;
91
+ if (trimmed === "null")
92
+ return null;
93
+ if (/^['"].*['"]$/.test(trimmed)) {
94
+ return trimmed.slice(1, -1);
95
+ }
96
+ const num = Number(trimmed);
97
+ if (!Number.isNaN(num))
98
+ return num;
99
+ return trimmed;
100
+ }
101
+ export function evaluateCondition(expr, payload, currentBranch) {
102
+ if (!expr || !expr.trim())
103
+ return true;
104
+ const match = expr.match(/^(.+?)\s*(===|!==|==|!=)\s*(.+)$/);
105
+ if (!match) {
106
+ const val = resolveContextValue(expr.trim(), payload, currentBranch);
107
+ return Boolean(val);
108
+ }
109
+ const [, leftRaw, op, rightRaw] = match;
110
+ const leftVal = resolveContextValue(leftRaw.trim(), payload, currentBranch);
111
+ const rightVal = parseLiteral(rightRaw.trim());
112
+ const isEqualOp = op === "==" || op === "===";
113
+ if (typeof rightVal === "string" &&
114
+ typeof leftVal === "string" &&
115
+ rightVal.includes("*")) {
116
+ const matchesGlob = globMatch(rightVal, leftVal);
117
+ return isEqualOp ? matchesGlob : !matchesGlob;
118
+ }
119
+ const areEqual = leftVal === rightVal;
120
+ return isEqualOp ? areEqual : !areEqual;
121
+ }
122
+ //# sourceMappingURL=matcher.js.map