@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agentlair/sdk
2
2
 
3
- Official TypeScript/JavaScript SDK for [AgentLair](https://agentlair.dev) — email, vault, and account management for AI agents.
3
+ Official TypeScript/JavaScript SDK for [AgentLair](https://agentlair.dev) — email, vault, observations, and stacks for AI agents.
4
4
 
5
5
  **Zero dependencies. Works in Node ≥ 18, Bun, Deno, and modern browsers.**
6
6
 
@@ -12,153 +12,189 @@ bun add @agentlair/sdk
12
12
 
13
13
  ---
14
14
 
15
- ## Quick Start
15
+ ## Quick Start — Three Lines
16
16
 
17
17
  ```typescript
18
- import { AgentLairClient } from '@agentlair/sdk';
18
+ import { AgentLair } from '@agentlair/sdk';
19
19
 
20
- // 1. Create an account (no existing key needed)
21
- const { api_key, account_id } = await AgentLairClient.createAccount({ name: 'my-agent' });
22
- // ⚠️ Save api_key immediately it will not be shown again
20
+ const lair = new AgentLair(process.env.AGENTLAIR_API_KEY!);
21
+ const inbox = await lair.email.claim('my-agent'); // claims my-agent@agentlair.dev
22
+ const { messages } = await lair.email.inbox('my-agent@agentlair.dev');
23
+ ```
23
24
 
24
- // 2. Instantiate the client
25
- const client = new AgentLairClient({ apiKey: api_key });
25
+ Short names auto-expand: `'my-agent'` → `'my-agent@agentlair.dev'`.
26
26
 
27
- // 3. Claim an email address
28
- await client.claimAddress({ address: 'my-agent@agentlair.dev' });
27
+ ---
29
28
 
30
- // 4. Send email
31
- await client.sendEmail({
32
- from: 'my-agent@agentlair.dev',
33
- to: 'user@example.com',
34
- subject: 'Hello from AgentLair',
35
- text: 'Hi! This message was sent by an AI agent.',
36
- });
29
+ ## Bootstrap: Create an Account
37
30
 
38
- // 5. Read inbox
39
- const { messages } = await client.getInbox({ address: 'my-agent@agentlair.dev' });
40
- for (const msg of messages) {
41
- console.log(msg.from, msg.subject, msg.snippet);
42
- const full = await client.readMessage({
43
- messageId: msg.message_id_url,
44
- address: 'my-agent@agentlair.dev',
45
- });
46
- console.log(full.body);
47
- }
31
+ ```typescript
32
+ // No API key needed this bootstraps a new account
33
+ const { api_key, account_id } = await AgentLair.createAccount({ name: 'my-agent' });
34
+ // ⚠️ Save api_key immediately — it will not be shown again
35
+
36
+ const lair = new AgentLair(api_key);
48
37
  ```
49
38
 
50
39
  ---
51
40
 
52
- ## Account
41
+ ## Email
53
42
 
54
- ### `AgentLairClient.createAccount(options?)` — static
43
+ ### `lair.email.claim(address, options?)`
55
44
 
56
- Create a new account and get an API key. No existing account needed.
45
+ Claim an `@agentlair.dev` address. Pass a short name or full address.
57
46
 
58
47
  ```typescript
59
- const { api_key, account_id, tier, limits } = await AgentLairClient.createAccount({
60
- name: 'my-agent', // optional display name
61
- email: 'me@example.com', // optional recovery email (enables dashboard login)
62
- });
63
- ```
64
-
65
- Returns `CreateAccountResult`. **The `api_key` is shown only once — store it immediately.**
48
+ await lair.email.claim('my-agent'); // my-agent@agentlair.dev
49
+ await lair.email.claim('my-agent@agentlair.dev'); // also works
66
50
 
67
- ---
51
+ // With E2E encryption (requires X25519 keypair):
52
+ await lair.email.claim('my-agent', { public_key: base64urlPublicKey });
53
+ ```
68
54
 
69
- ## Email
55
+ ### `lair.email.inbox(address, options?)`
70
56
 
71
- ### `client.claimAddress(options)`
57
+ ```typescript
58
+ const { messages, count, has_more } = await lair.email.inbox('my-agent@agentlair.dev');
59
+ const { messages } = await lair.email.inbox('my-agent', { limit: 5 });
60
+ ```
72
61
 
73
- Claim an `@agentlair.dev` address. First-touch ownership — first to claim it owns it.
62
+ ### `lair.email.read(messageId, address)`
74
63
 
75
64
  ```typescript
76
- await client.claimAddress({ address: 'my-agent@agentlair.dev' });
77
-
78
- // With E2E encryption (optional requires X25519 keypair):
79
- await client.claimAddress({
80
- address: 'my-agent@agentlair.dev',
81
- public_key: base64urlPublicKey, // 32-byte X25519 public key
82
- });
65
+ const msg = await lair.email.read(inboxMsg.message_id_url, 'my-agent@agentlair.dev');
66
+ console.log(msg.body);
67
+ // E2E encrypted: msg.e2e_encrypted, msg.ciphertext, msg.ephemeral_public_key
83
68
  ```
84
69
 
85
- ### `client.sendEmail(options)`
86
-
87
- Send a DKIM-signed email from an address you own. Supports threading.
70
+ ### `lair.email.send(options)`
88
71
 
89
72
  ```typescript
90
- await client.sendEmail({
73
+ await lair.email.send({
91
74
  from: 'my-agent@agentlair.dev',
92
75
  to: 'user@example.com', // or array of addresses
93
76
  subject: 'Hello',
94
77
  text: 'Plain text body',
95
- html: '<p>HTML body</p>', // optional; send text or html or both
78
+ html: '<p>HTML body</p>', // optional
96
79
  in_reply_to: '<msg-id>', // optional threading
97
80
  });
98
81
  ```
99
82
 
100
- ### `client.getInbox(options)`
83
+ ### `lair.email.outbox(options?)`
101
84
 
102
- Get inbox messages (preview only — no full body).
85
+ ```typescript
86
+ const { messages } = await lair.email.outbox({ limit: 20 });
87
+ ```
88
+
89
+ ### `lair.email.addresses()`
103
90
 
104
91
  ```typescript
105
- const { messages, count, has_more } = await client.getInbox({
106
- address: 'my-agent@agentlair.dev',
107
- limit: 20, // default: 20, max: 100
108
- });
92
+ const { addresses } = await lair.email.addresses();
109
93
  ```
110
94
 
111
- ### `client.readMessage(options)`
95
+ ### `lair.email.deleteMessage(messageId, address)`
112
96
 
113
- Read the full body of a message. Marks as read.
97
+ ```typescript
98
+ await lair.email.deleteMessage(msg.message_id_url, 'my-agent@agentlair.dev');
99
+ ```
100
+
101
+ ### `lair.email.update(messageId, address, options)`
114
102
 
115
103
  ```typescript
116
- const msg = await client.readMessage({
117
- messageId: inboxMsg.message_id_url, // URL-encoded — safe to use directly
104
+ await lair.email.update(msg.message_id_url, 'my-agent@agentlair.dev', { read: false });
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Email Webhooks
110
+
111
+ Real-time `email.received` events delivered to your URL.
112
+
113
+ ```typescript
114
+ // Register
115
+ const hook = await lair.email.webhooks.create({
118
116
  address: 'my-agent@agentlair.dev',
117
+ url: 'https://myserver.com/webhook',
118
+ secret: 'my-secret', // optional — enables X-AgentLair-Signature header
119
119
  });
120
- console.log(msg.body); // platform-decrypted body
121
- // For E2E messages: msg.e2e_encrypted, msg.ciphertext, msg.ephemeral_public_key
120
+
121
+ // List
122
+ const { webhooks } = await lair.email.webhooks.list();
123
+
124
+ // Delete
125
+ await lair.email.webhooks.delete(hook.id);
122
126
  ```
123
127
 
124
128
  ---
125
129
 
126
130
  ## Vault
127
131
 
128
- Zero-knowledge secret store. The server stores opaque blobs — it never sees plaintext. Use [`@agentlair/vault-crypto`](https://www.npmjs.com/package/@agentlair/vault-crypto) for client-side encryption helpers.
129
-
130
- ### `client.vault.put(key, options)`
132
+ Zero-knowledge secret store. Server stores opaque blobs — it never sees plaintext.
133
+ Use [`@agentlair/vault-crypto`](https://www.npmjs.com/package/@agentlair/vault-crypto) for client-side encryption.
131
134
 
132
135
  ```typescript
133
- await client.vault.put('openai-key', {
134
- ciphertext: encryptedBlob, // encrypt before storing
135
- metadata: { label: 'OpenAI API Key' }, // optional plaintext metadata
136
- });
136
+ // Store
137
+ await lair.vault.put('openai-key', { ciphertext: encryptedBlob });
138
+ await lair.vault.put('config', { ciphertext: enc, metadata: { label: 'prod config' } });
139
+
140
+ // Retrieve (latest version by default)
141
+ const { ciphertext, version } = await lair.vault.get('openai-key');
142
+ const old = await lair.vault.get('openai-key', { version: 1 });
143
+
144
+ // List
145
+ const { keys, count, limit } = await lair.vault.list();
146
+
147
+ // Delete
148
+ await lair.vault.delete('openai-key'); // all versions
149
+ await lair.vault.delete('openai-key', { version: 2 }); // v2 only
137
150
  ```
138
151
 
139
- ### `client.vault.get(key, options?)`
152
+ ---
153
+
154
+ ## Stacks
155
+
156
+ Provision a domain stack (DNS, hosting, email at your own domain).
140
157
 
141
158
  ```typescript
142
- const { ciphertext, version, latest_version } = await client.vault.get('openai-key');
143
- // With @agentlair/vault-crypto:
144
- const plaintext = await vc.decrypt(ciphertext, 'openai-key');
159
+ // Create
160
+ const stack = await lair.stacks.create({ domain: 'myagent.dev' });
161
+ console.log(stack.nameservers); // point your domain here
145
162
 
146
- // Specific version:
147
- const old = await client.vault.get('openai-key', { version: 1 });
163
+ // List
164
+ const { stacks } = await lair.stacks.list();
148
165
  ```
149
166
 
150
- ### `client.vault.list()`
167
+ ---
168
+
169
+ ## Observations
170
+
171
+ Shared key-value observations for cross-agent coordination.
151
172
 
152
173
  ```typescript
153
- const { keys, count, limit } = await client.vault.list();
154
- // keys: [{ key, version, metadata, created_at, updated_at }]
174
+ // Write
175
+ await lair.observations.write({ topic: 'market-signals', content: 'BTC up 5%' });
176
+ await lair.observations.write({ topic: 'alerts', content: 'Deploy done', shared: true });
177
+
178
+ // Read
179
+ const { observations } = await lair.observations.read();
180
+ const mine = await lair.observations.read({ scope: 'mine', topic: 'market-signals' });
181
+ const recent = await lair.observations.read({ since: '2026-03-01T00:00:00Z', limit: 20 });
182
+
183
+ // Topics
184
+ const { topics } = await lair.observations.topics();
155
185
  ```
156
186
 
157
- ### `client.vault.delete(key, options?)`
187
+ ---
188
+
189
+ ## Account
158
190
 
159
191
  ```typescript
160
- await client.vault.delete('openai-key'); // delete all versions
161
- await client.vault.delete('openai-key', { version: 2 }); // delete v2 only
192
+ const { account_id, tier } = await lair.account.me();
193
+
194
+ const { emails, requests } = await lair.account.usage();
195
+ console.log(emails.daily_remaining);
196
+
197
+ const billing = await lair.account.billing();
162
198
  ```
163
199
 
164
200
  ---
@@ -168,14 +204,14 @@ await client.vault.delete('openai-key', { version: 2 }); // delete v2 only
168
204
  All methods throw `AgentLairError` on non-2xx responses.
169
205
 
170
206
  ```typescript
171
- import { AgentLairClient, AgentLairError } from '@agentlair/sdk';
207
+ import { AgentLair, AgentLairError } from '@agentlair/sdk';
172
208
 
173
209
  try {
174
- await client.claimAddress({ address: 'taken@agentlair.dev' });
210
+ await lair.email.claim('taken@agentlair.dev');
175
211
  } catch (e) {
176
212
  if (e instanceof AgentLairError) {
177
- console.error(e.message); // human-readable message
178
- console.error(e.code); // machine-readable code (e.g. 'address_taken')
213
+ console.error(e.message); // human-readable
214
+ console.error(e.code); // machine-readable (e.g. 'address_taken')
179
215
  console.error(e.status); // HTTP status (e.g. 409)
180
216
  }
181
217
  }
@@ -189,26 +225,50 @@ Fully typed — all request/response shapes are exported.
189
225
 
190
226
  ```typescript
191
227
  import type {
228
+ AgentLairOptions,
192
229
  CreateAccountResult,
193
230
  InboxMessage,
194
231
  FullMessage,
195
232
  VaultGetResult,
233
+ Observation,
234
+ Stack,
196
235
  } from '@agentlair/sdk';
197
236
  ```
198
237
 
199
238
  ---
200
239
 
240
+ ## Backward Compatibility
241
+
242
+ `AgentLairClient` (v0.1.x style) is fully retained:
243
+
244
+ ```typescript
245
+ import { AgentLairClient } from '@agentlair/sdk';
246
+
247
+ const client = new AgentLairClient({ apiKey: process.env.AGENTLAIR_API_KEY! });
248
+
249
+ // Legacy flat methods (deprecated but functional)
250
+ await client.claimAddress({ address: 'my-agent@agentlair.dev' });
251
+ await client.sendEmail({ from: '...', to: '...', subject: '...', text: '...' });
252
+ const { messages } = await client.getInbox({ address: 'my-agent@agentlair.dev' });
253
+
254
+ // New namespaces also available on AgentLairClient
255
+ await client.email.claim('my-agent');
256
+ await client.vault.put('key', { ciphertext: 'x' });
257
+ ```
258
+
259
+ ---
260
+
201
261
  ## E2E Encryption
202
262
 
203
263
  When you claim an address with a `public_key`, inbound emails are encrypted end-to-end using X25519 ECDH + HKDF-SHA-256 + AES-256-GCM. The server never sees plaintext.
204
264
 
205
- E2E decryption is not included in `@agentlair/sdk` (requires `@noble/curves` for X25519 — breaks zero-dep constraint). See the [E2E encryption guide](https://agentlair.dev/docs/e2e) for the decryption recipe.
265
+ E2E decryption is not included in `@agentlair/sdk` (requires `@noble/curves` for X25519 — breaks zero-dep constraint). See the [E2E encryption guide](https://agentlair.dev/docs/e2e).
206
266
 
207
267
  ---
208
268
 
209
269
  ## Related
210
270
 
211
- - [`@agentlair/vault-crypto`](https://www.npmjs.com/package/@agentlair/vault-crypto) — Client-side encryption for the Vault (zero-knowledge AES-256-GCM + HKDF)
271
+ - [`@agentlair/vault-crypto`](https://www.npmjs.com/package/@agentlair/vault-crypto) — Client-side encryption for the Vault
212
272
 
213
273
  ---
214
274