Communitygithub.com

pikcloud/skills

PikPak cloud storage CLI (pikpak). Run every cloud-drive operation from the command line: browse and search files, cloud-download magnets/torrents/links, manage download tasks, stream videos, upload local files and folders, download to disk, share files and restore others' shares, move/copy/rename/delete, register accounts and manage access tokens. Use this skill when the user mentions PikPak or a cloud drive, or asks to: - cloud-download a magnet, torrent or link, or check task progress - upload a local file or folder, or download cloud files to disk - play or watch a video, movie or series stored in the cloud - share files, or save/restore someone else's share - browse, search, move, copy, rename or delete files, or create folders - log in, register an account, or save a personal access token - check account info, storage space or VIP status Trigger it even without the word "PikPak" (e.g. "download this link", "upload this file", "what's in my cloud drive", "play that video", "share this file").

What is skills?

skills is a Claude Code agent skill that pikPak cloud storage CLI (pikpak). Run every cloud-drive operation from the command line: browse and search files, cloud-download magnets/torrents/links, manage download tasks, stream videos, upload local files and folders, download to disk, share files and restore others' shares, move/copy/rename/delete, register accounts and manage access tokens. Use this skill when the user mentions PikPak or a cloud drive, or asks to: - cloud-download a magnet, torrent or link, or check task progress - upload a local file or folder, or download cloud files to disk - play or watch a video, movie or series stored in the cloud - share files, or save/restore someone else's share - browse, search, move, copy, rename or delete files, or create folders - log in, register an account, or save a personal access token - check account info, storage space or VIP status Trigger it even without the word "PikPak" (e.g. "download this link", "upload this file", "what's in my cloud drive", "play that video", "share this file").

Works with✓Claude Code✓Codex CLI✓Cursor✓Gemini CLI✓OpenCode
npx skills add https://github.com/pikcloud/skills/tree/HEAD/skills/pikpak-cli

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

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

VariableDescriptionDefault
PIKPAK_DIRCredentials directory~/.pikpak
PIKPAK_CREDENTIALSFull path to OAuth credentials file~/.pikpak/.credentials.json
PIKPAK_TOKENPersonal Access Token; takes precedence over any saved credentialunset
PIKPAK_PAT_PATHFull path to the PAT file saved by auth token~/.pikpak/.pat.json
PIKPAK_VERSIONVersion specified during installLatest
PIKPAK_INSTALL_DIRInstallation directory~/.local/bin (macOS/Linux), %USERPROFILE%\.pikpak (Windows)
PIKPAK_NO_UPDATE_NOTIFIERSet to 1 to disable automatic update checks and reminders; explicit version --check and update still workunset
PIKPAK_NO_LOGSet to any non-empty value (e.g. 1) to override config log and never record the diagnostic logunset
PIKPAK_NO_SKILL_SYNCSet to any non-empty value to stop pikpak update from refreshing installed agent skillsunset
PIKPAK_AFFILIATEInvitation 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:

  1. Run pikpak auth login -m device -F json to get device_code, verification_uri, user_code, expires_in, interval
  2. Show the user the verification URL and code (or QR code)
  3. Poll with pikpak auth login --continue <device_code> -F json until status is success

If the environment supports a browser (macOS/Linux with GUI), pikpak auth login (auto-detect) or pikpak auth login -m browser works without any flags. Login checks existing credentials first; use --force to create a new session.

Fully non-interactive alternative: pikpak auth token <PAT>, or export PIKPAK_TOKEN=<PAT>.

