Problem
pos3 is Python-only today. Using it from a shell — peeking at a prefix, grabbing a dataset into the cache, pushing a directory back — requires writing a script. We want these to be one-liners.
Deliverables
A pos3 console-script entry point with three subcommands: ls, download, upload. After this lands, uv run pos3 ... works.
pos3 ls <s3-url> [-r/--recursive] [--profile NAME]
Lists objects under the prefix. One full s3://... URL per line on stdout.
pos3 download <s3-url> [--local PATH] [--profile NAME] [--delete] [--exclude PATTERN]...
Downloads the prefix or object. Prints the resulting local path to stdout (and nothing else); progress and logs go to stderr. This must work:
data_dir=$(uv run pos3 download s3://bucket/dataset/)
pos3 upload <s3-url> [--local PATH] [--profile NAME] [--delete] [--exclude PATTERN]...
One-shot upload (no background loop, no interval). Source defaults to the cache path corresponding to <s3-url> — same path pos3 download would have produced. --local overrides. Errors if the source doesn't exist.
Decisions
- No
sync and no background/interval mode in v1. Those bind to a mirror() lifetime that doesn't fit one-shot invocations.
--delete defaults to OFF for both download and upload, even though the Python API defaults to delete=True. CLI defaults should be conservative for interactive use.
ls prints full S3 URLs, matching the Python API and keeping output pipe-friendly.
--profile is supported alongside the existing URL form s3://profile@bucket/.... URL form wins on conflict (matches existing Python precedence).
Out of scope (possible follow-ups)
pos3 run -- cmd args wrapper covering the long-running-job case.
pos3 sync, pos3 profiles list, ls -l with sizes.
Problem
pos3is Python-only today. Using it from a shell — peeking at a prefix, grabbing a dataset into the cache, pushing a directory back — requires writing a script. We want these to be one-liners.Deliverables
A
pos3console-script entry point with three subcommands:ls,download,upload. After this lands,uv run pos3 ...works.pos3 ls <s3-url> [-r/--recursive] [--profile NAME]Lists objects under the prefix. One full
s3://...URL per line on stdout.pos3 download <s3-url> [--local PATH] [--profile NAME] [--delete] [--exclude PATTERN]...Downloads the prefix or object. Prints the resulting local path to stdout (and nothing else); progress and logs go to stderr. This must work:
data_dir=$(uv run pos3 download s3://bucket/dataset/)pos3 upload <s3-url> [--local PATH] [--profile NAME] [--delete] [--exclude PATTERN]...One-shot upload (no background loop, no interval). Source defaults to the cache path corresponding to
<s3-url>— same pathpos3 downloadwould have produced.--localoverrides. Errors if the source doesn't exist.Decisions
syncand no background/interval mode in v1. Those bind to amirror()lifetime that doesn't fit one-shot invocations.--deletedefaults to OFF for bothdownloadandupload, even though the Python API defaults todelete=True. CLI defaults should be conservative for interactive use.lsprints full S3 URLs, matching the Python API and keeping output pipe-friendly.--profileis supported alongside the existing URL forms3://profile@bucket/.... URL form wins on conflict (matches existing Python precedence).Out of scope (possible follow-ups)
pos3 run -- cmd argswrapper covering the long-running-job case.pos3 sync,pos3 profiles list,ls -lwith sizes.