hippo-memory 1.2.1 → 1.3.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/README.md +17 -0
- package/dist/cli.js +10 -0
- 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 +272 -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 +192 -0
- package/dist/connectors/github/cli-impl.js.map +1 -0
- package/dist/connectors/github/deletion.d.ts +43 -0
- package/dist/connectors/github/deletion.d.ts.map +1 -0
- package/dist/connectors/github/deletion.js +83 -0
- package/dist/connectors/github/deletion.js.map +1 -0
- package/dist/connectors/github/dlq.d.ts +108 -0
- package/dist/connectors/github/dlq.d.ts.map +1 -0
- package/dist/connectors/github/dlq.js +182 -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 +138 -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 +47 -0
- package/dist/connectors/github/signature.d.ts.map +1 -0
- package/dist/connectors/github/signature.js +58 -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 +94 -1
- package/dist/db.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +5 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +313 -2
- package/dist/server.js.map +1 -1
- package/dist/src/cli.js +10 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connectors/github/backfill.js +272 -0
- package/dist/src/connectors/github/backfill.js.map +1 -0
- package/dist/src/connectors/github/cli-impl.js +192 -0
- package/dist/src/connectors/github/cli-impl.js.map +1 -0
- package/dist/src/connectors/github/deletion.js +83 -0
- package/dist/src/connectors/github/deletion.js.map +1 -0
- package/dist/src/connectors/github/dlq.js +182 -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 +138 -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 +58 -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 +94 -1
- package/dist/src/db.js.map +1 -1
- package/dist/src/mcp/server.js +5 -4
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/server.js +313 -2
- package/dist/src/server.js.map +1 -1
- package/dist/src/version.js +35 -0
- package/dist/src/version.js.map +1 -0
- package/dist/version.d.ts +25 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +35 -0
- package/dist/version.js.map +1 -0
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../../src/connectors/github/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Octokit-shaped HTTP fetcher for the GitHub connector backfill.
|
|
3
|
+
*
|
|
4
|
+
* Production uses `realGitHubFetcher` against `https://api.github.com`.
|
|
5
|
+
* Tests inject a fake `GitHubFetcher` so they never hit the network.
|
|
6
|
+
*
|
|
7
|
+
* Codex P1 #4 mandate: any non-200 response that is NOT a recognized
|
|
8
|
+
* rate-limit pause MUST throw `GitHubFetchError`. Silently turning
|
|
9
|
+
* 401/403/404/500 into empty pages produced empty backfills with no
|
|
10
|
+
* operator signal, so this code path is now load-bearing.
|
|
11
|
+
*/
|
|
12
|
+
import { type RateLimitInfo } from './ratelimit.js';
|
|
13
|
+
export declare class GitHubFetchError extends Error {
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly bodyExcerpt: string;
|
|
16
|
+
readonly url: string;
|
|
17
|
+
constructor(status: number, bodyExcerpt: string, url: string);
|
|
18
|
+
}
|
|
19
|
+
export interface GitHubBackfillPage {
|
|
20
|
+
readonly items: ReadonlyArray<unknown>;
|
|
21
|
+
readonly next: string | null;
|
|
22
|
+
readonly rateLimit: RateLimitInfo;
|
|
23
|
+
}
|
|
24
|
+
export type GitHubFetcher = (args: {
|
|
25
|
+
url: string;
|
|
26
|
+
token: string;
|
|
27
|
+
}) => Promise<GitHubBackfillPage>;
|
|
28
|
+
/**
|
|
29
|
+
* Parse the rel="next" URL from an RFC 5988 `Link` header.
|
|
30
|
+
*
|
|
31
|
+
* Header format: `<url1>; rel="next", <url2>; rel="last"`.
|
|
32
|
+
* Returns the URL whose rel parameter is exactly `"next"`, or null.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseNextLink(linkHeader: string): string | null;
|
|
35
|
+
export declare const realGitHubFetcher: GitHubFetcher;
|
|
36
|
+
//# sourceMappingURL=octokit-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"octokit-client.d.ts","sourceRoot":"","sources":["../../../src/connectors/github/octokit-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,qBAAa,gBAAiB,SAAQ,KAAK;IAEvC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAFX,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM;CAKvB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;CACnC;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAElC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAUD,eAAO,MAAM,iBAAiB,EAAE,aAsB/B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Octokit-shaped HTTP fetcher for the GitHub connector backfill.
|
|
3
|
+
*
|
|
4
|
+
* Production uses `realGitHubFetcher` against `https://api.github.com`.
|
|
5
|
+
* Tests inject a fake `GitHubFetcher` so they never hit the network.
|
|
6
|
+
*
|
|
7
|
+
* Codex P1 #4 mandate: any non-200 response that is NOT a recognized
|
|
8
|
+
* rate-limit pause MUST throw `GitHubFetchError`. Silently turning
|
|
9
|
+
* 401/403/404/500 into empty pages produced empty backfills with no
|
|
10
|
+
* operator signal, so this code path is now load-bearing.
|
|
11
|
+
*/
|
|
12
|
+
import { parseRateLimit } from './ratelimit.js';
|
|
13
|
+
export class GitHubFetchError extends Error {
|
|
14
|
+
status;
|
|
15
|
+
bodyExcerpt;
|
|
16
|
+
url;
|
|
17
|
+
constructor(status, bodyExcerpt, url) {
|
|
18
|
+
super(`GitHub ${status} on ${url}: ${bodyExcerpt}`);
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.bodyExcerpt = bodyExcerpt;
|
|
21
|
+
this.url = url;
|
|
22
|
+
this.name = 'GitHubFetchError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse the rel="next" URL from an RFC 5988 `Link` header.
|
|
27
|
+
*
|
|
28
|
+
* Header format: `<url1>; rel="next", <url2>; rel="last"`.
|
|
29
|
+
* Returns the URL whose rel parameter is exactly `"next"`, or null.
|
|
30
|
+
*/
|
|
31
|
+
export function parseNextLink(linkHeader) {
|
|
32
|
+
if (!linkHeader)
|
|
33
|
+
return null;
|
|
34
|
+
const re = /<([^>]+)>\s*;\s*rel="next"/;
|
|
35
|
+
const m = linkHeader.match(re);
|
|
36
|
+
return m ? m[1] : null;
|
|
37
|
+
}
|
|
38
|
+
function headersToRecord(h) {
|
|
39
|
+
const out = {};
|
|
40
|
+
h.forEach((v, k) => {
|
|
41
|
+
out[k.toLowerCase()] = v;
|
|
42
|
+
});
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
export const realGitHubFetcher = async ({ url, token }) => {
|
|
46
|
+
const res = await fetch(url, {
|
|
47
|
+
headers: {
|
|
48
|
+
Authorization: `Bearer ${token}`,
|
|
49
|
+
Accept: 'application/vnd.github+json',
|
|
50
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const headers = headersToRecord(res.headers);
|
|
54
|
+
const rateLimit = parseRateLimit(headers, res.status);
|
|
55
|
+
// Codex P1 #4: don't silently turn 401/403/404/500 into empty pages.
|
|
56
|
+
if (res.status !== 200 && rateLimit.reason === 'none') {
|
|
57
|
+
const body = await res.text().catch(() => '');
|
|
58
|
+
throw new GitHubFetchError(res.status, body.slice(0, 256), url);
|
|
59
|
+
}
|
|
60
|
+
const items = res.status === 200 ? (await res.json()) : [];
|
|
61
|
+
const link = res.headers.get('link') ?? '';
|
|
62
|
+
const next = parseNextLink(link);
|
|
63
|
+
return { items, next, rateLimit };
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=octokit-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"octokit-client.js","sourceRoot":"","sources":["../../../src/connectors/github/octokit-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AAEpE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAE9B;IACA;IACA;IAHX,YACW,MAAc,EACd,WAAmB,EACnB,GAAW;QAEpB,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,KAAK,WAAW,EAAE,CAAC,CAAC;QAJ3C,WAAM,GAAN,MAAM,CAAQ;QACd,gBAAW,GAAX,WAAW,CAAQ;QACnB,QAAG,GAAH,GAAG,CAAQ;QAGpB,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAaD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,EAAE,GAAG,4BAA4B,CAAC;IACxC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/B,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAED,SAAS,eAAe,CAAC,CAAU;IACjC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjB,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAkB,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;IACvE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,MAAM,EAAE,6BAA6B;YACrC,sBAAsB,EAAE,YAAY;SACrC;KACF,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAEtD,qEAAqE;IACrE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,KAAK,GACT,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAE,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC,CAAC"}
|
|
@@ -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,47 @@
|
|
|
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
|
+
* Source-aware idempotency key. v1.3.1 hotfix (codex round 1 P0 #3 + claude
|
|
12
|
+
* round 2 P0 #3).
|
|
13
|
+
*
|
|
14
|
+
* Round 1 design: sha256(eventName + ':' + rawBody) so an attacker rotating
|
|
15
|
+
* X-GitHub-Delivery cannot bypass dedupe.
|
|
16
|
+
*
|
|
17
|
+
* Round 2 found that key produced different hashes for the SAME source event
|
|
18
|
+
* delivered via webhook vs via REST backfill, because backfill rawBody is the
|
|
19
|
+
* REST list-item shape while webhook rawBody is the envelope. Result:
|
|
20
|
+
* backfill + later webhook of the same issue created two `kind='raw'` rows
|
|
21
|
+
* with the same artifact_ref. Combined with the deletion bug, deletion could
|
|
22
|
+
* not archive both.
|
|
23
|
+
*
|
|
24
|
+
* v1.3.1 fix: key from the SOURCE-NORMALIZED identifier — artifact_ref plus
|
|
25
|
+
* the source-side updated_at timestamp. Same artifact + same revision = same
|
|
26
|
+
* key, regardless of which path delivered it. Different revisions of the same
|
|
27
|
+
* issue (an edit) get different keys, which is correct: each edit IS a new
|
|
28
|
+
* memory revision.
|
|
29
|
+
*
|
|
30
|
+
* Both inputs are upstream-derived from the parsed event, not from the
|
|
31
|
+
* unsigned delivery header — replay attacks still cannot bypass dedupe.
|
|
32
|
+
*
|
|
33
|
+
* Inputs:
|
|
34
|
+
* - artifactRef: e.g. 'github://acme/repo/issue/42' or
|
|
35
|
+
* 'github://acme/repo/issue/42/comment/123'.
|
|
36
|
+
* - updatedAt: source-side ISO timestamp (issue.updated_at,
|
|
37
|
+
* comment.updated_at, pull_request.updated_at). Empty string when the
|
|
38
|
+
* payload omits it (rare; older REST shapes).
|
|
39
|
+
*
|
|
40
|
+
* Migration note for v1.3.0 → v1.3.1: existing github_event_log rows from
|
|
41
|
+
* v1.3.0 used the round-1 key shape and will not collide with v1.3.1 keys.
|
|
42
|
+
* The first webhook delivery after upgrading creates a new log row with the
|
|
43
|
+
* new key. This is acceptable for a hotfix (no production users on v1.3.0)
|
|
44
|
+
* and correct semantics going forward.
|
|
45
|
+
*/
|
|
46
|
+
export declare function computeIdempotencyKey(artifactRef: string, updatedAt: string | null | undefined): string;
|
|
47
|
+
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEvG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
* Source-aware idempotency key. v1.3.1 hotfix (codex round 1 P0 #3 + claude
|
|
21
|
+
* round 2 P0 #3).
|
|
22
|
+
*
|
|
23
|
+
* Round 1 design: sha256(eventName + ':' + rawBody) so an attacker rotating
|
|
24
|
+
* X-GitHub-Delivery cannot bypass dedupe.
|
|
25
|
+
*
|
|
26
|
+
* Round 2 found that key produced different hashes for the SAME source event
|
|
27
|
+
* delivered via webhook vs via REST backfill, because backfill rawBody is the
|
|
28
|
+
* REST list-item shape while webhook rawBody is the envelope. Result:
|
|
29
|
+
* backfill + later webhook of the same issue created two `kind='raw'` rows
|
|
30
|
+
* with the same artifact_ref. Combined with the deletion bug, deletion could
|
|
31
|
+
* not archive both.
|
|
32
|
+
*
|
|
33
|
+
* v1.3.1 fix: key from the SOURCE-NORMALIZED identifier — artifact_ref plus
|
|
34
|
+
* the source-side updated_at timestamp. Same artifact + same revision = same
|
|
35
|
+
* key, regardless of which path delivered it. Different revisions of the same
|
|
36
|
+
* issue (an edit) get different keys, which is correct: each edit IS a new
|
|
37
|
+
* memory revision.
|
|
38
|
+
*
|
|
39
|
+
* Both inputs are upstream-derived from the parsed event, not from the
|
|
40
|
+
* unsigned delivery header — replay attacks still cannot bypass dedupe.
|
|
41
|
+
*
|
|
42
|
+
* Inputs:
|
|
43
|
+
* - artifactRef: e.g. 'github://acme/repo/issue/42' or
|
|
44
|
+
* 'github://acme/repo/issue/42/comment/123'.
|
|
45
|
+
* - updatedAt: source-side ISO timestamp (issue.updated_at,
|
|
46
|
+
* comment.updated_at, pull_request.updated_at). Empty string when the
|
|
47
|
+
* payload omits it (rare; older REST shapes).
|
|
48
|
+
*
|
|
49
|
+
* Migration note for v1.3.0 → v1.3.1: existing github_event_log rows from
|
|
50
|
+
* v1.3.0 used the round-1 key shape and will not collide with v1.3.1 keys.
|
|
51
|
+
* The first webhook delivery after upgrading creates a new log row with the
|
|
52
|
+
* new key. This is acceptable for a hotfix (no production users on v1.3.0)
|
|
53
|
+
* and correct semantics going forward.
|
|
54
|
+
*/
|
|
55
|
+
export function computeIdempotencyKey(artifactRef, updatedAt) {
|
|
56
|
+
return createHash('sha256').update(`${artifactRef}:${updatedAt ?? ''}`).digest('hex');
|
|
57
|
+
}
|
|
58
|
+
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,SAAoC;IAC7F,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,WAAW,IAAI,SAAS,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACxF,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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"}
|