Registration caveat: pikpak auth register -F json prints signup_uri, handoff_code, qr_png_path, expires_in (and affiliate whenever a code is in play — flag or recorded) and exits — it does not return a device_code, so it cannot be polled with --continue. For AI-driven registration, run pikpak auth register without -F (it polls interactively), or have the user register in the browser and then use pikpak 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 fieldMeaningWhy it matters
params.durationPlayback length in seconds, as a string ("1420")The only source of video duration — absent from both ls and get default output
params.width / params.heightPixel dimensions, as strings ("1920" / "1080")Resolution filtering; get --url-type play only gives a label like 1080p
params.urlWhere the file came from: magnet:?xt=urn:btih:… for torrents, an http(s) URL for link downloadsThe only record of an offline download's source
params.task_idID of the offline task that produced the fileTies a file back to pikpak task get <taskID>
params.db_modify_time, params.platform, params.platform_icon, params.original_share_idService-side metadata, present on every itemProvenance
sizeRaw bytes as a string ("392927574")Default output formats it (375 MB) and cannot be compared numerically
created_time / modified_time / user_modified_timeRFC3339 with offsetDefault 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_idRemaining API fieldsAnything 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 into jq.
  • Always structured. --raw implies JSON output even without -F; -F yaml converts the same body. There is no human-readable table in this mode.
  • ls --raw is one request per page, not per file. Same -p / -l / -t / --type flags and the same server-side filters (non-trashed, PHASE_TYPE_COMPLETE) as plain ls. Paging is unchanged: read next_page_token from 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 looping get.
  • --type combines with --raw. It is a server-side kind filter (drive#file / drive#folder), so the passthrough body already contains only the requested kind and a page still holds up to -l items. Client-side jq filtering on .files[] | select(.kind == …) is no longer needed.
  • get --raw always requests usage=ALL — the fullest response — so --url-type is ignored and no longer needed: medias[].link.url and web_content_link are already in the body. It also skips the download-URL liveness probe, so it stays a single read request and never adds the derived download_url / play_url / play_resolution keys.
  • Names differ from the reduced shapes. Raw uses the API's own keys: kind is drive#file / drive#folder (not file / folder), modified_time (not modified), starred (not star).
  • Field presence is not guaranteed. params is absent or partial for folders and non-media files; medias is often [] until the server finishes transcoding. Guard with select(.params.duration != null) or // "0" defaults.
  • Do not filter raw output on file_category, and do not rely on mime_type either. The API frequently omits file_category even for videos (the reduced ls -F json shape derives it), and mime_type comes back as "" on a fair share of media files, so both select(.file_category == "video") and select(.mime_type | startswith("video")) silently drop real videos. The reliable video test in raw output is the presence of params.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 saysUse 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

  1. Prefer JSON format: pikpak ls -F json contains files plus an optional next_page_token, with file fields including id, parent_id, mime_type, file_category, modified, and hash
  2. File type: Use mime_type (e.g., video/mp4) or file_category (e.g., video), more reliable than file extension
  3. ID lookup: File/folder IDs from ls or get output; task IDs from task add return or task ls; share IDs from share create or share ls; feedback IDs from feedback submit return or feedback ls
  4. Root directory: ID is empty string "", not / or /root
  5. Sizes and timestamps are display strings: size is formatted (e.g. 1.2 GB, - for folders), modified/created are local text — YYYY-MM-DD HH:MM from ls, YYYY-MM-DD HH:MM:SS from get — not bytes or RFC3339, so never parse them as numbers. --raw gives byte counts and RFC3339 instead
  6. Missing a field? Use --raw, not a get loop: ls/get return 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). One pikpak ls --raw covers a whole folder; calling pikpak get per file is slower and still has no duration

Non-existent Features

  • No trash/restore-from-trash command: rm is the only deletion entry point
  • No --continue for auth register: registration cannot be polled non-interactively (see Authentication)
  • No pikpak url: use download --url-only, play --url-only or get --url-type
  • No pikpak data: local cleanup is config log clean
  • No pikpak search: the entry point is disabled while the server-side search API is down; walk folders with ls

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 codeCauseSolution
UNAUTHENTICATED / UNAUTHORIZED / TOKEN_EXPIRED / TOKEN_REFRESH_FAILEDNot 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_DENIEDAuthorization 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_IDWrong IDVerify the ID with pikpak ls
CREDENTIALS_ERRORBroken/missing credentials fileRe-run pikpak auth login, or check PIKPAK_DIR / PIKPAK_CREDENTIALS
FILE_SPACE_NOT_ENOUGHInsufficient cloud storage spacepikpak auth status to check quota, clean up files
TASK_DAILY_CREATE_LIMITDaily task creation limit reachedVIP limit, wait for next day to reset
DAILY_CREATE_LIMIT_FREEFree user daily limitUpgrade to VIP or wait for reset
TRANSFER_QUOTA_EXCEEDEDTransfer quota exceededVIP limit
CONNECTED_APPS_QUOTA_EXCEEDEDOpen-platform (Connected Apps) quota hitWait for reset. Uploads resume with parts preserved; downloads/streaming may throttle to ~100 KB/s
TIMEOUT / REQUEST_FAILED / NETWORK_ERRORNetwork problemCheck connectivity and retry
FILESYSTEM_ERRORLocal path missing or not readable/writable (upload, download, --checkpoint)Check the path and its permissions
INVALID_ARGUMENT / USAGE_ERRORWrong arguments, or a confirmation flag (-y) missing in a non-interactive runRe-run with --help to check the arguments
SERVICE_UNAVAILABLEServer-side feature is off (currently search)Use the documented alternative (ls instead of search)
COMMAND_FAILEDFallback for an error with no service codeRe-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.

Related Skills