nyxora 26.6.21 → 26.6.22-1
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/bin/nyxora.mjs +14 -2
- package/dist/packages/core/src/agent/cronManager.js +107 -0
- package/dist/packages/core/src/agent/reasoning.js +85 -22
- package/dist/packages/core/src/agent/transactionManager.js +2 -2
- package/dist/packages/core/src/agent/updateIdentity.js +71 -0
- package/dist/packages/core/src/config/paths.js +5 -20
- package/dist/packages/core/src/gateway/chat.js +38 -0
- package/dist/packages/core/src/gateway/cli.js +20 -20
- package/dist/packages/core/src/gateway/server.js +43 -8
- package/dist/packages/core/src/gateway/telegram.js +43 -0
- package/dist/packages/core/src/gateway/tracker.js +58 -0
- package/dist/packages/core/src/memory/logger.js +1 -1
- package/dist/packages/core/src/system/skills/cancelTask.js +40 -0
- package/dist/packages/core/src/system/skills/editFile.js +5 -0
- package/dist/packages/core/src/system/skills/scheduleTask.js +39 -0
- package/dist/packages/core/src/system/skills/writeFile.js +5 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +1 -1
- package/dist/packages/policy/src/server.js +1 -1
- package/package.json +4 -1
- package/packages/core/package.json +5 -1
- package/packages/core/src/agent/cronManager.ts +87 -0
- package/packages/core/src/agent/reasoning.ts +91 -21
- package/packages/core/src/agent/transactionManager.ts +2 -1
- package/packages/core/src/agent/updateIdentity.ts +68 -0
- package/packages/core/src/config/paths.ts +5 -23
- package/packages/core/src/gateway/chat.ts +40 -1
- package/packages/core/src/gateway/cli.ts +10 -10
- package/packages/core/src/gateway/server.ts +44 -7
- package/packages/core/src/gateway/telegram.ts +49 -0
- package/packages/core/src/gateway/tracker.ts +55 -0
- package/packages/core/src/memory/logger.ts +1 -1
- package/packages/core/src/system/skills/cancelTask.ts +38 -0
- package/packages/core/src/system/skills/editFile.ts +7 -0
- package/packages/core/src/system/skills/scheduleTask.ts +38 -0
- package/packages/core/src/system/skills/writeFile.ts +7 -0
- package/packages/core/src/web3/skills/getPrice.ts +1 -1
- package/packages/dashboard/dist/assets/index-CjZWf1Ei.css +1 -0
- package/packages/dashboard/dist/assets/index-CmWZofn_.js +16 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/dist/routers/0x.png +0 -0
- package/packages/dashboard/dist/routers/1inch.png +0 -0
- package/packages/dashboard/dist/routers/cmc.png +0 -0
- package/packages/dashboard/dist/routers/kyberswap.png +0 -0
- package/packages/dashboard/dist/routers/lifi.png +0 -0
- package/packages/dashboard/dist/routers/openocean.png +0 -0
- package/packages/dashboard/dist/routers/relay.png +0 -0
- package/packages/dashboard/dist/routers/zerion.png +0 -0
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/policy/src/server.ts +1 -1
- package/packages/signer/package.json +1 -1
- package/packages/dashboard/dist/assets/index-CQNHWZtN.css +0 -1
- package/packages/dashboard/dist/assets/index-Di9x08yk.js +0 -16
|
@@ -18,6 +18,7 @@ process.on('uncaughtException', (error) => {
|
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
19
|
const helmet_1 = __importDefault(require("helmet"));
|
|
20
20
|
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
21
|
+
const os_1 = __importDefault(require("os"));
|
|
21
22
|
const paths_1 = require("../config/paths");
|
|
22
23
|
const state_1 = require("../utils/state");
|
|
23
24
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -29,6 +30,7 @@ const config_1 = require("../web3/config");
|
|
|
29
30
|
const tokens_1 = require("../web3/utils/tokens");
|
|
30
31
|
const tracker_1 = require("./tracker");
|
|
31
32
|
const transactionManager_1 = require("../agent/transactionManager");
|
|
33
|
+
const multer_1 = __importDefault(require("multer"));
|
|
32
34
|
const transfer_1 = require("../web3/skills/transfer");
|
|
33
35
|
const swapToken_1 = require("../web3/skills/swapToken");
|
|
34
36
|
const getBalance_1 = require("../web3/skills/getBalance");
|
|
@@ -64,6 +66,9 @@ const xManager_1 = require("../system/skills/xManager");
|
|
|
64
66
|
const notionWorkspace_1 = require("../system/skills/notionWorkspace");
|
|
65
67
|
const audioTranscribe_1 = require("../system/skills/audioTranscribe");
|
|
66
68
|
const summarizeText_1 = require("../system/skills/summarizeText");
|
|
69
|
+
const scheduleTask_1 = require("../system/skills/scheduleTask");
|
|
70
|
+
const cancelTask_1 = require("../system/skills/cancelTask");
|
|
71
|
+
const cronManager_1 = require("../agent/cronManager");
|
|
67
72
|
const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
|
|
68
73
|
const writeFile_1 = require("../system/skills/writeFile");
|
|
69
74
|
const generateExcel_1 = require("../system/skills/generateExcel");
|
|
@@ -141,14 +146,11 @@ app.use('/api', (req, res, next) => {
|
|
|
141
146
|
next();
|
|
142
147
|
});
|
|
143
148
|
// Serve Static Dashboard
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
break;
|
|
149
|
-
rootDir = nextDir;
|
|
149
|
+
// __dirname is packages/core/dist/gateway (compiled) OR packages/core/src/gateway (dev)
|
|
150
|
+
let dashboardPath = path_1.default.join(__dirname, '..', '..', '..', 'dashboard', 'dist'); // Dev
|
|
151
|
+
if (!fs_1.default.existsSync(dashboardPath)) {
|
|
152
|
+
dashboardPath = path_1.default.join(__dirname, '..', '..', '..', '..', '..', 'packages', 'dashboard', 'dist'); // Compiled
|
|
150
153
|
}
|
|
151
|
-
const dashboardPath = path_1.default.join(rootDir, 'packages', 'dashboard', 'dist');
|
|
152
154
|
app.use(express_1.default.static(dashboardPath));
|
|
153
155
|
app.get('/', (req, res) => {
|
|
154
156
|
res.sendFile(path_1.default.join(dashboardPath, 'index.html'));
|
|
@@ -159,6 +161,31 @@ app.get('/privacy', (req, res) => {
|
|
|
159
161
|
app.get('/tos', (req, res) => {
|
|
160
162
|
res.send((0, legalGenerator_1.generateTosHtml)());
|
|
161
163
|
});
|
|
164
|
+
const storage = multer_1.default.diskStorage({
|
|
165
|
+
destination: function (req, file, cb) {
|
|
166
|
+
const docsDir = path_1.default.join(os_1.default.homedir(), '.nyxora', 'docs');
|
|
167
|
+
if (!fs_1.default.existsSync(docsDir))
|
|
168
|
+
fs_1.default.mkdirSync(docsDir, { recursive: true });
|
|
169
|
+
cb(null, docsDir);
|
|
170
|
+
},
|
|
171
|
+
filename: function (req, file, cb) {
|
|
172
|
+
const safeName = file.originalname.replace(/[^a-zA-Z0-9.\-_]/g, '_');
|
|
173
|
+
cb(null, `${Date.now()}-${safeName}`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
const upload = (0, multer_1.default)({ storage });
|
|
177
|
+
app.post('/api/upload', upload.single('file'), (req, res) => {
|
|
178
|
+
try {
|
|
179
|
+
if (!req.file) {
|
|
180
|
+
return res.status(400).json({ error: 'No file uploaded' });
|
|
181
|
+
}
|
|
182
|
+
return res.json({ filePath: req.file.path });
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
console.error('[Upload] Error:', err);
|
|
186
|
+
return res.status(500).json({ error: 'Failed to save file' });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
162
189
|
app.post('/api/upload-google-credentials', (req, res) => {
|
|
163
190
|
try {
|
|
164
191
|
const credentials = req.body.credentials;
|
|
@@ -352,7 +379,9 @@ const systemSkills = [
|
|
|
352
379
|
xManager_1.xManagerToolDefinition,
|
|
353
380
|
notionWorkspace_1.notionWorkspaceToolDefinition,
|
|
354
381
|
audioTranscribe_1.audioTranscribeToolDefinition,
|
|
355
|
-
summarizeText_1.summarizeTextToolDefinition
|
|
382
|
+
summarizeText_1.summarizeTextToolDefinition,
|
|
383
|
+
scheduleTask_1.scheduleTaskDefinition,
|
|
384
|
+
cancelTask_1.cancelTaskDefinition
|
|
356
385
|
];
|
|
357
386
|
app.get('/api/stats', (req, res) => {
|
|
358
387
|
const stats = tracker_1.Tracker.getStats();
|
|
@@ -366,6 +395,12 @@ app.get('/api/stats', (req, res) => {
|
|
|
366
395
|
app.get('/api/logs', (req, res) => {
|
|
367
396
|
res.json(tracker_1.Tracker.getLogs());
|
|
368
397
|
});
|
|
398
|
+
app.get('/api/cron', (req, res) => {
|
|
399
|
+
res.json({
|
|
400
|
+
activeJobs: cronManager_1.cronManager.getActiveJobsCount(),
|
|
401
|
+
jobs: cronManager_1.cronManager.getJobs()
|
|
402
|
+
});
|
|
403
|
+
});
|
|
369
404
|
app.get('/api/skills', (req, res) => {
|
|
370
405
|
const skillsWithStatus = allSkills.map(skill => ({
|
|
371
406
|
...skill,
|
|
@@ -19,6 +19,9 @@ const executeDefi_1 = require("../web3/skills/executeDefi");
|
|
|
19
19
|
const revokeApprovals_1 = require("../web3/skills/revokeApprovals");
|
|
20
20
|
const formatter_1 = require("../utils/formatter");
|
|
21
21
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
22
|
+
const fs_1 = __importDefault(require("fs"));
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
|
+
const os_1 = __importDefault(require("os"));
|
|
22
25
|
let globalBotInstance = null;
|
|
23
26
|
function formatToTelegramHTML(text) {
|
|
24
27
|
if (!text)
|
|
@@ -162,6 +165,46 @@ function startTelegramBot() {
|
|
|
162
165
|
await ctx.reply('❌ Sorry, I encountered an error while processing your message.');
|
|
163
166
|
}
|
|
164
167
|
});
|
|
168
|
+
bot.on('document', async (ctx) => {
|
|
169
|
+
const doc = ctx.message.document;
|
|
170
|
+
const caption = ctx.message.caption || '';
|
|
171
|
+
console.log(`[Telegram] Received document from ${ctx.from?.first_name || 'User'}: ${doc.file_name}`);
|
|
172
|
+
await ctx.sendChatAction('typing');
|
|
173
|
+
try {
|
|
174
|
+
const fileLink = await ctx.telegram.getFileLink(doc.file_id);
|
|
175
|
+
const docsDir = path_1.default.join(os_1.default.homedir(), '.nyxora', 'docs');
|
|
176
|
+
if (!fs_1.default.existsSync(docsDir))
|
|
177
|
+
fs_1.default.mkdirSync(docsDir, { recursive: true });
|
|
178
|
+
const safeName = (doc.file_name || 'telegram_doc').replace(/[^a-zA-Z0-9.\-_]/g, '_');
|
|
179
|
+
const localFilePath = path_1.default.join(docsDir, `${Date.now()}-${safeName}`);
|
|
180
|
+
const res = await fetch(fileLink.toString());
|
|
181
|
+
const buffer = await res.arrayBuffer();
|
|
182
|
+
fs_1.default.writeFileSync(localFilePath, Buffer.from(buffer));
|
|
183
|
+
const prompt = `Tolong analisis dokumen ini: ${localFilePath}\n\n${caption}`;
|
|
184
|
+
let progressMsgId = null;
|
|
185
|
+
const onProgress = async (progressText) => {
|
|
186
|
+
try {
|
|
187
|
+
if (!progressMsgId) {
|
|
188
|
+
const sent = await ctx.reply(`<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
|
|
189
|
+
progressMsgId = sent.message_id;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
await ctx.telegram.editMessageText(ctx.chat.id, progressMsgId, undefined, `<i>${progressText.replace(/_/g, '')}</i>`, { parse_mode: 'HTML' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (e) { }
|
|
196
|
+
};
|
|
197
|
+
const response = await (0, reasoning_1.processUserInput)(prompt, 'user', onProgress, ctx.chat?.id.toString());
|
|
198
|
+
if (progressMsgId) {
|
|
199
|
+
await ctx.telegram.deleteMessage(ctx.chat.id, progressMsgId).catch(() => { });
|
|
200
|
+
}
|
|
201
|
+
await ctx.reply(formatToTelegramHTML(response), { parse_mode: 'HTML' });
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
console.error('[Telegram] Error processing document:', error);
|
|
205
|
+
await ctx.reply('❌ Sorry, I failed to download or analyze the document.');
|
|
206
|
+
}
|
|
207
|
+
});
|
|
165
208
|
// Handle callbacks
|
|
166
209
|
bot.action(/^approve_(.+)$/, async (ctx) => {
|
|
167
210
|
const txId = ctx.match[1];
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.Tracker = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const paths_1 = require("../config/paths");
|
|
4
9
|
const stats = {
|
|
5
10
|
cost: 0,
|
|
6
11
|
tokens: 0,
|
|
@@ -9,6 +14,55 @@ const stats = {
|
|
|
9
14
|
const eventLogs = [];
|
|
10
15
|
const gatewayLogs = [];
|
|
11
16
|
const MAX_LOGS = 100;
|
|
17
|
+
let trackerFile = '';
|
|
18
|
+
try {
|
|
19
|
+
trackerFile = (0, paths_1.getPath)('tracker.json');
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
// Fallback
|
|
23
|
+
}
|
|
24
|
+
function loadState() {
|
|
25
|
+
if (!trackerFile)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
if (fs_1.default.existsSync(trackerFile)) {
|
|
29
|
+
const data = JSON.parse(fs_1.default.readFileSync(trackerFile, 'utf8'));
|
|
30
|
+
if (data.stats)
|
|
31
|
+
Object.assign(stats, data.stats);
|
|
32
|
+
if (data.eventLogs && Array.isArray(data.eventLogs)) {
|
|
33
|
+
eventLogs.splice(0, eventLogs.length, ...data.eventLogs);
|
|
34
|
+
}
|
|
35
|
+
if (data.gatewayLogs && Array.isArray(data.gatewayLogs)) {
|
|
36
|
+
gatewayLogs.splice(0, gatewayLogs.length, ...data.gatewayLogs);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (e) { }
|
|
41
|
+
}
|
|
42
|
+
let savePending = false;
|
|
43
|
+
function saveState() {
|
|
44
|
+
if (!trackerFile)
|
|
45
|
+
return;
|
|
46
|
+
if (savePending)
|
|
47
|
+
return;
|
|
48
|
+
savePending = true;
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
flushState();
|
|
51
|
+
savePending = false;
|
|
52
|
+
}, 1000);
|
|
53
|
+
}
|
|
54
|
+
function flushState() {
|
|
55
|
+
if (!trackerFile)
|
|
56
|
+
return;
|
|
57
|
+
try {
|
|
58
|
+
fs_1.default.writeFileSync(trackerFile, JSON.stringify({ stats, eventLogs, gatewayLogs }));
|
|
59
|
+
}
|
|
60
|
+
catch (e) { }
|
|
61
|
+
}
|
|
62
|
+
process.on('exit', flushState);
|
|
63
|
+
process.on('SIGTERM', () => { flushState(); process.exit(0); });
|
|
64
|
+
process.on('SIGINT', () => { flushState(); process.exit(0); });
|
|
65
|
+
loadState();
|
|
12
66
|
function formatTime() {
|
|
13
67
|
const now = new Date();
|
|
14
68
|
return now.toTimeString().split(' ')[0]; // Returns HH:MM:SS
|
|
@@ -23,9 +77,11 @@ exports.Tracker = {
|
|
|
23
77
|
else if (provider === 'gemini')
|
|
24
78
|
rate = 0.00001;
|
|
25
79
|
stats.cost += (amount * rate);
|
|
80
|
+
saveState();
|
|
26
81
|
},
|
|
27
82
|
addMessage: () => {
|
|
28
83
|
stats.messages += 1;
|
|
84
|
+
saveState();
|
|
29
85
|
},
|
|
30
86
|
getStats: () => {
|
|
31
87
|
return { ...stats, cost: Number(stats.cost.toFixed(4)) };
|
|
@@ -34,11 +90,13 @@ exports.Tracker = {
|
|
|
34
90
|
eventLogs.unshift({ timestamp: formatTime(), event, meta });
|
|
35
91
|
if (eventLogs.length > MAX_LOGS)
|
|
36
92
|
eventLogs.pop();
|
|
93
|
+
saveState();
|
|
37
94
|
},
|
|
38
95
|
addGatewayLog: (message, meta) => {
|
|
39
96
|
gatewayLogs.unshift({ timestamp: formatTime(), message, meta });
|
|
40
97
|
if (gatewayLogs.length > MAX_LOGS)
|
|
41
98
|
gatewayLogs.pop();
|
|
99
|
+
saveState();
|
|
42
100
|
},
|
|
43
101
|
getLogs: () => {
|
|
44
102
|
return {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cancelTaskDefinition = void 0;
|
|
4
|
+
exports.executeCancelTask = executeCancelTask;
|
|
5
|
+
const cronManager_1 = require("../../agent/cronManager");
|
|
6
|
+
exports.cancelTaskDefinition = {
|
|
7
|
+
type: "function",
|
|
8
|
+
function: {
|
|
9
|
+
name: "cancel_task",
|
|
10
|
+
description: "Cancel a scheduled background task (cron job). Use this when the user asks you to stop monitoring or cancel a previously scheduled task.",
|
|
11
|
+
parameters: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
jobId: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "The unique Job ID of the scheduled task to cancel."
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
required: ["jobId"]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
async function executeCancelTask(args) {
|
|
24
|
+
const { jobId } = args;
|
|
25
|
+
if (!jobId) {
|
|
26
|
+
return "Error: Missing required parameter jobId.";
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const success = cronManager_1.cronManager.removeJob(jobId);
|
|
30
|
+
if (success) {
|
|
31
|
+
return `Success! I have cancelled the scheduled background task with Job ID: ${jobId}.`;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return `Failed to cancel task. No active task found with Job ID: ${jobId}. You can check active jobs by asking me what tasks are currently running.`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return `Failed to cancel task: ${error.message}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -10,6 +10,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
function editLocalFile(filePath, searchString, replacementString) {
|
|
11
11
|
try {
|
|
12
12
|
const absolutePath = path_1.default.resolve(filePath);
|
|
13
|
+
// Security Firewall: Block modification of core configuration files
|
|
14
|
+
const basename = path_1.default.basename(absolutePath);
|
|
15
|
+
if (['config.yaml', 'rpc_key.yaml', 'policy.yaml'].includes(basename)) {
|
|
16
|
+
return `Error: Access Denied. You are strictly forbidden from modifying core configuration files directly. If you need to update your agent name, use the update_identity tool instead.`;
|
|
17
|
+
}
|
|
13
18
|
if (!fs_1.default.existsSync(absolutePath)) {
|
|
14
19
|
return `Error: File not found at ${absolutePath}`;
|
|
15
20
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scheduleTaskDefinition = void 0;
|
|
4
|
+
exports.executeScheduleTask = executeScheduleTask;
|
|
5
|
+
const cronManager_1 = require("../../agent/cronManager");
|
|
6
|
+
exports.scheduleTaskDefinition = {
|
|
7
|
+
type: "function",
|
|
8
|
+
function: {
|
|
9
|
+
name: "schedule_task",
|
|
10
|
+
description: "Schedule a recurring background task for the AI to execute automatically using a cron expression. Use this when the user asks you to remind them or monitor something periodically (e.g. 'check price every hour', 'monitor my wallet every 5 minutes'). The AI will execute the provided prompt at the scheduled interval and send the result via Telegram notification.",
|
|
11
|
+
parameters: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
cronExpression: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "A standard 5-field cron expression (minute hour day month day-of-week). Examples: '*/5 * * * *' (every 5 mins), '0 * * * *' (every hour), '0 8 * * *' (every day at 8 AM)."
|
|
17
|
+
},
|
|
18
|
+
prompt: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The prompt/command that the AI should execute when the cron triggers. E.g., 'What is the current price of Ethereum?'"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
required: ["cronExpression", "prompt"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
async function executeScheduleTask(args) {
|
|
28
|
+
const { cronExpression, prompt } = args;
|
|
29
|
+
if (!cronExpression || !prompt) {
|
|
30
|
+
return "Error: Missing required parameters cronExpression or prompt.";
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const jobId = cronManager_1.cronManager.addJob(cronExpression, prompt);
|
|
34
|
+
return `Success! I have scheduled the background task.\nJob ID: ${jobId}\nSchedule: ${cronExpression}\nPrompt to execute: "${prompt}"\n\nYou will receive a notification via Telegram every time this task completes.`;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return `Failed to schedule task: ${error.message}. Please ensure the cron expression is valid.`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -10,6 +10,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
function writeLocalFile(filePath, content) {
|
|
11
11
|
try {
|
|
12
12
|
const absolutePath = path_1.default.resolve(filePath);
|
|
13
|
+
// Security Firewall: Block modification of core configuration files
|
|
14
|
+
const basename = path_1.default.basename(absolutePath);
|
|
15
|
+
if (['config.yaml', 'rpc_key.yaml', 'policy.yaml'].includes(basename)) {
|
|
16
|
+
return `Error: Access Denied. You are strictly forbidden from modifying core configuration files directly. If you need to update your agent name, use the update_identity tool instead.`;
|
|
17
|
+
}
|
|
13
18
|
const dir = path_1.default.dirname(absolutePath);
|
|
14
19
|
if (!fs_1.default.existsSync(dir)) {
|
|
15
20
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
@@ -7,7 +7,7 @@ exports.getPriceToolDefinition = {
|
|
|
7
7
|
type: "function",
|
|
8
8
|
function: {
|
|
9
9
|
name: "get_price",
|
|
10
|
-
description: "Fetches the current price of a cryptocurrency.
|
|
10
|
+
description: "Fetches the current price of a cryptocurrency.",
|
|
11
11
|
parameters: {
|
|
12
12
|
type: "object",
|
|
13
13
|
properties: {
|
|
@@ -237,7 +237,7 @@ app.post('/approve-tx/:id', (req, res) => {
|
|
|
237
237
|
signerReq.write(requestPayload);
|
|
238
238
|
signerReq.end();
|
|
239
239
|
});
|
|
240
|
-
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
240
|
+
const server = app.listen(Number(PORT), '127.0.0.1', () => {
|
|
241
241
|
console.log(`[Policy Engine] Listening on 127.0.0.1:${PORT} (Secured Local Loopback)`);
|
|
242
242
|
});
|
|
243
243
|
server.on('error', (e) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.22-1",
|
|
4
4
|
"description": "Your Personal Web3 Assistant",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web3",
|
|
@@ -65,6 +65,8 @@
|
|
|
65
65
|
"isolated-vm": "^6.1.2",
|
|
66
66
|
"jsonwebtoken": "^9.0.2",
|
|
67
67
|
"mammoth": "^1.6.0",
|
|
68
|
+
"multer": "^2.2.0",
|
|
69
|
+
"node-cron": "^4.4.1",
|
|
68
70
|
"open": "^11.0.0",
|
|
69
71
|
"openai": "^6.39.0",
|
|
70
72
|
"pdf-parse": "^2.4.5",
|
|
@@ -97,6 +99,7 @@
|
|
|
97
99
|
"devDependencies": {
|
|
98
100
|
"@types/jsonwebtoken": "^9.0.5",
|
|
99
101
|
"@types/node": "^25.9.1",
|
|
102
|
+
"@types/node-cron": "^3.0.11",
|
|
100
103
|
"concurrently": "^10.0.3",
|
|
101
104
|
"oxc-minify": "^0.135.0",
|
|
102
105
|
"vitepress": "^2.0.0-alpha.17"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora-agent-core",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.22-1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "src/gateway/server.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"express-rate-limit": "^7.5.0",
|
|
15
15
|
"helmet": "^8.0.0",
|
|
16
16
|
"mammoth": "^1.12.0",
|
|
17
|
+
"multer": "^2.2.0",
|
|
18
|
+
"node-cron": "^4.4.1",
|
|
17
19
|
"open": "^11.0.0",
|
|
18
20
|
"openai": "^6.39.0",
|
|
19
21
|
"pdf-parse": "^2.4.5",
|
|
@@ -26,7 +28,9 @@
|
|
|
26
28
|
"zod": "^3.25.76"
|
|
27
29
|
},
|
|
28
30
|
"devDependencies": {
|
|
31
|
+
"@types/multer": "^2.1.0",
|
|
29
32
|
"@types/node": "^25.9.2",
|
|
33
|
+
"@types/node-cron": "^3.0.11",
|
|
30
34
|
"@types/pdf-parse": "^1.1.5",
|
|
31
35
|
"vitest": "^4.1.8"
|
|
32
36
|
},
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as cron from 'node-cron';
|
|
2
|
+
import { loadConfig } from '../config/parser';
|
|
3
|
+
import { sendPushNotification } from '../gateway/telegram';
|
|
4
|
+
import { randomUUID } from 'crypto';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
|
|
7
|
+
export interface CronJob {
|
|
8
|
+
id: string;
|
|
9
|
+
expression: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
task: cron.ScheduledTask;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class CronManager {
|
|
16
|
+
private jobs: Map<string, CronJob> = new Map();
|
|
17
|
+
|
|
18
|
+
public addJob(expression: string, prompt: string): string {
|
|
19
|
+
const id = randomUUID();
|
|
20
|
+
|
|
21
|
+
// Validate expression
|
|
22
|
+
if (!cron.validate(expression)) {
|
|
23
|
+
throw new Error(`Invalid cron expression: ${expression}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const task = cron.schedule(expression, async () => {
|
|
27
|
+
console.log(pc.cyan(`[Cron] Executing job ${id}: "${prompt}"`));
|
|
28
|
+
try {
|
|
29
|
+
// Dynamically import processUserInput to avoid circular dependencies
|
|
30
|
+
const { processUserInput } = await import('./reasoning');
|
|
31
|
+
|
|
32
|
+
// Execute the prompt as a background system task
|
|
33
|
+
const response = await processUserInput(prompt, 'system', undefined, `cron-${id}`);
|
|
34
|
+
|
|
35
|
+
// Push notification to Telegram if configured
|
|
36
|
+
const config = loadConfig();
|
|
37
|
+
if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
|
|
38
|
+
const message = `🤖 *AI Scheduled Report*\n\n${response}`;
|
|
39
|
+
await sendPushNotification(config.integrations.telegram.authorized_chat_id, message);
|
|
40
|
+
}
|
|
41
|
+
} catch (err: any) {
|
|
42
|
+
console.error(pc.red(`[Cron] Failed to execute job ${id}:`), err);
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
if (config.integrations?.telegram?.enabled && config.integrations?.telegram?.authorized_chat_id) {
|
|
45
|
+
await sendPushNotification(config.integrations.telegram.authorized_chat_id, `⚠️ *Cron Job Error*\n\nPrompt: ${prompt}\nError: ${err.message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.jobs.set(id, {
|
|
51
|
+
id,
|
|
52
|
+
expression,
|
|
53
|
+
prompt,
|
|
54
|
+
task,
|
|
55
|
+
createdAt: Date.now()
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log(pc.green(`[Cron] Scheduled new job ${id} with expression '${expression}'`));
|
|
59
|
+
return id;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public removeJob(id: string): boolean {
|
|
63
|
+
const job = this.jobs.get(id);
|
|
64
|
+
if (job) {
|
|
65
|
+
job.task.stop();
|
|
66
|
+
this.jobs.delete(id);
|
|
67
|
+
console.log(pc.yellow(`[Cron] Removed job ${id}`));
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public getJobs(): Omit<CronJob, 'task'>[] {
|
|
74
|
+
return Array.from(this.jobs.values()).map(job => ({
|
|
75
|
+
id: job.id,
|
|
76
|
+
expression: job.expression,
|
|
77
|
+
prompt: job.prompt,
|
|
78
|
+
createdAt: job.createdAt
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public getActiveJobsCount(): number {
|
|
83
|
+
return this.jobs.size;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const cronManager = new CronManager();
|