Files
cobble/src/utils/link-computer-refs.ts
T
tben aebd6b8ccc Serve every media file privately at /media/<id>
There is no public media any more. `/media/public/<id>` served a media
row with no session at all, so anyone with the id could read a file.
The public/private split is gone: the path is `/media/<id>` (and
`/media/<id>/thumbnail`), every request is signed in first, and
`grabUserMedia` decides whether the user may see it: their own upload,
a bot's or workspace's picture or file they reach, or a file on a task
they reach. Anything else is a 404 before a path is built.

The `media.is_private` column and the two per-user directories went
with it: uploads live in `media/<user_id>/` and there is nothing to
pick between. The files browser's media volume is now the viewer's
own folder whatever their role.

Bots also say which of the two things they mean. A `[file:ID]` ref is a
saved file and its chip opens the bytes; a folder is a new
`[folder:volume:owner:path]` ref (`src/utils/folder-ref.ts`) whose chip
opens the files browser there and downloads nothing. The old `[path:...]`
ref and the extension sniffing that guessed file from folder are gone,
so `import_file` names the folder holding a file it copied rather than
the file itself.

The prompts say what the Assistant got wrong: list before importing or
saving, one copy per artifact, never split a file into parts unless
asked, never claim the store is empty without a fresh list, and never
invent a size cap. A re-import now says the path already existed
instead of silently overwriting and reporting a fresh copy.
2026-10-22 10:10:53 +01:00

75 lines
3.0 KiB
TypeScript

/**
* The link target a computer ref (`computerRef`) becomes, e.g.
* `#computer-3`. The chat's markdown renders these links as buttons that
* switch the bot to that computer.
*/
export const ComputerRefHrefPrefix = "#computer-";
/**
* The link target a take-over ref (`takeOverRef`) becomes, e.g.
* `#take-over-3-browser`. The chat renders these as buttons that open the
* computer in that mode.
*/
export const TakeOverRefHrefPrefix = "#take-over-";
/**
* The link target a bot ref (`botRef`) becomes, e.g. `#bot-3`. The chat
* renders these as links to that bot's conversation.
*/
export const BotRefHrefPrefix = "#bot-";
/**
* The link target a workspace ref (`workspaceRef`) becomes, e.g.
* `#workspace-2`. The chat renders these as links to that workspace.
*/
export const WorkspaceRefHrefPrefix = "#workspace-";
/**
* The link target a file ref (`fileRef`) becomes, e.g. `#file-12`. The
* chat renders these as a chip that opens the file.
*/
export const FileRefHrefPrefix = "#file-";
/**
* The link target a folder ref (`folderRef`) becomes, e.g.
* `#folder-computer%3A3%3Aseo%2Fraw`. The chat renders it as a chip that
* opens that folder in the files browser.
*/
export const FolderRefHrefPrefix = "#folder-";
/**
* Turns every computer ref (`[computer:3]`) and take-over ref
* (`[take-over:3:browser]`), bot ref (`[bot:3]`), workspace ref
* (`[workspace:2]`), file ref (`[file:12]`) and folder ref
* (`[folder:computer:3:seo/raw]`) in a message into a markdown link, so it
* survives markdown rendering as a link the chat can swap for a button,
* chip or download.
*
* A file ref and a folder ref are different things and are never guessed
* from a name: `[file:12]` is a saved file (its chip downloads it),
* `[folder:...]` a folder (its chip opens the files browser there).
*/
export default function linkComputerRefs({ text }: { text: string }) {
return text
.replace(
/\[computer:(\d+)\](?!\()/g,
(_, id: string) => `[computer ${id}](${ComputerRefHrefPrefix}${id})`,
)
.replace(
/\[take-over:(\d+):([a-z]+)\](?!\()/g,
(_, id: string, mode: string) => `[take over](${TakeOverRefHrefPrefix}${id}-${mode})`,
)
.replace(/\[bot:(\d+)\](?!\()/g, (_, id: string) => `[bot ${id}](${BotRefHrefPrefix}${id})`)
.replace(
/\[workspace:(\d+)\](?!\()/g,
(_, id: string) => `[workspace ${id}](${WorkspaceRefHrefPrefix}${id})`,
)
.replace(/\[file:(\d+)\](?!\()/g, (_, id: string) => `[file ${id}](${FileRefHrefPrefix}${id})`)
.replace(/\[folder:([a-z]+):(\d+):([^\]\n]+)\](?!\()/g, (_, volume: string, owner: string, path: string) =>
`[${path}](${FolderRefHrefPrefix}${encodeURIComponent(`${volume}:${owner}:${path}`)})`,
)
.replace(/\[folder:([a-z]+):(\d+)\](?!\()/g, (_, volume: string, owner: string) =>
`[${volume} ${owner}](${FolderRefHrefPrefix}${encodeURIComponent(`${volume}:${owner}`)})`,
);
}