New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

@buildinternet/uploads

Package Overview
Dependencies
Maintainers
1
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@buildinternet/uploads

CLI and client for uploads.sh — workspace-scoped image hosting for GitHub embeds

latest
Source
npmnpm
Version
0.56.3
Version published
Weekly downloads
226
-53.11%
Maintainers
1
Weekly downloads
 
Created
Source

@buildinternet/uploads

CLI and client for uploads.sh — upload screenshots, recordings, and other artifacts (reports, logs, JSON, PDFs, zips), get stable public URLs, and produce GitHub-ready markdown.

CLI

Binary: uploads. Install globally (or use an npx one-shot):

npm install --global @buildinternet/uploads
npx @buildinternet/uploads --help
uploads setup
uploads --version
uploads attach ./before.png ./after.png
uploads screenshot https://app.example --pr 123   # capture + host in one step
uploads screenshot ./report.html --dark --selector "main"
uploads screenshot http://localhost:3000 --via local --annotate ./callouts.json
uploads annotate ./shot.png --spec ./callouts.json --out ./shot.marked.png
uploads put ./shot.png
uploads put ./shot.png --destination screenshots
uploads put ./shot.png --no-optimize
uploads put ./mobile.png --frame phone
uploads put ./ui.png --frame browser --frame-url "https://app.example"
uploads put ./after.png --pr 123
uploads put ./capture-2026-…Z.png --pr 123 --name hero.png   # clean leaf, stable path
uploads put ./shot.png --pr 123 --name hero.png --dry-run --format url  # preview URL, no upload
uploads put --url https://cdn.example/shot.png --pr 123
uploads put --url http://localhost:4321/shot.png
uploads gallery create --title "Release screenshots"
uploads put ./after.png --gallery gal_example
uploads feed create owner/repo
uploads feed create --repo owner/repo --pr 123
# custom metadata (queryable): page URL, in-app path, which surface
uploads put ./shot.png --meta url=https://app.example/settings --meta path=/settings --meta app=web
uploads meta get screenshots/myapp/42/shot.webp
uploads meta set screenshots/myapp/42/shot.webp path=/onboarding --delete url
uploads find app=web path=/settings                             # or: list --meta app=web
uploads doctor

Inside this monorepo only, pnpm uploads … builds the package first so you pick up local source; product docs and PR “how to try it” examples should use the global uploads form above.

Commands: attach, put, screenshot, annotate, gallery, feed, comment, list, find, meta, delete, usage, reconcile, purge-expired, setup, install, login, whoami (alias status), logout, invite, admin, config, telemetry, report, doctor, health, changelog, docs, mcp, completion.

Help: bare uploads / uploads help / --help shows essentials; use uploads help --all (or --help --all) for the full command list. Per-command: uploads <cmd> --help.

Errors: every failure prints a short error: line on stderr, followed by a runnable example when an argument is missing, then a hint — never a help dump, so trimmed output (| tail) still carries the reason. A mistyped command also suggests the closest real one: uploads set-metadata answers did you mean: uploads meta set. With --json, an unknown command returns { "error", "code": "USAGE", "didYouMean" } on stdout.

Shell completion: uploads completion bash|zsh|fish prints a script to stdout. Save it where your shell looks for completions, then start a new shell. For zsh, write the script to ~/.zsh/completions/_uploads, then bind it at the end of ~/.zshrc with fpath=(~/.zsh/completions $fpath) and autoload -Uz _uploads && compdef _uploads uploads. The compdef line matters: a cached compinit -C never rescans fpath, so the file alone can go unnoticed. uploads completion --help covers the rest.

Globals (before the command): --api-url, --token, --workspace / -w, --env-file, --json, --quiet, --version / -V, -h / --help, --all (with root help).

Update hints: after a successful run the CLI may print one stderr line when a newer npm release is available (at most once/day, ~/.cache/uploads/). Silence with --quiet, --json, UPLOADS_NO_UPDATE=1, or NO_UPDATE_NOTIFIER=1. Not used for uploads mcp.

Telemetry: the CLI and MCP server send anonymous usage pings (command name, version, OS/arch, exit code, duration, optional error code) to POST /v1/telemetry on the configured API. No arguments, paths, tokens, workspace names, or file content. Opt out with UPLOADS_TELEMETRY_DISABLED=1, DO_NOT_TRACK=1, or uploads telemetry disable. First interactive run prints a one-line notice.

Diagnostic reports (opt-in): uploads report "what broke" sends a short message to the team. Attach a log with --file ./trace.log, or pipe: uploads doctor --json 2>&1 | uploads report "doctor failed". Never automatic — also available as the MCP report tool when the user asks to submit feedback. Attachments are text-only (max 256 KiB) and stored under an unguessable R2 key.

Exit codes: 0 ok, 2 usage/token/file, 3 auth/policy, 4 network, 1 other. Failures go to stderr; under --format json|url|markdown they also go to stdout so piped runs stay self-diagnosing. Prefer JSON code over message text. put --dry-run previews the key + public URL without uploading.

