@haystackeditor/cli 0.13.0 → 0.13.2
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/commands/config.js +7 -5
- package/dist/commands/dismiss.js +16 -10
- package/dist/commands/login.d.ts +4 -5
- package/dist/commands/login.js +95 -71
- package/dist/commands/pr-status.js +8 -4
- package/dist/commands/request-review.js +10 -8
- package/dist/commands/setup.js +8 -4
- package/dist/commands/submit.d.ts +1 -0
- package/dist/commands/submit.js +123 -19
- package/dist/commands/triage.js +12 -3
- package/dist/index.js +22 -5
- package/dist/utils/auth.d.ts +50 -0
- package/dist/utils/auth.js +386 -0
- package/dist/utils/git.d.ts +15 -2
- package/dist/utils/git.js +72 -11
- package/dist/utils/github-api.d.ts +36 -15
- package/dist/utils/github-api.js +52 -44
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@ export interface CreatePROptions {
|
|
|
11
11
|
base: string;
|
|
12
12
|
body?: string;
|
|
13
13
|
draft?: boolean;
|
|
14
|
+
token?: string;
|
|
14
15
|
}
|
|
15
16
|
export interface PRResponse {
|
|
16
17
|
number: number;
|
|
@@ -29,11 +30,33 @@ export interface LabelInfo {
|
|
|
29
30
|
color: string;
|
|
30
31
|
description?: string;
|
|
31
32
|
}
|
|
33
|
+
export interface GitHubUser {
|
|
34
|
+
login: string;
|
|
35
|
+
id: number;
|
|
36
|
+
}
|
|
37
|
+
export interface RepositoryPermissions {
|
|
38
|
+
admin?: boolean;
|
|
39
|
+
maintain?: boolean;
|
|
40
|
+
push?: boolean;
|
|
41
|
+
triage?: boolean;
|
|
42
|
+
pull?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface RepositoryAccess {
|
|
45
|
+
full_name: string;
|
|
46
|
+
owner: {
|
|
47
|
+
login: string;
|
|
48
|
+
};
|
|
49
|
+
permissions?: RepositoryPermissions;
|
|
50
|
+
}
|
|
32
51
|
export declare const HAYSTACK_LABELS: Record<string, LabelInfo>;
|
|
33
52
|
/**
|
|
34
|
-
* Get
|
|
53
|
+
* Get a GitHub token, throw if not authenticated
|
|
35
54
|
*/
|
|
36
|
-
export declare function requireGitHubAuth(
|
|
55
|
+
export declare function requireGitHubAuth(options?: {
|
|
56
|
+
preferredLogin?: string;
|
|
57
|
+
owner?: string;
|
|
58
|
+
repo?: string;
|
|
59
|
+
}): Promise<string>;
|
|
37
60
|
/**
|
|
38
61
|
* Create a pull request
|
|
39
62
|
*/
|
|
@@ -41,43 +64,41 @@ export declare function createPullRequest(options: CreatePROptions): Promise<PRR
|
|
|
41
64
|
/**
|
|
42
65
|
* Check if a PR already exists for a branch
|
|
43
66
|
*/
|
|
44
|
-
export declare function findExistingPR(owner: string, repo: string, head: string): Promise<PRResponse | null>;
|
|
67
|
+
export declare function findExistingPR(owner: string, repo: string, head: string, token?: string): Promise<PRResponse | null>;
|
|
45
68
|
/**
|
|
46
69
|
* Add labels to an issue/PR
|
|
47
70
|
*/
|
|
48
|
-
export declare function addLabelsToIssue(owner: string, repo: string, issueNumber: number, labels: string[]): Promise<void>;
|
|
71
|
+
export declare function addLabelsToIssue(owner: string, repo: string, issueNumber: number, labels: string[], token?: string): Promise<void>;
|
|
49
72
|
/**
|
|
50
73
|
* Get all labels from an issue/PR
|
|
51
74
|
*/
|
|
52
|
-
export declare function getLabelsFromIssue(owner: string, repo: string, issueNumber: number): Promise<string[]>;
|
|
75
|
+
export declare function getLabelsFromIssue(owner: string, repo: string, issueNumber: number, token?: string): Promise<string[]>;
|
|
53
76
|
/**
|
|
54
77
|
* Remove a label from an issue/PR
|
|
55
78
|
*/
|
|
56
|
-
export declare function removeLabelFromIssue(owner: string, repo: string, issueNumber: number, labelName: string): Promise<void>;
|
|
79
|
+
export declare function removeLabelFromIssue(owner: string, repo: string, issueNumber: number, labelName: string, token?: string): Promise<void>;
|
|
57
80
|
/**
|
|
58
81
|
* Check if a label exists in the repo
|
|
59
82
|
*/
|
|
60
|
-
export declare function labelExists(owner: string, repo: string, labelName: string): Promise<boolean>;
|
|
83
|
+
export declare function labelExists(owner: string, repo: string, labelName: string, token?: string): Promise<boolean>;
|
|
61
84
|
/**
|
|
62
85
|
* Create a label in the repo
|
|
63
86
|
*/
|
|
64
|
-
export declare function createLabel(owner: string, repo: string, label: LabelInfo): Promise<void>;
|
|
87
|
+
export declare function createLabel(owner: string, repo: string, label: LabelInfo, token?: string): Promise<void>;
|
|
65
88
|
/**
|
|
66
89
|
* Ensure all required Haystack labels exist in the repo
|
|
67
90
|
*/
|
|
68
|
-
export declare function ensureHaystackLabels(owner: string, repo: string, labelNames: string[]): Promise<void>;
|
|
91
|
+
export declare function ensureHaystackLabels(owner: string, repo: string, labelNames: string[], token?: string): Promise<void>;
|
|
69
92
|
/**
|
|
70
93
|
* Request reviewers on a pull request
|
|
71
94
|
*/
|
|
72
|
-
export declare function requestReviewers(owner: string, repo: string, prNumber: number, reviewers: string[]): Promise<void>;
|
|
95
|
+
export declare function requestReviewers(owner: string, repo: string, prNumber: number, reviewers: string[], token?: string): Promise<void>;
|
|
73
96
|
/**
|
|
74
97
|
* Get the authenticated user's info
|
|
75
98
|
*/
|
|
76
|
-
export declare function getCurrentUser(): Promise<
|
|
77
|
-
|
|
78
|
-
id: number;
|
|
79
|
-
}>;
|
|
99
|
+
export declare function getCurrentUser(token?: string): Promise<GitHubUser>;
|
|
100
|
+
export declare function getRepositoryAccess(owner: string, repo: string, token?: string): Promise<RepositoryAccess>;
|
|
80
101
|
/**
|
|
81
102
|
* Check if user has push access to a repo
|
|
82
103
|
*/
|
|
83
|
-
export declare function hasPushAccess(owner: string, repo: string): Promise<boolean>;
|
|
104
|
+
export declare function hasPushAccess(owner: string, repo: string, token?: string): Promise<boolean>;
|
package/dist/utils/github-api.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides functions for creating PRs, managing labels, etc.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { resolveAuthContext } from './auth.js';
|
|
7
7
|
const GITHUB_API_BASE = 'https://api.github.com';
|
|
8
8
|
// Label definitions for Haystack workflow
|
|
9
9
|
export const HAYSTACK_LABELS = {
|
|
@@ -27,14 +27,14 @@ export const HAYSTACK_LABELS = {
|
|
|
27
27
|
// Authentication
|
|
28
28
|
// ============================================================================
|
|
29
29
|
/**
|
|
30
|
-
* Get
|
|
30
|
+
* Get a GitHub token, throw if not authenticated
|
|
31
31
|
*/
|
|
32
|
-
export async function requireGitHubAuth() {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return token;
|
|
32
|
+
export async function requireGitHubAuth(options) {
|
|
33
|
+
const context = await resolveAuthContext(options);
|
|
34
|
+
return context.token;
|
|
35
|
+
}
|
|
36
|
+
async function resolveToken(token, options) {
|
|
37
|
+
return token ?? requireGitHubAuth(options);
|
|
38
38
|
}
|
|
39
39
|
// ============================================================================
|
|
40
40
|
// API helpers
|
|
@@ -88,7 +88,10 @@ async function handleApiError(response, context) {
|
|
|
88
88
|
* Create a pull request
|
|
89
89
|
*/
|
|
90
90
|
export async function createPullRequest(options) {
|
|
91
|
-
const token = await
|
|
91
|
+
const token = await resolveToken(options.token, {
|
|
92
|
+
owner: options.owner,
|
|
93
|
+
repo: options.repo,
|
|
94
|
+
});
|
|
92
95
|
const response = await githubApiRequest('POST', `/repos/${options.owner}/${options.repo}/pulls`, token, {
|
|
93
96
|
title: options.title,
|
|
94
97
|
head: options.head,
|
|
@@ -139,11 +142,11 @@ async function findMergedPRForBranch(owner, repo, head, token) {
|
|
|
139
142
|
/**
|
|
140
143
|
* Check if a PR already exists for a branch
|
|
141
144
|
*/
|
|
142
|
-
export async function findExistingPR(owner, repo, head) {
|
|
143
|
-
const
|
|
145
|
+
export async function findExistingPR(owner, repo, head, token) {
|
|
146
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
144
147
|
// head format should be "owner:branch" for cross-repo or just "branch" for same-repo
|
|
145
148
|
const headParam = head.includes(':') ? head : `${owner}:${head}`;
|
|
146
|
-
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/pulls?head=${encodeURIComponent(headParam)}&state=open`,
|
|
149
|
+
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/pulls?head=${encodeURIComponent(headParam)}&state=open`, authToken);
|
|
147
150
|
if (!response.ok) {
|
|
148
151
|
if (response.status === 404)
|
|
149
152
|
return null;
|
|
@@ -158,9 +161,9 @@ export async function findExistingPR(owner, repo, head) {
|
|
|
158
161
|
/**
|
|
159
162
|
* Add labels to an issue/PR
|
|
160
163
|
*/
|
|
161
|
-
export async function addLabelsToIssue(owner, repo, issueNumber, labels) {
|
|
162
|
-
const
|
|
163
|
-
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
|
|
164
|
+
export async function addLabelsToIssue(owner, repo, issueNumber, labels, token) {
|
|
165
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
166
|
+
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/issues/${issueNumber}/labels`, authToken, { labels });
|
|
164
167
|
if (!response.ok) {
|
|
165
168
|
await handleApiError(response, `adding labels to PR #${issueNumber}`);
|
|
166
169
|
}
|
|
@@ -168,9 +171,9 @@ export async function addLabelsToIssue(owner, repo, issueNumber, labels) {
|
|
|
168
171
|
/**
|
|
169
172
|
* Get all labels from an issue/PR
|
|
170
173
|
*/
|
|
171
|
-
export async function getLabelsFromIssue(owner, repo, issueNumber) {
|
|
172
|
-
const
|
|
173
|
-
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
|
|
174
|
+
export async function getLabelsFromIssue(owner, repo, issueNumber, token) {
|
|
175
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
176
|
+
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/issues/${issueNumber}/labels`, authToken);
|
|
174
177
|
if (!response.ok) {
|
|
175
178
|
await handleApiError(response, `getting labels from PR #${issueNumber}`);
|
|
176
179
|
}
|
|
@@ -180,9 +183,9 @@ export async function getLabelsFromIssue(owner, repo, issueNumber) {
|
|
|
180
183
|
/**
|
|
181
184
|
* Remove a label from an issue/PR
|
|
182
185
|
*/
|
|
183
|
-
export async function removeLabelFromIssue(owner, repo, issueNumber, labelName) {
|
|
184
|
-
const
|
|
185
|
-
const response = await githubApiRequest('DELETE', `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(labelName)}`,
|
|
186
|
+
export async function removeLabelFromIssue(owner, repo, issueNumber, labelName, token) {
|
|
187
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
188
|
+
const response = await githubApiRequest('DELETE', `/repos/${owner}/${repo}/issues/${issueNumber}/labels/${encodeURIComponent(labelName)}`, authToken);
|
|
186
189
|
// 404 is fine - label wasn't on the issue
|
|
187
190
|
if (!response.ok && response.status !== 404) {
|
|
188
191
|
await handleApiError(response, `removing label '${labelName}' from PR #${issueNumber}`);
|
|
@@ -191,17 +194,17 @@ export async function removeLabelFromIssue(owner, repo, issueNumber, labelName)
|
|
|
191
194
|
/**
|
|
192
195
|
* Check if a label exists in the repo
|
|
193
196
|
*/
|
|
194
|
-
export async function labelExists(owner, repo, labelName) {
|
|
195
|
-
const
|
|
196
|
-
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/labels/${encodeURIComponent(labelName)}`,
|
|
197
|
+
export async function labelExists(owner, repo, labelName, token) {
|
|
198
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
199
|
+
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}/labels/${encodeURIComponent(labelName)}`, authToken);
|
|
197
200
|
return response.ok;
|
|
198
201
|
}
|
|
199
202
|
/**
|
|
200
203
|
* Create a label in the repo
|
|
201
204
|
*/
|
|
202
|
-
export async function createLabel(owner, repo, label) {
|
|
203
|
-
const
|
|
204
|
-
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/labels`,
|
|
205
|
+
export async function createLabel(owner, repo, label, token) {
|
|
206
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
207
|
+
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/labels`, authToken, {
|
|
205
208
|
name: label.name,
|
|
206
209
|
color: label.color,
|
|
207
210
|
description: label.description,
|
|
@@ -214,14 +217,14 @@ export async function createLabel(owner, repo, label) {
|
|
|
214
217
|
/**
|
|
215
218
|
* Ensure all required Haystack labels exist in the repo
|
|
216
219
|
*/
|
|
217
|
-
export async function ensureHaystackLabels(owner, repo, labelNames) {
|
|
220
|
+
export async function ensureHaystackLabels(owner, repo, labelNames, token) {
|
|
218
221
|
for (const name of labelNames) {
|
|
219
222
|
const labelInfo = HAYSTACK_LABELS[name];
|
|
220
223
|
if (!labelInfo)
|
|
221
224
|
continue;
|
|
222
|
-
const exists = await labelExists(owner, repo, name);
|
|
225
|
+
const exists = await labelExists(owner, repo, name, token);
|
|
223
226
|
if (!exists) {
|
|
224
|
-
await createLabel(owner, repo, labelInfo);
|
|
227
|
+
await createLabel(owner, repo, labelInfo, token);
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
}
|
|
@@ -231,9 +234,9 @@ export async function ensureHaystackLabels(owner, repo, labelNames) {
|
|
|
231
234
|
/**
|
|
232
235
|
* Request reviewers on a pull request
|
|
233
236
|
*/
|
|
234
|
-
export async function requestReviewers(owner, repo, prNumber, reviewers) {
|
|
235
|
-
const
|
|
236
|
-
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/pulls/${prNumber}/requested_reviewers`,
|
|
237
|
+
export async function requestReviewers(owner, repo, prNumber, reviewers, token) {
|
|
238
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
239
|
+
const response = await githubApiRequest('POST', `/repos/${owner}/${repo}/pulls/${prNumber}/requested_reviewers`, authToken, { reviewers });
|
|
237
240
|
if (!response.ok) {
|
|
238
241
|
await handleApiError(response, `requesting reviewers on PR #${prNumber}`);
|
|
239
242
|
}
|
|
@@ -244,23 +247,28 @@ export async function requestReviewers(owner, repo, prNumber, reviewers) {
|
|
|
244
247
|
/**
|
|
245
248
|
* Get the authenticated user's info
|
|
246
249
|
*/
|
|
247
|
-
export async function getCurrentUser() {
|
|
248
|
-
const
|
|
249
|
-
const response = await githubApiRequest('GET', '/user',
|
|
250
|
+
export async function getCurrentUser(token) {
|
|
251
|
+
const authToken = await resolveToken(token);
|
|
252
|
+
const response = await githubApiRequest('GET', '/user', authToken);
|
|
250
253
|
if (!response.ok) {
|
|
251
254
|
await handleApiError(response, 'getting current user');
|
|
252
255
|
}
|
|
253
256
|
return response.json();
|
|
254
257
|
}
|
|
258
|
+
export async function getRepositoryAccess(owner, repo, token) {
|
|
259
|
+
const authToken = await resolveToken(token, { owner, repo });
|
|
260
|
+
const response = await githubApiRequest('GET', `/repos/${owner}/${repo}`, authToken);
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
await handleApiError(response, `getting repository access for ${owner}/${repo}`);
|
|
263
|
+
}
|
|
264
|
+
return response.json();
|
|
265
|
+
}
|
|
255
266
|
/**
|
|
256
267
|
* Check if user has push access to a repo
|
|
257
268
|
*/
|
|
258
|
-
export async function hasPushAccess(owner, repo) {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
const data = await response.json();
|
|
265
|
-
return data.permissions?.push === true;
|
|
269
|
+
export async function hasPushAccess(owner, repo, token) {
|
|
270
|
+
const data = await getRepositoryAccess(owner, repo, token);
|
|
271
|
+
return data.permissions?.admin === true
|
|
272
|
+
|| data.permissions?.maintain === true
|
|
273
|
+
|| data.permissions?.push === true;
|
|
266
274
|
}
|