@reneturcios/greenlight 0.4.0 → 0.6.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.
Files changed (47) hide show
  1. package/README.md +50 -1
  2. package/dist/babysit.d.ts +132 -0
  3. package/dist/babysit.js +439 -0
  4. package/dist/babysit.js.map +1 -0
  5. package/dist/cli.js +207 -3
  6. package/dist/cli.js.map +1 -1
  7. package/dist/digest.d.ts +1 -1
  8. package/dist/digest.js +1 -1
  9. package/dist/doctor.d.ts +34 -0
  10. package/dist/doctor.js +158 -0
  11. package/dist/doctor.js.map +1 -0
  12. package/dist/evidence.d.ts +48 -0
  13. package/dist/evidence.js +254 -0
  14. package/dist/evidence.js.map +1 -0
  15. package/dist/github.js +2 -2
  16. package/dist/github.js.map +1 -1
  17. package/dist/guard.d.ts +63 -3
  18. package/dist/guard.js +131 -4
  19. package/dist/guard.js.map +1 -1
  20. package/dist/help.d.ts +25 -0
  21. package/dist/help.js +234 -0
  22. package/dist/help.js.map +1 -0
  23. package/dist/index.js +79 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/init.d.ts +61 -0
  26. package/dist/init.js +298 -0
  27. package/dist/init.js.map +1 -0
  28. package/dist/passport.d.ts +52 -0
  29. package/dist/passport.js +218 -0
  30. package/dist/passport.js.map +1 -0
  31. package/dist/verify-comment.d.ts +75 -0
  32. package/dist/verify-comment.js +149 -0
  33. package/dist/verify-comment.js.map +1 -0
  34. package/dist/verify-parsers.d.ts +25 -0
  35. package/dist/verify-parsers.js +56 -0
  36. package/dist/verify-parsers.js.map +1 -0
  37. package/dist/verify.d.ts +52 -0
  38. package/dist/verify.js +276 -0
  39. package/dist/verify.js.map +1 -0
  40. package/integrations/README.md +4 -1
  41. package/integrations/claude-code/CLAUDE.snippet.md +6 -5
  42. package/integrations/cline/README.md +1 -1
  43. package/integrations/copilot/copilot-instructions.snippet.md +6 -3
  44. package/integrations/cursor/greenlight.mdc +2 -1
  45. package/integrations/windsurf/README.md +1 -1
  46. package/package.json +1 -1
  47. package/server.json +2 -2
