@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.
Files changed (91) hide show
  1. package/README.md +139 -0
  2. package/index.js +58 -0
  3. package/package.json +64 -0
  4. package/src/data/connectionTypes.json +18587 -0
  5. package/src/engine/index.js +183 -0
  6. package/src/engine/llm.js +71 -0
  7. package/src/engine/promptBuilder.js +38 -0
  8. package/src/index.js +156 -0
  9. package/src/middleware/auth.js +63 -0
  10. package/src/providers/embedding/index.js +69 -0
  11. package/src/providers/llm/index.js +136 -0
  12. package/src/routes/admin.js +686 -0
  13. package/src/routes/agents.js +193 -0
  14. package/src/routes/apiKeys.js +55 -0
  15. package/src/routes/audio.js +111 -0
  16. package/src/routes/auth.js +83 -0
  17. package/src/routes/chat.js +381 -0
  18. package/src/routes/connectors.js +435 -0
  19. package/src/routes/dashboard.js +244 -0
  20. package/src/routes/documents.js +443 -0
  21. package/src/routes/embed.js +103 -0
  22. package/src/routes/marketplace.js +49 -0
  23. package/src/routes/models.js +81 -0
  24. package/src/routes/oauth.js +423 -0
  25. package/src/routes/projects.js +238 -0
  26. package/src/routes/settings.js +45 -0
  27. package/src/routes/setup.js +42 -0
  28. package/src/routes/sso.js +218 -0
  29. package/src/routes/superadmin.js +51 -0
  30. package/src/routes/templates.js +75 -0
  31. package/src/routes/threads.js +53 -0
  32. package/src/routes/workspaces.js +464 -0
  33. package/src/telemetry/bootstrap.js +39 -0
  34. package/src/telemetry/registration.js +16 -0
  35. package/src/utils/activityLog.js +16 -0
  36. package/src/utils/agentChain.js +141 -0
  37. package/src/utils/buildVisualization.js +102 -0
  38. package/src/utils/dlpScanner.js +57 -0
  39. package/src/utils/ingestionQueue.js +240 -0
  40. package/src/utils/prepareConnectors.js +66 -0
  41. package/src/utils/rag/_settings.js +6 -0
  42. package/src/utils/rag/chroma.js +82 -0
  43. package/src/utils/rag/lancedb.js +103 -0
  44. package/src/utils/rag/milvus.js +116 -0
  45. package/src/utils/rag/pgvector.js +104 -0
  46. package/src/utils/rag/pinecone.js +71 -0
  47. package/src/utils/rag/qdrant.js +106 -0
  48. package/src/utils/rag/weaviate.js +137 -0
  49. package/src/utils/rag/zilliz.js +115 -0
  50. package/src/utils/scheduler.js +168 -0
  51. package/src/utils/tier.js +106 -0
  52. package/src/utils/tools/adapters/_template.js +74 -0
  53. package/src/utils/tools/adapters/box.js +71 -0
  54. package/src/utils/tools/adapters/confluence.js +80 -0
  55. package/src/utils/tools/adapters/database.js +215 -0
  56. package/src/utils/tools/adapters/dropbox.js +74 -0
  57. package/src/utils/tools/adapters/elasticsearch.js +90 -0
  58. package/src/utils/tools/adapters/filesystem.js +197 -0
  59. package/src/utils/tools/adapters/freshdesk.js +87 -0
  60. package/src/utils/tools/adapters/gdrive.js +326 -0
  61. package/src/utils/tools/adapters/github.js +169 -0
  62. package/src/utils/tools/adapters/gmail.js +157 -0
  63. package/src/utils/tools/adapters/graphql.js +73 -0
  64. package/src/utils/tools/adapters/hubspot.js +101 -0
  65. package/src/utils/tools/adapters/image-gen.js +120 -0
  66. package/src/utils/tools/adapters/jira.js +86 -0
  67. package/src/utils/tools/adapters/kafka.js +126 -0
  68. package/src/utils/tools/adapters/ldap.js +118 -0
  69. package/src/utils/tools/adapters/mcp-client.js +138 -0
  70. package/src/utils/tools/adapters/mongodb.js +119 -0
  71. package/src/utils/tools/adapters/mqtt.js +106 -0
  72. package/src/utils/tools/adapters/music-gen.js +118 -0
  73. package/src/utils/tools/adapters/notion.js +93 -0
  74. package/src/utils/tools/adapters/ocr.js +107 -0
  75. package/src/utils/tools/adapters/onedrive.js +72 -0
  76. package/src/utils/tools/adapters/redis.js +104 -0
  77. package/src/utils/tools/adapters/rest-api.js +119 -0
  78. package/src/utils/tools/adapters/s3.js +142 -0
  79. package/src/utils/tools/adapters/search.js +80 -0
  80. package/src/utils/tools/adapters/sftp.js +121 -0
  81. package/src/utils/tools/adapters/shell.js +97 -0
  82. package/src/utils/tools/adapters/slack.js +83 -0
  83. package/src/utils/tools/adapters/speech.js +160 -0
  84. package/src/utils/tools/adapters/ssh.js +113 -0
  85. package/src/utils/tools/adapters/video-gen.js +152 -0
  86. package/src/utils/tools/adapters/web3.js +111 -0
  87. package/src/utils/tools/adapters/zendesk.js +82 -0
  88. package/src/utils/tools/adapters/zoho-mail.js +246 -0
  89. package/src/utils/tools/registry.js +229 -0
  90. package/src/utils/vectorStore.js +68 -0
  91. package/src/utils/workflowEngine.js +4 -0
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ const axios = require("axios");
3
+
4
+ function getToolDefinitions(connector) {
5
+ return [
6
+ {
7
+ type: "function",
8
+ function: {
9
+ name: `conn_${connector.id}_extract_text`,
10
+ description: `Extract text from an image or document using "${connector.name}" OCR.`,
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ image_url: { type: "string", description: "Publicly accessible URL of the image or document to process" },
15
+ language: { type: "string", description: "Language hint (e.g. 'en', 'fr', 'de'). Optional." },
16
+ },
17
+ required: ["image_url"],
18
+ },
19
+ },
20
+ },
21
+ ];
22
+ }
23
+
24
+ function getAnthropicToolDefinitions(connector) {
25
+ return getToolDefinitions(connector).map(t => ({
26
+ name: t.function.name,
27
+ description: t.function.description,
28
+ input_schema: t.function.parameters,
29
+ }));
30
+ }
31
+
32
+ async function executeTool(action, args, connector) {
33
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
34
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
35
+ const creds = { ...cfg, ...auth };
36
+ const type = connector.type;
37
+
38
+ if (action !== "extract_text") return `Unknown action: ${action}`;
39
+
40
+ const imageUrl = args.image_url;
41
+ if (!imageUrl) return "Error: image_url is required.";
42
+
43
+ // ── Azure Computer Vision ─────────────────────────────────────────────────
44
+ if (type === "azure-vision") {
45
+ const endpoint = (creds.endpoint || "").replace(/\/$/, "");
46
+ const resp = await axios.post(
47
+ `${endpoint}/computervision/imageanalysis:analyze?api-version=2023-02-01-preview&features=read`,
48
+ { url: imageUrl },
49
+ { headers: { "Ocp-Apim-Subscription-Key": creds.apiKey, "Content-Type": "application/json" } }
50
+ );
51
+ const blocks = resp.data.readResult?.blocks || [];
52
+ return blocks.flatMap(b => b.lines.map(l => l.text)).join("\n") || "No text found.";
53
+ }
54
+
55
+ // ── Google Cloud Vision ───────────────────────────────────────────────────
56
+ if (type === "google-vision") {
57
+ const resp = await axios.post(
58
+ `https://vision.googleapis.com/v1/images:annotate?key=${creds.apiKey}`,
59
+ {
60
+ requests: [{
61
+ image: { source: { imageUri: imageUrl } },
62
+ features: [{ type: "DOCUMENT_TEXT_DETECTION" }],
63
+ }],
64
+ }
65
+ );
66
+ return resp.data.responses?.[0]?.fullTextAnnotation?.text || "No text found.";
67
+ }
68
+
69
+ // ── AWS Textract ──────────────────────────────────────────────────────────
70
+ if (type === "aws-textract") {
71
+ const { AwsClient } = await (async () => {
72
+ try { return require("aws4"); } catch { return null; }
73
+ })();
74
+ // Simplified: use Textract via direct HTTPS with AWS4 signing
75
+ // For URL-based images, download first then send as bytes — use URL form here
76
+ const region = creds.region || "us-east-1";
77
+ const resp = await axios.post(
78
+ `https://textract.${region}.amazonaws.com/`,
79
+ { Document: { S3Object: null, Bytes: null }, FeatureTypes: [] },
80
+ {
81
+ headers: {
82
+ "X-Amz-Target": "Textract.DetectDocumentText",
83
+ "Content-Type": "application/x-amz-json-1.1",
84
+ "X-Api-Source-Url": imageUrl,
85
+ },
86
+ }
87
+ ).catch(() => null);
88
+ if (!resp) return "Textract: provide an S3 URL or configure AWS credentials with proper signing.";
89
+ const blocks = (resp.data.Blocks || []).filter(b => b.BlockType === "LINE");
90
+ return blocks.map(b => b.Text).join("\n") || "No text found.";
91
+ }
92
+
93
+ // ── Tesseract (self-hosted OCR service) ───────────────────────────────────
94
+ if (type === "tesseract-ocr") {
95
+ const baseUrl = (creds.baseUrl || "http://localhost:8884").replace(/\/$/, "");
96
+ const resp = await axios.post(
97
+ `${baseUrl}/ocr`,
98
+ { url: imageUrl, language: args.language || "eng" },
99
+ { headers: { "Content-Type": "application/json" } }
100
+ );
101
+ return resp.data.text || resp.data.result || "No text found.";
102
+ }
103
+
104
+ return `Unsupported OCR connector type: ${type}`;
105
+ }
106
+
107
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,72 @@
1
+ const axios = require("axios");
2
+
3
+ function cfg(connector) { return connector.config ? JSON.parse(connector.config) : {}; }
4
+ function auth(connector) { return connector.authConfig ? JSON.parse(connector.authConfig) : {}; }
5
+
6
+ async function getToken(connector, db) {
7
+ const a = auth(connector);
8
+ const c = cfg(connector);
9
+ if (!a.refreshToken) return a.accessToken;
10
+ if (a.expiresAt && Date.now() < a.expiresAt - 60000) return a.accessToken;
11
+ const tenantId = c.tenantId || "common";
12
+ const { data } = await axios.post(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
13
+ new URLSearchParams({ grant_type: "refresh_token", refresh_token: a.refreshToken, client_id: c.clientId, client_secret: c.clientSecret, scope: "Files.Read.All offline_access User.Read" }),
14
+ { headers: { "Content-Type": "application/x-www-form-urlencoded" } });
15
+ const newAuth = { ...a, accessToken: data.access_token, expiresAt: Date.now() + data.expires_in * 1000, refreshToken: data.refresh_token || a.refreshToken };
16
+ if (db) await db.connector.update({ where: { id: connector.id }, data: { authConfig: JSON.stringify(newAuth) } });
17
+ return data.access_token;
18
+ }
19
+
20
+ function client(token) {
21
+ return axios.create({ baseURL: "https://graph.microsoft.com/v1.0", headers: { Authorization: `Bearer ${token}` } });
22
+ }
23
+
24
+ const TOOLS = c => [
25
+ { action: "list_files", desc: `List files/folders in OneDrive via ${c.name}.`,
26
+ params: { itemId: { type: "string", description: 'Item ID or path. Use "root" for root folder.' } }, required: [] },
27
+ { action: "read_file", desc: `Read the text content of a OneDrive file via ${c.name}.`,
28
+ params: { itemId: { type: "string", description: "OneDrive file item ID." } }, required: ["itemId"] },
29
+ { action: "search", desc: `Search files in OneDrive via ${c.name}.`,
30
+ params: { query: { type: "string", description: "Search query." } }, required: ["query"] },
31
+ ];
32
+
33
+ function getToolDefinitions(connector) {
34
+ return TOOLS(connector).map(t => ({ type: "function", function: { name: `conn_${connector.id}_${t.action}`, description: t.desc, parameters: { type: "object", properties: t.params, required: t.required } } }));
35
+ }
36
+ function getAnthropicToolDefinitions(connector) {
37
+ return TOOLS(connector).map(t => ({ name: `conn_${connector.id}_${t.action}`, description: t.desc, input_schema: { type: "object", properties: t.params, required: t.required } }));
38
+ }
39
+
40
+ async function executeTool(action, args, connector, db) {
41
+ const token = await getToken(connector, db);
42
+ const api = client(token);
43
+ try {
44
+ if (action === "list_files") {
45
+ const id = args.itemId || "root";
46
+ const res = await api.get(`/me/drive/items/${id}/children?$top=100`);
47
+ const items = res.data.value || [];
48
+ return items.map(i => `${i.folder ? "📁" : "📄"} ${i.name} (id: ${i.id})`).join("\n") || "Empty folder.";
49
+ }
50
+ if (action === "read_file") {
51
+ const res = await api.get(`/me/drive/items/${args.itemId}/content`, { responseType: "text" });
52
+ return String(res.data).slice(0, 8000);
53
+ }
54
+ if (action === "search") {
55
+ const res = await api.get(`/me/drive/root/search(q='${encodeURIComponent(args.query)}')?$top=20`);
56
+ const items = res.data.value || [];
57
+ return items.map(i => `${i.name} (id: ${i.id})`).join("\n") || "No results.";
58
+ }
59
+ return `Unknown OneDrive action: ${action}`;
60
+ } catch (err) { return `OneDrive error: ${err.response?.data?.error?.message || err.message}`; }
61
+ }
62
+
63
+ async function testConnection(authConfig, config, db, connectorId) {
64
+ try {
65
+ const connector = { authConfig: JSON.stringify(authConfig), config: JSON.stringify(config || {}), id: connectorId };
66
+ const token = await getToken(connector, db);
67
+ const res = await client(token).get("/me/drive");
68
+ return !!res.data.id;
69
+ } catch { return false; }
70
+ }
71
+
72
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool, testConnection, getToken, client: (t) => client(t) };
@@ -0,0 +1,104 @@
1
+ const Redis = require("ioredis");
2
+
3
+ function cfg(connector) {
4
+ return connector.config ? JSON.parse(connector.config) : {};
5
+ }
6
+ function auth(connector) {
7
+ return connector.authConfig ? JSON.parse(connector.authConfig) : {};
8
+ }
9
+
10
+ function client(connector) {
11
+ const c = cfg(connector);
12
+ const a = auth(connector);
13
+ return new Redis({
14
+ host: c.host || "localhost",
15
+ port: parseInt(c.port || "6379"),
16
+ password: a.password || undefined,
17
+ db: parseInt(c.db || "0"),
18
+ tls: c.tls ? {} : undefined,
19
+ lazyConnect: true,
20
+ connectTimeout: 8000,
21
+ commandTimeout: 10000,
22
+ });
23
+ }
24
+
25
+ const TOOLS = c => [
26
+ { action: "get", desc: `Get the value of a key from ${c.name}.`,
27
+ params: { key: { type: "string", description: "The Redis key." } }, required: ["key"] },
28
+ { action: "set", desc: `Set a key-value pair in ${c.name}.`,
29
+ params: { key: { type: "string" }, value: { type: "string" }, ttl: { type: "number", description: "TTL in seconds (optional)." } }, required: ["key","value"] },
30
+ { action: "del", desc: `Delete one or more keys from ${c.name}.`,
31
+ params: { keys: { type: "array", items: { type: "string" }, description: "Keys to delete." } }, required: ["keys"] },
32
+ { action: "keys", desc: `List keys matching a pattern in ${c.name}.`,
33
+ params: { pattern: { type: "string", description: 'Pattern e.g. "user:*"' } }, required: ["pattern"] },
34
+ { action: "hgetall",desc: `Get all fields of a hash key from ${c.name}.`,
35
+ params: { key: { type: "string" } }, required: ["key"] },
36
+ { action: "lrange", desc: `Get a range of elements from a list in ${c.name}.`,
37
+ params: { key: { type: "string" }, start: { type: "number", description: "Start index (0)." }, stop: { type: "number", description: "Stop index (-1 for all)." } }, required: ["key"] },
38
+ ];
39
+
40
+ function getToolDefinitions(connector) {
41
+ return TOOLS(connector).map(t => ({
42
+ type: "function",
43
+ function: { name: `conn_${connector.id}_${t.action}`, description: t.desc,
44
+ parameters: { type: "object", properties: t.params, required: t.required } },
45
+ }));
46
+ }
47
+
48
+ function getAnthropicToolDefinitions(connector) {
49
+ return TOOLS(connector).map(t => ({
50
+ name: `conn_${connector.id}_${t.action}`, description: t.desc,
51
+ input_schema: { type: "object", properties: t.params, required: t.required },
52
+ }));
53
+ }
54
+
55
+ async function executeTool(action, args, connector) {
56
+ const r = client(connector);
57
+ try {
58
+ await r.connect();
59
+
60
+ if (action === "get") {
61
+ const val = await r.get(args.key);
62
+ return val === null ? `Key "${args.key}" not found.` : val;
63
+ }
64
+ if (action === "set") {
65
+ if (args.ttl) await r.set(args.key, args.value, "EX", args.ttl);
66
+ else await r.set(args.key, args.value);
67
+ return `OK — set "${args.key}"`;
68
+ }
69
+ if (action === "del") {
70
+ const n = await r.del(...args.keys);
71
+ return `Deleted ${n} key(s).`;
72
+ }
73
+ if (action === "keys") {
74
+ const keys = await r.keys(args.pattern || "*");
75
+ return keys.length ? keys.slice(0, 200).join("\n") : "No keys matched.";
76
+ }
77
+ if (action === "hgetall") {
78
+ const hash = await r.hgetall(args.key);
79
+ return hash && Object.keys(hash).length ? JSON.stringify(hash, null, 2) : `Hash "${args.key}" not found or empty.`;
80
+ }
81
+ if (action === "lrange") {
82
+ const items = await r.lrange(args.key, args.start ?? 0, args.stop ?? -1);
83
+ return items.length ? JSON.stringify(items, null, 2) : `List "${args.key}" not found or empty.`;
84
+ }
85
+ return `Unknown Redis action: ${action}`;
86
+ } catch (err) {
87
+ return `Redis error: ${err.message}`;
88
+ } finally {
89
+ r.disconnect();
90
+ }
91
+ }
92
+
93
+ async function testConnection(authConfig, config) {
94
+ const connector = { config: JSON.stringify(config || {}), authConfig: JSON.stringify(authConfig || {}) };
95
+ const r = client(connector);
96
+ try {
97
+ await r.connect();
98
+ await r.ping();
99
+ return true;
100
+ } catch { return false; }
101
+ finally { r.disconnect(); }
102
+ }
103
+
104
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool, testConnection };
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ const axios = require("axios");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+
7
+ function buildHeaders(creds) {
8
+ const headers = { "Content-Type": "application/json", Accept: "application/json" };
9
+ if (creds.bearerToken) headers["Authorization"] = `Bearer ${creds.bearerToken}`;
10
+ if (creds.apiKey) headers[creds.headerName || "X-API-Key"] = creds.apiKey;
11
+ if (creds.username && creds.password)
12
+ headers["Authorization"] = "Basic " + Buffer.from(`${creds.username}:${creds.password}`).toString("base64");
13
+ if (creds.headers && typeof creds.headers === "object") Object.assign(headers, creds.headers);
14
+ return headers;
15
+ }
16
+
17
+ function buildParams(creds, extra) {
18
+ const params = { ...(extra || {}) };
19
+ if (creds.queryParamName && creds.apiKey) params[creds.queryParamName] = creds.apiKey;
20
+ return params;
21
+ }
22
+
23
+ function getToolDefinitions(connector) {
24
+ const desc = (method) => `Perform an HTTP ${method} request against the "${connector.name}" API.`;
25
+ const pathP = { path: { type: "string", description: "URL path (e.g. /users or /generate)" } };
26
+ const qP = { params: { type: "object", description: "Query parameters as key-value pairs", additionalProperties: { type: "string" } } };
27
+ const bodyP = { body: { type: "object", description: "JSON request body", additionalProperties: true } };
28
+
29
+ return [
30
+ { type: "function", function: { name: `conn_${connector.id}_get`, description: desc("GET"), parameters: { type: "object", properties: { ...pathP, ...qP }, required: ["path"] } } },
31
+ { type: "function", function: { name: `conn_${connector.id}_post`, description: desc("POST"), parameters: { type: "object", properties: { ...pathP, ...bodyP }, required: ["path", "body"] } } },
32
+ { type: "function", function: { name: `conn_${connector.id}_put`, description: desc("PUT"), parameters: { type: "object", properties: { ...pathP, ...bodyP }, required: ["path", "body"] } } },
33
+ { type: "function", function: { name: `conn_${connector.id}_patch`, description: desc("PATCH"), parameters: { type: "object", properties: { ...pathP, ...bodyP }, required: ["path", "body"] } } },
34
+ { type: "function", function: { name: `conn_${connector.id}_delete`, description: desc("DELETE"), parameters: { type: "object", properties: { ...pathP, ...qP }, required: ["path"] } } },
35
+ ];
36
+ }
37
+
38
+ function getAnthropicToolDefinitions(connector) {
39
+ return getToolDefinitions(connector).map(t => ({
40
+ name: t.function.name,
41
+ description: t.function.description,
42
+ input_schema: t.function.parameters,
43
+ }));
44
+ }
45
+
46
+ async function executeTool(action, args, connector) {
47
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
48
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
49
+ const creds = { ...cfg, ...auth };
50
+
51
+ const baseUrl = (creds.baseUrl || "").replace(/\/$/, "");
52
+ if (!baseUrl) return "Connector base URL not configured. Add baseUrl to oe-config.json.";
53
+
54
+ const headers = buildHeaders(creds);
55
+ const params = buildParams(creds, args.params);
56
+ const url = baseUrl + (args.path || "");
57
+ const opts = { headers, params, timeout: 300000, responseType: "arraybuffer" };
58
+
59
+ try {
60
+ // Serialize body as URL-encoded form data when Content-Type demands it
61
+ const isFormEncoded = (headers["Content-Type"] || "").includes("application/x-www-form-urlencoded");
62
+ const serializeBody = (b) => isFormEncoded ? new URLSearchParams(b || {}).toString() : (b || {});
63
+
64
+ let res;
65
+ if (action === "get") res = await axios.get(url, opts);
66
+ if (action === "post") res = await axios.post(url, serializeBody(args.body), opts);
67
+ if (action === "put") res = await axios.put(url, serializeBody(args.body), opts);
68
+ if (action === "patch") res = await axios.patch(url, serializeBody(args.body), opts);
69
+ if (action === "delete") res = await axios.delete(url, opts);
70
+ if (!res) return "Unknown HTTP action.";
71
+
72
+ const contentType = (res.headers["content-type"] || "").toLowerCase();
73
+
74
+ // Binary response — save audio/video to a temp file and return the path
75
+ if (
76
+ contentType.includes("audio/") ||
77
+ contentType.includes("video/") ||
78
+ contentType.includes("application/octet-stream")
79
+ ) {
80
+ const ext = contentType.includes("audio/mpeg") ? ".mp3"
81
+ : contentType.includes("audio/wav") ? ".wav"
82
+ : contentType.includes("audio/ogg") ? ".ogg"
83
+ : contentType.includes("video/mp4") ? ".mp4"
84
+ : ".bin";
85
+ const tmpFile = path.join(os.tmpdir(), `oe-output-${Date.now()}${ext}`);
86
+ fs.writeFileSync(tmpFile, Buffer.from(res.data));
87
+ return `Audio/video saved to: ${tmpFile}`;
88
+ }
89
+
90
+ // JSON or text
91
+ const text = Buffer.from(res.data).toString("utf8");
92
+ try {
93
+ const parsed = JSON.parse(text);
94
+
95
+ // OpenAI-style image response with b64_json — save each image to a temp file
96
+ if (Array.isArray(parsed?.data) && parsed.data[0]?.b64_json) {
97
+ const saved = parsed.data.map((item, i) => {
98
+ const tmpFile = path.join(os.tmpdir(), `oe-image-${Date.now()}-${i}.png`);
99
+ fs.writeFileSync(tmpFile, Buffer.from(item.b64_json, "base64"));
100
+ return { ...item, b64_json: undefined, local_path: tmpFile };
101
+ });
102
+ return JSON.stringify({ ...parsed, data: saved }, null, 2).slice(0, 8000);
103
+ }
104
+
105
+ return JSON.stringify(parsed, null, 2).slice(0, 8000);
106
+ } catch { return text.slice(0, 8000); }
107
+
108
+ } catch (err) {
109
+ if (err.response) {
110
+ const body = Buffer.isBuffer(err.response.data)
111
+ ? Buffer.from(err.response.data).toString("utf8").slice(0, 500)
112
+ : String(err.response.data || "").slice(0, 500);
113
+ return `API error ${err.response.status}: ${body}`;
114
+ }
115
+ return `Request error: ${err.message}`;
116
+ }
117
+ }
118
+
119
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,142 @@
1
+ const { S3Client, ListBucketsCommand, ListObjectsV2Command, GetObjectCommand, PutObjectCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3");
2
+
3
+ function getClient(connector) {
4
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
5
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
6
+
7
+ const accessKeyId = auth.accessKeyId || cfg.accessKeyId || "";
8
+ const secretAccessKey = auth.secretAccessKey || cfg.secretAccessKey || "";
9
+ const region = auth.region || cfg.region || "us-east-1";
10
+ const endpoint = auth.endpoint || cfg.endpoint || auth.baseUrl || cfg.baseUrl || undefined;
11
+
12
+ return {
13
+ client: new S3Client({
14
+ region,
15
+ credentials: accessKeyId ? { accessKeyId, secretAccessKey } : undefined,
16
+ endpoint,
17
+ forcePathStyle: !!endpoint,
18
+ }),
19
+ defaultBucket: auth.bucket || cfg.bucket || "",
20
+ };
21
+ }
22
+
23
+ function getToolDefinitions(connector) {
24
+ return [
25
+ {
26
+ type: "function",
27
+ function: {
28
+ name: `conn_${connector.id}_list_buckets`,
29
+ description: `List all S3 buckets accessible from "${connector.name}".`,
30
+ parameters: { type: "object", properties: {}, required: [] },
31
+ },
32
+ },
33
+ {
34
+ type: "function",
35
+ function: {
36
+ name: `conn_${connector.id}_list_objects`,
37
+ description: `List objects in a bucket on "${connector.name}".`,
38
+ parameters: {
39
+ type: "object",
40
+ properties: {
41
+ bucket: { type: "string", description: "Bucket name (uses configured default if omitted)" },
42
+ prefix: { type: "string", description: "Optional key prefix to filter" },
43
+ },
44
+ required: [],
45
+ },
46
+ },
47
+ },
48
+ {
49
+ type: "function",
50
+ function: {
51
+ name: `conn_${connector.id}_get_object`,
52
+ description: `Read/download an object from "${connector.name}".`,
53
+ parameters: {
54
+ type: "object",
55
+ properties: {
56
+ bucket: { type: "string" },
57
+ key: { type: "string", description: "Object key" },
58
+ },
59
+ required: ["key"],
60
+ },
61
+ },
62
+ },
63
+ {
64
+ type: "function",
65
+ function: {
66
+ name: `conn_${connector.id}_put_object`,
67
+ description: `Upload/write an object to "${connector.name}".`,
68
+ parameters: {
69
+ type: "object",
70
+ properties: {
71
+ bucket: { type: "string" },
72
+ key: { type: "string" },
73
+ content: { type: "string", description: "Text content to upload" },
74
+ },
75
+ required: ["key", "content"],
76
+ },
77
+ },
78
+ },
79
+ {
80
+ type: "function",
81
+ function: {
82
+ name: `conn_${connector.id}_delete_object`,
83
+ description: `Delete an object from "${connector.name}".`,
84
+ parameters: {
85
+ type: "object",
86
+ properties: {
87
+ bucket: { type: "string" },
88
+ key: { type: "string" },
89
+ },
90
+ required: ["key"],
91
+ },
92
+ },
93
+ },
94
+ ];
95
+ }
96
+
97
+ function getAnthropicToolDefinitions(connector) {
98
+ return getToolDefinitions(connector).map(t => ({
99
+ name: t.function.name,
100
+ description: t.function.description,
101
+ input_schema: t.function.parameters,
102
+ }));
103
+ }
104
+
105
+ async function executeTool(action, args, connector) {
106
+ const { client, defaultBucket } = getClient(connector);
107
+ const bucket = args.bucket || defaultBucket;
108
+
109
+ try {
110
+ if (action === "list_buckets") {
111
+ const res = await client.send(new ListBucketsCommand({}));
112
+ return (res.Buckets || []).map(b => `${b.Name} (created: ${b.CreationDate})`).join("\n") || "No buckets found.";
113
+ }
114
+ if (action === "list_objects") {
115
+ if (!bucket) return "Bucket name required.";
116
+ const res = await client.send(new ListObjectsV2Command({ Bucket: bucket, Prefix: args.prefix || undefined, MaxKeys: 100 }));
117
+ return (res.Contents || []).map(o => `${o.Key} (${o.Size} bytes)`).join("\n") || "Empty bucket.";
118
+ }
119
+ if (action === "get_object") {
120
+ if (!bucket) return "Bucket name required.";
121
+ const res = await client.send(new GetObjectCommand({ Bucket: bucket, Key: args.key }));
122
+ const chunks = [];
123
+ for await (const chunk of res.Body) chunks.push(chunk);
124
+ return Buffer.concat(chunks).toString("utf8").slice(0, 8000);
125
+ }
126
+ if (action === "put_object") {
127
+ if (!bucket) return "Bucket name required.";
128
+ await client.send(new PutObjectCommand({ Bucket: bucket, Key: args.key, Body: args.content || "" }));
129
+ return `Uploaded: s3://${bucket}/${args.key}`;
130
+ }
131
+ if (action === "delete_object") {
132
+ if (!bucket) return "Bucket name required.";
133
+ await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: args.key }));
134
+ return `Deleted: s3://${bucket}/${args.key}`;
135
+ }
136
+ return `Unknown S3 action: ${action}`;
137
+ } catch (err) {
138
+ return `S3 error: ${err.message}`;
139
+ }
140
+ }
141
+
142
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ const axios = require("axios");
3
+
4
+ function getToolDefinitions(connector) {
5
+ return [
6
+ {
7
+ type: "function",
8
+ function: {
9
+ name: `conn_${connector.id}_search`,
10
+ description: `Search the web using "${connector.name}" and return the top results with titles, URLs, and snippets.`,
11
+ parameters: {
12
+ type: "object",
13
+ properties: {
14
+ query: { type: "string", description: "Search query" },
15
+ num_results: { type: "number", description: "Number of results to return (default 5, max 10)" },
16
+ },
17
+ required: ["query"],
18
+ },
19
+ },
20
+ },
21
+ ];
22
+ }
23
+
24
+ function getAnthropicToolDefinitions(connector) {
25
+ return getToolDefinitions(connector).map(t => ({
26
+ name: t.function.name,
27
+ description: t.function.description,
28
+ input_schema: t.function.parameters,
29
+ }));
30
+ }
31
+
32
+ async function executeTool(action, args, connector) {
33
+ const auth = connector.authConfig ? JSON.parse(connector.authConfig) : {};
34
+ const cfg = connector.config ? JSON.parse(connector.config) : {};
35
+ const creds = { ...cfg, ...auth };
36
+ const type = connector.type;
37
+ const query = args.query || "";
38
+ const num = Math.min(args.num_results || 5, 10);
39
+
40
+ if (action !== "search") return `Unknown action: ${action}`;
41
+
42
+ // ── Perplexity ────────────────────────────────────────────────────────────
43
+ if (type === "perplexity-search") {
44
+ const resp = await axios.post(
45
+ "https://api.perplexity.ai/chat/completions",
46
+ {
47
+ model: "sonar",
48
+ messages: [{ role: "user", content: query }],
49
+ max_tokens: 1024,
50
+ },
51
+ { headers: { Authorization: `Bearer ${creds.apiKey}`, "Content-Type": "application/json" } }
52
+ );
53
+ const answer = resp.data.choices?.[0]?.message?.content || "";
54
+ const citations = (resp.data.citations || []).slice(0, num).map((url, i) => `[${i + 1}] ${url}`).join("\n");
55
+ return `${answer}\n\nSources:\n${citations}`.trim();
56
+ }
57
+
58
+ // ── Google Custom Search ──────────────────────────────────────────────────
59
+ if (type === "google-search") {
60
+ const resp = await axios.get("https://www.googleapis.com/customsearch/v1", {
61
+ params: { key: creds.apiKey, cx: creds.searchEngineId, q: query, num },
62
+ });
63
+ const items = resp.data.items || [];
64
+ return items.map((r, i) => `${i + 1}. ${r.title}\n ${r.link}\n ${r.snippet || ""}`).join("\n\n") || "No results found.";
65
+ }
66
+
67
+ // ── Bing Web Search ───────────────────────────────────────────────────────
68
+ if (type === "bing-search") {
69
+ const resp = await axios.get("https://api.bing.microsoft.com/v7.0/search", {
70
+ params: { q: query, count: num },
71
+ headers: { "Ocp-Apim-Subscription-Key": creds.apiKey },
72
+ });
73
+ const results = resp.data.webPages?.value || [];
74
+ return results.map((r, i) => `${i + 1}. ${r.name}\n ${r.url}\n ${r.snippet || ""}`).join("\n\n") || "No results found.";
75
+ }
76
+
77
+ return `Unsupported search connector type: ${type}`;
78
+ }
79
+
80
+ module.exports = { getToolDefinitions, getAnthropicToolDefinitions, executeTool };