@prettier-ai/dsh-webhook-github 0.1.2-alpha.1
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.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +86 -0
- package/README.zh.md +86 -0
- package/lib/index.js +193 -0
- package/lib/invariant.js +20 -0
- package/lib/types/body.d.ts +17 -0
- package/lib/types/body.js +65 -0
- package/lib/types/handler.d.ts +18 -0
- package/lib/types/handler.js +115 -0
- package/lib/types/index.d.ts +23 -0
- package/lib/types/index.js +39 -0
- package/lib/types/invariant.d.ts +13 -0
- package/lib/types/invariant.js +18 -0
- package/lib/types/types.d.ts +20 -0
- package/lib/types/types.js +3 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
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.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/webhook/webhook-github/README.md
|
|
5
|
+
README.md: ce0e2da0aa355166ba940b9a991f6ee5c7fa1f9c
|
|
6
|
+
README.zh.md: c5ad924ec2040281d12c5890e5679413a07a4c81
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Signed GitHub webhook adapter for deployments routing authenticated JSON events into the webhook runtime."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-webhook-github
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-webhook-github` registers one exact HTTP route on the injected `ctx.webServer`. It bounds and verifies GitHub's raw JSON body, projects a provider-neutral delivery, calls `ctx.webhookRuntime.dispatch()`, and returns `202` without waiting for rules or Sessions. Use it when a deployment needs authenticated GitHub ingress for the generic webhook runtime.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Configuration](#configuration)
|
|
17
|
+
- [HTTP contract](#http-contract)
|
|
18
|
+
- [Dedicated listener composition](#dedicated-listener-composition)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="configuration"></a>
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
| Key | Meaning |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `source` | Non-empty adapter instance carried to rules, such as `primary-github`. |
|
|
31
|
+
| `path` | Exact non-root pathname without trailing slash, query, or fragment. |
|
|
32
|
+
| `secretEnv` | Credential reference containing the GitHub webhook secret. |
|
|
33
|
+
| `maxBodyBytes` | Positive safe-integer ceiling for the untouched request body. |
|
|
34
|
+
|
|
35
|
+
All fields are required. The secret reference is resolved for every request, so rotation affects the next delivery without reloading the plugin.
|
|
36
|
+
|
|
37
|
+
<a id="http-contract"></a>
|
|
38
|
+
## HTTP contract
|
|
39
|
+
|
|
40
|
+
Only `POST application/json` is accepted. The adapter reads a bounded UTF-8 body, requires `X-Hub-Signature-256`, `X-GitHub-Delivery`, and `X-GitHub-Event`, resolves the secret, verifies HMAC before JSON parsing, and requires a top-level lossless-JSON object. It never logs the secret, signature, or payload.
|
|
41
|
+
|
|
42
|
+
| Status | Meaning |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `202` | Verified JSON was dispatched in memory. |
|
|
45
|
+
| `400` | Required header, UTF-8, JSON, or top-level object was invalid. |
|
|
46
|
+
| `401` | Signature was invalid. |
|
|
47
|
+
| `405` | Method was not `POST`. |
|
|
48
|
+
| `413` | Declared or streamed body exceeded `maxBodyBytes`. |
|
|
49
|
+
| `415` | Media type was not `application/json`. |
|
|
50
|
+
| `503` | Credential or webhook runtime was unavailable. |
|
|
51
|
+
|
|
52
|
+
`202` does not state that any rule matched or that a Session was created. GitHub event-specific field validation belongs to each rule; the adapter guarantees only authenticated generic JSON.
|
|
53
|
+
|
|
54
|
+
<a id="dedicated-listener-composition"></a>
|
|
55
|
+
## Dedicated listener composition
|
|
56
|
+
|
|
57
|
+
The normal Web profile already owns `ctx.webServer`. Mount another `dsh-host-webserver` and this adapter inside a group that isolates only `webServer`; the adapter still inherits credentials and `webhookRuntime`. The [GitHub review guide](../../../docs/user/guide/github-review.md) uses `127.0.0.1:3081/github` behind a TLS reverse proxy while the UI remains on port 3080.
|
|
58
|
+
|
|
59
|
+
<a id="model-experience"></a>
|
|
60
|
+
## Model Experience
|
|
61
|
+
|
|
62
|
+
Indirectly, through `dsh-webhook`: this adapter contributes no prompt or tool schema; a matching rule owns the Session request and model-visible text.
|
|
63
|
+
|
|
64
|
+
#### KV Cache effect
|
|
65
|
+
|
|
66
|
+
Independent. Authentication and HTTP dispatch do not touch a model request; any new Session prefix belongs to the consuming rule and runtime.
|
|
67
|
+
|
|
68
|
+
## Known Limitations and Deferred Work
|
|
69
|
+
|
|
70
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
71
|
+
|
|
72
|
+
- **No TLS** — the injected development WebServer is normally loopback-only behind a TLS reverse proxy or tunnel.
|
|
73
|
+
- **Generic payload validation only** — rules own validation of the GitHub event fields they consume.
|
|
74
|
+
- **No provider acknowledgement of downstream work** — `202` precedes arbitrary rule calls and Session creation.
|
|
75
|
+
- **No form encoding** — GitHub must send `application/json`; `application/x-www-form-urlencoded` is rejected.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
<a id="dev-note"></a>
|
|
79
|
+
### Dev Note
|
|
80
|
+
|
|
81
|
+
<details>
|
|
82
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
83
|
+
|
|
84
|
+
None.
|
|
85
|
+
|
|
86
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向把已认证 JSON 事件路由到 webhook 运行时的部署,说明带签名的 GitHub webhook 适配器。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-webhook-github
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-webhook-github` 会在注入的 `ctx.webServer` 上注册一条精确 HTTP 路由。它限制并验证 GitHub 原始 JSON body,投影提供方无关的交付,调用 `ctx.webhookRuntime.dispatch()`,并在不等待规则或 Session 的情况下返回 `202`。部署需要为通用 webhook runtime 提供经过身份验证的 GitHub 入口时,请使用它。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [配置](#configuration)
|
|
17
|
+
- [HTTP 约定](#http-contract)
|
|
18
|
+
- [专用监听器组合](#dedicated-listener-composition)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="configuration"></a>
|
|
26
|
+
## 配置
|
|
27
|
+
|
|
28
|
+
| Key | 含义 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `source` | 携带给规则的非空适配器实例,例如 `primary-github`。 |
|
|
31
|
+
| `path` | 不带尾随斜杠、查询或片段的精确非根路径。 |
|
|
32
|
+
| `secretEnv` | 包含 GitHub webhook 密钥的凭据引用。 |
|
|
33
|
+
| `maxBodyBytes` | 未改动请求 body 的正安全整数上限。 |
|
|
34
|
+
|
|
35
|
+
所有字段均为必填。每次请求都会重新解析密钥引用,因此轮换会在下一次交付生效,而无需重新加载插件。
|
|
36
|
+
|
|
37
|
+
<a id="http-contract"></a>
|
|
38
|
+
## HTTP 约定
|
|
39
|
+
|
|
40
|
+
只接受 `POST application/json`。适配器读取有界 UTF-8 body,要求 `X-Hub-Signature-256`、`X-GitHub-Delivery` 与 `X-GitHub-Event`,解析密钥,在 JSON 解析前验证 HMAC,并要求顶层是无损 JSON 对象。它绝不记录密钥、签名或 payload。
|
|
41
|
+
|
|
42
|
+
| 状态 | 含义 |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `202` | 已验证 JSON 已在内存中分发。 |
|
|
45
|
+
| `400` | 必需 header、UTF-8、JSON 或顶层对象无效。 |
|
|
46
|
+
| `401` | 签名无效。 |
|
|
47
|
+
| `405` | 方法不是 `POST`。 |
|
|
48
|
+
| `413` | 声明或流式 body 超过 `maxBodyBytes`。 |
|
|
49
|
+
| `415` | media type 不是 `application/json`。 |
|
|
50
|
+
| `503` | 凭据或 webhook runtime 不可用。 |
|
|
51
|
+
|
|
52
|
+
`202` 不表示任何规则已经匹配,也不表示已创建 Session。GitHub 事件特定字段的验证属于各规则;适配器只保证通过身份验证的通用 JSON。
|
|
53
|
+
|
|
54
|
+
<a id="dedicated-listener-composition"></a>
|
|
55
|
+
## 专用监听器组合
|
|
56
|
+
|
|
57
|
+
普通 Web profile 已经拥有 `ctx.webServer`。把另一个 `dsh-host-webserver` 和此适配器挂载到仅隔离 `webServer` 的 group 内;适配器仍会继承凭据与 `webhookRuntime`。[GitHub 评审指南](../../../docs/user/guide/github-review.zh.md)在 TLS 反向代理后使用 `127.0.0.1:3081/github`,而 UI 继续位于端口 3080。
|
|
58
|
+
|
|
59
|
+
<a id="model-experience"></a>
|
|
60
|
+
## Model Experience
|
|
61
|
+
|
|
62
|
+
通过 `dsh-webhook` 间接产生影响:此适配器不贡献提示词或工具 schema;匹配规则拥有 Session 请求与模型可见文本。
|
|
63
|
+
|
|
64
|
+
#### KV Cache effect
|
|
65
|
+
|
|
66
|
+
相互独立。身份验证与 HTTP 分发不触碰模型请求;任何新 Session 前缀都属于消费它的规则与 runtime。
|
|
67
|
+
|
|
68
|
+
## Known Limitations and Deferred Work
|
|
69
|
+
|
|
70
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
71
|
+
|
|
72
|
+
- **无 TLS** — 注入的开发 WebServer 通常只监听 loopback,并位于 TLS 反向代理或 tunnel 后。
|
|
73
|
+
- **仅通用 payload 验证** — 规则负责验证自己消费的 GitHub 事件字段。
|
|
74
|
+
- **不向提供方确认下游工作** — `202` 先于任意规则调用与 Session 创建。
|
|
75
|
+
- **不支持表单编码** — GitHub 必须发送 `application/json`;`application/x-www-form-urlencoded` 会被拒绝。
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
<a id="dev-note"></a>
|
|
79
|
+
### 开发备注
|
|
80
|
+
|
|
81
|
+
<details>
|
|
82
|
+
<summary>维护者工作上下文——点击展开</summary>
|
|
83
|
+
|
|
84
|
+
无。
|
|
85
|
+
|
|
86
|
+
</details>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { credentialRef } from "@prettier-ai/dsh-credentials";
|
|
2
|
+
import z from "@prettier-ai/schemastery";
|
|
3
|
+
import { Webhooks } from "@octokit/webhooks";
|
|
4
|
+
import { snapshotJsonValue } from "@prettier-ai/dsh-session";
|
|
5
|
+
import { WebhookDeliveryId, WebhookSourceId } from "@prettier-ai/dsh-webhook";
|
|
6
|
+
//#region lib/types/body.js
|
|
7
|
+
/** Bounded raw HTTP body intake for GitHub signature verification. */
|
|
8
|
+
/** HTTP refusal whose message is safe to return without request data. */
|
|
9
|
+
var WebhookHttpError = class extends Error {
|
|
10
|
+
status;
|
|
11
|
+
name = "WebhookHttpError";
|
|
12
|
+
constructor(status, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.status = status;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** Parse a decimal Content-Length or reject an ambiguous header. */
|
|
18
|
+
function contentLength(request) {
|
|
19
|
+
const value = request.headers["content-length"];
|
|
20
|
+
if (value === void 0) return void 0;
|
|
21
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) throw new WebhookHttpError(400, "invalid Content-Length");
|
|
22
|
+
const length = Number(value);
|
|
23
|
+
if (!Number.isSafeInteger(length)) throw new WebhookHttpError(413, "request body is too large");
|
|
24
|
+
return length;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read one request body as exact, bounded UTF-8 text.
|
|
28
|
+
* @param request - incoming request before any parser consumes it.
|
|
29
|
+
* @param maxBodyBytes - positive byte ceiling.
|
|
30
|
+
* @returns the decoded body after EOF.
|
|
31
|
+
* @throws {WebhookHttpError} for invalid length, excessive bytes, invalid UTF-8, or an aborted stream.
|
|
32
|
+
*/
|
|
33
|
+
async function readBoundedUtf8Body(request, maxBodyBytes) {
|
|
34
|
+
const declared = contentLength(request);
|
|
35
|
+
if (declared !== void 0 && declared > maxBodyBytes) {
|
|
36
|
+
request.resume();
|
|
37
|
+
throw new WebhookHttpError(413, "request body is too large");
|
|
38
|
+
}
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let size = 0;
|
|
41
|
+
try {
|
|
42
|
+
for await (const raw of request) {
|
|
43
|
+
const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
44
|
+
size += chunk.byteLength;
|
|
45
|
+
if (size > maxBodyBytes) {
|
|
46
|
+
request.resume();
|
|
47
|
+
throw new WebhookHttpError(413, "request body is too large");
|
|
48
|
+
}
|
|
49
|
+
chunks.push(chunk);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof WebhookHttpError) throw error;
|
|
53
|
+
throw new WebhookHttpError(400, "request body was aborted");
|
|
54
|
+
}
|
|
55
|
+
if (!request.complete) throw new WebhookHttpError(400, "request body was aborted");
|
|
56
|
+
try {
|
|
57
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks, size));
|
|
58
|
+
} catch {
|
|
59
|
+
throw new WebhookHttpError(400, "request body is not valid UTF-8");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region lib/types/handler.js
|
|
64
|
+
/** GitHub HTTP authentication, parsing, and fire-and-forget dispatch. */
|
|
65
|
+
/** Require one unambiguous non-empty request header. */
|
|
66
|
+
function requiredHeader(request, name) {
|
|
67
|
+
const values = request.headersDistinct[name];
|
|
68
|
+
const value = values?.[0];
|
|
69
|
+
if (values?.length !== 1 || value === void 0 || value.trim() === "") throw new WebhookHttpError(400, `missing ${name} header`);
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
/** Whether Content-Type names JSON with at most one UTF-8 charset parameter. */
|
|
73
|
+
function isJsonContentType(value) {
|
|
74
|
+
if (value === void 0) return false;
|
|
75
|
+
const [mediaType, parameter, ...extra] = value.split(";").map((part) => part.trim());
|
|
76
|
+
if (mediaType?.toLowerCase() !== "application/json") return false;
|
|
77
|
+
if (parameter === void 0) return true;
|
|
78
|
+
return extra.length === 0 && /^charset=(?:utf-8|"utf-8")$/i.test(parameter);
|
|
79
|
+
}
|
|
80
|
+
/** Send one empty or plain-text response exactly once. */
|
|
81
|
+
function respond(response, status, message) {
|
|
82
|
+
if (message === void 0) {
|
|
83
|
+
response.writeHead(status);
|
|
84
|
+
response.end();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
response.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
|
|
88
|
+
response.end(message);
|
|
89
|
+
}
|
|
90
|
+
/** Convert a parsed value into the adapter's generic signed-object guarantee. */
|
|
91
|
+
function parsePayload(body) {
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
parsed = JSON.parse(body);
|
|
95
|
+
} catch {
|
|
96
|
+
throw new WebhookHttpError(400, "request body is not valid JSON");
|
|
97
|
+
}
|
|
98
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new WebhookHttpError(400, "GitHub webhook payload must be a JSON object");
|
|
99
|
+
const snapshot = snapshotJsonValue(parsed);
|
|
100
|
+
if (snapshot === void 0) throw new WebhookHttpError(400, "GitHub webhook payload is not lossless JSON");
|
|
101
|
+
return snapshot;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create one exact-route GitHub handler.
|
|
105
|
+
* @param ctx - adapter context carrying credentials and webhook runtime.
|
|
106
|
+
* @param config - validated source, credential reference, and body ceiling.
|
|
107
|
+
* @returns an HTTP handler that answers after in-memory dispatch, never rule settlement.
|
|
108
|
+
*/
|
|
109
|
+
function createGitHubWebhookHandler(ctx, config) {
|
|
110
|
+
return async (request, response) => {
|
|
111
|
+
try {
|
|
112
|
+
if (request.method !== "POST") {
|
|
113
|
+
response.setHeader("allow", "POST");
|
|
114
|
+
throw new WebhookHttpError(405, "method not allowed");
|
|
115
|
+
}
|
|
116
|
+
if (!isJsonContentType(request.headers["content-type"])) throw new WebhookHttpError(415, "content type must be application/json");
|
|
117
|
+
const body = await readBoundedUtf8Body(request, config.maxBodyBytes);
|
|
118
|
+
const signature = requiredHeader(request, "x-hub-signature-256");
|
|
119
|
+
const deliveryId = requiredHeader(request, "x-github-delivery");
|
|
120
|
+
const eventName = requiredHeader(request, "x-github-event");
|
|
121
|
+
const credential = await ctx.credentials.resolve(config.secretEnv);
|
|
122
|
+
if (credential === void 0 || credential.value === "") throw new WebhookHttpError(503, "GitHub webhook secret is unavailable");
|
|
123
|
+
let verified = false;
|
|
124
|
+
try {
|
|
125
|
+
verified = await new Webhooks({ secret: credential.value }).verify(body, signature);
|
|
126
|
+
} catch {}
|
|
127
|
+
if (!verified) throw new WebhookHttpError(401, "invalid webhook signature");
|
|
128
|
+
const payload = parsePayload(body);
|
|
129
|
+
const delivery = {
|
|
130
|
+
kind: "github",
|
|
131
|
+
source: WebhookSourceId(config.source),
|
|
132
|
+
deliveryId: WebhookDeliveryId(deliveryId),
|
|
133
|
+
event: {
|
|
134
|
+
name: eventName,
|
|
135
|
+
payload
|
|
136
|
+
},
|
|
137
|
+
receivedAt: Date.now()
|
|
138
|
+
};
|
|
139
|
+
try {
|
|
140
|
+
ctx.webhookRuntime.dispatch(delivery);
|
|
141
|
+
} catch {
|
|
142
|
+
ctx.logger.warn("webhook-github: dispatch unavailable");
|
|
143
|
+
throw new WebhookHttpError(503, "webhook runtime is unavailable");
|
|
144
|
+
}
|
|
145
|
+
respond(response, 202);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof WebhookHttpError) {
|
|
148
|
+
respond(response, error.status, error.message);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
ctx.logger.warn("webhook-github: request failed");
|
|
152
|
+
respond(response, 503, "webhook ingress is unavailable");
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region lib/types/index.js
|
|
158
|
+
/** Signed GitHub HTTP adapter for the provider-neutral webhook runtime. */
|
|
159
|
+
/** Cordis function-plugin name. */
|
|
160
|
+
const name = "webhook-github";
|
|
161
|
+
/** Host services required before the exact route can register. */
|
|
162
|
+
const inject = [
|
|
163
|
+
"webServer",
|
|
164
|
+
"webhookRuntime",
|
|
165
|
+
"credentials"
|
|
166
|
+
];
|
|
167
|
+
const Config = z.object({
|
|
168
|
+
source: z.string().required(),
|
|
169
|
+
path: z.string().required(),
|
|
170
|
+
secretEnv: z.string().role("credential-ref").required(),
|
|
171
|
+
maxBodyBytes: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).required()
|
|
172
|
+
});
|
|
173
|
+
/** Validate route and source facts that Schemastery cannot express. */
|
|
174
|
+
function assertConfig(config) {
|
|
175
|
+
if (config.source.trim() !== config.source || config.source === "") throw new Error("webhook-github source must be a non-empty trimmed string");
|
|
176
|
+
if (!config.path.startsWith("/") || config.path === "/" || config.path.endsWith("/") || config.path.includes("?") || config.path.includes("#")) throw new Error("webhook-github path must be an absolute non-root pathname without a trailing slash, query, or fragment");
|
|
177
|
+
}
|
|
178
|
+
/** Register one signed GitHub endpoint on the injected WebServer. */
|
|
179
|
+
function apply(ctx, config) {
|
|
180
|
+
assertConfig(config);
|
|
181
|
+
const route = {
|
|
182
|
+
kind: "exact",
|
|
183
|
+
path: config.path,
|
|
184
|
+
handler: createGitHubWebhookHandler(ctx, {
|
|
185
|
+
source: config.source,
|
|
186
|
+
secretEnv: credentialRef(config.secretEnv),
|
|
187
|
+
maxBodyBytes: config.maxBodyBytes
|
|
188
|
+
})
|
|
189
|
+
};
|
|
190
|
+
ctx.effect(() => ctx.webServer.register(route), `webhook-github: ${config.path}`);
|
|
191
|
+
}
|
|
192
|
+
//#endregion
|
|
193
|
+
export { Config, apply, inject, name };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/** Package-owned invariant companion for the GitHub webhook adapter. */
|
|
3
|
+
const PACKAGE_NAME = "@prettier-ai/dsh-webhook-github";
|
|
4
|
+
/** Cordis invariant-companion plugin name. */
|
|
5
|
+
const name = "webhook-github-invariant";
|
|
6
|
+
/** Registry required before reserving this package's invariant ownership. */
|
|
7
|
+
const inject = ["invariants"];
|
|
8
|
+
/**
|
|
9
|
+
* No runtime invariant: authentication and input validation occur at the exact
|
|
10
|
+
* HTTP operation; dsh-host-webserver owns route/disposer symmetry.
|
|
11
|
+
*/
|
|
12
|
+
const install = () => {};
|
|
13
|
+
/**
|
|
14
|
+
* Register this package's explained empty invariant.
|
|
15
|
+
* @param ctx - Cordis context carrying the invariant registry.
|
|
16
|
+
* @returns the invariant registration disposer.
|
|
17
|
+
*/
|
|
18
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
19
|
+
//#endregion
|
|
20
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Bounded raw HTTP body intake for GitHub signature verification. */
|
|
2
|
+
import type { IncomingMessage } from 'node:http';
|
|
3
|
+
/** HTTP refusal whose message is safe to return without request data. */
|
|
4
|
+
export declare class WebhookHttpError extends Error {
|
|
5
|
+
readonly status: 400 | 401 | 405 | 413 | 415 | 503;
|
|
6
|
+
readonly name = "WebhookHttpError";
|
|
7
|
+
constructor(status: 400 | 401 | 405 | 413 | 415 | 503, message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read one request body as exact, bounded UTF-8 text.
|
|
11
|
+
* @param request - incoming request before any parser consumes it.
|
|
12
|
+
* @param maxBodyBytes - positive byte ceiling.
|
|
13
|
+
* @returns the decoded body after EOF.
|
|
14
|
+
* @throws {WebhookHttpError} for invalid length, excessive bytes, invalid UTF-8, or an aborted stream.
|
|
15
|
+
*/
|
|
16
|
+
export declare function readBoundedUtf8Body(request: IncomingMessage, maxBodyBytes: number): Promise<string>;
|
|
17
|
+
//# sourceMappingURL=body.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Bounded raw HTTP body intake for GitHub signature verification. */
|
|
2
|
+
/** HTTP refusal whose message is safe to return without request data. */
|
|
3
|
+
export class WebhookHttpError extends Error {
|
|
4
|
+
status;
|
|
5
|
+
name = 'WebhookHttpError';
|
|
6
|
+
constructor(status, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Parse a decimal Content-Length or reject an ambiguous header. */
|
|
12
|
+
function contentLength(request) {
|
|
13
|
+
const value = request.headers['content-length'];
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) {
|
|
17
|
+
throw new WebhookHttpError(400, 'invalid Content-Length');
|
|
18
|
+
}
|
|
19
|
+
const length = Number(value);
|
|
20
|
+
if (!Number.isSafeInteger(length))
|
|
21
|
+
throw new WebhookHttpError(413, 'request body is too large');
|
|
22
|
+
return length;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read one request body as exact, bounded UTF-8 text.
|
|
26
|
+
* @param request - incoming request before any parser consumes it.
|
|
27
|
+
* @param maxBodyBytes - positive byte ceiling.
|
|
28
|
+
* @returns the decoded body after EOF.
|
|
29
|
+
* @throws {WebhookHttpError} for invalid length, excessive bytes, invalid UTF-8, or an aborted stream.
|
|
30
|
+
*/
|
|
31
|
+
export async function readBoundedUtf8Body(request, maxBodyBytes) {
|
|
32
|
+
const declared = contentLength(request);
|
|
33
|
+
if (declared !== undefined && declared > maxBodyBytes) {
|
|
34
|
+
request.resume();
|
|
35
|
+
throw new WebhookHttpError(413, 'request body is too large');
|
|
36
|
+
}
|
|
37
|
+
const chunks = [];
|
|
38
|
+
let size = 0;
|
|
39
|
+
try {
|
|
40
|
+
for await (const raw of request) {
|
|
41
|
+
const chunk = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
|
|
42
|
+
size += chunk.byteLength;
|
|
43
|
+
if (size > maxBodyBytes) {
|
|
44
|
+
request.resume();
|
|
45
|
+
throw new WebhookHttpError(413, 'request body is too large');
|
|
46
|
+
}
|
|
47
|
+
chunks.push(chunk);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof WebhookHttpError)
|
|
52
|
+
throw error;
|
|
53
|
+
throw new WebhookHttpError(400, 'request body was aborted');
|
|
54
|
+
}
|
|
55
|
+
if (!request.complete)
|
|
56
|
+
throw new WebhookHttpError(400, 'request body was aborted');
|
|
57
|
+
try {
|
|
58
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks, size));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// TextDecoder is the only statement in the try; GitHub JSON must be valid UTF-8.
|
|
62
|
+
throw new WebhookHttpError(400, 'request body is not valid UTF-8');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** GitHub HTTP authentication, parsing, and fire-and-forget dispatch. */
|
|
2
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
3
|
+
import type { CredentialRef } from '@prettier-ai/dsh-credentials';
|
|
4
|
+
import type { WebRoute } from '@prettier-ai/dsh-host-webserver';
|
|
5
|
+
/** Handler values validated once at plugin load. */
|
|
6
|
+
export interface GitHubWebhookHandlerConfig {
|
|
7
|
+
readonly source: string;
|
|
8
|
+
readonly secretEnv: CredentialRef;
|
|
9
|
+
readonly maxBodyBytes: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create one exact-route GitHub handler.
|
|
13
|
+
* @param ctx - adapter context carrying credentials and webhook runtime.
|
|
14
|
+
* @param config - validated source, credential reference, and body ceiling.
|
|
15
|
+
* @returns an HTTP handler that answers after in-memory dispatch, never rule settlement.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createGitHubWebhookHandler(ctx: Context, config: GitHubWebhookHandlerConfig): WebRoute['handler'];
|
|
18
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** GitHub HTTP authentication, parsing, and fire-and-forget dispatch. */
|
|
2
|
+
import { Webhooks } from '@octokit/webhooks';
|
|
3
|
+
import { snapshotJsonValue } from '@prettier-ai/dsh-session';
|
|
4
|
+
import { WebhookDeliveryId, WebhookSourceId, } from '@prettier-ai/dsh-webhook';
|
|
5
|
+
import { readBoundedUtf8Body, WebhookHttpError } from "./body.js";
|
|
6
|
+
/** Require one unambiguous non-empty request header. */
|
|
7
|
+
function requiredHeader(request, name) {
|
|
8
|
+
const values = request.headersDistinct[name];
|
|
9
|
+
const value = values?.[0];
|
|
10
|
+
if (values?.length !== 1 || value === undefined || value.trim() === '') {
|
|
11
|
+
throw new WebhookHttpError(400, `missing ${name} header`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
/** Whether Content-Type names JSON with at most one UTF-8 charset parameter. */
|
|
16
|
+
function isJsonContentType(value) {
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return false;
|
|
19
|
+
const parts = value.split(';').map(part => part.trim());
|
|
20
|
+
const [mediaType, parameter, ...extra] = parts;
|
|
21
|
+
if (mediaType?.toLowerCase() !== 'application/json')
|
|
22
|
+
return false;
|
|
23
|
+
if (parameter === undefined)
|
|
24
|
+
return true;
|
|
25
|
+
return extra.length === 0 && /^charset=(?:utf-8|"utf-8")$/i.test(parameter);
|
|
26
|
+
}
|
|
27
|
+
/** Send one empty or plain-text response exactly once. */
|
|
28
|
+
function respond(response, status, message) {
|
|
29
|
+
if (message === undefined) {
|
|
30
|
+
response.writeHead(status);
|
|
31
|
+
response.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
response.writeHead(status, { 'content-type': 'text/plain; charset=utf-8' });
|
|
35
|
+
response.end(message);
|
|
36
|
+
}
|
|
37
|
+
/** Convert a parsed value into the adapter's generic signed-object guarantee. */
|
|
38
|
+
function parsePayload(body) {
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(body);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// JSON.parse is the only statement in the try; no other failure is normalized.
|
|
45
|
+
throw new WebhookHttpError(400, 'request body is not valid JSON');
|
|
46
|
+
}
|
|
47
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
48
|
+
throw new WebhookHttpError(400, 'GitHub webhook payload must be a JSON object');
|
|
49
|
+
}
|
|
50
|
+
const snapshot = snapshotJsonValue(parsed);
|
|
51
|
+
if (snapshot === undefined)
|
|
52
|
+
throw new WebhookHttpError(400, 'GitHub webhook payload is not lossless JSON');
|
|
53
|
+
return snapshot;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create one exact-route GitHub handler.
|
|
57
|
+
* @param ctx - adapter context carrying credentials and webhook runtime.
|
|
58
|
+
* @param config - validated source, credential reference, and body ceiling.
|
|
59
|
+
* @returns an HTTP handler that answers after in-memory dispatch, never rule settlement.
|
|
60
|
+
*/
|
|
61
|
+
export function createGitHubWebhookHandler(ctx, config) {
|
|
62
|
+
return async (request, response) => {
|
|
63
|
+
try {
|
|
64
|
+
if (request.method !== 'POST') {
|
|
65
|
+
response.setHeader('allow', 'POST');
|
|
66
|
+
throw new WebhookHttpError(405, 'method not allowed');
|
|
67
|
+
}
|
|
68
|
+
if (!isJsonContentType(request.headers['content-type'])) {
|
|
69
|
+
throw new WebhookHttpError(415, 'content type must be application/json');
|
|
70
|
+
}
|
|
71
|
+
const body = await readBoundedUtf8Body(request, config.maxBodyBytes);
|
|
72
|
+
const signature = requiredHeader(request, 'x-hub-signature-256');
|
|
73
|
+
const deliveryId = requiredHeader(request, 'x-github-delivery');
|
|
74
|
+
const eventName = requiredHeader(request, 'x-github-event');
|
|
75
|
+
const credential = await ctx.credentials.resolve(config.secretEnv);
|
|
76
|
+
if (credential === undefined || credential.value === '') {
|
|
77
|
+
throw new WebhookHttpError(503, 'GitHub webhook secret is unavailable');
|
|
78
|
+
}
|
|
79
|
+
let verified = false;
|
|
80
|
+
try {
|
|
81
|
+
verified = await new Webhooks({ secret: credential.value }).verify(body, signature);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Octokit verification errors carry no response detail safe or useful to the sender.
|
|
85
|
+
}
|
|
86
|
+
if (!verified)
|
|
87
|
+
throw new WebhookHttpError(401, 'invalid webhook signature');
|
|
88
|
+
const payload = parsePayload(body);
|
|
89
|
+
const delivery = {
|
|
90
|
+
kind: 'github',
|
|
91
|
+
source: WebhookSourceId(config.source),
|
|
92
|
+
deliveryId: WebhookDeliveryId(deliveryId),
|
|
93
|
+
event: { name: eventName, payload },
|
|
94
|
+
receivedAt: Date.now(),
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
ctx.webhookRuntime.dispatch(delivery);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
ctx.logger.warn('webhook-github: dispatch unavailable');
|
|
101
|
+
throw new WebhookHttpError(503, 'webhook runtime is unavailable');
|
|
102
|
+
}
|
|
103
|
+
respond(response, 202);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error instanceof WebhookHttpError) {
|
|
107
|
+
respond(response, error.status, error.message);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
ctx.logger.warn('webhook-github: request failed');
|
|
111
|
+
respond(response, 503, 'webhook ingress is unavailable');
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Signed GitHub HTTP adapter for the provider-neutral webhook runtime. */
|
|
2
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
3
|
+
import z from '@prettier-ai/schemastery';
|
|
4
|
+
export type * from './types.ts';
|
|
5
|
+
/** Cordis function-plugin name. */
|
|
6
|
+
export declare const name = "webhook-github";
|
|
7
|
+
/** Host services required before the exact route can register. */
|
|
8
|
+
export declare const inject: string[];
|
|
9
|
+
/** Required GitHub ingress configuration. */
|
|
10
|
+
export interface Config {
|
|
11
|
+
/** Adapter instance name carried to rules. */
|
|
12
|
+
readonly source: string;
|
|
13
|
+
/** Exact absolute route path. */
|
|
14
|
+
readonly path: string;
|
|
15
|
+
/** Credential reference containing the shared webhook secret. */
|
|
16
|
+
readonly secretEnv: string;
|
|
17
|
+
/** Positive raw body ceiling in bytes. */
|
|
18
|
+
readonly maxBodyBytes: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const Config: z<Config>;
|
|
21
|
+
/** Register one signed GitHub endpoint on the injected WebServer. */
|
|
22
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Signed GitHub HTTP adapter for the provider-neutral webhook runtime. */
|
|
2
|
+
import { credentialRef } from '@prettier-ai/dsh-credentials';
|
|
3
|
+
import z from '@prettier-ai/schemastery';
|
|
4
|
+
import { createGitHubWebhookHandler } from "./handler.js";
|
|
5
|
+
/** Cordis function-plugin name. */
|
|
6
|
+
export const name = 'webhook-github';
|
|
7
|
+
/** Host services required before the exact route can register. */
|
|
8
|
+
export const inject = ['webServer', 'webhookRuntime', 'credentials'];
|
|
9
|
+
export const Config = z.object({
|
|
10
|
+
source: z.string().required(),
|
|
11
|
+
path: z.string().required(),
|
|
12
|
+
secretEnv: z.string().role('credential-ref').required(),
|
|
13
|
+
maxBodyBytes: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).required(),
|
|
14
|
+
});
|
|
15
|
+
/** Validate route and source facts that Schemastery cannot express. */
|
|
16
|
+
function assertConfig(config) {
|
|
17
|
+
if (config.source.trim() !== config.source || config.source === '') {
|
|
18
|
+
throw new Error('webhook-github source must be a non-empty trimmed string');
|
|
19
|
+
}
|
|
20
|
+
if (!config.path.startsWith('/') || config.path === '/' || config.path.endsWith('/')
|
|
21
|
+
|| config.path.includes('?') || config.path.includes('#')) {
|
|
22
|
+
throw new Error('webhook-github path must be an absolute non-root pathname without a trailing slash, query, or fragment');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Register one signed GitHub endpoint on the injected WebServer. */
|
|
26
|
+
export function apply(ctx, config) {
|
|
27
|
+
assertConfig(config);
|
|
28
|
+
const route = {
|
|
29
|
+
kind: 'exact',
|
|
30
|
+
path: config.path,
|
|
31
|
+
handler: createGitHubWebhookHandler(ctx, {
|
|
32
|
+
source: config.source,
|
|
33
|
+
secretEnv: credentialRef(config.secretEnv),
|
|
34
|
+
maxBodyBytes: config.maxBodyBytes,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
ctx.effect(() => ctx.webServer.register(route), `webhook-github: ${config.path}`);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Package-owned invariant companion for the GitHub webhook adapter. */
|
|
2
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
3
|
+
/** Cordis invariant-companion plugin name. */
|
|
4
|
+
export declare const name = "webhook-github-invariant";
|
|
5
|
+
/** Registry required before reserving this package's invariant ownership. */
|
|
6
|
+
export declare const inject: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Register this package's explained empty invariant.
|
|
9
|
+
* @param ctx - Cordis context carrying the invariant registry.
|
|
10
|
+
* @returns the invariant registration disposer.
|
|
11
|
+
*/
|
|
12
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
13
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Package-owned invariant companion for the GitHub webhook adapter. */
|
|
2
|
+
const PACKAGE_NAME = '@prettier-ai/dsh-webhook-github';
|
|
3
|
+
/** Cordis invariant-companion plugin name. */
|
|
4
|
+
export const name = 'webhook-github-invariant';
|
|
5
|
+
/** Registry required before reserving this package's invariant ownership. */
|
|
6
|
+
export const inject = ['invariants'];
|
|
7
|
+
/**
|
|
8
|
+
* No runtime invariant: authentication and input validation occur at the exact
|
|
9
|
+
* HTTP operation; dsh-host-webserver owns route/disposer symmetry.
|
|
10
|
+
*/
|
|
11
|
+
const install = () => { };
|
|
12
|
+
/**
|
|
13
|
+
* Register this package's explained empty invariant.
|
|
14
|
+
* @param ctx - Cordis context carrying the invariant registry.
|
|
15
|
+
* @returns the invariant registration disposer.
|
|
16
|
+
*/
|
|
17
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
18
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** GitHub event values projected after signature verification. */
|
|
2
|
+
import type { JsonValue } from '@prettier-ai/dsh-session';
|
|
3
|
+
/** Signed GitHub JSON object. Event-specific field validation belongs to each rule. */
|
|
4
|
+
export type GitHubJsonObject = {
|
|
5
|
+
readonly [key: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
/** Provider event supplied to `WebhookRule<'github'>`. */
|
|
8
|
+
export interface GitHubWebhookEvent {
|
|
9
|
+
/** Raw `X-GitHub-Event` name such as `pull_request`. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Signed JSON object exactly as parsed from the request body. */
|
|
12
|
+
readonly payload: GitHubJsonObject;
|
|
13
|
+
}
|
|
14
|
+
declare module '@prettier-ai/dsh-webhook' {
|
|
15
|
+
interface WebhookEventMap {
|
|
16
|
+
github: GitHubWebhookEvent;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export type { EmitterWebhookEvent, EmitterWebhookEventName } from '@octokit/webhooks';
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prettier-ai/dsh-webhook-github",
|
|
3
|
+
"description": "Signed GitHub HTTP webhook adapter for the DeepSeek Harness webhook runtime",
|
|
4
|
+
"version": "0.1.2-alpha.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/webhook/webhook-github"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./types": {
|
|
22
|
+
"types": "./lib/types/types.d.ts",
|
|
23
|
+
"default": "./lib/types/types.js"
|
|
24
|
+
},
|
|
25
|
+
"./invariant": {
|
|
26
|
+
"types": "./lib/types/invariant.d.ts",
|
|
27
|
+
"default": "./lib/invariant.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"lib/index.js",
|
|
34
|
+
"lib/invariant.js",
|
|
35
|
+
"lib/types/**/*.js",
|
|
36
|
+
"lib/types/**/*.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
41
|
+
"@prettier-ai/dsh-host-webserver": "^0.1.2-alpha.1",
|
|
42
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1",
|
|
43
|
+
"@prettier-ai/dsh-webhook": "^0.1.2-alpha.1",
|
|
44
|
+
"@prettier-ai/dsh-credentials": "^0.1.2-alpha.1",
|
|
45
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@octokit/webhooks": "^14.2.0",
|
|
49
|
+
"@prettier-ai/schemastery": "^3.18.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
53
|
+
"@prettier-ai/cordis-plugin-include": "^1.0.6",
|
|
54
|
+
"@prettier-ai/cordis-plugin-loader": "^1.0.2",
|
|
55
|
+
"@prettier-ai/dsh-credentials": "^0.1.2-alpha.1",
|
|
56
|
+
"@prettier-ai/dsh-host-webserver": "^0.1.2-alpha.1",
|
|
57
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
58
|
+
"@prettier-ai/dsh-session": "^0.1.2-alpha.1",
|
|
59
|
+
"@prettier-ai/dsh-webhook": "^0.1.2-alpha.1"
|
|
60
|
+
}
|
|
61
|
+
}
|