agent-mailbox-cli 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -0
- package/dist/api/auth.d.ts +4 -1
- package/dist/api/auth.d.ts.map +1 -1
- package/dist/api/auth.js +33 -2
- package/dist/api/auth.js.map +1 -1
- package/dist/api/routes/auth.d.ts +3 -0
- package/dist/api/routes/auth.d.ts.map +1 -0
- package/dist/api/routes/auth.js +102 -0
- package/dist/api/routes/auth.js.map +1 -0
- package/dist/api/routes/orgs.d.ts.map +1 -1
- package/dist/api/routes/orgs.js +28 -5
- package/dist/api/routes/orgs.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +2 -0
- package/dist/api/server.js.map +1 -1
- package/dist/cli/config.d.ts +9 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +28 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.js +164 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -0
- package/dist/config.js.map +1 -1
- package/dist/db/schema.sql +35 -0
- package/dist/email/send.d.ts +2 -0
- package/dist/email/send.d.ts.map +1 -0
- package/dist/email/send.js +24 -0
- package/dist/email/send.js.map +1 -0
- package/dist/smtp/receiver.d.ts.map +1 -1
- package/dist/smtp/receiver.js +82 -29
- package/dist/smtp/receiver.js.map +1 -1
- package/dist/smtp-server.js +79 -0
- package/dist/smtp-server.js.map +1 -1
- package/package.json +2 -1
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# AgentMailbox
|
|
2
|
+
|
|
3
|
+
Email addresses for AI agents. Zero cost per account.
|
|
4
|
+
|
|
5
|
+
AgentMailbox gives any AI agent its own email address at `@agentmailbox.io`. Agents can receive verification codes, order confirmations, and notifications — no Gmail or Google Workspace fees per account.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g agent-mailbox-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Set your API key
|
|
17
|
+
export AGENT_EMAIL_API_KEY=ae_xxxxx
|
|
18
|
+
|
|
19
|
+
# Create an email account
|
|
20
|
+
agentmailbox account create grocery-bot
|
|
21
|
+
# → grocery-bot@agentmailbox.io created
|
|
22
|
+
|
|
23
|
+
# List inbox
|
|
24
|
+
agentmailbox msg list grocery-bot@agentmailbox.io
|
|
25
|
+
|
|
26
|
+
# Wait for a new email (blocks until one arrives)
|
|
27
|
+
agentmailbox msg wait grocery-bot@agentmailbox.io --timeout 60
|
|
28
|
+
|
|
29
|
+
# Read a message
|
|
30
|
+
agentmailbox msg read grocery-bot@agentmailbox.io <message-id>
|
|
31
|
+
|
|
32
|
+
# Reply
|
|
33
|
+
agentmailbox msg reply grocery-bot@agentmailbox.io <message-id> "Got it, thanks"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
Base URL: `https://agent-email-api-production.up.railway.app`
|
|
39
|
+
|
|
40
|
+
Auth: `Authorization: Bearer ae_xxxxx`
|
|
41
|
+
|
|
42
|
+
### Organizations
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
POST /v1/orgs Create org, get API key
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Accounts
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
POST /v1/accounts Create email account
|
|
52
|
+
GET /v1/accounts List accounts
|
|
53
|
+
DELETE /v1/accounts/:address Delete account
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Messages
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
GET /v1/accounts/:address/messages List inbox
|
|
60
|
+
GET /v1/accounts/:address/messages/:id Read message
|
|
61
|
+
GET /v1/accounts/:address/messages/wait?timeout=30 Wait for new email
|
|
62
|
+
POST /v1/accounts/:address/messages/:id/reply Reply to message
|
|
63
|
+
POST /v1/accounts/:address/send Send new email
|
|
64
|
+
DELETE /v1/accounts/:address/messages/:id Delete message
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Example: Agent Signup Flow
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
const API = 'https://agent-email-api-production.up.railway.app';
|
|
71
|
+
const KEY = 'ae_xxxxx';
|
|
72
|
+
|
|
73
|
+
// 1. Create an email for the agent
|
|
74
|
+
const { account } = await fetch(`${API}/v1/accounts`, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Authorization': `Bearer ${KEY}`, 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({ local_part: 'bot-123' })
|
|
78
|
+
}).then(r => r.json());
|
|
79
|
+
// → bot-123@agentmailbox.io
|
|
80
|
+
|
|
81
|
+
// 2. Sign up for a service using that email
|
|
82
|
+
// ... (agent signs up on grocery site with bot-123@agentmailbox.io)
|
|
83
|
+
|
|
84
|
+
// 3. Wait for verification email
|
|
85
|
+
const { message } = await fetch(
|
|
86
|
+
`${API}/v1/accounts/bot-123@agentmailbox.io/messages/wait?timeout=60`,
|
|
87
|
+
{ headers: { 'Authorization': `Bearer ${KEY}` } }
|
|
88
|
+
).then(r => r.json());
|
|
89
|
+
|
|
90
|
+
// 4. Extract verification code
|
|
91
|
+
const code = message.text.match(/\d{6}/)?.[0];
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Architecture
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Internet ──SMTP:25──▶ Fly.io (SMTP receiver) ──▶ PostgreSQL (Railway)
|
|
98
|
+
▲
|
|
99
|
+
Agents ──HTTPS──▶ Railway (REST API) ─────────────────┘
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- **API**: Fastify on Railway
|
|
103
|
+
- **SMTP**: Node.js smtp-server on Fly.io, port 25
|
|
104
|
+
- **Database**: PostgreSQL on Railway
|
|
105
|
+
- **Domain**: agentmailbox.io (Vercel DNS, MX + SPF + DMARC configured)
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Start Postgres
|
|
111
|
+
docker compose up -d
|
|
112
|
+
|
|
113
|
+
# Init database
|
|
114
|
+
npm run db:init
|
|
115
|
+
|
|
116
|
+
# Run everything locally
|
|
117
|
+
npm run dev
|
|
118
|
+
|
|
119
|
+
# Or run API and SMTP separately
|
|
120
|
+
npm run dev:api
|
|
121
|
+
npm run dev:smtp
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Deploy
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# API → Railway
|
|
128
|
+
railway up -s agent-email-api
|
|
129
|
+
|
|
130
|
+
# SMTP → Fly.io
|
|
131
|
+
fly deploy --app agent-email-smtp
|
|
132
|
+
```
|
package/dist/api/auth.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function hashToken(key: string): string;
|
|
3
3
|
export declare function generateApiKey(): string;
|
|
4
|
+
export declare function generateMagicToken(): string;
|
|
5
|
+
export declare function generateSessionToken(): string;
|
|
4
6
|
export declare function authenticate(request: FastifyRequest, reply: FastifyReply): Promise<undefined>;
|
|
7
|
+
export declare function authenticateAdmin(request: FastifyRequest, reply: FastifyReply): Promise<undefined>;
|
|
5
8
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/api/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvD,wBAAgB,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,sBAyBpB;AAGD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,sBAiCpB"}
|
package/dist/api/auth.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
import { pool } from '../db/index.js';
|
|
3
|
-
export function
|
|
3
|
+
export function hashToken(key) {
|
|
4
4
|
return crypto.createHash('sha256').update(key).digest('hex');
|
|
5
5
|
}
|
|
6
6
|
export function generateApiKey() {
|
|
7
7
|
return 'ae_' + crypto.randomBytes(24).toString('hex');
|
|
8
8
|
}
|
|
9
|
+
export function generateMagicToken() {
|
|
10
|
+
return String(crypto.randomInt(100000, 999999));
|
|
11
|
+
}
|
|
12
|
+
export function generateSessionToken() {
|
|
13
|
+
return 'as_' + crypto.randomBytes(24).toString('hex');
|
|
14
|
+
}
|
|
15
|
+
// Org-level API key auth (existing)
|
|
9
16
|
export async function authenticate(request, reply) {
|
|
10
17
|
const header = request.headers['authorization'] || request.headers['x-api-key'];
|
|
11
18
|
if (!header) {
|
|
@@ -15,11 +22,35 @@ export async function authenticate(request, reply) {
|
|
|
15
22
|
if (!key) {
|
|
16
23
|
return reply.code(401).send({ error: 'Invalid API key' });
|
|
17
24
|
}
|
|
18
|
-
const hash =
|
|
25
|
+
const hash = hashToken(key);
|
|
19
26
|
const result = await pool.query('SELECT id, name FROM orgs WHERE api_key_hash = $1', [hash]);
|
|
20
27
|
if (result.rows.length === 0) {
|
|
21
28
|
return reply.code(401).send({ error: 'Invalid API key' });
|
|
22
29
|
}
|
|
23
30
|
request.org = result.rows[0];
|
|
24
31
|
}
|
|
32
|
+
// Admin session auth
|
|
33
|
+
export async function authenticateAdmin(request, reply) {
|
|
34
|
+
const header = request.headers['authorization'];
|
|
35
|
+
if (!header) {
|
|
36
|
+
return reply.code(401).send({ error: 'Missing session token. Run: agentmailbox login' });
|
|
37
|
+
}
|
|
38
|
+
const token = typeof header === 'string' ? header.replace(/^Bearer\s+/i, '') : '';
|
|
39
|
+
if (!token || !token.startsWith('as_')) {
|
|
40
|
+
return reply.code(401).send({ error: 'Invalid session token' });
|
|
41
|
+
}
|
|
42
|
+
const hash = hashToken(token);
|
|
43
|
+
const result = await pool.query(`SELECT s.id, s.admin_user_id, s.expires_at, u.email
|
|
44
|
+
FROM admin_sessions s
|
|
45
|
+
JOIN admin_users u ON u.id = s.admin_user_id
|
|
46
|
+
WHERE s.token_hash = $1`, [hash]);
|
|
47
|
+
if (result.rows.length === 0 ||
|
|
48
|
+
new Date(result.rows[0].expires_at) < new Date()) {
|
|
49
|
+
return reply.code(401).send({ error: 'Session expired. Run: agentmailbox login' });
|
|
50
|
+
}
|
|
51
|
+
request.admin = {
|
|
52
|
+
id: result.rows[0].admin_user_id,
|
|
53
|
+
email: result.rows[0].email,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
25
56
|
//# sourceMappingURL=auth.js.map
|
package/dist/api/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/api/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA2B1C,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,iBAkJxD"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { pool } from '../../db/index.js';
|
|
2
|
+
import { hashToken, generateMagicToken, generateSessionToken, authenticateAdmin, } from '../auth.js';
|
|
3
|
+
import { sendMagicLinkEmail } from '../../email/send.js';
|
|
4
|
+
import { config } from '../../config.js';
|
|
5
|
+
async function sendMagicLink(adminUserId, email) {
|
|
6
|
+
const token = generateMagicToken();
|
|
7
|
+
const hash = hashToken(token);
|
|
8
|
+
const expiresAt = new Date(Date.now() + config.auth.magicLinkTtlMinutes * 60 * 1000);
|
|
9
|
+
await pool.query(`INSERT INTO magic_link_tokens (admin_user_id, token_hash, expires_at) VALUES ($1, $2, $3)`, [adminUserId, hash, expiresAt]);
|
|
10
|
+
await sendMagicLinkEmail(email, token);
|
|
11
|
+
return { emailSent: true };
|
|
12
|
+
}
|
|
13
|
+
export async function authRoutes(fastify) {
|
|
14
|
+
// Signup
|
|
15
|
+
fastify.post('/v1/auth/signup', async (request, reply) => {
|
|
16
|
+
const { email } = request.body || {};
|
|
17
|
+
if (!email || !email.includes('@')) {
|
|
18
|
+
return reply.code(400).send({ error: 'Valid email is required' });
|
|
19
|
+
}
|
|
20
|
+
const normalized = email.toLowerCase().trim();
|
|
21
|
+
// Check if already exists
|
|
22
|
+
const existing = await pool.query('SELECT id FROM admin_users WHERE email = $1', [normalized]);
|
|
23
|
+
if (existing.rows.length > 0) {
|
|
24
|
+
return reply
|
|
25
|
+
.code(409)
|
|
26
|
+
.send({ error: 'Account already exists. Use: agentmailbox login' });
|
|
27
|
+
}
|
|
28
|
+
const result = await pool.query('INSERT INTO admin_users (email) VALUES ($1) RETURNING id', [normalized]);
|
|
29
|
+
await sendMagicLink(result.rows[0].id, normalized);
|
|
30
|
+
return reply.code(201).send({
|
|
31
|
+
message: 'Check your email for a 6-digit verification code.',
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
// Login
|
|
35
|
+
fastify.post('/v1/auth/login', async (request, reply) => {
|
|
36
|
+
const { email } = request.body || {};
|
|
37
|
+
if (!email || !email.includes('@')) {
|
|
38
|
+
return reply.code(400).send({ error: 'Valid email is required' });
|
|
39
|
+
}
|
|
40
|
+
const normalized = email.toLowerCase().trim();
|
|
41
|
+
const result = await pool.query('SELECT id FROM admin_users WHERE email = $1', [normalized]);
|
|
42
|
+
if (result.rows.length === 0) {
|
|
43
|
+
return reply
|
|
44
|
+
.code(404)
|
|
45
|
+
.send({ error: 'No account with this email. Run: agentmailbox signup' });
|
|
46
|
+
}
|
|
47
|
+
await sendMagicLink(result.rows[0].id, normalized);
|
|
48
|
+
return {
|
|
49
|
+
message: 'Check your email for a 6-digit verification code.',
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
// Verify magic link token
|
|
53
|
+
fastify.post('/v1/auth/verify', async (request, reply) => {
|
|
54
|
+
const { token } = request.body || {};
|
|
55
|
+
if (!token) {
|
|
56
|
+
return reply.code(400).send({ error: 'Token is required' });
|
|
57
|
+
}
|
|
58
|
+
const hash = hashToken(token);
|
|
59
|
+
// Atomic: mark used only if valid and unused
|
|
60
|
+
const result = await pool.query(`UPDATE magic_link_tokens
|
|
61
|
+
SET used_at = NOW()
|
|
62
|
+
WHERE token_hash = $1 AND used_at IS NULL AND expires_at > NOW()
|
|
63
|
+
RETURNING admin_user_id`, [hash]);
|
|
64
|
+
if (result.rows.length === 0) {
|
|
65
|
+
return reply
|
|
66
|
+
.code(401)
|
|
67
|
+
.send({ error: 'Invalid or expired code' });
|
|
68
|
+
}
|
|
69
|
+
const adminUserId = result.rows[0].admin_user_id;
|
|
70
|
+
// Create session
|
|
71
|
+
const sessionToken = generateSessionToken();
|
|
72
|
+
const sessionHash = hashToken(sessionToken);
|
|
73
|
+
const expiresAt = new Date(Date.now() + config.auth.sessionTtlDays * 24 * 60 * 60 * 1000);
|
|
74
|
+
await pool.query(`INSERT INTO admin_sessions (admin_user_id, token_hash, expires_at) VALUES ($1, $2, $3)`, [adminUserId, sessionHash, expiresAt]);
|
|
75
|
+
const user = await pool.query('SELECT email FROM admin_users WHERE id = $1', [adminUserId]);
|
|
76
|
+
return {
|
|
77
|
+
session_token: sessionToken,
|
|
78
|
+
admin: { id: adminUserId, email: user.rows[0].email },
|
|
79
|
+
expires_at: expiresAt,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
// Logout
|
|
83
|
+
fastify.post('/v1/auth/logout', async (request, reply) => {
|
|
84
|
+
const header = request.headers['authorization'];
|
|
85
|
+
if (!header) {
|
|
86
|
+
return reply.code(401).send({ error: 'Missing session token' });
|
|
87
|
+
}
|
|
88
|
+
const token = typeof header === 'string' ? header.replace(/^Bearer\s+/i, '') : '';
|
|
89
|
+
if (!token) {
|
|
90
|
+
return reply.code(401).send({ error: 'Invalid session token' });
|
|
91
|
+
}
|
|
92
|
+
const hash = hashToken(token);
|
|
93
|
+
await pool.query('DELETE FROM admin_sessions WHERE token_hash = $1', [hash]);
|
|
94
|
+
return { message: 'Logged out' };
|
|
95
|
+
});
|
|
96
|
+
// Who am I
|
|
97
|
+
fastify.get('/v1/auth/me', { onRequest: authenticateAdmin }, async (request) => {
|
|
98
|
+
const admin = request.admin;
|
|
99
|
+
return { admin };
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/api/routes/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../../src/api/routes/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C,wBAAsB,SAAS,CAAC,OAAO,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"orgs.d.ts","sourceRoot":"","sources":["../../../src/api/routes/orgs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C,wBAAsB,SAAS,CAAC,OAAO,EAAE,eAAe,iBAqEvD"}
|
package/dist/api/routes/orgs.js
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
import { pool } from '../../db/index.js';
|
|
2
|
-
import { generateApiKey,
|
|
2
|
+
import { generateApiKey, hashToken, authenticateAdmin } from '../auth.js';
|
|
3
3
|
export async function orgRoutes(fastify) {
|
|
4
|
-
// Create org —
|
|
5
|
-
fastify.post('/v1/orgs', async (request, reply) => {
|
|
4
|
+
// Create org — requires admin auth
|
|
5
|
+
fastify.post('/v1/orgs', { onRequest: authenticateAdmin }, async (request, reply) => {
|
|
6
|
+
const admin = request.admin;
|
|
6
7
|
const { name } = request.body || {};
|
|
7
8
|
if (!name) {
|
|
8
9
|
return reply.code(400).send({ error: 'name is required' });
|
|
9
10
|
}
|
|
10
11
|
const apiKey = generateApiKey();
|
|
11
|
-
const hash =
|
|
12
|
-
const result = await pool.query('INSERT INTO orgs (name, api_key_hash) VALUES ($1, $2) RETURNING id, name, created_at', [name, hash]);
|
|
12
|
+
const hash = hashToken(apiKey);
|
|
13
|
+
const result = await pool.query('INSERT INTO orgs (name, api_key_hash, admin_user_id) VALUES ($1, $2, $3) RETURNING id, name, created_at', [name, hash, admin.id]);
|
|
13
14
|
return reply.code(201).send({
|
|
14
15
|
org: result.rows[0],
|
|
15
16
|
api_key: apiKey,
|
|
16
17
|
note: 'Save this API key — it cannot be retrieved again.',
|
|
17
18
|
});
|
|
18
19
|
});
|
|
20
|
+
// List orgs for admin
|
|
21
|
+
fastify.get('/v1/orgs', { onRequest: authenticateAdmin }, async (request) => {
|
|
22
|
+
const admin = request.admin;
|
|
23
|
+
const result = await pool.query('SELECT id, name, created_at FROM orgs WHERE admin_user_id = $1 ORDER BY created_at DESC', [admin.id]);
|
|
24
|
+
return { orgs: result.rows };
|
|
25
|
+
});
|
|
26
|
+
// Rotate API key
|
|
27
|
+
fastify.post('/v1/orgs/:orgId/rotate-key', { onRequest: authenticateAdmin }, async (request, reply) => {
|
|
28
|
+
const admin = request.admin;
|
|
29
|
+
const { orgId } = request.params;
|
|
30
|
+
const apiKey = generateApiKey();
|
|
31
|
+
const hash = hashToken(apiKey);
|
|
32
|
+
const result = await pool.query('UPDATE orgs SET api_key_hash = $1 WHERE id = $2 AND admin_user_id = $3 RETURNING id, name', [hash, orgId, admin.id]);
|
|
33
|
+
if (result.rows.length === 0) {
|
|
34
|
+
return reply.code(404).send({ error: 'Org not found' });
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
org: result.rows[0],
|
|
38
|
+
api_key: apiKey,
|
|
39
|
+
note: 'Save this API key — it cannot be retrieved again.',
|
|
40
|
+
};
|
|
41
|
+
});
|
|
19
42
|
}
|
|
20
43
|
//# sourceMappingURL=orgs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../../src/api/routes/orgs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"orgs.js","sourceRoot":"","sources":["../../../src/api/routes/orgs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAwB;IACtD,mCAAmC;IACnC,OAAO,CAAC,IAAI,CAET,UAAU,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACxE,MAAM,KAAK,GAAI,OAAe,CAAC,KAAK,CAAC;QACrC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,yGAAyG,EACzG,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,CACvB,CAAC;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACnB,OAAO,EAAE,MAAM;YACf,IAAI,EAAE,mDAAmD;SAC1D,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,sBAAsB;IACtB,OAAO,CAAC,GAAG,CACT,UAAU,EACV,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAChC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,KAAK,GAAI,OAAe,CAAC,KAAK,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,yFAAyF,EACzF,CAAC,KAAK,CAAC,EAAE,CAAC,CACX,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC,CACF,CAAC;IAEF,iBAAiB;IACjB,OAAO,CAAC,IAAI,CAGV,4BAA4B,EAC5B,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAChC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,KAAK,GAAI,OAAe,CAAC,KAAK,CAAC;QACrC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAEjC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,2FAA2F,EAC3F,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CACxB,CAAC;QAEF,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,OAAO;YACL,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACnB,OAAO,EAAE,MAAM;YACf,IAAI,EAAE,mDAAmD;SAC1D,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/dist/api/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAQA,wBAAsB,eAAe;;;GA4BpC"}
|
package/dist/api/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { config } from '../config.js';
|
|
|
4
4
|
import { orgRoutes } from './routes/orgs.js';
|
|
5
5
|
import { accountRoutes } from './routes/accounts.js';
|
|
6
6
|
import { messageRoutes } from './routes/messages.js';
|
|
7
|
+
import { authRoutes } from './routes/auth.js';
|
|
7
8
|
export async function createApiServer() {
|
|
8
9
|
const fastify = Fastify({
|
|
9
10
|
logger: true,
|
|
@@ -12,6 +13,7 @@ export async function createApiServer() {
|
|
|
12
13
|
// Health check
|
|
13
14
|
fastify.get('/health', async () => ({ status: 'ok' }));
|
|
14
15
|
// Routes
|
|
16
|
+
await fastify.register(authRoutes);
|
|
15
17
|
await fastify.register(orgRoutes);
|
|
16
18
|
await fastify.register(accountRoutes);
|
|
17
19
|
await fastify.register(messageRoutes);
|
package/dist/api/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,eAAe,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC;QACtB,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7B,eAAe;IACf,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEvD,SAAS;IACT,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAEtC,OAAO;QACL,KAAK,CAAC,KAAK;YACT,MAAM,OAAO,CAAC,MAAM,CAAC;gBACnB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI;gBACrB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI;aACtB,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,2BAA2B,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface CliConfig {
|
|
2
|
+
api_url: string;
|
|
3
|
+
session_token?: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadConfig(): CliConfig;
|
|
7
|
+
export declare function saveConfig(config: CliConfig): void;
|
|
8
|
+
export declare function clearSession(): void;
|
|
9
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,IAAI,SAAS,CAWtC;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAGlD;AAED,wBAAgB,YAAY,IAAI,IAAI,CAKnC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.agentmailbox');
|
|
5
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
export function loadConfig() {
|
|
7
|
+
try {
|
|
8
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {
|
|
13
|
+
api_url: process.env.AGENT_EMAIL_API_URL ||
|
|
14
|
+
'https://agent-email-api-production.up.railway.app',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function saveConfig(config) {
|
|
19
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
20
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
|
|
21
|
+
}
|
|
22
|
+
export function clearSession() {
|
|
23
|
+
const config = loadConfig();
|
|
24
|
+
delete config.session_token;
|
|
25
|
+
delete config.email;
|
|
26
|
+
saveConfig(config);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;AAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAQzD,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EACL,OAAO,CAAC,GAAG,CAAC,mBAAmB;gBAC/B,mDAAmD;SACtD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,aAAa,CAAC;IAC5B,OAAO,MAAM,CAAC,KAAK,CAAC;IACpB,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC"}
|