@reneturcios/greenlight 0.9.0 → 0.9.1
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 +22 -1
- package/dist/babysit.d.ts +45 -1
- package/dist/babysit.js +187 -71
- package/dist/babysit.js.map +1 -1
- package/dist/claim-import.d.ts +59 -0
- package/dist/claim-import.js +164 -0
- package/dist/claim-import.js.map +1 -0
- package/dist/cli.js +76 -5
- package/dist/cli.js.map +1 -1
- package/dist/doctor.js +14 -6
- package/dist/doctor.js.map +1 -1
- package/dist/guard.d.ts +13 -1
- package/dist/guard.js +15 -1
- package/dist/guard.js.map +1 -1
- package/dist/help.js +19 -4
- package/dist/help.js.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/dist/init.js +1 -1
- package/dist/proxy.d.ts +50 -0
- package/dist/proxy.js +111 -0
- package/dist/proxy.js.map +1 -0
- package/integrations/README.md +3 -1
- package/integrations/claude-code/CLAUDE.snippet.md +3 -2
- package/integrations/cline/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `greenlight proxy`: a tiny localhost forwarder that attaches the user's
|
|
3
|
+
* GitHub token server-side. Greenlight's own reads are unauthenticated at
|
|
4
|
+
* 60/hr on the shared anonymous quota; the proxy lifts a user's setup to
|
|
5
|
+
* the authenticated 5000/hr budget without their token ever touching a
|
|
6
|
+
* browser, a log, or a tool's arguments beyond one env var.
|
|
7
|
+
*
|
|
8
|
+
* export GITHUB_TOKEN=<classic-token>
|
|
9
|
+
* greenlight proxy & # default 127.0.0.1:8792
|
|
10
|
+
* export GREENLIGHT_GITHUB_BASE_URL=http://127.0.0.1:8792 GITHUB_TOKEN=proxy
|
|
11
|
+
*
|
|
12
|
+
* Security model, stated plainly: the token is read once at startup from
|
|
13
|
+
* GITHUB_TOKEN (or GH_TOKEN) and is never logged, never stored, and never
|
|
14
|
+
* echoed. Incoming Authorization headers are ignored so a stray value
|
|
15
|
+
* cannot leak through. The server binds 127.0.0.1 only, and it forwards
|
|
16
|
+
* to exactly one upstream: https://api.github.com. Absolute-form request
|
|
17
|
+
* targets (http://...) are rejected so the proxy cannot be aimed at other
|
|
18
|
+
* hosts.
|
|
19
|
+
*/
|
|
20
|
+
import { createServer, request as httpRequest } from "node:http";
|
|
21
|
+
import { request as httpsRequest } from "node:https";
|
|
22
|
+
export const PROXY_DEFAULT_PORT = 8792;
|
|
23
|
+
export const PROXY_UPSTREAM = "https://api.github.com";
|
|
24
|
+
/** The exact client env line the command prints on startup. */
|
|
25
|
+
export function proxyClientEnv(port) {
|
|
26
|
+
return `GREENLIGHT_GITHUB_BASE_URL=http://127.0.0.1:${port} GITHUB_TOKEN=proxy`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read the proxy configuration from the environment. Pure: returns the
|
|
30
|
+
* config or the human-readable refusal, and never the token on the
|
|
31
|
+
* refusal path either.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveProxyConfig(env, port = PROXY_DEFAULT_PORT, upstream = PROXY_UPSTREAM) {
|
|
34
|
+
const tokenVar = env.GITHUB_TOKEN ? "GITHUB_TOKEN" : env.GH_TOKEN ? "GH_TOKEN" : undefined;
|
|
35
|
+
const token = tokenVar ? env[tokenVar] : undefined;
|
|
36
|
+
if (!tokenVar || !token) {
|
|
37
|
+
return {
|
|
38
|
+
error: "greenlight proxy needs a token: export GITHUB_TOKEN=<your-classic-token> first " +
|
|
39
|
+
"(a classic personal access token; no scopes needed for public repos). The token is read once at startup and never logged or stored.",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return { token, tokenVar, port, upstream };
|
|
43
|
+
}
|
|
44
|
+
function forwardRequest(upstream, token, clientReq, clientRes) {
|
|
45
|
+
const target = clientReq.url ?? "/";
|
|
46
|
+
// Absolute-form targets would let a client aim the proxy at another
|
|
47
|
+
// host; refuse them outright.
|
|
48
|
+
if (/^https?:\/\//i.test(target)) {
|
|
49
|
+
clientRes.writeHead(400, { "content-type": "text/plain" });
|
|
50
|
+
clientRes.end("greenlight proxy: absolute-form request targets are rejected");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const upstreamUrl = new URL(target, upstream);
|
|
54
|
+
const useHttps = upstreamUrl.protocol === "https:";
|
|
55
|
+
const doRequest = useHttps ? httpsRequest : httpRequest;
|
|
56
|
+
const headers = { ...clientReq.headers };
|
|
57
|
+
delete headers["authorization"];
|
|
58
|
+
delete headers["host"];
|
|
59
|
+
delete headers["connection"];
|
|
60
|
+
// The whole point: attach the startup token server-side.
|
|
61
|
+
headers["authorization"] = `Bearer ${token}`;
|
|
62
|
+
const upstreamReq = doRequest({
|
|
63
|
+
protocol: upstreamUrl.protocol,
|
|
64
|
+
hostname: upstreamUrl.hostname,
|
|
65
|
+
port: upstreamUrl.port || (useHttps ? 443 : 80),
|
|
66
|
+
path: upstreamUrl.pathname + upstreamUrl.search,
|
|
67
|
+
method: clientReq.method,
|
|
68
|
+
headers: headers,
|
|
69
|
+
}, (upstreamRes) => {
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const [k, v] of Object.entries(upstreamRes.headers)) {
|
|
72
|
+
if (k.toLowerCase() === "transfer-encoding")
|
|
73
|
+
continue;
|
|
74
|
+
out[k] = v;
|
|
75
|
+
}
|
|
76
|
+
clientRes.writeHead(upstreamRes.statusCode ?? 502, out);
|
|
77
|
+
upstreamRes.pipe(clientRes);
|
|
78
|
+
});
|
|
79
|
+
upstreamReq.on("error", () => {
|
|
80
|
+
if (!clientRes.headersSent)
|
|
81
|
+
clientRes.writeHead(502, { "content-type": "text/plain" });
|
|
82
|
+
clientRes.end("greenlight proxy: upstream unreachable");
|
|
83
|
+
});
|
|
84
|
+
upstreamReq.setTimeout(60000, () => upstreamReq.destroy(new Error("upstream timeout")));
|
|
85
|
+
clientReq.pipe(upstreamReq);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Start the proxy. Resolves when the socket is bound; the returned stop()
|
|
89
|
+
* drains it. The token travels only in the Authorization header toward the
|
|
90
|
+
* fixed upstream.
|
|
91
|
+
*/
|
|
92
|
+
export async function startProxy(config) {
|
|
93
|
+
const server = createServer((req, res) => forwardRequest(config.upstream, config.token, req, res));
|
|
94
|
+
await new Promise((resolve, reject) => {
|
|
95
|
+
server.once("error", reject);
|
|
96
|
+
server.listen(config.port, "127.0.0.1", () => {
|
|
97
|
+
server.off("error", reject);
|
|
98
|
+
resolve();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
const address = server.address();
|
|
102
|
+
const port = typeof address === "object" && address ? address.port : config.port;
|
|
103
|
+
return {
|
|
104
|
+
server,
|
|
105
|
+
port,
|
|
106
|
+
stop: () => new Promise((resolve, reject) => {
|
|
107
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/integrations/README.md
CHANGED
|
@@ -41,7 +41,7 @@ without it the tools still work but share the small anonymous budget.
|
|
|
41
41
|
## Verify it worked
|
|
42
42
|
|
|
43
43
|
Ask the agent: "list your MCP tools." You should see the `greenlight`
|
|
44
|
-
server with all
|
|
44
|
+
server with all 17 tools:
|
|
45
45
|
|
|
46
46
|
- `check_issue` — verdict on whether an issue is landable (GO / RISKY / TAKEN / GATED / STALE)
|
|
47
47
|
- `scan_repo` — find starter issues worth working in a repo
|
|
@@ -58,6 +58,8 @@ server with all 15 tools:
|
|
|
58
58
|
- `verify_comment` — draft the verified-finding comment for maintainer-only repos (draft only, never posts)
|
|
59
59
|
- `babysit_prs` — watch your open PRs: one prioritized action list (needs you now, waiting on them, quiet), diffed against stored watermarks
|
|
60
60
|
- `evidence_pack` — the Verified PR evidence pack for one PR: guard verdict, CI triage, review triage, protocol status of linked issues, every claim citing its source
|
|
61
|
+
- `scout_campaign` — campaign mode: scan repos, rank survivors, append SCOUTED rows to a campaign file, budget-aware
|
|
62
|
+
- `my_claims` — bucket your my-claims registry by live protocol status (linked, healthy-active, heartbeat-due, expiring, unlinked-stale, queue-lost)
|
|
61
63
|
|
|
62
64
|
## Format notes
|
|
63
65
|
|
|
@@ -7,9 +7,10 @@ your project's CLAUDE.md (or keep the file and reference it from CLAUDE.md).
|
|
|
7
7
|
## Greenlight: check before you open a PR
|
|
8
8
|
|
|
9
9
|
You have the Greenlight MCP server connected (server name: greenlight,
|
|
10
|
-
|
|
10
|
+
17 tools: check_issue, scan_repo, repo_friction, claim_issue, prep_issue,
|
|
11
11
|
open_pr, ci_triage, review_triage, guard_pr, repo_digest, claim_status,
|
|
12
|
-
preflight, verify_comment, babysit_prs, evidence_pack
|
|
12
|
+
preflight, verify_comment, babysit_prs, evidence_pack, scout_campaign,
|
|
13
|
+
my_claims). It answers one
|
|
13
14
|
question at every step: is this contribution worth doing, and is the
|
|
14
15
|
problem mine?
|
|
15
16
|
|
|
@@ -7,7 +7,7 @@ file form is:
|
|
|
7
7
|
|
|
8
8
|
Copy the `cline_mcp_settings.json` file in this folder there (merging with
|
|
9
9
|
any servers you already have), then check the Cline "MCP Servers" panel:
|
|
10
|
-
the `greenlight` server should list
|
|
10
|
+
the `greenlight` server should list 17 tools. The `disabled: false` field is
|
|
11
11
|
Cline's way of keeping the server enabled without deleting it.
|
|
12
12
|
|
|
13
13
|
Workflow: see the integrations README — your agent checks Greenlight
|
|
@@ -6,7 +6,7 @@ Windsurf's global MCP config lives at:
|
|
|
6
6
|
|
|
7
7
|
Copy the `mcp_config.json` file in this folder there (merging with any
|
|
8
8
|
servers you already have), then restart Windsurf and check its MCP panel:
|
|
9
|
-
the `greenlight` server should list
|
|
9
|
+
the `greenlight` server should list 17 tools. Newer Windsurf versions also
|
|
10
10
|
accept a project-level `.windsurf/mcp.json` file using the same
|
|
11
11
|
`mcpServers` format.
|
|
12
12
|
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
"url": "https://github.com/CloudCorpRecords/greenlight",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.9.
|
|
9
|
+
"version": "0.9.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
14
14
|
"identifier": "@reneturcios/greenlight",
|
|
15
|
-
"version": "0.9.
|
|
15
|
+
"version": "0.9.1",
|
|
16
16
|
"runtimeHint": "npx",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{
|