@h-rig/github-provider-plugin 0.0.6-alpha.157 → 0.0.6-alpha.159
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/src/identity-env.d.ts +16 -0
- package/dist/src/identity-env.js +239 -0
- package/dist/src/identity.d.ts +17 -0
- package/dist/src/identity.js +134 -0
- package/dist/src/index.d.ts +1 -4
- package/dist/src/index.js +360 -740
- package/dist/src/issue-analysis.d.ts +5 -5
- package/dist/src/issue-analysis.js +6 -6
- package/dist/src/lib.d.ts +16 -0
- package/dist/src/lib.js +485 -0
- package/dist/src/plugin.d.ts +6 -2
- package/dist/src/plugin.js +792 -31
- package/dist/src/profile-ops.d.ts +8 -0
- package/dist/src/profile-ops.js +9 -0
- package/dist/src/service.js +1 -35
- package/dist/src/token-env.d.ts +3 -0
- package/dist/src/token-env.js +26 -0
- package/dist/src/triage-run.js +12 -10
- package/package.json +24 -4
- package/dist/src/auth-store.d.ts +0 -42
- package/dist/src/auth-store.js +0 -226
- package/dist/src/credentials.d.ts +0 -20
- package/dist/src/credentials.js +0 -118
- package/dist/src/github-api.d.ts +0 -107
- package/dist/src/github-api.js +0 -451
- package/dist/src/projects.d.ts +0 -31
- package/dist/src/projects.js +0 -147
package/dist/src/index.js
CHANGED
|
@@ -15,715 +15,6 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
|
|
18
|
-
// packages/github-provider-plugin/src/credentials.ts
|
|
19
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
20
|
-
import { resolve as resolve2 } from "path";
|
|
21
|
-
function selectedRepoTokenKey(input) {
|
|
22
|
-
return `user:${input.userId}|repo:${input.owner}/${input.repo}|workspace:${input.workspaceId}`;
|
|
23
|
-
}
|
|
24
|
-
function cleanToken(value) {
|
|
25
|
-
const trimmed = value?.trim() ?? "";
|
|
26
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
27
|
-
}
|
|
28
|
-
function createGitHubCredentialProvider(options = {}) {
|
|
29
|
-
const sessionTokens = options.sessionTokens ?? {};
|
|
30
|
-
const hostToken = cleanToken(options.hostToken ?? process.env.GH_TOKEN ?? null);
|
|
31
|
-
return {
|
|
32
|
-
async resolveGitHubToken(input) {
|
|
33
|
-
const owner = input.owner.trim();
|
|
34
|
-
const repo = input.repo.trim();
|
|
35
|
-
const workspaceId = input.workspaceId.trim();
|
|
36
|
-
const userId = input.userId?.trim() ?? "";
|
|
37
|
-
if (input.purpose === "selected-repo") {
|
|
38
|
-
if (!owner || !repo || !workspaceId || !userId) {
|
|
39
|
-
throw new Error("No signed-in GitHub token is available for the selected repo; sign in to GitHub for this workspace.");
|
|
40
|
-
}
|
|
41
|
-
const token = cleanToken(sessionTokens[selectedRepoTokenKey({ owner, repo, workspaceId, userId })]);
|
|
42
|
-
if (!token) {
|
|
43
|
-
throw new Error("No signed-in GitHub token is available for the selected repo; sign in to GitHub for this workspace.");
|
|
44
|
-
}
|
|
45
|
-
return { token, source: "signed-in-user" };
|
|
46
|
-
}
|
|
47
|
-
if (hostToken) {
|
|
48
|
-
return { token: hostToken, source: "host-admin-fallback" };
|
|
49
|
-
}
|
|
50
|
-
throw new Error("No host GitHub token is configured for the explicit admin fallback operation.");
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function createEnvGitHubCredentialProvider() {
|
|
55
|
-
return {
|
|
56
|
-
async resolveGitHubToken(input) {
|
|
57
|
-
if (input.purpose === "selected-repo") {
|
|
58
|
-
return { token: cleanToken(process.env.RIG_GITHUB_SELECTED_TOKEN ?? null) ?? "", source: "signed-in-user" };
|
|
59
|
-
}
|
|
60
|
-
const token = cleanToken(process.env.RIG_GITHUB_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
|
|
61
|
-
if (!token) {
|
|
62
|
-
throw new Error("No host GitHub token is configured for admin fallback.");
|
|
63
|
-
}
|
|
64
|
-
return { token, source: "host-admin-fallback" };
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function createStateGitHubCredentialProvider(options = {}) {
|
|
69
|
-
const addCandidate = (candidates, path) => {
|
|
70
|
-
const trimmed = path?.trim();
|
|
71
|
-
if (!trimmed)
|
|
72
|
-
return;
|
|
73
|
-
const resolved = resolve2(trimmed);
|
|
74
|
-
if (!candidates.includes(resolved))
|
|
75
|
-
candidates.push(resolved);
|
|
76
|
-
};
|
|
77
|
-
const addStateDir = (candidates, dir) => {
|
|
78
|
-
const trimmed = dir?.trim();
|
|
79
|
-
if (!trimmed)
|
|
80
|
-
return;
|
|
81
|
-
addCandidate(candidates, resolve2(trimmed, "github-auth.json"));
|
|
82
|
-
};
|
|
83
|
-
const addProjectStateDir = (candidates, root) => {
|
|
84
|
-
const trimmed = root?.trim();
|
|
85
|
-
if (!trimmed)
|
|
86
|
-
return;
|
|
87
|
-
addStateDir(candidates, resolve2(trimmed, ".rig", "state"));
|
|
88
|
-
};
|
|
89
|
-
const stateFileCandidates = () => {
|
|
90
|
-
const candidates = [];
|
|
91
|
-
addCandidate(candidates, options.stateFile ?? process.env.RIG_GITHUB_AUTH_STATE_FILE);
|
|
92
|
-
addStateDir(candidates, options.stateDir);
|
|
93
|
-
addStateDir(candidates, process.env.RIG_STATE_DIR);
|
|
94
|
-
addProjectStateDir(candidates, process.env.PROJECT_RIG_ROOT);
|
|
95
|
-
addProjectStateDir(candidates, process.env.RIG_PROJECT_ROOT);
|
|
96
|
-
addProjectStateDir(candidates, process.env.RIG_HOST_PROJECT_ROOT);
|
|
97
|
-
addProjectStateDir(candidates, process.cwd());
|
|
98
|
-
return candidates;
|
|
99
|
-
};
|
|
100
|
-
const readToken = () => {
|
|
101
|
-
for (const stateFile of stateFileCandidates()) {
|
|
102
|
-
if (!existsSync2(stateFile))
|
|
103
|
-
continue;
|
|
104
|
-
try {
|
|
105
|
-
const parsed = JSON.parse(readFileSync2(stateFile, "utf8"));
|
|
106
|
-
const token = typeof parsed.token === "string" ? cleanToken(parsed.token) : null;
|
|
107
|
-
if (token)
|
|
108
|
-
return token;
|
|
109
|
-
} catch {}
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
};
|
|
113
|
-
return {
|
|
114
|
-
async resolveGitHubToken(input) {
|
|
115
|
-
const token = readToken();
|
|
116
|
-
if (input.purpose === "selected-repo") {
|
|
117
|
-
return { token: token ?? cleanToken(process.env.RIG_GITHUB_SELECTED_TOKEN ?? null) ?? "", source: "signed-in-user" };
|
|
118
|
-
}
|
|
119
|
-
if (token) {
|
|
120
|
-
return { token, source: "signed-in-user" };
|
|
121
|
-
}
|
|
122
|
-
const fallback = cleanToken(process.env.RIG_GITHUB_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? null);
|
|
123
|
-
if (!fallback) {
|
|
124
|
-
throw new Error("No signed-in GitHub token is stored for Rig and no host admin fallback token is configured.");
|
|
125
|
-
}
|
|
126
|
-
return { token: fallback, source: "host-admin-fallback" };
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
var init_credentials = () => {};
|
|
131
|
-
|
|
132
|
-
// packages/github-provider-plugin/src/service.ts
|
|
133
|
-
var exports_service = {};
|
|
134
|
-
__export(exports_service, {
|
|
135
|
-
githubProviderService: () => githubProviderService
|
|
136
|
-
});
|
|
137
|
-
var githubProviderService;
|
|
138
|
-
var init_service = __esm(() => {
|
|
139
|
-
init_credentials();
|
|
140
|
-
githubProviderService = {
|
|
141
|
-
createCredentialProvider: (options) => createGitHubCredentialProvider(options)
|
|
142
|
-
};
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// packages/github-provider-plugin/src/auth-store.ts
|
|
146
|
-
import { randomBytes } from "crypto";
|
|
147
|
-
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
148
|
-
import { dirname, resolve } from "path";
|
|
149
|
-
import { resolveRigStatePaths } from "@rig/runtime/control-plane/server-paths";
|
|
150
|
-
function cleanString(value) {
|
|
151
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
152
|
-
}
|
|
153
|
-
function cleanScopes(value) {
|
|
154
|
-
if (!Array.isArray(value))
|
|
155
|
-
return [];
|
|
156
|
-
return value.flatMap((entry) => {
|
|
157
|
-
const clean = cleanString(entry);
|
|
158
|
-
return clean ? [clean] : [];
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
function parseApiSessions(value) {
|
|
162
|
-
if (!Array.isArray(value))
|
|
163
|
-
return [];
|
|
164
|
-
return value.flatMap((entry) => {
|
|
165
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
166
|
-
return [];
|
|
167
|
-
const record = entry;
|
|
168
|
-
const token = cleanString(record.token);
|
|
169
|
-
if (!token)
|
|
170
|
-
return [];
|
|
171
|
-
return [{
|
|
172
|
-
token,
|
|
173
|
-
login: cleanString(record.login),
|
|
174
|
-
userId: cleanString(record.userId),
|
|
175
|
-
createdAt: cleanString(record.createdAt) ?? undefined
|
|
176
|
-
}];
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
function parsePendingDevice(value) {
|
|
180
|
-
if (!value || typeof value !== "object")
|
|
181
|
-
return null;
|
|
182
|
-
const record = value;
|
|
183
|
-
const pollId = cleanString(record.pollId);
|
|
184
|
-
const deviceCode = cleanString(record.deviceCode);
|
|
185
|
-
const expiresAt = cleanString(record.expiresAt);
|
|
186
|
-
const intervalSeconds = typeof record.intervalSeconds === "number" && Number.isFinite(record.intervalSeconds) ? Math.max(1, Math.floor(record.intervalSeconds)) : null;
|
|
187
|
-
if (!pollId || !deviceCode || !expiresAt || !intervalSeconds)
|
|
188
|
-
return null;
|
|
189
|
-
return { pollId, deviceCode, expiresAt, intervalSeconds };
|
|
190
|
-
}
|
|
191
|
-
function parsePendingDevices(value) {
|
|
192
|
-
if (!Array.isArray(value))
|
|
193
|
-
return [];
|
|
194
|
-
return value.flatMap((entry) => {
|
|
195
|
-
const pending = parsePendingDevice(entry);
|
|
196
|
-
return pending ? [pending] : [];
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
function readStoredAuth(stateFile) {
|
|
200
|
-
if (!existsSync(stateFile))
|
|
201
|
-
return {};
|
|
202
|
-
try {
|
|
203
|
-
const parsed = JSON.parse(readFileSync(stateFile, "utf8"));
|
|
204
|
-
return {
|
|
205
|
-
...cleanString(parsed.token) ? { token: cleanString(parsed.token) } : {},
|
|
206
|
-
login: cleanString(parsed.login),
|
|
207
|
-
userId: cleanString(parsed.userId),
|
|
208
|
-
scopes: cleanScopes(parsed.scopes),
|
|
209
|
-
selectedRepo: cleanString(parsed.selectedRepo),
|
|
210
|
-
tokenSource: parsed.tokenSource === "oauth-device" || parsed.tokenSource === "manual-token" || parsed.tokenSource === "env" ? parsed.tokenSource : undefined,
|
|
211
|
-
pendingDevice: parsePendingDevice(parsed.pendingDevice),
|
|
212
|
-
pendingDevices: parsePendingDevices(parsed.pendingDevices),
|
|
213
|
-
apiSessions: parseApiSessions(parsed.apiSessions),
|
|
214
|
-
updatedAt: cleanString(parsed.updatedAt) ?? undefined
|
|
215
|
-
};
|
|
216
|
-
} catch {
|
|
217
|
-
return {};
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function newApiSessionToken() {
|
|
221
|
-
return `rig_${randomBytes(32).toString("base64url")}`;
|
|
222
|
-
}
|
|
223
|
-
function writeStoredAuth(stateFile, payload) {
|
|
224
|
-
mkdirSync(dirname(stateFile), { recursive: true });
|
|
225
|
-
writeFileSync(stateFile, `${JSON.stringify(payload, null, 2)}
|
|
226
|
-
`, { encoding: "utf8", mode: 384 });
|
|
227
|
-
try {
|
|
228
|
-
chmodSync(stateFile, 384);
|
|
229
|
-
} catch {}
|
|
230
|
-
}
|
|
231
|
-
function localProjectAuthStateFile(projectRoot) {
|
|
232
|
-
return resolve(projectRoot, ".rig", "state", "github-auth.json");
|
|
233
|
-
}
|
|
234
|
-
function resolveGitHubAuthStateFile(projectRoot) {
|
|
235
|
-
return resolve(resolveRigStatePaths(projectRoot).stateDir, "github-auth.json");
|
|
236
|
-
}
|
|
237
|
-
function copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot) {
|
|
238
|
-
const targetFile = localProjectAuthStateFile(projectRoot);
|
|
239
|
-
mkdirSync(dirname(targetFile), { recursive: true });
|
|
240
|
-
if (existsSync(stateFile)) {
|
|
241
|
-
copyFileSync(stateFile, targetFile);
|
|
242
|
-
try {
|
|
243
|
-
chmodSync(targetFile, 384);
|
|
244
|
-
} catch {}
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
writeStoredAuth(targetFile, {});
|
|
248
|
-
}
|
|
249
|
-
function createGitHubAuthStoreFromStateFile(stateFile) {
|
|
250
|
-
return {
|
|
251
|
-
stateFile,
|
|
252
|
-
status(options) {
|
|
253
|
-
const stored = readStoredAuth(stateFile);
|
|
254
|
-
const token = cleanString(stored.token);
|
|
255
|
-
return {
|
|
256
|
-
signedIn: Boolean(token),
|
|
257
|
-
login: cleanString(stored.login),
|
|
258
|
-
userId: cleanString(stored.userId),
|
|
259
|
-
scopes: cleanScopes(stored.scopes),
|
|
260
|
-
selectedRepo: cleanString(stored.selectedRepo),
|
|
261
|
-
oauthConfigured: options?.oauthConfigured === true,
|
|
262
|
-
tokenSource: token ? stored.tokenSource ?? "manual-token" : null
|
|
263
|
-
};
|
|
264
|
-
},
|
|
265
|
-
readToken() {
|
|
266
|
-
return cleanString(readStoredAuth(stateFile).token);
|
|
267
|
-
},
|
|
268
|
-
saveToken(input) {
|
|
269
|
-
const previous = readStoredAuth(stateFile);
|
|
270
|
-
writeStoredAuth(stateFile, {
|
|
271
|
-
...previous,
|
|
272
|
-
token: input.token,
|
|
273
|
-
tokenSource: input.tokenSource,
|
|
274
|
-
login: input.login ?? null,
|
|
275
|
-
userId: input.userId ?? null,
|
|
276
|
-
scopes: input.scopes ?? [],
|
|
277
|
-
selectedRepo: input.selectedRepo ?? previous.selectedRepo ?? null,
|
|
278
|
-
pendingDevice: null,
|
|
279
|
-
pendingDevices: [],
|
|
280
|
-
apiSessions: previous.apiSessions ?? [],
|
|
281
|
-
updatedAt: new Date().toISOString()
|
|
282
|
-
});
|
|
283
|
-
},
|
|
284
|
-
createApiSession() {
|
|
285
|
-
const previous = readStoredAuth(stateFile);
|
|
286
|
-
const token = newApiSessionToken();
|
|
287
|
-
const session = {
|
|
288
|
-
token,
|
|
289
|
-
login: cleanString(previous.login),
|
|
290
|
-
userId: cleanString(previous.userId),
|
|
291
|
-
createdAt: new Date().toISOString()
|
|
292
|
-
};
|
|
293
|
-
writeStoredAuth(stateFile, {
|
|
294
|
-
...previous,
|
|
295
|
-
apiSessions: [...(previous.apiSessions ?? []).slice(-9), session],
|
|
296
|
-
updatedAt: new Date().toISOString()
|
|
297
|
-
});
|
|
298
|
-
return { token, login: session.login ?? null, userId: session.userId ?? null };
|
|
299
|
-
},
|
|
300
|
-
readApiSession(token) {
|
|
301
|
-
const clean = cleanString(token);
|
|
302
|
-
if (!clean)
|
|
303
|
-
return null;
|
|
304
|
-
const previous = readStoredAuth(stateFile);
|
|
305
|
-
const session = (previous.apiSessions ?? []).find((candidate) => candidate.token === clean);
|
|
306
|
-
return session ? { login: cleanString(session.login), userId: cleanString(session.userId) } : null;
|
|
307
|
-
},
|
|
308
|
-
copyToProjectRoot(projectRoot) {
|
|
309
|
-
const targetFile = resolveGitHubAuthStateFile(projectRoot);
|
|
310
|
-
writeStoredAuth(targetFile, readStoredAuth(stateFile));
|
|
311
|
-
},
|
|
312
|
-
copyToLocalProjectRoot(projectRoot) {
|
|
313
|
-
copyGitHubAuthStateToLocalProjectRoot(stateFile, projectRoot);
|
|
314
|
-
},
|
|
315
|
-
savePendingDevice(input) {
|
|
316
|
-
const previous = readStoredAuth(stateFile);
|
|
317
|
-
const pendingDevices = [
|
|
318
|
-
...previous.pendingDevice ? [previous.pendingDevice] : [],
|
|
319
|
-
...previous.pendingDevices ?? [],
|
|
320
|
-
input
|
|
321
|
-
].filter((entry, index, entries) => entries.findIndex((candidate) => candidate.pollId === entry.pollId) === index);
|
|
322
|
-
writeStoredAuth(stateFile, {
|
|
323
|
-
...previous,
|
|
324
|
-
pendingDevice: null,
|
|
325
|
-
pendingDevices,
|
|
326
|
-
updatedAt: new Date().toISOString()
|
|
327
|
-
});
|
|
328
|
-
},
|
|
329
|
-
saveSelectedRepo(selectedRepo) {
|
|
330
|
-
const previous = readStoredAuth(stateFile);
|
|
331
|
-
writeStoredAuth(stateFile, {
|
|
332
|
-
...previous,
|
|
333
|
-
selectedRepo: selectedRepo ?? null,
|
|
334
|
-
updatedAt: new Date().toISOString()
|
|
335
|
-
});
|
|
336
|
-
},
|
|
337
|
-
readPendingDevice(pollId) {
|
|
338
|
-
const previous = readStoredAuth(stateFile);
|
|
339
|
-
const pending = [
|
|
340
|
-
...previous.pendingDevice ? [previous.pendingDevice] : [],
|
|
341
|
-
...previous.pendingDevices ?? []
|
|
342
|
-
].find((entry) => entry.pollId === pollId) ?? null;
|
|
343
|
-
if (!pending)
|
|
344
|
-
return null;
|
|
345
|
-
if (Date.parse(pending.expiresAt) <= Date.now())
|
|
346
|
-
return null;
|
|
347
|
-
return pending;
|
|
348
|
-
},
|
|
349
|
-
clearPendingDevice(pollId) {
|
|
350
|
-
const previous = readStoredAuth(stateFile);
|
|
351
|
-
const remaining = pollId ? (previous.pendingDevices ?? []).filter((entry) => entry.pollId !== pollId) : [];
|
|
352
|
-
writeStoredAuth(stateFile, {
|
|
353
|
-
...previous,
|
|
354
|
-
pendingDevice: null,
|
|
355
|
-
pendingDevices: remaining,
|
|
356
|
-
updatedAt: new Date().toISOString()
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
function createGitHubAuthStore(projectRoot) {
|
|
362
|
-
return createGitHubAuthStoreFromStateFile(resolveGitHubAuthStateFile(projectRoot));
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// packages/github-provider-plugin/src/index.ts
|
|
366
|
-
init_credentials();
|
|
367
|
-
|
|
368
|
-
// packages/github-provider-plugin/src/projects.ts
|
|
369
|
-
function asRecord(value) {
|
|
370
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
371
|
-
}
|
|
372
|
-
function asString(value) {
|
|
373
|
-
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
374
|
-
}
|
|
375
|
-
function asNumber(value) {
|
|
376
|
-
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
377
|
-
}
|
|
378
|
-
async function defaultGraphQLFetch(query, variables, token) {
|
|
379
|
-
const response = await fetch("https://api.github.com/graphql", {
|
|
380
|
-
method: "POST",
|
|
381
|
-
headers: {
|
|
382
|
-
"content-type": "application/json",
|
|
383
|
-
authorization: `Bearer ${token}`,
|
|
384
|
-
accept: "application/vnd.github+json"
|
|
385
|
-
},
|
|
386
|
-
body: JSON.stringify({ query, variables })
|
|
387
|
-
});
|
|
388
|
-
const json = await response.json().catch(() => ({}));
|
|
389
|
-
if (!response.ok || json.errors) {
|
|
390
|
-
throw new Error(`GitHub Projects GraphQL request failed: ${JSON.stringify(json.errors ?? { status: response.status })}`);
|
|
391
|
-
}
|
|
392
|
-
return json.data;
|
|
393
|
-
}
|
|
394
|
-
function projectNodesFrom(data) {
|
|
395
|
-
const root = asRecord(data);
|
|
396
|
-
const owner = asRecord(root?.organization) ?? asRecord(root?.user);
|
|
397
|
-
const projects = asRecord(owner?.projectsV2);
|
|
398
|
-
const nodes = projects?.nodes;
|
|
399
|
-
return Array.isArray(nodes) ? nodes : [];
|
|
400
|
-
}
|
|
401
|
-
async function listGitHubProjects(input) {
|
|
402
|
-
const query = `
|
|
403
|
-
query RigListProjects($owner: String!, $first: Int!) {
|
|
404
|
-
organization(login: $owner) { projectsV2(first: $first, orderBy: { field: UPDATED_AT, direction: DESC }) { nodes { id number title url } } }
|
|
405
|
-
user(login: $owner) { projectsV2(first: $first, orderBy: { field: UPDATED_AT, direction: DESC }) { nodes { id number title url } } }
|
|
406
|
-
}
|
|
407
|
-
`;
|
|
408
|
-
const fetchGraphQL = input.fetchGraphQL ?? defaultGraphQLFetch;
|
|
409
|
-
const data = await fetchGraphQL(query, { owner: input.owner, first: input.first ?? 20 }, input.token);
|
|
410
|
-
return projectNodesFrom(data).flatMap((node) => {
|
|
411
|
-
const record = asRecord(node);
|
|
412
|
-
const id = asString(record?.id);
|
|
413
|
-
const number = asNumber(record?.number);
|
|
414
|
-
const title = asString(record?.title);
|
|
415
|
-
if (!id || number === undefined || !title)
|
|
416
|
-
return [];
|
|
417
|
-
return [{ id, number, title, ...asString(record?.url) ? { url: asString(record?.url) } : {} }];
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
async function resolveProjectStatusField(input) {
|
|
421
|
-
const query = `
|
|
422
|
-
query RigProjectStatusField($projectId: ID!) {
|
|
423
|
-
node(id: $projectId) {
|
|
424
|
-
... on ProjectV2 {
|
|
425
|
-
fields(first: 50) {
|
|
426
|
-
nodes {
|
|
427
|
-
... on ProjectV2FieldCommon { id name }
|
|
428
|
-
... on ProjectV2SingleSelectField { id name options { id name } }
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
`;
|
|
435
|
-
const fetchGraphQL = input.fetchGraphQL ?? defaultGraphQLFetch;
|
|
436
|
-
const data = await fetchGraphQL(query, { projectId: input.projectId }, input.token);
|
|
437
|
-
const fields = asRecord(asRecord(asRecord(data)?.node)?.fields)?.nodes;
|
|
438
|
-
for (const node of Array.isArray(fields) ? fields : []) {
|
|
439
|
-
const record = asRecord(node);
|
|
440
|
-
if (asString(record?.name)?.toLowerCase() !== "status")
|
|
441
|
-
continue;
|
|
442
|
-
const id = asString(record?.id);
|
|
443
|
-
if (!id)
|
|
444
|
-
continue;
|
|
445
|
-
const options = Array.isArray(record?.options) ? record.options.flatMap((option) => {
|
|
446
|
-
const optionRecord = asRecord(option);
|
|
447
|
-
const optionId = asString(optionRecord?.id);
|
|
448
|
-
const name = asString(optionRecord?.name);
|
|
449
|
-
return optionId && name ? [{ id: optionId, name }] : [];
|
|
450
|
-
}) : [];
|
|
451
|
-
return { id, name: "Status", options };
|
|
452
|
-
}
|
|
453
|
-
throw new Error(`GitHub Project ${input.projectId} does not expose a Status single-select field.`);
|
|
454
|
-
}
|
|
455
|
-
async function ensureIssueProjectItem(input) {
|
|
456
|
-
const query = `
|
|
457
|
-
query RigFindProjectIssueItem($projectId: ID!, $issueNodeId: ID!) {
|
|
458
|
-
node(id: $projectId) {
|
|
459
|
-
... on ProjectV2 {
|
|
460
|
-
items(first: 100) { nodes { id content { ... on Issue { id } } } }
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
`;
|
|
465
|
-
const fetchGraphQL = input.fetchGraphQL ?? defaultGraphQLFetch;
|
|
466
|
-
const data = await fetchGraphQL(query, { projectId: input.projectId, issueNodeId: input.issueNodeId }, input.token);
|
|
467
|
-
const nodes = asRecord(asRecord(asRecord(data)?.node)?.items)?.nodes;
|
|
468
|
-
for (const node of Array.isArray(nodes) ? nodes : []) {
|
|
469
|
-
const record = asRecord(node);
|
|
470
|
-
const content = asRecord(record?.content);
|
|
471
|
-
if (asString(content?.id) === input.issueNodeId) {
|
|
472
|
-
const id2 = asString(record?.id);
|
|
473
|
-
if (id2)
|
|
474
|
-
return { id: id2, created: false };
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
const mutation = `
|
|
478
|
-
mutation RigAddIssueToProject($projectId: ID!, $contentId: ID!) {
|
|
479
|
-
addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) { item { id } }
|
|
480
|
-
}
|
|
481
|
-
`;
|
|
482
|
-
const created = await fetchGraphQL(mutation, { projectId: input.projectId, contentId: input.issueNodeId }, input.token);
|
|
483
|
-
const addResult = asRecord(asRecord(created)?.addProjectV2ItemById);
|
|
484
|
-
const id = asString(asRecord(addResult?.item)?.id);
|
|
485
|
-
if (!id)
|
|
486
|
-
throw new Error("GitHub Project item creation did not return an item id.");
|
|
487
|
-
return { id, created: true };
|
|
488
|
-
}
|
|
489
|
-
async function updateIssueProjectStatus(input) {
|
|
490
|
-
const mutation = `
|
|
491
|
-
mutation RigUpdateProjectStatus($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
492
|
-
updateProjectV2ItemFieldValue(input: {
|
|
493
|
-
projectId: $projectId,
|
|
494
|
-
itemId: $itemId,
|
|
495
|
-
fieldId: $fieldId,
|
|
496
|
-
value: { singleSelectOptionId: $optionId }
|
|
497
|
-
}) { projectV2Item { id } }
|
|
498
|
-
}
|
|
499
|
-
`;
|
|
500
|
-
const fetchGraphQL = input.fetchGraphQL ?? defaultGraphQLFetch;
|
|
501
|
-
await fetchGraphQL(mutation, {
|
|
502
|
-
projectId: input.projectId,
|
|
503
|
-
itemId: input.itemId,
|
|
504
|
-
fieldId: input.fieldId,
|
|
505
|
-
optionId: input.optionId
|
|
506
|
-
}, input.token);
|
|
507
|
-
}
|
|
508
|
-
// packages/github-provider-plugin/src/github-api.ts
|
|
509
|
-
import { randomUUID } from "crypto";
|
|
510
|
-
function normalizeString(value) {
|
|
511
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
512
|
-
}
|
|
513
|
-
function normalizeScopes(value) {
|
|
514
|
-
if (Array.isArray(value)) {
|
|
515
|
-
return value.flatMap((entry) => {
|
|
516
|
-
const normalized = normalizeString(entry);
|
|
517
|
-
return normalized ? [normalized] : [];
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
if (typeof value === "string") {
|
|
521
|
-
return value.split(/[ ,]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
522
|
-
}
|
|
523
|
-
return [];
|
|
524
|
-
}
|
|
525
|
-
async function defaultPostGitHubForm(endpoint, body) {
|
|
526
|
-
const response = await fetch(endpoint, {
|
|
527
|
-
method: "POST",
|
|
528
|
-
headers: {
|
|
529
|
-
accept: "application/json",
|
|
530
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
531
|
-
"user-agent": "rig"
|
|
532
|
-
},
|
|
533
|
-
body: new URLSearchParams(body)
|
|
534
|
-
});
|
|
535
|
-
const payload = await response.json().catch(() => ({}));
|
|
536
|
-
return { status: response.status, payload };
|
|
537
|
-
}
|
|
538
|
-
async function fetchGitHubUserInfo(token) {
|
|
539
|
-
const response = await fetch("https://api.github.com/user", {
|
|
540
|
-
headers: {
|
|
541
|
-
accept: "application/vnd.github+json",
|
|
542
|
-
authorization: `Bearer ${token}`,
|
|
543
|
-
"user-agent": "rig"
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
const payload = await response.json().catch(() => ({}));
|
|
547
|
-
if (!response.ok) {
|
|
548
|
-
throw new Error(typeof payload.message === "string" ? payload.message : `GitHub user lookup failed (${response.status}).`);
|
|
549
|
-
}
|
|
550
|
-
const login = normalizeString(payload.login);
|
|
551
|
-
const id = typeof payload.id === "number" ? String(payload.id) : normalizeString(payload.id);
|
|
552
|
-
if (!login || !id) {
|
|
553
|
-
throw new Error("GitHub user lookup did not return login/id.");
|
|
554
|
-
}
|
|
555
|
-
return { login, id, scopes: normalizeScopes(response.headers.get("x-oauth-scopes")) };
|
|
556
|
-
}
|
|
557
|
-
function resolveGitHubAuthStatus(input) {
|
|
558
|
-
return createGitHubAuthStore(input.projectRoot).status({ oauthConfigured: input.oauthConfigured });
|
|
559
|
-
}
|
|
560
|
-
async function saveGitHubTokenForProject(input) {
|
|
561
|
-
const token = normalizeString(input.token);
|
|
562
|
-
if (!token) {
|
|
563
|
-
throw new Error("token is required");
|
|
564
|
-
}
|
|
565
|
-
const user = await (input.fetchUser ?? fetchGitHubUserInfo)(token);
|
|
566
|
-
const store = createGitHubAuthStore(input.projectRoot);
|
|
567
|
-
store.saveToken({
|
|
568
|
-
token,
|
|
569
|
-
tokenSource: input.tokenSource ?? "manual-token",
|
|
570
|
-
login: user.login,
|
|
571
|
-
userId: user.id,
|
|
572
|
-
scopes: user.scopes ?? [],
|
|
573
|
-
selectedRepo: input.selectedRepo ?? undefined
|
|
574
|
-
});
|
|
575
|
-
return { ok: true, ...store.status({ oauthConfigured: true }) };
|
|
576
|
-
}
|
|
577
|
-
async function beginGitHubDeviceFlow(input) {
|
|
578
|
-
const clientId = normalizeString(input.clientId);
|
|
579
|
-
if (!clientId) {
|
|
580
|
-
throw new Error("clientId is required");
|
|
581
|
-
}
|
|
582
|
-
const postForm = input.postForm ?? defaultPostGitHubForm;
|
|
583
|
-
const result = await postForm("https://github.com/login/device/code", {
|
|
584
|
-
client_id: clientId,
|
|
585
|
-
scope: normalizeString(input.scope) ?? "repo read:project user:email"
|
|
586
|
-
});
|
|
587
|
-
const deviceCode = normalizeString(result.payload.device_code);
|
|
588
|
-
if (result.status < 200 || result.status >= 300 || !deviceCode) {
|
|
589
|
-
throw new Error(normalizeString(result.payload.error_description) ?? "GitHub device flow start failed.");
|
|
590
|
-
}
|
|
591
|
-
const expiresIn = typeof result.payload.expires_in === "number" ? result.payload.expires_in : 900;
|
|
592
|
-
const intervalSeconds = typeof result.payload.interval === "number" ? result.payload.interval : 5;
|
|
593
|
-
const pollId = randomUUID();
|
|
594
|
-
createGitHubAuthStore(input.projectRoot).savePendingDevice({
|
|
595
|
-
pollId,
|
|
596
|
-
deviceCode,
|
|
597
|
-
expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(),
|
|
598
|
-
intervalSeconds
|
|
599
|
-
});
|
|
600
|
-
if (input.selectedRepo) {
|
|
601
|
-
createGitHubAuthStore(input.projectRoot).saveSelectedRepo(input.selectedRepo);
|
|
602
|
-
}
|
|
603
|
-
return {
|
|
604
|
-
ok: true,
|
|
605
|
-
pollId,
|
|
606
|
-
userCode: normalizeString(result.payload.user_code),
|
|
607
|
-
verificationUri: normalizeString(result.payload.verification_uri),
|
|
608
|
-
expiresIn,
|
|
609
|
-
intervalSeconds
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
async function pollGitHubDeviceFlow(input) {
|
|
613
|
-
const clientId = normalizeString(input.clientId);
|
|
614
|
-
if (!clientId) {
|
|
615
|
-
throw new Error("clientId is required");
|
|
616
|
-
}
|
|
617
|
-
const store = createGitHubAuthStore(input.projectRoot);
|
|
618
|
-
const pending = store.readPendingDevice(input.pollId);
|
|
619
|
-
if (!pending) {
|
|
620
|
-
return { ok: false, status: "expired", error: "GitHub device flow expired or unknown." };
|
|
621
|
-
}
|
|
622
|
-
const result = await (input.postForm ?? defaultPostGitHubForm)("https://github.com/login/oauth/access_token", {
|
|
623
|
-
client_id: clientId,
|
|
624
|
-
device_code: pending.deviceCode,
|
|
625
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
626
|
-
});
|
|
627
|
-
const error = normalizeString(result.payload.error);
|
|
628
|
-
if (error === "authorization_pending" || error === "slow_down") {
|
|
629
|
-
return {
|
|
630
|
-
ok: false,
|
|
631
|
-
status: error === "slow_down" ? "slow-down" : "pending",
|
|
632
|
-
intervalSeconds: error === "slow_down" ? pending.intervalSeconds + 5 : pending.intervalSeconds
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
if (error || typeof result.payload.access_token !== "string") {
|
|
636
|
-
return {
|
|
637
|
-
ok: false,
|
|
638
|
-
status: "error",
|
|
639
|
-
error: normalizeString(result.payload.error_description) ?? "GitHub device authorization failed."
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
const token = result.payload.access_token;
|
|
643
|
-
const user = await (input.fetchUser ?? fetchGitHubUserInfo)(token);
|
|
644
|
-
store.saveToken({
|
|
645
|
-
token,
|
|
646
|
-
tokenSource: "oauth-device",
|
|
647
|
-
login: user.login,
|
|
648
|
-
userId: user.id,
|
|
649
|
-
scopes: user.scopes ?? normalizeScopes(result.payload.scope),
|
|
650
|
-
selectedRepo: input.selectedRepo ?? undefined
|
|
651
|
-
});
|
|
652
|
-
store.clearPendingDevice(input.pollId);
|
|
653
|
-
return { ok: true, status: "signed-in", ...store.status({ oauthConfigured: true }) };
|
|
654
|
-
}
|
|
655
|
-
function checkGitHubRepoPermissions(input) {
|
|
656
|
-
const store = createGitHubAuthStore(input.projectRoot);
|
|
657
|
-
const auth = store.status({ oauthConfigured: input.oauthConfigured });
|
|
658
|
-
if (!auth.signedIn) {
|
|
659
|
-
return { ok: false, signedIn: false, canOpenPullRequest: false, reason: "not-authenticated" };
|
|
660
|
-
}
|
|
661
|
-
const normalizedScopes = auth.scopes.map((scope) => scope.toLowerCase());
|
|
662
|
-
const broadEnough = normalizedScopes.includes("repo") || normalizedScopes.includes("public_repo");
|
|
663
|
-
return {
|
|
664
|
-
ok: true,
|
|
665
|
-
signedIn: true,
|
|
666
|
-
login: auth.login,
|
|
667
|
-
scopes: auth.scopes,
|
|
668
|
-
canOpenPullRequest: broadEnough,
|
|
669
|
-
pullRequests: broadEnough,
|
|
670
|
-
push: broadEnough,
|
|
671
|
-
reason: broadEnough ? "stored-token" : "token-scope-unverified"
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
async function probeGitHubRepository(input) {
|
|
675
|
-
const headers = {
|
|
676
|
-
accept: "application/vnd.github+json",
|
|
677
|
-
"user-agent": "rig"
|
|
678
|
-
};
|
|
679
|
-
if (input.token) {
|
|
680
|
-
headers.authorization = `Bearer ${input.token}`;
|
|
681
|
-
}
|
|
682
|
-
try {
|
|
683
|
-
const response = await (input.fetchRepository ?? fetch)(`https://api.github.com/repos/${encodeURIComponent(input.owner)}/${encodeURIComponent(input.repo)}`, { headers });
|
|
684
|
-
const payload = await response.json().catch(() => ({}));
|
|
685
|
-
if (response.ok) {
|
|
686
|
-
return {
|
|
687
|
-
ok: true,
|
|
688
|
-
owner: input.owner,
|
|
689
|
-
repo: input.repo,
|
|
690
|
-
status: response.status,
|
|
691
|
-
authenticated: Boolean(input.token),
|
|
692
|
-
authenticationRequired: payload.private === true && !input.token,
|
|
693
|
-
fullName: typeof payload.full_name === "string" ? payload.full_name : `${input.owner}/${input.repo}`,
|
|
694
|
-
private: typeof payload.private === "boolean" ? payload.private : null,
|
|
695
|
-
message: input.token ? "Repository access verified with signed-in GitHub credentials." : "Public repository access verified without credentials.",
|
|
696
|
-
scopes: input.scopes
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
const authenticationRequired = !input.token && (response.status === 401 || response.status === 403 || response.status === 404);
|
|
700
|
-
return {
|
|
701
|
-
ok: false,
|
|
702
|
-
owner: input.owner,
|
|
703
|
-
repo: input.repo,
|
|
704
|
-
status: response.status,
|
|
705
|
-
authenticated: Boolean(input.token),
|
|
706
|
-
authenticationRequired,
|
|
707
|
-
fullName: null,
|
|
708
|
-
private: null,
|
|
709
|
-
message: authenticationRequired ? "Repository is private, missing, or inaccessible without GitHub sign-in. Sign in before saving this config." : typeof payload.message === "string" ? payload.message : `GitHub repository probe failed (${response.status}).`,
|
|
710
|
-
scopes: input.scopes
|
|
711
|
-
};
|
|
712
|
-
} catch (error) {
|
|
713
|
-
return {
|
|
714
|
-
ok: false,
|
|
715
|
-
owner: input.owner,
|
|
716
|
-
repo: input.repo,
|
|
717
|
-
status: 0,
|
|
718
|
-
authenticated: Boolean(input.token),
|
|
719
|
-
authenticationRequired: !input.token,
|
|
720
|
-
fullName: null,
|
|
721
|
-
private: null,
|
|
722
|
-
message: error instanceof Error ? error.message : "GitHub repository probe failed.",
|
|
723
|
-
scopes: input.scopes
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
18
|
// packages/github-provider-plugin/src/issue-analysis.ts
|
|
728
19
|
import { createHash } from "crypto";
|
|
729
20
|
function stableIssueHash(issue) {
|
|
@@ -945,7 +236,7 @@ function createIssueAnalysisWriteBack(input) {
|
|
|
945
236
|
throw new Error("Issue analysis writeback requires removeLabels for labelsToRemove.");
|
|
946
237
|
await input.target.removeLabels(issue.id, uniqueLabels(result.labelsToRemove));
|
|
947
238
|
}
|
|
948
|
-
const comment = (input.buildStatusComment ?? defaultStatusComment)({ issue, result, reason });
|
|
239
|
+
const comment = (input.buildStatusComment ?? defaultStatusComment)({ issue, result, ...reason !== undefined ? { reason } : {} });
|
|
949
240
|
if (comment?.trim()) {
|
|
950
241
|
if (!input.target.updateTask)
|
|
951
242
|
throw new Error("Issue analysis writeback requires updateTask for sticky status comments.");
|
|
@@ -970,7 +261,7 @@ function sourceWithWriteBackCapabilities(source) {
|
|
|
970
261
|
if (typeof candidate.updateTask !== "function")
|
|
971
262
|
return null;
|
|
972
263
|
return {
|
|
973
|
-
get: candidate.get
|
|
264
|
+
...typeof candidate.get === "function" ? { get: candidate.get.bind(candidate) } : {},
|
|
974
265
|
updateTask: candidate.updateTask.bind(candidate),
|
|
975
266
|
...typeof candidate.addLabels === "function" ? { addLabels: candidate.addLabels.bind(candidate) } : {},
|
|
976
267
|
...typeof candidate.removeLabels === "function" ? { removeLabels: candidate.removeLabels.bind(candidate) } : {},
|
|
@@ -999,8 +290,8 @@ function createConfiguredIssueAnalysisRunner(input) {
|
|
|
999
290
|
if (!target)
|
|
1000
291
|
return null;
|
|
1001
292
|
const analyzer = input.analyzer ?? createPiIssueAnalyzer({
|
|
1002
|
-
runCommand: input.runCommand,
|
|
1003
|
-
model: input.context.config.issueAnalysis
|
|
293
|
+
...input.runCommand ? { runCommand: input.runCommand } : {},
|
|
294
|
+
...input.context.config.issueAnalysis?.model ? { model: input.context.config.issueAnalysis.model } : {}
|
|
1004
295
|
});
|
|
1005
296
|
const baseWriteBack = createIssueAnalysisWriteBack({ target });
|
|
1006
297
|
const service = createIssueAnalysisService({
|
|
@@ -1013,7 +304,7 @@ function createConfiguredIssueAnalysisRunner(input) {
|
|
|
1013
304
|
return createContinuousIssueAnalysisRunner({
|
|
1014
305
|
loadIssues: async () => [...await source.list()],
|
|
1015
306
|
service,
|
|
1016
|
-
intervalMs: input.intervalMs,
|
|
307
|
+
...input.intervalMs !== undefined ? { intervalMs: input.intervalMs } : {},
|
|
1017
308
|
reason: "continuous-issue-analysis",
|
|
1018
309
|
...input.setIntervalFn ? { setIntervalFn: input.setIntervalFn } : {},
|
|
1019
310
|
...input.clearIntervalFn ? { clearIntervalFn: input.clearIntervalFn } : {},
|
|
@@ -1034,7 +325,7 @@ function createIssueAnalysisService(input) {
|
|
|
1034
325
|
const result = await input.analyzer({ issue, neighbors, prompt });
|
|
1035
326
|
analyzedHashes.set(issue.id, hash);
|
|
1036
327
|
if (result.metadataPatch || result.labelsToAdd?.length || result.labelsToRemove?.length || result.generatedIssues?.length) {
|
|
1037
|
-
await input.writeBack?.({ issue, result, reason: options.reason });
|
|
328
|
+
await input.writeBack?.({ issue, result, ...options.reason !== undefined ? { reason: options.reason } : {} });
|
|
1038
329
|
}
|
|
1039
330
|
results.push({ issue, result });
|
|
1040
331
|
}
|
|
@@ -1094,8 +385,14 @@ function createContinuousIssueAnalysisRunner(input) {
|
|
|
1094
385
|
}
|
|
1095
386
|
};
|
|
1096
387
|
}
|
|
388
|
+
var init_issue_analysis = () => {};
|
|
389
|
+
|
|
1097
390
|
// packages/github-provider-plugin/src/triage-run.ts
|
|
1098
|
-
|
|
391
|
+
var exports_triage_run = {};
|
|
392
|
+
__export(exports_triage_run, {
|
|
393
|
+
runIssueAnalysisTriage: () => runIssueAnalysisTriage
|
|
394
|
+
});
|
|
395
|
+
import { buildPluginHostContext } from "@rig/core/plugin-host-context";
|
|
1099
396
|
function summarizeResults(results) {
|
|
1100
397
|
return results.reduce((summary, entry) => {
|
|
1101
398
|
if (entry.result.metadataPatch && Object.keys(entry.result.metadataPatch).length > 0) {
|
|
@@ -1112,7 +409,9 @@ async function loadContext(projectRoot) {
|
|
|
1112
409
|
if (!context)
|
|
1113
410
|
return null;
|
|
1114
411
|
return {
|
|
1115
|
-
config:
|
|
412
|
+
config: {
|
|
413
|
+
...context.config.issueAnalysis ? { issueAnalysis: context.config.issueAnalysis } : {}
|
|
414
|
+
},
|
|
1116
415
|
taskSourceRegistry: context.taskSourceRegistry
|
|
1117
416
|
};
|
|
1118
417
|
}
|
|
@@ -1170,8 +469,8 @@ async function runIssueAnalysisTriage(options) {
|
|
|
1170
469
|
const runner = createConfiguredIssueAnalysisRunner({
|
|
1171
470
|
projectRoot: options.projectRoot,
|
|
1172
471
|
context,
|
|
1173
|
-
analyzer: options.analyzer,
|
|
1174
|
-
runCommand: options.runCommand,
|
|
472
|
+
...options.analyzer ? { analyzer: options.analyzer } : {},
|
|
473
|
+
...options.runCommand ? { runCommand: options.runCommand } : {},
|
|
1175
474
|
onWriteBack: refreshSnapshotAfterWriteBack
|
|
1176
475
|
});
|
|
1177
476
|
if (!runner) {
|
|
@@ -1191,10 +490,343 @@ async function runIssueAnalysisTriage(options) {
|
|
|
1191
490
|
refreshedIssueCount
|
|
1192
491
|
};
|
|
1193
492
|
}
|
|
493
|
+
var init_triage_run = __esm(() => {
|
|
494
|
+
init_issue_analysis();
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// packages/github-provider-plugin/src/identity.ts
|
|
498
|
+
import {
|
|
499
|
+
RIG_WORKFLOW_STARTED
|
|
500
|
+
} from "@rig/contracts";
|
|
501
|
+
function stringField(value) {
|
|
502
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
503
|
+
}
|
|
504
|
+
function numericStringField(value) {
|
|
505
|
+
return typeof value === "number" && Number.isInteger(value) ? String(value) : stringField(value);
|
|
506
|
+
}
|
|
507
|
+
function asRecord(value) {
|
|
508
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
509
|
+
}
|
|
510
|
+
function customEntries(entries) {
|
|
511
|
+
const customs = [];
|
|
512
|
+
for (const entry of entries) {
|
|
513
|
+
if (entry.type === "custom")
|
|
514
|
+
customs.push(entry);
|
|
515
|
+
}
|
|
516
|
+
return customs;
|
|
517
|
+
}
|
|
518
|
+
function candidateFromStartedData(data) {
|
|
519
|
+
const record = asRecord(data);
|
|
520
|
+
if (!record)
|
|
521
|
+
return null;
|
|
522
|
+
const owner = asRecord(record.owner);
|
|
523
|
+
const selectedRepo = stringField(record.selectedRepo);
|
|
524
|
+
const githubUserId = stringField(owner?.githubUserId) ?? numericStringField(record.githubUserId) ?? numericStringField(record.userId);
|
|
525
|
+
const login = stringField(owner?.login) ?? stringField(record.login);
|
|
526
|
+
const namespaceKey = stringField(owner?.namespaceKey) ?? stringField(record.namespaceKey) ?? stringField(record.userNamespaceKey);
|
|
527
|
+
if (!selectedRepo && !githubUserId && !login && !namespaceKey)
|
|
528
|
+
return null;
|
|
529
|
+
return { selectedRepo, githubUserId, login, namespaceKey, source: "workflow-metadata" };
|
|
530
|
+
}
|
|
531
|
+
function workflowIdentityCandidate(entries) {
|
|
532
|
+
const customs = customEntries(entries);
|
|
533
|
+
for (let index = customs.length - 1;index >= 0; index -= 1) {
|
|
534
|
+
const entry = customs[index];
|
|
535
|
+
if (entry?.customType !== RIG_WORKFLOW_STARTED)
|
|
536
|
+
continue;
|
|
537
|
+
const candidate = candidateFromStartedData(entry.data);
|
|
538
|
+
if (candidate)
|
|
539
|
+
return candidate;
|
|
540
|
+
}
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
function ompGitHubIdentityCandidate(ctx) {
|
|
544
|
+
const account = asRecord(ctx.modelRegistry?.authStorage?.getOAuthAccountIdentity?.("github-copilot", ctx.sessionManager.getSessionId()));
|
|
545
|
+
if (!account)
|
|
546
|
+
return null;
|
|
547
|
+
const githubUserId = stringField(account.accountId);
|
|
548
|
+
const login = stringField(account.login) ?? stringField(account.email);
|
|
549
|
+
if (!githubUserId && !login)
|
|
550
|
+
return null;
|
|
551
|
+
return {
|
|
552
|
+
githubUserId,
|
|
553
|
+
login,
|
|
554
|
+
namespaceKey: githubUserId ? `gh:${githubUserId}` : undefined,
|
|
555
|
+
source: "omp-github-copilot-auth"
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function envField(name) {
|
|
559
|
+
return stringField(process.env[name]);
|
|
560
|
+
}
|
|
561
|
+
function envIdentityCandidate() {
|
|
562
|
+
const selectedRepo = envField("RIG_SELECTED_REPO");
|
|
563
|
+
const githubUserId = envField("RIG_GITHUB_USER_ID");
|
|
564
|
+
const login = envField("RIG_GITHUB_LOGIN");
|
|
565
|
+
const namespaceKey = envField("RIG_GITHUB_NAMESPACE_KEY") ?? (githubUserId ? `gh:${githubUserId}` : undefined);
|
|
566
|
+
if (!selectedRepo && !githubUserId && !login && !namespaceKey)
|
|
567
|
+
return null;
|
|
568
|
+
return {
|
|
569
|
+
selectedRepo,
|
|
570
|
+
githubUserId,
|
|
571
|
+
login,
|
|
572
|
+
namespaceKey,
|
|
573
|
+
source: "rig-public-identity-env"
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function mergeSource(sources, source) {
|
|
577
|
+
if (source && !sources.includes(source))
|
|
578
|
+
sources.push(source);
|
|
579
|
+
}
|
|
580
|
+
function completeIdentity(candidate, sources) {
|
|
581
|
+
const selectedRepo = stringField(candidate.selectedRepo);
|
|
582
|
+
const githubUserId = stringField(candidate.githubUserId);
|
|
583
|
+
const login = stringField(candidate.login);
|
|
584
|
+
const namespaceKey = stringField(candidate.namespaceKey) ?? (githubUserId ? `gh:${githubUserId}` : undefined);
|
|
585
|
+
if (!selectedRepo || !githubUserId || !login || !namespaceKey)
|
|
586
|
+
return null;
|
|
587
|
+
const uniqueSources = [];
|
|
588
|
+
for (const value of sources) {
|
|
589
|
+
if (!uniqueSources.includes(value))
|
|
590
|
+
uniqueSources.push(value);
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
selectedRepo,
|
|
594
|
+
owner: { githubUserId, login, namespaceKey },
|
|
595
|
+
source: uniqueSources.join("+")
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function resolveRigIdentity(ctx) {
|
|
599
|
+
const workflow = workflowIdentityCandidate(ctx.sessionManager.getBranch());
|
|
600
|
+
const omp = ompGitHubIdentityCandidate(ctx);
|
|
601
|
+
const env = envIdentityCandidate();
|
|
602
|
+
const sources = [];
|
|
603
|
+
const selectedRepo = workflow?.selectedRepo ?? env?.selectedRepo;
|
|
604
|
+
const githubUserId = workflow?.githubUserId ?? omp?.githubUserId ?? env?.githubUserId;
|
|
605
|
+
const login = workflow?.login ?? omp?.login ?? env?.login;
|
|
606
|
+
const namespaceKey = workflow?.namespaceKey ?? omp?.namespaceKey ?? env?.namespaceKey;
|
|
607
|
+
if (workflow && (workflow.selectedRepo || workflow.githubUserId || workflow.login || workflow.namespaceKey))
|
|
608
|
+
mergeSource(sources, workflow.source);
|
|
609
|
+
if (omp && (!workflow?.githubUserId || !workflow?.login || !workflow?.namespaceKey))
|
|
610
|
+
mergeSource(sources, omp.source);
|
|
611
|
+
if (env && (!workflow?.selectedRepo && env.selectedRepo || !workflow?.githubUserId && !omp?.githubUserId && env.githubUserId || !workflow?.login && !omp?.login && env.login || !workflow?.namespaceKey && !omp?.namespaceKey && env.namespaceKey))
|
|
612
|
+
mergeSource(sources, env.source);
|
|
613
|
+
return completeIdentity({ selectedRepo, githubUserId, login, namespaceKey, source: sources.join("+") }, sources);
|
|
614
|
+
}
|
|
615
|
+
function resolveRigIdentityFilter(ctx) {
|
|
616
|
+
const identity = resolveRigIdentity(ctx);
|
|
617
|
+
if (!identity)
|
|
618
|
+
return null;
|
|
619
|
+
return {
|
|
620
|
+
cwd: ctx.sessionManager.getCwd(),
|
|
621
|
+
selectedRepo: identity.selectedRepo,
|
|
622
|
+
githubUserId: identity.owner.githubUserId,
|
|
623
|
+
namespaceKey: identity.owner.namespaceKey
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
var init_identity = () => {};
|
|
627
|
+
|
|
628
|
+
// packages/github-provider-plugin/src/identity-env.ts
|
|
629
|
+
var exports_identity_env = {};
|
|
630
|
+
__export(exports_identity_env, {
|
|
631
|
+
stripRunChildCredentialEnv: () => stripRunChildCredentialEnv,
|
|
632
|
+
resolveRigIdentityFilter: () => resolveRigIdentityFilter,
|
|
633
|
+
resolveRigIdentity: () => resolveRigIdentity,
|
|
634
|
+
resolveIdentityEnv: () => resolveIdentityEnv,
|
|
635
|
+
identityFilterFromEnv: () => identityFilterFromEnv,
|
|
636
|
+
applyIdentityEnv: () => applyIdentityEnv,
|
|
637
|
+
RUN_CHILD_STRIPPED_CREDENTIAL_ENV_KEYS: () => RUN_CHILD_STRIPPED_CREDENTIAL_ENV_KEYS
|
|
638
|
+
});
|
|
639
|
+
import { existsSync, readFileSync } from "fs";
|
|
640
|
+
import { resolve } from "path";
|
|
641
|
+
function stringField2(value) {
|
|
642
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
643
|
+
}
|
|
644
|
+
function githubUserId(value) {
|
|
645
|
+
if (typeof value === "number" && Number.isInteger(value))
|
|
646
|
+
return String(value);
|
|
647
|
+
return stringField2(value);
|
|
648
|
+
}
|
|
649
|
+
function readJsonRecord(path) {
|
|
650
|
+
if (!existsSync(path))
|
|
651
|
+
return null;
|
|
652
|
+
try {
|
|
653
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
654
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
655
|
+
} catch {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function resolveIdentityEnv(workspaceRoot) {
|
|
660
|
+
const stateDir = resolve(workspaceRoot, ".rig", "state");
|
|
661
|
+
const auth = readJsonRecord(resolve(stateDir, "github-auth.json"));
|
|
662
|
+
const connection = readJsonRecord(resolve(stateDir, "connection.json"));
|
|
663
|
+
const projectLink = readJsonRecord(resolve(stateDir, "project-link.json"));
|
|
664
|
+
const values = {};
|
|
665
|
+
const selectedRepo = stringField2(auth?.selectedRepo) ?? stringField2(connection?.project) ?? stringField2(projectLink?.repoSlug);
|
|
666
|
+
const userId = githubUserId(auth?.userId);
|
|
667
|
+
const login = stringField2(auth?.login);
|
|
668
|
+
const namespaceKey = stringField2(auth?.userNamespaceKey);
|
|
669
|
+
if (selectedRepo)
|
|
670
|
+
values.RIG_SELECTED_REPO = selectedRepo;
|
|
671
|
+
if (userId)
|
|
672
|
+
values.RIG_GITHUB_USER_ID = userId;
|
|
673
|
+
if (login)
|
|
674
|
+
values.RIG_GITHUB_LOGIN = login;
|
|
675
|
+
if (namespaceKey)
|
|
676
|
+
values.RIG_GITHUB_NAMESPACE_KEY = namespaceKey;
|
|
677
|
+
return values;
|
|
678
|
+
}
|
|
679
|
+
function applyIdentityEnv(workspaceRoot) {
|
|
680
|
+
const previous = new Map;
|
|
681
|
+
for (const key of [...PUBLIC_IDENTITY_ENV_KEYS, ...RIG_STATE_ENV_KEYS])
|
|
682
|
+
previous.set(key, process.env[key]);
|
|
683
|
+
const stateDir = resolve(workspaceRoot, ".rig", "state");
|
|
684
|
+
const auth = readJsonRecord(resolve(stateDir, "github-auth.json"));
|
|
685
|
+
const connection = readJsonRecord(resolve(stateDir, "connection.json"));
|
|
686
|
+
const projectLink = readJsonRecord(resolve(stateDir, "project-link.json"));
|
|
687
|
+
const values = { ...resolveIdentityEnv(workspaceRoot) };
|
|
688
|
+
values.RIG_GITHUB_AUTH_STATE_FILE = resolve(stateDir, "github-auth.json");
|
|
689
|
+
const hasLocalIdentityState = Boolean(auth || connection || projectLink);
|
|
690
|
+
for (const key of [...PUBLIC_IDENTITY_ENV_KEYS, ...RIG_STATE_ENV_KEYS]) {
|
|
691
|
+
const value = values[key];
|
|
692
|
+
if (value)
|
|
693
|
+
process.env[key] = value;
|
|
694
|
+
else if (hasLocalIdentityState)
|
|
695
|
+
delete process.env[key];
|
|
696
|
+
}
|
|
697
|
+
return () => {
|
|
698
|
+
for (const key of [...PUBLIC_IDENTITY_ENV_KEYS, ...RIG_STATE_ENV_KEYS]) {
|
|
699
|
+
const value = previous.get(key);
|
|
700
|
+
if (value === undefined)
|
|
701
|
+
delete process.env[key];
|
|
702
|
+
else
|
|
703
|
+
process.env[key] = value;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
function identityFilterFromEnv() {
|
|
708
|
+
return {
|
|
709
|
+
...process.env.RIG_SELECTED_REPO ? { selectedRepo: process.env.RIG_SELECTED_REPO } : {},
|
|
710
|
+
...process.env.RIG_GITHUB_USER_ID ? { githubUserId: process.env.RIG_GITHUB_USER_ID } : {},
|
|
711
|
+
...process.env.RIG_GITHUB_NAMESPACE_KEY ? { namespaceKey: process.env.RIG_GITHUB_NAMESPACE_KEY } : {}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
function stripRunChildCredentialEnv(env = process.env) {
|
|
715
|
+
const next = { ...env };
|
|
716
|
+
for (const key of RUN_CHILD_STRIPPED_CREDENTIAL_ENV_KEYS)
|
|
717
|
+
delete next[key];
|
|
718
|
+
return next;
|
|
719
|
+
}
|
|
720
|
+
var PUBLIC_IDENTITY_ENV_KEYS, RIG_STATE_ENV_KEYS, RUN_CHILD_STRIPPED_CREDENTIAL_ENV_KEYS;
|
|
721
|
+
var init_identity_env = __esm(() => {
|
|
722
|
+
init_identity();
|
|
723
|
+
PUBLIC_IDENTITY_ENV_KEYS = [
|
|
724
|
+
"RIG_SELECTED_REPO",
|
|
725
|
+
"RIG_GITHUB_USER_ID",
|
|
726
|
+
"RIG_GITHUB_LOGIN",
|
|
727
|
+
"RIG_GITHUB_NAMESPACE_KEY"
|
|
728
|
+
];
|
|
729
|
+
RIG_STATE_ENV_KEYS = [
|
|
730
|
+
"RIG_GITHUB_AUTH_STATE_FILE"
|
|
731
|
+
];
|
|
732
|
+
RUN_CHILD_STRIPPED_CREDENTIAL_ENV_KEYS = [
|
|
733
|
+
"RIG_GITHUB_TOKEN",
|
|
734
|
+
"RIG_GITHUB_SELECTED_TOKEN",
|
|
735
|
+
"GH_TOKEN",
|
|
736
|
+
"GITHUB_TOKEN"
|
|
737
|
+
];
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// packages/github-provider-plugin/src/service.ts
|
|
741
|
+
var exports_service = {};
|
|
742
|
+
__export(exports_service, {
|
|
743
|
+
githubProviderService: () => githubProviderService
|
|
744
|
+
});
|
|
745
|
+
import { createGitHubCredentialProvider } from "@rig/github-lib";
|
|
746
|
+
var githubProviderService;
|
|
747
|
+
var init_service = __esm(() => {
|
|
748
|
+
githubProviderService = {
|
|
749
|
+
createCredentialProvider: (options) => createGitHubCredentialProvider(options)
|
|
750
|
+
};
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// packages/github-provider-plugin/src/index.ts
|
|
754
|
+
init_issue_analysis();
|
|
755
|
+
init_triage_run();
|
|
756
|
+
|
|
757
|
+
export * from "@rig/github-lib";
|
|
758
|
+
|
|
1194
759
|
// packages/github-provider-plugin/src/plugin.ts
|
|
760
|
+
import { spawnSync } from "child_process";
|
|
1195
761
|
import { definePlugin } from "@rig/core/config";
|
|
1196
|
-
import {
|
|
762
|
+
import { defineCapability } from "@rig/core/capability";
|
|
763
|
+
import {
|
|
764
|
+
GITHUB_PROVIDER_CAPABILITY_ID,
|
|
765
|
+
ISSUE_TRIAGE,
|
|
766
|
+
RUN_IDENTITY_ENV
|
|
767
|
+
} from "@rig/contracts";
|
|
768
|
+
var IssueTriageCap = defineCapability(ISSUE_TRIAGE);
|
|
769
|
+
var issueTriageService = async () => {
|
|
770
|
+
const { runIssueAnalysisTriage: runIssueAnalysisTriage2 } = await Promise.resolve().then(() => (init_triage_run(), exports_triage_run));
|
|
771
|
+
return {
|
|
772
|
+
runTriage: (input) => runIssueAnalysisTriage2({ projectRoot: input.projectRoot, ...input.reason !== undefined ? { reason: input.reason } : {} })
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
var RunIdentityEnvCap = defineCapability(RUN_IDENTITY_ENV);
|
|
776
|
+
var runIdentityEnvService = async () => {
|
|
777
|
+
const {
|
|
778
|
+
resolveIdentityEnv: resolveIdentityEnv2,
|
|
779
|
+
applyIdentityEnv: applyIdentityEnv2,
|
|
780
|
+
identityFilterFromEnv: identityFilterFromEnv2,
|
|
781
|
+
stripRunChildCredentialEnv: stripRunChildCredentialEnv2,
|
|
782
|
+
resolveRigIdentity: resolveRigIdentity2,
|
|
783
|
+
resolveRigIdentityFilter: resolveRigIdentityFilter2
|
|
784
|
+
} = await Promise.resolve().then(() => (init_identity_env(), exports_identity_env));
|
|
785
|
+
return {
|
|
786
|
+
resolveIdentityEnv: resolveIdentityEnv2,
|
|
787
|
+
applyIdentityEnv: applyIdentityEnv2,
|
|
788
|
+
identityFilterFromEnv: identityFilterFromEnv2,
|
|
789
|
+
stripRunChildCredentialEnv: stripRunChildCredentialEnv2,
|
|
790
|
+
resolveRigIdentity: resolveRigIdentity2,
|
|
791
|
+
resolveRigIdentityFilter: resolveRigIdentityFilter2
|
|
792
|
+
};
|
|
793
|
+
};
|
|
1197
794
|
var GITHUB_PROVIDER_PLUGIN_NAME = "@rig/github-provider-plugin";
|
|
795
|
+
function parseGitHubSlugFromRemote(remoteUrl) {
|
|
796
|
+
const match = remoteUrl.trim().match(/github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i);
|
|
797
|
+
return match ? `${match[1]}/${match[2]}` : null;
|
|
798
|
+
}
|
|
799
|
+
function detectGitHubRepoSlug(projectRoot) {
|
|
800
|
+
try {
|
|
801
|
+
const result = spawnSync("git", ["-C", projectRoot, "remote", "get-url", "origin"], {
|
|
802
|
+
encoding: "utf8",
|
|
803
|
+
timeout: 5000
|
|
804
|
+
});
|
|
805
|
+
if (result.status !== 0 || result.error)
|
|
806
|
+
return null;
|
|
807
|
+
return parseGitHubSlugFromRemote(result.stdout.trim());
|
|
808
|
+
} catch {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function githubConfigDefaults(context) {
|
|
813
|
+
const slug = context.repoSlug ?? detectGitHubRepoSlug(context.projectRoot);
|
|
814
|
+
if (!slug)
|
|
815
|
+
return null;
|
|
816
|
+
const [owner, repo] = slug.split("/", 2);
|
|
817
|
+
if (!owner || !repo)
|
|
818
|
+
return null;
|
|
819
|
+
return {
|
|
820
|
+
project: { name: slug, repo: slug },
|
|
821
|
+
taskSource: { kind: "github-issues", owner, repo, state: "open" },
|
|
822
|
+
github: { issueUpdates: "lifecycle", projects: { enabled: false } },
|
|
823
|
+
standard: {
|
|
824
|
+
githubWorkspaceId: slug,
|
|
825
|
+
githubUserId: process.env.RIG_GITHUB_USER_ID ?? "server-selected-user"
|
|
826
|
+
},
|
|
827
|
+
issueAnalysis: { enabled: true, harness: "pi", mode: "continuous" }
|
|
828
|
+
};
|
|
829
|
+
}
|
|
1198
830
|
var githubProviderPlugin = definePlugin({
|
|
1199
831
|
name: GITHUB_PROVIDER_PLUGIN_NAME,
|
|
1200
832
|
version: "0.0.0-alpha.1",
|
|
@@ -1205,43 +837,31 @@ var githubProviderPlugin = definePlugin({
|
|
|
1205
837
|
title: "GitHub SCM provider",
|
|
1206
838
|
description: "Resolve GitHub credentials for the configured SCM provider.",
|
|
1207
839
|
run: async () => (await Promise.resolve().then(() => (init_service(), exports_service))).githubProviderService
|
|
1208
|
-
}
|
|
1209
|
-
|
|
840
|
+
},
|
|
841
|
+
IssueTriageCap.provide(issueTriageService, { title: "GitHub issue-analysis triage" }),
|
|
842
|
+
RunIdentityEnvCap.provide(runIdentityEnvService, {
|
|
843
|
+
title: "GitHub/Rig run identity env",
|
|
844
|
+
description: "Resolve GitHub/Rig identity, hydrate public identity env, and strip run-scoped credential tokens."
|
|
845
|
+
})
|
|
846
|
+
],
|
|
847
|
+
config: { defaults: githubConfigDefaults }
|
|
1210
848
|
}
|
|
1211
849
|
});
|
|
1212
850
|
function createGitHubProviderPlugin() {
|
|
1213
851
|
return githubProviderPlugin;
|
|
1214
852
|
}
|
|
1215
853
|
export {
|
|
1216
|
-
updateIssueProjectStatus,
|
|
1217
|
-
saveGitHubTokenForProject,
|
|
1218
854
|
runIssueAnalysisTriage,
|
|
1219
|
-
resolveProjectStatusField,
|
|
1220
|
-
resolveGitHubAuthStatus,
|
|
1221
|
-
resolveGitHubAuthStateFile,
|
|
1222
855
|
renderIssueAnalysisPrompt,
|
|
1223
|
-
probeGitHubRepository,
|
|
1224
|
-
pollGitHubDeviceFlow,
|
|
1225
856
|
parseIssueAnalysisResult,
|
|
1226
|
-
listGitHubProjects,
|
|
1227
857
|
issueAnalysisEnabled,
|
|
1228
858
|
githubProviderPlugin,
|
|
1229
|
-
fetchGitHubUserInfo,
|
|
1230
|
-
ensureIssueProjectItem,
|
|
1231
|
-
createStateGitHubCredentialProvider,
|
|
1232
859
|
createPiIssueAnalyzer,
|
|
1233
860
|
createIssueAnalysisWriteBack,
|
|
1234
861
|
createIssueAnalysisService,
|
|
1235
862
|
createGitHubProviderPlugin,
|
|
1236
|
-
createGitHubCredentialProvider,
|
|
1237
|
-
createGitHubAuthStoreFromStateFile,
|
|
1238
|
-
createGitHubAuthStore,
|
|
1239
|
-
createEnvGitHubCredentialProvider,
|
|
1240
863
|
createDefaultPiIssueAnalysisCommandRunner,
|
|
1241
864
|
createContinuousIssueAnalysisRunner,
|
|
1242
865
|
createConfiguredIssueAnalysisRunner,
|
|
1243
|
-
copyGitHubAuthStateToLocalProjectRoot,
|
|
1244
|
-
checkGitHubRepoPermissions,
|
|
1245
|
-
beginGitHubDeviceFlow,
|
|
1246
866
|
GITHUB_PROVIDER_PLUGIN_NAME
|
|
1247
867
|
};
|