* wip: connector workbench * wip: improve workbench * feat(workbench): improve UI with sidebar, routing, and syntax highlighting - Replace connector dropdown with Slack-style sidebar for connector selection - Add URL-based routing with react-router-dom for browser navigation support - Add JSON syntax highlighting with react-syntax-highlighter - Rework State tab layout to list/detail view with filtering - Update terminology from "create" to "instantiate" for connectors - Fix duplicate selection bug in lists (ID generation collision) - Unify data table row heights across tabs - Increase code block font sizes for readability - Hide navigation tabs on homepage when no connector selected - Make CW logo clickable to return to home view Co-authored-by: Cursor <[email protected]> * fix(workbench): use textarea for PEM/certificate config fields Fields containing 'privateKey', 'certificate', or 'pem' in their name now use a textarea instead of single-line input, preserving newlines in PEM content that would otherwise be stripped. Co-authored-by: Cursor <[email protected]> * fix(workbench): address PR review issues - Fix data race on idCounter using atomic.Int64 - Fix path traversal vulnerability in introspect.go - Fix pointer comparison for DefaultAsset in baseline.go Co-authored-by: Cursor <[email protected]> * feat(workbench): add generic connector server for integration testing Expose the generic connector REST API from the workbench so staging services can install a "generic connector" pointing to the local workbench, enabling end-to-end integration testing. - Add /generic/* routes (accounts, balances, beneficiaries, transactions) - Return cached data from storage instead of making fresh API calls - Add connector selection and optional API key authentication - Add UI panel in Debug tab for configuration Co-authored-by: Cursor <[email protected]> * fix(workbench): use internal registry to avoid pkg dependency Change imports from pkg/registry to internal/connectors/plugins/registry so the workbench can be merged before the pkg refactoring. The internal registry will automatically delegate to pkg/registry once that refactor lands. Also adds light/dark mode toggle to the UI with system preference detection. Co-authored-by: Cursor <[email protected]> * fix(workbench): address PR review feedback - Add ctx.Done() checks in fetch loops to exit early on client disconnect - Refactor generic server to get connector directly instead of via context, providing clearer data flow as suggested in review Co-authored-by: Cursor <[email protected]> * fix(workbench): include built UI files for embed directive The //go:embed ui/dist/* directive requires files to exist at build time. Add exception to .gitignore and commit the built UI distribution files. Co-authored-by: Cursor <[email protected]> * fix(workbench): address golangci-lint errors - Remove unused globalDebugTransport variable - Remove unused parseIntParam function - Fix printf format for models.TaskType (use %v instead of %s) - Add error return value handling for various function calls Co-authored-by: Cursor <[email protected]> * fix: remove .vite cache folder from git Co-authored-by: Cursor <[email protected]> * fix(workbench): fix remaining errcheck issue Co-authored-by: Cursor <[email protected]> * fix(workbench): fix all remaining lint issues - Add error handling for Body.Close() calls - Remove unnecessary fmt.Sprintf wrapper - Simplify hex character check condition Co-authored-by: Cursor <[email protected]> * fix(workbench): fix errcheck in cmd/workbench.go Co-authored-by: Cursor <[email protected]> * chore: replace ui dist build artifacts with placeholder The //go:embed directive needs at least one file to exist. Instead of committing the entire built UI, commit only a .gitkeep placeholder. The server has fallback code that displays instructions when the full UI isn't available. Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
Connector Workbench
A lightweight development environment for building and testing payment connectors without requiring Temporal, PostgreSQL, or other heavy infrastructure.
Why?
Running the full payments stack locally requires Docker, Temporal, PostgreSQL, and more - which can be slow and resource-intensive. The workbench provides a stripped-down alternative that lets you:
- Develop connectors faster - No Docker, no database, just run and test
- Debug easily - See all HTTP traffic, plugin calls, and state changes
- Iterate quickly - Hot reload-friendly, instant feedback
How It Works
The workbench is an alternative entry point to the same binary:
payments binary
│
├── payments server ← Full production mode (Temporal + PostgreSQL)
│
└── payments workbench ← Lightweight dev mode (in-memory)
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Workbench │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Engine │ │ Storage │ │ DebugStore │ │
│ │ (in-memory) │ │ (in-memory) │ │ (captures) │ │
│ └──────┬──────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Plugin (e.g., wise, stripe) │ │
│ │ - Loaded via registry │ │
│ │ - Same code as production │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
The In-Memory Engine
In production, connector operations are orchestrated by Temporal workflows:
Temporal Workflow
└─→ Schedule activity → Retry on failure → Persist state
The workbench replaces this with direct function calls:
// engine.go - simplified orchestration
func (e *Engine) RunOneCycle(ctx context.Context) error {
for _, task := range *tree {
e.executeTask(ctx, task, nil) // Direct call, no Temporal
}
}
This means:
- No Temporal needed - Tasks execute synchronously
- No PostgreSQL needed - State stored in memory
- Same connector code - Plugin interface is identical
Connector Loading
Connectors self-register via init():
// In wise/plugin.go
func init() {
registry.RegisterPlugin("wise", connector.PluginTypePSP, func(...) {
return New(name, logger, config)
}, ...)
}
The workbench asks the registry for a connector by name:
// In workbench.go
plugin, err := registry.GetPlugin(connectorID, logger, "wise", name, config)
Usage
Basic Usage
# Run with inline config
payments workbench --provider=wise --config='{"apiKey":"your-api-key"}'
# Run with config file
payments workbench --provider=stripe --config-file=./stripe-config.json
# List available providers
payments workbench --list-providers
Options
| Flag | Description | Default |
|---|---|---|
--provider, -p |
Connector provider name (required) | - |
--config, -c |
Connector config as JSON string | - |
--config-file, -f |
Path to config JSON file | - |
--listen |
HTTP server address | 127.0.0.1:8080 |
--auto-poll |
Enable automatic polling | false |
--poll-interval |
Polling interval | 30s |
--page-size |
Page size for fetch operations | 25 |
Web UI
Once running, open http://localhost:8080 in your browser to access the debug UI:
- Debug Log - All plugin calls, HTTP requests, state changes
- Tasks - Workflow task tree and execution status
- Data - Fetched accounts, payments, balances
- HTTP Traffic - Captured requests/responses to PSP APIs
HTTP API
# Install connector (initializes workflow)
curl -X POST http://localhost:8080/api/install
# Run one fetch cycle
curl -X POST http://localhost:8080/api/cycle
# Get fetched data
curl http://localhost:8080/api/accounts
curl http://localhost:8080/api/payments
curl http://localhost:8080/api/balances
# Get debug info
curl http://localhost:8080/api/debug
Key Components
| File | Purpose |
|---|---|
workbench.go |
Main orchestrator, lifecycle management |
engine.go |
In-memory Temporal replacement |
storage.go |
In-memory data storage |
debug.go |
Debug info capture |
transport.go |
HTTP traffic interception |
server.go |
HTTP API and UI serving |
tasks.go |
Task tree tracking |
Relation to pkg/connector
The workbench imports from internal/models (canonical types) because it's a platform tool, not a connector. Connectors themselves should import from pkg/connector (the public API):
┌─────────────────────┐
│ pkg/connector │ ← Public API for connectors
│ (type aliases) │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ internal/models │ ← Canonical types
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ tools/workbench │ ← Uses internal/models (platform tool)
└─────────────────────┘
Example Session
# 1. Start workbench with wise connector
$ payments workbench -p wise -f ./wise-config.json
INFO HTTP debug transport installed
INFO Connector workbench starting
INFO Web UI available at http://127.0.0.1:8080
# 2. In another terminal, install and run
$ curl -X POST localhost:8080/api/install
{"status":"installed","tasks":3}
$ curl -X POST localhost:8080/api/cycle
{"status":"completed","accounts":5,"payments":23}
# 3. Check the web UI for detailed debug info
# Open http://localhost:8080
Limitations
- No persistence - Data is lost on restart (by design)
- No webhooks - Webhook endpoints are mocked
- Single connector - One connector per workbench instance
- No retries - Errors fail immediately (easier to debug)
These limitations are intentional - the workbench is for development, not production.