olares-files

olares-cli files command tree: list (ls), upload, download, cat, rm, cp, mv, rename (rn), share (internal / public / smb), and Sync-repo CRUD (repos list / get / create / rename / rm) against the per-user files-backend (drive/Home, drive/Data, sync, cache, external, awss3, dropbox, google, tencent, share). Covers the 3-segment frontend path schema (<fileType>/<extend>/<subPath>), resumable chunked upload (Drive v2 protocol), Range-based resumable download, recursive directory transfer with errgroup parallelism, batch DELETE wire shape, server-side copy/move via PATCH /api/paste/<node>/ (async task_id queue, cross-volume supported), synchronous in-place rename via PATCH /api/resources/.../?destination=..., folder-share creation across the three flavors (Internal cross-user, Public link with password+expiration, SMB Samba mount) via POST /api/share/share_path/<...>/, share management (list / get / rm) plus SMB-account roster (smb-users list/create), Sync (Seafile) library catalog management via /api/repos/ (GET list with type=mine|share_to_me|shared, POST create with ?repoName=, PATCH rename with ?destination=&repoId=, DELETE with ?repoId=), and two server-side quirks the user MUST know about (POST mkdir auto-renames existing dirs to 'Foo (1)'; GET single-file resource returns HTTP 500). Use whenever the user mentions files / drive / Home / Data / sync / cache, uploading or downloading files, listing a remote directory, deleting remote files, cat-ting a remote file, copying or moving (renaming) remote files / directories, in-place renaming, sharing a folder with other users, public links with password / expiration, SMB / Samba network shares, listing / creating / renaming / deleting Sync (Seafile) libraries, repo_id discovery, /api/resources, /api/raw, /api/paste, /api/share, /api/repos, frontend path, or sees errors like 'Documents (1)' appearing on the server.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "olares-files" with this command: npx skills add pengpeng/olares-files

files (Drive v2 + per-user files-backend)

CRITICAL — before doing anything, MUST use the Read tool to read ../olares-shared/SKILL.md for the profile selection, login, and HTTP 401/403 recovery rules that every command here depends on.

Core concept: the 3-segment frontend path

Every resource on the per-user files-backend is addressed by a 3-segment "frontend path" (see cli/cmd/ctl/files/path.go):

<fileType>/<extend>[/<subPath>]
SegmentMeaning
fileTypeStorage class (lowercase, case-sensitive). One of: drive, cache, sync, external, awss3, dropbox, google, tencent, share, internal
extendVolume / repo / account inside that class. Case-sensitive. Drive: only Home or Data. Cache / external: node name. Sync: seafile repo id. Cloud (awss3/dropbox/google/tencent): account key
subPathPath inside extend (root if omitted). The leading / is implicit

Examples:

drive/Home/                            # Home volume root
drive/Home/Documents/report.pdf        # a file under Home/Documents
drive/Data/Backups/                    # Data volume, Backups subfolder
sync/<repo_id>/notes/                  # seafile sync repo
cache/<node>/                          # node-local cache
awss3/<account>/<bucket>/key.txt       # S3-compatible cloud drive

The first segment is normalized to lowercase by the backend; the CLI accepts only the canonical lowercase form on input. Drive's extend MUST be Home or Data exactly — home will be rejected with invalid drive type.

Trailing-slash convention (critical)

Whether a path ends with / is meaningful and changes command behavior:

Path formMeaning
drive/Home/Foo/Directory intent
drive/Home/FooFile intent

This shows up in five places:

  • files rm drive/Home/Foo/ requires -r (the trailing / declares "this is a directory")
  • files upload <local> drive/Home/Documents/ means "upload INTO Documents/"; files upload <local> drive/Home/Documents/2026-Q1.pdf means "upload AS that exact path (rename on the way in)"
  • files cp <src> <dst>/ drops <src> into the directory by basename; files cp <src> <dst> (no trailing slash, single source only) treats <dst> as the full target path (rename / exact-target mode). Same for files mv.
  • files cp -r drive/Home/old/ (trailing / on a source) requires -r — Unix-style refusal to operate on directories without recursion. Same for files mv.
  • files ls drive/Home/ lists the volume root; the parser tolerates both drive/Home and drive/Home/ for ls but the trailing slash is recommended for clarity

Server-side quirks (critical, do not work around)

These are real backend behaviors that have already cost us debugging time. Teach yourself and the user to respect them; do not suggest "workarounds" that bypass the CLI's existing handling.

1. POST /api/resources/<dir>/ auto-renames existing directories

Hitting the directory-create endpoint against an existing directory does not return 409. The server creates a sibling named <dir> (1) instead. See the docstring on cli/internal/files/upload/api.go's Mkdir for the precise wording.

Consequence baked into the CLI: files upload does not pre-create the destination directory. It relies on the chunk POST to implicitly materialize parents. The destination directory MUST already exist on the server — if you need a fresh directory, create it through the LarePass web app first (a future files mkdir verb may cover this).

User-visible symptom of getting this wrong (older CLI versions): an extra Documents (1) directory appears on the server even though the upload "succeeded".

2. GET /api/resources/<file> (no trailing slash) returns HTTP 500

The backend's single-file List handler hard-codes Content: true (files/pkg/drivers/posix/posix/posix.go getFiles) and tries to slurp the file's bytes into the response envelope. For json / binary / large files, this just 500s.

Consequence baked into the CLI: Stat always lists the parent directory and looks up the leaf in its items array (see cli/internal/files/download/stat.go). This matches what the LarePass web app does — it never probes a single-file resource directly. Both download and cat use this code path.

