neoagent 2.4.2-beta.4 → 2.4.2-beta.6
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/.env.example +0 -9
- package/README.md +48 -18
- package/docs/configuration.md +0 -10
- package/flutter_app/lib/main.dart +0 -1
- package/flutter_app/lib/main_controller.dart +0 -160
- package/flutter_app/lib/main_models.dart +28 -0
- package/flutter_app/lib/main_operations.dart +17 -1
- package/flutter_app/lib/main_runtime.dart +0 -22
- package/flutter_app/lib/main_settings.dart +9 -0
- package/flutter_app/lib/main_shared.dart +0 -161
- package/flutter_app/lib/src/backend_client.dart +8 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -2
- package/flutter_app/pubspec.lock +0 -8
- package/flutter_app/pubspec.yaml +0 -1
- package/package.json +1 -1
- package/server/admin/admin.css +23 -0
- package/server/admin/admin.js +17 -19
- package/server/admin/analytics.js +254 -48
- package/server/admin/index.html +9 -2
- package/server/db/database.js +112 -0
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/NOTICES +0 -183
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +77723 -78469
- package/server/routes/admin.js +73 -24
- package/server/routes/mcp.js +9 -0
- package/server/routes/memory.js +35 -2
- package/server/routes/runtime.js +5 -2
- package/server/routes/settings.js +34 -1
- package/server/routes/skills.js +42 -0
- package/server/routes/task_webhooks.js +65 -0
- package/server/services/ai/engine.js +525 -24
- package/server/services/ai/learning.js +56 -8
- package/server/services/ai/models.js +4 -4
- package/server/services/ai/providers/anthropic.js +40 -8
- package/server/services/ai/providers/google.js +10 -0
- package/server/services/ai/providers/openai.js +3 -15
- package/server/services/ai/providers/openaiCompatible.js +11 -0
- package/server/services/ai/repetitionGuard.js +60 -0
- package/server/services/ai/systemPrompt.js +52 -16
- package/server/services/ai/taskAnalysis.js +15 -6
- package/server/services/ai/toolEvidence.js +3 -1
- package/server/services/ai/toolRunner.js +43 -1
- package/server/services/ai/toolSelector.js +92 -46
- package/server/services/ai/tools.js +89 -9
- package/server/services/ai/usage.js +114 -0
- package/server/services/manager.js +34 -0
- package/server/services/memory/manager.js +170 -4
- package/server/services/security/capability_audit.js +107 -0
- package/server/services/tasks/adapters/index.js +1 -0
- package/server/services/tasks/adapters/webhook.js +14 -0
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/tasks/webhooks.js +146 -0
- package/server/services/workspace/code_navigation.js +194 -0
- package/server/services/workspace/structured_data.js +93 -0
- package/server/utils/cloud-security.js +24 -2
- package/flutter_app/lib/src/analytics_service.dart +0 -294
- package/server/config/analytics.js +0 -30
package/server/routes/admin.js
CHANGED
|
@@ -416,20 +416,69 @@ router.delete('/api/settings/apikey', requireAdminAuth, settingsLimiter, (req, r
|
|
|
416
416
|
|
|
417
417
|
router.get('/api/analytics', requireAdminAuth, (req, res) => {
|
|
418
418
|
const db = require('../db/database');
|
|
419
|
+
const range = Math.min(Math.max(parseInt(req.query.range) || 30, 1), 365);
|
|
419
420
|
const now = new Date().toISOString();
|
|
420
421
|
const dayAgo = new Date(Date.now() - 86_400_000).toISOString();
|
|
421
422
|
const weekAgo = new Date(Date.now() - 7 * 86_400_000).toISOString();
|
|
423
|
+
const rangeAgo = new Date(Date.now() - range * 86_400_000).toISOString();
|
|
422
424
|
try {
|
|
425
|
+
const totalRunsRow = db.prepare('SELECT COUNT(*) AS n, COALESCE(SUM(total_tokens),0) AS t FROM agent_runs').get();
|
|
426
|
+
const successRow = db.prepare("SELECT COUNT(*) AS n FROM agent_runs WHERE status = 'completed'").get();
|
|
423
427
|
const stats = {
|
|
424
|
-
totalUsers:
|
|
425
|
-
activeToday:
|
|
426
|
-
newThisWeek:
|
|
427
|
-
totalRuns:
|
|
428
|
-
runsToday:
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
428
|
+
totalUsers: db.prepare('SELECT COUNT(*) AS n FROM users').get().n,
|
|
429
|
+
activeToday: db.prepare('SELECT COUNT(*) AS n FROM users WHERE last_login > ?').get(dayAgo).n,
|
|
430
|
+
newThisWeek: db.prepare('SELECT COUNT(*) AS n FROM users WHERE created_at > ?').get(weekAgo).n,
|
|
431
|
+
totalRuns: totalRunsRow.n,
|
|
432
|
+
runsToday: db.prepare('SELECT COUNT(*) AS n FROM agent_runs WHERE created_at > ?').get(dayAgo).n,
|
|
433
|
+
runsThisWeek: db.prepare('SELECT COUNT(*) AS n FROM agent_runs WHERE created_at > ?').get(weekAgo).n,
|
|
434
|
+
totalTokens: totalRunsRow.t,
|
|
435
|
+
tokensToday: db.prepare("SELECT COALESCE(SUM(total_tokens),0) AS n FROM agent_runs WHERE created_at > ?").get(dayAgo).n,
|
|
436
|
+
avgTokensPerRun: totalRunsRow.n > 0 ? Math.round(totalRunsRow.t / totalRunsRow.n) : 0,
|
|
437
|
+
successRate: totalRunsRow.n > 0 ? Math.round((successRow.n / totalRunsRow.n) * 100) : 0,
|
|
438
|
+
activeSessions: db.prepare('SELECT COUNT(*) AS n FROM user_sessions WHERE revoked_at IS NULL AND expires_at > ?').get(now).n,
|
|
439
|
+
totalStorage: db.prepare('SELECT COALESCE(SUM(byte_size),0) AS n FROM artifacts').get().n,
|
|
432
440
|
};
|
|
441
|
+
|
|
442
|
+
// Time-series: runs + tokens per day for selected range
|
|
443
|
+
const runsByDay = db.prepare(`
|
|
444
|
+
SELECT date(created_at) AS date,
|
|
445
|
+
COUNT(*) AS runs,
|
|
446
|
+
COALESCE(SUM(total_tokens), 0) AS tokens
|
|
447
|
+
FROM agent_runs
|
|
448
|
+
WHERE created_at > ?
|
|
449
|
+
GROUP BY date(created_at)
|
|
450
|
+
ORDER BY date
|
|
451
|
+
`).all(rangeAgo);
|
|
452
|
+
|
|
453
|
+
// New users per day for selected range
|
|
454
|
+
const usersByDay = db.prepare(`
|
|
455
|
+
SELECT date(created_at) AS date, COUNT(*) AS newUsers
|
|
456
|
+
FROM users
|
|
457
|
+
WHERE created_at > ?
|
|
458
|
+
GROUP BY date(created_at)
|
|
459
|
+
ORDER BY date
|
|
460
|
+
`).all(rangeAgo);
|
|
461
|
+
|
|
462
|
+
// Model breakdown (top 10 by runs)
|
|
463
|
+
const modelBreakdown = db.prepare(`
|
|
464
|
+
SELECT COALESCE(model, 'unknown') AS model,
|
|
465
|
+
COUNT(*) AS runs,
|
|
466
|
+
COALESCE(SUM(total_tokens), 0) AS tokens
|
|
467
|
+
FROM agent_runs
|
|
468
|
+
WHERE created_at > ?
|
|
469
|
+
GROUP BY model
|
|
470
|
+
ORDER BY runs DESC
|
|
471
|
+
LIMIT 10
|
|
472
|
+
`).all(rangeAgo);
|
|
473
|
+
|
|
474
|
+
// Run status breakdown
|
|
475
|
+
const statusBreakdown = db.prepare(`
|
|
476
|
+
SELECT status, COUNT(*) AS count
|
|
477
|
+
FROM agent_runs
|
|
478
|
+
GROUP BY status
|
|
479
|
+
ORDER BY count DESC
|
|
480
|
+
`).all();
|
|
481
|
+
|
|
433
482
|
const topUsers = db.prepare(`
|
|
434
483
|
SELECT u.id, u.username, u.display_name,
|
|
435
484
|
COALESCE(r.runs, 0) AS runs,
|
|
@@ -437,25 +486,25 @@ router.get('/api/analytics', requireAdminAuth, (req, res) => {
|
|
|
437
486
|
COALESCE(a.storage, 0) AS storage
|
|
438
487
|
FROM users u
|
|
439
488
|
LEFT JOIN (
|
|
440
|
-
SELECT user_id,
|
|
441
|
-
COUNT(*) AS runs,
|
|
442
|
-
COALESCE(SUM(total_tokens),0) AS tokens
|
|
489
|
+
SELECT user_id, COUNT(*) AS runs, COALESCE(SUM(total_tokens),0) AS tokens
|
|
443
490
|
FROM agent_runs GROUP BY user_id
|
|
444
491
|
) r ON r.user_id = u.id
|
|
445
492
|
LEFT JOIN (
|
|
446
493
|
SELECT user_id, COALESCE(SUM(byte_size),0) AS storage
|
|
447
494
|
FROM artifacts GROUP BY user_id
|
|
448
495
|
) a ON a.user_id = u.id
|
|
449
|
-
ORDER BY
|
|
496
|
+
ORDER BY tokens DESC LIMIT 10
|
|
450
497
|
`).all();
|
|
498
|
+
|
|
451
499
|
const recentRuns = db.prepare(`
|
|
452
|
-
SELECT r.id, u.username, r.title, r.status, r.total_tokens,
|
|
500
|
+
SELECT r.id, u.username, r.title, r.status, r.model, r.total_tokens,
|
|
453
501
|
r.created_at, r.completed_at
|
|
454
502
|
FROM agent_runs r
|
|
455
503
|
JOIN users u ON u.id = r.user_id
|
|
456
|
-
ORDER BY r.created_at DESC LIMIT
|
|
504
|
+
ORDER BY r.created_at DESC LIMIT 25
|
|
457
505
|
`).all();
|
|
458
|
-
|
|
506
|
+
|
|
507
|
+
res.json({ stats, runsByDay, usersByDay, modelBreakdown, statusBreakdown, topUsers, recentRuns });
|
|
459
508
|
} catch (err) {
|
|
460
509
|
res.status(500).json({ error: String(err.message || err) });
|
|
461
510
|
}
|
|
@@ -645,20 +694,20 @@ router.get('/api/models', requireAdminAuth, async (req, res) => {
|
|
|
645
694
|
const { getSupportedModels } = require('../services/ai/models');
|
|
646
695
|
try {
|
|
647
696
|
const models = await getSupportedModels(null, null);
|
|
648
|
-
const
|
|
649
|
-
const
|
|
650
|
-
res.json({ models,
|
|
697
|
+
const disabledStr = process.env.NEOAGENT_DISABLED_MODELS || '';
|
|
698
|
+
const disabledModels = disabledStr ? disabledStr.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
699
|
+
res.json({ models, disabledModels });
|
|
651
700
|
} catch (err) {
|
|
652
701
|
res.status(500).json({ error: String(err.message || err) });
|
|
653
702
|
}
|
|
654
703
|
});
|
|
655
704
|
|
|
656
|
-
router.put('/api/models/
|
|
657
|
-
const {
|
|
658
|
-
if (!Array.isArray(
|
|
659
|
-
const value =
|
|
660
|
-
upsertEnvValue(ENV_FILE, '
|
|
661
|
-
process.env.
|
|
705
|
+
router.put('/api/models/config', requireAdminAuth, express.json(), (req, res) => {
|
|
706
|
+
const { disabledModels } = req.body || {};
|
|
707
|
+
if (!Array.isArray(disabledModels)) return res.status(400).json({ error: 'disabledModels must be an array' });
|
|
708
|
+
const value = disabledModels.join(',');
|
|
709
|
+
upsertEnvValue(ENV_FILE, 'NEOAGENT_DISABLED_MODELS', value);
|
|
710
|
+
process.env.NEOAGENT_DISABLED_MODELS = value;
|
|
662
711
|
res.json({ ok: true });
|
|
663
712
|
});
|
|
664
713
|
|
package/server/routes/mcp.js
CHANGED
|
@@ -20,6 +20,15 @@ function getTrustedPostMessageOrigin(req) {
|
|
|
20
20
|
|
|
21
21
|
router.use(requireAuth);
|
|
22
22
|
|
|
23
|
+
router.get('/audit', (req, res) => {
|
|
24
|
+
try {
|
|
25
|
+
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
|
26
|
+
res.json(req.app.locals.capabilityAuditService.auditMcp(req.session.userId, { agentId }));
|
|
27
|
+
} catch (err) {
|
|
28
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
23
32
|
// List configured MCP servers
|
|
24
33
|
router.get('/', (req, res) => {
|
|
25
34
|
const agentId = resolveAgentId(req.session.userId, getAgentIdFromRequest(req));
|
package/server/routes/memory.js
CHANGED
|
@@ -226,6 +226,39 @@ router.get('/entities', (req, res) => {
|
|
|
226
226
|
}
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
router.get('/facts', (req, res) => {
|
|
230
|
+
const mm = req.app.locals.memoryManager;
|
|
231
|
+
const userId = req.session.userId;
|
|
232
|
+
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
233
|
+
try {
|
|
234
|
+
res.json(mm.listFacts(userId, {
|
|
235
|
+
agentId,
|
|
236
|
+
status: req.query.status || '',
|
|
237
|
+
subject: req.query.subject || '',
|
|
238
|
+
predicate: req.query.predicate || '',
|
|
239
|
+
validAt: req.query.validAt || '',
|
|
240
|
+
knownAt: req.query.knownAt || '',
|
|
241
|
+
limit: req.query.limit,
|
|
242
|
+
}));
|
|
243
|
+
} catch (err) {
|
|
244
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
router.post('/facts/reconcile', (req, res) => {
|
|
249
|
+
const mm = req.app.locals.memoryManager;
|
|
250
|
+
const userId = req.session.userId;
|
|
251
|
+
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
252
|
+
try {
|
|
253
|
+
res.json(mm.reconcileFacts(userId, {
|
|
254
|
+
agentId,
|
|
255
|
+
minimumConfidence: req.body?.minimumConfidence,
|
|
256
|
+
}));
|
|
257
|
+
} catch (err) {
|
|
258
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
229
262
|
router.post('/knowledge-views/materialize', (req, res) => {
|
|
230
263
|
const mm = req.app.locals.memoryManager;
|
|
231
264
|
const userId = req.session.userId;
|
|
@@ -424,7 +457,7 @@ router.post('/transfer-import', transferImportLimiter, async (req, res) => {
|
|
|
424
457
|
if (applyCoreMemory && parsed.coreEntries) {
|
|
425
458
|
for (const [key, value] of Object.entries(parsed.coreEntries)) {
|
|
426
459
|
if (!key || key === 'active_context') continue;
|
|
427
|
-
mm.updateCore(userId, key, value, { agentId });
|
|
460
|
+
mm.updateCore(userId, key, value, { agentId, confirmed: true });
|
|
428
461
|
coreUpdatedCount += 1;
|
|
429
462
|
}
|
|
430
463
|
}
|
|
@@ -456,7 +489,7 @@ router.put('/core/:key', (req, res) => {
|
|
|
456
489
|
if (req.params.key === 'active_context') {
|
|
457
490
|
return res.status(400).json({ error: 'active_context is no longer a supported core memory key' });
|
|
458
491
|
}
|
|
459
|
-
mm.updateCore(userId, req.params.key, value, { agentId });
|
|
492
|
+
mm.updateCore(userId, req.params.key, value, { agentId, confirmed: true });
|
|
460
493
|
res.json({ success: true });
|
|
461
494
|
});
|
|
462
495
|
|
package/server/routes/runtime.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const express = require('express');
|
|
4
|
-
const { getAnalyticsConfig } = require('../config/analytics');
|
|
5
4
|
|
|
6
5
|
const router = express.Router();
|
|
7
6
|
|
|
8
7
|
router.get('/config', (req, res) => {
|
|
9
8
|
res.json({
|
|
10
|
-
analytics:
|
|
9
|
+
analytics: {
|
|
10
|
+
enabled: false,
|
|
11
|
+
provider: null,
|
|
12
|
+
requiresConsent: true,
|
|
13
|
+
},
|
|
11
14
|
});
|
|
12
15
|
});
|
|
13
16
|
|
|
@@ -410,6 +410,23 @@ router.get('/token-usage/summary', (req, res) => {
|
|
|
410
410
|
return acc;
|
|
411
411
|
}, { count: 0, system: 0, tools: 0, history: 0, recall: 0, replay: 0 });
|
|
412
412
|
|
|
413
|
+
const normalizedUsage = db.prepare(`
|
|
414
|
+
SELECT
|
|
415
|
+
COALESCE(SUM(input_tokens), 0) AS inputTokens,
|
|
416
|
+
COALESCE(SUM(output_tokens), 0) AS outputTokens,
|
|
417
|
+
COALESCE(SUM(reasoning_tokens), 0) AS reasoningTokens,
|
|
418
|
+
COALESCE(SUM(cached_read_tokens), 0) AS cachedReadTokens,
|
|
419
|
+
COALESCE(SUM(cache_write_tokens), 0) AS cacheWriteTokens,
|
|
420
|
+
COALESCE(SUM(total_tokens), 0) AS totalTokens,
|
|
421
|
+
SUM(estimated_cost_usd) AS estimatedCostUsd,
|
|
422
|
+
COUNT(estimated_cost_usd) AS pricedCallCount,
|
|
423
|
+
COUNT(*) AS callCount
|
|
424
|
+
FROM agent_model_usage
|
|
425
|
+
WHERE user_id = ? AND agent_id = ?
|
|
426
|
+
`).get(userId, agentId);
|
|
427
|
+
const cacheEligibleTokens = Number(normalizedUsage?.inputTokens || 0)
|
|
428
|
+
+ Number(normalizedUsage?.cachedReadTokens || 0);
|
|
429
|
+
|
|
413
430
|
res.json({
|
|
414
431
|
totals: {
|
|
415
432
|
totalTokens: Number(totals?.totalTokens || 0),
|
|
@@ -429,7 +446,23 @@ router.get('/token-usage/summary', (req, res) => {
|
|
|
429
446
|
toolReplayTokens: Math.round(metricTotals.replay / metricTotals.count)
|
|
430
447
|
},
|
|
431
448
|
latest: parsedMetrics[0] || null
|
|
432
|
-
}
|
|
449
|
+
},
|
|
450
|
+
modelUsage: {
|
|
451
|
+
inputTokens: Number(normalizedUsage?.inputTokens || 0),
|
|
452
|
+
outputTokens: Number(normalizedUsage?.outputTokens || 0),
|
|
453
|
+
reasoningTokens: Number(normalizedUsage?.reasoningTokens || 0),
|
|
454
|
+
cachedReadTokens: Number(normalizedUsage?.cachedReadTokens || 0),
|
|
455
|
+
cacheWriteTokens: Number(normalizedUsage?.cacheWriteTokens || 0),
|
|
456
|
+
totalTokens: Number(normalizedUsage?.totalTokens || 0),
|
|
457
|
+
estimatedCostUsd: Number(normalizedUsage?.pricedCallCount || 0) > 0
|
|
458
|
+
? Number(normalizedUsage.estimatedCostUsd)
|
|
459
|
+
: null,
|
|
460
|
+
pricedCallCount: Number(normalizedUsage?.pricedCallCount || 0),
|
|
461
|
+
callCount: Number(normalizedUsage?.callCount || 0),
|
|
462
|
+
cacheHitRatio: cacheEligibleTokens > 0
|
|
463
|
+
? Number(normalizedUsage?.cachedReadTokens || 0) / cacheEligibleTokens
|
|
464
|
+
: 0,
|
|
465
|
+
},
|
|
433
466
|
});
|
|
434
467
|
});
|
|
435
468
|
|
package/server/routes/skills.js
CHANGED
|
@@ -2,6 +2,7 @@ const express = require('express');
|
|
|
2
2
|
const router = express.Router();
|
|
3
3
|
const { requireAuth } = require('../middleware/auth');
|
|
4
4
|
const { sanitizeError } = require('../utils/security');
|
|
5
|
+
const db = require('../db/database');
|
|
5
6
|
const {
|
|
6
7
|
getSkillRunner,
|
|
7
8
|
serializeInstalledSkill,
|
|
@@ -62,6 +63,47 @@ router.get('/', async (req, res) => {
|
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
65
|
|
|
66
|
+
router.get('/metrics/summary', (req, res) => {
|
|
67
|
+
try {
|
|
68
|
+
const rows = db.prepare(
|
|
69
|
+
`SELECT skill_name, invocation_count, success_count, failure_count,
|
|
70
|
+
correction_count, total_tokens, last_used_at
|
|
71
|
+
FROM skill_metrics WHERE user_id = ?
|
|
72
|
+
ORDER BY invocation_count DESC, skill_name ASC`
|
|
73
|
+
).all(req.session.userId);
|
|
74
|
+
res.json(rows.map((row) => {
|
|
75
|
+
const invocations = Number(row.invocation_count || 0);
|
|
76
|
+
const successes = Number(row.success_count || 0);
|
|
77
|
+
const failures = Number(row.failure_count || 0);
|
|
78
|
+
let recommendation = 'keep';
|
|
79
|
+
if (invocations >= 5 && failures / invocations >= 0.4) recommendation = 'improve_or_disable';
|
|
80
|
+
else if (invocations >= 10 && successes === 0) recommendation = 'disable_or_delete';
|
|
81
|
+
else if (invocations === 0) recommendation = 'review_unused';
|
|
82
|
+
return {
|
|
83
|
+
skillName: row.skill_name,
|
|
84
|
+
invocationCount: invocations,
|
|
85
|
+
successCount: successes,
|
|
86
|
+
failureCount: failures,
|
|
87
|
+
correctionCount: Number(row.correction_count || 0),
|
|
88
|
+
totalTokens: Number(row.total_tokens || 0),
|
|
89
|
+
successRate: invocations > 0 ? successes / invocations : null,
|
|
90
|
+
lastUsedAt: row.last_used_at,
|
|
91
|
+
recommendation,
|
|
92
|
+
};
|
|
93
|
+
}));
|
|
94
|
+
} catch (err) {
|
|
95
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
router.get('/audit/summary', (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
res.json(req.app.locals.capabilityAuditService.auditSkills());
|
|
102
|
+
} catch (err) {
|
|
103
|
+
res.status(500).json({ error: sanitizeError(err) });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
65
107
|
router.get('/:name', async (req, res) => {
|
|
66
108
|
try {
|
|
67
109
|
const runner = await getSkillRunner(req.app);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const { requireAuth } = require('../middleware/auth');
|
|
5
|
+
const { sanitizeError } = require('../utils/security');
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
function parseTaskId(value) {
|
|
10
|
+
const taskId = Number.parseInt(value, 10);
|
|
11
|
+
if (!Number.isInteger(taskId) || taskId <= 0) throw new Error('Invalid task id.');
|
|
12
|
+
return taskId;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
router.post('/:taskId/deliver', async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await req.app.locals.taskWebhookService.deliver(
|
|
18
|
+
parseTaskId(req.params.taskId),
|
|
19
|
+
req.headers,
|
|
20
|
+
req.rawBody || JSON.stringify(req.body || {}),
|
|
21
|
+
req.body || {},
|
|
22
|
+
);
|
|
23
|
+
res.status(202).json(result);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
router.use(requireAuth);
|
|
30
|
+
|
|
31
|
+
router.get('/:taskId', (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
res.json(req.app.locals.taskWebhookService.getConfiguration(
|
|
34
|
+
req.session.userId,
|
|
35
|
+
parseTaskId(req.params.taskId),
|
|
36
|
+
));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
router.post('/:taskId/rotate', (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
res.json(req.app.locals.taskWebhookService.rotateSecret(
|
|
45
|
+
req.session.userId,
|
|
46
|
+
parseTaskId(req.params.taskId),
|
|
47
|
+
));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
router.get('/:taskId/deliveries', (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
res.json(req.app.locals.taskWebhookService.listDeliveries(
|
|
56
|
+
req.session.userId,
|
|
57
|
+
parseTaskId(req.params.taskId),
|
|
58
|
+
req.query?.limit,
|
|
59
|
+
));
|
|
60
|
+
} catch (err) {
|
|
61
|
+
res.status(err.status || 400).json({ error: sanitizeError(err) });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
module.exports = router;
|