PikPak CLI Operation Skill
Operate PikPak cloud storage through the pikpak command-line tool.
Install pikpak
Choose installation method based on your operating system:
macOS / Linux:
curl -fsSL https://download.mypikpak.com/cli/install.sh | bash
Windows (PowerShell):
irm https://download.mypikpak.com/cli/install.ps1 | iex
Homebrew:
brew install pikcloud/tap/pikpak-cli
After installation, run pikpak version to verify.
With an inviter's invitation code — the installer records it for the next
pikpak auth register on this machine, without contacting the invitation API:
curl -fsSL https://download.mypikpak.com/cli/install.sh | bash -s -- --affiliate <code>
$env:PIKPAK_AFFILIATE="<code>"; irm https://download.mypikpak.com/cli/install.ps1 | iex
brew install pikcloud/tap/pikpak-cli && pikpak auth register --affiliate <code>
Installers save the code in a separate plain-text affiliate file; no Python or
JSON parser is needed, and settings.json is never read or modified. A write
failure warns but does not fail installation or overwrite the previous code.
In that case, pass the code with pikpak auth register --affiliate <code>.
pikpak skill install writes this document into the skills directory of every
AI coding agent on the machine (Claude Code, Codex, Gemini CLI, opencode,
Cursor, goose, iFlow, Qwen Code, ...). Run it after pikpak update if this
document ever disagrees with the installed binary; pikpak skill ls shows where
it is installed and whether it is current.
Environment Variables
| Variable | Description | Default |
|---|---|---|
PIKPAK_DIR | Credentials directory | ~/.pikpak |
PIKPAK_CREDENTIALS | Full path to OAuth credentials file | ~/.pikpak/.credentials.json |
PIKPAK_TOKEN | Personal Access Token; takes precedence over any saved credential | unset |
PIKPAK_PAT_PATH | Full path to the PAT file saved by auth token | ~/.pikpak/.pat.json |
PIKPAK_VERSION | Version specified during install | Latest |
PIKPAK_INSTALL_DIR | Installation directory | ~/.local/bin (macOS/Linux), %USERPROFILE%\.pikpak (Windows) |
PIKPAK_NO_UPDATE_NOTIFIER | Set to 1 to disable automatic update checks and reminders; explicit version --check and update still work | unset |
PIKPAK_NO_LOG | Set to any non-empty value (e.g. 1) to override config log and never record the diagnostic log | unset |
PIKPAK_NO_SKILL_SYNC | Set to any non-empty value to stop pikpak update from refreshing installed agent skills | unset |
PIKPAK_AFFILIATE | Invitation code read by install.sh / install.ps1 at install time (the CLI itself does not read it) | unset |
Credential priority: PIKPAK_TOKEN → PAT saved by pikpak auth token → OAuth credentials from pikpak auth login.
Device identity: the CLI keeps one stable device ID per installation in ~/.pikpak/.device.json
(X-Device-Id). It is reused for every login method and survives token rotation and auth logout;
deleting it makes the machine look like a brand-new device to PikPak's risk control.
Authentication
Most commands require login first. Credentials are stored in ~/.pikpak/.credentials.json.
pikpak auth login # Auto-detect login method (browser if available, device flow otherwise)
pikpak auth login -m browser # Browser OAuth login (opens browser, 30s no response -> falls back to device flow)
pikpak auth login -m browser --no-launch-browser # Browser OAuth login (print URL, no auto-open)
pikpak auth login -m device # Device flow (QR code + verification URL + auto-polling)
pikpak auth login -m device -F json # Device flow: output auth info as JSON and exit (no polling, for AI)
pikpak auth login --continue <device_code> # Poll device code status once and exit (use with -F json)
pikpak auth login --force # Re-login even if already authenticated
pikpak auth login -l "my-session" # Set a custom session label
pikpak auth login --scope files:manage,cloud_download # Pre-select OAuth scopes (comma-separated)
pikpak auth register # Register a new account (same browser/device flows as login)
pikpak auth register --affiliate <code> # Register with an affiliate / invitation code
pikpak auth register --affiliate= # Skip the installer's saved code for this registration
pikpak auth register -m device # Force device flow for registration
pikpak auth token <token> # Save a Personal Access Token for non-interactive use
pikpak auth logout # Clear the local session; works offline and on broken/expired credentials (cannot remove PIKPAK_TOKEN from the environment)
pikpak auth status # Login status + account panel: identity, VIP, storage quota (add -F json for scripts)
Supported --scope identifiers: files:read, files:write, files:manage, files:purge,
cloud_download, share, invite, account. Omitting --scope sends no scope and lets the
authorization server apply its defaults.
AI Agent login guide: Password login is no longer supported. For non-interactive/AI environments, use Device Flow:
- Run
pikpak auth login -m device -F jsonto getdevice_code,verification_uri,user_code,expires_in,interval- Show the user the verification URL and code (or QR code)
- Poll with
pikpak auth login --continue <device_code> -F jsonuntilstatusissuccessIf the environment supports a browser (macOS/Linux with GUI),
pikpak auth login(auto-detect) orpikpak auth login -m browserworks without any flags. Login checks existing credentials first; use--forceto create a new session.Fully non-interactive alternative:
pikpak auth token <PAT>, or exportPIKPAK_TOKEN=<PAT>.Registration caveat:
pikpak auth register -F jsonprintssignup_uri,handoff_code,qr_png_path,expires_in(andaffiliatewhenever a code is in play — flag or recorded) and exits — it does not return adevice_code, so it cannot be polled with--continue. For AI-driven registration, runpikpak auth registerwithout-F(it polls interactively), or have the user register in the browser and then usepikpak auth login.
Command Reference
File Browsing
Root directory ID is empty string "", not / or /root.
pikpak ls # List root directory
pikpak ls <folderID> # List specified folder (positional)
pikpak ls -p <folderID> # Same, via flag (positional arg wins if both given)
pikpak ls --type file # Show only files (server-side kind filter, works with --raw too)
pikpak ls --type folder # Show only folders (server-side kind filter, works with --raw too)
pikpak ls -l 50 # Items per page (--limit, default 100; the server floors values under 10)
pikpak ls -t <token> # Pagination (--token, from previous ls output)
pikpak ls -F json # JSON output (AI preferred: id, parent_id, name, kind, size, mime_type, file_category, modified, hash; mime_type/file_category are omitted for folders and for files the service did not classify)
pikpak ls --raw # Unmodified API JSON for the whole listing: adds params.duration/width/height, params.url, audit, thumbnail_link, spell_name, apps; size in bytes
Only non-trashed, fully-processed files are listed. One ls is one page: read next_page_token
from the output and pass it back with -t to continue. -l is a page-size request, not a hard
cap — the service returns 10 items for any smaller value, and a page may also come back short of
-l when the folder ends.
File Search (unavailable)
The search command is disabled in this release because the server-side search
API is unavailable. Do not suggest or run pikpak search; locate files by
walking folders with pikpak ls (optionally -F json) instead.
File Details
pikpak get <fileID> # Basic info
pikpak get <fileID> --url-type download # Include download URL
pikpak get <fileID> --url-type play # Include playback URL
pikpak get <fileID> --url-type all # Include all URLs
pikpak get <fileID> --raw # Unmodified API JSON (usage=ALL; url_type ignored)
get -F json fields: id, name, type (file/folder), size, mime, hash, created,
modified, phase, plus download_url, play_url, play_resolution when --url-type asks for
them. Note the naming differs from ls: type/mime here versus kind/mime_type there.
URLs are only returned for files — --url-type on a folder yields metadata only.
--raw bypasses this reduced shape entirely — see Raw API Output below.
Raw API Output (--raw)
pikpak ls --raw and pikpak get <fileID> --raw print the PikPak drive API's response body
verbatim (only re-indented). Use them whenever the field you need is not in the reduced ls /
get shapes above.
pikpak ls --raw # Whole listing, every field the API returns
pikpak ls --raw -p <folderID> -l 500 # Same filters/paging flags as plain ls
pikpak get <fileID> --raw # One file, usage=ALL
pikpak ls --raw -F yaml # Same body converted to YAML
What --raw adds (per file, beyond ls -F json's 9 fields):
| Raw field | Meaning | Why it matters |
|---|---|---|
params.duration | Playback length in seconds, as a string ("1420") | The only source of video duration — absent from both ls and get default output |
params.width / params.height | Pixel dimensions, as strings ("1920" / "1080") | Resolution filtering; get --url-type play only gives a label like 1080p |
params.url | Where the file came from: magnet:?xt=urn:btih:… for torrents, an http(s) URL for link downloads | The only record of an offline download's source |
params.task_id | ID of the offline task that produced the file | Ties a file back to pikpak task get <taskID> |
params.db_modify_time, params.platform, params.platform_icon, params.original_share_id | Service-side metadata, present on every item | Provenance |
size | Raw bytes as a string ("392927574") | Default output formats it (375 MB) and cannot be compared numerically |
created_time / modified_time / user_modified_time | RFC3339 with offset | Default output is truncated local YYYY-MM-DD HH:MM text |
medias[] | Full array: media_id, media_name, resolution_name, link.url, video.{width,height,duration,bit_rate,frame_rate,video_codec,audio_codec,hdr_type}, audio.{artist,album,sample_rate,…} | Every playable variant plus real codec/HDR metadata, not just the one play picks |
audit | {status, message, title} | Detect blocked/under-review resources |
apps, spell_name, tags, reference_events, thumbnail_link, icon_link, web_content_link, phase, starred, trashed, writable, folder_type, revision, user_id | Remaining API fields | Anything the reduced shape drops, including fields added by future API versions |
Semantics — read these before using it:
- Verbatim. The body is not re-encoded: field order is the API's, and
&in signed links stays a literal&(never\u0026). Safe to feed straight intojq. - Always structured.
--rawimplies JSON output even without-F;-F yamlconverts the same body. There is no human-readable table in this mode. ls --rawis one request per page, not per file. Same-p/-l/-t/--typeflags and the same server-side filters (non-trashed,PHASE_TYPE_COMPLETE) as plainls. Paging is unchanged: readnext_page_tokenfrom the raw body and pass it back with-t. Raise-l(e.g.-l 500) to cover a large folder in one call instead of loopingget.--typecombines with--raw. It is a server-sidekindfilter (drive#file/drive#folder), so the passthrough body already contains only the requested kind and a page still holds up to-litems. Client-sidejqfiltering on.files[] | select(.kind == …)is no longer needed.get --rawalways requestsusage=ALL— the fullest response — so--url-typeis ignored and no longer needed:medias[].link.urlandweb_content_linkare already in the body. It also skips the download-URL liveness probe, so it stays a single read request and never adds the deriveddownload_url/play_url/play_resolutionkeys.- Names differ from the reduced shapes. Raw uses the API's own keys:
kindisdrive#file/drive#folder(notfile/folder),modified_time(notmodified),starred(notstar). - Field presence is not guaranteed.
paramsis absent or partial for folders and non-media files;mediasis often[]until the server finishes transcoding. Guard withselect(.params.duration != null)or// "0"defaults. - Do not filter raw output on
file_category, and do not rely onmime_typeeither. The API frequently omitsfile_categoryeven for videos (the reducedls -F jsonshape derives it), andmime_typecomes back as""on a fair share of media files, so bothselect(.file_category == "video")andselect(.mime_type | startswith("video"))silently drop real videos. The reliable video test in raw output is the presence ofparams.duration.
Recipes:
# Videos longer than one hour (ONE API call — never loop `get` over a folder)
pikpak ls --raw | jq -r '.files[] | select((.params.duration // "0" | tonumber) > 3600)
| "\(.name)\t\(.params.duration)s"'
# 4K files, largest first, sizes in real bytes. Compare the long edge: portrait clips come back
# as width=2160 / height=3840, so a bare `.params.width >= 3840` test misses them.
pikpak ls --raw -l 500 | jq -r '.files[]
| select(([(.params.width // "0" | tonumber), (.params.height // "0" | tonumber)] | max) >= 3840)
| [.name, .size] | @tsv' | sort -k2 -rn
# Where this file came from (magnet, or the http(s) page it was pulled from)
pikpak get <fileID> --raw | jq -r '.params.url'
# Every playable variant of one video
pikpak get <fileID> --raw | jq -r '.medias[] | "\(.media_name)\t\(.link.url)"'
# Folders only, without --type
pikpak ls --raw | jq -r '.files[] | select(.kind == "drive#folder") | .name'
Video Playback
pikpak play <fileID> # Open with system default player
pikpak play <fileID> --url-only # Print playback URL only
pikpak play <fileID> --player mpv # Open with specified player
pikpak play <fileID> --player "iina --pip" # Player with arguments (run through the shell)
Playback automatically selects original quality (Origin). If no original quality is available, it
falls back to the first media with a valid URL, then to the file's web content link. Use play
directly, no need to first run get --url-type play.
Upload Files to Cloud
pikpak upload ./movie.mp4 # Upload a file to root directory
pikpak upload ./movie.mp4 -p <folderID> # Upload into a specific folder
pikpak upload ./movie.mp4 -n "new-name.mp4" # Set remote name (default: basename)
pikpak upload ./my-folder # Upload a directory recursively (recreates structure)
pikpak upload ./movie.mp4 -w # Wait until PikPak finishes processing
pikpak upload ./big.iso --part-size 8MB # Multipart chunk size (default: 6MB; smaller files go in one request)
pikpak upload ./big.iso --parallel 5 # Concurrent chunks (default: 3)
pikpak upload ./movie.mp4 --checkpoint ./ckpt # Checkpoint dir for resumable upload (default: system temp)
pikpak upload ./movie.mp4 -F json # JSON output (file_id, name, phase, instant_upload, task_id)
pikpak upload ./my-folder -F json # Directory JSON output (root_folder_id, total, succeeded, failed, files[])
Uploads are resumable: if interrupted, re-running the same command reuses the local checkpoint to continue instead of restarting. If PikPak already has a file with identical content, it is matched instantly (instant upload) with no data transfer. A progress bar is shown during real transfers.
Download Files to Local
pikpak download <fileID> # Download to current directory (original filename)
pikpak download <fileID> -o ./movie.mp4 # Specify output path (single file only)
pikpak download <fileID> --url-only # Print download URL only
pikpak download <id1> <id2> ... # Download several files at once (each keeps its original name)
pikpak download <fileID> --parallel 4 # Connections per file, 1-8 (default 8)
-o/--output is rejected when more than one file ID is given. Use download directly; no need to
get the URL first then curl: files of 16MB or more are fetched over up to 8 parallel ranged connections
(PikPak's per-link connection cap), several files download one after another, and the data is written
to <output>.part, renamed when complete. A dropped connection retries only its own part. A progress
bar is shown during download. On server returned status 503, PikPak's connection limit was hit
(e.g. another download of the same link is running): retry later or lower --parallel.
To print a direct link without downloading:
pikpak download <fileID> [fileID...] --url-only # Download link(s), one per line
pikpak play <fileID> --url-only # Streaming/playback link
pikpak get <fileID> --url-type download|play|all # URLs alongside file metadata
Which one to use:
download --url-only— the go-to for "just give me the link(s)". Raw URLs only, batchable, ideal for scripting/piping.play --url-only— the media/streaming link for a single file.get --url-type— when you also need metadata (name, size, mime_type) in one structured record.
There is no pikpak url command; the link is always a flag on the verb that owns the intent.
Cloud Download / Cloud Add
pikpak task add <url> # Add link to root directory (alias: task add-link)
pikpak task add <url> -p <folderID> # Specify target folder
pikpak task add <url> -w # Auto-watch task progress after adding (--watch)
Supports magnet links (magnet:), HTTP/HTTPS links, etc. task add -F json returns task_id and
file_name — keep the task_id for task get / task watch / task rm.
Task Management
pikpak task ls # List tasks (default type: offline, default limit 20)
pikpak task ls -t offline # Filter by type (--type)
pikpak task ls -s running,pending # Filter by status (--phase)
pikpak task ls -l 50 # Limit number (--limit, default 20)
pikpak task ls --token <token> # Pagination
pikpak task get <taskID> # View task details (including subtask progress)
pikpak task rm <taskID> [taskID...] # Delete tasks (batch)
pikpak task rm <taskID> --delete-files # Delete task and associated files
pikpak task watch <taskID> # Real-time monitoring (Ctrl+C to stop)
Task status (phase): pending | running | error | complete — these are the values -s/--phase
accepts. task ls -F json returns tasks plus an optional next_page_token, with per-task id,
name, type, status, progress, size, updated; status comes back title-cased
(Running, Complete) and name is truncated to 30 characters, so match tasks by id, never by
name, and compare status case-insensitively.
task get -F json is a different, richer shape: id, name, type, status, progress, plus
file_id, file_name, file_size, created, updated, message and a subtasks array
(id, file_name, status, progress, size) when the service returns them. file_id is how
you get from a finished task to the file for play / download.
File Operations
pikpak mkdir <name> # Create folder in root directory
pikpak mkdir <name> -p <parentID> # Create folder in specified directory
pikpak rm <fileID> # Delete file/folder
pikpak rm <id1> <id2> <id3> # Batch delete
pikpak mv <id>... <targetFolderID> # Move (supports multiple source files)
pikpak cp <id>... <targetFolderID> # Copy (supports multiple source files)
pikpak mv <id>... "" # Move back to the root folder (empty target)
pikpak rename <fileID> <newName> # Rename
pikpak star <fileID> # Star
pikpak unstar <fileID> # Unstar
Share Management
# List shares
pikpak share ls
pikpak share ls -l 20 --token <token> # --limit default 20
# Create share (supports batch files)
pikpak share create <fileID...>
pikpak share create <fileID> -t "title" -e 7 # 7 days expiration (default -1 = never)
pikpak share create <fileID> -p "mypass" # Custom password
pikpak share create <fileID> -P # Server random password
pikpak share create <fileID> -r 10 # Limit restore count (default -1 = unlimited)
# View share details
pikpak share get <shareID>
pikpak share get <shareID> -p <password> # Password protected share
pikpak share get <shareID> --parent <folderID> # Browse subfolders
pikpak share get <shareID> -l 50 --token <token> # Paginate the file listing (--limit default 100)
# Delete share (batch)
pikpak share rm <shareID...>
# Restore shared files to your cloud storage
pikpak share restore <shareID>
pikpak share restore <shareID> -p <password>
pikpak share restore <shareID> --parent <folderID>
pikpak share restore <shareID> -f "id1,id2" # Restore specified files only (empty = all)
# View restore history
pikpak share history
pikpak share history -l 20 --token <token> # --limit default 20
Password mode: no flag → public no password; -p <password> → custom password; -P → server random password.
-F json shapes: share create → share_id, share_url, pass_code, share_text (the
ready-to-send link+code line). share ls → shares + optional next_page_token, per item
share_id, title, status, files, views, expiration, created. share get → title,
status, files, views, expiration, restore_count_left, shared_by, file_list
(id, name, kind, size) + optional next_page_token — the ids in file_list are what
share restore -f takes. share restore → restore_status, file_id, restore_task_id.
share history → records + optional next_page_token, per item id, share_id, title,
created.
Feedback
Submit feedback to PikPak support and follow up on replies.
# Submit feedback (--type default OTHER, e.g. ACCOUNT, VIP, DOWNLOAD, UPLOAD)
pikpak feedback submit "Downloads stall at 99%" -y
pikpak feedback submit "Cannot sign in" -t ACCOUNT -y
pikpak feedback submit - < report.txt # Read content from stdin
pikpak feedback submit "Broken thumbnail" -i shot.png # Attach images (repeatable, max 20 MB each)
pikpak feedback submit "Still broken" --related 42 # Follow-up on feedback 42
# List my feedback (Unread marks new support replies)
pikpak feedback ls
# Show one conversation with its message history (marks replies as read)
pikpak feedback get <feedbackID>
# Reply, optionally with images
pikpak feedback reply <feedbackID> "Here is the log" -y
pikpak feedback reply <feedbackID> -i shot.png # Content optional when an image is attached
# Confirm a resolved conversation (only valid while status is resolved)
pikpak feedback resolve <feedbackID>
# Inspect / opt out of the diagnostic log
pikpak feedback log # Preview shown before the upload
pikpak feedback log -l 50 --path # Last 50 lines / log directory
pikpak feedback submit "Typo in help" --log=false # Submit without uploading the log
feedback submit -F json returns feedback_id, message_id, log_lines, log_task_id,
log_archive and, when images are attached, image_message_id plus image_refs. Image
attachments are uploaded after the conversation exists, so they arrive as a follow-up message.
CLI version, OS, architecture and device ID are sent as metadata automatically.
feedback reply -F json returns the same shape minus image_message_id. feedback ls -F json
is rooted at feedback (per item: id, issue_type, status, unread, updated, optional
close_reason, last_message, last_sender); feedback resolve -F json returns feedback_id
and status; feedback log -F json returns dir, recording, lines, log.
Attachments are capped at 20 MB per image.
Log confirmation is mandatory: when the diagnostic log has entries, feedback submit/reply
refuse to run non-interactively without -y (exit code 2, INVALID_ARGUMENT). Pass -y to
consent, or --log=false to send nothing. On a terminal without -y a preview is printed and a
y answer is required.
Diagnostic Logging
Recording is off by default and time-boxed, so a bug report needs the switch turned on before reproducing the problem:
pikpak config log on # Record for 2h (default)
pikpak config log on -d 30m # Record for 30 minutes
pikpak config log on -d 0 # Record until turned off explicitly
pikpak config log off # Stop recording (files are kept)
pikpak config log clean [-y] # Delete the recorded log files
pikpak config ls # Settings file, log directory, state, expiry, log size, invitation file/code
Workflow for reporting a bug: pikpak config log on → reproduce the failure → pikpak feedback submit "<what happened>" -y → pikpak config log off.
Content: ~/.pikpak/logs/cli-<date>.log holds the invocation (secrets masked), the outcome
(error code/reason/request ID) and one line per HTTP request (method, path, query parameter
names, status, duration, request ID) — never bodies, headers or tokens. Files rotate daily and
at 2 MB, history is pruned after 7 days. The switch expires by itself; PIKPAK_NO_LOG=1
overrides it. Reading the log always works, even after the switch expired.
pikpak config ls -F json returns settings_path, log_recording, log_enabled,
log_expires_at, log_dir, log_files, log_bytes, log_disabled_by_env, affiliate_path
and affiliate (omitted when no code is recorded).
Transport: the log is zipped (max 8 MB of raw log, newest first) and uploaded to Aliyun OSS
through the log service on api-drive.mypikpak.com — POST /log/v1/report for a pre-signed PUT
URL and task ID, the PUT itself, then POST /log/v1/commit. The feedback message carries only
the reference (log.task_id / log.zip_name), never the log text, so the message stays exactly
as the user wrote it. The upload runs before the conversation is created: if it fails, nothing is
filed and the command exits non-zero.
Cleanup: pikpak config log clean deletes only the log files — credentials, the saved PAT, the
device ID, affiliate and settings.json are untouched, and upload
checkpoints are untouched too (system temp, or a user-owned --checkpoint directory). It lists the
files and asks for confirmation; non-interactive without -y fails with exit code 2 and
INVALID_ARGUMENT. -F json returns removed_files and removed_bytes. Cleaning while recording
is on only clears history — the next command starts a new file, so pair it with config log off.
Invitation Code
Installers write a supplied non-empty code to the plain-text affiliate file,
without source or timestamp metadata. No code means no file change. The path is
PIKPAK_DIR/affiliate, then affiliate beside PIKPAK_CREDENTIALS, then
~/.pikpak/affiliate. Homebrew users can pass the code when registering:
pikpak config ls # Show the saved code and invitation file path
pikpak auth register # Use the saved code
pikpak auth register --affiliate <code> # Override it for this registration
pikpak auth register --affiliate= # Skip it for this registration
The CLI itself does not read PIKPAK_AFFILIATE. Registration uses the saved code
only when --affiliate is absent. Inviter lookup errors neither discard the code
nor block registration; the signup page handles validation. The invitation file
is deleted after a completed registration using it. Explicit overrides and link-only
-F json / -F yaml runs leave it untouched. pikpak config ls -F json reports
affiliate (when non-empty) and affiliate_path. Registration ignores surrounding
whitespace in a manually edited invitation file.
Account & System
pikpak auth status # Account panel: identity, storage quota, VIP status (also in the Authentication section)
pikpak version # View version
pikpak version --check # Check for new version
pikpak update # Update to latest version
auth status -F json returns logged_in, user_id, name, email, status, created, plus
vip (type/status/expire_time/items), storage (total/used/in_trash/is_unlimited/complimentary/expires)
and additional_quotas (upload_flow, decompress_limit, space_size, trash_duration).
MCP Server
Start a Model Context Protocol (MCP) server to expose PikPak CLI to external LLMs.
pikpak mcp # Start in Streamable HTTP mode (default, port 8080)
pikpak mcp --port 9090 # Custom HTTP port
pikpak mcp --stdio # stdio mode for local MCP clients (e.g., Claude Desktop)
Agent Skill Installation
Install this document into the skills directory of the AI coding agents on the machine, so any agent can discover it.
pikpak skill install # Every detected agent
pikpak skill install claude codex # Only these (claude, codex, gemini, opencode, cursor, omp, goose, iflow, qwen, agents)
pikpak skill install --project # Into ./.claude/skills etc. instead of the home directory
pikpak skill install --dir <path> # Into an arbitrary skills directory
pikpak skill install --from <path|url> # Use a local .skill archive, SKILL.md or directory instead of the published URL
pikpak skill install --dry-run # Show what would be written
pikpak skill install --force # Overwrite a locally edited or foreign skill directory
pikpak skill install --all # Include agents that are not installed on this machine
pikpak skill ls # Per-agent state: installed | outdated | modified | foreign | missing (-F json for scripts)
pikpak skill path # Print the resolved skills directories, one per line (--all for undetected agents too)
pikpak skill uninstall # Remove the files this CLI installed (--force for edited/foreign dirs)
An install that was edited locally is reported as modified and left alone
until --force is given; a directory with no provenance record is foreign and
treated the same way. A successful pikpak update refreshes untouched installs.
Global Flags
-v, --verbose # Enable verbose output (shows API request logs, useful for troubleshooting)
-F, --format # Output format: json | yaml (default: human-readable; only json and yaml are accepted as explicit values)
--version # Show version without checking for updates
Common Workflows
Find and Play Video
pikpak ls --type file -F json # Browse the folder to find the video file ID
pikpak play <fileID> # Play directly
Cloud Download with Monitoring
pikpak task add <url> -w # Add and watch progress (recommended)
# Or step by step
pikpak task add <url> # Note the returned taskID
pikpak task watch <taskID> # Watch separately
Download File to Local
pikpak ls --type file -F json # Browse the folder to find the file ID
pikpak download <fileID> -o ~/Downloads/movie.mp4
Upload Local File/Folder to Cloud
pikpak mkdir "Uploads" # (Optional) create target folder, get its ID
pikpak upload ./movie.mp4 -p <folderID> -w # Upload into folder and wait for processing
pikpak upload ./my-folder # Or upload a whole directory recursively
Organize Files
pikpak mkdir "Movies" # Create folder, get new folder ID
pikpak mv <id1> <id2> <newFolderID> # Batch move
Share Files
pikpak share create <fileID> -t "Movie for you" -e 7 -P # 7 days + random password
# Get share link and password from output
Save Others' Shares
pikpak share get <shareID> -p <password> # View content first
pikpak share restore <shareID> -p <password> --parent <folderID> # Restore to specified folder
AI Agent Decision Guide
User Intent → Command Mapping
| User says | Use this |
|---|---|
| "Am I logged in" / "Login status" / "Who am I" | pikpak auth status |
| "Log in" / "Sign in" | pikpak auth login |
| "Register" / "Create an account" | pikpak auth register |
| "I have an invite code" / "Use my friend's invitation" | pikpak auth register --affiliate <code> |
| "Use this token" / "Set my API token" | pikpak auth token <token> or PIKPAK_TOKEN=<token> |
| "What's in the cloud" / "List files" | pikpak ls |
| "Search/find a file or folder" | pikpak ls (search is unavailable) |
| "Videos longer than X" / "Duration, resolution, or source magnet of files" | pikpak ls --raw (once, then filter .files[].params), never get per file |
| "Download this link" / "Cloud download" / "Cloud add" | pikpak task add <url> -w |
| "Upload this file" / "Upload this folder" / "Put this into the cloud" | pikpak upload <path> |
| "Play this video" / "Watch this movie" | pikpak play <fileID> |
| "Download to local" / "Download this file" | pikpak download <fileID> |
| "Just give me the link" | pikpak download <fileID> --url-only (play --url-only for streaming) |
| "Task progress" / "How's the download going" | pikpak task ls or pikpak task watch <taskID> |
| "Share this file" | pikpak share create <fileID> |
| "Save this share" / "Restore share" | pikpak share restore <shareID> |
| "Create folder" / "Make a directory" | pikpak mkdir <name> |
| "Delete this" | pikpak rm <fileID> |
| "How much space left" / "Account info" | pikpak auth status |
| "Report a bug" / "Contact support" / "Submit feedback" | pikpak config log on, reproduce, then pikpak feedback submit "<content>" -y |
| "Did support reply" / "My feedback status" | pikpak feedback ls then pikpak feedback get <feedbackID> |
| "Turn on debug logs" / "Collect logs" | pikpak config log on [-d 2h], later pikpak config log off |
| "Clean up local files" / "Delete the logs it wrote" | pikpak config log clean [-y] (does not sign you out) |
AI Usage Principles
- Prefer JSON format:
pikpak ls -F jsoncontainsfilesplus an optionalnext_page_token, with file fields includingid,parent_id,mime_type,file_category,modified, andhash - File type: Use
mime_type(e.g.,video/mp4) orfile_category(e.g.,video), more reliable than file extension - ID lookup: File/folder IDs from
lsorgetoutput; task IDs fromtask addreturn ortask ls; share IDs fromshare createorshare ls; feedback IDs fromfeedback submitreturn orfeedback ls - Root directory: ID is empty string
"", not/or/root - Sizes and timestamps are display strings:
sizeis formatted (e.g.1.2 GB,-for folders),modified/createdare local text —YYYY-MM-DD HH:MMfromls,YYYY-MM-DD HH:MM:SSfromget— not bytes or RFC3339, so never parse them as numbers.--rawgives byte counts and RFC3339 instead - Missing a field? Use
--raw, not agetloop:ls/getreturn a reduced 9/12-field shape. Duration, resolution, source magnet, codecs, audit status, byte-exact size and anything the API adds later live only in--raw(see Raw API Output). Onepikpak ls --rawcovers a whole folder; callingpikpak getper file is slower and still has no duration
Non-existent Features
- No trash/restore-from-trash command:
rmis the only deletion entry point - No
--continueforauth register: registration cannot be polled non-interactively (see Authentication) - No
pikpak url: usedownload --url-only,play --url-onlyorget --url-type - No
pikpak data: local cleanup isconfig log clean - No
pikpak search: the entry point is disabled while the server-side search API is down; walk folders withls
Error Handling
All failures go to stderr with one stable schema. Human-readable output shows the failed
operation, a machine-stable Code, Reason, Request ID (when provided), and an actionable Hint:
Error: Failed to get file info
Code: FILE_NOT_FOUND
Reason: File does not exist
Request ID: req-123
Hint: Check the resource ID with 'pikpak ls' and retry.
With -F json / -F yaml the payload is rooted at error and contains code, message,
reason, hint, and optionally status, request_id, trace_id.
Exit codes: 1 = command/runtime failure, 2 = invalid usage (unknown flag, wrong arg count).
Common Errors and Solutions
| Error code | Cause | Solution |
|---|---|---|
UNAUTHENTICATED / UNAUTHORIZED / TOKEN_EXPIRED / TOKEN_REFRESH_FAILED | Not logged in, or the saved session expired/was revoked. Such dead sessions are cleared automatically on the next command. | Re-run pikpak auth login |
ACCESS_DENIED | Authorization was denied, or the token lacks permission for this resource (local credentials are left untouched). | Re-run pikpak auth login |
FILE_NOT_FOUND / INVALID_FILE_ID / INVALID_PARENT_ID | Wrong ID | Verify the ID with pikpak ls |
CREDENTIALS_ERROR | Broken/missing credentials file | Re-run pikpak auth login, or check PIKPAK_DIR / PIKPAK_CREDENTIALS |
FILE_SPACE_NOT_ENOUGH | Insufficient cloud storage space | pikpak auth status to check quota, clean up files |
TASK_DAILY_CREATE_LIMIT | Daily task creation limit reached | VIP limit, wait for next day to reset |
DAILY_CREATE_LIMIT_FREE | Free user daily limit | Upgrade to VIP or wait for reset |
TRANSFER_QUOTA_EXCEEDED | Transfer quota exceeded | VIP limit |
CONNECTED_APPS_QUOTA_EXCEEDED | Open-platform (Connected Apps) quota hit | Wait for reset. Uploads resume with parts preserved; downloads/streaming may throttle to ~100 KB/s |
TIMEOUT / REQUEST_FAILED / NETWORK_ERROR | Network problem | Check connectivity and retry |
FILESYSTEM_ERROR | Local path missing or not readable/writable (upload, download, --checkpoint) | Check the path and its permissions |
INVALID_ARGUMENT / USAGE_ERROR | Wrong arguments, or a confirmation flag (-y) missing in a non-interactive run | Re-run with --help to check the arguments |
SERVICE_UNAVAILABLE | Server-side feature is off (currently search) | Use the documented alternative (ls instead of search) |
COMMAND_FAILED | Fallback for an error with no service code | Re-run with -v for details |
Service codes are normalized to upper case before they are printed or serialized, so match
Code / error.code case-insensitively or against the upper-case spelling — never against the
lower-case file_space_not_enough form the API sends.
Technical SDK/response details are hidden by default; re-run with -v to include them.