nyxora 1.2.0 → 1.4.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.
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cancelLimitOrderToolDefinition = exports.listLimitOrdersToolDefinition = exports.createLimitOrderToolDefinition = exports.limitOrderManager = exports.LimitOrderManager = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const parser_1 = require("../config/parser");
9
+ const paths_1 = require("../config/paths");
10
+ const tokens_1 = require("../web3/utils/tokens");
11
+ const swapToken_1 = require("../web3/skills/swapToken");
12
+ const transactionManager_1 = require("./transactionManager");
13
+ const reasoning_1 = require("./reasoning");
14
+ class LimitOrderManager {
15
+ filePath;
16
+ orders = [];
17
+ monitorInterval = null;
18
+ constructor() {
19
+ const config = (0, parser_1.loadConfig)();
20
+ this.filePath = (0, paths_1.getPath)(config.memory?.path ? config.memory.path.replace('memory.json', 'orders.json') : 'orders.json');
21
+ this.loadOrders();
22
+ }
23
+ loadOrders() {
24
+ if (fs_1.default.existsSync(this.filePath)) {
25
+ try {
26
+ const data = fs_1.default.readFileSync(this.filePath, 'utf-8');
27
+ this.orders = JSON.parse(data);
28
+ }
29
+ catch (error) {
30
+ this.orders = [];
31
+ }
32
+ }
33
+ }
34
+ saveOrders() {
35
+ try {
36
+ fs_1.default.writeFileSync(this.filePath, JSON.stringify(this.orders, null, 2));
37
+ }
38
+ catch (error) { }
39
+ }
40
+ createOrder(chainName, fromToken, toToken, amountStr, targetPriceUsd, condition) {
41
+ const id = `order_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
42
+ const order = {
43
+ id, chainName, fromToken, toToken, amountStr, targetPriceUsd, condition, status: 'pending', createdAt: Date.now()
44
+ };
45
+ this.orders.push(order);
46
+ this.saveOrders();
47
+ return `Limit order created successfully. ID: ${id}. The system will monitor ${fromToken} price on ${chainName} and execute the swap to ${toToken} when price is ${condition} $${targetPriceUsd}.`;
48
+ }
49
+ listOrders() {
50
+ const pending = this.orders.filter(o => o.status === 'pending');
51
+ if (pending.length === 0)
52
+ return "No active limit orders.";
53
+ let report = "Active Limit Orders:\n";
54
+ pending.forEach(o => {
55
+ report += `- [${o.id}] Swap ${o.amountStr} ${o.fromToken} -> ${o.toToken} on ${o.chainName} when ${o.fromToken} is ${o.condition} $${o.targetPriceUsd}\n`;
56
+ });
57
+ return report;
58
+ }
59
+ cancelOrder(id) {
60
+ const order = this.orders.find(o => o.id === id);
61
+ if (!order)
62
+ return `Order ${id} not found.`;
63
+ if (order.status !== 'pending')
64
+ return `Order ${id} cannot be cancelled because it is ${order.status}.`;
65
+ order.status = 'cancelled';
66
+ this.saveOrders();
67
+ return `Order ${id} cancelled successfully.`;
68
+ }
69
+ startMonitor() {
70
+ if (this.monitorInterval)
71
+ clearInterval(this.monitorInterval);
72
+ // Monitor every 60 seconds
73
+ this.monitorInterval = setInterval(() => this.checkOrders(), 60000);
74
+ console.log('[LimitOrderManager] Order monitoring started (interval: 60s)');
75
+ }
76
+ async checkOrders() {
77
+ const pending = this.orders.filter(o => o.status === 'pending');
78
+ if (pending.length === 0)
79
+ return;
80
+ for (const order of pending) {
81
+ try {
82
+ let tokenAddress = (0, tokens_1.resolveToken)(order.fromToken, order.chainName);
83
+ if (tokenAddress === "0x0000000000000000000000000000000000000000") {
84
+ tokenAddress = (0, tokens_1.resolveToken)("W" + order.fromToken, order.chainName);
85
+ }
86
+ const res = await fetch(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`);
87
+ if (!res.ok)
88
+ continue;
89
+ const data = await res.json();
90
+ if (!data.pairs || data.pairs.length === 0)
91
+ continue;
92
+ let pair = data.pairs.find((p) => p.chainId === order.chainName) || data.pairs[0];
93
+ const currentPrice = parseFloat(pair.priceUsd);
94
+ let shouldExecute = false;
95
+ if (order.condition === 'above' && currentPrice >= order.targetPriceUsd)
96
+ shouldExecute = true;
97
+ if (order.condition === 'below' && currentPrice <= order.targetPriceUsd)
98
+ shouldExecute = true;
99
+ if (shouldExecute) {
100
+ console.log(`[LimitOrderManager] Condition met for order ${order.id}. Current price $${currentPrice} is ${order.condition} $${order.targetPriceUsd}. Executing...`);
101
+ // 1. Prepare Swap
102
+ const prepareResult = await (0, swapToken_1.prepareSwapToken)(order.chainName, order.fromToken, order.toToken, order.amountStr, 'auto');
103
+ // 2. Extract Tx ID
104
+ const txMatch = prepareResult.match(/Transaction ID: ([\w-]+)\./);
105
+ if (!txMatch) {
106
+ order.status = 'failed';
107
+ this.saveOrders();
108
+ (0, reasoning_1.processUserInput)(`Limit order ${order.id} execution failed during preparation. Output: ${prepareResult}`, 'system').catch(() => { });
109
+ continue;
110
+ }
111
+ const txId = txMatch[1];
112
+ const tx = transactionManager_1.txManager.getTransaction(txId);
113
+ if (!tx)
114
+ throw new Error("Transaction not found in manager");
115
+ // 3. Execute Swap automatically
116
+ const executeResult = await (0, swapToken_1.executeSwap)(order.chainName, tx.details);
117
+ if (executeResult.includes('successful')) {
118
+ transactionManager_1.txManager.updateStatus(txId, 'executed', executeResult);
119
+ order.status = 'executed';
120
+ this.saveOrders();
121
+ (0, reasoning_1.processUserInput)(`Limit order ${order.id} just EXECUTED automatically! Price hit $${currentPrice}. Swap result: ${executeResult}. Please notify the user immediately!`, 'system').catch(() => { });
122
+ }
123
+ else {
124
+ transactionManager_1.txManager.updateStatus(txId, 'failed', executeResult);
125
+ order.status = 'failed';
126
+ this.saveOrders();
127
+ (0, reasoning_1.processUserInput)(`Limit order ${order.id} FAILED to execute. Price hit $${currentPrice} but execution failed: ${executeResult}. Please notify the user.`, 'system').catch(() => { });
128
+ }
129
+ }
130
+ }
131
+ catch (error) {
132
+ console.error(`[LimitOrderManager] Error checking order ${order.id}:`, error.message);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ exports.LimitOrderManager = LimitOrderManager;
138
+ exports.limitOrderManager = new LimitOrderManager();
139
+ exports.createLimitOrderToolDefinition = {
140
+ type: "function",
141
+ function: {
142
+ name: "create_limit_order",
143
+ description: "Creates an automatic cut-loss or take-profit limit order. The system will automatically execute the swap when the price condition is met.",
144
+ parameters: {
145
+ type: "object",
146
+ properties: {
147
+ chainName: { type: "string", enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"] },
148
+ fromToken: { type: "string", description: "Token to sell" },
149
+ toToken: { type: "string", description: "Token to buy" },
150
+ amountStr: { type: "string", description: "Amount to sell" },
151
+ targetPriceUsd: { type: "number", description: "Target price in USD for the fromToken" },
152
+ condition: { type: "string", enum: ["above", "below"], description: "Trigger when price goes above (take-profit) or below (cut-loss) target" }
153
+ },
154
+ required: ["chainName", "fromToken", "toToken", "amountStr", "targetPriceUsd", "condition"],
155
+ },
156
+ },
157
+ };
158
+ exports.listLimitOrdersToolDefinition = {
159
+ type: "function",
160
+ function: {
161
+ name: "list_limit_orders",
162
+ description: "Lists all active automated limit orders.",
163
+ parameters: { type: "object", properties: {}, required: [] },
164
+ },
165
+ };
166
+ exports.cancelLimitOrderToolDefinition = {
167
+ type: "function",
168
+ function: {
169
+ name: "cancel_limit_order",
170
+ description: "Cancels an active limit order by ID.",
171
+ parameters: {
172
+ type: "object",
173
+ properties: { id: { type: "string" } },
174
+ required: ["id"],
175
+ },
176
+ },
177
+ };
@@ -17,6 +17,19 @@ const swapToken_1 = require("../web3/skills/swapToken");
17
17
  const bridgeToken_1 = require("../web3/skills/bridgeToken");
18
18
  const mintNft_1 = require("../web3/skills/mintNft");
19
19
  const customTx_1 = require("../web3/skills/customTx");
20
+ const createWallet_1 = require("../web3/skills/createWallet");
21
+ const checkSecurity_1 = require("../web3/skills/checkSecurity");
22
+ const marketAnalysis_1 = require("../web3/skills/marketAnalysis");
23
+ const checkPortfolio_1 = require("../web3/skills/checkPortfolio");
24
+ const limitOrderManager_1 = require("./limitOrderManager");
25
+ const updateProfile_1 = require("./updateProfile");
26
+ const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
27
+ const readFile_1 = require("../system/skills/readFile");
28
+ const writeFile_1 = require("../system/skills/writeFile");
29
+ const executeShell_1 = require("../system/skills/executeShell");
30
+ const browseWeb_1 = require("../system/skills/browseWeb");
31
+ const installSkill_1 = require("../system/skills/installSkill");
32
+ const pluginManager_1 = require("../system/pluginManager");
20
33
  const paths_1 = require("../config/paths");
21
34
  exports.logger = new logger_1.Logger();
22
35
  let currentKeyIndex = 0;
@@ -108,6 +121,17 @@ If the user doesn't specify a chain, default to: ${config.agent.default_chain}.`
108
121
  catch (error) {
109
122
  console.error('Failed to read user.md:', error);
110
123
  }
124
+ // Read security_policy.md for NLP security constraints
125
+ try {
126
+ const policyPath = (0, paths_1.getPath)('security_policy.md');
127
+ if (fs_1.default.existsSync(policyPath)) {
128
+ const securityInstructions = fs_1.default.readFileSync(policyPath, 'utf8');
129
+ basePrompt += `\n\n--- SECURITY POLICY (MANDATORY RULES) ---\n${securityInstructions}\n\nCRITICAL: If the user asks you to perform an action that violates the Security Policy above, YOU MUST NOT EXECUTE IT DIRECTLY. Instead, ask for their explicit permission first.`;
130
+ }
131
+ }
132
+ catch (error) {
133
+ console.error('Failed to read security_policy.md:', error);
134
+ }
111
135
  return basePrompt;
112
136
  }
113
137
  async function processUserInput(input, role = 'user') {
@@ -150,7 +174,22 @@ async function processUserInput(input, role = 'user') {
150
174
  swapToken_1.swapTokenToolDefinition,
151
175
  bridgeToken_1.bridgeTokenToolDefinition,
152
176
  mintNft_1.mintNftToolDefinition,
153
- customTx_1.customTxToolDefinition
177
+ customTx_1.customTxToolDefinition,
178
+ createWallet_1.createWalletToolDefinition,
179
+ checkSecurity_1.checkSecurityToolDefinition,
180
+ marketAnalysis_1.marketAnalysisToolDefinition,
181
+ checkPortfolio_1.checkPortfolioToolDefinition,
182
+ limitOrderManager_1.createLimitOrderToolDefinition,
183
+ limitOrderManager_1.listLimitOrdersToolDefinition,
184
+ limitOrderManager_1.cancelLimitOrderToolDefinition,
185
+ updateProfile_1.updateProfileToolDefinition,
186
+ updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
187
+ readFile_1.readLocalFileToolDefinition,
188
+ writeFile_1.writeLocalFileToolDefinition,
189
+ executeShell_1.runTerminalCommandToolDefinition,
190
+ browseWeb_1.browseWebsiteToolDefinition,
191
+ installSkill_1.installExternalSkillToolDefinition,
192
+ ...pluginManager_1.pluginManager.getToolDefinitions()
154
193
  ],
155
194
  tool_choice: "auto",
156
195
  });
