@agentidentity/sdk 0.1.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/LICENSE +21 -0
- package/README.md +171 -0
- package/dist/client.d.ts +788 -0
- package/dist/client.js +985 -0
- package/dist/errors.d.ts +26 -0
- package/dist/errors.js +55 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/vault-crypto.d.ts +11 -0
- package/dist/vault-crypto.js +55 -0
- package/package.json +57 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,985 @@
|
|
|
1
|
+
import { generateVaultKeypair, vaultDecrypt, vaultEncrypt } from "./vault-crypto.js";
|
|
2
|
+
import { AidApiError, AidConnectionError } from "./errors.js";
|
|
3
|
+
export { AidApiError, AidConnectionError } from "./errors.js";
|
|
4
|
+
export async function createOrganization(baseUrl, input, fetchImpl = fetch) {
|
|
5
|
+
const body = await sendRequest(fetchImpl, baseUrl, "POST", "/v1/organizations", {
|
|
6
|
+
body: { name: input.name, slug: input.slug },
|
|
7
|
+
});
|
|
8
|
+
const b = body;
|
|
9
|
+
return { id: b.id, slug: b.slug, name: b.name, adminApiKey: b.admin_api_key };
|
|
10
|
+
}
|
|
11
|
+
function toAuthSession(body) {
|
|
12
|
+
const b = body;
|
|
13
|
+
return { apiKey: b.api_key, orgId: b.org_id, orgName: b.org_name, orgSlug: b.org_slug };
|
|
14
|
+
}
|
|
15
|
+
export async function signUp(baseUrl, input, fetchImpl = fetch) {
|
|
16
|
+
const body = await sendRequest(fetchImpl, baseUrl, "POST", "/v1/auth/signup", {
|
|
17
|
+
body: { org_name: input.orgName, org_slug: input.orgSlug, email: input.email, password: input.password },
|
|
18
|
+
});
|
|
19
|
+
return toAuthSession(body);
|
|
20
|
+
}
|
|
21
|
+
export async function logIn(baseUrl, input, fetchImpl = fetch) {
|
|
22
|
+
const body = await sendRequest(fetchImpl, baseUrl, "POST", "/v1/auth/login", {
|
|
23
|
+
body: { email: input.email, password: input.password },
|
|
24
|
+
});
|
|
25
|
+
return toAuthSession(body);
|
|
26
|
+
}
|
|
27
|
+
/** Resolves after `ms`, or as soon as `signal` aborts (whichever comes first). */
|
|
28
|
+
function sleep(ms, signal) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
if (signal?.aborted)
|
|
31
|
+
return resolve();
|
|
32
|
+
const timer = setTimeout(() => {
|
|
33
|
+
signal?.removeEventListener("abort", onAbort);
|
|
34
|
+
resolve();
|
|
35
|
+
}, ms);
|
|
36
|
+
const onAbort = () => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
resolve();
|
|
39
|
+
};
|
|
40
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export class AgentClient {
|
|
44
|
+
baseUrl;
|
|
45
|
+
apiKey;
|
|
46
|
+
fetchImpl;
|
|
47
|
+
timeoutMs;
|
|
48
|
+
maxRetries;
|
|
49
|
+
identities;
|
|
50
|
+
mail;
|
|
51
|
+
events;
|
|
52
|
+
apiKeys;
|
|
53
|
+
webhooks;
|
|
54
|
+
vault;
|
|
55
|
+
tunnels;
|
|
56
|
+
contacts;
|
|
57
|
+
contactRules;
|
|
58
|
+
a2a;
|
|
59
|
+
domains;
|
|
60
|
+
providerAccounts;
|
|
61
|
+
providers;
|
|
62
|
+
/** Liveness and readiness probes. Unauthenticated on the server, but exposed here so callers
|
|
63
|
+
* can reuse one configured client rather than hand-rolling a fetch. */
|
|
64
|
+
status;
|
|
65
|
+
constructor(options) {
|
|
66
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
67
|
+
this.apiKey = options.apiKey;
|
|
68
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
69
|
+
this.timeoutMs = options.timeoutMs ?? 30_000;
|
|
70
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
71
|
+
this.identities = {
|
|
72
|
+
create: async (input) => toIdentitySummary(await this.request("POST", "/v1/identities", {
|
|
73
|
+
body: { handle: input.handle, display_name: input.displayName },
|
|
74
|
+
})),
|
|
75
|
+
list: async () => {
|
|
76
|
+
const body = await this.request("GET", "/v1/identities");
|
|
77
|
+
return body.map(toIdentitySummary);
|
|
78
|
+
},
|
|
79
|
+
get: async (id) => toIdentityDetail(await this.request("GET", `/v1/identities/${encodeURIComponent(id)}`)),
|
|
80
|
+
suspend: async (id, reason) => {
|
|
81
|
+
await this.request("POST", `/v1/identities/${encodeURIComponent(id)}/suspend`, { body: { reason } });
|
|
82
|
+
},
|
|
83
|
+
reinstate: async (id) => {
|
|
84
|
+
await this.request("POST", `/v1/identities/${encodeURIComponent(id)}/reinstate`);
|
|
85
|
+
},
|
|
86
|
+
listSuspensions: async () => {
|
|
87
|
+
const body = await this.request("GET", "/v1/identities/suspensions");
|
|
88
|
+
return body.map(toIdentitySuspension);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
this.mail = {
|
|
92
|
+
send: async (input) => {
|
|
93
|
+
const headers = {};
|
|
94
|
+
if (input.idempotencyKey !== undefined)
|
|
95
|
+
headers["idempotency-key"] = input.idempotencyKey;
|
|
96
|
+
const body = await this.request("POST", `/v1/identities/${encodeURIComponent(input.identityId)}/mail/send`, {
|
|
97
|
+
headers,
|
|
98
|
+
body: {
|
|
99
|
+
to: Array.isArray(input.to) ? input.to : [input.to],
|
|
100
|
+
...(input.cc !== undefined ? { cc: input.cc } : {}),
|
|
101
|
+
...(input.subject !== undefined ? { subject: input.subject } : {}),
|
|
102
|
+
...(input.text !== undefined ? { text: input.text } : {}),
|
|
103
|
+
...(input.html !== undefined ? { html: input.html } : {}),
|
|
104
|
+
...(input.inReplyToMessageId !== undefined
|
|
105
|
+
? { in_reply_to_message_id: input.inReplyToMessageId }
|
|
106
|
+
: {}),
|
|
107
|
+
...(toAttachmentInputs(input.attachments) !== undefined
|
|
108
|
+
? { attachments: toAttachmentInputs(input.attachments) }
|
|
109
|
+
: {}),
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
return toMailMessage(body);
|
|
113
|
+
},
|
|
114
|
+
listMessages: async (identityId) => {
|
|
115
|
+
const body = await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/mail/messages`);
|
|
116
|
+
return body.map(toMailMessage);
|
|
117
|
+
},
|
|
118
|
+
getThread: async (identityId, threadId) => toMailThread(await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/mail/threads/${encodeURIComponent(threadId)}`)),
|
|
119
|
+
saveDraft: async (input) => toMailMessage(await this.request("POST", `/v1/identities/${encodeURIComponent(input.identityId)}/mail/drafts`, {
|
|
120
|
+
body: {
|
|
121
|
+
...(input.to !== undefined ? { to: input.to } : {}),
|
|
122
|
+
...(input.cc !== undefined ? { cc: input.cc } : {}),
|
|
123
|
+
...(input.subject !== undefined ? { subject: input.subject } : {}),
|
|
124
|
+
...(input.text !== undefined ? { text: input.text } : {}),
|
|
125
|
+
...(input.html !== undefined ? { html: input.html } : {}),
|
|
126
|
+
...(input.inReplyToMessageId !== undefined
|
|
127
|
+
? { in_reply_to_message_id: input.inReplyToMessageId }
|
|
128
|
+
: {}),
|
|
129
|
+
...(toAttachmentInputs(input.attachments) !== undefined
|
|
130
|
+
? { attachments: toAttachmentInputs(input.attachments) }
|
|
131
|
+
: {}),
|
|
132
|
+
},
|
|
133
|
+
})),
|
|
134
|
+
listDrafts: async (identityId) => {
|
|
135
|
+
const body = await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/mail/drafts`);
|
|
136
|
+
return body.map(toMailMessage);
|
|
137
|
+
},
|
|
138
|
+
updateDraft: async (identityId, messageId, input) => toMailMessage(await this.request("PATCH", `/v1/identities/${encodeURIComponent(identityId)}/mail/drafts/${encodeURIComponent(messageId)}`, {
|
|
139
|
+
body: {
|
|
140
|
+
...(input.to !== undefined ? { to: input.to } : {}),
|
|
141
|
+
...(input.cc !== undefined ? { cc: input.cc } : {}),
|
|
142
|
+
...(input.subject !== undefined ? { subject: input.subject } : {}),
|
|
143
|
+
...(input.text !== undefined ? { text: input.text } : {}),
|
|
144
|
+
...(input.html !== undefined ? { html: input.html } : {}),
|
|
145
|
+
...(toAttachmentInputs(input.attachments) !== undefined
|
|
146
|
+
? { attachments: toAttachmentInputs(input.attachments) }
|
|
147
|
+
: {}),
|
|
148
|
+
},
|
|
149
|
+
})),
|
|
150
|
+
deleteDraft: async (identityId, messageId) => {
|
|
151
|
+
await this.request("DELETE", `/v1/identities/${encodeURIComponent(identityId)}/mail/drafts/${encodeURIComponent(messageId)}`);
|
|
152
|
+
},
|
|
153
|
+
sendDraft: async (identityId, messageId) => toMailMessage(await this.request("POST", `/v1/identities/${encodeURIComponent(identityId)}/mail/drafts/${encodeURIComponent(messageId)}/send`)),
|
|
154
|
+
markSpam: async (identityId, messageId) => toMailMessage(await this.request("POST", `/v1/identities/${encodeURIComponent(identityId)}/mail/messages/${encodeURIComponent(messageId)}/mark-spam`)),
|
|
155
|
+
unmarkSpam: async (identityId, messageId) => toMailMessage(await this.request("POST", `/v1/identities/${encodeURIComponent(identityId)}/mail/messages/${encodeURIComponent(messageId)}/unmark-spam`)),
|
|
156
|
+
listAttachments: async (identityId, messageId) => {
|
|
157
|
+
const body = await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/mail/messages/${encodeURIComponent(messageId)}/attachments`);
|
|
158
|
+
return body.map(toAttachment);
|
|
159
|
+
},
|
|
160
|
+
downloadAttachment: async (identityId, messageId, attachmentId) => {
|
|
161
|
+
const url = `${this.baseUrl.replace(/\/$/, "")}/v1/identities/${encodeURIComponent(identityId)}/mail/messages/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}/download`;
|
|
162
|
+
const res = await this.fetchImpl(url, { headers: { authorization: `Bearer ${this.apiKey}` } });
|
|
163
|
+
if (!res.ok) {
|
|
164
|
+
const text = await res.text().catch(() => undefined);
|
|
165
|
+
let data;
|
|
166
|
+
try {
|
|
167
|
+
data = text ? JSON.parse(text) : undefined;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
data = text;
|
|
171
|
+
}
|
|
172
|
+
throw new AidApiError(res.status, data);
|
|
173
|
+
}
|
|
174
|
+
const contentType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
175
|
+
const disposition = res.headers.get("content-disposition") ?? "";
|
|
176
|
+
const filenameMatch = /filename="([^"]*)"/.exec(disposition);
|
|
177
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
178
|
+
return { contentType, filename: filenameMatch?.[1] ?? "attachment", bytes };
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
this.events = {
|
|
182
|
+
wait: async (input, options) => {
|
|
183
|
+
const params = new URLSearchParams({ type: input.type });
|
|
184
|
+
if (input.identityId !== undefined)
|
|
185
|
+
params.set("identity_id", input.identityId);
|
|
186
|
+
if (input.aggregateType !== undefined)
|
|
187
|
+
params.set("aggregate_type", input.aggregateType);
|
|
188
|
+
if (input.aggregateId !== undefined)
|
|
189
|
+
params.set("aggregate_id", input.aggregateId);
|
|
190
|
+
if (input.filter?.threadId !== undefined)
|
|
191
|
+
params.set("thread_id", input.filter.threadId);
|
|
192
|
+
if (input.since !== undefined) {
|
|
193
|
+
params.set("since", input.since instanceof Date ? input.since.toISOString() : input.since);
|
|
194
|
+
}
|
|
195
|
+
if (input.timeoutMs !== undefined)
|
|
196
|
+
params.set("timeout_ms", String(input.timeoutMs));
|
|
197
|
+
// This endpoint deliberately holds the connection open, so the client's normal request
|
|
198
|
+
// deadline would abort a perfectly healthy long-poll. Allow the server's own window
|
|
199
|
+
// plus a margin for travel, and fall back to a generous default when unspecified.
|
|
200
|
+
const pollWindowMs = (input.timeoutMs ?? 25_000) + 10_000;
|
|
201
|
+
// `T` only narrows payload types for callers; the decoded shape is the same either
|
|
202
|
+
// way, so the assertion is confined to this one line rather than leaking to callers.
|
|
203
|
+
return toWaitForEventResult(await this.request("GET", `/v1/events/wait?${params.toString()}`, {
|
|
204
|
+
timeoutMs: pollWindowMs,
|
|
205
|
+
...(options?.signal !== undefined ? { signal: options.signal } : {}),
|
|
206
|
+
}));
|
|
207
|
+
},
|
|
208
|
+
subscribe: (input) => {
|
|
209
|
+
// Cursor discipline is the whole point of this helper. A hand-written loop typically
|
|
210
|
+
// does `since = new Date()` after each wait returns, which (a) uses the *client's*
|
|
211
|
+
// clock against server-assigned timestamps, so skew silently skips events, and (b)
|
|
212
|
+
// covers the gap between the event's own createdAt and the reset. Advancing strictly
|
|
213
|
+
// to the last delivered event's createdAt has neither problem.
|
|
214
|
+
// `wait` is an arrow bound to the client, so detaching it here is safe and avoids
|
|
215
|
+
// aliasing `this` into the iterator object below.
|
|
216
|
+
const waitForEvent = this.events.wait;
|
|
217
|
+
const pollTimeoutMs = input.pollTimeoutMs ?? 25_000;
|
|
218
|
+
let cursor = input.since === undefined
|
|
219
|
+
? undefined
|
|
220
|
+
: input.since instanceof Date
|
|
221
|
+
? input.since.toISOString()
|
|
222
|
+
: input.since;
|
|
223
|
+
let attempt = 0;
|
|
224
|
+
let done = false;
|
|
225
|
+
const iterator = {
|
|
226
|
+
[Symbol.asyncIterator]() {
|
|
227
|
+
return iterator;
|
|
228
|
+
},
|
|
229
|
+
async next() {
|
|
230
|
+
for (;;) {
|
|
231
|
+
if (done || input.signal?.aborted)
|
|
232
|
+
return { done: true, value: undefined };
|
|
233
|
+
try {
|
|
234
|
+
const result = await waitForEvent({
|
|
235
|
+
...input,
|
|
236
|
+
timeoutMs: pollTimeoutMs,
|
|
237
|
+
...(cursor !== undefined ? { since: cursor } : {}),
|
|
238
|
+
}, ...(input.signal !== undefined ? [{ signal: input.signal }] : []));
|
|
239
|
+
attempt = 0;
|
|
240
|
+
if (result.timedOut || !result.event)
|
|
241
|
+
continue;
|
|
242
|
+
cursor = result.event.createdAt;
|
|
243
|
+
return { done: false, value: result.event };
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
if (input.signal?.aborted)
|
|
247
|
+
return { done: true, value: undefined };
|
|
248
|
+
attempt += 1;
|
|
249
|
+
if (input.onError?.(err, attempt) === false) {
|
|
250
|
+
done = true;
|
|
251
|
+
return { done: true, value: undefined };
|
|
252
|
+
}
|
|
253
|
+
// Exponential backoff, capped — a server restart shouldn't become a hot loop.
|
|
254
|
+
const delay = Math.min(1000 * 2 ** (attempt - 1), 30_000);
|
|
255
|
+
await sleep(delay, input.signal);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
async return() {
|
|
260
|
+
done = true;
|
|
261
|
+
return { done: true, value: undefined };
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
return iterator;
|
|
265
|
+
},
|
|
266
|
+
list: async (options) => {
|
|
267
|
+
const params = new URLSearchParams();
|
|
268
|
+
if (options?.limit !== undefined)
|
|
269
|
+
params.set("limit", String(options.limit));
|
|
270
|
+
if (options?.before !== undefined)
|
|
271
|
+
params.set("before", options.before);
|
|
272
|
+
const query = params.toString();
|
|
273
|
+
const body = await this.request("GET", `/v1/events${query ? `?${query}` : ""}`);
|
|
274
|
+
return body.map(toEvent);
|
|
275
|
+
},
|
|
276
|
+
replay: async (id) => {
|
|
277
|
+
const body = await this.request("POST", `/v1/events/${encodeURIComponent(id)}/replay`);
|
|
278
|
+
return body;
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
this.apiKeys = {
|
|
282
|
+
list: async () => {
|
|
283
|
+
const body = await this.request("GET", "/v1/api-keys");
|
|
284
|
+
return body.map(toApiKeySummary);
|
|
285
|
+
},
|
|
286
|
+
create: async (input) => toCreatedApiKey(await this.request("POST", "/v1/api-keys", {
|
|
287
|
+
body: {
|
|
288
|
+
name: input.name,
|
|
289
|
+
...(input.identityId !== undefined ? { identity_id: input.identityId } : {}),
|
|
290
|
+
},
|
|
291
|
+
})),
|
|
292
|
+
revoke: async (id) => toApiKeySummary(await this.request("POST", `/v1/api-keys/${encodeURIComponent(id)}/revoke`)),
|
|
293
|
+
};
|
|
294
|
+
this.webhooks = {
|
|
295
|
+
list: async () => {
|
|
296
|
+
const body = await this.request("GET", "/v1/webhooks");
|
|
297
|
+
return body.map(toWebhookEndpoint);
|
|
298
|
+
},
|
|
299
|
+
create: async (input) => toCreatedWebhookEndpoint(await this.request("POST", "/v1/webhooks", {
|
|
300
|
+
body: {
|
|
301
|
+
url: input.url,
|
|
302
|
+
...(input.identityId !== undefined ? { identity_id: input.identityId } : {}),
|
|
303
|
+
...(input.eventTypes !== undefined ? { event_types: input.eventTypes } : {}),
|
|
304
|
+
},
|
|
305
|
+
})),
|
|
306
|
+
listDeliveries: async (options) => {
|
|
307
|
+
const params = new URLSearchParams();
|
|
308
|
+
if (options?.limit !== undefined)
|
|
309
|
+
params.set("limit", String(options.limit));
|
|
310
|
+
if (options?.before !== undefined)
|
|
311
|
+
params.set("before", options.before);
|
|
312
|
+
const query = params.toString();
|
|
313
|
+
const body = await this.request("GET", `/v1/webhooks/deliveries${query ? `?${query}` : ""}`);
|
|
314
|
+
return body.map(toWebhookDelivery);
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
this.vault = {
|
|
318
|
+
generateIdentityKey: async (identityId) => {
|
|
319
|
+
const keypair = generateVaultKeypair();
|
|
320
|
+
await this.request("PUT", `/v1/identities/${encodeURIComponent(identityId)}/vault-key`, {
|
|
321
|
+
body: { public_key: keypair.publicKey },
|
|
322
|
+
});
|
|
323
|
+
return keypair;
|
|
324
|
+
},
|
|
325
|
+
listSecrets: async () => {
|
|
326
|
+
const body = await this.request("GET", "/v1/vault/secrets");
|
|
327
|
+
return body.map(toVaultSecretSummary);
|
|
328
|
+
},
|
|
329
|
+
createSecret: async (input) => {
|
|
330
|
+
const identity = await this.identities.get(input.identityId);
|
|
331
|
+
if (!identity.vaultPublicKey) {
|
|
332
|
+
throw new Error(`identity ${input.identityId} has no vault key registered — call vault.generateIdentityKey() first`);
|
|
333
|
+
}
|
|
334
|
+
const envelope = vaultEncrypt(Buffer.from(input.plaintext, "utf8"), identity.vaultPublicKey);
|
|
335
|
+
return toVaultSecretSummary(await this.request("POST", "/v1/vault/secrets", {
|
|
336
|
+
body: {
|
|
337
|
+
name: input.name,
|
|
338
|
+
identity_id: input.identityId,
|
|
339
|
+
ciphertext: envelope.ciphertext,
|
|
340
|
+
ephemeral_public_key: envelope.ephemeralPublicKey,
|
|
341
|
+
},
|
|
342
|
+
}));
|
|
343
|
+
},
|
|
344
|
+
rotateSecret: async (secretId, keypair) => {
|
|
345
|
+
const current = await this.request("GET", `/v1/vault/secrets/${encodeURIComponent(secretId)}/envelope`);
|
|
346
|
+
const envelope = toVaultEnvelope(current);
|
|
347
|
+
const plaintext = vaultDecrypt(envelope, keypair.publicKey, keypair.privateKey);
|
|
348
|
+
const fresh = vaultEncrypt(plaintext, keypair.publicKey);
|
|
349
|
+
return toVaultSecretSummary(await this.request("POST", `/v1/vault/secrets/${encodeURIComponent(secretId)}/rotate`, {
|
|
350
|
+
body: { ciphertext: fresh.ciphertext, ephemeral_public_key: fresh.ephemeralPublicKey },
|
|
351
|
+
}));
|
|
352
|
+
},
|
|
353
|
+
createLease: async (secretId, input) => toCreatedLease(await this.request("POST", `/v1/vault/secrets/${encodeURIComponent(secretId)}/lease`, {
|
|
354
|
+
body: {
|
|
355
|
+
operation: input.operation,
|
|
356
|
+
...(input.ttlSeconds !== undefined ? { ttl_seconds: input.ttlSeconds } : {}),
|
|
357
|
+
...(input.identityId !== undefined ? { identity_id: input.identityId } : {}),
|
|
358
|
+
},
|
|
359
|
+
})),
|
|
360
|
+
redeemLease: async (leaseId, keypair) => {
|
|
361
|
+
const body = await this.request("POST", `/v1/vault/leases/${encodeURIComponent(leaseId)}/redeem`);
|
|
362
|
+
const envelope = toVaultEnvelope(body);
|
|
363
|
+
const plaintext = vaultDecrypt(envelope, keypair.publicKey, keypair.privateKey);
|
|
364
|
+
return { secret: plaintext.toString("utf8") };
|
|
365
|
+
},
|
|
366
|
+
listLeases: async (options) => {
|
|
367
|
+
const params = new URLSearchParams();
|
|
368
|
+
if (options?.limit !== undefined)
|
|
369
|
+
params.set("limit", String(options.limit));
|
|
370
|
+
const query = params.toString();
|
|
371
|
+
const body = await this.request("GET", `/v1/vault/leases${query ? `?${query}` : ""}`);
|
|
372
|
+
return body.map(toVaultLease);
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
this.tunnels = {
|
|
376
|
+
create: async (identityId, input) => toTunnel(await this.request("POST", `/v1/identities/${encodeURIComponent(identityId)}/tunnels`, {
|
|
377
|
+
body: { ...(input?.hostname !== undefined ? { hostname: input.hostname } : {}) },
|
|
378
|
+
})),
|
|
379
|
+
list: async (identityId) => {
|
|
380
|
+
const body = await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/tunnels`);
|
|
381
|
+
return body.map(toTunnel);
|
|
382
|
+
},
|
|
383
|
+
get: async (tunnelId) => toTunnel(await this.request("GET", `/v1/tunnels/${encodeURIComponent(tunnelId)}`)),
|
|
384
|
+
};
|
|
385
|
+
this.contacts = {
|
|
386
|
+
create: async (input) => toContact(await this.request("POST", "/v1/contacts", { body: fromContactInput(input) })),
|
|
387
|
+
list: async (options) => {
|
|
388
|
+
const query = options?.status ? `?status=${encodeURIComponent(options.status)}` : "";
|
|
389
|
+
const body = await this.request("GET", `/v1/contacts${query}`);
|
|
390
|
+
return body.map(toContact);
|
|
391
|
+
},
|
|
392
|
+
get: async (id) => toContact(await this.request("GET", `/v1/contacts/${encodeURIComponent(id)}`)),
|
|
393
|
+
update: async (id, input) => toContact(await this.request("PATCH", `/v1/contacts/${encodeURIComponent(id)}`, { body: fromContactInput(input) })),
|
|
394
|
+
delete: async (id) => {
|
|
395
|
+
await this.request("DELETE", `/v1/contacts/${encodeURIComponent(id)}`);
|
|
396
|
+
},
|
|
397
|
+
approve: async (id) => toContact(await this.request("POST", `/v1/contacts/${encodeURIComponent(id)}/approve`)),
|
|
398
|
+
reject: async (id) => {
|
|
399
|
+
await this.request("POST", `/v1/contacts/${encodeURIComponent(id)}/reject`);
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
this.contactRules = {
|
|
403
|
+
create: async (input) => toContactRule(await this.request("POST", "/v1/contact-rules", {
|
|
404
|
+
body: {
|
|
405
|
+
...(input.identityId !== undefined ? { identity_id: input.identityId } : {}),
|
|
406
|
+
match_address: input.matchAddress,
|
|
407
|
+
action: input.action,
|
|
408
|
+
},
|
|
409
|
+
})),
|
|
410
|
+
list: async () => {
|
|
411
|
+
const body = await this.request("GET", "/v1/contact-rules");
|
|
412
|
+
return body.map(toContactRule);
|
|
413
|
+
},
|
|
414
|
+
delete: async (id) => {
|
|
415
|
+
await this.request("DELETE", `/v1/contact-rules/${encodeURIComponent(id)}`);
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
this.a2a = {
|
|
419
|
+
createTrustRule: async (input) => toA2ATrustRule(await this.request("POST", "/v1/a2a/trust-rules", {
|
|
420
|
+
body: { identity_id: input.identityId, peer_identity_id: input.peerIdentityId },
|
|
421
|
+
})),
|
|
422
|
+
listTrustRules: async () => {
|
|
423
|
+
const body = await this.request("GET", "/v1/a2a/trust-rules");
|
|
424
|
+
return body.map(toA2ATrustRule);
|
|
425
|
+
},
|
|
426
|
+
deleteTrustRule: async (id) => {
|
|
427
|
+
await this.request("DELETE", `/v1/a2a/trust-rules/${encodeURIComponent(id)}`);
|
|
428
|
+
},
|
|
429
|
+
sendTask: async (input) => toA2ATask(await this.request("POST", "/v1/a2a/tasks", {
|
|
430
|
+
body: { target_identity_id: input.targetIdentityId, message: input.message },
|
|
431
|
+
})),
|
|
432
|
+
getTask: async (id) => toA2ATask(await this.request("GET", `/v1/a2a/tasks/${encodeURIComponent(id)}`)),
|
|
433
|
+
listTasks: async (identityId) => {
|
|
434
|
+
const body = await this.request("GET", `/v1/a2a/tasks?identity_id=${encodeURIComponent(identityId)}`);
|
|
435
|
+
return body.map(toA2ATask);
|
|
436
|
+
},
|
|
437
|
+
updateTask: async (id, input) => toA2ATask(await this.request("PATCH", `/v1/a2a/tasks/${encodeURIComponent(id)}`, {
|
|
438
|
+
body: { state: input.state, ...(input.result !== undefined ? { result: input.result } : {}) },
|
|
439
|
+
})),
|
|
440
|
+
getAgentCard: async (identityId) => toAgentCard(await this.request("GET", `/v1/identities/${encodeURIComponent(identityId)}/a2a/agent-card.json`)),
|
|
441
|
+
listAgentOverviews: async () => {
|
|
442
|
+
const body = await this.request("GET", "/v1/a2a/agents");
|
|
443
|
+
return body.map(toA2AAgentOverview);
|
|
444
|
+
},
|
|
445
|
+
setEnabled: async (identityId, enabled) => {
|
|
446
|
+
await this.request("POST", `/v1/identities/${encodeURIComponent(identityId)}/a2a/enabled`, {
|
|
447
|
+
body: { enabled },
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
setSkills: async (identityId, skills) => {
|
|
451
|
+
await this.request("PUT", `/v1/identities/${encodeURIComponent(identityId)}/a2a/skills`, {
|
|
452
|
+
body: { skills },
|
|
453
|
+
});
|
|
454
|
+
},
|
|
455
|
+
setListed: async (identityId, listed) => {
|
|
456
|
+
await this.request("PUT", `/v1/identities/${encodeURIComponent(identityId)}/a2a/listed`, {
|
|
457
|
+
body: { listed },
|
|
458
|
+
});
|
|
459
|
+
},
|
|
460
|
+
searchDirectory: async (query) => {
|
|
461
|
+
const params = new URLSearchParams();
|
|
462
|
+
if (query !== undefined)
|
|
463
|
+
params.set("q", query);
|
|
464
|
+
const qs = params.toString();
|
|
465
|
+
const body = await this.request("GET", `/v1/a2a/directory${qs ? `?${qs}` : ""}`);
|
|
466
|
+
return body.map(toA2ADirectoryEntry);
|
|
467
|
+
},
|
|
468
|
+
createInvitation: async (input) => toA2AInvitation(await this.request("POST", "/v1/a2a/invitations", {
|
|
469
|
+
body: {
|
|
470
|
+
from_identity_id: input.fromIdentityId,
|
|
471
|
+
to_identity_id: input.toIdentityId,
|
|
472
|
+
...(input.message !== undefined ? { message: input.message } : {}),
|
|
473
|
+
},
|
|
474
|
+
})),
|
|
475
|
+
listInvitations: async () => {
|
|
476
|
+
const body = await this.request("GET", "/v1/a2a/invitations");
|
|
477
|
+
return body.map(toA2AInvitationWithContext);
|
|
478
|
+
},
|
|
479
|
+
acceptInvitation: async (id) => toA2AInvitation(await this.request("POST", `/v1/a2a/invitations/${encodeURIComponent(id)}/accept`)),
|
|
480
|
+
declineInvitation: async (id) => toA2AInvitation(await this.request("POST", `/v1/a2a/invitations/${encodeURIComponent(id)}/decline`)),
|
|
481
|
+
revokeInvitation: async (id) => toA2AInvitation(await this.request("POST", `/v1/a2a/invitations/${encodeURIComponent(id)}/revoke`)),
|
|
482
|
+
};
|
|
483
|
+
this.domains = {
|
|
484
|
+
add: async (domain) => toDomain(await this.request("POST", "/v1/domains", { body: { domain } })),
|
|
485
|
+
list: async () => {
|
|
486
|
+
const body = await this.request("GET", "/v1/domains");
|
|
487
|
+
return body.map(toDomain);
|
|
488
|
+
},
|
|
489
|
+
verify: async (id) => toDomain(await this.request("POST", `/v1/domains/${encodeURIComponent(id)}/verify`)),
|
|
490
|
+
delete: async (id) => {
|
|
491
|
+
await this.request("DELETE", `/v1/domains/${encodeURIComponent(id)}`);
|
|
492
|
+
},
|
|
493
|
+
deliverability: async (id) => toDeliverabilityGuidance(await this.request("GET", `/v1/domains/${encodeURIComponent(id)}/deliverability`)),
|
|
494
|
+
};
|
|
495
|
+
this.status = {
|
|
496
|
+
health: async () => (await this.request("GET", "/v1/health")),
|
|
497
|
+
ready: async () => (await this.request("GET", "/v1/ready")),
|
|
498
|
+
};
|
|
499
|
+
this.providerAccounts = {
|
|
500
|
+
create: async (input) => toProviderAccount(await this.request("POST", "/v1/provider-accounts", {
|
|
501
|
+
body: { kind: input.kind, config: input.config },
|
|
502
|
+
})),
|
|
503
|
+
list: async () => {
|
|
504
|
+
const body = await this.request("GET", "/v1/provider-accounts");
|
|
505
|
+
return body.map(toProviderAccount);
|
|
506
|
+
},
|
|
507
|
+
delete: async (id) => {
|
|
508
|
+
await this.request("DELETE", `/v1/provider-accounts/${encodeURIComponent(id)}`);
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
this.providers = {
|
|
512
|
+
health: async () => toProviderHealth(await this.request("GET", "/v1/providers/health")),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
async whoami() {
|
|
516
|
+
return toWhoAmI(await this.request("GET", "/v1/me"));
|
|
517
|
+
}
|
|
518
|
+
/** Attaches a real email+password login to the org this API key belongs to — for orgs
|
|
519
|
+
* bootstrapped the old way (a raw admin key, no account behind it). Org-admin key only. */
|
|
520
|
+
async claimAccount(input) {
|
|
521
|
+
return toAuthSession(await this.request("POST", "/v1/auth/claim", { body: { email: input.email, password: input.password } }));
|
|
522
|
+
}
|
|
523
|
+
async request(method, path, options = {}) {
|
|
524
|
+
return sendRequest(this.fetchImpl, this.baseUrl, method, path, {
|
|
525
|
+
timeoutMs: this.timeoutMs,
|
|
526
|
+
maxRetries: this.maxRetries,
|
|
527
|
+
...options,
|
|
528
|
+
headers: { authorization: `Bearer ${this.apiKey}`, ...options.headers },
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/** Methods safe to replay after a network failure, since repeating them can't create duplicates. */
|
|
533
|
+
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "PUT", "DELETE"]);
|
|
534
|
+
/**
|
|
535
|
+
* Whether a failed attempt is worth repeating.
|
|
536
|
+
*
|
|
537
|
+
* A network-level failure on a non-idempotent method is deliberately *not* retried: the request
|
|
538
|
+
* may well have been applied before the connection dropped, and replaying it would send the mail
|
|
539
|
+
* twice. Explicit backpressure (429) and the server's own `retryable` flag are honoured for every
|
|
540
|
+
* method, because those mean the request demonstrably was not processed.
|
|
541
|
+
*/
|
|
542
|
+
function isRetryable(method, error) {
|
|
543
|
+
if (error instanceof AidApiError) {
|
|
544
|
+
if (error.status === 429)
|
|
545
|
+
return true;
|
|
546
|
+
if (error.retryable)
|
|
547
|
+
return true;
|
|
548
|
+
if (error.status >= 500 && IDEMPOTENT_METHODS.has(method))
|
|
549
|
+
return true;
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
if (error instanceof AidConnectionError)
|
|
553
|
+
return IDEMPOTENT_METHODS.has(method);
|
|
554
|
+
// Anything else came from a caller-supplied `fetch`, not from the transport — including
|
|
555
|
+
// framework control flow such as Next.js `redirect()`, which works by throwing. Retrying or
|
|
556
|
+
// rewriting those would break the caller's own semantics, so they pass straight through.
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
/** Honours `Retry-After` when the server sends one, else exponential backoff with jitter. */
|
|
560
|
+
function backoffMs(attempt, retryAfter) {
|
|
561
|
+
if (retryAfter) {
|
|
562
|
+
const seconds = Number(retryAfter);
|
|
563
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
564
|
+
return Math.min(seconds * 1000, 30_000);
|
|
565
|
+
}
|
|
566
|
+
const base = Math.min(250 * 2 ** attempt, 8_000);
|
|
567
|
+
return base + Math.random() * base * 0.25;
|
|
568
|
+
}
|
|
569
|
+
async function sendRequest(fetchImpl, baseUrl, method, path, options) {
|
|
570
|
+
const headers = { ...options.headers };
|
|
571
|
+
let payload;
|
|
572
|
+
if (options.body !== undefined) {
|
|
573
|
+
headers["content-type"] = "application/json";
|
|
574
|
+
payload = JSON.stringify(options.body);
|
|
575
|
+
}
|
|
576
|
+
const maxRetries = options.maxRetries ?? 0;
|
|
577
|
+
const timeoutMs = options.timeoutMs ?? 0;
|
|
578
|
+
let lastRetryAfter = null;
|
|
579
|
+
for (let attempt = 0;; attempt++) {
|
|
580
|
+
// A per-attempt deadline, combined with the caller's own signal so either can cancel. Without
|
|
581
|
+
// this a stalled connection hangs the caller forever.
|
|
582
|
+
const timeoutSignal = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
583
|
+
const signals = [options.signal, timeoutSignal].filter((s) => s !== undefined);
|
|
584
|
+
const signal = signals.length > 1 ? AbortSignal.any(signals) : signals[0];
|
|
585
|
+
try {
|
|
586
|
+
const res = await fetchImpl(`${baseUrl.replace(/\/$/, "")}${path}`, {
|
|
587
|
+
method,
|
|
588
|
+
headers,
|
|
589
|
+
...(payload !== undefined ? { body: payload } : {}),
|
|
590
|
+
...(signal !== undefined ? { signal } : {}),
|
|
591
|
+
});
|
|
592
|
+
const text = await res.text();
|
|
593
|
+
let data;
|
|
594
|
+
try {
|
|
595
|
+
data = text.length > 0 ? JSON.parse(text) : undefined;
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
// A SyntaxError, matching what JSON.parse itself raises — and distinguishable from a
|
|
599
|
+
// transport failure, since this response demonstrably came back from the API.
|
|
600
|
+
throw new SyntaxError(`unexpected non-JSON response (status ${res.status}): ${text.slice(0, 200)}`);
|
|
601
|
+
}
|
|
602
|
+
if (!res.ok) {
|
|
603
|
+
lastRetryAfter = res.headers.get("retry-after");
|
|
604
|
+
throw new AidApiError(res.status, data);
|
|
605
|
+
}
|
|
606
|
+
return data;
|
|
607
|
+
}
|
|
608
|
+
catch (raw) {
|
|
609
|
+
// The caller cancelled: surface that untouched, so `err.name === "AbortError"` and any
|
|
610
|
+
// signal-based control flow still works the way callers expect.
|
|
611
|
+
if (options.signal?.aborted)
|
|
612
|
+
throw raw;
|
|
613
|
+
// Only genuine transport failures get rewritten: `fetch` rejects with a TypeError when it
|
|
614
|
+
// cannot reach the host, and our own deadline shows up as an abort. Everything else —
|
|
615
|
+
// an AidApiError, a parse failure, or whatever a caller-supplied `fetch` chose to throw
|
|
616
|
+
// (Next.js `redirect()`, for one) — is passed through exactly as it was raised.
|
|
617
|
+
const timedOut = timeoutSignal?.aborted ?? false;
|
|
618
|
+
const err = raw instanceof TypeError || timedOut
|
|
619
|
+
? new AidConnectionError(method, `${baseUrl.replace(/\/$/, "")}${path}`, raw, timedOut)
|
|
620
|
+
: raw;
|
|
621
|
+
if (attempt >= maxRetries || !isRetryable(method, err))
|
|
622
|
+
throw err;
|
|
623
|
+
await sleep(backoffMs(attempt, lastRetryAfter), options.signal);
|
|
624
|
+
lastRetryAfter = null;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function toAttachmentInputs(inputs) {
|
|
629
|
+
if (!inputs?.length)
|
|
630
|
+
return undefined;
|
|
631
|
+
return inputs.map((a) => ({
|
|
632
|
+
filename: a.filename,
|
|
633
|
+
content_type: a.contentType,
|
|
634
|
+
content_base64: Buffer.from(a.content).toString("base64"),
|
|
635
|
+
...(a.contentId !== undefined ? { content_id: a.contentId } : {}),
|
|
636
|
+
}));
|
|
637
|
+
}
|
|
638
|
+
function toAttachment(body) {
|
|
639
|
+
const b = body;
|
|
640
|
+
return {
|
|
641
|
+
id: b.id,
|
|
642
|
+
filename: b.filename,
|
|
643
|
+
contentType: b.content_type,
|
|
644
|
+
sizeBytes: b.size_bytes,
|
|
645
|
+
sha256: b.sha256,
|
|
646
|
+
contentId: b.content_id,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function toIdentitySummary(body) {
|
|
650
|
+
const b = body;
|
|
651
|
+
return {
|
|
652
|
+
id: b.id,
|
|
653
|
+
handle: b.handle,
|
|
654
|
+
status: b.status,
|
|
655
|
+
mailboxAddress: b.mailbox_address,
|
|
656
|
+
vaultPublicKey: b.vault_public_key,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function toIdentityDetail(body) {
|
|
660
|
+
const b = body;
|
|
661
|
+
return {
|
|
662
|
+
id: b.id,
|
|
663
|
+
handle: b.handle,
|
|
664
|
+
displayName: b.display_name,
|
|
665
|
+
status: b.status,
|
|
666
|
+
vaultPublicKey: b.vault_public_key,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function toMailMessage(body) {
|
|
670
|
+
const b = body;
|
|
671
|
+
return {
|
|
672
|
+
id: b.id,
|
|
673
|
+
threadId: b.thread_id,
|
|
674
|
+
direction: b.direction,
|
|
675
|
+
status: b.status,
|
|
676
|
+
lastError: b.last_error ?? null,
|
|
677
|
+
providerMessageId: b.provider_message_id,
|
|
678
|
+
from: b.from,
|
|
679
|
+
to: b.to,
|
|
680
|
+
cc: b.cc,
|
|
681
|
+
subject: b.subject,
|
|
682
|
+
textBody: b.text_body,
|
|
683
|
+
htmlBody: b.html_body,
|
|
684
|
+
folder: b.folder,
|
|
685
|
+
createdAt: b.created_at,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function toMailThread(body) {
|
|
689
|
+
const b = body;
|
|
690
|
+
return { id: b.id, subject: b.subject, messages: b.messages.map(toMailMessage) };
|
|
691
|
+
}
|
|
692
|
+
function toWhoAmI(body) {
|
|
693
|
+
const b = body;
|
|
694
|
+
return { actorType: b.actor_type, actorId: b.actor_id, orgId: b.org_id, orgSlug: b.org_slug, orgName: b.org_name };
|
|
695
|
+
}
|
|
696
|
+
function toApiKeySummary(body) {
|
|
697
|
+
const b = body;
|
|
698
|
+
return {
|
|
699
|
+
id: b.id,
|
|
700
|
+
identityId: b.identity_id,
|
|
701
|
+
name: b.name,
|
|
702
|
+
keyPrefix: b.key_prefix,
|
|
703
|
+
scopes: b.scopes,
|
|
704
|
+
revokedAt: b.revoked_at,
|
|
705
|
+
createdAt: b.created_at,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function toCreatedApiKey(body) {
|
|
709
|
+
const b = body;
|
|
710
|
+
return { id: b.id, key: b.key, keyPrefix: b.key_prefix };
|
|
711
|
+
}
|
|
712
|
+
function toWebhookEndpoint(body) {
|
|
713
|
+
const b = body;
|
|
714
|
+
return {
|
|
715
|
+
id: b.id,
|
|
716
|
+
identityId: b.identity_id,
|
|
717
|
+
url: b.url,
|
|
718
|
+
eventTypes: b.event_types,
|
|
719
|
+
status: b.status,
|
|
720
|
+
createdAt: b.created_at,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function toCreatedWebhookEndpoint(body) {
|
|
724
|
+
const b = body;
|
|
725
|
+
return {
|
|
726
|
+
id: b.id,
|
|
727
|
+
identityId: b.identity_id,
|
|
728
|
+
url: b.url,
|
|
729
|
+
secret: b.secret,
|
|
730
|
+
eventTypes: b.event_types,
|
|
731
|
+
createdAt: b.created_at,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
function toWebhookDelivery(body) {
|
|
735
|
+
const b = body;
|
|
736
|
+
return {
|
|
737
|
+
id: b.id,
|
|
738
|
+
webhookEndpointId: b.webhook_endpoint_id,
|
|
739
|
+
endpointUrl: b.endpoint_url,
|
|
740
|
+
eventId: b.event_id,
|
|
741
|
+
eventType: b.event_type,
|
|
742
|
+
attemptCount: b.attempt_count,
|
|
743
|
+
status: b.status,
|
|
744
|
+
nextAttemptAt: b.next_attempt_at,
|
|
745
|
+
lastError: b.last_error,
|
|
746
|
+
deliveredAt: b.delivered_at,
|
|
747
|
+
eventCreatedAt: b.event_created_at,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function toVaultSecretSummary(body) {
|
|
751
|
+
const b = body;
|
|
752
|
+
return {
|
|
753
|
+
id: b.id,
|
|
754
|
+
identityId: b.identity_id,
|
|
755
|
+
name: b.name,
|
|
756
|
+
version: b.version,
|
|
757
|
+
status: b.status,
|
|
758
|
+
createdAt: b.created_at,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function toVaultEnvelope(body) {
|
|
762
|
+
const b = body;
|
|
763
|
+
return { ciphertext: b.ciphertext, ephemeralPublicKey: b.ephemeral_public_key };
|
|
764
|
+
}
|
|
765
|
+
function toCreatedLease(body) {
|
|
766
|
+
const b = body;
|
|
767
|
+
return { leaseId: b.lease_id, operation: b.operation, expiresAt: b.expires_at };
|
|
768
|
+
}
|
|
769
|
+
function toContact(body) {
|
|
770
|
+
const b = body;
|
|
771
|
+
return {
|
|
772
|
+
id: b.id,
|
|
773
|
+
prefix: b.prefix,
|
|
774
|
+
firstName: b.first_name,
|
|
775
|
+
middleName: b.middle_name,
|
|
776
|
+
lastName: b.last_name,
|
|
777
|
+
suffix: b.suffix,
|
|
778
|
+
displayName: b.display_name,
|
|
779
|
+
company: b.company,
|
|
780
|
+
jobTitle: b.job_title,
|
|
781
|
+
emails: b.emails,
|
|
782
|
+
phones: b.phones,
|
|
783
|
+
websites: b.websites,
|
|
784
|
+
addresses: b.addresses.map((a) => ({
|
|
785
|
+
label: a.label,
|
|
786
|
+
street: a.street,
|
|
787
|
+
city: a.city,
|
|
788
|
+
state: a.state,
|
|
789
|
+
postalCode: a.postal_code,
|
|
790
|
+
country: a.country,
|
|
791
|
+
})),
|
|
792
|
+
dates: b.dates,
|
|
793
|
+
customFields: b.custom_fields,
|
|
794
|
+
notes: b.notes,
|
|
795
|
+
status: b.status,
|
|
796
|
+
sourceIdentityId: b.source_identity_id,
|
|
797
|
+
createdAt: b.created_at,
|
|
798
|
+
updatedAt: b.updated_at,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function fromContactInput(input) {
|
|
802
|
+
return {
|
|
803
|
+
...(input.prefix !== undefined ? { prefix: input.prefix } : {}),
|
|
804
|
+
...(input.firstName !== undefined ? { first_name: input.firstName } : {}),
|
|
805
|
+
...(input.middleName !== undefined ? { middle_name: input.middleName } : {}),
|
|
806
|
+
...(input.lastName !== undefined ? { last_name: input.lastName } : {}),
|
|
807
|
+
...(input.suffix !== undefined ? { suffix: input.suffix } : {}),
|
|
808
|
+
...(input.displayName !== undefined ? { display_name: input.displayName } : {}),
|
|
809
|
+
...(input.company !== undefined ? { company: input.company } : {}),
|
|
810
|
+
...(input.jobTitle !== undefined ? { job_title: input.jobTitle } : {}),
|
|
811
|
+
...(input.emails !== undefined ? { emails: input.emails } : {}),
|
|
812
|
+
...(input.phones !== undefined ? { phones: input.phones } : {}),
|
|
813
|
+
...(input.websites !== undefined ? { websites: input.websites } : {}),
|
|
814
|
+
...(input.addresses !== undefined
|
|
815
|
+
? {
|
|
816
|
+
addresses: input.addresses.map((a) => ({
|
|
817
|
+
label: a.label,
|
|
818
|
+
street: a.street,
|
|
819
|
+
city: a.city,
|
|
820
|
+
state: a.state,
|
|
821
|
+
postal_code: a.postalCode,
|
|
822
|
+
country: a.country,
|
|
823
|
+
})),
|
|
824
|
+
}
|
|
825
|
+
: {}),
|
|
826
|
+
...(input.dates !== undefined ? { dates: input.dates } : {}),
|
|
827
|
+
...(input.customFields !== undefined ? { custom_fields: input.customFields } : {}),
|
|
828
|
+
...(input.notes !== undefined ? { notes: input.notes } : {}),
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function toContactRule(body) {
|
|
832
|
+
const b = body;
|
|
833
|
+
return {
|
|
834
|
+
id: b.id,
|
|
835
|
+
identityId: b.identity_id,
|
|
836
|
+
channel: b.channel,
|
|
837
|
+
matchAddress: b.match_address,
|
|
838
|
+
action: b.action,
|
|
839
|
+
createdAt: b.created_at,
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
function toA2ATrustRule(body) {
|
|
843
|
+
const b = body;
|
|
844
|
+
return { id: b.id, identityId: b.identity_id, peerIdentityId: b.peer_identity_id, createdAt: b.created_at };
|
|
845
|
+
}
|
|
846
|
+
function toA2ATask(body) {
|
|
847
|
+
const b = body;
|
|
848
|
+
return {
|
|
849
|
+
id: b.id,
|
|
850
|
+
targetIdentityId: b.target_identity_id,
|
|
851
|
+
callerIdentityId: b.caller_identity_id,
|
|
852
|
+
state: b.state,
|
|
853
|
+
message: b.message,
|
|
854
|
+
result: b.result,
|
|
855
|
+
createdAt: b.created_at,
|
|
856
|
+
updatedAt: b.updated_at,
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
function toAgentCard(body) {
|
|
860
|
+
return body;
|
|
861
|
+
}
|
|
862
|
+
function toA2AAgentOverview(body) {
|
|
863
|
+
const b = body;
|
|
864
|
+
return {
|
|
865
|
+
identityId: b.identity_id,
|
|
866
|
+
handle: b.handle,
|
|
867
|
+
enabled: b.enabled,
|
|
868
|
+
skills: b.skills,
|
|
869
|
+
listed: b.listed,
|
|
870
|
+
inboundTasks: b.inbound_tasks,
|
|
871
|
+
outboundTasks: b.outbound_tasks,
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
function toA2ADirectoryEntry(body) {
|
|
875
|
+
const b = body;
|
|
876
|
+
return { identityId: b.identity_id, handle: b.handle, orgName: b.org_name, skills: b.skills };
|
|
877
|
+
}
|
|
878
|
+
function toA2AInvitation(body) {
|
|
879
|
+
const b = body;
|
|
880
|
+
return {
|
|
881
|
+
id: b.id,
|
|
882
|
+
fromIdentityId: b.from_identity_id,
|
|
883
|
+
toIdentityId: b.to_identity_id,
|
|
884
|
+
status: b.status,
|
|
885
|
+
message: b.message,
|
|
886
|
+
createdAt: b.created_at,
|
|
887
|
+
respondedAt: b.responded_at,
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function toA2AInvitationWithContext(body) {
|
|
891
|
+
const b = body;
|
|
892
|
+
return { ...toA2AInvitation(body), fromHandle: b.from_handle, toHandle: b.to_handle, direction: b.direction };
|
|
893
|
+
}
|
|
894
|
+
function toDomain(body) {
|
|
895
|
+
const b = body;
|
|
896
|
+
return {
|
|
897
|
+
id: b.id,
|
|
898
|
+
domain: b.domain,
|
|
899
|
+
status: b.status,
|
|
900
|
+
verification: {
|
|
901
|
+
recordType: b.verification.record_type,
|
|
902
|
+
recordName: b.verification.record_name,
|
|
903
|
+
recordValue: b.verification.record_value,
|
|
904
|
+
},
|
|
905
|
+
verifiedAt: b.verified_at,
|
|
906
|
+
createdAt: b.created_at,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function toDeliverabilityGuidance(body) {
|
|
910
|
+
const b = body;
|
|
911
|
+
return {
|
|
912
|
+
providerKind: b.provider_kind,
|
|
913
|
+
records: b.records.map((r) => ({
|
|
914
|
+
purpose: r.purpose,
|
|
915
|
+
recordType: r.record_type,
|
|
916
|
+
recordName: r.record_name,
|
|
917
|
+
recordValue: r.record_value,
|
|
918
|
+
note: r.note,
|
|
919
|
+
})),
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function toProviderAccount(body) {
|
|
923
|
+
const b = body;
|
|
924
|
+
return { id: b.id, kind: b.kind, status: b.status, createdAt: b.created_at };
|
|
925
|
+
}
|
|
926
|
+
function toProviderHealth(body) {
|
|
927
|
+
const b = body;
|
|
928
|
+
return { healthy: b.healthy, detail: b.detail, source: b.source };
|
|
929
|
+
}
|
|
930
|
+
function toIdentitySuspension(body) {
|
|
931
|
+
const b = body;
|
|
932
|
+
return {
|
|
933
|
+
id: b.id,
|
|
934
|
+
identityId: b.identity_id,
|
|
935
|
+
reason: b.reason,
|
|
936
|
+
suspendedBy: b.suspended_by,
|
|
937
|
+
suspendedAt: b.suspended_at,
|
|
938
|
+
reinstatedAt: b.reinstated_at,
|
|
939
|
+
reinstatedBy: b.reinstated_by,
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function toVaultLease(body) {
|
|
943
|
+
const b = body;
|
|
944
|
+
return {
|
|
945
|
+
id: b.id,
|
|
946
|
+
secretId: b.secret_id,
|
|
947
|
+
secretName: b.secret_name,
|
|
948
|
+
identityId: b.identity_id,
|
|
949
|
+
identityHandle: b.identity_handle,
|
|
950
|
+
operation: b.operation,
|
|
951
|
+
expiresAt: b.expires_at,
|
|
952
|
+
consumedAt: b.consumed_at,
|
|
953
|
+
createdAt: b.created_at,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
function toTunnel(body) {
|
|
957
|
+
const b = body;
|
|
958
|
+
return {
|
|
959
|
+
id: b.id,
|
|
960
|
+
identityId: b.identity_id,
|
|
961
|
+
hostname: b.hostname,
|
|
962
|
+
status: b.status,
|
|
963
|
+
connected: b.connected,
|
|
964
|
+
edgeUrl: b.edge_url,
|
|
965
|
+
lastConnectedAt: b.last_connected_at,
|
|
966
|
+
lastDisconnectedAt: b.last_disconnected_at,
|
|
967
|
+
createdAt: b.created_at,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
function toEvent(raw) {
|
|
971
|
+
const b = raw;
|
|
972
|
+
return {
|
|
973
|
+
id: b.id,
|
|
974
|
+
type: b.type,
|
|
975
|
+
identityId: b.identity_id,
|
|
976
|
+
aggregateType: b.aggregate_type,
|
|
977
|
+
aggregateId: b.aggregate_id,
|
|
978
|
+
payload: b.payload,
|
|
979
|
+
createdAt: b.created_at,
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function toWaitForEventResult(body) {
|
|
983
|
+
const b = body;
|
|
984
|
+
return { event: b.event ? toEvent(b.event) : null, timedOut: b.timed_out };
|
|
985
|
+
}
|