The REST API returns a clone as a giant `files` map JSON blob with no
first-party way to use it — users were left staring at kilobytes of
escaped JSON with no path from "you got JSON" to "here's a project".
Add packages/cli: a zero-dependency `ditto` CLI whose `unpack` command
walks the files{} map from POST /v1/clones (or GET .../result) and
writes a real project tree to disk:
- text files written from inline content;
- binary assets materialized from inline base64, else fetched from their
reference URL using $DITTO_API_URL / $DITTO_API_KEY (--no-fetch writes
just the text tree and lists skipped assets);
- path-traversal guards (clone results are untrusted) and sha256
integrity checks;
- reads JSON from stdin ("-") so a curl response pipes straight in;
- clear errors for queued jobs with no files yet, and a tip pointing at
the /bundle endpoint when assets can't be fetched.
Document the pipe-from-curl one-liner prominently next to the REST
examples in README.md and docs/SERVICE.md, add a package README, a
Repository Map row, a root `unpack` script, and a CHANGELOG entry.
Covered by 9 tests (text tree, stdin, inline base64, live URL fetch with
auth + relative-URL resolution, --no-fetch, traversal refusal, queued-job
detection, sha256 mismatch).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.5 KiB
Markdown
47 lines
1.5 KiB
Markdown
# ditto (CLI)
|
|
|
|
The official ditto.site command-line helper. Turns a clone **result JSON** —
|
|
the giant blob you get back from `POST /v1/clones` or
|
|
`GET /v1/clones/:id/result` — into an actual project tree on disk.
|
|
|
|
Zero dependencies. Needs Node >= 20.
|
|
|
|
## Unpack
|
|
|
|
```bash
|
|
# from a saved file
|
|
ditto unpack clone.json ./out
|
|
|
|
# straight from curl, no temp file
|
|
curl -sS -X POST "$DITTO_API_URL/v1/clones" \
|
|
-H "authorization: Bearer $DITTO_API_KEY" \
|
|
-H "content-type: application/json" \
|
|
-d '{"url":"https://example.com/","options":{"mode":"single"}}' \
|
|
| ditto unpack - ./out
|
|
```
|
|
|
|
`ditto unpack <clone.json|-> <out-dir>`:
|
|
|
|
- writes every text file from its inline `content`,
|
|
- materializes binary assets from inline base64 when present, otherwise fetches
|
|
them from their reference `url`,
|
|
- refuses paths that escape `<out-dir>` and verifies `sha256` when the result
|
|
carries it.
|
|
|
|
### Binary assets
|
|
|
|
Clone results return binaries by reference (`{ "type": "binary", "url": ... }`)
|
|
rather than inlining megabytes of base64. To fetch them, `ditto` needs to know
|
|
where the API lives:
|
|
|
|
| Source | Flag | Env |
|
|
| --- | --- | --- |
|
|
| Base URL for relative asset URLs | `--base-url <url>` | `DITTO_API_URL` |
|
|
| Bearer key for authenticated APIs | `--api-key <key>` | `DITTO_API_KEY` |
|
|
|
|
Use `--no-fetch` to write only the text tree and list the binaries as skipped.
|
|
To grab everything in one shot instead, download the archive directly:
|
|
`GET /v1/clones/<id>/bundle?format=tgz`.
|
|
|
|
Run `ditto --help` for the full option list.
|