Skip to content

Commit dcd688c

Browse files
author
yinkscss
committed
Add agent APIs, onboarding, preferences, and deploy tooling
- Dashboard: agent provision/update/clear-state APIs, auth helpers, onboarding wizard, agent preferences, wallet UI and copy utils - Agent runtime: execution service updates, tools (transactions, constants), create-wallet/swap/transfer refinements - Deploy: Dockerfiles, Render config, smoke-test workflow and scripts, DEPLOY docs - Docs: confirmation flow review, task plan, progress - Lint: reduce nesting and nested ternaries, scripts tsconfig, extract helpers Made-with: Cursor
1 parent 8c1b89d commit dcd688c

49 files changed

Lines changed: 3134 additions & 1433 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Dependencies
2+
node_modules
3+
**/node_modules
4+
.pnp
5+
.pnp.js
6+
7+
# Build outputs
8+
.next
9+
**/.next
10+
dist
11+
**/dist
12+
out
13+
.turbo
14+
15+
# Git and IDE
16+
.git
17+
.gitignore
18+
.cursor
19+
*.md
20+
!README.md
21+
22+
# Env and secrets
23+
.env
24+
.env.*
25+
!.env.example
26+
27+
# Tests and dev
28+
**/*.test.ts
29+
**/*.spec.ts
30+
**/__tests__
31+
**/tests
32+
coverage
33+
.nyc_output
34+
35+
# Misc
36+
*.log
37+
.DS_Store
38+
*.local

.env.production.example

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Production env example (Render). Copy to .env for local reference only.
2+
# Do not commit real values. Set these in Render Dashboard per service.
3+
4+
# --- Postgres (from Render Blueprint: fromDatabase solagent-db) ---
5+
# DATABASE_URL=postgresql://user:password@host:5432/database
6+
7+
# --- Redis (from Render Blueprint: fromService solagent-redis connectionString) ---
8+
# REDIS_URL=redis://red-xxxx:6379
9+
10+
# --- Backend services: set in Render per service ---
11+
# OPENAI_API_KEY=sk-...
12+
# SOLANA_RPC_URL=https://api.devnet.solana.com
13+
# API_KEYS=your-api-key
14+
15+
# --- API Gateway → backends (internal URLs; set in render.yaml) ---
16+
# AGENT_RUNTIME_URL=http://agent-runtime:3001
17+
# WALLET_ENGINE_URL=http://wallet-engine:3002
18+
# POLICY_ENGINE_URL=http://policy-engine:3003
19+
# TRANSACTION_ENGINE_URL=http://transaction-engine:3004
20+
# DEFI_ENGINE_URL=http://defi-integration:3005
21+
# NOTIFICATION_URL=http://notification:3006
22+
23+
# --- Dashboard: set after deploy ---
24+
# NEXT_PUBLIC_API_URL=https://api-gateway-xxxx.onrender.com
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Run after deploying to Render. Set RENDER_DASHBOARD_URL and RENDER_API_GATEWAY_URL in repo secrets.
2+
# Optional: API_KEY (or API_KEYS) for protected endpoint check.
3+
name: Smoke test (deployed)
4+
5+
on:
6+
workflow_dispatch:
7+
8+
jobs:
9+
smoke:
10+
name: Smoke test deployed stack
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 5
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Setup Bun
18+
uses: oven-sh/setup-bun@v2
19+
with:
20+
bun-version: latest
21+
22+
- name: Run smoke test
23+
run: |
24+
bun run scripts/smoke-test-deployed.ts "${{ secrets.RENDER_DASHBOARD_URL }}" "${{ secrets.RENDER_API_GATEWAY_URL }}"
25+
env:
26+
API_KEY: ${{ secrets.API_KEY }}
27+
API_KEYS: ${{ secrets.API_KEYS }}

README.md

Lines changed: 72 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ Spins up 3 agent wallets, funds them via airdrop, and executes cross-agent trans
114114

