linear-grab-bridge 0.13.0 → 0.18.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/linear-grab-bridge.mjs +43 -1
- package/package.json +1 -1
package/linear-grab-bridge.mjs
CHANGED
|
@@ -28,7 +28,7 @@ const flag = (name, fallback) => {
|
|
|
28
28
|
const PORT = Number(flag('--port', '4577'));
|
|
29
29
|
const DIR = flag('--dir', process.cwd());
|
|
30
30
|
const CLAUDE_BIN = flag('--claude', 'claude');
|
|
31
|
-
const VERSION = '0.
|
|
31
|
+
const VERSION = '0.18.0';
|
|
32
32
|
|
|
33
33
|
/** Best-effort command runner (git/gh introspection). Never throws. */
|
|
34
34
|
function run(cmd, args, cwd = DIR) {
|
|
@@ -68,6 +68,9 @@ const tasks = new Map();
|
|
|
68
68
|
/** Linear Authorization header supplied by the panel (for the media proxy). */
|
|
69
69
|
let linearAuth = null;
|
|
70
70
|
|
|
71
|
+
/** PR state cache (gh pr view) — 60s TTL. */
|
|
72
|
+
const prStatusCache = new Map();
|
|
73
|
+
|
|
71
74
|
// ---- persistence -----------------------------------------------------------
|
|
72
75
|
|
|
73
76
|
const HISTORY_DIR = join(homedir(), '.linear-grab');
|
|
@@ -579,6 +582,44 @@ createServer(async (req, res) => {
|
|
|
579
582
|
});
|
|
580
583
|
return res.end(Buffer.from(await upstream.arrayBuffer()));
|
|
581
584
|
}
|
|
585
|
+
// Real PR states (OPEN/MERGED/CLOSED) via gh — the panel can't know
|
|
586
|
+
// merge status from Linear attachments alone.
|
|
587
|
+
if (req.method === 'POST' && url.pathname === '/pr/status') {
|
|
588
|
+
const body = await readBody(req);
|
|
589
|
+
const urls = (Array.isArray(body.urls) ? body.urls : []).slice(0, 20);
|
|
590
|
+
const statuses = {};
|
|
591
|
+
const previews = {};
|
|
592
|
+
await Promise.all(
|
|
593
|
+
urls.map(async (u) => {
|
|
594
|
+
const prUrl = String(u);
|
|
595
|
+
if (!/^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/\d+$/.test(prUrl)) return;
|
|
596
|
+
const cached = prStatusCache.get(prUrl);
|
|
597
|
+
if (cached && Date.now() - cached.at < 60_000) {
|
|
598
|
+
statuses[prUrl] = cached.state;
|
|
599
|
+
if (cached.preview) previews[prUrl] = cached.preview;
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
// comments carry the Vercel bot's preview link; the PR body often
|
|
603
|
+
// has it too (agents embed it per the completion gate).
|
|
604
|
+
const out = await run('gh', ['pr', 'view', prUrl, '--json', 'state,comments,body']);
|
|
605
|
+
try {
|
|
606
|
+
const parsed = JSON.parse(out ?? '');
|
|
607
|
+
const state = parsed.state;
|
|
608
|
+
const haystack =
|
|
609
|
+
(parsed.comments ?? []).map((c) => c.body ?? '').join('\n') + '\n' + (parsed.body ?? '');
|
|
610
|
+
const preview = (haystack.match(/https:\/\/[a-z0-9-]+\.vercel\.app[^\s)"\]]*/i) ?? [])[0] ?? null;
|
|
611
|
+
if (state) {
|
|
612
|
+
prStatusCache.set(prUrl, { state, preview, at: Date.now() });
|
|
613
|
+
statuses[prUrl] = state;
|
|
614
|
+
if (preview) previews[prUrl] = preview;
|
|
615
|
+
}
|
|
616
|
+
} catch {
|
|
617
|
+
/* gh missing / not accessible */
|
|
618
|
+
}
|
|
619
|
+
}),
|
|
620
|
+
);
|
|
621
|
+
return json(res, 200, { statuses, previews });
|
|
622
|
+
}
|
|
582
623
|
// Fast-merge: after reviewing the demo, one click merges the PR via gh.
|
|
583
624
|
if (req.method === 'POST' && url.pathname === '/pr/merge') {
|
|
584
625
|
const body = await readBody(req);
|
|
@@ -587,6 +628,7 @@ createServer(async (req, res) => {
|
|
|
587
628
|
return json(res, 400, { error: 'invalid PR url' });
|
|
588
629
|
}
|
|
589
630
|
const out = await run('gh', ['pr', 'merge', prUrl, '--squash']);
|
|
631
|
+
prStatusCache.delete(prUrl);
|
|
590
632
|
if (out === null) {
|
|
591
633
|
return json(res, 502, { error: 'gh pr merge failed — check gh auth / merge conflicts / required checks' });
|
|
592
634
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linear-grab-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Local bridge for Linear Grab — delegate issues from the browser panel to headless Claude Code sessions running in your repo, with live status and an upload relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|