@openenthrium/oe-runtime-sdk 1.7.5
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/README.md +139 -0
- package/index.js +58 -0
- package/package.json +64 -0
- package/src/data/connectionTypes.json +18587 -0
- package/src/engine/index.js +183 -0
- package/src/engine/llm.js +71 -0
- package/src/engine/promptBuilder.js +38 -0
- package/src/index.js +156 -0
- package/src/middleware/auth.js +63 -0
- package/src/providers/embedding/index.js +69 -0
- package/src/providers/llm/index.js +136 -0
- package/src/routes/admin.js +686 -0
- package/src/routes/agents.js +193 -0
- package/src/routes/apiKeys.js +55 -0
- package/src/routes/audio.js +111 -0
- package/src/routes/auth.js +83 -0
- package/src/routes/chat.js +381 -0
- package/src/routes/connectors.js +435 -0
- package/src/routes/dashboard.js +244 -0
- package/src/routes/documents.js +443 -0
- package/src/routes/embed.js +103 -0
- package/src/routes/marketplace.js +49 -0
- package/src/routes/models.js +81 -0
- package/src/routes/oauth.js +423 -0
- package/src/routes/projects.js +238 -0
- package/src/routes/settings.js +45 -0
- package/src/routes/setup.js +42 -0
- package/src/routes/sso.js +218 -0
- package/src/routes/superadmin.js +51 -0
- package/src/routes/templates.js +75 -0
- package/src/routes/threads.js +53 -0
- package/src/routes/workspaces.js +464 -0
- package/src/telemetry/bootstrap.js +39 -0
- package/src/telemetry/registration.js +16 -0
- package/src/utils/activityLog.js +16 -0
- package/src/utils/agentChain.js +141 -0
- package/src/utils/buildVisualization.js +102 -0
- package/src/utils/dlpScanner.js +57 -0
- package/src/utils/ingestionQueue.js +240 -0
- package/src/utils/prepareConnectors.js +66 -0
- package/src/utils/rag/_settings.js +6 -0
- package/src/utils/rag/chroma.js +82 -0
- package/src/utils/rag/lancedb.js +103 -0
- package/src/utils/rag/milvus.js +116 -0
- package/src/utils/rag/pgvector.js +104 -0
- package/src/utils/rag/pinecone.js +71 -0
- package/src/utils/rag/qdrant.js +106 -0
- package/src/utils/rag/weaviate.js +137 -0
- package/src/utils/rag/zilliz.js +115 -0
- package/src/utils/scheduler.js +168 -0
- package/src/utils/tier.js +106 -0
- package/src/utils/tools/adapters/_template.js +74 -0
- package/src/utils/tools/adapters/box.js +71 -0
- package/src/utils/tools/adapters/confluence.js +80 -0
- package/src/utils/tools/adapters/database.js +215 -0
- package/src/utils/tools/adapters/dropbox.js +74 -0
- package/src/utils/tools/adapters/elasticsearch.js +90 -0
- package/src/utils/tools/adapters/filesystem.js +197 -0
- package/src/utils/tools/adapters/freshdesk.js +87 -0
- package/src/utils/tools/adapters/gdrive.js +326 -0
- package/src/utils/tools/adapters/github.js +169 -0
- package/src/utils/tools/adapters/gmail.js +157 -0
- package/src/utils/tools/adapters/graphql.js +73 -0
- package/src/utils/tools/adapters/hubspot.js +101 -0
- package/src/utils/tools/adapters/image-gen.js +120 -0
- package/src/utils/tools/adapters/jira.js +86 -0
- package/src/utils/tools/adapters/kafka.js +126 -0
- package/src/utils/tools/adapters/ldap.js +118 -0
- package/src/utils/tools/adapters/mcp-client.js +138 -0
- package/src/utils/tools/adapters/mongodb.js +119 -0
- package/src/utils/tools/adapters/mqtt.js +106 -0
- package/src/utils/tools/adapters/music-gen.js +118 -0
- package/src/utils/tools/adapters/notion.js +93 -0
- package/src/utils/tools/adapters/ocr.js +107 -0
- package/src/utils/tools/adapters/onedrive.js +72 -0
- package/src/utils/tools/adapters/redis.js +104 -0
- package/src/utils/tools/adapters/rest-api.js +119 -0
- package/src/utils/tools/adapters/s3.js +142 -0
- package/src/utils/tools/adapters/search.js +80 -0
- package/src/utils/tools/adapters/sftp.js +121 -0
- package/src/utils/tools/adapters/shell.js +97 -0
- package/src/utils/tools/adapters/slack.js +83 -0
- package/src/utils/tools/adapters/speech.js +160 -0
- package/src/utils/tools/adapters/ssh.js +113 -0
- package/src/utils/tools/adapters/video-gen.js +152 -0
- package/src/utils/tools/adapters/web3.js +111 -0
- package/src/utils/tools/adapters/zendesk.js +82 -0
- package/src/utils/tools/adapters/zoho-mail.js +246 -0
- package/src/utils/tools/registry.js +229 -0
- package/src/utils/vectorStore.js +68 -0
- package/src/utils/workflowEngine.js +4 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const { Client } = require("ssh2");
|
|
2
|
+
|
|
3
|
+
function getConfig(connector) {
|
|
4
|
+
const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
|
|
5
|
+
const cfg = connector.config ? JSON.parse(connector.config) : {};
|
|
6
|
+
return {
|
|
7
|
+
host: auth.host || cfg.host || "localhost",
|
|
8
|
+
port: parseInt(auth.port || cfg.port || "22"),
|
|
9
|
+
username: auth.username || cfg.username || "root",
|
|
10
|
+
password: auth.password || cfg.password || undefined,
|
|
11
|
+
privateKey: auth.privateKey || cfg.privateKey || undefined,
|
|
12
|
+
passphrase: auth.passphrase || cfg.passphrase || undefined,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function withSftp(cfg, fn) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const conn = new Client();
|
|
19
|
+
conn.on("ready", () => {
|
|
20
|
+
conn.sftp((err, sftp) => {
|
|
21
|
+
if (err) { conn.end(); return reject(err); }
|
|
22
|
+
Promise.resolve(fn(sftp))
|
|
23
|
+
.then(r => { conn.end(); resolve(r); })
|
|
24
|
+
.catch(e => { conn.end(); reject(e); });
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
conn.on("error", reject);
|
|
28
|
+
const connectCfg = { host: cfg.host, port: cfg.port, username: cfg.username };
|
|
29
|
+
if (cfg.privateKey) { connectCfg.privateKey = cfg.privateKey; if (cfg.passphrase) connectCfg.passphrase = cfg.passphrase; }
|
|
30
|
+
else if (cfg.password) connectCfg.password = cfg.password;
|
|
31
|
+
conn.connect(connectCfg);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getToolDefinitions(connector) {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
type: "function",
|
|
39
|
+
function: {
|
|
40
|
+
name: `conn_${connector.id}_list`,
|
|
41
|
+
description: `List files and directories on the SFTP server "${connector.name}".`,
|
|
42
|
+
parameters: { type: "object", properties: { path: { type: "string", description: "Remote directory path (default: /)" } }, required: [] },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: "function",
|
|
47
|
+
function: {
|
|
48
|
+
name: `conn_${connector.id}_read`,
|
|
49
|
+
description: `Read a text file from the SFTP server "${connector.name}".`,
|
|
50
|
+
parameters: { type: "object", properties: { path: { type: "string", description: "Full remote file path" } }, required: ["path"] },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: "function",
|
|
55
|
+
function: {
|
|
56
|
+
name: `conn_${connector.id}_write`,
|
|
57
|
+
description: `Write content to a file on the SFTP server "${connector.name}".`,
|
|
58
|
+
parameters: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: "function",
|
|
63
|
+
function: {
|
|
64
|
+
name: `conn_${connector.id}_delete`,
|
|
65
|
+
description: `Delete a file on the SFTP server "${connector.name}".`,
|
|
66
|
+
parameters: { type: "object", properties: { path: { type: "string" } }, required: ["path"] },
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getAnthropicToolDefinitions(connector) {
|
|
73
|
+
return getToolDefinitions(connector).map(t => ({
|
|
74
|
+
name: t.function.name,
|
|
75
|
+
description: t.function.description,
|
|
76
|
+
input_schema: t.function.parameters,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function executeTool(action, args, connector) {
|
|
81
|
+
const cfg = getConfig(connector);
|
|
82
|
+
if (!cfg.password && !cfg.privateKey) return "SFTP not configured: provide password or privateKey.";
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
if (action === "list") {
|
|
86
|
+
return await withSftp(cfg, sftp => new Promise((res, rej) => {
|
|
87
|
+
sftp.readdir(args.path || "/", (err, list) => {
|
|
88
|
+
if (err) return rej(err);
|
|
89
|
+
res(list.map(f => `${f.longname}`).join("\n"));
|
|
90
|
+
});
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
if (action === "read") {
|
|
94
|
+
return await withSftp(cfg, sftp => new Promise((res, rej) => {
|
|
95
|
+
const chunks = [];
|
|
96
|
+
const stream = sftp.createReadStream(args.path);
|
|
97
|
+
stream.on("data", d => chunks.push(d));
|
|
98
|
+
stream.on("end", () => res(Buffer.concat(chunks).toString("utf8").slice(0, 8000)));
|
|
99
|
+
stream.on("error", rej);
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
if (action === "write") {
|
|
103
|
+
return await withSftp(cfg, sftp => new Promise((res, rej) => {
|
|
104
|
+
const stream = sftp.createWriteStream(args.path);
|
|
105
|
+
stream.on("close", () => res(`Written: ${args.path}`));
|
|
106
|
+
stream.on("error", rej);
|
|
107
|
+
stream.end(args.content || "");
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
if (action === "delete") {
|
|
111
|
+
return await withSftp(cfg, sftp => new Promise((res, rej) => {
|
|
112
|
+
sftp.unlink(args.path, err => err ? rej(err) : res(`Deleted: ${args.path}`));
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
return `Unknown SFTP action: ${action}`;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
return `SFTP error: ${err.message}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { exec } = require("child_process");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
function getConfig(connector) {
|
|
10
|
+
const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
|
|
11
|
+
const config = connector.config ? JSON.parse(connector.config) : {};
|
|
12
|
+
return {
|
|
13
|
+
cwd: auth.cwd || config.cwd || process.cwd(),
|
|
14
|
+
timeout: parseInt(auth.timeout || config.timeout || "30"),
|
|
15
|
+
shell: auth.shell || config.shell || (os.platform() === "win32" ? "cmd" : "bash"),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function runCommand(command, cfg) {
|
|
20
|
+
const timeoutMs = Math.min(cfg.timeout, 300) * 1000;
|
|
21
|
+
const cwd = path.resolve(cfg.cwd);
|
|
22
|
+
const shellOpt = cfg.shell === "cmd"
|
|
23
|
+
? { shell: "cmd.exe" }
|
|
24
|
+
: { shell: cfg.shell === "sh" ? "/bin/sh" : "/bin/bash" };
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
exec(command, { cwd, timeout: timeoutMs, maxBuffer: 1024 * 1024 * 4, ...shellOpt }, (err, stdout, stderr) => {
|
|
28
|
+
if (err && err.killed) {
|
|
29
|
+
resolve({ stdout: stdout || "", stderr: stderr || "", exit_code: -1, error: `Command timed out after ${cfg.timeout}s` });
|
|
30
|
+
} else {
|
|
31
|
+
resolve({ stdout: stdout || "", stderr: stderr || "", exit_code: err ? (err.code ?? 1) : 0 });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatResult({ stdout, stderr, exit_code, error }) {
|
|
38
|
+
const parts = [];
|
|
39
|
+
if (error) parts.push(`Error: ${error}`);
|
|
40
|
+
if (stdout.trim()) parts.push(stdout.trim());
|
|
41
|
+
if (stderr.trim()) parts.push(`STDERR:\n${stderr.trim()}`);
|
|
42
|
+
parts.push(`exit_code: ${exit_code}`);
|
|
43
|
+
return parts.join("\n\n").slice(0, 8000);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Tool definitions ─────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function getToolDefinitions(connector) {
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
type: "function",
|
|
52
|
+
function: {
|
|
53
|
+
name: `conn_${connector.id}_exec`,
|
|
54
|
+
description: `Run a shell command on the local machine (${connector.name}). Use to execute Python, PHP, Node.js, Java, Bash scripts or any installed CLI tool. Returns stdout, stderr, and exit code.`,
|
|
55
|
+
parameters: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
command: { type: "string", description: "The shell command to run (e.g. 'python3 script.py --arg value')." },
|
|
59
|
+
cwd: { type: "string", description: "Working directory override. Defaults to the cwd set in oe-config.json." },
|
|
60
|
+
timeout: { type: "number", description: "Timeout in seconds (default from config, max 300)." },
|
|
61
|
+
},
|
|
62
|
+
required: ["command"],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getAnthropicToolDefinitions(connector) {
|
|
70
|
+
return getToolDefinitions(connector).map(t => ({
|
|
71
|
+
name: t.function.name,
|
|
72
|
+
description: t.function.description,
|
|
73
|
+
input_schema: t.function.parameters,
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Execute ──────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
async function executeTool(action, args, connector) {
|
|
80
|
+
if (action !== "exec") return `Unknown shell action: ${action}`;
|
|
81
|
+
|
|
82
|
+
const { command, cwd: cwdOverride, timeout: timeoutOverride } = args;
|
|
83
|
+
if (!command) return "Missing required field: command.";
|
|
84
|
+
|
|
85
|
+
const cfg = getConfig(connector);
|
|
86
|
+
if (cwdOverride) cfg.cwd = cwdOverride;
|
|
87
|
+
if (timeoutOverride) cfg.timeout = Math.min(parseInt(timeoutOverride), 300);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const result = await runCommand(command, cfg);
|
|
91
|
+
return formatResult(result);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
return `Shell error: ${err.message}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
|
|
3
|
+
function cfg(connector) {
|
|
4
|
+
return connector.authConfig ? JSON.parse(connector.authConfig) : {};
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function client(token) {
|
|
8
|
+
return axios.create({
|
|
9
|
+
baseURL: "https://slack.com/api",
|
|
10
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const TOOLS = c => [
|
|
15
|
+
{ action: "post_message", desc: `Post a message to a Slack channel via ${c.name}.`,
|
|
16
|
+
params: { channel: { type: "string", description: "Channel name or ID (e.g. #general)" },
|
|
17
|
+
text: { type: "string", description: "Message text to post." } }, required: ["channel","text"] },
|
|
18
|
+
{ action: "list_channels", desc: `List public Slack channels via ${c.name}.`, params: {}, required: [] },
|
|
19
|
+
{ action: "search_messages", desc: `Search Slack messages via ${c.name}.`,
|
|
20
|
+
params: { query: { type: "string", description: "Search query." } }, required: ["query"] },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
function getToolDefinitions(connector) {
|
|
24
|
+
return TOOLS(connector).map(t => ({
|
|
25
|
+
type: "function",
|
|
26
|
+
function: {
|
|
27
|
+
name: `conn_${connector.id}_${t.action}`,
|
|
28
|
+
description: t.desc,
|
|
29
|
+
parameters: { type: "object", properties: t.params, required: t.required },
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getAnthropicToolDefinitions(connector) {
|
|
35
|
+
return TOOLS(connector).map(t => ({
|
|
36
|
+
name: `conn_${connector.id}_${t.action}`,
|
|
37
|
+
description: t.desc,
|
|
38
|
+
input_schema: { type: "object", properties: t.params, required: t.required },
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function executeTool(action, args, connector) {
|
|
43
|
+
const { botToken } = cfg(connector);
|
|
44
|
+
if (!botToken) return "Slack not configured. Please add credentials in Integrations.";
|
|
45
|
+
const api = client(botToken);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
if (action === "post_message") {
|
|
49
|
+
const { channel, text } = args;
|
|
50
|
+
const res = await api.post("/chat.postMessage", { channel, text });
|
|
51
|
+
if (!res.data.ok) return `Slack error: ${res.data.error}`;
|
|
52
|
+
return `Message posted to ${channel}.`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (action === "list_channels") {
|
|
56
|
+
const res = await api.get("/conversations.list?types=public_channel&limit=50");
|
|
57
|
+
if (!res.data.ok) return `Slack error: ${res.data.error}`;
|
|
58
|
+
const channels = (res.data.channels || []).map(c => `#${c.name} (${c.num_members} members)`);
|
|
59
|
+
return channels.length ? channels.join("\n") : "No channels found.";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (action === "search_messages") {
|
|
63
|
+
const res = await api.get(`/search.messages?query=${encodeURIComponent(args.query)}&count=10`);
|
|
64
|
+
if (!res.data.ok) return `Slack error: ${res.data.error}`;
|
|
65
|
+
const matches = res.data.messages?.matches || [];
|
|
66
|
+
if (!matches.length) return "No messages found.";
|
|
67
|
+
return matches.map(m => `[${m.channel?.name}] ${m.username}: ${m.text}`).join("\n\n");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return `Unknown Slack action: ${action}`;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
return `Slack error: ${err.response?.data?.error || err.message}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function testConnection(authConfig) {
|
|
77
|
+
try {
|
|
78
|
+
const res = await client(authConfig.botToken).get("/auth.test");
|
|
79
|
+
return res.data.ok;
|
|
80
|
+
} catch { return false; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool, testConnection };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const axios = require("axios");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
function getToolDefinitions(connector) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
type: "function",
|
|
11
|
+
function: {
|
|
12
|
+
name: `conn_${connector.id}_text_to_speech`,
|
|
13
|
+
description: `Convert text to speech audio using "${connector.name}". Returns a URL or base64 audio.`,
|
|
14
|
+
parameters: {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
text: { type: "string", description: "Text to convert to speech" },
|
|
18
|
+
voice: { type: "string", description: "Voice ID or name to use (provider-specific)" },
|
|
19
|
+
language: { type: "string", description: "Language code (e.g. 'en-US', 'fr-FR'). Optional." },
|
|
20
|
+
speed: { type: "number", description: "Speaking speed multiplier (0.5 to 2.0, default 1.0)" },
|
|
21
|
+
},
|
|
22
|
+
required: ["text"],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: "function",
|
|
28
|
+
function: {
|
|
29
|
+
name: `conn_${connector.id}_list_voices`,
|
|
30
|
+
description: `List available voices from "${connector.name}".`,
|
|
31
|
+
parameters: { type: "object", properties: {}, required: [] },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getAnthropicToolDefinitions(connector) {
|
|
38
|
+
return getToolDefinitions(connector).map(t => ({
|
|
39
|
+
name: t.function.name,
|
|
40
|
+
description: t.function.description,
|
|
41
|
+
input_schema: t.function.parameters,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function executeTool(action, args, connector) {
|
|
46
|
+
const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
|
|
47
|
+
const cfg = connector.config ? JSON.parse(connector.config) : {};
|
|
48
|
+
const creds = { ...cfg, ...auth };
|
|
49
|
+
const type = connector.type;
|
|
50
|
+
|
|
51
|
+
// ── ElevenLabs ────────────────────────────────────────────────────────────
|
|
52
|
+
if (type === "elevenlabs") {
|
|
53
|
+
if (action === "list_voices") {
|
|
54
|
+
const resp = await axios.get("https://api.elevenlabs.io/v1/voices", {
|
|
55
|
+
headers: { "xi-api-key": creds.apiKey },
|
|
56
|
+
});
|
|
57
|
+
const voices = (resp.data.voices || []).map(v => `${v.name} (${v.voice_id})`).join("\n");
|
|
58
|
+
return `Available voices:\n${voices}`;
|
|
59
|
+
}
|
|
60
|
+
if (action === "text_to_speech") {
|
|
61
|
+
const voiceId = args.voice || creds.voiceId || "EXAVITQu4vr4xnSDxMaL";
|
|
62
|
+
const resp = await axios.post(
|
|
63
|
+
`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`,
|
|
64
|
+
{ text: args.text, model_id: "eleven_monolingual_v1", voice_settings: { stability: 0.5, similarity_boost: 0.75 } },
|
|
65
|
+
{ headers: { "xi-api-key": creds.apiKey, "Content-Type": "application/json" }, responseType: "arraybuffer" }
|
|
66
|
+
);
|
|
67
|
+
const tmpFile = path.join(os.tmpdir(), `oe-tts-${Date.now()}.mp3`);
|
|
68
|
+
fs.writeFileSync(tmpFile, resp.data);
|
|
69
|
+
return `Audio generated and saved to: ${tmpFile} (${Math.round(resp.data.byteLength / 1024)}KB MP3)`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── OpenAI TTS ────────────────────────────────────────────────────────────
|
|
74
|
+
if (type === "openai-tts") {
|
|
75
|
+
if (action === "list_voices") {
|
|
76
|
+
return "Available voices: alloy, echo, fable, onyx, nova, shimmer";
|
|
77
|
+
}
|
|
78
|
+
if (action === "text_to_speech") {
|
|
79
|
+
const resp = await axios.post(
|
|
80
|
+
"https://api.openai.com/v1/audio/speech",
|
|
81
|
+
{ model: "tts-1", input: args.text, voice: args.voice || creds.voice || "alloy", speed: args.speed || 1.0 },
|
|
82
|
+
{ headers: { Authorization: `Bearer ${creds.apiKey}`, "Content-Type": "application/json" }, responseType: "arraybuffer" }
|
|
83
|
+
);
|
|
84
|
+
const tmpFile = path.join(os.tmpdir(), `oe-tts-${Date.now()}.mp3`);
|
|
85
|
+
fs.writeFileSync(tmpFile, resp.data);
|
|
86
|
+
return `Audio generated and saved to: ${tmpFile} (${Math.round(resp.data.byteLength / 1024)}KB MP3)`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Azure Cognitive Speech ────────────────────────────────────────────────
|
|
91
|
+
if (type === "azure-speech") {
|
|
92
|
+
if (action === "list_voices") {
|
|
93
|
+
const region = creds.region || "eastus";
|
|
94
|
+
const token = await axios.post(
|
|
95
|
+
`https://${region}.api.cognitive.microsoft.com/sts/v1.0/issuetoken`,
|
|
96
|
+
null,
|
|
97
|
+
{ headers: { "Ocp-Apim-Subscription-Key": creds.subscriptionKey } }
|
|
98
|
+
);
|
|
99
|
+
const resp = await axios.get(
|
|
100
|
+
`https://${region}.tts.speech.microsoft.com/cognitiveservices/voices/list`,
|
|
101
|
+
{ headers: { Authorization: `Bearer ${token.data}` } }
|
|
102
|
+
);
|
|
103
|
+
const voices = (resp.data || []).slice(0, 20).map(v => `${v.ShortName} (${v.Locale})`).join("\n");
|
|
104
|
+
return `Available voices (first 20):\n${voices}`;
|
|
105
|
+
}
|
|
106
|
+
if (action === "text_to_speech") {
|
|
107
|
+
const region = creds.region || "eastus";
|
|
108
|
+
const voice = args.voice || "en-US-JennyNeural";
|
|
109
|
+
const lang = args.language || "en-US";
|
|
110
|
+
const ssml = `<speak version='1.0' xml:lang='${lang}'><voice name='${voice}'>${args.text}</voice></speak>`;
|
|
111
|
+
const token = await axios.post(
|
|
112
|
+
`https://${region}.api.cognitive.microsoft.com/sts/v1.0/issuetoken`,
|
|
113
|
+
null,
|
|
114
|
+
{ headers: { "Ocp-Apim-Subscription-Key": creds.subscriptionKey } }
|
|
115
|
+
);
|
|
116
|
+
const resp = await axios.post(
|
|
117
|
+
`https://${region}.tts.speech.microsoft.com/cognitiveservices/v1`,
|
|
118
|
+
ssml,
|
|
119
|
+
{
|
|
120
|
+
headers: { Authorization: `Bearer ${token.data}`, "Content-Type": "application/ssml+xml", "X-Microsoft-OutputFormat": "audio-24khz-48kbitrate-mono-mp3" },
|
|
121
|
+
responseType: "arraybuffer",
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
const tmpFile = path.join(os.tmpdir(), `oe-tts-${Date.now()}.mp3`);
|
|
125
|
+
fs.writeFileSync(tmpFile, resp.data);
|
|
126
|
+
return `Audio generated and saved to: ${tmpFile} (${Math.round(resp.data.byteLength / 1024)}KB MP3)`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Google Text-to-Speech ─────────────────────────────────────────────────
|
|
131
|
+
if (type === "google-tts") {
|
|
132
|
+
if (action === "list_voices") {
|
|
133
|
+
const resp = await axios.get(
|
|
134
|
+
`https://texttospeech.googleapis.com/v1/voices?key=${creds.apiKey}`
|
|
135
|
+
);
|
|
136
|
+
const voices = (resp.data.voices || []).slice(0, 20).map(v => `${v.name} (${v.languageCodes?.join(", ")})`).join("\n");
|
|
137
|
+
return `Available voices (first 20):\n${voices}`;
|
|
138
|
+
}
|
|
139
|
+
if (action === "text_to_speech") {
|
|
140
|
+
const lang = args.language || "en-US";
|
|
141
|
+
const voice = args.voice || "en-US-Standard-A";
|
|
142
|
+
const resp = await axios.post(
|
|
143
|
+
`https://texttospeech.googleapis.com/v1/text:synthesize?key=${creds.apiKey}`,
|
|
144
|
+
{
|
|
145
|
+
input: { text: args.text },
|
|
146
|
+
voice: { languageCode: lang, name: voice },
|
|
147
|
+
audioConfig: { audioEncoding: "MP3", speakingRate: args.speed || 1.0 },
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
const audio = Buffer.from(resp.data.audioContent, "base64");
|
|
151
|
+
const tmpFile = path.join(os.tmpdir(), `oe-tts-${Date.now()}.mp3`);
|
|
152
|
+
fs.writeFileSync(tmpFile, audio);
|
|
153
|
+
return `Audio generated and saved to: ${tmpFile} (${Math.round(audio.byteLength / 1024)}KB MP3)`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return `Unsupported action "${action}" for speech connector type: ${type}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const { Client } = require("ssh2");
|
|
2
|
+
|
|
3
|
+
function getConfig(connector) {
|
|
4
|
+
const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
|
|
5
|
+
const config = connector.config ? JSON.parse(connector.config) : {};
|
|
6
|
+
return {
|
|
7
|
+
host: auth.host || config.host || "localhost",
|
|
8
|
+
port: parseInt(auth.port || config.port || "22"),
|
|
9
|
+
username: auth.username || config.username || "root",
|
|
10
|
+
privateKey: auth.privateKey || config.privateKey || null,
|
|
11
|
+
passphrase: auth.passphrase || config.passphrase || undefined,
|
|
12
|
+
password: auth.password || config.password || undefined,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function runCommand(cfg, command, timeout = 30000) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const conn = new Client();
|
|
19
|
+
let output = "";
|
|
20
|
+
let stderr = "";
|
|
21
|
+
let settled = false;
|
|
22
|
+
|
|
23
|
+
const done = (err) => {
|
|
24
|
+
if (settled) return;
|
|
25
|
+
settled = true;
|
|
26
|
+
conn.end();
|
|
27
|
+
if (err) reject(err);
|
|
28
|
+
else resolve({ stdout: output, stderr });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const timer = setTimeout(() => done(new Error(`Command timed out after ${timeout / 1000}s`)), timeout);
|
|
32
|
+
|
|
33
|
+
conn.on("ready", () => {
|
|
34
|
+
conn.exec(command, (err, stream) => {
|
|
35
|
+
if (err) { clearTimeout(timer); return done(err); }
|
|
36
|
+
stream
|
|
37
|
+
.on("close", () => { clearTimeout(timer); done(); })
|
|
38
|
+
.on("data", d => { output += d.toString(); })
|
|
39
|
+
.stderr.on("data", d => { stderr += d.toString(); });
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
conn.on("error", err => { clearTimeout(timer); done(err); });
|
|
44
|
+
|
|
45
|
+
const connectCfg = { host: cfg.host, port: cfg.port, username: cfg.username };
|
|
46
|
+
if (cfg.privateKey) {
|
|
47
|
+
connectCfg.privateKey = cfg.privateKey;
|
|
48
|
+
if (cfg.passphrase) connectCfg.passphrase = cfg.passphrase;
|
|
49
|
+
} else if (cfg.password) {
|
|
50
|
+
connectCfg.password = cfg.password;
|
|
51
|
+
}
|
|
52
|
+
conn.connect(connectCfg);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getToolDefinitions(connector) {
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
type: "function",
|
|
60
|
+
function: {
|
|
61
|
+
name: `conn_${connector.id}_exec`,
|
|
62
|
+
description: `Run a shell command on the remote server via SSH (${connector.name}). Use for security audits, log inspection, process checks, port scans, disk usage, and system diagnostics.`,
|
|
63
|
+
parameters: {
|
|
64
|
+
type: "object",
|
|
65
|
+
properties: {
|
|
66
|
+
command: { type: "string", description: "Shell command to execute on the remote server." },
|
|
67
|
+
timeout: { type: "number", description: "Timeout in seconds (default 30, max 120)." },
|
|
68
|
+
},
|
|
69
|
+
required: ["command"],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getAnthropicToolDefinitions(connector) {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
name: `conn_${connector.id}_exec`,
|
|
80
|
+
description: `Run a shell command on the remote server via SSH (${connector.name}). Use for security audits, log inspection, process checks, port scans, disk usage, and system diagnostics.`,
|
|
81
|
+
input_schema: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
command: { type: "string", description: "Shell command to execute on the remote server." },
|
|
85
|
+
timeout: { type: "number", description: "Timeout in seconds (default 30, max 120)." },
|
|
86
|
+
},
|
|
87
|
+
required: ["command"],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function executeTool(action, args, connector) {
|
|
94
|
+
if (action !== "exec") return `Unknown SSH action: ${action}`;
|
|
95
|
+
|
|
96
|
+
const cfg = getConfig(connector);
|
|
97
|
+
if (!cfg.privateKey && !cfg.password) return "SSH connector not configured: provide privateKeyPath or password.";
|
|
98
|
+
|
|
99
|
+
const { command, timeout } = args;
|
|
100
|
+
if (!command) return "Missing required field: command.";
|
|
101
|
+
|
|
102
|
+
const timeoutMs = Math.min((parseInt(timeout) || 30), 120) * 1000;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const { stdout, stderr } = await runCommand(cfg, command, timeoutMs);
|
|
106
|
+
const out = (stdout + (stderr ? `\nSTDERR:\n${stderr}` : "")).trim();
|
|
107
|
+
return out || "(no output)";
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return `SSH error: ${err.message}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
|