Prints (@fungi.computer/prints)
Prints generates images and video from a prompt and keeps them in a gallery on your Computer.

Prints is one of the starter Apps of Oyster, the open App host that runs on your own Cloudflare account. On Oyster, Prints generates with Workers AI and saves every file to a folder on the Computer, a Linux workspace in a Cloudflare Container.
What you can do
- Type a prompt, pick a model and a size, and generate an image or a video. Each file keeps its model's native format: PNG where the model supports it, JPEG otherwise, MP4 for video.
- Add your own images as style references, on models that accept them.
- Upload files into the same folder, or choose another folder for the gallery.
- Organize inside your folder. The Library rail shows the folder as a tree, with All prints and Favorites above it. Make folders such as Characters and Locations, drag prints and folders onto them, rename them from the row menu, or choose Select and Move to…; Undo puts a move back. A heart favorites a print for your whole Team. Folders are real folders on the Computer, so Inkcap and your Agents see them too. New prints and uploads land in the folder you are viewing.
- Browse the gallery newest first. Click a picture to see it fitted inside Prints; Left and Right step through the loaded wall, and Esc returns to the last-viewed tile. Open in Inkcap is an explicit editing action for files on a Computer.
- Open output from an Agent or a recent-output row opens that same viewer. A new open request selects its output even while you are viewing another print; Image info remains an explicit action.
- Open a video to play with sound and native playback controls; stepping away stops it. If the browser refuses autoplay, press Play. Video-focused arrow keys seek rather than changing prints. Hub and Oyster permit App-window fullscreen.
- Step into a loading file or a pending generation without leaving the viewer. Video jobs show their live elapsed time; a video that finishes while you are watching its placeholder stays paused. A fresh visit to the ready video plays it. Retry a failed file read/preview or retry and dismiss a failed job in place. Changing the folder, Computer or connection closes the viewer.
- Open Image info in the viewer or from a focused gallery tile (press I).
- Toggle Actual size to see an image at its natural pixel dimensions. Scroll the image with the browser's native scrolling; focused scroll-viewport arrows pan instead of stepping prints. Toggle it off to fit again. Stepping to another print or reopening the viewer starts fitted.
- Download an image from the viewer or Image info. The download drops the prompt and lineage and keeps the AI marker and the print's id; the file on the Computer stays as it is.
- Share a generated print from Image info, where the host offers it (the Fungi Hub does): a public, read-only page shows the picture or video, with its prompt only if you leave On page on. Prints are private until you turn sharing on, and turning it off stops the link at once. Changing On page while shared makes a new link.
- Retry a failed attempt from its tile or from Activity, the key in the window's status bar. A retry resumes the same attempt instead of generating again, and Resume picks up an unfinished image after a reload.
Get it
Prints installs with the other starter Apps. On a deployed Oyster (see the Oyster quick start), run:
export OYSTER_URL=https://oyster.<your-subdomain>.workers.dev
export OYSTER_OWNER_KEY=<your owner key>
npx oyster install-apps
The installer clones this repository and the six others, seeds each on your
Oyster with git push, builds them on your Computer and installs them. Sign in
at $OYSTER_URL/login with the owner key and open Prints from the Tourtois
desktop. Run install-apps again to update; each App keeps its data. Generation
uses Workers AI on your Cloudflare account.
Change it
Every installed App is also a template you can fork on your own Oyster. Fork
Prints into a Bonsai repository and clone it with ordinary git:
curl -X POST -H "Authorization: Bearer $OYSTER_OWNER_KEY" -H 'content-type: application/json' \
-d '{"appId":"app_prints","name":"my-prints"}' \
$OYSTER_URL/apps/bonsai/methods/apps.fork
git -c http.extraHeader="Authorization: Bearer $OYSTER_OWNER_KEY" \
clone $OYSTER_URL/git/bonsai/my-prints.git
The reply names your fork's App id and repository. Push your edits, open the
repository in Bonsai and choose Settings, then Build & install with that
App id and your commit as git:<sha>. Install the Release at prints to
replace the running Prints, or at a new id to keep both.
To work from GitHub instead, fork this repository and point the installer at
your forks with --repositories <base URL> and --ref <branch or tag>. The
installer reads all seven starter Apps from that base, so fork them under one
account.
Agent tools
Agents call Prints through Oyster's MCP endpoint, or at
POST $OYSTER_URL/apps/prints/methods/<name> with a key; see
Git, keys and MCP.
| Method | What it does |
|---|---|
listModels | Lists the image and video models, with their sizes, settings and how many references each takes. |
generateImage | Generates one image from a prompt and saves it to the gallery folder. Optional characters names saved characters to keep consistent. |
generateVideo | Generates one video. While it works, the method returns {kind: "pending", retryAfterMs}; call again with the same inputs after that delay. |
listCharacters | Lists the saved characters: name, description and reference image paths. Use the names as characters or @name. |
listStyles | Lists the saved and built-in styles: name and text. Use a name as style. |
listOutputs | Lists the prints this App made, newest first. folder and recursive list one folder or everything under it; each item has its model and a video's duration. |
getOutput | Reads one print by file path, from any folder: its prompt, model, characters, style and the reference images it used. |
openOutput | Reads up to 128 KiB of one print per call. |
listFolders | Lists the sub-folders of a folder in the library. |
moveFiles | Moves files or folders into a library folder (createFolder makes it). Use it instead of mv: prints keep their prompt, references and favorite. |
describeFiles | For up to 24 paths: whether Prints made each file, its model, a video's duration and whether it is a favorite. |
setFavorite | Favorites or unfavorites files for the whole Team. listFavorites lists them. |
generateImage and generateVideo take an optional folder, relative to the
library folder (Characters/Mira), and save there; it is created if missing.
Each generation carries a request id (media_request_ and 21 lowercase letters
or digits). Retrying with the same id and inputs recovers the same generation
instead of paying for another. References are file paths, such as
[{ path: "/canon/style.png" }]. In a host with Agent Computer attachments,
{ computerId: "computer-b", path: "/canon/style.png" } reads from any attached
Computer using that Computer's own read authorization. Omit computerId for the
selected Computer; the output still saves there. Revoked or denied reads stop
generation before provider dispatch. Callers never pass image bytes or URLs.
Reference reads are not confined to the App's library folder: the Computer grant
is the boundary.
listModels includes a labelled priceEstimate for its stated default settings
(including the example prompt and no references). basis: "team-price" includes
the host's current Team markup; "provider-cost" is an unmarked provider
estimate. The image result's costMilliunits and human-readable receipt use
Media's admitted charge, not the catalogue hint. The receipt states that charge
plainly; only priceEstimate is an estimate. Changing dimensions, steps, prompt
or references can change the charge.
Set optional settings.steps only for models whose input.steps is not
"fixed"; obey input.maxSteps where present. Omission uses the model default.
Refusals retain Media's bounded provider diagnostic. The typed
provider.reason === "credits_exhausted" means Cloudflare AI Gateway couldn't
fund the request, not a Team credit refusal. Check the account's credits and
billing in the Cloudflare dashboard under AI Gateway or choose a Workers AI
model; do not retry a retryable: false result.
Build and check
Prints is a React App built with Vite, with controls from Caps. Its backend keeps folder choices, recovery records and the index of finished prints in the App's own SQLite database.
pnpm install
pnpm build
pnpm typecheck
pnpm test
src/MediaPane.tsx puts the composer, gallery and the Activity status key together. src/media-generation.ts owns attempts and retries, src/media-gallery.ts the gallery and uploads, and backend/prints-index.ts records each finished print. The agent tools are in backend/methods.ts, and package.json declares the permissions the App asks for.
License
MIT. See LICENSE.