Skip to content

feat(cli): embeddable CLI library + optional reqwest TLS - #189

Merged
chaliy merged 3 commits into
mainfrom
claude/project-thread-solgcd
Nov 1, 2026
Merged

chaliy merged 3 commits into
mainfrom
claude/project-thread-solgcd

Conversation

@chaliy

@chaliy chaliy commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

Prep so bashkit can ship a fetchkit builtin with the exact same CLI as the standalone binary.

  • CLI is now a library. fetchkit_cli::run(args, &dyn CliHost) -> i32. The fetchkit binary is a 6-line wrapper over it with NativeHost. The host owns stdout/stderr, env lookups (CLOUDFLARE_*), MCP stdio, and egress policy (configure_tool, e.g. a custom HttpTransport). run never calls process::exit, prints directly, or reads std::env.
  • Hosts can refuse host-only flags (--allow-env-proxy, --render-rakers, --bot-auth-key/--bot-auth-agent, --allow-remote-conversion; mcp is refused by any host that doesn't implement serve_mcp). Flags still parse and show in --help; the command fails before any request with Error: <flag> <reason>, exit 1.
  • reqwest default features off. New default reqwest-tls feature carries TLS, HTTP/2, proxy discovery and gzip/brotli/deflate for ReqwestTransport. With default-features = false fetchkit links no TLS stack and no aws-lc-sys (bashkit is ring-only because aws-lc-sys breaks its aarch64 manylinux cross builds).
  • clap without color/suggestions, matching bashkit's clap set (features unify; otherwise output would differ between the binary and the builtin).
  • Markkit 0.2 (was 0.1.1): one markkit copy when bashkit links both. Small API updates (limits.*, typed warning codes).
  • MCP server reads stdin per line instead of holding StdinLock across .await (needed for a Send future; same behavior).

Why

Design: markkit + fetchkit "preinstalled" in bashkit sessions with an identical CLI. Sharing the code (not copying clap structs) is the only way to keep the interface from drifting. Companion: everruns/markkit#73.

Before / After

Binary behavior unchanged. New tests/host.rs proves parity and the host contract:

  • --help, fetch --help, mcp --help, --version: run() output == binary stdout, byte for byte, exit 0
  • usage error: same stderr as binary, exit 2
  • fetch through a host transport: frontmatter markdown + JSON output, every request (page and agent-discovery probes) went through the host
  • each refused flag: exit 1, one-line error, zero transport calls
  • broken stdout pipe: exit 0
cargo tree -p fetchkit --no-default-features -e normal -i aws-lc-sys
error: package ID specification `aws-lc-sys` did not match any packages

Risk

  • Low/Medium
  • Breaking for default-features = false users who rely on HTTPS through ReqwestTransport: enable reqwest-tls. CHANGELOG marks it.
  • CLI help/errors lose ANSI color on terminals.

Checklist

  • Unit tests are passed (cargo test --workspace, clippy -D warnings, fmt, docs -D warnings, release build)
  • Smoke tests are passed
  • Documentation is updated (CHANGELOG)
  • Knowledge is up to date and not in conflict (tool-contract, fetchers, log; OKF check passes)

reqwest default features off; new default `reqwest-tls` feature carries
TLS, HTTP/2, proxy discovery and decompression for ReqwestTransport, so
embedders with their own HttpTransport get no second TLS stack.
clap without color/suggestions to match bashkit. Markkit 0.2 API updates.
Move the fetchkit CLI into `fetchkit_cli::run(args, &dyn CliHost)`; the
binary is a thin wrapper. Hosts own stdout/stderr, env, MCP stdio and
egress, and may refuse host-only flags. Lets bashkit ship a `fetchkit`
builtin with the identical interface.
chaliy added a commit to everruns/markkit that referenced this pull request Nov 1, 2026
<!-- claude-projects-attribution: {"github_login":"chaliy"} -->
_Requested by **Михайло** · [project
thread](https://claude.ai/code/project/chan_019YEkBJuZMHJdXrjom86cGX?thread=cmsg_019YEkBJuZMHJdXrjom86cGXFSAmywce8BSJgNQ4YNsrks)_

## What changed
Prep so bashkit can ship a `markkit` builtin with the exact same CLI as
the standalone binary.

- **CLI is now a library.** `markkit_cli::run(args, &impl CliHost) ->
i32`; the `markkit` binary is a thin wrapper with `NativeHost`. The host
owns stdout/stderr, stdin, file reads (`--file`, `--rules`), URL
extraction and MCP stdio. `run` never exits the process or touches the
host filesystem.
- **URL extraction is a host method** that gets the configured
`Markkit`. `NativeHost` keeps calling `extract_url_document` (markkit's
SSRF policy). A sandbox fetches through its own egress, sets `base_url`
to the final URL and calls `extract_document`.
- **`native-http` feature** (default) carries `markkit/http_client` for
`NativeHost`. Without it the CLI library links no reqwest/TLS.
- **Hosts can refuse `--ocr`** (host Tesseract binary, host model
files). It still parses and shows in help; exit 1 with a one-line error
before any input is read. `mcp` is refused by any host that doesn't
implement `serve_mcp`.
- **clap without color/suggestions**, matching bashkit's clap set
(features unify).
- Errors keep anyhow's `Error: …` / `Caused by:` layout but no longer
append a backtrace when `RUST_BACKTRACE` is set (output must not depend
on env; embedders must not leak internals). A closed stdout pipe now
exits 0 instead of panicking in `print!`.

## Why
Design: markkit + fetchkit "preinstalled" in bashkit sessions with an
identical CLI. Sharing the code (not copying clap structs) keeps the
interface from drifting. Companion: everruns/fetchkit#189.

## Before / After
Binary output unchanged apart from the backtrace/broken-pipe notes
above. New `tests/host.rs`:

- `--help`, `extract --help`, `mcp --help`, `--version`: `run()` stdout
== binary stdout, byte for byte
- usage errors (unknown flag, no subcommand, `--layout` without
`--json`): same stderr as binary, exit 2
- runtime error (`extract` with no source): same stderr as binary, exit
1
- stdin (`--stdin`, `-`), `--file` + `--rules` read from an in-memory
host fs
- URL via host: relative links resolve against the host-reported final
URL
- refused `--ocr`: exit 1, no fetch; `mcp` refused by default host;
broken pipe exits 0

```
cargo tree -p markkit-cli --no-default-features --features all_formats -e normal -i reqwest
error: package ID specification `reqwest` did not match any packages
```

`just pre-pr`: fmt, clippy, test, check-okf, script tests, examples pass
locally. `cargo deny` is not installed here, so CI runs it (no new
dependencies).

## Risk
- Low
- **Breaking:** `markkit-cli` with `default-features = false` must
enable `native-http` for URL sources (CHANGELOG).
- CLI help/errors lose ANSI color on terminals.
@chaliy
chaliy merged commit d236614 into main Nov 1, 2026
12 checks passed
@chaliy
chaliy deleted the claude/project-thread-solgcd branch November 1, 2026 02:50
@chaliy chaliy mentioned this pull request Nov 1, 2026
4 tasks done
chaliy added a commit that referenced this pull request Nov 1, 2026
<!-- claude-projects-attribution: {"github_login":"chaliy"} -->
_Requested by **Михайло** · [project
thread](https://claude.ai/code/project/chan_019YEkBJuZMHJdXrjom86cGX?thread=cmsg_019YEkBJuZMHJdXrjom86cGXFSAmywce8BSJgNQ4YNsrks)_

## What changed
Release v0.7.0: workspace version 0.7.0, CHANGELOG section with
Highlights / Breaking Changes / What's Changed, knowledge log entry.

Two release-blocking fixes for embedding, found while wiring bashkit:
- **Markkit 0.3** (just published) so bashkit, which also embeds markkit
0.3, links one copy.
- **`fetchkit-cli` depends on `fetchkit` with `default-features =
false`** and gains matching `reqwest-tls` / `docs-pdf` features (default
on). Before, any `fetchkit-cli` dependency forced reqwest TLS
(aws-lc-sys) back on.

### Highlights
- Embed the exact `fetchkit` CLI with `fetchkit_cli::run(args, &dyn
CliHost)` (#189)
- TikTok, Vimeo, SoundCloud oEmbed metadata; YouTube Shorts/live/embed
URLs (#188)
- Build without a TLS stack when supplying your own `HttpTransport`

## Before / After
```
cargo tree -p fetchkit-cli --no-default-features -e normal -i aws-lc-sys
error: package ID specification `aws-lc-sys` did not match any packages
```
Default builds unchanged. `cargo test --workspace`, clippy (`-D
warnings`, also `fetchkit-cli --no-default-features`), fmt and OKF check
pass locally.

## Risk
- Low. Merging runs `release.yml` (tag, GitHub Release) and
`publish.yml` (`fetchkit`, then `fetchkit-cli`).
- Breaking (minor bump): `reqwest-tls` split, documented in CHANGELOG.

### Checklist
- [x] Unit tests are passed
- [x] Smoke tests are passed
- [x] Documentation is updated
- [x] Knowledge is up to date and not in conflict

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Wy7MByPXsW4jHxfLeTfGEc)_
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