gh-manager-cli 1.6.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/chunk-OQGG2X5P.js +648 -0
- package/dist/github-TWXF5AWM.js +29 -0
- package/dist/index.js +460 -781
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.8.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.7.0...v1.8.0) (2025-08-31)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* implement organization support feature ([#1](https://github.com/wiiiimm/gh-manager-cli/issues/1)) ([5a404b2](https://github.com/wiiiimm/gh-manager-cli/commit/5a404b29cdd3a30bd12b59438e0c3fd978da93b9))
|
|
7
|
+
|
|
8
|
+
# [1.7.0](https://github.com/wiiiimm/gh-manager-cli/compare/v1.6.2...v1.7.0) (2025-08-31)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* implement smarter infinite scroll prefetch trigger at 80% ([d124b94](https://github.com/wiiiimm/gh-manager-cli/commit/d124b9483196fe703ccffaea1ffdecef9cca31a5))
|
|
14
|
+
|
|
1
15
|
## [1.6.2](https://github.com/wiiiimm/gh-manager-cli/compare/v1.6.1...v1.6.2) (2025-08-31)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
4
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/github.ts
|
|
8
|
+
import { graphql as makeGraphQL } from "@octokit/graphql";
|
|
9
|
+
import { ApolloClient, InMemoryCache, HttpLink, gql } from "@apollo/client/core/index.js";
|
|
10
|
+
import { persistCache } from "apollo3-cache-persist";
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import envPaths from "env-paths";
|
|
14
|
+
function makeClient(token) {
|
|
15
|
+
return makeGraphQL.defaults({
|
|
16
|
+
headers: { authorization: `token ${token}` }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async function makeApolloClient(token) {
|
|
20
|
+
try {
|
|
21
|
+
if (typeof globalThis.fetch === "undefined") {
|
|
22
|
+
throw new Error("Fetch API not available. Node 18+ is required.");
|
|
23
|
+
}
|
|
24
|
+
const cache = new InMemoryCache();
|
|
25
|
+
const storage = {
|
|
26
|
+
async getItem(key) {
|
|
27
|
+
try {
|
|
28
|
+
const p = envPaths("gh-manager-cli").data;
|
|
29
|
+
const file = path.join(p, "apollo-cache.json");
|
|
30
|
+
return fs.readFileSync(file, "utf8");
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async setItem(key, value) {
|
|
36
|
+
try {
|
|
37
|
+
const p = envPaths("gh-manager-cli").data;
|
|
38
|
+
fs.mkdirSync(p, { recursive: true });
|
|
39
|
+
const file = path.join(p, "apollo-cache.json");
|
|
40
|
+
fs.writeFileSync(file, value, "utf8");
|
|
41
|
+
if (process.platform !== "win32") {
|
|
42
|
+
try {
|
|
43
|
+
fs.chmodSync(file, 384);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async removeItem(key) {
|
|
51
|
+
try {
|
|
52
|
+
const p = envPaths("gh-manager-cli").data;
|
|
53
|
+
const file = path.join(p, "apollo-cache.json");
|
|
54
|
+
fs.unlinkSync(file);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
await persistCache({ cache, storage, debounce: 500, maxSize: 5 * 1024 * 1024 });
|
|
60
|
+
const link = new HttpLink({
|
|
61
|
+
uri: "https://api.github.com/graphql",
|
|
62
|
+
fetch: globalThis.fetch,
|
|
63
|
+
headers: { authorization: `Bearer ${token}` }
|
|
64
|
+
});
|
|
65
|
+
const client = new ApolloClient({ cache, link });
|
|
66
|
+
return { client, gql };
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const debug = process.env.GH_MANAGER_DEBUG === "1";
|
|
69
|
+
if (debug) {
|
|
70
|
+
process.stderr.write(`
|
|
71
|
+
\u274C Failed to initialize Apollo Client: ${error.message}
|
|
72
|
+
`);
|
|
73
|
+
if (error.stack) {
|
|
74
|
+
process.stderr.write(`Stack: ${error.stack}
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Apollo Client initialization failed: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function getViewerLogin(client) {
|
|
82
|
+
const query = (
|
|
83
|
+
/* GraphQL */
|
|
84
|
+
`
|
|
85
|
+
query ViewerLogin {
|
|
86
|
+
viewer {
|
|
87
|
+
login
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`
|
|
91
|
+
);
|
|
92
|
+
const res = await client(query);
|
|
93
|
+
return res.viewer.login;
|
|
94
|
+
}
|
|
95
|
+
async function fetchViewerOrganizations(client) {
|
|
96
|
+
const query = (
|
|
97
|
+
/* GraphQL */
|
|
98
|
+
`
|
|
99
|
+
query ViewerOrganizations {
|
|
100
|
+
viewer {
|
|
101
|
+
organizations(first: 100) {
|
|
102
|
+
nodes {
|
|
103
|
+
id
|
|
104
|
+
login
|
|
105
|
+
name
|
|
106
|
+
avatarUrl
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
`
|
|
112
|
+
);
|
|
113
|
+
const res = await client(query);
|
|
114
|
+
return res.viewer.organizations.nodes;
|
|
115
|
+
}
|
|
116
|
+
async function fetchViewerReposPage(client, first, after, orderBy, includeForkTracking = true, ownerAffiliations = ["OWNER"], organizationLogin) {
|
|
117
|
+
const sortField = orderBy?.field || "UPDATED_AT";
|
|
118
|
+
const sortDirection = orderBy?.direction || "DESC";
|
|
119
|
+
const isOrgContext = !!organizationLogin;
|
|
120
|
+
if (isOrgContext) {
|
|
121
|
+
const query2 = (
|
|
122
|
+
/* GraphQL */
|
|
123
|
+
`
|
|
124
|
+
query OrgRepos(
|
|
125
|
+
$first: Int!
|
|
126
|
+
$after: String
|
|
127
|
+
$sortField: RepositoryOrderField!
|
|
128
|
+
$sortDirection: OrderDirection!
|
|
129
|
+
$orgLogin: String!
|
|
130
|
+
) {
|
|
131
|
+
rateLimit {
|
|
132
|
+
limit
|
|
133
|
+
remaining
|
|
134
|
+
resetAt
|
|
135
|
+
}
|
|
136
|
+
organization(login: $orgLogin) {
|
|
137
|
+
repositories(
|
|
138
|
+
first: $first
|
|
139
|
+
after: $after
|
|
140
|
+
orderBy: { field: $sortField, direction: $sortDirection }
|
|
141
|
+
) {
|
|
142
|
+
totalCount
|
|
143
|
+
pageInfo {
|
|
144
|
+
endCursor
|
|
145
|
+
hasNextPage
|
|
146
|
+
}
|
|
147
|
+
nodes {
|
|
148
|
+
id
|
|
149
|
+
name
|
|
150
|
+
nameWithOwner
|
|
151
|
+
description
|
|
152
|
+
visibility
|
|
153
|
+
isPrivate
|
|
154
|
+
isFork
|
|
155
|
+
isArchived
|
|
156
|
+
stargazerCount
|
|
157
|
+
forkCount
|
|
158
|
+
primaryLanguage {
|
|
159
|
+
name
|
|
160
|
+
color
|
|
161
|
+
}
|
|
162
|
+
updatedAt
|
|
163
|
+
pushedAt
|
|
164
|
+
diskUsage
|
|
165
|
+
${includeForkTracking ? `
|
|
166
|
+
parent {
|
|
167
|
+
nameWithOwner
|
|
168
|
+
defaultBranchRef {
|
|
169
|
+
name
|
|
170
|
+
target {
|
|
171
|
+
... on Commit {
|
|
172
|
+
history(first: 0) {
|
|
173
|
+
totalCount
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
defaultBranchRef {
|
|
180
|
+
name
|
|
181
|
+
target {
|
|
182
|
+
... on Commit {
|
|
183
|
+
history(first: 0) {
|
|
184
|
+
totalCount
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}` : `
|
|
189
|
+
parent {
|
|
190
|
+
nameWithOwner
|
|
191
|
+
}
|
|
192
|
+
defaultBranchRef { name }
|
|
193
|
+
`}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
`
|
|
199
|
+
);
|
|
200
|
+
const res2 = await client(query2, {
|
|
201
|
+
first,
|
|
202
|
+
after: after ?? null,
|
|
203
|
+
sortField,
|
|
204
|
+
sortDirection,
|
|
205
|
+
orgLogin: organizationLogin
|
|
206
|
+
});
|
|
207
|
+
const data2 = res2.organization.repositories;
|
|
208
|
+
return {
|
|
209
|
+
nodes: data2.nodes,
|
|
210
|
+
endCursor: data2.pageInfo.endCursor,
|
|
211
|
+
hasNextPage: data2.pageInfo.hasNextPage,
|
|
212
|
+
totalCount: data2.totalCount,
|
|
213
|
+
rateLimit: res2.rateLimit
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const query = (
|
|
217
|
+
/* GraphQL */
|
|
218
|
+
`
|
|
219
|
+
query ViewerRepos(
|
|
220
|
+
$first: Int!
|
|
221
|
+
$after: String
|
|
222
|
+
$sortField: RepositoryOrderField!
|
|
223
|
+
$sortDirection: OrderDirection!
|
|
224
|
+
$affiliations: [RepositoryAffiliation!]!
|
|
225
|
+
) {
|
|
226
|
+
rateLimit {
|
|
227
|
+
limit
|
|
228
|
+
remaining
|
|
229
|
+
resetAt
|
|
230
|
+
}
|
|
231
|
+
viewer {
|
|
232
|
+
repositories(
|
|
233
|
+
ownerAffiliations: $affiliations
|
|
234
|
+
first: $first
|
|
235
|
+
after: $after
|
|
236
|
+
orderBy: { field: $sortField, direction: $sortDirection }
|
|
237
|
+
) {
|
|
238
|
+
totalCount
|
|
239
|
+
pageInfo {
|
|
240
|
+
endCursor
|
|
241
|
+
hasNextPage
|
|
242
|
+
}
|
|
243
|
+
nodes {
|
|
244
|
+
id
|
|
245
|
+
name
|
|
246
|
+
nameWithOwner
|
|
247
|
+
description
|
|
248
|
+
visibility
|
|
249
|
+
isPrivate
|
|
250
|
+
isFork
|
|
251
|
+
isArchived
|
|
252
|
+
stargazerCount
|
|
253
|
+
forkCount
|
|
254
|
+
primaryLanguage {
|
|
255
|
+
name
|
|
256
|
+
color
|
|
257
|
+
}
|
|
258
|
+
updatedAt
|
|
259
|
+
pushedAt
|
|
260
|
+
diskUsage
|
|
261
|
+
${includeForkTracking ? `
|
|
262
|
+
parent {
|
|
263
|
+
nameWithOwner
|
|
264
|
+
defaultBranchRef {
|
|
265
|
+
name
|
|
266
|
+
target {
|
|
267
|
+
... on Commit {
|
|
268
|
+
history(first: 0) {
|
|
269
|
+
totalCount
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
defaultBranchRef {
|
|
276
|
+
name
|
|
277
|
+
target {
|
|
278
|
+
... on Commit {
|
|
279
|
+
history(first: 0) {
|
|
280
|
+
totalCount
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}` : `
|
|
285
|
+
parent {
|
|
286
|
+
nameWithOwner
|
|
287
|
+
}
|
|
288
|
+
defaultBranchRef { name }
|
|
289
|
+
`}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
`
|
|
295
|
+
);
|
|
296
|
+
const res = await client(query, {
|
|
297
|
+
first,
|
|
298
|
+
after: after ?? null,
|
|
299
|
+
sortField,
|
|
300
|
+
sortDirection,
|
|
301
|
+
affiliations: ownerAffiliations
|
|
302
|
+
});
|
|
303
|
+
const data = res.viewer.repositories;
|
|
304
|
+
return {
|
|
305
|
+
nodes: data.nodes,
|
|
306
|
+
endCursor: data.pageInfo.endCursor,
|
|
307
|
+
hasNextPage: data.pageInfo.hasNextPage,
|
|
308
|
+
totalCount: data.totalCount,
|
|
309
|
+
rateLimit: res.rateLimit
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
async function fetchViewerReposPageUnified(token, first, after, orderBy, includeForkTracking = true, fetchPolicy = "cache-first", ownerAffiliations = ["OWNER"], organizationLogin) {
|
|
313
|
+
const isApolloEnabled = true;
|
|
314
|
+
const debug = process.env.GH_MANAGER_DEBUG === "1";
|
|
315
|
+
const isOrgContext = !!organizationLogin;
|
|
316
|
+
if (debug) {
|
|
317
|
+
console.log(`\u{1F50D} Apollo enabled: ${isApolloEnabled}, Policy: ${fetchPolicy}, After: ${after || "null"}, Context: ${isOrgContext ? "Organization" : "Personal"}`);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
if (isApolloEnabled) {
|
|
321
|
+
if (debug) console.log("\u{1F680} Attempting Apollo Client...");
|
|
322
|
+
const ap = await makeApolloClient(token);
|
|
323
|
+
const sortField = orderBy?.field || "UPDATED_AT";
|
|
324
|
+
const sortDirection = orderBy?.direction || "DESC";
|
|
325
|
+
let q;
|
|
326
|
+
let variables = { first, after: after ?? null, sortField, sortDirection };
|
|
327
|
+
if (isOrgContext) {
|
|
328
|
+
variables.orgLogin = organizationLogin;
|
|
329
|
+
q = ap.gql`
|
|
330
|
+
query OrgRepos($first: Int!, $after: String, $sortField: RepositoryOrderField!, $sortDirection: OrderDirection!, $orgLogin: String!) {
|
|
331
|
+
rateLimit { limit remaining resetAt }
|
|
332
|
+
organization(login: $orgLogin) {
|
|
333
|
+
repositories(first: $first, after: $after, orderBy: { field: $sortField, direction: $sortDirection }) {
|
|
334
|
+
totalCount
|
|
335
|
+
pageInfo { endCursor hasNextPage }
|
|
336
|
+
nodes {
|
|
337
|
+
id
|
|
338
|
+
name
|
|
339
|
+
nameWithOwner
|
|
340
|
+
description
|
|
341
|
+
visibility
|
|
342
|
+
isPrivate
|
|
343
|
+
isFork
|
|
344
|
+
isArchived
|
|
345
|
+
stargazerCount
|
|
346
|
+
forkCount
|
|
347
|
+
primaryLanguage { name color }
|
|
348
|
+
updatedAt
|
|
349
|
+
pushedAt
|
|
350
|
+
diskUsage
|
|
351
|
+
${includeForkTracking ? `
|
|
352
|
+
parent { nameWithOwner defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } } }
|
|
353
|
+
defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } }` : `
|
|
354
|
+
parent { nameWithOwner }
|
|
355
|
+
defaultBranchRef { name }`}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
`;
|
|
361
|
+
} else {
|
|
362
|
+
variables.affiliations = ownerAffiliations;
|
|
363
|
+
q = ap.gql`
|
|
364
|
+
query ViewerRepos($first: Int!, $after: String, $sortField: RepositoryOrderField!, $sortDirection: OrderDirection!, $affiliations: [RepositoryAffiliation!]!) {
|
|
365
|
+
rateLimit { limit remaining resetAt }
|
|
366
|
+
viewer {
|
|
367
|
+
repositories(ownerAffiliations: $affiliations, first: $first, after: $after, orderBy: { field: $sortField, direction: $sortDirection }) {
|
|
368
|
+
totalCount
|
|
369
|
+
pageInfo { endCursor hasNextPage }
|
|
370
|
+
nodes {
|
|
371
|
+
id
|
|
372
|
+
name
|
|
373
|
+
nameWithOwner
|
|
374
|
+
description
|
|
375
|
+
visibility
|
|
376
|
+
isPrivate
|
|
377
|
+
isFork
|
|
378
|
+
isArchived
|
|
379
|
+
stargazerCount
|
|
380
|
+
forkCount
|
|
381
|
+
primaryLanguage { name color }
|
|
382
|
+
updatedAt
|
|
383
|
+
pushedAt
|
|
384
|
+
diskUsage
|
|
385
|
+
${includeForkTracking ? `
|
|
386
|
+
parent { nameWithOwner defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } } }
|
|
387
|
+
defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } }` : `
|
|
388
|
+
parent { nameWithOwner }
|
|
389
|
+
defaultBranchRef { name }`}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
`;
|
|
395
|
+
}
|
|
396
|
+
const startTime = Date.now();
|
|
397
|
+
const res = await ap.client.query({
|
|
398
|
+
query: q,
|
|
399
|
+
variables,
|
|
400
|
+
fetchPolicy
|
|
401
|
+
});
|
|
402
|
+
const duration = Date.now() - startTime;
|
|
403
|
+
if (debug) {
|
|
404
|
+
console.log(`\u26A1 Apollo query completed in ${duration}ms`);
|
|
405
|
+
console.log(`\u{1F4CA} From cache: ${res.loading === false && duration < 50 ? "YES" : "NO"}`);
|
|
406
|
+
console.log(`\u{1F504} Network status: ${res.networkStatus}`);
|
|
407
|
+
}
|
|
408
|
+
const data = isOrgContext ? res.data.organization.repositories : res.data.viewer.repositories;
|
|
409
|
+
return {
|
|
410
|
+
nodes: data.nodes,
|
|
411
|
+
endCursor: data.pageInfo.endCursor,
|
|
412
|
+
hasNextPage: data.pageInfo.hasNextPage,
|
|
413
|
+
totalCount: data.totalCount,
|
|
414
|
+
rateLimit: res.data.rateLimit
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
} catch (e) {
|
|
418
|
+
if (debug) console.log(`\u274C Apollo failed, falling back to Octokit:`, e.message);
|
|
419
|
+
}
|
|
420
|
+
if (debug) console.log("\u{1F4E1} Using Octokit fallback...");
|
|
421
|
+
const octo = makeClient(token);
|
|
422
|
+
return fetchViewerReposPage(octo, first, after, orderBy, includeForkTracking, ownerAffiliations, organizationLogin);
|
|
423
|
+
}
|
|
424
|
+
async function searchRepositoriesUnified(token, viewer, text, first, after, sortKey = "UPDATED_AT", sortDir = "DESC", includeForkTracking = true, fetchPolicy = "network-only") {
|
|
425
|
+
const q = `${text} user:${viewer} in:name,description fork:true`;
|
|
426
|
+
try {
|
|
427
|
+
const ap = await makeApolloClient(token);
|
|
428
|
+
const queryDoc = ap.gql`
|
|
429
|
+
query SearchRepos($q: String!, $first: Int!, $after: String) {
|
|
430
|
+
rateLimit { limit remaining resetAt }
|
|
431
|
+
search(query: $q, type: REPOSITORY, first: $first, after: $after) {
|
|
432
|
+
repositoryCount
|
|
433
|
+
pageInfo { endCursor hasNextPage }
|
|
434
|
+
nodes {
|
|
435
|
+
... on Repository {
|
|
436
|
+
id
|
|
437
|
+
name
|
|
438
|
+
nameWithOwner
|
|
439
|
+
description
|
|
440
|
+
visibility
|
|
441
|
+
isPrivate
|
|
442
|
+
isFork
|
|
443
|
+
isArchived
|
|
444
|
+
stargazerCount
|
|
445
|
+
forkCount
|
|
446
|
+
primaryLanguage { name color }
|
|
447
|
+
updatedAt
|
|
448
|
+
pushedAt
|
|
449
|
+
diskUsage
|
|
450
|
+
${includeForkTracking ? `
|
|
451
|
+
parent { nameWithOwner defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } } }
|
|
452
|
+
defaultBranchRef { name target { ... on Commit { history(first: 0) { totalCount } } } }` : `
|
|
453
|
+
parent { nameWithOwner }
|
|
454
|
+
defaultBranchRef { name }`}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
const res = await ap.client.query({
|
|
461
|
+
query: queryDoc,
|
|
462
|
+
variables: { q, first, after: after ?? null },
|
|
463
|
+
fetchPolicy
|
|
464
|
+
});
|
|
465
|
+
const data = res.data.search;
|
|
466
|
+
return {
|
|
467
|
+
nodes: data.nodes,
|
|
468
|
+
endCursor: data.pageInfo.endCursor,
|
|
469
|
+
hasNextPage: data.pageInfo.hasNextPage,
|
|
470
|
+
totalCount: data.repositoryCount,
|
|
471
|
+
rateLimit: res.data.rateLimit
|
|
472
|
+
};
|
|
473
|
+
} catch (e) {
|
|
474
|
+
const debug = process.env.GH_MANAGER_DEBUG === "1";
|
|
475
|
+
if (debug) {
|
|
476
|
+
process.stderr.write(`
|
|
477
|
+
\u274C Search failed: ${e.message}
|
|
478
|
+
`);
|
|
479
|
+
if (e.graphQLErrors) {
|
|
480
|
+
process.stderr.write(`GraphQL errors: ${JSON.stringify(e.graphQLErrors, null, 2)}
|
|
481
|
+
`);
|
|
482
|
+
}
|
|
483
|
+
if (e.networkError) {
|
|
484
|
+
process.stderr.write(`Network error: ${e.networkError.message}
|
|
485
|
+
`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
throw e;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async function deleteRepositoryRest(token, owner, repo) {
|
|
492
|
+
const url = `https://api.github.com/repos/${owner}/${repo}`;
|
|
493
|
+
const res = await fetch(url, {
|
|
494
|
+
method: "DELETE",
|
|
495
|
+
headers: {
|
|
496
|
+
"Authorization": `token ${token}`,
|
|
497
|
+
"Accept": "application/vnd.github+json",
|
|
498
|
+
"User-Agent": "gh-manager-cli"
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
if (res.status === 204) return;
|
|
502
|
+
let msg = `GitHub REST delete failed (status ${res.status})`;
|
|
503
|
+
try {
|
|
504
|
+
const body = await res.json();
|
|
505
|
+
if (body && body.message) msg += `: ${body.message}`;
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
throw new Error(msg);
|
|
509
|
+
}
|
|
510
|
+
async function archiveRepositoryById(client, repositoryId) {
|
|
511
|
+
const mutation = (
|
|
512
|
+
/* GraphQL */
|
|
513
|
+
`
|
|
514
|
+
mutation ArchiveRepo($repositoryId: ID!) {
|
|
515
|
+
archiveRepository(input: { repositoryId: $repositoryId }) {
|
|
516
|
+
clientMutationId
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
`
|
|
520
|
+
);
|
|
521
|
+
await client(mutation, { repositoryId });
|
|
522
|
+
}
|
|
523
|
+
async function unarchiveRepositoryById(client, repositoryId) {
|
|
524
|
+
const mutation = (
|
|
525
|
+
/* GraphQL */
|
|
526
|
+
`
|
|
527
|
+
mutation UnarchiveRepo($repositoryId: ID!) {
|
|
528
|
+
unarchiveRepository(input: { repositoryId: $repositoryId }) {
|
|
529
|
+
clientMutationId
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
`
|
|
533
|
+
);
|
|
534
|
+
await client(mutation, { repositoryId });
|
|
535
|
+
}
|
|
536
|
+
async function syncForkWithUpstream(token, owner, repo, branch = "main") {
|
|
537
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/merge-upstream`;
|
|
538
|
+
const res = await fetch(url, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: {
|
|
541
|
+
"Authorization": `token ${token}`,
|
|
542
|
+
"Accept": "application/vnd.github+json",
|
|
543
|
+
"User-Agent": "gh-manager-cli"
|
|
544
|
+
},
|
|
545
|
+
body: JSON.stringify({ branch })
|
|
546
|
+
});
|
|
547
|
+
if (res.status === 204) {
|
|
548
|
+
return { message: "Already up-to-date", merge_type: "none", base_branch: branch };
|
|
549
|
+
}
|
|
550
|
+
if (res.status === 200) {
|
|
551
|
+
const body = await res.json();
|
|
552
|
+
return body;
|
|
553
|
+
}
|
|
554
|
+
let msg = `Fork sync failed (status ${res.status})`;
|
|
555
|
+
try {
|
|
556
|
+
const body = await res.json();
|
|
557
|
+
if (body && body.message) {
|
|
558
|
+
msg += `: ${body.message}`;
|
|
559
|
+
if (res.status === 409) {
|
|
560
|
+
msg += " (conflicts detected - manual merge required)";
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
throw new Error(msg);
|
|
566
|
+
}
|
|
567
|
+
async function purgeApolloCacheFiles() {
|
|
568
|
+
try {
|
|
569
|
+
const fs2 = await import("fs");
|
|
570
|
+
const path2 = await import("path");
|
|
571
|
+
const envPaths2 = (await import("env-paths")).default;
|
|
572
|
+
const p = envPaths2("gh-manager-cli").data;
|
|
573
|
+
const cacheFile = path2.join(p, "apollo-cache.json");
|
|
574
|
+
const metaFile = path2.join(p, "apollo-cache-meta.json");
|
|
575
|
+
if (process.env.GH_MANAGER_DEBUG === "1") {
|
|
576
|
+
console.log(`\u{1F5D1}\uFE0F Purging cache files from: ${p}`);
|
|
577
|
+
}
|
|
578
|
+
try {
|
|
579
|
+
fs2.unlinkSync(cacheFile);
|
|
580
|
+
} catch {
|
|
581
|
+
}
|
|
582
|
+
try {
|
|
583
|
+
fs2.unlinkSync(metaFile);
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
} catch {
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function inspectCacheStatus() {
|
|
590
|
+
try {
|
|
591
|
+
const fs2 = await import("fs");
|
|
592
|
+
const path2 = await import("path");
|
|
593
|
+
const envPaths2 = (await import("env-paths")).default;
|
|
594
|
+
const p = envPaths2("gh-manager-cli").data;
|
|
595
|
+
const cacheFile = path2.join(p, "apollo-cache.json");
|
|
596
|
+
const metaFile = path2.join(p, "apollo-cache-meta.json");
|
|
597
|
+
process.stderr.write(`
|
|
598
|
+
\u{1F4C2} Cache directory: ${p}
|
|
599
|
+
`);
|
|
600
|
+
try {
|
|
601
|
+
const cacheStats = fs2.statSync(cacheFile);
|
|
602
|
+
process.stderr.write(`\u{1F4BE} Cache file: ${Math.round(cacheStats.size / 1024)}KB (${cacheStats.mtime.toISOString()})
|
|
603
|
+
`);
|
|
604
|
+
} catch {
|
|
605
|
+
process.stderr.write(`\u{1F4BE} Cache file: NOT FOUND
|
|
606
|
+
`);
|
|
607
|
+
}
|
|
608
|
+
try {
|
|
609
|
+
const metaStats = fs2.statSync(metaFile);
|
|
610
|
+
const metaContent = fs2.readFileSync(metaFile, "utf8");
|
|
611
|
+
const meta = JSON.parse(metaContent);
|
|
612
|
+
process.stderr.write(`\u{1F4CA} Meta file: ${Object.keys(meta.fetched || {}).length} entries (${metaStats.mtime.toISOString()})
|
|
613
|
+
`);
|
|
614
|
+
const entries = Object.entries(meta.fetched || {});
|
|
615
|
+
if (entries.length > 0) {
|
|
616
|
+
process.stderr.write("\u{1F4CB} Recent cache entries:\n");
|
|
617
|
+
entries.slice(-3).forEach(([key, timestamp]) => {
|
|
618
|
+
const age = Date.now() - Date.parse(timestamp);
|
|
619
|
+
process.stderr.write(` ${key} (${Math.round(age / 1e3)}s ago)
|
|
620
|
+
`);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
} catch {
|
|
624
|
+
process.stderr.write(`\u{1F4CA} Meta file: NOT FOUND
|
|
625
|
+
`);
|
|
626
|
+
}
|
|
627
|
+
process.stderr.write("\n");
|
|
628
|
+
} catch (e) {
|
|
629
|
+
process.stderr.write(`\u274C Cache inspection failed: ${e.message}
|
|
630
|
+
`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export {
|
|
635
|
+
__commonJS,
|
|
636
|
+
makeClient,
|
|
637
|
+
getViewerLogin,
|
|
638
|
+
fetchViewerOrganizations,
|
|
639
|
+
fetchViewerReposPage,
|
|
640
|
+
fetchViewerReposPageUnified,
|
|
641
|
+
searchRepositoriesUnified,
|
|
642
|
+
deleteRepositoryRest,
|
|
643
|
+
archiveRepositoryById,
|
|
644
|
+
unarchiveRepositoryById,
|
|
645
|
+
syncForkWithUpstream,
|
|
646
|
+
purgeApolloCacheFiles,
|
|
647
|
+
inspectCacheStatus
|
|
648
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
archiveRepositoryById,
|
|
4
|
+
deleteRepositoryRest,
|
|
5
|
+
fetchViewerOrganizations,
|
|
6
|
+
fetchViewerReposPage,
|
|
7
|
+
fetchViewerReposPageUnified,
|
|
8
|
+
getViewerLogin,
|
|
9
|
+
inspectCacheStatus,
|
|
10
|
+
makeClient,
|
|
11
|
+
purgeApolloCacheFiles,
|
|
12
|
+
searchRepositoriesUnified,
|
|
13
|
+
syncForkWithUpstream,
|
|
14
|
+
unarchiveRepositoryById
|
|
15
|
+
} from "./chunk-OQGG2X5P.js";
|
|
16
|
+
export {
|
|
17
|
+
archiveRepositoryById,
|
|
18
|
+
deleteRepositoryRest,
|
|
19
|
+
fetchViewerOrganizations,
|
|
20
|
+
fetchViewerReposPage,
|
|
21
|
+
fetchViewerReposPageUnified,
|
|
22
|
+
getViewerLogin,
|
|
23
|
+
inspectCacheStatus,
|
|
24
|
+
makeClient,
|
|
25
|
+
purgeApolloCacheFiles,
|
|
26
|
+
searchRepositoriesUnified,
|
|
27
|
+
syncForkWithUpstream,
|
|
28
|
+
unarchiveRepositoryById
|
|
29
|
+
};
|