@khanglvm/relay 0.13.0 → 0.13.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/package.json +1 -1
- package/skills/relay/SKILL.md +22 -0
- package/src/cli.js +4 -1
- package/src/server.js +18 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanglvm/relay",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Question boards with rich blocks (markdown, charts, mermaid, tables, code, diffs, video, sandboxed HTML), clickable local file-links, and element-level annotations for AI coding agents (Claude Code, Codex, …): ask users structured questions, present interactive visuals, collect inline comments, read answers as JSON — in a local browser board OR rendered INLINE inside the Claude & Codex apps as an MCP App (SEP-1865).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
package/skills/relay/SKILL.md
CHANGED
|
@@ -472,9 +472,31 @@ one yesno "Does this match your mental model?" + a textarea for notes.
|
|
|
472
472
|
`rly history` (saved boards) · `rly spec <id>` (print spec to modify) ·
|
|
473
473
|
`rly reuse <id>` (re-run blank) · `rly reopen <id>` (re-open with saved
|
|
474
474
|
answers prefilled) · `rly reopen <id> --replies file.json` (add agent replies) ·
|
|
475
|
+
`rly rescue <id>` (re-serve a dropped board on its ORIGINAL port) ·
|
|
475
476
|
`rly list` / `rly open` / `rly stop <id>` · `rly rm <id>`.
|
|
476
477
|
Multiple boards can run concurrently.
|
|
477
478
|
|
|
479
|
+
### Continue a board — reconnect, NEVER recreate it
|
|
480
|
+
|
|
481
|
+
When the user refers to a board that already exists — a URL/port ("the board on
|
|
482
|
+
`127.0.0.1:59926`"), "the board from yesterday", "reopen it", "it disconnected"
|
|
483
|
+
— do **NOT** run `rly ask`/`rly show`. A fresh board lands on a **new port**,
|
|
484
|
+
**strands the user's open tab** on the dead one, and **loses their comments**.
|
|
485
|
+
Find the real board and reconnect it:
|
|
486
|
+
|
|
487
|
+
1. **Identify it** — `rly list` (running) and `rly history` (saved) print each
|
|
488
|
+
board's id, title, and url/port. Match by what the user said (port, title).
|
|
489
|
+
2. **Tab still open but "connection lost"** (server died / machine slept) →
|
|
490
|
+
`rly rescue <id>`. Re-serves on the SAME port so that tab reconnects on its
|
|
491
|
+
own and re-flushes any comments it buffered — no new tab, no lost input.
|
|
492
|
+
3. **Want a fresh tab** with prior answers prefilled → `rly reopen <id>` (also
|
|
493
|
+
reuses the board's last port, so an old tab still reconnects).
|
|
494
|
+
4. `rly reuse <id>` is the ONLY "make a new board from this one" path — use it
|
|
495
|
+
solely for a deliberately blank re-run, never to "continue" or "reconnect".
|
|
496
|
+
|
|
497
|
+
Rule of thumb: **an existing board is reconnected (`rescue`/`reopen`), never
|
|
498
|
+
re-asked.** Only call `rly ask`/`rly show` for a genuinely new question.
|
|
499
|
+
|
|
478
500
|
### Live mutation — `rly update`
|
|
479
501
|
|
|
480
502
|
Push a new spec to a running board. The page reloads and prefills answers from the
|
package/src/cli.js
CHANGED
|
@@ -349,7 +349,10 @@ async function cmdReopen(args) {
|
|
|
349
349
|
printJson({ status: 'open', boardId: record.id, url: running.url, note: 'already running — browser re-opened' });
|
|
350
350
|
return 0;
|
|
351
351
|
}
|
|
352
|
-
|
|
352
|
+
// Reuse the board's last port (unless the user forced one) so a tab still open
|
|
353
|
+
// from a previous run reconnects on its own instead of being stranded on a
|
|
354
|
+
// dead port. runBoard falls back to a random port if it's been taken.
|
|
355
|
+
return runOrDetach(record, { ...args, port: args.port ?? record.lastPort });
|
|
353
356
|
}
|
|
354
357
|
|
|
355
358
|
// Rescue a board whose browser tab is still open but disconnected (its server
|
package/src/server.js
CHANGED
|
@@ -693,9 +693,25 @@ export async function runBoard({ id, port = 0, open = true, timeoutSec = 1800, q
|
|
|
693
693
|
}
|
|
694
694
|
});
|
|
695
695
|
|
|
696
|
+
// Bind the requested port (reopen/rescue reuse the board's last port so a
|
|
697
|
+
// still-open tab reconnects). If that port was grabbed by another process in
|
|
698
|
+
// the meantime, fall back to a random free one rather than failing to boot.
|
|
696
699
|
await new Promise((resolve, reject) => {
|
|
697
|
-
|
|
698
|
-
|
|
700
|
+
const bind = (p, allowFallback) => {
|
|
701
|
+
const onErr = (e) => {
|
|
702
|
+
if (allowFallback && e && e.code === 'EADDRINUSE' && p !== 0) {
|
|
703
|
+
bind(0, false); // desired port busy → random free one
|
|
704
|
+
} else {
|
|
705
|
+
reject(e);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
server.once('error', onErr);
|
|
709
|
+
server.listen(p, '127.0.0.1', () => {
|
|
710
|
+
server.removeListener('error', onErr);
|
|
711
|
+
resolve();
|
|
712
|
+
});
|
|
713
|
+
};
|
|
714
|
+
bind(port, true);
|
|
699
715
|
});
|
|
700
716
|
const actualPort = server.address().port;
|
|
701
717
|
const url = `http://127.0.0.1:${actualPort}/`;
|