path linkify: require last segment to look like name.ext (skips dir refs)

This commit is contained in:
müde 2026-05-17 23:32:37 +02:00
parent 9585edef9b
commit 0e2d26304e

View file

@ -48,13 +48,18 @@
// perspective (we'd need to know which agent the message is about
// to translate it). Prefer `/agents/<name>/state/...` in agent
// outputs and the link will resolve.
// Each branch insists the tail is at least one segment AND the
// last character is `[\w.-]` (not `/`), so trailing-slash paths
// — i.e. plain directories — don't linkify. The /api/state-file
// endpoint also refuses non-files; this is the front-end peer so
// the operator doesn't see a dead link they'll just get an error
// from on click.
const PATH_RE = /(\/var\/lib\/hyperhive\/agents\/[\w.-]+\/state\/(?:[\w.-]+\/)*[\w.-]+|\/var\/lib\/hyperhive\/shared\/(?:[\w.-]+\/)*[\w.-]+|\/agents\/[\w.-]+\/state\/(?:[\w.-]+\/)*[\w.-]+|\/shared\/(?:[\w.-]+\/)*[\w.-]+)/g;
// Each branch insists the final segment looks like a filename:
// at least one non-dot char, a literal dot, then an extension
// (`[\w-]+\.[\w.-]+`). That catches the common case (`notes.md`,
// `2026-01.log`, `foo.bar.baz`) while skipping bare directory
// names like `/agents/foo/state/notes` whether or not they carry
// a trailing slash. Misses extensionless files (`README`,
// `Makefile`) — accepted trade-off; the /api/state-file endpoint
// still serves them if the operator types the path manually.
// The endpoint also refuses non-files at the server level; this
// is the front-end peer so the operator doesn't see a dead link
// they'll just get an error from on click.
const PATH_RE = /(\/var\/lib\/hyperhive\/agents\/[\w.-]+\/state\/(?:[\w.-]+\/)*[\w-]+\.[\w.-]+|\/var\/lib\/hyperhive\/shared\/(?:[\w.-]+\/)*[\w-]+\.[\w.-]+|\/agents\/[\w.-]+\/state\/(?:[\w.-]+\/)*[\w-]+\.[\w.-]+|\/shared\/(?:[\w.-]+\/)*[\w-]+\.[\w.-]+)/g;
async function fetchStateFile(path) {
const resp = await fetch('/api/state-file?path=' + encodeURIComponent(path));
const text = await resp.text();