If the user reports HTTP 500 against /api/resources/.../<filename> with no trailing slash, do NOT suggest "just retry". The right answer is: use the CLI command (files cat / files download), or list the parent and look at items.

Authentication transport

Every files API call carries X-Authorization: <access_token> as a header (NOT the standard Authorization: Bearer ...). The Factory's refreshingTransport injects this automatically; see cli/pkg/cmdutil/factory.go. Do not try to call the backend via curl with a Bearer token — that header shape is not what the per-user files-backend expects and the request will fail.

The transport auto-refreshes expired tokens transparently through two paths (both detailed in ../olares-shared/SKILL.md "Automatic token refresh"):

Verb(s)Body shapeRefresh path
ls, cat, download, rm, cp, mv, rename, share (all subcommands), repos (all subcommands)No body or *bytes.Reader/*bytes.Buffer (replayable)Reactive — send with current token; on 401/403 call /api/refresh and retry once with the new token.
upload (chunk POST)*os.File slice (non-replayable streaming body)Pro-active — decode the JWT's exp before each chunk; if within 60s of expiry, refresh BEFORE handing the body to the transport.

The pro-active path on upload exists because once a *os.File chunk is consumed by the first send, we can't replay it on a 401 — the resume probe would re-pull from the server-known offset on the next run, but the in-flight chunk would already have failed the user's command. Pre-flight rotation collapses that into a silent rotate-and-continue, even when --parallel N>1 has multiple chunks racing the same expiry window (the Refresher's in-process mutex + cross-process flock guarantee a single /api/refresh hit per stale token).

Stat / Range probes inside download and cat use the reactive path normally — they're cheap GETs with no body.

When the refresh leg itself fails (/api/refresh rejects the refresh_token), the typed *credential.ErrTokenInvalidated propagates through reformatHTTPErr / reformatRmHTTPErr so the user sees the canonical "run profile login" CTA directly, without a Get "https://...": URL prefix. Recovery rules live in olares-shared.

Command cheatsheet (10 top-level verbs)

files ls <path> [--json]

List a remote directory. See cli/cmd/ctl/files/ls.go.

olares-cli files ls drive/Home/
olares-cli files ls drive/Home/Documents
olares-cli files ls sync/<repo_id>/
olares-cli files ls drive/Home/Documents --json   # raw envelope, pretty-printed

Default output: a one-line header (<path> (N dirs, M files, modified ...)) followed by a 5-column table MODE SIZE TYPE MODIFIED NAME. Directories sort before files; directory names get a trailing /. Empty directories print (empty).

--json prints the raw JSON envelope from the backend, useful for scripting.

files upload <local-path> <remote-path>

Resumable chunked upload to drive/Home/<...>. See cli/cmd/ctl/files/upload.go and cli/internal/files/upload/.

# Upload one file into an existing directory.
olares-cli files upload report.pdf drive/Home/Documents/

# Upload AND rename on the server.
olares-cli files upload report.pdf drive/Home/Documents/2026-Q1.pdf

# Upload a whole directory tree.
olares-cli files upload ./photos drive/Home/Backups/

# Two files in flight at a time, chunks remain sequential per file.
olares-cli files upload ./photos drive/Home/Backups/ --parallel 2

Wire protocol (Drive v2 / Resumable.js-compatible):

  1. GET /upload/upload-link/<node>/... → upload session
  2. GET /upload/file-uploaded-bytes/<node>/... → server-driven resume offset (no local progress file)
  3. POST chunks (8 MiB default) with Content-Range: bytes <start>-<end>/<total> until done

Constraints / flags:

  • Destination MUST be under drive/Home (drive/Data is read-only on the wire); the CLI rejects anything else with upload destination must be under drive/Home.
  • Destination directory MUST already exist — see "POST auto-renames" above.
  • A trailing / on <remote-path> means "into this directory"; without one, <remote-path> is treated as the full target path (rename on the way in).
  • --parallel N (default 2): per-file concurrency. Per-file chunks remain sequential by design — the resume probe assumes one in-flight chunk per file.
  • --chunk-size <bytes> (default 8 MiB): align with the server's expected size; rarely needs tuning.
  • --max-retries N: per-chunk retry budget on transient failures.
  • --node <name>: override the upload node; default is the first node from /api/nodes/.

Resume is automatic and server-driven: re-running the same command after a Ctrl-C / network drop just re-asks the server how many bytes it already has, floors to a chunk boundary, and continues.

files download <remote-path> [<local-path>]

Download a single file or a whole directory tree. See cli/cmd/ctl/files/download.go and cli/internal/files/download/.

# Single file into the current directory (./<basename>).
olares-cli files download drive/Home/Documents/report.pdf

# Same, but pick a different local name.
olares-cli files download drive/Home/Documents/report.pdf ./Q1.pdf

# Resume an interrupted download via Range:.
olares-cli files download drive/Home/Backups/big.tar ./big.tar --resume

# Recursively pull a directory; 4 files at a time.
olares-cli files download drive/Home/Documents/ ./out/ --parallel 4

Local destination resolution (single-file mode):

<local-path>Result
omitted./<basename(remote)>
existing directory<local-path>/<basename(remote)> (mirrors cp)
any other path (incl. trailing / if not yet existing)treated as the full target file path

Flags:

  • --resume: send Range: bytes=<localSize>- and append (server-native, no sidecar progress file).
  • --overwrite: replace an existing local file via <dst>.tmp + atomic rename. The previous version stays intact until the new one lands.
  • --resume and --overwrite are mutually exclusive — pick one.
  • --parallel N (default 4): only meaningful in directory mode (errgroup-bounded concurrency).
  • --max-retries N: per-file transient-failure budget (5xx triggers retry; 4xx fails fast).

Directory mode (trailing / on <remote-path>):

  • The remote tree is walked recursively via /api/resources/.../.
  • The remote root's basename becomes the top-level directory under <local-path> (matches the LarePass folder-download UX). Empty subdirectories are mirrored locally.
  • Single Stat lookup at the start to confirm the path is actually a directory; then BuildPlan materializes the file list before any byte is written.

files cat <remote-file>

Stream a single file's bytes to stdout. See cli/cmd/ctl/files/cat.go.

olares-cli files cat drive/Home/Documents/notes.md
olares-cli files cat drive/Home/Logs/today.log | tail -n 50
olares-cli files cat drive/Home/Photos/banner.png > banner.png  # binary-safe

Wire shape: GET /api/raw/<encPath>?inline=true (the same endpoint LarePass uses for text previews; inline=true only affects Content-Disposition, the body is identical).

  • Binary-safe: bytes are copied verbatim, no sniffing or transformation. Pipe into less / head -c / hexdump as needed.
  • Pre-flight Stat (parent listing) refuses directories early with a clear error, instead of letting the server return its terser 400. Use files download for directories.

files rm [-r] [-f] <remote-path>...

Delete one or more remote files / directories. See cli/cmd/ctl/files/rm.go and cli/internal/files/rm/.

# Delete one file.
olares-cli files rm drive/Home/Documents/old.pdf

# Recursively remove a directory.
olares-cli files rm -r drive/Home/Backups/2024/

# Multiple targets, no prompt (scripts).
olares-cli files rm -rf drive/Home/junk drive/Home/scratch/

Wire shape (one batch DELETE per parent dir):

DELETE /api/resources/<encParentDir>/   body: {"dirents": ["<name1>", "<name2>", ...]}

Targets sharing a parent collapse into a single request (matches the LarePass web app's batchDeleteFileItems). Targets across different parents send one request each, sorted by fileType + extend + parent for stable output.

Flags / rules:

  • -r / -R / --recursive: required for directories. A trailing / on a target IS a directory-intent signal and triggers the same check (so files rm drive/Home/Foo/ errors without -r even if Foo is technically empty).
  • -f / --force: skip the y/N prompt. In a non-TTY context (CI, piped stdin) the command refuses without --force rather than guessing.
  • Without -f: prints "will delete N entries in M batches" with the full list, then prompts [y/N].
  • Removing the root of a volume (drive/Home/, sync/<repo>/, ...) is rejected by the planner: refusing to delete the root of <fileType>/<extend>.

Aliases: olares-cli files remove ..., olares-cli files delete ... are the same command.

files cp [-r] <src>... <dst>

Server-side copy across remote paths via the per-user files-backend's paste endpoint. See cli/cmd/ctl/files/cp.go and cli/internal/files/cp/.

# Copy one file into a directory.
olares-cli files cp drive/Home/notes.md drive/Home/Documents/

# Copy with a new name on the destination side.
olares-cli files cp drive/Home/notes.md drive/Home/notes-2026.md

# Recursive directory copy.
olares-cli files cp -r drive/Home/Photos/ drive/Home/Backups/

# Multiple sources into a directory.
olares-cli files cp drive/Home/a.pdf drive/Home/b.pdf drive/Home/Archive/

# Cross-volume copy (drive → sync repo).
olares-cli files cp drive/Home/notes.md sync/<repo_id>/inbox/

Wire shape (one PATCH per source — the endpoint does not batch like rm):

PATCH /api/paste/<node>/   body: {"action": "copy", "source": "/<fileType>/<extend><sub>", "destination": "/<fileType>/<extend><sub>"}

Source / destination are plain UTF-8 paths (not percent-encoded) — the LarePass web app decodeURIComponents before serializing, and the CLI builds them directly in that shape. Cross-volume / cross-storage-class is fully supported because the endpoint takes raw string paths and the backend dispatches by <fileType>.

Destination semantics (Unix-style):

Form of <dst>Meaning
ends with /Drop-into-directory mode. Each <src>'s basename is appended; the dir / file marker on the source is preserved on the destination.
no trailing / (single source only)Rename / exact-target mode. <dst> is used verbatim as the full target path.
no trailing / with multi-sourceRejected: target ... must end with '/' when more than one source is given.

Recursion + safety rules:

  • -r / -R / --recursive is required for directory sources. A trailing / on a <src> IS a directory-intent signal — without -r you get ... is a directory: pass -r/-R to copy it recursively.
  • Volume-root sources are rejected: cp drive/Home/ ... errors with refusing to copy the root of drive/Home. Same rule that protects rm.
  • src == dst is rejected — almost always a typo.
  • Cycle detection: copying drive/Home/a/ into drive/Home/a/sub/ errors with destination ... is inside source ... (would create a cycle).

Async / task semantics — important:

  • The PATCH returns a task_id after the server enqueues the copy task. The actual byte movement happens asynchronously on the files-backend's task queue; the CLI does NOT block until completion. The summary line prints queued N copy task(s): <id>, <id>, ....
  • For multi-source cp src1 src2 src3 dst/, the CLI sends N PATCH requests serially (no parallelism) so a per-call failure aborts the rest cleanly — paste tasks have no transactional rollback, and serial execution lets the user see exactly which call failed and re-run from there.
  • There is currently no built-in completion polling. If the user needs "fail / succeed in foreground", they have to monitor server-side task progress through the LarePass web app for now.

Node selection (--node):

  • Each PATCH carries a <node> URL segment. Default is the first entry from /api/nodes/ (same default files upload uses) — the CLI fetches /api/nodes/ once per invocation, but skips the round-trip when both source and destination already supply a node hint.
  • For external and cache fileTypes the path's <extend> IS the node name, and the CLI follows the LarePass web app's dst_node || src_node || default cascade automatically. Drive-↔-Drive copies and <sync> use the default.
  • --node <name> forces a specific node for every PATCH in the batch and overrides the cascade. Useful when copying across multi-master clusters where you need to pin the operation to a particular node.

Server-side rejection signal (code: -1):

  • The endpoint returns HTTP 200 with {"code": -1, "message": "..."} for malformed paths (most commonly a literal backslash anywhere in <src> or <dst> — same failure mode the LarePass web app surfaces as the files.backslash_upload notification). The CLI surfaces this as paste <src> → <dst>: <message>. Do not retry blindly on this — the path itself needs to be fixed.

files mv [-r] <src>... <dst>

Same wire endpoint as files cp (PATCH /api/paste/<node>/) but with action: "move" instead of "copy" — the server moves the source instead of duplicating it. Every flag, rule, and failure mode from files cp applies verbatim; the only difference is the verb.

# Rename a file in place.
olares-cli files mv drive/Home/notes.md drive/Home/notes-2026.md

# Move several files into a directory.
olares-cli files mv drive/Home/a.pdf drive/Home/b.pdf drive/Home/Archive/

# Recursive directory move.
olares-cli files mv -r drive/Home/Photos/ drive/Home/Backups/

mv is a separate cobra command (not a cp --move alias) so the help text stays honest about what each verb does and so olares-cli files mv reads the way users expect.

mv is a single-step destructive operation from the user's POV. Even though it's just a flag flip on the wire, treat it the way you would a mv on local disk: confirm with the user before running it on directories, and prefer cp then rm when you want a "verify then delete" workflow.

files rename <remote-path> <new-name> (alias files rn)

Synchronous in-place rename of a single file or directory. Different wire endpoint and semantics from mv — pick rename whenever you literally just want to change the basename. See cli/cmd/ctl/files/rename.go and cli/internal/files/rename/.

# Rename a file in place (notes.md → 2026-Q1-notes.md, same parent dir).
olares-cli files rename drive/Home/Documents/notes.md 2026-Q1-notes.md

# Rename a directory; the trailing '/' on the source confirms directory intent.
olares-cli files rename drive/Home/Photos/ Photos-old/

# Short alias.
olares-cli files rn drive/Home/draft.txt final.txt

Wire shape:

PATCH /api/resources/<fileType>/<extend><subPath>[/]?destination=<newName>

Key differences vs. mv:

Aspectmvrename
Wire endpointPATCH /api/paste/<node>/PATCH /api/resources/.../?destination=...
Body{action:"move", source, destination}None (new name is a query param)
Async / syncAsync (returns task_id, processed by task queue)Synchronous (returns immediately, change is visible right away)
Cross-directoryYes (move within or across volumes)No — basename change only, same parent dir
Multi-source / batchYes (one PATCH per source)No — exactly one path + one new name per call
<node> URL segmentYes (per-node routing)No

Validation rules (rejected client-side, before any HTTP call):

  • <new-name> MUST NOT contain / or \ — those are not basename characters; use mv for cross-directory moves.
  • <new-name> MUST NOT be empty, ., or ...
  • The source MUST NOT be the volume root (drive/Home/, sync/<repo>/, ...).
  • <new-name> MUST differ from the source's current basename — same-name rename is a no-op the server would silently accept; we reject it client-side so a typo doesn't go unnoticed.

If the server replies HTTP 409, that's typically a basename collision (a sibling under the same parent already has <new-name>). The CLI surfaces this as ... server reported a conflict (HTTP 409); .... Pick a different name or rm the existing sibling first.

Use rename for in-place basename changes; use mv for moves between directories or volumes. Picking the right verb keeps the wire shape simple and makes the user's intent legible in shell history.

files share <subcommand>

Create / list / remove shares for files-backend resources. Three creation flavors (Internal cross-user, Public link, SMB Samba) plus management verbs (list / get / rm) plus an SMB-account roster (smb-users). All flavors converge on the same wire endpoint and disambiguate via the share_type field in the JSON body.

See cli/cmd/ctl/files/share.go, cli/cmd/ctl/files/share_create.go, and cli/internal/files/share/.

Three share flavors

FlavorAudienceAuth modelLifetimeOutput the user needs
internalOther Olares users on the same nodeOlares user identityPersistent until share rmShare id, member list
publicAnyone who has the link + passwordPer-share password (auto-gen 8-char default)Required: --expire-days N OR --expire-time RFC3339Share id, password (printed once, NOT echoed back later), link template <share-host>/sharable-link/<id>/
smbLAN clients via SMB protocolPer-share smb_user / smb_password issued by the server, OR public-SMB ("anyone on the local network")Persistent until share rmUNC smb_link, smb_user, smb_password

Common wire shape

POST /api/share/share_path/<fileType>/<extend><subPath>/
body: {name, share_type, permission, password, expire_in?, expire_time?,
       users?, public_smb?, upload_size_limit?}

Permission integers (SharePermission in the LarePass app):

ValueLabelMeaning
0nonefilter sentinel only — not a sensible create-time value
1viewread-only
2uploadupload-only (Public-link "drop-box" mode)
3editread + write (default Public-link recipient perm; default SMB read-write)
4adminfull control (default Internal-share owner perm)

The CLI accepts canonical labels OR the numeric form: --permission edit, --permission 3, and view / read / ro / read-only are all aliases for 1. See share.ParsePermission in cli/internal/files/share/share.go for the full alias list.

files share internal <remote-path> [--users name:perm,...] [--permission admin]

# Bare share record, no members yet.
olares-cli files share internal drive/Home/Backups/

# Share a single file with two members.
olares-cli files share internal drive/Home/Reports/Q1.pdf \
    --users alice:edit,bob:view

Two-call sequence on the wire:

POST /api/share/share_path/<...>/      → creates the share record (returns id)
POST /api/share/share_member/          → adds the listed users (only when --users is given)

If the second call fails, the share record is already on the server — the CLI surfaces the share id in the error message so the user can recover by calling member-add directly (or re-running share internal with the same --users).

--permission controls the OWNER's permission on the share record; default admin matches the LarePass web app and the only other sensible value is edit. Per-user permission lives in --users's name:perm syntax (default view if omitted).

files share public <remote-path> [--password] [--expire-days N | --expire-time RFC3339] [--upload-only] [--upload-size-limit 100M]

# 7-day expiration, auto-generated 8-char URL-safe password.
olares-cli files share public drive/Home/Photos/ --expire-days 7

# Explicit password, 30 days, 100 MiB upload cap.
olares-cli files share public drive/Home/Photos/ \
    --password "s3cret-pw-1" --expire-days 30 \
    --upload-size-limit 100M

# Upload-only "drop box" with explicit expiration time.
olares-cli files share public drive/Home/Inbox/ --upload-only \
    --password drop --expire-time 2026-12-31T23:59:00Z

Required flags:

  • An expiration is mandatory. Pass exactly one of --expire-days N or --expire-time RFC3339. The web app forces this choice; the CLI mirrors it client-side. Public links without an expiration are not supported by the backend.
  • --password is technically optional — when omitted, the CLI generates an 8-byte URL-safe random password (crypto/randbase64.RawURLEncoding, ≈11 chars) and prints it ONCE. The server does not echo the password back on subsequent reads (share get / share list show only the share record, not the cleartext password), so capture it from the create-output the first time.
  • Minimum password length is 6 chars (matches the web app's passwordLimitRule).

Optional flags:

  • --upload-only flips recipient permission from edit (read+write, default) to upload (drop-only — recipients can POST files but can't list / download). Useful for inbox-style intake links.
  • --upload-size-limit accepts 100M / 1G / 500K / 512 (bytes). Suffixes are 1024-based (KiB/MiB/GiB/TiB) to match the web app's fileLimitSize(). Omitted / zero means no cap.

The CLI prints the share id, password, expiration, and a link template of the form <share-host>/sharable-link/<id>/. The full link is built by the LarePass app from the user's hostname (shareBaseUrl() in apps/.../stores/files.ts); the CLI deliberately does NOT guess this prefix because the right value depends on subdomain layout the CLI can't observe — copy the id and let the LarePass app or the share-recipient's browser resolve the prefix.

files share smb <remote-path> [--public | --users id:perm,...] [--read-only]

# Public-SMB: anyone on the local network can mount.
olares-cli files share smb drive/Home/Movies/ --public

# Per-user SMB: must list SMB-account IDs (NOT Olares user names).
olares-cli files share smb drive/Home/Backups/ \
    --users smb-uid-1:edit,smb-uid-2:view

# Read-only override for every member.
olares-cli files share smb drive/Home/Reports/ \
    --users smb-uid-1 --read-only

Mutually exclusive recipient model:

ModeFlagBody shape
Public-SMB--publicpublic_smb: true, no users array
Per-user--users id:perm,...public_smb: false, users: [{id, permission}, ...]

Exactly one of these flags MUST be passed — the CLI rejects an empty SMB share at parse time because a share visible to no one is rarely intentional.

Permission shape:

  • SMB shares accept only view (1) or edit (3) per recipient. The CLI rejects upload / admin in --users parsing with a clean message.
  • --read-only overrides every recipient's perm to view regardless of --users's :perm annotations.
  • Public-SMB mode forces permission: edit at the share-record level (matches the web app).

The IDs in --users are SMB-account IDs, NOT Olares user names. Discover available IDs via:

olares-cli files share smb-users list

Create new SMB accounts via:

olares-cli files share smb-users create <name> <password>

The output of share smb includes the smb_link (UNC path), smb_user, and smb_password — these are what a Finder / Explorer / mount.cifs client needs to mount the share.

Management verbs

# List all shares (default: both shared-by-me and shared-to-me).
olares-cli files share list

# Filter by direction / type / owner.
olares-cli files share list --shared-by-me
olares-cli files share list --shared-by-me=false  # only "shared with me"
olares-cli files share list --type smb,external
olares-cli files share list --owner alice,bob

# Inspect one share by id (returns shareable details for SMB shares).
olares-cli files share get <share-id>

# Remove one or more shares (single batched DELETE on the wire).
olares-cli files share rm <share-id> [<share-id>...]

list columns: ID TYPE NAME OWNER PATH PERMISSION EXPIRE. Output is sorted by (type, id) for stable repeated invocations — the server doesn't guarantee any ordering.

get renders the full share record key:value-style; SMB shares' smb_link / smb_user / smb_password print on their own lines so they're easy to copy.

rm accepts multiple IDs and joins them into a single DELETE /api/share/share_path/?path_ids=<comma-joined> call (atomic from the CLI's perspective).

files repos <subcommand>

CRUD verbs for the per-user files-backend's catalog of Sync (Seafile) libraries. A "repo" (the Seafile name; LarePass UI calls them "libraries") is the unit of storage that backs every sync/<repo_id>/<sub> frontend path. Each repo has a stable UUID (repo_id) that becomes the <extend> segment, and a mutable display name. The CLI exposes the same five operations LarePass surfaces in its left-nav, plus --json for scripting. See cli/cmd/ctl/files/repos.go and cli/internal/files/repos/.

Why this is its own verb. sync/<repo_id>/... is the only fileType whose <extend> segment is a server-assigned UUID rather than a user-typed name. Without files repos, the repo_id had to be copied out of the LarePass web app every time. files repos list keeps the discovery loop in-CLI; the rest of the verbs let the CLI also create / rename / tear down libraries without leaving the terminal.

Wire shape (one endpoint, four methods)

VerbHTTP method + URLSource
list (mine)GET /api/repos/ (no type param)mirrors fetchMineRepo in apps/.../api/files/v2/sync/utils.ts
list --type share-to-meGET /api/repos/?type=share_to_mefetchtosharedRepo
list --type sharedGET /api/repos/?type=sharedfetchsharedRepo
create <name>POST /api/repos/?repoName=<name> (no body)createLibrary
rename <id> <name>PATCH /api/repos/?destination=<name>&repoId=<id> (no body)renameRepo
rm <id>DELETE /api/repos/?repoId=<id>deleteRepo

All write verbs return the standard {code, message, ...} envelope; a non-zero code is promoted to a Go error and surfaced verbatim, matching the LarePass response interceptor at apps/packages/app/src/api/files/fetch.ts L118-133.

files repos list [--type mine|share-to-me|shared|all] [--json]

# Default: repos you own (matches LarePass's "My Libraries" group).
olares-cli files repos list

# Repos others have shared with you, or repos you've shared out.
olares-cli files repos list --type share-to-me
olares-cli files repos list --type shared

# Concatenated fan-out across all three flavors (for one-shot
# discovery in fresh terminals). Adds a TYPE column.
olares-cli files repos list --type all

# Raw JSON for jq pipelines.
olares-cli files repos list --json

Default columns: REPO_ID NAME PERMISSION OWNER SIZE MODIFIED ENC. Rows are sorted (type group → name → id) so repeated invocations diff cleanly.

PERMISSION is rw / r / -. For shared variants the permission lives in the share_permission field on the wire — the CLI normalizes both into one column. OWNER falls back to the share counterparty when the row was reached via share-to-me / shared. ENC flags client-side encrypted libraries (not unlock-able from the CLI — see below).

files repos get <repo_id>

olares-cli files repos get <repo-id>

Fans out across the three flavors (mineshare-to-meshared) and returns the first match. Exits non-zero with repo not found if the id isn't in any list — useful for scripts that branch on absence without parsing list output. Output is key:value-style with Repo ID, Name, Owner, Permission, Encrypted, Size, Last modified, plus a trailing use with: olares-cli files ls sync/<repo_id>/ hint.

files repos create <name> [--json]

# Provision a new (unencrypted) library.
olares-cli files repos create "Project Alpha"
# Output:
#   created repo: Project Alpha (id: <uuid>)
#   use with: olares-cli files ls sync/<uuid>/

# Capture the id for scripting.
REPO_ID=$(olares-cli files repos create "Project Alpha" --json | jq -r .repo_id)
olares-cli files ls sync/$REPO_ID/

Encryption is NOT exposed. The per-user files-backend's createLibrary endpoint accepts no password / encryption flags, and the LarePass UI has no equivalent option either. If the user needs an encrypted library they must create it from the LarePass app or directly via Seahub; once it exists the CLI can list it (ENC=yes), but every upload / download / cat / ls against it will fail until the user unlocks the repo via the web app.

--json prints the full repo record (mirroring repos list's row shape) so jq pipelines can extract any field, not just repo_id.

files repos rename <repo_id> <new_name>

olares-cli files repos rename abc-123 "Project Alpha (archived)"
# Output (when the old name is fetchable):
#   renamed repo abc-123: "Project Alpha" -> "Project Alpha (archived)"

The repo's UUID is stable across renames — already-cached sync/<repo_id>/... frontend paths keep working. The CLI does a best-effort Get first to fetch the old name for the audit line; if that lookup fails, the rename still proceeds and the output simplifies to renamed repo <id> -> "<new>".

files repos rm <repo_id>... [--yes|-y] [--force|-f]

# In a real terminal: lists targets (id + name) and asks y/N first.
olares-cli files repos rm abc-123

# Opt out of the prompt (required when stdin is not a TTY — scripts, CI, pipes).
olares-cli files repos rm abc-123 --yes
olares-cli files repos rm abc-123 -y
olares-cli files repos rm abc-123 -f
olares-cli files repos rm abc-123 def-456 ghi-789 -y

The confirmation model matches files rm / cp / mv safety: default is interactive on a TTY (proceed with repo deletion? [y/N]:); in a non-interactive context the command refuses until you add -y / --yes (or --force / -f as a files rm -f-style alias). Both -y and -f bind the same bool — either alone skips the prompt.

Destructive: removes the repo and all of its contents. The Seafile deployment may keep the data in a server-side trash window, but the CLI does not expose a restore verb — recovery requires the LarePass app or direct Seahub access.

Multiple ids are deleted in turn. Per-id failures are printed as failed: <id> (<reason>), the loop continues on, and the command exits non-zero if any deletion failed (with all per-id errors joined via errors.Join).

Common errors → fixes

Error message (excerpt)Likely causeFix
server rejected the access token (HTTP 401) / (HTTP 403)Token expired / revokedFollow olares-shared's recovery: olares-cli profile login --olares-id <id>
HTTP 404 ... not found on the serverPath typo or wrong case (Home vs home, Data vs data)files ls the parent directory to confirm spelling
invalid drive type: <x>Drive's extend isn't Home or DataUse exactly Home or Data
upload destination must be under drive/HomeTried to upload to drive/Data/... or another fileTypeMove the target under drive/Home/...
Documents (1) (or similar) appearing on the server after uploadOlder CLI version triggered the POST-mkdir auto-rename quirkUpgrade to a CLI version that has the pre-mkdir removal fix
--overwrite and --resume are mutually exclusivePassed both download flagsPick one
refusing to delete without --force in a non-interactive context (no TTY)files rm from a script with no TTYAdd -f after explicitly listing the targets to the user first
refusing to delete the root of <fileType>/<extend>Tried files rm -r drive/Home/ (or another volume root)The CLI does not support volume-root deletion; remove children individually
cat ... is a directoryfiles cat on a path that resolves to a directoryUse files download <path>/ instead
HTTP 500 against /api/resources/.../<filename> (no trailing slash)Hit the backend's single-file List quirk directly (e.g. via curl)Don't bypass the CLI; the CLI uses parent-listing Stat for a reason
... is a directory: pass -r/-R to copy/move it recursivelycp / mv on a path with trailing / (or any directory) without -rAdd -r after confirming the user wants the whole tree
target ... must end with '/' when more than one source is givencp src1 src2 dst with dst not ending in /Add a trailing / to <dst> (drop-into-dir) or split into separate single-source cp calls
source and destination are the samecp foo foo (typo)Pick a real destination
destination ... is inside source ... (would create a cycle)cp -r drive/Home/a/ drive/Home/a/sub/Pick a destination outside the source tree
refusing to copy/move the root of <fileType>/<extend>cp drive/Home/ ...Volume-root copy/move is unsupported; specify a child path
paste <src> → <dst>: <message> (HTTP 200, code -1)Server-side rejection — typically a literal backslash in the pathFix the path; don't retry verbatim
cannot resolve {node} URL segmentNeither side has an External/Cache hint and /api/nodes/ returned no usable defaultPass --node <name> explicitly
queued N copy/move task(s): ... but the file isn't visible yetTask is queued; backend hasn't processed itWait briefly and files ls the destination; the CLI does not currently poll task completion
<new-name> contains '/' / ... contains '\\'rename was given a path-like new namerename only changes the basename; use mv for cross-directory moves
<new-name> is "." / is ".." / is emptyrename was given a sentinel basenamePick a real basename
<new-name> equals the current basenamerename was a no-opIf the user intended a cross-directory move, use mv
refusing to rename the root of <fileType>/<extend>rename drive/Home/ ...Volume roots can't be renamed; pick a child path
... server reported a conflict (HTTP 409) (rename)A sibling with <new-name> already exists under the same parentPick a different name or rm the existing sibling first
Public shares require an expirationshare public without --expire-days or --expire-timePass exactly one expiration flag (Public-link shares need a TTL)
--expire-days and --expire-time are mutually exclusiveBoth flags passed togetherPick one
--password must be at least 6 charactersshare public --password shorter than 6 charsUse a longer password (or omit --password to auto-generate one)
--public and --users are mutually exclusiveshare smb --public --users ...Pick one recipient model — Public-SMB OR a specific account list
share smb requires either --public OR --usersNeither flag set on share smbAdd --public or --users id:perm,...
entry "...": SMB shares accept only view or editshare smb --users id:upload or :adminSMB perm is binary; use view or edit
share <id> created, but adding members failed: ...share internal --users post-create call failedThe share record IS on the server (id is in the message); fix the underlying error and re-run share internal with the same --users, or call member-add directly through the LarePass app
share ... not found on the server (share get)Share id is wrong / already removedList shares to confirm: files share list
share ... server reported a conflict (HTTP 409) (share)Resource is already shared, or the share id is in useUse share list to find the existing share id; share rm it first if you intended to recreate
unknown repos type "..." (files repos list --type ...)Misspelled --type valueUse mine, share-to-me, shared, or all
repos Create: empty repo namefiles repos create "" (or whitespace-only)Pass a non-empty <name> argument
repos Create: server accepted the request but did not return a repo_idRare Seahub path that 200s but elides the response payloadRun files repos list to discover the new repo; the create likely succeeded
repos Create: server rejected (code N): ...Server-side validation failed (duplicate name, encryption-only deployment, etc.)Surface the message verbatim — it's the Seahub-side reason
repos Rename ...: server rejected (code N): ...Permission denied / target name conflict on renameRead the embedded message; if it's a name conflict, pick a different <new_name>
repo <id>: not found in any of mine / share-to-me / shared (repos get)Wrong / removed repo id, or it's an encrypted repo the caller has no access toRun files repos list --type all to confirm
repos rm: refusing to delete without -y / --yes (no TTY)Running in CI / pipe / heredoc without a confirmation opt-outAdd -y, --yes, -f, or --force (or run from a real TTY to get the y/N prompt)
repos rm: N of M failed: ...One or more ids hit per-id errors mid-batchEach failed: <id> (<reason>) line above identifies which one; fix or retry that subset

Typical workflow

# 1. Explore
olares-cli files ls drive/Home/
olares-cli files ls drive/Home/Documents/

# 2. Push a local tree up (target dir must already exist).
olares-cli files upload ~/local-dir drive/Home/Documents/

# 3. Pull a tree down with parallelism + resume.
olares-cli files download drive/Home/Documents/ ./out --parallel 4 --resume

# 4. Quick peek at a file.
olares-cli files cat drive/Home/Notes/today.md | tail -n 50

# 5. Reorganize on the server side (no local round-trip).
olares-cli files cp drive/Home/Documents/2026-Q1.pdf drive/Home/Archive/
olares-cli files mv drive/Home/Inbox/draft.md drive/Home/Notes/2026-04-draft.md
olares-cli files mv -r drive/Home/scratch/ drive/Home/Old/scratch/

# 5b. In-place basename change (synchronous, no task queue).
olares-cli files rename drive/Home/Documents/notes.md 2026-Q1-notes.md

# 6. Clean up after confirming with `ls` first.
olares-cli files ls drive/Home/Old/
olares-cli files rm -r drive/Home/Old/

# 7. Sharing workflows (orthogonal to the upload / move / cleanup loop).
olares-cli files share internal drive/Home/Reports/Q1.pdf \
    --users alice:edit,bob:view
olares-cli files share public drive/Home/Photos/ --expire-days 7
olares-cli files share smb drive/Home/Movies/ --public

# Inspect / clean up.
olares-cli files share list --shared-by-me
olares-cli files share rm <share-id>

# 8. Sync (Seafile) library lifecycle (orthogonal to drive/Home).
olares-cli files repos list                                  # discover repo_ids
REPO_ID=$(olares-cli files repos create "Project Alpha" --json | jq -r .repo_id)
olares-cli files upload ~/local-tree sync/$REPO_ID/Backups/  # use it like any other path
olares-cli files repos rename $REPO_ID "Project Alpha (archived)"
olares-cli files repos rm $REPO_ID --yes

When operating across multiple Olares instances, prefix each command with --profile <olaresId> (see olares-shared for the global flag) instead of flipping the persistent current pointer.

Security rules

  • Always preview destructive operations. Before passing -f to rm in a script, list the exact paths to the user and get explicit confirmation. The interactive [y/N] prompt is a safety net, not a substitute for thoughtful intent.
  • mv is destructive too. It has no [y/N] prompt (the wire endpoint is async / fire-and-forget), so always show the user the exact <src> → <dst> plan before running it on directories or in scripts. When in doubt, prefer cp + ls + rm so the user can verify the new copy before deleting the original.
  • Local files are never overwritten implicitly. files download refuses to clobber unless --overwrite (atomic via .tmp+rename) or --resume (append) is passed. Never recommend --overwrite without checking with the user.
  • Server-side overwrite on cp / mv is the backend's call, not the CLI's. The PATCH /api/paste endpoint does not expose an "overwrite vs auto-rename" flag the way the LarePass web app's modal dialog does; the server picks its own collision behavior, which historically matches the POST-mkdir auto-rename quirk (creates <name> (1) instead of overwriting). Treat cp dst/ and mv dst/ as non-idempotent and confirm the destination is empty / non-conflicting before running.
  • Do not echo <access_token> to the terminal. The token lives in the OS keychain for a reason; pulling it out into a shell variable for curl defeats that. Use the CLI commands.
  • files upload does NOT delete the local source — it's a copy, not a move. If a user wants delete-after-upload semantics, they have to do it explicitly and after verifying the upload succeeded.
  • files share public passwords are surfaced ONCE. The CLI prints the (auto- or user-supplied) password in the create-output; the server does NOT echo it back on share get / share list. When you script share public, capture the create-output before piping anywhere it might be lost — the only recovery path is share rm + recreate, which gives the recipient a new id.
  • SMB-share secrets (smb_user, smb_password) are returned by the create call too — same capture-once discipline as Public-link passwords. They are queryable later via share get <id>, but treat the create-time output as the canonical record.
  • share rm removes the share record only, not the underlying resource. A user who expects "stop sharing AND delete this folder" needs share rm <id> followed by files rm -r <path>. Confirm intent before chaining the two.
  • share public --password "" is rejected (the CLI minimum is 6 chars). Don't try to bypass it by passing a single character — the wire-side check exists, you'll get an opaque server rejection a few seconds later.
  • files repos rm is irreversible from the CLI. There is no restore verb; the Seafile-side trash window (if the deployment has one) is reachable only through the LarePass app or direct Seahub access. On a TTY the command lists id + name and asks y/N; in scripts, require -y after explicit user intent. Prefer repos rename "<old> (archived)" for "soft delete" workflows.
  • files repos create cannot make encrypted libraries. Don't suggest a password flag — the CLI will refuse, and the per-user files-backend has no endpoint for it. Encrypted libraries must be created from the LarePass app; the CLI can only enumerate / rename / delete them after the fact.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

wechat-publish-pro

Pure Python tool to convert Markdown to styled HTML and publish articles to WeChat official account drafts with AI-based content refinement and theme support.

Registry SourceRecently Updated
Coding

Miaoji Asin Clinic Pro

亚马逊ASIN诊所Pro版,90天行动计划+竞品对标+季节性优化日历。 从合规度、广告度、评论度、视觉度、内容度五维升级为可执行的长期作战方案。 基础功能可使用 miaoji-asin-clinic 免费版。

Registry SourceRecently Updated
Coding

Miaoji Asin Clinic

基于ASIN和品类,快速诊断亚马逊Listing五维健康指数并智能排序修复优先级,提供详细分析与个性化修复方案。

Registry SourceRecently Updated
Coding

Toonany

A Claude Code skill for creating AI-generated short dramas (漫剧) from novels and stories. Use when user mentions "漫剧创作", "小说转剧本", "分镜生成", "短剧制作", "故事线生成", "大纲...

Registry SourceRecently Updated