@slashfi/agents-sdk 0.16.0 → 0.18.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 +44 -11
- package/dist/agent-definitions/auth.js.map +1 -1
- package/dist/agent-definitions/integrations.d.ts.map +1 -1
- package/dist/agent-definitions/integrations.js +106 -45
- package/dist/agent-definitions/integrations.js.map +1 -1
- package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
- package/dist/agent-definitions/remote-registry.js +174 -45
- package/dist/agent-definitions/remote-registry.js.map +1 -1
- package/dist/agent-definitions/secrets.d.ts.map +1 -1
- package/dist/agent-definitions/secrets.js +1 -4
- package/dist/agent-definitions/secrets.js.map +1 -1
- package/dist/agent-definitions/users.d.ts.map +1 -1
- package/dist/agent-definitions/users.js +14 -3
- package/dist/agent-definitions/users.js.map +1 -1
- package/dist/define-config.d.ts +125 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/define-config.js +75 -0
- package/dist/define-config.js.map +1 -0
- package/dist/define.d.ts +11 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +57 -26
- package/dist/define.js.map +1 -1
- package/dist/events.d.ts +133 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +57 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/integration-interface.d.ts +3 -3
- package/dist/integration-interface.d.ts.map +1 -1
- package/dist/integration-interface.js +29 -21
- package/dist/integration-interface.js.map +1 -1
- package/dist/integrations-store.d.ts +2 -2
- package/dist/integrations-store.d.ts.map +1 -1
- package/dist/integrations-store.js +3 -3
- package/dist/integrations-store.js.map +1 -1
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +7 -5
- package/dist/jwt.js.map +1 -1
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/key-manager.js +5 -3
- package/dist/key-manager.js.map +1 -1
- package/dist/oidc-signin.d.ts +32 -0
- package/dist/oidc-signin.d.ts.map +1 -0
- package/dist/oidc-signin.js +138 -0
- package/dist/oidc-signin.js.map +1 -0
- package/dist/registry-consumer.d.ts +104 -0
- package/dist/registry-consumer.d.ts.map +1 -0
- package/dist/registry-consumer.js +230 -0
- package/dist/registry-consumer.js.map +1 -0
- package/dist/registry.d.ts +5 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +76 -4
- package/dist/registry.js.map +1 -1
- package/dist/secret-collection.d.ts.map +1 -1
- package/dist/secret-collection.js.map +1 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +222 -27
- package/dist/server.js.map +1 -1
- package/dist/test-utils/mock-oidc-server.d.ts +36 -0
- package/dist/test-utils/mock-oidc-server.d.ts.map +1 -0
- package/dist/test-utils/mock-oidc-server.js +96 -0
- package/dist/test-utils/mock-oidc-server.js.map +1 -0
- package/dist/types.d.ts +106 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/agent-definitions/auth.ts +106 -38
- package/src/agent-definitions/integrations.ts +201 -73
- package/src/agent-definitions/remote-registry.ts +262 -65
- package/src/agent-definitions/secrets.ts +22 -8
- package/src/agent-definitions/users.ts +16 -4
- package/src/cli.ts +293 -0
- package/src/codegen.test.ts +527 -0
- package/src/codegen.ts +1348 -0
- package/src/consumer.test.ts +536 -0
- package/src/define-config.ts +205 -0
- package/src/define.ts +134 -46
- package/src/events.ts +237 -0
- package/src/index.ts +107 -8
- package/src/integration-interface.ts +52 -28
- package/src/integrations-store.ts +9 -5
- package/src/jwt.ts +48 -19
- package/src/key-manager.test.ts +22 -13
- package/src/key-manager.ts +8 -10
- package/src/oidc-signin.ts +223 -0
- package/src/registry-consumer.ts +413 -0
- package/src/registry.ts +115 -9
- package/src/secret-collection.ts +2 -1
- package/src/server.test.ts +304 -238
- package/src/server.ts +371 -69
- package/src/test-utils/mock-oidc-server.ts +123 -0
- package/src/types.ts +172 -18
|
@@ -60,8 +60,16 @@ export function createRemoteRegistryAgent(
|
|
|
60
60
|
|
|
61
61
|
// --- Connection storage (KV via SecretStore) ---
|
|
62
62
|
|
|
63
|
-
async function storeConnection(
|
|
64
|
-
|
|
63
|
+
async function storeConnection(
|
|
64
|
+
ownerId: string,
|
|
65
|
+
conn: RegistryConnection,
|
|
66
|
+
): Promise<void> {
|
|
67
|
+
console.error(
|
|
68
|
+
"[remote-registry] storeConnection for owner:",
|
|
69
|
+
ownerId,
|
|
70
|
+
"conn:",
|
|
71
|
+
conn.id,
|
|
72
|
+
);
|
|
65
73
|
const all = await loadAllConnections(ownerId);
|
|
66
74
|
all[conn.id] = conn;
|
|
67
75
|
const value = JSON.stringify(all);
|
|
@@ -72,29 +80,49 @@ export function createRemoteRegistryAgent(
|
|
|
72
80
|
}
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
async function loadAllConnections(
|
|
83
|
+
async function loadAllConnections(
|
|
84
|
+
ownerId: string,
|
|
85
|
+
): Promise<Record<string, RegistryConnection>> {
|
|
76
86
|
console.error("[remote-registry] loadAllConnections for owner:", ownerId);
|
|
77
87
|
if (secretStore.resolveByEntity) {
|
|
78
88
|
const scope = { tenantId: ownerId };
|
|
79
|
-
const secretIds = await secretStore.resolveByEntity(
|
|
89
|
+
const secretIds = await secretStore.resolveByEntity(
|
|
90
|
+
ENTITY_TYPE,
|
|
91
|
+
ownerId,
|
|
92
|
+
scope,
|
|
93
|
+
);
|
|
80
94
|
if (secretIds?.length) {
|
|
81
|
-
const raw = await secretStore.resolve(
|
|
95
|
+
const raw = await secretStore.resolve(
|
|
96
|
+
secretIds[secretIds.length - 1],
|
|
97
|
+
ownerId,
|
|
98
|
+
);
|
|
82
99
|
if (raw) {
|
|
83
|
-
try {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(raw);
|
|
102
|
+
} catch {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
84
105
|
}
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
108
|
return {};
|
|
88
109
|
}
|
|
89
110
|
|
|
90
|
-
async function loadConnection(
|
|
111
|
+
async function loadConnection(
|
|
112
|
+
ownerId: string,
|
|
113
|
+
registryId: string,
|
|
114
|
+
): Promise<RegistryConnection | null> {
|
|
91
115
|
const all = await loadAllConnections(ownerId);
|
|
92
116
|
return all[registryId] ?? null;
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
// --- MCP call helper ---
|
|
96
120
|
|
|
97
|
-
async function mcpCall(
|
|
121
|
+
async function mcpCall(
|
|
122
|
+
url: string,
|
|
123
|
+
jwt: string,
|
|
124
|
+
request: Record<string, unknown>,
|
|
125
|
+
): Promise<any> {
|
|
98
126
|
const res = await globalThis.fetch(url, {
|
|
99
127
|
method: "POST",
|
|
100
128
|
headers: {
|
|
@@ -108,13 +136,18 @@ export function createRemoteRegistryAgent(
|
|
|
108
136
|
params: { name: "call_agent", arguments: { request } },
|
|
109
137
|
}),
|
|
110
138
|
});
|
|
111
|
-
const rpc = await res.json() as any;
|
|
139
|
+
const rpc = (await res.json()) as any;
|
|
112
140
|
const text = rpc.result?.content?.[0]?.text;
|
|
113
141
|
if (!text) return rpc.result;
|
|
114
142
|
const parsed = JSON.parse(text);
|
|
115
143
|
// Unwrap the remote call_agent envelope: { success, result } → just result
|
|
116
144
|
// The caller (proxyCall) will re-wrap with its own { success, result }
|
|
117
|
-
if (
|
|
145
|
+
if (
|
|
146
|
+
parsed &&
|
|
147
|
+
typeof parsed === "object" &&
|
|
148
|
+
"success" in parsed &&
|
|
149
|
+
"result" in parsed
|
|
150
|
+
) {
|
|
118
151
|
if (!parsed.success) {
|
|
119
152
|
throw new Error(parsed.error ?? "Remote call failed");
|
|
120
153
|
}
|
|
@@ -128,13 +161,24 @@ export function createRemoteRegistryAgent(
|
|
|
128
161
|
async function proxyCall(
|
|
129
162
|
ownerId: string,
|
|
130
163
|
registryId: string,
|
|
131
|
-
request: {
|
|
164
|
+
request: {
|
|
165
|
+
action: string;
|
|
166
|
+
path: string;
|
|
167
|
+
tool: string;
|
|
168
|
+
params?: Record<string, unknown>;
|
|
169
|
+
},
|
|
132
170
|
): Promise<any> {
|
|
133
171
|
const conn = await loadConnection(ownerId, registryId);
|
|
134
172
|
if (!conn) {
|
|
135
|
-
throw new Error(
|
|
173
|
+
throw new Error(
|
|
174
|
+
`No connection '${registryId}'. Use setup_integration first.`,
|
|
175
|
+
);
|
|
136
176
|
}
|
|
137
|
-
const jwt = await signJwt({
|
|
177
|
+
const jwt = await signJwt({
|
|
178
|
+
tenantId: conn.remoteTenantId,
|
|
179
|
+
action: "proxy",
|
|
180
|
+
type: "agent-registry",
|
|
181
|
+
});
|
|
138
182
|
// mcpCall already unwraps the remote call_agent envelope,
|
|
139
183
|
// so this returns the remote tool's result directly.
|
|
140
184
|
// registry.call() will wrap it in { success: true, result } for us.
|
|
@@ -170,7 +214,8 @@ export function createRemoteRegistryAgent(
|
|
|
170
214
|
|
|
171
215
|
const addConnectionTool = defineTool({
|
|
172
216
|
name: "add_connection",
|
|
173
|
-
description:
|
|
217
|
+
description:
|
|
218
|
+
"Directly store a connection to a remote registry (no OAuth exchange). Used for reverse registration.",
|
|
174
219
|
visibility: "public" as const,
|
|
175
220
|
inputSchema: {
|
|
176
221
|
type: "object" as const,
|
|
@@ -178,7 +223,10 @@ export function createRemoteRegistryAgent(
|
|
|
178
223
|
id: { type: "string", description: "Connection ID" },
|
|
179
224
|
name: { type: "string", description: "Display name" },
|
|
180
225
|
url: { type: "string", description: "Remote registry URL" },
|
|
181
|
-
remoteTenantId: {
|
|
226
|
+
remoteTenantId: {
|
|
227
|
+
type: "string",
|
|
228
|
+
description: "Tenant ID on the remote side",
|
|
229
|
+
},
|
|
182
230
|
},
|
|
183
231
|
required: ["id", "url"],
|
|
184
232
|
},
|
|
@@ -203,7 +251,7 @@ export function createRemoteRegistryAgent(
|
|
|
203
251
|
execute: async (_input: any, _ctx: ToolContext) => {
|
|
204
252
|
const all = await loadAllConnections("system");
|
|
205
253
|
return {
|
|
206
|
-
connections: Object.values(all).map(c => ({
|
|
254
|
+
connections: Object.values(all).map((c) => ({
|
|
207
255
|
id: c.id,
|
|
208
256
|
name: c.name,
|
|
209
257
|
url: c.url,
|
|
@@ -213,10 +261,15 @@ export function createRemoteRegistryAgent(
|
|
|
213
261
|
},
|
|
214
262
|
});
|
|
215
263
|
|
|
216
|
-
|
|
217
264
|
// Extract setup/connect as standalone functions to avoid circular reference
|
|
218
|
-
const setupFn = async (
|
|
219
|
-
|
|
265
|
+
const setupFn = async (
|
|
266
|
+
params: Record<string, unknown>,
|
|
267
|
+
_ctx: IntegrationMethodContext,
|
|
268
|
+
): Promise<IntegrationMethodResult> => {
|
|
269
|
+
console.log(
|
|
270
|
+
"[remote-registry] setupFn called with:",
|
|
271
|
+
JSON.stringify(params),
|
|
272
|
+
);
|
|
220
273
|
const url = params.url as string;
|
|
221
274
|
const name = (params.name as string) ?? "registry";
|
|
222
275
|
const oidcUserId = params.oidcUserId as string | undefined;
|
|
@@ -226,68 +279,143 @@ export function createRemoteRegistryAgent(
|
|
|
226
279
|
|
|
227
280
|
// Phase 2: Complete setup after OIDC — store connection with resolved tenant
|
|
228
281
|
if (oidcUserId) {
|
|
229
|
-
const jwt = await signJwt({
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
282
|
+
const jwt = await signJwt({
|
|
283
|
+
sub: oidcUserId,
|
|
284
|
+
action: "setup",
|
|
285
|
+
type: "agent-registry",
|
|
286
|
+
});
|
|
287
|
+
const tokenRes = await globalThis.fetch(`${baseUrl}/oauth/token`, {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "Content-Type": "application/json" },
|
|
290
|
+
body: JSON.stringify({
|
|
291
|
+
grant_type: "jwt_exchange",
|
|
292
|
+
assertion: jwt,
|
|
293
|
+
scope: "setup",
|
|
294
|
+
redirect_uri: params.redirect_uri ?? "",
|
|
295
|
+
}),
|
|
233
296
|
});
|
|
234
|
-
const tokenData = await tokenRes.json() as any;
|
|
297
|
+
const tokenData = (await tokenRes.json()) as any;
|
|
235
298
|
if (!tokenData.access_token && !tokenData.tenant_id) {
|
|
236
|
-
return {
|
|
299
|
+
return {
|
|
300
|
+
success: false,
|
|
301
|
+
error:
|
|
302
|
+
tokenData.error_description ??
|
|
303
|
+
tokenData.error ??
|
|
304
|
+
"Setup completion failed",
|
|
305
|
+
};
|
|
237
306
|
}
|
|
238
307
|
const remoteTenantId = tokenData.tenant_id ?? name;
|
|
239
308
|
const ownerId = "system";
|
|
240
|
-
await storeConnection(ownerId, {
|
|
241
|
-
|
|
309
|
+
await storeConnection(ownerId, {
|
|
310
|
+
id: name,
|
|
311
|
+
name,
|
|
312
|
+
url: baseUrl,
|
|
313
|
+
remoteTenantId,
|
|
314
|
+
createdAt: Date.now(),
|
|
315
|
+
});
|
|
316
|
+
return {
|
|
317
|
+
success: true,
|
|
318
|
+
data: { registryId: name, url, remoteTenantId },
|
|
319
|
+
};
|
|
242
320
|
}
|
|
243
321
|
|
|
244
322
|
// Phase 1: Discover JWKS, establish trust, then request OIDC
|
|
245
|
-
const configUrl = baseUrl
|
|
323
|
+
const configUrl = `${baseUrl}/.well-known/configuration`;
|
|
246
324
|
console.log("[setupFn] fetching config:", configUrl);
|
|
247
325
|
const configRes = await globalThis.fetch(configUrl);
|
|
248
326
|
console.log("[setupFn] config status:", configRes.status);
|
|
249
|
-
if (!configRes.ok)
|
|
250
|
-
|
|
327
|
+
if (!configRes.ok)
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
error: `Failed to discover registry at ${configUrl}`,
|
|
331
|
+
};
|
|
332
|
+
const remoteConfig = (await configRes.json()) as any;
|
|
251
333
|
if (remoteConfig.jwks_uri) {
|
|
252
334
|
console.log("[setupFn] fetching JWKS:", remoteConfig.jwks_uri);
|
|
253
335
|
const jwksRes = await globalThis.fetch(remoteConfig.jwks_uri);
|
|
254
336
|
console.log("[setupFn] JWKS status:", jwksRes.status);
|
|
255
337
|
if (!jwksRes.ok) return { success: false, error: "JWKS not reachable" };
|
|
256
338
|
}
|
|
257
|
-
if (addTrustedIssuer) {
|
|
339
|
+
if (addTrustedIssuer) {
|
|
340
|
+
console.log("[setupFn] adding trusted issuer:", baseUrl);
|
|
341
|
+
await addTrustedIssuer(baseUrl);
|
|
342
|
+
console.log("[setupFn] added trusted issuer");
|
|
343
|
+
}
|
|
258
344
|
|
|
259
345
|
// Request identity — atlas will return authorize URL for Slack OIDC
|
|
260
346
|
console.log("[setupFn] Phase 1: requesting identity via jwt_exchange");
|
|
261
|
-
const jwt = await signJwt({
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
347
|
+
const jwt = await signJwt({
|
|
348
|
+
action: "setup",
|
|
349
|
+
type: "agent-registry",
|
|
350
|
+
targetUrl: url,
|
|
351
|
+
});
|
|
352
|
+
console.log("[setupFn] POSTing to:", `${baseUrl}/oauth/token`);
|
|
353
|
+
const tokenRes = await globalThis.fetch(`${baseUrl}/oauth/token`, {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: { "Content-Type": "application/json" },
|
|
356
|
+
body: JSON.stringify({
|
|
357
|
+
grant_type: "jwt_exchange",
|
|
358
|
+
assertion: jwt,
|
|
359
|
+
scope: "setup",
|
|
360
|
+
redirect_uri: params.redirect_uri ?? "",
|
|
361
|
+
}),
|
|
266
362
|
});
|
|
267
363
|
console.log("[setupFn] token status:", tokenRes.status);
|
|
268
|
-
const tokenData = await tokenRes.json() as any;
|
|
269
|
-
console.log(
|
|
364
|
+
const tokenData = (await tokenRes.json()) as any;
|
|
365
|
+
console.log(
|
|
366
|
+
"[setupFn] tokenData:",
|
|
367
|
+
JSON.stringify(tokenData).substring(0, 300),
|
|
368
|
+
);
|
|
270
369
|
|
|
271
370
|
// If already set up (user linked), store connection directly
|
|
272
371
|
if (tokenData.access_token) {
|
|
273
372
|
const remoteTenantId = tokenData.tenant_id ?? name;
|
|
274
373
|
const ownerId = "system";
|
|
275
|
-
await storeConnection(ownerId, {
|
|
276
|
-
|
|
374
|
+
await storeConnection(ownerId, {
|
|
375
|
+
id: name,
|
|
376
|
+
name,
|
|
377
|
+
url: baseUrl,
|
|
378
|
+
remoteTenantId,
|
|
379
|
+
createdAt: Date.now(),
|
|
380
|
+
});
|
|
381
|
+
return {
|
|
382
|
+
success: true,
|
|
383
|
+
data: { registryId: name, url, remoteTenantId },
|
|
384
|
+
};
|
|
277
385
|
}
|
|
278
386
|
|
|
279
387
|
// Need OIDC — return authorize URL to caller
|
|
280
388
|
if (tokenData.error === "identity_required") {
|
|
281
|
-
return {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
error: "identity_required",
|
|
392
|
+
data: {
|
|
393
|
+
authorizeUrl: tokenData.authorize_url,
|
|
394
|
+
registryId: name,
|
|
395
|
+
url,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
282
398
|
}
|
|
283
399
|
|
|
284
|
-
return {
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
error:
|
|
403
|
+
tokenData.error_description ??
|
|
404
|
+
tokenData.error ??
|
|
405
|
+
"Unexpected response from token endpoint",
|
|
406
|
+
};
|
|
285
407
|
} catch (err) {
|
|
286
|
-
return {
|
|
408
|
+
return {
|
|
409
|
+
success: false,
|
|
410
|
+
error: err instanceof Error ? err.message : String(err),
|
|
411
|
+
};
|
|
287
412
|
}
|
|
288
413
|
};
|
|
289
414
|
|
|
290
|
-
const connectFn = async (
|
|
415
|
+
const connectFn = async (
|
|
416
|
+
params: Record<string, unknown>,
|
|
417
|
+
ctx: IntegrationMethodContext,
|
|
418
|
+
): Promise<IntegrationMethodResult> => {
|
|
291
419
|
const registryId = params.registryId as string;
|
|
292
420
|
const redirectUri = (params.redirectUri as string) ?? "";
|
|
293
421
|
const oidcUserId = params.oidcUserId as string | undefined;
|
|
@@ -295,20 +423,58 @@ export function createRemoteRegistryAgent(
|
|
|
295
423
|
try {
|
|
296
424
|
const ownerId = "system"; // tenant-scoped
|
|
297
425
|
const conn = await loadConnection(ownerId, registryId);
|
|
298
|
-
if (!conn)
|
|
426
|
+
if (!conn)
|
|
427
|
+
return { success: false, error: `No connection '${registryId}'` };
|
|
299
428
|
// Use OIDC-issued identity if available, never send "anonymous" as sub
|
|
300
|
-
const sub =
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
429
|
+
const sub =
|
|
430
|
+
oidcUserId ?? (ctx.callerId !== "anonymous" ? ctx.callerId : undefined);
|
|
431
|
+
const jwt = await signJwt({
|
|
432
|
+
...(sub ? { sub } : {}),
|
|
433
|
+
tenantId: conn.remoteTenantId,
|
|
434
|
+
action: "connect",
|
|
435
|
+
type: "agent-registry",
|
|
436
|
+
});
|
|
437
|
+
const tokenRes = await globalThis.fetch(`${conn.url}/oauth/token`, {
|
|
438
|
+
method: "POST",
|
|
439
|
+
headers: { "Content-Type": "application/json" },
|
|
440
|
+
body: JSON.stringify({
|
|
441
|
+
grant_type: "jwt_exchange",
|
|
442
|
+
assertion: jwt,
|
|
443
|
+
redirect_uri: redirectUri,
|
|
444
|
+
}),
|
|
305
445
|
});
|
|
306
|
-
const tokenData = await tokenRes.json() as any;
|
|
307
|
-
if (tokenData.access_token)
|
|
308
|
-
|
|
309
|
-
|
|
446
|
+
const tokenData = (await tokenRes.json()) as any;
|
|
447
|
+
if (tokenData.access_token)
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
data: {
|
|
451
|
+
registryId,
|
|
452
|
+
accessToken: tokenData.access_token,
|
|
453
|
+
userId: tokenData.user_id,
|
|
454
|
+
tenantId: tokenData.tenant_id,
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
if (tokenData.error === "identity_required")
|
|
458
|
+
return {
|
|
459
|
+
success: false,
|
|
460
|
+
error: "identity_required",
|
|
461
|
+
data: {
|
|
462
|
+
authorizeUrl: tokenData.authorize_url,
|
|
463
|
+
tenantId: tokenData.tenant_id,
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
return {
|
|
467
|
+
success: false,
|
|
468
|
+
error:
|
|
469
|
+
tokenData.error_description ??
|
|
470
|
+
tokenData.error ??
|
|
471
|
+
"Token exchange failed",
|
|
472
|
+
};
|
|
310
473
|
} catch (err) {
|
|
311
|
-
return {
|
|
474
|
+
return {
|
|
475
|
+
success: false,
|
|
476
|
+
error: err instanceof Error ? err.message : String(err),
|
|
477
|
+
};
|
|
312
478
|
}
|
|
313
479
|
};
|
|
314
480
|
|
|
@@ -320,7 +486,8 @@ export function createRemoteRegistryAgent(
|
|
|
320
486
|
"and proxy_call to make authenticated calls.",
|
|
321
487
|
config: {
|
|
322
488
|
name: "Remote Registry",
|
|
323
|
-
description:
|
|
489
|
+
description:
|
|
490
|
+
"Connect to remote agent registries via JWKS trust + jwt_exchange",
|
|
324
491
|
supportedActions: ["execute_tool", "describe_tools", "load"],
|
|
325
492
|
},
|
|
326
493
|
visibility: "public",
|
|
@@ -329,32 +496,62 @@ export function createRemoteRegistryAgent(
|
|
|
329
496
|
displayName: "Agent Registry",
|
|
330
497
|
icon: "server",
|
|
331
498
|
category: "infrastructure",
|
|
332
|
-
description:
|
|
499
|
+
description:
|
|
500
|
+
"Connect to a remote agent registry via JWKS trust exchange.",
|
|
333
501
|
setup: (params, ctx) => setupFn(params, ctx as any),
|
|
334
502
|
connect: (params, ctx) => connectFn(params, ctx as any),
|
|
335
503
|
async discover(params) {
|
|
336
504
|
const url = (params.url as string) ?? "";
|
|
337
505
|
try {
|
|
338
|
-
const res = await globalThis.fetch(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
506
|
+
const res = await globalThis.fetch(
|
|
507
|
+
`${url.replace(/\/$/, "")}/.well-known/configuration`,
|
|
508
|
+
);
|
|
509
|
+
if (!res.ok)
|
|
510
|
+
return {
|
|
511
|
+
success: false,
|
|
512
|
+
error: `No configuration endpoint at ${url}`,
|
|
513
|
+
};
|
|
514
|
+
const config = (await res.json()) as any;
|
|
515
|
+
return {
|
|
516
|
+
success: true,
|
|
517
|
+
data: {
|
|
518
|
+
url,
|
|
519
|
+
issuer: config.issuer,
|
|
520
|
+
grantTypes: config.supported_grant_types,
|
|
521
|
+
jwksUri: config.jwks_uri,
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
} catch (err) {
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
error: err instanceof Error ? err.message : String(err),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
343
530
|
},
|
|
344
531
|
async list() {
|
|
345
532
|
const all = await loadAllConnections("system");
|
|
346
|
-
return {
|
|
533
|
+
return {
|
|
534
|
+
success: true,
|
|
535
|
+
data: {
|
|
536
|
+
connections: Object.values(all).map((c) => ({
|
|
537
|
+
id: c.id,
|
|
538
|
+
name: c.name,
|
|
539
|
+
url: c.url,
|
|
540
|
+
remoteTenantId: c.remoteTenantId,
|
|
541
|
+
})),
|
|
542
|
+
},
|
|
543
|
+
};
|
|
347
544
|
},
|
|
348
545
|
async get(params) {
|
|
349
546
|
const id = (params.registryId as string) ?? "";
|
|
350
547
|
const conn = await loadConnection("system", id);
|
|
351
|
-
if (!conn) return { success: false, error:
|
|
548
|
+
if (!conn) return { success: false, error: `No connection '${id}'` };
|
|
352
549
|
return { success: true, data: conn };
|
|
353
550
|
},
|
|
354
551
|
async update(params) {
|
|
355
552
|
const id = (params.registryId as string) ?? "";
|
|
356
553
|
const conn = await loadConnection("system", id);
|
|
357
|
-
if (!conn) return { success: false, error:
|
|
554
|
+
if (!conn) return { success: false, error: `No connection '${id}'` };
|
|
358
555
|
if (params.name) conn.name = params.name as string;
|
|
359
556
|
if (params.url) conn.url = params.url as string;
|
|
360
557
|
await storeConnection("system", conn);
|
|
@@ -34,7 +34,11 @@ export interface SecretStore {
|
|
|
34
34
|
store(value: string, ownerId: string, scope?: SecretScope): Promise<string>;
|
|
35
35
|
|
|
36
36
|
/** Resolve a secret ID to its decrypted value. */
|
|
37
|
-
resolve(
|
|
37
|
+
resolve(
|
|
38
|
+
id: string,
|
|
39
|
+
ownerId: string,
|
|
40
|
+
scope?: SecretScope,
|
|
41
|
+
): Promise<string | null>;
|
|
38
42
|
|
|
39
43
|
/** Delete a secret. */
|
|
40
44
|
delete(id: string, ownerId: string, scope?: SecretScope): Promise<boolean>;
|
|
@@ -43,19 +47,32 @@ export interface SecretStore {
|
|
|
43
47
|
* Store multiple secrets in a single operation.
|
|
44
48
|
* Returns an array of secret IDs in the same order as the input values.
|
|
45
49
|
*/
|
|
46
|
-
storeBatch?(
|
|
50
|
+
storeBatch?(
|
|
51
|
+
values: string[],
|
|
52
|
+
ownerId: string,
|
|
53
|
+
scope?: SecretScope,
|
|
54
|
+
): Promise<string[]>;
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* Associate a secret with an entity (e.g., a provider config, a connection).
|
|
50
58
|
* Enables lookup of secrets by entity rather than by ID.
|
|
51
59
|
*/
|
|
52
|
-
associate?(
|
|
60
|
+
associate?(
|
|
61
|
+
secretId: string,
|
|
62
|
+
entityType: string,
|
|
63
|
+
entityId: string,
|
|
64
|
+
scope?: SecretScope,
|
|
65
|
+
): Promise<void>;
|
|
53
66
|
|
|
54
67
|
/**
|
|
55
68
|
* Resolve secrets associated with an entity.
|
|
56
69
|
* Returns all secret IDs linked to the given entity.
|
|
57
70
|
*/
|
|
58
|
-
resolveByEntity?(
|
|
71
|
+
resolveByEntity?(
|
|
72
|
+
entityType: string,
|
|
73
|
+
entityId: string,
|
|
74
|
+
scope?: SecretScope,
|
|
75
|
+
): Promise<string[]>;
|
|
59
76
|
}
|
|
60
77
|
|
|
61
78
|
// ============================================
|
|
@@ -218,10 +235,7 @@ export function createSecretsAgent(
|
|
|
218
235
|
description: "Encrypted secret storage and management",
|
|
219
236
|
visibility: "internal",
|
|
220
237
|
},
|
|
221
|
-
tools: [
|
|
222
|
-
storeSecretTool,
|
|
223
|
-
revokeSecretTool,
|
|
224
|
-
] as ToolDefinition<ToolContext>[],
|
|
238
|
+
tools: [storeSecretTool, revokeSecretTool] as ToolDefinition<ToolContext>[],
|
|
225
239
|
});
|
|
226
240
|
}
|
|
227
241
|
|
|
@@ -252,10 +252,17 @@ export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
|
252
252
|
metadata: { type: "object", description: "Additional metadata" },
|
|
253
253
|
externalRef: {
|
|
254
254
|
type: "object",
|
|
255
|
-
description:
|
|
255
|
+
description:
|
|
256
|
+
"Link to a user on a remote system. Creates identity automatically.",
|
|
256
257
|
properties: {
|
|
257
|
-
issuer: {
|
|
258
|
-
|
|
258
|
+
issuer: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "Issuer URL of the remote system",
|
|
261
|
+
},
|
|
262
|
+
userId: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "User ID on the remote system",
|
|
265
|
+
},
|
|
259
266
|
},
|
|
260
267
|
},
|
|
261
268
|
},
|
|
@@ -270,7 +277,12 @@ export function createUsersAgent(options: UsersAgentOptions): AgentDefinition {
|
|
|
270
277
|
);
|
|
271
278
|
if (existing) {
|
|
272
279
|
const user = await store.getUser(existing.userId);
|
|
273
|
-
return {
|
|
280
|
+
return {
|
|
281
|
+
success: true,
|
|
282
|
+
user,
|
|
283
|
+
identity: existing,
|
|
284
|
+
alreadyLinked: true,
|
|
285
|
+
};
|
|
274
286
|
}
|
|
275
287
|
}
|
|
276
288
|
|