packages feed

synapse-cc-0.4.0: README.md

# synapse-cc

**Unified compiler toolchain for Plexus backends**

`synapse-cc` (synapse compiler collection) orchestrates the complete pipeline from backend schema discovery to compiled, ready-to-use client libraries.

## Features

- πŸ”§ **Unified Interface**: Single command to generate clients from any backend
- πŸ” **Smart Tool Discovery**: Finds local development builds or installed versions
- ⚑ **Fast**: Incremental caching skips regeneration when schemas haven't changed (IR cache at `~/.cache/plexus-codegen/synapse/ir/`, per-file code cache via service-codegen)
- 🎯 **Multi-Language**: TypeScript and Rust today (`rust` requires a service-codegen built with `--features rust`); `python` parses but has no generator yet
- πŸ› οΈ **Complete Pipeline**: schema β†’ IR β†’ generated code β†’ installed deps β†’ compiled artifact β†’ smoke tests
- πŸ” **Credential lifecycle**: Per-backend defaults store shared with `synapse` β€” set, inspect, and rotate JWTs / cookies / headers via `synapse-cc _self <backend> …`. Values held as reference URIs (`literal:`, `env://`, `file://`, `keychain://`) so secrets live in the OS keychain or env vars, not plaintext. JWT decoding flags expired tokens at a glance.

## Installation

### From Source

```bash
cd synapse-cc
cabal build
cabal install
```

### Dependencies

`synapse-cc` requires these tools to be available:
- **synapse** - Schema discovery and IR generation
- **service-codegen** β‰₯ 0.5.0 - Code generation from IR. Earlier releases write a
  parameter the server injects (`pdSource`: auth, header, derived, …) into the
  generated methods as an optional `unknown` argument; synapse-cc says so when
  it finds one (SYNAPSE-CC-2)

The tool will automatically find them if they're:
- In your `$PATH`
- In local development directories (`../synapse`, `../service-codegen`)
- In `~/.plexus/bin/`

**The generator is `service-codegen`** (0.5.1 on; NAMING-10 β€” a Plexus backend
is a *service*). synapse-cc 0.3.6 looks for it under that name first β€” on
`$PATH`, then `../service-codegen/target/…`, `~/.plexus/bin`, `~/.cargo/bin` β€”
and only then under its old name, saying the old name is deprecated. Its path
is `--service-codegen PATH` (the old flag still works for one release, hidden,
with a note). `synapse.lock` is written with `serviceCodegen`, the generator's
JSON is read as `serviceCodegenVersion`, a code cache's toolchain as
`tvServiceCodegen`; each old key is still read. NAMING-18 removes the old
names; `scripts/naming-gate.sh` (the `naming` test suite) fails on a new one.
synapse-cc 0.3.6 builds against `plexus-synapse` 3.14.3 up to 3.17.

## Usage

The CLI is subcommand-shaped:

```bash
synapse-cc init [BACKEND]                  # scaffold synapse.config.json
synapse-cc build                           # build every target in synapse.config.json
synapse-cc build TARGET BACKEND [OPTIONS]  # one-off build, no config file
synapse-cc watch BACKEND [PLUGIN...]       # rebuild on schema changes
synapse-cc wait [BACKEND]                  # block until backend(s) reachable
synapse-cc _self BACKEND VERB ...          # credentials store (shared with synapse)
```

### Quickstart (config-driven)

A service scaffolded by `axon new` ships a `synapse.config.json` pre-pointed
at itself β€” `synapse-cc build` works unedited from the service directory
(server must be running). Real run:

```
$ synapse-cc build
==> Discovering tools...
[+] Found all required tools
[i] Building "client" β†’ src/lib/plexus

==> Reading schema...
[+] Schema ready (2 plugins)

==> Generating code...
[+] Code generated (11 files)

==> Installing dependencies...
[+] Dependencies installed

==> Building...
[+] Build passed

==> Running tests...
[+] Tests passed
[+] client β†’ src/lib/plexus/
```

Without a config file, `synapse-cc init <backend>` scaffolds one; with the
backend argument omitted it is **inferred** β€” from a co-located Plexus
service crate, then from the local registry β€” and the inference is
announced. There is no silent default backend:

```
$ synapse-cc init
[!] missing BACKEND argument β€” no co-located Plexus service crate found and
    no backend discoverable on the registry at 127.0.0.1:4444.
    Run: synapse-cc init <backend>
```

### Quickstart (one-off, no config)

```bash
synapse-cc build typescript substrate          # TS client β†’ ./generated/
synapse-cc build rust substrate -o ./client    # Rust client, ends in cargo build
```

The backend is resolved **by name via the registry** (endpoint from
`PLEXUS_REGISTRY_URL`, default `ws://127.0.0.1:4444`; `-H`/`-P` set the
fallback). A real `rust` run:

```
$ synapse-cc build rust zechodev -o ./rust-client
==> Discovering tools...
[+] Found all required tools

==> Resolving zechodev via registry at ws://127.0.0.1:4444...

==> Reading schema...
[+] Schema ready (2 plugins)

==> Generating code...
[+] Code generated (5 files)

==> Building...
[+] Build passed

==> Running tests...
[+] Tests passed

[+] Client β†’ ./rust-client/
```

`-t rust` ends in a real `cargo build` of the generated crate (Z2H-7) β€” the
artifact compiles or the command fails.

### Options (build / watch)

