# GhostOff plan A TypeScript CLI tool that exports public posts from a Ghost CMS using its Content API, transforms the HTML body and images into Offprint's block-based format, and uploads the resulting records to an AT Protocol PDS as `site.standard.document` + `app.offprint.document.article` pairs. ## Credentials and configuration Values can be read from a local `.env` file or passed as CLI flags: - `GHOST_URL` / `--ghost-url` — Ghost publication URL - `GHOST_API_KEY` / `--ghost-api-key` — Ghost Content API key - `ATP_IDENTIFIER` / `--atproto-identifier` — atproto handle or DID - `ATP_APP_PASSWORD` / `--atproto-app-password` — atproto app password - `ATP_SERVICE` / `--atproto-service` — PDS/service URL (default `https://bsky.social`) - `ATPUBLICATION_AT_URI` / `--publication-at-uri` — existing `site.standard.publication` AT-URI to attach documents to Additional CLI flags: - `--dry-run` — build records without uploading - `--export-dir` — local download/cache directory (default `ghostoff-export/`) - `--state-file` — idempotency state (default `ghostoff-state.json`) ## Tech stack - Node.js + TypeScript - AT Protocol SDK: `@atcute/client` + `@atcute/password-session` - AT Protocol schema/validation: `@atcute/atproto`, `@atcute/standard-site`, `@atcute/lexicons` - DOM parsing: `linkedom` - Image resize/compress: `sharp` - CLI/config: `commander`, `dotenv` ## Layout ``` src/ ├── index.ts # CLI orchestration ├── config.ts # env + argv loading ├── ghost.ts # Ghost Content API client ├── assets.ts # image download, cache, resize ├── html-to-offprint.ts # Ghost HTML -> app.offprint.content blocks ├── facets.ts # inline formatting -> richtext facets └── atproto.ts # auth, blob upload, create/put records, validation ``` ## Workflow 1. Load configuration from `.env` and/or CLI flags. 2. Authenticate with the PDS via `@atcute/password-session` and persist the session to `ghostoff-session.json`. 3. Fetch all public Ghost posts with `?include=authors,tags&formats=html,plaintext`, paginated. 4. For each post: - Download the `feature_image` and any inline `` assets into `--export-dir`. - Resize/compress images that approach the 1 MB atproto blob limit using `sharp`. - Convert the post HTML into an `app.offprint.content` wrapper inside the document's `content` array. - Map top-level block elements: - `

` -> `app.offprint.block.text` - `

`-`

` -> `app.offprint.block.heading` - `
` -> `app.offprint.block.blockquote` - `
` -> `app.offprint.block.codeBlock`
     - `