hippo-memory 1.2.0 → 1.3.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.
- package/README.md +15 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +30 -10
- package/dist/api.js.map +1 -1
- package/dist/cli.js +15 -3
- package/dist/cli.js.map +1 -1
- package/dist/connectors/github/backfill.d.ts +48 -0
- package/dist/connectors/github/backfill.d.ts.map +1 -0
- package/dist/connectors/github/backfill.js +257 -0
- package/dist/connectors/github/backfill.js.map +1 -0
- package/dist/connectors/github/cli-impl.d.ts +24 -0
- package/dist/connectors/github/cli-impl.d.ts.map +1 -0
- package/dist/connectors/github/cli-impl.js +152 -0
- package/dist/connectors/github/cli-impl.js.map +1 -0
- package/dist/connectors/github/deletion.d.ts +38 -0
- package/dist/connectors/github/deletion.d.ts.map +1 -0
- package/dist/connectors/github/deletion.js +78 -0
- package/dist/connectors/github/deletion.js.map +1 -0
- package/dist/connectors/github/dlq.d.ts +101 -0
- package/dist/connectors/github/dlq.d.ts.map +1 -0
- package/dist/connectors/github/dlq.js +181 -0
- package/dist/connectors/github/dlq.js.map +1 -0
- package/dist/connectors/github/idempotency.d.ts +19 -0
- package/dist/connectors/github/idempotency.d.ts.map +1 -0
- package/dist/connectors/github/idempotency.js +25 -0
- package/dist/connectors/github/idempotency.js.map +1 -0
- package/dist/connectors/github/ingest.d.ts +67 -0
- package/dist/connectors/github/ingest.d.ts.map +1 -0
- package/dist/connectors/github/ingest.js +107 -0
- package/dist/connectors/github/ingest.js.map +1 -0
- package/dist/connectors/github/octokit-client.d.ts +36 -0
- package/dist/connectors/github/octokit-client.d.ts.map +1 -0
- package/dist/connectors/github/octokit-client.js +65 -0
- package/dist/connectors/github/octokit-client.js.map +1 -0
- package/dist/connectors/github/ratelimit.d.ts +20 -0
- package/dist/connectors/github/ratelimit.d.ts.map +1 -0
- package/dist/connectors/github/ratelimit.js +31 -0
- package/dist/connectors/github/ratelimit.js.map +1 -0
- package/dist/connectors/github/scope.d.ts +8 -0
- package/dist/connectors/github/scope.d.ts.map +1 -0
- package/dist/connectors/github/scope.js +13 -0
- package/dist/connectors/github/scope.js.map +1 -0
- package/dist/connectors/github/signature.d.ts +24 -0
- package/dist/connectors/github/signature.d.ts.map +1 -0
- package/dist/connectors/github/signature.js +35 -0
- package/dist/connectors/github/signature.js.map +1 -0
- package/dist/connectors/github/tenant-routing.d.ts +33 -0
- package/dist/connectors/github/tenant-routing.d.ts.map +1 -0
- package/dist/connectors/github/tenant-routing.js +61 -0
- package/dist/connectors/github/tenant-routing.js.map +1 -0
- package/dist/connectors/github/transform.d.ts +7 -0
- package/dist/connectors/github/transform.d.ts.map +1 -0
- package/dist/connectors/github/transform.js +103 -0
- package/dist/connectors/github/transform.js.map +1 -0
- package/dist/connectors/github/types.d.ts +87 -0
- package/dist/connectors/github/types.d.ts.map +1 -0
- package/dist/connectors/github/types.js +94 -0
- package/dist/connectors/github/types.js.map +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +72 -1
- package/dist/db.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +6 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +310 -1
- package/dist/server.js.map +1 -1
- package/dist/src/api.js +30 -10
- package/dist/src/api.js.map +1 -1
- package/dist/src/cli.js +15 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connectors/github/backfill.js +257 -0
- package/dist/src/connectors/github/backfill.js.map +1 -0
- package/dist/src/connectors/github/cli-impl.js +152 -0
- package/dist/src/connectors/github/cli-impl.js.map +1 -0
- package/dist/src/connectors/github/deletion.js +78 -0
- package/dist/src/connectors/github/deletion.js.map +1 -0
- package/dist/src/connectors/github/dlq.js +181 -0
- package/dist/src/connectors/github/dlq.js.map +1 -0
- package/dist/src/connectors/github/idempotency.js +25 -0
- package/dist/src/connectors/github/idempotency.js.map +1 -0
- package/dist/src/connectors/github/ingest.js +107 -0
- package/dist/src/connectors/github/ingest.js.map +1 -0
- package/dist/src/connectors/github/octokit-client.js +65 -0
- package/dist/src/connectors/github/octokit-client.js.map +1 -0
- package/dist/src/connectors/github/ratelimit.js +31 -0
- package/dist/src/connectors/github/ratelimit.js.map +1 -0
- package/dist/src/connectors/github/scope.js +13 -0
- package/dist/src/connectors/github/scope.js.map +1 -0
- package/dist/src/connectors/github/signature.js +35 -0
- package/dist/src/connectors/github/signature.js.map +1 -0
- package/dist/src/connectors/github/tenant-routing.js +61 -0
- package/dist/src/connectors/github/tenant-routing.js.map +1 -0
- package/dist/src/connectors/github/transform.js +103 -0
- package/dist/src/connectors/github/transform.js.map +1 -0
- package/dist/src/connectors/github/types.js +94 -0
- package/dist/src/connectors/github/types.js.map +1 -0
- package/dist/src/db.js +72 -1
- package/dist/src/db.js.map +1 -1
- package/dist/src/mcp/server.js +6 -4
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/server.js +310 -1
- package/dist/src/server.js.map +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub rate-limit header parser.
|
|
3
|
+
*
|
|
4
|
+
* GitHub returns 403 for primary rate-limit (with `X-RateLimit-Remaining: 0`
|
|
5
|
+
* and `X-RateLimit-Reset: <epoch>`) and 429 + `Retry-After: <seconds>` for
|
|
6
|
+
* secondary rate-limit. Backfill must pause and resume rather than error.
|
|
7
|
+
*/
|
|
8
|
+
export interface RateLimitInfo {
|
|
9
|
+
readonly sleepSeconds: number;
|
|
10
|
+
readonly reason: 'primary' | 'secondary' | 'none';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse rate-limit signal from a GitHub HTTP response.
|
|
14
|
+
*
|
|
15
|
+
* @param headers Lower-cased HTTP response headers.
|
|
16
|
+
* @param status HTTP status code.
|
|
17
|
+
* @param now Optional current epoch seconds (for deterministic tests).
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseRateLimit(headers: Record<string, string | undefined>, status: number, now?: number): RateLimitInfo;
|
|
20
|
+
//# sourceMappingURL=ratelimit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/ratelimit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3C,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,MAAM,GACX,aAAa,CAkBf"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub rate-limit header parser.
|
|
3
|
+
*
|
|
4
|
+
* GitHub returns 403 for primary rate-limit (with `X-RateLimit-Remaining: 0`
|
|
5
|
+
* and `X-RateLimit-Reset: <epoch>`) and 429 + `Retry-After: <seconds>` for
|
|
6
|
+
* secondary rate-limit. Backfill must pause and resume rather than error.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Parse rate-limit signal from a GitHub HTTP response.
|
|
10
|
+
*
|
|
11
|
+
* @param headers Lower-cased HTTP response headers.
|
|
12
|
+
* @param status HTTP status code.
|
|
13
|
+
* @param now Optional current epoch seconds (for deterministic tests).
|
|
14
|
+
*/
|
|
15
|
+
export function parseRateLimit(headers, status, now) {
|
|
16
|
+
const _now = now ?? Math.floor(Date.now() / 1000);
|
|
17
|
+
if (status === 429) {
|
|
18
|
+
const retry = Number(headers['retry-after'] ?? '60');
|
|
19
|
+
return {
|
|
20
|
+
sleepSeconds: Number.isFinite(retry) && retry >= 0 ? retry : 60,
|
|
21
|
+
reason: 'secondary',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (status === 403 && Number(headers['x-ratelimit-remaining'] ?? '1') === 0) {
|
|
25
|
+
const reset = Number(headers['x-ratelimit-reset'] ?? '0');
|
|
26
|
+
const diff = reset - _now;
|
|
27
|
+
return { sleepSeconds: Math.max(diff, 1), reason: 'primary' };
|
|
28
|
+
}
|
|
29
|
+
return { sleepSeconds: 0, reason: 'none' };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ratelimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratelimit.js","sourceRoot":"","sources":["../../../src/connectors/github/ratelimit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA2C,EAC3C,MAAc,EACd,GAAY;IAEZ,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAElD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACnB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,CAAC;QACrD,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YAC/D,MAAM,EAAE,WAAW;SACpB,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;QAC1B,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAChE,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GitHubRepository } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Map a GitHub repository to a hippo scope string. Default-private when
|
|
4
|
+
* privacy is undetermined: cost of leaking public into private (recall returns
|
|
5
|
+
* nothing) << cost of leaking private into public (data exposure).
|
|
6
|
+
*/
|
|
7
|
+
export declare function scopeFromRepository(repo: GitHubRepository | undefined): string;
|
|
8
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,MAAM,CAI9E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a GitHub repository to a hippo scope string. Default-private when
|
|
3
|
+
* privacy is undetermined: cost of leaking public into private (recall returns
|
|
4
|
+
* nothing) << cost of leaking private into public (data exposure).
|
|
5
|
+
*/
|
|
6
|
+
export function scopeFromRepository(repo) {
|
|
7
|
+
if (!repo)
|
|
8
|
+
return 'github:private:unknown';
|
|
9
|
+
if (repo.private === false)
|
|
10
|
+
return `github:public:${repo.full_name}`;
|
|
11
|
+
return `github:private:${repo.full_name}`;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../../src/connectors/github/scope.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAkC;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,wBAAwB,CAAC;IAC3C,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,iBAAiB,IAAI,CAAC,SAAS,EAAE,CAAC;IACrE,OAAO,kBAAkB,IAAI,CAAC,SAAS,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface VerifyOpts {
|
|
2
|
+
rawBody: string;
|
|
3
|
+
/** Value of X-Hub-Signature-256, e.g. 'sha256=ab12...' */
|
|
4
|
+
signature: string;
|
|
5
|
+
webhookSecret: string;
|
|
6
|
+
/** Previous secret for rotation parity with Slack. Optional. */
|
|
7
|
+
previousSecret?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function verifyGitHubSignature(opts: VerifyOpts): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Replay-safe idempotency key (codex P0 #3).
|
|
12
|
+
*
|
|
13
|
+
* GitHub does NOT sign X-GitHub-Delivery, so an attacker who captures one
|
|
14
|
+
* signed payload can replay the body with any new delivery UUID. Deriving
|
|
15
|
+
* idempotency from the delivery_id is unsafe.
|
|
16
|
+
*
|
|
17
|
+
* Key = sha256(eventName + ':' + rawBody). Both inputs are tamper-evident:
|
|
18
|
+
* - eventName comes from X-GitHub-Event, gated by upstream type guards.
|
|
19
|
+
* - rawBody is signed by the HMAC.
|
|
20
|
+
* A valid replay of (eventName, body) IS the same event. delivery_id is kept
|
|
21
|
+
* as audit metadata only, not as the dedupe seam.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeIdempotencyKey(eventName: string, rawBody: string): string;
|
|
24
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/signature.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAWD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createHmac, createHash, timingSafeEqual } from 'crypto';
|
|
2
|
+
function verifyOne(rawBody, signature, secret) {
|
|
3
|
+
if (!signature.startsWith('sha256='))
|
|
4
|
+
return false;
|
|
5
|
+
const expected = `sha256=${createHmac('sha256', secret).update(rawBody).digest('hex')}`;
|
|
6
|
+
const a = Buffer.from(signature, 'utf8');
|
|
7
|
+
const b = Buffer.from(expected, 'utf8');
|
|
8
|
+
if (a.length !== b.length)
|
|
9
|
+
return false;
|
|
10
|
+
return timingSafeEqual(a, b);
|
|
11
|
+
}
|
|
12
|
+
export function verifyGitHubSignature(opts) {
|
|
13
|
+
if (verifyOne(opts.rawBody, opts.signature, opts.webhookSecret))
|
|
14
|
+
return true;
|
|
15
|
+
if (opts.previousSecret && verifyOne(opts.rawBody, opts.signature, opts.previousSecret))
|
|
16
|
+
return true;
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Replay-safe idempotency key (codex P0 #3).
|
|
21
|
+
*
|
|
22
|
+
* GitHub does NOT sign X-GitHub-Delivery, so an attacker who captures one
|
|
23
|
+
* signed payload can replay the body with any new delivery UUID. Deriving
|
|
24
|
+
* idempotency from the delivery_id is unsafe.
|
|
25
|
+
*
|
|
26
|
+
* Key = sha256(eventName + ':' + rawBody). Both inputs are tamper-evident:
|
|
27
|
+
* - eventName comes from X-GitHub-Event, gated by upstream type guards.
|
|
28
|
+
* - rawBody is signed by the HMAC.
|
|
29
|
+
* A valid replay of (eventName, body) IS the same event. delivery_id is kept
|
|
30
|
+
* as audit metadata only, not as the dedupe seam.
|
|
31
|
+
*/
|
|
32
|
+
export function computeIdempotencyKey(eventName, rawBody) {
|
|
33
|
+
return createHash('sha256').update(`${eventName}:${rawBody}`).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../../src/connectors/github/signature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAWjE,SAAS,SAAS,CAAC,OAAe,EAAE,SAAiB,EAAE,MAAc;IACnE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,MAAM,QAAQ,GAAG,UAAU,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACxF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAgB;IACpD,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,IAAI,CAAC,cAAc,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC;QAAE,OAAO,IAAI,CAAC;IACrG,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB,EAAE,OAAe;IACtE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DatabaseSyncLike } from '../../db.js';
|
|
2
|
+
export interface ResolveArgs {
|
|
3
|
+
/** String form of `installation.id`. `null`/`undefined` means "no installation field" (PAT-mode webhook). */
|
|
4
|
+
installationId?: string | null;
|
|
5
|
+
/** `repository.full_name` from the webhook envelope, used for PAT-mode multi-tenant routing. */
|
|
6
|
+
repoFullName?: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the tenant_id for a GitHub webhook envelope.
|
|
10
|
+
*
|
|
11
|
+
* Returns:
|
|
12
|
+
* - mapped tenant_id when `github_installations` has a row for `installationId`
|
|
13
|
+
* (App-mode multi-tenant — primary path)
|
|
14
|
+
* - mapped tenant_id when `installation` is absent, `repository.full_name`
|
|
15
|
+
* matches a `github_repositories` row (PAT-mode multi-tenant)
|
|
16
|
+
* - the deployment's HIPPO_TENANT fallback (or 'default') when BOTH routing
|
|
17
|
+
* tables are empty (single-tenant deployment — env fallback is safe)
|
|
18
|
+
* - null when:
|
|
19
|
+
* - `installationId` is present but unknown AND `github_installations`
|
|
20
|
+
* is non-empty (multi-tenant install with foreign installation)
|
|
21
|
+
* - `installationId` is missing AND either routing table is non-empty
|
|
22
|
+
* AND no `repository.full_name` match (PAT-mode webhook from a foreign
|
|
23
|
+
* account — codex P0 #4 regression target)
|
|
24
|
+
*
|
|
25
|
+
* Escape hatch: `GITHUB_ALLOW_UNKNOWN_INSTALLATION_FALLBACK=1` restores the
|
|
26
|
+
* env fallback for emergency rollback only. Mirrors the Slack equivalent
|
|
27
|
+
* (`SLACK_ALLOW_UNKNOWN_TEAM_FALLBACK`).
|
|
28
|
+
*
|
|
29
|
+
* The fail-closed contract lives here so every caller (route handler, CLI
|
|
30
|
+
* replay, future MCP) gets identical protection.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveTenantForGitHub(db: DatabaseSyncLike, args: ResolveArgs): string | null;
|
|
33
|
+
//# sourceMappingURL=tenant-routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-routing.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/tenant-routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,6GAA6G;IAC7G,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,EACpB,IAAI,EAAE,WAAW,GAChB,MAAM,GAAG,IAAI,CAsCf"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the tenant_id for a GitHub webhook envelope.
|
|
3
|
+
*
|
|
4
|
+
* Returns:
|
|
5
|
+
* - mapped tenant_id when `github_installations` has a row for `installationId`
|
|
6
|
+
* (App-mode multi-tenant — primary path)
|
|
7
|
+
* - mapped tenant_id when `installation` is absent, `repository.full_name`
|
|
8
|
+
* matches a `github_repositories` row (PAT-mode multi-tenant)
|
|
9
|
+
* - the deployment's HIPPO_TENANT fallback (or 'default') when BOTH routing
|
|
10
|
+
* tables are empty (single-tenant deployment — env fallback is safe)
|
|
11
|
+
* - null when:
|
|
12
|
+
* - `installationId` is present but unknown AND `github_installations`
|
|
13
|
+
* is non-empty (multi-tenant install with foreign installation)
|
|
14
|
+
* - `installationId` is missing AND either routing table is non-empty
|
|
15
|
+
* AND no `repository.full_name` match (PAT-mode webhook from a foreign
|
|
16
|
+
* account — codex P0 #4 regression target)
|
|
17
|
+
*
|
|
18
|
+
* Escape hatch: `GITHUB_ALLOW_UNKNOWN_INSTALLATION_FALLBACK=1` restores the
|
|
19
|
+
* env fallback for emergency rollback only. Mirrors the Slack equivalent
|
|
20
|
+
* (`SLACK_ALLOW_UNKNOWN_TEAM_FALLBACK`).
|
|
21
|
+
*
|
|
22
|
+
* The fail-closed contract lives here so every caller (route handler, CLI
|
|
23
|
+
* replay, future MCP) gets identical protection.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveTenantForGitHub(db, args) {
|
|
26
|
+
const envFallback = () => process.env.HIPPO_TENANT?.trim() || 'default';
|
|
27
|
+
const escapeHatch = process.env.GITHUB_ALLOW_UNKNOWN_INSTALLATION_FALLBACK === '1';
|
|
28
|
+
const instCount = db
|
|
29
|
+
.prepare(`SELECT COUNT(*) AS c FROM github_installations`)
|
|
30
|
+
.get().c;
|
|
31
|
+
const repoCount = db
|
|
32
|
+
.prepare(`SELECT COUNT(*) AS c FROM github_repositories`)
|
|
33
|
+
.get().c;
|
|
34
|
+
if (args.installationId) {
|
|
35
|
+
const row = db
|
|
36
|
+
.prepare(`SELECT tenant_id FROM github_installations WHERE installation_id = ?`)
|
|
37
|
+
.get(args.installationId);
|
|
38
|
+
if (row?.tenant_id)
|
|
39
|
+
return row.tenant_id;
|
|
40
|
+
if (Number(instCount) === 0) {
|
|
41
|
+
// Single-tenant install (table empty); env fallback is safe.
|
|
42
|
+
return envFallback();
|
|
43
|
+
}
|
|
44
|
+
// Multi-tenant install with unknown installation_id — fail closed.
|
|
45
|
+
return escapeHatch ? envFallback() : null;
|
|
46
|
+
}
|
|
47
|
+
// No installation.id (PAT-mode webhook).
|
|
48
|
+
if (Number(instCount) === 0 && Number(repoCount) === 0) {
|
|
49
|
+
// Single-tenant deployment with no routing tables populated.
|
|
50
|
+
return envFallback();
|
|
51
|
+
}
|
|
52
|
+
if (args.repoFullName) {
|
|
53
|
+
const row = db
|
|
54
|
+
.prepare(`SELECT tenant_id FROM github_repositories WHERE repo_full_name = ? ORDER BY added_at, tenant_id LIMIT 1`)
|
|
55
|
+
.get(args.repoFullName);
|
|
56
|
+
if (row?.tenant_id)
|
|
57
|
+
return row.tenant_id;
|
|
58
|
+
}
|
|
59
|
+
return escapeHatch ? envFallback() : null;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=tenant-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-routing.js","sourceRoot":"","sources":["../../../src/connectors/github/tenant-routing.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,sBAAsB,CACpC,EAAoB,EACpB,IAAiB;IAEjB,MAAM,WAAW,GAAG,GAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IAChF,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,0CAA0C,KAAK,GAAG,CAAC;IAEnF,MAAM,SAAS,GAAI,EAAE;SAClB,OAAO,CAAC,gDAAgD,CAAC;SACzD,GAAG,EAA6B,CAAC,CAAC,CAAC;IACtC,MAAM,SAAS,GAAI,EAAE;SAClB,OAAO,CAAC,+CAA+C,CAAC;SACxD,GAAG,EAA6B,CAAC,CAAC,CAAC;IAEtC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CAAC,sEAAsE,CAAC;aAC/E,GAAG,CAAC,IAAI,CAAC,cAAc,CAAuC,CAAC;QAClE,IAAI,GAAG,EAAE,SAAS;YAAE,OAAO,GAAG,CAAC,SAAS,CAAC;QACzC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,6DAA6D;YAC7D,OAAO,WAAW,EAAE,CAAC;QACvB,CAAC;QACD,mEAAmE;QACnE,OAAO,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5C,CAAC;IAED,yCAAyC;IACzC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,6DAA6D;QAC7D,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,EAAE;aACX,OAAO,CACN,yGAAyG,CAC1G;aACA,GAAG,CAAC,IAAI,CAAC,YAAY,CAAuC,CAAC;QAChE,IAAI,GAAG,EAAE,SAAS;YAAE,OAAO,GAAG,CAAC,SAAS,CAAC;IAC3C,CAAC;IACD,OAAO,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RememberOpts } from '../../api.js';
|
|
2
|
+
import type { GitHubIssueEvent, GitHubIssueCommentEvent, GitHubPullRequestEvent, GitHubPullRequestReviewCommentEvent } from './types.js';
|
|
3
|
+
export declare function issueEventToRememberOpts(evt: GitHubIssueEvent): RememberOpts | null;
|
|
4
|
+
export declare function issueCommentEventToRememberOpts(evt: GitHubIssueCommentEvent): RememberOpts | null;
|
|
5
|
+
export declare function pullRequestEventToRememberOpts(evt: GitHubPullRequestEvent): RememberOpts | null;
|
|
6
|
+
export declare function prReviewCommentEventToRememberOpts(evt: GitHubPullRequestReviewCommentEvent): RememberOpts | null;
|
|
7
|
+
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,KAAK,EACV,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,mCAAmC,EACpC,MAAM,YAAY,CAAC;AAqBpB,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,gBAAgB,GACpB,YAAY,GAAG,IAAI,CAoBrB;AAED,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,uBAAuB,GAC3B,YAAY,GAAG,IAAI,CAkBrB;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,sBAAsB,GAC1B,YAAY,GAAG,IAAI,CAoBrB;AAED,wBAAgB,kCAAkC,CAChD,GAAG,EAAE,mCAAmC,GACvC,YAAY,GAAG,IAAI,CAkBrB"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { scopeFromRepository } from './scope.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert GitHub webhook events into RememberOpts for api.remember(). Each
|
|
4
|
+
* function returns null when the event has no usable body so the webhook
|
|
5
|
+
* caller can mark the delivery seen for idempotency and skip the insert.
|
|
6
|
+
*
|
|
7
|
+
* Contract (mirrors src/connectors/slack/transform.ts):
|
|
8
|
+
* - kind is the literal 'raw' (E1.x connector boundary, see src/importers.ts).
|
|
9
|
+
* - artifact_ref formats are stable; deletion paths look up by these strings.
|
|
10
|
+
* - issue: github://<owner/repo>/issue/<number>
|
|
11
|
+
* - issue_comment: github://<owner/repo>/issue/<number>/comment/<id>
|
|
12
|
+
* - pull_request: github://<owner/repo>/pull/<number>
|
|
13
|
+
* - pr_review_comment: github://<owner/repo>/pull/<number>/review_comment/<id>
|
|
14
|
+
* - owner is `user:github:<login>`. Required by the v0.40.0 provenance gate.
|
|
15
|
+
* - scope is derived from repository.private via scopeFromRepository (default
|
|
16
|
+
* private when undetermined).
|
|
17
|
+
*/
|
|
18
|
+
const UNKNOWN_REPO = 'unknown/unknown';
|
|
19
|
+
export function issueEventToRememberOpts(evt) {
|
|
20
|
+
const body = evt.issue.body?.trim();
|
|
21
|
+
const title = evt.issue.title?.trim();
|
|
22
|
+
const text = [title, body].filter(Boolean).join('\n\n');
|
|
23
|
+
if (!text)
|
|
24
|
+
return null;
|
|
25
|
+
const repoFull = evt.repository?.full_name ?? UNKNOWN_REPO;
|
|
26
|
+
const login = evt.issue.user.login;
|
|
27
|
+
return {
|
|
28
|
+
content: text,
|
|
29
|
+
kind: 'raw',
|
|
30
|
+
scope: scopeFromRepository(evt.repository),
|
|
31
|
+
artifactRef: `github://${repoFull}/issue/${evt.issue.number}`,
|
|
32
|
+
owner: `user:github:${login}`,
|
|
33
|
+
tags: [
|
|
34
|
+
'source:github',
|
|
35
|
+
`repo:${repoFull}`,
|
|
36
|
+
`event:issues.${evt.action}`,
|
|
37
|
+
`user:github:${login}`,
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function issueCommentEventToRememberOpts(evt) {
|
|
42
|
+
const text = evt.comment.body?.trim();
|
|
43
|
+
if (!text)
|
|
44
|
+
return null;
|
|
45
|
+
const repoFull = evt.repository?.full_name ?? UNKNOWN_REPO;
|
|
46
|
+
const login = evt.comment.user.login;
|
|
47
|
+
return {
|
|
48
|
+
content: text,
|
|
49
|
+
kind: 'raw',
|
|
50
|
+
scope: scopeFromRepository(evt.repository),
|
|
51
|
+
artifactRef: `github://${repoFull}/issue/${evt.issue.number}/comment/${evt.comment.id}`,
|
|
52
|
+
owner: `user:github:${login}`,
|
|
53
|
+
tags: [
|
|
54
|
+
'source:github',
|
|
55
|
+
`repo:${repoFull}`,
|
|
56
|
+
`event:issue_comment.${evt.action}`,
|
|
57
|
+
`user:github:${login}`,
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function pullRequestEventToRememberOpts(evt) {
|
|
62
|
+
const body = evt.pull_request.body?.trim();
|
|
63
|
+
const title = evt.pull_request.title?.trim();
|
|
64
|
+
const text = [title, body].filter(Boolean).join('\n\n');
|
|
65
|
+
if (!text)
|
|
66
|
+
return null;
|
|
67
|
+
const repoFull = evt.repository?.full_name ?? UNKNOWN_REPO;
|
|
68
|
+
const login = evt.pull_request.user.login;
|
|
69
|
+
return {
|
|
70
|
+
content: text,
|
|
71
|
+
kind: 'raw',
|
|
72
|
+
scope: scopeFromRepository(evt.repository),
|
|
73
|
+
artifactRef: `github://${repoFull}/pull/${evt.pull_request.number}`,
|
|
74
|
+
owner: `user:github:${login}`,
|
|
75
|
+
tags: [
|
|
76
|
+
'source:github',
|
|
77
|
+
`repo:${repoFull}`,
|
|
78
|
+
`event:pull_request.${evt.action}`,
|
|
79
|
+
`user:github:${login}`,
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function prReviewCommentEventToRememberOpts(evt) {
|
|
84
|
+
const text = evt.comment.body?.trim();
|
|
85
|
+
if (!text)
|
|
86
|
+
return null;
|
|
87
|
+
const repoFull = evt.repository?.full_name ?? UNKNOWN_REPO;
|
|
88
|
+
const login = evt.comment.user.login;
|
|
89
|
+
return {
|
|
90
|
+
content: text,
|
|
91
|
+
kind: 'raw',
|
|
92
|
+
scope: scopeFromRepository(evt.repository),
|
|
93
|
+
artifactRef: `github://${repoFull}/pull/${evt.pull_request.number}/review_comment/${evt.comment.id}`,
|
|
94
|
+
owner: `user:github:${login}`,
|
|
95
|
+
tags: [
|
|
96
|
+
'source:github',
|
|
97
|
+
`repo:${repoFull}`,
|
|
98
|
+
`event:pull_request_review_comment.${evt.action}`,
|
|
99
|
+
`user:github:${login}`,
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/connectors/github/transform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub webhook event shapes used by the ingestion connector.
|
|
3
|
+
* Spec: https://docs.github.com/en/webhooks/webhook-events-and-payloads
|
|
4
|
+
*
|
|
5
|
+
* V1 cares about four event types: issues, issue_comment, pull_request,
|
|
6
|
+
* pull_request_review_comment. Each guard is gated on the X-GitHub-Event
|
|
7
|
+
* header value so a malicious or misrouted payload cannot satisfy a guard
|
|
8
|
+
* for the wrong event type.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Codex P1 #7: `private` MUST be optional, not required. The Slack-style
|
|
12
|
+
* fail-safe in scope.ts requires an envelope with `private: undefined` to
|
|
13
|
+
* map to private; a strict boolean type would reject the payload before
|
|
14
|
+
* scope can fail closed.
|
|
15
|
+
*/
|
|
16
|
+
export interface GitHubRepository {
|
|
17
|
+
full_name: string;
|
|
18
|
+
private?: boolean;
|
|
19
|
+
owner: {
|
|
20
|
+
login: string;
|
|
21
|
+
};
|
|
22
|
+
name: string;
|
|
23
|
+
id?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface GitHubSender {
|
|
26
|
+
login: string;
|
|
27
|
+
id: number;
|
|
28
|
+
}
|
|
29
|
+
export interface GitHubInstallation {
|
|
30
|
+
id: number;
|
|
31
|
+
}
|
|
32
|
+
export interface GitHubWebhookEnvelope {
|
|
33
|
+
action?: string;
|
|
34
|
+
repository?: GitHubRepository;
|
|
35
|
+
sender?: GitHubSender;
|
|
36
|
+
installation?: GitHubInstallation;
|
|
37
|
+
}
|
|
38
|
+
export interface GitHubIssueEvent extends GitHubWebhookEnvelope {
|
|
39
|
+
action: 'opened' | 'edited' | 'closed' | 'reopened' | 'deleted';
|
|
40
|
+
issue: {
|
|
41
|
+
number: number;
|
|
42
|
+
title: string;
|
|
43
|
+
body: string | null;
|
|
44
|
+
user: GitHubSender;
|
|
45
|
+
updated_at?: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface GitHubIssueCommentEvent extends GitHubWebhookEnvelope {
|
|
49
|
+
action: 'created' | 'edited' | 'deleted';
|
|
50
|
+
issue: {
|
|
51
|
+
number: number;
|
|
52
|
+
};
|
|
53
|
+
comment: {
|
|
54
|
+
id: number;
|
|
55
|
+
body: string | null;
|
|
56
|
+
user: GitHubSender;
|
|
57
|
+
updated_at?: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface GitHubPullRequestEvent extends GitHubWebhookEnvelope {
|
|
61
|
+
action: 'opened' | 'edited' | 'closed' | 'reopened' | 'synchronize' | 'ready_for_review';
|
|
62
|
+
pull_request: {
|
|
63
|
+
number: number;
|
|
64
|
+
title: string;
|
|
65
|
+
body: string | null;
|
|
66
|
+
user: GitHubSender;
|
|
67
|
+
updated_at?: string;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export interface GitHubPullRequestReviewCommentEvent extends GitHubWebhookEnvelope {
|
|
71
|
+
action: 'created' | 'edited' | 'deleted';
|
|
72
|
+
pull_request: {
|
|
73
|
+
number: number;
|
|
74
|
+
};
|
|
75
|
+
comment: {
|
|
76
|
+
id: number;
|
|
77
|
+
body: string | null;
|
|
78
|
+
user: GitHubSender;
|
|
79
|
+
updated_at?: string;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export declare function isGitHubWebhookEnvelope(x: unknown): x is GitHubWebhookEnvelope;
|
|
83
|
+
export declare function isGitHubIssueEvent(x: unknown, evtHeader: string): x is GitHubIssueEvent;
|
|
84
|
+
export declare function isGitHubIssueCommentEvent(x: unknown, evtHeader: string): x is GitHubIssueCommentEvent;
|
|
85
|
+
export declare function isGitHubPullRequestEvent(x: unknown, evtHeader: string): x is GitHubPullRequestEvent;
|
|
86
|
+
export declare function isGitHubPullRequestReviewCommentEvent(x: unknown, evtHeader: string): x is GitHubPullRequestReviewCommentEvent;
|
|
87
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub webhook event shapes used by the ingestion connector.
|
|
3
|
+
* Spec: https://docs.github.com/en/webhooks/webhook-events-and-payloads
|
|
4
|
+
*
|
|
5
|
+
* V1 cares about four event types: issues, issue_comment, pull_request,
|
|
6
|
+
* pull_request_review_comment. Each guard is gated on the X-GitHub-Event
|
|
7
|
+
* header value so a malicious or misrouted payload cannot satisfy a guard
|
|
8
|
+
* for the wrong event type.
|
|
9
|
+
*/
|
|
10
|
+
function isObject(x) {
|
|
11
|
+
return !!x && typeof x === 'object';
|
|
12
|
+
}
|
|
13
|
+
function hasSenderShape(x) {
|
|
14
|
+
if (!isObject(x))
|
|
15
|
+
return false;
|
|
16
|
+
return typeof x.login === 'string' && typeof x.id === 'number';
|
|
17
|
+
}
|
|
18
|
+
export function isGitHubWebhookEnvelope(x) {
|
|
19
|
+
if (!isObject(x))
|
|
20
|
+
return false;
|
|
21
|
+
// Duck-type: an envelope must at least carry an action OR a repository field.
|
|
22
|
+
return typeof x.action === 'string' || isObject(x.repository);
|
|
23
|
+
}
|
|
24
|
+
export function isGitHubIssueEvent(x, evtHeader) {
|
|
25
|
+
if (evtHeader !== 'issues')
|
|
26
|
+
return false;
|
|
27
|
+
if (!isObject(x))
|
|
28
|
+
return false;
|
|
29
|
+
if (typeof x.action !== 'string')
|
|
30
|
+
return false;
|
|
31
|
+
const issue = x.issue;
|
|
32
|
+
if (!isObject(issue))
|
|
33
|
+
return false;
|
|
34
|
+
if (typeof issue.number !== 'number')
|
|
35
|
+
return false;
|
|
36
|
+
if (!hasSenderShape(issue.user))
|
|
37
|
+
return false;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
export function isGitHubIssueCommentEvent(x, evtHeader) {
|
|
41
|
+
if (evtHeader !== 'issue_comment')
|
|
42
|
+
return false;
|
|
43
|
+
if (!isObject(x))
|
|
44
|
+
return false;
|
|
45
|
+
if (typeof x.action !== 'string')
|
|
46
|
+
return false;
|
|
47
|
+
const issue = x.issue;
|
|
48
|
+
if (!isObject(issue) || typeof issue.number !== 'number')
|
|
49
|
+
return false;
|
|
50
|
+
const comment = x.comment;
|
|
51
|
+
if (!isObject(comment))
|
|
52
|
+
return false;
|
|
53
|
+
if (typeof comment.id !== 'number')
|
|
54
|
+
return false;
|
|
55
|
+
if (!hasSenderShape(comment.user))
|
|
56
|
+
return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
export function isGitHubPullRequestEvent(x, evtHeader) {
|
|
60
|
+
if (evtHeader !== 'pull_request')
|
|
61
|
+
return false;
|
|
62
|
+
if (!isObject(x))
|
|
63
|
+
return false;
|
|
64
|
+
if (typeof x.action !== 'string')
|
|
65
|
+
return false;
|
|
66
|
+
const pr = x.pull_request;
|
|
67
|
+
if (!isObject(pr))
|
|
68
|
+
return false;
|
|
69
|
+
if (typeof pr.number !== 'number')
|
|
70
|
+
return false;
|
|
71
|
+
if (!hasSenderShape(pr.user))
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
export function isGitHubPullRequestReviewCommentEvent(x, evtHeader) {
|
|
76
|
+
if (evtHeader !== 'pull_request_review_comment')
|
|
77
|
+
return false;
|
|
78
|
+
if (!isObject(x))
|
|
79
|
+
return false;
|
|
80
|
+
if (typeof x.action !== 'string')
|
|
81
|
+
return false;
|
|
82
|
+
const pr = x.pull_request;
|
|
83
|
+
if (!isObject(pr) || typeof pr.number !== 'number')
|
|
84
|
+
return false;
|
|
85
|
+
const comment = x.comment;
|
|
86
|
+
if (!isObject(comment))
|
|
87
|
+
return false;
|
|
88
|
+
if (typeof comment.id !== 'number')
|
|
89
|
+
return false;
|
|
90
|
+
if (!hasSenderShape(comment.user))
|
|
91
|
+
return false;
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/connectors/github/types.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAQA,UAAU,iBAAiB;IACzB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG;QAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACnC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACxC,KAAK,IAAI,IAAI,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAQA,UAAU,iBAAiB;IACzB,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG;QAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACnC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACxC,KAAK,IAAI,IAAI,CAAC;CACf;AA4yBD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CA2B/D;AAkCD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,gBAAgB,GAAG,MAAM,CAI7D;AAwDD,wBAAgB,YAAY,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAEvD;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM,CAGhF;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,gBAAgB,GAAG,OAAO,CAE5D;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,gBAAgB,EAAE,IAAI,SAAK,GAAG,IAAI,CAS5E"}
|