@@ -171,76 +210,112 @@ async function processUserInput(input, role = 'user') {
171
210
  if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
172
211
  for (const _toolCall of responseMessage.tool_calls) {
173
212
  const toolCall = _toolCall;
174
- if (toolCall.function.name === 'get_balance') {
175
- const args = JSON.parse(toolCall.function.arguments);
176
- const balanceResult = await (0, getBalance_1.getBalance)(args.chainName, args.address, args.token);
177
- exports.logger.addEntry({
178
- role: 'tool',
179
- tool_call_id: toolCall.id,
180
- name: toolCall.function.name,
181
- content: balanceResult,
182
- });
183
- }
184
- else if (toolCall.function.name === 'transfer_token' || toolCall.function.name === 'transfer_native') {
185
- const args = JSON.parse(toolCall.function.arguments);
186
- const transferResult = await (0, transfer_1.prepareTransfer)(args.chainName, args.toAddress, args.amountStr || args.amountEth, args.token);
187
- exports.logger.addEntry({
188
- role: 'tool',
189
- tool_call_id: toolCall.id,
190
- name: toolCall.function.name,
191
- content: transferResult,
192
- });
193
- }
194
- else if (toolCall.function.name === 'get_price') {
195
- const args = JSON.parse(toolCall.function.arguments);
196
- const priceResult = await (0, getPrice_1.getPrice)(args.coinId);
197
- exports.logger.addEntry({
198
- role: 'tool',
199
- tool_call_id: toolCall.id,
200
- name: toolCall.function.name,
201
- content: priceResult,
202
- });
203
- }
204
- else if (toolCall.function.name === 'swap_token') {
205
- const args = JSON.parse(toolCall.function.arguments);
206
- const swapResult = await (0, swapToken_1.prepareSwapToken)(args.chainName, args.fromToken, args.toToken, args.amountStr || args.amount, args.mode, args.providerName);
207
- exports.logger.addEntry({
208
- role: 'tool',
209
- tool_call_id: toolCall.id,
210
- name: toolCall.function.name,
211
- content: swapResult,
212
- });
213
- }
214
- else if (toolCall.function.name === 'bridge_token') {
215
- const args = JSON.parse(toolCall.function.arguments);
216
- const bridgeResult = await (0, bridgeToken_1.prepareBridgeToken)(args.fromChainName, args.toChainName, args.fromToken, args.toToken, args.amountStr, args.mode, args.providerName);
217
- exports.logger.addEntry({
218
- role: 'tool',
219
- tool_call_id: toolCall.id,
220
- name: toolCall.function.name,
221
- content: bridgeResult,
222
- });
223
- }
224
- else if (toolCall.function.name === 'mint_nft') {
225
- const args = JSON.parse(toolCall.function.arguments);
226
- const mintResult = await (0, mintNft_1.prepareMintNft)(args.chainName, args.contractAddress, args.functionSignature, args.argsStr, args.valueEth);
227
- exports.logger.addEntry({
228
- role: 'tool',
229
- tool_call_id: toolCall.id,
230
- name: toolCall.function.name,
231
- content: mintResult,
232
- });
233
- }
234
- else if (toolCall.function.name === 'custom_tx') {
235
- const args = JSON.parse(toolCall.function.arguments);
236
- const customResult = await (0, customTx_1.prepareCustomTx)(args.chainName, args.toAddress, args.dataHex, args.valueEth, args.gasLimitStr);
237
- exports.logger.addEntry({
238
- role: 'tool',
239
- tool_call_id: toolCall.id,
240
- name: toolCall.function.name,
241
- content: customResult,
242
- });
213
+ let result = "";
214
+ const args = JSON.parse(toolCall.function.arguments);
215
+ const toolName = toolCall.function.name;
216
+ switch (toolName) {
217
+ case 'get_balance': {
218
+ result = await (0, getBalance_1.getBalance)(args.chainName, args.address, args.token);
219
+ break;
220
+ }
221
+ case 'transfer_token':
222
+ case 'transfer_native': {
223
+ result = await (0, transfer_1.prepareTransfer)(args.chainName, args.toAddress, args.amountStr || args.amountEth, args.token);
224
+ break;
225
+ }
226
+ case 'get_price': {
227
+ result = await (0, getPrice_1.getPrice)(args.coinId);
228
+ break;
229
+ }
230
+ case 'swap_token': {
231
+ result = await (0, swapToken_1.prepareSwapToken)(args.chainName, args.fromToken, args.toToken, args.amountStr || args.amount, args.mode, args.providerName);
232
+ break;
233
+ }
234
+ case 'bridge_token': {
235
+ result = await (0, bridgeToken_1.prepareBridgeToken)(args.fromChainName, args.toChainName, args.fromToken, args.toToken, args.amountStr, args.mode, args.providerName);
236
+ break;
237
+ }
238
+ case 'mint_nft': {
239
+ result = await (0, mintNft_1.prepareMintNft)(args.chainName, args.contractAddress, args.functionSignature, args.argsStr, args.valueEth);
240
+ break;
241
+ }
242
+ case 'custom_tx': {
243
+ result = await (0, customTx_1.prepareCustomTx)(args.chainName, args.toAddress, args.dataHex, args.valueEth, args.gasLimitStr);
244
+ break;
245
+ }
246
+ case 'create_wallet': {
247
+ result = await (0, createWallet_1.createWallet)();
248
+ break;
249
+ }
250
+ case 'check_token_security': {
251
+ result = await (0, checkSecurity_1.checkTokenSecurity)(args.chainName, args.contractAddress);
252
+ break;
253
+ }
254
+ case 'analyze_market': {
255
+ result = await (0, marketAnalysis_1.analyzeMarket)(args.chainName, args.tokenAddressOrSymbol);
256
+ break;
257
+ }
258
+ case 'check_portfolio': {
259
+ result = await (0, checkPortfolio_1.checkPortfolio)(args.chainName, args.address);
260
+ break;
261
+ }
262
+ case 'create_limit_order': {
263
+ result = limitOrderManager_1.limitOrderManager.createOrder(args.chainName, args.fromToken, args.toToken, args.amountStr, args.targetPriceUsd, args.condition);
264
+ break;
265
+ }
266
+ case 'list_limit_orders': {
267
+ result = limitOrderManager_1.limitOrderManager.listOrders();
268
+ break;
269
+ }
270
+ case 'cancel_limit_order': {
271
+ result = limitOrderManager_1.limitOrderManager.cancelOrder(args.id);
272
+ break;
273
+ }
274
+ case 'update_profile': {
275
+ result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
276
+ break;
277
+ }
278
+ case 'update_security_policy': {
279
+ result = (0, updateSecurityPolicy_1.updateSecurityPolicy)(args.rule, args.action);
280
+ break;
281
+ }
282
+ case 'read_local_file': {
283
+ result = (0, readFile_1.readLocalFile)(args.filePath);
284
+ break;
285
+ }
286
+ case 'write_local_file': {
287
+ result = (0, writeFile_1.writeLocalFile)(args.filePath, args.content);
288
+ break;
289
+ }
290
+ case 'run_terminal_command': {
291
+ result = await (0, executeShell_1.runTerminalCommand)(args.command);
292
+ break;
293
+ }
294
+ case 'browse_website': {
295
+ result = await (0, browseWeb_1.browseWebsite)(args.url);
296
+ break;
297
+ }
298
+ case 'install_external_skill': {
299
+ result = await (0, installSkill_1.installExternalSkill)(args.url);
300
+ break;
301
+ }
302
+ default: {
303
+ const externalResult = await pluginManager_1.pluginManager.executeTool(toolName, args);
304
+ if (externalResult !== null) {
305
+ result = externalResult;
306
+ }
307
+ else {
308
+ result = `Error: Tool ${toolName} is not implemented.`;
309
+ }
310
+ break;
311
+ }
243
312
  }
313
+ exports.logger.addEntry({
314
+ role: 'tool',
315
+ tool_call_id: toolCall.id,
316
+ name: toolName,
317
+ content: result,
318
+ });
244
319
  }
245
320
  // Second call to get the final answer after tool execution
246
321
  const secondMessages = [
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.updateProfileToolDefinition = void 0;
7
+ exports.updateProfile = updateProfile;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const paths_1 = require("../config/paths");
10
+ function updateProfile(content, mode) {
11
+ try {
12
+ const userMdPath = (0, paths_1.getPath)('user.md');
13
+ if (mode === 'replace') {
14
+ fs_1.default.writeFileSync(userMdPath, content, 'utf8');
15
+ return "Profile replaced successfully. New user.md has been saved.";
16
+ }
17
+ else {
18
+ let existingContent = "";
19
+ if (fs_1.default.existsSync(userMdPath)) {
20
+ existingContent = fs_1.default.readFileSync(userMdPath, 'utf8');
21
+ }
22
+ const newContent = existingContent + "\n" + content;
23
+ fs_1.default.writeFileSync(userMdPath, newContent, 'utf8');
24
+ return "Profile appended successfully. New instructions added to user.md.";
25
+ }
26
+ }
27
+ catch (error) {
28
+ return `Failed to update profile: ${error.message}`;
29
+ }
30
+ }
31
+ exports.updateProfileToolDefinition = {
32
+ type: "function",
33
+ function: {
34
+ name: "update_profile",
35
+ description: "Updates or rewrites the user.md file. Use this when the user asks you to remember something about them, change their persona, or update your instructions.",
36
+ parameters: {
37
+ type: "object",
38
+ properties: {
39
+ content: {
40
+ type: "string",
41
+ description: "The content to write or append to user.md",
42
+ },
43
+ mode: {
44
+ type: "string",
45
+ enum: ["append", "replace"],
46
+ description: "Whether to append the content to the existing file or replace the entire file.",
47
+ }
48
+ },
49
+ required: ["content", "mode"],
50
+ },
51
+ },
52
+ };
@@ -12,6 +12,8 @@ const reasoning_1 = require("../agent/reasoning");
12
12
  const parser_1 = require("../config/parser");
13
13
  const tracker_1 = require("./tracker");
14
14
  const transactionManager_1 = require("../agent/transactionManager");
15
+ const limitOrderManager_1 = require("../agent/limitOrderManager");
16
+ const pluginManager_1 = require("../system/pluginManager");
15
17
  const transfer_1 = require("../web3/skills/transfer");
16
18
  const swapToken_1 = require("../web3/skills/swapToken");
17
19
  const getBalance_1 = require("../web3/skills/getBalance");
@@ -180,6 +182,10 @@ app.use((req, res, next) => {
180
182
  }
181
183
  });
182
184
  function startServer() {
185
+ pluginManager_1.pluginManager.loadPlugins().then(() => {
186
+ console.log(`[PluginManager] Finished loading external skills.`);
187
+ });
188
+ limitOrderManager_1.limitOrderManager.startMonitor();
183
189
  const PORT = process.env.PORT || 3000;
184
190
  app.listen(PORT, () => {
185
191
  console.log(`🤖 Nyxora API Server running on port ${PORT}`);
@@ -28,6 +28,11 @@ function startTelegramBot() {
28
28
  const text = msg.text;
29
29
  if (!text)
30
30
  return;
31
+ if (text === '/clear') {
32
+ reasoning_1.logger.clear();
33
+ bot.sendMessage(chatId, '✅ Memori AI telah dihapus. Mari kita mulai obrolan baru!');
34
+ return;
35
+ }
31
36
  // Log incoming message
32
37
  console.log(`[Telegram] Received from ${msg.from?.first_name}: ${text}`);
33
38
  // Send typing action to Telegram
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.pluginManager = exports.PluginManager = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class PluginManager {
10
+ skills = new Map();
11
+ async loadPlugins() {
12
+ const pluginsDir = path_1.default.join(process.cwd(), 'src', 'external_skills');
13
+ if (!fs_1.default.existsSync(pluginsDir)) {
14
+ fs_1.default.mkdirSync(pluginsDir, { recursive: true });
15
+ return;
16
+ }
17
+ const files = fs_1.default.readdirSync(pluginsDir);
18
+ for (const file of files) {
19
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
20
+ try {
21
+ // Dynamic import requires relative path from this file or absolute path
22
+ // For TS compiled to JS, absolute path is safer
23
+ const absolutePath = path_1.default.resolve(pluginsDir, file);
24
+ // Note: In development with ts-node, requiring .ts works.
25
+ // In production, we need compiled .js files.
26
+ const module = require(absolutePath);
27
+ if (module.toolDefinition && module.execute) {
28
+ const toolName = module.toolDefinition.function.name;
29
+ this.skills.set(toolName, module);
30
+ console.log(`[PluginManager] Loaded external skill: ${toolName}`);
31
+ }
32
+ }
33
+ catch (error) {
34
+ console.error(`[PluginManager] Failed to load plugin ${file}:`, error);
35
+ }
36
+ }
37
+ }
38
+ }
39
+ getToolDefinitions() {
40
+ return Array.from(this.skills.values()).map(skill => skill.toolDefinition);
41
+ }
42
+ async executeTool(toolName, args) {
43
+ const skill = this.skills.get(toolName);
44
+ if (skill) {
45
+ try {
46
+ return await skill.execute(args);
47
+ }
48
+ catch (error) {
49
+ return `External skill ${toolName} failed: ${error.message}`;
50
+ }
51
+ }
52
+ return null; // Tool not found in external skills
53
+ }
54
+ }
55
+ exports.PluginManager = PluginManager;
56
+ exports.pluginManager = new PluginManager();
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.browseWebsiteToolDefinition = void 0;
4
+ exports.browseWebsite = browseWebsite;
5
+ async function browseWebsite(url) {
6
+ try {
7
+ const response = await fetch(url, {
8
+ headers: {
9
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Nyxora/1.0',
10
+ }
11
+ });
12
+ if (!response.ok) {
13
+ return `Error: Failed to fetch URL, Status: ${response.status} ${response.statusText}`;
14
+ }
15
+ const html = await response.text();
16
+ // A very basic HTML to text stripping to avoid exceeding context limits
17
+ // In a production app, we would use cheerio or puppeteer.
18
+ const text = html
19
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
20
+ .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
21
+ .replace(/<[^>]+>/g, ' ')
22
+ .replace(/\s+/g, ' ')
23
+ .trim();
24
+ // Limit to first 20000 characters to prevent context overflow
25
+ if (text.length > 20000) {
26
+ return text.substring(0, 20000) + "... [Content Truncated]";
27
+ }
28
+ return text;
29
+ }
30
+ catch (error) {
31
+ return `Failed to browse website: ${error.message}`;
32
+ }
33
+ }
34
+ exports.browseWebsiteToolDefinition = {
35
+ type: "function",
36
+ function: {
37
+ name: "browse_website",
38
+ description: "Fetches and reads the textual content of a webpage.",
39
+ parameters: {
40
+ type: "object",
41
+ properties: {
42
+ url: {
43
+ type: "string",
44
+ description: "The URL of the webpage to read.",
45
+ }
46
+ },
47
+ required: ["url"],
48
+ },
49
+ },
50
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runTerminalCommandToolDefinition = void 0;
4
+ exports.runTerminalCommand = runTerminalCommand;
5
+ const child_process_1 = require("child_process");
6
+ function runTerminalCommand(command) {
7
+ return new Promise((resolve) => {
8
+ (0, child_process_1.exec)(command, { maxBuffer: 1024 * 1024 * 10 }, (error, stdout, stderr) => {
9
+ let output = "";
10
+ if (stdout)
11
+ output += `STDOUT:\n${stdout}\n`;
12
+ if (stderr)
13
+ output += `STDERR:\n${stderr}\n`;
14
+ if (error)
15
+ output += `ERROR:\n${error.message}\n`;
16
+ if (!output)
17
+ output = "Command executed successfully with no output.";
18
+ resolve(output);
19
+ });
20
+ });
21
+ }
22
+ exports.runTerminalCommandToolDefinition = {
23
+ type: "function",
24
+ function: {
25
+ name: "run_terminal_command",
26
+ description: "Executes a shell/terminal command on the user's host machine. Use this to install packages, run scripts, manage processes, etc.",
27
+ parameters: {
28
+ type: "object",
29
+ properties: {
30
+ command: {
31
+ type: "string",
32
+ description: "The terminal command to execute.",
33
+ }
34
+ },
35
+ required: ["command"],
36
+ },
37
+ },
38
+ };