From 09422f46535698a99f004b5699748da4e2f1cc3d Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 14 Jan 2026 23:16:38 +0100 Subject: [PATCH] Add AGENTS.md for coding agent integration Comprehensive guide for AI coding assistants to efficiently use the Binect-JS library. Includes quick start, API methods, status codes, helper functions, polling patterns, error handling, and complete examples. Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..02986d5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,295 @@ +# AGENTS.md - Binect-JS Library Usage Guide + +This file helps coding agents (Claude, Cursor, Copilot, etc.) efficiently integrate and use the Binect-JS library for sending physical mail via PDF. + +## What This Library Does + +Binect-JS is a TypeScript/JavaScript SDK for the [Binect API](https://app.binect.de) that enables sending PDF documents as physical letters via Deutsche Post. Upload a PDF, the service extracts the recipient address, prints it, and mails it. + +## Installation + +The library is not yet published to npm. Reference it locally: + +```json +// package.json +{ + "dependencies": { + "@binect/js": "file:../path/to/binect-js" + } +} +``` + +Or link it: +```bash +cd /path/to/binect-js && npm link +cd /your/project && npm link @binect/js +``` + +## Quick Start + +```typescript +import { BinectClient, DocumentStatus, isShippable } from '@binect/js'; +import { readFileSync } from 'fs'; + +const client = new BinectClient({ + username: 'your@email.com', + password: 'your-password' +}); + +// Upload a PDF +const pdfContent = readFileSync('letter.pdf').toString('base64'); +const doc = await client.documents.upload({ + content: pdfContent, + filename: 'letter.pdf', + color: false, + simplex: false, // false = duplex (double-sided) + envelope: 'DINLANG', + franking: 'STANDARD_FRANKING' +}); + +console.log(`Document ${doc.id} status: ${doc.status.code}`); +``` + +## Core API Methods + +### Documents (`client.documents`) + +```typescript +// Upload PDF (base64 encoded) +const doc = await client.documents.upload({ + content: base64String, + filename: 'letter.pdf', + color: false, + simplex: false, + envelope: 'DINLANG', // or 'C4' + franking: 'STANDARD_FRANKING' +}); + +// Get document by ID +const doc = await client.documents.get(documentId); + +// List shippable documents +const list = await client.documents.list({ limit: 10, offset: 0 }); + +// Delete document +await client.documents.delete(documentId); + +// Get PDF preview +const response = await client.documents.getPdf(documentId); +const pdfBlob = await response.blob(); +``` + +### Sendings (`client.sendings`) + +```typescript +// Send a document (triggers physical mailing) +const sending = await client.sendings.send(documentId); + +// Cancel a sending (only works if status is PRODUCTION_QUEUE or PRINTING) +const result = await client.sendings.cancel(documentId); + +// Send multiple documents at once +const sendings = await client.sendings.announce([docId1, docId2]); + +// Cancel multiple +const results = await client.sendings.cancelMultiple([docId1, docId2]); +``` + +### Accounts (`client.accounts`) + +```typescript +// Get account balance +const account = await client.accounts.get(); +console.log(`Balance: ${account.credit} ${account.unit}`); // e.g., "401 EUROCENT" + +// Get personal data +const personal = await client.accounts.getPersonalData(); +``` + +### Attachments (`client.attachments`) + +```typescript +// Upload reusable attachment +const attachment = await client.attachments.upload({ + content: base64PdfContent, + name: 'terms-and-conditions.pdf' +}); + +// Add attachment to document +await client.documents.addAttachment(documentId, attachment.attachmentId); +``` + +## Document Status Codes + +```typescript +import { DocumentStatus } from '@binect/js'; + +DocumentStatus.IN_PREPARATION // 1 - Being validated +DocumentStatus.SHIPPABLE // 2 - Ready to send +DocumentStatus.PRODUCTION_QUEUE // 3 - Queued for printing +DocumentStatus.PRINTING // 4 - Currently printing +DocumentStatus.SENT // 5 - Mailed +DocumentStatus.CANCELED // 6 - Canceled +DocumentStatus.ERRONEOUS // 7 - Has errors +``` + +## Helper Functions + +```typescript +import { + isShippable, + isErroneous, + isCancelable, + isTerminal, + hasErrors, + getErrors, + pollUntil, + waitForShippable, + bufferToBase64, + fileToBase64 +} from '@binect/js'; + +// Status checks +if (isShippable(doc)) { /* ready to send */ } +if (isErroneous(doc)) { /* check errors */ } +if (isCancelable(doc)) { /* can still cancel */ } +if (isTerminal(doc)) { /* final state: sent/canceled/error */ } + +// Validation +if (hasErrors(doc)) { + const errors = getErrors(doc); + errors.forEach(e => console.log(e.message)); +} + +// Base64 encoding +const base64 = bufferToBase64(fs.readFileSync('letter.pdf')); // Node.js +const base64 = await fileToBase64(fileInput.files[0]); // Browser +``` + +## Polling for Status Changes + +```typescript +import { pollUntil, isShippable, isErroneous } from '@binect/js'; + +// Poll until document is ready or has errors +const doc = await pollUntil( + () => client.documents.get(documentId), + (doc) => isShippable(doc) || isErroneous(doc), + { intervalMs: 2000, maxAttempts: 30 } +); + +// Or use the convenience helper +import { waitForShippable } from '@binect/js'; +const doc = await waitForShippable( + () => client.documents.get(documentId) +); +``` + +## Error Handling + +```typescript +import { BinectApiError, BinectAuthError } from '@binect/js'; + +try { + await client.documents.upload({ ... }); +} catch (error) { + if (error instanceof BinectAuthError) { + // 401 - Invalid credentials + console.error('Authentication failed'); + } else if (error instanceof BinectApiError) { + // Other API errors (400, 403, 404, 500, etc.) + console.error(`API Error: ${error.message}`); + console.error(`Status: ${error.status}`); + console.error(`Endpoint: ${error.endpoint}`); + // Full details + console.error(error.toDetailedString()); + } +} +``` + +## Complete Send-and-Cancel Example + +```typescript +import { BinectClient, DocumentStatus, pollUntil } from '@binect/js'; +import { readFileSync } from 'fs'; + +const client = new BinectClient({ + username: process.env.BINECT_USERNAME!, + password: process.env.BINECT_PASSWORD! +}); + +// 1. Upload +const pdfContent = readFileSync('letter.pdf').toString('base64'); +const doc = await client.documents.upload({ + content: pdfContent, + filename: 'letter.pdf', + envelope: 'DINLANG', + franking: 'STANDARD_FRANKING' +}); +const docId = String(doc.id); + +// 2. Send +await client.sendings.send(docId); + +// 3. Wait for production queue +const sentDoc = await pollUntil( + () => client.documents.get(docId), + (d) => d.status.code !== DocumentStatus.IN_PREPARATION, + { intervalMs: 1000, maxAttempts: 10 } +); + +// 4. Cancel if still possible +if (sentDoc.status.code === DocumentStatus.PRODUCTION_QUEUE || + sentDoc.status.code === DocumentStatus.PRINTING) { + await client.sendings.cancel(docId); +} + +// 5. Cleanup +await client.documents.delete(docId); +``` + +## Type Imports + +```typescript +// All types are exported +import type { + Document, + DocumentUploadOptions, + Sending, + AccountInfo, + ValidationMessage, + PriceInfo, + ListResponse, + BinectClientConfig +} from '@binect/js'; +``` + +## Key Constraints + +1. **PDF must have recipient address** in DIN 5008 format (address window position) +2. **Max file size**: 12 MB +3. **Authentication**: HTTP Basic Auth (credentials not stored/cached) +4. **No retries**: Network failures throw immediately (no automatic retry) +5. **Cancel window**: Can only cancel while status is PRODUCTION_QUEUE (3) or PRINTING (4) + +## Environment Variables Pattern + +```typescript +// Recommended pattern for credentials +const client = new BinectClient({ + username: process.env.BINECT_USERNAME!, + password: process.env.BINECT_PASSWORD! +}); +``` + +## Testing + +```bash +# Run unit tests +npm test + +# Run e2e tests (requires credentials) +BINECT_USERNAME="user@example.com" BINECT_PASSWORD="password" npm run test:e2e +``` + +Note: Use double quotes for passwords containing `!` (bash history expansion).