Skip to main content

lightning-artifacts

Publish a local file (HTML report, PDF, image, dataset sample, build output) to Lightning AI and get a durable, public lightning.ai/artifacts/ link that never expires and renders inline in the browser. Also lists the artifacts drive, unpublishes (revokes) links and deletes the files behind them, all through the `lightning` CLI (`lightning-sdk` package) with `lightning login` or an API key, no code. Use when the user wants to share a file, a generated one-pager, or an agent-made artifact as a permanent URL, hand a file to a teammate or CI job, see or revoke existing shared links, or asks to "get a public / shareable link for this file".

Quellinformationen

Repository
Lightning-AI/skills
Letzte Quellaktivität
13. Oktober 2026 um 09:40
Erkannte Sprache von SKILL.md
Englisch
Sterne
9
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
lightning-artifacts
description
Publish a local file (HTML report, PDF, image, dataset sample, build output) to Lightning AI and get a durable, public lightning.ai/artifacts/ link that never expires and renders inline in the browser. Also lists the artifacts drive, unpublishes (revokes) links and deletes the files behind them, all through the `lightning` CLI (`lightning-sdk` package) with `lightning login` or an API key, no code. Use when the user wants to share a file, a generated one-pager, or an agent-made artifact as a permanent URL, hand a file to a teammate or CI job, see or revoke existing shared links, or asks to "get a public / shareable link for this file".
license
Apache-2.0
compatibility
Requires Python with uv or pip, the lightning CLI from the lightning-sdk package (installed on demand), network access to lightning.ai, and a Lightning AI account.
# Lightning AI Artifacts (durable shareable file links) Publish a file to a teamspace and get a `https://lightning.ai/artifacts/<id>` URL that anyone can open without a Lightning login. The control plane streams the bytes from storage on every request, so unlike a presigned S3 URL the link never expires (no ~1h cap). **The whole flow is CLI-only.** `lightning cp` / `ls` / `rm` handle the files, and `lightning api` makes the publish and unpublish calls. No Python or `curl`. ## Setup & auth ```bash export DEBUG=0 LIGHTNING_DEBUG=0 # an inherited DEBUG=1 logs the Authorization header; set this in every new shell # Use the Lightning AI CLI from the current env; install or upgrade it there if it's missing or older than 2026.9.18 v=$(lightning --version 2>/dev/null | sed -n 's/^Lightning CLI version //p') [ -n "$v" ] && [ "$(printf '%s\n' 2026.9.18 "$v" | sort -V | head -1)" = 2026.9.18 ] \ || uv pip install -U lightning-sdk || python3 -m pip install -U lightning-sdk lightning --version # must print "Lightning CLI version …"; if not, see the table below lightning login # interactive browser sign-in — enough for everything here # or: export LIGHTNING_API_KEY=... # non-interactive alternative (CI, agents) ``` Either credential works for **every call in this skill** — no token minting or extra auth steps. Get a key from lightning.ai → user/org settings, or `lightning api-key create --org <org> --name artifacts`. **Never hardcode the key** — read it from the environment. To target a non-prod control plane, set `LIGHTNING_CLOUD_URL` (default `https://lightning.ai`). This uses, and if needed installs into, the agent's current environment (project venv, conda env, …); then call plain `lightning …` everywhere. If setup doesn't go cleanly: | Symptom | Fix | |---|---| | No active env, `externally-managed-environment`, or a conflict with the project's own pins | Install it on its own: `uv tool install lightning-sdk` (or `pipx install lightning-sdk`), and call `"$(uv tool dir --bin)/lightning"` if that isn't on `PATH` | | Still no `Lightning CLI version` line after installing | Another `lightning` (PyTorch Lightning's) or an inactive env comes first: check `command -v lightning`, then activate the env or call its `bin/lightning` | | `No such command`, `No such option` or `unexpected extra argument` | The CLI is too old: upgrade it where `command -v lightning` points (`uv pip install -U lightning-sdk`, or `uv tool upgrade lightning-sdk`) | | Installing is blocked (read-only env or home, agent sandbox) | Run each command as `UV_CACHE_DIR="${TMPDIR:-/tmp}/uv" UV_TOOL_DIR="${TMPDIR:-/tmp}/uv-tools" uvx lightning-sdk …` | | SSL/CA errors on every call, even `lightning --version`, only inside an agent sandbox | A `**/*.pem` read-deny rule hides certifi's `cacert.pem`: allow that one path; don't disable the sandbox | | `NameResolutionError`, only inside an agent sandbox | If `lightning api` fails too, ask the user to allow `lightning.ai` and `*.lightning.ai` (`/sandbox`). If only `studio`/`job` commands or the SDK fail, they bypass the sandbox proxy: run just those outside it. Background pollers fail the same way, silently | `lightning api` flags: `-X` method, `-f key=val` string field, `-F key=val` typed field, `-H` header, `--input <file>` request body (`--input -` to pipe one), `-q` jq filter (needs the `jq` binary for `-q`), `-i` include response headers, `--silent` no output. Fields with no `-X` make it a POST. Fields are JSON body for POST/PUT-with-body and **query params** when the request also has `--input` or is a GET. ## Resolve the teamspace (do this first) Artifacts live in a teamspace (a "project" in the REST API). You need the **owner name** and **teamspace name** (for `lit://` paths) and the **project id** (for REST calls). **Never guess.** If more than one membership fits and none is configured, **ask the user which to use**: ```bash lightning api /v1/memberships -q '.memberships[] | [.name, .projectId, .ownerType, .ownerId] | @tsv' ``` The membership carries only the owner's id. Resolve the name by `ownerType`: ```bash PID=<projectId-from-above> TSNAME=<name-from-above> OWNER_ID=<ownerId-from-above> if [ "<ownerType-from-above>" = organization ]; then OWNER=$(lightning api "/v1/orgs/$OWNER_ID" | jq -r .name) else # a personal teamspace: yours, or another user's you were added to OWNER=$(lightning api /v1/users/search -X GET -f "query=$OWNER_ID" \ | jq -r --arg id "$OWNER_ID" '.users[] | select(.id==$id) | .username') fi [ -n "$OWNER" ] && [ "$OWNER" != null ] || echo "could not resolve the owner of $TSNAME — ask the user" >&2 ``` The user search is fuzzy, so match `.id` exactly (the SDK does the same). Never fall back to your own username: a wrong owner in the `lit://` path uploads into a different teamspace. Teamspaces reached through org-level permissions don't appear in `/v1/memberships`. If the user names one you can't find, ask them for the `<owner>/<teamspace>` pair and the project id. Don't try to look it up by name: `/v1/projects?name=…` doesn't exist. ## Publish a durable link (the CLI flow) **A public link (`private=false`, the default below) needs the user's say-so in this request.** "Publish this" or "get me a public link" is authorization — don't ask twice. "Send this to the team" is not: say what the link exposes and get a yes, or use `-F private=true`. Three calls: upload into the `artifacts/` drive, read back the storage cluster it landed on, then register the object as a shared artifact. `share` and `shares` (below) need the `jq` binary; without it, parse the JSON yourself. ```bash # share <local-file> [remote-name] [content-type] # Pretty status goes to stderr; the bare URL is the only thing on stdout, so # URL=$(share file.html) captures cleanly while interactive use looks like: # ✅ Published report.html (text/html; charset=utf-8) # 🔗 https://lightning.ai/artifacts/art_... share() { local FILE="$1" NAME="${2:-$(basename "$1")}" CT="${3:-$(file -b --mime-type "$1")}" local KEY="artifacts/${NAME#artifacts/}" # publish only finds objects under artifacts/ # 1. upload; the server picks where to store it (see Gotchas) lightning cp "$FILE" "lit://$OWNER/$TSNAME/$KEY" >&2 || return 1 # 2. publish needs the blob's storage cluster, read from the listing (see Gotchas) local CLUSTER; CLUSTER=$(lightning ls --json "lit://$OWNER/$TSNAME/$KEY" | jq -r '.[0].clusterId') # 3. register it -> durable, no-expiry lightning.ai/artifacts/<id> local LINK; LINK=$(lightning api "/v1/projects/$PID/shared-artifacts" -X POST \ -f clusterId="$CLUSTER" -f filename="$KEY" -f contentType="$CT" -F private=false -q .url | tr -d '"') echo "✅ Published $NAME ($CT)" >&2 printf '🔗 ' >&2 echo "$LINK" } share report.html # -> https://lightning.ai/artifacts/art_... share dashboard.html reports/dash.html text/html # custom remote name + explicit type ``` **Always show the user the full URL on its own line**, never just "done". Terminals make it clickable. **Shares are confined to the `artifacts/` folder.** Publish only finds objects under `lit://<owner>/<teamspace>/artifacts/...`. Files under `uploads/` or `lightning_storage/` can be copied with `lightning cp`, but publish won't find them. The `KEY` variable keeps the upload path and the publish `filename` identical. A bare `filename` gets `artifacts/` prepended by the server, but matching the two explicitly is clearest. **The default `private=false` link is open to anyone who has it, with no auth.** Set `-F private=true` so only authorized project readers can open it. ### Content types that render inline **The publish call's `contentType` decides whether HTML/PDF render or download.** It is set at publish time, not upload time, and overrides the stored object's type on every request. `file -b --mime-type` guesses most cases; pass an explicit type when the extension is ambiguous. | File | Content-Type | | --- | --- | | `.html` | `text/html; charset=utf-8` | | `.pdf` | `application/pdf` | | `.svg` | `image/svg+xml` | | `.png` / `.jpg` | `image/png` / `image/jpeg` | | `.json` / `.txt` / `.csv` | `application/json` / `text/plain` / `text/csv` | | anything to force-download | `application/octet-stream` | ## List, see what's published, unpublish, delete **`lightning ls` lists the drive completely, even for folders with many thousands of files.** It follows the server's pages. It shows one level by default, `-r` for every file underneath, and `--json` for entries with `size` and `clusterId`: ```bash lightning ls "lit://$OWNER/$TSNAME/artifacts/" # one level; folders end in / lightning ls -r "lit://$OWNER/$TSNAME/artifacts/reports" # full relative file paths lightning ls --json "lit://$OWNER/$TSNAME/artifacts/reports" ``` **`shares` lists every published link in the project, newest first.** It calls `GET /v1/projects/{pid}/shared-artifacts`, which returns id, filename, content type, public/private, download count and URL: ```bash # shares -> one block per published artifact shares() { lightning api "/v1/projects/$PID/shared-artifacts" | jq -r '.artifacts // [] | .[] | "\(if .private then "🔒" else "🌐" end) \(.filename) · ⬇ \(.downloads // 0) · \(.createdAt // "") id: \(.id) 🔗 \(.url)"' } ``` **`unshare` revokes a link but keeps the file in the drive:** ```bash # unshare <artifact-id> unshare() { lightning api "/v1/projects/$PID/shared-artifacts/$1" -X DELETE --silent \ && echo "🗑️ Unpublished $1 — link is dead, file kept in the drive" >&2 } unshare art_01kxgaep54zzs84arfns1j21wd # illustrative id; take yours from shares ``` **`lightning rm` deletes the file itself, with no cluster id.** The server removes it wherever it is stored. Use it after unpublishing, or to clean up an abandoned upload: ```bash lightning rm "lit://$OWNER/$TSNAME/artifacts/report.html" lightning rm -r "lit://$OWNER/$TSNAME/artifacts/reports" # a folder and everything under it ``` `rm` fails on a path that doesn't exist (pass `-f` to ignore) and refuses a folder without `-r`. **Re-running `lightning cp` to the same `artifacts/<name>` updates the bytes behind existing links.** Their ids keep serving the new contents. Repeating the publish call with the same `filename` gives a new id and URL; do that to change the served Content-Type. ## Example workflows Prompts this skill handles: *"share this HTML report as a permanent link"*, *"give me a public URL for output.pdf"*, *"publish this one-pager so I can send it"*, *"drop this file somewhere CI can curl it"*. **An agent-generated HTML one-pager needs only `share`; anyone can open the printed URL:** ```bash share summary.html # hand the printed URL to anyone ``` **A loop over `share` publishes a folder of reports, one link each:** ```bash for f in out/*.html; do share "$f" "reports/$(basename "$f")" "text/html; charset=utf-8"; done ``` **`share` prints only the URL on stdout, so a CI job can capture and curl it:** ```bash URL=$(share model-metrics.json) # elsewhere: curl -sL "$URL" -o metrics.json ``` ## Gotchas - **Publishing with a compute cluster's id fails with HTTP 500; publish needs the blob's storage cluster.** A teamspace can be bound to compute clusters that store files under a parent cluster's bucket, so the upload cluster is not always the storage cluster. Read the real `clusterId` from the listing, as `share` does. - **`lightning cp` needs no cluster flag, because the server picks the storage.** Only if the server rejects the upload for a missing cluster does `cp` choose one itself (`LIGHTNING_CLUSTER_ID` first, which is set inside a Studio, then the teamspace's only or default cloud account) and warn `No cloud account specified. Using cloud account: <id>.` Pass `--cloud-account <id>` only to steer placement deliberately, and pick a cluster whose `status.phase` is `CLUSTER_STATE_RUNNING` (`/v1/projects/$PID/clusters`). A bound but unusable cluster makes the upload fail with a drive error that says nothing about cluster health. - **Unpublish reports success for links that don't exist.** `DELETE /v1/projects/{pid}/shared-artifacts/{id}` returns `HTTP 200` with `{}` for an id that was already revoked or mistyped. Exit code 0 is not proof; check that the public URL 404s. (`lightning rm`, by contrast, fails on a missing path unless you pass `-f`.) - **Served HTML is not byte-identical to the upload, so don't checksum it against the source.** Cloudflare injects a Browser-Insights RUM beacon (`static.cloudflareinsights.com/beacon.min.js`) into HTML responses. It is harmless for viewing, but don't promise a bit-exact document. - **`shares` fails with HTTP 501 "Method Not Allowed" on control planes built before mid-July 2026.** The list endpoint `GET /v1/projects/{pid}/shared-artifacts` is newer than the rest. Publish and unpublish still work there. - **A public link is open to anyone who has it, and revoking it doesn't recall a copy already fetched.** Use `-F private=true` for anything that shouldn't be world-readable.
Auf GitHub ansehen