aimeat 1.7.0 → 1.9.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/.env.example +4 -1
- package/dist/locales/en.json +573 -10
- package/dist/locales/fi.json +572 -9
- package/dist/public/css/views/admin-agent-integration.css +304 -0
- package/dist/public/css/views/agents-detail.css +1697 -120
- package/dist/public/css/views/profile.css +22 -2
- package/dist/public/css/views/public-knowledge-viewer.css +479 -0
- package/dist/public/js/i18n.js +9 -3
- package/dist/public/js/services/admin-agent-integration.js +52 -0
- package/dist/public/js/services/agent-integration.js +80 -0
- package/dist/public/js/services/agent-messages.js +3 -1
- package/dist/public/js/services/generator-spec-validate.js +23 -0
- package/dist/public/js/services/memory.js +39 -0
- package/dist/public/lib/live-updates.js +9 -4
- package/dist/public/llms-template.txt +106 -0
- package/dist/public/spa.html +7 -2
- package/dist/public/views/admin/agent-integration-tab.js +390 -0
- package/dist/public/views/admin/federation-tab.js +5 -5
- package/dist/public/views/admin/prompts-tab.js +9 -10
- package/dist/public/views/admin.js +2 -0
- package/dist/public/views/profile/access-tab.js +18 -0
- package/dist/public/views/profile/agents/agent-card.js +396 -0
- package/dist/public/views/profile/agents/shared-board.js +97 -0
- package/dist/public/views/profile/agents/state-detector.js +62 -0
- package/dist/public/views/profile/agents/tab-activity.js +232 -0
- package/dist/public/views/profile/agents/tab-agent-config.js +199 -0
- package/dist/public/views/profile/agents/tab-data-access.js +304 -0
- package/dist/public/views/profile/agents/tab-directives.js +178 -0
- package/dist/public/views/profile/agents/tab-integration.js +505 -0
- package/dist/public/views/profile/agents/tab-messages.js +278 -0
- package/dist/public/views/profile/agents/tab-services.js +17 -0
- package/dist/public/views/profile/agents/tab-tasks.js +17 -0
- package/dist/public/views/profile/agents-messages-subtab.js +12 -10
- package/dist/public/views/profile/agents-services-subtab.js +17 -11
- package/dist/public/views/profile/agents-tab.js +159 -255
- package/dist/public/views/profile/agents-task-builder.js +403 -0
- package/dist/public/views/profile/agents-tasks-subtab.js +186 -140
- package/dist/public/views/profile/generator-detail.js +8 -1
- package/dist/public/views/profile/memory-tab.js +371 -7
- package/dist/public/views/profile/wallet-tab.js +14 -0
- package/dist/public/views/public-knowledge-viewer.js +650 -0
- package/dist/src/auth/middleware.d.ts.map +1 -1
- package/dist/src/auth/middleware.js +17 -0
- package/dist/src/auth/middleware.js.map +1 -1
- package/dist/src/config.d.ts +1 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +1 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/middleware/readiness-gate.d.ts +18 -0
- package/dist/src/middleware/readiness-gate.d.ts.map +1 -0
- package/dist/src/middleware/readiness-gate.js +50 -0
- package/dist/src/middleware/readiness-gate.js.map +1 -0
- package/dist/src/models/agent-capabilities-schemas.d.ts +3 -0
- package/dist/src/models/agent-capabilities-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-capabilities-schemas.js +3 -0
- package/dist/src/models/agent-capabilities-schemas.js.map +1 -1
- package/dist/src/models/agent-directives-schemas.d.ts +12 -0
- package/dist/src/models/agent-directives-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-directives-schemas.js +7 -0
- package/dist/src/models/agent-directives-schemas.js.map +1 -1
- package/dist/src/models/agent-onboarding-schemas.d.ts +45 -0
- package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -0
- package/dist/src/models/agent-onboarding-schemas.js +91 -0
- package/dist/src/models/agent-onboarding-schemas.js.map +1 -0
- package/dist/src/models/webhook-schemas.d.ts +266 -0
- package/dist/src/models/webhook-schemas.d.ts.map +1 -0
- package/dist/src/models/webhook-schemas.js +137 -0
- package/dist/src/models/webhook-schemas.js.map +1 -0
- package/dist/src/routes/admin-agent-integration.d.ts +24 -0
- package/dist/src/routes/admin-agent-integration.d.ts.map +1 -0
- package/dist/src/routes/admin-agent-integration.js +186 -0
- package/dist/src/routes/admin-agent-integration.js.map +1 -0
- package/dist/src/routes/admin-monitoring.js +12 -7
- package/dist/src/routes/admin-monitoring.js.map +1 -1
- package/dist/src/routes/agent-activity.d.ts.map +1 -1
- package/dist/src/routes/agent-activity.js +17 -0
- package/dist/src/routes/agent-activity.js.map +1 -1
- package/dist/src/routes/agent-capabilities.d.ts +1 -0
- package/dist/src/routes/agent-capabilities.d.ts.map +1 -1
- package/dist/src/routes/agent-capabilities.js +9 -0
- package/dist/src/routes/agent-capabilities.js.map +1 -1
- package/dist/src/routes/agent-directives.d.ts +5 -1
- package/dist/src/routes/agent-directives.d.ts.map +1 -1
- package/dist/src/routes/agent-directives.js +74 -20
- package/dist/src/routes/agent-directives.js.map +1 -1
- package/dist/src/routes/agent-integration.d.ts +1 -0
- package/dist/src/routes/agent-integration.d.ts.map +1 -1
- package/dist/src/routes/agent-integration.js +128 -11
- package/dist/src/routes/agent-integration.js.map +1 -1
- package/dist/src/routes/agent-messages.d.ts +5 -1
- package/dist/src/routes/agent-messages.d.ts.map +1 -1
- package/dist/src/routes/agent-messages.js +20 -1
- package/dist/src/routes/agent-messages.js.map +1 -1
- package/dist/src/routes/agent-onboarding.d.ts +23 -0
- package/dist/src/routes/agent-onboarding.d.ts.map +1 -0
- package/dist/src/routes/agent-onboarding.js +374 -0
- package/dist/src/routes/agent-onboarding.js.map +1 -0
- package/dist/src/routes/agent-skill-bundle.d.ts +16 -0
- package/dist/src/routes/agent-skill-bundle.d.ts.map +1 -0
- package/dist/src/routes/agent-skill-bundle.js +133 -0
- package/dist/src/routes/agent-skill-bundle.js.map +1 -0
- package/dist/src/routes/agent-tasks.d.ts +8 -1
- package/dist/src/routes/agent-tasks.d.ts.map +1 -1
- package/dist/src/routes/agent-tasks.js +192 -14
- package/dist/src/routes/agent-tasks.js.map +1 -1
- package/dist/src/routes/agent-telemetry.d.ts +15 -0
- package/dist/src/routes/agent-telemetry.d.ts.map +1 -0
- package/dist/src/routes/agent-telemetry.js +99 -0
- package/dist/src/routes/agent-telemetry.js.map +1 -0
- package/dist/src/routes/agent-webhook.d.ts +18 -0
- package/dist/src/routes/agent-webhook.d.ts.map +1 -0
- package/dist/src/routes/agent-webhook.js +243 -0
- package/dist/src/routes/agent-webhook.js.map +1 -0
- package/dist/src/routes/agents.d.ts.map +1 -1
- package/dist/src/routes/agents.js +203 -4
- package/dist/src/routes/agents.js.map +1 -1
- package/dist/src/routes/auth.d.ts.map +1 -1
- package/dist/src/routes/auth.js +45 -2
- package/dist/src/routes/auth.js.map +1 -1
- package/dist/src/routes/federation-auth.d.ts.map +1 -1
- package/dist/src/routes/federation-auth.js +3 -1
- package/dist/src/routes/federation-auth.js.map +1 -1
- package/dist/src/routes/federation-peer.d.ts.map +1 -1
- package/dist/src/routes/federation-peer.js +11 -2
- package/dist/src/routes/federation-peer.js.map +1 -1
- package/dist/src/routes/federation-sync.d.ts +1 -0
- package/dist/src/routes/federation-sync.d.ts.map +1 -1
- package/dist/src/routes/federation-sync.js +29 -0
- package/dist/src/routes/federation-sync.js.map +1 -1
- package/dist/src/routes/ghii.d.ts.map +1 -1
- package/dist/src/routes/ghii.js +42 -5
- package/dist/src/routes/ghii.js.map +1 -1
- package/dist/src/routes/knowledge.d.ts.map +1 -1
- package/dist/src/routes/knowledge.js +19 -7
- package/dist/src/routes/knowledge.js.map +1 -1
- package/dist/src/routes/memory.d.ts +9 -0
- package/dist/src/routes/memory.d.ts.map +1 -1
- package/dist/src/routes/memory.js +307 -12
- package/dist/src/routes/memory.js.map +1 -1
- package/dist/src/routes/portal.d.ts.map +1 -1
- package/dist/src/routes/portal.js +1 -0
- package/dist/src/routes/portal.js.map +1 -1
- package/dist/src/routes/prompts.d.ts +1 -0
- package/dist/src/routes/prompts.d.ts.map +1 -1
- package/dist/src/routes/prompts.js +40 -1
- package/dist/src/routes/prompts.js.map +1 -1
- package/dist/src/routes/sharing-groups.d.ts.map +1 -1
- package/dist/src/routes/sharing-groups.js +8 -2
- package/dist/src/routes/sharing-groups.js.map +1 -1
- package/dist/src/routes/wallet.d.ts.map +1 -1
- package/dist/src/routes/wallet.js +2 -0
- package/dist/src/routes/wallet.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +47 -3
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +2 -1
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/core-jobs.d.ts.map +1 -1
- package/dist/src/services/core-jobs.js +3 -2
- package/dist/src/services/core-jobs.js.map +1 -1
- package/dist/src/services/federation-helpers.d.ts +1 -0
- package/dist/src/services/federation-helpers.d.ts.map +1 -1
- package/dist/src/services/federation-helpers.js +1 -1
- package/dist/src/services/federation-helpers.js.map +1 -1
- package/dist/src/services/generator-autopilot.d.ts.map +1 -1
- package/dist/src/services/generator-autopilot.js +125 -26
- package/dist/src/services/generator-autopilot.js.map +1 -1
- package/dist/src/services/generator-prompts/index.d.ts +1 -1
- package/dist/src/services/generator-prompts/index.d.ts.map +1 -1
- package/dist/src/services/generator-prompts/index.js +1 -1
- package/dist/src/services/generator-prompts/index.js.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.d.ts +1 -0
- package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.js +20 -0
- package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
- package/dist/src/services/generator-prompts/validate.d.ts +7 -1
- package/dist/src/services/generator-prompts/validate.d.ts.map +1 -1
- package/dist/src/services/generator-prompts/validate.js +70 -0
- package/dist/src/services/generator-prompts/validate.js.map +1 -1
- package/dist/src/services/onboarding-validator.d.ts +22 -0
- package/dist/src/services/onboarding-validator.d.ts.map +1 -0
- package/dist/src/services/onboarding-validator.js +202 -0
- package/dist/src/services/onboarding-validator.js.map +1 -0
- package/dist/src/services/platform-detector.d.ts +27 -0
- package/dist/src/services/platform-detector.d.ts.map +1 -0
- package/dist/src/services/platform-detector.js +73 -0
- package/dist/src/services/platform-detector.js.map +1 -0
- package/dist/src/services/prompt-defaults.d.ts +1 -0
- package/dist/src/services/prompt-defaults.d.ts.map +1 -1
- package/dist/src/services/prompt-defaults.js +875 -47
- package/dist/src/services/prompt-defaults.js.map +1 -1
- package/dist/src/services/prompt-seeder.js +1 -1
- package/dist/src/services/prompt-seeder.js.map +1 -1
- package/dist/src/services/readiness-scorer.d.ts +34 -0
- package/dist/src/services/readiness-scorer.d.ts.map +1 -0
- package/dist/src/services/readiness-scorer.js +87 -0
- package/dist/src/services/readiness-scorer.js.map +1 -0
- package/dist/src/services/skill-bundle/generator.d.ts +17 -0
- package/dist/src/services/skill-bundle/generator.d.ts.map +1 -0
- package/dist/src/services/skill-bundle/generator.js +387 -0
- package/dist/src/services/skill-bundle/generator.js.map +1 -0
- package/dist/src/services/skill-bundle/generic-adapter.d.ts +16 -0
- package/dist/src/services/skill-bundle/generic-adapter.d.ts.map +1 -0
- package/dist/src/services/skill-bundle/generic-adapter.js +96 -0
- package/dist/src/services/skill-bundle/generic-adapter.js.map +1 -0
- package/dist/src/services/skill-bundle/hermes-adapter.d.ts +25 -0
- package/dist/src/services/skill-bundle/hermes-adapter.d.ts.map +1 -0
- package/dist/src/services/skill-bundle/hermes-adapter.js +302 -0
- package/dist/src/services/skill-bundle/hermes-adapter.js.map +1 -0
- package/dist/src/services/skill-bundle/types.d.ts +54 -0
- package/dist/src/services/skill-bundle/types.d.ts.map +1 -0
- package/dist/src/services/skill-bundle/types.js +8 -0
- package/dist/src/services/skill-bundle/types.js.map +1 -0
- package/dist/src/services/task-stall-detector.d.ts +10 -2
- package/dist/src/services/task-stall-detector.d.ts.map +1 -1
- package/dist/src/services/task-stall-detector.js +34 -5
- package/dist/src/services/task-stall-detector.js.map +1 -1
- package/dist/src/services/webhook-dispatcher.d.ts +71 -0
- package/dist/src/services/webhook-dispatcher.d.ts.map +1 -0
- package/dist/src/services/webhook-dispatcher.js +259 -0
- package/dist/src/services/webhook-dispatcher.js.map +1 -0
- package/dist/src/storage/interface.d.ts +82 -2
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +20 -1
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +254 -3
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +18 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +173 -4
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-directives.js +8 -4
- package/dist/src/storage/providers/sqlite/repos/agent-directives.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent.js +25 -4
- package/dist/src/storage/providers/sqlite/repos/agent.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +73 -1
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/agent-onboarding.repository.d.ts +16 -0
- package/dist/src/storage/repositories/agent-onboarding.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-onboarding.repository.js +8 -0
- package/dist/src/storage/repositories/agent-onboarding.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent-webhook.repository.d.ts +21 -0
- package/dist/src/storage/repositories/agent-webhook.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-webhook.repository.js +2 -0
- package/dist/src/storage/repositories/agent-webhook.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent.repository.d.ts +1 -0
- package/dist/src/storage/repositories/agent.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/index.d.ts +2 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -1
- package/dist/src/utils/env-config.d.ts.map +1 -1
- package/dist/src/utils/env-config.js +7 -1
- package/dist/src/utils/env-config.js.map +1 -1
- package/dist/src/utils/url-validator.d.ts.map +1 -1
- package/dist/src/utils/url-validator.js +3 -0
- package/dist/src/utils/url-validator.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +67 -7
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file agent-webhook.ts
|
|
3
|
+
* @description REST endpoints for agent webhook CRUD (register, get, delete, test, delivery log).
|
|
4
|
+
* Owners can manage webhooks for any of their agents; agents can manage their own.
|
|
5
|
+
* @structure
|
|
6
|
+
* - PUT /v1/agents/:name/webhook -- Register or update webhook
|
|
7
|
+
* - GET /v1/agents/:name/webhook -- Get webhook config and status
|
|
8
|
+
* - DELETE /v1/agents/:name/webhook -- Remove webhook
|
|
9
|
+
* - POST /v1/agents/:name/webhook/test -- Send a test event to the webhook
|
|
10
|
+
* - GET /v1/agents/:name/webhook/log -- Get delivery log
|
|
11
|
+
* @version-history
|
|
12
|
+
* v1.0.0 -- 2026-05-23 -- Initial creation for Agent Dashboard webhook management
|
|
13
|
+
*/
|
|
14
|
+
import { Router } from 'express';
|
|
15
|
+
import { randomBytes, randomUUID, createHmac } from 'node:crypto';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { success, error } from '../middleware/envelope.js';
|
|
18
|
+
import { requireAuth } from '../auth/middleware.js';
|
|
19
|
+
import { buildGAII } from '../utils/gaii.js';
|
|
20
|
+
import { validateOutboundUrl } from '../utils/url-validator.js';
|
|
21
|
+
import { emitChange } from '../services/event-bus.js';
|
|
22
|
+
/* ── Zod validation schema ── */
|
|
23
|
+
const WebhookPutSchema = z.object({
|
|
24
|
+
url: z.string().url().max(2048),
|
|
25
|
+
secret: z.string().min(16).max(256).optional(),
|
|
26
|
+
});
|
|
27
|
+
export function agentWebhookRouter(config, storage) {
|
|
28
|
+
const router = Router();
|
|
29
|
+
/** Build GAII for the named agent under the authenticated owner */
|
|
30
|
+
function resolveAgentGaii(req, agentName) {
|
|
31
|
+
const owner = req.auth.owner;
|
|
32
|
+
return buildGAII(agentName, owner, config.nodeId);
|
|
33
|
+
}
|
|
34
|
+
/** Check if current session can access an agent's webhook */
|
|
35
|
+
function canAccessAgent(req, agentName) {
|
|
36
|
+
const roles = req.auth.roles;
|
|
37
|
+
const isOwnerSession = roles.includes('owner') && !roles.includes('agent');
|
|
38
|
+
if (isOwnerSession)
|
|
39
|
+
return true;
|
|
40
|
+
if (roles.includes('agent')) {
|
|
41
|
+
return req.auth.sub === resolveAgentGaii(req, agentName);
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/* ── PUT /v1/agents/:name/webhook -- Register or update webhook ── */
|
|
46
|
+
router.put('/v1/agents/:name/webhook', requireAuth(), async (req, res) => {
|
|
47
|
+
const agentName = req.params.name;
|
|
48
|
+
if (!canAccessAgent(req, agentName)) {
|
|
49
|
+
res.status(403).json(error(config.nodeId, 'FORBIDDEN', 'Access denied'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const agentGaii = resolveAgentGaii(req, agentName);
|
|
53
|
+
// Verify agent exists
|
|
54
|
+
const agent = await storage.getAgent(agentGaii);
|
|
55
|
+
if (!agent) {
|
|
56
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Agent '${agentName}' not found`));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// Validate body
|
|
60
|
+
const parsed = WebhookPutSchema.safeParse(req.body);
|
|
61
|
+
if (!parsed.success) {
|
|
62
|
+
res.status(400).json(error(config.nodeId, 'INVALID_INPUT', parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ')));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const { url, secret } = parsed.data;
|
|
66
|
+
// SSRF check
|
|
67
|
+
const urlCheck = await validateOutboundUrl(url);
|
|
68
|
+
if (!urlCheck.valid) {
|
|
69
|
+
res.status(400).json(error(config.nodeId, 'INVALID_URL', urlCheck.reason ?? 'URL is not allowed'));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Generate a random secret if not provided
|
|
73
|
+
const webhookSecret = secret ?? randomBytes(32).toString('hex');
|
|
74
|
+
await storage.updateAgent(agentGaii, {
|
|
75
|
+
webhookUrl: url,
|
|
76
|
+
webhookSecret,
|
|
77
|
+
webhookEnabled: true,
|
|
78
|
+
webhookFailCount: 0,
|
|
79
|
+
});
|
|
80
|
+
emitChange('agents');
|
|
81
|
+
res.json(success(config.nodeId, {
|
|
82
|
+
url,
|
|
83
|
+
secret: webhookSecret,
|
|
84
|
+
enabled: true,
|
|
85
|
+
}));
|
|
86
|
+
});
|
|
87
|
+
/* ── GET /v1/agents/:name/webhook -- Get webhook config and status ── */
|
|
88
|
+
router.get('/v1/agents/:name/webhook', requireAuth(), async (req, res) => {
|
|
89
|
+
const agentName = req.params.name;
|
|
90
|
+
if (!canAccessAgent(req, agentName)) {
|
|
91
|
+
res.status(403).json(error(config.nodeId, 'FORBIDDEN', 'Access denied'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const agentGaii = resolveAgentGaii(req, agentName);
|
|
95
|
+
const agent = await storage.getAgent(agentGaii);
|
|
96
|
+
if (!agent) {
|
|
97
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Agent '${agentName}' not found`));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!agent.webhookUrl) {
|
|
101
|
+
res.json(success(config.nodeId, {
|
|
102
|
+
configured: false,
|
|
103
|
+
}));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
res.json(success(config.nodeId, {
|
|
107
|
+
configured: true,
|
|
108
|
+
url: agent.webhookUrl,
|
|
109
|
+
enabled: agent.webhookEnabled ?? false,
|
|
110
|
+
last_success: agent.webhookLastSuccess ?? null,
|
|
111
|
+
last_failure: agent.webhookLastFailure ?? null,
|
|
112
|
+
fail_count: agent.webhookFailCount ?? 0,
|
|
113
|
+
}));
|
|
114
|
+
});
|
|
115
|
+
/* ── DELETE /v1/agents/:name/webhook -- Remove webhook ── */
|
|
116
|
+
router.delete('/v1/agents/:name/webhook', requireAuth(), async (req, res) => {
|
|
117
|
+
const agentName = req.params.name;
|
|
118
|
+
if (!canAccessAgent(req, agentName)) {
|
|
119
|
+
res.status(403).json(error(config.nodeId, 'FORBIDDEN', 'Access denied'));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const agentGaii = resolveAgentGaii(req, agentName);
|
|
123
|
+
const agent = await storage.getAgent(agentGaii);
|
|
124
|
+
if (!agent) {
|
|
125
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Agent '${agentName}' not found`));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Use null (cast through any) to clear nullable fields in MongoDB/Prisma.
|
|
129
|
+
// Prisma treats undefined as "don't update" but null as "set to null".
|
|
130
|
+
await storage.updateAgent(agentGaii, {
|
|
131
|
+
webhookUrl: null,
|
|
132
|
+
webhookSecret: null,
|
|
133
|
+
webhookEnabled: false,
|
|
134
|
+
webhookFailCount: 0,
|
|
135
|
+
webhookLastSuccess: null,
|
|
136
|
+
webhookLastFailure: null,
|
|
137
|
+
});
|
|
138
|
+
emitChange('agents');
|
|
139
|
+
res.json(success(config.nodeId, { removed: true }));
|
|
140
|
+
});
|
|
141
|
+
/* ── POST /v1/agents/:name/webhook/test -- Send test event ── */
|
|
142
|
+
router.post('/v1/agents/:name/webhook/test', requireAuth(), async (req, res) => {
|
|
143
|
+
const agentName = req.params.name;
|
|
144
|
+
if (!canAccessAgent(req, agentName)) {
|
|
145
|
+
res.status(403).json(error(config.nodeId, 'FORBIDDEN', 'Access denied'));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const agentGaii = resolveAgentGaii(req, agentName);
|
|
149
|
+
const agent = await storage.getAgent(agentGaii);
|
|
150
|
+
if (!agent) {
|
|
151
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Agent '${agentName}' not found`));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!agent.webhookUrl || !agent.webhookSecret) {
|
|
155
|
+
res.status(400).json(error(config.nodeId, 'NO_WEBHOOK', 'No webhook configured for this agent'));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Build test payload
|
|
159
|
+
const payload = {
|
|
160
|
+
event: 'webhook.test',
|
|
161
|
+
agent: agentGaii,
|
|
162
|
+
node: config.nodeId,
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
data: { message: 'Webhook test delivery' },
|
|
165
|
+
};
|
|
166
|
+
const body = JSON.stringify(payload);
|
|
167
|
+
const signature = createHmac('sha256', agent.webhookSecret).update(body).digest('hex');
|
|
168
|
+
const startMs = Date.now();
|
|
169
|
+
let httpStatus;
|
|
170
|
+
let deliveryError;
|
|
171
|
+
try {
|
|
172
|
+
const resp = await fetch(agent.webhookUrl, {
|
|
173
|
+
method: 'POST',
|
|
174
|
+
headers: {
|
|
175
|
+
'Content-Type': 'application/json',
|
|
176
|
+
'X-AIMEAT-Signature': `sha256=${signature}`,
|
|
177
|
+
'X-AIMEAT-Event': 'webhook.test',
|
|
178
|
+
},
|
|
179
|
+
body,
|
|
180
|
+
signal: AbortSignal.timeout(10_000),
|
|
181
|
+
});
|
|
182
|
+
httpStatus = resp.status;
|
|
183
|
+
if (!resp.ok) {
|
|
184
|
+
deliveryError = `HTTP ${resp.status}`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
deliveryError = err instanceof Error ? err.message : 'Delivery failed';
|
|
189
|
+
}
|
|
190
|
+
const latencyMs = Date.now() - startMs;
|
|
191
|
+
const deliveryStatus = deliveryError ? 'failed' : 'success';
|
|
192
|
+
// Log the delivery
|
|
193
|
+
await storage.appendDeliveryLog({
|
|
194
|
+
id: randomUUID(),
|
|
195
|
+
agentGaii,
|
|
196
|
+
event: 'webhook.test',
|
|
197
|
+
payload,
|
|
198
|
+
status: deliveryStatus,
|
|
199
|
+
httpStatus,
|
|
200
|
+
errorMessage: deliveryError,
|
|
201
|
+
attemptCount: 1,
|
|
202
|
+
latencyMs,
|
|
203
|
+
createdAt: new Date().toISOString(),
|
|
204
|
+
});
|
|
205
|
+
// Update last success/failure on the agent
|
|
206
|
+
const now = new Date().toISOString();
|
|
207
|
+
if (deliveryStatus === 'success') {
|
|
208
|
+
await storage.updateAgent(agentGaii, { webhookLastSuccess: now });
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
await storage.updateAgent(agentGaii, {
|
|
212
|
+
webhookLastFailure: now,
|
|
213
|
+
webhookFailCount: (agent.webhookFailCount ?? 0) + 1,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
res.json(success(config.nodeId, {
|
|
217
|
+
delivered: deliveryStatus === 'success',
|
|
218
|
+
status: deliveryStatus,
|
|
219
|
+
http_status: httpStatus ?? null,
|
|
220
|
+
error: deliveryError ?? null,
|
|
221
|
+
latency_ms: latencyMs,
|
|
222
|
+
}));
|
|
223
|
+
});
|
|
224
|
+
/* ── GET /v1/agents/:name/webhook/log -- Delivery log ── */
|
|
225
|
+
router.get('/v1/agents/:name/webhook/log', requireAuth(), async (req, res) => {
|
|
226
|
+
const agentName = req.params.name;
|
|
227
|
+
if (!canAccessAgent(req, agentName)) {
|
|
228
|
+
res.status(403).json(error(config.nodeId, 'FORBIDDEN', 'Access denied'));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const agentGaii = resolveAgentGaii(req, agentName);
|
|
232
|
+
const agent = await storage.getAgent(agentGaii);
|
|
233
|
+
if (!agent) {
|
|
234
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Agent '${agentName}' not found`));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const limit = Math.min(Math.max(parseInt(req.query.limit) || 50, 1), 200);
|
|
238
|
+
const deliveries = await storage.listDeliveryLog(agentGaii, limit);
|
|
239
|
+
res.json(success(config.nodeId, { deliveries }));
|
|
240
|
+
});
|
|
241
|
+
return router;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=agent-webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-webhook.js","sourceRoot":"","sources":["../../../src/routes/agent-webhook.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAOjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAOjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAkBvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CA6wC3E"}
|
|
@@ -15,6 +15,8 @@ import { AgentRegistrationSchema, validateBody } from '../models/schemas.js';
|
|
|
15
15
|
import { verifyJWT, issueJWT, generateSessionId } from '../auth/jwt.js';
|
|
16
16
|
import { rateLimit } from '../middleware/rate-limit.js';
|
|
17
17
|
import { emitChange } from '../services/event-bus.js';
|
|
18
|
+
import { createDefaultSteps } from '../models/agent-onboarding-schemas.js';
|
|
19
|
+
import { detectPlatform } from '../services/platform-detector.js';
|
|
18
20
|
/** Device authorization code expires after 30 minutes */
|
|
19
21
|
const DEVICE_AUTH_EXPIRY_MS = 1_800_000;
|
|
20
22
|
export function agentsRouter(config, storage) {
|
|
@@ -78,8 +80,9 @@ export function agentsRouter(config, storage) {
|
|
|
78
80
|
verification_uri_complete: verificationUriComplete,
|
|
79
81
|
expires_in: 1800,
|
|
80
82
|
interval: 5,
|
|
83
|
+
user_instructions: `Tell your owner to approve this request. They can find it in their AIMEAT profile under Agents (${baseUrl}/v1/profile -> Agents tab). The verification code is: ${userCode}. Once approved, poll POST /v1/agents/device-token with { "device_code": "${deviceCode}", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" } every 5 seconds until you receive your token.`,
|
|
81
84
|
}, [
|
|
82
|
-
{ description: '
|
|
85
|
+
{ description: 'Owner approves in AIMEAT profile -> Agents tab', method: 'GET', url: `${baseUrl}/v1/profile` },
|
|
83
86
|
{ description: 'Poll for authorization result', method: 'POST', url: '/v1/agents/device-token' },
|
|
84
87
|
]));
|
|
85
88
|
emitChange('agents');
|
|
@@ -141,17 +144,41 @@ export function agentsRouter(config, storage) {
|
|
|
141
144
|
// Return credentials and immediately clear them
|
|
142
145
|
const creds = request.agentCredentials;
|
|
143
146
|
await storage.updateDeviceAuth(device_code, { agentCredentials: null });
|
|
147
|
+
const baseUrl = config.baseUrl;
|
|
148
|
+
const agentName = request.agentName;
|
|
144
149
|
res.json({
|
|
150
|
+
access_token: creds.token,
|
|
151
|
+
token: creds.token,
|
|
152
|
+
token_type: 'Bearer',
|
|
145
153
|
gaii: creds.gaii,
|
|
146
|
-
name:
|
|
154
|
+
name: agentName,
|
|
147
155
|
owner: request.ownerName,
|
|
148
|
-
token: creds.token,
|
|
149
156
|
expires_at: creds.expires_at,
|
|
150
157
|
privateKey: creds.privateKey,
|
|
151
158
|
publicKey: creds.publicKey,
|
|
152
159
|
scopes: request.scopes,
|
|
153
160
|
morselBalance: 0,
|
|
154
|
-
|
|
161
|
+
next_steps: {
|
|
162
|
+
message: 'Authentication successful. Your MANDATORY first action: download and read your skill bundle.',
|
|
163
|
+
step_1_skill_bundle: {
|
|
164
|
+
action: 'Download your skill bundle ZIP, extract it, and read SKILL.md. It contains your personalized operating instructions, directives, rules, and complete API reference.',
|
|
165
|
+
method: 'GET',
|
|
166
|
+
url: `${baseUrl}/v1/agents/${agentName}/skill-bundle`,
|
|
167
|
+
auth: 'Authorization: Bearer <your token from above>',
|
|
168
|
+
},
|
|
169
|
+
step_2_system_prompt: {
|
|
170
|
+
action: 'Read your system prompt for additional operating context.',
|
|
171
|
+
method: 'GET',
|
|
172
|
+
url: `${baseUrl}/v1/prompts/tier1`,
|
|
173
|
+
auth: 'Authorization: Bearer <your token from above>',
|
|
174
|
+
},
|
|
175
|
+
step_3_hello_integration: {
|
|
176
|
+
action: 'Check your onboarding status and complete the Hello Integration steps. A test task is already created for you. GET this endpoint auto-validates steps and auto-starts the test task after you propose todos -- always re-check here instead of trying POST /start.',
|
|
177
|
+
method: 'GET',
|
|
178
|
+
url: `${baseUrl}/v1/agents/${agentName}/onboarding`,
|
|
179
|
+
auth: 'Authorization: Bearer <your token from above>',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
155
182
|
});
|
|
156
183
|
return;
|
|
157
184
|
}
|
|
@@ -310,6 +337,150 @@ export function agentsRouter(config, storage) {
|
|
|
310
337
|
existing_agent: !!existing,
|
|
311
338
|
}));
|
|
312
339
|
emitChange('agents');
|
|
340
|
+
// ── Auto-start Hello Integration onboarding ──
|
|
341
|
+
const onboardingSteps = createDefaultSteps();
|
|
342
|
+
onboardingSteps[0].status = 'passed';
|
|
343
|
+
onboardingSteps[0].validatedAt = now;
|
|
344
|
+
onboardingSteps[0].validationMethod = 'automatic';
|
|
345
|
+
onboardingSteps[0].details = { createdAt: existing?.createdAt ?? now };
|
|
346
|
+
const detectedPlatform = detectPlatform(req.headers['user-agent']);
|
|
347
|
+
if (detectedPlatform) {
|
|
348
|
+
onboardingSteps[1].status = 'passed';
|
|
349
|
+
onboardingSteps[1].validatedAt = now;
|
|
350
|
+
onboardingSteps[1].validationMethod = 'automatic';
|
|
351
|
+
onboardingSteps[1].details = { platform: detectedPlatform.id, version: detectedPlatform.version };
|
|
352
|
+
await storage.updateAgent(gaii, {
|
|
353
|
+
platform: detectedPlatform.id,
|
|
354
|
+
platformVersion: detectedPlatform.version,
|
|
355
|
+
platformDetectedBy: detectedPlatform.detectedBy,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
const existingOnboarding = await storage.getOnboarding(gaii);
|
|
359
|
+
if (!existingOnboarding) {
|
|
360
|
+
const testTaskId = randomUUID();
|
|
361
|
+
await storage.createAgentTask({
|
|
362
|
+
id: testTaskId,
|
|
363
|
+
agentGaii: gaii,
|
|
364
|
+
ownerGaii: `${ownerPayload.owner}@${config.nodeId}`,
|
|
365
|
+
title: 'Onboarding verification',
|
|
366
|
+
description: 'This is a test task created during Hello Integration. Propose todos, get approval, execute, and complete.',
|
|
367
|
+
status: 'queued',
|
|
368
|
+
scope: [],
|
|
369
|
+
rules: [],
|
|
370
|
+
todos: [],
|
|
371
|
+
verification: { userExpects: 'Agent completes the onboarding test task successfully', technicalChecks: [] },
|
|
372
|
+
createdAt: now,
|
|
373
|
+
updatedAt: now,
|
|
374
|
+
});
|
|
375
|
+
onboardingSteps[8].details = { testTaskId };
|
|
376
|
+
await storage.createOnboarding({
|
|
377
|
+
agentGaii: gaii,
|
|
378
|
+
status: 'in_progress',
|
|
379
|
+
startedAt: now,
|
|
380
|
+
steps: onboardingSteps,
|
|
381
|
+
detectedPlatform: detectedPlatform?.id,
|
|
382
|
+
});
|
|
383
|
+
emitChange('agent-onboarding');
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
// POST /v1/agents/connect — register an agent via connectivity key (no auth required)
|
|
387
|
+
router.post('/v1/agents/connect', async (req, res) => {
|
|
388
|
+
const { connectivity_key, agent_name, display_name } = req.body ?? {};
|
|
389
|
+
if (!connectivity_key) {
|
|
390
|
+
res.status(400).json(error(config.nodeId, 'INVALID_INPUT', 'connectivity_key is required'));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const otk = await storage.consumeOtk(connectivity_key, config.otkGraceMs);
|
|
394
|
+
if (!otk || otk.action !== 'register_agent') {
|
|
395
|
+
res.status(404).json(error(config.nodeId, 'INVALID_KEY', 'Connectivity key not found, already used, or invalid'));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const owner = otk.params.owner;
|
|
399
|
+
const finalName = agent_name ?? otk.params.agent_name;
|
|
400
|
+
if (!finalName) {
|
|
401
|
+
res.status(400).json(error(config.nodeId, 'INVALID_INPUT', 'agent_name is required (either in the connectivity key or in the request body)'));
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
const nameError = validateAgentName(finalName);
|
|
405
|
+
if (nameError) {
|
|
406
|
+
res.status(400).json(error(config.nodeId, 'INVALID_INPUT', nameError));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
const ownerRecord = await storage.getOwner(owner);
|
|
410
|
+
if (!ownerRecord) {
|
|
411
|
+
res.status(404).json(error(config.nodeId, 'NOT_FOUND', `Owner "${owner}" not found`));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const gaii = buildGAII(finalName, owner, config.nodeId);
|
|
415
|
+
const existing = await storage.getAgent(gaii);
|
|
416
|
+
if (existing) {
|
|
417
|
+
res.status(409).json(error(config.nodeId, 'NAME_TAKEN', `Agent "${finalName}" already exists under owner "${owner}"`));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const keyPair = await generateKeyPair();
|
|
421
|
+
const now = new Date().toISOString();
|
|
422
|
+
const description = otk.params.description ?? undefined;
|
|
423
|
+
const requestedScopes = config.defaultAgentScopes;
|
|
424
|
+
await storage.createAgent({
|
|
425
|
+
name: finalName,
|
|
426
|
+
owner,
|
|
427
|
+
gaii,
|
|
428
|
+
displayName: display_name ?? finalName,
|
|
429
|
+
description,
|
|
430
|
+
capabilities: [],
|
|
431
|
+
defaultScopes: requestedScopes,
|
|
432
|
+
publicKey: keyPair.publicKey,
|
|
433
|
+
trustScore: 50,
|
|
434
|
+
morselBalance: 0,
|
|
435
|
+
createdAt: now,
|
|
436
|
+
lastSeen: now,
|
|
437
|
+
});
|
|
438
|
+
fireHook(config, storage, 'post_agent_registration', { gaii, owner });
|
|
439
|
+
// Auto-start Hello Integration onboarding
|
|
440
|
+
const connectSteps = createDefaultSteps();
|
|
441
|
+
connectSteps[0].status = 'passed';
|
|
442
|
+
connectSteps[0].validatedAt = now;
|
|
443
|
+
connectSteps[0].validationMethod = 'automatic';
|
|
444
|
+
connectSteps[0].details = { createdAt: now };
|
|
445
|
+
const connectDetected = detectPlatform(req.headers['user-agent']);
|
|
446
|
+
if (connectDetected) {
|
|
447
|
+
connectSteps[1].status = 'passed';
|
|
448
|
+
connectSteps[1].validatedAt = now;
|
|
449
|
+
connectSteps[1].validationMethod = 'automatic';
|
|
450
|
+
connectSteps[1].details = { platform: connectDetected.id, version: connectDetected.version };
|
|
451
|
+
await storage.updateAgent(gaii, {
|
|
452
|
+
platform: connectDetected.id,
|
|
453
|
+
platformVersion: connectDetected.version,
|
|
454
|
+
platformDetectedBy: connectDetected.detectedBy,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
await storage.createOnboarding({
|
|
458
|
+
agentGaii: gaii,
|
|
459
|
+
status: 'in_progress',
|
|
460
|
+
startedAt: now,
|
|
461
|
+
steps: connectSteps,
|
|
462
|
+
detectedPlatform: connectDetected?.id,
|
|
463
|
+
});
|
|
464
|
+
emitChange('agent-onboarding');
|
|
465
|
+
res.set('Cache-Control', 'no-store');
|
|
466
|
+
res.set('Pragma', 'no-cache');
|
|
467
|
+
res.status(201).json(success(config.nodeId, {
|
|
468
|
+
agent: {
|
|
469
|
+
gaii,
|
|
470
|
+
display_name: display_name ?? finalName,
|
|
471
|
+
description,
|
|
472
|
+
scopes: requestedScopes,
|
|
473
|
+
},
|
|
474
|
+
private_key: keyPair.privateKey,
|
|
475
|
+
public_key: keyPair.publicKey,
|
|
476
|
+
}, [
|
|
477
|
+
{
|
|
478
|
+
description: 'Authenticate as this agent to get a JWT',
|
|
479
|
+
method: 'POST',
|
|
480
|
+
url: '/v1/auth/token',
|
|
481
|
+
},
|
|
482
|
+
]));
|
|
483
|
+
emitChange('agents');
|
|
313
484
|
});
|
|
314
485
|
// POST /v1/agents — register a new agent (requires owner JWT, local session only)
|
|
315
486
|
router.post('/v1/agents', requireAuth(), requireLocalSession(), requireRole('owner'), validateBody(AgentRegistrationSchema, config.nodeId), async (req, res) => {
|
|
@@ -416,6 +587,32 @@ export function agentsRouter(config, storage) {
|
|
|
416
587
|
},
|
|
417
588
|
]));
|
|
418
589
|
emitChange('agents');
|
|
590
|
+
// ── Auto-start Hello Integration onboarding (direct registration path) ──
|
|
591
|
+
const regOnboardingSteps = createDefaultSteps();
|
|
592
|
+
regOnboardingSteps[0].status = 'passed';
|
|
593
|
+
regOnboardingSteps[0].validatedAt = now;
|
|
594
|
+
regOnboardingSteps[0].validationMethod = 'automatic';
|
|
595
|
+
regOnboardingSteps[0].details = { createdAt: now };
|
|
596
|
+
const regDetected = detectPlatform(req.headers['user-agent']);
|
|
597
|
+
if (regDetected) {
|
|
598
|
+
regOnboardingSteps[1].status = 'passed';
|
|
599
|
+
regOnboardingSteps[1].validatedAt = now;
|
|
600
|
+
regOnboardingSteps[1].validationMethod = 'automatic';
|
|
601
|
+
regOnboardingSteps[1].details = { platform: regDetected.id, version: regDetected.version };
|
|
602
|
+
await storage.updateAgent(gaii, {
|
|
603
|
+
platform: regDetected.id,
|
|
604
|
+
platformVersion: regDetected.version,
|
|
605
|
+
platformDetectedBy: regDetected.detectedBy,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
await storage.createOnboarding({
|
|
609
|
+
agentGaii: gaii,
|
|
610
|
+
status: 'in_progress',
|
|
611
|
+
startedAt: now,
|
|
612
|
+
steps: regOnboardingSteps,
|
|
613
|
+
detectedPlatform: regDetected?.id,
|
|
614
|
+
});
|
|
615
|
+
emitChange('agent-onboarding');
|
|
419
616
|
});
|
|
420
617
|
// GET /v1/agents/device-authorize/pending — list pending device auth requests for the logged-in owner
|
|
421
618
|
router.get('/v1/agents/device-authorize/pending', requireAuth(), requireRole('owner'), async (req, res) => {
|
|
@@ -522,6 +719,8 @@ export function agentsRouter(config, storage) {
|
|
|
522
719
|
display_name: a.displayName,
|
|
523
720
|
description: a.description,
|
|
524
721
|
capabilities: a.capabilities,
|
|
722
|
+
technical_capabilities: a.technicalCapabilities,
|
|
723
|
+
domain_capabilities: a.domainCapabilities,
|
|
525
724
|
default_scopes: a.defaultScopes ?? ['*'],
|
|
526
725
|
trust_score: a.trustScore,
|
|
527
726
|
morsel_balance: a.morselBalance,
|