Skip to content

feat: smart default download path + stdin/stdout support#222

Merged
jason-rl merged 8 commits into
mainfrom
jason/object-download-smart-path
May 4, 2026
Merged

feat: smart default download path + stdin/stdout support#222
jason-rl merged 8 commits into
mainfrom
jason/object-download-smart-path

Conversation

@jason-rl
Copy link
Copy Markdown
Contributor

@jason-rl jason-rl commented Apr 24, 2026

Summary

  • Optional download path: object download <id> [path] — when path is omitted, auto-generates from object name + content_type with smart extension inference. Use - to write to stdout.
  • Stdin upload: object upload - --name <name> --content-type <type> reads from stdin. Also auto-detected when stdin is piped with no paths (e.g. echo data | rli obj upload --name foo --content-type text). Zero-byte pipes upload an empty object rather than printing the URL.
  • URL-only upload: object upload --name <name> with no paths and no piped stdin creates the object and prints the pre-signed upload URL to stdout, enabling external upload workflows (e.g. curl -X PUT -T file "$(rli object upload --name foo)")
  • processUtils async I/O: stdout.write/stderr.write accept string | Buffer (was string only), and stdin is now AsyncIterable<Buffer> — upload and download go through the mockable abstraction instead of process globals directly
  • TUI improvement: Default download path in both list and detail TUI screens now infers extensions from content_type

Extension inference rules (case-insensitive)

Content type No suffix Mismatched suffix Special
text .txt no change
binary no change no change
gzip .gz → append .gz .tar.tgz
tar .tar → append .tar
tgz .tgz → append .tgz

This is the complement of adjustFileExtension() from runloop-fe PR #1714 — that strips extensions for mount paths (post-decompression), this adds extensions for download paths.

Test plan

  • 40 unit tests for inferDownloadExtension and getDefaultDownloadPath pass
  • 4 unit tests for 0-paths URL-only upload mode pass
  • 9 unit tests for stdin upload (explicit - path and piped stdin auto-detection, including 0-byte pipes)
  • 7 unit tests for download command (file path, auto-resolve, stdout, binary TTY warning, structured stderr output)
  • rl object download <id> without path → verify auto-generated filename has correct extension
  • rl object download <id> custom.out → verify explicit path still works
  • rl object download <id> - → verify data goes to stdout; verify TTY warning for binary types
  • echo "hello" | rl object upload --name test --content-type text → verify upload from stdin (no - needed)
  • echo -n '' | rl object upload --name test --content-type text → verify 0-byte upload (not URL mode)
  • rl object upload - --name test --content-type text → verify explicit stdin upload
  • rl object upload --name test-url --content-type text → verify prints upload URL (no pipe)
  • TUI: select download on objects with various content types → verify pre-filled path has correct extension

🤖 Generated with Claude Code