attach is the agent-friendly default for GitHub media. It accepts one or more files, infers the pull request for the current branch via gh, uploads stable URLs, and creates or updates one marker-owned GitHub comment. It keeps loose gh/... attachments and linked public galleries in distinct sections, shows up to three available gallery images inline, and updates that same comment in place on every sync. Use --pr, --issue, and --repo to select the target explicitly, or --no-comment to upload without changing GitHub comments.

Branch staging (pre-PR): attach <files> --branch [name] (also on screenshot) stages files against a git branch before any PR exists — the working mode for coding agents capturing as they go. Staged files live under gh/<owner>/<repo>/branch/<branch>/… and are promoted into the PR's attachments when one opens: automatically via the GitHub App webhook, or on the first attach after the PR exists (--promote forces it with no new files, --no-promote opts out). uploads github link inspects or claims the workspace↔repo binding the webhook path uses. Promotion is copy-and-keep — the staged original is never deleted, so any URL already embedded keeps serving — and staged objects follow only normal per-workspace retention and explicit deletes. Promotion (auto or --promote) does skip files staged more than 30 days before the PR opens, though; they're still there, just no longer auto-promoted.

Metadata edits re-sync the comment too: uploads meta set on a gh/…-keyed object refreshes the managed comment automatically when it touches path or state — best-effort, so backfilled metadata shows up without waiting on the next attach.

