Skill flagged — suspicious patterns detected

ClawHub Security flagged this skill as suspicious. Review the scan results before using.

Olares Files (olares-cli files)

v1.4.0

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 / g...

0· 34· 1 versions· 0 current· 0 all-time· Updated 4h ago· MIT-0
byPeng Peng@pengpeng

Install

openclaw skills install 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.

Version tags

latestvk972jn1typmapjc663t0874zws85tb6z