linear-grab 0.15.0 → 0.15.2
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 +1 -1
- package/bin/linear-grab-bridge.mjs +34 -1
- package/dist/index.global.js +33 -33
- package/dist/linear-grab.js +3585 -3532
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ export default function RootLayout({ children }) {
|
|
|
26
26
|
{children}
|
|
27
27
|
{process.env.NODE_ENV === "development" && (
|
|
28
28
|
<Script
|
|
29
|
-
src="https://cdn.jsdelivr.net/gh/ahmedbanihanibh/linear-grab@v0.15.
|
|
29
|
+
src="https://cdn.jsdelivr.net/gh/ahmedbanihanibh/linear-grab@v0.15.2/dist/index.global.js"
|
|
30
30
|
crossOrigin="anonymous"
|
|
31
31
|
strategy="afterInteractive"
|
|
32
32
|
/>
|
|
@@ -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.15.1';
|
|
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,35 @@ 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
|
+
await Promise.all(
|
|
592
|
+
urls.map(async (u) => {
|
|
593
|
+
const prUrl = String(u);
|
|
594
|
+
if (!/^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/pull\/\d+$/.test(prUrl)) return;
|
|
595
|
+
const cached = prStatusCache.get(prUrl);
|
|
596
|
+
if (cached && Date.now() - cached.at < 60_000) {
|
|
597
|
+
statuses[prUrl] = cached.state;
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const out = await run('gh', ['pr', 'view', prUrl, '--json', 'state']);
|
|
601
|
+
try {
|
|
602
|
+
const state = JSON.parse(out ?? '').state;
|
|
603
|
+
if (state) {
|
|
604
|
+
prStatusCache.set(prUrl, { state, at: Date.now() });
|
|
605
|
+
statuses[prUrl] = state;
|
|
606
|
+
}
|
|
607
|
+
} catch {
|
|
608
|
+
/* gh missing / not accessible */
|
|
609
|
+
}
|
|
610
|
+
}),
|
|
611
|
+
);
|
|
612
|
+
return json(res, 200, { statuses });
|
|
613
|
+
}
|
|
582
614
|
// Fast-merge: after reviewing the demo, one click merges the PR via gh.
|
|
583
615
|
if (req.method === 'POST' && url.pathname === '/pr/merge') {
|
|
584
616
|
const body = await readBody(req);
|
|
@@ -587,6 +619,7 @@ createServer(async (req, res) => {
|
|
|
587
619
|
return json(res, 400, { error: 'invalid PR url' });
|
|
588
620
|
}
|
|
589
621
|
const out = await run('gh', ['pr', 'merge', prUrl, '--squash']);
|
|
622
|
+
prStatusCache.delete(prUrl);
|
|
590
623
|
if (out === null) {
|
|
591
624
|
return json(res, 502, { error: 'gh pr merge failed — check gh auth / merge conflicts / required checks' });
|
|
592
625
|
}
|