Skip to content

feat: add attachment upload, list, and download commands#6

Open
illumitry-mf wants to merge 2 commits intoXeroAPI:mainfrom
illumitry-mf:feature/attachments
Open

feat: add attachment upload, list, and download commands#6
illumitry-mf wants to merge 2 commits intoXeroAPI:mainfrom
illumitry-mf:feature/attachments

Conversation

@illumitry-mf
Copy link
Copy Markdown

@illumitry-mf illumitry-mf commented Apr 14, 2026

Summary

Adds attachments sub-commands to the seven resource types that support file attachments via the Xero Accounting API: invoices, credit-notes, bank-transactions, quotes, contacts, accounts, and manual-journals.

Commands added

Each resource gains three new sub-commands following the existing topic/verb pattern:

# Upload a file to a resource
xero invoices attachments upload --invoice-id <ID> --file <path>
xero invoices attachments upload --invoice-id <ID> --file <path> --include-online

# List attachments on a resource
xero invoices attachments list --invoice-id <ID>

# Download an attachment to disk
xero invoices attachments download --invoice-id <ID> --attachment-id <ID>
xero invoices attachments download --invoice-id <ID> --attachment-id <ID> --output ./downloads/

The same three commands are available for credit-notes, bank-transactions, quotes, contacts, accounts, and manual-journals with their respective ID flags.

--include-online is available on invoices and credit-notes upload — makes the attachment visible to the end customer in their online invoice/credit note view.

Note: The Xero Accounting API does not support deleting attachments, so no delete command is included.

Architecture

All API logic lives in a new shared lib src/lib/attachments.ts:

  • Dispatch map routes each AttachmentResource type to the correct xero-node SDK method (createInvoiceAttachmentByFileName, getInvoiceAttachments, getInvoiceAttachmentById, etc.)
  • Pre-flight validation before any API call: file-exists check, 25MB size limit, supported MIME type check
  • Built-in MIME detection for .pdf, .png, .jpg/.jpeg, .gif, .webp, .xml, .csv, .txt — no new npm dependencies
  • Download resolves the original filename via a listAttachments call internally, enabling smart --output handling (accepts a file path or directory; defaults to current working directory)

Command files are thin wrappers: parse flags → this.xeroCall → delegate to lib → format output.

Test plan

  • 21 new unit tests in test/lib/attachments.test.ts covering MIME detection, file validation (not-found, oversized), uploadAttachment dispatch per resource, listAttachments, and downloadAttachment (including attachment-not-found error)
  • All 87 tests pass (npm test)
  • TypeScript build is clean (npm run build)
  • Smoke tested against live Xero API: invoices attachments list, upload, and download all work end-to-end

🤖 Generated with Claude Code

illumitry-mf and others added 2 commits April 14, 2026 23:18
Adds `attachments` sub-commands to the seven resource types that support
file attachments via the Xero Accounting API: invoices, credit-notes,
bank-transactions, quotes, contacts, accounts, and manual-journals.

## Commands added

Each resource gains three new sub-commands:

  xero <resource> attachments upload   --<resource>-id <ID> --file <path>
  xero <resource> attachments list     --<resource>-id <ID>
  xero <resource> attachments download --<resource>-id <ID> --attachment-id <ID> [--output <path>]

For invoices and credit notes, `upload` also accepts `--include-online` to
make the attachment visible to the end customer in their online invoice view.

Note: the Xero API does not support deleting attachments.

## Architecture

All API logic lives in a single shared lib (`src/lib/attachments.ts`) with:
- A dispatch map routing each resource type to the correct xero-node SDK method
- Pre-flight validation: file-exists check, 25MB size limit, MIME type check
- Built-in MIME detection for .pdf, .png, .jpg/.jpeg, .gif, .webp, .xml, .csv, .txt
  (no new dependencies required)
- Download resolves the original filename via a list call before fetching bytes,
  enabling smart `--output` path handling (file path or directory)

Command files are thin wrappers: parse flags → xeroCall → delegate to lib → format output.

## Tests

21 new unit tests in `test/lib/attachments.test.ts` covering MIME detection,
file validation, and all three operations across multiple resource types.
All 87 tests pass; TypeScript build is clean.
Add bin/bun-run.ts as a Bun-specific entry point that statically imports
all command classes so they are bundled into the compiled binary. After
loading the oclif Config, each command's lazy loader is patched to return
the in-binary class instead of doing a dynamic filesystem import. This
fixes the @oclif/core MODULE_NOT_FOUND error that occurs in containers
where node_modules is absent but the binary is present alongside dist/.

Also adds npm scripts build:binary and build:binary:linux, updates
.gitignore to exclude compiled binaries, and includes the updated
skills/xero-command-line/SKILL.md with env-var auth bypass docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.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.

1 participant