```
  -o, --output DIR        Output directory (default: ./generated)
  --transport ws|browser  ws (Node.js, default) or browser (native WebSocket,
                          for Tauri/WebView)
  --no-install            Skip dependency installation
  --no-build              Skip compilation step
  --no-tests              Skip running smoke tests
  --cache-dir DIR         Cache directory (default: ~/.cache/plexus-codegen)
  --force                 Force regeneration (ignore cache)
  --debug                 Enable debug logging
  --synapse PATH          synapse binary path (overrides discovery)
  --service-codegen PATH      service-codegen binary path (overrides discovery)
  -t, --token JWT         JWT auth token (see Credentials below)
  --token-file PATH       Read JWT from file
  -H, --host HOST         Registry/discovery host (default: 127.0.0.1)
  -P, --port PORT         Registry/discovery port (default: 4444)
```

### Rust target prerequisite

`build rust` requires a service-codegen binary compiled with the `rust` feature
(`cargo build --release --features all` in the service-codegen repo). A
typescript-only binary fails with:

```
[!] Error: service-codegen failed (exit code 1)
Error: Rust codegen not enabled. Rebuild with --features rust
```

Point `--service-codegen` at a rust-enabled build if the discovered one is not.

### Credentials & Headers

synapse-cc shares a **single defaults store** with synapse at `~/.plexus/<backend>/defaults.json`. Any JWT, cookie, or header you set via either CLI is immediately visible to the other. The file holds credential-reference URIs (`literal:`, `env://`, `file://`, `keychain://`), resolved at request time.

```bash
# Inspect what's stored + decode any JWTs + flag expired tokens
synapse-cc _self <backend> show

# Store a JWT (auto-wrapped as literal:)
synapse-cc _self <backend> set cookie access_token "eyJ..."

# Store a reference to an env var (preferred in CI)
synapse-cc _self <backend> set cookie access_token "env://MY_JWT"

# Import a JWT file
synapse-cc _self <backend> import-token ~/Downloads/jwt.txt

# Clear all defaults for a backend
synapse-cc _self <backend> clear --yes
```

The `_self` subcommand tree is identical to `synapse _self <backend> …` β€” they share the same library implementation. See synapse's README for the full verb list, URI scheme reference, and legacy `~/.plexus/tokens/<backend>` migration behavior.

Per-invocation overrides: `--token <jwt>`, `SYNAPSE_TOKEN` env, `--token-file <path>`, `--cookie KEY=VALUE`, `--header KEY=VALUE` β€” all flow through the same priority chain as synapse, with CLI values winning over stored defaults per key.

## Architecture

```
synapse-cc (Haskell)
    ↓
    β”œβ”€β†’ synapse (Haskell) β†’ IR (JSON)
    ↓
    β”œβ”€β†’ service-codegen (Rust) β†’ Generated Code
    ↓
    └─→ Language Tools (npm, tsc, etc.) β†’ Compiled Artifact
```

## Development

### Project Structure

```
synapse-cc/
β”œβ”€β”€ synapse-cc.cabal         # Cabal project file
β”œβ”€β”€ app/
β”‚   └── Main.hs              # Entry point, subcommand dispatch
└── src/
    └── SynapseCC/
        β”œβ”€β”€ Types.hs           # Core types, Options, synapse.config.json types
        β”œβ”€β”€ CLI.hs             # Subcommand + option parsing
        β”œβ”€β”€ Config.hs          # synapse.config.json load/init
        β”œβ”€β”€ Detect.hs          # Project detection for `init` inference
        β”œβ”€β”€ Discover.hs        # Tool discovery (synapse, service-codegen)
        β”œβ”€β”€ RegistryResolve.hs # Backend-name resolution via the registry
        β”œβ”€β”€ Pipeline.hs        # Pipeline orchestration
        β”œβ”€β”€ Process.hs         # Subprocess helpers
        β”œβ”€β”€ Cache.hs           # Incremental IR caching
        β”œβ”€β”€ Merge.hs           # Three-way merge bookkeeping
        β”œβ”€β”€ Dependency.hs      # Plugin dependency resolution
        β”œβ”€β”€ Language.hs        # Per-language install/build/test steps
        β”œβ”€β”€ Auth.hs            # Token priority chain (wraps Synapse.Self)
        β”œβ”€β”€ Watch.hs           # watch subcommand
        β”œβ”€β”€ Wait.hs            # wait subcommand
        └── Logging.hs         # Pretty output
```

### Building

```bash
cabal build
```

### Running from source

```bash
cabal run synapse-cc -- build typescript substrate --debug
```

### Testing

```bash
# Scaffold and start a throwaway backend (any free port)
axon new mysvc --port 4452 && cd mysvc && cargo run &

# Generate, compile, and smoke-test a client against it β€” the scaffold's
# synapse.config.json is already pointed at ws://127.0.0.1:4452
synapse-cc build
```

## Status

Shipped: tool discovery, pipeline orchestration, config-driven multi-target
builds (`synapse.config.json`), incremental IR + code caching with three-way
merge, dependency installation + compile + smoke-test steps (bun/npm for
TypeScript, cargo for Rust), watch mode, `wait`, registry-based backend
resolution (`PLEXUS_REGISTRY_URL`), shared credentials store (`_self`).

Not shipped: a `python` generator (the target parses; service-codegen has no
emitter), remote/distributed caching.

## Contributing

See [PLAN.md](./PLAN.md) for detailed implementation plan and design decisions.

## License

MIT