@dainprotocol/service-sdk 2.0.94 → 2.0.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/api-sdk.d.ts +7 -1
- package/dist/client/api-sdk.js +2 -2
- package/dist/client/api-sdk.js.map +1 -1
- package/dist/client/client-auth.d.ts +38 -0
- package/dist/client/client-auth.js +131 -2
- package/dist/client/client-auth.js.map +1 -1
- package/dist/client/client.d.ts +1 -0
- package/dist/client/client.js +92 -25
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/crypto-plugin.d.ts +26 -0
- package/dist/plugins/crypto-plugin.js +44 -4
- package/dist/plugins/crypto-plugin.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/protocol/account.d.ts +91 -0
- package/dist/protocol/account.js +3 -0
- package/dist/protocol/account.js.map +1 -0
- package/dist/protocol/grants.d.ts +22 -0
- package/dist/protocol/grants.js +3 -0
- package/dist/protocol/grants.js.map +1 -0
- package/dist/protocol/index.d.ts +8 -0
- package/dist/protocol/index.js +12 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/payments.d.ts +53 -0
- package/dist/protocol/payments.js +3 -0
- package/dist/protocol/payments.js.map +1 -0
- package/dist/protocol/permissions.d.ts +21 -0
- package/dist/protocol/permissions.js +3 -0
- package/dist/protocol/permissions.js.map +1 -0
- package/dist/protocol/programs.d.ts +44 -0
- package/dist/protocol/programs.js +46 -0
- package/dist/protocol/programs.js.map +1 -0
- package/dist/protocol/registry.d.ts +41 -0
- package/dist/protocol/registry.js +3 -0
- package/dist/protocol/registry.js.map +1 -0
- package/dist/protocol/runtime.d.ts +15 -0
- package/dist/protocol/runtime.js +3 -0
- package/dist/protocol/runtime.js.map +1 -0
- package/dist/protocol/transactions.d.ts +63 -0
- package/dist/protocol/transactions.js +3 -0
- package/dist/protocol/transactions.js.map +1 -0
- package/dist/service/core.js +9 -0
- package/dist/service/core.js.map +1 -1
- package/dist/service/index.d.ts +1 -0
- package/dist/service/index.js +1 -0
- package/dist/service/index.js.map +1 -1
- package/dist/service/nodeService.js +30 -2
- package/dist/service/nodeService.js.map +1 -1
- package/dist/service/server.js +470 -102
- package/dist/service/server.js.map +1 -1
- package/dist/service/types.d.ts +38 -25
- package/package.json +20 -7
package/dist/service/server.js
CHANGED
|
@@ -98,6 +98,123 @@ async function safeParseBody(c) {
|
|
|
98
98
|
return {};
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
function isRecord(value) {
|
|
102
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
103
|
+
}
|
|
104
|
+
const DAIN_RUNTIME_CONTEXT_KEYS = new Set([
|
|
105
|
+
"dainAccountId",
|
|
106
|
+
"dainGroupId",
|
|
107
|
+
"smartAccountPDA",
|
|
108
|
+
"account",
|
|
109
|
+
"group",
|
|
110
|
+
"auth",
|
|
111
|
+
"grants",
|
|
112
|
+
"dataPermissions",
|
|
113
|
+
"registryPolicy",
|
|
114
|
+
]);
|
|
115
|
+
function stripRuntimeContextFields(value) {
|
|
116
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !DAIN_RUNTIME_CONTEXT_KEYS.has(key)));
|
|
117
|
+
}
|
|
118
|
+
function getStringClaim(payload, ...keys) {
|
|
119
|
+
if (!payload)
|
|
120
|
+
return undefined;
|
|
121
|
+
for (const key of keys) {
|
|
122
|
+
const value = payload[key];
|
|
123
|
+
if (typeof value === "string" && value.length > 0) {
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
function getObjectClaim(payload, key) {
|
|
130
|
+
if (!payload)
|
|
131
|
+
return undefined;
|
|
132
|
+
const value = payload[key];
|
|
133
|
+
return isRecord(value) ? value : undefined;
|
|
134
|
+
}
|
|
135
|
+
function getArrayClaim(payload, key) {
|
|
136
|
+
if (!payload)
|
|
137
|
+
return undefined;
|
|
138
|
+
const value = payload[key];
|
|
139
|
+
return Array.isArray(value) ? value : undefined;
|
|
140
|
+
}
|
|
141
|
+
function getRuntimeContextFromJwtPayload(payload, options = {}) {
|
|
142
|
+
const account = getObjectClaim(payload, "account_context");
|
|
143
|
+
const group = getObjectClaim(payload, "group_context");
|
|
144
|
+
const auth = getObjectClaim(payload, "auth_context");
|
|
145
|
+
const dainAccountId = getStringClaim(payload, "dain_account_id", "account_id") ??
|
|
146
|
+
account?.id;
|
|
147
|
+
const dainGroupId = getStringClaim(payload, "dain_group_id", "group_id") ??
|
|
148
|
+
group?.id;
|
|
149
|
+
const smartAccountPDA = getStringClaim(payload, "smart_account_pda") ??
|
|
150
|
+
options.fallbackSmartAccountPDA;
|
|
151
|
+
if (account?.id && dainAccountId && account.id !== dainAccountId) {
|
|
152
|
+
throw new http_exception_1.HTTPException(401, { message: "JWT DAIN context mismatch: dain_account_id does not match account_context.id" });
|
|
153
|
+
}
|
|
154
|
+
if (group?.id && dainGroupId && group.id !== dainGroupId) {
|
|
155
|
+
throw new http_exception_1.HTTPException(401, { message: "JWT DAIN context mismatch: dain_group_id does not match group_context.id" });
|
|
156
|
+
}
|
|
157
|
+
if (account?.smartAccount?.pda && smartAccountPDA && account.smartAccount.pda !== smartAccountPDA) {
|
|
158
|
+
throw new http_exception_1.HTTPException(401, { message: "JWT DAIN context mismatch: smart_account_pda does not match account_context.smartAccount.pda" });
|
|
159
|
+
}
|
|
160
|
+
const grants = getArrayClaim(payload, "action_grants");
|
|
161
|
+
const dataPermissions = getArrayClaim(payload, "data_permissions");
|
|
162
|
+
const registryPolicy = getObjectClaim(payload, "registry_policy");
|
|
163
|
+
const subject = getStringClaim(payload, "sub") ??
|
|
164
|
+
options.smartAccountId ??
|
|
165
|
+
dainAccountId ??
|
|
166
|
+
dainGroupId ??
|
|
167
|
+
smartAccountPDA;
|
|
168
|
+
return {
|
|
169
|
+
...(dainAccountId ? { dainAccountId } : {}),
|
|
170
|
+
...(dainGroupId ? { dainGroupId } : {}),
|
|
171
|
+
...(smartAccountPDA ? { smartAccountPDA } : {}),
|
|
172
|
+
...(account ? { account } : {}),
|
|
173
|
+
...(group ? { group } : {}),
|
|
174
|
+
auth: auth ?? {
|
|
175
|
+
subject: subject ?? "unknown",
|
|
176
|
+
issuer: getStringClaim(payload, "iss"),
|
|
177
|
+
scopes: options.scopes,
|
|
178
|
+
source: "dain_id",
|
|
179
|
+
},
|
|
180
|
+
...(grants ? { grants } : {}),
|
|
181
|
+
...(dataPermissions ? { dataPermissions } : {}),
|
|
182
|
+
...(registryPolicy ? { registryPolicy } : {}),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function getRuntimeContextFromAgentInfo(agentInfo) {
|
|
186
|
+
return {
|
|
187
|
+
...(agentInfo.dainAccountId ? { dainAccountId: agentInfo.dainAccountId } : {}),
|
|
188
|
+
...(agentInfo.dainGroupId ? { dainGroupId: agentInfo.dainGroupId } : {}),
|
|
189
|
+
...(agentInfo.smartAccountPDA ? { smartAccountPDA: agentInfo.smartAccountPDA } : {}),
|
|
190
|
+
...(agentInfo.account ? { account: agentInfo.account } : {}),
|
|
191
|
+
...(agentInfo.group ? { group: agentInfo.group } : {}),
|
|
192
|
+
...(agentInfo.auth ? { auth: agentInfo.auth } : {}),
|
|
193
|
+
...(agentInfo.grants ? { grants: agentInfo.grants } : {}),
|
|
194
|
+
...(agentInfo.dataPermissions ? { dataPermissions: agentInfo.dataPermissions } : {}),
|
|
195
|
+
...(agentInfo.registryPolicy ? { registryPolicy: agentInfo.registryPolicy } : {}),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function getRequestExtraData(request) {
|
|
199
|
+
if (!isRecord(request))
|
|
200
|
+
return {};
|
|
201
|
+
return isRecord(request.DAIN_EXTRA_DATA)
|
|
202
|
+
? stripRuntimeContextFields(request.DAIN_EXTRA_DATA)
|
|
203
|
+
: {};
|
|
204
|
+
}
|
|
205
|
+
function buildServiceExtraData(agentInfo, options = {}) {
|
|
206
|
+
const requestExtra = getRequestExtraData(options.request);
|
|
207
|
+
const runtimeContext = getRuntimeContextFromAgentInfo(agentInfo);
|
|
208
|
+
return {
|
|
209
|
+
...requestExtra,
|
|
210
|
+
...(options.extra ?? {}),
|
|
211
|
+
...runtimeContext,
|
|
212
|
+
...(options.plugins ? { plugins: options.plugins } : {}),
|
|
213
|
+
...(Object.prototype.hasOwnProperty.call(options, "oauth2Client")
|
|
214
|
+
? { oauth2Client: options.oauth2Client }
|
|
215
|
+
: {}),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
101
218
|
/**
|
|
102
219
|
* Middleware factory to require specific OAuth scopes.
|
|
103
220
|
* Defense-in-depth: Validates scopes even though JWT middleware already checked them.
|
|
@@ -120,6 +237,17 @@ function signedStreamSSE(c, privateKey, config, handler) {
|
|
|
120
237
|
return (0, streaming_1.streamSSE)(c, async (stream) => {
|
|
121
238
|
const requestSignal = c.req.raw?.signal;
|
|
122
239
|
const isAborted = () => stream.aborted || stream.closed || requestSignal?.aborted === true;
|
|
240
|
+
const abortStream = () => {
|
|
241
|
+
if (!stream.closed) {
|
|
242
|
+
stream.abort();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
if (requestSignal?.aborted) {
|
|
246
|
+
abortStream();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
requestSignal?.addEventListener("abort", abortStream, { once: true });
|
|
250
|
+
}
|
|
123
251
|
const signedStream = {
|
|
124
252
|
writeSSE: async (event) => {
|
|
125
253
|
if (isAborted())
|
|
@@ -140,12 +268,19 @@ function signedStreamSSE(c, privateKey, config, handler) {
|
|
|
140
268
|
await stream.writeSSE({ event: event.event, data: dataWithSignature, id: event.id });
|
|
141
269
|
},
|
|
142
270
|
isAborted,
|
|
271
|
+
onAbort: (listener) => stream.onAbort(listener),
|
|
143
272
|
};
|
|
144
|
-
|
|
273
|
+
try {
|
|
274
|
+
await handler(signedStream);
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
requestSignal?.removeEventListener("abort", abortStream);
|
|
278
|
+
}
|
|
145
279
|
});
|
|
146
280
|
}
|
|
147
281
|
function setupHttpServer(config, tools, services, toolboxes, metadata, privateKey, contexts, widgets, datasources = [], agents = []) {
|
|
148
282
|
const app = new hono_1.Hono();
|
|
283
|
+
const datasourceStreamAbortControllers = new Map();
|
|
149
284
|
const processHandler = new processes_1.ProcessHandler({
|
|
150
285
|
serviceId: "service_" + config.identity.orgId + "_" + config.identity.agentId,
|
|
151
286
|
privateKey,
|
|
@@ -160,6 +295,8 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
160
295
|
allowHeaders: [
|
|
161
296
|
"X-DAIN-SIGNATURE",
|
|
162
297
|
"X-DAIN-SMART-ACCOUNT-PDA",
|
|
298
|
+
"X-DAIN-ACCOUNT-ID",
|
|
299
|
+
"X-DAIN-GROUP-ID",
|
|
163
300
|
"X-DAIN-AGENT-ID",
|
|
164
301
|
"X-DAIN-ORG-ID",
|
|
165
302
|
"X-DAIN-ADDRESS",
|
|
@@ -177,7 +314,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
177
314
|
return c.text("Enabling CORS Pre-Flight", 200, {
|
|
178
315
|
"Access-Control-Allow-Origin": "*",
|
|
179
316
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
180
|
-
"Access-Control-Allow-Headers": "X-DAIN-SIGNATURE, X-DAIN-SMART-ACCOUNT-PDA, X-DAIN-AGENT-ID, X-DAIN-ORG-ID, X-DAIN-ADDRESS, X-DAIN-TIMESTAMP, Content-Type, Authorization, Accept, Origin, X-Requested-With",
|
|
317
|
+
"Access-Control-Allow-Headers": "X-DAIN-SIGNATURE, X-DAIN-SMART-ACCOUNT-PDA, X-DAIN-ACCOUNT-ID, X-DAIN-GROUP-ID, X-DAIN-AGENT-ID, X-DAIN-ORG-ID, X-DAIN-ADDRESS, X-DAIN-TIMESTAMP, Content-Type, Authorization, Accept, Origin, X-Requested-With",
|
|
181
318
|
"Access-Control-Max-Age": "86400",
|
|
182
319
|
});
|
|
183
320
|
});
|
|
@@ -277,7 +414,13 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
277
414
|
c.set('address', result.smartAccountId); // Use smartAccountId as address for JWT auth
|
|
278
415
|
c.set('scope', result.scope);
|
|
279
416
|
c.set('jwtPayload', result.payload);
|
|
280
|
-
|
|
417
|
+
const jwtRuntimeContext = getRuntimeContextFromJwtPayload(result.payload, {
|
|
418
|
+
scopes: result.scope,
|
|
419
|
+
smartAccountId: result.smartAccountId,
|
|
420
|
+
fallbackSmartAccountPDA: c.req.header("X-DAIN-SMART-ACCOUNT-PDA"),
|
|
421
|
+
});
|
|
422
|
+
c.set('dainRuntimeContext', jwtRuntimeContext);
|
|
423
|
+
c.set('smartAccountPDA', jwtRuntimeContext.smartAccountPDA);
|
|
281
424
|
debugLog(`[Auth Middleware] JWT auth SUCCESS - smartAccountId: ${result.smartAccountId}`);
|
|
282
425
|
}
|
|
283
426
|
else if (apiKey) {
|
|
@@ -300,6 +443,11 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
300
443
|
c.set('orgId', parsed.orgId);
|
|
301
444
|
c.set('address', parsed.agentId); // Use agentId as address for API key auth
|
|
302
445
|
c.set('smartAccountPDA', c.req.header("X-DAIN-SMART-ACCOUNT-PDA"));
|
|
446
|
+
c.set('dainRuntimeContext', {
|
|
447
|
+
...(c.req.header("X-DAIN-ACCOUNT-ID") ? { dainAccountId: c.req.header("X-DAIN-ACCOUNT-ID") } : {}),
|
|
448
|
+
...(c.req.header("X-DAIN-GROUP-ID") ? { dainGroupId: c.req.header("X-DAIN-GROUP-ID") } : {}),
|
|
449
|
+
...(c.req.header("X-DAIN-SMART-ACCOUNT-PDA") ? { smartAccountPDA: c.req.header("X-DAIN-SMART-ACCOUNT-PDA") } : {}),
|
|
450
|
+
});
|
|
303
451
|
debugLog(`[Auth Middleware] API Key auth SUCCESS - agentId: ${parsed.agentId}, orgId: ${parsed.orgId}`);
|
|
304
452
|
}
|
|
305
453
|
else {
|
|
@@ -311,7 +459,8 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
311
459
|
async function getAgentInfo(c) {
|
|
312
460
|
debugLog("[getAgentInfo] START");
|
|
313
461
|
debugLog("[getAgentInfo] Auth method:", c.get('authMethod'));
|
|
314
|
-
const
|
|
462
|
+
const runtimeContext = (c.get('dainRuntimeContext') || {});
|
|
463
|
+
const smartAccountPDA = runtimeContext.smartAccountPDA || c.get('smartAccountPDA') || c.req.header("X-DAIN-SMART-ACCOUNT-PDA");
|
|
315
464
|
const webhookUrl = c.req.header("X-DAIN-WEBHOOK-URL");
|
|
316
465
|
debugLog("[getAgentInfo] smartAccountPDA:", smartAccountPDA);
|
|
317
466
|
debugLog("[getAgentInfo] webhookUrl:", webhookUrl);
|
|
@@ -322,7 +471,17 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
322
471
|
agentId: smartAccountId,
|
|
323
472
|
address: smartAccountId,
|
|
324
473
|
smartAccountPDA,
|
|
325
|
-
|
|
474
|
+
dainAccountId: runtimeContext.dainAccountId,
|
|
475
|
+
dainGroupId: runtimeContext.dainGroupId,
|
|
476
|
+
account: runtimeContext.account,
|
|
477
|
+
group: runtimeContext.group,
|
|
478
|
+
auth: runtimeContext.auth,
|
|
479
|
+
grants: runtimeContext.grants,
|
|
480
|
+
dataPermissions: runtimeContext.dataPermissions,
|
|
481
|
+
registryPolicy: runtimeContext.registryPolicy,
|
|
482
|
+
id: runtimeContext.dainAccountId ??
|
|
483
|
+
runtimeContext.dainGroupId ??
|
|
484
|
+
(smartAccountPDA ? `dain_id_${smartAccountPDA}` : `smart_account_${smartAccountId}`),
|
|
326
485
|
webhookUrl,
|
|
327
486
|
};
|
|
328
487
|
debugLog("[getAgentInfo] JWT - Returning agent info:", agentInfo);
|
|
@@ -335,7 +494,17 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
335
494
|
agentId,
|
|
336
495
|
address,
|
|
337
496
|
smartAccountPDA,
|
|
338
|
-
|
|
497
|
+
dainAccountId: runtimeContext.dainAccountId,
|
|
498
|
+
dainGroupId: runtimeContext.dainGroupId,
|
|
499
|
+
account: runtimeContext.account,
|
|
500
|
+
group: runtimeContext.group,
|
|
501
|
+
auth: runtimeContext.auth,
|
|
502
|
+
grants: runtimeContext.grants,
|
|
503
|
+
dataPermissions: runtimeContext.dataPermissions,
|
|
504
|
+
registryPolicy: runtimeContext.registryPolicy,
|
|
505
|
+
id: runtimeContext.dainAccountId ??
|
|
506
|
+
runtimeContext.dainGroupId ??
|
|
507
|
+
(smartAccountPDA ? `dain_id_${smartAccountPDA}` : `address_${address}`),
|
|
339
508
|
webhookUrl,
|
|
340
509
|
};
|
|
341
510
|
debugLog("[getAgentInfo] API Key - Returning agent info:", agentInfo);
|
|
@@ -378,6 +547,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
378
547
|
datasourcePolicy: true,
|
|
379
548
|
widgetPolicy: true,
|
|
380
549
|
toolSafety: true,
|
|
550
|
+
dainAccount: true,
|
|
551
|
+
registryDiscovery: true,
|
|
552
|
+
dataPermissions: true,
|
|
553
|
+
actionGrants: true,
|
|
554
|
+
smartAccountTransactions: true,
|
|
555
|
+
payments: true,
|
|
381
556
|
};
|
|
382
557
|
const compatibility = (() => {
|
|
383
558
|
if (!requestedContract)
|
|
@@ -451,7 +626,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
451
626
|
description: context.description,
|
|
452
627
|
}));
|
|
453
628
|
// Process plugins for the response
|
|
454
|
-
const processedResponse = await processPluginsForResponse(contextInfo, body, {
|
|
629
|
+
const processedResponse = await processPluginsForResponse(contextInfo, body, {
|
|
630
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
631
|
+
request: processedPluginData,
|
|
632
|
+
plugins: processedPluginData.plugins,
|
|
633
|
+
}),
|
|
634
|
+
});
|
|
455
635
|
return c.json(processedResponse);
|
|
456
636
|
});
|
|
457
637
|
app.post("/contexts/:contextId", async (c) => {
|
|
@@ -465,17 +645,19 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
465
645
|
const body = await safeParseBody(c);
|
|
466
646
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
467
647
|
const oauth2Client = app.oauth2?.getClient();
|
|
468
|
-
const
|
|
648
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
649
|
+
request: processedPluginData,
|
|
469
650
|
plugins: processedPluginData.plugins,
|
|
470
|
-
oauth2Client
|
|
651
|
+
oauth2Client,
|
|
471
652
|
});
|
|
653
|
+
const contextData = await context.getContextData(agentInfo, extraData);
|
|
472
654
|
const response = {
|
|
473
655
|
id: context.id,
|
|
474
656
|
name: context.name,
|
|
475
657
|
description: context.description,
|
|
476
658
|
data: contextData
|
|
477
659
|
};
|
|
478
|
-
const processedResponse = await processPluginsForResponse(response, body, { extraData
|
|
660
|
+
const processedResponse = await processPluginsForResponse(response, body, { extraData });
|
|
479
661
|
return c.json(processedResponse);
|
|
480
662
|
}
|
|
481
663
|
else {
|
|
@@ -487,6 +669,11 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
487
669
|
const body = await safeParseBody(c);
|
|
488
670
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
489
671
|
const oauth2Client = app.oauth2?.getClient();
|
|
672
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
673
|
+
request: processedPluginData,
|
|
674
|
+
plugins: processedPluginData.plugins,
|
|
675
|
+
oauth2Client,
|
|
676
|
+
});
|
|
490
677
|
const autoContexts = app.oauth2?.getDirectProviderContexts?.() || [];
|
|
491
678
|
const serviceSlug = (0, core_1.toSlug)(metadata.title);
|
|
492
679
|
const skillsContext = config.skills?.getSkillsContext?.(serviceSlug);
|
|
@@ -495,22 +682,23 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
495
682
|
id: ctx.id,
|
|
496
683
|
name: ctx.name,
|
|
497
684
|
description: ctx.description,
|
|
498
|
-
data: await ctx.getContextData(agentInfo,
|
|
499
|
-
plugins: processedPluginData.plugins,
|
|
500
|
-
oauth2Client
|
|
501
|
-
}),
|
|
685
|
+
data: await ctx.getContextData(agentInfo, extraData),
|
|
502
686
|
})));
|
|
503
|
-
const processedResponse = await processPluginsForResponse(contextsFull, body, { extraData
|
|
687
|
+
const processedResponse = await processPluginsForResponse(contextsFull, body, { extraData });
|
|
504
688
|
return c.json(processedResponse);
|
|
505
689
|
});
|
|
506
690
|
app.post("/widgets", async (c) => {
|
|
507
691
|
const agentInfo = await getAgentInfo(c);
|
|
508
692
|
const body = await safeParseBody(c);
|
|
509
693
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
694
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
695
|
+
request: processedPluginData,
|
|
696
|
+
plugins: processedPluginData.plugins,
|
|
697
|
+
});
|
|
510
698
|
const widgetIds = config.getUserWidgets
|
|
511
|
-
? await config.getUserWidgets(agentInfo,
|
|
699
|
+
? await config.getUserWidgets(agentInfo, extraData)
|
|
512
700
|
: widgets.map(widget => widget.id);
|
|
513
|
-
const processedResponse = await processPluginsForResponse(widgetIds, body, { extraData
|
|
701
|
+
const processedResponse = await processPluginsForResponse(widgetIds, body, { extraData });
|
|
514
702
|
return c.json(processedResponse);
|
|
515
703
|
});
|
|
516
704
|
app.post("/widgets/all", async (c) => {
|
|
@@ -518,18 +706,23 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
518
706
|
const body = await safeParseBody(c);
|
|
519
707
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
520
708
|
const widgetIds = config.getUserWidgets
|
|
521
|
-
? await config.getUserWidgets(agentInfo, {
|
|
709
|
+
? await config.getUserWidgets(agentInfo, buildServiceExtraData(agentInfo, {
|
|
710
|
+
request: processedPluginData,
|
|
711
|
+
plugins: processedPluginData.plugins,
|
|
712
|
+
}))
|
|
522
713
|
: widgets.map(widget => widget.id);
|
|
523
714
|
const oauth2Client = app.oauth2?.getClient();
|
|
715
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
716
|
+
request: processedPluginData,
|
|
717
|
+
plugins: processedPluginData.plugins,
|
|
718
|
+
oauth2Client,
|
|
719
|
+
app,
|
|
720
|
+
});
|
|
524
721
|
const widgetsFull = await Promise.all(widgetIds.map(async (widgetId) => {
|
|
525
722
|
const widget = widgets.find(w => w.id === widgetId);
|
|
526
723
|
if (!widget)
|
|
527
724
|
return null;
|
|
528
|
-
const widgetData = await widget.getWidget(agentInfo,
|
|
529
|
-
plugins: processedPluginData.plugins,
|
|
530
|
-
oauth2Client,
|
|
531
|
-
app
|
|
532
|
-
});
|
|
725
|
+
const widgetData = await widget.getWidget(agentInfo, extraData);
|
|
533
726
|
const response = {
|
|
534
727
|
id: widget.id,
|
|
535
728
|
name: widget.name,
|
|
@@ -552,7 +745,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
552
745
|
return response;
|
|
553
746
|
}));
|
|
554
747
|
const validWidgets = widgetsFull.filter(w => w !== null);
|
|
555
|
-
const processedResponse = await processPluginsForResponse(validWidgets, body, { extraData
|
|
748
|
+
const processedResponse = await processPluginsForResponse(validWidgets, body, { extraData });
|
|
556
749
|
return c.json(processedResponse);
|
|
557
750
|
});
|
|
558
751
|
app.post("/widgets/:widgetId", async (c) => {
|
|
@@ -565,25 +758,31 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
565
758
|
const body = await safeParseBody(c);
|
|
566
759
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
567
760
|
if (config.getUserWidgets) {
|
|
761
|
+
const accessExtraData = buildServiceExtraData(agentInfo, {
|
|
762
|
+
request: processedPluginData,
|
|
763
|
+
plugins: processedPluginData.plugins,
|
|
764
|
+
});
|
|
568
765
|
const userWidgetIds = await config.getUserWidgets(agentInfo, {
|
|
569
|
-
|
|
766
|
+
...accessExtraData
|
|
570
767
|
});
|
|
571
768
|
let homeUIWidgetId = null;
|
|
572
769
|
if (config.homeUI) {
|
|
573
770
|
homeUIWidgetId = typeof config.homeUI === 'string'
|
|
574
771
|
? config.homeUI
|
|
575
|
-
: await config.homeUI(agentInfo,
|
|
772
|
+
: await config.homeUI(agentInfo, accessExtraData);
|
|
576
773
|
}
|
|
577
774
|
if (!userWidgetIds.includes(widgetId) && widgetId !== homeUIWidgetId) {
|
|
578
775
|
throw new http_exception_1.HTTPException(403, { message: "Access denied to this widget" });
|
|
579
776
|
}
|
|
580
777
|
}
|
|
581
778
|
const oauth2Client = app.oauth2?.getClient();
|
|
582
|
-
const
|
|
779
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
780
|
+
request: processedPluginData,
|
|
583
781
|
plugins: processedPluginData.plugins,
|
|
584
782
|
oauth2Client,
|
|
585
|
-
app
|
|
783
|
+
app,
|
|
586
784
|
});
|
|
785
|
+
const widgetData = await widget.getWidget(agentInfo, extraData);
|
|
587
786
|
const response = {
|
|
588
787
|
id: widget.id,
|
|
589
788
|
name: widget.name,
|
|
@@ -603,7 +802,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
603
802
|
scope: "account",
|
|
604
803
|
};
|
|
605
804
|
}
|
|
606
|
-
const processedResponse = await processPluginsForResponse(response, body, { extraData
|
|
805
|
+
const processedResponse = await processPluginsForResponse(response, body, { extraData });
|
|
607
806
|
return c.json(processedResponse);
|
|
608
807
|
});
|
|
609
808
|
app.post("/homeUI", async (c) => {
|
|
@@ -613,9 +812,13 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
613
812
|
const agentInfo = await getAgentInfo(c);
|
|
614
813
|
const body = await safeParseBody(c);
|
|
615
814
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
815
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
816
|
+
request: processedPluginData,
|
|
817
|
+
plugins: processedPluginData.plugins,
|
|
818
|
+
});
|
|
616
819
|
const homeUIWidgetId = typeof config.homeUI === 'string'
|
|
617
820
|
? config.homeUI
|
|
618
|
-
: await config.homeUI(agentInfo,
|
|
821
|
+
: await config.homeUI(agentInfo, extraData);
|
|
619
822
|
if (!homeUIWidgetId) {
|
|
620
823
|
return c.json({ widgetId: null });
|
|
621
824
|
}
|
|
@@ -623,7 +826,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
623
826
|
if (!widget) {
|
|
624
827
|
return c.json({ widgetId: null });
|
|
625
828
|
}
|
|
626
|
-
const processedResponse = await processPluginsForResponse({ widgetId: homeUIWidgetId }, body, { extraData
|
|
829
|
+
const processedResponse = await processPluginsForResponse({ widgetId: homeUIWidgetId }, body, { extraData });
|
|
627
830
|
return c.json(processedResponse);
|
|
628
831
|
});
|
|
629
832
|
function mapDatasourceInfo(datasource) {
|
|
@@ -648,14 +851,24 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
648
851
|
const agentInfo = await getAgentInfo(c);
|
|
649
852
|
const body = await safeParseBody(c);
|
|
650
853
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
651
|
-
const processedResponse = await processPluginsForResponse(datasources.map(mapDatasourceInfo), body, {
|
|
854
|
+
const processedResponse = await processPluginsForResponse(datasources.map(mapDatasourceInfo), body, {
|
|
855
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
856
|
+
request: processedPluginData,
|
|
857
|
+
plugins: processedPluginData.plugins,
|
|
858
|
+
}),
|
|
859
|
+
});
|
|
652
860
|
return c.json(processedResponse);
|
|
653
861
|
});
|
|
654
862
|
app.post("/datasources/all", async (c) => {
|
|
655
863
|
const agentInfo = await getAgentInfo(c);
|
|
656
864
|
const body = await safeParseBody(c);
|
|
657
865
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
658
|
-
const processedResponse = await processPluginsForResponse(datasources.map(mapDatasourceInfo), body, {
|
|
866
|
+
const processedResponse = await processPluginsForResponse(datasources.map(mapDatasourceInfo), body, {
|
|
867
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
868
|
+
request: processedPluginData,
|
|
869
|
+
plugins: processedPluginData.plugins,
|
|
870
|
+
}),
|
|
871
|
+
});
|
|
659
872
|
return c.json(processedResponse);
|
|
660
873
|
});
|
|
661
874
|
app.post("/datasources/:datasourceId", async (c) => {
|
|
@@ -671,10 +884,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
671
884
|
try {
|
|
672
885
|
const parsedParams = datasource.input.parse(params);
|
|
673
886
|
const oauth2Client = app.oauth2?.getClient();
|
|
674
|
-
const
|
|
887
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
888
|
+
request: params,
|
|
675
889
|
plugins: pluginsData,
|
|
676
|
-
oauth2Client
|
|
890
|
+
oauth2Client,
|
|
677
891
|
});
|
|
892
|
+
const data = await datasource.getDatasource(agentInfo, parsedParams, extraData);
|
|
678
893
|
const requestedPolicy = {
|
|
679
894
|
refreshIntervalMs: datasource.refreshIntervalMs,
|
|
680
895
|
maxStalenessMs: datasource.maxStalenessMs,
|
|
@@ -702,7 +917,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
702
917
|
scope: datasource.scope,
|
|
703
918
|
},
|
|
704
919
|
};
|
|
705
|
-
const processedResponse = await processPluginsForResponse(response, { plugins: pluginsData }, { extraData
|
|
920
|
+
const processedResponse = await processPluginsForResponse(response, { plugins: pluginsData }, { extraData });
|
|
706
921
|
return c.json(processedResponse);
|
|
707
922
|
}
|
|
708
923
|
catch (error) {
|
|
@@ -718,6 +933,17 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
718
933
|
throw error;
|
|
719
934
|
}
|
|
720
935
|
});
|
|
936
|
+
app.delete("/datasources/:datasourceId/stream/:streamId", async (c) => {
|
|
937
|
+
const datasourceId = c.req.param("datasourceId");
|
|
938
|
+
const streamId = c.req.param("streamId");
|
|
939
|
+
const streamKey = `${datasourceId}:${streamId}`;
|
|
940
|
+
const abortController = datasourceStreamAbortControllers.get(streamKey);
|
|
941
|
+
if (abortController) {
|
|
942
|
+
abortController.abort();
|
|
943
|
+
datasourceStreamAbortControllers.delete(streamKey);
|
|
944
|
+
}
|
|
945
|
+
return c.json({ success: true });
|
|
946
|
+
});
|
|
721
947
|
// Stream datasource updates over SSE. This is primarily used for "fresh" UIs
|
|
722
948
|
// (positions/orders) where clients want stream-first updates with a poll fallback.
|
|
723
949
|
//
|
|
@@ -735,6 +961,11 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
735
961
|
!Array.isArray(rawBody) &&
|
|
736
962
|
"params" in rawBody;
|
|
737
963
|
const requestedIntervalMsRaw = hasWrappedParams ? rawBody.intervalMs : undefined;
|
|
964
|
+
const requestedStreamIdRaw = hasWrappedParams ? rawBody.streamId : undefined;
|
|
965
|
+
const streamId = typeof requestedStreamIdRaw === "string" &&
|
|
966
|
+
/^[A-Za-z0-9._:-]{1,128}$/.test(requestedStreamIdRaw)
|
|
967
|
+
? requestedStreamIdRaw
|
|
968
|
+
: null;
|
|
738
969
|
const requestParamsRaw = hasWrappedParams ? rawBody.params : rawBody;
|
|
739
970
|
let params = await processPluginsForRequest(requestParamsRaw && typeof requestParamsRaw === "object" ? requestParamsRaw : {}, agentInfo);
|
|
740
971
|
const pluginsData = (params.plugins && typeof params.plugins === "object"
|
|
@@ -757,19 +988,26 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
757
988
|
}
|
|
758
989
|
throw error;
|
|
759
990
|
}
|
|
991
|
+
const oauth2Client = app.oauth2?.getClient();
|
|
992
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
993
|
+
request: params,
|
|
994
|
+
plugins: pluginsData,
|
|
995
|
+
oauth2Client,
|
|
996
|
+
});
|
|
760
997
|
const pluginRequestContext = hasWrappedParams
|
|
761
998
|
? {
|
|
762
999
|
params: parsedParams,
|
|
763
1000
|
intervalMs: requestedIntervalMsRaw,
|
|
764
1001
|
plugins: pluginsData,
|
|
1002
|
+
DAIN_EXTRA_DATA: extraData,
|
|
765
1003
|
}
|
|
766
1004
|
: {
|
|
767
1005
|
...(typeof parsedParams === "object" && parsedParams !== null
|
|
768
1006
|
? parsedParams
|
|
769
1007
|
: {}),
|
|
770
1008
|
plugins: pluginsData,
|
|
1009
|
+
DAIN_EXTRA_DATA: extraData,
|
|
771
1010
|
};
|
|
772
|
-
const oauth2Client = app.oauth2?.getClient();
|
|
773
1011
|
const requestedPolicy = {
|
|
774
1012
|
refreshIntervalMs: datasource.refreshIntervalMs,
|
|
775
1013
|
maxStalenessMs: datasource.maxStalenessMs,
|
|
@@ -786,15 +1024,22 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
786
1024
|
? datasource.refreshIntervalMs
|
|
787
1025
|
: 15_000);
|
|
788
1026
|
const intervalMs = Math.max(1_000, Math.floor(baseIntervalMs));
|
|
789
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
790
1027
|
debugLog(`[SSE] Datasource ${datasource.id} stream start (${intervalMs}ms interval)`);
|
|
791
1028
|
return signedStreamSSE(c, privateKey, config, async (stream) => {
|
|
792
1029
|
let eventId = 0;
|
|
1030
|
+
const signal = c.req.raw?.signal;
|
|
1031
|
+
const streamAbortController = new AbortController();
|
|
1032
|
+
const streamKey = streamId ? `${datasource.id}:${streamId}` : null;
|
|
1033
|
+
if (streamKey) {
|
|
1034
|
+
datasourceStreamAbortControllers.set(streamKey, streamAbortController);
|
|
1035
|
+
}
|
|
1036
|
+
const isCancelled = () => stream.isAborted() || signal?.aborted === true || streamAbortController.signal.aborted;
|
|
793
1037
|
const emitUpdate = async () => {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1038
|
+
if (isCancelled())
|
|
1039
|
+
return false;
|
|
1040
|
+
const data = await datasource.getDatasource(agentInfo, parsedParams, extraData);
|
|
1041
|
+
if (isCancelled())
|
|
1042
|
+
return false;
|
|
798
1043
|
const response = {
|
|
799
1044
|
id: datasource.id,
|
|
800
1045
|
name: datasource.name,
|
|
@@ -814,51 +1059,77 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
814
1059
|
scope: datasource.scope,
|
|
815
1060
|
},
|
|
816
1061
|
};
|
|
817
|
-
const processedResponse = await processPluginsForResponse(response, pluginRequestContext, { extraData
|
|
1062
|
+
const processedResponse = await processPluginsForResponse(response, pluginRequestContext, { extraData });
|
|
1063
|
+
if (isCancelled())
|
|
1064
|
+
return false;
|
|
818
1065
|
await stream.writeSSE({
|
|
819
1066
|
event: "datasource-update",
|
|
820
1067
|
data: JSON.stringify(processedResponse),
|
|
821
1068
|
id: String(eventId++),
|
|
822
1069
|
});
|
|
1070
|
+
return true;
|
|
823
1071
|
};
|
|
824
|
-
// Send initial snapshot immediately.
|
|
825
1072
|
try {
|
|
826
|
-
|
|
827
|
-
}
|
|
828
|
-
catch (error) {
|
|
829
|
-
console.error(`[SSE] Datasource ${datasource.id} initial update failed:`, error);
|
|
830
|
-
if (!stream.isAborted()) {
|
|
831
|
-
await stream.writeSSE({
|
|
832
|
-
event: "error",
|
|
833
|
-
data: JSON.stringify({ message: error?.message || "Datasource stream error" }),
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
return;
|
|
837
|
-
}
|
|
838
|
-
// Continue sending updates until client disconnects.
|
|
839
|
-
// Hono exposes the underlying Request via c.req.raw, which includes an AbortSignal.
|
|
840
|
-
const signal = c.req.raw?.signal;
|
|
841
|
-
while (!stream.isAborted() && !signal?.aborted) {
|
|
842
|
-
await sleep(intervalMs);
|
|
843
|
-
if (stream.isAborted() || signal?.aborted)
|
|
844
|
-
break;
|
|
1073
|
+
// Send initial snapshot immediately.
|
|
845
1074
|
try {
|
|
846
|
-
await emitUpdate();
|
|
1075
|
+
const didEmitInitialUpdate = await emitUpdate();
|
|
1076
|
+
if (!didEmitInitialUpdate)
|
|
1077
|
+
return;
|
|
847
1078
|
}
|
|
848
1079
|
catch (error) {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
console.error(`[SSE] Datasource ${datasource.id} update failed:`, error);
|
|
852
|
-
// Keep the stream alive; clients should rely on poll fallback if needed.
|
|
853
|
-
if (!stream.isAborted()) {
|
|
1080
|
+
console.error(`[SSE] Datasource ${datasource.id} initial update failed:`, error);
|
|
1081
|
+
if (!isCancelled()) {
|
|
854
1082
|
await stream.writeSSE({
|
|
855
1083
|
event: "error",
|
|
856
1084
|
data: JSON.stringify({ message: error?.message || "Datasource stream error" }),
|
|
857
1085
|
});
|
|
858
1086
|
}
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
// Continue sending updates until client disconnects.
|
|
1090
|
+
// Hono exposes the underlying Request via c.req.raw, which includes an AbortSignal.
|
|
1091
|
+
const abortPromise = new Promise((resolve) => {
|
|
1092
|
+
if (isCancelled()) {
|
|
1093
|
+
resolve();
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
stream.onAbort(() => resolve());
|
|
1097
|
+
signal?.addEventListener("abort", () => resolve(), { once: true });
|
|
1098
|
+
streamAbortController.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
1099
|
+
});
|
|
1100
|
+
const sleepUntilNextUpdate = (ms) => Promise.race([
|
|
1101
|
+
new Promise((resolve) => setTimeout(resolve, ms)),
|
|
1102
|
+
abortPromise,
|
|
1103
|
+
]);
|
|
1104
|
+
while (!isCancelled()) {
|
|
1105
|
+
await sleepUntilNextUpdate(intervalMs);
|
|
1106
|
+
if (isCancelled())
|
|
1107
|
+
break;
|
|
1108
|
+
try {
|
|
1109
|
+
const didEmitUpdate = await emitUpdate();
|
|
1110
|
+
if (!didEmitUpdate)
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
catch (error) {
|
|
1114
|
+
if (isCancelled())
|
|
1115
|
+
break;
|
|
1116
|
+
console.error(`[SSE] Datasource ${datasource.id} update failed:`, error);
|
|
1117
|
+
// Keep the stream alive; clients should rely on poll fallback if needed.
|
|
1118
|
+
if (!isCancelled()) {
|
|
1119
|
+
await stream.writeSSE({
|
|
1120
|
+
event: "error",
|
|
1121
|
+
data: JSON.stringify({ message: error?.message || "Datasource stream error" }),
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
859
1125
|
}
|
|
860
1126
|
}
|
|
861
|
-
|
|
1127
|
+
finally {
|
|
1128
|
+
if (streamKey) {
|
|
1129
|
+
datasourceStreamAbortControllers.delete(streamKey);
|
|
1130
|
+
}
|
|
1131
|
+
debugLog(`[SSE] Datasource ${datasource.id} stream end`);
|
|
1132
|
+
}
|
|
862
1133
|
});
|
|
863
1134
|
});
|
|
864
1135
|
function mapAgentInfo(agent) {
|
|
@@ -880,7 +1151,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
880
1151
|
const agentInfo = await getAgentInfo(c);
|
|
881
1152
|
const body = await safeParseBody(c);
|
|
882
1153
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
883
|
-
const processedResponse = await processPluginsForResponse(agents.map(mapAgentInfo), body, {
|
|
1154
|
+
const processedResponse = await processPluginsForResponse(agents.map(mapAgentInfo), body, {
|
|
1155
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
1156
|
+
request: processedPluginData,
|
|
1157
|
+
plugins: processedPluginData.plugins,
|
|
1158
|
+
}),
|
|
1159
|
+
});
|
|
884
1160
|
return c.json(processedResponse);
|
|
885
1161
|
});
|
|
886
1162
|
app.get("/agents/:agentId", (c) => {
|
|
@@ -898,7 +1174,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
898
1174
|
const agentInfo = await getAgentInfo(c);
|
|
899
1175
|
const body = await safeParseBody(c);
|
|
900
1176
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
901
|
-
const processedResponse = await processPluginsForResponse(mapAgentInfo(agent), body, {
|
|
1177
|
+
const processedResponse = await processPluginsForResponse(mapAgentInfo(agent), body, {
|
|
1178
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
1179
|
+
request: processedPluginData,
|
|
1180
|
+
plugins: processedPluginData.plugins,
|
|
1181
|
+
}),
|
|
1182
|
+
});
|
|
902
1183
|
return c.json(processedResponse);
|
|
903
1184
|
});
|
|
904
1185
|
function fixEmptySchemas(schema) {
|
|
@@ -971,7 +1252,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
971
1252
|
reccomendedPrompts,
|
|
972
1253
|
}),
|
|
973
1254
|
};
|
|
974
|
-
const processedResponse = await processPluginsForResponse(response, body, {
|
|
1255
|
+
const processedResponse = await processPluginsForResponse(response, body, {
|
|
1256
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
1257
|
+
request: processedPluginData,
|
|
1258
|
+
plugins: processedPluginData.plugins,
|
|
1259
|
+
}),
|
|
1260
|
+
});
|
|
975
1261
|
return c.json(processedResponse);
|
|
976
1262
|
});
|
|
977
1263
|
// Webhook triggers endpoint
|
|
@@ -1487,7 +1773,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1487
1773
|
const agentInfo = await getAgentInfo(c);
|
|
1488
1774
|
const body = await safeParseBody(c);
|
|
1489
1775
|
const processedPluginData = await processPluginsForRequest(body, agentInfo);
|
|
1490
|
-
const processedResponse = await processPluginsForResponse(config.exampleQueries || [], body, {
|
|
1776
|
+
const processedResponse = await processPluginsForResponse(config.exampleQueries || [], body, {
|
|
1777
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
1778
|
+
request: processedPluginData,
|
|
1779
|
+
plugins: processedPluginData.plugins,
|
|
1780
|
+
}),
|
|
1781
|
+
});
|
|
1491
1782
|
return c.json(processedResponse);
|
|
1492
1783
|
});
|
|
1493
1784
|
// Services list endpoint
|
|
@@ -1625,7 +1916,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1625
1916
|
count: cards.length,
|
|
1626
1917
|
query,
|
|
1627
1918
|
};
|
|
1628
|
-
const processedResponse = await processPluginsForResponse(response, body, {
|
|
1919
|
+
const processedResponse = await processPluginsForResponse(response, body, {
|
|
1920
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
1921
|
+
request: processedPluginData,
|
|
1922
|
+
plugins: processedPluginData.plugins,
|
|
1923
|
+
}),
|
|
1924
|
+
});
|
|
1629
1925
|
return c.json(processedResponse);
|
|
1630
1926
|
});
|
|
1631
1927
|
const recommendationRenderSchema = zod_1.z.object({
|
|
@@ -1729,7 +2025,12 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1729
2025
|
renderedCount: renderedCards.length,
|
|
1730
2026
|
requestedCount: requestedCards.length,
|
|
1731
2027
|
};
|
|
1732
|
-
const processedResponse = await processPluginsForResponse(response, body, {
|
|
2028
|
+
const processedResponse = await processPluginsForResponse(response, body, {
|
|
2029
|
+
extraData: buildServiceExtraData(agentInfo, {
|
|
2030
|
+
request: processedPluginData,
|
|
2031
|
+
plugins: processedPluginData.plugins,
|
|
2032
|
+
}),
|
|
2033
|
+
});
|
|
1733
2034
|
const statusCode = !continueOnError && renderErrors.length > 0 ? 400 : 200;
|
|
1734
2035
|
return c.json(processedResponse, statusCode);
|
|
1735
2036
|
});
|
|
@@ -1740,17 +2041,56 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1740
2041
|
if (!processedRequest.plugins) {
|
|
1741
2042
|
processedRequest.plugins = {};
|
|
1742
2043
|
}
|
|
2044
|
+
processedRequest.DAIN_EXTRA_DATA = buildServiceExtraData(agentInfo, {
|
|
2045
|
+
request: processedRequest,
|
|
2046
|
+
plugins: processedRequest.plugins,
|
|
2047
|
+
});
|
|
1743
2048
|
// Auto-inject smartAccountPDA into CryptoPlugin wallet context for automations
|
|
1744
2049
|
// This allows automation wallets to work seamlessly without manual configuration
|
|
1745
2050
|
if (agentInfo.smartAccountPDA) {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
2051
|
+
processedRequest.plugins['crypto-plugin'] = processedRequest.plugins['crypto-plugin'] || {};
|
|
2052
|
+
const cryptoData = isRecord(processedRequest.plugins['crypto-plugin'])
|
|
2053
|
+
? stripRuntimeContextFields(processedRequest.plugins['crypto-plugin'])
|
|
2054
|
+
: {};
|
|
2055
|
+
processedRequest.plugins['crypto-plugin'] = cryptoData;
|
|
2056
|
+
const smartAccountWallet = {
|
|
2057
|
+
chain: 'sol',
|
|
2058
|
+
address: agentInfo.smartAccountPDA,
|
|
2059
|
+
accountId: agentInfo.dainAccountId ?? agentInfo.dainGroupId,
|
|
2060
|
+
kind: 'smart_account_vault',
|
|
2061
|
+
capabilities: ['solana-smart-account'],
|
|
2062
|
+
};
|
|
2063
|
+
if (Array.isArray(cryptoData.wallets)) {
|
|
2064
|
+
const hasSmartAccountWallet = cryptoData.wallets.some((wallet) => isRecord(wallet) &&
|
|
2065
|
+
wallet.chain === 'sol' &&
|
|
2066
|
+
wallet.address === agentInfo.smartAccountPDA);
|
|
2067
|
+
if (!hasSmartAccountWallet) {
|
|
2068
|
+
cryptoData.wallets = [smartAccountWallet, ...cryptoData.wallets];
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
else if (isRecord(cryptoData.wallets)) {
|
|
2072
|
+
cryptoData.wallets = [
|
|
2073
|
+
smartAccountWallet,
|
|
2074
|
+
...Object.entries(cryptoData.wallets).map(([chain, address]) => ({
|
|
2075
|
+
chain,
|
|
2076
|
+
address: String(address),
|
|
2077
|
+
})),
|
|
1751
2078
|
];
|
|
1752
2079
|
}
|
|
2080
|
+
else {
|
|
2081
|
+
cryptoData.wallets = [smartAccountWallet];
|
|
2082
|
+
}
|
|
1753
2083
|
}
|
|
2084
|
+
if (processedRequest.plugins['crypto-plugin']) {
|
|
2085
|
+
processedRequest.plugins['crypto-plugin'] = {
|
|
2086
|
+
...stripRuntimeContextFields(processedRequest.plugins['crypto-plugin']),
|
|
2087
|
+
...getRuntimeContextFromAgentInfo(agentInfo),
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
processedRequest.DAIN_EXTRA_DATA = buildServiceExtraData(agentInfo, {
|
|
2091
|
+
request: processedRequest,
|
|
2092
|
+
plugins: processedRequest.plugins,
|
|
2093
|
+
});
|
|
1754
2094
|
// Process plugins if configured
|
|
1755
2095
|
if (!config.plugins || config.plugins.length === 0) {
|
|
1756
2096
|
return processedRequest;
|
|
@@ -1760,6 +2100,10 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1760
2100
|
processedRequest = await plugin.processInputService(processedRequest);
|
|
1761
2101
|
}
|
|
1762
2102
|
}
|
|
2103
|
+
processedRequest.DAIN_EXTRA_DATA = buildServiceExtraData(agentInfo, {
|
|
2104
|
+
request: processedRequest,
|
|
2105
|
+
plugins: processedRequest.plugins,
|
|
2106
|
+
});
|
|
1763
2107
|
return processedRequest;
|
|
1764
2108
|
}
|
|
1765
2109
|
// Process response with plugins
|
|
@@ -1769,6 +2113,9 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1769
2113
|
}
|
|
1770
2114
|
let processedResponse = { ...response };
|
|
1771
2115
|
processedResponse.plugins = processedResponse.plugins || {};
|
|
2116
|
+
const extraData = isRecord(context?.extraData)
|
|
2117
|
+
? context.extraData
|
|
2118
|
+
: (isRecord(context) ? context : {});
|
|
1772
2119
|
for (const plugin of config.plugins) {
|
|
1773
2120
|
if (plugin.processOutputService) {
|
|
1774
2121
|
// Provide context to the plugin
|
|
@@ -1776,7 +2123,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1776
2123
|
...processedResponse,
|
|
1777
2124
|
context: {
|
|
1778
2125
|
request,
|
|
1779
|
-
extraData
|
|
2126
|
+
extraData
|
|
1780
2127
|
}
|
|
1781
2128
|
});
|
|
1782
2129
|
if (pluginOutput) {
|
|
@@ -1787,6 +2134,31 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1787
2134
|
return processedResponse;
|
|
1788
2135
|
}
|
|
1789
2136
|
// Automatically create routes for each tool
|
|
2137
|
+
function buildToolContext(agentInfo, request, pluginsData, hooks = {}) {
|
|
2138
|
+
const oauth2Client = app.oauth2 ? app.oauth2.getClient() : undefined;
|
|
2139
|
+
const extraData = buildServiceExtraData(agentInfo, {
|
|
2140
|
+
request,
|
|
2141
|
+
plugins: pluginsData,
|
|
2142
|
+
oauth2Client,
|
|
2143
|
+
app,
|
|
2144
|
+
});
|
|
2145
|
+
const runtimeContext = getRuntimeContextFromAgentInfo(agentInfo);
|
|
2146
|
+
return {
|
|
2147
|
+
app,
|
|
2148
|
+
oauth2Client,
|
|
2149
|
+
extraData,
|
|
2150
|
+
dainAccountId: runtimeContext.dainAccountId ?? extraData.dainAccountId,
|
|
2151
|
+
dainGroupId: runtimeContext.dainGroupId ?? extraData.dainGroupId,
|
|
2152
|
+
smartAccountPDA: runtimeContext.smartAccountPDA ?? extraData.smartAccountPDA,
|
|
2153
|
+
account: runtimeContext.account ?? extraData.account,
|
|
2154
|
+
group: runtimeContext.group ?? extraData.group,
|
|
2155
|
+
auth: runtimeContext.auth ?? extraData.auth,
|
|
2156
|
+
grants: runtimeContext.grants ?? extraData.grants,
|
|
2157
|
+
dataPermissions: runtimeContext.dataPermissions ?? extraData.dataPermissions,
|
|
2158
|
+
registryPolicy: runtimeContext.registryPolicy ?? extraData.registryPolicy,
|
|
2159
|
+
...hooks,
|
|
2160
|
+
};
|
|
2161
|
+
}
|
|
1790
2162
|
tools.forEach((tool) => {
|
|
1791
2163
|
/**
|
|
1792
2164
|
* Shared handler for tool execution with streaming support
|
|
@@ -1801,13 +2173,8 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1801
2173
|
try {
|
|
1802
2174
|
// Execute the tool first
|
|
1803
2175
|
debugLog(`[SSE] Executing tool ${tool.id} in streaming mode${withContext ? ' with context' : ''}`);
|
|
1804
|
-
const
|
|
1805
|
-
|
|
1806
|
-
oauth2Client: app.oauth2 ? app.oauth2.getClient() : undefined,
|
|
1807
|
-
extraData: {
|
|
1808
|
-
...body.DAIN_EXTRA_DATA,
|
|
1809
|
-
plugins: pluginsData
|
|
1810
|
-
},
|
|
2176
|
+
const { DAIN_EXTRA_DATA: _dainExtraData, ...toolInput } = body;
|
|
2177
|
+
const result = await tool.handler(toolInput, agentInfo, buildToolContext(agentInfo, body, pluginsData, {
|
|
1811
2178
|
updateUI: async (update) => {
|
|
1812
2179
|
try {
|
|
1813
2180
|
// Check if this is a progress update
|
|
@@ -1841,7 +2208,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1841
2208
|
console.error(`Error sending process update in ${tool.id}:`, error);
|
|
1842
2209
|
}
|
|
1843
2210
|
}
|
|
1844
|
-
});
|
|
2211
|
+
}));
|
|
1845
2212
|
// If we need to include context data
|
|
1846
2213
|
let response = result;
|
|
1847
2214
|
if (withContext) {
|
|
@@ -1856,8 +2223,11 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1856
2223
|
name: context.name,
|
|
1857
2224
|
description: context.description,
|
|
1858
2225
|
data: await context.getContextData(agentInfo, {
|
|
1859
|
-
|
|
1860
|
-
|
|
2226
|
+
...buildServiceExtraData(agentInfo, {
|
|
2227
|
+
request: body,
|
|
2228
|
+
plugins: pluginsData,
|
|
2229
|
+
oauth2Client: app.oauth2 ? app.oauth2.getClient() : undefined,
|
|
2230
|
+
}),
|
|
1861
2231
|
}),
|
|
1862
2232
|
};
|
|
1863
2233
|
}
|
|
@@ -1957,13 +2327,8 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1957
2327
|
const uiUpdates = [];
|
|
1958
2328
|
const progressUpdates = [];
|
|
1959
2329
|
const processes = [];
|
|
1960
|
-
const
|
|
1961
|
-
|
|
1962
|
-
oauth2Client: app.oauth2 ? app.oauth2.getClient() : undefined,
|
|
1963
|
-
extraData: {
|
|
1964
|
-
...body.DAIN_EXTRA_DATA,
|
|
1965
|
-
plugins: pluginsData
|
|
1966
|
-
},
|
|
2330
|
+
const { DAIN_EXTRA_DATA: _dainExtraData, ...toolInput } = body;
|
|
2331
|
+
const result = await tool.handler(toolInput, agentInfo, buildToolContext(agentInfo, body, pluginsData, {
|
|
1967
2332
|
updateUI: (update) => {
|
|
1968
2333
|
// Collect UI updates instead of streaming them (synchronous for performance)
|
|
1969
2334
|
if (update.type === 'progress') {
|
|
@@ -1979,7 +2344,7 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1979
2344
|
processes.push(processId);
|
|
1980
2345
|
return Promise.resolve();
|
|
1981
2346
|
}
|
|
1982
|
-
});
|
|
2347
|
+
}));
|
|
1983
2348
|
// If we need to include context data
|
|
1984
2349
|
let response = result;
|
|
1985
2350
|
if (withContext) {
|
|
@@ -1990,8 +2355,11 @@ function setupHttpServer(config, tools, services, toolboxes, metadata, privateKe
|
|
|
1990
2355
|
name: context.name,
|
|
1991
2356
|
description: context.description,
|
|
1992
2357
|
data: await context.getContextData(agentInfo, {
|
|
1993
|
-
|
|
1994
|
-
|
|
2358
|
+
...buildServiceExtraData(agentInfo, {
|
|
2359
|
+
request: body,
|
|
2360
|
+
plugins: pluginsData,
|
|
2361
|
+
oauth2Client: app.oauth2 ? app.oauth2.getClient() : undefined,
|
|
2362
|
+
}),
|
|
1995
2363
|
}),
|
|
1996
2364
|
})));
|
|
1997
2365
|
// Create the complete response with context
|