Skip to content

Comments

Add blob.download command and attachment metadata to email.read#12

Merged
turian merged 3 commits intomainfrom
feature/blob-download
Feb 10, 2026
Merged

Add blob.download command and attachment metadata to email.read#12
turian merged 3 commits intomainfrom
feature/blob-download

Conversation

@turian
Copy link
Member

@turian turian commented Feb 10, 2026

Summary

  • blob.download: New command that downloads a blob to a local file path using the JMAP session downloadUrl URI template (RFC 8620 §6.2). Returns a JSON envelope with metadata — never writes binary to stdout, preserving the CLI's "every command returns a JSON envelope" convention.
  • email.read attachments: Now includes an attachments array with partId, blobId, name, type, and size for each attachment, enabling a clean two-step agent workflow.

Agent workflow example

# Step 1: Read email, discover attachments
fastmail-cli email.read --id StqohozpQSO7 --json pretty
# → data.attachments[0] = {blobId: "G70a...", name: "Invoice.pdf", type: "application/pdf", size: 29174}

# Step 2: Download attachment
fastmail-cli blob.download \
  --blob-id "G70a0de1bb1905db4c4b6295951910d78877bf722" \
  --name "Invoice-99F05531-0017.pdf" \
  --type "application/pdf" \
  --output "./invoice.pdf"
# → {ok: true, data: {bytesWritten: 29174, sha256: "d4d2...", output: "./invoice.pdf"}}

blob.download envelope fields

Field Description
blobId The blob ID that was downloaded
name Filename passed to the server
contentType MIME type passed to the server
output Local file path written to
bytesWritten Number of bytes written
sha256 SHA-256 hex digest of the downloaded content

Test plan

Tested against live Fastmail API:

  • email.query --filter '{"hasAttachment": true}' to find email with attachment
  • email.read --id <id> returns attachments array with blobId, name, type, size, partId
  • email.read on email without attachments returns attachments: []
  • blob.download with blobId/name/type/output downloads file successfully
  • Downloaded file verified as valid PDF (file command), correct size (29174 bytes matches attachment metadata)
  • JSON envelope includes sha256 hash and bytesWritten
  • API token redacted in envelope args
  • describe blob.download shows all four required options
  • help lists blob.download with correct summary

🤖 Generated with Claude Code

turian and others added 2 commits February 10, 2026 04:09
Add blob.download command that downloads a blob to a local file using the
JMAP session downloadUrl template (RFC 8620 Section 6.2). Returns a JSON
envelope with blobId, name, contentType, output path, bytesWritten, and
sha256 hash. File-only output keeps the JSON envelope convention intact.

Extend email.read to include attachment metadata (partId, blobId, name,
type, size) so agents can discover attachments and download them in a
clean two-step workflow: email.read → blob.download.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skips 0.2.2 which had a broken tag (pyproject.toml wasn't bumped before
tagging, so CI failed to publish to PyPI with "File already exists").

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

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e0f04a9e32

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 728 to 733
blob_url = download_url_template.format(
accountId=account_id,
blobId=args.blob_id,
name=args.name,
type=args.type,
)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Expand downloadUrl variables with URI encoding

downloadUrl is a URI template, but this code uses plain str.format(...) with raw blob_id/name/type values. When an attachment name contains reserved characters (for example #, /, ?, or non-ASCII text), the generated URL is malformed or semantically different (# becomes a fragment, / changes path segments), so blob.download can fail or fetch the wrong resource for valid inputs. Use proper URI-template expansion (or percent-encode each variable according to RFC6570 rules) before issuing the request.

Useful? React with 👍 / 👎.

Replace raw str.format() on the JMAP downloadUrl URI template with
expand_download_url() that percent-encodes each variable (accountId,
blobId, name, type) using urllib.parse.quote(safe=""). Without this,
attachment names containing reserved characters (#, ?, /, spaces) or
non-ASCII text would produce malformed URLs.

Centralizes the logic so any future use of downloadUrl goes through
the same safe expansion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@turian
Copy link
Member Author

turian commented Feb 10, 2026

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Delightful!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@turian turian merged commit b43a4d1 into main Feb 10, 2026
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.

1 participant