@agentlair/sdk 0.1.0 → 0.3.0
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 +150 -90
- package/dist/client.d.ts +299 -94
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +479 -159
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +10 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -18
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +273 -0
- package/dist/index.test.js.map +1 -0
- package/dist/types.d.ts +235 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -1,241 +1,561 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @agentlair/sdk — AgentLairClient
|
|
2
|
+
* @agentlair/sdk — AgentLair + AgentLairClient
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `AgentLair` is the primary class. Accepts an API key string directly.
|
|
5
|
+
* `AgentLairClient` is the legacy class (accepts options object) — still fully supported.
|
|
6
6
|
*
|
|
7
|
-
* @example
|
|
7
|
+
* @example Three-line onboarding
|
|
8
|
+
* const lair = new AgentLair(process.env.AGENTLAIR_API_KEY!);
|
|
9
|
+
* const inbox = await lair.email.claim('my-agent'); // auto-expands to my-agent@agentlair.dev
|
|
10
|
+
* const { messages } = await lair.email.inbox('my-agent@agentlair.dev');
|
|
11
|
+
*
|
|
12
|
+
* @example Full flow
|
|
8
13
|
* // Create a new account (no API key needed)
|
|
9
|
-
* const { api_key
|
|
14
|
+
* const { api_key } = await AgentLair.createAccount({ name: 'my-agent' });
|
|
15
|
+
*
|
|
16
|
+
* const lair = new AgentLair(api_key);
|
|
17
|
+
* await lair.email.claim('my-agent');
|
|
18
|
+
* await lair.email.send({ from: 'my-agent@agentlair.dev', to: 'user@example.com', subject: 'Hello', text: 'Hi!' });
|
|
19
|
+
* const { messages } = await lair.email.inbox('my-agent@agentlair.dev');
|
|
10
20
|
*
|
|
11
|
-
* @
|
|
12
|
-
*
|
|
13
|
-
* const
|
|
14
|
-
* await client.claimAddress({ address: 'my-agent@agentlair.dev' });
|
|
15
|
-
* await client.sendEmail({ from: 'my-agent@agentlair.dev', to: 'user@example.com', subject: 'Hello', text: 'Hi!' });
|
|
16
|
-
* const inbox = await client.getInbox({ address: 'my-agent@agentlair.dev' });
|
|
21
|
+
* // Vault: store secrets (zero-knowledge — use @agentlair/vault-crypto to encrypt first)
|
|
22
|
+
* await lair.vault.put('openai-key', { ciphertext: encryptedBlob });
|
|
23
|
+
* const { ciphertext } = await lair.vault.get('openai-key');
|
|
17
24
|
*/
|
|
18
25
|
import { AgentLairError } from './errors.js';
|
|
19
26
|
const DEFAULT_BASE_URL = 'https://agentlair.dev';
|
|
20
|
-
// ───
|
|
27
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
21
28
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* The AgentLair Vault is a zero-knowledge secret store.
|
|
25
|
-
* The server stores opaque encrypted blobs — plaintext never leaves your client.
|
|
26
|
-
* Use @agentlair/vault-crypto for client-side encryption helpers.
|
|
29
|
+
* Expand a short name like "my-agent" to "my-agent@agentlair.dev".
|
|
30
|
+
* If the address already contains "@", returns it as-is.
|
|
27
31
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
function expandAddress(address) {
|
|
33
|
+
return address.includes('@') ? address : `${address}@agentlair.dev`;
|
|
34
|
+
}
|
|
35
|
+
// ─── Internal request helper (shared by both classes) ─────────────────────────
|
|
36
|
+
async function makeRequest(baseUrl, apiKey, method, path, opts = {}) {
|
|
37
|
+
const url = new URL(baseUrl + path);
|
|
38
|
+
if (opts.query) {
|
|
39
|
+
for (const [k, v] of Object.entries(opts.query)) {
|
|
40
|
+
url.searchParams.set(k, v);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const headers = {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
};
|
|
46
|
+
if (opts.auth !== null) {
|
|
47
|
+
headers['Authorization'] = `Bearer ${opts.auth ?? apiKey}`;
|
|
48
|
+
}
|
|
49
|
+
const response = await fetch(url.toString(), {
|
|
50
|
+
method,
|
|
51
|
+
headers,
|
|
52
|
+
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
53
|
+
});
|
|
54
|
+
let data;
|
|
55
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
56
|
+
if (contentType.includes('application/json')) {
|
|
57
|
+
data = await response.json();
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
data = await response.text();
|
|
61
|
+
}
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const body = data;
|
|
64
|
+
throw new AgentLairError(body?.message ?? `HTTP ${response.status}`, response.status, body?.error ?? 'error');
|
|
65
|
+
}
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
// ─── WebhooksNamespace ────────────────────────────────────────────────────────
|
|
69
|
+
class WebhooksNamespace {
|
|
70
|
+
_req;
|
|
71
|
+
constructor(_req) {
|
|
72
|
+
this._req = _req;
|
|
32
73
|
}
|
|
33
74
|
/**
|
|
34
|
-
*
|
|
75
|
+
* Register a webhook URL to receive real-time `email.received` events.
|
|
35
76
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
77
|
+
* @example
|
|
78
|
+
* const hook = await lair.email.webhooks.create({
|
|
79
|
+
* address: 'my-agent@agentlair.dev',
|
|
80
|
+
* url: 'https://myserver.com/webhook',
|
|
81
|
+
* secret: 'my-secret',
|
|
82
|
+
* });
|
|
83
|
+
*/
|
|
84
|
+
async create(options) {
|
|
85
|
+
return this._req('POST', '/v1/email/webhooks', {
|
|
86
|
+
body: { ...options, address: expandAddress(options.address) },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* List registered webhooks, optionally filtered by address.
|
|
38
91
|
*
|
|
39
92
|
* @example
|
|
40
|
-
*
|
|
41
|
-
* const vc = VaultCrypto.fromSeed(seed);
|
|
42
|
-
* const ciphertext = await vc.encrypt('sk-openai-...', 'openai-key');
|
|
43
|
-
* await client.vault.put('openai-key', { ciphertext });
|
|
93
|
+
* const { webhooks } = await lair.email.webhooks.list();
|
|
44
94
|
*/
|
|
45
|
-
async
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
async list(options = {}) {
|
|
96
|
+
const query = {};
|
|
97
|
+
if (options.address)
|
|
98
|
+
query.address = expandAddress(options.address);
|
|
99
|
+
return this._req('GET', '/v1/email/webhooks', { query });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Delete a webhook by ID.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* await lair.email.webhooks.delete('wh_abc123');
|
|
106
|
+
*/
|
|
107
|
+
async delete(id) {
|
|
108
|
+
return this._req('DELETE', `/v1/email/webhooks/${encodeURIComponent(id)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ─── EmailNamespace ───────────────────────────────────────────────────────────
|
|
112
|
+
class EmailNamespace {
|
|
113
|
+
_req;
|
|
114
|
+
/** Webhook management: webhooks.create / webhooks.list / webhooks.delete */
|
|
115
|
+
webhooks;
|
|
116
|
+
constructor(_req) {
|
|
117
|
+
this._req = _req;
|
|
118
|
+
this.webhooks = new WebhooksNamespace(_req);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Claim an @agentlair.dev email address.
|
|
122
|
+
*
|
|
123
|
+
* Pass a short name (e.g. `"my-agent"`) and it auto-expands to `my-agent@agentlair.dev`.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* await lair.email.claim('my-agent'); // → my-agent@agentlair.dev
|
|
127
|
+
* await lair.email.claim('my-agent@agentlair.dev'); // also works
|
|
128
|
+
*/
|
|
129
|
+
async claim(address, options = {}) {
|
|
130
|
+
return this._req('POST', '/v1/email/claim', {
|
|
131
|
+
body: { ...options, address: expandAddress(address) },
|
|
48
132
|
});
|
|
49
133
|
}
|
|
50
134
|
/**
|
|
51
|
-
*
|
|
135
|
+
* List all @agentlair.dev addresses claimed by this account.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* const { addresses } = await lair.email.addresses();
|
|
139
|
+
*/
|
|
140
|
+
async addresses() {
|
|
141
|
+
return this._req('GET', '/v1/email/addresses');
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get inbox messages (previews — no full body).
|
|
145
|
+
* Use `read()` for the full body of a specific message.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* const { messages } = await lair.email.inbox('my-agent@agentlair.dev');
|
|
149
|
+
* const { messages } = await lair.email.inbox('my-agent', { limit: 5 });
|
|
150
|
+
*/
|
|
151
|
+
async inbox(address, options = {}) {
|
|
152
|
+
const query = { address: expandAddress(address) };
|
|
153
|
+
if (options.limit !== undefined)
|
|
154
|
+
query.limit = String(options.limit);
|
|
155
|
+
return this._req('GET', '/v1/email/inbox', { query });
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Read the full body of a message. Marks as read.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* const msg = await lair.email.read(inboxMsg.message_id_url, 'my-agent@agentlair.dev');
|
|
162
|
+
* console.log(msg.body);
|
|
163
|
+
*/
|
|
164
|
+
async read(messageId, address) {
|
|
165
|
+
return this._req('GET', `/v1/email/messages/${messageId}`, { query: { address: expandAddress(address) } });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Delete a message.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* await lair.email.deleteMessage(inboxMsg.message_id_url, 'my-agent@agentlair.dev');
|
|
172
|
+
*/
|
|
173
|
+
async deleteMessage(messageId, address) {
|
|
174
|
+
return this._req('DELETE', `/v1/email/messages/${messageId}`, { query: { address: expandAddress(address) } });
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Update message properties (mark read/unread).
|
|
52
178
|
*
|
|
53
|
-
*
|
|
179
|
+
* @example
|
|
180
|
+
* await lair.email.update(msg.message_id_url, 'my-agent@agentlair.dev', { read: false });
|
|
181
|
+
*/
|
|
182
|
+
async update(messageId, address, options) {
|
|
183
|
+
return this._req('PATCH', `/v1/email/messages/${messageId}`, { query: { address: expandAddress(address) }, body: options });
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Send an email from an address you own.
|
|
54
187
|
*
|
|
55
188
|
* @example
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
189
|
+
* await lair.email.send({
|
|
190
|
+
* from: 'my-agent@agentlair.dev',
|
|
191
|
+
* to: 'user@example.com',
|
|
192
|
+
* subject: 'Hello',
|
|
193
|
+
* text: 'Hi from an AI agent!',
|
|
194
|
+
* });
|
|
195
|
+
*/
|
|
196
|
+
async send(options) {
|
|
197
|
+
return this._req('POST', '/v1/email/send', { body: options });
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* List sent messages (outbox).
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* const { messages } = await lair.email.outbox({ limit: 5 });
|
|
204
|
+
*/
|
|
205
|
+
async outbox(options = {}) {
|
|
206
|
+
const query = {};
|
|
207
|
+
if (options.limit !== undefined)
|
|
208
|
+
query.limit = String(options.limit);
|
|
209
|
+
return this._req('GET', '/v1/email/outbox', { query });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// ─── VaultNamespace ───────────────────────────────────────────────────────────
|
|
213
|
+
/**
|
|
214
|
+
* Zero-knowledge secret store.
|
|
215
|
+
* Server stores opaque encrypted blobs — plaintext never leaves your client.
|
|
216
|
+
* Use @agentlair/vault-crypto for client-side encryption.
|
|
217
|
+
*/
|
|
218
|
+
class VaultNamespace {
|
|
219
|
+
_req;
|
|
220
|
+
constructor(_req) {
|
|
221
|
+
this._req = _req;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Store an encrypted blob (versioned, append-only).
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* await lair.vault.put('openai-key', { ciphertext: encryptedBlob });
|
|
228
|
+
*/
|
|
229
|
+
async put(key, options) {
|
|
230
|
+
return this._req('PUT', `/v1/vault/${encodeURIComponent(key)}`, { body: options });
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Retrieve an encrypted blob. Returns latest version by default.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* const { ciphertext } = await lair.vault.get('openai-key');
|
|
237
|
+
* const old = await lair.vault.get('openai-key', { version: 1 });
|
|
59
238
|
*/
|
|
60
239
|
async get(key, options = {}) {
|
|
61
240
|
const query = {};
|
|
62
241
|
if (options.version !== undefined)
|
|
63
242
|
query.version = String(options.version);
|
|
64
|
-
return this.
|
|
243
|
+
return this._req('GET', `/v1/vault/${encodeURIComponent(key)}`, { query });
|
|
65
244
|
}
|
|
66
245
|
/**
|
|
67
|
-
* List all Vault keys
|
|
246
|
+
* List all Vault keys (metadata only).
|
|
68
247
|
*
|
|
69
248
|
* @example
|
|
70
|
-
* const { keys, count, limit } = await
|
|
249
|
+
* const { keys, count, limit } = await lair.vault.list();
|
|
71
250
|
*/
|
|
72
251
|
async list() {
|
|
73
|
-
return this.
|
|
252
|
+
return this._req('GET', '/v1/vault/');
|
|
74
253
|
}
|
|
75
254
|
/**
|
|
76
|
-
* Delete a
|
|
77
|
-
*
|
|
78
|
-
* Pass `version` to delete only that version. Omit to delete all versions.
|
|
255
|
+
* Delete a key (all versions) or a specific version.
|
|
79
256
|
*
|
|
80
257
|
* @example
|
|
81
|
-
* await
|
|
82
|
-
* await
|
|
258
|
+
* await lair.vault.delete('openai-key');
|
|
259
|
+
* await lair.vault.delete('openai-key', { version: 2 });
|
|
83
260
|
*/
|
|
84
261
|
async delete(key, options = {}) {
|
|
85
262
|
const query = {};
|
|
86
263
|
if (options.version !== undefined)
|
|
87
264
|
query.version = String(options.version);
|
|
88
|
-
return this.
|
|
89
|
-
query,
|
|
90
|
-
});
|
|
265
|
+
return this._req('DELETE', `/v1/vault/${encodeURIComponent(key)}`, { query });
|
|
91
266
|
}
|
|
92
267
|
}
|
|
93
|
-
// ───
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
268
|
+
// ─── StacksNamespace ──────────────────────────────────────────────────────────
|
|
269
|
+
class StacksNamespace {
|
|
270
|
+
_req;
|
|
271
|
+
constructor(_req) {
|
|
272
|
+
this._req = _req;
|
|
273
|
+
}
|
|
97
274
|
/**
|
|
98
|
-
*
|
|
275
|
+
* Create a domain stack. Points nameservers to AgentLair DNS.
|
|
276
|
+
* Beta: DNS provisioning is stubbed. Full CF DNS integration coming Q2 2026.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* const stack = await lair.stacks.create({ domain: 'myagent.dev' });
|
|
99
280
|
*/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this._apiKey = options.apiKey;
|
|
103
|
-
this._baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
104
|
-
this.vault = new VaultNamespace(this._request.bind(this));
|
|
105
|
-
}
|
|
106
|
-
// ─── Internal request helper ─────────────────────────────────────────────
|
|
107
|
-
async _request(method, path, opts = {}) {
|
|
108
|
-
const url = new URL(this._baseUrl + path);
|
|
109
|
-
if (opts.query) {
|
|
110
|
-
for (const [k, v] of Object.entries(opts.query)) {
|
|
111
|
-
url.searchParams.set(k, v);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const headers = {
|
|
115
|
-
'Content-Type': 'application/json',
|
|
116
|
-
};
|
|
117
|
-
// auth defaults to Bearer <apiKey>; pass null to skip auth
|
|
118
|
-
if (opts.auth !== null) {
|
|
119
|
-
headers['Authorization'] = `Bearer ${opts.auth ?? this._apiKey}`;
|
|
120
|
-
}
|
|
121
|
-
const response = await fetch(url.toString(), {
|
|
122
|
-
method,
|
|
123
|
-
headers,
|
|
124
|
-
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
125
|
-
});
|
|
126
|
-
let data;
|
|
127
|
-
const contentType = response.headers.get('content-type') ?? '';
|
|
128
|
-
if (contentType.includes('application/json')) {
|
|
129
|
-
data = await response.json();
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
data = await response.text();
|
|
133
|
-
}
|
|
134
|
-
if (!response.ok) {
|
|
135
|
-
const body = data;
|
|
136
|
-
throw new AgentLairError(body?.message ?? `HTTP ${response.status}`, response.status, body?.error ?? 'error');
|
|
137
|
-
}
|
|
138
|
-
return data;
|
|
281
|
+
async create(options) {
|
|
282
|
+
return this._req('POST', '/v1/stack', { body: options });
|
|
139
283
|
}
|
|
140
|
-
// ─── Static: createAccount ───────────────────────────────────────────────
|
|
141
284
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* No existing account or API key needed — this is the bootstrapping method.
|
|
145
|
-
* **Save the returned `api_key` immediately** — it will not be shown again.
|
|
146
|
-
*
|
|
147
|
-
* @param options Optional name and recovery email
|
|
148
|
-
* @param baseUrl Override API base URL (default: https://agentlair.dev)
|
|
285
|
+
* List all domain stacks for this account.
|
|
149
286
|
*
|
|
150
287
|
* @example
|
|
151
|
-
* const {
|
|
152
|
-
* // Store api_key securely — never log it
|
|
153
|
-
* const client = new AgentLairClient({ apiKey: api_key });
|
|
288
|
+
* const { stacks } = await lair.stacks.list();
|
|
154
289
|
*/
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
throw new AgentLairError(data.message ?? `HTTP ${response.status}`, response.status, data.error ?? 'error');
|
|
165
|
-
}
|
|
166
|
-
return data;
|
|
290
|
+
async list() {
|
|
291
|
+
return this._req('GET', '/v1/stack');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// ─── CalendarNamespace ────────────────────────────────────────────────────────
|
|
295
|
+
class CalendarNamespace {
|
|
296
|
+
_req;
|
|
297
|
+
constructor(_req) {
|
|
298
|
+
this._req = _req;
|
|
167
299
|
}
|
|
168
|
-
// ─── Email: claimAddress ─────────────────────────────────────────────────
|
|
169
300
|
/**
|
|
170
|
-
*
|
|
301
|
+
* Create a new calendar event.
|
|
171
302
|
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
303
|
+
* @example
|
|
304
|
+
* const event = await lair.calendar.createEvent({
|
|
305
|
+
* summary: 'Team Standup',
|
|
306
|
+
* start: '2026-03-21T10:00:00Z',
|
|
307
|
+
* end: '2026-03-21T10:30:00Z',
|
|
308
|
+
* location: 'Zoom',
|
|
309
|
+
* attendees: ['alice@example.com'],
|
|
310
|
+
* });
|
|
311
|
+
*/
|
|
312
|
+
async createEvent(options) {
|
|
313
|
+
return this._req('POST', '/v1/calendar/events', { body: options });
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* List calendar events. Optionally filter by date range.
|
|
177
317
|
*
|
|
178
318
|
* @example
|
|
179
|
-
* await
|
|
319
|
+
* const { events } = await lair.calendar.listEvents({ from: '2026-03-01', to: '2026-03-31' });
|
|
180
320
|
*/
|
|
181
|
-
async
|
|
182
|
-
|
|
321
|
+
async listEvents(options = {}) {
|
|
322
|
+
const query = {};
|
|
323
|
+
if (options.from)
|
|
324
|
+
query.from = options.from;
|
|
325
|
+
if (options.to)
|
|
326
|
+
query.to = options.to;
|
|
327
|
+
if (options.limit !== undefined)
|
|
328
|
+
query.limit = String(options.limit);
|
|
329
|
+
return this._req('GET', '/v1/calendar/events', { query });
|
|
183
330
|
}
|
|
184
|
-
// ─── Email: sendEmail ────────────────────────────────────────────────────
|
|
185
331
|
/**
|
|
186
|
-
*
|
|
332
|
+
* Delete a calendar event by ID.
|
|
187
333
|
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
334
|
+
* @example
|
|
335
|
+
* await lair.calendar.deleteEvent('evt_abc123');
|
|
336
|
+
*/
|
|
337
|
+
async deleteEvent(id) {
|
|
338
|
+
return this._req('DELETE', `/v1/calendar/events/${encodeURIComponent(id)}`);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get (or generate) the public iCal subscription URL for this calendar.
|
|
342
|
+
* Share this URL with Google Calendar / Apple Calendar / Outlook to subscribe.
|
|
190
343
|
*
|
|
191
344
|
* @example
|
|
192
|
-
* await
|
|
193
|
-
* from: 'my-agent@agentlair.dev',
|
|
194
|
-
* to: 'user@example.com',
|
|
195
|
-
* subject: 'Hello from AgentLair',
|
|
196
|
-
* text: 'Hi! This email was sent by an AI agent.',
|
|
197
|
-
* });
|
|
345
|
+
* const { feed_url } = await lair.calendar.getFeed();
|
|
198
346
|
*/
|
|
199
|
-
async
|
|
200
|
-
return this.
|
|
347
|
+
async getFeed() {
|
|
348
|
+
return this._req('GET', '/v1/calendar/feed');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// ─── ObservationsNamespace ────────────────────────────────────────────────────
|
|
352
|
+
class ObservationsNamespace {
|
|
353
|
+
_req;
|
|
354
|
+
constructor(_req) {
|
|
355
|
+
this._req = _req;
|
|
201
356
|
}
|
|
202
|
-
// ─── Email: getInbox ─────────────────────────────────────────────────────
|
|
203
357
|
/**
|
|
204
|
-
*
|
|
358
|
+
* Write a structured observation. Account-scoped by default.
|
|
359
|
+
* Set `shared: true` to make visible to all authenticated agents.
|
|
205
360
|
*
|
|
206
|
-
*
|
|
361
|
+
* @example
|
|
362
|
+
* await lair.observations.write({ topic: 'market-signals', content: 'BTC up 5%' });
|
|
363
|
+
*/
|
|
364
|
+
async write(options) {
|
|
365
|
+
return this._req('POST', '/v1/observations', { body: options });
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Read observations. Returns own + shared by default.
|
|
207
369
|
*
|
|
208
370
|
* @example
|
|
209
|
-
* const {
|
|
210
|
-
*
|
|
211
|
-
* console.log(msg.from, msg.subject, msg.snippet);
|
|
212
|
-
* }
|
|
371
|
+
* const { observations } = await lair.observations.read({ topic: 'market-signals' });
|
|
372
|
+
* const shared = await lair.observations.read({ scope: 'shared' });
|
|
213
373
|
*/
|
|
214
|
-
async
|
|
215
|
-
const query = {
|
|
374
|
+
async read(options = {}) {
|
|
375
|
+
const query = {};
|
|
376
|
+
if (options.topic)
|
|
377
|
+
query.topic = options.topic;
|
|
378
|
+
if (options.agent_id)
|
|
379
|
+
query.agent_id = options.agent_id;
|
|
380
|
+
if (options.since)
|
|
381
|
+
query.since = options.since;
|
|
382
|
+
if (options.scope)
|
|
383
|
+
query.scope = options.scope;
|
|
216
384
|
if (options.limit !== undefined)
|
|
217
385
|
query.limit = String(options.limit);
|
|
218
|
-
return this.
|
|
386
|
+
return this._req('GET', '/v1/observations', { query });
|
|
219
387
|
}
|
|
220
|
-
// ─── Email: readMessage ──────────────────────────────────────────────────
|
|
221
388
|
/**
|
|
222
|
-
*
|
|
389
|
+
* List distinct topics with observation count.
|
|
223
390
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
391
|
+
* @example
|
|
392
|
+
* const { topics } = await lair.observations.topics();
|
|
393
|
+
*/
|
|
394
|
+
async topics() {
|
|
395
|
+
return this._req('GET', '/v1/observations/topics');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// ─── AccountNamespace ─────────────────────────────────────────────────────────
|
|
399
|
+
class AccountNamespace {
|
|
400
|
+
_req;
|
|
401
|
+
constructor(_req) {
|
|
402
|
+
this._req = _req;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get the current account profile.
|
|
226
406
|
*
|
|
227
|
-
* @
|
|
228
|
-
*
|
|
407
|
+
* @example
|
|
408
|
+
* const { account_id, tier } = await lair.account.me();
|
|
409
|
+
*/
|
|
410
|
+
async me() {
|
|
411
|
+
return this._req('GET', '/v1/account/me');
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get current usage statistics.
|
|
229
415
|
*
|
|
230
416
|
* @example
|
|
231
|
-
* const
|
|
232
|
-
*
|
|
233
|
-
* address: 'my-agent@agentlair.dev',
|
|
234
|
-
* });
|
|
235
|
-
* console.log(msg.body);
|
|
417
|
+
* const { emails } = await lair.account.usage();
|
|
418
|
+
* console.log(emails.daily_remaining);
|
|
236
419
|
*/
|
|
420
|
+
async usage() {
|
|
421
|
+
return this._req('GET', '/v1/usage');
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get billing information.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* const billing = await lair.account.billing();
|
|
428
|
+
*/
|
|
429
|
+
async billing() {
|
|
430
|
+
return this._req('GET', '/v1/billing');
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// ─── AgentLair (primary class) ────────────────────────────────────────────────
|
|
434
|
+
/**
|
|
435
|
+
* AgentLair — primary SDK class.
|
|
436
|
+
*
|
|
437
|
+
* Accepts an API key string directly (or an options object for advanced config).
|
|
438
|
+
*
|
|
439
|
+
* @example Three-line onboarding
|
|
440
|
+
* const lair = new AgentLair(process.env.AGENTLAIR_API_KEY!);
|
|
441
|
+
* const inbox = await lair.email.claim('my-agent');
|
|
442
|
+
* const { messages } = await lair.email.inbox('my-agent@agentlair.dev');
|
|
443
|
+
*/
|
|
444
|
+
export class AgentLair {
|
|
445
|
+
_apiKey;
|
|
446
|
+
_baseUrl;
|
|
447
|
+
/** Email: claim / inbox / send / read / deleteMessage / update / outbox / addresses / webhooks */
|
|
448
|
+
email;
|
|
449
|
+
/** Vault: put / get / list / delete */
|
|
450
|
+
vault;
|
|
451
|
+
/** Stacks: create / list */
|
|
452
|
+
stacks;
|
|
453
|
+
/** Calendar: createEvent / listEvents / deleteEvent / getFeed */
|
|
454
|
+
calendar;
|
|
455
|
+
/** Observations: write / read / topics */
|
|
456
|
+
observations;
|
|
457
|
+
/** Account: me / usage / billing */
|
|
458
|
+
account;
|
|
459
|
+
/**
|
|
460
|
+
* @param apiKeyOrOptions API key string (e.g. "al_live_...") or options object
|
|
461
|
+
*/
|
|
462
|
+
constructor(apiKeyOrOptions) {
|
|
463
|
+
if (typeof apiKeyOrOptions === 'string') {
|
|
464
|
+
this._apiKey = apiKeyOrOptions;
|
|
465
|
+
this._baseUrl = DEFAULT_BASE_URL;
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
this._apiKey = apiKeyOrOptions.apiKey;
|
|
469
|
+
this._baseUrl = (apiKeyOrOptions.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
470
|
+
}
|
|
471
|
+
const req = this._request.bind(this);
|
|
472
|
+
this.email = new EmailNamespace(req);
|
|
473
|
+
this.vault = new VaultNamespace(req);
|
|
474
|
+
this.stacks = new StacksNamespace(req);
|
|
475
|
+
this.calendar = new CalendarNamespace(req);
|
|
476
|
+
this.observations = new ObservationsNamespace(req);
|
|
477
|
+
this.account = new AccountNamespace(req);
|
|
478
|
+
}
|
|
479
|
+
_request(method, path, opts = {}) {
|
|
480
|
+
return makeRequest(this._baseUrl, this._apiKey, method, path, opts);
|
|
481
|
+
}
|
|
482
|
+
// ─── Static: createAccount ─────────────────────────────────────────────────
|
|
483
|
+
/**
|
|
484
|
+
* Create a new AgentLair account and get an API key.
|
|
485
|
+
* No existing account needed. **Save the returned `api_key` immediately** — not shown again.
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* const { api_key } = await AgentLair.createAccount({ name: 'my-agent' });
|
|
489
|
+
* const lair = new AgentLair(api_key);
|
|
490
|
+
*/
|
|
491
|
+
static async createAccount(options = {}, baseUrl = DEFAULT_BASE_URL) {
|
|
492
|
+
const url = baseUrl.replace(/\/$/, '') + '/v1/auth/keys';
|
|
493
|
+
const response = await fetch(url, {
|
|
494
|
+
method: 'POST',
|
|
495
|
+
headers: { 'Content-Type': 'application/json' },
|
|
496
|
+
body: JSON.stringify(options),
|
|
497
|
+
});
|
|
498
|
+
const data = await response.json();
|
|
499
|
+
if (!response.ok) {
|
|
500
|
+
throw new AgentLairError(data.message ?? `HTTP ${response.status}`, response.status, data.error ?? 'error');
|
|
501
|
+
}
|
|
502
|
+
return data;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// ─── AgentLairClient (legacy — fully backward compatible) ─────────────────────
|
|
506
|
+
/**
|
|
507
|
+
* @deprecated Use `AgentLair` instead — simpler string constructor, same namespaces.
|
|
508
|
+
* `AgentLairClient` is fully retained for backward compatibility.
|
|
509
|
+
*/
|
|
510
|
+
export class AgentLairClient {
|
|
511
|
+
_apiKey;
|
|
512
|
+
_baseUrl;
|
|
513
|
+
/** Vault: put / get / list / delete */
|
|
514
|
+
vault;
|
|
515
|
+
/** Email: claim / inbox / send / read / outbox / addresses / webhooks */
|
|
516
|
+
email;
|
|
517
|
+
/** Stacks: create / list */
|
|
518
|
+
stacks;
|
|
519
|
+
/** Calendar: createEvent / listEvents / deleteEvent / getFeed */
|
|
520
|
+
calendar;
|
|
521
|
+
/** Observations: write / read / topics */
|
|
522
|
+
observations;
|
|
523
|
+
/** Account: me / usage / billing */
|
|
524
|
+
account;
|
|
525
|
+
constructor(options) {
|
|
526
|
+
this._apiKey = options.apiKey;
|
|
527
|
+
this._baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
528
|
+
const req = this._request.bind(this);
|
|
529
|
+
this.vault = new VaultNamespace(req);
|
|
530
|
+
this.email = new EmailNamespace(req);
|
|
531
|
+
this.stacks = new StacksNamespace(req);
|
|
532
|
+
this.calendar = new CalendarNamespace(req);
|
|
533
|
+
this.observations = new ObservationsNamespace(req);
|
|
534
|
+
this.account = new AccountNamespace(req);
|
|
535
|
+
}
|
|
536
|
+
_request(method, path, opts = {}) {
|
|
537
|
+
return makeRequest(this._baseUrl, this._apiKey, method, path, opts);
|
|
538
|
+
}
|
|
539
|
+
/** @deprecated Prefer `AgentLair.createAccount()` */
|
|
540
|
+
static async createAccount(options = {}, baseUrl = DEFAULT_BASE_URL) {
|
|
541
|
+
return AgentLair.createAccount(options, baseUrl);
|
|
542
|
+
}
|
|
543
|
+
// ─── Legacy flat methods (backward compatible) ────────────────────────────
|
|
544
|
+
/** @deprecated Use `client.email.claim(address)` */
|
|
545
|
+
async claimAddress(options) {
|
|
546
|
+
return this.email.claim(options.address, { public_key: options.public_key });
|
|
547
|
+
}
|
|
548
|
+
/** @deprecated Use `client.email.send(options)` */
|
|
549
|
+
async sendEmail(options) {
|
|
550
|
+
return this.email.send(options);
|
|
551
|
+
}
|
|
552
|
+
/** @deprecated Use `client.email.inbox(address, options)` */
|
|
553
|
+
async getInbox(options) {
|
|
554
|
+
return this.email.inbox(options.address, { limit: options.limit });
|
|
555
|
+
}
|
|
556
|
+
/** @deprecated Use `client.email.read(messageId, address)` */
|
|
237
557
|
async readMessage(options) {
|
|
238
|
-
return this.
|
|
558
|
+
return this.email.read(options.messageId, options.address);
|
|
239
559
|
}
|
|
240
560
|
}
|
|
241
561
|
//# sourceMappingURL=client.js.map
|