Bare put stages too, by default (issue #403): on a non-default git branch, a put with none of --pr/--issue/--key/--ref/--prefix/--destination set (and not --no-git) stages exactly like attach --branch — same key, same gh.* metadata. The classic dated layout (<prefix>/<repo>/<ref-or-date>/<name>) remains the default branch/detached HEAD/non-repo/--no-git behavior, and the explicit-flags opt-out.

Screenshot capture: uploads screenshot <url|file.html> renders a page to a hosted image in one step — no separate browser tooling needed. --via auto (default) drives a Chrome/Chromium already on the machine (playwright-core ships no browser; --browser <path>, --cdp <endpoint>, or the Playwright/ Puppeteer caches all work), and falls back to server-side rendering when none is found. localhost/private URLs are local-only; .html files work on both backends. After capture it joins the same pipeline as put (optimize, frames, --pr/--issue comments, galleries). See uploads screenshot --help.

Keys / destinations: default put uses the screenshots layout. Typed destinations (--destination screenshots|gh|f, MCP destination) set the root; --pr/--issue use gh/…. Workspaces may restrict put/sign to those roots via allowedKeyPrefixes (see workspaces).

Image optimization: by default, still images are re-encoded to WebP (long edge capped, high quality) before upload so GitHub embeds stay small, and EXIF is stripped. Pass --keep-exif / UPLOADS_KEEP_EXIF=1 to preserve image metadata, or --no-optimize / UPLOADS_NO_OPTIMIZE=1 to upload originals unchanged. Optimize notes print human sizes (e.g. 411.5 KB → 94.2 KB).

Re-upload / hot-swap: the same key overwrites in place with no prompt (stable --pr / attach paths). Human mode notes >> replaced existing object (same URL); JSON includes replaced. Preview with --dry-run (reports would replace when the key already exists).

Frames (opt-in): --frame phone|browser|iphone-16-pro composites chrome before optimize. phone/browser are procedural; iphone-16-pro fetches community art from device-frames-media into ~/.cache/uploads/frames (not bundled).

Annotating screenshots

Bake hand-drawn boxes, arrows, labels, freeform strokes, and redactions onto a screenshot before it's uploaded:

uploads screenshot http://localhost:3000 --via local --annotate ./callouts.json
uploads annotate ./shot.png --spec ./callouts.json --out ./shot.marked.png

screenshot --annotate resolves CSS selectors against the live page (local capture backend only); annotate works on an existing image and accepts pixel coordinates only, no selectors. Spec format and workflow: skills/annotate-screenshots/SKILL.md.

Public galleries

Create an ordered gallery, then add existing uploads by key. The API returns the canonical public URL; the CLI never constructs it. Anyone who knows that URL can view the gallery and its media—GitHub or repository visibility does not restrict it. Deleting a gallery removes only the gallery record, not its uploaded objects or their retention policy. A workspace can hold up to 100 active galleries, each with up to 100 items and 20 external references.

uploads gallery create --title "Release screenshots"
uploads gallery add gal_example screenshots/myapp/42/after.webp --alt "Updated dashboard"
uploads put ./before.png --gallery gal_example
uploads gallery show gal_example
uploads gallery link gal_example --github buildinternet/uploads#58
uploads gallery list --github https://github.com/buildinternet/uploads/pull/58

When adding several keys, uploads gallery add processes them sequentially and reports any individual failures in --json output. Gallery item updates use the API's current version to avoid overwriting concurrent changes.

Change feeds

A feed is a public newest-first page of screenshots already tagged with a GitHub owner/repo, or with one pull request or issue. Same product — --pr / --issue / --github is one extra filter. It is a live query, not a curated gallery. Use a gallery when you pick the files; use a repo feed for the latest shots across a repo; use a PR feed when reviewers should see only that pull request. The API returns the canonical public URL.

uploads feed create owner/repo
uploads feed create --repo owner/repo
uploads feed create --repo owner/repo --pr 123
uploads feed create --github owner/repo#123
uploads feed create --repo owner/repo --path /settings

A positional owner/repo is the same as --repo. Different repos get different feeds. Creating the same scope again returns the existing URL. Anyone who knows that URL can view the feed. Each shot also has /c/<id>/<item> with previous / next. Syncing a managed PR comment creates the PR feed if needed and points image clicks at that pager. MCP: feed_create (repo, plus pr / issue / github / path) and feed_get.

Link a gallery to a GitHub issue or pull request with gallery link --github. Run uploads comment --pr <number> to refresh that target’s one managed comment with every linked gallery and loose attachment. Coordinates and strict https://github.com/<owner>/<repo>/issues|pull/<number> URLs are accepted; gallery list --github performs the authenticated reverse lookup. Links never change gallery identity, and GitHub repository visibility does not make the public gallery private.

Config layers (first match wins): CLI flags → env vars → --env-file → ~/.config/buildinternet/config. See config.example for keys.

MCP server

uploads mcp serves the Model Context Protocol over stdio (newline-delimited JSON-RPC, no extra dependencies). Tools include file operations plus public gallery workflows (gallery_create, gallery_get, gallery_add, gallery_link, gallery_find_by_reference) and change feeds (feed_create, feed_get — pass pr or github to scope a feed to one pull request). Gallery and feed tools return API-provided canonical URLs and never need GitHub credentials. The remaining stdio tools are put, attach, list, delete, get_metadata, set_metadata, find_files, usage, reconcile, purge_expired, comment, whoami, and doctor — with the same config resolution and defaults, plus a per-call workspace argument. put and attach accept a metadata param (same gh.* auto-injection as the CLI's attach); get_metadata, set_metadata, and find_files mirror uploads meta get / meta set / find. Interactive/credential commands (setup, login, admin, config) are not exposed. A token isn't required to start the server; auth errors surface per tool call (whoami needs no auth).

{ "command": "uploads", "args": ["--env-file", "/path/to/.env", "mcp"] }

Or with UPLOADS_TOKEN/UPLOADS_WORKSPACE in the environment or user config. Claude Code: claude mcp add uploads -- uploads --env-file /path/to/.env mcp.

The MCP Registry lists this server as sh.uploads/mcp.

For HTTP clients there's also a hosted variant at https://agents.uploads.sh/mcp — the workspace is inferred from the bearer token, so only the URL and token are needed (https://agents.uploads.sh/<workspace>/mcp and the mcp.uploads.sh hostname also work). Tools: file operations (including get_metadata / set_metadata / find_files) plus gallery_create, gallery_get, gallery_add, gallery_link, gallery_find_by_reference, feed_create, and feed_get; all use the same bearer-token workspace scopes and gallery/feed URLs come from the API — see apps/mcp in the repo. The hosted put also accepts a metadata param. uploads install registers the skills + hosted MCP with whichever of Claude Code, Codex, and Grok are on PATH (a missing CLI is skipped) + Grok/Cursor hooks (short progress; --verbose for underlying output). Claude and Codex use their plugins for the same pre-PR screenshot reminder (uploads hook pre-pr-screenshot). Its put takes no content type: the stored type is sniffed server-side from the bytes and checked against the workspace allowlist, and writes are rate limited per workspace.

Programmatic use

import { createUploadsClient } from "@buildinternet/uploads";

Agent/MCP helpers: @buildinternet/uploads/agent (createUploadsWorkerFileTools for Workers); for local stdio MCP, use uploads mcp (above).

Layout

src/
  cli.ts              Entry + help
  package-version.ts  Shared version for --version / doctor / headers
  update-check.ts     Optional npm update hint (stderr)
  commands.ts         put, list, delete, comment, …
  commands/mcp.ts     `mcp` command entry
  mcp/                Stdio MCP server (server.ts, tools.ts)
  client.ts           HTTP client for the API
  github.ts           PR/issue key paths + attachment comments
  embed.ts            Markdown image output
bin/uploads.js        Bin shim

Commands

pnpm build        # tsc → dist/
pnpm typecheck
pnpm test
pnpm pack:check   # verify the npm tarball contents

Maintainer release instructions: docs/releasing.md.

Agent-oriented usage: skills/uploads-cli/SKILL.md (full CLI reference), skills/github-screenshots/SKILL.md (visuals into PRs/issues), and skills/annotate-screenshots/SKILL.md (callouts and redaction). REST details: docs/api.md.

FAQs

Package last updated on 23 Sep 2026

Related posts