jason-rl and others added 2 commits April 24, 2026 10:42
Add shared utilities for inferring file extensions from object content
types when generating default download paths. This is the complement of
adjustFileExtension() in runloop-fe (PR #1714), which strips extensions
for mount paths — this module adds extensions for download paths.

Rules:
- text/binary with no suffix → .txt/.bin
- gzip + .tar suffix → .tgz (not .tar.gz)
- gzip/tar/tgz with mismatched suffix → append correct extension
- All suffix checks are case-insensitive

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change `object download <id> <path>` to `object download <id> [path]`
- When path is omitted, auto-resolve using object name/content_type
  via the new getDefaultDownloadPath utility
- When path is `-`, write downloaded data to stdout
- Warn on stderr when writing binary content types to a TTY
- Structured output (-o json) goes to stderr when using stdout mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jason-rl jason-rl force-pushed the jason/object-download-smart-path branch from cc1315c to a76fc90 Compare April 24, 2026 17:45
jason-rl and others added 3 commits April 24, 2026 10:54
- When path is `-`, read upload data from stdin
- Require --name and --content-type for stdin uploads since they
  cannot be inferred from a filename
- Update command description to document stdin support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update both object list and detail TUI screens to use
getDefaultDownloadPath, which infers file extensions from
the object's content_type when pre-filling the download path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-generated by docs:commands pre-push hook.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jason-rl jason-rl force-pushed the jason/object-download-smart-path branch from a76fc90 to 633528d Compare April 24, 2026 17:55
@jason-rl jason-rl marked this pull request as ready for review April 24, 2026 17:56
@jason-rl jason-rl requested review from dines-rl and tode-rl April 24, 2026 17:56
jason-rl and others added 2 commits April 24, 2026 11:47
Binary is too broad a category to infer a meaningful extension.
Files with binary content type now keep their name unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When `rli object upload --name <name>` is called without any file paths,
create the object via the API and print the pre-signed upload URL to
stdout. This enables external upload workflows (e.g. curl) matching the
frontend's "Copy URL and Close" pattern. The object stays in UPLOADING
state until the user uploads and completes externally.

- Change `<paths...>` to `[paths...]` (Commander optional variadic)
- Skip interactive screen buffer for 0-paths mode
- Default content type to "unspecified" when omitted
- Add null guard for upload_url from API response

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dines-rl
Copy link
Copy Markdown
Contributor

path - as a signifier is probably not a user friendly way to allow download to std out, maybe --stdout or something

@dines-rl
Copy link
Copy Markdown
Contributor

Stdin support: object upload - --name --content-type reads from stdin (cannot be mixed with other paths)

this can be automatic with cat text.txt | object upload --name x same way password managers do cat password.txt | pulumi secret set

@dines-rl
Copy link
Copy Markdown
Contributor

URL-only upload: object upload --name with no paths creates the object and prints the pre-signed upload URL to stdout, enabling external upload workflows (e.g. curl -X PUT -T file "$(rli object upload --name foo)")

i dont understnad this

@jason-rl
Copy link
Copy Markdown
Contributor Author

jason-rl commented Apr 30, 2026

path - as a signifier is probably not a user friendly way to allow download to std out, maybe --stdout or something

the - is already a precedent with many coreutils/tools. Although instead of a stderr warning if the content is binary, I can make it an error (even in the text case where the content is just generally very long in terms of lines (including wrapping from terminal width)).

Using a single hyphen (-) as a path argument is a long-standing Unix convention that tells a tool to use standard input (stdin) or standard output (stdout) instead of a physical file on disk. This is particularly useful for "piping" data between commands without creating temporary files.

Here are several prominent open-source tools that implement this convention:

  1. tar (Tape Archiver)

The tar utility uses -f - to indicate that the archive itself should be read from or written to the standard streams.

Write to stdout: tar -cvf - /path/to/dir > archive.tar (Creates a tarball and sends it to stdout).

Read from stdin: cat archive.tar | tar -xvf - (Unpacks a tarball arriving via a pipe).
  1. ffmpeg (Multimedia Framework)

ffmpeg uses the hyphen to handle raw media streams in a pipeline.

Example: ffmpeg -i input.mp4 -f mpegts - | some_other_tool

In this case, the final - tells ffmpeg to output the muxed data to stdout. For input, you can use -i -.
  1. curl (Data Transfer)

While curl writes to stdout by default, the -o (output) and -T (upload) flags can explicitly use the hyphen.

Upload from stdin: echo "hello" | curl -T - [http://example.com/upload](http://example.com/upload)

Write to stdout: curl [http://example.com](http://example.com) -o - (Redundant but functional).
  1. diff (File Comparison)

diff can compare a local file against data coming from a pipe by using -.

Example: ls /dir1 | diff - /dir2_contents.txt

This compares the output of ls (stdin) against the text file provided as the second argument.
  1. pandoc (Document Converter)

pandoc is the "Swiss Army knife" of document conversion. If you don't provide an input file, it defaults to stdin, but you can be explicit.

Example: echo "# Hello" | pandoc -f markdown -t html -o -

This reads the string from stdin and prints the HTML conversion directly to stdout.
  1. jq (JSON Processor)

jq uses the hyphen to explicitly define where the JSON data is coming from when multiple arguments might confuse the parser.

Example: cat data.json | jq . -

Stdin support: object upload - --name --content-type reads from stdin (cannot be mixed with other paths)

this can be automatic with cat text.txt | object upload --name x same way password managers do cat password.txt | pulumi secret set

good idea actually (will change so that it won't print the URL in this case and actually work)!

URL-only upload: object upload --name with no paths creates the object and prints the pre-signed upload URL to stdout, enabling external upload workflows (e.g. curl -X PUT -T file "$(rli object upload --name foo)")

i dont understnad this

in our website, when you create an object, it doesn't ask you for the file to upload only until you press the create button. Then it gives you a pre-signed upload URL, or you can upload the object instead. rl-cli doesn't support pre-signed upload URL, so I figured that not specifying a input file to upload means one wants to do the upload URL way

When no paths are provided and stdin is a pipe (not a terminal), upload
now reads from piped stdin instead of printing the pre-signed URL. This
enables `echo data | rli obj upload --name foo --content-type text`
without explicitly passing `-`. Zero-byte pipes are handled correctly.

Extend processUtils with Buffer support on stdout/stderr.write and
AsyncIterable on stdin, so upload and download go through the mockable
abstraction instead of process globals directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jason-rl jason-rl merged commit 419a961 into main May 4, 2026
17 checks passed
@jason-rl jason-rl deleted the jason/object-download-smart-path branch May 4, 2026 22:40
tode-rl pushed a commit that referenced this pull request May 12, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.17.0](v1.16.0...v1.17.0)
(2026-05-12)


### Features

* add --public flag to agent create, fix object upload --public
([#219](#219))
([6e7a8b3](6e7a8b3))
* add clipboard keybinds to detail screens
([#231](#231))
([83874ca](83874ca))
* add TUI features and fix benchmark pagination total count
([#230](#230))
([7565d45](7565d45))
* agent object picker, multi-mount support, and TUI improvements
([#217](#217))
([dbe2a5c](dbe2a5c))
* pty support ([#234](#234))
([3cfd720](3cfd720))
* smart default download path + stdin/stdout support
([#222](#222))
([419a961](419a961))
* support multi-path tar/tgz archive creation in obj upload
([#220](#220))
([3528701](3528701))


### Bug Fixes

* menu header clipping and breadcrumb hyperlink
([#221](#221))
([3ef6271](3ef6271))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants