# @emdash-cms/marketplace

Standalone Cloudflare Worker that hosts the EmDash plugin marketplace — discovery, publishing, and moderation.

## Development

```bash
pnpm dev        # starts wrangler dev server on :8787
pnpm test       # runs vitest
```

Requires an AI binding (`wrangler.jsonc` has it configured). Code and image audits run on Workers AI.

## Manual audit testing

The `/api/v1/dev/audit` endpoint (localhost only) runs the code + image audit pipeline without auth or DB writes. Use it to evaluate AI model accuracy against the fixture corpus.

### Using the test script

```bash
# Run a single fixture
tests/fixtures/audit/test-audit.sh tests/fixtures/audit/prompt-injection

# Against a different host
tests/fixtures/audit/test-audit.sh tests/fixtures/audit/data-exfiltration http://localhost:8787
```

The script tars the fixture directory and POSTs it as a multipart bundle. Output is the raw audit JSON.

### Using curl directly

Tarball mode (full bundle with manifest, code, and images):

```bash
tar -czf /tmp/bundle.tar.gz -C tests/fixtures/audit/crypto-miner .
curl -s -X POST http://localhost:8787/api/v1/dev/audit -F "bundle=@/tmp/bundle.tar.gz" | jq
```

JSON mode (code only, no manifest required):

```bash
curl -s -X POST http://localhost:8787/api/v1/dev/audit \
  -H "Content-Type: application/json" \
  -d '{"backendCode": "const x = eval(\"1+1\");"}' | jq
```

### Running all fixtures

```bash
for d in tests/fixtures/audit/*/; do
  echo "=== $(basename "$d") ==="
  tests/fixtures/audit/test-audit.sh "$d"
  echo
done
```

Compare the `verdict` and `riskScore` in each response against the fixture's `expected.json` to evaluate model accuracy.

## Fixture format

Each fixture in `tests/fixtures/audit/` is a directory containing:

| File                | Required | Purpose                             |
| ------------------- | -------- | ----------------------------------- |
| `manifest.json`     | yes      | Plugin manifest                     |
| `backend.js`        | yes      | Backend code (primary audit target) |
| `admin.js`          | no       | Admin UI code                       |
| `icon.png`          | no       | Plugin icon (triggers image audit)  |
| `screenshots/*.png` | no       | Screenshots (trigger image audit)   |
| `expected.json`     | yes      | Expected verdict, score, categories |

`expected.json` shape:

```json
{
  "verdict": "pass" | "warn" | "fail",
  "minRiskScore": 50,
  "maxRiskScore": 10,
  "categories": ["data-exfiltration", "obfuscation"]
}
```

`minRiskScore` and `maxRiskScore` are optional bounds. `categories` lists the finding categories the model should detect.
