@slashfi/agents-sdk 0.5.0 → 0.7.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/agent-definitions/auth.d.ts.map +1 -1
- package/dist/agent-definitions/auth.js +16 -3
- package/dist/agent-definitions/auth.js.map +1 -1
- package/dist/agent-definitions/integrations.d.ts +162 -0
- package/dist/agent-definitions/integrations.d.ts.map +1 -0
- package/dist/agent-definitions/integrations.js +861 -0
- package/dist/agent-definitions/integrations.js.map +1 -0
- package/dist/agent-definitions/secrets.d.ts +26 -3
- package/dist/agent-definitions/secrets.d.ts.map +1 -1
- package/dist/agent-definitions/secrets.js +34 -28
- package/dist/agent-definitions/secrets.js.map +1 -1
- package/dist/agent-definitions/users.d.ts +80 -0
- package/dist/agent-definitions/users.d.ts.map +1 -0
- package/dist/agent-definitions/users.js +397 -0
- package/dist/agent-definitions/users.js.map +1 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js.map +1 -1
- package/dist/define.d.ts +6 -1
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -0
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js.map +1 -1
- package/dist/server.d.ts +28 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +477 -26
- package/dist/server.js.map +1 -1
- package/dist/slack-oauth.d.ts +27 -0
- package/dist/slack-oauth.d.ts.map +1 -0
- package/dist/slack-oauth.js +48 -0
- package/dist/slack-oauth.js.map +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/web-pages.d.ts +8 -0
- package/dist/web-pages.d.ts.map +1 -0
- package/dist/web-pages.js +169 -0
- package/dist/web-pages.js.map +1 -0
- package/package.json +2 -1
- package/src/agent-definitions/auth.ts +37 -14
- package/src/agent-definitions/integrations.ts +1209 -0
- package/src/agent-definitions/secrets.ts +85 -36
- package/src/agent-definitions/users.ts +533 -0
- package/src/crypto.ts +3 -1
- package/src/define.ts +8 -0
- package/src/index.ts +57 -3
- package/src/jwt.ts +7 -5
- package/src/server.ts +565 -33
- package/src/slack-oauth.ts +66 -0
- package/src/types.ts +83 -0
- package/src/web-pages.ts +178 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Users Agent (@users)
|
|
3
|
+
*
|
|
4
|
+
* Built-in agent for user and user identity management.
|
|
5
|
+
* Provides user CRUD and identity linking (OAuth providers, SSO, etc.).
|
|
6
|
+
*
|
|
7
|
+
* A User is a human entity. A UserIdentity links a user to an external
|
|
8
|
+
* identity provider (e.g. Google, Slack, GitHub) — one user can have
|
|
9
|
+
* many identities.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { createAgentRegistry, createUsersAgent } from '@slashfi/agents-sdk';
|
|
14
|
+
*
|
|
15
|
+
* const registry = createAgentRegistry();
|
|
16
|
+
* registry.register(createUsersAgent({
|
|
17
|
+
* store: myUserStore,
|
|
18
|
+
* }));
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { defineAgent, defineTool } from "../define.js";
|
|
23
|
+
import type { AgentDefinition, ToolContext, ToolDefinition } from "../types.js";
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// User Types
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
export interface User {
|
|
30
|
+
id: string;
|
|
31
|
+
tenantId: string;
|
|
32
|
+
email?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
avatarUrl?: string;
|
|
35
|
+
metadata?: Record<string, unknown>;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
updatedAt: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A link between a User and an external identity provider.
|
|
42
|
+
* One user can have many identities (e.g. Google + Slack + GitHub).
|
|
43
|
+
*/
|
|
44
|
+
export interface UserIdentity {
|
|
45
|
+
id: string;
|
|
46
|
+
userId: string;
|
|
47
|
+
provider: string;
|
|
48
|
+
/** External provider's user ID */
|
|
49
|
+
providerUserId: string;
|
|
50
|
+
/** Display info from the provider */
|
|
51
|
+
email?: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
avatarUrl?: string;
|
|
54
|
+
/** OAuth tokens (encrypted at rest in the store) */
|
|
55
|
+
accessToken?: string;
|
|
56
|
+
refreshToken?: string;
|
|
57
|
+
expiresAt?: number;
|
|
58
|
+
tokenType?: string;
|
|
59
|
+
scopes?: string[];
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
connectedAt: number;
|
|
62
|
+
updatedAt: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================
|
|
66
|
+
// User Store Interface
|
|
67
|
+
// ============================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Pluggable storage backend for users and identities.
|
|
71
|
+
*/
|
|
72
|
+
export interface UserStore {
|
|
73
|
+
// Users
|
|
74
|
+
createUser(user: Omit<User, "createdAt" | "updatedAt">): Promise<User>;
|
|
75
|
+
getUser(userId: string): Promise<User | null>;
|
|
76
|
+
getUserByEmail(tenantId: string, email: string): Promise<User | null>;
|
|
77
|
+
listUsers(tenantId: string): Promise<User[]>;
|
|
78
|
+
updateUser(
|
|
79
|
+
userId: string,
|
|
80
|
+
updates: Partial<Pick<User, "email" | "name" | "avatarUrl" | "metadata">>,
|
|
81
|
+
): Promise<User | null>;
|
|
82
|
+
deleteUser(userId: string): Promise<boolean>;
|
|
83
|
+
|
|
84
|
+
// Identities
|
|
85
|
+
createIdentity(
|
|
86
|
+
identity: Omit<UserIdentity, "connectedAt" | "updatedAt">,
|
|
87
|
+
): Promise<UserIdentity>;
|
|
88
|
+
getIdentity(identityId: string): Promise<UserIdentity | null>;
|
|
89
|
+
getIdentityByProvider(
|
|
90
|
+
userId: string,
|
|
91
|
+
provider: string,
|
|
92
|
+
): Promise<UserIdentity | null>;
|
|
93
|
+
findIdentityByProviderUserId(
|
|
94
|
+
provider: string,
|
|
95
|
+
providerUserId: string,
|
|
96
|
+
): Promise<UserIdentity | null>;
|
|
97
|
+
listIdentities(userId: string): Promise<UserIdentity[]>;
|
|
98
|
+
updateIdentity(
|
|
99
|
+
identityId: string,
|
|
100
|
+
updates: Partial<
|
|
101
|
+
Pick<
|
|
102
|
+
UserIdentity,
|
|
103
|
+
| "accessToken"
|
|
104
|
+
| "refreshToken"
|
|
105
|
+
| "expiresAt"
|
|
106
|
+
| "email"
|
|
107
|
+
| "name"
|
|
108
|
+
| "avatarUrl"
|
|
109
|
+
| "metadata"
|
|
110
|
+
>
|
|
111
|
+
>,
|
|
112
|
+
): Promise<UserIdentity | null>;
|
|
113
|
+
deleteIdentity(identityId: string): Promise<boolean>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ============================================
|
|
117
|
+
// In-Memory User Store
|
|
118
|
+
// ============================================
|
|
119
|
+
|
|
120
|
+
function generateId(prefix: string): string {
|
|
121
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
122
|
+
let id = prefix;
|
|
123
|
+
for (let i = 0; i < 20; i++) {
|
|
124
|
+
id += chars[Math.floor(Math.random() * chars.length)];
|
|
125
|
+
}
|
|
126
|
+
return id;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createInMemoryUserStore(): UserStore {
|
|
130
|
+
const users = new Map<string, User>();
|
|
131
|
+
const identities = new Map<string, UserIdentity>();
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
async createUser(input) {
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
const user: User = { ...input, createdAt: now, updatedAt: now };
|
|
137
|
+
users.set(user.id, user);
|
|
138
|
+
return user;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async getUser(userId) {
|
|
142
|
+
return users.get(userId) ?? null;
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
async getUserByEmail(tenantId, email) {
|
|
146
|
+
for (const u of users.values()) {
|
|
147
|
+
if (u.tenantId === tenantId && u.email === email) return u;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async listUsers(tenantId) {
|
|
153
|
+
return Array.from(users.values()).filter((u) => u.tenantId === tenantId);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async updateUser(userId, updates) {
|
|
157
|
+
const user = users.get(userId);
|
|
158
|
+
if (!user) return null;
|
|
159
|
+
const updated = { ...user, ...updates, updatedAt: Date.now() };
|
|
160
|
+
users.set(userId, updated);
|
|
161
|
+
return updated;
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
async deleteUser(userId) {
|
|
165
|
+
// Delete identities too
|
|
166
|
+
for (const [id, identity] of identities) {
|
|
167
|
+
if (identity.userId === userId) identities.delete(id);
|
|
168
|
+
}
|
|
169
|
+
return users.delete(userId);
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
async createIdentity(input) {
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const identity: UserIdentity = {
|
|
175
|
+
...input,
|
|
176
|
+
connectedAt: now,
|
|
177
|
+
updatedAt: now,
|
|
178
|
+
};
|
|
179
|
+
identities.set(identity.id, identity);
|
|
180
|
+
return identity;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
async getIdentity(identityId) {
|
|
184
|
+
return identities.get(identityId) ?? null;
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
async getIdentityByProvider(userId, provider) {
|
|
188
|
+
for (const identity of identities.values()) {
|
|
189
|
+
if (identity.userId === userId && identity.provider === provider)
|
|
190
|
+
return identity;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async findIdentityByProviderUserId(provider, providerUserId) {
|
|
196
|
+
for (const identity of identities.values()) {
|
|
197
|
+
if (
|
|
198
|
+
identity.provider === provider &&
|
|
199
|
+
identity.providerUserId === providerUserId
|
|
200
|
+
)
|
|
201
|
+
return identity;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
async listIdentities(userId) {
|
|
207
|
+
return Array.from(identities.values()).filter((i) => i.userId === userId);
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
async updateIdentity(identityId, updates) {
|
|
211
|
+
const identity = identities.get(identityId);
|
|
212
|
+
if (!identity) return null;
|
|
213
|
+
const updated = { ...identity, ...updates, updatedAt: Date.now() };
|
|
214
|
+
identities.set(identityId, updated);
|
|
215
|
+
return updated;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
async deleteIdentity(identityId) {
|
|
219
|
+
return identities.delete(identityId);
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ============================================
|
|
225
|
+
// Create Users Agent
|
|
226
|
+
// ============================================
|
|
227
|
+
|
|
228
|
+
export interface UsersAgentOptions {
|
|
229
|
+
/** User store backend */
|
|
230
|
+
store: UserStore;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
234
|
+
const { store } = options;
|
|
235
|
+
|
|
236
|
+
// ---- create_user ----
|
|
237
|
+
const createUserTool = defineTool({
|
|
238
|
+
name: "create_user",
|
|
239
|
+
description: "Create a new user.",
|
|
240
|
+
visibility: "public" as const,
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object" as const,
|
|
243
|
+
properties: {
|
|
244
|
+
id: {
|
|
245
|
+
type: "string",
|
|
246
|
+
description: "User ID (optional, auto-generated if omitted)",
|
|
247
|
+
},
|
|
248
|
+
tenantId: { type: "string", description: "Tenant ID" },
|
|
249
|
+
email: { type: "string", description: "Email address" },
|
|
250
|
+
name: { type: "string", description: "Display name" },
|
|
251
|
+
avatarUrl: { type: "string", description: "Avatar URL" },
|
|
252
|
+
metadata: { type: "object", description: "Additional metadata" },
|
|
253
|
+
},
|
|
254
|
+
required: ["tenantId"],
|
|
255
|
+
},
|
|
256
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
257
|
+
const user = await store.createUser({
|
|
258
|
+
id: input.id ?? generateId("user_"),
|
|
259
|
+
tenantId: input.tenantId,
|
|
260
|
+
email: input.email,
|
|
261
|
+
name: input.name,
|
|
262
|
+
avatarUrl: input.avatarUrl,
|
|
263
|
+
metadata: input.metadata,
|
|
264
|
+
});
|
|
265
|
+
return { success: true, user };
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// ---- get_user ----
|
|
270
|
+
const getUserTool = defineTool({
|
|
271
|
+
name: "get_user",
|
|
272
|
+
description: "Get a user by ID.",
|
|
273
|
+
visibility: "public" as const,
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: "object" as const,
|
|
276
|
+
properties: {
|
|
277
|
+
userId: { type: "string", description: "User ID" },
|
|
278
|
+
},
|
|
279
|
+
required: ["userId"],
|
|
280
|
+
},
|
|
281
|
+
execute: async (input: { userId: string }, __ctx: ToolContext) => {
|
|
282
|
+
const user = await store.getUser(input.userId);
|
|
283
|
+
if (!user) return { error: `User '${input.userId}' not found` };
|
|
284
|
+
return { user };
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ---- list_users ----
|
|
289
|
+
const listUsersTool = defineTool({
|
|
290
|
+
name: "list_users",
|
|
291
|
+
description: "List users in a tenant.",
|
|
292
|
+
visibility: "public" as const,
|
|
293
|
+
inputSchema: {
|
|
294
|
+
type: "object" as const,
|
|
295
|
+
properties: {
|
|
296
|
+
tenantId: { type: "string", description: "Tenant ID" },
|
|
297
|
+
},
|
|
298
|
+
required: ["tenantId"],
|
|
299
|
+
},
|
|
300
|
+
execute: async (input: { tenantId: string }, __ctx: ToolContext) => {
|
|
301
|
+
const users = await store.listUsers(input.tenantId);
|
|
302
|
+
return { users };
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ---- update_user ----
|
|
307
|
+
const updateUserTool = defineTool({
|
|
308
|
+
name: "update_user",
|
|
309
|
+
description: "Update a user's profile.",
|
|
310
|
+
visibility: "public" as const,
|
|
311
|
+
inputSchema: {
|
|
312
|
+
type: "object" as const,
|
|
313
|
+
properties: {
|
|
314
|
+
userId: { type: "string", description: "User ID" },
|
|
315
|
+
email: { type: "string", description: "New email" },
|
|
316
|
+
name: { type: "string", description: "New name" },
|
|
317
|
+
avatarUrl: { type: "string", description: "New avatar URL" },
|
|
318
|
+
metadata: { type: "object", description: "New metadata (merged)" },
|
|
319
|
+
},
|
|
320
|
+
required: ["userId"],
|
|
321
|
+
},
|
|
322
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
323
|
+
const { userId, ...updates } = input;
|
|
324
|
+
const user = await store.updateUser(userId, updates);
|
|
325
|
+
if (!user) return { error: `User '${userId}' not found` };
|
|
326
|
+
return { success: true, user };
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// ---- link_identity ----
|
|
331
|
+
const linkIdentityTool = defineTool({
|
|
332
|
+
name: "link_identity",
|
|
333
|
+
description:
|
|
334
|
+
"Link an external identity (OAuth provider) to a user. " +
|
|
335
|
+
"Creates a UserIdentity record associating the user with a provider account.",
|
|
336
|
+
visibility: "public" as const,
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: "object" as const,
|
|
339
|
+
properties: {
|
|
340
|
+
userId: { type: "string", description: "User ID to link to" },
|
|
341
|
+
provider: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Provider name (e.g. 'google', 'slack', 'github')",
|
|
344
|
+
},
|
|
345
|
+
providerUserId: {
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "The user's ID in the external provider",
|
|
348
|
+
},
|
|
349
|
+
email: { type: "string", description: "Email from the provider" },
|
|
350
|
+
name: { type: "string", description: "Name from the provider" },
|
|
351
|
+
avatarUrl: {
|
|
352
|
+
type: "string",
|
|
353
|
+
description: "Avatar URL from the provider",
|
|
354
|
+
},
|
|
355
|
+
accessToken: { type: "string", description: "OAuth access token" },
|
|
356
|
+
refreshToken: { type: "string", description: "OAuth refresh token" },
|
|
357
|
+
expiresAt: {
|
|
358
|
+
type: "number",
|
|
359
|
+
description: "Token expiry timestamp (ms)",
|
|
360
|
+
},
|
|
361
|
+
scopes: {
|
|
362
|
+
type: "array",
|
|
363
|
+
items: { type: "string" },
|
|
364
|
+
description: "Granted scopes",
|
|
365
|
+
},
|
|
366
|
+
metadata: {
|
|
367
|
+
type: "object",
|
|
368
|
+
description: "Additional provider-specific data",
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
required: ["userId", "provider", "providerUserId"],
|
|
372
|
+
},
|
|
373
|
+
execute: async (input: any, __ctx: ToolContext) => {
|
|
374
|
+
// Check if identity already exists
|
|
375
|
+
const existing = await store.getIdentityByProvider(
|
|
376
|
+
input.userId,
|
|
377
|
+
input.provider,
|
|
378
|
+
);
|
|
379
|
+
if (existing) {
|
|
380
|
+
// Update existing
|
|
381
|
+
const updated = await store.updateIdentity(existing.id, {
|
|
382
|
+
accessToken: input.accessToken,
|
|
383
|
+
refreshToken: input.refreshToken,
|
|
384
|
+
expiresAt: input.expiresAt,
|
|
385
|
+
email: input.email,
|
|
386
|
+
name: input.name,
|
|
387
|
+
avatarUrl: input.avatarUrl,
|
|
388
|
+
metadata: input.metadata,
|
|
389
|
+
});
|
|
390
|
+
return { success: true, identity: updated, updated: true };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const identity = await store.createIdentity({
|
|
394
|
+
id: generateId("ident_"),
|
|
395
|
+
userId: input.userId,
|
|
396
|
+
provider: input.provider,
|
|
397
|
+
providerUserId: input.providerUserId,
|
|
398
|
+
email: input.email,
|
|
399
|
+
name: input.name,
|
|
400
|
+
avatarUrl: input.avatarUrl,
|
|
401
|
+
accessToken: input.accessToken,
|
|
402
|
+
refreshToken: input.refreshToken,
|
|
403
|
+
expiresAt: input.expiresAt,
|
|
404
|
+
tokenType: input.tokenType,
|
|
405
|
+
scopes: input.scopes,
|
|
406
|
+
metadata: input.metadata,
|
|
407
|
+
});
|
|
408
|
+
return { success: true, identity, created: true };
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// ---- list_identities ----
|
|
413
|
+
const listIdentitiesTool = defineTool({
|
|
414
|
+
name: "list_identities",
|
|
415
|
+
description: "List all linked identities for a user.",
|
|
416
|
+
visibility: "public" as const,
|
|
417
|
+
inputSchema: {
|
|
418
|
+
type: "object" as const,
|
|
419
|
+
properties: {
|
|
420
|
+
userId: { type: "string", description: "User ID" },
|
|
421
|
+
},
|
|
422
|
+
required: ["userId"],
|
|
423
|
+
},
|
|
424
|
+
execute: async (input: { userId: string }, __ctx: ToolContext) => {
|
|
425
|
+
const identities = await store.listIdentities(input.userId);
|
|
426
|
+
return {
|
|
427
|
+
identities: identities.map((i) => ({
|
|
428
|
+
id: i.id,
|
|
429
|
+
provider: i.provider,
|
|
430
|
+
providerUserId: i.providerUserId,
|
|
431
|
+
email: i.email,
|
|
432
|
+
name: i.name,
|
|
433
|
+
connectedAt: i.connectedAt,
|
|
434
|
+
hasAccessToken: !!i.accessToken,
|
|
435
|
+
expiresAt: i.expiresAt,
|
|
436
|
+
})),
|
|
437
|
+
};
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ---- unlink_identity ----
|
|
442
|
+
const unlinkIdentityTool = defineTool({
|
|
443
|
+
name: "unlink_identity",
|
|
444
|
+
description: "Remove a linked identity from a user.",
|
|
445
|
+
visibility: "public" as const,
|
|
446
|
+
inputSchema: {
|
|
447
|
+
type: "object" as const,
|
|
448
|
+
properties: {
|
|
449
|
+
identityId: { type: "string", description: "Identity ID to unlink" },
|
|
450
|
+
},
|
|
451
|
+
required: ["identityId"],
|
|
452
|
+
},
|
|
453
|
+
execute: async (input: { identityId: string }, __ctx: ToolContext) => {
|
|
454
|
+
const deleted = await store.deleteIdentity(input.identityId);
|
|
455
|
+
return { success: deleted };
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// ---- resolve_identity ----
|
|
460
|
+
const resolveIdentityTool = defineTool({
|
|
461
|
+
name: "resolve_identity",
|
|
462
|
+
description:
|
|
463
|
+
"Find a user by their external provider identity. " +
|
|
464
|
+
"Useful for login flows — given a provider + providerUserId, find the linked user.",
|
|
465
|
+
visibility: "public" as const,
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: "object" as const,
|
|
468
|
+
properties: {
|
|
469
|
+
provider: {
|
|
470
|
+
type: "string",
|
|
471
|
+
description: "Provider name (e.g. 'google')",
|
|
472
|
+
},
|
|
473
|
+
providerUserId: {
|
|
474
|
+
type: "string",
|
|
475
|
+
description: "External user ID from the provider",
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
required: ["provider", "providerUserId"],
|
|
479
|
+
},
|
|
480
|
+
execute: async (
|
|
481
|
+
input: { provider: string; providerUserId: string },
|
|
482
|
+
__ctx: ToolContext,
|
|
483
|
+
) => {
|
|
484
|
+
const identity = await store.findIdentityByProviderUserId(
|
|
485
|
+
input.provider,
|
|
486
|
+
input.providerUserId,
|
|
487
|
+
);
|
|
488
|
+
if (!identity) return { found: false };
|
|
489
|
+
|
|
490
|
+
const user = await store.getUser(identity.userId);
|
|
491
|
+
return {
|
|
492
|
+
found: true,
|
|
493
|
+
user: user
|
|
494
|
+
? {
|
|
495
|
+
id: user.id,
|
|
496
|
+
email: user.email,
|
|
497
|
+
name: user.name,
|
|
498
|
+
tenantId: user.tenantId,
|
|
499
|
+
}
|
|
500
|
+
: null,
|
|
501
|
+
identity: {
|
|
502
|
+
id: identity.id,
|
|
503
|
+
provider: identity.provider,
|
|
504
|
+
providerUserId: identity.providerUserId,
|
|
505
|
+
email: identity.email,
|
|
506
|
+
},
|
|
507
|
+
};
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
return defineAgent({
|
|
512
|
+
path: "@users",
|
|
513
|
+
entrypoint:
|
|
514
|
+
"You are the users agent. You manage user accounts and their linked external identities " +
|
|
515
|
+
"(OAuth providers like Google, Slack, GitHub, etc.).",
|
|
516
|
+
config: {
|
|
517
|
+
name: "Users",
|
|
518
|
+
description: "User and identity management",
|
|
519
|
+
supportedActions: ["execute_tool", "describe_tools", "load"],
|
|
520
|
+
},
|
|
521
|
+
visibility: "public",
|
|
522
|
+
tools: [
|
|
523
|
+
createUserTool,
|
|
524
|
+
getUserTool,
|
|
525
|
+
listUsersTool,
|
|
526
|
+
updateUserTool,
|
|
527
|
+
linkIdentityTool,
|
|
528
|
+
listIdentitiesTool,
|
|
529
|
+
unlinkIdentityTool,
|
|
530
|
+
resolveIdentityTool,
|
|
531
|
+
] as ToolDefinition[],
|
|
532
|
+
});
|
|
533
|
+
}
|
package/src/crypto.ts
CHANGED
|
@@ -43,7 +43,9 @@ export async function encryptSecret(
|
|
|
43
43
|
key,
|
|
44
44
|
encoder.encode(plaintext),
|
|
45
45
|
);
|
|
46
|
-
const combined = new Uint8Array(
|
|
46
|
+
const combined = new Uint8Array(
|
|
47
|
+
iv.length + new Uint8Array(ciphertext).length,
|
|
48
|
+
);
|
|
47
49
|
combined.set(iv);
|
|
48
50
|
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
49
51
|
return btoa(String.fromCharCode(...combined));
|
package/src/define.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
AgentConfig,
|
|
9
9
|
AgentDefinition,
|
|
10
10
|
AgentRuntime,
|
|
11
|
+
IntegrationMethods,
|
|
11
12
|
JsonSchema,
|
|
12
13
|
ToolContext,
|
|
13
14
|
ToolDefinition,
|
|
@@ -112,6 +113,12 @@ export interface DefineAgentOptions<
|
|
|
112
113
|
|
|
113
114
|
/** Explicit allowed callers */
|
|
114
115
|
allowedCallers?: string[];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Integration method callbacks.
|
|
119
|
+
* Implement these when this agent acts as an integration.
|
|
120
|
+
*/
|
|
121
|
+
integrationMethods?: IntegrationMethods;
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
/**
|
|
@@ -149,5 +156,6 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
|
|
|
149
156
|
runtime: options.runtime,
|
|
150
157
|
visibility: options.visibility,
|
|
151
158
|
allowedCallers: options.allowedCallers,
|
|
159
|
+
integrationMethods: options.integrationMethods,
|
|
152
160
|
};
|
|
153
161
|
}
|
package/src/index.ts
CHANGED
|
@@ -81,6 +81,10 @@ export type {
|
|
|
81
81
|
ToolDefinition,
|
|
82
82
|
ToolSchema,
|
|
83
83
|
ToolSelectionContext,
|
|
84
|
+
IntegrationConfig,
|
|
85
|
+
IntegrationMethods,
|
|
86
|
+
IntegrationMethodResult,
|
|
87
|
+
IntegrationMethodContext,
|
|
84
88
|
Visibility,
|
|
85
89
|
} from "./types.js";
|
|
86
90
|
|
|
@@ -97,7 +101,10 @@ export { createAgentServer } from "./server.js";
|
|
|
97
101
|
export type { AgentServer, AgentServerOptions } from "./server.js";
|
|
98
102
|
|
|
99
103
|
// Auth
|
|
100
|
-
export {
|
|
104
|
+
export {
|
|
105
|
+
createAuthAgent,
|
|
106
|
+
createMemoryAuthStore,
|
|
107
|
+
} from "./agent-definitions/auth.js";
|
|
101
108
|
export type {
|
|
102
109
|
AuthClient,
|
|
103
110
|
AuthIdentity,
|
|
@@ -111,8 +118,17 @@ export { buildAgents } from "./build.js";
|
|
|
111
118
|
export type { BuildAgentsOptions, BuildAgentsResult } from "./build.js";
|
|
112
119
|
|
|
113
120
|
// Secrets
|
|
114
|
-
export {
|
|
115
|
-
|
|
121
|
+
export {
|
|
122
|
+
createSecretsAgent,
|
|
123
|
+
createInMemorySecretStore,
|
|
124
|
+
isSecretRef,
|
|
125
|
+
processSecretParams,
|
|
126
|
+
} from "./agent-definitions/secrets.js";
|
|
127
|
+
export type {
|
|
128
|
+
SecretScope,
|
|
129
|
+
SecretStore,
|
|
130
|
+
SecretsAgentOptions,
|
|
131
|
+
} from "./agent-definitions/secrets.js";
|
|
116
132
|
|
|
117
133
|
// Crypto
|
|
118
134
|
export { encryptSecret, decryptSecret } from "./crypto.js";
|
|
@@ -122,3 +138,41 @@ export { signJwt, verifyJwt } from "./jwt.js";
|
|
|
122
138
|
export type { JwtPayload } from "./jwt.js";
|
|
123
139
|
|
|
124
140
|
// Postgres Secret Store
|
|
141
|
+
|
|
142
|
+
// Integrations
|
|
143
|
+
export {
|
|
144
|
+
createIntegrationsAgent,
|
|
145
|
+
createInMemoryIntegrationStore,
|
|
146
|
+
exchangeCodeForToken,
|
|
147
|
+
refreshAccessToken,
|
|
148
|
+
getDefaultTokenBodyParams,
|
|
149
|
+
getDefaultRefreshBodyParams,
|
|
150
|
+
} from "./agent-definitions/integrations.js";
|
|
151
|
+
export type {
|
|
152
|
+
IntegrationStore,
|
|
153
|
+
IntegrationsAgentOptions,
|
|
154
|
+
ProviderConfig,
|
|
155
|
+
IntegrationOAuthConfig,
|
|
156
|
+
IntegrationApiConfig,
|
|
157
|
+
IntegrationApiAuthConfig,
|
|
158
|
+
IntegrationCallInput,
|
|
159
|
+
RestCallInput,
|
|
160
|
+
GraphqlCallInput,
|
|
161
|
+
AgentRegistryCallInput,
|
|
162
|
+
UserConnection,
|
|
163
|
+
ClientAuthMethod,
|
|
164
|
+
TokenContentType,
|
|
165
|
+
TokenExchangeResult,
|
|
166
|
+
} from "./agent-definitions/integrations.js";
|
|
167
|
+
|
|
168
|
+
// Users
|
|
169
|
+
export {
|
|
170
|
+
createUsersAgent,
|
|
171
|
+
createInMemoryUserStore,
|
|
172
|
+
} from "./agent-definitions/users.js";
|
|
173
|
+
export type {
|
|
174
|
+
User,
|
|
175
|
+
UserIdentity,
|
|
176
|
+
UserStore,
|
|
177
|
+
UsersAgentOptions,
|
|
178
|
+
} from "./agent-definitions/users.js";
|
package/src/jwt.ts
CHANGED
|
@@ -18,10 +18,7 @@ function base64UrlDecode(str: string): Uint8Array {
|
|
|
18
18
|
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
async function hmacSign(
|
|
22
|
-
data: string,
|
|
23
|
-
secret: string,
|
|
24
|
-
): Promise<Uint8Array> {
|
|
21
|
+
async function hmacSign(data: string, secret: string): Promise<Uint8Array> {
|
|
25
22
|
const key = await crypto.subtle.importKey(
|
|
26
23
|
"raw",
|
|
27
24
|
encoder.encode(secret),
|
|
@@ -45,7 +42,12 @@ async function hmacVerify(
|
|
|
45
42
|
false,
|
|
46
43
|
["verify"],
|
|
47
44
|
);
|
|
48
|
-
return crypto.subtle.verify(
|
|
45
|
+
return crypto.subtle.verify(
|
|
46
|
+
"HMAC",
|
|
47
|
+
key,
|
|
48
|
+
signature.buffer as ArrayBuffer,
|
|
49
|
+
encoder.encode(data),
|
|
50
|
+
);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/** JWT payload for auth tokens */
|