@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/dist/client.js CHANGED
@@ -1,241 +1,561 @@
1
1
  /**
2
- * @agentlair/sdk — AgentLairClient
2
+ * @agentlair/sdk — AgentLair + AgentLairClient
3
3
  *
4
- * Official TypeScript/JavaScript SDK for AgentLair.
5
- * Zero dependencies. Works in Node 18, Bun, Deno, and modern browsers.
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, account_id } = await AgentLairClient.createAccount({ name: 'my-agent' });
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
- * @example
12
- * // Use an existing key
13
- * const client = new AgentLairClient({ apiKey: process.env.AGENTLAIR_API_KEY! });
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
- // ─── VaultNamespace ───────────────────────────────────────────────────────────
27
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
21
28
  /**
22
- * vault.put / vault.get / vault.list / vault.delete
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
- class VaultNamespace {
29
- _request;
30
- constructor(_request) {
31
- this._request = _request;
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
- * Store an encrypted blob in the Vault.
75
+ * Register a webhook URL to receive real-time `email.received` events.
35
76
  *
36
- * Each PUT creates a new version (append-only, versioned).
37
- * Free tier: up to 50 keys, 3 versions each.
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
- * // With @agentlair/vault-crypto:
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 put(key, options) {
46
- return this._request('PUT', `/v1/vault/${encodeURIComponent(key)}`, {
47
- body: options,
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
- * Retrieve an encrypted blob from the Vault.
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
- * Returns the latest version by default. Pass `version` for a specific one.
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
- * const { ciphertext } = await client.vault.get('openai-key');
57
- * // With @agentlair/vault-crypto:
58
- * const plaintext = await vc.decrypt(ciphertext, 'openai-key');
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._request('GET', `/v1/vault/${encodeURIComponent(key)}`, { query });
243
+ return this._req('GET', `/v1/vault/${encodeURIComponent(key)}`, { query });
65
244
  }
66
245
  /**
67
- * List all Vault keys for this account (metadata only — no ciphertext).
246
+ * List all Vault keys (metadata only).
68
247
  *
69
248
  * @example
70
- * const { keys, count, limit } = await client.vault.list();
249
+ * const { keys, count, limit } = await lair.vault.list();
71
250
  */
72
251
  async list() {
73
- return this._request('GET', '/v1/vault/');
252
+ return this._req('GET', '/v1/vault/');
74
253
  }
75
254
  /**
76
- * Delete a Vault key (all versions) or a specific version.
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 client.vault.delete('openai-key'); // delete all versions
82
- * await client.vault.delete('openai-key', { version: 2 }); // delete v2 only
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._request('DELETE', `/v1/vault/${encodeURIComponent(key)}`, {
89
- query,
90
- });
265
+ return this._req('DELETE', `/v1/vault/${encodeURIComponent(key)}`, { query });
91
266
  }
92
267
  }
93
- // ─── AgentLairClient ──────────────────────────────────────────────────────────
94
- export class AgentLairClient {
95
- _apiKey;
96
- _baseUrl;
268
+ // ─── StacksNamespace ──────────────────────────────────────────────────────────
269
+ class StacksNamespace {
270
+ _req;
271
+ constructor(_req) {
272
+ this._req = _req;
273
+ }
97
274
  /**
98
- * Namespaced Vault operations: vault.put / vault.get / vault.list / vault.delete
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
- vault;
101
- constructor(options) {
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
- * Create a new AgentLair account and get an API key.
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 { api_key, account_id } = await AgentLairClient.createAccount({ name: 'my-agent' });
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
- static async createAccount(options = {}, baseUrl = DEFAULT_BASE_URL) {
156
- const url = baseUrl.replace(/\/$/, '') + '/v1/auth/keys';
157
- const response = await fetch(url, {
158
- method: 'POST',
159
- headers: { 'Content-Type': 'application/json' },
160
- body: JSON.stringify(options),
161
- });
162
- const data = await response.json();
163
- if (!response.ok) {
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
- * Claim an @agentlair.dev email address for this account.
301
+ * Create a new calendar event.
171
302
  *
172
- * First-touch ownership model — the first agent to claim an address owns it.
173
- * DKIM, SPF, and DMARC are pre-configured. Ready to send in under 5 seconds.
174
- *
175
- * Optionally provide a `public_key` (base64url X25519, 32 bytes) to enable
176
- * end-to-end encryption for inbound messages.
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 client.claimAddress({ address: 'my-agent@agentlair.dev' });
319
+ * const { events } = await lair.calendar.listEvents({ from: '2026-03-01', to: '2026-03-31' });
180
320
  */
181
- async claimAddress(options) {
182
- return this._request('POST', '/v1/email/claim', { body: options });
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
- * Send a DKIM-signed email from an @agentlair.dev address you own.
332
+ * Delete a calendar event by ID.
187
333
  *
188
- * Supports plain text, HTML, or both. Supports threading via `in_reply_to`.
189
- * Free tier: 10 emails/day per address.
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 client.sendEmail({
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 sendEmail(options) {
200
- return this._request('POST', '/v1/email/send', { body: options });
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
- * Get the inbox for an @agentlair.dev address you own.
358
+ * Write a structured observation. Account-scoped by default.
359
+ * Set `shared: true` to make visible to all authenticated agents.
205
360
  *
206
- * Returns message previews (no full body). Use `readMessage()` for the full body.
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 { messages, count } = await client.getInbox({ address: 'my-agent@agentlair.dev' });
210
- * for (const msg of messages) {
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 getInbox(options) {
215
- const query = { address: options.address };
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._request('GET', '/v1/email/inbox', { query });
386
+ return this._req('GET', '/v1/observations', { query });
219
387
  }
220
- // ─── Email: readMessage ──────────────────────────────────────────────────
221
388
  /**
222
- * Read the full body of a specific message.
389
+ * List distinct topics with observation count.
223
390
  *
224
- * Marks the message as read. For E2E-encrypted messages, returns
225
- * `ciphertext` and `ephemeral_public_key` for client-side decryption.
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
- * @param messageId Use `message_id_url` from inbox (URL-encoded) or raw `message_id`
228
- * @param address The @agentlair.dev address that received the message
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 msg = await client.readMessage({
232
- * messageId: inboxMsg.message_id_url,
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._request('GET', `/v1/email/messages/${options.messageId}`, { query: { address: options.address } });
558
+ return this.email.read(options.messageId, options.address);
239
559
  }
240
560
  }
241
561
  //# sourceMappingURL=client.js.map