@khanglvm/relay 0.14.1 → 0.15.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/README.md +15 -10
- package/docs/AGENT.md +25 -17
- package/package.json +1 -1
- package/skills/relay/SKILL.md +21 -8
- package/src/cli.js +30 -13
- package/src/server.js +222 -119
- package/src/ui/annotate.js +16 -7
- package/src/ui/app.js +77 -32
- package/src/ui/blocks.css +5 -3
- package/src/ui/blocks.js +113 -20
package/README.md
CHANGED
|
@@ -80,10 +80,11 @@ unchanged; pick whichever surface fits.
|
|
|
80
80
|
| "Type *done* when finished reviewing" | A Submit button; answers, notes, and inline comments returned as JSON |
|
|
81
81
|
| Feedback = another wall of text | Click any chart point, diagram node, table cell, or sentence to comment — the agent replies and the thread grows on the board |
|
|
82
82
|
|
|
83
|
-
Everything autosaves in real time
|
|
84
|
-
run at once, and the package has **zero npm
|
|
85
|
-
Node ≥ 18. Browser-side Chart.js, Mermaid, and
|
|
86
|
-
the package and lazy-loaded offline when a board
|
|
83
|
+
Everything autosaves in real time, detached board links keep serving after
|
|
84
|
+
agent timeouts, multiple boards run at once, and the package has **zero npm
|
|
85
|
+
runtime dependencies** — plain Node ≥ 18. Browser-side Chart.js, Mermaid, and
|
|
86
|
+
Viz.js assets are vendored in the package and lazy-loaded offline when a board
|
|
87
|
+
actually needs them.
|
|
87
88
|
|
|
88
89
|
## Learn more
|
|
89
90
|
|
|
@@ -92,7 +93,7 @@ the package and lazy-loaded offline when a board actually needs them.
|
|
|
92
93
|
| `rly help` | every command at a glance |
|
|
93
94
|
| `rly git pick` / `rly git cherry-pick` | choose commit actions and rank the order directly on a board; add `--code` to cherry-pick with split code review and per-hunk Apply/Skip/Hold |
|
|
94
95
|
| `rly git conflict [files…]` | auto-detect unmerged conflict files, or open specific local paths, and return resolved content in `result.blockEdits` |
|
|
95
|
-
| `rly share <board-id>` | activate/list/revoke same-Wi-Fi reviewer or
|
|
96
|
+
| `rly share <board-id>` | activate/list/revoke same-Wi-Fi collaborator, reference-only reviewer, or read-only links for a running browser board |
|
|
96
97
|
| `rly view <file.md> …` | open a quick read-only board that renders local markdown file(s), data files, or PDFs — library-free; great for plans, READMEs, reports, quotes |
|
|
97
98
|
| `rly install --target <agent>` | write relay's rules into an agent's instruction file — `claude` `codex` `cursor` `copilot` `kiro` `windsurf` `cline` `gemini` `opencode` `droid` `agents`; `--all`, `--scope`, `--print`, `--list` |
|
|
98
99
|
| `rly upgrade` | update the CLI **and** refresh the skill in one step (safe around open boards; `--dry-run`, `--cli-only`, `--skill-only`) |
|
|
@@ -171,9 +172,13 @@ npm test # smoke tests with no external services (spawns real servers, fake-
|
|
|
171
172
|
embedded in the payload.
|
|
172
173
|
- **`pdf` block** — render local `.pdf` files or PDF URLs inline. Local PDFs
|
|
173
174
|
stream from the board server and are never embedded in the page payload.
|
|
174
|
-
- **Durable
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
- **Durable boards / rescue** — detached boards keep serving after timeout
|
|
176
|
+
until Submit or `rly stop`; every autosave mirrors to `localStorage`; a board
|
|
177
|
+
whose connection drops blocks further input instead of losing it, and
|
|
178
|
+
`rly rescue <id>` re-serves on the same port so an open tab reconnects. Active
|
|
179
|
+
same-Wi-Fi share links stay tied to the board and survive a same-port re-serve
|
|
180
|
+
until revoked. Reviewer submissions are isolated, reference-only side reviews
|
|
181
|
+
that never finish the owner's board; read-only links cannot mutate feedback.
|
|
177
182
|
- Still **zero npm runtime dependencies**, offline, and cross-platform. Vendored
|
|
178
183
|
browser libraries are loaded only for boards that need them.
|
|
179
184
|
|
|
@@ -201,8 +206,8 @@ npm test # smoke tests with no external services (spawns real servers, fake-
|
|
|
201
206
|
- **Markdown blocks render GFM tables**; element comments moved to an
|
|
202
207
|
Outline-style right sidebar with inline highlights on commented text.
|
|
203
208
|
- **Seamless timeouts** — a detached board that runs past its deadline keeps
|
|
204
|
-
serving so you can still submit (it lands as
|
|
205
|
-
calm note instead of disconnecting.
|
|
209
|
+
serving until Submit or explicit stop, so you can still submit (it lands as
|
|
210
|
+
`submitted`); the page shows a calm note instead of disconnecting.
|
|
206
211
|
- **`rly upgrade`** — install the latest CLI and refresh the skill in one step.
|
|
207
212
|
- Per-question notes are multi-line textboxes.
|
|
208
213
|
|
package/docs/AGENT.md
CHANGED
|
@@ -75,23 +75,28 @@ results), so partial input is never lost.
|
|
|
75
75
|
|
|
76
76
|
Browser boards are local-owner only by default. A same-Wi-Fi phone/tablet/other
|
|
77
77
|
laptop gets a locked page until sharing is explicitly activated. The owner can
|
|
78
|
-
click **Share**
|
|
79
|
-
the user:
|
|
78
|
+
click the topbar **Share** icon, or you can manage the same links for the user:
|
|
80
79
|
|
|
81
80
|
```sh
|
|
82
81
|
rly share b-xxxxx # list active share roles
|
|
83
|
-
rly share b-xxxxx --role review # reviewer:
|
|
84
|
-
rly share b-xxxxx --role collab # collaborator: edit/comment/submit
|
|
82
|
+
rly share b-xxxxx --role review # reviewer: answer/comment + submit a side review
|
|
83
|
+
rly share b-xxxxx --role collab # collaborator: edit/comment/final-submit as owner-authorized
|
|
84
|
+
rly share b-xxxxx --role read # read-only viewer: no answers/comments/submit
|
|
85
85
|
rly share b-xxxxx --role review --revoke # revoke one role
|
|
86
86
|
rly share b-xxxxx --revoke --all # revoke all active links
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Use `review`
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
submit
|
|
94
|
-
|
|
89
|
+
Use `review` for independent feedback: each browser gets an isolated review
|
|
90
|
+
draft and may answer, comment, and submit, but the submission is stored as a
|
|
91
|
+
**reference-only side review**. It does not finish the board, wake the agent, or
|
|
92
|
+
complete `rly wait`; the owner (or an owner-authorized collaborator) must still
|
|
93
|
+
submit the final answer. `rly result <id>` exposes current side-review drafts and
|
|
94
|
+
submissions on demand under `sideReviews`, clearly marked `referenceOnly:true`.
|
|
95
|
+
Use `read` when the other device should only view the board and existing
|
|
96
|
+
feedback. Read-only viewers cannot answer, comment, edit blocks, open local file
|
|
97
|
+
links, or submit. Collaborators retain owner-authorized edit/comment/final-submit
|
|
98
|
+
permission. Active links keep the same token across a same-port
|
|
99
|
+
`rly reopen`/`rly rescue` until revoked.
|
|
95
100
|
|
|
96
101
|
## Inline mode — relay as an MCP App (Claude & Codex apps)
|
|
97
102
|
|
|
@@ -672,12 +677,13 @@ raising `--timeout`.
|
|
|
672
677
|
For a **detached** board, the `timeout` deadline is *soft*: it hands you a
|
|
673
678
|
`timeout` result (with the autosaved draft) so you regain control, but the
|
|
674
679
|
server stays up and the board stays fully usable — the user can keep
|
|
675
|
-
commenting and still hit Submit.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
`
|
|
679
|
-
|
|
680
|
-
|
|
680
|
+
commenting and still hit Submit. It stays on the same port until Submit or an
|
|
681
|
+
explicit `rly stop`, so don't reopen just because your wait timed out. The page
|
|
682
|
+
tells them you stopped waiting and to prompt you afterward. So if you got a
|
|
683
|
+
`timeout` and the user might still be working: re-check later with `rly result
|
|
684
|
+
<id>` (its status flips to `submitted` once they finish), or pass `--on-result`
|
|
685
|
+
so a late submit push-wakes you. A blocking `rly ask` (no `--detach`) still ends
|
|
686
|
+
hard on timeout, since there's no separate waiter to hand back to.
|
|
681
687
|
|
|
682
688
|
## Push-wake — get notified instead of polling
|
|
683
689
|
|
|
@@ -745,7 +751,9 @@ rly rm <id> | --all # delete saved board(s)
|
|
|
745
751
|
```
|
|
746
752
|
|
|
747
753
|
Multiple boards can run at once (each gets its own port on 127.0.0.1).
|
|
748
|
-
|
|
754
|
+
Detached boards are durable: they keep serving past timeout, `reopen`/`rescue`
|
|
755
|
+
prefer the last port, and active same-Wi-Fi share links keep the same token until
|
|
756
|
+
revoked. Storage lives in `~/.relay` (override with `RLY_HOME`).
|
|
749
757
|
|
|
750
758
|
## Live board mutation — `rly update`
|
|
751
759
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanglvm/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
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
|
@@ -135,6 +135,10 @@ rly wait b-xxxxx --timeout 550 # blocks until submit, prints result JSON
|
|
|
135
135
|
rly result b-xxxxx # non-blocking peek (includes live draft)
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
+
Detached boards are durable: their own timeout hands the agent a `timeout`
|
|
139
|
+
result but keeps the same URL/port serving until Submit or `rly stop`. Do not
|
|
140
|
+
create a new board just because a wait timed out.
|
|
141
|
+
|
|
138
142
|
For long waits prefer presence-aware waiting over a huge --timeout:
|
|
139
143
|
|
|
140
144
|
```sh
|
|
@@ -178,23 +182,31 @@ answers and any annotations written so far.
|
|
|
178
182
|
|
|
179
183
|
Browser boards are local-owner only by default. A phone/tablet/other laptop on
|
|
180
184
|
the same Wi-Fi gets a locked page until sharing is explicitly activated. The
|
|
181
|
-
owner can click **Share**
|
|
182
|
-
|
|
185
|
+
owner can click the topbar **Share** icon, or an agent can manage the same links
|
|
186
|
+
with `rly share`:
|
|
183
187
|
|
|
184
188
|
```sh
|
|
185
189
|
rly share b-xxxxx # list active share roles
|
|
186
190
|
rly share b-xxxxx --role review # activate a reviewer link
|
|
187
191
|
rly share b-xxxxx --role collab # activate a collaborator link
|
|
192
|
+
rly share b-xxxxx --role read # activate a read-only link
|
|
188
193
|
rly share b-xxxxx --role review --revoke # revoke one role
|
|
189
194
|
rly share b-xxxxx --revoke --all # revoke all active share links
|
|
190
195
|
```
|
|
191
196
|
|
|
192
|
-
Use `review`
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
Use `review` for independent feedback. Each reviewer browser gets an isolated
|
|
198
|
+
draft and can answer, comment, and submit a **reference-only side review**. That
|
|
199
|
+
submission never finishes the board or wakes/completes the waiting agent; the
|
|
200
|
+
owner (or owner-authorized collaborator) must still provide the final answer.
|
|
201
|
+
When the owner asks for current reviewer input, run `rly result <id>` and inspect
|
|
202
|
+
`sideReviews.submissions` plus `sideReviews.drafts`; both are explicitly marked
|
|
203
|
+
reference-only. Use `read` for a view-only link with no answers, comments, block
|
|
204
|
+
edits, local-file opening, or submit. Use `collab` only when the user explicitly
|
|
195
205
|
wants that device to act as the owner: collaborators can edit answers, comment,
|
|
196
|
-
open allowed local file links, and submit. Shared viewers refresh from the
|
|
197
|
-
draft when another viewer
|
|
206
|
+
open allowed local file links, and final-submit. Shared viewers refresh from the
|
|
207
|
+
relevant live draft when another viewer in the same role/session saves.
|
|
208
|
+
Active share links are durable too: they keep the same token across a same-port
|
|
209
|
+
`rly reopen`/`rly rescue` until `rly share --revoke` disables them.
|
|
198
210
|
|
|
199
211
|
## Minimal spec
|
|
200
212
|
|
|
@@ -534,7 +546,8 @@ one yesno "Does this match your mental model?" + a textarea for notes.
|
|
|
534
546
|
answers prefilled) · `rly reopen <id> --replies file.json` (add agent replies) ·
|
|
535
547
|
`rly rescue <id>` (re-serve a dropped board on its ORIGINAL port) ·
|
|
536
548
|
`rly list` / `rly open` / `rly stop <id>` · `rly rm <id>`.
|
|
537
|
-
Multiple boards can run concurrently.
|
|
549
|
+
Multiple boards can run concurrently. Detached boards keep serving past timeout;
|
|
550
|
+
use `rly stop` when you actually want to close one.
|
|
538
551
|
|
|
539
552
|
### Continue a board — reconnect, NEVER recreate it
|
|
540
553
|
|
package/src/cli.js
CHANGED
|
@@ -761,21 +761,27 @@ async function cmdWaitLoop(id, deadline, opts) {
|
|
|
761
761
|
|
|
762
762
|
async function cmdResult(args) {
|
|
763
763
|
const record = mustLoad(args._[0]);
|
|
764
|
+
const sideReviews = {
|
|
765
|
+
referenceOnly: true,
|
|
766
|
+
note: 'Side reviews are reference only. Wait for the board owner (or owner-authorized collaborator) to submit the final answer.',
|
|
767
|
+
submissions: Array.isArray(record.sideReviews) ? record.sideReviews : [],
|
|
768
|
+
drafts: record.reviewDrafts && typeof record.reviewDrafts === 'object' ? record.reviewDrafts : {},
|
|
769
|
+
};
|
|
764
770
|
if (record.result && record.result.finishedAt) {
|
|
765
|
-
printResult(record.result);
|
|
771
|
+
printResult({ ...record.result, sideReviews });
|
|
766
772
|
return exitCodeFor(record.result.status);
|
|
767
773
|
}
|
|
768
774
|
const running = loadRunning(record.id);
|
|
769
775
|
if (running && isAlive(running.pid)) {
|
|
770
776
|
// While open, expose the real-time autosaved draft so agents can peek, plus
|
|
771
777
|
// best-effort presence (whether the user is still viewing/focused/active).
|
|
772
|
-
const out = { status: 'open', boardId: record.id, url: running.url, draft: record.draft ?? null };
|
|
778
|
+
const out = { status: 'open', boardId: record.id, url: running.url, draft: record.draft ?? null, sideReviews };
|
|
773
779
|
const presence = await fetchPresence(running.url);
|
|
774
780
|
if (presence) out.presence = presence;
|
|
775
781
|
printResult(out); // draft can hold many annotations — sidecar it too
|
|
776
782
|
return 0;
|
|
777
783
|
}
|
|
778
|
-
printResult({ status: 'lost', boardId: record.id, draft: record.draft ?? null });
|
|
784
|
+
printResult({ status: 'lost', boardId: record.id, draft: record.draft ?? null, sideReviews });
|
|
779
785
|
return 5;
|
|
780
786
|
}
|
|
781
787
|
|
|
@@ -785,9 +791,10 @@ async function cmdShare(args) {
|
|
|
785
791
|
if (!running || !isAlive(running.pid)) {
|
|
786
792
|
throw new CliError(`board "${record.id}" is not running. Use \`rly reopen ${record.id} --detach\` first.`, 5);
|
|
787
793
|
}
|
|
788
|
-
|
|
789
|
-
if (role
|
|
790
|
-
|
|
794
|
+
let role = args.all ? 'all' : args.role;
|
|
795
|
+
if (role === 'readonly' || role === 'read-only') role = 'read';
|
|
796
|
+
if (role !== undefined && !['collab', 'review', 'read', 'all'].includes(role)) {
|
|
797
|
+
throw new CliError('--role must be collab, review, or read.', 4);
|
|
791
798
|
}
|
|
792
799
|
const shareUrl = new URL('/api/share', running.url);
|
|
793
800
|
let res;
|
|
@@ -1084,6 +1091,9 @@ the user should view — put it in relay instead of printing it.**
|
|
|
1084
1091
|
if it is still open and the user may continue, run \`rly wait\` again. Do not
|
|
1085
1092
|
use \`--on-result\` as the primary Codex return path; normal Codex CLI sessions
|
|
1086
1093
|
do not expose a portable inbound API that wakes the current agent turn.
|
|
1094
|
+
Detached boards are durable: a board timeout hands the agent a \`timeout\`
|
|
1095
|
+
result but keeps the same URL/port serving until Submit or \`rly stop\`. Do
|
|
1096
|
+
not create a new board just because a wait timed out.
|
|
1087
1097
|
- **A plan, structure, architecture, data, or prototype** → a relay board with
|
|
1088
1098
|
diagram/chart/table/code/image/html blocks — never ASCII diagrams or walls of prose.
|
|
1089
1099
|
- **"Show me the diff / git diff / these changes"** → \`rly diff\` (runs git diff →
|
|
@@ -1097,8 +1107,12 @@ the user should view — put it in relay instead of printing it.**
|
|
|
1097
1107
|
option its own visual (\`options[].blocks\`) so the user picks by looking.
|
|
1098
1108
|
- Same-Wi-Fi sharing is locked by default. When the user wants another device to
|
|
1099
1109
|
review or co-fill a running board, manage explicit links with \`rly share <id>\`:
|
|
1100
|
-
\`--role review\` creates
|
|
1101
|
-
|
|
1110
|
+
\`--role review\` creates an answer/comment link whose submissions are reference-only
|
|
1111
|
+
side reviews (\`rly result\` exposes them but they never complete \`rly wait\`),
|
|
1112
|
+
\`--role collab\` creates an owner-authorized edit/comment/final-submit link,
|
|
1113
|
+
\`--role read\` creates a view-only link, and \`--revoke\` disables active links. Active
|
|
1114
|
+
share links are durable across same-port \`rly reopen\`/\`rly rescue\` until
|
|
1115
|
+
revoked.
|
|
1102
1116
|
- **There's a purpose-built component for most content — use the MOST SPECIFIC one,
|
|
1103
1117
|
never plain prose when a block fits.** Blocks: \`table\` (sortable/filterable/CSV,
|
|
1104
1118
|
load from .csv/.json), \`chart\`, \`kpi\` (stat cards), \`mermaid\`/\`graphviz\`/\`plantuml\`,
|
|
@@ -1568,8 +1582,8 @@ async function cmdServeInternal(args) {
|
|
|
1568
1582
|
open: args.open !== false,
|
|
1569
1583
|
timeoutSec: args.timeout !== undefined ? Math.max(0, Number.parseInt(args.timeout, 10) || 0) : 1800,
|
|
1570
1584
|
quiet: true,
|
|
1571
|
-
// Detached board: timeout hands back to the agent but keeps
|
|
1572
|
-
//
|
|
1585
|
+
// Detached board: timeout hands back to the agent but keeps the same
|
|
1586
|
+
// URL/port serving until Submit or explicit stop.
|
|
1573
1587
|
keepAliveOnTimeout: true,
|
|
1574
1588
|
});
|
|
1575
1589
|
await done;
|
|
@@ -1605,10 +1619,12 @@ USAGE
|
|
|
1605
1619
|
read that file if your shell truncates stdout; never pipe to head/tail
|
|
1606
1620
|
rly result <id> result/status now (includes live autosaved draft + presence while open)
|
|
1607
1621
|
rly share <id> list active same-Wi-Fi share links for a running board
|
|
1608
|
-
rly share <id> --role review
|
|
1609
|
-
rly share <id> --role collab
|
|
1622
|
+
rly share <id> --role review reviewer: answer/comment/submit a reference-only side review
|
|
1623
|
+
rly share <id> --role collab collaborator: edit/comment/final-submit as owner-authorized
|
|
1624
|
+
rly share <id> --role read activate a read-only viewer link
|
|
1610
1625
|
rly share <id> --role review --revoke
|
|
1611
1626
|
revoke one share role; use --revoke --all to disable all roles
|
|
1627
|
+
active links survive same-port reopen/rescue until revoked
|
|
1612
1628
|
rly list [--json] running boards
|
|
1613
1629
|
rly open [id] re-open the browser tab of a running board
|
|
1614
1630
|
rly reopen <id> [--replies f.json] serve a saved board again, prefilled with saved answers
|
|
@@ -1643,7 +1659,8 @@ COMMON FLAGS
|
|
|
1643
1659
|
|
|
1644
1660
|
EXIT CODES 0 submitted/acknowledged · 2 timeout · 3 cancelled · 4 usage · 5 not found
|
|
1645
1661
|
|
|
1646
|
-
NOTES
|
|
1662
|
+
NOTES detached boards keep serving after timeout until Submit or stop;
|
|
1663
|
+
answers & annotations autosave in real time (drafts survive timeout/cancel);
|
|
1647
1664
|
submitting auto-closes the tab and unblocks the CLI.
|
|
1648
1665
|
|
|
1649
1666
|
AI AGENTS run \`rly agent\` for the complete machine-oriented guide.
|