package/dist/init.js ADDED
@@ -0,0 +1,298 @@
1
+ /**
2
+ * `greenlight init`: one-command agent setup.
3
+ *
4
+ * Before this, onboarding an agent meant cloning the repo and hand-copying
5
+ * MCP configs plus instruction snippets from integrations/ per tool. Init
6
+ * does that from the installed package: it copies (or JSON-merges) the MCP
7
+ * server config into the current project and installs the tool's instruction
8
+ * snippet, then prints the restart + verify steps.
9
+ *
10
+ * Safety rules: existing configs are merged (other servers are preserved),
11
+ * instruction appends are marker-guarded and idempotent, nothing is
12
+ * overwritten unless --force is given, and --dry-run only reports.
13
+ */
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { homedir } from "node:os";
17
+ import { fileURLToPath } from "node:url";
18
+ export const INIT_TOOLS = [
19
+ {
20
+ id: "claude-code",
21
+ label: "Claude Code",
22
+ configSrc: "claude-code/.mcp.json",
23
+ configDest: ".mcp.json",
24
+ serverMapKey: "mcpServers",
25
+ instructionsSrc: "claude-code/CLAUDE.snippet.md",
26
+ instructionsDest: "CLAUDE.md",
27
+ instructionsMode: "append-markers",
28
+ detectMarkers: [".mcp.json", "CLAUDE.md"],
29
+ restartNote: "Restart Claude Code, then ask it to list its MCP tools.",
30
+ },
31
+ {
32
+ id: "cursor",
33
+ label: "Cursor",
34
+ configSrc: "cursor/.cursor/mcp.json",
35
+ configDest: ".cursor/mcp.json",
36
+ serverMapKey: "mcpServers",
37
+ instructionsSrc: "cursor/greenlight.mdc",
38
+ instructionsDest: ".cursor/rules/greenlight.mdc",
39
+ instructionsMode: "copy",
40
+ detectMarkers: [".cursor"],
41
+ restartNote: "Restart Cursor, then ask the agent to list its MCP tools.",
42
+ },
43
+ {
44
+ id: "copilot",
45
+ label: "VS Code / Copilot",
46
+ configSrc: "copilot/.vscode/mcp.json",
47
+ configDest: ".vscode/mcp.json",
48
+ serverMapKey: "servers",
49
+ instructionsSrc: "copilot/copilot-instructions.snippet.md",
50
+ instructionsDest: ".github/copilot-instructions.md",
51
+ instructionsMode: "append-markers",
52
+ detectMarkers: [".vscode", ".github/copilot-instructions.md"],
53
+ restartNote: "Reload VS Code, then check the MCP server list.",
54
+ },
55
+ {
56
+ id: "windsurf",
57
+ label: "Windsurf",
58
+ configSrc: "windsurf/mcp_config.json",
59
+ configDest: ".codeium/windsurf/mcp_config.json",
60
+ homeDest: true,
61
+ serverMapKey: "mcpServers",
62
+ detectMarkers: [],
63
+ restartNote: "Restart Windsurf, then open Manage MCP Servers to confirm greenlight is listed.",
64
+ },
65
+ {
66
+ id: "cline",
67
+ label: "Cline",
68
+ configSrc: "cline/cline_mcp_settings.json",
69
+ configDest: ".cline/mcp_settings.json",
70
+ serverMapKey: "mcpServers",
71
+ detectMarkers: [".cline"],
72
+ restartNote: "Restart your editor, then check Cline's MCP Servers list.",
73
+ },
74
+ {
75
+ id: "generic",
76
+ label: "Any other MCP client",
77
+ configSrc: "mcp.json",
78
+ configDest: "greenlight.mcp.json",
79
+ serverMapKey: null,
80
+ detectMarkers: [],
81
+ restartNote: "Point your MCP client at greenlight.mcp.json (command npx, args -y @reneturcios/greenlight mcp).",
82
+ },
83
+ ];
84
+ /** Markers guarding an appended instruction block; presence means "already installed". */
85
+ export const INIT_BEGIN_MARKER = "<!-- greenlight:init:begin -->";
86
+ export const INIT_END_MARKER = "<!-- greenlight:init:end -->";
87
+ /** The integrations pack ships inside the npm package next to dist/. */
88
+ export function defaultPackDir() {
89
+ return fileURLToPath(new URL("../integrations/", import.meta.url));
90
+ }
91
+ export function findTool(id) {
92
+ return INIT_TOOLS.find((t) => t.id === id.toLowerCase());
93
+ }
94
+ /** Split --agent values on commas, lowercase, dedupe, drop empties. */
95
+ export function parseAgentList(values) {
96
+ const out = [];
97
+ for (const v of values) {
98
+ for (const part of v.split(",")) {
99
+ const id = part.trim().toLowerCase();
100
+ if (id && !out.includes(id))
101
+ out.push(id);
102
+ }
103
+ }
104
+ return out;
105
+ }
106
+ /** Tool ids whose marker files exist in the project (a hint, never auto-applied). */
107
+ export function detectTools(projectDir) {
108
+ return INIT_TOOLS.filter((t) => t.detectMarkers.length > 0 && t.detectMarkers.some((m) => existsSync(join(projectDir, m)))).map((t) => t.id);
109
+ }
110
+ function sameJson(a, b) {
111
+ return JSON.stringify(a) === JSON.stringify(b);
112
+ }
113
+ function writeFileEnsured(path, content, dryRun) {
114
+ if (dryRun)
115
+ return;
116
+ mkdirSync(dirname(path), { recursive: true });
117
+ writeFileSync(path, content, "utf8");
118
+ }
119
+ /**
120
+ * Install one tool's MCP config. Existing JSON configs are merged (the
121
+ * greenlight server is added, everything else preserved). Returns the file
122
+ * result; throws only when the destination is unparseable and --force was
123
+ * not given, since silently dropping a broken config would hide user data.
124
+ */
125
+ function installConfig(tool, destPath, packContent, force, dryRun) {
126
+ if (tool.serverMapKey === null) {
127
+ if (!existsSync(destPath)) {
128
+ writeFileEnsured(destPath, packContent, dryRun);
129
+ return { path: destPath, action: "created", detail: "config copied" };
130
+ }
131
+ const existing = readFileSync(destPath, "utf8");
132
+ if (existing === packContent) {
133
+ return { path: destPath, action: "skipped", detail: "already set up" };
134
+ }
135
+ if (!force) {
136
+ return {
137
+ path: destPath,
138
+ action: "skipped",
139
+ detail: "exists with different content; re-run with --force to overwrite",
140
+ };
141
+ }
142
+ writeFileEnsured(destPath, packContent, dryRun);
143
+ return { path: destPath, action: "overwritten", detail: "overwritten with --force" };
144
+ }
145
+ const packDoc = JSON.parse(packContent);
146
+ const packEntry = packDoc[tool.serverMapKey]?.greenlight;
147
+ if (!existsSync(destPath)) {
148
+ writeFileEnsured(destPath, packContent.endsWith("\n") ? packContent : packContent + "\n", dryRun);
149
+ return { path: destPath, action: "created", detail: "config copied" };
150
+ }
151
+ let destDoc;
152
+ try {
153
+ destDoc = JSON.parse(readFileSync(destPath, "utf8"));
154
+ }
155
+ catch {
156
+ if (!force) {
157
+ throw new Error(`${destPath} exists but is not valid JSON; fix it or re-run with --force to overwrite it`);
158
+ }
159
+ writeFileEnsured(destPath, packContent.endsWith("\n") ? packContent : packContent + "\n", dryRun);
160
+ return { path: destPath, action: "overwritten", detail: "invalid JSON overwritten with --force" };
161
+ }
162
+ if (typeof destDoc !== "object" || destDoc === null || Array.isArray(destDoc)) {
163
+ if (!force) {
164
+ throw new Error(`${destPath} exists but is not a JSON object; fix it or re-run with --force to overwrite it`);
165
+ }
166
+ writeFileEnsured(destPath, packContent.endsWith("\n") ? packContent : packContent + "\n", dryRun);
167
+ return { path: destPath, action: "overwritten", detail: "non-object JSON overwritten with --force" };
168
+ }
169
+ if (typeof destDoc[tool.serverMapKey] !== "object" || destDoc[tool.serverMapKey] === null) {
170
+ destDoc[tool.serverMapKey] = {};
171
+ }
172
+ const map = destDoc[tool.serverMapKey];
173
+ if ("greenlight" in map && sameJson(map.greenlight, packEntry)) {
174
+ return { path: destPath, action: "skipped", detail: "greenlight server already configured" };
175
+ }
176
+ const hadEntry = "greenlight" in map;
177
+ map.greenlight = packEntry;
178
+ writeFileEnsured(destPath, JSON.stringify(destDoc, null, 2) + "\n", dryRun);
179
+ return {
180
+ path: destPath,
181
+ action: hadEntry ? "updated" : "merged",
182
+ detail: hadEntry ? "greenlight server entry refreshed, other servers kept" : "greenlight server added, other servers kept",
183
+ };
184
+ }
185
+ /** Install one tool's instruction snippet (append-with-markers or whole copy). */
186
+ function installInstructions(tool, destPath, snippet, force, dryRun) {
187
+ if (tool.instructionsMode === "copy") {
188
+ if (!existsSync(destPath)) {
189
+ writeFileEnsured(destPath, snippet, dryRun);
190
+ return { path: destPath, action: "created", detail: "rule file copied" };
191
+ }
192
+ if (readFileSync(destPath, "utf8") === snippet) {
193
+ return { path: destPath, action: "skipped", detail: "already set up" };
194
+ }
195
+ if (!force) {
196
+ return {
197
+ path: destPath,
198
+ action: "skipped",
199
+ detail: "exists with different content; re-run with --force to overwrite",
200
+ };
201
+ }
202
+ writeFileEnsured(destPath, snippet, dryRun);
203
+ return { path: destPath, action: "overwritten", detail: "overwritten with --force" };
204
+ }
205
+ // append-markers: idempotent by marker presence; never touches user text.
206
+ if (existsSync(destPath) && readFileSync(destPath, "utf8").includes(INIT_BEGIN_MARKER)) {
207
+ return { path: destPath, action: "skipped", detail: "instructions already installed" };
208
+ }
209
+ const block = `${INIT_BEGIN_MARKER}\n${snippet.trim()}\n${INIT_END_MARKER}\n`;
210
+ if (!existsSync(destPath)) {
211
+ writeFileEnsured(destPath, block, dryRun);
212
+ return { path: destPath, action: "created", detail: "instructions installed" };
213
+ }
214
+ const existing = readFileSync(destPath, "utf8");
215
+ const joined = existing + (existing.endsWith("\n") ? "\n" : "\n\n") + block;
216
+ writeFileEnsured(destPath, joined, dryRun);
217
+ return { path: destPath, action: "updated", detail: "instructions appended, existing content kept" };
218
+ }
219
+ /**
220
+ * Set up the given tools. Throws on unknown tool ids or a missing pack so
221
+ * the CLI can show the tool list instead of half-writing.
222
+ */
223
+ export function initTools(toolIds, opts = {}) {
224
+ const projectDir = opts.projectDir ?? process.cwd();
225
+ const homeDir = opts.homeDir ?? homedir();
226
+ const packDir = opts.packDir ?? defaultPackDir();
227
+ const force = opts.force ?? false;
228
+ const includeInstructions = opts.includeInstructions ?? true;
229
+ const dryRun = opts.dryRun ?? false;
230
+ const tools = toolIds.map((id) => {
231
+ const tool = findTool(id);
232
+ if (!tool) {
233
+ throw new Error(`Unknown agent "${id}". Run 'greenlight init --list' to see the supported tools.`);
234
+ }
235
+ return tool;
236
+ });
237
+ // Read every pack file up front so a missing pack fails before writing.
238
+ const packFiles = new Map();
239
+ const readPack = (rel) => {
240
+ const cached = packFiles.get(rel);
241
+ if (cached !== undefined)
242
+ return cached;
243
+ const abs = join(packDir, rel);
244
+ if (!existsSync(abs)) {
245
+ throw new Error(`Pack file missing: ${rel} (looked in ${packDir})`);
246
+ }
247
+ const content = readFileSync(abs, "utf8");
248
+ packFiles.set(rel, content);
249
+ return content;
250
+ };
251
+ for (const tool of tools) {
252
+ readPack(tool.configSrc);
253
+ if (includeInstructions && tool.instructionsSrc)
254
+ readPack(tool.instructionsSrc);
255
+ }
256
+ return tools.map((tool) => {
257
+ const notes = [];
258
+ const files = [];
259
+ const base = tool.homeDest ? homeDir : projectDir;
260
+ const configDest = join(base, tool.configDest);
261
+ files.push(installConfig(tool, configDest, readPack(tool.configSrc), force, dryRun));
262
+ if (includeInstructions && tool.instructionsSrc && tool.instructionsDest) {
263
+ const snippetDest = join(projectDir, tool.instructionsDest);
264
+ files.push(installInstructions(tool, snippetDest, readPack(tool.instructionsSrc), force, dryRun));
265
+ }
266
+ else if (tool.instructionsSrc && !includeInstructions) {
267
+ notes.push("instructions skipped (--no-instructions)");
268
+ }
269
+ notes.push(tool.restartNote);
270
+ return { id: tool.id, label: tool.label, files, notes };
271
+ });
272
+ }
273
+ /** Human-readable report of an init run (file actions plus next steps). */
274
+ export function formatInitHuman(results, dryRun) {
275
+ const verb = {
276
+ created: "create",
277
+ merged: "merge into",
278
+ updated: "update",
279
+ overwritten: "overwrite",
280
+ skipped: "skip",
281
+ };
282
+ const lines = [];
283
+ if (dryRun)
284
+ lines.push("Dry run: nothing was written. Re-run without --dry-run to apply.");
285
+ for (const r of results) {
286
+ lines.push(`${r.label}:`);
287
+ for (const f of r.files) {
288
+ const action = dryRun ? `would ${verb[f.action]}` : f.action;
289
+ lines.push(` ${action}: ${f.path} (${f.detail})`);
290
+ }
291
+ for (const n of r.notes)
292
+ lines.push(` next: ${n}`);
293
+ }
294
+ lines.push("Verify: ask your agent to list its MCP tools; greenlight should appear with 12 tools.");
295
+ lines.push("Tip: set GITHUB_TOKEN (classic PAT, no scopes needed for public repos) for the 5000 req/hr limit.");
296
+ return lines.join("\n");
297
+ }
298
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ export declare function ledgerBaseUrl(): string;
2
+ export interface LedgerClaim {
3
+ repo: string;
4
+ issue: number;
5
+ url: string;
6
+ claimedAt: string;
7
+ expiresAt: string;
8
+ state: string;
9
+ commentUrl: string | null;
10
+ }
11
+ export interface LedgerAccount {
12
+ login: string;
13
+ activeClaims: LedgerClaim[];
14
+ queuedClaims: LedgerClaim[];
15
+ completedCount: number | null;
16
+ expiredCount: number | null;
17
+ overCap: boolean | null;
18
+ }
19
+ export interface PassportDeps {
20
+ /** Null means the Ledger has no file for this account (honest 404). */
21
+ fetchAccount?: (login: string) => Promise<LedgerAccount | null>;
22
+ /** Null means the lookup failed: reported as unknown, never zero. */
23
+ fetchMergedCount?: (login: string) => Promise<number | null>;
24
+ /**
25
+ * Enrich one claim with the Ledger's per-issue status file
26
+ * (v1/issues/<repo>/<n>/status.json), which carries the claim dates the
27
+ * per-account file omits. Null means no enrichment: the claim renders
28
+ * from the account file alone.
29
+ */
30
+ fetchClaimDetail?: (repo: string, issue: number) => Promise<{
31
+ claimedAt: string;
32
+ expiresAt: string;
33
+ commentUrl: string | null;
34
+ } | null>;
35
+ }
36
+ /**
37
+ * Normalize a Ledger account payload defensively: the Ledger is our own
38
+ * API and its fields can drift, so unknown shapes degrade to empties
39
+ * rather than crashing. Whatever is normalized is stated as coming from
40
+ * the Ledger; whatever is absent is stated as absent.
41
+ */
42
+ export declare function normalizeAccount(login: string, raw: unknown): LedgerAccount;
43
+ export interface Passport {
44
+ login: string;
45
+ found: boolean;
46
+ account: LedgerAccount | null;
47
+ mergedPrs: number | null;
48
+ honorRatio: string;
49
+ markdown: string;
50
+ json: Record<string, unknown>;
51
+ }
52
+ export declare function buildPassport(login: string, deps?: PassportDeps, now?: Date): Promise<Passport>;
@@ -0,0 +1,218 @@
1
+ /**
2
+ * The contributor passport. Reads the Claim Ledger's public API for the
3
+ * account's protocol claims (held, completed, expired, queued, over-cap)
4
+ * and the GitHub search API for merged PRs, and renders one honest page.
5
+ *
6
+ * If the Ledger has no file for the account, it says so plainly: a
7
+ * passport starts with your first protocol claim.
8
+ */
9
+ import { createRequire } from "node:module";
10
+ const GREENLIGHT_VERSION = (() => {
11
+ try {
12
+ return createRequire(import.meta.url)("../package.json").version;
13
+ }
14
+ catch {
15
+ return "unknown";
16
+ }
17
+ })();
18
+ export function ledgerBaseUrl() {
19
+ const env = process.env["GREENLIGHT_LEDGER_URL"];
20
+ return (env && env.trim().length > 0 ? env : "https://cloudcorprecords.github.io/claim-ledger").replace(/\/+$/, "");
21
+ }
22
+ /**
23
+ * Normalize a Ledger account payload defensively: the Ledger is our own
24
+ * API and its fields can drift, so unknown shapes degrade to empties
25
+ * rather than crashing. Whatever is normalized is stated as coming from
26
+ * the Ledger; whatever is absent is stated as absent.
27
+ */
28
+ export function normalizeAccount(login, raw) {
29
+ const empty = { login, activeClaims: [], queuedClaims: [], completedCount: null, expiredCount: null, overCap: null };
30
+ if (!raw || typeof raw !== "object")
31
+ return empty;
32
+ const r = raw;
33
+ const normClaim = (c) => {
34
+ if (!c || typeof c !== "object")
35
+ return null;
36
+ const o = c;
37
+ const repo = typeof o["repo"] === "string"
38
+ ? o["repo"]
39
+ : typeof o["owner"] === "string" && typeof o["repo_name"] === "string"
40
+ ? `${o["owner"]}/${o["repo_name"]}`
41
+ : "unknown/unknown";
42
+ const issue = typeof o["issue"] === "number" ? o["issue"] : typeof o["number"] === "number" ? o["number"] : 0;
43
+ return {
44
+ repo,
45
+ issue,
46
+ url: typeof o["url"] === "string" && o["url"]
47
+ ? o["url"]
48
+ : issue > 0 && repo !== "unknown/unknown"
49
+ ? `https://github.com/${repo}/issues/${issue}`
50
+ : "",
51
+ claimedAt: typeof o["claimed_at"] === "string" ? o["claimed_at"] : typeof o["claimedAt"] === "string" ? o["claimedAt"] : "",
52
+ expiresAt: typeof o["expires_at"] === "string" ? o["expires_at"] : typeof o["expiresAt"] === "string" ? o["expiresAt"] : "",
53
+ state: typeof o["state"] === "string" ? o["state"] : "active",
54
+ commentUrl: typeof o["comment_url"] === "string" ? o["comment_url"] : typeof o["commentUrl"] === "string" ? o["commentUrl"] : null,
55
+ };
56
+ };
57
+ const arr = (v) => (Array.isArray(v) ? v.map(normClaim).filter((c) => c !== null) : []);
58
+ const num = (v) => (typeof v === "number" ? v : null);
59
+ const bool = (v) => (typeof v === "boolean" ? v : null);
60
+ return {
61
+ login,
62
+ activeClaims: arr(r["active_claims"] ?? r["activeClaims"] ?? r["claims"]),
63
+ queuedClaims: arr(r["queued_claims"] ?? r["queuedClaims"] ?? r["queue"]),
64
+ completedCount: num(r["completed_count"] ?? r["completedClaims"]),
65
+ expiredCount: num(r["expired_count"] ?? r["expiredClaims"]),
66
+ overCap: bool(r["over_cap"] ?? r["overCap"]),
67
+ };
68
+ }
69
+ async function defaultFetchAccount(login) {
70
+ const url = `${ledgerBaseUrl()}/v1/accounts/${encodeURIComponent(login)}.json`;
71
+ let res;
72
+ try {
73
+ res = await fetch(url);
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ if (res.status === 404)
79
+ return null;
80
+ if (!res.ok)
81
+ return null;
82
+ try {
83
+ return normalizeAccount(login, await res.json());
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ async function defaultFetchClaimDetail(repo, issue) {
90
+ const url = `${ledgerBaseUrl()}/v1/issues/${repo}/${issue}/status.json`;
91
+ try {
92
+ const res = await fetch(url);
93
+ if (!res.ok)
94
+ return null;
95
+ const data = (await res.json());
96
+ const claim = data.claim;
97
+ if (!claim || typeof claim !== "object")
98
+ return null;
99
+ return {
100
+ claimedAt: typeof claim.claimed_at === "string" ? claim.claimed_at : "",
101
+ expiresAt: typeof claim.expires_at === "string" ? claim.expires_at : "",
102
+ commentUrl: typeof claim.comment_url === "string" ? claim.comment_url : null,
103
+ };
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ async function defaultFetchMergedCount(login) {
110
+ const q = encodeURIComponent(`author:${login} type:pr is:merged`);
111
+ try {
112
+ const res = await fetch(`https://api.github.com/search/issues?q=${q}&per_page=1`, {
113
+ headers: { "User-Agent": "greenlight", Accept: "application/vnd.github+json" },
114
+ });
115
+ if (!res.ok)
116
+ return null;
117
+ const data = (await res.json());
118
+ return typeof data.total_count === "number" ? data.total_count : null;
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
124
+ function claimLine(c) {
125
+ const dates = [c.claimedAt.slice(0, 10), c.expiresAt.slice(0, 10)].filter((d) => d.length > 0).join(" -> ");
126
+ return `${c.repo}#${c.issue} (${c.state}${dates ? `, ${dates}` : ""}) ${c.commentUrl ? c.commentUrl : c.url}`;
127
+ }
128
+ export async function buildPassport(login, deps = {}, now = new Date()) {
129
+ const fetchAccount = deps.fetchAccount ?? defaultFetchAccount;
130
+ const fetchMergedCount = deps.fetchMergedCount ?? defaultFetchMergedCount;
131
+ const fetchClaimDetail = deps.fetchClaimDetail ?? defaultFetchClaimDetail;
132
+ const account = await fetchAccount(login).catch(() => null);
133
+ const mergedPrs = await fetchMergedCount(login).catch(() => null);
134
+ if (!account) {
135
+ const markdown = [
136
+ `# Contributor passport: @${login}`,
137
+ "",
138
+ "No passport file for this account in the Claim Ledger yet.",
139
+ "",
140
+ "Your passport starts with your first protocol claim: claim an issue with the Greenlight protocol marker " +
141
+ "(`greenlight claim` drafts it for you) and the Ledger indexes it within 15 minutes.",
142
+ "",
143
+ `Ledger: ${ledgerBaseUrl()}. Merged PRs on GitHub: ${mergedPrs === null ? "unknown (lookup failed)" : mergedPrs}.`,
144
+ "",
145
+ `_Passport generated by Greenlight v${GREENLIGHT_VERSION}._`,
146
+ ].join("\n");
147
+ return {
148
+ login,
149
+ found: false,
150
+ account: null,
151
+ mergedPrs,
152
+ honorRatio: "no ledger history",
153
+ markdown,
154
+ json: { login, found: false, mergedPrs, ledger: ledgerBaseUrl() },
155
+ };
156
+ }
157
+ // Enrich claims with dates from the per-issue status files. Each
158
+ // enrichment is independent and best-effort: a missing file leaves the
159
+ // claim exactly as the account file described it.
160
+ const enrich = async (claims) => Promise.all(claims.map(async (c) => {
161
+ if (c.issue <= 0)
162
+ return c;
163
+ const detail = await fetchClaimDetail(c.repo, c.issue).catch(() => null);
164
+ if (!detail)
165
+ return c;
166
+ return {
167
+ ...c,
168
+ claimedAt: c.claimedAt || detail.claimedAt,
169
+ expiresAt: c.expiresAt || detail.expiresAt,
170
+ commentUrl: c.commentUrl ?? detail.commentUrl,
171
+ };
172
+ }));
173
+ account.activeClaims = await enrich(account.activeClaims);
174
+ account.queuedClaims = await enrich(account.queuedClaims);
175
+ const completed = account.completedCount ?? account.activeClaims.filter((c) => c.state === "completed").length;
176
+ const expired = account.expiredCount ?? account.activeClaims.filter((c) => c.state === "expired").length;
177
+ const historyKnown = account.completedCount !== null || account.expiredCount !== null || account.activeClaims.some((c) => c.state === "completed" || c.state === "expired");
178
+ const honorRatio = !historyKnown || completed + expired === 0
179
+ ? "no completed/expired history in the ledger yet"
180
+ : `${completed} completed vs ${expired} expired (${Math.round((completed / (completed + expired)) * 100)}% completed)`;
181
+ const lines = [];
182
+ lines.push(`# Contributor passport: @${login}`);
183
+ lines.push("");
184
+ lines.push("## Claim Protocol record");
185
+ lines.push(`Active protocol claims: ${account.activeClaims.length}`);
186
+ for (const c of account.activeClaims)
187
+ lines.push(`- ${claimLine(c)}`);
188
+ lines.push(`Queued behind others: ${account.queuedClaims.length}`);
189
+ for (const c of account.queuedClaims)
190
+ lines.push(`- ${claimLine(c)}`);
191
+ lines.push(`Claim honor record: ${honorRatio}`);
192
+ lines.push(`Over the active-claim cap: ${account.overCap === null ? "not reported by the ledger" : account.overCap ? "yes (squatting flag)" : "no"}`);
193
+ lines.push("");
194
+ lines.push("## GitHub record");
195
+ lines.push(`Merged PRs: ${mergedPrs === null ? "unknown (lookup failed)" : mergedPrs}`);
196
+ lines.push("");
197
+ lines.push(`Ledger: ${ledgerBaseUrl()}. Checked ${now.toISOString().slice(0, 10)}.`);
198
+ lines.push(`_Passport generated by Greenlight v${GREENLIGHT_VERSION}._`);
199
+ return {
200
+ login,
201
+ found: true,
202
+ account,
203
+ mergedPrs,
204
+ honorRatio,
205
+ markdown: lines.join("\n"),
206
+ json: {
207
+ login,
208
+ found: true,
209
+ ledger: ledgerBaseUrl(),
210
+ activeClaims: account.activeClaims,
211
+ queuedClaims: account.queuedClaims,
212
+ honorRatio,
213
+ overCap: account.overCap,
214
+ mergedPrs,
215
+ },
216
+ };
217
+ }
218
+ //# sourceMappingURL=passport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passport.js","sourceRoot":"","sources":["../src/passport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,kBAAkB,GAAW,CAAC,GAAG,EAAE;IACvC,IAAI,CAAC;QACH,OAAQ,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;IAC5F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC;AAEL,MAAM,UAAU,aAAa;IAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACtH,CAAC;AAmCD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,GAAY;IAC1D,MAAM,KAAK,GAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACpI,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,SAAS,GAAG,CAAC,CAAU,EAAsB,EAAE;QACnD,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC7C,MAAM,CAAC,GAAG,CAA4B,CAAC;QACvC,MAAM,IAAI,GACR,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,QAAQ;YAC3B,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACX,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,QAAQ;gBACpE,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,EAAE;gBACnC,CAAC,CAAC,iBAAiB,CAAC;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9G,OAAO;YACL,IAAI;YACJ,KAAK;YACL,GAAG,EACD,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,CAAC;gBACtC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;gBACV,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,KAAK,iBAAiB;oBACvC,CAAC,CAAC,sBAAsB,IAAI,WAAW,KAAK,EAAE;oBAC9C,CAAC,CAAC,EAAE;YACV,SAAS,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;YAC3H,SAAS,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE;YAC3H,KAAK,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ;YAC7D,UAAU,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;SACnI,CAAC;IACJ,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,CAAU,EAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAClI,MAAM,GAAG,GAAG,CAAC,CAAU,EAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,CAAC,CAAU,EAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjF,OAAO;QACL,KAAK;QACL,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;QACzE,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QACxE,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACjE,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC;QAC3D,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,KAAa;IAC9C,MAAM,GAAG,GAAG,GAAG,aAAa,EAAE,gBAAgB,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;IAC/E,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,IAAY,EAAE,KAAa;IAChE,MAAM,GAAG,GAAG,GAAG,aAAa,EAAE,cAAc,IAAI,IAAI,KAAK,cAAc,CAAC;IACxE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsF,CAAC;QACrH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACrD,OAAO;YACL,SAAS,EAAE,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;YACvE,SAAS,EAAE,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;YACvE,UAAU,EAAE,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;SAC7E,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,KAAa;IAClD,MAAM,CAAC,GAAG,kBAAkB,CAAC,UAAU,KAAK,oBAAoB,CAAC,CAAC;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,0CAA0C,CAAC,aAAa,EAAE;YAChF,OAAO,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,6BAA6B,EAAE;SAC/E,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA8B,CAAC;QAC7D,OAAO,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAYD,SAAS,SAAS,CAAC,CAAc;IAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5G,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAChH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa,EAAE,OAAqB,EAAE,EAAE,MAAY,IAAI,IAAI,EAAE;IAChG,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,mBAAmB,CAAC;IAC9D,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;IAC1E,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;IAC1E,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAElE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG;YACf,4BAA4B,KAAK,EAAE;YACnC,EAAE;YACF,4DAA4D;YAC5D,EAAE;YACF,0GAA0G;gBACxG,qFAAqF;YACvF,EAAE;YACF,WAAW,aAAa,EAAE,2BAA2B,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS,GAAG;YAClH,EAAE;YACF,sCAAsC,kBAAkB,IAAI;SAC7D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,OAAO;YACL,KAAK;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI;YACb,SAAS;YACT,UAAU,EAAE,mBAAmB;YAC/B,QAAQ;YACR,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE;SAClE,CAAC;IACJ,CAAC;IAED,iEAAiE;IACjE,uEAAuE;IACvE,kDAAkD;IAClD,MAAM,MAAM,GAAG,KAAK,EAAE,MAAqB,EAA0B,EAAE,CACrE,OAAO,CAAC,GAAG,CACT,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QACrB,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC;QACtB,OAAO;YACL,GAAG,CAAC;YACJ,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS;YAC1C,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS;YAC1C,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU;SAC9C,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IACJ,OAAO,CAAC,YAAY,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,OAAO,CAAC,YAAY,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAE1D,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IAC/G,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzG,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc,KAAK,IAAI,IAAI,OAAO,CAAC,YAAY,KAAK,IAAI,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAC5K,MAAM,UAAU,GACd,CAAC,YAAY,IAAI,SAAS,GAAG,OAAO,KAAK,CAAC;QACxC,CAAC,CAAC,gDAAgD;QAClD,CAAC,CAAC,GAAG,SAAS,iBAAiB,OAAO,aAAa,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC;IAE3H,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,CAAC,2BAA2B,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtE,KAAK,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtE,KAAK,CAAC,IAAI,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CACR,8BAA8B,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,EAAE,CAC1I,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,eAAe,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACxF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,WAAW,aAAa,EAAE,aAAa,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IACrF,KAAK,CAAC,IAAI,CAAC,sCAAsC,kBAAkB,IAAI,CAAC,CAAC;IAEzE,OAAO;QACL,KAAK;QACL,KAAK,EAAE,IAAI;QACX,OAAO;QACP,SAAS;QACT,UAAU;QACV,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1B,IAAI,EAAE;YACJ,KAAK;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,aAAa,EAAE;YACvB,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,UAAU;YACV,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,SAAS;SACV;KACF,CAAC;AACJ,CAAC"}