relay-companion
Version:
Install Relay for Claude Code, Cowork, and Codex, then sign in from the Relay pill.
84 lines (79 loc) • 3.78 kB
JavaScript
// Send-time scan for links that will not survive the trip to the recipient.
//
// Two families, one symptom (the recipient clicks and gets a login wall, a 404,
// or an expired signature — and tells the sender Relay is broken):
//
// 1. Provider-scoped links: claude.ai artifacts/chats, ChatGPT canvases,
// private Notion pages. Scoped to the SENDER's account; the recipient's
// browser has no session that can see them.
// 2. Relay's own signed /storage URLs: valid for anyone, but only for ~15
// minutes — long dead by the time a person reads the message.
//
// Advisory by design, sibling to attachments.js's refuse-and-warn model but
// softer: the send is never blocked (agents attach public links in legitimate
// shapes we cannot enumerate), the warning rides back on the send result's
// agentInstruction so the agent can repair the message it just sent — attach
// the file, or paste the content — while the moment is still live.
const FRAGILE_LINK_RULES = [
{
kind: "claude",
// claude.ai/public/* artifacts are the one shareable shape; everything
// else under claude.ai is account-scoped.
test: (url) => /^https?:\/\/(?:www\.)?claude\.ai\//i.test(url) && !/claude\.ai\/public\//i.test(url),
describe: (url) => `${url} is a Claude link scoped to YOUR account — the recipient will hit a login wall or a 404.`,
},
{
kind: "chatgpt",
// chatgpt.com/share/* links are published; canvases and chats are not.
test: (url) =>
/^https?:\/\/(?:www\.)?(?:chatgpt\.com|chat\.openai\.com)\//i.test(url) &&
!/(?:chatgpt\.com|chat\.openai\.com)\/share\//i.test(url),
describe: (url) => `${url} is a ChatGPT link scoped to YOUR account — the recipient will hit a login wall.`,
},
{
kind: "notion",
// notion.site is Notion's publish-to-web domain and is genuinely public;
// notion.so pages are workspace-scoped unless shared, which we can't see.
test: (url) => /^https?:\/\/(?:www\.)?notion\.so\//i.test(url),
describe: (url) => `${url} is a Notion workspace link — it only opens for the recipient if that page is explicitly shared with them.`,
},
{
kind: "signed-storage",
test: (url) => /\/storage\/[^\s]*[?&](?:sig|expires)=/i.test(url),
describe: (url) => `${url} is a signed Relay storage URL that expires in minutes — it will be dead when the recipient reads this.`,
},
];
const URL_RE = /https?:\/\/[^\s<>"')\]]+/gi;
/** Every fragile link in a markdown body, with why it is fragile. */
export function scanFragileLinks(forHuman) {
const body = String(forHuman || "");
const findings = [];
const seen = new Set();
for (const match of body.matchAll(URL_RE)) {
const url = match[0].replace(/[.,;:!?]+$/, "");
if (seen.has(url)) continue;
seen.add(url);
for (const rule of FRAGILE_LINK_RULES) {
if (rule.test(url)) {
findings.push({ kind: rule.kind, url, message: rule.describe(url) });
break;
}
}
}
return findings;
}
/**
* The agentInstruction paragraph for a body's fragile links, or "" when clean.
* Attaching the file is the remedy we point at: attachments download locally
* on the recipient's machine and carry durable links everywhere else.
*/
export function fragileLinkWarning(forHuman) {
const findings = scanFragileLinks(forHuman);
if (!findings.length) return "";
const details = findings.map((f) => f.message).join(" ");
return (
`WARNING — this message contains ${findings.length === 1 ? "a link" : "links"} the recipient likely cannot open: ${details} ` +
"The message was still sent. Repair it now: send a follow-up that attaches the actual file (relay_send's attachments) " +
"or pastes the relevant content, instead of the link."
);
}