Skip to content

feat(dev-run): add aileron-connector-dev-run harness#406

Open
ALRubinger wants to merge 2 commits intomainfrom
feat/connector-dev-runner
Open

feat(dev-run): add aileron-connector-dev-run harness#406
ALRubinger wants to merge 2 commits intomainfrom
feat/connector-dev-runner

Conversation

@ALRubinger
Copy link
Copy Markdown
Owner

Summary

A developer-only CLI for running an Aileron WASM connector against real external APIs without the install/binding/launch round-trip. Spawned out of #402: connector authors need a fast iteration loop for aileron-connector-google and the connectors that follow.

Usage

```sh
task build:connector-dev-run

Get an OAuth token (e.g. https://developers.google.com/oauthplayground for Google).

export AILERON_DEV_TOKEN=ya29...

./build/aileron-connector-dev-run \
--wasm ~/git/ALRubinger/aileron-connector-google/connector.wasm \
--manifest ~/git/ALRubinger/aileron-connector-google/connector/manifest.toml \
--op list_recent_emails \
--args '{"query":"is:unread","max_results":3}'
```

Output:

  • Connector logs → stderr (`[connector ] `)
  • Final result → stdout as `{"output": {...}}` or `{"error": "..."}` (mirrors the production tool-result envelope)

What it does

  • Reads `connector.wasm` + `manifest.toml` from disk.
  • Parses + validates the manifest via `internal/cstore` — same path the production install pipeline uses.
  • Spins up `internal/sandbox` (Wazero runtime) with the manifest's `[capabilities.network]` grant. The network policy is enforced exactly as in production; calls outside the grant deny with `capability_denied`.
  • Wires a stub `credential.Resolver` that returns the bearer token from `AILERON_DEV_TOKEN` for calls referencing the connector's `[capabilities.credential].kind`. No vault, no binding setup, but the same credential-mediation code path runs.
  • Invokes via `Connector.Invoke` and prints the result.

What it deliberately does NOT do

  • Not installed alongside `aileron`. Lives in `cmd/` so it can import `internal/sandbox` directly. Connector authors build it locally; end users never see it.
  • No vault, no binding store. The whole point is to skip those for fast iteration. Production credentials still flow through the real path (`aileron binding setup` → vault → resolver) once the connector ships.
  • No action manifest involvement. The harness invokes a connector op directly; the action layer is exercised by the existing E2E flow (`aileron action add` → MCP discovery → `POST /v1/actions/{name}/run`).

Verifying with the Google connector

Smoke-tested locally against `aileron-connector-google`:

  • Without `AILERON_DEV_TOKEN`: harness surfaces the structured envelope correctly (`binding_required: credential: no binding for this capability (boundary=sandbox)`). Confirms the credential-mediation path runs.
  • Connector logs are captured on stderr with structured level prefix.
  • The manifest loads + validates cleanly through `cstore.ParseManifest` + `cstore.ValidateManifest` (the same code the install pipeline runs against published tarballs).

A live API test against Gmail with a real token requires the publisher `client_id` (gating on Google Cloud Console registration in #402) — not on the critical path for landing this harness.

Test plan

  • `task lint:go` clean
  • `task build:connector-dev-run` builds
  • `go test ./...` green for the new package
  • Manual smoke test against `aileron-connector-google/connector.wasm` confirms envelope shape, credential-mediation path, and connector-log capture
  • Live Gmail API call with a real token (pending Google Cloud Console registration)

🤖 Generated with Claude Code

…ectors

A developer-only CLI that runs an Aileron WASM connector against real
external APIs without going through install/binding/launch. The
connector author iterates locally — edit connector/main.go, task
build, dev-run — instead of cutting a release for every change.

Loads the connector's manifest.toml and connector.wasm from disk,
spins up the same Wazero-backed sandbox runtime the production gateway
uses, enforces the manifest's [capabilities.network] grant, and
invokes the connector with the supplied op + args. A stub
credential.Resolver returns the OAuth bearer token from the
AILERON_DEV_TOKEN env var so calls that mark themselves
credential:"oauth2" get the token injected at the network boundary
exactly as a vault-bound credential would in production.

Out of scope: this binary is not installed alongside `aileron` for end
users — it lives in cmd/ so it can import internal/* directly. It's
a developer harness for connector authors; the README of the published
connector repos points at it for local iteration.

Refs #402.
@railway-app
Copy link
Copy Markdown

railway-app Bot commented May 4, 2026

🚅 Deployed to the aileron-pr-406 environment in aileron

1 service not affected by this PR
  • docs

@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

❌ Patch coverage is 14.47368% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.34%. Comparing base (2c91b31) to head (db79b1e).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #406      +/-   ##
==========================================
- Coverage   82.56%   82.34%   -0.23%     
==========================================
  Files         212      213       +1     
  Lines       21902    21978      +76     
==========================================
+ Hits        18084    18098      +14     
- Misses       2852     2915      +63     
+ Partials      966      965       -1     
Flag Coverage Δ
integration 9.12% <ø> (ø)
unit 78.60% <14.47%> (-0.21%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The new cmd/aileron-connector-dev-run module wasn't in GO_TEST_PACKAGES
or the CI's cache-dependency-path list, so:

  - 'task vet:go' (run by CI's lint job) skipped the package
  - 'task test:go:ci' (run by CI's test job) skipped its tests
  - The setup-go action couldn't cache its module dependencies

All three are fixed by adding the package to GO_TEST_PACKAGES (a single
list driving vet:go, test:go, and test:go:ci) and adding the new
go.mod to the workflow's cache-dependency-path. Verified locally:
'task vet:go' and 'task test:go' now both touch the new package.
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