ghscout 0.1.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/README.md +195 -0
- package/dist/analysis/ai-scorer.d.ts +34 -0
- package/dist/analysis/ai-scorer.d.ts.map +1 -0
- package/dist/analysis/ai-scorer.js +139 -0
- package/dist/analysis/ai-scorer.js.map +1 -0
- package/dist/analysis/cluster.d.ts +14 -0
- package/dist/analysis/cluster.d.ts.map +1 -0
- package/dist/analysis/cluster.js +223 -0
- package/dist/analysis/cluster.js.map +1 -0
- package/dist/analysis/scorer.d.ts +27 -0
- package/dist/analysis/scorer.d.ts.map +1 -0
- package/dist/analysis/scorer.js +196 -0
- package/dist/analysis/scorer.js.map +1 -0
- package/dist/analysis/signals.d.ts +30 -0
- package/dist/analysis/signals.d.ts.map +1 -0
- package/dist/analysis/signals.js +59 -0
- package/dist/analysis/signals.js.map +1 -0
- package/dist/analysis/tokenizer.d.ts +15 -0
- package/dist/analysis/tokenizer.d.ts.map +1 -0
- package/dist/analysis/tokenizer.js +64 -0
- package/dist/analysis/tokenizer.js.map +1 -0
- package/dist/commands/evidence.d.ts +9 -0
- package/dist/commands/evidence.d.ts.map +1 -0
- package/dist/commands/evidence.js +229 -0
- package/dist/commands/evidence.js.map +1 -0
- package/dist/commands/scan-org.d.ts +3 -0
- package/dist/commands/scan-org.d.ts.map +1 -0
- package/dist/commands/scan-org.js +88 -0
- package/dist/commands/scan-org.js.map +1 -0
- package/dist/commands/scan.d.ts +32 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +197 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/trending.d.ts +14 -0
- package/dist/commands/trending.d.ts.map +1 -0
- package/dist/commands/trending.js +145 -0
- package/dist/commands/trending.js.map +1 -0
- package/dist/github/auth.d.ts +3 -0
- package/dist/github/auth.d.ts.map +1 -0
- package/dist/github/auth.js +33 -0
- package/dist/github/auth.js.map +1 -0
- package/dist/github/cache.d.ts +18 -0
- package/dist/github/cache.d.ts.map +1 -0
- package/dist/github/cache.js +51 -0
- package/dist/github/cache.js.map +1 -0
- package/dist/github/client.d.ts +24 -0
- package/dist/github/client.d.ts.map +1 -0
- package/dist/github/client.js +140 -0
- package/dist/github/client.js.map +1 -0
- package/dist/github/fetchers.d.ts +13 -0
- package/dist/github/fetchers.d.ts.map +1 -0
- package/dist/github/fetchers.js +142 -0
- package/dist/github/fetchers.js.map +1 -0
- package/dist/github/types.d.ts +46 -0
- package/dist/github/types.d.ts.map +1 -0
- package/dist/github/types.js +2 -0
- package/dist/github/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/output/formatters.d.ts +35 -0
- package/dist/output/formatters.d.ts.map +1 -0
- package/dist/output/formatters.js +195 -0
- package/dist/output/formatters.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
/** Repo metadata TTL: 24 hours */
|
|
6
|
+
export const REPO_TTL = 24 * 60 * 60 * 1000;
|
|
7
|
+
/** Issue data TTL: 1 hour */
|
|
8
|
+
export const ISSUE_TTL = 60 * 60 * 1000;
|
|
9
|
+
function getCacheDir() {
|
|
10
|
+
const xdg = process.env.XDG_CACHE_HOME;
|
|
11
|
+
const base = xdg || join(homedir(), ".cache");
|
|
12
|
+
return join(base, "ghscout");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate a cache key from a URL and optional params.
|
|
16
|
+
* Returns the first 16 chars of a sha256 hex digest.
|
|
17
|
+
*/
|
|
18
|
+
export function getCacheKey(url, params) {
|
|
19
|
+
const input = params
|
|
20
|
+
? url + JSON.stringify(params, Object.keys(params).sort())
|
|
21
|
+
: url;
|
|
22
|
+
return createHash("sha256").update(input).digest("hex").slice(0, 16);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read a cached value. Returns null if missing or expired.
|
|
26
|
+
*/
|
|
27
|
+
export async function getCached(key, ttlMs) {
|
|
28
|
+
try {
|
|
29
|
+
const filePath = join(getCacheDir(), `${key}.json`);
|
|
30
|
+
const raw = await readFile(filePath, "utf-8");
|
|
31
|
+
const entry = JSON.parse(raw);
|
|
32
|
+
if (Date.now() - entry.timestamp > ttlMs) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return entry.data;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Write a value to the cache.
|
|
43
|
+
*/
|
|
44
|
+
export async function setCache(key, data) {
|
|
45
|
+
const dir = getCacheDir();
|
|
46
|
+
await mkdir(dir, { recursive: true });
|
|
47
|
+
const filePath = join(dir, `${key}.json`);
|
|
48
|
+
const entry = { data, timestamp: Date.now() };
|
|
49
|
+
await writeFile(filePath, JSON.stringify(entry), "utf-8");
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/github/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,kCAAkC;AAClC,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE5C,6BAA6B;AAC7B,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAOxC,SAAS,WAAW;IAClB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,MAA+B;IAE/B,MAAM,KAAK,GAAG,MAAM;QAClB,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,CAAC,CAAC,GAAG,CAAC;IACR,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,KAAa;IAEb,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAe,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,IAAS,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAW,EACX,IAAa;IAEb,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAe,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC1D,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare class RateLimitError extends Error {
|
|
2
|
+
resetAt: number;
|
|
3
|
+
constructor(resetAt: number);
|
|
4
|
+
}
|
|
5
|
+
export declare class GitHubClient {
|
|
6
|
+
private token;
|
|
7
|
+
private rateLimit;
|
|
8
|
+
private rateLimitRemaining;
|
|
9
|
+
private searchRateLimit;
|
|
10
|
+
private searchRateLimitRemaining;
|
|
11
|
+
private searchRateLimitReset;
|
|
12
|
+
private lastSearchTime;
|
|
13
|
+
/** Minimum delay between consecutive search requests (ms) */
|
|
14
|
+
private static SEARCH_DELAY_MS;
|
|
15
|
+
constructor(token: string | null);
|
|
16
|
+
private isSearchUrl;
|
|
17
|
+
private applySearchThrottle;
|
|
18
|
+
private trackSearchRateLimit;
|
|
19
|
+
get<T>(url: string, params?: Record<string, string>): Promise<T>;
|
|
20
|
+
getPaginated<T>(url: string, params?: Record<string, string>, maxPages?: number): Promise<T[]>;
|
|
21
|
+
private trackRateLimit;
|
|
22
|
+
private parseNextLink;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"AAEA,qBAAa,cAAe,SAAQ,KAAK;IACpB,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAInC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,kBAAkB,CAAuB;IACjD,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,wBAAwB,CAAuB;IACvD,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,cAAc,CAAa;IAEnC,6DAA6D;IAC7D,OAAO,CAAC,MAAM,CAAC,eAAe,CAAQ;gBAE1B,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,CAAC,WAAW;YAIL,mBAAmB;IA+BjC,OAAO,CAAC,oBAAoB;IAkBtB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA8BhE,YAAY,CAAC,CAAC,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,QAAQ,GAAE,MAAiB,GAC1B,OAAO,CAAC,CAAC,EAAE,CAAC;IAkCf,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,aAAa;CAMtB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { buildHeaders } from "./auth.js";
|
|
2
|
+
export class RateLimitError extends Error {
|
|
3
|
+
resetAt;
|
|
4
|
+
constructor(resetAt) {
|
|
5
|
+
super(`GitHub API rate limit exhausted. Resets at ${new Date(resetAt * 1000).toISOString()}`);
|
|
6
|
+
this.resetAt = resetAt;
|
|
7
|
+
this.name = "RateLimitError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class GitHubClient {
|
|
11
|
+
token;
|
|
12
|
+
rateLimit = null;
|
|
13
|
+
rateLimitRemaining = null;
|
|
14
|
+
searchRateLimit = null;
|
|
15
|
+
searchRateLimitRemaining = null;
|
|
16
|
+
searchRateLimitReset = null;
|
|
17
|
+
lastSearchTime = 0;
|
|
18
|
+
/** Minimum delay between consecutive search requests (ms) */
|
|
19
|
+
static SEARCH_DELAY_MS = 2000;
|
|
20
|
+
constructor(token) {
|
|
21
|
+
this.token = token;
|
|
22
|
+
}
|
|
23
|
+
isSearchUrl(url) {
|
|
24
|
+
return url.includes("/search/");
|
|
25
|
+
}
|
|
26
|
+
async applySearchThrottle() {
|
|
27
|
+
// If search rate limit is exhausted, wait for reset
|
|
28
|
+
if (this.searchRateLimitRemaining !== null && this.searchRateLimitRemaining === 0 && this.searchRateLimitReset !== null) {
|
|
29
|
+
const waitMs = Math.max(0, this.searchRateLimitReset * 1000 - Date.now());
|
|
30
|
+
if (waitMs > 0) {
|
|
31
|
+
process.stderr.write(`Search rate limit exhausted. Waiting ${Math.ceil(waitMs / 1000)}s for reset...\n`);
|
|
32
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Warn when search rate limit is low
|
|
36
|
+
if (this.searchRateLimit !== null &&
|
|
37
|
+
this.searchRateLimitRemaining !== null &&
|
|
38
|
+
this.searchRateLimitRemaining > 0 &&
|
|
39
|
+
this.searchRateLimitRemaining < 5) {
|
|
40
|
+
process.stderr.write(`Search rate limit low (${this.searchRateLimitRemaining}/${this.searchRateLimit} remaining). Slowing down...\n`);
|
|
41
|
+
}
|
|
42
|
+
// Enforce minimum delay between search requests
|
|
43
|
+
const elapsed = Date.now() - this.lastSearchTime;
|
|
44
|
+
if (this.lastSearchTime > 0 && elapsed < GitHubClient.SEARCH_DELAY_MS) {
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, GitHubClient.SEARCH_DELAY_MS - elapsed));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
trackSearchRateLimit(response) {
|
|
49
|
+
const remaining = response.headers.get("x-ratelimit-remaining");
|
|
50
|
+
const limit = response.headers.get("x-ratelimit-limit");
|
|
51
|
+
const reset = response.headers.get("x-ratelimit-reset");
|
|
52
|
+
if (limit !== null) {
|
|
53
|
+
this.searchRateLimit = parseInt(limit, 10);
|
|
54
|
+
}
|
|
55
|
+
if (remaining !== null) {
|
|
56
|
+
this.searchRateLimitRemaining = parseInt(remaining, 10);
|
|
57
|
+
}
|
|
58
|
+
if (reset !== null) {
|
|
59
|
+
this.searchRateLimitReset = parseInt(reset, 10);
|
|
60
|
+
}
|
|
61
|
+
this.lastSearchTime = Date.now();
|
|
62
|
+
}
|
|
63
|
+
async get(url, params) {
|
|
64
|
+
const isSearch = this.isSearchUrl(url);
|
|
65
|
+
if (isSearch) {
|
|
66
|
+
await this.applySearchThrottle();
|
|
67
|
+
}
|
|
68
|
+
const fullUrl = new URL(url);
|
|
69
|
+
if (params) {
|
|
70
|
+
for (const [key, value] of Object.entries(params)) {
|
|
71
|
+
fullUrl.searchParams.set(key, value);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const headers = buildHeaders(this.token);
|
|
75
|
+
const response = await fetch(fullUrl.toString(), { headers });
|
|
76
|
+
if (isSearch) {
|
|
77
|
+
this.trackSearchRateLimit(response);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.trackRateLimit(response);
|
|
81
|
+
}
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
84
|
+
}
|
|
85
|
+
return response.json();
|
|
86
|
+
}
|
|
87
|
+
async getPaginated(url, params, maxPages = Infinity) {
|
|
88
|
+
let results = [];
|
|
89
|
+
let nextUrl = url;
|
|
90
|
+
let page = 0;
|
|
91
|
+
// Build initial URL with params
|
|
92
|
+
if (params && nextUrl) {
|
|
93
|
+
const fullUrl = new URL(nextUrl);
|
|
94
|
+
for (const [key, value] of Object.entries(params)) {
|
|
95
|
+
fullUrl.searchParams.set(key, value);
|
|
96
|
+
}
|
|
97
|
+
nextUrl = fullUrl.toString();
|
|
98
|
+
}
|
|
99
|
+
while (nextUrl && page < maxPages) {
|
|
100
|
+
const headers = buildHeaders(this.token);
|
|
101
|
+
const response = await fetch(nextUrl, { headers });
|
|
102
|
+
this.trackRateLimit(response);
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const data = (await response.json());
|
|
107
|
+
results = results.concat(data);
|
|
108
|
+
page++;
|
|
109
|
+
nextUrl = this.parseNextLink(response.headers.get("link"));
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
trackRateLimit(response) {
|
|
114
|
+
const remaining = response.headers.get("x-ratelimit-remaining");
|
|
115
|
+
const limit = response.headers.get("x-ratelimit-limit");
|
|
116
|
+
const reset = response.headers.get("x-ratelimit-reset");
|
|
117
|
+
if (limit !== null) {
|
|
118
|
+
this.rateLimit = parseInt(limit, 10);
|
|
119
|
+
}
|
|
120
|
+
if (remaining !== null) {
|
|
121
|
+
this.rateLimitRemaining = parseInt(remaining, 10);
|
|
122
|
+
}
|
|
123
|
+
if (this.rateLimitRemaining !== null && this.rateLimitRemaining === 0) {
|
|
124
|
+
const resetAt = reset ? parseInt(reset, 10) : 0;
|
|
125
|
+
throw new RateLimitError(resetAt);
|
|
126
|
+
}
|
|
127
|
+
if (this.rateLimit !== null &&
|
|
128
|
+
this.rateLimitRemaining !== null &&
|
|
129
|
+
this.rateLimitRemaining / this.rateLimit <= 0.2) {
|
|
130
|
+
process.stderr.write(`Warning: GitHub API rate limit low — ${this.rateLimitRemaining}/${this.rateLimit} requests remaining\n`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
parseNextLink(linkHeader) {
|
|
134
|
+
if (!linkHeader)
|
|
135
|
+
return null;
|
|
136
|
+
const match = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
|
|
137
|
+
return match ? match[1] : null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/github/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GitHubClient } from "./client.js";
|
|
2
|
+
import type { RepoMeta, Issue, Pull, FetchOptions } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Parse a period string like "30d" into an ISO 8601 date string
|
|
5
|
+
* representing that many days before now.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parsePeriod(period: string): string;
|
|
8
|
+
export declare function fetchRepoMeta(client: GitHubClient, fullName: string): Promise<RepoMeta>;
|
|
9
|
+
export declare function fetchIssues(client: GitHubClient, fullName: string, opts?: FetchOptions): Promise<Issue[]>;
|
|
10
|
+
export declare function fetchPulls(client: GitHubClient, fullName: string, opts?: FetchOptions): Promise<Pull[]>;
|
|
11
|
+
export declare function fetchOrgRepos(client: GitHubClient, org: string, minStars?: number): Promise<RepoMeta[]>;
|
|
12
|
+
export declare function searchReposByTopic(client: GitHubClient, topic: string, lang?: string, minStars?: number): Promise<RepoMeta[]>;
|
|
13
|
+
//# sourceMappingURL=fetchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetchers.d.ts","sourceRoot":"","sources":["../../src/github/fetchers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAItE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASlD;AAUD,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,CAAC,CAgBnB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,KAAK,EAAE,CAAC,CA8BlB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,IAAI,EAAE,CAAC,CAkBjB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAU,GACnB,OAAO,CAAC,QAAQ,EAAE,CAAC,CASrB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAerB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const BASE = "https://api.github.com";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a period string like "30d" into an ISO 8601 date string
|
|
4
|
+
* representing that many days before now.
|
|
5
|
+
*/
|
|
6
|
+
export function parsePeriod(period) {
|
|
7
|
+
const match = period.match(/^(\d+)d$/);
|
|
8
|
+
if (!match) {
|
|
9
|
+
throw new Error(`Invalid period format: "${period}". Expected format like "30d".`);
|
|
10
|
+
}
|
|
11
|
+
const days = parseInt(match[1], 10);
|
|
12
|
+
const date = new Date();
|
|
13
|
+
date.setDate(date.getDate() - days);
|
|
14
|
+
return date.toISOString();
|
|
15
|
+
}
|
|
16
|
+
function splitFullName(fullName) {
|
|
17
|
+
const [owner, repo] = fullName.split("/");
|
|
18
|
+
if (!owner || !repo) {
|
|
19
|
+
throw new Error(`Invalid repo format: "${fullName}". Expected "owner/repo".`);
|
|
20
|
+
}
|
|
21
|
+
return { owner, repo };
|
|
22
|
+
}
|
|
23
|
+
export async function fetchRepoMeta(client, fullName) {
|
|
24
|
+
const { owner, repo } = splitFullName(fullName);
|
|
25
|
+
const data = await client.get(`${BASE}/repos/${owner}/${repo}`);
|
|
26
|
+
return {
|
|
27
|
+
owner,
|
|
28
|
+
repo,
|
|
29
|
+
fullName: data.full_name ?? fullName,
|
|
30
|
+
stars: data.stargazers_count ?? 0,
|
|
31
|
+
pushedAt: data.pushed_at ?? "",
|
|
32
|
+
topics: data.topics ?? [],
|
|
33
|
+
language: data.language ?? null,
|
|
34
|
+
description: data.description ?? "",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function fetchIssues(client, fullName, opts = {}) {
|
|
38
|
+
const { owner, repo } = splitFullName(fullName);
|
|
39
|
+
const limit = opts.limit ?? 200;
|
|
40
|
+
const perPage = Math.min(limit, 100);
|
|
41
|
+
const maxPages = Math.ceil(limit / perPage);
|
|
42
|
+
const params = {
|
|
43
|
+
state: opts.state ?? "open",
|
|
44
|
+
per_page: String(perPage),
|
|
45
|
+
sort: "reactions-+1",
|
|
46
|
+
direction: "desc",
|
|
47
|
+
};
|
|
48
|
+
// Only apply period filter if explicitly requested
|
|
49
|
+
if (opts.period) {
|
|
50
|
+
params.since = parsePeriod(opts.period);
|
|
51
|
+
}
|
|
52
|
+
const data = await client.getPaginated(`${BASE}/repos/${owner}/${repo}/issues`, params, maxPages);
|
|
53
|
+
// Filter out pull requests (GitHub issues API includes PRs)
|
|
54
|
+
const issues = data.filter((item) => !item.pull_request);
|
|
55
|
+
return issues.slice(0, limit).map(mapIssue);
|
|
56
|
+
}
|
|
57
|
+
export async function fetchPulls(client, fullName, opts = {}) {
|
|
58
|
+
const { owner, repo } = splitFullName(fullName);
|
|
59
|
+
const limit = opts.limit ?? 100;
|
|
60
|
+
const perPage = Math.min(limit, 100);
|
|
61
|
+
const params = {
|
|
62
|
+
state: opts.state ?? "closed",
|
|
63
|
+
per_page: String(perPage),
|
|
64
|
+
sort: "created",
|
|
65
|
+
direction: "desc",
|
|
66
|
+
};
|
|
67
|
+
const data = await client.get(`${BASE}/repos/${owner}/${repo}/pulls`, params);
|
|
68
|
+
return data.slice(0, limit).map(mapPull);
|
|
69
|
+
}
|
|
70
|
+
export async function fetchOrgRepos(client, org, minStars = 0) {
|
|
71
|
+
const data = await client.get(`${BASE}/orgs/${org}/repos`, { sort: "stars", per_page: "100", direction: "desc" });
|
|
72
|
+
return data
|
|
73
|
+
.filter((r) => (r.stargazers_count ?? 0) >= minStars)
|
|
74
|
+
.map(mapRepoMeta);
|
|
75
|
+
}
|
|
76
|
+
export async function searchReposByTopic(client, topic, lang, minStars) {
|
|
77
|
+
let q = `topic:${topic}`;
|
|
78
|
+
if (lang) {
|
|
79
|
+
q += ` language:${lang}`;
|
|
80
|
+
}
|
|
81
|
+
if (minStars !== undefined && minStars > 0) {
|
|
82
|
+
q += ` stars:>=${minStars}`;
|
|
83
|
+
}
|
|
84
|
+
const data = await client.get(`${BASE}/search/repositories`, { q, sort: "stars" });
|
|
85
|
+
return (data.items ?? []).map(mapRepoMeta);
|
|
86
|
+
}
|
|
87
|
+
// --- mapping helpers ---
|
|
88
|
+
function mapRepoMeta(data) {
|
|
89
|
+
const fullName = data.full_name;
|
|
90
|
+
const [owner, repo] = fullName.split("/");
|
|
91
|
+
return {
|
|
92
|
+
owner,
|
|
93
|
+
repo,
|
|
94
|
+
fullName,
|
|
95
|
+
stars: data.stargazers_count ?? 0,
|
|
96
|
+
pushedAt: data.pushed_at ?? "",
|
|
97
|
+
topics: data.topics ?? [],
|
|
98
|
+
language: data.language ?? null,
|
|
99
|
+
description: data.description ?? "",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function mapIssue(data) {
|
|
103
|
+
const reactions = data.reactions;
|
|
104
|
+
const labels = data.labels ?? [];
|
|
105
|
+
const user = data.user;
|
|
106
|
+
return {
|
|
107
|
+
number: data.number,
|
|
108
|
+
title: data.title,
|
|
109
|
+
body: data.body ?? "",
|
|
110
|
+
labels: labels.map((l) => (typeof l === "string" ? l : l.name)),
|
|
111
|
+
reactions: {
|
|
112
|
+
thumbsUp: reactions?.["+1"] ?? 0,
|
|
113
|
+
thumbsDown: reactions?.["-1"] ?? 0,
|
|
114
|
+
total: reactions?.total_count ?? 0,
|
|
115
|
+
},
|
|
116
|
+
commentsCount: data.comments ?? 0,
|
|
117
|
+
createdAt: data.created_at,
|
|
118
|
+
htmlUrl: data.html_url,
|
|
119
|
+
user: user?.login ?? "",
|
|
120
|
+
state: data.state,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function mapPull(data) {
|
|
124
|
+
const reactions = data.reactions;
|
|
125
|
+
const user = data.user;
|
|
126
|
+
const mergedAt = data.merged_at ?? null;
|
|
127
|
+
return {
|
|
128
|
+
number: data.number,
|
|
129
|
+
title: data.title,
|
|
130
|
+
merged: mergedAt !== null,
|
|
131
|
+
mergedAt,
|
|
132
|
+
reactions: {
|
|
133
|
+
thumbsUp: reactions?.["+1"] ?? 0,
|
|
134
|
+
thumbsDown: reactions?.["-1"] ?? 0,
|
|
135
|
+
total: reactions?.total_count ?? 0,
|
|
136
|
+
},
|
|
137
|
+
htmlUrl: data.html_url,
|
|
138
|
+
user: user?.login ?? "",
|
|
139
|
+
createdAt: data.created_at,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=fetchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetchers.js","sourceRoot":"","sources":["../../src/github/fetchers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface RepoMeta {
|
|
2
|
+
owner: string;
|
|
3
|
+
repo: string;
|
|
4
|
+
fullName: string;
|
|
5
|
+
stars: number;
|
|
6
|
+
pushedAt: string;
|
|
7
|
+
topics: string[];
|
|
8
|
+
language: string | null;
|
|
9
|
+
description: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Issue {
|
|
12
|
+
number: number;
|
|
13
|
+
title: string;
|
|
14
|
+
body: string;
|
|
15
|
+
labels: string[];
|
|
16
|
+
reactions: {
|
|
17
|
+
thumbsUp: number;
|
|
18
|
+
thumbsDown: number;
|
|
19
|
+
total: number;
|
|
20
|
+
};
|
|
21
|
+
commentsCount: number;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
htmlUrl: string;
|
|
24
|
+
user: string;
|
|
25
|
+
state: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Pull {
|
|
28
|
+
number: number;
|
|
29
|
+
title: string;
|
|
30
|
+
merged: boolean;
|
|
31
|
+
mergedAt: string | null;
|
|
32
|
+
reactions: {
|
|
33
|
+
thumbsUp: number;
|
|
34
|
+
thumbsDown: number;
|
|
35
|
+
total: number;
|
|
36
|
+
};
|
|
37
|
+
htmlUrl: string;
|
|
38
|
+
user: string;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}
|
|
41
|
+
export interface FetchOptions {
|
|
42
|
+
limit?: number;
|
|
43
|
+
period?: string;
|
|
44
|
+
state?: string;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/github/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/github/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runScan, runTopicScan } from './commands/scan.js';
|
|
4
|
+
import { runOrgScan } from './commands/scan-org.js';
|
|
5
|
+
import { runEvidence } from './commands/evidence.js';
|
|
6
|
+
import { runTrending } from './commands/trending.js';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('ghscout')
|
|
10
|
+
.description('Evidence engine for product discovery from GitHub issues')
|
|
11
|
+
.version('0.1.0');
|
|
12
|
+
// scan command
|
|
13
|
+
program
|
|
14
|
+
.command('scan')
|
|
15
|
+
.description('Scan a repo, org, or topic for opportunity clusters')
|
|
16
|
+
.argument('[repo]', 'Repository in owner/repo format (e.g., vercel/next.js)')
|
|
17
|
+
.option('--org <org>', 'Scan top repos of a GitHub organization')
|
|
18
|
+
.option('--topic <topic>', 'Scan trending repos in a GitHub topic')
|
|
19
|
+
.option('--lang <language>', 'Filter repos by programming language (used with --topic)')
|
|
20
|
+
.option('--output <format>', 'Output format: json | table | pretty', 'pretty')
|
|
21
|
+
.option('--limit <n>', 'Max issues to fetch per repo', '200')
|
|
22
|
+
.option('--period <duration>', 'Time window: 7d, 30d, 90d (default: all open)')
|
|
23
|
+
.option('--min-stars <n>', 'Min repo stars to include', '100')
|
|
24
|
+
.option('--verbose', 'Show API calls and rate limit status', false)
|
|
25
|
+
.option('--no-cache', 'Skip cache, fetch fresh data')
|
|
26
|
+
.option('--top <n>', 'Show only top N clusters', '0')
|
|
27
|
+
.option('--min-reactions <n>', 'Minimum total reactions per cluster', '0')
|
|
28
|
+
.option('--json', 'Shorthand for --output json', false)
|
|
29
|
+
.option('--ai-score', 'Score opportunities using AI via Claude Code CLI', false)
|
|
30
|
+
.action(async (repo, cmdOpts) => {
|
|
31
|
+
const scanOpts = {
|
|
32
|
+
output: cmdOpts.json === true ? 'json' : cmdOpts.output,
|
|
33
|
+
limit: parseInt(cmdOpts.limit, 10),
|
|
34
|
+
period: cmdOpts.period || "",
|
|
35
|
+
minStars: parseInt(cmdOpts.minStars, 10),
|
|
36
|
+
verbose: cmdOpts.verbose === true,
|
|
37
|
+
noCache: cmdOpts.cache === false,
|
|
38
|
+
top: parseInt(cmdOpts.top, 10) || 0,
|
|
39
|
+
minReactions: parseInt(cmdOpts.minReactions, 10) || 0,
|
|
40
|
+
aiScore: cmdOpts.aiScore === true,
|
|
41
|
+
};
|
|
42
|
+
const org = cmdOpts.org;
|
|
43
|
+
const topic = cmdOpts.topic;
|
|
44
|
+
const lang = cmdOpts.lang;
|
|
45
|
+
// Validate conflicting flags
|
|
46
|
+
const modes = [repo, org, topic].filter(Boolean);
|
|
47
|
+
if (modes.length > 1) {
|
|
48
|
+
console.error('Error: Cannot combine repo, --org, and --topic. Use only one.');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
if (lang && !topic) {
|
|
52
|
+
console.error('Error: --lang can only be used with --topic.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (topic) {
|
|
56
|
+
await runTopicScan({
|
|
57
|
+
topic,
|
|
58
|
+
lang,
|
|
59
|
+
...scanOpts,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (org) {
|
|
63
|
+
await runOrgScan(org, scanOpts);
|
|
64
|
+
}
|
|
65
|
+
else if (repo) {
|
|
66
|
+
await runScan(repo, scanOpts);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.error('Error: Provide a repo (owner/repo), --org <org>, or --topic <topic>.');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// evidence command
|
|
74
|
+
program
|
|
75
|
+
.command('evidence')
|
|
76
|
+
.description('Deep-dive on a specific pain topic with hard numbers')
|
|
77
|
+
.argument('<repo>', 'Repository in owner/repo format (e.g., vercel/next.js)')
|
|
78
|
+
.argument('<query>', 'Search query (e.g., "auth middleware")')
|
|
79
|
+
.option('--output <format>', 'Output format: json | table | pretty', 'pretty')
|
|
80
|
+
.option('--sort <sort>', 'Sort by: reactions | recent | comments', 'reactions')
|
|
81
|
+
.option('--limit <n>', 'Max issues to return', '20')
|
|
82
|
+
.option('--verbose', 'Show API calls and rate limit status', false)
|
|
83
|
+
.option('--no-cache', 'Skip cache, fetch fresh data')
|
|
84
|
+
.option('--json', 'Shorthand for --output json', false)
|
|
85
|
+
.action(async (repo, query, cmdOpts) => {
|
|
86
|
+
await runEvidence(repo, query, {
|
|
87
|
+
output: cmdOpts.json === true ? 'json' : cmdOpts.output,
|
|
88
|
+
sort: cmdOpts.sort,
|
|
89
|
+
limit: parseInt(cmdOpts.limit, 10),
|
|
90
|
+
verbose: cmdOpts.verbose === true,
|
|
91
|
+
noCache: cmdOpts.cache === false,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
// trending command
|
|
95
|
+
program
|
|
96
|
+
.command('trending')
|
|
97
|
+
.description('Top pain clusters across GitHub right now')
|
|
98
|
+
.option('--output <format>', 'Output format: json | table | pretty', 'pretty')
|
|
99
|
+
.option('--top <n>', 'Show only top N clusters', '10')
|
|
100
|
+
.option('--topic <topic>', 'Filter by GitHub topic')
|
|
101
|
+
.option('--lang <language>', 'Filter by programming language')
|
|
102
|
+
.option('--verbose', 'Show API calls and rate limit status', false)
|
|
103
|
+
.option('--no-cache', 'Skip cache, fetch fresh data')
|
|
104
|
+
.option('--json', 'Shorthand for --output json', false)
|
|
105
|
+
.action(async (cmdOpts) => {
|
|
106
|
+
await runTrending({
|
|
107
|
+
output: cmdOpts.json === true ? 'json' : cmdOpts.output,
|
|
108
|
+
top: parseInt(cmdOpts.top, 10) || 10,
|
|
109
|
+
topic: cmdOpts.topic,
|
|
110
|
+
lang: cmdOpts.lang,
|
|
111
|
+
verbose: cmdOpts.verbose === true,
|
|
112
|
+
noCache: cmdOpts.cache === false,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
program.parse(process.argv);
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ScoredCluster {
|
|
2
|
+
name: string;
|
|
3
|
+
issues: Array<{
|
|
4
|
+
number: number;
|
|
5
|
+
title: string;
|
|
6
|
+
htmlUrl: string;
|
|
7
|
+
reactions: {
|
|
8
|
+
total: number;
|
|
9
|
+
};
|
|
10
|
+
createdAt: string;
|
|
11
|
+
}>;
|
|
12
|
+
issueCount: number;
|
|
13
|
+
totalReactions: number;
|
|
14
|
+
labels: string[];
|
|
15
|
+
score: number;
|
|
16
|
+
breakdown: {
|
|
17
|
+
demand: number;
|
|
18
|
+
frequency: number;
|
|
19
|
+
frustration: number;
|
|
20
|
+
marketSize: number;
|
|
21
|
+
gap: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface OutputOptions {
|
|
25
|
+
format: "json" | "table" | "pretty";
|
|
26
|
+
top?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function formatScanResult(clusters: ScoredCluster[], opts: OutputOptions): string;
|
|
29
|
+
export interface AIScoredCluster extends ScoredCluster {
|
|
30
|
+
aiScore: number;
|
|
31
|
+
verdict: "BUILD" | "SKIP" | "WATCH";
|
|
32
|
+
rationale: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function formatAIScanResult(clusters: AIScoredCluster[], opts: OutputOptions): string;
|
|
35
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/output/formatters.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE;QACT,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAgJD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,EAAE,EACzB,IAAI,EAAE,aAAa,GAClB,MAAM,CASR;AAID,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AA4DD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,eAAe,EAAE,EAC3B,IAAI,EAAE,aAAa,GAClB,MAAM,CAoCR"}
|