@mastra/github-signals 0.0.0-knowledge-org-scope-20260902233451 → 0.0.0-knowledge-w2-20261007195847
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/dist/github-app-owner.d.ts +13 -0
- package/dist/github-app-owner.d.ts.map +1 -0
- package/dist/github-auth-env.d.ts +19 -0
- package/dist/github-auth-env.d.ts.map +1 -0
- package/dist/index.cjs +133 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134 -34
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type GithubAppOwner = {
|
|
2
|
+
login: string;
|
|
3
|
+
type: 'User' | 'Organization';
|
|
4
|
+
};
|
|
5
|
+
export type GithubAppOwnerCommandRunner = (args: readonly string[]) => Promise<{
|
|
6
|
+
stdout: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare class GithubAppOwnerResolver {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(runGhApi?: GithubAppOwnerCommandRunner);
|
|
11
|
+
getOwner(botLogin: string, isCurrentGeneration?: () => boolean): Promise<GithubAppOwner | undefined>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=github-app-owner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-app-owner.d.ts","sourceRoot":"","sources":["../src/github-app-owner.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAyBnG,qBAAa,sBAAsB;;IAIjC,YAAY,QAAQ,GAAE,2BAA6C,EAElE;IAEK,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CA2BzG;CAWF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a GitHub credential from the global `gh` CLI and return an environment
|
|
3
|
+
* that presents it to a child process.
|
|
4
|
+
*
|
|
5
|
+
* gitcrawl takes the first non-empty value of the environment variable named by
|
|
6
|
+
* `[github].token_env` (default `GITHUB_TOKEN`) and only discovers it is invalid
|
|
7
|
+
* when GitHub rejects it, so a stale exported token fails `sync` outright even
|
|
8
|
+
* when `gh` can still mint a working credential. Injecting a credential here
|
|
9
|
+
* makes gitcrawl's own env lookup win, so the stale value is never consulted.
|
|
10
|
+
*
|
|
11
|
+
* `gh auth token` is asked with the token variables removed: `gh` answers with
|
|
12
|
+
* `GH_TOKEN`/`GITHUB_TOKEN` verbatim when either is set, which would hand back
|
|
13
|
+
* the very credential being replaced.
|
|
14
|
+
*
|
|
15
|
+
* Returns `undefined` when no credential can be resolved, so callers leave the
|
|
16
|
+
* inherited environment untouched rather than stripping a working token.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveGithubAuthEnv(): Promise<NodeJS.ProcessEnv | undefined>;
|
|
19
|
+
//# sourceMappingURL=github-auth-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-auth-env.d.ts","sourceRoot":"","sources":["../src/github-auth-env.ts"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,GAAG,SAAS,CAAC,CAqBnF"}
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,111 @@ let _mastra_core_signals = require("@mastra/core/signals");
|
|
|
30
30
|
let _mastra_core_tools = require("@mastra/core/tools");
|
|
31
31
|
let zod = require("zod");
|
|
32
32
|
zod = __toESM(zod, 1);
|
|
33
|
+
//#region src/github-auth-env.ts
|
|
34
|
+
let execFileAsync$2;
|
|
35
|
+
/** Environment variables both gitcrawl and the `gh` CLI treat as a GitHub credential. */
|
|
36
|
+
const GITHUB_TOKEN_ENV_VARS = ["GH_TOKEN", "GITHUB_TOKEN"];
|
|
37
|
+
/** Remove every casing of the token variables: Windows treats environment names case-insensitively. */
|
|
38
|
+
function withoutGithubTokens(env) {
|
|
39
|
+
const scrubbed = { ...env };
|
|
40
|
+
for (const name of Object.keys(scrubbed)) if (GITHUB_TOKEN_ENV_VARS.some((tokenEnv) => tokenEnv.toLowerCase() === name.toLowerCase())) delete scrubbed[name];
|
|
41
|
+
return scrubbed;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a GitHub credential from the global `gh` CLI and return an environment
|
|
45
|
+
* that presents it to a child process.
|
|
46
|
+
*
|
|
47
|
+
* gitcrawl takes the first non-empty value of the environment variable named by
|
|
48
|
+
* `[github].token_env` (default `GITHUB_TOKEN`) and only discovers it is invalid
|
|
49
|
+
* when GitHub rejects it, so a stale exported token fails `sync` outright even
|
|
50
|
+
* when `gh` can still mint a working credential. Injecting a credential here
|
|
51
|
+
* makes gitcrawl's own env lookup win, so the stale value is never consulted.
|
|
52
|
+
*
|
|
53
|
+
* `gh auth token` is asked with the token variables removed: `gh` answers with
|
|
54
|
+
* `GH_TOKEN`/`GITHUB_TOKEN` verbatim when either is set, which would hand back
|
|
55
|
+
* the very credential being replaced.
|
|
56
|
+
*
|
|
57
|
+
* Returns `undefined` when no credential can be resolved, so callers leave the
|
|
58
|
+
* inherited environment untouched rather than stripping a working token.
|
|
59
|
+
*/
|
|
60
|
+
async function resolveGithubAuthEnv() {
|
|
61
|
+
const scrubbed = withoutGithubTokens(process.env);
|
|
62
|
+
if (!execFileAsync$2) {
|
|
63
|
+
const { execFile } = await import("child_process");
|
|
64
|
+
execFileAsync$2 = (0, util.promisify)(execFile);
|
|
65
|
+
}
|
|
66
|
+
let token;
|
|
67
|
+
try {
|
|
68
|
+
const { stdout } = await execFileAsync$2("gh", ["auth", "token"], { env: scrubbed });
|
|
69
|
+
token = stdout.trim();
|
|
70
|
+
} catch {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!token) return void 0;
|
|
74
|
+
return {
|
|
75
|
+
...process.env,
|
|
76
|
+
GH_TOKEN: token,
|
|
77
|
+
GITHUB_TOKEN: token
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/github-app-owner.ts
|
|
82
|
+
const OWNER_CACHE_TTL_MS = 1440 * 60 * 1e3;
|
|
83
|
+
let execFileAsync$1;
|
|
84
|
+
const defaultRunGhApi = async (args) => {
|
|
85
|
+
if (!execFileAsync$1) {
|
|
86
|
+
const { execFile } = await import("child_process");
|
|
87
|
+
execFileAsync$1 = (0, util.promisify)(execFile);
|
|
88
|
+
}
|
|
89
|
+
return execFileAsync$1("gh", args, { env: await resolveGithubAuthEnv() });
|
|
90
|
+
};
|
|
91
|
+
var GithubAppOwnerResolver = class {
|
|
92
|
+
#cache = /* @__PURE__ */ new Map();
|
|
93
|
+
#runGhApi;
|
|
94
|
+
constructor(runGhApi = defaultRunGhApi) {
|
|
95
|
+
this.#runGhApi = runGhApi;
|
|
96
|
+
}
|
|
97
|
+
async getOwner(botLogin, isCurrentGeneration) {
|
|
98
|
+
const appSlug = botLogin.replace(/\[bot\]$/i, "");
|
|
99
|
+
if (!appSlug || isCurrentGeneration && !isCurrentGeneration()) return void 0;
|
|
100
|
+
const cacheKey = appSlug.toLowerCase();
|
|
101
|
+
const cached = this.#cache.get(cacheKey);
|
|
102
|
+
if (cached?.expiresAt && cached.expiresAt > Date.now()) {
|
|
103
|
+
if (isCurrentGeneration && !isCurrentGeneration()) return void 0;
|
|
104
|
+
return cached.owner;
|
|
105
|
+
}
|
|
106
|
+
if (cached) this.#cache.delete(cacheKey);
|
|
107
|
+
try {
|
|
108
|
+
const { stdout } = await this.#runGhApi(["api", `apps/${appSlug}`]);
|
|
109
|
+
const parsed = JSON.parse(stdout);
|
|
110
|
+
const owner = this.#parseOwner(parsed);
|
|
111
|
+
if (!owner || isCurrentGeneration && !isCurrentGeneration()) return void 0;
|
|
112
|
+
this.#cache.set(cacheKey, {
|
|
113
|
+
owner,
|
|
114
|
+
expiresAt: Date.now() + OWNER_CACHE_TTL_MS
|
|
115
|
+
});
|
|
116
|
+
if (isCurrentGeneration && !isCurrentGeneration()) {
|
|
117
|
+
this.#cache.delete(cacheKey);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
return owner;
|
|
121
|
+
} catch {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
#parseOwner(value) {
|
|
126
|
+
if (!value || typeof value !== "object" || !("owner" in value)) return void 0;
|
|
127
|
+
const owner = value.owner;
|
|
128
|
+
if (!owner || typeof owner !== "object" || !("login" in owner) || !("type" in owner)) return void 0;
|
|
129
|
+
if (typeof owner.login !== "string" || !owner.login.trim()) return void 0;
|
|
130
|
+
if (owner.type !== "User" && owner.type !== "Organization") return void 0;
|
|
131
|
+
return {
|
|
132
|
+
login: owner.login,
|
|
133
|
+
type: owner.type
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
//#endregion
|
|
33
138
|
//#region src/index.ts
|
|
34
139
|
let _execFileAsync;
|
|
35
140
|
async function execFileAsync(file, args, options) {
|
|
@@ -603,7 +708,8 @@ var GitcrawlSyncClient = class {
|
|
|
603
708
|
const { stdout, stderr } = await execFileAsync(this.#command, args, {
|
|
604
709
|
cwd: input.cwd,
|
|
605
710
|
signal: input.abortSignal,
|
|
606
|
-
maxBuffer: 10 * 1024 * 1024
|
|
711
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
712
|
+
env: await resolveGithubAuthEnv()
|
|
607
713
|
});
|
|
608
714
|
return {
|
|
609
715
|
ok: true,
|
|
@@ -664,12 +770,12 @@ var GitcrawlSyncClient = class {
|
|
|
664
770
|
join repositories r on r.id=t.repo_id
|
|
665
771
|
where r.owner=${owner} and r.name=${repo} and t.number=${number} and rt.is_resolved=0`);
|
|
666
772
|
const latestComments = await this.#queryDb(`select c.author_login, c.author_type, c.is_bot, c.body, json_extract(c.raw_json, '$.html_url') as html_url,
|
|
667
|
-
coalesce(c.updated_at_gh, c.created_at_gh) as updated_at
|
|
773
|
+
coalesce(c.updated_at_gh, c.created_at_gh, json_extract(c.raw_json, '$.submitted_at')) as updated_at
|
|
668
774
|
from comments c
|
|
669
775
|
join threads t on t.id=c.thread_id
|
|
670
776
|
join repositories r on r.id=t.repo_id
|
|
671
777
|
where r.owner=${owner} and r.name=${repo} and t.number=${number}
|
|
672
|
-
order by coalesce(c.updated_at_gh, c.created_at_gh) desc
|
|
778
|
+
order by coalesce(c.updated_at_gh, c.created_at_gh, json_extract(c.raw_json, '$.submitted_at')) desc
|
|
673
779
|
limit 20`);
|
|
674
780
|
const latestComment = latestComments[0];
|
|
675
781
|
const checks = normalizeGithubChecksForSnapshot({
|
|
@@ -794,6 +900,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
794
900
|
#options;
|
|
795
901
|
#syncClient;
|
|
796
902
|
#repositoryResolver;
|
|
903
|
+
#appOwnerResolver;
|
|
797
904
|
#polling = /* @__PURE__ */ new Map();
|
|
798
905
|
#pollingThreadGenerations = /* @__PURE__ */ new Map();
|
|
799
906
|
#pollingGeneration = 0;
|
|
@@ -807,6 +914,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
807
914
|
this.#options = options;
|
|
808
915
|
this.#syncClient = options.syncClient ?? new GitcrawlSyncClient({ command: options.gitcrawlCommand });
|
|
809
916
|
this.#repositoryResolver = options.repositoryResolver ?? new GitRemoteRepositoryResolver();
|
|
917
|
+
this.#appOwnerResolver = new GithubAppOwnerResolver();
|
|
810
918
|
if (options.getNotificationStreamOptions) this.#agentOptions = { getNotificationStreamOptions: options.getNotificationStreamOptions };
|
|
811
919
|
}
|
|
812
920
|
/**
|
|
@@ -847,7 +955,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
847
955
|
async subscribeThreadToPR(input) {
|
|
848
956
|
const pr = typeof input.pr === "number" ? { number: input.pr } : input.pr;
|
|
849
957
|
return this.#subscribe({
|
|
850
|
-
id: `github-command-subscribe-${
|
|
958
|
+
id: `github-command-subscribe-${globalThis.crypto.randomUUID()}`,
|
|
851
959
|
...pr,
|
|
852
960
|
mode: normalizeGithubSubscriptionMode(input.mode),
|
|
853
961
|
threadId: input.threadId,
|
|
@@ -857,7 +965,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
857
965
|
async unsubscribeThreadFromPR(input) {
|
|
858
966
|
const pr = typeof input.pr === "number" ? { number: input.pr } : input.pr;
|
|
859
967
|
return this.#unsubscribe({
|
|
860
|
-
id: `github-command-unsubscribe-${
|
|
968
|
+
id: `github-command-unsubscribe-${globalThis.crypto.randomUUID()}`,
|
|
861
969
|
...pr,
|
|
862
970
|
threadId: input.threadId,
|
|
863
971
|
resourceId: input.resourceId
|
|
@@ -1058,7 +1166,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
1058
1166
|
const results = [];
|
|
1059
1167
|
for (const pr of requestedPrs) try {
|
|
1060
1168
|
const result = await this.#subscribe({
|
|
1061
|
-
id: `github-tool-subscribe-${
|
|
1169
|
+
id: `github-tool-subscribe-${globalThis.crypto.randomUUID()}`,
|
|
1062
1170
|
owner: pr.owner,
|
|
1063
1171
|
repo: pr.repo,
|
|
1064
1172
|
number: pr.number,
|
|
@@ -1112,7 +1220,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
1112
1220
|
const results = [];
|
|
1113
1221
|
for (const pr of requestedPrs) try {
|
|
1114
1222
|
const result = await this.#unsubscribe({
|
|
1115
|
-
id: `github-tool-unsubscribe-${
|
|
1223
|
+
id: `github-tool-unsubscribe-${globalThis.crypto.randomUUID()}`,
|
|
1116
1224
|
owner: pr.owner,
|
|
1117
1225
|
repo: pr.repo,
|
|
1118
1226
|
number: pr.number,
|
|
@@ -1374,39 +1482,23 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
1374
1482
|
...latestCommentExcerpt ? { latestCommentExcerpt } : {},
|
|
1375
1483
|
...input.snapshot.latestCommentUrl ? { latestCommentUrl: input.snapshot.latestCommentUrl } : {},
|
|
1376
1484
|
...input.snapshot.latestCommentUpdatedAt ? { latestCommentUpdatedAt: input.snapshot.latestCommentUpdatedAt } : {},
|
|
1377
|
-
...failingChecks.length
|
|
1378
|
-
|
|
1485
|
+
...failingChecks.length ? {
|
|
1486
|
+
failingChecks: failingChecks.map((check) => check.name).join(", "),
|
|
1487
|
+
failingCheckUrls: failingChecks.filter((check) => check.detailsUrl).map((check) => `${check.name}: ${check.detailsUrl}`).join("; ")
|
|
1488
|
+
} : {},
|
|
1489
|
+
...pendingChecks.length ? { pendingChecks: pendingChecks.map((check) => check.name).join(", ") } : {}
|
|
1379
1490
|
},
|
|
1380
1491
|
metadata: { github: {
|
|
1381
|
-
owner: input.subscription.owner,
|
|
1382
|
-
repo: input.subscription.repo,
|
|
1383
|
-
number: input.subscription.number,
|
|
1384
|
-
mode: input.subscription.mode,
|
|
1385
|
-
title: input.snapshot.title,
|
|
1386
|
-
state: input.snapshot.state,
|
|
1387
|
-
htmlUrl: input.snapshot.htmlUrl,
|
|
1388
|
-
githubUpdatedAt: input.snapshot.githubUpdatedAt,
|
|
1389
1492
|
previousGithubUpdatedAt: input.previousGithubUpdatedAt,
|
|
1390
|
-
contentHash: input.snapshot.contentHash,
|
|
1391
1493
|
previousContentHash: input.previousContentHash,
|
|
1494
|
+
contentHash: input.snapshot.contentHash,
|
|
1392
1495
|
threadContentHash: input.snapshot.threadContentHash,
|
|
1393
1496
|
headSha: input.snapshot.headSha,
|
|
1394
1497
|
headRef: input.snapshot.headRef,
|
|
1395
|
-
mergeableState: input.snapshot.mergeableState,
|
|
1396
|
-
ciState: input.snapshot.ciState,
|
|
1397
|
-
closedAt: input.snapshot.closedAt,
|
|
1398
|
-
mergedAt: input.snapshot.mergedAt,
|
|
1399
|
-
unresolvedReviewThreads: input.snapshot.unresolvedReviewThreads,
|
|
1400
1498
|
reviewStateHash: input.snapshot.reviewStateHash,
|
|
1401
1499
|
latestReviewThreadAt: input.snapshot.latestReviewThreadAt,
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
latestCommentIsBot: input.snapshot.latestCommentIsBot,
|
|
1405
|
-
latestCommentExcerpt,
|
|
1406
|
-
latestCommentUrl: input.snapshot.latestCommentUrl,
|
|
1407
|
-
latestCommentUpdatedAt: input.snapshot.latestCommentUpdatedAt,
|
|
1408
|
-
failingChecks,
|
|
1409
|
-
pendingChecks
|
|
1500
|
+
closedAt: input.snapshot.closedAt,
|
|
1501
|
+
mergedAt: input.snapshot.mergedAt
|
|
1410
1502
|
} }
|
|
1411
1503
|
};
|
|
1412
1504
|
}
|
|
@@ -1441,7 +1533,15 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
1441
1533
|
const normalizedUser = user.toLowerCase();
|
|
1442
1534
|
if (metadata.isBot === true || metadata.authorType?.toLowerCase() === "bot" || normalizedUser.endsWith("[bot]")) {
|
|
1443
1535
|
if ((this.#options.ignoredBots ?? []).some((bot) => bot.toLowerCase() === normalizedUser)) return false;
|
|
1444
|
-
|
|
1536
|
+
if ((this.#options.authorizedBots ?? DEFAULT_AUTHORIZED_BOTS).some((bot) => bot.toLowerCase() === normalizedUser)) return true;
|
|
1537
|
+
const appOwner = await this.#appOwnerResolver.getOwner(user, isCurrentGeneration);
|
|
1538
|
+
if (!appOwner || isCurrentGeneration && !isCurrentGeneration()) return false;
|
|
1539
|
+
if (appOwner.type === "Organization") return appOwner.login.toLowerCase() === owner.toLowerCase();
|
|
1540
|
+
if (appOwner.type !== "User") return false;
|
|
1541
|
+
const permission = await this.#loadAuthorPermission(owner, repo, appOwner.login, isCurrentGeneration);
|
|
1542
|
+
if (isCurrentGeneration && !isCurrentGeneration()) return false;
|
|
1543
|
+
const authorizedPermissions = this.#options.authorizedPermissions ?? DEFAULT_AUTHORIZED_PERMISSIONS;
|
|
1544
|
+
return !!permission && authorizedPermissions.includes(permission);
|
|
1445
1545
|
}
|
|
1446
1546
|
const permission = await this.#loadAuthorPermission(owner, repo, user, isCurrentGeneration);
|
|
1447
1547
|
if (isCurrentGeneration && !isCurrentGeneration()) return false;
|
|
@@ -1503,7 +1603,7 @@ var GithubSignals = class extends _mastra_core_signals.SignalProvider {
|
|
|
1503
1603
|
`repos/${owner}/${repo}/collaborators/${user}/permission`,
|
|
1504
1604
|
"--jq",
|
|
1505
1605
|
".permission"
|
|
1506
|
-
]);
|
|
1606
|
+
], { env: await resolveGithubAuthEnv() });
|
|
1507
1607
|
const raw = stdout.trim();
|
|
1508
1608
|
permission = [
|
|
1509
1609
|
"admin",
|