115115
Navigate to [http://localhost:3000](http://localhost:3000) to view agents, wallets, transactions, and policies in the web UI.
116116

117+
### Deploy to production
118+
119+
See [docs/DEPLOY.md](docs/DEPLOY.md) for Docker Compose, Render (full cloud), Vercel + backends, and production checklist.
120+
117121
## Project Structure
118122

119123
```
@@ -163,75 +167,75 @@ solana-agent/
163167

164168
### Wallets (Wallet Engine — port 3002)
165169

166-
| Method | Endpoint | Description |
167-
|--------|----------|-------------|
168-
| POST | `/api/v1/wallets` | Create a new wallet |
169-
| GET | `/api/v1/wallets/:walletId` | Get wallet details |
170-
| GET | `/api/v1/wallets/:walletId/balance` | Get SOL balance |
171-
| GET | `/api/v1/wallets/:walletId/tokens` | Get SPL token balances |
172-
| POST | `/api/v1/wallets/:walletId/sign` | Sign a transaction |
173-
| POST | `/api/v1/wallets/:walletId/recover` | Recover a wallet |
174-
| DELETE | `/api/v1/wallets/:walletId` | Deactivate a wallet |
175-
| GET | `/api/v1/agents/:agentId/wallets` | List wallets for an agent |
170+
| Method | Endpoint | Description |
171+
| ------ | ----------------------------------- | ------------------------- |
172+
| POST | `/api/v1/wallets` | Create a new wallet |
173+
| GET | `/api/v1/wallets/:walletId` | Get wallet details |
174+
| GET | `/api/v1/wallets/:walletId/balance` | Get SOL balance |
175+
| GET | `/api/v1/wallets/:walletId/tokens` | Get SPL token balances |
176+
| POST | `/api/v1/wallets/:walletId/sign` | Sign a transaction |
177+
| POST | `/api/v1/wallets/:walletId/recover` | Recover a wallet |
178+
| DELETE | `/api/v1/wallets/:walletId` | Deactivate a wallet |
179+
| GET | `/api/v1/agents/:agentId/wallets` | List wallets for an agent |
176180

177181
### Transactions (Transaction Engine — port 3004)
178182

179-
| Method | Endpoint | Description |
180-
|--------|----------|-------------|
181-
| POST | `/api/v1/transactions` | Create and submit a transaction |
182-
| GET | `/api/v1/transactions/:txId` | Get transaction status |
183-
| POST | `/api/v1/transactions/:txId/retry` | Retry a failed transaction |
184-
| GET | `/api/v1/wallets/:walletId/transactions` | List wallet transactions |
183+
| Method | Endpoint | Description |
184+
| ------ | ---------------------------------------- | ------------------------------- |
185+
| POST | `/api/v1/transactions` | Create and submit a transaction |
186+
| GET | `/api/v1/transactions/:txId` | Get transaction status |
187+
| POST | `/api/v1/transactions/:txId/retry` | Retry a failed transaction |
188+
| GET | `/api/v1/wallets/:walletId/transactions` | List wallet transactions |
185189

186190
### Policies (Policy Engine — port 3003)
187191

188-
| Method | Endpoint | Description |
189-
|--------|----------|-------------|
190-
| POST | `/api/v1/policies` | Create a policy |
191-
| GET | `/api/v1/policies/:policyId` | Get policy details |
192-
| PUT | `/api/v1/policies/:policyId` | Update a policy |
193-
| DELETE | `/api/v1/policies/:policyId` | Deactivate a policy |
194-
| POST | `/api/v1/policies/:policyId/activate` | Reactivate a policy |
195-
| GET | `/api/v1/wallets/:walletId/policies` | List wallet policies |
196-
| POST | `/api/v1/evaluate` | Evaluate a transaction against policies |
192+
| Method | Endpoint | Description |
193+
| ------ | ------------------------------------- | --------------------------------------- |
194+
| POST | `/api/v1/policies` | Create a policy |
195+
| GET | `/api/v1/policies/:policyId` | Get policy details |
196+
| PUT | `/api/v1/policies/:policyId` | Update a policy |
197+
| DELETE | `/api/v1/policies/:policyId` | Deactivate a policy |
198+
| POST | `/api/v1/policies/:policyId/activate` | Reactivate a policy |
199+
| GET | `/api/v1/wallets/:walletId/policies` | List wallet policies |
200+
| POST | `/api/v1/evaluate` | Evaluate a transaction against policies |
197201

198202
### DeFi (DeFi Integration — port 3005)
199203

200-
| Method | Endpoint | Description |
201-
|--------|----------|-------------|
202-
| GET | `/api/v1/defi/quote` | Get a swap quote |
203-
| POST | `/api/v1/defi/swap` | Execute a token swap |
204-
| POST | `/api/v1/defi/stake` | Stake SOL |
205-
| POST | `/api/v1/defi/unstake` | Unstake SOL |
206-
| GET | `/api/v1/defi/price/:mint` | Get token price |
207-
| GET | `/api/v1/defi/protocols` | List supported protocols |
208-
| GET | `/api/v1/defi/pools/:protocol/:poolId` | Get pool info |
204+
| Method | Endpoint | Description |
205+
| ------ | -------------------------------------- | ------------------------ |
206+
| GET | `/api/v1/defi/quote` | Get a swap quote |
207+
| POST | `/api/v1/defi/swap` | Execute a token swap |
208+
| POST | `/api/v1/defi/stake` | Stake SOL |
209+
| POST | `/api/v1/defi/unstake` | Unstake SOL |
210+
| GET | `/api/v1/defi/price/:mint` | Get token price |
211+
| GET | `/api/v1/defi/protocols` | List supported protocols |
212+
| GET | `/api/v1/defi/pools/:protocol/:poolId` | Get pool info |
209213

210214
### Agents (Agent Runtime — port 3001)
211215

212-
| Method | Endpoint | Description |
213-
|--------|----------|-------------|
214-
| POST | `/api/v1/agents` | Create an agent |
215-
| GET | `/api/v1/agents/:agentId` | Get agent details |
216-
| PUT | `/api/v1/agents/:agentId` | Update agent configuration |
217-
| POST | `/api/v1/agents/:agentId/start` | Start an agent |
218-
| POST | `/api/v1/agents/:agentId/pause` | Pause an agent |
219-
| POST | `/api/v1/agents/:agentId/stop` | Stop an agent |
220-
| POST | `/api/v1/agents/:agentId/execute` | Execute an agent action |
221-
| DELETE | `/api/v1/agents/:agentId` | Delete an agent |
222-
| GET | `/api/v1/orgs/:orgId/agents` | List agents for an organization |
216+
| Method | Endpoint | Description |
217+
| ------ | --------------------------------- | ------------------------------- |
218+
| POST | `/api/v1/agents` | Create an agent |
219+
| GET | `/api/v1/agents/:agentId` | Get agent details |
220+
| PUT | `/api/v1/agents/:agentId` | Update agent configuration |
221+
| POST | `/api/v1/agents/:agentId/start` | Start an agent |
222+
| POST | `/api/v1/agents/:agentId/pause` | Pause an agent |
223+
| POST | `/api/v1/agents/:agentId/stop` | Stop an agent |
224+
| POST | `/api/v1/agents/:agentId/execute` | Execute an agent action |
225+
| DELETE | `/api/v1/agents/:agentId` | Delete an agent |
226+
| GET | `/api/v1/orgs/:orgId/agents` | List agents for an organization |
223227

224228
### Notifications (Notification Service — port 3006)
225229

226-
| Method | Endpoint | Description |
227-
|--------|----------|-------------|
228-
| POST | `/api/v1/webhooks` | Register a webhook |
229-
| GET | `/api/v1/webhooks/:webhookId` | Get webhook details |
230-
| PUT | `/api/v1/webhooks/:webhookId` | Update a webhook |
231-
| DELETE | `/api/v1/webhooks/:webhookId` | Delete a webhook |
232-
| POST | `/api/v1/alerts` | Create an alert rule |
233-
| GET | `/api/v1/orgs/:orgId/alerts` | List organization alerts |
234-
| WS | `/ws?orgId=...` | WebSocket stream for real-time events |
230+
| Method | Endpoint | Description |
231+
| ------ | ----------------------------- | ------------------------------------- |
232+
| POST | `/api/v1/webhooks` | Register a webhook |
233+
| GET | `/api/v1/webhooks/:webhookId` | Get webhook details |
234+
| PUT | `/api/v1/webhooks/:webhookId` | Update a webhook |
235+
| DELETE | `/api/v1/webhooks/:webhookId` | Delete a webhook |
236+
| POST | `/api/v1/alerts` | Create an alert rule |
237+
| GET | `/api/v1/orgs/:orgId/alerts` | List organization alerts |
238+
| WS | `/ws?orgId=...` | WebSocket stream for real-time events |
235239

236240
All endpoints are accessible through the API Gateway at `http://localhost:8080` with the same paths.
237241

@@ -314,20 +318,20 @@ Both scripts include retry logic for devnet airdrop rate limits and produce Sola
314318

315319
## Environment Variables
316320

317-
| Variable | Default | Description |
318-
|----------|---------|-------------|
319-
| `DATABASE_URL` | `postgresql://solagent:dev_password@localhost:5432/solagent` | PostgreSQL connection |
320-
| `REDIS_URL` | `redis://localhost:6379` | Redis connection |
321-
| `REDPANDA_BROKERS` | `localhost:9092` | RedPanda/Kafka brokers |
322-
| `SOLANA_RPC_URL` | `https://api.devnet.solana.com` | Solana RPC endpoint |
323-
| `SOLANA_NETWORK` | `devnet` | Solana network |
324-
| `KORA_URL` | `http://localhost:8911` | Kora fee relayer |
325-
| `API_GATEWAY_PORT` | `8080` | Gateway port |
326-
| `RATE_LIMIT_RPM` | `100` | Rate limit (requests/min) |
327-
| `HELIUS_API_KEY` || Optional Helius RPC key |
328-
| `TURNKEY_API_KEY` || Turnkey HSM API key |
329-
| `TURNKEY_ORGANIZATION_ID` || Turnkey organization |
330-
| `LOG_LEVEL` | `debug` | Logging verbosity |
321+
| Variable | Default | Description |
322+
| ------------------------- | ------------------------------------------------------------ | ------------------------- |
323+
| `DATABASE_URL` | `postgresql://solagent:dev_password@localhost:5432/solagent` | PostgreSQL connection |
324+
| `REDIS_URL` | `redis://localhost:6379` | Redis connection |
325+
| `REDPANDA_BROKERS` | `localhost:9092` | RedPanda/Kafka brokers |
326+
| `SOLANA_RPC_URL` | `https://api.devnet.solana.com` | Solana RPC endpoint |
327+
| `SOLANA_NETWORK` | `devnet` | Solana network |
328+
| `KORA_URL` | `http://localhost:8911` | Kora fee relayer |
329+
| `API_GATEWAY_PORT` | `8080` | Gateway port |
330+
| `RATE_LIMIT_RPM` | `100` | Rate limit (requests/min) |
331+
| `HELIUS_API_KEY` | | Optional Helius RPC key |
332+
| `TURNKEY_API_KEY` | | Turnkey HSM API key |
333+
| `TURNKEY_ORGANIZATION_ID` | | Turnkey organization |
334+
| `LOG_LEVEL` | `debug` | Logging verbosity |
331335

332336
## Observability
333337

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { NextRequest } from 'next/server';
2+
import { NextResponse } from 'next/server';
3+
4+
type AuthSuccess = { apiKey: string };
5+
type AuthFailure = { error: string; status: 401 };
6+
7+
export function requireApiKey(req: NextRequest): AuthSuccess | AuthFailure {
8+
const fromHeader = req.headers.get('x-api-key');
9+
if (fromHeader) return { apiKey: fromHeader };
10+
11+
const auth = req.headers.get('authorization');
12+
if (auth?.startsWith('Bearer ')) {
13+
const token = auth.slice(7).trim();
14+
if (token) return { apiKey: token };
15+
}
16+
17+
return { error: 'API key required', status: 401 };
18+
}
19+
20+
export function isAuthFailure(result: AuthSuccess | AuthFailure): result is AuthFailure {
21+
return 'error' in result;
22+
}
23+
24+
export function authErrorResponse(result: AuthFailure) {
25+
return NextResponse.json({ error: result.error }, { status: result.status });
26+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { NextRequest } from 'next/server';
2+
import { NextResponse } from 'next/server';
3+
import { requireApiKey, isAuthFailure, authErrorResponse } from '../_lib/auth';
4+
5+
const AGENT_RUNTIME_URL = process.env.AGENT_RUNTIME_URL || 'http://localhost:3001';
6+
7+
export async function POST(req: NextRequest) {
8+
const auth = requireApiKey(req);
9+
if (isAuthFailure(auth)) return authErrorResponse(auth);
10+
try {
11+
const body = await req.json();
12+
const { agentId } = body;
13+
14+
if (!agentId) {
15+
return NextResponse.json({ error: 'agentId is required' }, { status: 400 });
16+
}
17+
18+
const res = await fetch(`${AGENT_RUNTIME_URL}/api/v1/agents/${agentId}/clear-state`, {
19+
method: 'POST',
20+
headers: { 'Content-Type': 'application/json' },
21+
});
22+
23+
if (!res.ok) {
24+
return NextResponse.json({ error: 'Failed to clear state' }, { status: res.status });
25+
}
26+
27+
return NextResponse.json({ success: true });
28+
} catch (err) {
29+
const message = err instanceof Error ? err.message : 'Unknown error';
30+
return NextResponse.json({ error: message }, { status: 500 });
31+
}
32+
}

0 commit comments

Comments
 (0)